From 4a82ee410917d58265ef657e6dee7c2c5f230fe2 Mon Sep 17 00:00:00 2001 From: alma Date: Tue, 6 May 2025 16:20:31 +0200 Subject: [PATCH] missions api2 --- lib/services/integration-service.ts | 33 +++++++--- lib/services/leantime-service.ts | 95 ++++++++++++++++++++++++++++- 2 files changed, 118 insertions(+), 10 deletions(-) diff --git a/lib/services/integration-service.ts b/lib/services/integration-service.ts index c77b55f0..b8be7ff2 100644 --- a/lib/services/integration-service.ts +++ b/lib/services/integration-service.ts @@ -69,6 +69,7 @@ export class IntegrationService { try { // Step 1: Create Leantime project (Consider this a critical integration) try { + console.log('Starting Leantime project creation...'); leantimeProjectId = await this.leantimeService.createProject(mission); console.log(`Leantime project created with ID: ${leantimeProjectId}`); integrationStatus.leantime.success = true; @@ -81,11 +82,13 @@ export class IntegrationService { throw leantimeError; // Leantime is critical, so we rethrow } - // Add a delay to avoid rate limits - await new Promise(resolve => setTimeout(resolve, 500)); + // Add a delay to avoid rate limits (extended to 3 seconds) + console.log('Waiting 3 seconds before proceeding to Outline integration...'); + await new Promise(resolve => setTimeout(resolve, 3000)); // Step 2: Create Outline collection (Consider this non-critical) try { + console.log('Starting Outline collection creation...'); outlineCollectionId = await this.outlineService.createCollection(mission); console.log(`Outline collection created with ID: ${outlineCollectionId}`); integrationStatus.outline.success = true; @@ -98,16 +101,20 @@ export class IntegrationService { // Check if it's an authentication error (401) if (axios.isAxiosError(outlineError) && outlineError.response?.status === 401) { console.log('⚠️ Outline authentication error. Please check your API credentials.'); + } else if (axios.isAxiosError(outlineError) && outlineError.response?.status === 429) { + console.log('⚠️ Outline rate limiting error (429). The integration will be skipped for now.'); } // Don't set criticalFailure - Outline is non-critical } - // Add a delay to avoid rate limits - await new Promise(resolve => setTimeout(resolve, 500)); + // Add a delay to avoid rate limits (extended to 3 seconds) + console.log('Waiting 3 seconds before proceeding to Rocket.Chat integration...'); + await new Promise(resolve => setTimeout(resolve, 3000)); // Step 3: Create Rocket.Chat channel (Consider this non-critical) try { + console.log('Starting Rocket.Chat channel creation...'); rocketChatChannelId = await this.rocketChatService.createChannel(mission); console.log(`Rocket.Chat channel created with ID: ${rocketChatChannelId}`); integrationStatus.rocketchat.success = true; @@ -116,6 +123,11 @@ export class IntegrationService { console.error('Error creating Rocket.Chat channel:', rocketChatError); integrationStatus.rocketchat.success = false; integrationStatus.rocketchat.error = rocketChatError instanceof Error ? rocketChatError.message : String(rocketChatError); + + // Check for rate limiting + if (axios.isAxiosError(rocketChatError) && rocketChatError.response?.status === 429) { + console.log('⚠️ Rocket.Chat rate limiting error (429). The integration will be skipped for now.'); + } // Don't set criticalFailure - Rocket.Chat is non-critical } @@ -197,12 +209,14 @@ export class IntegrationService { // Attempt to delete Leantime project if (leantimeProjectId) { try { + console.log(`Attempting to delete Leantime project: ${leantimeProjectId}`); const leantimeSuccess = await this.leantimeService.deleteProject(leantimeProjectId); rollbackStatuses.leantime = leantimeSuccess; console.log(`Leantime project deletion ${leantimeSuccess ? 'successful' : 'failed'}: ${leantimeProjectId}`); - // Add a delay to avoid rate limiting - await new Promise(resolve => setTimeout(resolve, 1000)); + // Add a longer delay to avoid rate limiting (3 seconds) + console.log('Waiting 3 seconds before next rollback operation...'); + await new Promise(resolve => setTimeout(resolve, 3000)); } catch (leantimeError) { console.error('Error during Leantime rollback:', leantimeError); console.log(`⚠️ Note: Leantime project ${leantimeProjectId} may need to be deleted manually`); @@ -212,12 +226,14 @@ export class IntegrationService { // Attempt to delete Outline collection if (outlineCollectionId) { try { + console.log(`Attempting to delete Outline collection: ${outlineCollectionId}`); const outlineSuccess = await this.outlineService.deleteCollection(outlineCollectionId); rollbackStatuses.outline = outlineSuccess; console.log(`Outline collection deletion ${outlineSuccess ? 'successful' : 'failed'}: ${outlineCollectionId}`); - // Add a delay to avoid rate limiting - await new Promise(resolve => setTimeout(resolve, 1000)); + // Add a longer delay to avoid rate limiting (3 seconds) + console.log('Waiting 3 seconds before next rollback operation...'); + await new Promise(resolve => setTimeout(resolve, 3000)); } catch (outlineError) { console.error('Error during Outline rollback:', outlineError); console.log(`⚠️ Note: Outline collection ${outlineCollectionId} may need to be deleted manually`); @@ -227,6 +243,7 @@ export class IntegrationService { // Attempt to delete Rocket.Chat channel if (rocketChatChannelId) { try { + console.log(`Attempting to delete Rocket.Chat channel: ${rocketChatChannelId}`); const rocketChatSuccess = await this.rocketChatService.deleteChannel(rocketChatChannelId); rollbackStatuses.rocketchat = rocketChatSuccess; console.log(`Rocket.Chat channel deletion ${rocketChatSuccess ? 'successful' : 'failed'}: ${rocketChatChannelId}`); diff --git a/lib/services/leantime-service.ts b/lib/services/leantime-service.ts index 5fe5c5f0..c6c57169 100644 --- a/lib/services/leantime-service.ts +++ b/lib/services/leantime-service.ts @@ -452,7 +452,8 @@ export class LeantimeService { { method: 'leantime.rpc.Clients.Clients.getAll', jsonrpc: '2.0', - id: 1 + id: 1, + params: {} }, { headers: { @@ -475,10 +476,20 @@ export class LeantimeService { return parseInt(client.id); } else { console.log(`Client "${clientName}" not found. Creating it...`); + // Add delay before creating client to avoid rate limiting + await new Promise(resolve => setTimeout(resolve, 1000)); return await this.createClient(clientName); } } catch (error) { console.error('Error getting client by name:', error); + + // Check if this is a rate limiting error + if (axios.isAxiosError(error) && error.response?.status === 429) { + console.log('Rate limiting detected (429). Waiting before retry...'); + // Wait 2 seconds before next API call to respect rate limits + await new Promise(resolve => setTimeout(resolve, 2000)); + } + // Try to create the client if we couldn't find it try { console.log(`Attempting to create client "${clientName}" after error...`); @@ -500,7 +511,7 @@ export class LeantimeService { const response = await axios.post( this.getApiEndpoint(), { - method: 'leantime.rpc.Clients.Clients.addClient', + method: 'leantime.rpc.Clients.Clients.create', jsonrpc: '2.0', id: 1, params: { @@ -533,6 +544,52 @@ export class LeantimeService { console.log(`Created client "${clientName}" with ID: ${clientId}`); return clientId; } catch (error) { + // Check if this is a rate limiting error + if (axios.isAxiosError(error) && error.response?.status === 429) { + console.log('Rate limiting detected (429). Waiting and retrying...'); + // Wait 3 seconds and then retry + await new Promise(resolve => setTimeout(resolve, 3000)); + + try { + // Try again with a different API method name + const retryResponse = await axios.post( + this.getApiEndpoint(), + { + method: 'leantime.rpc.Clients.Clients.addClient', + jsonrpc: '2.0', + id: 1, + params: { + values: { + name: clientName, + street: '', + zip: '', + city: '', + state: '', + country: '', + phone: '', + internet: '', + email: '' + } + } + }, + { + headers: { + 'Content-Type': 'application/json', + 'X-API-Key': this.apiToken + } + } + ); + + if (retryResponse.data && retryResponse.data.result) { + const retryClientId = parseInt(retryResponse.data.result); + console.log(`Created client "${clientName}" with ID: ${retryClientId} (retry)`); + return retryClientId; + } + } catch (retryError) { + console.error('Error on retry:', retryError); + } + } + console.error(`Error creating client "${clientName}":`, error); return null; } @@ -669,6 +726,40 @@ export class LeantimeService { return response.data && response.data.result === true; } catch (error) { + // Check if this is a rate limiting error + if (axios.isAxiosError(error) && error.response?.status === 429) { + console.log('Rate limiting detected (429) on delete. Waiting and retrying...'); + + // Wait 3 seconds and then retry + await new Promise(resolve => setTimeout(resolve, 3000)); + + try { + // Retry the delete + const retryResponse = await axios.post( + this.getApiEndpoint(), + { + method: 'leantime.rpc.Projects.Projects.deleteProject', + jsonrpc: '2.0', + id: 1, + params: { + id: projectId + } + }, + { + headers: { + 'Content-Type': 'application/json', + 'X-API-Key': this.apiToken + } + } + ); + + return retryResponse.data && retryResponse.data.result === true; + } catch (retryError) { + console.error(`Error on retry delete for project ${projectId}:`, retryError); + return false; + } + } + console.error(`Error deleting Leantime project ${projectId}:`, error); return false; }