@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
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { gitHubPullRequestRelationshipQualifiers } from '../../git-github/api/pullRequestSearchQuery.js';
|
|
1
2
|
import type { Account, UnidentifiedAuthor } from '../../../git/models/author.js';
|
|
2
3
|
import type { DefaultBranch } from '../../../git/models/defaultBranch.js';
|
|
3
4
|
import type { Issue, IssueSearchCriteria, IssueShape } from '../../../git/models/issue.js';
|
|
@@ -50,17 +51,6 @@ import {
|
|
|
50
51
|
} from './models.js';
|
|
51
52
|
import type { ProvidersApi } from './providersApi.js';
|
|
52
53
|
|
|
53
|
-
/**
|
|
54
|
-
* Page size for GitHub's account-wide search reads: the maximum GitHub's search connection accepts, and what
|
|
55
|
-
* `GitHubApi.searchMyPullRequestsPage` already caps itself to.
|
|
56
|
-
*
|
|
57
|
-
* Requested explicitly rather than left to the SDK's own fallback. That fallback is also 100 as of
|
|
58
|
-
* `@gitkraken/provider-apis` 0.54.0 (it was 15 before, which cost a drained sweep ~7x the round trips it needs),
|
|
59
|
-
* so this is now a pin rather than an override — it keeps the size this read is tuned for from silently tracking
|
|
60
|
-
* a shared SDK default that other reads may want smaller.
|
|
61
|
-
*/
|
|
62
|
-
const githubSearchMaxPageSize = 100;
|
|
63
|
-
|
|
64
54
|
type GitHubPullRequestFacetCursor = Record<string, string>;
|
|
65
55
|
|
|
66
56
|
function toPullRequestFacetCursor(value: unknown): GitHubPullRequestFacetCursor {
|
|
@@ -91,13 +81,6 @@ function parsePullRequestFacetCursor(cursor: string | undefined): GitHubPullRequ
|
|
|
91
81
|
}
|
|
92
82
|
}
|
|
93
83
|
|
|
94
|
-
const pullRequestRelationshipQualifier: Record<PullRequestFilter, string> = {
|
|
95
|
-
[PullRequestFilter.Author]: 'author:@me',
|
|
96
|
-
[PullRequestFilter.Assignee]: 'assignee:@me',
|
|
97
|
-
[PullRequestFilter.ReviewRequested]: 'review-requested:@me',
|
|
98
|
-
[PullRequestFilter.Mention]: 'mentions:@me',
|
|
99
|
-
};
|
|
100
|
-
|
|
101
84
|
const metadata = providersMetadata[GitCloudHostIntegrationId.GitHub];
|
|
102
85
|
const authProvider: IntegrationAuthenticationProviderDescriptor = Object.freeze({
|
|
103
86
|
id: metadata.id,
|
|
@@ -441,140 +424,124 @@ abstract class GitHubIntegrationBase<ID extends GitHubIntegrationIds> extends Gi
|
|
|
441
424
|
},
|
|
442
425
|
): Promise<ProviderApiPagedResult<ProviderPullRequest> | undefined> {
|
|
443
426
|
const explicitFilters = options?.filters?.length ? [...new Set(options.filters)] : undefined;
|
|
444
|
-
//
|
|
445
|
-
//
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
}
|
|
427
|
+
// Every account-wide read goes through our own facet search — there is deliberately no SDK
|
|
428
|
+
// `getPullRequestsForUser` fallback. It could express neither a state set, nor exact relationships (its
|
|
429
|
+
// `involves:` is a different question), nor the full projection, so which reads it could serve depended on
|
|
430
|
+
// the options, and the two routes disagreed on more than they had to: only this one applies the Launchpad
|
|
431
|
+
// ignored/included repository and organization qualifiers, so the same read returned different pull
|
|
432
|
+
// requests depending on options that were supposed to affect only projection or relationship. With no
|
|
433
|
+
// state and no filters this emits a single facet with no relationship qualifier, which is GitHub's own
|
|
434
|
+
// `involves:@me` and `is:open` default — the same question the fallback asked, now on one route.
|
|
435
|
+
const github = await this.authenticationService.apis.github;
|
|
436
|
+
if (github == null) return undefined;
|
|
437
|
+
|
|
438
|
+
const requestedStates: PullRequestStateFilter[] =
|
|
439
|
+
options?.state != null && options.state.length > 0 ? [...new Set(options.state)] : ['open'];
|
|
440
|
+
const facets =
|
|
441
|
+
explicitFilters?.length != null
|
|
442
|
+
? requestedStates.flatMap(state =>
|
|
443
|
+
explicitFilters.map(filter => ({
|
|
444
|
+
key: `${state}:${filter}`,
|
|
445
|
+
state: state,
|
|
446
|
+
search: gitHubPullRequestRelationshipQualifiers[filter],
|
|
447
|
+
})),
|
|
448
|
+
)
|
|
449
|
+
: requestedStates.flatMap(state => [
|
|
450
|
+
{ key: state, state: state, search: undefined },
|
|
451
|
+
...(options?.includeReviewRequested === true
|
|
452
|
+
? [
|
|
453
|
+
{
|
|
454
|
+
key: `${state}:${PullRequestFilter.ReviewRequested}`,
|
|
455
|
+
state: state,
|
|
456
|
+
search: gitHubPullRequestRelationshipQualifiers[
|
|
457
|
+
PullRequestFilter.ReviewRequested
|
|
458
|
+
],
|
|
459
|
+
},
|
|
460
|
+
]
|
|
461
|
+
: []),
|
|
462
|
+
]);
|
|
463
|
+
const cursors = parsePullRequestFacetCursor(options?.cursor);
|
|
464
|
+
const hasResumableFacetCursor = Object.keys(cursors).length !== 0;
|
|
465
|
+
const facetsWithCursor = facets.filter(facet => cursors[facet.key] != null);
|
|
466
|
+
// The first call has no cursor, so query every requested state. A continuation only happens after a
|
|
467
|
+
// prior page reported `more:true`, whose bundle carries a cursor for each facet still in flight;
|
|
468
|
+
// facets absent from the bundle are exhausted, so re-querying them from scratch would refetch the
|
|
469
|
+
// same PRs (duplicated by the dedup-free sweep) and waste an API call per page. Query only the
|
|
470
|
+
// states that still have a cursor, but degrade a malformed/empty cursor bundle, or one that doesn't
|
|
471
|
+
// apply to the current requested states, to the first page rather than returning an empty page.
|
|
472
|
+
const facetsToQuery =
|
|
473
|
+
options?.cursor != null && hasResumableFacetCursor && facetsWithCursor.length !== 0
|
|
474
|
+
? facetsWithCursor
|
|
475
|
+
: facets;
|
|
476
|
+
const results = await Promise.allSettled(
|
|
477
|
+
facetsToQuery.map(async facet => ({
|
|
478
|
+
key: facet.key,
|
|
479
|
+
result: await github.searchMyPullRequestsPage(this, toTokenWithInfo(this.id, session), {
|
|
480
|
+
baseUrl: this.apiBaseUrl,
|
|
481
|
+
state: facet.state,
|
|
482
|
+
cursor: cursors[facet.key],
|
|
483
|
+
summary: options?.summary,
|
|
484
|
+
...(facet.search != null ? { search: facet.search, includeDefaultInvolvement: false } : undefined),
|
|
485
|
+
}),
|
|
486
|
+
})),
|
|
487
|
+
);
|
|
488
|
+
if (results.every(result => result.status === 'rejected')) {
|
|
489
|
+
const first = results[0];
|
|
490
|
+
if (first?.status === 'rejected') throw first.reason;
|
|
491
|
+
}
|
|
504
492
|
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
493
|
+
const values = new Map<string, ProviderPullRequest>();
|
|
494
|
+
const nextCursors: GitHubPullRequestFacetCursor = {};
|
|
495
|
+
const failures = [];
|
|
496
|
+
let hasMore = false;
|
|
497
|
+
let truncated = false;
|
|
498
|
+
let structuralIncompleteness = false;
|
|
499
|
+
let unkeyedPullRequest = 0;
|
|
500
|
+
for (const outcome of results) {
|
|
501
|
+
if (outcome.status === 'rejected') {
|
|
502
|
+
failures.push(toCollectionScopeFailure({ providerId: this.id }, outcome.reason));
|
|
503
|
+
truncated = true;
|
|
504
|
+
continue;
|
|
505
|
+
}
|
|
518
506
|
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
}
|
|
507
|
+
const { key, result } = outcome.value;
|
|
508
|
+
for (const pr of result.values) {
|
|
509
|
+
const mapped = toProviderPullRequest(pr);
|
|
510
|
+
const identity = getProviderPullRequestIdentity(mapped) ?? `unkeyed:${unkeyedPullRequest++}`;
|
|
511
|
+
if (!values.has(identity)) {
|
|
512
|
+
values.set(identity, mapped);
|
|
526
513
|
}
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
structuralIncompleteness = true;
|
|
531
|
-
} else {
|
|
532
|
-
hasMore = true;
|
|
533
|
-
nextCursors[key] = result.cursor;
|
|
534
|
-
}
|
|
535
|
-
}
|
|
536
|
-
if (result.truncated) {
|
|
514
|
+
}
|
|
515
|
+
if (result.hasMore) {
|
|
516
|
+
if (result.cursor == null || result.cursor === '{}' || result.cursor === cursors[key]) {
|
|
537
517
|
truncated = true;
|
|
518
|
+
structuralIncompleteness = true;
|
|
519
|
+
} else {
|
|
520
|
+
hasMore = true;
|
|
521
|
+
nextCursors[key] = result.cursor;
|
|
538
522
|
}
|
|
539
523
|
}
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
paging: {
|
|
544
|
-
more: hasMore,
|
|
545
|
-
cursor: hasMore ? JSON.stringify({ type: 'cursor', cursors: nextCursors }) : '{}',
|
|
546
|
-
truncated: truncated || undefined,
|
|
547
|
-
},
|
|
548
|
-
...(failures.length || structuralIncompleteness
|
|
549
|
-
? {
|
|
550
|
-
metadata: {
|
|
551
|
-
completeness: 'partial' as const,
|
|
552
|
-
...(failures.length ? { failures: failures } : {}),
|
|
553
|
-
},
|
|
554
|
-
}
|
|
555
|
-
: undefined),
|
|
556
|
-
};
|
|
524
|
+
if (result.truncated) {
|
|
525
|
+
truncated = true;
|
|
526
|
+
}
|
|
557
527
|
}
|
|
558
528
|
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
pageSize: githubSearchMaxPageSize,
|
|
576
|
-
});
|
|
577
|
-
return result ?? undefined;
|
|
529
|
+
return {
|
|
530
|
+
values: [...values.values()],
|
|
531
|
+
paging: {
|
|
532
|
+
more: hasMore,
|
|
533
|
+
cursor: hasMore ? JSON.stringify({ type: 'cursor', cursors: nextCursors }) : '{}',
|
|
534
|
+
truncated: truncated || undefined,
|
|
535
|
+
},
|
|
536
|
+
...(failures.length || structuralIncompleteness
|
|
537
|
+
? {
|
|
538
|
+
metadata: {
|
|
539
|
+
completeness: 'partial' as const,
|
|
540
|
+
...(failures.length ? { failures: failures } : {}),
|
|
541
|
+
},
|
|
542
|
+
}
|
|
543
|
+
: undefined),
|
|
544
|
+
};
|
|
578
545
|
}
|
|
579
546
|
|
|
580
547
|
protected override async searchProviderMyIssues(
|
|
@@ -618,6 +585,7 @@ abstract class GitHubIntegrationBase<ID extends GitHubIntegrationIds> extends Gi
|
|
|
618
585
|
includeBody: true,
|
|
619
586
|
includeAllAssignees: options?.includeAllAssignees,
|
|
620
587
|
cursor: options?.cursor,
|
|
588
|
+
sort: options?.sort,
|
|
621
589
|
categories: options?.filters?.length
|
|
622
590
|
? {
|
|
623
591
|
authored: options.filters.includes(IssueFilter.Author),
|
|
@@ -718,6 +686,28 @@ abstract class GitHubIntegrationBase<ID extends GitHubIntegrationIds> extends Gi
|
|
|
718
686
|
);
|
|
719
687
|
}
|
|
720
688
|
|
|
689
|
+
/**
|
|
690
|
+
* Counts several pull-request scopes in ONE request. Like {@link countProviderIssues}, GitHub's `search`
|
|
691
|
+
* reports `issueCount` on a zero-node selection, so a count preview costs no pull-request transfer.
|
|
692
|
+
*/
|
|
693
|
+
protected override async countProviderPullRequests(
|
|
694
|
+
session: ProviderAuthenticationSession,
|
|
695
|
+
scopes: readonly { repos?: ProviderRepoInput[]; org?: string; criteria?: PullRequestSearchCriteria }[],
|
|
696
|
+
cancellation?: AbortSignal,
|
|
697
|
+
): Promise<(number | undefined)[] | undefined> {
|
|
698
|
+
return (await this.authenticationService.apis.github)?.countPullRequests(
|
|
699
|
+
this,
|
|
700
|
+
toTokenWithInfo(this.id, session),
|
|
701
|
+
scopes.map(s => ({
|
|
702
|
+
repos: s.repos?.map(r => `${r.namespace}/${r.name}`),
|
|
703
|
+
org: s.org,
|
|
704
|
+
criteria: s.criteria,
|
|
705
|
+
})),
|
|
706
|
+
{ baseUrl: this.apiBaseUrl },
|
|
707
|
+
cancellation,
|
|
708
|
+
);
|
|
709
|
+
}
|
|
710
|
+
|
|
721
711
|
protected override async searchProviderPullRequests(
|
|
722
712
|
session: ProviderAuthenticationSession,
|
|
723
713
|
searchQuery: string,
|
|
@@ -23,6 +23,7 @@ import { toTokenWithInfo } from '../authentication/models.js';
|
|
|
23
23
|
import { toCollectionScopeFailure } from '../collectionMetadata.js';
|
|
24
24
|
import { GitCloudHostIntegrationId, GitSelfManagedHostIntegrationId } from '../constants.js';
|
|
25
25
|
import type { IntegrationServiceContext } from '../context.js';
|
|
26
|
+
import { IntegrationReadUnavailableError } from '../errors.js';
|
|
26
27
|
import type { IntegrationConnectionChangeEvent } from '../integrationService.js';
|
|
27
28
|
import type { SearchMyPullRequestsOptions } from '../models/gitHostIntegration.js';
|
|
28
29
|
import { GitHostIntegration } from '../models/gitHostIntegration.js';
|
|
@@ -64,13 +65,19 @@ const cloudEnterpriseAuthProvider: IntegrationAuthenticationProviderDescriptor =
|
|
|
64
65
|
type GitLabPullRequestAssociation = 'assigned' | 'authored' | 'reviewRequested';
|
|
65
66
|
type GitLabPullRequestFacetCursor = Partial<Record<GitLabPullRequestAssociation, string>>;
|
|
66
67
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
68
|
+
/**
|
|
69
|
+
* The account-wide relationships GitLab can express, and the `scope` each maps to. `undefined` means GitLab has
|
|
70
|
+
* no equivalent axis (it has neither `Mention` nor `reviewed-by`), and this map is the single declaration of
|
|
71
|
+
* that. Exhaustive over `PullRequestFilter` on purpose: a new member must be decided for GitLab here rather
|
|
72
|
+
* than compiling through as silently unsupported. Exported for the advertised-vs-expressible capability test,
|
|
73
|
+
* which is what keeps it agreeing with `supportedAccountWidePullRequestFilters`; no runtime consumer imports it.
|
|
74
|
+
*/
|
|
75
|
+
export const gitLabAssociationForFilter: Record<PullRequestFilter, GitLabPullRequestAssociation | undefined> = {
|
|
71
76
|
[PullRequestFilter.Assignee]: 'assigned',
|
|
72
77
|
[PullRequestFilter.Author]: 'authored',
|
|
73
78
|
[PullRequestFilter.ReviewRequested]: 'reviewRequested',
|
|
79
|
+
[PullRequestFilter.Reviewed]: undefined,
|
|
80
|
+
[PullRequestFilter.Mention]: undefined,
|
|
74
81
|
};
|
|
75
82
|
|
|
76
83
|
function parseGitLabPullRequestFacetCursor(cursor: string | undefined): GitLabPullRequestFacetCursor {
|
|
@@ -473,15 +480,26 @@ abstract class GitLabIntegrationBase<ID extends GitLabIntegrationIds> extends Gi
|
|
|
473
480
|
|
|
474
481
|
const api = await this.getProvidersApi();
|
|
475
482
|
const requested = options?.filters?.length ? new Set(options.filters) : undefined;
|
|
483
|
+
// The filter contract is all-or-nothing, so refuse as soon as ONE requested member is inexpressible rather
|
|
484
|
+
// than only when every one is: dropping the rest would answer a narrower question than was asked
|
|
485
|
+
// (`[Author, Mention]` becoming `authored`). Throw rather than return `undefined`, which the facade reads
|
|
486
|
+
// as "no session could be resolved" and a drain turns into a not-connected warning, misattributing a
|
|
487
|
+
// refusal; `getMyPullRequestsForUserResult` recovers the throw into `{ error }`. Defense in depth:
|
|
488
|
+
// `resolveAccountWidePullRequestFilters` already refuses such a set, so only a direct caller of the public
|
|
489
|
+
// method reaches this.
|
|
490
|
+
const unsupported = requested == null ? [] : [...requested].filter(f => gitLabAssociationForFilter[f] == null);
|
|
491
|
+
if (unsupported.length > 0) {
|
|
492
|
+
throw new IntegrationReadUnavailableError(
|
|
493
|
+
this.name,
|
|
494
|
+
`account-wide pull request filters not expressible by GitLab: ${unsupported.join(', ')}`,
|
|
495
|
+
);
|
|
496
|
+
}
|
|
497
|
+
|
|
476
498
|
const associations: GitLabPullRequestAssociation[] =
|
|
477
499
|
requested == null
|
|
478
500
|
? ['authored', 'assigned', 'reviewRequested']
|
|
479
|
-
: [
|
|
480
|
-
|
|
481
|
-
(filter): filter is Exclude<PullRequestFilter, PullRequestFilter.Mention> =>
|
|
482
|
-
filter !== PullRequestFilter.Mention,
|
|
483
|
-
)
|
|
484
|
-
.map(filter => gitLabAssociationForFilter[filter]);
|
|
501
|
+
: Array.from(requested, f => gitLabAssociationForFilter[f]).filter(a => a != null);
|
|
502
|
+
|
|
485
503
|
const cursors = parseGitLabPullRequestFacetCursor(options?.cursor);
|
|
486
504
|
const resumable = associations.filter(association => cursors[association] != null);
|
|
487
505
|
const associationsToQuery = options?.cursor != null && resumable.length > 0 ? resumable : associations;
|
|
@@ -668,6 +686,7 @@ abstract class GitLabIntegrationBase<ID extends GitLabIntegrationIds> extends Gi
|
|
|
668
686
|
isPAT: this.isEnterprise,
|
|
669
687
|
baseUrl: baseUrl,
|
|
670
688
|
cursor: cursor,
|
|
689
|
+
sort: options?.sort,
|
|
671
690
|
});
|
|
672
691
|
if (cancellation?.aborted) throw new CancellationError();
|
|
673
692
|
return page;
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { SUPPORTED_ISSUE_SORTS } from '@gitkraken/provider-apis';
|
|
2
|
+
import { getIssueComparator } from '../../../git/utils/issue.utils.js';
|
|
3
|
+
import type { IssueSorting } from '../providerFilters.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* The per-provider ordering vocabulary of the issue reads: which neutral `field:direction` keys each provider
|
|
7
|
+
* surface can express SERVER-SIDE.
|
|
8
|
+
*
|
|
9
|
+
* DERIVED from `@gitkraken/provider-apis`, not transcribed from it. The SDK is where a neutral key becomes a
|
|
10
|
+
* provider query, so it is the only place that can say what a provider accepts; a copy kept here would be a
|
|
11
|
+
* second hand-maintained table on a second release cadence, and the symptom of drift is not a failed build but a
|
|
12
|
+
* read this facade promised and the SDK then rejects at runtime — which is exactly what happened while these were
|
|
13
|
+
* transcribed, on the one key (`reactions`) whose meaning is narrower than its name.
|
|
14
|
+
*
|
|
15
|
+
* Why the asymmetries between surfaces exist (GitHub cannot order by close date, GitLab's two reads are different
|
|
16
|
+
* APIs with different vocabularies, Azure has no `dueDate` outside the Agile process, Trello has only its
|
|
17
|
+
* `sort:edited` modifier, Jira has votes rather than reactions) is documented on the SDK's own maps. This module
|
|
18
|
+
* adds exactly one rule of its own — {@link mergeableSorts} — and everything else is a rename from the SDK's
|
|
19
|
+
* surface names to the two fields `ProviderMetadata` publishes.
|
|
20
|
+
*
|
|
21
|
+
* A caveat for anything building ONE ordering control across providers: a neutral key does not always mean the
|
|
22
|
+
* same thing. `priority:desc` is most-important-first on Jira and Linear, and least-important-first on Azure
|
|
23
|
+
* DevOps, whose WIQL orders the raw numeric column where 1 is the highest priority — so no single direction
|
|
24
|
+
* means "highest priority first" everywhere, and a unified control has to pick the direction per provider.
|
|
25
|
+
* `title` likewise orders by each backing database's collation, never JavaScript's.
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
/** One SDK read surface, widened to the union this package uses (structurally the same `field:direction` type). */
|
|
29
|
+
const surface = (name: keyof typeof SUPPORTED_ISSUE_SORTS): IssueSorting[] => [...SUPPORTED_ISSUE_SORTS[name]];
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* The subset a MERGED page can honor: the keys a normalized issue carries.
|
|
33
|
+
*
|
|
34
|
+
* Applied to the account-wide surfaces because every account-wide read is a union of several provider queries
|
|
35
|
+
* merged in this facade — GitHub's three `@me` searches, GitLab's one REST call per relationship, Azure's
|
|
36
|
+
* (project × relationship) drains — and the caller has no scope count to reduce, so it ALWAYS merges. GitLab's
|
|
37
|
+
* REST endpoint really does order by `priority` and `dueDate`, and Azure's WIQL by `resolved` and `priority`;
|
|
38
|
+
* none of them survive a merge, so the surface cannot honor them and must not advertise them.
|
|
39
|
+
*
|
|
40
|
+
* Expressed as the rule rather than as a hand-removed list, so a key the SDK adds later is classified by the same
|
|
41
|
+
* predicate that will decide at read time whether the merge can order by it, instead of by whoever last edited a
|
|
42
|
+
* literal here. The repo-scoped surfaces are NOT filtered: those merge only when the caller passes several
|
|
43
|
+
* scopes, which is a property of the call rather than of the provider (see `mergesProviderQueries`).
|
|
44
|
+
*/
|
|
45
|
+
const mergeableSorts = (sorts: readonly IssueSorting[]): IssueSorting[] =>
|
|
46
|
+
sorts.filter(sort => getIssueComparator(sort) != null);
|
|
47
|
+
|
|
48
|
+
/** GitHub and GHE: one `search` channel serves all three issue reads, so all three order the same way. */
|
|
49
|
+
export const githubIssueSorts: IssueSorting[] = surface('github');
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* The same `search` channel, narrowed to what the three `@me` searches can order once this facade has merged them.
|
|
53
|
+
*
|
|
54
|
+
* Identical to {@link githubIssueSorts} today — every qualifier the SDK's GitHub surface carries is a field an
|
|
55
|
+
* `IssueShape` models — so this exists for the drift, not for a difference: `readAccountWideIssuesPage`
|
|
56
|
+
* deliberately runs no `unmergeable` check, on the stated grounds that an account-wide table only lists keys a
|
|
57
|
+
* merge can honor. Left as the raw surface, one qualifier added upstream (a `closed` or `interactions` sort) would
|
|
58
|
+
* make that assumption false for GitHub alone and publish three concatenated alias runs under it, unwarned.
|
|
59
|
+
*/
|
|
60
|
+
export const githubAccountWideIssueSorts: IssueSorting[] = mergeableSorts(surface('github'));
|
|
61
|
+
|
|
62
|
+
/** GitLab's repository-scoped read, which goes through GraphQL `IssueSort`. */
|
|
63
|
+
export const gitlabIssueSorts: IssueSorting[] = surface('gitlabRepository');
|
|
64
|
+
|
|
65
|
+
/** GitLab's account-wide read: the REST `order_by`/`sort` pair, narrowed to what its merge can order. */
|
|
66
|
+
export const gitlabAccountWideIssueSorts: IssueSorting[] = mergeableSorts(surface('gitlabAccountWide'));
|
|
67
|
+
|
|
68
|
+
/** Azure DevOps, as WIQL `ORDER BY` columns. */
|
|
69
|
+
export const azureIssueSorts: IssueSorting[] = surface('azureDevOps');
|
|
70
|
+
|
|
71
|
+
/** The same WIQL, narrowed to what the per-project fan-out can order once it has merged. */
|
|
72
|
+
export const azureAccountWideIssueSorts: IssueSorting[] = mergeableSorts(surface('azureDevOps'));
|
|
73
|
+
|
|
74
|
+
/** Jira Cloud and Server, as JQL `ORDER BY` fields — one JQL builder, so one surface. */
|
|
75
|
+
export const jiraIssueSorts: IssueSorting[] = surface('jira');
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Linear's one root `issues` query, which both of its reads share and which takes the same `sort` argument in
|
|
79
|
+
* either direction — so this is one surface, not a repo-scoped and an account-wide pair.
|
|
80
|
+
*
|
|
81
|
+
* NOT filtered by {@link mergeableSorts} even though the account-wide read is "my issues": that read is a
|
|
82
|
+
* single server-ordered query (one `or` filter over the four relationships), so nothing is merged in this
|
|
83
|
+
* facade and there is no comparator to satisfy. Filtering it would drop `priority` and `dueDate` — keys the
|
|
84
|
+
* server really does order by — for a merge that does not happen.
|
|
85
|
+
*/
|
|
86
|
+
export const linearIssueSorts: IssueSorting[] = surface('linear');
|
|
87
|
+
|
|
88
|
+
/** Trello's search modifiers: `sort:edited` / `sort:-edited`, and nothing else usable for issues. */
|
|
89
|
+
export const trelloIssueSorts: IssueSorting[] = surface('trello');
|
|
@@ -8,8 +8,9 @@ import { Logger } from '../../../utils/logger.js';
|
|
|
8
8
|
import type { IntegrationAuthenticationProviderDescriptor } from '../authentication/integrationAuthenticationProvider.js';
|
|
9
9
|
import type { ProviderAuthenticationSession } from '../authentication/models.js';
|
|
10
10
|
import { toTokenWithInfo } from '../authentication/models.js';
|
|
11
|
-
import { toCollectionScopeFailure } from '../collectionMetadata.js';
|
|
11
|
+
import { throwIfCallerContractError, toCollectionScopeFailure } from '../collectionMetadata.js';
|
|
12
12
|
import { IssuesCloudHostIntegrationId } from '../constants.js';
|
|
13
|
+
import type { IssuesForProjectOptions } from '../models/issueReads.js';
|
|
13
14
|
import { IssuesIntegration } from '../models/issuesIntegration.js';
|
|
14
15
|
import type { ProviderApiCollectionResult, ProviderIssue } from './models.js';
|
|
15
16
|
import { IssueFilter, providersMetadata, toAccount, toIssueShape } from './models.js';
|
|
@@ -234,7 +235,7 @@ export class JiraIntegration extends IssuesIntegration<IssuesCloudHostIntegratio
|
|
|
234
235
|
protected override async getProviderIssuesForProject(
|
|
235
236
|
session: ProviderAuthenticationSession,
|
|
236
237
|
project: JiraProjectDescriptor,
|
|
237
|
-
options?:
|
|
238
|
+
options?: IssuesForProjectOptions,
|
|
238
239
|
): Promise<IssueShape[] | undefined> {
|
|
239
240
|
return (await this.getProviderIssuesForProjectWithTruncation(session, project, options))?.values;
|
|
240
241
|
}
|
|
@@ -242,7 +243,7 @@ export class JiraIntegration extends IssuesIntegration<IssuesCloudHostIntegratio
|
|
|
242
243
|
protected override async getProviderIssuesForProjectWithTruncation(
|
|
243
244
|
session: ProviderAuthenticationSession,
|
|
244
245
|
project: JiraProjectDescriptor,
|
|
245
|
-
options?:
|
|
246
|
+
options?: IssuesForProjectOptions,
|
|
246
247
|
): Promise<{ values: IssueShape[]; truncated: boolean; metadata?: CollectionMetadata } | undefined> {
|
|
247
248
|
const tokenWithInfo = toTokenWithInfo(this.id, session);
|
|
248
249
|
|
|
@@ -273,6 +274,7 @@ export class JiraIntegration extends IssuesIntegration<IssuesCloudHostIntegratio
|
|
|
273
274
|
result = await api.getIssuesForProjectPaged(tokenWithInfo, project.name, project.resourceId, {
|
|
274
275
|
...scope,
|
|
275
276
|
cursor: cursor,
|
|
277
|
+
sort: options?.sort,
|
|
276
278
|
});
|
|
277
279
|
} catch (ex) {
|
|
278
280
|
// A page failure after the first page leaves the already-drained prefix intact; record the
|
|
@@ -390,6 +392,10 @@ export class JiraIntegration extends IssuesIntegration<IssuesCloudHostIntegratio
|
|
|
390
392
|
// incomplete: keep the sibling results but record a structured failure so the facade can warn on
|
|
391
393
|
// the specific filter (auth/rate-limit) instead of just a generic truncation flag.
|
|
392
394
|
if (outcome.status !== 'fulfilled') {
|
|
395
|
+
// Identical for every filter branch, so degrading it would report one failure per branch for a
|
|
396
|
+
// single invalid call — see `throwIfCallerContractError`.
|
|
397
|
+
throwIfCallerContractError(outcome.reason);
|
|
398
|
+
|
|
393
399
|
truncated = true;
|
|
394
400
|
const failure = toCollectionScopeFailure(
|
|
395
401
|
{ providerId: this.id, resourceId: project.resourceId, projectId: project.name },
|