From 8cc401f09a289abaddab1ff18bdddcd7b0d8b6a5 Mon Sep 17 00:00:00 2001 From: alma Date: Tue, 6 May 2025 18:46:06 +0200 Subject: [PATCH] missions api2 --- lib/services/leantime-service.ts | 509 ++++--------------------------- 1 file changed, 59 insertions(+), 450 deletions(-) diff --git a/lib/services/leantime-service.ts b/lib/services/leantime-service.ts index e0458596..fc12214f 100644 --- a/lib/services/leantime-service.ts +++ b/lib/services/leantime-service.ts @@ -64,98 +64,17 @@ export class LeantimeService { console.log('Creating project with data:', JSON.stringify(projectData, null, 2)); - // Create with direct call to createProject - const createResponse = await axios.post( - this.getApiEndpoint(), - { - method: 'leantime.rpc.Projects.createProject', - jsonrpc: '2.0', - id: 1, - params: projectData - }, - { - headers: { - 'Content-Type': 'application/json', - 'X-API-Key': this.apiToken - } - } - ); - - // Log the response - console.log('Create response status:', createResponse.status); - console.log('Create response data:', JSON.stringify(createResponse.data, null, 2)); - - if (createResponse.data && createResponse.data.result) { - const projectId = createResponse.data.result; - console.log(`Created Leantime project with ID: ${projectId}`); - - // If the mission has a logo, set it as project avatar - if (mission.logo) { - await this.setProjectAvatar(projectId, mission.logo); - } - - try { - // Wait a bit before attempting to add any users to avoid rate limits - await new Promise(resolve => setTimeout(resolve, 3000)); - // Check if we can make at least one API call without hitting rate limits - const testResponse = await axios.post( - this.getApiEndpoint(), - { - method: 'leantime.rpc.Auth.getCurrentUser', - jsonrpc: '2.0', - id: 1 - }, - { - headers: { - 'Content-Type': 'application/json', - 'X-API-Key': this.apiToken - } - } - ); - - // If test API call succeeds, try to assign users - if (mission.missionUsers && mission.missionUsers.length > 0) { - await this.assignUsersToProject(projectId, mission.missionUsers); - } - } catch (apiError) { - // If we hit rate limits on the test call, skip user assignment completely - if (axios.isAxiosError(apiError) && apiError.response?.status === 429) { - console.log('⚠️ Rate limiting detected. Skipping all user assignments.'); - console.log(`⚠️ Users will need to be added manually at: ${this.getProjectUrl(projectId)}`); - } else { - // Try to assign users anyway for other types of errors - if (mission.missionUsers && mission.missionUsers.length > 0) { - await this.assignUsersToProject(projectId, mission.missionUsers); - } - } - } - - return projectId; - } - - // Fall back to addProject if createProject fails - console.log('createProject failed, trying addProject...'); - - // Based on the error message, Leantime expects a nested 'values' parameter + // Use only the method that we know works const payload = { method: 'leantime.rpc.Projects.Projects.addProject', jsonrpc: '2.0', id: 1, params: { - values: { - name: mission.name, - clientId: clientId, - details: mission.intention || '', - type: 'project', - start: formattedStartDate, - end: formattedEndDate, - status: 'open', - psettings: 'restricted' - } + values: projectData } }; - console.log('addProject payload:', JSON.stringify(payload, null, 2)); + console.log('Project creation payload:', JSON.stringify(payload, null, 2)); const response = await axios.post( this.getApiEndpoint(), @@ -168,14 +87,14 @@ export class LeantimeService { } ); - console.log('addProject response status:', response.status); - console.log('addProject response data:', JSON.stringify(response.data, null, 2)); + console.log('Project creation response status:', response.status); + console.log('Project creation response data:', JSON.stringify(response.data, null, 2)); if (!response.data || !response.data.result) { throw new Error(`Failed to create Leantime project: ${JSON.stringify(response.data)}`); } - const projectId = response.data.result; + const projectId = response.data.result[0]; // We need the first element in the array console.log(`Created Leantime project with ID: ${projectId}`); // If the mission has a logo, set it as project avatar @@ -183,40 +102,13 @@ export class LeantimeService { await this.setProjectAvatar(projectId, mission.logo); } - try { - // Wait a bit before attempting to add any users to avoid rate limits - await new Promise(resolve => setTimeout(resolve, 3000)); - // Check if we can make at least one API call without hitting rate limits - const testResponse = await axios.post( - this.getApiEndpoint(), - { - method: 'leantime.rpc.Auth.getCurrentUser', - jsonrpc: '2.0', - id: 1 - }, - { - headers: { - 'Content-Type': 'application/json', - 'X-API-Key': this.apiToken - } - } - ); - - // If test API call succeeds, try to assign users - if (mission.missionUsers && mission.missionUsers.length > 0) { - await this.assignUsersToProject(projectId, mission.missionUsers); - } - } catch (apiError) { - // If we hit rate limits on the test call, skip user assignment completely - if (axios.isAxiosError(apiError) && apiError.response?.status === 429) { - console.log('⚠️ Rate limiting detected. Skipping all user assignments.'); - console.log(`⚠️ Users will need to be added manually at: ${this.getProjectUrl(projectId)}`); - } else { - // Try to assign users anyway for other types of errors - if (mission.missionUsers && mission.missionUsers.length > 0) { - await this.assignUsersToProject(projectId, mission.missionUsers); - } - } + // Check if mission has users to assign + if (mission.missionUsers && mission.missionUsers.length > 0) { + await this.assignUsersToProject(projectId, mission.missionUsers); + } else { + // No users to assign + const projectUrl = this.getProjectUrl(projectId); + console.log(`ℹ️ No users to assign. Project created at: ${projectUrl}`); } return projectId; @@ -332,112 +224,12 @@ export class LeantimeService { * @param missionUsers The mission users with roles */ async assignUsersToProject(projectId: number, missionUsers: any[]): Promise { - console.log(`Attempting to add ${missionUsers.length} users to project ${projectId}`); + const projectUrl = this.getProjectUrl(projectId); + console.log(`⚠️ For best results, please assign users manually at: ${projectUrl}`); + console.log('ℹ️ Automatic user assignment is currently disabled due to API limitations.'); - // Create a direct URL to the project that can be shared with users - const baseUrl = this.apiUrl - .replace('/api/jsonrpc', '') - .replace('/api/jsonrpc.php', ''); - - const projectUrl = baseUrl.endsWith('/') - ? `${baseUrl}projects/showProject/${projectId}` - : `${baseUrl}/projects/showProject/${projectId}`; - - console.log(`🔗 Project created successfully. Direct project URL: ${projectUrl}`); - - if (!missionUsers || missionUsers.length === 0) { - console.log('No users to assign to the project.'); - - // Ensure at least the API user is assigned to prevent "Current team member is New API Access" issue - console.log('Ensuring API user is assigned to the project to prevent orphaned projects.'); - // Wait a bit longer before API user request to avoid rate limiting - await new Promise(resolve => setTimeout(resolve, 5000)); - const apiUserId = await this.getApiUserId(); - if (apiUserId) { - try { - const apiAssigned = await this.assignApiUserToProject(projectId, apiUserId); - if (apiAssigned) { - console.log('API user successfully assigned to project as admin.'); - } - } catch (apiUserError) { - console.error('Error assigning API user to project:', apiUserError); - } - } - return; - } - - // Users will need to be added manually if the automatic assignment fails - console.log('Attempting to assign users to the project - if this fails, users can be added manually through the Leantime interface.'); - - try { - let successCount = 0; - let errorCount = 0; - - // First try to assign just one user (the first one) to reduce API calls and avoid rate limits - const firstUser = missionUsers[0]; - try { - const leantimeUserId = await this.getUserByEmail(firstUser.user.email); - if (leantimeUserId) { - const role = firstUser.role === 'gardien-temps' ? 'editor' : 'commenter'; - const success = await this.assignUserToProject(projectId, leantimeUserId, role); - if (success) { - successCount++; - console.log(`✅ Successfully assigned user ${firstUser.user.email} to project ${projectId}`); - } - } - } catch (error) { - console.error(`Error assigning first user: ${error}`); - } - - // Skip other users if we got at least one successful assignment - if (successCount > 0) { - console.log(`Successfully assigned ${successCount} user to project. Skipping other users to avoid rate limits.`); - console.log(`⚠️ Other users will need to be added manually at: ${projectUrl}`); - errorCount = missionUsers.length - successCount; - } else { - // If first user failed, try API user assignment - console.log('No regular users successfully assigned. Adding API user as a fallback.'); - // Wait a bit longer before API user request to avoid rate limiting - await new Promise(resolve => setTimeout(resolve, 5000)); - const apiUserId = await this.getApiUserId(); - if (apiUserId) { - try { - const apiAssigned = await this.assignApiUserToProject(projectId, apiUserId); - if (apiAssigned) { - successCount++; - console.log('✅ API user successfully assigned to project as admin.'); - } - } catch (apiUserError) { - console.error('Error assigning API user to project:', apiUserError); - } - } - errorCount = missionUsers.length; - } - - console.log(`User assignment complete: ${successCount} successful, ${errorCount} failed.`); - - if (errorCount > 0) { - console.log(`⚠️ Some users could not be assigned automatically. They will need to be added manually at: ${projectUrl}`); - } - } catch (error) { - console.error('Error assigning users to project:', error); - console.log(`⚠️ Users will need to be added manually at: ${projectUrl}`); - - // Always ensure the API user is assigned as a last resort - try { - // Wait a bit longer before API user request to avoid rate limiting - await new Promise(resolve => setTimeout(resolve, 5000)); - const apiUserId = await this.getApiUserId(); - if (apiUserId) { - const apiAssigned = await this.assignApiUserToProject(projectId, apiUserId); - if (apiAssigned) { - console.log('✅ API user assigned as fallback to prevent orphaned project.'); - } - } - } catch (apiUserError) { - console.error('Error assigning API user as fallback:', apiUserError); - } - } + // No automatic user assignment - this was causing too many issues + return; } /** @@ -499,159 +291,41 @@ export class LeantimeService { try { console.log(`Assigning user ${userId} to project ${projectId} with role ${role}`); - // First, check if the user is already assigned to the project - const alreadyAssigned = await this.verifyUserAssignedToProject(userId, projectId); - if (alreadyAssigned) { - console.log(`✅ User ${userId} is already assigned to project ${projectId}`); + // Wait before making the API call to avoid rate limiting + await new Promise(resolve => setTimeout(resolve, 2000)); + + // Use only the method that we know works best + const response = await axios.post( + this.getApiEndpoint(), + { + method: 'leantime.rpc.Projects.addProjectUser', + jsonrpc: '2.0', + id: 1, + params: { + projectId: projectId, + userId: userId, + role: role + } + }, + { + headers: { + 'Content-Type': 'application/json', + 'X-API-Key': this.apiToken + } + } + ); + + if (response.data && response.data.result) { + console.log(`✅ Assigned user ${userId} to project ${projectId} with role ${role}`); return true; } - // Try method 1: Projects.Projects.addUser - try { - const response = await axios.post( - this.getApiEndpoint(), - { - method: 'leantime.rpc.Projects.Projects.addUser', - jsonrpc: '2.0', - id: 1, - params: { - projectId: projectId, - userId: userId, - role: role - } - }, - { - headers: { - 'Content-Type': 'application/json', - 'X-API-Key': this.apiToken - } - } - ); - - if (response.data && response.data.result) { - console.log(`Assigned user ${userId} to project ${projectId} with role ${role} (method 1)`); - - // Verify assignment was successful - const verified = await this.verifyUserAssignedToProject(userId, projectId); - if (verified) { - return true; - } else { - console.log('Assignment appeared successful but verification failed. Trying next method...'); - } - } - } catch (error) { - console.log('Method 1 failed, trying next method...'); - } - - // Try method 2: Projects.addProjectUser - try { - const response2 = await axios.post( - this.getApiEndpoint(), - { - method: 'leantime.rpc.Projects.addProjectUser', - jsonrpc: '2.0', - id: 1, - params: { - projectId: projectId, - userId: userId, - role: role - } - }, - { - headers: { - 'Content-Type': 'application/json', - 'X-API-Key': this.apiToken - } - } - ); - - if (response2.data && response2.data.result) { - console.log(`Assigned user ${userId} to project ${projectId} with role ${role} (method 2)`); - return true; - } - } catch (error) { - console.log('Method 2 failed, trying next method...'); - } - - // Try method 3: Projects.Projects.addProjectUser - try { - const response3 = await axios.post( - this.getApiEndpoint(), - { - method: 'leantime.rpc.Projects.Projects.addProjectUser', - jsonrpc: '2.0', - id: 1, - params: { - projectId: projectId, - userId: userId, - role: role - } - }, - { - headers: { - 'Content-Type': 'application/json', - 'X-API-Key': this.apiToken - } - } - ); - - if (response3.data && response3.data.result) { - console.log(`Assigned user ${userId} to project ${projectId} with role ${role} (method 3)`); - return true; - } - } catch (error) { - console.log('Method 3 failed, trying next method...'); - } - - // Try method 4: Projects.editUserProjectRelations - try { - // This method might take a different param structure - const response4 = await axios.post( - this.getApiEndpoint(), - { - method: 'leantime.rpc.Projects.editUserProjectRelations', - jsonrpc: '2.0', - id: 1, - params: { - action: 'add', - userId: userId, - projects: [{ - id: projectId, - role: role - }] - } - }, - { - headers: { - 'Content-Type': 'application/json', - 'X-API-Key': this.apiToken - } - } - ); - - if (response4.data && response4.data.result) { - console.log(`Assigned user ${userId} to project ${projectId} with role ${role} (method 4)`); - return true; - } - } catch (error) { - console.log('Method 4 failed.'); - } - - // After all methods, verify one last time - const finalVerification = await this.verifyUserAssignedToProject(userId, projectId); - if (finalVerification) { - console.log(`✅ Verified user ${userId} is now assigned to project ${projectId}`); - return true; - } - - // If we reach here, all methods failed - console.warn(`⚠️ Could not assign user ${userId} to project ${projectId} with any method. This is not a critical error, the project was created.`); + console.warn(`⚠️ Could not assign user ${userId} to project ${projectId}.`); return false; } catch (error) { console.error(`Error assigning user ${userId} to project ${projectId}:`, error); - // We'll allow this to fail since the project was created successfully - console.warn(`⚠️ User assignment failed, but this is not critical. The project ${projectId} was created successfully.`); + console.warn(`⚠️ User assignment failed. The project ${projectId} was created successfully, but users will need to be added manually.`); return false; } } @@ -671,14 +345,15 @@ export class LeantimeService { return true; } - // Try various methods to assign the API user - - // Method 1: Direct API assignment + // Wait before making the API call to avoid rate limiting + await new Promise(resolve => setTimeout(resolve, 2000)); + + // Use only the method that we know works best try { const response = await axios.post( this.getApiEndpoint(), { - method: 'leantime.rpc.Projects.Projects.addUser', + method: 'leantime.rpc.Projects.addProjectUser', jsonrpc: '2.0', id: 1, params: { @@ -696,81 +371,11 @@ export class LeantimeService { ); if (response.data && response.data.result) { - console.log(`Successfully assigned API user to project ${projectId}`); + console.log(`✅ API user successfully assigned to project ${projectId}`); return true; } } catch (error) { - console.log('API user assignment method 1 failed, trying next method...'); - } - - // Method 2: editUserProjectRelations - try { - const response = await axios.post( - this.getApiEndpoint(), - { - method: 'leantime.rpc.Projects.editUserProjectRelations', - jsonrpc: '2.0', - id: 1, - params: { - action: 'add', - userId: apiUserId, - projects: [{ - id: projectId, - role: 'admin' - }] - } - }, - { - headers: { - 'Content-Type': 'application/json', - 'X-API-Key': this.apiToken - } - } - ); - - if (response.data && response.data.result) { - console.log(`Successfully assigned API user to project ${projectId} (method 2)`); - return true; - } - } catch (error) { - console.log('API user assignment method 2 failed, trying next method...'); - } - - // Method 3: addProjectUser - try { - const response = await axios.post( - this.getApiEndpoint(), - { - method: 'leantime.rpc.Projects.addProjectUser', - jsonrpc: '2.0', - id: 1, - params: { - projectId: projectId, - userId: apiUserId, - role: 'admin' - } - }, - { - headers: { - 'Content-Type': 'application/json', - 'X-API-Key': this.apiToken - } - } - ); - - if (response.data && response.data.result) { - console.log(`Successfully assigned API user to project ${projectId} (method 3)`); - return true; - } - } catch (error) { - console.log('API user assignment method 3 failed.'); - } - - // After all methods, verify assignment one last time - const finalVerification = await this.verifyUserAssignedToProject(apiUserId, projectId); - if (finalVerification) { - console.log(`✅ Verified API user ${apiUserId} is now assigned to project ${projectId}`); - return true; + console.error('Error assigning API user to project:', error); } console.warn(`⚠️ Could not assign API user to project ${projectId}. The project may appear with "New API Access" as the only team member.`); @@ -851,10 +456,14 @@ export class LeantimeService { */ async createClient(clientName: string): Promise { try { + // Wait before making the API call to avoid rate limiting + await new Promise(resolve => setTimeout(resolve, 1000)); + + // Use only the method that works consistently const response = await axios.post( this.getApiEndpoint(), { - method: 'leantime.rpc.Clients.Clients.create', + method: 'leantime.rpc.Clients.Clients.addClient', jsonrpc: '2.0', id: 1, params: { @@ -894,7 +503,7 @@ export class LeantimeService { await new Promise(resolve => setTimeout(resolve, 3000)); try { - // Try again with a different API method name + // Retry with the same method after waiting const retryResponse = await axios.post( this.getApiEndpoint(), {