@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,9 +1,10 @@
|
|
|
1
|
-
import type { IssueShape } from '../../../git/models/issue.js';
|
|
1
|
+
import type { IssueShape, IssueSorting } from '../../../git/models/issue.js';
|
|
2
2
|
import { mergeAssessmentInto } from '../collectionMetadata.js';
|
|
3
3
|
import type { IntegrationIds } from '../constants.js';
|
|
4
4
|
import { GitCloudHostIntegrationId, GitSelfManagedHostIntegrationId } from '../constants.js';
|
|
5
|
+
import type { GitHostIntegration } from '../models/gitHostIntegration.js';
|
|
5
6
|
import type { IssueFilter, ProviderReposInput } from '../providers/models.js';
|
|
6
|
-
import { PagingMode, providersMetadata } from '../providers/models.js';
|
|
7
|
+
import { isRepoIdsInput, PagingMode, providersMetadata } from '../providers/models.js';
|
|
7
8
|
import { mergeCollectionMetadata } from '../providers/utils/providerPaging.js';
|
|
8
9
|
import type { ProviderPagedResult, ProviderWarning } from '../results.js';
|
|
9
10
|
import { reconcileOmissionsWithFailure } from '../results.js';
|
|
@@ -15,6 +16,7 @@ import {
|
|
|
15
16
|
import type { ProviderReadContext } from './context.js';
|
|
16
17
|
import { runCaptured } from './drains.js';
|
|
17
18
|
import { resolveAccountWideIssueFilters } from './filters.js';
|
|
19
|
+
import { resolveIssueSort, toIssueOrdering } from './ordering.js';
|
|
18
20
|
import {
|
|
19
21
|
drainFlatPagesToRequestedPage,
|
|
20
22
|
drainToRequestedPage,
|
|
@@ -31,39 +33,115 @@ import {
|
|
|
31
33
|
issuesUnsupportedWarning,
|
|
32
34
|
otherWarning,
|
|
33
35
|
truncationWarning,
|
|
36
|
+
unmergeableIssueSortWarning,
|
|
34
37
|
unsupportedAccountWideIssueFiltersWarning,
|
|
38
|
+
unsupportedIssueSortWarning,
|
|
35
39
|
} from './warnings.js';
|
|
36
40
|
|
|
41
|
+
/**
|
|
42
|
+
* Whether a repo-scoped page is assembled from SEVERAL provider queries and merged here — which is what decides
|
|
43
|
+
* whether the requested order can be honored at all.
|
|
44
|
+
*
|
|
45
|
+
* `PagingMode.Repos` (GitHub) sends one search however many repositories are named, so its page arrives ordered by
|
|
46
|
+
* the provider. `Repo` (GitLab) issues one query per repository, and `Project` (Azure) one per PROJECT — several
|
|
47
|
+
* repositories of the same project are still one query, which is why that case counts distinct projects rather than
|
|
48
|
+
* repositories.
|
|
49
|
+
*
|
|
50
|
+
* The `Repo` case has a second way to merge that has nothing to do with the count: given repository IDS rather than
|
|
51
|
+
* descriptors, `getMyIssuesForReposResult` skips its per-repository fan-out entirely (that branch is guarded on
|
|
52
|
+
* `!isRepoIdsInput`) and calls the SDK's `getIssuesForRepos`, which for GitLab is the multi-project aggregate that
|
|
53
|
+
* merges in the SDK and refuses `priority`/`dueDate` however few scopes it was given. Treating that as merged is
|
|
54
|
+
* what makes this facade refuse it up front, with the message that names the reason, instead of letting the SDK
|
|
55
|
+
* reject a read the capability table had just promised.
|
|
56
|
+
*/
|
|
57
|
+
function mergesProviderQueries(pagingMode: PagingMode | undefined, repos: ProviderReposInput | undefined): boolean {
|
|
58
|
+
const scopes = repos ?? [];
|
|
59
|
+
switch (pagingMode) {
|
|
60
|
+
case PagingMode.Repo:
|
|
61
|
+
// `isRepoIdsInput` is the same predicate `ProvidersApi` selects the SDK aggregate with, so this cannot
|
|
62
|
+
// disagree with the branch it is predicting.
|
|
63
|
+
return isRepoIdsInput(scopes) ? scopes.length > 0 : scopes.length > 1;
|
|
64
|
+
case PagingMode.Project:
|
|
65
|
+
// Only the descriptor form carries a project; the id form is refused by the Azure read before it runs.
|
|
66
|
+
return new Set(scopes.map(r => (typeof r === 'object' ? r.project : undefined))).size > 1;
|
|
67
|
+
default:
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* What {@link listIssuesPage} accepts, shared by name with the two functions it dispatches to.
|
|
74
|
+
*
|
|
75
|
+
* Named rather than left inline: three functions now take this exact shape, and reaching it from the other two as
|
|
76
|
+
* `Parameters<typeof listIssuesPage>[1]` would define the contract in terms of the function that consumes it —
|
|
77
|
+
* inside out, and unreadable at the point where the fields are actually used.
|
|
78
|
+
*/
|
|
79
|
+
export interface ListIssuesPageOptions {
|
|
80
|
+
providerId: IntegrationIds;
|
|
81
|
+
repos?: ProviderReposInput;
|
|
82
|
+
/** Narrows the account-wide read to one org/account. Requires a host with a project layer (Azure). */
|
|
83
|
+
org?: string;
|
|
84
|
+
/** Narrows the account-wide read to one project. Requires a host with a project layer (Azure). */
|
|
85
|
+
project?: string;
|
|
86
|
+
/**
|
|
87
|
+
* Narrows to the requested relationship(s). On the account-wide path this replaces the provider's own
|
|
88
|
+
* definition of "my issues" (GitHub/GHE: authored ∪ assigned ∪ mentioned; Azure: assigned ∪ authored;
|
|
89
|
+
* GitLab: assigned-to-me), so `[Assignee]` yields `assignee:@me` everywhere it's expressible. A set the
|
|
90
|
+
* provider can't express server-side is refused whole (warning + `fetchFailed`), never widened — check
|
|
91
|
+
* getSupportedFilters first to avoid that path.
|
|
92
|
+
*/
|
|
93
|
+
filters?: IssueFilter[];
|
|
94
|
+
/** Broadens the read to every assignee. Contradicts `filters`; passing both is refused. */
|
|
95
|
+
includeAllAssignees?: boolean;
|
|
96
|
+
/**
|
|
97
|
+
* How to order the page, as `field:direction`. Omitted orders most-recently-updated-first wherever the
|
|
98
|
+
* provider can express that, which is this facade's default rather than the provider's own.
|
|
99
|
+
*
|
|
100
|
+
* Validated against `getSupportedFilters().issueSorts` on the repo-scoped path and `.issueSortsAccountWide`
|
|
101
|
+
* on the account-wide one — two genuinely different vocabularies for GitLab, whose two reads are different
|
|
102
|
+
* APIs. A key the provider can't express refuses the read (warning + `fetchFailed`) rather than serving a
|
|
103
|
+
* differently-ordered list, for the same reason an inexpressible filter does: the reachable window is
|
|
104
|
+
* bounded, so another order is another subset.
|
|
105
|
+
*
|
|
106
|
+
* On the repo-scoped path with SEVERAL scopes the page is a merge of one query per repository/project, so
|
|
107
|
+
* only a key a normalized issue carries can be honored: `priority`, `dueDate` and `resolved` are refused
|
|
108
|
+
* there even where the provider supports them on a single scope. Order is per page, and per scope across
|
|
109
|
+
* pages — the merge orders what a page contains, not the sequence of pages.
|
|
110
|
+
*/
|
|
111
|
+
sort?: IssueSorting;
|
|
112
|
+
page?: number;
|
|
113
|
+
cursor?: string;
|
|
114
|
+
itemsPerPage?: number;
|
|
115
|
+
forceSync?: boolean;
|
|
116
|
+
connectionId?: string;
|
|
117
|
+
/**
|
|
118
|
+
* Explicit self-managed host domain. Used only when the requested connection has no configured domain;
|
|
119
|
+
* it must come from the trusted authentication configuration, not repository or remote data.
|
|
120
|
+
*/
|
|
121
|
+
domain?: string;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* What both halves of {@link listIssuesPage} need from its shared prologue.
|
|
126
|
+
*
|
|
127
|
+
* A bundle rather than six parameters because the two reads take exactly the same inputs and differ only in what
|
|
128
|
+
* they do with them; threading them positionally would make the two call sites read as two different reads of two
|
|
129
|
+
* different things, which is the opposite of what is true.
|
|
130
|
+
*
|
|
131
|
+
* Note `ctx` is absent: every use of it — resolving the integration, the domain, the forced refresh, the
|
|
132
|
+
* connection warnings — is in the prologue, which is what makes this the right cut.
|
|
133
|
+
*/
|
|
134
|
+
interface IssueReadContext {
|
|
135
|
+
options: ListIssuesPageOptions;
|
|
136
|
+
integration: GitHostIntegration;
|
|
137
|
+
domain: string | undefined;
|
|
138
|
+
warnOnMissingSession: boolean;
|
|
139
|
+
page: number;
|
|
140
|
+
}
|
|
141
|
+
|
|
37
142
|
export async function listIssuesPage(
|
|
38
143
|
ctx: ProviderReadContext,
|
|
39
|
-
options:
|
|
40
|
-
providerId: IntegrationIds;
|
|
41
|
-
repos?: ProviderReposInput;
|
|
42
|
-
/** Narrows the account-wide read to one org/account. Requires a host with a project layer (Azure). */
|
|
43
|
-
org?: string;
|
|
44
|
-
/** Narrows the account-wide read to one project. Requires a host with a project layer (Azure). */
|
|
45
|
-
project?: string;
|
|
46
|
-
/**
|
|
47
|
-
* Narrows to the requested relationship(s). On the account-wide path this replaces the provider's own
|
|
48
|
-
* definition of "my issues" (GitHub/GHE: authored ∪ assigned ∪ mentioned; Azure: assigned ∪ authored;
|
|
49
|
-
* GitLab: assigned-to-me), so `[Assignee]` yields `assignee:@me` everywhere it's expressible. A set the
|
|
50
|
-
* provider can't express server-side is refused whole (warning + `fetchFailed`), never widened — check
|
|
51
|
-
* getSupportedFilters first to avoid that path.
|
|
52
|
-
*/
|
|
53
|
-
filters?: IssueFilter[];
|
|
54
|
-
/** Broadens the read to every assignee. Contradicts `filters`; passing both is refused. */
|
|
55
|
-
includeAllAssignees?: boolean;
|
|
56
|
-
page?: number;
|
|
57
|
-
cursor?: string;
|
|
58
|
-
itemsPerPage?: number;
|
|
59
|
-
forceSync?: boolean;
|
|
60
|
-
connectionId?: string;
|
|
61
|
-
/**
|
|
62
|
-
* Explicit self-managed host domain. Used only when the requested connection has no configured domain;
|
|
63
|
-
* it must come from the trusted authentication configuration, not repository or remote data.
|
|
64
|
-
*/
|
|
65
|
-
domain?: string;
|
|
66
|
-
},
|
|
144
|
+
options: ListIssuesPageOptions,
|
|
67
145
|
): Promise<ProviderPagedResult<IssueShape>> {
|
|
68
146
|
const page = Math.max(1, options.page ?? 1);
|
|
69
147
|
if (isIssuesHostIntegrationId(options.providerId)) {
|
|
@@ -100,188 +178,297 @@ export async function listIssuesPage(
|
|
|
100
178
|
return refusedPage(page, [issuesUnsupportedWarning(options.providerId, domain, options.connectionId)], true);
|
|
101
179
|
}
|
|
102
180
|
|
|
103
|
-
|
|
181
|
+
// `repos` is what selects between the two: with none there is no scope to query per repository, so the
|
|
182
|
+
// provider's own account-wide search answers instead. They are different provider queries with different
|
|
183
|
+
// capability tables, cursors and completeness signals — see each function.
|
|
184
|
+
const context: IssueReadContext = {
|
|
185
|
+
options: options,
|
|
186
|
+
integration: integration,
|
|
187
|
+
domain: domain,
|
|
188
|
+
warnOnMissingSession: warnOnMissingSession,
|
|
189
|
+
page: page,
|
|
190
|
+
};
|
|
191
|
+
return (options.repos?.length ?? 0) === 0 ? readAccountWideIssuesPage(context) : readRepoScopedIssuesPage(context);
|
|
192
|
+
}
|
|
104
193
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
194
|
+
/**
|
|
195
|
+
* The account-wide read: the provider's own definition of "my issues", behind a composite cursor that spans
|
|
196
|
+
* several provider searches.
|
|
197
|
+
*
|
|
198
|
+
* Its own function rather than a branch of the paged read, because almost nothing is shared past the guards: it
|
|
199
|
+
* validates a different filter table and a different sort table, it is cursor-only (a page number cannot address
|
|
200
|
+
* a composite cursor), it accumulates per-scope failures across the pages it walks, and it always merges several
|
|
201
|
+
* queries. The two used to share one 460-line function behind a mode flag, and the comments explaining how far
|
|
202
|
+
* apart they are were the clue that they wanted separating.
|
|
203
|
+
*/
|
|
204
|
+
async function readAccountWideIssuesPage({
|
|
205
|
+
options,
|
|
206
|
+
integration,
|
|
207
|
+
domain,
|
|
208
|
+
warnOnMissingSession,
|
|
209
|
+
page,
|
|
210
|
+
}: IssueReadContext): Promise<ProviderPagedResult<IssueShape>> {
|
|
211
|
+
// The account-wide read is cursor-only, so a refusal can't claim the requested position — it reports
|
|
212
|
+
// page 1, per ProviderPageInfo.currentPage.
|
|
213
|
+
const refused = (warning: ProviderWarning) => refusedPage<IssueShape>(1, [warning], true);
|
|
109
214
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
215
|
+
// Checked before the provider-specific guards below: a caller passing both has a contradictory request
|
|
216
|
+
// whatever the provider, and saying so is more useful than reporting one half of it as unsupported.
|
|
217
|
+
// `filters` narrows this read to a relationship (`[Assignee]` ⇒ just assigned-to-me); `includeAllAssignees`
|
|
218
|
+
// broadens it to every assignee. Honoring either silently would answer a question the caller didn't ask.
|
|
219
|
+
if (options.filters?.length && options.includeAllAssignees === true) {
|
|
220
|
+
return refused(
|
|
221
|
+
otherWarning(
|
|
222
|
+
options.providerId,
|
|
223
|
+
domain,
|
|
224
|
+
options.connectionId,
|
|
225
|
+
'`filters` and `includeAllAssignees` are contradictory for an account-wide issue read; pass only one.',
|
|
226
|
+
),
|
|
227
|
+
);
|
|
228
|
+
}
|
|
124
229
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
230
|
+
// GitHub expresses "any assignee" as `assignee:*`, which is meaningless without a scope: unscoped it
|
|
231
|
+
// matches millions of issues across all of GitHub instead of the user's own world. ANY scope makes it
|
|
232
|
+
// meaningful (one repository, several, or an org) — it is specifically THIS branch, the account-wide read,
|
|
233
|
+
// that has none to offer. Scope the read to repositories (or use `searchIssuesPage`, whose criteria model
|
|
234
|
+
// takes `any-assignee` alongside a `repos`/`org` scope) rather than reading unscoped.
|
|
235
|
+
if (
|
|
236
|
+
options.includeAllAssignees === true &&
|
|
237
|
+
(options.providerId === GitCloudHostIntegrationId.GitHub ||
|
|
238
|
+
options.providerId === GitSelfManagedHostIntegrationId.CloudGitHubEnterprise)
|
|
239
|
+
) {
|
|
240
|
+
return refused(
|
|
241
|
+
otherWarning(
|
|
242
|
+
options.providerId,
|
|
243
|
+
domain,
|
|
244
|
+
options.connectionId,
|
|
245
|
+
'`includeAllAssignees` is not supported for account-wide GitHub issue reads; scope the read to repositories instead.',
|
|
246
|
+
),
|
|
247
|
+
);
|
|
248
|
+
}
|
|
144
249
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
250
|
+
// Only a host with a project layer can narrow server-side. Reject the request rather than serving an
|
|
251
|
+
// unscoped list as if it had been narrowed: the caller would otherwise have to filter client-side,
|
|
252
|
+
// which desynchronizes the filtered `items` from this read's `hasMore`/`currentPage` and shows
|
|
253
|
+
// "no issues" for a page that simply held none of the requested project's.
|
|
254
|
+
if ((options.org != null || options.project != null) && !integration.supportsProjectDiscovery) {
|
|
255
|
+
return refused(
|
|
256
|
+
otherWarning(
|
|
257
|
+
options.providerId,
|
|
258
|
+
domain,
|
|
259
|
+
options.connectionId,
|
|
260
|
+
`Project-scoped issue reads are not supported by '${options.providerId}'; scope the read to repositories instead.`,
|
|
261
|
+
),
|
|
262
|
+
);
|
|
263
|
+
}
|
|
159
264
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
265
|
+
// Narrowing the account-wide read is only honest when the provider can express it server-side: its
|
|
266
|
+
// per-relationship queries produced the page and the cursor together, so dropping items afterward would
|
|
267
|
+
// leave `items` describing a different result set than `hasMore`/`currentPage`.
|
|
268
|
+
const resolvedIssueFilters = resolveAccountWideIssueFilters(options.providerId, options.filters);
|
|
269
|
+
if (resolvedIssueFilters.unsupported) {
|
|
270
|
+
return refused(
|
|
271
|
+
unsupportedAccountWideIssueFiltersWarning(
|
|
272
|
+
options.providerId,
|
|
273
|
+
domain,
|
|
274
|
+
options.connectionId,
|
|
275
|
+
options.filters!,
|
|
276
|
+
),
|
|
277
|
+
);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
// Every provider's account-wide read is a UNION of several queries — GitHub's three `@me` searches,
|
|
281
|
+
// GitLab's one REST call per relationship, Azure's (project x relationship) drains — so it always merges,
|
|
282
|
+
// and `supportedAccountWideIssueSorts` already lists only keys a merge can honor. No separate
|
|
283
|
+
// mergeability refusal is needed here, unlike on the repo-scoped path below.
|
|
284
|
+
const resolvedSort = resolveIssueSort(
|
|
285
|
+
providersMetadata[options.providerId]?.supportedAccountWideIssueSorts,
|
|
286
|
+
options.sort,
|
|
287
|
+
);
|
|
288
|
+
if (resolvedSort.rejection != null) {
|
|
289
|
+
return refused(
|
|
290
|
+
unsupportedIssueSortWarning(options.providerId, domain, options.connectionId, resolvedSort.rejection),
|
|
291
|
+
);
|
|
292
|
+
}
|
|
174
293
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
294
|
+
// `merged: true` unconditionally: this read has no scope count for a caller to reduce. It is also why no
|
|
295
|
+
// `unmergeable` check follows — `supportedAccountWideIssueSorts` declares only keys a merge can honor.
|
|
296
|
+
const accountWideOrdering = toIssueOrdering(resolvedSort.sort, true);
|
|
297
|
+
|
|
298
|
+
// The repo-scoped core rejects empty repos (GitHub/Bitbucket/Azure); read the account-wide,
|
|
299
|
+
// already-user-scoped core instead. GitHub exposes a composite cursor across its authored,
|
|
300
|
+
// assigned, and mentioned searches. Walk it internally when the caller supplies only page N.
|
|
301
|
+
const readAccountWidePage = (cursor: string | undefined) =>
|
|
302
|
+
runCaptured(
|
|
303
|
+
options.providerId,
|
|
304
|
+
domain,
|
|
305
|
+
options.connectionId,
|
|
306
|
+
() =>
|
|
307
|
+
integration.searchMyIssuesWithTruncationResult(undefined, undefined, options.connectionId, {
|
|
308
|
+
includeAllAssignees: options.includeAllAssignees,
|
|
309
|
+
filters: resolvedIssueFilters.filters,
|
|
310
|
+
cursor: cursor,
|
|
311
|
+
org: options.org,
|
|
312
|
+
project: options.project,
|
|
313
|
+
sort: accountWideOrdering.sort,
|
|
314
|
+
}),
|
|
315
|
+
{ warnOnMissingSession: warnOnMissingSession },
|
|
316
|
+
);
|
|
317
|
+
const first = await readAccountWidePage(options.cursor);
|
|
318
|
+
const warnings = first.warning != null ? [first.warning] : [];
|
|
319
|
+
// Merged across the walked pages, so a per-scope failure reported on an earlier page isn't lost by
|
|
320
|
+
// paging past it — the one thing this read accumulates that the filtered search doesn't.
|
|
321
|
+
let allMetadata = first.value?.metadata;
|
|
322
|
+
const drained = await drainFlatPagesToRequestedPage(first, {
|
|
323
|
+
requestedPage: page,
|
|
324
|
+
suppliedCursor: options.cursor,
|
|
325
|
+
warnings: warnings,
|
|
326
|
+
readPage: readAccountWidePage,
|
|
327
|
+
fold: p => {
|
|
328
|
+
allMetadata = mergeCollectionMetadata(allMetadata, p.metadata);
|
|
329
|
+
},
|
|
330
|
+
});
|
|
331
|
+
const { value, currentPage, requestedPageMissing } = drained;
|
|
332
|
+
const currentTruncated = drained.truncated;
|
|
333
|
+
const pageFetchFailed = drained.fetchFailed;
|
|
334
|
+
|
|
335
|
+
// GitHub, GitLab, and Azure implement an account-wide issue search; a provider that doesn't (Bitbucket
|
|
336
|
+
// exposes no issues at all, and `supportsIssues` already short-circuits it above) returns `undefined`
|
|
337
|
+
// with no error. Surface that as an explicit unsupported warning + fetchFailed rather than a silent
|
|
338
|
+
// empty success — the caller must fall back (e.g. broadenIssues over repos).
|
|
339
|
+
if (value == null && warnings.length === 0) {
|
|
340
|
+
return refused(
|
|
341
|
+
otherWarning(
|
|
180
342
|
options.providerId,
|
|
181
343
|
domain,
|
|
182
344
|
options.connectionId,
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
345
|
+
`Account-wide issue search is not supported by '${options.providerId}'; scope the read to repositories instead.`,
|
|
346
|
+
),
|
|
347
|
+
);
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
const items = requestedPageMissing ? [] : (value?.values ?? []);
|
|
351
|
+
// Each query arrived ordered by the provider; their union is not, and the union is what this read publishes.
|
|
352
|
+
// Idempotent for GitHub, whose aliased-search engine already ordered the same merge — one call here rather
|
|
353
|
+
// than the same logic repeated in each of the three provider implementations.
|
|
354
|
+
const orderedItems = accountWideOrdering.order(items);
|
|
355
|
+
// Fold in structured per-scope failures from the account-wide fan-out (e.g. Azure across projects):
|
|
356
|
+
// scope-aware warnings + `fetchFailed` when a scope failed, without discarding the successful items.
|
|
357
|
+
const assessment = mergeAssessmentInto(warnings, options.providerId, domain, options.connectionId, allMetadata);
|
|
358
|
+
// An account-wide search that couldn't confirm completeness (a provider cap with no cursor, or a
|
|
359
|
+
// per-scope backstop/failure) is incomplete and can't be paged; report it as truncated (+ a
|
|
360
|
+
// provider-neutral warning, unless a structured failure already explains it) rather than a complete
|
|
361
|
+
// list. Don't hard-code GitHub's "100 per category" cap here — Azure reaches this via a per-project
|
|
362
|
+
// backstop, and other providers may cap differently.
|
|
363
|
+
// The account-wide read is cursor-only (its composite cursor spans several provider searches, so a
|
|
364
|
+
// page number can't address it): `hasMore` without a real cursor is a dead end, so report it as
|
|
365
|
+
// terminal-but-incomplete rather than inviting the caller to page forever.
|
|
366
|
+
const continuation = resolveContinuation(
|
|
367
|
+
{
|
|
368
|
+
hasMore: requestedPageMissing ? false : (value?.hasMore ?? false),
|
|
369
|
+
cursor: requestedPageMissing ? undefined : usableCursor(value?.cursor),
|
|
370
|
+
truncated: currentTruncated,
|
|
371
|
+
},
|
|
372
|
+
undefined,
|
|
373
|
+
);
|
|
374
|
+
const truncated = continuation.truncated || assessment.truncated;
|
|
375
|
+
if (truncated && warnings.length === 0) {
|
|
376
|
+
warnings.push(
|
|
377
|
+
truncationWarning(
|
|
378
|
+
options.providerId,
|
|
379
|
+
domain,
|
|
380
|
+
options.connectionId,
|
|
381
|
+
'Account-wide issue search',
|
|
382
|
+
// `exhausted`: this composite read exposes no budget the caller can raise, so nothing it
|
|
383
|
+
// could call would return the withheld items.
|
|
384
|
+
assessment.fetchFailed || pageFetchFailed ? 'interrupted' : 'exhausted',
|
|
385
|
+
),
|
|
386
|
+
);
|
|
387
|
+
}
|
|
388
|
+
// A metadata omission from an earlier page asserts the read succeeded; a later page may since have failed.
|
|
389
|
+
reconcileOmissionsWithFailure(warnings, assessment.fetchFailed || pageFetchFailed);
|
|
390
|
+
return {
|
|
391
|
+
items: orderedItems,
|
|
392
|
+
warnings: warnings,
|
|
393
|
+
page: {
|
|
394
|
+
// Positional, per ProviderPageInfo.currentPage. `currentPage` already carries what the provider
|
|
395
|
+
// reported or what the internal drain counted; a requested page past the terminal cursor is
|
|
396
|
+
// reported as that empty page N. The account-wide read is cursor-only, so a `page` the caller
|
|
397
|
+
// didn't pair with a cursor is never echoed.
|
|
398
|
+
currentPage: requestedPageMissing
|
|
399
|
+
? page
|
|
400
|
+
: resolveCurrentPage({
|
|
401
|
+
providerPage: currentPage,
|
|
402
|
+
requestedPage: page,
|
|
403
|
+
suppliedCursor: options.cursor,
|
|
404
|
+
pageAdvanceable: false,
|
|
190
405
|
}),
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
requestedPage: page,
|
|
200
|
-
suppliedCursor: options.cursor,
|
|
201
|
-
warnings: warnings,
|
|
202
|
-
readPage: readAccountWidePage,
|
|
203
|
-
fold: p => {
|
|
204
|
-
allMetadata = mergeCollectionMetadata(allMetadata, p.metadata);
|
|
205
|
-
},
|
|
206
|
-
});
|
|
207
|
-
const { value, currentPage, requestedPageMissing } = drained;
|
|
208
|
-
const currentTruncated = drained.truncated;
|
|
209
|
-
const pageFetchFailed = drained.fetchFailed;
|
|
406
|
+
itemsPerPage: orderedItems.length,
|
|
407
|
+
truncated: truncated || undefined,
|
|
408
|
+
},
|
|
409
|
+
hasMore: continuation.hasMore,
|
|
410
|
+
cursor: continuation.cursor,
|
|
411
|
+
fetchFailed: assessment.fetchFailed || pageFetchFailed || undefined,
|
|
412
|
+
};
|
|
413
|
+
}
|
|
210
414
|
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
415
|
+
/**
|
|
416
|
+
* The repository-scoped read: one provider query per repository or project, paged by the provider's own
|
|
417
|
+
* continuation and drained internally when the caller supplied only a page number.
|
|
418
|
+
*
|
|
419
|
+
* See {@link readAccountWideIssuesPage} for why the two are separate functions.
|
|
420
|
+
*/
|
|
421
|
+
async function readRepoScopedIssuesPage({
|
|
422
|
+
options,
|
|
423
|
+
integration,
|
|
424
|
+
domain,
|
|
425
|
+
warnOnMissingSession,
|
|
426
|
+
page,
|
|
427
|
+
}: IssueReadContext): Promise<ProviderPagedResult<IssueShape>> {
|
|
428
|
+
const metadata = providersMetadata[options.providerId];
|
|
429
|
+
|
|
430
|
+
// How many separate provider queries this page is assembled from, which decides whether the requested order can
|
|
431
|
+
// be honored at all. `PagingMode.Repos` (GitHub) sends ONE search however many repositories are named, so its
|
|
432
|
+
// page arrives ordered by the provider; `Repo` (GitLab) and `Project` (Azure) issue one query per scope and are
|
|
433
|
+
// concatenated here, so a page spanning several of them is only as orderable as a normalized issue is.
|
|
434
|
+
const resolvedRepoScopedSort = resolveIssueSort(metadata?.supportedIssueSorts, options.sort);
|
|
435
|
+
if (resolvedRepoScopedSort.rejection != null) {
|
|
436
|
+
return refusedPage(
|
|
437
|
+
page,
|
|
438
|
+
[
|
|
439
|
+
unsupportedIssueSortWarning(
|
|
218
440
|
options.providerId,
|
|
219
441
|
domain,
|
|
220
442
|
options.connectionId,
|
|
221
|
-
|
|
443
|
+
resolvedRepoScopedSort.rejection,
|
|
222
444
|
),
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
const items = requestedPageMissing ? [] : (value?.values ?? []);
|
|
227
|
-
// Fold in structured per-scope failures from the account-wide fan-out (e.g. Azure across projects):
|
|
228
|
-
// scope-aware warnings + `fetchFailed` when a scope failed, without discarding the successful items.
|
|
229
|
-
const assessment = mergeAssessmentInto(warnings, options.providerId, domain, options.connectionId, allMetadata);
|
|
230
|
-
// An account-wide search that couldn't confirm completeness (a provider cap with no cursor, or a
|
|
231
|
-
// per-scope backstop/failure) is incomplete and can't be paged; report it as truncated (+ a
|
|
232
|
-
// provider-neutral warning, unless a structured failure already explains it) rather than a complete
|
|
233
|
-
// list. Don't hard-code GitHub's "100 per category" cap here — Azure reaches this via a per-project
|
|
234
|
-
// backstop, and other providers may cap differently.
|
|
235
|
-
// The account-wide read is cursor-only (its composite cursor spans several provider searches, so a
|
|
236
|
-
// page number can't address it): `hasMore` without a real cursor is a dead end, so report it as
|
|
237
|
-
// terminal-but-incomplete rather than inviting the caller to page forever.
|
|
238
|
-
const continuation = resolveContinuation(
|
|
239
|
-
{
|
|
240
|
-
hasMore: requestedPageMissing ? false : (value?.hasMore ?? false),
|
|
241
|
-
cursor: requestedPageMissing ? undefined : usableCursor(value?.cursor),
|
|
242
|
-
truncated: currentTruncated,
|
|
243
|
-
},
|
|
244
|
-
undefined,
|
|
445
|
+
],
|
|
446
|
+
true,
|
|
245
447
|
);
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
const issuesPagingMode = metadata?.issuesPagingMode;
|
|
451
|
+
const ordering = toIssueOrdering(
|
|
452
|
+
resolvedRepoScopedSort.sort,
|
|
453
|
+
mergesProviderQueries(issuesPagingMode, options.repos),
|
|
454
|
+
);
|
|
455
|
+
// Refused rather than served as concatenated per-scope runs, which would look ordered without being so — and
|
|
456
|
+
// only where it actually merges, since the same key against the same provider is perfectly answerable for a
|
|
457
|
+
// single repository or project.
|
|
458
|
+
if (ordering.unmergeable != null) {
|
|
459
|
+
return refusedPage(
|
|
460
|
+
page,
|
|
461
|
+
[
|
|
462
|
+
unmergeableIssueSortWarning(
|
|
250
463
|
options.providerId,
|
|
251
464
|
domain,
|
|
252
465
|
options.connectionId,
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
// could call would return the withheld items.
|
|
256
|
-
assessment.fetchFailed || pageFetchFailed ? 'interrupted' : 'exhausted',
|
|
466
|
+
ordering.unmergeable,
|
|
467
|
+
issuesPagingMode === PagingMode.Project ? 'projects' : 'repositories',
|
|
257
468
|
),
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
reconcileOmissionsWithFailure(warnings, assessment.fetchFailed || pageFetchFailed);
|
|
262
|
-
return {
|
|
263
|
-
items: items,
|
|
264
|
-
warnings: warnings,
|
|
265
|
-
page: {
|
|
266
|
-
// Positional, per ProviderPageInfo.currentPage. `currentPage` already carries what the provider
|
|
267
|
-
// reported or what the internal drain counted; a requested page past the terminal cursor is
|
|
268
|
-
// reported as that empty page N. The account-wide read is cursor-only, so a `page` the caller
|
|
269
|
-
// didn't pair with a cursor is never echoed.
|
|
270
|
-
currentPage: requestedPageMissing
|
|
271
|
-
? page
|
|
272
|
-
: resolveCurrentPage({
|
|
273
|
-
providerPage: currentPage,
|
|
274
|
-
requestedPage: page,
|
|
275
|
-
suppliedCursor: options.cursor,
|
|
276
|
-
pageAdvanceable: false,
|
|
277
|
-
}),
|
|
278
|
-
itemsPerPage: items.length,
|
|
279
|
-
truncated: truncated || undefined,
|
|
280
|
-
},
|
|
281
|
-
hasMore: continuation.hasMore,
|
|
282
|
-
cursor: continuation.cursor,
|
|
283
|
-
fetchFailed: assessment.fetchFailed || pageFetchFailed || undefined,
|
|
284
|
-
};
|
|
469
|
+
],
|
|
470
|
+
true,
|
|
471
|
+
);
|
|
285
472
|
}
|
|
286
473
|
|
|
287
474
|
const cursor = options.cursor ?? pageToCursor(page);
|
|
@@ -302,6 +489,7 @@ export async function listIssuesPage(
|
|
|
302
489
|
cursor: cursor,
|
|
303
490
|
page: options.page,
|
|
304
491
|
pageSize: options.itemsPerPage,
|
|
492
|
+
sort: ordering.sort,
|
|
305
493
|
},
|
|
306
494
|
options.connectionId,
|
|
307
495
|
),
|
|
@@ -317,7 +505,7 @@ export async function listIssuesPage(
|
|
|
317
505
|
// Cursor-only repo-scoped hosts (e.g. GitHub) ignore a synthesized page-number cursor, so a page-only
|
|
318
506
|
// request is advanced through the provider's own continuations (see drainToRequestedPage).
|
|
319
507
|
if (
|
|
320
|
-
|
|
508
|
+
issuesPagingMode === PagingMode.Repos &&
|
|
321
509
|
options.page != null &&
|
|
322
510
|
options.page > 1 &&
|
|
323
511
|
options.cursor == null &&
|
|
@@ -342,6 +530,7 @@ export async function listIssuesPage(
|
|
|
342
530
|
includeAllAssignees: options.includeAllAssignees,
|
|
343
531
|
cursor: cursor,
|
|
344
532
|
pageSize: options.itemsPerPage,
|
|
533
|
+
sort: ordering.sort,
|
|
345
534
|
},
|
|
346
535
|
options.connectionId,
|
|
347
536
|
),
|
|
@@ -355,12 +544,15 @@ export async function listIssuesPage(
|
|
|
355
544
|
pageFetchFailed = drained.fetchFailed;
|
|
356
545
|
}
|
|
357
546
|
|
|
547
|
+
// Ordered once the drain (above) has assembled whichever pages this position spans.
|
|
548
|
+
items = ordering.order(items);
|
|
549
|
+
|
|
358
550
|
// Convert the SDK collection metadata into scope-aware warnings + failure/truncation flags, appending
|
|
359
551
|
// them to any captured thrown-error warning without discarding the partial result's items.
|
|
360
552
|
const assessment = mergeAssessmentInto(warnings, options.providerId, domain, options.connectionId, allMetadata);
|
|
361
553
|
// Never advertise `hasMore` without a continuation the caller can act on, and only synthesize a page
|
|
362
554
|
// number for a host that reads it as one (see `isPageNumberAdvanceable`).
|
|
363
|
-
const issuesPageAdvanceable = isPageNumberAdvanceable(
|
|
555
|
+
const issuesPageAdvanceable = isPageNumberAdvanceable(issuesPagingMode);
|
|
364
556
|
const continuation = resolveContinuation(paged, issuesPageAdvanceable ? paged.page.currentPage + 1 : undefined);
|
|
365
557
|
// A provider read that couldn't confirm completeness (e.g. Bitbucket's single-page repo issue read
|
|
366
558
|
// that dropped a repo) sets `paging.truncated`; surface it as a terminal `page.truncated` so a partial
|