@gitkraken/core-gitlens 0.5.108 → 0.5.110
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 +49 -1
- package/dist/git/models/issue.d.ts +70 -0
- package/dist/git/models/issue.d.ts.map +1 -1
- package/dist/git/models/issue.js +17 -0
- package/dist/git/models/issue.js.map +1 -1
- package/dist/git/models/pullRequest.d.ts +81 -0
- package/dist/git/models/pullRequest.d.ts.map +1 -1
- package/dist/git/models/pullRequest.js +15 -0
- package/dist/git/models/pullRequest.js.map +1 -1
- package/dist/git/utils/issue.utils.d.ts +41 -1
- package/dist/git/utils/issue.utils.d.ts.map +1 -1
- package/dist/git/utils/issue.utils.js +80 -0
- package/dist/git/utils/issue.utils.js.map +1 -1
- package/dist/git/utils/pullRequest.utils.d.ts +11 -1
- package/dist/git/utils/pullRequest.utils.d.ts.map +1 -1
- package/dist/git/utils/pullRequest.utils.js +40 -0
- package/dist/git/utils/pullRequest.utils.js.map +1 -1
- package/dist/plus/git-github/api/github.d.ts +70 -10
- package/dist/plus/git-github/api/github.d.ts.map +1 -1
- package/dist/plus/git-github/api/github.js +284 -31
- package/dist/plus/git-github/api/github.js.map +1 -1
- package/dist/plus/git-github/api/issueSearchQuery.d.ts +49 -5
- package/dist/plus/git-github/api/issueSearchQuery.d.ts.map +1 -1
- package/dist/plus/git-github/api/issueSearchQuery.js +64 -6
- package/dist/plus/git-github/api/issueSearchQuery.js.map +1 -1
- package/dist/plus/git-github/api/pullRequestSearchQuery.d.ts +27 -1
- package/dist/plus/git-github/api/pullRequestSearchQuery.d.ts.map +1 -1
- package/dist/plus/git-github/api/pullRequestSearchQuery.js +73 -6
- package/dist/plus/git-github/api/pullRequestSearchQuery.js.map +1 -1
- package/dist/plus/git-github/models.d.ts +17 -5
- package/dist/plus/git-github/models.d.ts.map +1 -1
- package/dist/plus/git-github/models.js +14 -1
- package/dist/plus/git-github/models.js.map +1 -1
- package/dist/plus/integrations/collectionMetadata.d.ts +18 -0
- package/dist/plus/integrations/collectionMetadata.d.ts.map +1 -1
- package/dist/plus/integrations/collectionMetadata.js +24 -0
- package/dist/plus/integrations/collectionMetadata.js.map +1 -1
- package/dist/plus/integrations/integrationService.d.ts +31 -10
- package/dist/plus/integrations/integrationService.d.ts.map +1 -1
- package/dist/plus/integrations/integrationService.js +9 -1
- package/dist/plus/integrations/integrationService.js.map +1 -1
- package/dist/plus/integrations/manager.d.ts +85 -31
- package/dist/plus/integrations/manager.d.ts.map +1 -1
- package/dist/plus/integrations/models/gitHostIntegration.d.ts +18 -31
- package/dist/plus/integrations/models/gitHostIntegration.d.ts.map +1 -1
- package/dist/plus/integrations/models/gitHostIntegration.js +29 -1
- package/dist/plus/integrations/models/gitHostIntegration.js.map +1 -1
- package/dist/plus/integrations/models/integration.d.ts +1 -1
- package/dist/plus/integrations/models/integration.d.ts.map +1 -1
- package/dist/plus/integrations/models/integration.js.map +1 -1
- package/dist/plus/integrations/models/issueReads.d.ts +63 -1
- package/dist/plus/integrations/models/issueReads.d.ts.map +1 -1
- package/dist/plus/integrations/models/issuesIntegration.d.ts +7 -21
- package/dist/plus/integrations/models/issuesIntegration.d.ts.map +1 -1
- package/dist/plus/integrations/models/issuesIntegration.js.map +1 -1
- package/dist/plus/integrations/providerFilters.d.ts +2 -2
- package/dist/plus/integrations/providerFilters.d.ts.map +1 -1
- package/dist/plus/integrations/providers/accounts.d.ts +6 -0
- package/dist/plus/integrations/providers/accounts.d.ts.map +1 -0
- package/dist/plus/integrations/providers/accounts.js +30 -0
- package/dist/plus/integrations/providers/accounts.js.map +1 -0
- package/dist/plus/integrations/providers/azureDevOps.d.ts.map +1 -1
- package/dist/plus/integrations/providers/azureDevOps.js +1 -0
- package/dist/plus/integrations/providers/azureDevOps.js.map +1 -1
- package/dist/plus/integrations/providers/github.d.ts +9 -0
- package/dist/plus/integrations/providers/github.d.ts.map +1 -1
- package/dist/plus/integrations/providers/github.js +115 -135
- package/dist/plus/integrations/providers/github.js.map +1 -1
- package/dist/plus/integrations/providers/gitlab.d.ts +9 -0
- package/dist/plus/integrations/providers/gitlab.d.ts.map +1 -1
- package/dist/plus/integrations/providers/gitlab.js +24 -4
- package/dist/plus/integrations/providers/gitlab.js.map +1 -1
- package/dist/plus/integrations/providers/issueSorts.d.ts +36 -0
- package/dist/plus/integrations/providers/issueSorts.d.ts.map +1 -0
- package/dist/plus/integrations/providers/issueSorts.js +76 -0
- package/dist/plus/integrations/providers/issueSorts.js.map +1 -0
- package/dist/plus/integrations/providers/jira.d.ts +3 -9
- package/dist/plus/integrations/providers/jira.d.ts.map +1 -1
- package/dist/plus/integrations/providers/jira.js +5 -1
- package/dist/plus/integrations/providers/jira.js.map +1 -1
- package/dist/plus/integrations/providers/linear.d.ts +24 -9
- package/dist/plus/integrations/providers/linear.d.ts.map +1 -1
- package/dist/plus/integrations/providers/linear.js +63 -5
- package/dist/plus/integrations/providers/linear.js.map +1 -1
- package/dist/plus/integrations/providers/models.d.ts +76 -38
- package/dist/plus/integrations/providers/models.d.ts.map +1 -1
- package/dist/plus/integrations/providers/models.js +91 -105
- package/dist/plus/integrations/providers/models.js.map +1 -1
- package/dist/plus/integrations/providers/providersApi.d.ts +18 -3
- package/dist/plus/integrations/providers/providersApi.d.ts.map +1 -1
- package/dist/plus/integrations/providers/providersApi.js +24 -7
- package/dist/plus/integrations/providers/providersApi.js.map +1 -1
- package/dist/plus/integrations/providers/pullRequestReviews.d.ts +50 -0
- package/dist/plus/integrations/providers/pullRequestReviews.d.ts.map +1 -0
- package/dist/plus/integrations/providers/pullRequestReviews.js +99 -0
- package/dist/plus/integrations/providers/pullRequestReviews.js.map +1 -0
- package/dist/plus/integrations/providers/trello.d.ts +3 -9
- package/dist/plus/integrations/providers/trello.d.ts.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/providers/utils/providerPaging.d.ts.map +1 -1
- package/dist/plus/integrations/providers/utils/providerPaging.js +5 -1
- package/dist/plus/integrations/providers/utils/providerPaging.js.map +1 -1
- package/dist/plus/integrations/reads/broaden.d.ts +6 -0
- package/dist/plus/integrations/reads/broaden.d.ts.map +1 -1
- package/dist/plus/integrations/reads/broaden.js.map +1 -1
- package/dist/plus/integrations/reads/counts.d.ts +63 -1
- package/dist/plus/integrations/reads/counts.d.ts.map +1 -1
- package/dist/plus/integrations/reads/counts.js +146 -2
- package/dist/plus/integrations/reads/counts.js.map +1 -1
- package/dist/plus/integrations/reads/drains.d.ts +7 -1
- package/dist/plus/integrations/reads/drains.d.ts.map +1 -1
- package/dist/plus/integrations/reads/drains.js +13 -2
- package/dist/plus/integrations/reads/drains.js.map +1 -1
- package/dist/plus/integrations/reads/filters.d.ts +59 -9
- package/dist/plus/integrations/reads/filters.d.ts.map +1 -1
- package/dist/plus/integrations/reads/filters.js +39 -0
- package/dist/plus/integrations/reads/filters.js.map +1 -1
- package/dist/plus/integrations/reads/issueTracker.d.ts +18 -1
- package/dist/plus/integrations/reads/issueTracker.d.ts.map +1 -1
- package/dist/plus/integrations/reads/issueTracker.js +40 -3
- package/dist/plus/integrations/reads/issueTracker.js.map +1 -1
- package/dist/plus/integrations/reads/issues.d.ts +27 -3
- package/dist/plus/integrations/reads/issues.d.ts.map +1 -1
- package/dist/plus/integrations/reads/issues.js +223 -123
- package/dist/plus/integrations/reads/issues.js.map +1 -1
- package/dist/plus/integrations/reads/ordering.d.ts +101 -0
- package/dist/plus/integrations/reads/ordering.d.ts.map +1 -0
- package/dist/plus/integrations/reads/ordering.js +48 -0
- package/dist/plus/integrations/reads/ordering.js.map +1 -0
- package/dist/plus/integrations/reads/searchIssues.d.ts +7 -2
- package/dist/plus/integrations/reads/searchIssues.d.ts.map +1 -1
- package/dist/plus/integrations/reads/searchIssues.js +9 -3
- package/dist/plus/integrations/reads/searchIssues.js.map +1 -1
- package/dist/plus/integrations/reads/searchPullRequests.d.ts +4 -3
- package/dist/plus/integrations/reads/searchPullRequests.d.ts.map +1 -1
- package/dist/plus/integrations/reads/searchPullRequests.js +6 -4
- package/dist/plus/integrations/reads/searchPullRequests.js.map +1 -1
- package/dist/plus/integrations/reads/sweeps.js +1 -1
- package/dist/plus/integrations/reads/sweeps.js.map +1 -1
- package/dist/plus/integrations/reads/warnings.d.ts +35 -3
- package/dist/plus/integrations/reads/warnings.d.ts.map +1 -1
- package/dist/plus/integrations/reads/warnings.js +58 -4
- package/dist/plus/integrations/reads/warnings.js.map +1 -1
- package/dist/plus/integrations/results.d.ts +15 -0
- package/dist/plus/integrations/results.d.ts.map +1 -1
- package/dist/plus/integrations/results.js.map +1 -1
- package/docs/integrations.md +76 -10
- package/docs/kepler-read-api-parity.md +9 -0
- package/package.json +2 -2
- package/src/git/models/issue.ts +73 -0
- package/src/git/models/pullRequest.ts +84 -0
- package/src/git/utils/issue.utils.ts +91 -1
- package/src/git/utils/pullRequest.utils.ts +50 -0
- package/src/plus/git-github/api/github.ts +356 -36
- package/src/plus/git-github/api/issueSearchQuery.ts +67 -7
- package/src/plus/git-github/api/pullRequestSearchQuery.ts +81 -7
- package/src/plus/git-github/models.ts +29 -5
- package/src/plus/integrations/collectionMetadata.ts +24 -0
- package/src/plus/integrations/integrationService.ts +40 -12
- package/src/plus/integrations/manager.ts +89 -35
- package/src/plus/integrations/models/gitHostIntegration.ts +47 -31
- package/src/plus/integrations/models/integration.ts +1 -0
- package/src/plus/integrations/models/issueReads.ts +65 -1
- package/src/plus/integrations/models/issuesIntegration.ts +7 -6
- package/src/plus/integrations/providerFilters.ts +8 -1
- package/src/plus/integrations/providers/accounts.ts +34 -0
- package/src/plus/integrations/providers/azureDevOps.ts +1 -0
- package/src/plus/integrations/providers/github.ts +134 -144
- package/src/plus/integrations/providers/gitlab.ts +29 -10
- package/src/plus/integrations/providers/issueSorts.ts +89 -0
- package/src/plus/integrations/providers/jira.ts +9 -3
- package/src/plus/integrations/providers/linear.ts +77 -8
- package/src/plus/integrations/providers/models.ts +185 -128
- package/src/plus/integrations/providers/providersApi.ts +51 -13
- package/src/plus/integrations/providers/pullRequestReviews.ts +122 -0
- package/src/plus/integrations/providers/trello.ts +4 -3
- package/src/plus/integrations/providers/utils/providerPaging.ts +6 -1
- package/src/plus/integrations/reads/broaden.ts +6 -0
- package/src/plus/integrations/reads/counts.ts +281 -3
- package/src/plus/integrations/reads/drains.ts +12 -2
- package/src/plus/integrations/reads/filters.ts +92 -8
- package/src/plus/integrations/reads/issueTracker.ts +78 -4
- package/src/plus/integrations/reads/issues.ts +386 -194
- package/src/plus/integrations/reads/ordering.ts +124 -0
- package/src/plus/integrations/reads/searchIssues.ts +15 -3
- package/src/plus/integrations/reads/searchPullRequests.ts +12 -4
- package/src/plus/integrations/reads/sweeps.ts +1 -1
- package/src/plus/integrations/reads/warnings.ts +84 -2
- package/src/plus/integrations/results.ts +15 -0
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import type { GitPullRequest } from '@gitkraken/provider-apis';
|
|
2
|
+
import { GitPullRequestReviewState } from '@gitkraken/provider-apis';
|
|
3
|
+
import type { PullRequestReviewer } from '../../../git/models/pullRequest.js';
|
|
4
|
+
import { PullRequestReviewDecision, PullRequestReviewState } from '../../../git/models/pullRequest.js';
|
|
5
|
+
import { fromProviderAccount, toProviderAccount } from './accounts.js';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* The GitLens-local extension of provider-apis' review shape, in two parts: a `DISMISSED` state the upstream
|
|
9
|
+
* `GitPullRequestReviewState` has no member for, and the head-commit oid a review was submitted against
|
|
10
|
+
* (optional, so a raw SDK review stays assignable — only the GitHub path populates it).
|
|
11
|
+
*
|
|
12
|
+
* Both exist for the same case: on GitHub the `dismiss stale reviews` rule flips an approval to dismissed on
|
|
13
|
+
* the next push, which is exactly the "the PR moved past my review" situation the oid detects. Dropping such a
|
|
14
|
+
* review instead would hand a consumer a PR out of the `reviewed-by:@me` set with no review row at all,
|
|
15
|
+
* indistinguishable from never having reviewed it.
|
|
16
|
+
*/
|
|
17
|
+
export const providerPullRequestReviewStateDismissed = 'DISMISSED' as const;
|
|
18
|
+
export type ProviderPullRequestReview = Omit<NonNullable<GitPullRequest['reviews']>[number], 'state'> & {
|
|
19
|
+
state: GitPullRequestReviewState | typeof providerPullRequestReviewStateDismissed;
|
|
20
|
+
commitOid?: string;
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* The review list as it travels on {@link ProviderPullRequest}: `null` when the read carried no review data at
|
|
24
|
+
* all, as opposed to an empty array for a pull request nobody has reviewed.
|
|
25
|
+
*/
|
|
26
|
+
export type ProviderPullRequestReviews = ProviderPullRequestReview[] | null;
|
|
27
|
+
|
|
28
|
+
export const toProviderPullRequestReviewState = {
|
|
29
|
+
[PullRequestReviewState.Approved]: GitPullRequestReviewState.Approved,
|
|
30
|
+
[PullRequestReviewState.ChangesRequested]: GitPullRequestReviewState.ChangesRequested,
|
|
31
|
+
[PullRequestReviewState.Commented]: GitPullRequestReviewState.Commented,
|
|
32
|
+
[PullRequestReviewState.ReviewRequested]: GitPullRequestReviewState.ReviewRequested,
|
|
33
|
+
[PullRequestReviewState.Dismissed]: providerPullRequestReviewStateDismissed,
|
|
34
|
+
// A review the author started but never submitted. Visible only to that author and carrying no verdict, so
|
|
35
|
+
// it stays unmapped and is dropped by the projection.
|
|
36
|
+
[PullRequestReviewState.Pending]: null,
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
export const fromProviderPullRequestReviewState = {
|
|
40
|
+
[GitPullRequestReviewState.Approved]: PullRequestReviewState.Approved,
|
|
41
|
+
[GitPullRequestReviewState.ChangesRequested]: PullRequestReviewState.ChangesRequested,
|
|
42
|
+
[GitPullRequestReviewState.Commented]: PullRequestReviewState.Commented,
|
|
43
|
+
[GitPullRequestReviewState.ReviewRequested]: PullRequestReviewState.ReviewRequested,
|
|
44
|
+
[providerPullRequestReviewStateDismissed]: PullRequestReviewState.Dismissed,
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
export function toProviderReviews(reviewers: PullRequestReviewer[]): ProviderPullRequestReviews {
|
|
48
|
+
// Only `Pending` maps to null (see `toProviderPullRequestReviewState`), so this drops exactly the reviews
|
|
49
|
+
// that carry no verdict rather than defaulting them to `ReviewRequested` — which would report an unsubmitted
|
|
50
|
+
// draft as a pending request from that reviewer.
|
|
51
|
+
return reviewers
|
|
52
|
+
.filter(r => r.state !== PullRequestReviewState.Pending)
|
|
53
|
+
.map(reviewer => ({
|
|
54
|
+
reviewer: toProviderAccount(reviewer.reviewer),
|
|
55
|
+
state: toProviderPullRequestReviewState[reviewer.state] ?? GitPullRequestReviewState.ReviewRequested,
|
|
56
|
+
commitOid: reviewer.commitOid,
|
|
57
|
+
}));
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export function toReviewRequests(reviews: ProviderPullRequestReviews): PullRequestReviewer[] | undefined {
|
|
61
|
+
return reviews == null
|
|
62
|
+
? undefined
|
|
63
|
+
: reviews
|
|
64
|
+
?.filter(r => r.state === GitPullRequestReviewState.ReviewRequested)
|
|
65
|
+
.map(r => ({
|
|
66
|
+
isCodeOwner: false, // TODO: Find this value, and implement in the shared lib if needed
|
|
67
|
+
reviewer: fromProviderAccount(r.reviewer),
|
|
68
|
+
state: PullRequestReviewState.ReviewRequested,
|
|
69
|
+
}));
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export function toCompletedReviews(reviews: ProviderPullRequestReviews): PullRequestReviewer[] | undefined {
|
|
73
|
+
return reviews == null
|
|
74
|
+
? undefined
|
|
75
|
+
: reviews
|
|
76
|
+
?.filter(
|
|
77
|
+
r =>
|
|
78
|
+
r.state !== GitPullRequestReviewState.ReviewRequested &&
|
|
79
|
+
// provider-apis' own GitHub normalizer maps only its four known states, so a review it has no
|
|
80
|
+
// member for (a real `DISMISSED` one, which its `latestReviews(first: 100)` selection does
|
|
81
|
+
// return) arrives with `state: undefined` — a value its type says cannot happen. Publishing it
|
|
82
|
+
// would put an unswitchable state on `PullRequestShape.latestReviews`, and mapping back would
|
|
83
|
+
// hit `toProviderReviews`' fallback and report a dismissed review as an outstanding request.
|
|
84
|
+
// Our own GitHub path never lands here: it carries `providerPullRequestReviewStateDismissed`.
|
|
85
|
+
fromProviderPullRequestReviewState[r.state] != null,
|
|
86
|
+
)
|
|
87
|
+
.map(r => ({
|
|
88
|
+
isCodeOwner: false, // TODO: Find this value, and implement in the shared lib if needed
|
|
89
|
+
reviewer: fromProviderAccount(r.reviewer),
|
|
90
|
+
state: fromProviderPullRequestReviewState[r.state],
|
|
91
|
+
commitOid: r.commitOid,
|
|
92
|
+
}));
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export function toProviderReviewDecision(
|
|
96
|
+
reviewDecision?: PullRequestReviewDecision,
|
|
97
|
+
reviewers?: PullRequestReviewer[],
|
|
98
|
+
): GitPullRequestReviewState | null {
|
|
99
|
+
switch (reviewDecision) {
|
|
100
|
+
case PullRequestReviewDecision.Approved:
|
|
101
|
+
return GitPullRequestReviewState.Approved;
|
|
102
|
+
case PullRequestReviewDecision.ChangesRequested:
|
|
103
|
+
return GitPullRequestReviewState.ChangesRequested;
|
|
104
|
+
case PullRequestReviewDecision.ReviewRequired:
|
|
105
|
+
return GitPullRequestReviewState.ReviewRequested;
|
|
106
|
+
default: {
|
|
107
|
+
if (reviewers?.some(r => r.state === PullRequestReviewState.ReviewRequested)) {
|
|
108
|
+
return GitPullRequestReviewState.ReviewRequested;
|
|
109
|
+
} else if (reviewers?.some(r => r.state === PullRequestReviewState.Commented)) {
|
|
110
|
+
return GitPullRequestReviewState.Commented;
|
|
111
|
+
}
|
|
112
|
+
return null;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export const fromPullRequestReviewDecision = {
|
|
118
|
+
[GitPullRequestReviewState.Approved]: PullRequestReviewDecision.Approved,
|
|
119
|
+
[GitPullRequestReviewState.ChangesRequested]: PullRequestReviewDecision.ChangesRequested,
|
|
120
|
+
[GitPullRequestReviewState.Commented]: undefined,
|
|
121
|
+
[GitPullRequestReviewState.ReviewRequested]: PullRequestReviewDecision.ReviewRequired,
|
|
122
|
+
};
|
|
@@ -9,8 +9,8 @@ import type { ProviderAuthenticationSession } from '../authentication/models.js'
|
|
|
9
9
|
import { toTokenWithInfo } from '../authentication/models.js';
|
|
10
10
|
import { IssuesCloudHostIntegrationId } from '../constants.js';
|
|
11
11
|
import { IntegrationReadUnavailableError } from '../errors.js';
|
|
12
|
+
import type { IssuesForProjectOptions } from '../models/issueReads.js';
|
|
12
13
|
import { IssuesIntegration } from '../models/issuesIntegration.js';
|
|
13
|
-
import type { IssueFilter } from './models.js';
|
|
14
14
|
import { fromProviderIssue, providersMetadata, toIssueShape } from './models.js';
|
|
15
15
|
|
|
16
16
|
const metadata = providersMetadata[IssuesCloudHostIntegrationId.Trello];
|
|
@@ -95,7 +95,7 @@ export class TrelloIntegration extends IssuesIntegration<IssuesCloudHostIntegrat
|
|
|
95
95
|
protected override async getProviderIssuesForProject(
|
|
96
96
|
session: ProviderAuthenticationSession,
|
|
97
97
|
project: ResourceDescriptor,
|
|
98
|
-
options?:
|
|
98
|
+
options?: IssuesForProjectOptions,
|
|
99
99
|
): Promise<IssueShape[] | undefined> {
|
|
100
100
|
return (await this.getProviderIssuesForProjectWithTruncation(session, project, options))?.values;
|
|
101
101
|
}
|
|
@@ -103,7 +103,7 @@ export class TrelloIntegration extends IssuesIntegration<IssuesCloudHostIntegrat
|
|
|
103
103
|
protected override async getProviderIssuesForProjectWithTruncation(
|
|
104
104
|
session: ProviderAuthenticationSession,
|
|
105
105
|
project: ResourceDescriptor,
|
|
106
|
-
options?:
|
|
106
|
+
options?: IssuesForProjectOptions,
|
|
107
107
|
): Promise<{ values: IssueShape[]; truncated: boolean; metadata?: CollectionMetadata } | undefined> {
|
|
108
108
|
// A non-issue descriptor genuinely has nothing to read (empty), but a missing app key is a broken read.
|
|
109
109
|
if (!isIssueResourceDescriptor(project)) return undefined;
|
|
@@ -129,6 +129,7 @@ export class TrelloIntegration extends IssuesIntegration<IssuesCloudHostIntegrat
|
|
|
129
129
|
const result = await api.getTrelloIssuesForBoard(tokenWithInfo, appKey, project.id, {
|
|
130
130
|
assigneeLogins: options?.user != null ? [options.user] : undefined,
|
|
131
131
|
trelloBoardListsById: trelloBoardListsById,
|
|
132
|
+
sort: options?.sort,
|
|
132
133
|
});
|
|
133
134
|
|
|
134
135
|
const values = result.values.flatMap(issue => {
|
|
@@ -6,7 +6,7 @@ import type {
|
|
|
6
6
|
} from '@gitkraken/provider-apis';
|
|
7
7
|
import { isCancellationError } from '../../../../utils/cancellation.js';
|
|
8
8
|
import { uniqueBy } from '../../../../utils/iterable.js';
|
|
9
|
-
import { toCollectionScopeFailure } from '../../collectionMetadata.js';
|
|
9
|
+
import { throwIfCallerContractError, toCollectionScopeFailure } from '../../collectionMetadata.js';
|
|
10
10
|
import { collectionScopeKey } from '../../results.js';
|
|
11
11
|
import type { ProviderApiPagedResult, ProviderHierarchyResult } from '../models.js';
|
|
12
12
|
|
|
@@ -144,6 +144,11 @@ export async function collectProviderPagedResult<T>(
|
|
|
144
144
|
} catch (ex) {
|
|
145
145
|
if (isCancellationError(ex)) throw ex;
|
|
146
146
|
|
|
147
|
+
// A caller-contract error is not a fact about this scope, so it is never recorded as one — see
|
|
148
|
+
// `throwIfCallerContractError`. Checked alongside cancellation because both are errors that a
|
|
149
|
+
// per-scope failure would misdescribe.
|
|
150
|
+
throwIfCallerContractError(ex);
|
|
151
|
+
|
|
147
152
|
// When the caller supplied a scope, preserve the items already fetched from that scope and record the
|
|
148
153
|
// failure in collection metadata rather than re-throwing and discarding the prefix. Callers without a
|
|
149
154
|
// scope keep the legacy throw behavior.
|
|
@@ -33,6 +33,12 @@ import { gitHostOnlySurfaceWarning, issuesUnsupportedWarning, noConnectionWarnin
|
|
|
33
33
|
* `includeAllAssignees: true`, which resolves to no assignee constraint at all, so unassigned issues ARE
|
|
34
34
|
* included. The equivalent is an OMITTED `relationships`, not `['any-assignee']`: `assignee:*` means "has some
|
|
35
35
|
* assignee" and would silently exclude every unassigned issue, which is the opposite of broadening.
|
|
36
|
+
*
|
|
37
|
+
* Takes no `sort`, unlike `listIssuesPage` and `searchIssuesPage`. One logical page here spans several orgs, each
|
|
38
|
+
* at its own provider position in a cursor bundle, so honoring an order across them would need a k-way merge with
|
|
39
|
+
* a buffer per org rather than a sort of what arrived — the page is a slice of several independent walks, not a
|
|
40
|
+
* union of one round's results. `searchIssuesPage({ repos, criteria: { sort } })` answers the ordered version of
|
|
41
|
+
* this question for a caller that knows its repositories, and is already the recommended migration above.
|
|
36
42
|
*/
|
|
37
43
|
|
|
38
44
|
export interface BroadenIssuesOptions {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { IssueSearchCriteria } from '../../../git/models/issue.js';
|
|
2
|
+
import type { PullRequestSearchCriteria } from '../../../git/models/pullRequest.js';
|
|
2
3
|
import { chunk } from '../../../utils/array.js';
|
|
3
4
|
import { mapBounded } from '../../../utils/promise.js';
|
|
4
5
|
import type { IntegrationIds } from '../constants.js';
|
|
@@ -14,12 +15,18 @@ import {
|
|
|
14
15
|
} from '../utils/integration.utils.js';
|
|
15
16
|
import type { ProviderReadContext } from './context.js';
|
|
16
17
|
import { runCaptured } from './drains.js';
|
|
17
|
-
import {
|
|
18
|
+
import {
|
|
19
|
+
resolveIssueSearchCriteria,
|
|
20
|
+
resolveIssueSearchScope,
|
|
21
|
+
resolvePullRequestSearchCriteria,
|
|
22
|
+
resolvePullRequestSearchScope,
|
|
23
|
+
} from './filters.js';
|
|
18
24
|
import {
|
|
19
25
|
gitHostOnlySurfaceWarning,
|
|
20
26
|
issuesUnsupportedWarning,
|
|
21
27
|
otherWarning,
|
|
22
28
|
unsupportedIssueSearchCriteriaWarning,
|
|
29
|
+
unsupportedPullRequestSearchCriteriaWarning,
|
|
23
30
|
} from './warnings.js';
|
|
24
31
|
|
|
25
32
|
/**
|
|
@@ -32,6 +39,15 @@ import {
|
|
|
32
39
|
*/
|
|
33
40
|
const issueCountChunkSize = 25;
|
|
34
41
|
|
|
42
|
+
/**
|
|
43
|
+
* How many pull-request scopes go into one upstream request.
|
|
44
|
+
*
|
|
45
|
+
* The same latency/complexity reasoning as {@link issueCountChunkSize}, kept slightly smaller because a pull-request
|
|
46
|
+
* scope can fan out into one aliased count PER requested state (open/closed/merged), so a chunk of scopes carries a
|
|
47
|
+
* small multiple of that many `search` aliases.
|
|
48
|
+
*/
|
|
49
|
+
const pullRequestCountChunkSize = 15;
|
|
50
|
+
|
|
35
51
|
/**
|
|
36
52
|
* One scope to count.
|
|
37
53
|
*
|
|
@@ -44,7 +60,14 @@ export interface IssueCountScope {
|
|
|
44
60
|
/** Repositories to count over. Combines with `org`, exactly as in `searchIssuesPage`. */
|
|
45
61
|
repos?: ProviderReposInput;
|
|
46
62
|
org?: string;
|
|
47
|
-
/**
|
|
63
|
+
/**
|
|
64
|
+
* Same criteria model as `searchIssuesPage`, validated against the same capability table.
|
|
65
|
+
*
|
|
66
|
+
* `criteria.sort` does not affect the count — a total is invariant under ordering — but it is still VALIDATED,
|
|
67
|
+
* and an unsupported key refuses this scope exactly as it would refuse the read. That is the point of the
|
|
68
|
+
* count: it previews the constraints the read will apply, so one that accepted what the read refuses would
|
|
69
|
+
* promise a fetch that can't happen, which is worse than no preview at all.
|
|
70
|
+
*/
|
|
48
71
|
criteria?: IssueSearchCriteria;
|
|
49
72
|
}
|
|
50
73
|
|
|
@@ -66,6 +89,42 @@ export interface IssueCountResult {
|
|
|
66
89
|
providerLimit?: number;
|
|
67
90
|
}
|
|
68
91
|
|
|
92
|
+
/**
|
|
93
|
+
* One scope to count pull requests over — the PR twin of {@link IssueCountScope}.
|
|
94
|
+
*
|
|
95
|
+
* `key` is caller-owned and echoed back verbatim, never reaching the provider query (the aliases are generated), so
|
|
96
|
+
* it can be any string the caller finds meaningful.
|
|
97
|
+
*/
|
|
98
|
+
export interface PullRequestCountScope {
|
|
99
|
+
key: string;
|
|
100
|
+
/** Repositories to count over. Combines with `org`, exactly as in `searchPullRequestsPage`. */
|
|
101
|
+
repos?: ProviderReposInput;
|
|
102
|
+
org?: string;
|
|
103
|
+
/**
|
|
104
|
+
* Same criteria model as `searchPullRequestsPage`, validated against the same capability table — so a count
|
|
105
|
+
* refuses exactly what the read would, and never previews a fetch that can't happen.
|
|
106
|
+
*/
|
|
107
|
+
criteria?: PullRequestSearchCriteria;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/** The count for one {@link PullRequestCountScope}, echoed back under the caller's own `key`. */
|
|
111
|
+
export interface PullRequestCountResult {
|
|
112
|
+
key: string;
|
|
113
|
+
/**
|
|
114
|
+
* Total matches the provider reports. `undefined` when the provider didn't report one for this scope — NEVER
|
|
115
|
+
* zero, which is a real answer. For a multi-state scope this is the LARGEST of its per-state counts, the same
|
|
116
|
+
* total `searchPullRequestsPage` surfaces, not their sum.
|
|
117
|
+
*/
|
|
118
|
+
count?: number;
|
|
119
|
+
/**
|
|
120
|
+
* True when `count` exceeds the provider's own per-search result ceiling, so a full read CANNOT return
|
|
121
|
+
* everything no matter how it is paged. This is the signal to warn before starting an expensive fetch.
|
|
122
|
+
*/
|
|
123
|
+
exceedsProviderLimit: boolean;
|
|
124
|
+
/** The ceiling itself, when the provider declares one. */
|
|
125
|
+
providerLimit?: number;
|
|
126
|
+
}
|
|
127
|
+
|
|
69
128
|
/**
|
|
70
129
|
* Counts issues for many scopes without fetching any — the probe behind a "this will fetch ~N issues" preview.
|
|
71
130
|
*
|
|
@@ -221,7 +280,7 @@ export async function countIssues(
|
|
|
221
280
|
}
|
|
222
281
|
|
|
223
282
|
/** The first key that appears twice, or `undefined` when every key is unique. */
|
|
224
|
-
function findDuplicateKey(scopes: readonly
|
|
283
|
+
function findDuplicateKey(scopes: readonly { key: string }[]): string | undefined {
|
|
225
284
|
const seen = new Set<string>();
|
|
226
285
|
for (const scope of scopes) {
|
|
227
286
|
if (seen.has(scope.key)) return scope.key;
|
|
@@ -283,3 +342,222 @@ function rejectScope(
|
|
|
283
342
|
|
|
284
343
|
return undefined;
|
|
285
344
|
}
|
|
345
|
+
|
|
346
|
+
/**
|
|
347
|
+
* Counts pull requests for many scopes without fetching any — the PR twin of {@link countIssues}, and the probe
|
|
348
|
+
* behind a "this will fetch ~N pull requests" preview.
|
|
349
|
+
*
|
|
350
|
+
* Identical batching, per-scope isolation, and `key`-echo contract as {@link countIssues}; see it for the cost
|
|
351
|
+
* model and the `count: undefined` ≠ zero rule. The one difference is inherent to pull requests: a scope's criteria
|
|
352
|
+
* can name several STATES, which the provider counts as independent searches — the reported `count` is the LARGEST
|
|
353
|
+
* of them (mirroring `searchPullRequestsPage`'s total), so an unpaged read still can't exceed a single search's
|
|
354
|
+
* ceiling undetected.
|
|
355
|
+
*/
|
|
356
|
+
export async function countPullRequests(
|
|
357
|
+
ctx: ProviderReadContext,
|
|
358
|
+
options: {
|
|
359
|
+
providerId: IntegrationIds;
|
|
360
|
+
scopes: readonly PullRequestCountScope[];
|
|
361
|
+
connectionId?: string;
|
|
362
|
+
/**
|
|
363
|
+
* Explicit self-managed host domain. Used only when the requested connection has no configured domain;
|
|
364
|
+
* it must come from the trusted authentication configuration, not repository or remote data.
|
|
365
|
+
*/
|
|
366
|
+
domain?: string;
|
|
367
|
+
},
|
|
368
|
+
): Promise<ProviderResult<PullRequestCountResult>> {
|
|
369
|
+
const refused = (warning: ProviderWarning): ProviderResult<PullRequestCountResult> => ({
|
|
370
|
+
items: [],
|
|
371
|
+
warnings: [warning],
|
|
372
|
+
fetchFailed: true,
|
|
373
|
+
});
|
|
374
|
+
|
|
375
|
+
if (isIssuesHostIntegrationId(options.providerId)) {
|
|
376
|
+
return refused(
|
|
377
|
+
gitHostOnlySurfaceWarning(options.providerId, undefined, options.connectionId, 'Pull request counts'),
|
|
378
|
+
);
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
// Nothing was asked for, so nothing is missing: an empty success, not a refusal.
|
|
382
|
+
if (options.scopes.length === 0) return { items: [], warnings: [] };
|
|
383
|
+
|
|
384
|
+
const duplicateKey = findDuplicateKey(options.scopes);
|
|
385
|
+
if (duplicateKey != null) {
|
|
386
|
+
// Refuses the whole call rather than deduping: `key` exists so the caller can match results without
|
|
387
|
+
// positional bookkeeping, and two results under one key make that ambiguous for EVERY scope, not just the
|
|
388
|
+
// repeated one.
|
|
389
|
+
return refused(
|
|
390
|
+
otherWarning(
|
|
391
|
+
options.providerId,
|
|
392
|
+
undefined,
|
|
393
|
+
options.connectionId,
|
|
394
|
+
`Duplicate pull request count scope key '${duplicateKey}'; keys identify results, so each must be unique.`,
|
|
395
|
+
),
|
|
396
|
+
);
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
const integration = await ctx.getIntegrationForRead(options.providerId, options.connectionId, options.domain);
|
|
400
|
+
if (integration == null) {
|
|
401
|
+
// A supplied connection or domain that no longer resolves is a broken target, not an empty account.
|
|
402
|
+
const early = ctx.earlyReturnConnectionWarnings(options.providerId, options.connectionId, options.domain);
|
|
403
|
+
return { items: [], warnings: early.warnings, fetchFailed: early.fetchFailed || undefined };
|
|
404
|
+
}
|
|
405
|
+
if (!isGitHostIntegration(integration)) {
|
|
406
|
+
return refused(
|
|
407
|
+
gitHostOnlySurfaceWarning(options.providerId, undefined, options.connectionId, 'Pull request counts'),
|
|
408
|
+
);
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
const domain = ctx.domainForRead(integration, options.providerId, options.connectionId, options.domain);
|
|
412
|
+
const warnOnMissingSession = warnOnMissingSessionForDomain(options.providerId, options.domain);
|
|
413
|
+
|
|
414
|
+
// A provider with no filtered pull-request search has no count either: refuse ONCE for the provider rather than
|
|
415
|
+
// letting every scope repeat the same rejection. `undefined` criteria probes only the search's existence.
|
|
416
|
+
const searchSupport = resolvePullRequestSearchCriteria(options.providerId, undefined);
|
|
417
|
+
if (searchSupport.rejection != null) {
|
|
418
|
+
return refused(
|
|
419
|
+
unsupportedPullRequestSearchCriteriaWarning(
|
|
420
|
+
options.providerId,
|
|
421
|
+
domain,
|
|
422
|
+
options.connectionId,
|
|
423
|
+
searchSupport.rejection,
|
|
424
|
+
),
|
|
425
|
+
);
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
const providerLimit = providersMetadata[options.providerId]?.pullRequestSearchResultLimit;
|
|
429
|
+
const warnings: ProviderWarning[] = [];
|
|
430
|
+
let fetchFailed = false;
|
|
431
|
+
|
|
432
|
+
// Validate every scope first, so a refusal costs no request at all and the countable ones are still batched
|
|
433
|
+
// together.
|
|
434
|
+
const countable: PullRequestCountScope[] = [];
|
|
435
|
+
for (const scope of options.scopes) {
|
|
436
|
+
const warning = rejectPullRequestScope(options.providerId, domain, options.connectionId, scope);
|
|
437
|
+
if (warning != null) {
|
|
438
|
+
// `push`, not `appendDedupedWarning`: every rejection message embeds the scope's own key, and duplicate
|
|
439
|
+
// keys were already refused above, so no two of these can ever collapse.
|
|
440
|
+
warnings.push(warning);
|
|
441
|
+
fetchFailed = true;
|
|
442
|
+
continue;
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
countable.push(scope);
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
// Chunks are independent requests over their own slice of scopes, `runCaptured` never throws, so they run
|
|
449
|
+
// concurrently, bounded like every other fan-out on the facade. `mapBounded` returns in input order, so `items`
|
|
450
|
+
// and `warnings` stay in scope order.
|
|
451
|
+
const batches = await mapBounded(chunk(countable, pullRequestCountChunkSize), providerFanOutConcurrency, batch =>
|
|
452
|
+
runCaptured(
|
|
453
|
+
options.providerId,
|
|
454
|
+
domain,
|
|
455
|
+
options.connectionId,
|
|
456
|
+
() =>
|
|
457
|
+
integration.countPullRequestsResult(
|
|
458
|
+
batch.map(s => ({
|
|
459
|
+
repos: s.repos as ProviderRepoInput[] | undefined,
|
|
460
|
+
org: s.org,
|
|
461
|
+
criteria: s.criteria,
|
|
462
|
+
})),
|
|
463
|
+
undefined,
|
|
464
|
+
options.connectionId,
|
|
465
|
+
),
|
|
466
|
+
{ warnOnMissingSession: warnOnMissingSession },
|
|
467
|
+
).then(result => ({ batch: batch, ...result })),
|
|
468
|
+
);
|
|
469
|
+
|
|
470
|
+
const items: PullRequestCountResult[] = [];
|
|
471
|
+
for (const { batch, value, warning } of batches) {
|
|
472
|
+
if (warning != null) {
|
|
473
|
+
appendDedupedWarning(warnings, warning);
|
|
474
|
+
}
|
|
475
|
+
if (value == null) {
|
|
476
|
+
// This batch contributes nothing, but the batches around it still do. Drop only these scopes.
|
|
477
|
+
fetchFailed = true;
|
|
478
|
+
continue;
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
for (let i = 0; i < batch.length; i++) {
|
|
482
|
+
const count = value[i];
|
|
483
|
+
items.push({
|
|
484
|
+
key: batch[i].key,
|
|
485
|
+
count: count,
|
|
486
|
+
// Only a reported count can exceed a declared ceiling; unknown-vs-limit is not a comparison.
|
|
487
|
+
exceedsProviderLimit: count != null && providerLimit != null && count > providerLimit,
|
|
488
|
+
providerLimit: providerLimit,
|
|
489
|
+
});
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
// A provider that reported nothing for any scope and raised no warning: say so explicitly rather than letting it
|
|
494
|
+
// read as "every scope matched nothing".
|
|
495
|
+
if (items.length === 0 && warnings.length === 0) {
|
|
496
|
+
return refused(
|
|
497
|
+
unsupportedPullRequestSearchCriteriaWarning(options.providerId, domain, options.connectionId, {
|
|
498
|
+
reason: 'unsupported-search',
|
|
499
|
+
}),
|
|
500
|
+
);
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
return { items: items, warnings: warnings, fetchFailed: fetchFailed || undefined };
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
/**
|
|
507
|
+
* Why one pull-request scope can't be counted, as the warning to report — or `undefined` when it can. The PR twin
|
|
508
|
+
* of {@link rejectScope}, validating against the same {@link resolvePullRequestSearchScope} /
|
|
509
|
+
* {@link resolvePullRequestSearchCriteria} the read uses, so a count always previews the constraints the read
|
|
510
|
+
* would apply.
|
|
511
|
+
*/
|
|
512
|
+
function rejectPullRequestScope(
|
|
513
|
+
providerId: IntegrationIds,
|
|
514
|
+
domain: string | undefined,
|
|
515
|
+
connectionId: string | undefined,
|
|
516
|
+
scope: PullRequestCountScope,
|
|
517
|
+
): ProviderWarning | undefined {
|
|
518
|
+
const scoping = resolvePullRequestSearchScope(providerId, scope.repos, scope.org, scope.criteria);
|
|
519
|
+
switch (scoping.rejection) {
|
|
520
|
+
case 'repo-ids':
|
|
521
|
+
return otherWarning(
|
|
522
|
+
providerId,
|
|
523
|
+
domain,
|
|
524
|
+
connectionId,
|
|
525
|
+
`Pull request count scope '${scope.key}' cannot be scoped by repository id; pass repository descriptors (namespace + name) instead.`,
|
|
526
|
+
);
|
|
527
|
+
case 'unscoped':
|
|
528
|
+
return otherWarning(
|
|
529
|
+
providerId,
|
|
530
|
+
domain,
|
|
531
|
+
connectionId,
|
|
532
|
+
`Pull request count scope '${scope.key}' is unscoped; pass \`repos\`, \`org\`, or at least one current-user relationship.`,
|
|
533
|
+
);
|
|
534
|
+
case 'unsupported-repository-scope':
|
|
535
|
+
case 'unsupported-organization-scope':
|
|
536
|
+
return unsupportedPullRequestSearchCriteriaWarning(providerId, domain, connectionId, {
|
|
537
|
+
reason: 'unsupported-criteria',
|
|
538
|
+
criteria: [
|
|
539
|
+
scoping.rejection === 'unsupported-repository-scope' ? 'repositoryScope' : 'organizationScope',
|
|
540
|
+
],
|
|
541
|
+
});
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
const resolved = resolvePullRequestSearchCriteria(providerId, scope.criteria);
|
|
545
|
+
if (resolved.rejection != null) {
|
|
546
|
+
return unsupportedPullRequestSearchCriteriaWarning(providerId, domain, connectionId, resolved.rejection);
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
// Count-only, with no counterpart in the read: a relationship set is an OR across several searches, which one
|
|
550
|
+
// count can't express — summing would double-count overlaps and max would under-report. Ask the caller to count
|
|
551
|
+
// each relationship as its own scope, where the keys make the OR explicit. (States are disjoint, so a scope may
|
|
552
|
+
// still name several — the provider counts them as the max, not a refusal.)
|
|
553
|
+
if ((scope.criteria?.relationships?.length ?? 0) > 1) {
|
|
554
|
+
return otherWarning(
|
|
555
|
+
providerId,
|
|
556
|
+
domain,
|
|
557
|
+
connectionId,
|
|
558
|
+
`Pull request count scope '${scope.key}' requests several relationships, which a single count can't express (they are OR-ed, so overlapping matches would be double-counted); pass one scope per relationship.`,
|
|
559
|
+
);
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
return undefined;
|
|
563
|
+
}
|
|
@@ -70,7 +70,12 @@ export async function drainPullRequests(
|
|
|
70
70
|
repos: ProviderReposInput,
|
|
71
71
|
state: PullRequestStateFilter[] | undefined,
|
|
72
72
|
filters: PullRequestFilter[] | undefined,
|
|
73
|
-
|
|
73
|
+
/**
|
|
74
|
+
* Breadth opt-ins that only the account-wide read can express. Grouped rather than passed positionally
|
|
75
|
+
* because they are same-typed neighbors a transposition would silently swap, and picked from the sweep
|
|
76
|
+
* options rather than restated so a third opt-in is declared once.
|
|
77
|
+
*/
|
|
78
|
+
accountWideOptions: Pick<PullRequestSweepOptions, 'includeReviewRequested' | 'includeReviews'> | undefined,
|
|
74
79
|
connectionId: string | undefined,
|
|
75
80
|
maxPages: number,
|
|
76
81
|
attributeUnavailableProvider: boolean,
|
|
@@ -100,6 +105,10 @@ export async function drainPullRequests(
|
|
|
100
105
|
// With no repos this is an account-wide "my PRs" sweep. The repo-scoped core rejects an empty `repos`
|
|
101
106
|
// input, so read the provider-native account-wide core instead.
|
|
102
107
|
const accountWide = repos.length === 0;
|
|
108
|
+
// The breadth opt-ins are only expressible on the account-wide core, so gate them here — the single place
|
|
109
|
+
// that already knows which core it is about to call.
|
|
110
|
+
const includeReviewRequested = accountWide && (accountWideOptions?.includeReviewRequested ?? false);
|
|
111
|
+
const includeReviews = accountWide && (accountWideOptions?.includeReviews ?? false);
|
|
103
112
|
/** Every cursor already followed, so a provider that cycles them can't keep the drain walking in circles. */
|
|
104
113
|
const seenCursors = new Set<string>();
|
|
105
114
|
|
|
@@ -115,7 +124,8 @@ export async function drainPullRequests(
|
|
|
115
124
|
cursor: pageCursor,
|
|
116
125
|
includeReviewRequested: includeReviewRequested,
|
|
117
126
|
filters: filters,
|
|
118
|
-
|
|
127
|
+
// Lite by default; the full projection (reviews) only when a caller opts in.
|
|
128
|
+
summary: !includeReviews,
|
|
119
129
|
},
|
|
120
130
|
connectionId,
|
|
121
131
|
)
|