Skip to content

Commit 2b78649

Browse files
committed
Update Jira endpoint to v3
Update Jira query to return all issue fields, otherwise, no fields will be returned. Optimise fetch logic to fix duplicates and avoid looping through all issues
1 parent 6f8fa47 commit 2b78649

File tree

2 files changed

+54
-29
lines changed

2 files changed

+54
-29
lines changed

‎.changeset/large-cycles-thank.md‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@langchain/community": major
3+
---
4+
5+
Update Jira document loader with updated API endpoint, handle edge case where jira fields are missing.

‎libs/langchain-community/src/document_loaders/web/jira.ts‎

Lines changed: 49 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -324,7 +324,7 @@ export interface JiraProjectLoaderParams {
324324
}
325325

326326
const API_ENDPOINTS = {
327-
SEARCH: "/rest/api/2/search",
327+
SEARCH: "/rest/api/3/search/jql",
328328
};
329329

330330
/**
@@ -411,39 +411,59 @@ export class JiraProjectLoader extends BaseDocumentLoader {
411411
const url = `${this.host}${API_ENDPOINTS.SEARCH}`;
412412
const createdAfterAsString = this.toJiraDateString(this.createdAfter);
413413
let startAt = 0;
414+
const seenIds = new Set<string>();
415+
let hitDuplicate = false;
414416

415-
while (true) {
417+
while (!hitDuplicate) {
416418
try {
417419
const jqlProps = [
418420
`project=${this.projectKey}`,
419-
...(createdAfterAsString ? [`created>=${createdAfterAsString}`] : []),
420-
];
421-
const params = new URLSearchParams({
422-
jql: jqlProps.join(" AND "),
423-
startAt: `${startAt}`,
424-
maxResults: `${this.limitPerRequest}`,
425-
});
426-
const pageUrl = `${url}?${params}`;
427-
428-
const options = {
429-
method: "GET",
430-
headers: {
431-
Authorization: authorizationHeader,
432-
Accept: "application/json",
433-
},
434-
};
435-
436-
const response = await fetch(pageUrl, options);
437-
const data: JiraAPIResponse = await response.json();
438-
439-
if (!data.issues || data.issues.length === 0) break;
440-
441-
yield data.issues;
442-
startAt += this.limitPerRequest;
421+
...(createdAfterAsString ? [`created>= "${createdAfterAsString}"`] : []),
422+
];
423+
const jql = `${jqlProps.join(" AND ")} ORDER BY created ASC, key ASC`;
424+
425+
const params = new URLSearchParams({
426+
jql,
427+
startAt: `${startAt}`,
428+
maxResults: `${this.limitPerRequest}`,
429+
fields: '*all',
430+
});
431+
const pageUrl = `${url}?${params}`;
432+
433+
const options = {
434+
method: "GET",
435+
headers: {
436+
Authorization: authorizationHeader,
437+
Accept: "application/json",
438+
},
439+
};
440+
441+
const response = await fetch(pageUrl, options);
442+
const data: JiraAPIResponse = await response.json();
443+
444+
if (!data.issues || data.issues.length === 0) break;
445+
446+
const uniqueIssues = [];
447+
for (const issue of data.issues) {
448+
if (seenIds.has(issue.id)) {
449+
hitDuplicate = true;
450+
break;
451+
}
452+
seenIds.add(issue.id);
453+
uniqueIssues.push(issue);
454+
}
455+
456+
if (uniqueIssues.length > 0) yield uniqueIssues;
457+
458+
startAt += this.limitPerRequest;
459+
460+
// Exit if we have fewer issues than limit (end of result set)
461+
if (data.issues.length < this.limitPerRequest) break;
462+
443463
} catch (error) {
444-
console.error(error);
445-
yield [];
464+
console.error("Error fetching Jira issues:", error);
465+
break;
446466
}
447467
}
448468
}
449-
}
469+
}

0 commit comments

Comments
 (0)