@gitkraken/core-gitlens 0.9.3 → 0.9.5
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 +34 -1
- package/dist/git/models/issue.d.ts +25 -2
- package/dist/git/models/issue.js +3 -1
- package/dist/git/models/issue.js.map +1 -1
- package/dist/git/models/pullRequest.d.ts +55 -5
- package/dist/git/models/pullRequest.js +5 -1
- package/dist/git/models/pullRequest.js.map +1 -1
- package/dist/git/utils/issue.utils.d.ts +4 -0
- package/dist/git/utils/issue.utils.js +4 -0
- package/dist/git/utils/issue.utils.js.map +1 -1
- package/dist/git/utils/pullRequest.utils.d.ts +4 -0
- package/dist/git/utils/pullRequest.utils.js +8 -0
- package/dist/git/utils/pullRequest.utils.js.map +1 -1
- package/dist/plus/git-github/api/github.d.ts +64 -4
- package/dist/plus/git-github/api/github.js +232 -15
- package/dist/plus/git-github/api/github.js.map +1 -1
- package/dist/plus/git-github/models.d.ts +48 -6
- package/dist/plus/git-github/models.js +11 -10
- package/dist/plus/git-github/models.js.map +1 -1
- package/dist/plus/integrations/fieldPresence.d.ts +75 -0
- package/dist/plus/integrations/fieldPresence.js +381 -0
- package/dist/plus/integrations/fieldPresence.js.map +1 -0
- package/dist/plus/integrations/index.d.ts +6 -4
- package/dist/plus/integrations/index.js +2 -1
- package/dist/plus/integrations/index.js.map +1 -1
- package/dist/plus/integrations/integrationService.d.ts +13 -4
- package/dist/plus/integrations/integrationService.js.map +1 -1
- package/dist/plus/integrations/manager.d.ts +62 -7
- package/dist/plus/integrations/models/gitHostIntegration.d.ts +103 -9
- package/dist/plus/integrations/models/gitHostIntegration.js +116 -5
- package/dist/plus/integrations/models/gitHostIntegration.js.map +1 -1
- package/dist/plus/integrations/models/integration.d.ts +91 -4
- package/dist/plus/integrations/models/integration.js +56 -7
- package/dist/plus/integrations/models/integration.js.map +1 -1
- package/dist/plus/integrations/models/issueReads.d.ts +14 -0
- package/dist/plus/integrations/models/issuesIntegration.d.ts +44 -3
- package/dist/plus/integrations/models/issuesIntegration.js +51 -1
- package/dist/plus/integrations/models/issuesIntegration.js.map +1 -1
- package/dist/plus/integrations/providers/azure/azure.js +3 -3
- package/dist/plus/integrations/providers/azure/azure.js.map +1 -1
- package/dist/plus/integrations/providers/azure/models.d.ts +36 -4
- package/dist/plus/integrations/providers/azure/models.js +118 -8
- package/dist/plus/integrations/providers/azure/models.js.map +1 -1
- package/dist/plus/integrations/providers/azureDevOps.d.ts +102 -5
- package/dist/plus/integrations/providers/azureDevOps.js +264 -45
- package/dist/plus/integrations/providers/azureDevOps.js.map +1 -1
- package/dist/plus/integrations/providers/azureDevOpsServer.d.ts +30 -6
- package/dist/plus/integrations/providers/azureDevOpsServer.js +82 -21
- package/dist/plus/integrations/providers/azureDevOpsServer.js.map +1 -1
- package/dist/plus/integrations/providers/bitbucket/bitbucket.d.ts +21 -1
- package/dist/plus/integrations/providers/bitbucket/bitbucket.js +74 -10
- package/dist/plus/integrations/providers/bitbucket/bitbucket.js.map +1 -1
- package/dist/plus/integrations/providers/bitbucket/models.d.ts +45 -8
- package/dist/plus/integrations/providers/bitbucket/models.js +32 -9
- package/dist/plus/integrations/providers/bitbucket/models.js.map +1 -1
- package/dist/plus/integrations/providers/bitbucket-server/models.d.ts +2 -6
- package/dist/plus/integrations/providers/bitbucket-server/models.js +1 -3
- package/dist/plus/integrations/providers/bitbucket-server/models.js.map +1 -1
- package/dist/plus/integrations/providers/bitbucket-server/pullRequestSearch.d.ts +3 -1
- package/dist/plus/integrations/providers/bitbucket-server/pullRequestSearch.js +4 -1
- package/dist/plus/integrations/providers/bitbucket-server/pullRequestSearch.js.map +1 -1
- package/dist/plus/integrations/providers/bitbucket-server.d.ts +2 -2
- package/dist/plus/integrations/providers/bitbucket-server.js +7 -5
- package/dist/plus/integrations/providers/bitbucket-server.js.map +1 -1
- package/dist/plus/integrations/providers/bitbucket.d.ts +22 -2
- package/dist/plus/integrations/providers/bitbucket.js +29 -6
- package/dist/plus/integrations/providers/bitbucket.js.map +1 -1
- package/dist/plus/integrations/providers/github.d.ts +40 -3
- package/dist/plus/integrations/providers/github.js +133 -11
- package/dist/plus/integrations/providers/github.js.map +1 -1
- package/dist/plus/integrations/providers/gitlab/gitlab.d.ts +34 -1
- package/dist/plus/integrations/providers/gitlab/gitlab.js +118 -4
- package/dist/plus/integrations/providers/gitlab/gitlab.js.map +1 -1
- package/dist/plus/integrations/providers/gitlab/models.d.ts +70 -5
- package/dist/plus/integrations/providers/gitlab/models.js +106 -13
- package/dist/plus/integrations/providers/gitlab/models.js.map +1 -1
- package/dist/plus/integrations/providers/gitlab.d.ts +31 -2
- package/dist/plus/integrations/providers/gitlab.js +48 -8
- package/dist/plus/integrations/providers/gitlab.js.map +1 -1
- package/dist/plus/integrations/providers/jira-server.d.ts +3 -2
- package/dist/plus/integrations/providers/jira-server.js +18 -9
- package/dist/plus/integrations/providers/jira-server.js.map +1 -1
- package/dist/plus/integrations/providers/jira.d.ts +26 -2
- package/dist/plus/integrations/providers/jira.js +120 -9
- package/dist/plus/integrations/providers/jira.js.map +1 -1
- package/dist/plus/integrations/providers/jiraIssueByKey.d.ts +79 -0
- package/dist/plus/integrations/providers/jiraIssueByKey.js +58 -7
- package/dist/plus/integrations/providers/jiraIssueByKey.js.map +1 -1
- package/dist/plus/integrations/providers/linear.d.ts +19 -1
- package/dist/plus/integrations/providers/linear.js +105 -7
- package/dist/plus/integrations/providers/linear.js.map +1 -1
- package/dist/plus/integrations/providers/linearIssuesEtag.d.ts +42 -0
- package/dist/plus/integrations/providers/linearIssuesEtag.js +94 -0
- package/dist/plus/integrations/providers/linearIssuesEtag.js.map +1 -0
- package/dist/plus/integrations/providers/models.d.ts +70 -8
- package/dist/plus/integrations/providers/models.js +59 -21
- package/dist/plus/integrations/providers/models.js.map +1 -1
- package/dist/plus/integrations/providers/providerErrors.js +8 -1
- package/dist/plus/integrations/providers/providerErrors.js.map +1 -1
- package/dist/plus/integrations/providers/providersApi.d.ts +70 -4
- package/dist/plus/integrations/providers/providersApi.js +148 -4
- package/dist/plus/integrations/providers/providersApi.js.map +1 -1
- package/dist/plus/integrations/providers/pullRequestReviews.d.ts +13 -0
- package/dist/plus/integrations/providers/pullRequestReviews.js +30 -6
- package/dist/plus/integrations/providers/pullRequestReviews.js.map +1 -1
- package/dist/plus/integrations/providers/trello.js +11 -7
- package/dist/plus/integrations/providers/trello.js.map +1 -1
- package/dist/plus/integrations/providers/utils/providerPaging.d.ts +11 -0
- package/dist/plus/integrations/providers/utils/providerPaging.js +47 -1
- package/dist/plus/integrations/providers/utils/providerPaging.js.map +1 -1
- package/dist/plus/integrations/providers/utils.d.ts +1 -1
- package/dist/plus/integrations/reads/batchEtags.d.ts +69 -0
- package/dist/plus/integrations/reads/batchEtags.js +114 -0
- package/dist/plus/integrations/reads/batchEtags.js.map +1 -0
- package/dist/plus/integrations/reads/counts.js +2 -2
- package/dist/plus/integrations/reads/counts.js.map +1 -1
- package/dist/plus/integrations/reads/currentAccount.js +2 -2
- package/dist/plus/integrations/reads/currentAccount.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 +13 -4
- package/dist/plus/integrations/reads/drains.js +29 -5
- package/dist/plus/integrations/reads/drains.js.map +1 -1
- package/dist/plus/integrations/reads/etag.d.ts +26 -0
- package/dist/plus/integrations/reads/etag.js +108 -0
- package/dist/plus/integrations/reads/etag.js.map +1 -0
- 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/hierarchy.js +3 -3
- package/dist/plus/integrations/reads/hierarchy.js.map +1 -1
- package/dist/plus/integrations/reads/issueBatch.d.ts +47 -16
- package/dist/plus/integrations/reads/issueBatch.js +55 -87
- package/dist/plus/integrations/reads/issueBatch.js.map +1 -1
- package/dist/plus/integrations/reads/issueTracker.d.ts +10 -2
- package/dist/plus/integrations/reads/issueTracker.js +23 -6
- package/dist/plus/integrations/reads/issueTracker.js.map +1 -1
- package/dist/plus/integrations/reads/issues.d.ts +1 -1
- package/dist/plus/integrations/reads/issues.js +4 -4
- package/dist/plus/integrations/reads/issues.js.map +1 -1
- package/dist/plus/integrations/reads/pullRequestBatch.d.ts +44 -6
- package/dist/plus/integrations/reads/pullRequestBatch.js +31 -34
- package/dist/plus/integrations/reads/pullRequestBatch.js.map +1 -1
- package/dist/plus/integrations/reads/pullRequestBranches.d.ts +5 -2
- package/dist/plus/integrations/reads/pullRequestBranches.js +4 -3
- package/dist/plus/integrations/reads/pullRequestBranches.js.map +1 -1
- package/dist/plus/integrations/reads/pullRequests.js +8 -4
- package/dist/plus/integrations/reads/pullRequests.js.map +1 -1
- package/dist/plus/integrations/reads/sweeps.js +8 -4
- package/dist/plus/integrations/reads/sweeps.js.map +1 -1
- package/dist/plus/integrations/reads/warnings.d.ts +23 -8
- package/dist/plus/integrations/reads/warnings.js +43 -19
- package/dist/plus/integrations/reads/warnings.js.map +1 -1
- package/dist/plus/integrations/results.d.ts +12 -4
- package/dist/plus/integrations/results.js +2 -0
- package/dist/plus/integrations/results.js.map +1 -1
- package/docs/integrations.md +389 -23
- package/docs/kepler-read-api-parity.md +16 -2
- package/package.json +2 -2
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
import { chunk } from "../../../utils/array.js";
|
|
1
2
|
import { CancellationError } from "../../../utils/cancellation.js";
|
|
2
3
|
import { mapSettledBounded } from "../../../utils/promise.js";
|
|
4
|
+
import { PromiseCache } from "../../../utils/promiseCache.js";
|
|
3
5
|
import "../../../git/models/pullRequest.js";
|
|
4
6
|
import "../constants.js";
|
|
5
7
|
import { toCollectionScopeFailure } from "../collectionMetadata.js";
|
|
@@ -8,8 +10,11 @@ import "../providerFilters.js";
|
|
|
8
10
|
import { collectProviderPagedResult, flatSettledResultsOrThrow, mergeCollectionMetadata, resolveBranchPullRequests, selectBranchPullRequests } from "./utils/providerPaging.js";
|
|
9
11
|
import { decodePathSegment } from "../utils/domain.utils.js";
|
|
10
12
|
import { fromProviderIssue, fromProviderPullRequest, getProviderPullRequestIdentity, providerPullRequestMatchesSearch, providersMetadata, toIssueShape, toProviderPullRequestStates } from "./models.js";
|
|
13
|
+
import { toAzurePullRequestEtagFields, toAzureWorkItemEtagFields } from "./azure/models.js";
|
|
11
14
|
import { GitHostIntegration } from "../models/gitHostIntegration.js";
|
|
12
15
|
import { getAzurePullRequestIdentityFromMaybeUrl } from "./azure/azure.utils.js";
|
|
16
|
+
import { discoveryCacheTtl } from "./utils/discoveryCache.js";
|
|
17
|
+
import { GitPullRequestState } from "@gitkraken/provider-apis";
|
|
13
18
|
//#region ../plus/integrations/src/providers/azureDevOps.ts
|
|
14
19
|
function getAzureRepositoryIdentity(repo) {
|
|
15
20
|
const match = /^([^/]+)\/_git\/([^/]+)$/i.exec(repo.name);
|
|
@@ -49,6 +54,42 @@ function uniqueAzureNames(names) {
|
|
|
49
54
|
return unique;
|
|
50
55
|
}
|
|
51
56
|
/**
|
|
57
|
+
* Flags the reviews of `pr` that name one of the current user's groups (see `PullRequestReviewer.isMyGroup`), which a
|
|
58
|
+
* reviewer read finds through that group rather than through the user.
|
|
59
|
+
*/
|
|
60
|
+
function markMyGroupReviews(pr, groupIds) {
|
|
61
|
+
if (pr.reviews == null || !pr.reviews.some((r) => groupIds.has(r.reviewer.id))) return pr;
|
|
62
|
+
return {
|
|
63
|
+
...pr,
|
|
64
|
+
reviews: pr.reviews.map((r) => groupIds.has(r.reviewer.id) ? {
|
|
65
|
+
...r,
|
|
66
|
+
isMyGroup: true
|
|
67
|
+
} : r)
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* The reads a reviewer relationship over `states` (omitted: open) takes for `userId`, whose groups are `groupIds`:
|
|
72
|
+
*
|
|
73
|
+
* - Open pull requests: all of them, kept when one names the user, or names one of the user's groups and the user
|
|
74
|
+
* didn't write it. Azure's reviewer filter only matches the identity it names, never the groups that identity is in,
|
|
75
|
+
* and a group asked to review the user's own pull request isn't asking the user.
|
|
76
|
+
* - Closed and merged ones: Azure's own filter for the user. They are every pull request a project ever had, too many
|
|
77
|
+
* to read whole on every read, so a group's request is only followed while the pull request is open.
|
|
78
|
+
*/
|
|
79
|
+
function toAzureReviewerReads(userId, groupIds, states) {
|
|
80
|
+
const reads = [];
|
|
81
|
+
if (states == null || states.includes(GitPullRequestState.Open)) reads.push({
|
|
82
|
+
filter: { states: [GitPullRequestState.Open] },
|
|
83
|
+
keep: (pr) => pr.reviews?.some((r) => r.reviewer.id === userId || groupIds.has(r.reviewer.id) && pr.author?.id !== userId) ?? false
|
|
84
|
+
});
|
|
85
|
+
const closed = states?.filter((s) => s !== GitPullRequestState.Open) ?? [];
|
|
86
|
+
if (closed.length > 0) reads.push({ filter: {
|
|
87
|
+
reviewerId: userId,
|
|
88
|
+
states: closed
|
|
89
|
+
} });
|
|
90
|
+
return reads;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
52
93
|
* Matches an org/project descriptor against a caller-supplied name, mirroring the facade's own
|
|
53
94
|
* key/id/name comparison so `listIssuesPage({ org, project })` narrows on the same identifiers a consumer
|
|
54
95
|
* already got back from `listOrgs`/`listProjects`.
|
|
@@ -93,6 +134,44 @@ var AzureDevOpsIntegrationBase = class extends GitHostIntegration {
|
|
|
93
134
|
return (await this.getProviderCurrentAccount(session))?.id;
|
|
94
135
|
}
|
|
95
136
|
/**
|
|
137
|
+
* The current user's groups per credential, collection and user, kept for {@link discoveryCacheTtl} like the other
|
|
138
|
+
* discovered sets: membership changes rarely, and a sweep that reads every few minutes would otherwise pay for it
|
|
139
|
+
* every time. A failed read isn't kept, and a re-sync drops them all (see {@link invalidateDiscoveryCaches}).
|
|
140
|
+
*/
|
|
141
|
+
_reviewerGroupIds = new PromiseCache({
|
|
142
|
+
capacity: 50,
|
|
143
|
+
createTTL: discoveryCacheTtl
|
|
144
|
+
});
|
|
145
|
+
/**
|
|
146
|
+
* Every group the current user (`userId`, as {@link getFilterUserId} resolved it) is a member of in `collection`:
|
|
147
|
+
* teams and security groups, in any project, directly or through other groups. Two requests per collection,
|
|
148
|
+
* however many groups, and none while the last read is kept.
|
|
149
|
+
*/
|
|
150
|
+
getReviewerGroupIds(session, collection, userId) {
|
|
151
|
+
return this._reviewerGroupIds.getOrCreate(JSON.stringify([
|
|
152
|
+
this.discoveryKey(session),
|
|
153
|
+
collection.toLowerCase(),
|
|
154
|
+
userId
|
|
155
|
+
]), async () => {
|
|
156
|
+
const api = await this.getProvidersApi();
|
|
157
|
+
const { tokenWithInfo } = this.getApiOptions(session);
|
|
158
|
+
const ids = await api.getAzureGroupIdsForUser(tokenWithInfo, collection, userId, this.getCollectionApiOptions(session, collection));
|
|
159
|
+
return new Set(ids);
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
invalidateDiscoveryCaches() {
|
|
163
|
+
super.invalidateDiscoveryCaches();
|
|
164
|
+
this._reviewerGroupIds.clear();
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* For one read, the current account as each organization's pull requests identify it, given the account itself
|
|
168
|
+
* (`undefined` when it couldn't be read). Azure DevOps Services gives a person one id, so that is `account`; the
|
|
169
|
+
* server override resolves each collection's own, once per read.
|
|
170
|
+
*/
|
|
171
|
+
getOrganizationViewers(_session, account) {
|
|
172
|
+
return () => Promise.resolve(account);
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
96
175
|
* What the discovery caches (account, organizations, projects, and their stored copies) are keyed by. Azure DevOps
|
|
97
176
|
* Services has a single address, so the credential alone identifies what it discovers; the server override adds
|
|
98
177
|
* the installation address.
|
|
@@ -434,7 +513,7 @@ var AzureDevOpsIntegrationBase = class extends GitHostIntegration {
|
|
|
434
513
|
project: c.project
|
|
435
514
|
}, c.number, this.getCollectionApiOptions(session, c.owner));
|
|
436
515
|
if (issue == null) return void 0;
|
|
437
|
-
const shape = toIssueShape(issue, this);
|
|
516
|
+
const shape = toIssueShape(issue, this, { projection: "batch" });
|
|
438
517
|
if (shape == null) throw new Error(`Azure DevOps returned work item ${c.number} without a URL or change date`);
|
|
439
518
|
return shape;
|
|
440
519
|
});
|
|
@@ -447,6 +526,7 @@ var AzureDevOpsIntegrationBase = class extends GitHostIntegration {
|
|
|
447
526
|
async getProviderPullRequestsBatch(session, coordinates, options, _cancellation) {
|
|
448
527
|
const api = await this.getProvidersApi();
|
|
449
528
|
const { tokenWithInfo } = this.getApiOptions(session);
|
|
529
|
+
const viewerFor = this.getOrganizationViewers(session, options?.currentAccount);
|
|
450
530
|
return mapSettledBounded(coordinates, 6, async (c) => {
|
|
451
531
|
const pr = await api.getPullRequestForRepo(tokenWithInfo, {
|
|
452
532
|
namespace: c.owner,
|
|
@@ -456,7 +536,127 @@ var AzureDevOpsIntegrationBase = class extends GitHostIntegration {
|
|
|
456
536
|
...this.getCollectionApiOptions(session, c.owner),
|
|
457
537
|
includeRemoteInfo: true
|
|
458
538
|
});
|
|
459
|
-
|
|
539
|
+
if (pr == null) return void 0;
|
|
540
|
+
return fromProviderPullRequest(pr, this, {
|
|
541
|
+
currentAccount: await viewerFor(c.owner),
|
|
542
|
+
projection: "batch"
|
|
543
|
+
});
|
|
544
|
+
});
|
|
545
|
+
}
|
|
546
|
+
/**
|
|
547
|
+
* The cheap check behind the batch issue read's etags: one request per organization, project and
|
|
548
|
+
* {@link azureWorkItemsEtagFieldsMaxIds} distinct ids, where {@link getProviderIssuesBatch} sends one per target,
|
|
549
|
+
* asking only for each work item's change state. A request that throws rejects only its own targets' slots, with
|
|
550
|
+
* its error classified as the full read's would be.
|
|
551
|
+
*
|
|
552
|
+
* Never proves an absence. Azure silently leaves out every id it can't return, whether missing, hidden or failing,
|
|
553
|
+
* so such a target's slot is rejected and the full read, whose not-found does prove an absence, decides. So is a
|
|
554
|
+
* work item in another project, which the full read fails. When no target was answered and nothing failed, the
|
|
555
|
+
* check declines instead, so a batch of only such ids costs a full read, not a failure.
|
|
556
|
+
*
|
|
557
|
+
* A work item has no reactions, and its full row no count, so `'reactions'` reads nothing and costs nothing.
|
|
558
|
+
*/
|
|
559
|
+
async getProviderIssuesEtagFields(session, coordinates, _options, _cancellation) {
|
|
560
|
+
const slots = new Array(coordinates.length);
|
|
561
|
+
let answered = false;
|
|
562
|
+
let failed = false;
|
|
563
|
+
const byProject = /* @__PURE__ */ new Map();
|
|
564
|
+
for (const [index, c] of coordinates.entries()) {
|
|
565
|
+
if (c.project == null) {
|
|
566
|
+
slots[index] = {
|
|
567
|
+
status: "rejected",
|
|
568
|
+
reason: /* @__PURE__ */ new Error(`Azure DevOps needs a project to read work item ${c.number}`)
|
|
569
|
+
};
|
|
570
|
+
failed = true;
|
|
571
|
+
continue;
|
|
572
|
+
}
|
|
573
|
+
const key = JSON.stringify([c.owner, c.project]);
|
|
574
|
+
let group = byProject.get(key);
|
|
575
|
+
if (group == null) {
|
|
576
|
+
group = {
|
|
577
|
+
owner: c.owner,
|
|
578
|
+
project: c.project,
|
|
579
|
+
ids: /* @__PURE__ */ new Map()
|
|
580
|
+
};
|
|
581
|
+
byProject.set(key, group);
|
|
582
|
+
}
|
|
583
|
+
let indices = group.ids.get(c.number);
|
|
584
|
+
if (indices == null) {
|
|
585
|
+
indices = [];
|
|
586
|
+
group.ids.set(c.number, indices);
|
|
587
|
+
}
|
|
588
|
+
indices.push(index);
|
|
589
|
+
}
|
|
590
|
+
const requests = [...byProject.values()].flatMap((group) => chunk([...group.ids], 200).map((entries) => ({
|
|
591
|
+
...group,
|
|
592
|
+
entries
|
|
593
|
+
})));
|
|
594
|
+
if (!requests.length) return slots;
|
|
595
|
+
const api = await this.getProvidersApi();
|
|
596
|
+
const { tokenWithInfo } = this.getApiOptions(session);
|
|
597
|
+
const answers = await mapSettledBounded(requests, 6, (r) => api.getAzureWorkItemsEtagFields(tokenWithInfo, {
|
|
598
|
+
namespace: r.owner,
|
|
599
|
+
project: r.project
|
|
600
|
+
}, r.entries.map(([id]) => id), this.getCollectionApiOptions(session, r.owner)));
|
|
601
|
+
for (const [i, answer] of answers.entries()) {
|
|
602
|
+
const { project, entries } = requests[i];
|
|
603
|
+
if (answer.status === "rejected") {
|
|
604
|
+
failed = true;
|
|
605
|
+
for (const [, indices] of entries) for (const index of indices) slots[index] = answer;
|
|
606
|
+
continue;
|
|
607
|
+
}
|
|
608
|
+
const workItems = /* @__PURE__ */ new Map();
|
|
609
|
+
for (const workItem of answer.value) workItems.set(workItem.id, workItem);
|
|
610
|
+
for (const [id, indices] of entries) {
|
|
611
|
+
const workItem = workItems.get(id);
|
|
612
|
+
let slot;
|
|
613
|
+
if (workItem == null) slot = {
|
|
614
|
+
status: "rejected",
|
|
615
|
+
reason: /* @__PURE__ */ new Error(`Azure DevOps did not return work item ${id}`)
|
|
616
|
+
};
|
|
617
|
+
else try {
|
|
618
|
+
slot = {
|
|
619
|
+
status: "fulfilled",
|
|
620
|
+
value: toAzureWorkItemEtagFields(workItem, project)
|
|
621
|
+
};
|
|
622
|
+
answered = true;
|
|
623
|
+
} catch (ex) {
|
|
624
|
+
slot = {
|
|
625
|
+
status: "rejected",
|
|
626
|
+
reason: ex
|
|
627
|
+
};
|
|
628
|
+
failed = true;
|
|
629
|
+
}
|
|
630
|
+
for (const index of indices) slots[index] = slot;
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
return answered || failed ? slots : void 0;
|
|
634
|
+
}
|
|
635
|
+
/**
|
|
636
|
+
* The cheap check behind the batch pull request read's etags: still one request per target, as Azure DevOps has
|
|
637
|
+
* no read of several pull requests by id, but only the one {@link getProviderPullRequestsBatch} sends first —
|
|
638
|
+
* not the repository read provider-apis adds to it for clone URLs, once per pull request. Settled per target,
|
|
639
|
+
* with absence proven by the same not-found rule as the full read.
|
|
640
|
+
*
|
|
641
|
+
* Azure DevOps reports no update time, so a full row's `updatedDate` is its close time, else its creation time,
|
|
642
|
+
* and moves only when it closes. Its etag therefore adds a `revision` of the fields that change without it —
|
|
643
|
+
* title, description, target branch, and every reviewer and their vote — read off the same response at no extra
|
|
644
|
+
* cost. It sees a pull request's state, draft flag, head commit and those fields, and, when included, its
|
|
645
|
+
* mergeability and its required reviewers' review decision; never a comment or a label, which its full row
|
|
646
|
+
* doesn't carry either. A full row carries no check rollup, so the `checks` include adds nothing and costs nothing.
|
|
647
|
+
*/
|
|
648
|
+
async getProviderPullRequestsEtagFields(session, coordinates, options, _cancellation) {
|
|
649
|
+
const api = await this.getProvidersApi();
|
|
650
|
+
const { tokenWithInfo } = this.getApiOptions(session);
|
|
651
|
+
const etagIncludes = options.etagIncludes ?? [];
|
|
652
|
+
return mapSettledBounded(coordinates, 6, async (c) => {
|
|
653
|
+
if (c.project == null) throw new Error(`Azure DevOps needs a project to read pull request ${c.number}`);
|
|
654
|
+
const pr = await api.getAzurePullRequest(tokenWithInfo, {
|
|
655
|
+
namespace: c.owner,
|
|
656
|
+
project: c.project,
|
|
657
|
+
name: c.repo
|
|
658
|
+
}, c.number, this.getCollectionApiOptions(session, c.owner));
|
|
659
|
+
return pr != null ? toAzurePullRequestEtagFields(pr, etagIncludes) : void 0;
|
|
460
660
|
});
|
|
461
661
|
}
|
|
462
662
|
/**
|
|
@@ -518,42 +718,13 @@ var AzureDevOpsIntegrationBase = class extends GitHostIntegration {
|
|
|
518
718
|
return (await this.authenticationService.apis.azure)?.getRepositoryMetadata(this, toTokenWithInfo(this.id, session), repo.owner, repo.name, { baseUrl: getAzureRepositoryApiBaseUrl(this.apiBaseUrlFor(session), repo) }, cancellation);
|
|
519
719
|
}
|
|
520
720
|
async searchProviderMyPullRequests(session, repos, _cancellation, options) {
|
|
521
|
-
const api = await this.getProvidersApi();
|
|
522
721
|
if (repos != null) return;
|
|
523
|
-
const
|
|
524
|
-
if (
|
|
525
|
-
const
|
|
526
|
-
if (
|
|
527
|
-
const
|
|
528
|
-
|
|
529
|
-
const repoDescriptors = [...(await this.getRepoDescriptorsForProjects(session, projects.values) ?? /* @__PURE__ */ new Map()).values()].filter((r) => r != null).flat();
|
|
530
|
-
const { tokenWithInfo } = this.getApiOptions(session);
|
|
531
|
-
const assignedPrs = [];
|
|
532
|
-
const authoredPrs = [];
|
|
533
|
-
for (const org of uniqueAzureNames(projects.values.map((p) => p.resourceName))) {
|
|
534
|
-
const userId = await this.getFilterUserId(session, org);
|
|
535
|
-
if (userId == null) continue;
|
|
536
|
-
const projectInputs = projects.values.filter((p) => sameAzureName(p.resourceName, org)).map((p) => ({
|
|
537
|
-
namespace: p.resourceName,
|
|
538
|
-
project: p.name
|
|
539
|
-
}));
|
|
540
|
-
const orgOptions = this.getCollectionApiOptions(session, org);
|
|
541
|
-
const reads = [[{ assigneeLogins: [userId] }, assignedPrs], [{ authorLogin: userId }, authoredPrs]];
|
|
542
|
-
for (const [filter, into] of reads) {
|
|
543
|
-
const result = await api.getPullRequestsForAzureProjects(tokenWithInfo, projectInputs, {
|
|
544
|
-
...orgOptions,
|
|
545
|
-
...filter,
|
|
546
|
-
states
|
|
547
|
-
});
|
|
548
|
-
into.push(...result.values.map((pr) => this.fromAzureProviderPullRequest(pr, repoDescriptors, projects.values)));
|
|
549
|
-
}
|
|
550
|
-
}
|
|
551
|
-
const prsByIdentity = /* @__PURE__ */ new Map();
|
|
552
|
-
for (const pr of [...authoredPrs, ...assignedPrs]) {
|
|
553
|
-
const identity = `${pr.repository.owner}/${pr.repository.id || pr.repository.repo}#${pr.id}`;
|
|
554
|
-
if (!prsByIdentity.has(identity)) prsByIdentity.set(identity, pr);
|
|
555
|
-
}
|
|
556
|
-
return [...prsByIdentity.values()];
|
|
722
|
+
const result = await this.getProviderMyPullRequestsForUser(session, { state: options?.state != null ? [options.state] : void 0 });
|
|
723
|
+
if (result == null) return void 0;
|
|
724
|
+
const projects = (await this.getProviderProjectsForResources(session, await this.getProviderResourcesForUser(session) ?? [])).values;
|
|
725
|
+
if (projects.length === 0) return void 0;
|
|
726
|
+
const repoDescriptors = [...(await this.getRepoDescriptorsForProjects(session, projects) ?? /* @__PURE__ */ new Map()).values()].filter((r) => r != null).flat();
|
|
727
|
+
return result.values.map((pr) => this.fromAzureProviderPullRequest(pr, repoDescriptors, projects, "search"));
|
|
557
728
|
}
|
|
558
729
|
async getProviderMyPullRequestsForUser(session, options) {
|
|
559
730
|
const api = await this.getProvidersApi();
|
|
@@ -582,8 +753,8 @@ var AzureDevOpsIntegrationBase = class extends GitHostIntegration {
|
|
|
582
753
|
for (let i = 0; i < maxPagesPerProject; i++) try {
|
|
583
754
|
const result = await api.getPullRequestsForAzureProject(tokenWithInfo, project, {
|
|
584
755
|
...this.getCollectionApiOptions(session, project.namespace),
|
|
585
|
-
...filter,
|
|
586
756
|
states,
|
|
757
|
+
...filter,
|
|
587
758
|
page
|
|
588
759
|
});
|
|
589
760
|
if (result == null) {
|
|
@@ -625,6 +796,8 @@ var AzureDevOpsIntegrationBase = class extends GitHostIntegration {
|
|
|
625
796
|
const wantReviewed = requested == null || requested.has("assignee") || requested.has("review-requested");
|
|
626
797
|
const userIds = /* @__PURE__ */ new Map();
|
|
627
798
|
for (const org of uniqueAzureNames(projects.values.map((p) => p.resourceName))) userIds.set(org.toLowerCase(), await this.getFilterUserId(session, org));
|
|
799
|
+
const groups = wantReviewed ? await this.getReviewerGroupsByOrganization(session, projects.values, userIds, failures) : void 0;
|
|
800
|
+
if (groups?.incomplete) truncated = true;
|
|
628
801
|
const outcomes = await Promise.all(projects.values.flatMap((p) => {
|
|
629
802
|
const project = {
|
|
630
803
|
namespace: p.resourceName,
|
|
@@ -643,7 +816,13 @@ var AzureDevOpsIntegrationBase = class extends GitHostIntegration {
|
|
|
643
816
|
}
|
|
644
817
|
const drains = [];
|
|
645
818
|
if (wantAuthored) drains.push(drainProject(project, scope, { authorLogin: userId }));
|
|
646
|
-
if (wantReviewed)
|
|
819
|
+
if (wantReviewed) {
|
|
820
|
+
const groupIds = groups?.byOrganization.get(p.resourceName.toLowerCase()) ?? /* @__PURE__ */ new Set();
|
|
821
|
+
for (const { filter, keep } of toAzureReviewerReads(userId, groupIds, states)) drains.push(drainProject(project, scope, filter).then((o) => keep != null ? {
|
|
822
|
+
...o,
|
|
823
|
+
prs: o.prs.filter(keep)
|
|
824
|
+
} : o));
|
|
825
|
+
}
|
|
647
826
|
return drains;
|
|
648
827
|
}));
|
|
649
828
|
const prsByIdentity = /* @__PURE__ */ new Map();
|
|
@@ -658,8 +837,9 @@ var AzureDevOpsIntegrationBase = class extends GitHostIntegration {
|
|
|
658
837
|
completeness: "partial",
|
|
659
838
|
failures
|
|
660
839
|
} : void 0, projects.metadata);
|
|
840
|
+
const groupIds = groups?.groupIds ?? /* @__PURE__ */ new Set();
|
|
661
841
|
return {
|
|
662
|
-
values:
|
|
842
|
+
values: Array.from(prsByIdentity.values(), (pr) => markMyGroupReviews(pr, groupIds)),
|
|
663
843
|
paging: {
|
|
664
844
|
cursor: "{}",
|
|
665
845
|
more: false,
|
|
@@ -668,6 +848,38 @@ var AzureDevOpsIntegrationBase = class extends GitHostIntegration {
|
|
|
668
848
|
metadata
|
|
669
849
|
};
|
|
670
850
|
}
|
|
851
|
+
/**
|
|
852
|
+
* Per organization (keyed by its lowercased name), every group the user is in (see {@link getReviewerGroupIds}),
|
|
853
|
+
* and their union. An organization whose groups can't be read gets none and is recorded in `failures`, with the
|
|
854
|
+
* read reported `incomplete`: what only its groups review is missing, while what names the user is still read.
|
|
855
|
+
* Organizations whose user could not be resolved are left out; the read already reports them.
|
|
856
|
+
*/
|
|
857
|
+
async getReviewerGroupsByOrganization(session, projects, userIds, failures) {
|
|
858
|
+
const byOrganization = /* @__PURE__ */ new Map();
|
|
859
|
+
const groupIds = /* @__PURE__ */ new Set();
|
|
860
|
+
let incomplete = false;
|
|
861
|
+
await Promise.all(uniqueAzureNames(projects.map((p) => p.resourceName)).map(async (org) => {
|
|
862
|
+
const userId = userIds.get(org.toLowerCase());
|
|
863
|
+
if (userId == null) return;
|
|
864
|
+
try {
|
|
865
|
+
const ids = await this.getReviewerGroupIds(session, org, userId);
|
|
866
|
+
byOrganization.set(org.toLowerCase(), ids);
|
|
867
|
+
for (const id of ids) groupIds.add(id);
|
|
868
|
+
} catch (ex) {
|
|
869
|
+
const resourceId = projects.find((p) => sameAzureName(p.resourceName, org)).resourceId;
|
|
870
|
+
failures.push(toCollectionScopeFailure({
|
|
871
|
+
providerId: this.id,
|
|
872
|
+
resourceId
|
|
873
|
+
}, ex));
|
|
874
|
+
incomplete = true;
|
|
875
|
+
}
|
|
876
|
+
}));
|
|
877
|
+
return {
|
|
878
|
+
byOrganization,
|
|
879
|
+
groupIds,
|
|
880
|
+
incomplete
|
|
881
|
+
};
|
|
882
|
+
}
|
|
671
883
|
async searchProviderPullRequests(session, searchQuery, repos, cancellation, options) {
|
|
672
884
|
if (cancellation?.aborted) throw new CancellationError();
|
|
673
885
|
const orgs = await this.getProviderResourcesForUser(session);
|
|
@@ -719,7 +931,7 @@ var AzureDevOpsIntegrationBase = class extends GitHostIntegration {
|
|
|
719
931
|
return values;
|
|
720
932
|
}));
|
|
721
933
|
if (cancellation?.aborted) throw new CancellationError();
|
|
722
|
-
return [...new Map(providerPullRequests.map((pr) => [pr.url ?? `${pr.repository.id}:${pr.id}`, pr])).values()].filter((pr) => providerPullRequestMatchesSearch(pr, searchQuery)).map((pr) => this.fromAzureProviderPullRequest(pr, repoDescriptors, projects));
|
|
934
|
+
return [...new Map(providerPullRequests.map((pr) => [pr.url ?? `${pr.repository.id}:${pr.id}`, pr])).values()].filter((pr) => providerPullRequestMatchesSearch(pr, searchQuery)).map((pr) => this.fromAzureProviderPullRequest(pr, repoDescriptors, projects, "text-search"));
|
|
723
935
|
}
|
|
724
936
|
async searchProviderMyIssues(session, repos) {
|
|
725
937
|
return (await this.searchProviderMyIssuesWithTruncation(session, repos))?.values;
|
|
@@ -779,7 +991,10 @@ var AzureDevOpsIntegrationBase = class extends GitHostIntegration {
|
|
|
779
991
|
projectId: p.name
|
|
780
992
|
});
|
|
781
993
|
return {
|
|
782
|
-
issues: result.values.map((i) => fromProviderIssue(i, this, {
|
|
994
|
+
issues: result.values.map((i) => fromProviderIssue(i, this, {
|
|
995
|
+
project: p,
|
|
996
|
+
projection: "account"
|
|
997
|
+
})),
|
|
783
998
|
projectKey: `${p.resourceId}:${p.id}`,
|
|
784
999
|
truncated: result.truncated ?? false,
|
|
785
1000
|
metadata: result.metadata
|
|
@@ -887,8 +1102,9 @@ var AzureDevOpsIntegrationBase = class extends GitHostIntegration {
|
|
|
887
1102
|
this._organizations = void 0;
|
|
888
1103
|
this._projects = void 0;
|
|
889
1104
|
this._accounts = void 0;
|
|
1105
|
+
this._reviewerGroupIds.clear();
|
|
890
1106
|
}
|
|
891
|
-
fromAzureProviderPullRequest(azurePullRequest, repoDescriptors, projectDescriptors) {
|
|
1107
|
+
fromAzureProviderPullRequest(azurePullRequest, repoDescriptors, projectDescriptors, projection) {
|
|
892
1108
|
const baseRepoDescriptor = repoDescriptors.find((r) => r.id === azurePullRequest.repository.id);
|
|
893
1109
|
const headRepoDescriptor = azurePullRequest.headRepository != null ? repoDescriptors.find((r) => r.id === azurePullRequest.headRepository.id) : void 0;
|
|
894
1110
|
let project;
|
|
@@ -909,7 +1125,10 @@ var AzureDevOpsIntegrationBase = class extends GitHostIntegration {
|
|
|
909
1125
|
}
|
|
910
1126
|
};
|
|
911
1127
|
if (baseRepoDescriptor?.projectName != null) project = projectDescriptors.find((p) => p.resourceName === baseRepoDescriptor.resourceName && p.name === baseRepoDescriptor.projectName);
|
|
912
|
-
return fromProviderPullRequest(azurePullRequest, this, {
|
|
1128
|
+
return fromProviderPullRequest(azurePullRequest, this, {
|
|
1129
|
+
project,
|
|
1130
|
+
projection
|
|
1131
|
+
});
|
|
913
1132
|
}
|
|
914
1133
|
};
|
|
915
1134
|
const cloudMetadata = providersMetadata["azureDevOps"];
|
|
@@ -930,6 +1149,6 @@ var AzureDevOpsIntegration = class extends AzureDevOpsIntegrationBase {
|
|
|
930
1149
|
}
|
|
931
1150
|
};
|
|
932
1151
|
//#endregion
|
|
933
|
-
export { AzureDevOpsIntegration, AzureDevOpsIntegrationBase, getAzureRepositoryApiBaseUrl, getAzureRepositoryIdentity, sameAzureName, uniqueAzureNames };
|
|
1152
|
+
export { AzureDevOpsIntegration, AzureDevOpsIntegrationBase, getAzureRepositoryApiBaseUrl, getAzureRepositoryIdentity, markMyGroupReviews, sameAzureName, toAzureReviewerReads, uniqueAzureNames };
|
|
934
1153
|
|
|
935
1154
|
//# sourceMappingURL=azureDevOps.js.map
|