@gitkraken/core-gitlens 0.11.1 → 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +28 -1
- package/dist/git/models/issue.d.ts +3 -1
- package/dist/git/models/issue.js.map +1 -1
- package/dist/git/utils/issue.utils.d.ts +3 -1
- package/dist/git/utils/issue.utils.js +5 -1
- package/dist/git/utils/issue.utils.js.map +1 -1
- package/dist/plus/integrations/models/gitHostIntegration.js +3 -1
- package/dist/plus/integrations/models/gitHostIntegration.js.map +1 -1
- package/dist/plus/integrations/models/integration.js +1 -1
- package/dist/plus/integrations/providers/azure/azure.js +3 -2
- package/dist/plus/integrations/providers/azure/azure.js.map +1 -1
- package/dist/plus/integrations/providers/azure/models.d.ts +27 -10
- package/dist/plus/integrations/providers/azure/models.js +39 -19
- package/dist/plus/integrations/providers/azure/models.js.map +1 -1
- package/dist/plus/integrations/providers/azure/workItemStateCategories.d.ts +19 -0
- package/dist/plus/integrations/providers/azure/workItemStateCategories.js +32 -0
- package/dist/plus/integrations/providers/azure/workItemStateCategories.js.map +1 -0
- package/dist/plus/integrations/providers/azureDevOps.js +5 -5
- package/dist/plus/integrations/providers/azureDevOps.js.map +1 -1
- package/dist/plus/integrations/providers/issueSorts.d.ts +0 -1
- package/dist/plus/integrations/providers/jira-server.js +11 -5
- package/dist/plus/integrations/providers/jira-server.js.map +1 -1
- package/dist/plus/integrations/providers/jira.d.ts +5 -2
- package/dist/plus/integrations/providers/jira.js +45 -30
- package/dist/plus/integrations/providers/jira.js.map +1 -1
- package/dist/plus/integrations/providers/jiraIssueByKey.d.ts +3 -4
- package/dist/plus/integrations/providers/jiraIssueByKey.js +3 -4
- package/dist/plus/integrations/providers/jiraIssueByKey.js.map +1 -1
- package/dist/plus/integrations/providers/linearIssuesEtag.d.ts +5 -5
- package/dist/plus/integrations/providers/linearIssuesEtag.js +6 -6
- package/dist/plus/integrations/providers/linearIssuesEtag.js.map +1 -1
- package/dist/plus/integrations/providers/models.d.ts +11 -1
- package/dist/plus/integrations/providers/models.js +9 -9
- package/dist/plus/integrations/providers/models.js.map +1 -1
- package/dist/plus/integrations/providers/providersApi.d.ts +30 -32
- package/dist/plus/integrations/providers/providersApi.js +149 -46
- package/dist/plus/integrations/providers/providersApi.js.map +1 -1
- package/dist/plus/integrations/providers/utils/jiraIssueDrains.d.ts +27 -15
- package/dist/plus/integrations/providers/utils/jiraIssueDrains.js +72 -10
- package/dist/plus/integrations/providers/utils/jiraIssueDrains.js.map +1 -1
- package/dist/plus/integrations/reads/cursors.js +1 -1
- package/dist/plus/integrations/reads/drains.d.ts +0 -1
- package/dist/plus/integrations/reads/hierarchy.js +2 -2
- package/dist/plus/integrations/reads/hierarchy.js.map +1 -1
- package/dist/plus/integrations/reads/hierarchy.utils.d.ts +13 -0
- package/dist/plus/integrations/reads/hierarchy.utils.js +26 -1
- package/dist/plus/integrations/reads/hierarchy.utils.js.map +1 -1
- package/dist/plus/integrations/reads/issueTracker.js +4 -4
- package/dist/plus/integrations/reads/issueTracker.js.map +1 -1
- package/dist/plus/integrations/reads/issues.js +1 -1
- package/dist/plus/integrations/results.d.ts +12 -2
- package/dist/plus/integrations/results.js.map +1 -1
- package/dist/plus/integrations/utils/integration.utils.d.ts +6 -1
- package/dist/plus/integrations/utils/integration.utils.js +8 -1
- package/dist/plus/integrations/utils/integration.utils.js.map +1 -1
- package/docs/integrations.md +49 -19
- package/package.json +2 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"jiraIssueByKey.js","names":[],"sources":["../../../../../plus/integrations/src/providers/jiraIssueByKey.ts"],"sourcesContent":["import type { IssueEtagFields } from '../models/integration.js';\nimport type { ProviderAccount, ProviderIssue, ProviderRequestFunction } from './models.js';\n\nconst jiraCloudApiUrl = 'https://api.atlassian.com/ex/jira';\nconst jiraIssueByKeyFields = [\n\t'assignee',\n\t'comment',\n\t'created',\n\t'creator',\n\t'description',\n\t'issuetype',\n\t'labels',\n\t'project',\n\t'status',\n\t'summary',\n\t'updated',\n\t'votes',\n];\n\ntype JiraIssueMemberResponse = {\n\taccountId?: string;\n\tdisplayName?: string | null;\n\temailAddress?: string | null;\n\tavatarUrls?: Record<string, string | undefined>;\n};\n\ntype JiraIssueStatusResponse = {\n\tid: string;\n\tname: string;\n\tstatusCategory: {\n\t\tcolorName: string;\n\t\tkey: string;\n\t\tname: string;\n\t};\n};\n\ntype JiraIssueByKeyResponse = {\n\tid: string;\n\tkey: string;\n\tfields: {\n\t\tassignee?: JiraIssueMemberResponse | null;\n\t\tcomment?: { comments?: unknown[]; total?: number };\n\t\tcreated: string;\n\t\tcreator?: JiraIssueMemberResponse | null;\n\t\tdescription?: string | null;\n\t\tissuetype?: { name?: string | null };\n\t\tlabels?: string[];\n\t\tproject?: { id?: string | null; key?: string | null; name?: string | null };\n\t\tstatus: JiraIssueStatusResponse;\n\t\tsummary: string;\n\t\tupdated: string;\n\t\tvotes?: { votes?: number | null };\n\t};\n};\n\nfunction toAccount(member: JiraIssueMemberResponse | null | undefined): ProviderAccount | null {\n\tif (member == null) return null;\n\n\treturn {\n\t\tid: member.accountId ?? '',\n\t\tname: member.displayName ?? null,\n\t\tusername: member.displayName ?? null,\n\t\temail: member.emailAddress ?? null,\n\t\tavatarUrl: member.avatarUrls?.['48x48'] ?? null,\n\t\turl: null,\n\t};\n}\n\nfunction toStatusCategory(key: string): 'TO_DO' | 'IN_PROGRESS' | 'DONE' {\n\tswitch (key.toLowerCase()) {\n\t\tcase 'new':\n\t\t\treturn 'TO_DO';\n\t\tcase 'indeterminate':\n\t\t\treturn 'IN_PROGRESS';\n\t\tcase 'done':\n\t\t\treturn 'DONE';\n\t\tdefault:\n\t\t\treturn 'TO_DO';\n\t}\n}\n\nfunction toJiraIssueState(status: JiraIssueStatusResponse): NonNullable<ProviderIssue['state']> {\n\treturn {\n\t\tid: status.id,\n\t\tname: status.name,\n\t\tcolor: status.statusCategory.colorName,\n\t\tcategory: toStatusCategory(status.statusCategory.key),\n\t};\n}\n\nfunction toIssueWebUrl(resourceUrl: string, key: string): string {\n\tconst url = new URL(resourceUrl);\n\turl.pathname = `${url.pathname.replace(/\\/+$/, '')}/browse/${encodeURIComponent(key)}`;\n\turl.search = '';\n\turl.hash = '';\n\treturn url.toString();\n}\n\nfunction fromJiraIssueByKey(issue: JiraIssueByKeyResponse, resourceId: string, resourceUrl: string): ProviderIssue {\n\tconst assignee = toAccount(issue.fields.assignee);\n\tconst project = issue.fields.project;\n\tconst status = issue.fields.status;\n\n\treturn {\n\t\tid: issue.id,\n\t\tcommentCount: issue.fields.comment?.total ?? issue.fields.comment?.comments?.length ?? null,\n\t\tnumber: issue.key,\n\t\ttitle: issue.fields.summary,\n\t\turl: toIssueWebUrl(resourceUrl, issue.key),\n\t\tclosedDate: null,\n\t\tcreatedDate: new Date(issue.fields.created),\n\t\tauthor: toAccount(issue.fields.creator),\n\t\tupdatedDate: new Date(issue.fields.updated),\n\t\tassignees: assignee != null ? [assignee] : [],\n\t\tdescription: issue.fields.description ?? null,\n\t\trepository: null,\n\t\tproject:\n\t\t\tproject != null\n\t\t\t\t? {\n\t\t\t\t\t\tname: project.name ?? '',\n\t\t\t\t\t\tresourceId: resourceId,\n\t\t\t\t\t\tkey: project.key ?? null,\n\t\t\t\t\t\tnamespace: null,\n\t\t\t\t\t\tid: project.id ?? null,\n\t\t\t\t\t}\n\t\t\t\t: undefined,\n\t\tstate: toJiraIssueState(status),\n\t\ttype: issue.fields.issuetype?.name ?? null,\n\t\tupvoteCount: issue.fields.votes?.votes ?? null,\n\t\tlabels: (issue.fields.labels ?? []).map(label => ({\n\t\t\tcolor: null,\n\t\t\tdescription: null,\n\t\t\tid: null,\n\t\t\tname: label,\n\t\t})),\n\t};\n}\n\n/**\n * The SDK's Jira point read performs resource and field discovery before the issue GET. This focused path uses\n * caller-supplied resource identity so a cold lookup remains one upstream request.\n */\nexport async function requestJiraIssueByKey(\n\trequest: ProviderRequestFunction,\n\taccessToken: string,\n\tresourceId: string,\n\tresourceUrl: string,\n\tkey: string,\n): Promise<ProviderIssue> {\n\tconst url = new URL(\n\t\t`${jiraCloudApiUrl}/${encodeURIComponent(resourceId)}/rest/api/2/issue/${encodeURIComponent(key)}`,\n\t);\n\turl.searchParams.set('fields', jiraIssueByKeyFields.join(','));\n\n\tconst response = await request<JiraIssueByKeyResponse>({\n\t\turl: url.toString(),\n\t\theaders: { Authorization: `Bearer ${accessToken}` },\n\t});\n\treturn fromJiraIssueByKey(response.body, resourceId, resourceUrl);\n}\n\n/**\n * The most keys one bulk fetch takes. Atlassian allows up to 1000 for a request that names its fields explicitly, but\n * documents 100 as the default limit.\n */\nexport const jiraBulkFetchMaxKeys = 100;\n\n/**\n * One issue of a bulk fetch that asks for `status` and `updated` only. The bulk fetch is API v3 while the full read is\n * v2, but these two fields have the same shape in both: the v2 and v3 OpenAPI specs define the same `StatusDetails`\n * and `StatusCategory`, and a live comparison found both fields byte-identical. v3 differs in rich-text fields, which\n * it returns as ADF, so this request must never widen to `description`. `self` differs too, and is never read.\n */\nexport type JiraIssueEtagResponse = {\n\tid: string;\n\tkey: string;\n\tfields: Partial<Pick<JiraIssueByKeyResponse['fields'], 'status' | 'updated'>>;\n};\n\ntype JiraBulkFetchResponse = {\n\tissues?: JiraIssueEtagResponse[];\n\t/**\n\t * Only issues Jira could not return for a retriable error or a payload constraint, by numeric issue id rather than\n\t * the key asked for. Issues that don't exist or aren't visible are NOT listed here, nor in `issues`: they are\n\t * silently omitted, so a bulk fetch never proves an absence.\n\t */\n\tissueErrors?: { id?: string; errorMessage?: string }[];\n};\n\n/**\n * The cheap check behind the batch issue read's etags: `status` and `updated` of up to {@link jiraBulkFetchMaxKeys}\n * issues of one site in ONE request, where {@link requestJiraIssueByKey} reads one issue per request. Jira returns the\n * issues in its own order, each under its CURRENT key (a lowercase or moved key resolves to the issue it names now), and\n * omits the ones it can't find, so a caller matches them to its keys by key, never by position.\n */\nexport async function requestJiraIssuesEtagFields(\n\trequest: ProviderRequestFunction,\n\taccessToken: string,\n\tresourceId: string,\n\tkeys: readonly string[],\n): Promise<{ issues: JiraIssueEtagResponse[]; errorCount: number }> {\n\tconst response = await request<JiraBulkFetchResponse | null>({\n\t\turl: `${jiraCloudApiUrl}/${encodeURIComponent(resourceId)}/rest/api/3/issue/bulkfetch`,\n\t\tmethod: 'POST',\n\t\theaders: { Authorization: `Bearer ${accessToken}`, 'Content-Type': 'application/json' },\n\t\tbody: JSON.stringify({ issueIdsOrKeys: keys, fields: ['status', 'updated'] }),\n\t});\n\n\tconst issues = response.body?.issues;\n\tif (!Array.isArray(issues)) throw new Error('Jira bulk fetch returned no issues');\n\n\treturn { issues: issues, errorCount: response.body?.issueErrors?.length ?? 0 };\n}\n\n/**\n * A bulk-fetched issue's change state, as the fields the full row's etag reads. {@link fromJiraIssueByKey} maps the\n * status with the same {@link toJiraIssueState} and never sets `closedDate`, so `toIssueShape` (with\n * `reliableStateCategory`) closes exactly the issues whose status category is done. Throws on a missing status or an\n * unparseable `updated`, so the target falls through to the full read rather than matching an etag that would ignore\n * its update time.\n */\nexport function toJiraIssueEtagFields(issue: JiraIssueEtagResponse): IssueEtagFields {\n\tconst { status, updated } = issue.fields;\n\tif (status?.statusCategory == null) throw new Error(`Jira bulk fetch returned no status for ${issue.key}`);\n\n\tconst updatedDate = new Date(updated ?? Number.NaN);\n\tif (Number.isNaN(updatedDate.getTime())) {\n\t\tthrow new Error(`Jira bulk fetch returned no update time for ${issue.key}`);\n\t}\n\n\treturn { state: toJiraIssueState(status).category === 'DONE' ? 'closed' : 'opened', updatedDate: updatedDate };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"jiraIssueByKey.js","names":[],"sources":["../../../../../plus/integrations/src/providers/jiraIssueByKey.ts"],"sourcesContent":["import type { IssueEtagFields } from '../models/integration.js';\nimport type { ProviderAccount, ProviderIssue, ProviderRequestFunction } from './models.js';\n\nconst jiraCloudApiUrl = 'https://api.atlassian.com/ex/jira';\nconst jiraIssueByKeyFields = [\n\t'assignee',\n\t'comment',\n\t'created',\n\t'creator',\n\t'description',\n\t'issuetype',\n\t'labels',\n\t'project',\n\t'status',\n\t'summary',\n\t'updated',\n\t'votes',\n];\n\ntype JiraIssueMemberResponse = {\n\taccountId?: string;\n\tdisplayName?: string | null;\n\temailAddress?: string | null;\n\tavatarUrls?: Record<string, string | undefined>;\n};\n\ntype JiraIssueStatusResponse = {\n\tid: string;\n\tname: string;\n\tstatusCategory: {\n\t\tcolorName: string;\n\t\tkey: string;\n\t\tname: string;\n\t};\n};\n\ntype JiraIssueByKeyResponse = {\n\tid: string;\n\tkey: string;\n\tfields: {\n\t\tassignee?: JiraIssueMemberResponse | null;\n\t\tcomment?: { comments?: unknown[]; total?: number };\n\t\tcreated: string;\n\t\tcreator?: JiraIssueMemberResponse | null;\n\t\tdescription?: string | null;\n\t\tissuetype?: { name?: string | null };\n\t\tlabels?: string[];\n\t\tproject?: { id?: string | null; key?: string | null; name?: string | null };\n\t\tstatus: JiraIssueStatusResponse;\n\t\tsummary: string;\n\t\tupdated: string;\n\t\tvotes?: { votes?: number | null };\n\t};\n};\n\nfunction toAccount(member: JiraIssueMemberResponse | null | undefined): ProviderAccount | null {\n\tif (member == null) return null;\n\n\treturn {\n\t\tid: member.accountId ?? '',\n\t\tname: member.displayName ?? null,\n\t\tusername: member.displayName ?? null,\n\t\temail: member.emailAddress ?? null,\n\t\tavatarUrl: member.avatarUrls?.['48x48'] ?? null,\n\t\turl: null,\n\t};\n}\n\nfunction toStatusCategory(key: string): 'TO_DO' | 'IN_PROGRESS' | 'DONE' {\n\tswitch (key.toLowerCase()) {\n\t\tcase 'new':\n\t\t\treturn 'TO_DO';\n\t\tcase 'indeterminate':\n\t\t\treturn 'IN_PROGRESS';\n\t\tcase 'done':\n\t\t\treturn 'DONE';\n\t\tdefault:\n\t\t\treturn 'TO_DO';\n\t}\n}\n\nfunction toJiraIssueState(status: JiraIssueStatusResponse): NonNullable<ProviderIssue['state']> {\n\treturn {\n\t\tid: status.id,\n\t\tname: status.name,\n\t\tcolor: status.statusCategory.colorName,\n\t\tcategory: toStatusCategory(status.statusCategory.key),\n\t};\n}\n\nfunction toIssueWebUrl(resourceUrl: string, key: string): string {\n\tconst url = new URL(resourceUrl);\n\turl.pathname = `${url.pathname.replace(/\\/+$/, '')}/browse/${encodeURIComponent(key)}`;\n\turl.search = '';\n\turl.hash = '';\n\treturn url.toString();\n}\n\nfunction fromJiraIssueByKey(issue: JiraIssueByKeyResponse, resourceId: string, resourceUrl: string): ProviderIssue {\n\tconst assignee = toAccount(issue.fields.assignee);\n\tconst project = issue.fields.project;\n\tconst status = issue.fields.status;\n\n\treturn {\n\t\tid: issue.id,\n\t\tcommentCount: issue.fields.comment?.total ?? issue.fields.comment?.comments?.length ?? null,\n\t\tnumber: issue.key,\n\t\ttitle: issue.fields.summary,\n\t\turl: toIssueWebUrl(resourceUrl, issue.key),\n\t\tclosedDate: null,\n\t\tcreatedDate: new Date(issue.fields.created),\n\t\tauthor: toAccount(issue.fields.creator),\n\t\tupdatedDate: new Date(issue.fields.updated),\n\t\tassignees: assignee != null ? [assignee] : [],\n\t\tdescription: issue.fields.description ?? null,\n\t\trepository: null,\n\t\tproject:\n\t\t\tproject != null\n\t\t\t\t? {\n\t\t\t\t\t\tname: project.name ?? '',\n\t\t\t\t\t\tresourceId: resourceId,\n\t\t\t\t\t\tkey: project.key ?? null,\n\t\t\t\t\t\tnamespace: null,\n\t\t\t\t\t\tid: project.id ?? null,\n\t\t\t\t\t}\n\t\t\t\t: undefined,\n\t\tstate: toJiraIssueState(status),\n\t\ttype: issue.fields.issuetype?.name ?? null,\n\t\tupvoteCount: issue.fields.votes?.votes ?? null,\n\t\tlabels: (issue.fields.labels ?? []).map(label => ({\n\t\t\tcolor: null,\n\t\t\tdescription: null,\n\t\t\tid: null,\n\t\t\tname: label,\n\t\t})),\n\t};\n}\n\n/**\n * The SDK's Jira point read performs resource and field discovery before the issue GET. This focused path uses\n * caller-supplied resource identity so a cold lookup remains one upstream request.\n */\nexport async function requestJiraIssueByKey(\n\trequest: ProviderRequestFunction,\n\taccessToken: string,\n\tresourceId: string,\n\tresourceUrl: string,\n\tkey: string,\n): Promise<ProviderIssue> {\n\tconst url = new URL(\n\t\t`${jiraCloudApiUrl}/${encodeURIComponent(resourceId)}/rest/api/2/issue/${encodeURIComponent(key)}`,\n\t);\n\turl.searchParams.set('fields', jiraIssueByKeyFields.join(','));\n\n\tconst response = await request<JiraIssueByKeyResponse>({\n\t\turl: url.toString(),\n\t\theaders: { Authorization: `Bearer ${accessToken}` },\n\t});\n\treturn fromJiraIssueByKey(response.body, resourceId, resourceUrl);\n}\n\n/**\n * The most keys one bulk fetch takes. Atlassian allows up to 1000 for a request that names its fields explicitly, but\n * documents 100 as the default limit.\n */\nexport const jiraBulkFetchMaxKeys = 100;\n\n/**\n * One issue of a bulk fetch that asks for `status` and `updated` only. The bulk fetch is API v3 while the full read is\n * v2, but these two fields have the same shape in both: the v2 and v3 OpenAPI specs define the same `StatusDetails`\n * and `StatusCategory`, and a live comparison found both fields byte-identical. v3 differs in rich-text fields, which\n * it returns as ADF, so this request must never widen to `description`. `self` differs too, and is never read.\n */\nexport type JiraIssueEtagResponse = {\n\tid: string;\n\tkey: string;\n\tfields: Partial<Pick<JiraIssueByKeyResponse['fields'], 'status' | 'updated'>>;\n};\n\ntype JiraBulkFetchResponse = {\n\tissues?: JiraIssueEtagResponse[];\n\t/**\n\t * Only issues Jira could not return for a retriable error or a payload constraint, by numeric issue id rather than\n\t * the key asked for. Issues that don't exist or aren't visible are NOT listed here, nor in `issues`: they are\n\t * silently omitted, so a bulk fetch never proves an absence.\n\t */\n\tissueErrors?: { id?: string; errorMessage?: string }[];\n};\n\n/**\n * The cheap check behind the batch issue read's etags: `status` and `updated` of up to {@link jiraBulkFetchMaxKeys}\n * issues of one site in ONE request, where {@link requestJiraIssueByKey} reads one issue per request. Jira returns the\n * issues in its own order, each under its CURRENT key (a lowercase or moved key resolves to the issue it names now), and\n * omits the ones it can't find, so a caller matches them to its keys by key, never by position.\n */\nexport async function requestJiraIssuesEtagFields(\n\trequest: ProviderRequestFunction,\n\taccessToken: string,\n\tresourceId: string,\n\tkeys: readonly string[],\n): Promise<{ issues: JiraIssueEtagResponse[]; errorCount: number }> {\n\tconst response = await request<JiraBulkFetchResponse | null>({\n\t\turl: `${jiraCloudApiUrl}/${encodeURIComponent(resourceId)}/rest/api/3/issue/bulkfetch`,\n\t\tmethod: 'POST',\n\t\theaders: { Authorization: `Bearer ${accessToken}`, 'Content-Type': 'application/json' },\n\t\tbody: JSON.stringify({ issueIdsOrKeys: keys, fields: ['status', 'updated'] }),\n\t});\n\n\tconst issues = response.body?.issues;\n\tif (!Array.isArray(issues)) throw new Error('Jira bulk fetch returned no issues');\n\n\treturn { issues: issues, errorCount: response.body?.issueErrors?.length ?? 0 };\n}\n\n/**\n * A bulk-fetched issue's change state, as the fields the full row's etag reads. {@link fromJiraIssueByKey} maps the\n * status with the same {@link toJiraIssueState} and never sets `closedDate`, so `toIssueShape` closes exactly the\n * issues whose status category is done. Throws on a missing status or an unparseable `updated`, so the target falls\n * through to the full read rather than matching an etag that would ignore its update time.\n */\nexport function toJiraIssueEtagFields(issue: JiraIssueEtagResponse): IssueEtagFields {\n\tconst { status, updated } = issue.fields;\n\tif (status?.statusCategory == null) throw new Error(`Jira bulk fetch returned no status for ${issue.key}`);\n\n\tconst updatedDate = new Date(updated ?? Number.NaN);\n\tif (Number.isNaN(updatedDate.getTime())) {\n\t\tthrow new Error(`Jira bulk fetch returned no update time for ${issue.key}`);\n\t}\n\n\treturn { state: toJiraIssueState(status).category === 'DONE' ? 'closed' : 'opened', updatedDate: updatedDate };\n}\n"],"mappings":";AAGA,MAAM,kBAAkB;AACxB,MAAM,uBAAuB;CAC5B;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACD;AAsCA,SAAS,UAAU,QAA4E;CAC9F,IAAI,UAAU,MAAM,OAAO;CAE3B,OAAO;EACN,IAAI,OAAO,aAAa;EACxB,MAAM,OAAO,eAAe;EAC5B,UAAU,OAAO,eAAe;EAChC,OAAO,OAAO,gBAAgB;EAC9B,WAAW,OAAO,aAAa,YAAY;EAC3C,KAAK;CACN;AACD;AAEA,SAAS,iBAAiB,KAA+C;CACxE,QAAQ,IAAI,YAAY,GAAxB;EACC,KAAK,OACJ,OAAO;EACR,KAAK,iBACJ,OAAO;EACR,KAAK,QACJ,OAAO;EACR,SACC,OAAO;CACT;AACD;AAEA,SAAS,iBAAiB,QAAsE;CAC/F,OAAO;EACN,IAAI,OAAO;EACX,MAAM,OAAO;EACb,OAAO,OAAO,eAAe;EAC7B,UAAU,iBAAiB,OAAO,eAAe,GAAG;CACrD;AACD;AAEA,SAAS,cAAc,aAAqB,KAAqB;CAChE,MAAM,MAAM,IAAI,IAAI,WAAW;CAC/B,IAAI,WAAW,GAAG,IAAI,SAAS,QAAQ,QAAQ,EAAE,EAAE,UAAU,mBAAmB,GAAG;CACnF,IAAI,SAAS;CACb,IAAI,OAAO;CACX,OAAO,IAAI,SAAS;AACrB;AAEA,SAAS,mBAAmB,OAA+B,YAAoB,aAAoC;CAClH,MAAM,WAAW,UAAU,MAAM,OAAO,QAAQ;CAChD,MAAM,UAAU,MAAM,OAAO;CAC7B,MAAM,SAAS,MAAM,OAAO;CAE5B,OAAO;EACN,IAAI,MAAM;EACV,cAAc,MAAM,OAAO,SAAS,SAAS,MAAM,OAAO,SAAS,UAAU,UAAU;EACvF,QAAQ,MAAM;EACd,OAAO,MAAM,OAAO;EACpB,KAAK,cAAc,aAAa,MAAM,GAAG;EACzC,YAAY;EACZ,aAAa,IAAI,KAAK,MAAM,OAAO,OAAO;EAC1C,QAAQ,UAAU,MAAM,OAAO,OAAO;EACtC,aAAa,IAAI,KAAK,MAAM,OAAO,OAAO;EAC1C,WAAW,YAAY,OAAO,CAAC,QAAQ,IAAI,CAAC;EAC5C,aAAa,MAAM,OAAO,eAAe;EACzC,YAAY;EACZ,SACC,WAAW,OACR;GACA,MAAM,QAAQ,QAAQ;GACV;GACZ,KAAK,QAAQ,OAAO;GACpB,WAAW;GACX,IAAI,QAAQ,MAAM;EACnB,IACC,KAAA;EACJ,OAAO,iBAAiB,MAAM;EAC9B,MAAM,MAAM,OAAO,WAAW,QAAQ;EACtC,aAAa,MAAM,OAAO,OAAO,SAAS;EAC1C,SAAS,MAAM,OAAO,UAAU,CAAC,EAAA,CAAG,KAAI,WAAU;GACjD,OAAO;GACP,aAAa;GACb,IAAI;GACJ,MAAM;EACP,EAAE;CACH;AACD;;;;;AAMA,eAAsB,sBACrB,SACA,aACA,YACA,aACA,KACyB;CACzB,MAAM,MAAM,IAAI,IACf,GAAG,gBAAgB,GAAG,mBAAmB,UAAU,EAAE,oBAAoB,mBAAmB,GAAG,GAChG;CACA,IAAI,aAAa,IAAI,UAAU,qBAAqB,KAAK,GAAG,CAAC;CAM7D,OAAO,oBAAmB,MAJH,QAAgC;EACtD,KAAK,IAAI,SAAS;EAClB,SAAS,EAAE,eAAe,UAAU,cAAc;CACnD,CAAC,EAAA,CACkC,MAAM,YAAY,WAAW;AACjE;;;;;AAMA,MAAa,uBAAuB;;;;;;;AA8BpC,eAAsB,4BACrB,SACA,aACA,YACA,MACmE;CACnE,MAAM,WAAW,MAAM,QAAsC;EAC5D,KAAK,GAAG,gBAAgB,GAAG,mBAAmB,UAAU,EAAE;EAC1D,QAAQ;EACR,SAAS;GAAE,eAAe,UAAU;GAAe,gBAAgB;EAAmB;EACtF,MAAM,KAAK,UAAU;GAAE,gBAAgB;GAAM,QAAQ,CAAC,UAAU,SAAS;EAAE,CAAC;CAC7E,CAAC;CAED,MAAM,SAAS,SAAS,MAAM;CAC9B,IAAI,CAAC,MAAM,QAAQ,MAAM,GAAG,MAAM,IAAI,MAAM,oCAAoC;CAEhF,OAAO;EAAU;EAAQ,YAAY,SAAS,MAAM,aAAa,UAAU;CAAE;AAC9E;;;;;;;AAQA,SAAgB,sBAAsB,OAA+C;CACpF,MAAM,EAAE,QAAQ,YAAY,MAAM;CAClC,IAAI,QAAQ,kBAAkB,MAAM,MAAM,IAAI,MAAM,0CAA0C,MAAM,KAAK;CAEzG,MAAM,cAAc,IAAI,KAAK,WAAW,GAAU;CAClD,IAAI,OAAO,MAAM,YAAY,QAAQ,CAAC,GACrC,MAAM,IAAI,MAAM,+CAA+C,MAAM,KAAK;CAG3E,OAAO;EAAE,OAAO,iBAAiB,MAAM,CAAC,CAAC,aAAa,SAAS,WAAW;EAAuB;CAAY;AAC9G"}
|
|
@@ -25,12 +25,12 @@ export type LinearIssueEtagNode = {
|
|
|
25
25
|
export declare function requestLinearIssuesEtagFields(request: ProviderRequestFunction, accessToken: string, teamKey: string, numbers: readonly number[]): Promise<LinearIssueEtagNode[]>;
|
|
26
26
|
/**
|
|
27
27
|
* A cheap-checked issue's change state, as the fields the full row's etag reads. The full read's row is provider-apis'
|
|
28
|
-
* Linear issue normalizer (0.
|
|
28
|
+
* Linear issue normalizer (0.65.0) then `fromProviderIssue`, so this mirrors both:
|
|
29
29
|
*
|
|
30
|
-
* - provider-apis maps `state.type` to a category — `completed`
|
|
31
|
-
* anything else (`backlog`, `unstarted`, `triage`) to `TO_DO`, and no state to none —
|
|
32
|
-
* `archivedAt` (a falsy value reads as none);
|
|
33
|
-
* - `fromProviderIssue` closes an issue that has a `closedDate` or a
|
|
30
|
+
* - provider-apis maps `state.type` to a category — `completed` to `DONE`, `canceled` and `duplicate` to `CANCELED`,
|
|
31
|
+
* `started` to `IN_PROGRESS`, anything else (`backlog`, `unstarted`, `triage`) to `TO_DO`, and no state to none —
|
|
32
|
+
* and sets `closedDate` from `archivedAt` (a falsy value reads as none);
|
|
33
|
+
* - `fromProviderIssue` closes an issue that has a `closedDate` or a terminal category, so an archived issue is closed
|
|
34
34
|
* whatever its state.
|
|
35
35
|
*
|
|
36
36
|
* Throws on a missing or unparseable `updatedAt`, so the target falls through to the full read rather than matching an
|
|
@@ -67,12 +67,12 @@ async function requestLinearIssuesEtagFields(request, accessToken, teamKey, numb
|
|
|
67
67
|
}
|
|
68
68
|
/**
|
|
69
69
|
* A cheap-checked issue's change state, as the fields the full row's etag reads. The full read's row is provider-apis'
|
|
70
|
-
* Linear issue normalizer (0.
|
|
70
|
+
* Linear issue normalizer (0.65.0) then `fromProviderIssue`, so this mirrors both:
|
|
71
71
|
*
|
|
72
|
-
* - provider-apis maps `state.type` to a category — `completed`
|
|
73
|
-
* anything else (`backlog`, `unstarted`, `triage`) to `TO_DO`, and no state to none —
|
|
74
|
-
* `archivedAt` (a falsy value reads as none);
|
|
75
|
-
* - `fromProviderIssue` closes an issue that has a `closedDate` or a
|
|
72
|
+
* - provider-apis maps `state.type` to a category — `completed` to `DONE`, `canceled` and `duplicate` to `CANCELED`,
|
|
73
|
+
* `started` to `IN_PROGRESS`, anything else (`backlog`, `unstarted`, `triage`) to `TO_DO`, and no state to none —
|
|
74
|
+
* and sets `closedDate` from `archivedAt` (a falsy value reads as none);
|
|
75
|
+
* - `fromProviderIssue` closes an issue that has a `closedDate` or a terminal category, so an archived issue is closed
|
|
76
76
|
* whatever its state.
|
|
77
77
|
*
|
|
78
78
|
* Throws on a missing or unparseable `updatedAt`, so the target falls through to the full read rather than matching an
|
|
@@ -84,7 +84,7 @@ function toLinearIssueEtagFields(issue) {
|
|
|
84
84
|
if (Number.isNaN(updatedDate.getTime())) throw new Error(`Linear returned no update time for ${issue.identifier}`);
|
|
85
85
|
const type = issue.state?.type;
|
|
86
86
|
return {
|
|
87
|
-
state: Boolean(issue.archivedAt) || type === "completed" || type === "canceled" ? "closed" : "opened",
|
|
87
|
+
state: Boolean(issue.archivedAt) || type === "completed" || type === "canceled" || type === "duplicate" ? "closed" : "opened",
|
|
88
88
|
updatedDate
|
|
89
89
|
};
|
|
90
90
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"linearIssuesEtag.js","names":[],"sources":["../../../../../plus/integrations/src/providers/linearIssuesEtag.ts"],"sourcesContent":["import type { IssueEtagFields } from '../models/integration.js';\nimport type { ProviderRequestFunction } from './models.js';\n\nconst linearGraphQLUrl = 'https://api.linear.app/graphql';\n\n/** The most issue numbers one cheap check asks of one team. */\nexport const linearIssuesEtagMaxNumbers = 50;\n\n/**\n * Archived issues are left out of Linear's `issues` connection unless asked for, so `includeArchived` keeps an archived\n * issue answerable here as the full read's `issue(id:)` answers it.\n */\nconst linearIssuesEtagQuery = `\n\tquery GetIssuesEtagFields($teamKey: String!, $numbers: [Float!]!) {\n\t\tissues(\n\t\t\tfirst: ${linearIssuesEtagMaxNumbers}\n\t\t\tincludeArchived: true\n\t\t\tfilter: { team: { key: { eq: $teamKey } }, number: { in: $numbers } }\n\t\t) {\n\t\t\tpageInfo {\n\t\t\t\thasNextPage\n\t\t\t}\n\t\t\tnodes {\n\t\t\t\tidentifier\n\t\t\t\tnumber\n\t\t\t\tupdatedAt\n\t\t\t\tarchivedAt\n\t\t\t\tstate {\n\t\t\t\t\ttype\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n`;\n\nexport type LinearIssueEtagNode = {\n\tidentifier: string;\n\tnumber: number;\n\tupdatedAt?: string | null;\n\tarchivedAt?: string | null;\n\tstate?: { type?: string | null } | null;\n};\n\ntype LinearIssuesEtagResponse = {\n\tdata?: {\n\t\tissues?: { pageInfo?: { hasNextPage?: boolean }; nodes?: LinearIssueEtagNode[] } | null;\n\t} | null;\n\terrors?: unknown[];\n};\n\n/**\n * The cheap check behind the batch issue read's etags: the change state of up to {@link linearIssuesEtagMaxNumbers}\n * issues of one team in ONE GraphQL request, where the full read sends provider-apis' `issue(id:)` once per issue.\n * Linear returns only the issues it found, in its own order, so a caller matches them by number, never by position.\n *\n * Sent as provider-apis sends every Linear request: the token as the whole `Authorization` header, with no scheme.\n * GraphQL `errors` reject the whole request, as provider-apis rejects them, but keep the response so a throttling\n * code is still classified as one. A next page also rejects it: with every number unique within a team, more\n * issues than were asked for means the filter was not applied as written.\n */\nexport async function requestLinearIssuesEtagFields(\n\trequest: ProviderRequestFunction,\n\taccessToken: string,\n\tteamKey: string,\n\tnumbers: readonly number[],\n): Promise<LinearIssueEtagNode[]> {\n\tconst response = await request<LinearIssuesEtagResponse | null>({\n\t\turl: linearGraphQLUrl,\n\t\tmethod: 'POST',\n\t\theaders: { Authorization: accessToken, 'Content-Type': 'application/json' },\n\t\tbody: JSON.stringify({ query: linearIssuesEtagQuery, variables: { teamKey: teamKey, numbers: numbers } }),\n\t});\n\n\tconst body = response.body;\n\tif (body?.errors != null) {\n\t\tconst error = new Error(`Linear GraphQL errors: ${JSON.stringify(body.errors)}`);\n\t\tObject.assign(error, { response: response });\n\t\tthrow error;\n\t}\n\n\tconst issues = body?.data?.issues;\n\tif (!Array.isArray(issues?.nodes)) throw new Error('Linear returned no issues');\n\tif (issues.pageInfo?.hasNextPage !== false) throw new Error('Linear returned more issues than were asked for');\n\n\treturn issues.nodes;\n}\n\n/**\n * A cheap-checked issue's change state, as the fields the full row's etag reads. The full read's row is provider-apis'\n * Linear issue normalizer (0.
|
|
1
|
+
{"version":3,"file":"linearIssuesEtag.js","names":[],"sources":["../../../../../plus/integrations/src/providers/linearIssuesEtag.ts"],"sourcesContent":["import type { IssueEtagFields } from '../models/integration.js';\nimport type { ProviderRequestFunction } from './models.js';\n\nconst linearGraphQLUrl = 'https://api.linear.app/graphql';\n\n/** The most issue numbers one cheap check asks of one team. */\nexport const linearIssuesEtagMaxNumbers = 50;\n\n/**\n * Archived issues are left out of Linear's `issues` connection unless asked for, so `includeArchived` keeps an archived\n * issue answerable here as the full read's `issue(id:)` answers it.\n */\nconst linearIssuesEtagQuery = `\n\tquery GetIssuesEtagFields($teamKey: String!, $numbers: [Float!]!) {\n\t\tissues(\n\t\t\tfirst: ${linearIssuesEtagMaxNumbers}\n\t\t\tincludeArchived: true\n\t\t\tfilter: { team: { key: { eq: $teamKey } }, number: { in: $numbers } }\n\t\t) {\n\t\t\tpageInfo {\n\t\t\t\thasNextPage\n\t\t\t}\n\t\t\tnodes {\n\t\t\t\tidentifier\n\t\t\t\tnumber\n\t\t\t\tupdatedAt\n\t\t\t\tarchivedAt\n\t\t\t\tstate {\n\t\t\t\t\ttype\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n`;\n\nexport type LinearIssueEtagNode = {\n\tidentifier: string;\n\tnumber: number;\n\tupdatedAt?: string | null;\n\tarchivedAt?: string | null;\n\tstate?: { type?: string | null } | null;\n};\n\ntype LinearIssuesEtagResponse = {\n\tdata?: {\n\t\tissues?: { pageInfo?: { hasNextPage?: boolean }; nodes?: LinearIssueEtagNode[] } | null;\n\t} | null;\n\terrors?: unknown[];\n};\n\n/**\n * The cheap check behind the batch issue read's etags: the change state of up to {@link linearIssuesEtagMaxNumbers}\n * issues of one team in ONE GraphQL request, where the full read sends provider-apis' `issue(id:)` once per issue.\n * Linear returns only the issues it found, in its own order, so a caller matches them by number, never by position.\n *\n * Sent as provider-apis sends every Linear request: the token as the whole `Authorization` header, with no scheme.\n * GraphQL `errors` reject the whole request, as provider-apis rejects them, but keep the response so a throttling\n * code is still classified as one. A next page also rejects it: with every number unique within a team, more\n * issues than were asked for means the filter was not applied as written.\n */\nexport async function requestLinearIssuesEtagFields(\n\trequest: ProviderRequestFunction,\n\taccessToken: string,\n\tteamKey: string,\n\tnumbers: readonly number[],\n): Promise<LinearIssueEtagNode[]> {\n\tconst response = await request<LinearIssuesEtagResponse | null>({\n\t\turl: linearGraphQLUrl,\n\t\tmethod: 'POST',\n\t\theaders: { Authorization: accessToken, 'Content-Type': 'application/json' },\n\t\tbody: JSON.stringify({ query: linearIssuesEtagQuery, variables: { teamKey: teamKey, numbers: numbers } }),\n\t});\n\n\tconst body = response.body;\n\tif (body?.errors != null) {\n\t\tconst error = new Error(`Linear GraphQL errors: ${JSON.stringify(body.errors)}`);\n\t\tObject.assign(error, { response: response });\n\t\tthrow error;\n\t}\n\n\tconst issues = body?.data?.issues;\n\tif (!Array.isArray(issues?.nodes)) throw new Error('Linear returned no issues');\n\tif (issues.pageInfo?.hasNextPage !== false) throw new Error('Linear returned more issues than were asked for');\n\n\treturn issues.nodes;\n}\n\n/**\n * A cheap-checked issue's change state, as the fields the full row's etag reads. The full read's row is provider-apis'\n * Linear issue normalizer (0.65.0) then `fromProviderIssue`, so this mirrors both:\n *\n * - provider-apis maps `state.type` to a category — `completed` to `DONE`, `canceled` and `duplicate` to `CANCELED`,\n * `started` to `IN_PROGRESS`, anything else (`backlog`, `unstarted`, `triage`) to `TO_DO`, and no state to none —\n * and sets `closedDate` from `archivedAt` (a falsy value reads as none);\n * - `fromProviderIssue` closes an issue that has a `closedDate` or a terminal category, so an archived issue is closed\n * whatever its state.\n *\n * Throws on a missing or unparseable `updatedAt`, so the target falls through to the full read rather than matching an\n * etag the full row would compute from a fallback date (`fromProviderIssue` falls back to `closedDate`, then\n * `createdDate`).\n */\nexport function toLinearIssueEtagFields(issue: LinearIssueEtagNode): IssueEtagFields {\n\tconst updatedDate = new Date(issue.updatedAt || Number.NaN);\n\tif (Number.isNaN(updatedDate.getTime())) {\n\t\tthrow new Error(`Linear returned no update time for ${issue.identifier}`);\n\t}\n\n\tconst type = issue.state?.type;\n\tconst closed = Boolean(issue.archivedAt) || type === 'completed' || type === 'canceled' || type === 'duplicate';\n\treturn { state: closed ? 'closed' : 'opened', updatedDate: updatedDate };\n}\n"],"mappings":";AAGA,MAAM,mBAAmB;;AAGzB,MAAa,6BAA6B;;;;;AAM1C,MAAM,wBAAwB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgD9B,eAAsB,8BACrB,SACA,aACA,SACA,SACiC;CACjC,MAAM,WAAW,MAAM,QAAyC;EAC/D,KAAK;EACL,QAAQ;EACR,SAAS;GAAE,eAAe;GAAa,gBAAgB;EAAmB;EAC1E,MAAM,KAAK,UAAU;GAAE,OAAO;GAAuB,WAAW;IAAW;IAAkB;GAAQ;EAAE,CAAC;CACzG,CAAC;CAED,MAAM,OAAO,SAAS;CACtB,IAAI,MAAM,UAAU,MAAM;EACzB,MAAM,wBAAQ,IAAI,MAAM,0BAA0B,KAAK,UAAU,KAAK,MAAM,GAAG;EAC/E,OAAO,OAAO,OAAO,EAAY,SAAS,CAAC;EAC3C,MAAM;CACP;CAEA,MAAM,SAAS,MAAM,MAAM;CAC3B,IAAI,CAAC,MAAM,QAAQ,QAAQ,KAAK,GAAG,MAAM,IAAI,MAAM,2BAA2B;CAC9E,IAAI,OAAO,UAAU,gBAAgB,OAAO,MAAM,IAAI,MAAM,iDAAiD;CAE7G,OAAO,OAAO;AACf;;;;;;;;;;;;;;;AAgBA,SAAgB,wBAAwB,OAA6C;CACpF,MAAM,cAAc,IAAI,KAAK,MAAM,aAAa,GAAU;CAC1D,IAAI,OAAO,MAAM,YAAY,QAAQ,CAAC,GACrC,MAAM,IAAI,MAAM,sCAAsC,MAAM,YAAY;CAGzE,MAAM,OAAO,MAAM,OAAO;CAE1B,OAAO;EAAE,OADM,QAAQ,MAAM,UAAU,KAAK,SAAS,eAAe,SAAS,cAAc,SAAS,cAC3E,WAAW;EAAuB;CAAY;AACxE"}
|
|
@@ -520,6 +520,17 @@ export type GetBitbucketServerPullRequestsForCurrentUserFn = (input: {
|
|
|
520
520
|
};
|
|
521
521
|
data: GitPullRequest[];
|
|
522
522
|
}>;
|
|
523
|
+
/**
|
|
524
|
+
* One page of a Jira issue search; `nextCursor` is the raw provider cursor, fed back verbatim. `metadata` is the
|
|
525
|
+
* SDK's: a page that dropped an issue it could not read reports it there as `partial` with a `provider` failure
|
|
526
|
+
* (GKDEV-3652).
|
|
527
|
+
*/
|
|
528
|
+
export type JiraIssuePage = {
|
|
529
|
+
data: ProviderIssue[];
|
|
530
|
+
hasMore: boolean;
|
|
531
|
+
nextCursor: string | undefined;
|
|
532
|
+
metadata?: CollectionMetadata;
|
|
533
|
+
} | undefined;
|
|
523
534
|
export type GetIssuesForProjectFn = Jira['getIssuesForProject'];
|
|
524
535
|
export type GetJiraServerCurrentUserFn = JiraServer['getCurrentUser'];
|
|
525
536
|
export type GetJiraServerProjectsFn = (options?: EnterpriseOptions) => Promise<{
|
|
@@ -710,7 +721,6 @@ export type ProvidersMetadata = Record<IntegrationIds, ProviderMetadata>;
|
|
|
710
721
|
export declare const providersMetadata: ProvidersMetadata;
|
|
711
722
|
export declare function getReasonsForUserIssue(issue: ProviderIssue, userLogin: string): string[];
|
|
712
723
|
export declare function toIssueShape(issue: ProviderIssue, provider: ProviderReference, options?: {
|
|
713
|
-
reliableStateCategory?: boolean;
|
|
714
724
|
projection?: IssueProjection;
|
|
715
725
|
}): IssueShape | undefined;
|
|
716
726
|
/**
|
|
@@ -2,9 +2,11 @@ import { equalsIgnoreCase } from "../../../utils/string.js";
|
|
|
2
2
|
import { gitSuffixRegex } from "../../../git/utils/remote.utils.js";
|
|
3
3
|
import { Issue } from "../../../git/models/issue.js";
|
|
4
4
|
import { PullRequest, PullRequestFilter } from "../../../git/models/pullRequest.js";
|
|
5
|
+
import { isTerminalIssueStateCategory } from "../../../git/utils/issue.utils.js";
|
|
5
6
|
import "../constants.js";
|
|
6
7
|
import { githubSearchResultLimit } from "../../git-github/api/config.js";
|
|
7
8
|
import { IssueFilter } from "../providerFilters.js";
|
|
9
|
+
import { isJiraIntegrationId } from "../utils/integration.utils.js";
|
|
8
10
|
import { fromProviderAccount, toProviderAccount } from "./accounts.js";
|
|
9
11
|
import { azurePullRequestSearchSorts, azureWorkItemSearchRelationships, azureWorkItemSearchResultLimit, azureWorkItemSearchSorts } from "./azure/search.js";
|
|
10
12
|
import { azureAccountWideIssueSorts, azureIssueSorts, githubAccountWideIssueSorts, githubIssueSorts, gitlabAccountWideIssueSorts, gitlabIssueSorts, jiraIssueSorts, linearIssueSorts, trelloIssueSorts } from "./issueSorts.js";
|
|
@@ -574,19 +576,17 @@ function getReasonsForUserIssue(issue, userLogin) {
|
|
|
574
576
|
function toIssueIdentifier(value) {
|
|
575
577
|
return String(value);
|
|
576
578
|
}
|
|
577
|
-
function toIssueProviderState(state
|
|
579
|
+
function toIssueProviderState(state) {
|
|
578
580
|
return state == null ? void 0 : {
|
|
579
581
|
id: state.id,
|
|
580
582
|
name: state.name,
|
|
581
583
|
color: state.color ?? void 0,
|
|
582
|
-
category:
|
|
584
|
+
category: state.category
|
|
583
585
|
};
|
|
584
586
|
}
|
|
585
587
|
function toIssueShape(issue, provider, options) {
|
|
586
588
|
if (issue.updatedDate == null || issue.url == null) return void 0;
|
|
587
|
-
const
|
|
588
|
-
const reliableStateCategory = !isJira || options?.reliableStateCategory === true;
|
|
589
|
-
const closed = issue.closedDate != null || issue.state?.category === "DONE" && reliableStateCategory;
|
|
589
|
+
const closed = issue.closedDate != null || isTerminalIssueStateCategory(issue.state?.category);
|
|
590
590
|
return {
|
|
591
591
|
type: "issue",
|
|
592
592
|
provider,
|
|
@@ -599,7 +599,7 @@ function toIssueShape(issue, provider, options) {
|
|
|
599
599
|
closedDate: issue.closedDate ?? void 0,
|
|
600
600
|
closed,
|
|
601
601
|
state: closed ? "closed" : "opened",
|
|
602
|
-
providerState: toIssueProviderState(issue.state
|
|
602
|
+
providerState: toIssueProviderState(issue.state),
|
|
603
603
|
author: {
|
|
604
604
|
id: issue.author?.id ?? "",
|
|
605
605
|
name: issue.author?.name ?? void 0,
|
|
@@ -630,7 +630,7 @@ function toIssueShape(issue, provider, options) {
|
|
|
630
630
|
commentsCount: issue.commentCount ?? void 0,
|
|
631
631
|
thumbsUpCount: issue.upvoteCount ?? void 0,
|
|
632
632
|
body: issue.description ?? void 0,
|
|
633
|
-
bodyFormat:
|
|
633
|
+
bodyFormat: isJiraIntegrationId(provider.id) ? "jira-wiki" : void 0,
|
|
634
634
|
issueType: issue.type ?? void 0,
|
|
635
635
|
iterations: toIssueIterations(issue),
|
|
636
636
|
...options?.projection != null ? { projection: options.projection } : {}
|
|
@@ -956,7 +956,7 @@ function authoredByCurrentAccount(author, provider, currentAccount) {
|
|
|
956
956
|
}
|
|
957
957
|
function fromProviderIssue(issue, integration, options) {
|
|
958
958
|
const identifier = toIssueIdentifier(issue.number);
|
|
959
|
-
const closed = issue.closedDate != null || issue.state?.category
|
|
959
|
+
const closed = issue.closedDate != null || isTerminalIssueStateCategory(issue.state?.category);
|
|
960
960
|
return new Issue(integration, identifier, issue.graphQLId ?? issue.id, issue.title, issue.url ?? "", issue.createdDate, issue.updatedDate ?? issue.closedDate ?? issue.createdDate, closed, closed ? "closed" : "opened", fromProviderAccount(issue.author), issue.assignees?.map(fromProviderAccount) ?? void 0, issue.repository != null ? {
|
|
961
961
|
owner: issue.repository.owner.login ?? "",
|
|
962
962
|
repo: issue.repository.name,
|
|
@@ -974,7 +974,7 @@ function fromProviderIssue(issue, integration, options) {
|
|
|
974
974
|
name: issue.project.name,
|
|
975
975
|
resourceId: issue.project.resourceId,
|
|
976
976
|
resourceName: issue.project.namespace
|
|
977
|
-
} : void 0, identifier, issue.type ?? void 0, toIssueProviderState(issue.state
|
|
977
|
+
} : void 0, identifier, issue.type ?? void 0, toIssueProviderState(issue.state), integration.id === "jira" ? "jira-wiki" : void 0, toIssueIterations(issue), options?.projection);
|
|
978
978
|
}
|
|
979
979
|
function toProviderPullRequestWithUniqueId(pr) {
|
|
980
980
|
const { reviews, mergeableState, ...providerPr } = toProviderPullRequest(pr);
|