@gitkraken/core-gitlens 0.5.107 → 0.5.109
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 +24 -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/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/plus/git-github/api/github.d.ts +33 -6
- package/dist/plus/git-github/api/github.d.ts.map +1 -1
- package/dist/plus/git-github/api/github.js +137 -15
- 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/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/index.d.ts +1 -1
- package/dist/plus/integrations/index.d.ts.map +1 -1
- package/dist/plus/integrations/index.js.map +1 -1
- package/dist/plus/integrations/integrationService.d.ts +15 -9
- package/dist/plus/integrations/integrationService.d.ts.map +1 -1
- package/dist/plus/integrations/integrationService.js.map +1 -1
- package/dist/plus/integrations/manager.d.ts +115 -26
- package/dist/plus/integrations/manager.d.ts.map +1 -1
- package/dist/plus/integrations/models/gitHostIntegration.d.ts +6 -31
- package/dist/plus/integrations/models/gitHostIntegration.d.ts.map +1 -1
- package/dist/plus/integrations/models/gitHostIntegration.js +11 -1
- package/dist/plus/integrations/models/gitHostIntegration.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 +1 -1
- package/dist/plus/integrations/providerFilters.d.ts.map +1 -1
- 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.map +1 -1
- package/dist/plus/integrations/providers/github.js +1 -0
- package/dist/plus/integrations/providers/github.js.map +1 -1
- package/dist/plus/integrations/providers/gitlab.d.ts.map +1 -1
- package/dist/plus/integrations/providers/gitlab.js +1 -0
- 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 +63 -7
- package/dist/plus/integrations/providers/models.d.ts.map +1 -1
- package/dist/plus/integrations/providers/models.js +55 -0
- 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/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 +8 -1
- package/dist/plus/integrations/reads/counts.d.ts.map +1 -1
- package/dist/plus/integrations/reads/counts.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 +17 -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/sweeps.d.ts.map +1 -1
- package/dist/plus/integrations/reads/sweeps.js +120 -56
- package/dist/plus/integrations/reads/sweeps.js.map +1 -1
- package/dist/plus/integrations/reads/warnings.d.ts +34 -2
- package/dist/plus/integrations/reads/warnings.d.ts.map +1 -1
- package/dist/plus/integrations/reads/warnings.js +47 -2
- package/dist/plus/integrations/reads/warnings.js.map +1 -1
- package/dist/plus/integrations/results.d.ts +10 -0
- package/dist/plus/integrations/results.d.ts.map +1 -1
- package/dist/plus/integrations/results.js.map +1 -1
- package/package.json +2 -2
- package/src/git/models/issue.ts +73 -0
- package/src/git/utils/issue.utils.ts +91 -1
- package/src/plus/git-github/api/github.ts +180 -20
- package/src/plus/git-github/api/issueSearchQuery.ts +67 -7
- package/src/plus/integrations/collectionMetadata.ts +24 -0
- package/src/plus/integrations/index.ts +1 -0
- package/src/plus/integrations/integrationService.ts +15 -10
- package/src/plus/integrations/manager.ts +116 -31
- package/src/plus/integrations/models/gitHostIntegration.ts +18 -31
- package/src/plus/integrations/models/issueReads.ts +65 -1
- package/src/plus/integrations/models/issuesIntegration.ts +7 -6
- package/src/plus/integrations/providerFilters.ts +2 -0
- package/src/plus/integrations/providers/azureDevOps.ts +1 -0
- package/src/plus/integrations/providers/github.ts +1 -0
- package/src/plus/integrations/providers/gitlab.ts +1 -0
- 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 +122 -5
- package/src/plus/integrations/providers/providersApi.ts +51 -13
- 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 +8 -1
- package/src/plus/integrations/reads/filters.ts +70 -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/sweeps.ts +150 -76
- package/src/plus/integrations/reads/warnings.ts +72 -1
- package/src/plus/integrations/results.ts +10 -0
|
@@ -12,14 +12,30 @@ import { toTokenWithInfo } from '../authentication/models.js';
|
|
|
12
12
|
import { toCollectionScopeFailure } from '../collectionMetadata.js';
|
|
13
13
|
import { IssuesCloudHostIntegrationId } from '../constants.js';
|
|
14
14
|
import { IntegrationReadUnavailableError } from '../errors.js';
|
|
15
|
+
import type { AccountWideIssuesResult, IssuesForProjectOptions, SearchMyIssuesOptions } from '../models/issueReads.js';
|
|
15
16
|
import { IssuesIntegration } from '../models/issuesIntegration.js';
|
|
16
|
-
import type {
|
|
17
|
+
import type { ProviderApiCollectionResult, ProviderIssue } from './models.js';
|
|
17
18
|
import { fromProviderIssue, providersMetadata, toIssueShape } from './models.js';
|
|
18
19
|
import { mergeCollectionMetadata } from './utils/providerPaging.js';
|
|
19
20
|
|
|
20
21
|
const metadata = providersMetadata[IssuesCloudHostIntegrationId.Linear];
|
|
21
22
|
const authProvider = Object.freeze({ id: metadata.id, scopes: metadata.scopes });
|
|
22
23
|
const maxPagesPerRequest = 10;
|
|
24
|
+
/**
|
|
25
|
+
* The account-wide drain's own backstop, separate from {@link maxPagesPerRequest}.
|
|
26
|
+
*
|
|
27
|
+
* Raised to 50 (5,000 issues at the SDK's 100-per-page) because 10 no longer means what it used to. That read
|
|
28
|
+
* once fanned out over four overlapping relationship queries and merged them, so most of what a page cost was
|
|
29
|
+
* rows already returned — a low budget capped the waste. It is now a single server-ordered query over an `or`
|
|
30
|
+
* filter, so every page is 100 issues the caller has not seen, and the same budget just truncates real results
|
|
31
|
+
* at 1,000.
|
|
32
|
+
*
|
|
33
|
+
* Reaching it is reported rather than silent: {@link LinearIntegration.searchProviderMyIssuesWithTruncation}
|
|
34
|
+
* returns `truncated`, so the facade can say the read was incomplete instead of publishing a capped list as a
|
|
35
|
+
* whole account. The bound stays — a runaway cursor must not spend requests forever — but it is now set where a
|
|
36
|
+
* real Linear account is unlikely to reach it rather than where the duplication used to hurt.
|
|
37
|
+
*/
|
|
38
|
+
const maxAccountWidePagesPerRequest = 50;
|
|
23
39
|
const linearImplicitTeamsPageSize = 50;
|
|
24
40
|
|
|
25
41
|
export interface LinearTeamDescriptor extends IssueResourceDescriptor {
|
|
@@ -194,7 +210,7 @@ export class LinearIntegration extends IssuesIntegration<IssuesCloudHostIntegrat
|
|
|
194
210
|
protected override async getProviderIssuesForProject(
|
|
195
211
|
session: ProviderAuthenticationSession,
|
|
196
212
|
project: ResourceDescriptor,
|
|
197
|
-
options?:
|
|
213
|
+
options?: IssuesForProjectOptions,
|
|
198
214
|
): Promise<IssueShape[] | undefined> {
|
|
199
215
|
return (await this.getProviderIssuesForProjectWithTruncation(session, project, options))?.values;
|
|
200
216
|
}
|
|
@@ -202,7 +218,7 @@ export class LinearIntegration extends IssuesIntegration<IssuesCloudHostIntegrat
|
|
|
202
218
|
protected override async getProviderIssuesForProjectWithTruncation(
|
|
203
219
|
session: ProviderAuthenticationSession,
|
|
204
220
|
project: ResourceDescriptor,
|
|
205
|
-
options?:
|
|
221
|
+
options?: IssuesForProjectOptions,
|
|
206
222
|
): Promise<{ values: IssueShape[]; truncated: boolean; metadata?: CollectionMetadata } | undefined> {
|
|
207
223
|
if (!isIssueResourceDescriptor(project)) return undefined;
|
|
208
224
|
|
|
@@ -222,7 +238,7 @@ export class LinearIntegration extends IssuesIntegration<IssuesCloudHostIntegrat
|
|
|
222
238
|
result = await api.getLinearIssues(
|
|
223
239
|
toTokenWithInfo(this.id, session),
|
|
224
240
|
{ teams: [project.id] },
|
|
225
|
-
{ cursor: cursor },
|
|
241
|
+
{ cursor: cursor, sort: options?.sort },
|
|
226
242
|
);
|
|
227
243
|
} catch (ex) {
|
|
228
244
|
// A page failure after the first page leaves the already-drained prefix intact; record the
|
|
@@ -305,14 +321,45 @@ export class LinearIntegration extends IssuesIntegration<IssuesCloudHostIntegrat
|
|
|
305
321
|
resources?: ResourceDescriptor[],
|
|
306
322
|
cancellation?: AbortSignal,
|
|
307
323
|
): Promise<IssueShape[] | undefined> {
|
|
324
|
+
return (await this.searchProviderMyIssuesWithTruncation(session, resources, cancellation))?.values;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
/**
|
|
328
|
+
* Account-wide "my issues" for Linear, drained to exhaustion and reporting whether it got there.
|
|
329
|
+
*
|
|
330
|
+
* The SDK read is one server-ordered query over an `or` filter across the four relationships (assigned,
|
|
331
|
+
* created, named in an issue's content, named in a comment), so each page is 100 issues the caller has not
|
|
332
|
+
* seen and the pages are ordered as one sequence. Draining it is therefore just following the cursor.
|
|
333
|
+
*
|
|
334
|
+
* Overridden rather than left to the base default because that default hardcodes `truncated: false`, and a
|
|
335
|
+
* bounded drain that stops early is exactly the case a caller must be able to see. Three things end the
|
|
336
|
+
* drain, and only one of them is completion:
|
|
337
|
+
*
|
|
338
|
+
* - the provider says there is no next page — complete;
|
|
339
|
+
* - the backstop is reached — `truncated`, because issues beyond it exist and were not read;
|
|
340
|
+
* - the provider claims a next page but hands back a cursor that does not advance — also `truncated`, since
|
|
341
|
+
* the read cannot continue and the remainder is unreachable rather than absent.
|
|
342
|
+
*
|
|
343
|
+
* Cancellation is deliberately NOT truncation: the caller asked for the read to stop, so the partial list is
|
|
344
|
+
* what it asked for, and flagging it would surface an incompleteness warning for a user action.
|
|
345
|
+
*/
|
|
346
|
+
protected override async searchProviderMyIssuesWithTruncation(
|
|
347
|
+
session: ProviderAuthenticationSession,
|
|
348
|
+
resources?: ResourceDescriptor[],
|
|
349
|
+
cancellation?: AbortSignal,
|
|
350
|
+
options?: SearchMyIssuesOptions,
|
|
351
|
+
): Promise<AccountWideIssuesResult | undefined> {
|
|
308
352
|
if (resources != null) {
|
|
309
353
|
return undefined;
|
|
310
354
|
}
|
|
311
355
|
|
|
312
356
|
const api = await this.getProvidersApi();
|
|
313
357
|
let cursor = undefined;
|
|
314
|
-
|
|
358
|
+
// Starts false so an immediate cancellation, which leaves the loop before the first response, reads as
|
|
359
|
+
// "no more pages known" rather than as an unfinished drain.
|
|
360
|
+
let hasMore = false;
|
|
315
361
|
let requestCount = 0;
|
|
362
|
+
let truncated = false;
|
|
316
363
|
const issues = [];
|
|
317
364
|
try {
|
|
318
365
|
do {
|
|
@@ -322,25 +369,47 @@ export class LinearIntegration extends IssuesIntegration<IssuesCloudHostIntegrat
|
|
|
322
369
|
|
|
323
370
|
const result = await api.getIssuesForCurrentUser(toTokenWithInfo(this.id, session), {
|
|
324
371
|
cursor: cursor,
|
|
372
|
+
sort: options?.sort,
|
|
325
373
|
});
|
|
326
374
|
requestCount += 1;
|
|
327
375
|
hasMore = result.paging?.more ?? false;
|
|
328
|
-
|
|
376
|
+
const nextCursor = result.paging?.cursor;
|
|
377
|
+
|
|
378
|
+
// Keep this page before deciding whether to continue: the request is already paid for, so
|
|
379
|
+
// dropping its rows would lose real results to save nothing.
|
|
329
380
|
const formattedIssues = result.values
|
|
330
381
|
.map(issue => toIssueShape(issue, this))
|
|
331
382
|
.filter((result): result is IssueShape => result != null);
|
|
332
383
|
if (formattedIssues.length > 0) {
|
|
333
384
|
issues.push(...formattedIssues);
|
|
334
385
|
}
|
|
335
|
-
|
|
386
|
+
|
|
387
|
+
// The provider claims more but hands back no advancing cursor: continuing would re-read the page
|
|
388
|
+
// just returned. `ProvidersApi.getPagedResult` already normalizes this into `more: false`, so
|
|
389
|
+
// this is a backstop for if that ever stops holding rather than a case seen today -- but the rest
|
|
390
|
+
// of the account is unreachable either way, which is what `truncated` says.
|
|
391
|
+
if (hasMore && (nextCursor == null || nextCursor === cursor)) {
|
|
392
|
+
truncated = true;
|
|
393
|
+
break;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
cursor = nextCursor;
|
|
397
|
+
} while (requestCount < maxAccountWidePagesPerRequest && hasMore);
|
|
398
|
+
|
|
399
|
+
// Ran out of budget with pages still to come.
|
|
400
|
+
if (hasMore && requestCount >= maxAccountWidePagesPerRequest) {
|
|
401
|
+
truncated = true;
|
|
402
|
+
}
|
|
336
403
|
} catch (ex) {
|
|
337
404
|
if (issues.length === 0) {
|
|
338
405
|
throw ex;
|
|
339
406
|
}
|
|
340
407
|
|
|
408
|
+
// Kept what was already fetched, so the list is real but short of the account.
|
|
409
|
+
truncated = true;
|
|
341
410
|
Logger.error(ex, 'searchProviderMyIssues');
|
|
342
411
|
}
|
|
343
|
-
return issues;
|
|
412
|
+
return { values: issues, truncated: truncated };
|
|
344
413
|
}
|
|
345
414
|
protected override async getProviderLinkedIssueOrPullRequest(
|
|
346
415
|
session: ProviderAuthenticationSession,
|
|
@@ -75,7 +75,7 @@ import {
|
|
|
75
75
|
IssuesCloudHostIntegrationId,
|
|
76
76
|
} from '../constants.js';
|
|
77
77
|
import type { Integration, IntegrationType } from '../models/integration.js';
|
|
78
|
-
import type { IssueSearchCapabilities, PullRequestSearchCapabilities } from '../providerFilters.js';
|
|
78
|
+
import type { IssueSearchCapabilities, IssueSorting, PullRequestSearchCapabilities } from '../providerFilters.js';
|
|
79
79
|
import { IssueFilter, PullRequestFilter } from '../providerFilters.js';
|
|
80
80
|
|
|
81
81
|
export { IssueFilter, PullRequestFilter } from '../providerFilters.js';
|
|
@@ -83,12 +83,25 @@ export type {
|
|
|
83
83
|
IssueSearchCapabilities,
|
|
84
84
|
IssueSearchCriteria,
|
|
85
85
|
IssueSearchRelationship,
|
|
86
|
+
IssueSortField,
|
|
87
|
+
IssueSorting,
|
|
86
88
|
PullRequestSearchCapabilities,
|
|
87
89
|
PullRequestSearchCriteria,
|
|
88
90
|
} from '../providerFilters.js';
|
|
89
91
|
import type { ProviderRepositoryShape } from '../results.js';
|
|
90
92
|
|
|
91
93
|
export type { ProviderOrganization, ProviderRepositoryShape } from '../results.js';
|
|
94
|
+
import {
|
|
95
|
+
azureAccountWideIssueSorts,
|
|
96
|
+
azureIssueSorts,
|
|
97
|
+
githubAccountWideIssueSorts,
|
|
98
|
+
githubIssueSorts,
|
|
99
|
+
gitlabAccountWideIssueSorts,
|
|
100
|
+
gitlabIssueSorts,
|
|
101
|
+
jiraIssueSorts,
|
|
102
|
+
linearIssueSorts,
|
|
103
|
+
trelloIssueSorts,
|
|
104
|
+
} from './issueSorts.js';
|
|
92
105
|
import { getEntityIdentifierInput } from './utils.js';
|
|
93
106
|
|
|
94
107
|
type GitBuildStatusState = GitBuildStatusStateType;
|
|
@@ -156,6 +169,28 @@ type EnrichableItem = any;
|
|
|
156
169
|
export type ProviderAccount = Account;
|
|
157
170
|
export type ProviderReposInput = (string | number)[] | GetRepoInput[];
|
|
158
171
|
export type ProviderRepoInput = GetRepoInput;
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Which half of {@link ProviderReposInput} a caller supplied: repository IDS rather than descriptors.
|
|
175
|
+
*
|
|
176
|
+
* The union has no discriminant, so the answer is the element shape — and several layers need it for different
|
|
177
|
+
* reasons (`ProvidersApi` to pick `repoIds` vs `repos` in the SDK input, `GitHostIntegration` to refuse an
|
|
178
|
+
* id-based read a provider can't serve, `listIssuesPage` to know GitLab's id form takes the SDK's own merging
|
|
179
|
+
* aggregate). One predicate rather than one per layer, because each copy is free to disagree about the shape,
|
|
180
|
+
* and the symptom of disagreement is a read routed to the wrong provider query.
|
|
181
|
+
*
|
|
182
|
+
* Takes `unknown` rather than {@link ProviderReposInput} because `ProvidersApi.isRepoIdsInput` forwards here and
|
|
183
|
+
* is public with an unvalidated parameter, so the array check has to stay part of the rule rather than be assumed
|
|
184
|
+
* from the parameter type.
|
|
185
|
+
*/
|
|
186
|
+
export function isRepoIdsInput(input: unknown): input is (string | number)[] {
|
|
187
|
+
return (
|
|
188
|
+
input != null &&
|
|
189
|
+
Array.isArray(input) &&
|
|
190
|
+
input.every(repo => typeof repo === 'string' || typeof repo === 'number')
|
|
191
|
+
);
|
|
192
|
+
}
|
|
193
|
+
|
|
159
194
|
export type ProviderPullRequest = GitPullRequest;
|
|
160
195
|
export type ProviderRepository = GitRepository;
|
|
161
196
|
export type ProviderIssue = ProviderApiIssue;
|
|
@@ -331,6 +366,20 @@ export interface GetIssuesOptions {
|
|
|
331
366
|
page?: number;
|
|
332
367
|
// Items to request per page (numbered-page providers, plus GitHub's maxPageSize).
|
|
333
368
|
pageSize?: number;
|
|
369
|
+
/**
|
|
370
|
+
* How the provider should order the result, as `field:direction`.
|
|
371
|
+
*
|
|
372
|
+
* Passed through to the SDK verbatim — the translation to each provider's own vocabulary lives THERE, next to
|
|
373
|
+
* the capability map it validates against, so that the table this facade publishes and the table that does the
|
|
374
|
+
* translating can't drift apart across two repositories. A key the provider can't express raises the SDK's
|
|
375
|
+
* `UnsupportedSortError`, which is why the facade validates against `supportedIssueSorts` first: reaching that
|
|
376
|
+
* error means this table has outrun the SDK's.
|
|
377
|
+
*
|
|
378
|
+
* Always supplied by the facade's reads, never omitted. Omitting it would delegate the order to each provider's
|
|
379
|
+
* own default, which is the incoherence across providers the SDK deliberately preserves and this layer
|
|
380
|
+
* deliberately does not.
|
|
381
|
+
*/
|
|
382
|
+
sort?: IssueSorting;
|
|
334
383
|
}
|
|
335
384
|
|
|
336
385
|
export interface GetIssuesForRepoInput extends GetIssuesOptions {
|
|
@@ -492,6 +541,8 @@ export type GetIssuesForCurrentUserInput = PagingInput & {
|
|
|
492
541
|
assigneeUsername?: string;
|
|
493
542
|
authorUsername?: string;
|
|
494
543
|
pageSize?: number;
|
|
544
|
+
/** See {@link GetIssuesOptions.sort}. */
|
|
545
|
+
sort?: IssueSorting;
|
|
495
546
|
};
|
|
496
547
|
|
|
497
548
|
export type GetIssuesForCurrentUserFn = (
|
|
@@ -624,10 +675,10 @@ export type GetBitbucketServerPullRequestsForCurrentUserFn = (
|
|
|
624
675
|
data: GitPullRequest[];
|
|
625
676
|
}>;
|
|
626
677
|
export type GetIssuesForProjectFn = Jira['getIssuesForProject'];
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
678
|
+
// Derived from the client method rather than hand-declared, as its project-scoped sibling above already is: the
|
|
679
|
+
// hand-written shape named only `resourceId`, so every other field the SDK accepts (the cursor, the sort, the
|
|
680
|
+
// transitions switch) was invisible to the type system and had to be smuggled through `getPagedResult`'s `any`.
|
|
681
|
+
export type GetIssuesForResourceForCurrentUserFn = Jira['getIssuesForResourceForCurrentUser'];
|
|
631
682
|
|
|
632
683
|
// Trello reads (issues-capable provider). The Trello client is keyed by an `appKey` (the Trello app key from
|
|
633
684
|
// the cloud token exchange) alongside the OAuth token, so these mirror the client method shapes directly.
|
|
@@ -729,6 +780,36 @@ export interface ProviderMetadata {
|
|
|
729
780
|
* axes; it omits `Mention` because that REST read has no first-class mention filter.
|
|
730
781
|
*/
|
|
731
782
|
supportedAccountWideIssueFilters?: IssueFilter[];
|
|
783
|
+
/**
|
|
784
|
+
* Sort keys the REPO-scoped issue read can express server-side — and, for an issue tracker (Jira/Linear/Trello),
|
|
785
|
+
* its project-scoped read, which is the only issue surface a tracker has.
|
|
786
|
+
*
|
|
787
|
+
* Absent/empty means the read can't be ordered at all, so a request carrying `sort` is refused rather than
|
|
788
|
+
* served in whatever order the provider felt like. Present, it is a promise: every key here reaches the
|
|
789
|
+
* provider query.
|
|
790
|
+
*
|
|
791
|
+
* A key listed here is expressible on ONE provider query, which is what this read is when it is given ONE
|
|
792
|
+
* scope. Given several (GitLab across repositories, Azure across projects, a tracker across projects) it merges
|
|
793
|
+
* their results here and can only honor a key a normalized issue carries, so it additionally refuses
|
|
794
|
+
* `priority`/`dueDate`/`resolved` — see `getIssueComparator`. That depends on the caller's scope count rather
|
|
795
|
+
* than on the provider, which is why it is a runtime refusal and not a fourth table: encoding it here would
|
|
796
|
+
* under-report the single-scope read, which really does support those keys.
|
|
797
|
+
*/
|
|
798
|
+
supportedIssueSorts?: IssueSorting[];
|
|
799
|
+
/**
|
|
800
|
+
* Sort keys the ACCOUNT-WIDE issue read can express server-side, which is NOT the same set as
|
|
801
|
+
* {@link ProviderMetadata.supportedIssueSorts} — for GitLab the two reads are different APIs (GraphQL vs REST)
|
|
802
|
+
* with genuinely different vocabularies, not one narrowed twice.
|
|
803
|
+
*
|
|
804
|
+
* Absent means the account-wide read can't be ordered (or doesn't exist). An issue tracker leaves this empty
|
|
805
|
+
* and reports under `supportedIssueSorts`, so reading a tracker's capability from here under-reports it.
|
|
806
|
+
*
|
|
807
|
+
* Unlike {@link ProviderMetadata.supportedIssueSorts} this needs no companion runtime refusal, because every
|
|
808
|
+
* provider's account-wide read is a union of several queries with no scope count for a caller to reduce: it
|
|
809
|
+
* ALWAYS merges. So the keys a merge can't order by are simply absent here — the table states what the read can
|
|
810
|
+
* honor, which is what makes intersecting against it sufficient.
|
|
811
|
+
*/
|
|
812
|
+
supportedAccountWideIssueSorts?: IssueSorting[];
|
|
732
813
|
/**
|
|
733
814
|
* What the provider's FILTERED issue search (`searchIssuesPage`, and the `countIssues` probe over the same
|
|
734
815
|
* criteria) can express server-side. A third, wider surface than either filter set above: it is not bound to
|
|
@@ -774,6 +855,8 @@ const githubIssueSearchCapabilities: IssueSearchCapabilities = {
|
|
|
774
855
|
withoutLinkedPullRequest: true,
|
|
775
856
|
// `is:open` / `is:closed`, or neither for all states.
|
|
776
857
|
states: true,
|
|
858
|
+
// `sort:created-desc`, `sort:updated`, `sort:comments-asc`, … — see `githubIssueSorts`.
|
|
859
|
+
sorts: githubIssueSorts,
|
|
777
860
|
};
|
|
778
861
|
|
|
779
862
|
/** GitHub and GHE use the same filtered pull-request search syntax and result ceiling. */
|
|
@@ -820,6 +903,10 @@ export const providersMetadata: ProvidersMetadata = {
|
|
|
820
903
|
// The account-wide read is three independent searches (`author:@me`, `assignee:@me`, `mentions:@me`) behind
|
|
821
904
|
// one composite cursor, so any subset of them is expressible.
|
|
822
905
|
supportedAccountWideIssueFilters: [IssueFilter.Author, IssueFilter.Assignee, IssueFilter.Mention],
|
|
906
|
+
// One `search` field serves all three issue reads (repo-scoped, account-wide, filtered search), so the same
|
|
907
|
+
// qualifiers are expressible on each — the account-wide one narrowed to what its three-alias merge can order.
|
|
908
|
+
supportedIssueSorts: githubIssueSorts,
|
|
909
|
+
supportedAccountWideIssueSorts: githubAccountWideIssueSorts,
|
|
823
910
|
supportedIssueSearch: githubIssueSearchCapabilities,
|
|
824
911
|
issueSearchResultLimit: githubSearchResultLimit,
|
|
825
912
|
scopes: ['repo', 'read:user', 'user:email'],
|
|
@@ -852,6 +939,10 @@ export const providersMetadata: ProvidersMetadata = {
|
|
|
852
939
|
// The account-wide read is three independent searches (`author:@me`, `assignee:@me`, `mentions:@me`) behind
|
|
853
940
|
// one composite cursor, so any subset of them is expressible.
|
|
854
941
|
supportedAccountWideIssueFilters: [IssueFilter.Author, IssueFilter.Assignee, IssueFilter.Mention],
|
|
942
|
+
// One `search` field serves all three issue reads (repo-scoped, account-wide, filtered search), so the same
|
|
943
|
+
// qualifiers are expressible on each — the account-wide one narrowed to what its three-alias merge can order.
|
|
944
|
+
supportedIssueSorts: githubIssueSorts,
|
|
945
|
+
supportedAccountWideIssueSorts: githubAccountWideIssueSorts,
|
|
855
946
|
supportedIssueSearch: githubIssueSearchCapabilities,
|
|
856
947
|
issueSearchResultLimit: githubSearchResultLimit,
|
|
857
948
|
scopes: ['repo', 'read:user', 'user:email'],
|
|
@@ -881,6 +972,10 @@ export const providersMetadata: ProvidersMetadata = {
|
|
|
881
972
|
// unioned, so either axis is expressible on its own. Mention is absent because GitLab's REST issue read has
|
|
882
973
|
// no first-class mention filter to narrow with.
|
|
883
974
|
supportedAccountWideIssueFilters: [IssueFilter.Assignee, IssueFilter.Author],
|
|
975
|
+
// Two different surfaces, not one narrowed twice: the repository-scoped read is GraphQL and the
|
|
976
|
+
// account-wide one is REST, and REST has neither `title` nor `closed_at`.
|
|
977
|
+
supportedIssueSorts: gitlabIssueSorts,
|
|
978
|
+
supportedAccountWideIssueSorts: gitlabAccountWideIssueSorts,
|
|
884
979
|
scopes: ['api', 'read_user', 'read_repository'],
|
|
885
980
|
},
|
|
886
981
|
[GitSelfManagedHostIntegrationId.CloudGitLabSelfHosted]: {
|
|
@@ -908,6 +1003,10 @@ export const providersMetadata: ProvidersMetadata = {
|
|
|
908
1003
|
// unioned, so either axis is expressible on its own. Mention is absent because GitLab's REST issue read has
|
|
909
1004
|
// no first-class mention filter to narrow with.
|
|
910
1005
|
supportedAccountWideIssueFilters: [IssueFilter.Assignee, IssueFilter.Author],
|
|
1006
|
+
// Two different surfaces, not one narrowed twice: the repository-scoped read is GraphQL and the
|
|
1007
|
+
// account-wide one is REST, and REST has neither `title` nor `closed_at`.
|
|
1008
|
+
supportedIssueSorts: gitlabIssueSorts,
|
|
1009
|
+
supportedAccountWideIssueSorts: gitlabAccountWideIssueSorts,
|
|
911
1010
|
scopes: ['api', 'read_user', 'read_repository'],
|
|
912
1011
|
},
|
|
913
1012
|
[GitCloudHostIntegrationId.Bitbucket]: {
|
|
@@ -956,6 +1055,11 @@ export const providersMetadata: ProvidersMetadata = {
|
|
|
956
1055
|
// The account-wide read drains one (project × assignee) and one (project × author) query per project, so
|
|
957
1056
|
// either axis is expressible on its own. There is no mention query to narrow to.
|
|
958
1057
|
supportedAccountWideIssueFilters: [IssueFilter.Author, IssueFilter.Assignee],
|
|
1058
|
+
// Both reads emit the same WIQL, but the account-wide one drains every (project x relationship) query and
|
|
1059
|
+
// merges the results, so it can only honor keys a normalized issue carries — `resolved` and `priority` are
|
|
1060
|
+
// dropped from that surface for the same reason they are dropped from GitLab's.
|
|
1061
|
+
supportedIssueSorts: azureIssueSorts,
|
|
1062
|
+
supportedAccountWideIssueSorts: azureAccountWideIssueSorts,
|
|
959
1063
|
scopes: ['vso.code', 'vso.identity', 'vso.project', 'vso.profile', 'vso.work'],
|
|
960
1064
|
},
|
|
961
1065
|
[GitSelfManagedHostIntegrationId.AzureDevOpsServer]: {
|
|
@@ -982,6 +1086,11 @@ export const providersMetadata: ProvidersMetadata = {
|
|
|
982
1086
|
// The account-wide read drains one (project × assignee) and one (project × author) query per project, so
|
|
983
1087
|
// either axis is expressible on its own. There is no mention query to narrow to.
|
|
984
1088
|
supportedAccountWideIssueFilters: [IssueFilter.Author, IssueFilter.Assignee],
|
|
1089
|
+
// Both reads emit the same WIQL, but the account-wide one drains every (project x relationship) query and
|
|
1090
|
+
// merges the results, so it can only honor keys a normalized issue carries — `resolved` and `priority` are
|
|
1091
|
+
// dropped from that surface for the same reason they are dropped from GitLab's.
|
|
1092
|
+
supportedIssueSorts: azureIssueSorts,
|
|
1093
|
+
supportedAccountWideIssueSorts: azureAccountWideIssueSorts,
|
|
985
1094
|
scopes: ['vso.code', 'vso.identity', 'vso.project', 'vso.profile', 'vso.work'],
|
|
986
1095
|
},
|
|
987
1096
|
[IssuesCloudHostIntegrationId.Jira]: {
|
|
@@ -1027,6 +1136,9 @@ export const providersMetadata: ProvidersMetadata = {
|
|
|
1027
1136
|
'read:project-version:jira',
|
|
1028
1137
|
],
|
|
1029
1138
|
supportedIssueFilters: [IssueFilter.Author, IssueFilter.Assignee, IssueFilter.Mention],
|
|
1139
|
+
// A tracker's issues live under resource -> project, so it has no account-wide surface to declare: its
|
|
1140
|
+
// capability is reported under `issues`, which is what `listIssueTrackerIssuesPage` validates against.
|
|
1141
|
+
supportedIssueSorts: jiraIssueSorts,
|
|
1030
1142
|
},
|
|
1031
1143
|
[IssuesCloudHostIntegrationId.Linear]: {
|
|
1032
1144
|
domain: 'linear.app',
|
|
@@ -1037,6 +1149,9 @@ export const providersMetadata: ProvidersMetadata = {
|
|
|
1037
1149
|
scopes: [],
|
|
1038
1150
|
// Linear scopes "my issues" client-side by the viewer's assignee id; author/mention aren't supported.
|
|
1039
1151
|
supportedIssueFilters: [IssueFilter.Assignee],
|
|
1152
|
+
// Both directions, and one table for both reads: they are the same root `issues` query taking the same
|
|
1153
|
+
// `sort` argument, so there is no account-wide narrowing to declare.
|
|
1154
|
+
supportedIssueSorts: linearIssueSorts,
|
|
1040
1155
|
},
|
|
1041
1156
|
[IssuesCloudHostIntegrationId.Trello]: {
|
|
1042
1157
|
domain: 'trello.com',
|
|
@@ -1047,6 +1162,8 @@ export const providersMetadata: ProvidersMetadata = {
|
|
|
1047
1162
|
scopes: [],
|
|
1048
1163
|
// Trello cards are filtered by the assignee (member) only; author/mention have no Trello equivalent.
|
|
1049
1164
|
supportedIssueFilters: [IssueFilter.Assignee],
|
|
1165
|
+
// `sort:edited` / `sort:-edited`. Trello's other search sorts (`created`, `due`) have no card-order effect.
|
|
1166
|
+
supportedIssueSorts: trelloIssueSorts,
|
|
1050
1167
|
},
|
|
1051
1168
|
};
|
|
1052
1169
|
|
|
@@ -39,6 +39,7 @@ import type {
|
|
|
39
39
|
GetPullRequestsOptions,
|
|
40
40
|
GetReposOptions,
|
|
41
41
|
IssueFilter,
|
|
42
|
+
IssueSorting,
|
|
42
43
|
PageInfo,
|
|
43
44
|
PagingInput,
|
|
44
45
|
PagingMode,
|
|
@@ -67,7 +68,7 @@ import type {
|
|
|
67
68
|
Providers,
|
|
68
69
|
PullRequestFilter,
|
|
69
70
|
} from './models.js';
|
|
70
|
-
import { providersMetadata } from './models.js';
|
|
71
|
+
import { isRepoIdsInput, providersMetadata } from './models.js';
|
|
71
72
|
import {
|
|
72
73
|
collectProviderPagedResult,
|
|
73
74
|
mergeCollectionMetadata,
|
|
@@ -132,6 +133,14 @@ function isGraphQLRepoNotFoundError(ex: unknown): boolean {
|
|
|
132
133
|
|
|
133
134
|
const trelloBaseUrl = 'https://api.trello.com';
|
|
134
135
|
|
|
136
|
+
/**
|
|
137
|
+
* Jira computes an issue's available transitions per issue, from the workflow and the reader's permissions, so a
|
|
138
|
+
* list read at a page size of 100 pays for 100 of those in both server work and response payload. Nothing in this
|
|
139
|
+
* package reads `statusTransitions` off a list — only the singular `getIssue` path exposes them, and it always
|
|
140
|
+
* expands them — so every list read opts out. Revisit the day a list row offers a status change straight off it.
|
|
141
|
+
*/
|
|
142
|
+
const jiraListIncludeTransitions = false;
|
|
143
|
+
|
|
135
144
|
type TrelloMemberResponse = {
|
|
136
145
|
id: string;
|
|
137
146
|
username?: string | null;
|
|
@@ -479,12 +488,9 @@ export class ProvidersApi {
|
|
|
479
488
|
);
|
|
480
489
|
}
|
|
481
490
|
|
|
482
|
-
isRepoIdsInput
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
Array.isArray(input) &&
|
|
486
|
-
input.every((id: any) => typeof id === 'string' || typeof id === 'number')
|
|
487
|
-
);
|
|
491
|
+
/** See {@link isRepoIdsInput}, which owns the rule; kept as a method because callers reach it through the api. */
|
|
492
|
+
isRepoIdsInput(input: unknown): input is (string | number)[] {
|
|
493
|
+
return isRepoIdsInput(input);
|
|
488
494
|
}
|
|
489
495
|
|
|
490
496
|
private async getProviderToken<T extends IntegrationIds>(
|
|
@@ -868,7 +874,10 @@ export class ProvidersApi {
|
|
|
868
874
|
async getLinearIssues(
|
|
869
875
|
tokenOptInfo: TokenWithInfo<IssuesCloudHostIntegrationId.Linear>,
|
|
870
876
|
input: { teams?: string[]; projects?: string[]; labels?: string[] },
|
|
871
|
-
options?: PagingInput
|
|
877
|
+
options?: PagingInput & {
|
|
878
|
+
/** See {@link GetIssuesOptions.sort}. Linear expresses `created`/`updated`, descending only. */
|
|
879
|
+
sort?: IssueSorting;
|
|
880
|
+
},
|
|
872
881
|
): Promise<PagedResult<ProviderIssue>> {
|
|
873
882
|
const { provider, tokenWithInfo } = await this.ensureProviderTokenAndFunction(
|
|
874
883
|
tokenOptInfo,
|
|
@@ -1565,6 +1574,8 @@ export class ProvidersApi {
|
|
|
1565
1574
|
*/
|
|
1566
1575
|
authorUsername?: string;
|
|
1567
1576
|
pageSize?: number;
|
|
1577
|
+
/** See {@link GetIssuesOptions.sort}. Forwarded to the provider fn; ordering is translated in the SDK. */
|
|
1578
|
+
sort?: IssueSorting;
|
|
1568
1579
|
isPAT?: boolean;
|
|
1569
1580
|
baseUrl?: string;
|
|
1570
1581
|
},
|
|
@@ -1580,6 +1591,7 @@ export class ProvidersApi {
|
|
|
1580
1591
|
authorUsername: options?.authorUsername,
|
|
1581
1592
|
page: options?.page,
|
|
1582
1593
|
pageSize: options?.pageSize,
|
|
1594
|
+
sort: options?.sort,
|
|
1583
1595
|
},
|
|
1584
1596
|
provider.getIssuesForCurrentUserFn,
|
|
1585
1597
|
tokenWithInfo,
|
|
@@ -1626,7 +1638,12 @@ export class ProvidersApi {
|
|
|
1626
1638
|
|
|
1627
1639
|
try {
|
|
1628
1640
|
const result = await provider.getIssuesForProjectFn?.(
|
|
1629
|
-
{
|
|
1641
|
+
{
|
|
1642
|
+
projectKey: project,
|
|
1643
|
+
resourceId: resourceId,
|
|
1644
|
+
...options,
|
|
1645
|
+
includeTransitions: jiraListIncludeTransitions,
|
|
1646
|
+
},
|
|
1630
1647
|
{ token: token },
|
|
1631
1648
|
);
|
|
1632
1649
|
|
|
@@ -1655,7 +1672,12 @@ export class ProvidersApi {
|
|
|
1655
1672
|
|
|
1656
1673
|
try {
|
|
1657
1674
|
const result = await provider.getIssuesForProjectFn?.(
|
|
1658
|
-
{
|
|
1675
|
+
{
|
|
1676
|
+
projectKey: project,
|
|
1677
|
+
resourceId: resourceId,
|
|
1678
|
+
...options,
|
|
1679
|
+
includeTransitions: jiraListIncludeTransitions,
|
|
1680
|
+
},
|
|
1659
1681
|
{ token: token },
|
|
1660
1682
|
);
|
|
1661
1683
|
if (result == null) return undefined;
|
|
@@ -1760,7 +1782,12 @@ export class ProvidersApi {
|
|
|
1760
1782
|
tokenOptInfo: TokenWithInfo,
|
|
1761
1783
|
appKey: string,
|
|
1762
1784
|
boardId: string,
|
|
1763
|
-
options?: {
|
|
1785
|
+
options?: {
|
|
1786
|
+
assigneeLogins?: string[];
|
|
1787
|
+
trelloBoardListsById?: Record<string, { name: string }>;
|
|
1788
|
+
/** See {@link GetIssuesOptions.sort}. Trello expresses only `updated`, in either direction. */
|
|
1789
|
+
sort?: IssueSorting;
|
|
1790
|
+
},
|
|
1764
1791
|
): Promise<ProviderApiCollectionResult<ProviderIssue>> {
|
|
1765
1792
|
const { provider, tokenWithInfo } = await this.ensureProviderTokenAndFunction(
|
|
1766
1793
|
tokenOptInfo,
|
|
@@ -1782,7 +1809,18 @@ export class ProvidersApi {
|
|
|
1782
1809
|
async getIssuesForResourceForCurrentUser(
|
|
1783
1810
|
tokenOptInfo: TokenWithInfo,
|
|
1784
1811
|
resourceId: string,
|
|
1785
|
-
options?: {
|
|
1812
|
+
options?: {
|
|
1813
|
+
cursor?: string;
|
|
1814
|
+
/**
|
|
1815
|
+
* See {@link GetIssuesOptions.sort}. Forwarded for the same reason as every sibling issue read, even
|
|
1816
|
+
* though nothing reaches this one with a sort today: `IssuesIntegration.searchProviderMyIssues` is the
|
|
1817
|
+
* only caller and takes no options. A wrapper that accepted the SDK's input and silently dropped one
|
|
1818
|
+
* field of it would give whoever adds that option no ordering and no error.
|
|
1819
|
+
*/
|
|
1820
|
+
sort?: IssueSorting;
|
|
1821
|
+
isPAT?: boolean;
|
|
1822
|
+
baseUrl?: string;
|
|
1823
|
+
},
|
|
1786
1824
|
): Promise<PagedResult<ProviderIssue>> {
|
|
1787
1825
|
const { provider, tokenWithInfo } = await this.ensureProviderTokenAndFunction(
|
|
1788
1826
|
tokenOptInfo,
|
|
@@ -1790,7 +1828,7 @@ export class ProvidersApi {
|
|
|
1790
1828
|
);
|
|
1791
1829
|
|
|
1792
1830
|
return this.getPagedResult<ProviderIssue>(
|
|
1793
|
-
{ resourceId: resourceId },
|
|
1831
|
+
{ resourceId: resourceId, sort: options?.sort, includeTransitions: jiraListIncludeTransitions },
|
|
1794
1832
|
provider.getIssuesForResourceForCurrentUserFn,
|
|
1795
1833
|
tokenWithInfo,
|
|
1796
1834
|
options?.cursor,
|
|
@@ -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 {
|
|
@@ -44,7 +44,14 @@ export interface IssueCountScope {
|
|
|
44
44
|
/** Repositories to count over. Combines with `org`, exactly as in `searchIssuesPage`. */
|
|
45
45
|
repos?: ProviderReposInput;
|
|
46
46
|
org?: string;
|
|
47
|
-
/**
|
|
47
|
+
/**
|
|
48
|
+
* Same criteria model as `searchIssuesPage`, validated against the same capability table.
|
|
49
|
+
*
|
|
50
|
+
* `criteria.sort` does not affect the count — a total is invariant under ordering — but it is still VALIDATED,
|
|
51
|
+
* and an unsupported key refuses this scope exactly as it would refuse the read. That is the point of the
|
|
52
|
+
* count: it previews the constraints the read will apply, so one that accepted what the read refuses would
|
|
53
|
+
* promise a fetch that can't happen, which is worse than no preview at all.
|
|
54
|
+
*/
|
|
48
55
|
criteria?: IssueSearchCriteria;
|
|
49
56
|
}
|
|
50
57
|
|