@@ -122,7 +122,7 @@ export type JiraIssue = {
122122 fields : {
123123 assignee ?: JiraUser ;
124124 created : string ;
125- description : string ;
125+ description : ADFNode ;
126126 issuelinks : JiraIssueLink [ ] ;
127127 issuetype : JiraIssueType ;
128128 labels ?: string [ ] ;
@@ -151,6 +151,28 @@ export type JiraAPIResponse = {
151151 issues : JiraIssue [ ] ;
152152} ;
153153
154+ export interface ADFNode {
155+ type : string ;
156+ text ?: string ;
157+ content ?: ADFNode [ ] ;
158+ }
159+
160+ export interface ADFDocument extends ADFNode {
161+ type : "doc" ;
162+ version : number ;
163+ content : ADFNode [ ] ;
164+ }
165+
166+ export function adfToText ( adf : ADFNode | null | undefined ) : string {
167+ if ( ! adf || ! adf . content ) return "" ;
168+ const recur = ( node : ADFNode ) : string => {
169+ if ( node . text ) return node . text ;
170+ if ( node . content ) return node . content . map ( recur ) . join ( "" ) ;
171+ return "" ;
172+ } ;
173+ return recur ( adf ) . trim ( ) ;
174+ }
175+
154176/**
155177 * Interface representing the parameters for configuring the
156178 * JiraDocumentConverter.
@@ -194,88 +216,93 @@ export class JiraDocumentConverter {
194216 private formatIssueInfo ( {
195217 issue,
196218 host,
197- } : {
219+ } : {
198220 issue : JiraIssue ;
199221 host : string ;
200- } ) : string {
222+ } ) : string {
223+ const {
224+ project,
225+ status,
226+ priority,
227+ issuetype,
228+ creator,
229+ labels,
230+ created,
231+ updated,
232+ reporter,
233+ assignee,
234+ duedate,
235+ timeestimate,
236+ timespent,
237+ resolutiondate,
238+ description,
239+ progress,
240+ parent,
241+ subtasks,
242+ issuelinks,
243+ } = issue . fields ;
244+
201245 let text = `Issue: ${ this . formatMainIssueInfoText ( { issue, host } ) } \n` ;
202- text += `Project: ${ issue . fields . project . name } (${ issue . fields . project . key } , ID ${ issue . fields . project . id } )\n` ;
203- text += `Status: ${ issue . fields . status . name } \n` ;
204- text += `Priority: ${ issue . fields . priority . name } \n` ;
205- text += `Type: ${ issue . fields . issuetype . name } \n` ;
206- text += `Creator: ${ issue . fields . creator ?. displayName } \n` ;
207-
208- if ( issue . fields . labels && issue . fields . labels . length > 0 ) {
209- text += `Labels: ${ issue . fields . labels . join ( ", " ) } \n` ;
246+ text += `Project: ${ project . name } (${ project . key } , ID ${ project . id } )\n` ;
247+ text += `Status: ${ status . name } \n` ;
248+ text += `Priority: ${ priority . name } \n` ;
249+ text += `Type: ${ issuetype . name } \n` ;
250+ text += `Creator: ${ creator ?. displayName } \n` ;
251+
252+ if ( labels ?. length ) {
253+ text += `Labels: ${ labels . join ( ", " ) } \n` ;
210254 }
211255
212- text += `Created: ${ issue . fields . created } \n` ;
213- text += `Updated: ${ issue . fields . updated } \n` ;
256+ text += `Created: ${ created } \n` ;
257+ text += `Updated: ${ updated } \n` ;
214258
215- if ( issue . fields . reporter ) {
216- text += `Reporter: ${ issue . fields . reporter . displayName } \n` ;
259+ if ( reporter ) {
260+ text += `Reporter: ${ reporter . displayName } \n` ;
217261 }
218262
219- text += `Assignee: ${ issue . fields . assignee ?. displayName ?? "Unassigned" } \n` ;
263+ text += `Assignee: ${ assignee ?. displayName ?? "Unassigned" } \n` ;
220264
221- if ( issue . fields . duedate ) {
222- text += `Due Date: ${ issue . fields . duedate } \n` ;
265+ if ( duedate ) {
266+ text += `Due Date: ${ duedate } \n` ;
223267 }
224-
225- if ( issue . fields . timeestimate ) {
226- text += `Time Estimate: ${ issue . fields . timeestimate } \n` ;
268+ if ( timeestimate ) {
269+ text += `Time Estimate: ${ timeestimate } \n` ;
227270 }
228-
229- if ( issue . fields . timespent ) {
230- text += `Time Spent: ${ issue . fields . timespent } \n` ;
271+ if ( timespent ) {
272+ text += `Time Spent: ${ timespent } \n` ;
231273 }
232-
233- if ( issue . fields . resolutiondate ) {
234- text += `Resolution Date: ${ issue . fields . resolutiondate } \n` ;
274+ if ( resolutiondate ) {
275+ text += `Resolution Date: ${ resolutiondate } \n` ;
235276 }
236-
237- if ( issue . fields . description ) {
238- text += `Description: ${ issue . fields . description } \n` ;
277+ if ( description ) {
278+ text += `Description: ${ adfToText ( description ) } \n` ;
239279 }
240-
241- if ( issue . fields . progress ?. percent ) {
242- text += `Progress: ${ issue . fields . progress . percent } %\n` ;
280+ if ( progress ?. percent ) {
281+ text += `Progress: ${ progress . percent } %\n` ;
243282 }
244283
245- if ( issue . fields . parent ) {
246- text += `Parent Issue: ${ this . formatMainIssueInfoText ( {
247- issue : issue . fields . parent ,
248- host,
249- } ) } \n`;
284+ if ( parent ) {
285+ text += `Parent Issue: ${ this . formatMainIssueInfoText ( { issue : parent , host } ) } \n` ;
250286 }
251287
252- if ( issue . fields . subtasks ?. length > 0 ) {
253- text += `Subtasks:\n` ;
254- issue . fields . subtasks . forEach ( ( subtask ) => {
255- text += ` - ${ this . formatMainIssueInfoText ( {
256- issue : subtask ,
257- host,
258- } ) } \n`;
259- } ) ;
288+ if ( subtasks ?. length ) {
289+ text += `Subtasks:\n` ;
290+ subtasks . forEach ( subtask => {
291+ text += ` - ${ this . formatMainIssueInfoText ( { issue : subtask , host } ) } \n` ;
292+ } ) ;
260293 }
261294
262- if ( issue . fields . issuelinks ?. length > 0 ) {
263- text += `Issue Links:\n` ;
264- issue . fields . issuelinks . forEach ( ( link ) => {
295+ if ( issuelinks ?. length ) {
296+ text += `Issue Links:\n` ;
297+ issuelinks . forEach ( link => {
265298 text += ` - ${ link . type . name } \n` ;
266299 if ( link . inwardIssue ) {
267- text += ` - ${ this . formatMainIssueInfoText ( {
268- issue : link . inwardIssue ,
269- host,
270- } ) } \n`;
300+ text += ` - ${ this . formatMainIssueInfoText ( { issue : link . inwardIssue , host } ) } \n` ;
271301 }
272302 if ( link . outwardIssue ) {
273- text += ` - ${ this . formatMainIssueInfoText ( {
274- issue : link . outwardIssue ,
275- host,
276- } ) } \n`;
303+ text += ` - ${ this . formatMainIssueInfoText ( { issue : link . outwardIssue , host } ) } \n` ;
277304 }
278- } ) ;
305+ } ) ;
279306 }
280307
281308 return text ;
@@ -321,10 +348,11 @@ export interface JiraProjectLoaderParams {
321348 personalAccessToken ?: string ;
322349 limitPerRequest ?: number ;
323350 createdAfter ?: Date ;
351+ filterFn ?: ( issue : JiraIssue ) => boolean ;
324352}
325353
326354const API_ENDPOINTS = {
327- SEARCH : "/rest/api/2 /search" ,
355+ SEARCH : "/rest/api/3 /search/jql " ,
328356} ;
329357
330358/**
@@ -343,6 +371,8 @@ export class JiraProjectLoader extends BaseDocumentLoader {
343371
344372 private readonly createdAfter ?: Date ;
345373
374+ private readonly filterFn ?: ( issue : JiraIssue ) => boolean ;
375+
346376 private readonly documentConverter : JiraDocumentConverter ;
347377
348378 private readonly personalAccessToken ?: string ;
@@ -355,6 +385,7 @@ export class JiraProjectLoader extends BaseDocumentLoader {
355385 limitPerRequest = 100 ,
356386 createdAfter,
357387 personalAccessToken,
388+ filterFn,
358389 } : JiraProjectLoaderParams ) {
359390 super ( ) ;
360391 this . host = host ;
@@ -365,6 +396,7 @@ export class JiraProjectLoader extends BaseDocumentLoader {
365396 this . createdAfter = createdAfter ;
366397 this . documentConverter = new JiraDocumentConverter ( { host, projectKey } ) ;
367398 this . personalAccessToken = personalAccessToken ;
399+ this . filterFn = filterFn ;
368400 }
369401
370402 private buildAuthorizationHeader ( ) : string {
@@ -379,7 +411,13 @@ export class JiraProjectLoader extends BaseDocumentLoader {
379411 public async load ( ) : Promise < Document [ ] > {
380412 try {
381413 const allJiraIssues = await this . loadAsIssues ( ) ;
382- return this . documentConverter . convertToDocuments ( allJiraIssues ) ;
414+ const filtered = allJiraIssues . filter ( issue => {
415+ if ( this . filterFn ) {
416+ return this . filterFn ( issue ) ;
417+ }
418+ return true ;
419+ } ) ;
420+ return this . documentConverter . convertToDocuments ( filtered ) ;
383421 } catch ( error ) {
384422 console . error ( "Error:" , error ) ;
385423 return [ ] ;
@@ -416,19 +454,22 @@ export class JiraProjectLoader extends BaseDocumentLoader {
416454 try {
417455 const jqlProps = [
418456 `project=${ this . projectKey } ` ,
419- ...( createdAfterAsString ? [ `created>=${ createdAfterAsString } ` ] : [ ] ) ,
457+ ...( createdAfterAsString ? [ `created>= " ${ createdAfterAsString } " ` ] : [ ] ) ,
420458 ] ;
459+ const jql = `${ jqlProps . join ( " AND " ) } ORDER BY created ASC, key ASC` ;
460+
421461 const params = new URLSearchParams ( {
422- jql : jqlProps . join ( " AND " ) ,
462+ jql,
423463 startAt : `${ startAt } ` ,
424464 maxResults : `${ this . limitPerRequest } ` ,
465+ fields : '*all' ,
425466 } ) ;
426467 const pageUrl = `${ url } ?${ params } ` ;
427468
428469 const options = {
429470 method : "GET" ,
430471 headers : {
431- Authorization : authorizationHeader ,
472+ Authorization : authorizationHeader ,
432473 Accept : "application/json" ,
433474 } ,
434475 } ;
@@ -438,12 +479,22 @@ export class JiraProjectLoader extends BaseDocumentLoader {
438479
439480 if ( ! data . issues || data . issues . length === 0 ) break ;
440481
441- yield data . issues ;
482+ const allIssues = [ ] ;
483+ for ( const issue of data . issues ) {
484+ allIssues . push ( issue ) ;
485+ }
486+
487+ if ( allIssues . length > 0 ) yield allIssues ;
488+
442489 startAt += this . limitPerRequest ;
490+
491+ if ( data . issues . length < this . limitPerRequest ) break ;
492+
443493 } catch ( error ) {
444- console . error ( error ) ;
494+ console . error ( "Error fetching Jira issues:" , error ) ;
445495 yield [ ] ;
496+ break ;
446497 }
447498 }
448499 }
449- }
500+ }
0 commit comments