@gitkraken/core-gitlens 0.9.3 → 0.9.4
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 +13 -1
- package/dist/plus/integrations/integrationService.d.ts +6 -1
- package/dist/plus/integrations/integrationService.js.map +1 -1
- package/dist/plus/integrations/manager.d.ts +6 -1
- package/dist/plus/integrations/models/issueReads.d.ts +14 -0
- package/dist/plus/integrations/providers/bitbucket/models.d.ts +5 -5
- package/dist/plus/integrations/providers/bitbucket/models.js +2 -2
- package/dist/plus/integrations/providers/bitbucket/models.js.map +1 -1
- package/dist/plus/integrations/providers/issueSorts.d.ts +0 -1
- package/dist/plus/integrations/providers/jira-server.d.ts +2 -2
- package/dist/plus/integrations/providers/jira-server.js +9 -3
- package/dist/plus/integrations/providers/jira-server.js.map +1 -1
- package/dist/plus/integrations/providers/jira.d.ts +6 -1
- package/dist/plus/integrations/providers/jira.js +19 -3
- package/dist/plus/integrations/providers/jira.js.map +1 -1
- package/dist/plus/integrations/providers/linear.js +8 -2
- package/dist/plus/integrations/providers/linear.js.map +1 -1
- package/dist/plus/integrations/providers/models.d.ts +19 -1
- package/dist/plus/integrations/providers/models.js +14 -5
- package/dist/plus/integrations/providers/models.js.map +1 -1
- package/dist/plus/integrations/providers/providersApi.d.ts +8 -1
- package/dist/plus/integrations/providers/providersApi.js +3 -0
- package/dist/plus/integrations/providers/providersApi.js.map +1 -1
- package/dist/plus/integrations/providers/trello.js +1 -0
- package/dist/plus/integrations/providers/trello.js.map +1 -1
- package/dist/plus/integrations/reads/cursors.d.ts +8 -0
- package/dist/plus/integrations/reads/cursors.js +5 -3
- package/dist/plus/integrations/reads/cursors.js.map +1 -1
- package/dist/plus/integrations/reads/drains.d.ts +0 -1
- package/dist/plus/integrations/reads/filters.d.ts +8 -0
- package/dist/plus/integrations/reads/filters.js +2 -1
- package/dist/plus/integrations/reads/filters.js.map +1 -1
- package/dist/plus/integrations/reads/issueTracker.d.ts +9 -1
- package/dist/plus/integrations/reads/issueTracker.js +20 -3
- package/dist/plus/integrations/reads/issueTracker.js.map +1 -1
- package/docs/integrations.md +4 -0
- package/package.json +2 -2
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Event } from "../../utils/event.js";
|
|
2
|
-
import { IssueSearchCriteria, IssueShape, IssueSorting } from "../../git/models/issue.js";
|
|
2
|
+
import { IssueSearchCriteria, IssueShape, IssueSorting, IssueStateFilter } from "../../git/models/issue.js";
|
|
3
3
|
import { PullRequestFilter, PullRequestSearchCriteria, PullRequestShape, PullRequestStateFilter } from "../../git/models/pullRequest.js";
|
|
4
4
|
import { ConfiguredIntegrationDescriptor } from "./authentication/models.js";
|
|
5
5
|
import { IntegrationIds } from "./constants.js";
|
|
@@ -751,6 +751,11 @@ export interface IntegrationManager {
|
|
|
751
751
|
* full, so which projects a round covers doesn't depend on how their issues are ordered.
|
|
752
752
|
*/
|
|
753
753
|
sort?: IssueSorting;
|
|
754
|
+
/**
|
|
755
|
+
* Which issue states to read. Omitted reads open issues. `'closed'`/`'all'` need
|
|
756
|
+
* `getSupportedFilters().issueStates` and are refused otherwise (warning + `fetchFailed`).
|
|
757
|
+
*/
|
|
758
|
+
state?: IssueStateFilter;
|
|
754
759
|
forceSync?: boolean;
|
|
755
760
|
page?: number;
|
|
756
761
|
/**
|
|
@@ -92,6 +92,13 @@ export type IssuesForProjectOptions = {
|
|
|
92
92
|
* no normalized issue carries; this option orders one project's query.
|
|
93
93
|
*/
|
|
94
94
|
sort?: IssueSorting;
|
|
95
|
+
/**
|
|
96
|
+
* Which issue states to read. Omitted reads open issues only, which is every tracker's own default.
|
|
97
|
+
*
|
|
98
|
+
* Validated by the facade against `ProviderMetadata.supportsIssueStates`. A tracker that can't express a state
|
|
99
|
+
* (Trello) refuses anything but `'open'` rather than serving its open cards as if they were the closed ones.
|
|
100
|
+
*/
|
|
101
|
+
state?: IssueStateFilter;
|
|
95
102
|
};
|
|
96
103
|
export type ProjectIssuesDrain = {
|
|
97
104
|
values: IssueShape[];
|
|
@@ -168,6 +175,13 @@ export type SearchMyIssuesOptions = {
|
|
|
168
175
|
*/
|
|
169
176
|
org?: string;
|
|
170
177
|
project?: string;
|
|
178
|
+
/**
|
|
179
|
+
* Which issue states to read. Omitted reads open issues only.
|
|
180
|
+
*
|
|
181
|
+
* Honored by the issue trackers that declare `ProviderMetadata.supportsIssueStates` (Jira, Jira Data Center,
|
|
182
|
+
* Linear). The git hosts' account-wide reads ignore it and keep their own open-only default.
|
|
183
|
+
*/
|
|
184
|
+
state?: IssueStateFilter;
|
|
171
185
|
/**
|
|
172
186
|
* How the provider should order the read, as `field:direction`.
|
|
173
187
|
*
|
|
@@ -19,15 +19,15 @@ interface BitbucketLink {
|
|
|
19
19
|
name?: string;
|
|
20
20
|
}
|
|
21
21
|
interface BitbucketUser {
|
|
22
|
-
type: 'user';
|
|
22
|
+
type: 'user' | 'app_user';
|
|
23
23
|
uuid: string;
|
|
24
24
|
display_name: string;
|
|
25
25
|
account_id?: string;
|
|
26
26
|
nickname?: string;
|
|
27
|
-
links
|
|
28
|
-
self
|
|
29
|
-
avatar
|
|
30
|
-
html
|
|
27
|
+
links?: {
|
|
28
|
+
self?: BitbucketLink;
|
|
29
|
+
avatar?: BitbucketLink;
|
|
30
|
+
html?: BitbucketLink;
|
|
31
31
|
};
|
|
32
32
|
}
|
|
33
33
|
interface BitbucketWorkspace {
|
|
@@ -39,10 +39,10 @@ function isClosedBitbucketIssueState(state) {
|
|
|
39
39
|
}
|
|
40
40
|
function fromBitbucketUser(user) {
|
|
41
41
|
return {
|
|
42
|
-
avatarUrl: user.links
|
|
42
|
+
avatarUrl: user.links?.avatar?.href,
|
|
43
43
|
name: user.display_name,
|
|
44
44
|
username: user.nickname,
|
|
45
|
-
url: user.links
|
|
45
|
+
url: user.links?.html?.href,
|
|
46
46
|
id: user.uuid
|
|
47
47
|
};
|
|
48
48
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"models.js","names":[],"sources":["../../../../../../plus/integrations/src/providers/bitbucket/models.ts"],"sourcesContent":["import type { IssueRepository } from '@gitlens/git/models/issue.js';\nimport { Issue, RepositoryAccessLevel } from '@gitlens/git/models/issue.js';\nimport type { IssueOrPullRequestState } from '@gitlens/git/models/issueOrPullRequest.js';\nimport type { PullRequestMember, PullRequestReviewer } from '@gitlens/git/models/pullRequest.js';\nimport {\n\tPullRequest,\n\tPullRequestMergeableState,\n\tPullRequestReviewDecision,\n\tPullRequestReviewState,\n} from '@gitlens/git/models/pullRequest.js';\nimport type { Provider } from '@gitlens/git/models/remoteProvider.js';\nimport type { ResourceDescriptor } from '@gitlens/git/models/resourceDescriptor.js';\n\nexport interface BitbucketRepositoryDescriptor extends ResourceDescriptor {\n\towner: string;\n\tname: string;\n}\n\nexport interface BitbucketWorkspaceDescriptor extends ResourceDescriptor {\n\tid: string;\n\tname: string;\n\tslug: string;\n}\n\nexport type BitbucketPullRequestState = 'OPEN' | 'DECLINED' | 'MERGED' | 'SUPERSEDED';\n\ninterface BitbucketLink {\n\thref: string;\n\tname?: string;\n}\n\ninterface BitbucketUser {\n\ttype: 'user';\n\tuuid: string;\n\tdisplay_name: string;\n\taccount_id?: string;\n\tnickname?: string;\n\tlinks: {\n\t\tself: BitbucketLink;\n\t\tavatar: BitbucketLink;\n\t\thtml: BitbucketLink;\n\t};\n}\n\ninterface BitbucketWorkspace {\n\ttype: 'workspace';\n\tuuid: string;\n\tname: string;\n\tslug: string;\n\tlinks: {\n\t\tself: BitbucketLink;\n\t\thtml: BitbucketLink;\n\t\tavatar: BitbucketLink;\n\t};\n}\n\ninterface BitbucketProject {\n\ttype: 'project';\n\tkey: string;\n\tuuid: string;\n\tname: string;\n\tlinks: {\n\t\tself: BitbucketLink;\n\t\thtml: BitbucketLink;\n\t\tavatar: BitbucketLink;\n\t};\n}\n\ninterface BitbucketPullRequestParticipant {\n\ttype: 'participant';\n\tuser: BitbucketUser;\n\trole: 'PARTICIPANT' | 'REVIEWER';\n\tapproved: boolean;\n\tstate: null | 'approved' | 'changes_requested';\n\tparticipated_on: null | string;\n}\n\nexport interface BitbucketRepository {\n\ttype: 'repository';\n\tuuid: string;\n\tfull_name: string;\n\tname: string;\n\tslug: string;\n\tdescription?: string;\n\tis_private: boolean;\n\tparent: null | BitbucketRepository;\n\tscm: 'git';\n\towner: BitbucketUser;\n\tworkspace: BitbucketWorkspace;\n\tproject: BitbucketProject;\n\tcreated_on: string;\n\tupdated_on: string;\n\tsize: number;\n\tlanguage: string;\n\thas_issues: boolean;\n\thas_wiki: boolean;\n\tfork_policy: 'allow_forks' | 'no_public_forks' | 'no_forks';\n\twebsite: string;\n\tmainbranch?: BitbucketBranch;\n\tlinks: {\n\t\tself: BitbucketLink;\n\t\thtml: BitbucketLink;\n\t\tavatar: BitbucketLink;\n\t};\n}\n\ninterface BitbucketCommitAuthor {\n\ttype: 'author';\n\traw: string;\n\tuser: BitbucketUser;\n}\n\ntype BitbucketMergeStrategy =\n\t| 'merge_commit'\n\t| 'squash'\n\t| 'fast_forward'\n\t| 'squash_fast_forward'\n\t| 'rebase_fast_forward'\n\t| 'rebase_merge';\n\ninterface BitbucketBranch {\n\tname: string;\n\tmerge_strategies?: BitbucketMergeStrategy[];\n\tdefault_merge_strategy?: BitbucketMergeStrategy;\n}\n\n// It parses a raw author sitring like \"Sergei Shmakov GK <sergei.shmakov@gitkraken.com>\" to name and email\nconst parseRawBitbucketAuthorRegex = /^(.*) <(.*)>$/;\nexport function parseRawBitbucketAuthor(raw: string): { name: string; email: string } {\n\tconst match = raw.match(parseRawBitbucketAuthorRegex);\n\tif (match) {\n\t\treturn { name: match[1], email: match[2] };\n\t}\n\treturn { name: raw, email: '' };\n}\n\nexport interface BitbucketCommit extends BitbucketBriefCommit {\n\tauthor: BitbucketCommitAuthor;\n\tdate: string;\n\tlinks: {\n\t\tapprove: BitbucketLink;\n\t\tcomments: BitbucketLink;\n\t\tdiff: BitbucketLink;\n\t\thtml: BitbucketLink;\n\t\tself: BitbucketLink;\n\t\tstatuses: BitbucketLink;\n\t};\n\tmessage: string;\n\tparents: BitbucketBriefCommit[];\n\tparticipants: BitbucketPullRequestParticipant[];\n\trendered: {\n\t\tmessage: string;\n\t};\n\trepository: BitbucketRepository;\n\tsummary: {\n\t\ttype: 'rendered';\n\t\traw: string;\n\t\tmarkup: string;\n\t\thtml: string;\n\t};\n}\n\ninterface BitbucketBriefCommit {\n\ttype: 'commit';\n\thash: string;\n\tlinks: {\n\t\tself: BitbucketLink;\n\t\thtml: BitbucketLink;\n\t};\n}\n\nexport type BitbucketIssueState =\n\t| 'submitted'\n\t| 'new'\n\t| 'open'\n\t| 'resolved'\n\t| 'on hold'\n\t| 'invalid'\n\t| 'duplicate'\n\t| 'wontfix'\n\t| 'closed';\n\nexport interface BitbucketPullRequest {\n\ttype: 'pullrequest';\n\tid: number;\n\ttitle: string;\n\tdescription: string;\n\tstate: BitbucketPullRequestState;\n\tmerge_commit: null | BitbucketBriefCommit;\n\tcomment_count: number;\n\ttask_count: number;\n\tclose_source_branch: boolean;\n\tclosed_by: BitbucketUser | null;\n\tauthor: BitbucketUser;\n\treason: string;\n\tcreated_on: string;\n\tupdated_on: string;\n\tdestination: {\n\t\tbranch: BitbucketBranch;\n\t\tcommit: BitbucketBriefCommit;\n\t\trepository: BitbucketRepository;\n\t};\n\tsource: {\n\t\tbranch: BitbucketBranch;\n\t\tcommit: BitbucketBriefCommit;\n\t\trepository: BitbucketRepository;\n\t};\n\tsummary: {\n\t\ttype: 'rendered';\n\t\traw: string;\n\t\tmarkup: string;\n\t\thtml: string;\n\t};\n\treviewers?: BitbucketUser[];\n\tparticipants?: BitbucketPullRequestParticipant[];\n\tlinks: {\n\t\tself: BitbucketLink;\n\t\thtml: BitbucketLink;\n\t\tcommits: BitbucketLink;\n\t\tapprove: BitbucketLink;\n\t\t'request-changes': BitbucketLink;\n\t\tdiff: BitbucketLink;\n\t\tdiffstat: BitbucketLink;\n\t\tcomments: BitbucketLink;\n\t\tactivity: BitbucketLink;\n\t\tmerge: BitbucketLink;\n\t\tdecline: BitbucketLink;\n\t\tstatuses: BitbucketLink;\n\t};\n}\n\nexport interface BitbucketIssue {\n\ttype: string;\n\tid: number;\n\ttitle: string;\n\treporter: BitbucketUser;\n\tassignee?: BitbucketUser;\n\tstate: BitbucketIssueState;\n\tcreated_on: string;\n\tupdated_on: string;\n\trepository: BitbucketRepository;\n\tvotes?: number;\n\tcontent: {\n\t\traw: string;\n\t\tmarkup: string;\n\t\thtml: string;\n\t};\n\tlinks: {\n\t\tself: BitbucketLink;\n\t\thtml: BitbucketLink;\n\t\tcomments: BitbucketLink;\n\t\tattachments: BitbucketLink;\n\t\twatch: BitbucketLink;\n\t\tvote: BitbucketLink;\n\t};\n}\n\nexport function bitbucketPullRequestStateToState(state: BitbucketPullRequestState): IssueOrPullRequestState {\n\tswitch (state) {\n\t\tcase 'DECLINED':\n\t\tcase 'SUPERSEDED':\n\t\t\treturn 'closed';\n\t\tcase 'MERGED':\n\t\t\treturn 'merged';\n\t\tcase 'OPEN':\n\t\tdefault:\n\t\t\treturn 'opened';\n\t}\n}\n\nexport function bitbucketIssueStateToState(state: BitbucketIssueState): IssueOrPullRequestState {\n\tswitch (state) {\n\t\tcase 'resolved':\n\t\tcase 'invalid':\n\t\tcase 'duplicate':\n\t\tcase 'wontfix':\n\t\tcase 'closed':\n\t\t\treturn 'closed';\n\t\tcase 'submitted':\n\t\tcase 'new':\n\t\tcase 'open':\n\t\tcase 'on hold':\n\t\tdefault:\n\t\t\treturn 'opened';\n\t}\n}\n\nexport function isClosedBitbucketPullRequestState(state: BitbucketPullRequestState): boolean {\n\treturn bitbucketPullRequestStateToState(state) !== 'opened';\n}\n\nexport function isClosedBitbucketIssueState(state: BitbucketIssueState): boolean {\n\treturn bitbucketIssueStateToState(state) !== 'opened';\n}\n\nexport function fromBitbucketUser(user: BitbucketUser): PullRequestMember {\n\treturn {\n\t\tavatarUrl: user.links.avatar.href,\n\t\tname: user.display_name,\n\t\tusername: user.nickname,\n\t\turl: user.links.html.href,\n\t\tid: user.uuid,\n\t};\n}\n\nexport function fromBitbucketParticipantToReviewer(\n\tprt: BitbucketPullRequestParticipant,\n\tclosedBy: BitbucketUser | null,\n\tprState: BitbucketPullRequestState,\n): PullRequestReviewer {\n\treturn {\n\t\treviewer: fromBitbucketUser(prt.user),\n\t\tstate: prt.approved\n\t\t\t? PullRequestReviewState.Approved\n\t\t\t: prt.state === 'changes_requested'\n\t\t\t\t? PullRequestReviewState.ChangesRequested\n\t\t\t\t: prt.participated_on != null\n\t\t\t\t\t? PullRequestReviewState.Commented\n\t\t\t\t\t: prt.user.uuid === closedBy?.uuid && prState === 'DECLINED'\n\t\t\t\t\t\t? PullRequestReviewState.Dismissed\n\t\t\t\t\t\t: PullRequestReviewState.ReviewRequested,\n\t};\n}\n\nfunction getBitbucketReviewDecision(pr: BitbucketPullRequest): PullRequestReviewDecision | undefined {\n\tif (!pr.participants?.length && pr.reviewers?.length) {\n\t\treturn PullRequestReviewDecision.ReviewRequired;\n\t}\n\tif (!pr.participants) {\n\t\treturn undefined;\n\t}\n\n\tlet hasReviews = false;\n\tlet hasChangeRequests = false;\n\tlet hasApprovals = false;\n\tfor (const prt of pr.participants) {\n\t\tif (prt.participated_on != null) {\n\t\t\thasReviews = true;\n\t\t}\n\t\tif (prt.approved) {\n\t\t\thasApprovals = true;\n\t\t}\n\t\tif (prt.state === 'changes_requested') {\n\t\t\thasChangeRequests = true;\n\t\t}\n\t}\n\tif (hasChangeRequests) return PullRequestReviewDecision.ChangesRequested;\n\tif (hasApprovals) return PullRequestReviewDecision.Approved;\n\tif (hasReviews) return undefined; // not approved, not rejected, but reviewed\n\treturn PullRequestReviewDecision.ReviewRequired; // nobody has reviewed yet.\n}\n\nfunction fromBitbucketRepository(repo: BitbucketRepository): IssueRepository {\n\treturn {\n\t\towner: repo.full_name.split('/')[0],\n\t\trepo: repo.name,\n\t\tid: repo.uuid,\n\t\t// TODO: Remove this assumption once actual access level is available\n\t\taccessLevel: RepositoryAccessLevel.Write,\n\t};\n}\n\nexport function fromBitbucketIssue(issue: BitbucketIssue, provider: Provider): Issue {\n\treturn new Issue(\n\t\tprovider,\n\t\tissue.id.toString(),\n\t\tissue.id.toString(),\n\t\tissue.title,\n\t\tissue.links.html.href,\n\t\tnew Date(issue.created_on),\n\t\tnew Date(issue.updated_on),\n\t\tisClosedBitbucketIssueState(issue.state),\n\t\tbitbucketIssueStateToState(issue.state),\n\t\tfromBitbucketUser(issue.reporter),\n\t\tissue.assignee ? [fromBitbucketUser(issue.assignee)] : [],\n\t\tfromBitbucketRepository(issue.repository),\n\t\tundefined, // closedDate\n\t\tundefined, // labels\n\t\tundefined, // commentsCount\n\t\tissue.votes, // thumbsUpCount\n\t\tissue.content.html, // body\n\t\t!issue.repository?.project\n\t\t\t? undefined\n\t\t\t: {\n\t\t\t\t\tid: issue.repository.project.uuid,\n\t\t\t\t\tname: issue.repository.project.name,\n\t\t\t\t\tresourceId: issue.repository.project.uuid,\n\t\t\t\t\tresourceName: issue.repository.project.name,\n\t\t\t\t},\n\t);\n}\n\nexport function fromBitbucketPullRequest(\n\tpr: BitbucketPullRequest,\n\tprovider: Provider,\n\toptions?: { currentAccount?: { id: string; username?: string } },\n): PullRequest {\n\tconst author = fromBitbucketUser(pr.author);\n\treturn new PullRequest(\n\t\tprovider,\n\t\tauthor,\n\t\tpr.id.toString(),\n\t\tpr.id.toString(),\n\t\tpr.title,\n\t\tpr.links.html.href,\n\t\tfromBitbucketRepository(pr.destination.repository),\n\t\tbitbucketPullRequestStateToState(pr.state),\n\t\tnew Date(pr.created_on),\n\t\tnew Date(pr.updated_on),\n\t\tpr.closed_by ? new Date(pr.updated_on) : undefined,\n\t\tpr.state === 'MERGED' ? new Date(pr.updated_on) : undefined,\n\t\t// TODO: Remove this assumption once actual mergeable state is available\n\t\tPullRequestMergeableState.Mergeable, // mergeableState\n\t\tundefined, // viewerCanUpdate\n\t\t{\n\t\t\tbase: {\n\t\t\t\tbranch: pr.destination.branch.name,\n\t\t\t\tsha: pr.destination.commit.hash,\n\t\t\t\trepo: pr.destination.repository.name,\n\t\t\t\towner: pr.destination.repository.full_name.split('/')[0],\n\t\t\t\texists: true,\n\t\t\t\turl: pr.destination.repository.links.html.href,\n\t\t\t},\n\t\t\thead: {\n\t\t\t\tbranch: pr.source.branch.name,\n\t\t\t\tsha: pr.source.commit.hash,\n\t\t\t\trepo: pr.source.repository.name,\n\t\t\t\towner: pr.source.repository.full_name.split('/')[0],\n\t\t\t\texists: true,\n\t\t\t\turl: pr.source.repository.links.html.href,\n\t\t\t},\n\t\t\tisCrossRepository: pr.source.repository.uuid !== pr.destination.repository.uuid,\n\t\t},\n\t\tundefined, // isDraft\n\t\tundefined, // additions\n\t\tundefined, // deletions\n\t\tundefined, // commentsCount\n\t\tundefined, // thumbsCount\n\t\tgetBitbucketReviewDecision(pr),\n\t\tpr.participants // reviewRequests:PullRequestReviewer[]\n\t\t\t?.filter(prt => prt.role === 'REVIEWER')\n\t\t\t.map(prt => fromBitbucketParticipantToReviewer(prt, pr.closed_by, pr.state))\n\t\t\t.filter(rv => rv.state === PullRequestReviewState.ReviewRequested),\n\t\tpr.participants // latestReviews:PullRequestReviewer[]\n\t\t\t?.filter(prt => prt.participated_on != null)\n\t\t\t.map(prt => fromBitbucketParticipantToReviewer(prt, pr.closed_by, pr.state)),\n\t\tundefined, // assignees:PullRequestMember[] -- it looks like there is no such thing as assignees on Bitbucket\n\t\tundefined, // PullRequestStatusCheckRollupState\n\t\tundefined, // IssueProject\n\t\tundefined, // version\n\t\tundefined, // commitCount\n\t\tundefined, // stack\n\t\tundefined, // filesChanged\n\t\tpr.description ?? undefined,\n\t\tpr.id,\n\t\toptions?.currentAccount != null ? author.id === options.currentAccount.id : undefined,\n\t);\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"models.js","names":[],"sources":["../../../../../../plus/integrations/src/providers/bitbucket/models.ts"],"sourcesContent":["import type { IssueRepository } from '@gitlens/git/models/issue.js';\nimport { Issue, RepositoryAccessLevel } from '@gitlens/git/models/issue.js';\nimport type { IssueOrPullRequestState } from '@gitlens/git/models/issueOrPullRequest.js';\nimport type { PullRequestMember, PullRequestReviewer } from '@gitlens/git/models/pullRequest.js';\nimport {\n\tPullRequest,\n\tPullRequestMergeableState,\n\tPullRequestReviewDecision,\n\tPullRequestReviewState,\n} from '@gitlens/git/models/pullRequest.js';\nimport type { Provider } from '@gitlens/git/models/remoteProvider.js';\nimport type { ResourceDescriptor } from '@gitlens/git/models/resourceDescriptor.js';\n\nexport interface BitbucketRepositoryDescriptor extends ResourceDescriptor {\n\towner: string;\n\tname: string;\n}\n\nexport interface BitbucketWorkspaceDescriptor extends ResourceDescriptor {\n\tid: string;\n\tname: string;\n\tslug: string;\n}\n\nexport type BitbucketPullRequestState = 'OPEN' | 'DECLINED' | 'MERGED' | 'SUPERSEDED';\n\ninterface BitbucketLink {\n\thref: string;\n\tname?: string;\n}\n\n// Bitbucket's `account` schema: a person (`user`) or an app/bot (`app_user`, e.g. a review bot on a PR).\n// An `app_user` has no `links.html` and no `nickname`, and the schema requires none of the links.\ninterface BitbucketUser {\n\ttype: 'user' | 'app_user';\n\tuuid: string;\n\tdisplay_name: string;\n\taccount_id?: string;\n\tnickname?: string;\n\tlinks?: {\n\t\tself?: BitbucketLink;\n\t\tavatar?: BitbucketLink;\n\t\thtml?: BitbucketLink;\n\t};\n}\n\ninterface BitbucketWorkspace {\n\ttype: 'workspace';\n\tuuid: string;\n\tname: string;\n\tslug: string;\n\tlinks: {\n\t\tself: BitbucketLink;\n\t\thtml: BitbucketLink;\n\t\tavatar: BitbucketLink;\n\t};\n}\n\ninterface BitbucketProject {\n\ttype: 'project';\n\tkey: string;\n\tuuid: string;\n\tname: string;\n\tlinks: {\n\t\tself: BitbucketLink;\n\t\thtml: BitbucketLink;\n\t\tavatar: BitbucketLink;\n\t};\n}\n\ninterface BitbucketPullRequestParticipant {\n\ttype: 'participant';\n\tuser: BitbucketUser;\n\trole: 'PARTICIPANT' | 'REVIEWER';\n\tapproved: boolean;\n\tstate: null | 'approved' | 'changes_requested';\n\tparticipated_on: null | string;\n}\n\nexport interface BitbucketRepository {\n\ttype: 'repository';\n\tuuid: string;\n\tfull_name: string;\n\tname: string;\n\tslug: string;\n\tdescription?: string;\n\tis_private: boolean;\n\tparent: null | BitbucketRepository;\n\tscm: 'git';\n\towner: BitbucketUser;\n\tworkspace: BitbucketWorkspace;\n\tproject: BitbucketProject;\n\tcreated_on: string;\n\tupdated_on: string;\n\tsize: number;\n\tlanguage: string;\n\thas_issues: boolean;\n\thas_wiki: boolean;\n\tfork_policy: 'allow_forks' | 'no_public_forks' | 'no_forks';\n\twebsite: string;\n\tmainbranch?: BitbucketBranch;\n\tlinks: {\n\t\tself: BitbucketLink;\n\t\thtml: BitbucketLink;\n\t\tavatar: BitbucketLink;\n\t};\n}\n\ninterface BitbucketCommitAuthor {\n\ttype: 'author';\n\traw: string;\n\tuser: BitbucketUser;\n}\n\ntype BitbucketMergeStrategy =\n\t| 'merge_commit'\n\t| 'squash'\n\t| 'fast_forward'\n\t| 'squash_fast_forward'\n\t| 'rebase_fast_forward'\n\t| 'rebase_merge';\n\ninterface BitbucketBranch {\n\tname: string;\n\tmerge_strategies?: BitbucketMergeStrategy[];\n\tdefault_merge_strategy?: BitbucketMergeStrategy;\n}\n\n// It parses a raw author sitring like \"Sergei Shmakov GK <sergei.shmakov@gitkraken.com>\" to name and email\nconst parseRawBitbucketAuthorRegex = /^(.*) <(.*)>$/;\nexport function parseRawBitbucketAuthor(raw: string): { name: string; email: string } {\n\tconst match = raw.match(parseRawBitbucketAuthorRegex);\n\tif (match) {\n\t\treturn { name: match[1], email: match[2] };\n\t}\n\treturn { name: raw, email: '' };\n}\n\nexport interface BitbucketCommit extends BitbucketBriefCommit {\n\tauthor: BitbucketCommitAuthor;\n\tdate: string;\n\tlinks: {\n\t\tapprove: BitbucketLink;\n\t\tcomments: BitbucketLink;\n\t\tdiff: BitbucketLink;\n\t\thtml: BitbucketLink;\n\t\tself: BitbucketLink;\n\t\tstatuses: BitbucketLink;\n\t};\n\tmessage: string;\n\tparents: BitbucketBriefCommit[];\n\tparticipants: BitbucketPullRequestParticipant[];\n\trendered: {\n\t\tmessage: string;\n\t};\n\trepository: BitbucketRepository;\n\tsummary: {\n\t\ttype: 'rendered';\n\t\traw: string;\n\t\tmarkup: string;\n\t\thtml: string;\n\t};\n}\n\ninterface BitbucketBriefCommit {\n\ttype: 'commit';\n\thash: string;\n\tlinks: {\n\t\tself: BitbucketLink;\n\t\thtml: BitbucketLink;\n\t};\n}\n\nexport type BitbucketIssueState =\n\t| 'submitted'\n\t| 'new'\n\t| 'open'\n\t| 'resolved'\n\t| 'on hold'\n\t| 'invalid'\n\t| 'duplicate'\n\t| 'wontfix'\n\t| 'closed';\n\nexport interface BitbucketPullRequest {\n\ttype: 'pullrequest';\n\tid: number;\n\ttitle: string;\n\tdescription: string;\n\tstate: BitbucketPullRequestState;\n\tmerge_commit: null | BitbucketBriefCommit;\n\tcomment_count: number;\n\ttask_count: number;\n\tclose_source_branch: boolean;\n\tclosed_by: BitbucketUser | null;\n\tauthor: BitbucketUser;\n\treason: string;\n\tcreated_on: string;\n\tupdated_on: string;\n\tdestination: {\n\t\tbranch: BitbucketBranch;\n\t\tcommit: BitbucketBriefCommit;\n\t\trepository: BitbucketRepository;\n\t};\n\tsource: {\n\t\tbranch: BitbucketBranch;\n\t\tcommit: BitbucketBriefCommit;\n\t\trepository: BitbucketRepository;\n\t};\n\tsummary: {\n\t\ttype: 'rendered';\n\t\traw: string;\n\t\tmarkup: string;\n\t\thtml: string;\n\t};\n\treviewers?: BitbucketUser[];\n\tparticipants?: BitbucketPullRequestParticipant[];\n\tlinks: {\n\t\tself: BitbucketLink;\n\t\thtml: BitbucketLink;\n\t\tcommits: BitbucketLink;\n\t\tapprove: BitbucketLink;\n\t\t'request-changes': BitbucketLink;\n\t\tdiff: BitbucketLink;\n\t\tdiffstat: BitbucketLink;\n\t\tcomments: BitbucketLink;\n\t\tactivity: BitbucketLink;\n\t\tmerge: BitbucketLink;\n\t\tdecline: BitbucketLink;\n\t\tstatuses: BitbucketLink;\n\t};\n}\n\nexport interface BitbucketIssue {\n\ttype: string;\n\tid: number;\n\ttitle: string;\n\treporter: BitbucketUser;\n\tassignee?: BitbucketUser;\n\tstate: BitbucketIssueState;\n\tcreated_on: string;\n\tupdated_on: string;\n\trepository: BitbucketRepository;\n\tvotes?: number;\n\tcontent: {\n\t\traw: string;\n\t\tmarkup: string;\n\t\thtml: string;\n\t};\n\tlinks: {\n\t\tself: BitbucketLink;\n\t\thtml: BitbucketLink;\n\t\tcomments: BitbucketLink;\n\t\tattachments: BitbucketLink;\n\t\twatch: BitbucketLink;\n\t\tvote: BitbucketLink;\n\t};\n}\n\nexport function bitbucketPullRequestStateToState(state: BitbucketPullRequestState): IssueOrPullRequestState {\n\tswitch (state) {\n\t\tcase 'DECLINED':\n\t\tcase 'SUPERSEDED':\n\t\t\treturn 'closed';\n\t\tcase 'MERGED':\n\t\t\treturn 'merged';\n\t\tcase 'OPEN':\n\t\tdefault:\n\t\t\treturn 'opened';\n\t}\n}\n\nexport function bitbucketIssueStateToState(state: BitbucketIssueState): IssueOrPullRequestState {\n\tswitch (state) {\n\t\tcase 'resolved':\n\t\tcase 'invalid':\n\t\tcase 'duplicate':\n\t\tcase 'wontfix':\n\t\tcase 'closed':\n\t\t\treturn 'closed';\n\t\tcase 'submitted':\n\t\tcase 'new':\n\t\tcase 'open':\n\t\tcase 'on hold':\n\t\tdefault:\n\t\t\treturn 'opened';\n\t}\n}\n\nexport function isClosedBitbucketPullRequestState(state: BitbucketPullRequestState): boolean {\n\treturn bitbucketPullRequestStateToState(state) !== 'opened';\n}\n\nexport function isClosedBitbucketIssueState(state: BitbucketIssueState): boolean {\n\treturn bitbucketIssueStateToState(state) !== 'opened';\n}\n\nexport function fromBitbucketUser(user: BitbucketUser): PullRequestMember {\n\treturn {\n\t\tavatarUrl: user.links?.avatar?.href,\n\t\tname: user.display_name,\n\t\tusername: user.nickname,\n\t\turl: user.links?.html?.href,\n\t\tid: user.uuid,\n\t};\n}\n\nexport function fromBitbucketParticipantToReviewer(\n\tprt: BitbucketPullRequestParticipant,\n\tclosedBy: BitbucketUser | null,\n\tprState: BitbucketPullRequestState,\n): PullRequestReviewer {\n\treturn {\n\t\treviewer: fromBitbucketUser(prt.user),\n\t\tstate: prt.approved\n\t\t\t? PullRequestReviewState.Approved\n\t\t\t: prt.state === 'changes_requested'\n\t\t\t\t? PullRequestReviewState.ChangesRequested\n\t\t\t\t: prt.participated_on != null\n\t\t\t\t\t? PullRequestReviewState.Commented\n\t\t\t\t\t: prt.user.uuid === closedBy?.uuid && prState === 'DECLINED'\n\t\t\t\t\t\t? PullRequestReviewState.Dismissed\n\t\t\t\t\t\t: PullRequestReviewState.ReviewRequested,\n\t};\n}\n\nfunction getBitbucketReviewDecision(pr: BitbucketPullRequest): PullRequestReviewDecision | undefined {\n\tif (!pr.participants?.length && pr.reviewers?.length) {\n\t\treturn PullRequestReviewDecision.ReviewRequired;\n\t}\n\tif (!pr.participants) {\n\t\treturn undefined;\n\t}\n\n\tlet hasReviews = false;\n\tlet hasChangeRequests = false;\n\tlet hasApprovals = false;\n\tfor (const prt of pr.participants) {\n\t\tif (prt.participated_on != null) {\n\t\t\thasReviews = true;\n\t\t}\n\t\tif (prt.approved) {\n\t\t\thasApprovals = true;\n\t\t}\n\t\tif (prt.state === 'changes_requested') {\n\t\t\thasChangeRequests = true;\n\t\t}\n\t}\n\tif (hasChangeRequests) return PullRequestReviewDecision.ChangesRequested;\n\tif (hasApprovals) return PullRequestReviewDecision.Approved;\n\tif (hasReviews) return undefined; // not approved, not rejected, but reviewed\n\treturn PullRequestReviewDecision.ReviewRequired; // nobody has reviewed yet.\n}\n\nfunction fromBitbucketRepository(repo: BitbucketRepository): IssueRepository {\n\treturn {\n\t\towner: repo.full_name.split('/')[0],\n\t\trepo: repo.name,\n\t\tid: repo.uuid,\n\t\t// TODO: Remove this assumption once actual access level is available\n\t\taccessLevel: RepositoryAccessLevel.Write,\n\t};\n}\n\nexport function fromBitbucketIssue(issue: BitbucketIssue, provider: Provider): Issue {\n\treturn new Issue(\n\t\tprovider,\n\t\tissue.id.toString(),\n\t\tissue.id.toString(),\n\t\tissue.title,\n\t\tissue.links.html.href,\n\t\tnew Date(issue.created_on),\n\t\tnew Date(issue.updated_on),\n\t\tisClosedBitbucketIssueState(issue.state),\n\t\tbitbucketIssueStateToState(issue.state),\n\t\tfromBitbucketUser(issue.reporter),\n\t\tissue.assignee ? [fromBitbucketUser(issue.assignee)] : [],\n\t\tfromBitbucketRepository(issue.repository),\n\t\tundefined, // closedDate\n\t\tundefined, // labels\n\t\tundefined, // commentsCount\n\t\tissue.votes, // thumbsUpCount\n\t\tissue.content.html, // body\n\t\t!issue.repository?.project\n\t\t\t? undefined\n\t\t\t: {\n\t\t\t\t\tid: issue.repository.project.uuid,\n\t\t\t\t\tname: issue.repository.project.name,\n\t\t\t\t\tresourceId: issue.repository.project.uuid,\n\t\t\t\t\tresourceName: issue.repository.project.name,\n\t\t\t\t},\n\t);\n}\n\nexport function fromBitbucketPullRequest(\n\tpr: BitbucketPullRequest,\n\tprovider: Provider,\n\toptions?: { currentAccount?: { id: string; username?: string } },\n): PullRequest {\n\tconst author = fromBitbucketUser(pr.author);\n\treturn new PullRequest(\n\t\tprovider,\n\t\tauthor,\n\t\tpr.id.toString(),\n\t\tpr.id.toString(),\n\t\tpr.title,\n\t\tpr.links.html.href,\n\t\tfromBitbucketRepository(pr.destination.repository),\n\t\tbitbucketPullRequestStateToState(pr.state),\n\t\tnew Date(pr.created_on),\n\t\tnew Date(pr.updated_on),\n\t\tpr.closed_by ? new Date(pr.updated_on) : undefined,\n\t\tpr.state === 'MERGED' ? new Date(pr.updated_on) : undefined,\n\t\t// TODO: Remove this assumption once actual mergeable state is available\n\t\tPullRequestMergeableState.Mergeable, // mergeableState\n\t\tundefined, // viewerCanUpdate\n\t\t{\n\t\t\tbase: {\n\t\t\t\tbranch: pr.destination.branch.name,\n\t\t\t\tsha: pr.destination.commit.hash,\n\t\t\t\trepo: pr.destination.repository.name,\n\t\t\t\towner: pr.destination.repository.full_name.split('/')[0],\n\t\t\t\texists: true,\n\t\t\t\turl: pr.destination.repository.links.html.href,\n\t\t\t},\n\t\t\thead: {\n\t\t\t\tbranch: pr.source.branch.name,\n\t\t\t\tsha: pr.source.commit.hash,\n\t\t\t\trepo: pr.source.repository.name,\n\t\t\t\towner: pr.source.repository.full_name.split('/')[0],\n\t\t\t\texists: true,\n\t\t\t\turl: pr.source.repository.links.html.href,\n\t\t\t},\n\t\t\tisCrossRepository: pr.source.repository.uuid !== pr.destination.repository.uuid,\n\t\t},\n\t\tundefined, // isDraft\n\t\tundefined, // additions\n\t\tundefined, // deletions\n\t\tundefined, // commentsCount\n\t\tundefined, // thumbsCount\n\t\tgetBitbucketReviewDecision(pr),\n\t\tpr.participants // reviewRequests:PullRequestReviewer[]\n\t\t\t?.filter(prt => prt.role === 'REVIEWER')\n\t\t\t.map(prt => fromBitbucketParticipantToReviewer(prt, pr.closed_by, pr.state))\n\t\t\t.filter(rv => rv.state === PullRequestReviewState.ReviewRequested),\n\t\tpr.participants // latestReviews:PullRequestReviewer[]\n\t\t\t?.filter(prt => prt.participated_on != null)\n\t\t\t.map(prt => fromBitbucketParticipantToReviewer(prt, pr.closed_by, pr.state)),\n\t\tundefined, // assignees:PullRequestMember[] -- it looks like there is no such thing as assignees on Bitbucket\n\t\tundefined, // PullRequestStatusCheckRollupState\n\t\tundefined, // IssueProject\n\t\tundefined, // version\n\t\tundefined, // commitCount\n\t\tundefined, // stack\n\t\tundefined, // filesChanged\n\t\tpr.description ?? undefined,\n\t\tpr.id,\n\t\toptions?.currentAccount != null ? author.id === options.currentAccount.id : undefined,\n\t);\n}\n"],"mappings":"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"}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { IssueSorting } from "../../../git/models/issue.js";
|
|
2
|
-
import "../providerFilters.js";
|
|
3
2
|
//#region ../plus/integrations/src/providers/issueSorts.d.ts
|
|
4
3
|
/** GitHub and GHE: one `search` channel serves all three issue reads, so all three order the same way. */
|
|
5
4
|
export declare const githubIssueSorts: IssueSorting[];
|
|
@@ -6,7 +6,7 @@ import { Account } from "../../../git/models/author.js";
|
|
|
6
6
|
import { ProviderAuthenticationSession } from "../authentication/models.js";
|
|
7
7
|
import { IssuesSelfManagedHostIntegrationId } from "../constants.js";
|
|
8
8
|
import { ProvidersApi } from "./providersApi.js";
|
|
9
|
-
import { AccountWideIssuesResult, IssuesForProjectOptions, ProjectIssuesDrain } from "../models/issueReads.js";
|
|
9
|
+
import { AccountWideIssuesResult, IssuesForProjectOptions, ProjectIssuesDrain, SearchMyIssuesOptions } from "../models/issueReads.js";
|
|
10
10
|
import { IssuesIntegration } from "../models/issuesIntegration.js";
|
|
11
11
|
import { IntegrationKey } from "../models/integration.js";
|
|
12
12
|
import { IntegrationConnectionChangeEvent } from "../integrationService.js";
|
|
@@ -71,7 +71,7 @@ export declare class JiraServerIntegration extends IssuesIntegration<IssuesSelfM
|
|
|
71
71
|
* published as a complete account. Cancellation is NOT truncation — the caller asked for the stop and
|
|
72
72
|
* already knows the prefix is partial — so it breaks without setting the flag.
|
|
73
73
|
*/
|
|
74
|
-
protected searchProviderMyIssuesWithTruncation(session: ProviderAuthenticationSession, _resources?: JiraServerResourceDescriptor[], cancellation?: AbortSignal): Promise<AccountWideIssuesResult | undefined>;
|
|
74
|
+
protected searchProviderMyIssuesWithTruncation(session: ProviderAuthenticationSession, _resources?: JiraServerResourceDescriptor[], cancellation?: AbortSignal, options?: SearchMyIssuesOptions): Promise<AccountWideIssuesResult | undefined>;
|
|
75
75
|
protected getProviderLinkedIssueOrPullRequest(session: ProviderAuthenticationSession, _resource: JiraServerResourceDescriptor, { key }: {
|
|
76
76
|
id: string;
|
|
77
77
|
key: string;
|
|
@@ -6,7 +6,7 @@ import { toTokenWithInfo } from "../authentication/models.js";
|
|
|
6
6
|
import "../providerFilters.js";
|
|
7
7
|
import { mergeCollectionMetadata } from "./utils/providerPaging.js";
|
|
8
8
|
import { areDomainsOnSameHost, baseUrlFromDomain } from "../utils/domain.utils.js";
|
|
9
|
-
import { providersMetadata, toAccount, toIssueShape } from "./models.js";
|
|
9
|
+
import { providersMetadata, toAccount, toIssueShape, toProviderIssueStates } from "./models.js";
|
|
10
10
|
import { IssuesIntegration } from "../models/issuesIntegration.js";
|
|
11
11
|
import { DiscoveryCache } from "./utils/discoveryCache.js";
|
|
12
12
|
//#region ../plus/integrations/src/providers/jira-server.ts
|
|
@@ -99,6 +99,7 @@ var JiraServerIntegration = class extends IssuesIntegration {
|
|
|
99
99
|
resourceId: project.resourceId,
|
|
100
100
|
projectId: project.id
|
|
101
101
|
};
|
|
102
|
+
const states = toProviderIssueStates(options?.state);
|
|
102
103
|
const drainIssues = async (scope) => {
|
|
103
104
|
const collected = [];
|
|
104
105
|
let cursor;
|
|
@@ -110,6 +111,7 @@ var JiraServerIntegration = class extends IssuesIntegration {
|
|
|
110
111
|
result = await api.getJiraServerIssuesForProjectPaged(tokenWithInfo, baseUrl, projectJqlKey, {
|
|
111
112
|
...scope,
|
|
112
113
|
cursor,
|
|
114
|
+
states,
|
|
113
115
|
sort: options?.sort
|
|
114
116
|
});
|
|
115
117
|
} catch (ex) {
|
|
@@ -260,8 +262,9 @@ var JiraServerIntegration = class extends IssuesIntegration {
|
|
|
260
262
|
* published as a complete account. Cancellation is NOT truncation — the caller asked for the stop and
|
|
261
263
|
* already knows the prefix is partial — so it breaks without setting the flag.
|
|
262
264
|
*/
|
|
263
|
-
async searchProviderMyIssuesWithTruncation(session, _resources, cancellation) {
|
|
265
|
+
async searchProviderMyIssuesWithTruncation(session, _resources, cancellation, options) {
|
|
264
266
|
const api = await this.getProvidersApi();
|
|
267
|
+
const states = toProviderIssueStates(options?.state);
|
|
265
268
|
const tokenWithInfo = toTokenWithInfo(this.id, session);
|
|
266
269
|
const baseUrl = this.apiBaseUrlFor(session);
|
|
267
270
|
const accountScope = { providerId: this.id };
|
|
@@ -278,7 +281,10 @@ var JiraServerIntegration = class extends IssuesIntegration {
|
|
|
278
281
|
};
|
|
279
282
|
for (let i = 0; i < maxPagesPerRequest; i++) {
|
|
280
283
|
if (cancellation?.aborted) break;
|
|
281
|
-
const page = await api.getJiraServerIssuesForCurrentUser(tokenWithInfo, baseUrl, {
|
|
284
|
+
const page = await api.getJiraServerIssuesForCurrentUser(tokenWithInfo, baseUrl, {
|
|
285
|
+
cursor,
|
|
286
|
+
states
|
|
287
|
+
});
|
|
282
288
|
if (page == null) {
|
|
283
289
|
if (cursor != null) stopIncomplete("Jira Server returned no page after advertising a continuation");
|
|
284
290
|
break;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"jira-server.js","names":[],"sources":["../../../../../plus/integrations/src/providers/jira-server.ts"],"sourcesContent":["import type { CollectionMetadata } from '@gitkraken/provider-apis';\nimport type { Account } from '@gitlens/git/models/author.js';\nimport type { Issue, IssueShape } from '@gitlens/git/models/issue.js';\nimport type { IssueOrPullRequest } from '@gitlens/git/models/issueOrPullRequest.js';\nimport type { IssueResourceDescriptor } from '@gitlens/git/models/resourceDescriptor.js';\nimport type { Emitter } from '@gitlens/utils/event.js';\nimport { Logger } from '@gitlens/utils/logger.js';\nimport type { IntegrationAuthenticationProviderDescriptor } from '../authentication/integrationAuthenticationProvider.js';\nimport type { IntegrationAuthenticationService } from '../authentication/integrationAuthenticationService.js';\nimport type { ProviderAuthenticationSession } from '../authentication/models.js';\nimport { toTokenWithInfo } from '../authentication/models.js';\nimport { throwIfCallerContractError, toCollectionScopeFailure } from '../collectionMetadata.js';\nimport { IssuesSelfManagedHostIntegrationId } from '../constants.js';\nimport type { IntegrationServiceContext } from '../context.js';\nimport type { IntegrationConnectionChangeEvent } from '../integrationService.js';\nimport type { IntegrationKey } from '../models/integration.js';\nimport type { AccountWideIssuesResult, IssuesForProjectOptions, ProjectIssuesDrain } from '../models/issueReads.js';\nimport { IssuesIntegration } from '../models/issuesIntegration.js';\nimport { areDomainsOnSameHost, baseUrlFromDomain } from '../utils/domain.utils.js';\nimport type { ProviderIssue } from './models.js';\nimport { IssueFilter, providersMetadata, toAccount, toIssueShape } from './models.js';\nimport { isJiraMissingProjectError } from './providerErrors.js';\nimport type { ProvidersApi } from './providersApi.js';\nimport { DiscoveryCache } from './utils/discoveryCache.js';\nimport { mergeCollectionMetadata } from './utils/providerPaging.js';\n\n/**\n * Why three states rather than a boolean, matching `jira.ts`: the backstop is recoverable by narrowing the\n * scope, a page error or a stalled cursor is not, and {@link ProjectIssuesDrain} makes the caller say which.\n */\ntype JiraServerProjectIssuesDrain<T> = {\n\tissues: T[];\n\tstatus: 'complete' | 'backstop' | 'incomplete';\n\tmetadata?: CollectionMetadata;\n};\n\nconst metadata = providersMetadata[IssuesSelfManagedHostIntegrationId.JiraServer];\nconst authProvider = Object.freeze({ id: metadata.id, scopes: metadata.scopes });\nconst maxPagesPerRequest = 10;\n\n/**\n * The single resource a Jira Server connection has: the instance itself.\n *\n * Jira Cloud's account spans many sites, so its resources come from an API call that lists them. A\n * self-hosted instance IS the one site the connection addresses, so there is nothing to enumerate — this\n * descriptor is synthesized from the configured host. Modelling it as a resource anyway keeps the\n * resource → project contract of {@link IssuesIntegration} intact, so every tracker read (`listOrgs`,\n * `listProjects`, `listIssueTrackerIssuesPage`) works unchanged.\n */\nexport interface JiraServerResourceDescriptor extends IssueResourceDescriptor {\n\turl: string;\n}\n\nexport interface JiraServerProjectDescriptor extends IssueResourceDescriptor {\n\tresourceId: string;\n}\n\nexport class JiraServerIntegration extends IssuesIntegration<IssuesSelfManagedHostIntegrationId.JiraServer> {\n\treadonly authProvider: IntegrationAuthenticationProviderDescriptor = authProvider;\n\treadonly id = IssuesSelfManagedHostIntegrationId.JiraServer;\n\tprotected readonly key: IntegrationKey<IssuesSelfManagedHostIntegrationId.JiraServer>;\n\treadonly name: string = 'Jira Data Center';\n\n\tconstructor(\n\t\tctx: IntegrationServiceContext,\n\t\tauthenticationService: IntegrationAuthenticationService,\n\t\tgetProvidersApi: () => Promise<ProvidersApi>,\n\t\tdidChangeConnection: Emitter<IntegrationConnectionChangeEvent>,\n\t\tprivate readonly _domain: string,\n\t) {\n\t\tsuper(ctx, authenticationService, getProvidersApi, didChangeConnection);\n\t\tthis.key = `${this.id}:${this._domain}`;\n\t}\n\n\tget domain(): string {\n\t\treturn this._domain;\n\t}\n\n\t/**\n\t * The URL every request for this connection is addressed to, built from the SESSION's domain rather than\n\t * the integration's.\n\t *\n\t * `this.domain` is the host-keyed identity — `IntegrationService.get()` runs it through `hostFromDomain`\n\t * so one host can't build two instances — which means it has had any context path stripped, and so has\n\t * `session.domain`, which is stored as that same normalized host. Jira Data Center officially supports\n\t * being mounted below a context path (`https://jira.example.com/jira`), so the address comes from\n\t * {@link ProviderAuthenticationSession.baseUrl}, which carries the configured value verbatim; it falls\n\t * back to the host when the backend reported nothing more, which is the common case.\n\t *\n\t * Takes the session rather than reading `_session` so a per-connection (multi-account) read is addressed\n\t * to the host of the connection it resolved, not to whichever one happens to be current.\n\t */\n\tprotected apiBaseUrlFor(session: ProviderAuthenticationSession | undefined): string {\n\t\tconst configured = baseUrlFromDomain(session?.baseUrl, session?.protocol);\n\t\t// A `baseUrl` from another host is not this connection's address — refuse it rather than routing this\n\t\t// integration's reads somewhere its key never named.\n\t\tif (configured != null && areDomainsOnSameHost(session?.baseUrl, this.domain)) return configured;\n\n\t\treturn (\n\t\t\tbaseUrlFromDomain(session?.domain, session?.protocol) ??\n\t\t\tbaseUrlFromDomain(this.domain, session?.protocol) ??\n\t\t\t`${session?.protocol ?? 'https:'}//${this.domain}`\n\t\t);\n\t}\n\n\t/** The one resource this connection has — see {@link JiraServerResourceDescriptor}. */\n\tprivate resourceFor(session: ProviderAuthenticationSession | undefined): JiraServerResourceDescriptor {\n\t\treturn { id: this.domain, key: this.domain, name: this.domain, url: this.apiBaseUrlFor(session) };\n\t}\n\n\t// No `autolinks()` override, unlike Jira Cloud: an autolink prefix has to be the Jira project KEY, which\n\t// is what a commit message or branch name carries (`PROJ-123`). `/rest/api/2/project` reports no key — see\n\t// `getProviderProjectsForResources`, which falls back to the name because JQL accepts it — so the only\n\t// prefix this class could build is the display name, which matches nothing and yields a `/browse/` URL that\n\t// is not an issue. Registering that would be worse than registering none. Restore the override once the SDK\n\t// surfaces the project key (GKDEV-3629's sibling gap).\n\n\tprotected override async getProviderAccountForResource(\n\t\tsession: ProviderAuthenticationSession,\n\t\t_resource: JiraServerResourceDescriptor,\n\t): Promise<Account | undefined> {\n\t\tconst api = await this.getProvidersApi();\n\t\tconst user = await api.getJiraServerCurrentUser(toTokenWithInfo(this.id, session), this.apiBaseUrlFor(session));\n\n\t\tif (user == null) return undefined;\n\t\treturn toAccount(user, this);\n\t}\n\n\tprotected override getProviderResourcesForUser(\n\t\tsession: ProviderAuthenticationSession,\n\t\t_force?: boolean,\n\t): Promise<JiraServerResourceDescriptor[] | undefined> {\n\t\t// Synthesized from the configured host rather than fetched, so there is nothing to cache or force.\n\t\treturn Promise.resolve([this.resourceFor(session)]);\n\t}\n\n\t/** Expires, and is dropped on a re-sync, so a project created or deleted mid-session is picked up (#5907). */\n\tprivate readonly _projects = new DiscoveryCache<JiraServerProjectDescriptor[]>();\n\tprotected override async getProviderProjectsForResources(\n\t\tsession: ProviderAuthenticationSession,\n\t\tresources: JiraServerResourceDescriptor[],\n\t\tforce: boolean = false,\n\t): Promise<JiraServerProjectDescriptor[] | undefined> {\n\t\t// Every resource here is this instance (see `getProviderResourcesForUser`), so one read serves them\n\t\t// all; a caller that passes none is asking for nothing.\n\t\tif (resources.length === 0) return [];\n\n\t\tconst cacheKey = this.projectsCacheKey(session);\n\t\tconst cached = this._projects.get(cacheKey);\n\t\tif (cached != null && !force) return cached;\n\n\t\tconst generation = this._projects.generation;\n\t\tconst api = await this.getProvidersApi();\n\t\tconst projects = await api.getJiraServerProjects(\n\t\t\ttoTokenWithInfo(this.id, session),\n\t\t\tthis.apiBaseUrlFor(session),\n\t\t);\n\t\tif (projects == null) return undefined;\n\n\t\tconst resourceId = this.resourceFor(session).id;\n\t\tconst descriptors = projects.map<JiraServerProjectDescriptor>(p => ({\n\t\t\tid: p.id,\n\t\t\t// The display name, NOT what the reads address the project by. `/rest/api/2/project` reports no\n\t\t\t// project key (GKDEV-3629's sibling gap) and the SDK's `JiraServerProject` carries only `id` and\n\t\t\t// `name`, so there is no key to put here; the reads below use `id` instead, because JQL resolves a\n\t\t\t// `project = \"<value>\"` clause against key before name, and a display name that collides with\n\t\t\t// ANOTHER project's key would silently return that project's issues.\n\t\t\tkey: p.name,\n\t\t\tname: p.name,\n\t\t\tresourceId: resourceId,\n\t\t}));\n\n\t\tthis._projects.set(cacheKey, descriptors, { generation: generation });\n\n\t\treturn descriptors;\n\t}\n\n\tprotected override async getProviderIssuesForProject(\n\t\tsession: ProviderAuthenticationSession,\n\t\tproject: JiraServerProjectDescriptor,\n\t\toptions?: IssuesForProjectOptions,\n\t): Promise<IssueShape[] | undefined> {\n\t\treturn (await this.getProviderIssuesForProjectWithTruncation(session, project, options))?.values;\n\t}\n\n\tprotected override async getProviderIssuesForProjectWithTruncation(\n\t\tsession: ProviderAuthenticationSession,\n\t\tproject: JiraServerProjectDescriptor,\n\t\toptions?: IssuesForProjectOptions,\n\t): Promise<ProjectIssuesDrain | undefined> {\n\t\tconst tokenWithInfo = toTokenWithInfo(this.id, session);\n\t\tconst api = await this.getProvidersApi();\n\t\tconst baseUrl = this.apiBaseUrlFor(session);\n\n\t\t// Mirrors Jira Cloud's drain (see `jira.ts`): page to a defensive backstop, and report `truncated` plus\n\t\t// a structured failure rather than discarding an already-fetched prefix, so the facade can warn and set\n\t\t// fetchFailed while still publishing what was read.\n\t\t// The numeric project id, not the name: JQL resolves `project = \"<value>\"` against key first, so a\n\t\t// display name that matches another project's key reads that project's issues instead. A Jira project\n\t\t// key must begin with a letter, so a purely numeric value can only be an id.\n\t\tconst projectJqlKey = project.id;\n\t\tconst projectScope = {\n\t\t\tproviderId: this.id,\n\t\t\tresourceId: project.resourceId,\n\t\t\tprojectId: project.id,\n\t\t};\n\t\tconst drainIssues = async (scope: {\n\t\t\tauthorLogin?: string;\n\t\t\tassigneeLogins?: string[];\n\t\t\tmentionLogin?: string;\n\t\t}): Promise<JiraServerProjectIssuesDrain<ProviderIssue>> => {\n\t\t\tconst collected: ProviderIssue[] = [];\n\t\t\tlet cursor: string | undefined;\n\t\t\tlet status: JiraServerProjectIssuesDrain<ProviderIssue>['status'] = 'complete';\n\t\t\tlet metadata: CollectionMetadata | undefined;\n\t\t\tfor (let i = 0; i < maxPagesPerRequest; i++) {\n\t\t\t\tlet result: Awaited<ReturnType<typeof api.getJiraServerIssuesForProjectPaged>> | undefined;\n\t\t\t\ttry {\n\t\t\t\t\tresult = await api.getJiraServerIssuesForProjectPaged(tokenWithInfo, baseUrl, projectJqlKey, {\n\t\t\t\t\t\t...scope,\n\t\t\t\t\t\tcursor: cursor,\n\t\t\t\t\t\tsort: options?.sort,\n\t\t\t\t\t});\n\t\t\t\t} catch (ex) {\n\t\t\t\t\t// A project this connection can no longer see (deleted, or its browse permission removed) holds\n\t\t\t\t\t// no issues for it, and the project list that named it is stale: drop that list so the next read\n\t\t\t\t\t// re-reads it, instead of failing the whole provider on every read until a restart (#5907).\n\t\t\t\t\tif (collected.length === 0 && isJiraMissingProjectError(ex)) {\n\t\t\t\t\t\tLogger.warn(\n\t\t\t\t\t\t\t`Jira Server project '${project.name}' (${project.id}) no longer exists or is not visible`,\n\t\t\t\t\t\t);\n\t\t\t\t\t\tthis._projects.delete(this.projectsCacheKey(session));\n\t\t\t\t\t\treturn { issues: [], status: 'complete' };\n\t\t\t\t\t}\n\n\t\t\t\t\t// A page failure after the first leaves the drained prefix intact; record it at the project\n\t\t\t\t\t// scope instead of re-throwing. With nothing fetched yet the original throw is preserved, so\n\t\t\t\t\t// the caller sees a hard error rather than an empty partial success.\n\t\t\t\t\tif (collected.length === 0) throw ex;\n\n\t\t\t\t\tstatus = 'incomplete';\n\t\t\t\t\tmetadata = mergeCollectionMetadata(metadata, {\n\t\t\t\t\t\tcompleteness: 'partial',\n\t\t\t\t\t\tfailures: [toCollectionScopeFailure(projectScope, ex)],\n\t\t\t\t\t});\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tif (result == null) {\n\t\t\t\t\tif (cursor == null) break;\n\n\t\t\t\t\tstatus = 'incomplete';\n\t\t\t\t\tmetadata = mergeCollectionMetadata(metadata, {\n\t\t\t\t\t\tcompleteness: 'partial',\n\t\t\t\t\t\tfailures: [\n\t\t\t\t\t\t\ttoCollectionScopeFailure(\n\t\t\t\t\t\t\t\tprojectScope,\n\t\t\t\t\t\t\t\tnew Error('Jira Server returned no page after advertising a continuation'),\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t],\n\t\t\t\t\t});\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tcollected.push(...result.data);\n\t\t\t\tif (!result.hasMore) break;\n\n\t\t\t\t// More pages are claimed but no advancing cursor came back: the drain can't continue, so flag it\n\t\t\t\t// rather than stopping silently.\n\t\t\t\tif (result.nextCursor == null || result.nextCursor === cursor) {\n\t\t\t\t\tstatus = 'incomplete';\n\t\t\t\t\tmetadata = mergeCollectionMetadata(metadata, {\n\t\t\t\t\t\tcompleteness: 'partial',\n\t\t\t\t\t\tfailures: [\n\t\t\t\t\t\t\ttoCollectionScopeFailure(\n\t\t\t\t\t\t\t\tprojectScope,\n\t\t\t\t\t\t\t\tnew Error('Jira Server returned no advancing issue continuation'),\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t],\n\t\t\t\t\t});\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tcursor = result.nextCursor;\n\t\t\t\t// Pages remain but this was the last allowed iteration: the backstop stopped the drain, which a\n\t\t\t\t// narrower scope would avoid.\n\t\t\t\tif (i === maxPagesPerRequest - 1) {\n\t\t\t\t\tstatus = 'backstop';\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn { issues: collected, status: status, metadata: metadata };\n\t\t};\n\n\t\t/**\n\t\t * Every clause is scoped by the HANDLE, and `options.userId` is deliberately ignored here — the one\n\t\t * place this provider diverges from Jira Cloud's, which applies it to `assignee`/`creator` (#5857).\n\t\t *\n\t\t * The two are not interchangeable because `Account.id` means something different per deployment. On\n\t\t * Cloud it is the `accountId`, which JQL resolves, so scoping by it fixes the deactivated-account case\n\t\t * where the display name no longer resolves. On Server it is the user KEY (`JIRAUSER10224`), which JQL\n\t\t * does not resolve at all: measured against a live Jira Server 8.20.13 site, `assignee in\n\t\t * (\"JIRAUSER10224\")` returns 0 with `\"The value 'JIRAUSER10224' does not exist for the field\n\t\t * 'assignee'\"` — the identical warning a genuinely nonexistent user produces — while the username and\n\t\t * the display name both return the real 4.\n\t\t *\n\t\t * So applying `userId` here would silently empty the read rather than harden it. Do not \"fix\" this\n\t\t * asymmetry to match Cloud without re-measuring against a Server instance.\n\t\t *\n\t\t * The handle itself is already the right one: `provider-apis` 0.60.0 maps `Account.username` from the\n\t\t * Server payload's `name` (falling back to `displayName` when absent), so these clauses are scoped by the\n\t\t * real username rather than a display name (GKDEV-3629). That is what makes the `mention` clause work at\n\t\t * all — it is a free-text `comment ~ \"...\"` over comment bodies, and a Jira mention is stored as\n\t\t * `[~username]`, which a display name never matches.\n\t\t */\n\t\tconst getSearchedUserIssuesForFilter = async (\n\t\t\tuser: string,\n\t\t\tfilter: IssueFilter,\n\t\t): Promise<JiraServerProjectIssuesDrain<IssueShape>> => {\n\t\t\tconst result = await drainIssues({\n\t\t\t\tauthorLogin: filter === IssueFilter.Author ? user : undefined,\n\t\t\t\tassigneeLogins: filter === IssueFilter.Assignee ? [user] : undefined,\n\t\t\t\tmentionLogin: filter === IssueFilter.Mention ? user : undefined,\n\t\t\t});\n\n\t\t\treturn {\n\t\t\t\tissues: result.issues\n\t\t\t\t\t.map(issue => toIssueShape(issue, this))\n\t\t\t\t\t.filter((r): r is IssueShape => r !== undefined),\n\t\t\t\tstatus: result.status,\n\t\t\t\tmetadata: result.metadata,\n\t\t\t};\n\t\t};\n\n\t\tif (options?.user != null) {\n\t\t\tconst user = options.user;\n\t\t\t// A resolved user always scopes the read, defaulting to \"my issues\" when no explicit filters are\n\t\t\t// given — otherwise it would fall through to the unscoped fetch and return the whole project (#5438).\n\t\t\tconst filters = options.filters?.length ? options.filters : [IssueFilter.Assignee];\n\t\t\tconst settled = await Promise.allSettled(\n\t\t\t\tfilters.map(filter => getSearchedUserIssuesForFilter(user, filter)),\n\t\t\t);\n\n\t\t\t// Every branch rejected: the read failed outright, so propagate the first reason as-is (not wrapped)\n\t\t\t// to keep its auth/rate-limit classification, and log the rest, which would otherwise be discarded.\n\t\t\tif (settled.every(r => r.status === 'rejected')) {\n\t\t\t\tfor (let i = 1; i < settled.length; i++) {\n\t\t\t\t\tconst outcome = settled[i];\n\t\t\t\t\tif (outcome.status === 'rejected') {\n\t\t\t\t\t\tLogger.error(\n\t\t\t\t\t\t\toutcome.reason,\n\t\t\t\t\t\t\t`getProviderIssuesForProjectWithTruncation: filter '${filters[i]}' failed`,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tthrow settled[0].status === 'rejected' ? settled[0].reason : new Error('Jira Server issue read failed');\n\t\t\t}\n\n\t\t\tlet truncated = false;\n\t\t\tlet metadata: CollectionMetadata | undefined;\n\t\t\tconst resultsById = new Map<string, IssueShape>();\n\t\t\tfor (let i = 0; i < settled.length; i++) {\n\t\t\t\tconst outcome = settled[i];\n\t\t\t\tconst filter = filters[i];\n\t\t\t\t// One rejected branch with a succeeding sibling means this project's issues are incomplete: keep\n\t\t\t\t// the sibling results and record a per-filter failure so the facade can warn on it specifically.\n\t\t\t\tif (outcome.status !== 'fulfilled') {\n\t\t\t\t\t// Identical for every branch, so degrading it would report one failure per filter for a\n\t\t\t\t\t// single invalid call — see `throwIfCallerContractError`.\n\t\t\t\t\tthrowIfCallerContractError(outcome.reason);\n\n\t\t\t\t\ttruncated = true;\n\t\t\t\t\tconst failure = toCollectionScopeFailure(projectScope, outcome.reason);\n\t\t\t\t\tmetadata = mergeCollectionMetadata(metadata, {\n\t\t\t\t\t\tcompleteness: 'partial',\n\t\t\t\t\t\tfailures: [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t...failure,\n\t\t\t\t\t\t\t\tmessage: `Issue filter '${filter}' could not be read${\n\t\t\t\t\t\t\t\t\tfailure.message != null ? `: ${failure.message}` : ''\n\t\t\t\t\t\t\t\t}`,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t],\n\t\t\t\t\t});\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tif (outcome.value.status !== 'complete') {\n\t\t\t\t\ttruncated = true;\n\t\t\t\t}\n\t\t\t\tif (outcome.value.metadata != null) {\n\t\t\t\t\tmetadata = mergeCollectionMetadata(metadata, outcome.value.metadata);\n\t\t\t\t}\n\t\t\t\tfor (const resultIssue of outcome.value.issues) {\n\t\t\t\t\tif (!resultsById.has(resultIssue.id)) {\n\t\t\t\t\t\tresultsById.set(resultIssue.id, resultIssue);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// `recovery: 'none'` for the whole set: the filter branches merge here, so even a pure backstop in one\n\t\t\t// branch is not fixable by narrowing this read's scope.\n\t\t\treturn truncated\n\t\t\t\t? { values: [...resultsById.values()], truncated: true, recovery: 'none', metadata: metadata }\n\t\t\t\t: { values: [...resultsById.values()], truncated: false, metadata: metadata };\n\t\t}\n\n\t\tconst unscoped = await drainIssues({});\n\t\tconst values = unscoped.issues\n\t\t\t.map(issue => toIssueShape(issue, this))\n\t\t\t.filter((result): result is IssueShape => result !== undefined);\n\t\treturn unscoped.status !== 'complete'\n\t\t\t? {\n\t\t\t\t\tvalues: values,\n\t\t\t\t\ttruncated: true,\n\t\t\t\t\trecovery: unscoped.status === 'backstop' ? 'narrow-scope' : 'none',\n\t\t\t\t\tmetadata: unscoped.metadata,\n\t\t\t\t}\n\t\t\t: { values: values, truncated: false, metadata: unscoped.metadata };\n\t}\n\n\tprotected override async searchProviderMyIssues(\n\t\tsession: ProviderAuthenticationSession,\n\t\tresources?: JiraServerResourceDescriptor[],\n\t\tcancellation?: AbortSignal,\n\t): Promise<IssueShape[] | undefined> {\n\t\treturn (await this.searchProviderMyIssuesWithTruncation(session, resources, cancellation))?.values;\n\t}\n\n\t/**\n\t * Account-wide \"my issues\" for this instance, drained to the backstop and reporting whether it got there.\n\t *\n\t * The override exists for the same reason the project drain reports `status`: the default\n\t * implementation wraps whatever the normalized read returned as `truncated: false`, so a run that stopped\n\t * at `maxPagesPerRequest` with pages left, or on a continuation the server never advanced, would be\n\t * published as a complete account. Cancellation is NOT truncation — the caller asked for the stop and\n\t * already knows the prefix is partial — so it breaks without setting the flag.\n\t */\n\tprotected override async searchProviderMyIssuesWithTruncation(\n\t\tsession: ProviderAuthenticationSession,\n\t\t_resources?: JiraServerResourceDescriptor[],\n\t\tcancellation?: AbortSignal,\n\t): Promise<AccountWideIssuesResult | undefined> {\n\t\tconst api = await this.getProvidersApi();\n\t\tconst tokenWithInfo = toTokenWithInfo(this.id, session);\n\t\tconst baseUrl = this.apiBaseUrlFor(session);\n\n\t\t// Errors propagate, unlike Jira Cloud's sibling. Cloud swallows per resource because it loops over the\n\t\t// account's many sites and one bad site must not discard the others; this connection addresses exactly\n\t\t// one instance, so there is no sibling to protect and swallowing would turn every failure — an expired\n\t\t// token, an unreachable host — into an empty success. `searchMyIssuesWithTruncationResult` recovers the\n\t\t// throw into `{ error }`, which is what lets the caller warn instead of reporting \"no issues\".\n\t\t// Only the provider: the instance is this connection's single resource, so naming it would publish a\n\t\t// failure of the whole connection as one confined to a sub-scope (see `ProviderWarning.scope`).\n\t\tconst accountScope = { providerId: this.id };\n\t\tconst results: IssueShape[] = [];\n\t\tlet cursor: string | undefined;\n\t\tlet truncated = false;\n\t\tlet metadata: CollectionMetadata | undefined;\n\n\t\tconst stopIncomplete = (reason: string) => {\n\t\t\ttruncated = true;\n\t\t\tmetadata = mergeCollectionMetadata(metadata, {\n\t\t\t\tcompleteness: 'partial',\n\t\t\t\tfailures: [toCollectionScopeFailure(accountScope, new Error(reason))],\n\t\t\t});\n\t\t};\n\n\t\tfor (let i = 0; i < maxPagesPerRequest; i++) {\n\t\t\tif (cancellation?.aborted) break;\n\n\t\t\tconst page = await api.getJiraServerIssuesForCurrentUser(tokenWithInfo, baseUrl, { cursor: cursor });\n\t\t\tif (page == null) {\n\t\t\t\t// Nothing fetched yet and no page is an empty account, not a failure; after a continuation it\n\t\t\t\t// means the server dropped the drain mid-way.\n\t\t\t\tif (cursor != null) {\n\t\t\t\t\tstopIncomplete('Jira Server returned no page after advertising a continuation');\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tresults.push(\n\t\t\t\t...page.data.map(issue => toIssueShape(issue, this)).filter((r): r is IssueShape => r != null),\n\t\t\t);\n\n\t\t\tif (!page.hasMore) break;\n\n\t\t\t// More pages are claimed but no advancing cursor came back: the drain can't continue, so flag it\n\t\t\t// rather than stopping silently.\n\t\t\tif (page.nextCursor == null || page.nextCursor === cursor) {\n\t\t\t\tstopIncomplete('Jira Server returned no advancing issue continuation');\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcursor = page.nextCursor;\n\t\t\t// Pages remain but this was the last allowed iteration: the backstop stopped the drain.\n\t\t\tif (i === maxPagesPerRequest - 1) {\n\t\t\t\tstopIncomplete(\n\t\t\t\t\t`Jira Server account-wide issue drain stopped at the ${maxPagesPerRequest}-page backstop`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\treturn { values: results, truncated: truncated, metadata: metadata };\n\t}\n\n\tprotected override async getProviderLinkedIssueOrPullRequest(\n\t\tsession: ProviderAuthenticationSession,\n\t\t_resource: JiraServerResourceDescriptor,\n\t\t{ key }: { id: string; key: string },\n\t): Promise<IssueOrPullRequest | undefined> {\n\t\tconst api = await this.getProvidersApi();\n\t\tconst issue = await api.getJiraServerIssue(toTokenWithInfo(this.id, session), this.apiBaseUrlFor(session), key);\n\t\treturn issue != null ? toIssueShape(issue, this) : undefined;\n\t}\n\n\tprotected override async getProviderIssue(\n\t\tsession: ProviderAuthenticationSession,\n\t\t_resource: JiraServerResourceDescriptor,\n\t\tid: string,\n\t): Promise<Issue | undefined> {\n\t\tconst api = await this.getProvidersApi();\n\t\tconst apiResult = await api.getJiraServerIssue(\n\t\t\ttoTokenWithInfo(this.id, session),\n\t\t\tthis.apiBaseUrlFor(session),\n\t\t\tid,\n\t\t);\n\t\tconst issue = apiResult != null ? toIssueShape(apiResult, this) : undefined;\n\t\treturn issue != null ? { ...issue, type: 'issue' } : undefined;\n\t}\n\n\t/**\n\t * The point read behind `getIssuesBatch`'s tracker targets (#5872). `resourceId` is this connection's single\n\t * synthetic resource — the host itself, see {@link JiraServerResourceDescriptor} — so it selects nothing further\n\t * here: the host was chosen upstream by `domain` (or the connection's configured domain) and the request is\n\t * addressed to the SESSION's base URL like every other read. No `resourceUrl` is needed either, unlike Jira\n\t * Cloud: its REST `self` link is an API endpoint, whereas `getJiraServerIssue` builds the browser link from\n\t * the base URL it was addressed to. A key that names no issue resolves to `undefined`, a proven absence.\n\t */\n\tprotected override getProviderIssueByResourceId(\n\t\tsession: ProviderAuthenticationSession,\n\t\t_resourceId: string,\n\t\tid: string,\n\t\t_resourceUrl: string | undefined,\n\t): Promise<Issue | undefined> {\n\t\treturn this.getProviderIssue(session, this.resourceFor(session), id);\n\t}\n\n\t/**\n\t * Caches are keyed by the ADDRESS the read was made against as well as the token: one integration instance\n\t * exists per configured host and two hosts can hold the same token, so a token-only key would serve one\n\t * host's projects for the other — and the host alone is not enough either, because a connection re-pointed\n\t * to another context path on the same host keeps its token and would otherwise be served the old path's\n\t * projects.\n\t */\n\tprivate projectsCacheKey(session: ProviderAuthenticationSession): string {\n\t\treturn `${this.apiBaseUrlFor(session)}:${session.accessToken}`;\n\t}\n\n\toverride invalidateDiscoveryCaches(): void {\n\t\tthis._projects.clear();\n\t}\n\n\tprotected override providerOnDisconnect(): void {\n\t\tthis._projects.clear();\n\t}\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"jira-server.js","names":[],"sources":["../../../../../plus/integrations/src/providers/jira-server.ts"],"sourcesContent":["import type { CollectionMetadata } from '@gitkraken/provider-apis';\nimport type { Account } from '@gitlens/git/models/author.js';\nimport type { Issue, IssueShape } from '@gitlens/git/models/issue.js';\nimport type { IssueOrPullRequest } from '@gitlens/git/models/issueOrPullRequest.js';\nimport type { IssueResourceDescriptor } from '@gitlens/git/models/resourceDescriptor.js';\nimport type { Emitter } from '@gitlens/utils/event.js';\nimport { Logger } from '@gitlens/utils/logger.js';\nimport type { IntegrationAuthenticationProviderDescriptor } from '../authentication/integrationAuthenticationProvider.js';\nimport type { IntegrationAuthenticationService } from '../authentication/integrationAuthenticationService.js';\nimport type { ProviderAuthenticationSession } from '../authentication/models.js';\nimport { toTokenWithInfo } from '../authentication/models.js';\nimport { throwIfCallerContractError, toCollectionScopeFailure } from '../collectionMetadata.js';\nimport { IssuesSelfManagedHostIntegrationId } from '../constants.js';\nimport type { IntegrationServiceContext } from '../context.js';\nimport type { IntegrationConnectionChangeEvent } from '../integrationService.js';\nimport type { IntegrationKey } from '../models/integration.js';\nimport type {\n\tAccountWideIssuesResult,\n\tIssuesForProjectOptions,\n\tProjectIssuesDrain,\n\tSearchMyIssuesOptions,\n} from '../models/issueReads.js';\nimport { IssuesIntegration } from '../models/issuesIntegration.js';\nimport { areDomainsOnSameHost, baseUrlFromDomain } from '../utils/domain.utils.js';\nimport type { ProviderIssue } from './models.js';\nimport { IssueFilter, providersMetadata, toAccount, toIssueShape, toProviderIssueStates } from './models.js';\nimport { isJiraMissingProjectError } from './providerErrors.js';\nimport type { ProvidersApi } from './providersApi.js';\nimport { DiscoveryCache } from './utils/discoveryCache.js';\nimport { mergeCollectionMetadata } from './utils/providerPaging.js';\n\n/**\n * Why three states rather than a boolean, matching `jira.ts`: the backstop is recoverable by narrowing the\n * scope, a page error or a stalled cursor is not, and {@link ProjectIssuesDrain} makes the caller say which.\n */\ntype JiraServerProjectIssuesDrain<T> = {\n\tissues: T[];\n\tstatus: 'complete' | 'backstop' | 'incomplete';\n\tmetadata?: CollectionMetadata;\n};\n\nconst metadata = providersMetadata[IssuesSelfManagedHostIntegrationId.JiraServer];\nconst authProvider = Object.freeze({ id: metadata.id, scopes: metadata.scopes });\nconst maxPagesPerRequest = 10;\n\n/**\n * The single resource a Jira Server connection has: the instance itself.\n *\n * Jira Cloud's account spans many sites, so its resources come from an API call that lists them. A\n * self-hosted instance IS the one site the connection addresses, so there is nothing to enumerate — this\n * descriptor is synthesized from the configured host. Modelling it as a resource anyway keeps the\n * resource → project contract of {@link IssuesIntegration} intact, so every tracker read (`listOrgs`,\n * `listProjects`, `listIssueTrackerIssuesPage`) works unchanged.\n */\nexport interface JiraServerResourceDescriptor extends IssueResourceDescriptor {\n\turl: string;\n}\n\nexport interface JiraServerProjectDescriptor extends IssueResourceDescriptor {\n\tresourceId: string;\n}\n\nexport class JiraServerIntegration extends IssuesIntegration<IssuesSelfManagedHostIntegrationId.JiraServer> {\n\treadonly authProvider: IntegrationAuthenticationProviderDescriptor = authProvider;\n\treadonly id = IssuesSelfManagedHostIntegrationId.JiraServer;\n\tprotected readonly key: IntegrationKey<IssuesSelfManagedHostIntegrationId.JiraServer>;\n\treadonly name: string = 'Jira Data Center';\n\n\tconstructor(\n\t\tctx: IntegrationServiceContext,\n\t\tauthenticationService: IntegrationAuthenticationService,\n\t\tgetProvidersApi: () => Promise<ProvidersApi>,\n\t\tdidChangeConnection: Emitter<IntegrationConnectionChangeEvent>,\n\t\tprivate readonly _domain: string,\n\t) {\n\t\tsuper(ctx, authenticationService, getProvidersApi, didChangeConnection);\n\t\tthis.key = `${this.id}:${this._domain}`;\n\t}\n\n\tget domain(): string {\n\t\treturn this._domain;\n\t}\n\n\t/**\n\t * The URL every request for this connection is addressed to, built from the SESSION's domain rather than\n\t * the integration's.\n\t *\n\t * `this.domain` is the host-keyed identity — `IntegrationService.get()` runs it through `hostFromDomain`\n\t * so one host can't build two instances — which means it has had any context path stripped, and so has\n\t * `session.domain`, which is stored as that same normalized host. Jira Data Center officially supports\n\t * being mounted below a context path (`https://jira.example.com/jira`), so the address comes from\n\t * {@link ProviderAuthenticationSession.baseUrl}, which carries the configured value verbatim; it falls\n\t * back to the host when the backend reported nothing more, which is the common case.\n\t *\n\t * Takes the session rather than reading `_session` so a per-connection (multi-account) read is addressed\n\t * to the host of the connection it resolved, not to whichever one happens to be current.\n\t */\n\tprotected apiBaseUrlFor(session: ProviderAuthenticationSession | undefined): string {\n\t\tconst configured = baseUrlFromDomain(session?.baseUrl, session?.protocol);\n\t\t// A `baseUrl` from another host is not this connection's address — refuse it rather than routing this\n\t\t// integration's reads somewhere its key never named.\n\t\tif (configured != null && areDomainsOnSameHost(session?.baseUrl, this.domain)) return configured;\n\n\t\treturn (\n\t\t\tbaseUrlFromDomain(session?.domain, session?.protocol) ??\n\t\t\tbaseUrlFromDomain(this.domain, session?.protocol) ??\n\t\t\t`${session?.protocol ?? 'https:'}//${this.domain}`\n\t\t);\n\t}\n\n\t/** The one resource this connection has — see {@link JiraServerResourceDescriptor}. */\n\tprivate resourceFor(session: ProviderAuthenticationSession | undefined): JiraServerResourceDescriptor {\n\t\treturn { id: this.domain, key: this.domain, name: this.domain, url: this.apiBaseUrlFor(session) };\n\t}\n\n\t// No `autolinks()` override, unlike Jira Cloud: an autolink prefix has to be the Jira project KEY, which\n\t// is what a commit message or branch name carries (`PROJ-123`). `/rest/api/2/project` reports no key — see\n\t// `getProviderProjectsForResources`, which falls back to the name because JQL accepts it — so the only\n\t// prefix this class could build is the display name, which matches nothing and yields a `/browse/` URL that\n\t// is not an issue. Registering that would be worse than registering none. Restore the override once the SDK\n\t// surfaces the project key (GKDEV-3629's sibling gap).\n\n\tprotected override async getProviderAccountForResource(\n\t\tsession: ProviderAuthenticationSession,\n\t\t_resource: JiraServerResourceDescriptor,\n\t): Promise<Account | undefined> {\n\t\tconst api = await this.getProvidersApi();\n\t\tconst user = await api.getJiraServerCurrentUser(toTokenWithInfo(this.id, session), this.apiBaseUrlFor(session));\n\n\t\tif (user == null) return undefined;\n\t\treturn toAccount(user, this);\n\t}\n\n\tprotected override getProviderResourcesForUser(\n\t\tsession: ProviderAuthenticationSession,\n\t\t_force?: boolean,\n\t): Promise<JiraServerResourceDescriptor[] | undefined> {\n\t\t// Synthesized from the configured host rather than fetched, so there is nothing to cache or force.\n\t\treturn Promise.resolve([this.resourceFor(session)]);\n\t}\n\n\t/** Expires, and is dropped on a re-sync, so a project created or deleted mid-session is picked up (#5907). */\n\tprivate readonly _projects = new DiscoveryCache<JiraServerProjectDescriptor[]>();\n\tprotected override async getProviderProjectsForResources(\n\t\tsession: ProviderAuthenticationSession,\n\t\tresources: JiraServerResourceDescriptor[],\n\t\tforce: boolean = false,\n\t): Promise<JiraServerProjectDescriptor[] | undefined> {\n\t\t// Every resource here is this instance (see `getProviderResourcesForUser`), so one read serves them\n\t\t// all; a caller that passes none is asking for nothing.\n\t\tif (resources.length === 0) return [];\n\n\t\tconst cacheKey = this.projectsCacheKey(session);\n\t\tconst cached = this._projects.get(cacheKey);\n\t\tif (cached != null && !force) return cached;\n\n\t\tconst generation = this._projects.generation;\n\t\tconst api = await this.getProvidersApi();\n\t\tconst projects = await api.getJiraServerProjects(\n\t\t\ttoTokenWithInfo(this.id, session),\n\t\t\tthis.apiBaseUrlFor(session),\n\t\t);\n\t\tif (projects == null) return undefined;\n\n\t\tconst resourceId = this.resourceFor(session).id;\n\t\tconst descriptors = projects.map<JiraServerProjectDescriptor>(p => ({\n\t\t\tid: p.id,\n\t\t\t// The display name, NOT what the reads address the project by. `/rest/api/2/project` reports no\n\t\t\t// project key (GKDEV-3629's sibling gap) and the SDK's `JiraServerProject` carries only `id` and\n\t\t\t// `name`, so there is no key to put here; the reads below use `id` instead, because JQL resolves a\n\t\t\t// `project = \"<value>\"` clause against key before name, and a display name that collides with\n\t\t\t// ANOTHER project's key would silently return that project's issues.\n\t\t\tkey: p.name,\n\t\t\tname: p.name,\n\t\t\tresourceId: resourceId,\n\t\t}));\n\n\t\tthis._projects.set(cacheKey, descriptors, { generation: generation });\n\n\t\treturn descriptors;\n\t}\n\n\tprotected override async getProviderIssuesForProject(\n\t\tsession: ProviderAuthenticationSession,\n\t\tproject: JiraServerProjectDescriptor,\n\t\toptions?: IssuesForProjectOptions,\n\t): Promise<IssueShape[] | undefined> {\n\t\treturn (await this.getProviderIssuesForProjectWithTruncation(session, project, options))?.values;\n\t}\n\n\tprotected override async getProviderIssuesForProjectWithTruncation(\n\t\tsession: ProviderAuthenticationSession,\n\t\tproject: JiraServerProjectDescriptor,\n\t\toptions?: IssuesForProjectOptions,\n\t): Promise<ProjectIssuesDrain | undefined> {\n\t\tconst tokenWithInfo = toTokenWithInfo(this.id, session);\n\t\tconst api = await this.getProvidersApi();\n\t\tconst baseUrl = this.apiBaseUrlFor(session);\n\n\t\t// Mirrors Jira Cloud's drain (see `jira.ts`): page to a defensive backstop, and report `truncated` plus\n\t\t// a structured failure rather than discarding an already-fetched prefix, so the facade can warn and set\n\t\t// fetchFailed while still publishing what was read.\n\t\t// The numeric project id, not the name: JQL resolves `project = \"<value>\"` against key first, so a\n\t\t// display name that matches another project's key reads that project's issues instead. A Jira project\n\t\t// key must begin with a letter, so a purely numeric value can only be an id.\n\t\tconst projectJqlKey = project.id;\n\t\tconst projectScope = {\n\t\t\tproviderId: this.id,\n\t\t\tresourceId: project.resourceId,\n\t\t\tprojectId: project.id,\n\t\t};\n\t\tconst states = toProviderIssueStates(options?.state);\n\t\tconst drainIssues = async (scope: {\n\t\t\tauthorLogin?: string;\n\t\t\tassigneeLogins?: string[];\n\t\t\tmentionLogin?: string;\n\t\t}): Promise<JiraServerProjectIssuesDrain<ProviderIssue>> => {\n\t\t\tconst collected: ProviderIssue[] = [];\n\t\t\tlet cursor: string | undefined;\n\t\t\tlet status: JiraServerProjectIssuesDrain<ProviderIssue>['status'] = 'complete';\n\t\t\tlet metadata: CollectionMetadata | undefined;\n\t\t\tfor (let i = 0; i < maxPagesPerRequest; i++) {\n\t\t\t\tlet result: Awaited<ReturnType<typeof api.getJiraServerIssuesForProjectPaged>> | undefined;\n\t\t\t\ttry {\n\t\t\t\t\tresult = await api.getJiraServerIssuesForProjectPaged(tokenWithInfo, baseUrl, projectJqlKey, {\n\t\t\t\t\t\t...scope,\n\t\t\t\t\t\tcursor: cursor,\n\t\t\t\t\t\tstates: states,\n\t\t\t\t\t\tsort: options?.sort,\n\t\t\t\t\t});\n\t\t\t\t} catch (ex) {\n\t\t\t\t\t// A project this connection can no longer see (deleted, or its browse permission removed) holds\n\t\t\t\t\t// no issues for it, and the project list that named it is stale: drop that list so the next read\n\t\t\t\t\t// re-reads it, instead of failing the whole provider on every read until a restart (#5907).\n\t\t\t\t\tif (collected.length === 0 && isJiraMissingProjectError(ex)) {\n\t\t\t\t\t\tLogger.warn(\n\t\t\t\t\t\t\t`Jira Server project '${project.name}' (${project.id}) no longer exists or is not visible`,\n\t\t\t\t\t\t);\n\t\t\t\t\t\tthis._projects.delete(this.projectsCacheKey(session));\n\t\t\t\t\t\treturn { issues: [], status: 'complete' };\n\t\t\t\t\t}\n\n\t\t\t\t\t// A page failure after the first leaves the drained prefix intact; record it at the project\n\t\t\t\t\t// scope instead of re-throwing. With nothing fetched yet the original throw is preserved, so\n\t\t\t\t\t// the caller sees a hard error rather than an empty partial success.\n\t\t\t\t\tif (collected.length === 0) throw ex;\n\n\t\t\t\t\tstatus = 'incomplete';\n\t\t\t\t\tmetadata = mergeCollectionMetadata(metadata, {\n\t\t\t\t\t\tcompleteness: 'partial',\n\t\t\t\t\t\tfailures: [toCollectionScopeFailure(projectScope, ex)],\n\t\t\t\t\t});\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tif (result == null) {\n\t\t\t\t\tif (cursor == null) break;\n\n\t\t\t\t\tstatus = 'incomplete';\n\t\t\t\t\tmetadata = mergeCollectionMetadata(metadata, {\n\t\t\t\t\t\tcompleteness: 'partial',\n\t\t\t\t\t\tfailures: [\n\t\t\t\t\t\t\ttoCollectionScopeFailure(\n\t\t\t\t\t\t\t\tprojectScope,\n\t\t\t\t\t\t\t\tnew Error('Jira Server returned no page after advertising a continuation'),\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t],\n\t\t\t\t\t});\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tcollected.push(...result.data);\n\t\t\t\tif (!result.hasMore) break;\n\n\t\t\t\t// More pages are claimed but no advancing cursor came back: the drain can't continue, so flag it\n\t\t\t\t// rather than stopping silently.\n\t\t\t\tif (result.nextCursor == null || result.nextCursor === cursor) {\n\t\t\t\t\tstatus = 'incomplete';\n\t\t\t\t\tmetadata = mergeCollectionMetadata(metadata, {\n\t\t\t\t\t\tcompleteness: 'partial',\n\t\t\t\t\t\tfailures: [\n\t\t\t\t\t\t\ttoCollectionScopeFailure(\n\t\t\t\t\t\t\t\tprojectScope,\n\t\t\t\t\t\t\t\tnew Error('Jira Server returned no advancing issue continuation'),\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t],\n\t\t\t\t\t});\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tcursor = result.nextCursor;\n\t\t\t\t// Pages remain but this was the last allowed iteration: the backstop stopped the drain, which a\n\t\t\t\t// narrower scope would avoid.\n\t\t\t\tif (i === maxPagesPerRequest - 1) {\n\t\t\t\t\tstatus = 'backstop';\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn { issues: collected, status: status, metadata: metadata };\n\t\t};\n\n\t\t/**\n\t\t * Every clause is scoped by the HANDLE, and `options.userId` is deliberately ignored here — the one\n\t\t * place this provider diverges from Jira Cloud's, which applies it to `assignee`/`creator` (#5857).\n\t\t *\n\t\t * The two are not interchangeable because `Account.id` means something different per deployment. On\n\t\t * Cloud it is the `accountId`, which JQL resolves, so scoping by it fixes the deactivated-account case\n\t\t * where the display name no longer resolves. On Server it is the user KEY (`JIRAUSER10224`), which JQL\n\t\t * does not resolve at all: measured against a live Jira Server 8.20.13 site, `assignee in\n\t\t * (\"JIRAUSER10224\")` returns 0 with `\"The value 'JIRAUSER10224' does not exist for the field\n\t\t * 'assignee'\"` — the identical warning a genuinely nonexistent user produces — while the username and\n\t\t * the display name both return the real 4.\n\t\t *\n\t\t * So applying `userId` here would silently empty the read rather than harden it. Do not \"fix\" this\n\t\t * asymmetry to match Cloud without re-measuring against a Server instance.\n\t\t *\n\t\t * The handle itself is already the right one: `provider-apis` 0.60.0 maps `Account.username` from the\n\t\t * Server payload's `name` (falling back to `displayName` when absent), so these clauses are scoped by the\n\t\t * real username rather than a display name (GKDEV-3629). That is what makes the `mention` clause work at\n\t\t * all — it is a free-text `comment ~ \"...\"` over comment bodies, and a Jira mention is stored as\n\t\t * `[~username]`, which a display name never matches.\n\t\t */\n\t\tconst getSearchedUserIssuesForFilter = async (\n\t\t\tuser: string,\n\t\t\tfilter: IssueFilter,\n\t\t): Promise<JiraServerProjectIssuesDrain<IssueShape>> => {\n\t\t\tconst result = await drainIssues({\n\t\t\t\tauthorLogin: filter === IssueFilter.Author ? user : undefined,\n\t\t\t\tassigneeLogins: filter === IssueFilter.Assignee ? [user] : undefined,\n\t\t\t\tmentionLogin: filter === IssueFilter.Mention ? user : undefined,\n\t\t\t});\n\n\t\t\treturn {\n\t\t\t\tissues: result.issues\n\t\t\t\t\t.map(issue => toIssueShape(issue, this))\n\t\t\t\t\t.filter((r): r is IssueShape => r !== undefined),\n\t\t\t\tstatus: result.status,\n\t\t\t\tmetadata: result.metadata,\n\t\t\t};\n\t\t};\n\n\t\tif (options?.user != null) {\n\t\t\tconst user = options.user;\n\t\t\t// A resolved user always scopes the read, defaulting to \"my issues\" when no explicit filters are\n\t\t\t// given — otherwise it would fall through to the unscoped fetch and return the whole project (#5438).\n\t\t\tconst filters = options.filters?.length ? options.filters : [IssueFilter.Assignee];\n\t\t\tconst settled = await Promise.allSettled(\n\t\t\t\tfilters.map(filter => getSearchedUserIssuesForFilter(user, filter)),\n\t\t\t);\n\n\t\t\t// Every branch rejected: the read failed outright, so propagate the first reason as-is (not wrapped)\n\t\t\t// to keep its auth/rate-limit classification, and log the rest, which would otherwise be discarded.\n\t\t\tif (settled.every(r => r.status === 'rejected')) {\n\t\t\t\tfor (let i = 1; i < settled.length; i++) {\n\t\t\t\t\tconst outcome = settled[i];\n\t\t\t\t\tif (outcome.status === 'rejected') {\n\t\t\t\t\t\tLogger.error(\n\t\t\t\t\t\t\toutcome.reason,\n\t\t\t\t\t\t\t`getProviderIssuesForProjectWithTruncation: filter '${filters[i]}' failed`,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tthrow settled[0].status === 'rejected' ? settled[0].reason : new Error('Jira Server issue read failed');\n\t\t\t}\n\n\t\t\tlet truncated = false;\n\t\t\tlet metadata: CollectionMetadata | undefined;\n\t\t\tconst resultsById = new Map<string, IssueShape>();\n\t\t\tfor (let i = 0; i < settled.length; i++) {\n\t\t\t\tconst outcome = settled[i];\n\t\t\t\tconst filter = filters[i];\n\t\t\t\t// One rejected branch with a succeeding sibling means this project's issues are incomplete: keep\n\t\t\t\t// the sibling results and record a per-filter failure so the facade can warn on it specifically.\n\t\t\t\tif (outcome.status !== 'fulfilled') {\n\t\t\t\t\t// Identical for every branch, so degrading it would report one failure per filter for a\n\t\t\t\t\t// single invalid call — see `throwIfCallerContractError`.\n\t\t\t\t\tthrowIfCallerContractError(outcome.reason);\n\n\t\t\t\t\ttruncated = true;\n\t\t\t\t\tconst failure = toCollectionScopeFailure(projectScope, outcome.reason);\n\t\t\t\t\tmetadata = mergeCollectionMetadata(metadata, {\n\t\t\t\t\t\tcompleteness: 'partial',\n\t\t\t\t\t\tfailures: [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t...failure,\n\t\t\t\t\t\t\t\tmessage: `Issue filter '${filter}' could not be read${\n\t\t\t\t\t\t\t\t\tfailure.message != null ? `: ${failure.message}` : ''\n\t\t\t\t\t\t\t\t}`,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t],\n\t\t\t\t\t});\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tif (outcome.value.status !== 'complete') {\n\t\t\t\t\ttruncated = true;\n\t\t\t\t}\n\t\t\t\tif (outcome.value.metadata != null) {\n\t\t\t\t\tmetadata = mergeCollectionMetadata(metadata, outcome.value.metadata);\n\t\t\t\t}\n\t\t\t\tfor (const resultIssue of outcome.value.issues) {\n\t\t\t\t\tif (!resultsById.has(resultIssue.id)) {\n\t\t\t\t\t\tresultsById.set(resultIssue.id, resultIssue);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// `recovery: 'none'` for the whole set: the filter branches merge here, so even a pure backstop in one\n\t\t\t// branch is not fixable by narrowing this read's scope.\n\t\t\treturn truncated\n\t\t\t\t? { values: [...resultsById.values()], truncated: true, recovery: 'none', metadata: metadata }\n\t\t\t\t: { values: [...resultsById.values()], truncated: false, metadata: metadata };\n\t\t}\n\n\t\tconst unscoped = await drainIssues({});\n\t\tconst values = unscoped.issues\n\t\t\t.map(issue => toIssueShape(issue, this))\n\t\t\t.filter((result): result is IssueShape => result !== undefined);\n\t\treturn unscoped.status !== 'complete'\n\t\t\t? {\n\t\t\t\t\tvalues: values,\n\t\t\t\t\ttruncated: true,\n\t\t\t\t\trecovery: unscoped.status === 'backstop' ? 'narrow-scope' : 'none',\n\t\t\t\t\tmetadata: unscoped.metadata,\n\t\t\t\t}\n\t\t\t: { values: values, truncated: false, metadata: unscoped.metadata };\n\t}\n\n\tprotected override async searchProviderMyIssues(\n\t\tsession: ProviderAuthenticationSession,\n\t\tresources?: JiraServerResourceDescriptor[],\n\t\tcancellation?: AbortSignal,\n\t): Promise<IssueShape[] | undefined> {\n\t\treturn (await this.searchProviderMyIssuesWithTruncation(session, resources, cancellation))?.values;\n\t}\n\n\t/**\n\t * Account-wide \"my issues\" for this instance, drained to the backstop and reporting whether it got there.\n\t *\n\t * The override exists for the same reason the project drain reports `status`: the default\n\t * implementation wraps whatever the normalized read returned as `truncated: false`, so a run that stopped\n\t * at `maxPagesPerRequest` with pages left, or on a continuation the server never advanced, would be\n\t * published as a complete account. Cancellation is NOT truncation — the caller asked for the stop and\n\t * already knows the prefix is partial — so it breaks without setting the flag.\n\t */\n\tprotected override async searchProviderMyIssuesWithTruncation(\n\t\tsession: ProviderAuthenticationSession,\n\t\t_resources?: JiraServerResourceDescriptor[],\n\t\tcancellation?: AbortSignal,\n\t\toptions?: SearchMyIssuesOptions,\n\t): Promise<AccountWideIssuesResult | undefined> {\n\t\tconst api = await this.getProvidersApi();\n\t\tconst states = toProviderIssueStates(options?.state);\n\t\tconst tokenWithInfo = toTokenWithInfo(this.id, session);\n\t\tconst baseUrl = this.apiBaseUrlFor(session);\n\n\t\t// Errors propagate, unlike Jira Cloud's sibling. Cloud swallows per resource because it loops over the\n\t\t// account's many sites and one bad site must not discard the others; this connection addresses exactly\n\t\t// one instance, so there is no sibling to protect and swallowing would turn every failure — an expired\n\t\t// token, an unreachable host — into an empty success. `searchMyIssuesWithTruncationResult` recovers the\n\t\t// throw into `{ error }`, which is what lets the caller warn instead of reporting \"no issues\".\n\t\t// Only the provider: the instance is this connection's single resource, so naming it would publish a\n\t\t// failure of the whole connection as one confined to a sub-scope (see `ProviderWarning.scope`).\n\t\tconst accountScope = { providerId: this.id };\n\t\tconst results: IssueShape[] = [];\n\t\tlet cursor: string | undefined;\n\t\tlet truncated = false;\n\t\tlet metadata: CollectionMetadata | undefined;\n\n\t\tconst stopIncomplete = (reason: string) => {\n\t\t\ttruncated = true;\n\t\t\tmetadata = mergeCollectionMetadata(metadata, {\n\t\t\t\tcompleteness: 'partial',\n\t\t\t\tfailures: [toCollectionScopeFailure(accountScope, new Error(reason))],\n\t\t\t});\n\t\t};\n\n\t\tfor (let i = 0; i < maxPagesPerRequest; i++) {\n\t\t\tif (cancellation?.aborted) break;\n\n\t\t\tconst page = await api.getJiraServerIssuesForCurrentUser(tokenWithInfo, baseUrl, {\n\t\t\t\tcursor: cursor,\n\t\t\t\tstates: states,\n\t\t\t});\n\t\t\tif (page == null) {\n\t\t\t\t// Nothing fetched yet and no page is an empty account, not a failure; after a continuation it\n\t\t\t\t// means the server dropped the drain mid-way.\n\t\t\t\tif (cursor != null) {\n\t\t\t\t\tstopIncomplete('Jira Server returned no page after advertising a continuation');\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tresults.push(\n\t\t\t\t...page.data.map(issue => toIssueShape(issue, this)).filter((r): r is IssueShape => r != null),\n\t\t\t);\n\n\t\t\tif (!page.hasMore) break;\n\n\t\t\t// More pages are claimed but no advancing cursor came back: the drain can't continue, so flag it\n\t\t\t// rather than stopping silently.\n\t\t\tif (page.nextCursor == null || page.nextCursor === cursor) {\n\t\t\t\tstopIncomplete('Jira Server returned no advancing issue continuation');\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcursor = page.nextCursor;\n\t\t\t// Pages remain but this was the last allowed iteration: the backstop stopped the drain.\n\t\t\tif (i === maxPagesPerRequest - 1) {\n\t\t\t\tstopIncomplete(\n\t\t\t\t\t`Jira Server account-wide issue drain stopped at the ${maxPagesPerRequest}-page backstop`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\treturn { values: results, truncated: truncated, metadata: metadata };\n\t}\n\n\tprotected override async getProviderLinkedIssueOrPullRequest(\n\t\tsession: ProviderAuthenticationSession,\n\t\t_resource: JiraServerResourceDescriptor,\n\t\t{ key }: { id: string; key: string },\n\t): Promise<IssueOrPullRequest | undefined> {\n\t\tconst api = await this.getProvidersApi();\n\t\tconst issue = await api.getJiraServerIssue(toTokenWithInfo(this.id, session), this.apiBaseUrlFor(session), key);\n\t\treturn issue != null ? toIssueShape(issue, this) : undefined;\n\t}\n\n\tprotected override async getProviderIssue(\n\t\tsession: ProviderAuthenticationSession,\n\t\t_resource: JiraServerResourceDescriptor,\n\t\tid: string,\n\t): Promise<Issue | undefined> {\n\t\tconst api = await this.getProvidersApi();\n\t\tconst apiResult = await api.getJiraServerIssue(\n\t\t\ttoTokenWithInfo(this.id, session),\n\t\t\tthis.apiBaseUrlFor(session),\n\t\t\tid,\n\t\t);\n\t\tconst issue = apiResult != null ? toIssueShape(apiResult, this) : undefined;\n\t\treturn issue != null ? { ...issue, type: 'issue' } : undefined;\n\t}\n\n\t/**\n\t * The point read behind `getIssuesBatch`'s tracker targets (#5872). `resourceId` is this connection's single\n\t * synthetic resource — the host itself, see {@link JiraServerResourceDescriptor} — so it selects nothing further\n\t * here: the host was chosen upstream by `domain` (or the connection's configured domain) and the request is\n\t * addressed to the SESSION's base URL like every other read. No `resourceUrl` is needed either, unlike Jira\n\t * Cloud: its REST `self` link is an API endpoint, whereas `getJiraServerIssue` builds the browser link from\n\t * the base URL it was addressed to. A key that names no issue resolves to `undefined`, a proven absence.\n\t */\n\tprotected override getProviderIssueByResourceId(\n\t\tsession: ProviderAuthenticationSession,\n\t\t_resourceId: string,\n\t\tid: string,\n\t\t_resourceUrl: string | undefined,\n\t): Promise<Issue | undefined> {\n\t\treturn this.getProviderIssue(session, this.resourceFor(session), id);\n\t}\n\n\t/**\n\t * Caches are keyed by the ADDRESS the read was made against as well as the token: one integration instance\n\t * exists per configured host and two hosts can hold the same token, so a token-only key would serve one\n\t * host's projects for the other — and the host alone is not enough either, because a connection re-pointed\n\t * to another context path on the same host keeps its token and would otherwise be served the old path's\n\t * projects.\n\t */\n\tprivate projectsCacheKey(session: ProviderAuthenticationSession): string {\n\t\treturn `${this.apiBaseUrlFor(session)}:${session.accessToken}`;\n\t}\n\n\toverride invalidateDiscoveryCaches(): void {\n\t\tthis._projects.clear();\n\t}\n\n\tprotected override providerOnDisconnect(): void {\n\t\tthis._projects.clear();\n\t}\n}\n"],"mappings":"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"}
|
|
@@ -6,7 +6,7 @@ import { Account } from "../../../git/models/author.js";
|
|
|
6
6
|
import { ProviderAuthenticationSession } from "../authentication/models.js";
|
|
7
7
|
import { IssuesCloudHostIntegrationId } from "../constants.js";
|
|
8
8
|
import { ProviderRefusal } from "../collectionMetadata.js";
|
|
9
|
-
import { IssuesForProjectOptions, ProjectIssuesDrain } from "../models/issueReads.js";
|
|
9
|
+
import { AccountWideIssuesResult, IssuesForProjectOptions, ProjectIssuesDrain, SearchMyIssuesOptions } from "../models/issueReads.js";
|
|
10
10
|
import { IssuesIntegration } from "../models/issuesIntegration.js";
|
|
11
11
|
import { ProviderApiCollectionResult } from "./models.js";
|
|
12
12
|
import { IntegrationAuthenticationProviderDescriptor } from "../authentication/integrationAuthenticationProvider.js";
|
|
@@ -61,6 +61,11 @@ export declare class JiraIntegration extends IssuesIntegration<IssuesCloudHostIn
|
|
|
61
61
|
protected getProviderIssuesForProject(session: ProviderAuthenticationSession, project: JiraProjectDescriptor, options?: IssuesForProjectOptions): Promise<IssueShape[] | undefined>;
|
|
62
62
|
protected getProviderIssuesForProjectWithTruncation(session: ProviderAuthenticationSession, project: JiraProjectDescriptor, options?: IssuesForProjectOptions): Promise<ProjectIssuesDrain | undefined>;
|
|
63
63
|
protected searchProviderMyIssues(session: ProviderAuthenticationSession, resources?: JiraOrganizationDescriptor[], cancellation?: AbortSignal): Promise<IssueShape[] | undefined>;
|
|
64
|
+
/**
|
|
65
|
+
* Overridden so `state` reaches the per-site read, which the default drops. Still reports `truncated: false`,
|
|
66
|
+
* as the default did: the per-site loop has no way to say it stopped early.
|
|
67
|
+
*/
|
|
68
|
+
protected searchProviderMyIssuesWithTruncation(session: ProviderAuthenticationSession, resources?: JiraOrganizationDescriptor[], cancellation?: AbortSignal, options?: SearchMyIssuesOptions): Promise<AccountWideIssuesResult | undefined>;
|
|
64
69
|
protected getProviderLinkedIssueOrPullRequest(session: ProviderAuthenticationSession, resource: JiraOrganizationDescriptor, { key }: {
|
|
65
70
|
id: string;
|
|
66
71
|
key: string;
|
|
@@ -5,7 +5,7 @@ import { throwIfCallerContractError, toCollectionScopeFailure } from "../collect
|
|
|
5
5
|
import { toTokenWithInfo } from "../authentication/models.js";
|
|
6
6
|
import "../providerFilters.js";
|
|
7
7
|
import { collectProviderPagedResult, mergeCollectionMetadata } from "./utils/providerPaging.js";
|
|
8
|
-
import { providersMetadata, toAccount, toIssueShape } from "./models.js";
|
|
8
|
+
import { providersMetadata, toAccount, toIssueShape, toProviderIssueStates } from "./models.js";
|
|
9
9
|
import { IssuesIntegration } from "../models/issuesIntegration.js";
|
|
10
10
|
import { DiscoveryCache } from "./utils/discoveryCache.js";
|
|
11
11
|
import * as l10n from "@vscode/l10n";
|
|
@@ -217,6 +217,7 @@ var JiraIntegration = class extends IssuesIntegration {
|
|
|
217
217
|
resourceId: project.resourceId,
|
|
218
218
|
projectId: project.name
|
|
219
219
|
};
|
|
220
|
+
const states = toProviderIssueStates(options?.state);
|
|
220
221
|
const drainIssues = async (scope) => {
|
|
221
222
|
const collected = [];
|
|
222
223
|
let cursor;
|
|
@@ -228,6 +229,7 @@ var JiraIntegration = class extends IssuesIntegration {
|
|
|
228
229
|
result = await api.getIssuesForProjectPaged(tokenWithInfo, project.name, project.resourceId, {
|
|
229
230
|
...scope,
|
|
230
231
|
cursor,
|
|
232
|
+
states,
|
|
231
233
|
sort: options?.sort
|
|
232
234
|
});
|
|
233
235
|
} catch (ex) {
|
|
@@ -352,6 +354,14 @@ var JiraIntegration = class extends IssuesIntegration {
|
|
|
352
354
|
};
|
|
353
355
|
}
|
|
354
356
|
async searchProviderMyIssues(session, resources, cancellation) {
|
|
357
|
+
return (await this.searchProviderMyIssuesWithTruncation(session, resources, cancellation))?.values;
|
|
358
|
+
}
|
|
359
|
+
/**
|
|
360
|
+
* Overridden so `state` reaches the per-site read, which the default drops. Still reports `truncated: false`,
|
|
361
|
+
* as the default did: the per-site loop has no way to say it stopped early.
|
|
362
|
+
*/
|
|
363
|
+
async searchProviderMyIssuesWithTruncation(session, resources, cancellation, options) {
|
|
364
|
+
const states = toProviderIssueStates(options?.state);
|
|
355
365
|
const myResources = resources ?? await this.getProviderResourcesForUser(session);
|
|
356
366
|
if (!myResources) return void 0;
|
|
357
367
|
const api = await this.getProvidersApi();
|
|
@@ -364,7 +374,10 @@ var JiraIntegration = class extends IssuesIntegration {
|
|
|
364
374
|
let requestCount = 0;
|
|
365
375
|
do {
|
|
366
376
|
if (cancellation?.aborted) break;
|
|
367
|
-
const resourceIssues = await api.getIssuesForResourceForCurrentUser(toTokenWithInfo(this.id, session), resource.id, {
|
|
377
|
+
const resourceIssues = await api.getIssuesForResourceForCurrentUser(toTokenWithInfo(this.id, session), resource.id, {
|
|
378
|
+
cursor,
|
|
379
|
+
states
|
|
380
|
+
});
|
|
368
381
|
requestCount += 1;
|
|
369
382
|
hasMore = resourceIssues.paging?.more ?? false;
|
|
370
383
|
cursor = resourceIssues.paging?.cursor;
|
|
@@ -375,7 +388,10 @@ var JiraIntegration = class extends IssuesIntegration {
|
|
|
375
388
|
_logger.error(ex, "searchProviderMyIssues");
|
|
376
389
|
}
|
|
377
390
|
}
|
|
378
|
-
return
|
|
391
|
+
return {
|
|
392
|
+
values: results,
|
|
393
|
+
truncated: false
|
|
394
|
+
};
|
|
379
395
|
}
|
|
380
396
|
async getProviderLinkedIssueOrPullRequest(session, resource, { key }) {
|
|
381
397
|
const issue = await (await this.getProvidersApi()).getIssue(toTokenWithInfo(this.id, session), {
|