UNPKG

@langchain/community

Version:
1 lines 20.6 kB
{"version":3,"file":"jira.cjs","names":["Document","BaseDocumentLoader"],"sources":["../../../src/document_loaders/web/jira.ts"],"sourcesContent":["import { Document } from \"@langchain/core/documents\";\nimport { BaseDocumentLoader } from \"@langchain/core/document_loaders/base\";\n\nexport type JiraStatusCategory = {\n self: string;\n id: number;\n key: string;\n colorName: string;\n name: string;\n};\n\nexport type JiraStatus = {\n self: string;\n description: string;\n iconUrl: string;\n name: string;\n id: string;\n statusCategory: JiraStatusCategory;\n};\n\nexport type JiraUser = {\n accountId: string;\n accountType: string;\n active: boolean;\n avatarUrls: {\n \"16x16\": string;\n \"24x24\": string;\n \"32x32\": string;\n \"48x48\": string;\n };\n displayName: string;\n emailAddress: string;\n self: string;\n timeZone: string;\n};\n\nexport type JiraIssueType = {\n avatarId: number;\n description: string;\n entityId: string;\n hierarchyLevel: number;\n iconUrl: string;\n id: string;\n name: string;\n self: string;\n subtask: boolean;\n};\n\nexport type JiraPriority = {\n iconUrl: string;\n id: string;\n name: string;\n self: string;\n};\n\nexport type JiraProgress = {\n progress: number;\n total: number;\n percent?: number;\n};\n\nexport type JiraProject = {\n avatarUrls: {\n \"16x16\": string;\n \"24x24\": string;\n \"32x32\": string;\n \"48x48\": string;\n };\n id: string;\n key: string;\n name: string;\n projectTypeKey: string;\n self: string;\n simplified: boolean;\n};\n\nexport type JiraSubTask = {\n id: string;\n key: string;\n self: string;\n fields: {\n issuetype: JiraIssueType;\n priority: JiraPriority;\n status: JiraStatus;\n summary: string;\n };\n};\n\nexport type JiraIssueLinkType = {\n id: string;\n name: string;\n inward: string;\n outward: string;\n self: string;\n};\n\nexport type JiraBriefIssue = {\n id: string;\n key: string;\n self: string;\n fields: {\n summary: string;\n status: JiraStatus;\n priority: JiraPriority;\n issuetype: JiraIssueType;\n };\n};\n\nexport type JiraIssueLink = {\n id: string;\n self: string;\n type: JiraIssueLinkType;\n inwardIssue?: JiraBriefIssue;\n outwardIssue?: JiraBriefIssue;\n};\n\nexport type JiraIssue = {\n expand: string;\n id: string;\n self: string;\n key: string;\n fields: {\n assignee?: JiraUser;\n created: string;\n description: ADFNode;\n issuelinks: JiraIssueLink[];\n issuetype: JiraIssueType;\n labels?: string[];\n priority: JiraPriority;\n progress: JiraProgress;\n project: JiraProject;\n reporter?: JiraUser;\n creator: JiraUser;\n resolutiondate?: string;\n status: JiraStatus;\n subtasks: JiraSubTask[];\n summary: string;\n timeestimate?: number;\n timespent?: number;\n updated: string;\n duedate?: string;\n parent?: JiraBriefIssue;\n };\n};\n\nexport type JiraDescriptionFormatter = (\n content: ADFNode | null | undefined,\n issue?: JiraIssue\n) => string | ADFNode | null;\n\nexport type JiraAPIResponse = {\n expand?: string;\n startAt?: number;\n maxResults?: number;\n total?: number;\n issues: JiraIssue[];\n isLast?: boolean;\n nextPageToken?: string;\n};\n\nexport interface ADFNode {\n type: string;\n text?: string;\n content?: ADFNode[];\n attrs?: Record<string, unknown>;\n}\n\nexport interface ADFDocument extends ADFNode {\n type: \"doc\";\n version: number;\n content: ADFNode[];\n}\n\nexport function adfToText(adf: ADFNode | null | undefined): string {\n if (!adf || !adf.content) return \"\";\n const recur = (node: ADFNode): string => {\n if (node.text) return node.text;\n if (node.content) return node.content.map(recur).join(\"\");\n return \"\";\n };\n return recur(adf).trim();\n}\n\nexport const formatJiraDescriptionAsJSON: JiraDescriptionFormatter = (\n adfContent: ADFNode | null | undefined\n): ADFNode | null => {\n if (!adfContent) return null;\n\n const traverseNode = (node: ADFNode): ADFNode => {\n if (typeof node === \"string\") return { type: \"text\", text: node };\n\n const result: ADFNode = { type: node.type || \"unknown\" };\n if (node.text) result.text = node.text;\n if (node.attrs) result.attrs = node.attrs;\n if (node.content) result.content = node.content.map(traverseNode);\n return result;\n };\n\n return traverseNode(adfContent);\n};\n\nexport const formatJiraDescriptionAsText: JiraDescriptionFormatter = (\n adfContent: ADFNode | null | undefined\n): string => {\n if (!adfContent) return \"\";\n\n const traverseNode = (node: ADFNode): string => {\n if (node.text) return node.text;\n if (node.content) return node.content.map(traverseNode).join(\"\");\n return \"\";\n };\n\n return traverseNode(adfContent).trim();\n};\n\n/**\n * Interface representing the parameters for configuring the\n * JiraDocumentConverter.\n */\nexport interface JiraDocumentConverterParams {\n host: string;\n projectKey: string;\n formatter?: JiraDescriptionFormatter;\n}\n\n/**\n * Class responsible for converting Jira issues to Document objects\n */\nexport class JiraDocumentConverter {\n public readonly host: string;\n\n public readonly projectKey: string;\n\n private readonly formatter: JiraDescriptionFormatter;\n\n constructor({ host, projectKey, formatter }: JiraDocumentConverterParams) {\n this.host = host;\n this.projectKey = projectKey;\n this.formatter = formatter ?? formatJiraDescriptionAsText;\n }\n\n public convertToDocuments(issues: JiraIssue[]): Document[] {\n return issues.map((issue) => this.documentFromIssue(issue));\n }\n\n private documentFromIssue(issue: JiraIssue): Document {\n return new Document({\n pageContent: this.formatIssueInfo({ issue, host: this.host }),\n metadata: {\n id: issue.id,\n host: this.host,\n projectKey: this.projectKey,\n created: issue.fields.created,\n },\n });\n }\n\n private formatIssueInfo({\n issue,\n host,\n }: {\n issue: JiraIssue;\n host: string;\n }): string {\n let text = `Issue: ${this.formatMainIssueInfoText({ issue, host })}\\n`;\n text += `Project: ${issue.fields.project.name} (${issue.fields.project.key}, ID ${issue.fields.project.id})\\n`;\n text += `Status: ${issue.fields.status.name}\\n`;\n text += `Priority: ${issue.fields.priority.name}\\n`;\n text += `Type: ${issue.fields.issuetype.name}\\n`;\n text += `Creator: ${issue.fields.creator?.displayName}\\n`;\n\n if (issue.fields.labels && issue.fields.labels.length > 0) {\n text += `Labels: ${issue.fields.labels.join(\", \")}\\n`;\n }\n\n text += `Created: ${issue.fields.created}\\n`;\n text += `Updated: ${issue.fields.updated}\\n`;\n\n if (issue.fields.reporter) {\n text += `Reporter: ${issue.fields.reporter.displayName}\\n`;\n }\n\n text += `Assignee: ${issue.fields.assignee?.displayName ?? \"Unassigned\"}\\n`;\n\n if (issue.fields.duedate) {\n text += `Due Date: ${issue.fields.duedate}\\n`;\n }\n\n if (issue.fields.timeestimate) {\n text += `Time Estimate: ${issue.fields.timeestimate}\\n`;\n }\n\n if (issue.fields.timespent) {\n text += `Time Spent: ${issue.fields.timespent}\\n`;\n }\n\n if (issue.fields.resolutiondate) {\n text += `Resolution Date: ${issue.fields.resolutiondate}\\n`;\n }\n\n if (issue.fields.description) {\n const formattedDescription = this.formatter(\n issue.fields.description,\n issue\n );\n const descText =\n typeof formattedDescription === \"string\"\n ? formattedDescription\n : JSON.stringify(formattedDescription, null, 2);\n\n text += `Description: ${descText}\\n`;\n }\n\n if (issue.fields.progress?.percent) {\n text += `Progress: ${issue.fields.progress.percent}%\\n`;\n }\n\n if (issue.fields.parent) {\n text += `Parent Issue: ${this.formatMainIssueInfoText({\n issue: issue.fields.parent,\n host,\n })}\\n`;\n }\n\n if (issue.fields.subtasks?.length > 0) {\n text += `Subtasks:\\n`;\n issue.fields.subtasks.forEach((subtask) => {\n text += ` - ${this.formatMainIssueInfoText({\n issue: subtask,\n host,\n })}\\n`;\n });\n }\n\n if (issue.fields.issuelinks?.length > 0) {\n text += `Issue Links:\\n`;\n issue.fields.issuelinks.forEach((link) => {\n text += ` - ${link.type.name}\\n`;\n if (link.inwardIssue) {\n text += ` - ${this.formatMainIssueInfoText({\n issue: link.inwardIssue,\n host,\n })}\\n`;\n }\n if (link.outwardIssue) {\n text += ` - ${this.formatMainIssueInfoText({\n issue: link.outwardIssue,\n host,\n })}\\n`;\n }\n });\n }\n\n return text;\n }\n\n private getLinkToIssue({\n issueKey,\n host,\n }: {\n issueKey: string;\n host: string;\n }): string {\n return `${host}/browse/${issueKey}`;\n }\n\n private formatMainIssueInfoText({\n issue,\n host,\n }: {\n issue: JiraIssue | JiraBriefIssue;\n host: string;\n }): string {\n const link = this.getLinkToIssue({\n issueKey: issue.key,\n host,\n });\n\n const text = `${issue.key} (ID ${issue.id}) - ${issue.fields.summary} (${link})`;\n\n return text;\n }\n}\n\n/**\n * Interface representing the parameters for configuring the\n * JiraProjectLoader.\n */\nexport interface JiraProjectLoaderParams {\n host: string;\n projectKey: string;\n username?: string;\n accessToken?: string;\n personalAccessToken?: string;\n limitPerRequest?: number;\n createdAfter?: Date;\n filterFn?: (issue: JiraIssue) => boolean;\n descriptionFormatter?: JiraDescriptionFormatter;\n\n /**\n * Optional custom JQL query. When provided, this replaces the\n * auto-generated `project = <projectKey>` query entirely, giving\n * full control over filtering, sorting, and scoping.\n *\n * Examples:\n * \"project = AC AND status = 'In Progress'\"\n * \"project = AC AND sprint in openSprints() ORDER BY priority DESC\"\n * \"assignee = currentUser() AND updated >= -7d\"\n *\n * Note: `createdAfter` is ignored when `jql` is provided — include\n * any date filters directly in your JQL string.\n */\n jql?: string;\n\n /**\n * Maximum total number of issues to fetch across all pages.\n * When set, pagination stops early once this limit is reached.\n * Without this, the loader fetches every issue matching the query.\n *\n * Note: `limitPerRequest` controls the page size (how many issues\n * per API call), while `maxTotal` controls the overall cap.\n */\n maxTotal?: number;\n}\n\n/**\n * Class representing a document loader for loading issues from Jira.\n */\nexport class JiraProjectLoader extends BaseDocumentLoader {\n private readonly accessToken?: string;\n\n public readonly host: string;\n\n public readonly projectKey: string;\n\n public readonly username?: string;\n\n public readonly limitPerRequest: number;\n\n private readonly createdAfter?: Date;\n\n private readonly filterFn?: (issue: JiraIssue) => boolean;\n\n private readonly documentConverter: JiraDocumentConverter;\n\n private readonly personalAccessToken?: string;\n\n private readonly jql?: string;\n\n private readonly maxTotal?: number;\n\n constructor({\n host,\n projectKey,\n username,\n accessToken,\n limitPerRequest = 100,\n createdAfter,\n personalAccessToken,\n filterFn,\n descriptionFormatter: formatter,\n jql,\n maxTotal,\n }: JiraProjectLoaderParams) {\n super();\n this.host = host;\n this.projectKey = projectKey;\n this.username = username;\n this.accessToken = accessToken;\n this.limitPerRequest = limitPerRequest;\n this.createdAfter = createdAfter;\n this.documentConverter = new JiraDocumentConverter({\n host,\n projectKey,\n formatter,\n });\n this.personalAccessToken = personalAccessToken;\n this.filterFn = filterFn;\n this.jql = jql;\n this.maxTotal = maxTotal;\n }\n\n private buildAuthorizationHeader(): string {\n if (this.personalAccessToken) {\n return `Bearer ${this.personalAccessToken}`;\n }\n return `Basic ${Buffer.from(\n `${this.username}:${this.accessToken}`\n ).toString(\"base64\")}`;\n }\n\n public async load(): Promise<Document[]> {\n const allJiraIssues = await this.loadAsIssues();\n const filtered = allJiraIssues.filter((issue) => {\n if (this.filterFn) {\n return this.filterFn(issue);\n }\n return true;\n });\n return this.documentConverter.convertToDocuments(filtered);\n }\n\n public async loadAsIssues(): Promise<JiraIssue[]> {\n const allIssues: JiraIssue[] = [];\n\n for await (const issues of this.fetchIssues()) {\n allIssues.push(...issues);\n if (this.maxTotal && allIssues.length >= this.maxTotal) {\n return allIssues.slice(0, this.maxTotal);\n }\n }\n\n return allIssues;\n }\n\n protected toJiraDateString(date: Date | undefined): string | undefined {\n if (!date) {\n return undefined;\n }\n const year = date.getUTCFullYear();\n const month = String(date.getUTCMonth() + 1).padStart(2, \"0\");\n const dayOfMonth = String(date.getUTCDate()).padStart(2, \"0\");\n return `${year}-${month}-${dayOfMonth}`;\n }\n\n private buildJql(): string {\n // If custom JQL is provided, use it directly\n if (this.jql) {\n return this.jql;\n }\n\n // Otherwise, build JQL from projectKey and createdAfter\n const createdAfterAsString = this.toJiraDateString(this.createdAfter);\n const jqlParts = [\n `project = ${this.projectKey}`,\n ...(createdAfterAsString ? [`created >= \"${createdAfterAsString}\"`] : []),\n ];\n return `${jqlParts.join(\" AND \")} ORDER BY created ASC, key ASC`;\n }\n\n protected async *fetchIssues(): AsyncIterable<JiraIssue[]> {\n const authorizationHeader = this.buildAuthorizationHeader();\n const url = `${this.host}/rest/api/3/search/jql`;\n const jql = this.buildJql();\n\n let nextPageToken: string | undefined;\n let totalFetched = 0;\n\n while (true) {\n // If maxTotal is set, don't request more than we need\n let pageSize = this.limitPerRequest;\n if (this.maxTotal) {\n const remaining = this.maxTotal - totalFetched;\n if (remaining <= 0) break;\n pageSize = Math.min(pageSize, remaining);\n }\n\n const body: Record<string, unknown> = {\n jql,\n maxResults: pageSize,\n fields: [\"*all\"],\n };\n\n if (nextPageToken) {\n body.nextPageToken = nextPageToken;\n }\n\n const response = await fetch(url, {\n method: \"POST\",\n headers: {\n Authorization: authorizationHeader,\n Accept: \"application/json\",\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(body),\n });\n\n const data = await response.json();\n\n if (!response.ok) {\n throw new Error(\n `Jira API request failed with status ${response.status}: ${JSON.stringify(data)}`\n );\n }\n\n if (data.issues?.length) {\n yield data.issues;\n totalFetched += data.issues.length;\n }\n\n if (data.isLast === true) break;\n\n if (!data.nextPageToken) {\n throw new Error(\"Expected nextPageToken but none returned\");\n }\n\n nextPageToken = data.nextPageToken;\n }\n }\n}\n"],"mappings":"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"}