diff --git a/services/jira.ts b/services/jira.ts index 2b4871d..9554f42 100644 --- a/services/jira.ts +++ b/services/jira.ts @@ -65,25 +65,30 @@ export class JiraService { const fields = ['id', 'key', 'summary', 'description', 'status', 'priority', 'assignee', 'project', 'issuetype', 'duedate', 'created', 'updated', 'labels']; const allIssues: unknown[] = []; - let startAt = 0; - const maxResults = 100; // Taille des pages - let hasMorePages = true; + let nextPageToken: string | undefined = undefined; + let pageNumber = 1; - console.log('🔄 Récupération paginée des tickets Jira...'); + console.log('🔄 Récupération paginée des tickets Jira (POST /search/jql avec tokens)...'); - while (hasMorePages) { - const requestBody = { + while (true) { + console.log(`📄 Page ${pageNumber} ${nextPageToken ? `(token présent)` : '(première page)'}`); + + // Utiliser POST /rest/api/3/search/jql avec nextPageToken selon la doc officielle + const requestBody: any = { jql, - fields + fields, + maxResults: 50 }; - - console.log(`📄 Page ${Math.floor(startAt / maxResults) + 1} (tickets ${startAt + 1}-${startAt + maxResults})`); - - const response = await this.makeJiraRequest( - `/rest/api/3/search/jql?startAt=${startAt}&maxResults=${maxResults}`, - 'POST', - requestBody - ); + + if (nextPageToken) { + requestBody.nextPageToken = nextPageToken; + } + + console.log(`🌐 POST /rest/api/3/search/jql avec ${nextPageToken ? 'nextPageToken' : 'première page'}`); + + const response = await this.makeJiraRequest('/rest/api/3/search/jql', 'POST', requestBody); + + console.log(`📡 Status réponse: ${response.status}`); if (!response.ok) { const errorText = await response.text(); @@ -96,7 +101,11 @@ export class JiraService { throw new Error(`Erreur API Jira: ${response.status} ${response.statusText}. Détails: ${errorText.substring(0, 200)}`); } - const data = await response.json() as { issues: unknown[], total: number, maxResults: number, startAt: number }; + const data = await response.json() as { + issues: unknown[], + nextPageToken?: string, + isLast?: boolean + }; if (!data.issues || !Array.isArray(data.issues)) { console.error('❌ Format de données inattendu:', data); @@ -104,17 +113,26 @@ export class JiraService { } allIssues.push(...data.issues); - console.log(`✅ ${data.issues.length} tickets récupérés (total: ${allIssues.length}/${data.total || '?'})`); + console.log(`✅ ${data.issues.length} tickets récupérés (total accumulé: ${allIssues.length})`); + console.log(`🔍 Pagination info:`, { + issuesLength: data.issues.length, + hasNextPageToken: !!data.nextPageToken, + isLast: data.isLast, + pageNumber + }); - // Vérifier s'il y a plus de pages - hasMorePages = data.issues.length === maxResults && allIssues.length < (data.total || Number.MAX_SAFE_INTEGER); - startAt += maxResults; + // Vérifier s'il y a plus de pages selon la doc officielle + if (data.isLast === true || !data.nextPageToken) { + console.log('🏁 Dernière page atteinte (isLast=true ou pas de nextPageToken)'); + break; + } - console.log(`📊 Pagination: hasMorePages=${hasMorePages}, startAt=${startAt}, maxResults=${maxResults}`); + nextPageToken = data.nextPageToken; + pageNumber++; - // Sécurité: éviter les boucles infinies (augmenté avec la nouvelle taille de page) - if (allIssues.length > 10000) { - console.warn('⚠️ Limite de sécurité atteinte (10000 tickets). Arrêt de la pagination.'); + // Sécurité: éviter les boucles infinies + if (allIssues.length >= 10000) { + console.warn(`⚠️ Limite de sécurité atteinte (${allIssues.length} tickets). Arrêt de la pagination.`); break; } }