@gitkraken/core-gitlens 0.5.104 → 0.5.106
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 +15 -1
- package/dist/git/models/pullRequest.d.ts +47 -0
- package/dist/git/models/pullRequest.d.ts.map +1 -1
- package/dist/git/models/pullRequest.js +8 -0
- package/dist/git/models/pullRequest.js.map +1 -1
- package/dist/plus/git-github/api/github.d.ts +33 -1
- package/dist/plus/git-github/api/github.d.ts.map +1 -1
- package/dist/plus/git-github/api/github.js +176 -0
- package/dist/plus/git-github/api/github.js.map +1 -1
- package/dist/plus/git-github/api/pullRequestSearchQuery.d.ts +16 -0
- package/dist/plus/git-github/api/pullRequestSearchQuery.d.ts.map +1 -0
- package/dist/plus/git-github/api/pullRequestSearchQuery.js +64 -0
- package/dist/plus/git-github/api/pullRequestSearchQuery.js.map +1 -0
- package/dist/plus/integrations/authentication/configuredIntegrationService.d.ts +1 -0
- package/dist/plus/integrations/authentication/configuredIntegrationService.d.ts.map +1 -1
- package/dist/plus/integrations/authentication/configuredIntegrationService.js +4 -1
- package/dist/plus/integrations/authentication/configuredIntegrationService.js.map +1 -1
- package/dist/plus/integrations/index.d.ts +1 -0
- package/dist/plus/integrations/index.d.ts.map +1 -1
- package/dist/plus/integrations/integrationService.d.ts +16 -2
- package/dist/plus/integrations/integrationService.d.ts.map +1 -1
- package/dist/plus/integrations/integrationService.js +34 -23
- package/dist/plus/integrations/integrationService.js.map +1 -1
- package/dist/plus/integrations/manager.d.ts +46 -4
- package/dist/plus/integrations/manager.d.ts.map +1 -1
- package/dist/plus/integrations/models/gitHostIntegration.d.ts +22 -2
- package/dist/plus/integrations/models/gitHostIntegration.d.ts.map +1 -1
- package/dist/plus/integrations/models/gitHostIntegration.js +21 -0
- package/dist/plus/integrations/models/gitHostIntegration.js.map +1 -1
- package/dist/plus/integrations/models/integration.d.ts +2 -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/pullRequestReads.d.ts +18 -0
- package/dist/plus/integrations/models/pullRequestReads.d.ts.map +1 -0
- package/dist/plus/integrations/models/pullRequestReads.js +2 -0
- package/dist/plus/integrations/models/pullRequestReads.js.map +1 -0
- package/dist/plus/integrations/providerFilters.d.ts +7 -6
- package/dist/plus/integrations/providerFilters.d.ts.map +1 -1
- package/dist/plus/integrations/providerFilters.js +1 -7
- package/dist/plus/integrations/providerFilters.js.map +1 -1
- package/dist/plus/integrations/providers/github.d.ts +14 -2
- package/dist/plus/integrations/providers/github.d.ts.map +1 -1
- package/dist/plus/integrations/providers/github.js +15 -0
- package/dist/plus/integrations/providers/github.js.map +1 -1
- package/dist/plus/integrations/providers/models.d.ts +12 -2
- package/dist/plus/integrations/providers/models.d.ts.map +1 -1
- package/dist/plus/integrations/providers/models.js +18 -0
- package/dist/plus/integrations/providers/models.js.map +1 -1
- package/dist/plus/integrations/reads/context.d.ts +1 -1
- package/dist/plus/integrations/reads/filters.d.ts +34 -1
- package/dist/plus/integrations/reads/filters.d.ts.map +1 -1
- package/dist/plus/integrations/reads/filters.js +73 -0
- package/dist/plus/integrations/reads/filters.js.map +1 -1
- package/dist/plus/integrations/reads/searchPullRequests.d.ts +26 -0
- package/dist/plus/integrations/reads/searchPullRequests.d.ts.map +1 -0
- package/dist/plus/integrations/reads/searchPullRequests.js +108 -0
- package/dist/plus/integrations/reads/searchPullRequests.js.map +1 -0
- package/dist/plus/integrations/reads/warnings.d.ts +6 -2
- package/dist/plus/integrations/reads/warnings.d.ts.map +1 -1
- package/dist/plus/integrations/reads/warnings.js +40 -0
- package/dist/plus/integrations/reads/warnings.js.map +1 -1
- package/docs/integrations.md +53 -4
- package/package.json +1 -1
- package/src/git/models/pullRequest.ts +50 -0
- package/src/plus/git-github/api/github.ts +254 -1
- package/src/plus/git-github/api/pullRequestSearchQuery.ts +80 -0
- package/src/plus/integrations/authentication/configuredIntegrationService.ts +6 -1
- package/src/plus/integrations/index.ts +2 -0
- package/src/plus/integrations/integrationService.ts +108 -70
- package/src/plus/integrations/manager.ts +55 -4
- package/src/plus/integrations/models/gitHostIntegration.ts +56 -2
- package/src/plus/integrations/models/integration.ts +2 -0
- package/src/plus/integrations/models/pullRequestReads.ts +18 -0
- package/src/plus/integrations/providerFilters.ts +8 -6
- package/src/plus/integrations/providers/github.ts +37 -1
- package/src/plus/integrations/providers/models.ts +37 -2
- package/src/plus/integrations/reads/context.ts +1 -1
- package/src/plus/integrations/reads/filters.ts +105 -0
- package/src/plus/integrations/reads/searchPullRequests.ts +210 -0
- package/src/plus/integrations/reads/warnings.ts +73 -3
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import type { Account } from '../../git/models/author.js';
|
|
2
2
|
import type { IssueSearchCriteria, IssueShape } from '../../git/models/issue.js';
|
|
3
|
-
import type {
|
|
3
|
+
import type {
|
|
4
|
+
PullRequest,
|
|
5
|
+
PullRequestSearchCriteria,
|
|
6
|
+
PullRequestShape,
|
|
7
|
+
PullRequestStateFilter,
|
|
8
|
+
} from '../../git/models/pullRequest.js';
|
|
4
9
|
import type { GitRemote } from '../../git/models/remote.js';
|
|
5
10
|
import type { RemoteProviderId } from '../../git/models/remoteProvider.js';
|
|
6
11
|
import type { ResourceDescriptor } from '../../git/models/resourceDescriptor.js';
|
|
@@ -67,6 +72,7 @@ import type {
|
|
|
67
72
|
ProviderReposInput,
|
|
68
73
|
ProviderRepositoryShape,
|
|
69
74
|
PullRequestFilter,
|
|
75
|
+
PullRequestSearchCapabilities,
|
|
70
76
|
} from './providers/models.js';
|
|
71
77
|
import { providersMetadata } from './providers/models.js';
|
|
72
78
|
import type { ProvidersApi } from './providers/providersApi.js';
|
|
@@ -81,6 +87,7 @@ import { listIssueTrackerIssuesPage } from './reads/issueTracker.js';
|
|
|
81
87
|
import { listPullRequestsPage } from './reads/pullRequests.js';
|
|
82
88
|
import { resolveRepository } from './reads/resolveRepository.js';
|
|
83
89
|
import { searchIssuesPage } from './reads/searchIssues.js';
|
|
90
|
+
import { searchPullRequestsPage } from './reads/searchPullRequests.js';
|
|
84
91
|
import { sweepClosedPullRequests, sweepPullRequests } from './reads/sweeps.js';
|
|
85
92
|
import { noConnectionWarning } from './reads/warnings.js';
|
|
86
93
|
import type {
|
|
@@ -533,6 +540,7 @@ export class IntegrationService implements Disposable, RepositoryResolutionConte
|
|
|
533
540
|
getSupportedFilters(providerId: IntegrationIds): {
|
|
534
541
|
pullRequests: PullRequestFilter[];
|
|
535
542
|
pullRequestsAccountWide: PullRequestFilter[];
|
|
543
|
+
pullRequestSearch: PullRequestSearchCapabilities;
|
|
536
544
|
issues: IssueFilter[];
|
|
537
545
|
issuesAccountWide: IssueFilter[];
|
|
538
546
|
issueSearch: IssueSearchCapabilities;
|
|
@@ -1051,6 +1059,22 @@ export class IntegrationService implements Disposable, RepositoryResolutionConte
|
|
|
1051
1059
|
return listPullRequestsPage(this, options);
|
|
1052
1060
|
}
|
|
1053
1061
|
|
|
1062
|
+
/** See {@link IntegrationManager.searchPullRequestsPage}. */
|
|
1063
|
+
async searchPullRequestsPage(options: {
|
|
1064
|
+
providerId: IntegrationIds;
|
|
1065
|
+
repos?: ProviderReposInput;
|
|
1066
|
+
org?: string;
|
|
1067
|
+
criteria?: PullRequestSearchCriteria;
|
|
1068
|
+
page?: number;
|
|
1069
|
+
cursor?: string;
|
|
1070
|
+
itemsPerPage?: number;
|
|
1071
|
+
forceSync?: boolean;
|
|
1072
|
+
connectionId?: string;
|
|
1073
|
+
domain?: string;
|
|
1074
|
+
}): Promise<ProviderPagedResult<PullRequestShape>> {
|
|
1075
|
+
return searchPullRequestsPage(this, options);
|
|
1076
|
+
}
|
|
1077
|
+
|
|
1054
1078
|
async listIssuesPage(options: {
|
|
1055
1079
|
providerId: IntegrationIds;
|
|
1056
1080
|
repos?: ProviderReposInput;
|
|
@@ -1559,9 +1583,11 @@ export class IntegrationService implements Disposable, RepositoryResolutionConte
|
|
|
1559
1583
|
|
|
1560
1584
|
// Persist every account when the backend advertises per-connection identity (multi-account). This
|
|
1561
1585
|
// is a strict no-op for backends that return a single, id-less connection per provider.
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1586
|
+
await Promise.all(
|
|
1587
|
+
Array.from(connectionsById, ([integrationId, connections]) =>
|
|
1588
|
+
this.reconcileCloudConnections(integrationId, connections, forceConnect),
|
|
1589
|
+
),
|
|
1590
|
+
);
|
|
1565
1591
|
|
|
1566
1592
|
this.ctx.hooks?.connection?.onConnectedChanged?.({
|
|
1567
1593
|
integrationIds: [...connectedIntegrations.values()],
|
|
@@ -1612,79 +1638,91 @@ export class IntegrationService implements Disposable, RepositoryResolutionConte
|
|
|
1612
1638
|
const existingById = new Map(
|
|
1613
1639
|
this.configuredIntegrationService.getConfigured(id, { cloud: true }).map(c => [c.id, c]),
|
|
1614
1640
|
);
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1641
|
+
const preparedConnections = await Promise.all(
|
|
1642
|
+
identified.map(async connection => {
|
|
1643
|
+
// The wire `domain` is usually a full URL, though cloud providers can return a bare host.
|
|
1644
|
+
// Self-managed integrations are keyed/constructed by host.
|
|
1645
|
+
const host = hostFromDomain(connection.domain);
|
|
1646
|
+
|
|
1647
|
+
// Self-managed connections are keyed by host, so an unparseable/empty domain would store the
|
|
1648
|
+
// session and descriptor under an empty host — producing ambiguous keys (`connected:<id>:`) that
|
|
1649
|
+
// break later resolution and local-disconnect checks. Skip such a connection rather than corrupt
|
|
1650
|
+
// state; cloud providers key off their canonical domain and are unaffected.
|
|
1651
|
+
if (isGitSelfManagedHostIntegrationId(id) && !host) {
|
|
1652
|
+
scope?.warn(`Skipping connection '${connection.id}' for ${id}: unresolved host from domain`);
|
|
1653
|
+
return undefined;
|
|
1654
|
+
}
|
|
1628
1655
|
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
if (connection.primary) {
|
|
1646
|
-
const domain = isGitSelfManagedHostIntegrationId(id) ? host : undefined;
|
|
1647
|
-
if (!syncedPrimaryIdsByDomain.has(domain)) {
|
|
1648
|
-
syncedPrimaryIdsByDomain.set(domain, connection.id);
|
|
1649
|
-
}
|
|
1656
|
+
// Don't resurrect a connection the user disconnected locally: a host "disconnect" only clears
|
|
1657
|
+
// local state (the backend still lists the token), so without this the next non-forced sync would
|
|
1658
|
+
// re-store the secret/config. A forced reconnect clears this flag (in the sync loop above) before
|
|
1659
|
+
// reconcile runs, so it proceeds normally.
|
|
1660
|
+
if (this.isLocallyDisconnected(id, host)) return undefined;
|
|
1661
|
+
|
|
1662
|
+
syncEligibleIds.add(connection.id);
|
|
1663
|
+
|
|
1664
|
+
// On a routine (non-forced) check-in, skip the token fetch + secret write for a connection we
|
|
1665
|
+
// already have stored and that hasn't expired: nothing to refresh, so avoid the extra GK API
|
|
1666
|
+
// traffic and secret churn. Still treat it as synced (so it doesn't trip the prune guard) and
|
|
1667
|
+
// record its primary below. Forced syncs, new connections, and expired tokens fall through and
|
|
1668
|
+
// fetch as before.
|
|
1669
|
+
const cached = existingById.get(connection.id);
|
|
1670
|
+
if (!forceConnect && cached != null && !isDescriptorExpired(cached)) {
|
|
1671
|
+
return { kind: 'cached' as const, connection: connection, host: host };
|
|
1650
1672
|
}
|
|
1651
|
-
continue;
|
|
1652
|
-
}
|
|
1653
1673
|
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1674
|
+
try {
|
|
1675
|
+
const session = await cloudIntegrations.getConnectionSession(id, undefined, connection.id);
|
|
1676
|
+
if (session == null) return undefined;
|
|
1677
|
+
|
|
1678
|
+
let providerSession = toProviderSession(id, connection, session, host);
|
|
1679
|
+
|
|
1680
|
+
// Resolve a human-readable account handle with the same precedence as the gk CLI:
|
|
1681
|
+
// (1) the value the backend put on the connection, (2) a previously-resolved name cached in
|
|
1682
|
+
// our configured store (keyed by connection id), (3) a live provider-API lookup. This keeps
|
|
1683
|
+
// provider round-trips to the first sight of a connection; degrade to undefined on failure.
|
|
1684
|
+
const existing = existingById.get(connection.id);
|
|
1685
|
+
const accountName =
|
|
1686
|
+
normalizeAccountName(connection.accountName) ??
|
|
1687
|
+
normalizeAccountName(existing?.accountName) ??
|
|
1688
|
+
(await this.resolveAccountName(id, host, providerSession));
|
|
1689
|
+
if (accountName != null) {
|
|
1690
|
+
providerSession = {
|
|
1691
|
+
...providerSession,
|
|
1692
|
+
account: { ...providerSession.account, label: accountName },
|
|
1693
|
+
};
|
|
1694
|
+
}
|
|
1695
|
+
|
|
1696
|
+
return {
|
|
1697
|
+
kind: 'fetched' as const,
|
|
1698
|
+
connection: connection,
|
|
1699
|
+
host: host,
|
|
1700
|
+
providerSession: providerSession,
|
|
1673
1701
|
};
|
|
1702
|
+
} catch (ex) {
|
|
1703
|
+
scope?.warn(
|
|
1704
|
+
`Failed to sync connection '${connection.id}' for ${id}: ${ex instanceof Error ? ex.message : String(ex)}`,
|
|
1705
|
+
);
|
|
1706
|
+
return undefined;
|
|
1674
1707
|
}
|
|
1708
|
+
}),
|
|
1709
|
+
);
|
|
1675
1710
|
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1711
|
+
// The remote work above is independent, but session persistence remains ordered because storage
|
|
1712
|
+
// implementations may update a shared configured-connections collection with read-modify-write.
|
|
1713
|
+
for (const prepared of preparedConnections) {
|
|
1714
|
+
if (prepared == null) continue;
|
|
1715
|
+
|
|
1716
|
+
if (prepared.kind === 'fetched') {
|
|
1717
|
+
await this.configuredIntegrationService.storeSession(id, prepared.providerSession);
|
|
1718
|
+
}
|
|
1719
|
+
|
|
1720
|
+
syncedIds.add(prepared.connection.id);
|
|
1721
|
+
if (prepared.connection.primary) {
|
|
1722
|
+
const domain = isGitSelfManagedHostIntegrationId(id) ? prepared.host : undefined;
|
|
1723
|
+
if (!syncedPrimaryIdsByDomain.has(domain)) {
|
|
1724
|
+
syncedPrimaryIdsByDomain.set(domain, prepared.connection.id);
|
|
1683
1725
|
}
|
|
1684
|
-
} catch (ex) {
|
|
1685
|
-
scope?.warn(
|
|
1686
|
-
`Failed to sync connection '${connection.id}' for ${id}: ${ex instanceof Error ? ex.message : String(ex)}`,
|
|
1687
|
-
);
|
|
1688
1726
|
}
|
|
1689
1727
|
}
|
|
1690
1728
|
|
|
@@ -1,10 +1,19 @@
|
|
|
1
1
|
import type { IssueSearchCriteria, IssueShape } from '../../git/models/issue.js';
|
|
2
|
-
import type {
|
|
2
|
+
import type {
|
|
3
|
+
PullRequestSearchCriteria,
|
|
4
|
+
PullRequestShape,
|
|
5
|
+
PullRequestStateFilter,
|
|
6
|
+
} from '../../git/models/pullRequest.js';
|
|
3
7
|
import type { Event } from '../../utils/event.js';
|
|
4
8
|
import type { ConfiguredIntegrationsChangeEvent } from './authentication/configuredIntegrationService.js';
|
|
5
9
|
import type { ConfiguredIntegrationDescriptor } from './authentication/models.js';
|
|
6
10
|
import type { IntegrationIds } from './constants.js';
|
|
7
|
-
import type {
|
|
11
|
+
import type {
|
|
12
|
+
IssueFilter,
|
|
13
|
+
IssueSearchCapabilities,
|
|
14
|
+
PullRequestFilter,
|
|
15
|
+
PullRequestSearchCapabilities,
|
|
16
|
+
} from './providerFilters.js';
|
|
8
17
|
import type { IssueCountResult, IssueCountScope } from './reads/counts.js';
|
|
9
18
|
import type {
|
|
10
19
|
ConnectionStateChangeEvent,
|
|
@@ -142,8 +151,9 @@ export type ListProjectsOptions =
|
|
|
142
151
|
*
|
|
143
152
|
* ## Paging contract
|
|
144
153
|
*
|
|
145
|
-
* Every paged read (`listRepos`, `listPullRequestsPage`, `
|
|
146
|
-
* `broadenIssues`) takes both `page` and `cursor`, and the two are NOT
|
|
154
|
+
* Every paged read (`listRepos`, `listPullRequestsPage`, `searchPullRequestsPage`, `listIssuesPage`,
|
|
155
|
+
* `listIssueTrackerIssuesPage`, `broadenIssues`) takes both `page` and `cursor`, and the two are NOT
|
|
156
|
+
* interchangeable:
|
|
147
157
|
*
|
|
148
158
|
* - **Supplying `cursor` guarantees a single upstream round trip per provider scope.** A threaded continuation
|
|
149
159
|
* is handed to the provider as-is; the facade never walks the pages before it. This is a guarantee, not an
|
|
@@ -199,6 +209,11 @@ export interface IntegrationManager {
|
|
|
199
209
|
pullRequests: PullRequestFilter[];
|
|
200
210
|
/** Optional for structural compatibility; missing means no account-wide narrowing filters are supported. */
|
|
201
211
|
pullRequestsAccountWide?: PullRequestFilter[];
|
|
212
|
+
/**
|
|
213
|
+
* Criteria and scopes {@link searchPullRequestsPage} can express. Always present; an empty relationship list
|
|
214
|
+
* means the provider exposes no filtered pull-request search.
|
|
215
|
+
*/
|
|
216
|
+
pullRequestSearch: PullRequestSearchCapabilities;
|
|
202
217
|
issues: IssueFilter[];
|
|
203
218
|
issuesAccountWide: IssueFilter[];
|
|
204
219
|
/**
|
|
@@ -274,6 +289,42 @@ export interface IntegrationManager {
|
|
|
274
289
|
*/
|
|
275
290
|
domain?: string;
|
|
276
291
|
}): Promise<ProviderPagedResult<PullRequestShape>>;
|
|
292
|
+
/**
|
|
293
|
+
* Pull requests matching structured criteria over a repository/organization or current-user relationship
|
|
294
|
+
* scope.
|
|
295
|
+
*
|
|
296
|
+
* This is a separate read from {@link listPullRequestsPage}: free text reaches the provider instead of filtering
|
|
297
|
+
* whichever rows happened to be loaded, and every cursor-threaded page costs exactly one upstream request.
|
|
298
|
+
* `criteria.relationships` and `criteria.states` are OR sets, so `[closed, merged]` expresses the complete
|
|
299
|
+
* terminal set and `[Author, Assignee, ReviewRequested]` matches the visible PR list without falling back to
|
|
300
|
+
* GitHub's mismatched `involves:@me`. Omit relationships only with a repository/organization scope to search
|
|
301
|
+
* every PR there.
|
|
302
|
+
*
|
|
303
|
+
* Results are always ordered most-recently-updated-first. If the provider's result ceiling is reached, the
|
|
304
|
+
* request still succeeds and carries a warning omission with `totalCount`, `limit`, and `recovery: 'none'`.
|
|
305
|
+
* `totalCount` is the largest provider-reported pre-ceiling facet count, not the reachable or returned row
|
|
306
|
+
* count; this mirrors the per-search ceiling's own unit.
|
|
307
|
+
*
|
|
308
|
+
* Check `getSupportedFilters().pullRequestSearch` before calling. A provider that reports no relationships
|
|
309
|
+
* refuses the read rather than returning a page that never honored the criteria or scope.
|
|
310
|
+
*/
|
|
311
|
+
searchPullRequestsPage(options: {
|
|
312
|
+
providerId: IntegrationIds;
|
|
313
|
+
/** Repository descriptors that bound the search; ids cannot name provider search qualifiers. */
|
|
314
|
+
repos?: ProviderRepositoriesInput;
|
|
315
|
+
/** Organization/account that bounds the search. */
|
|
316
|
+
org?: string;
|
|
317
|
+
criteria?: PullRequestSearchCriteria;
|
|
318
|
+
/** Cursor-only: without a cursor, reaching page N costs O(N) upstream requests. */
|
|
319
|
+
page?: number;
|
|
320
|
+
cursor?: string;
|
|
321
|
+
/** Page size per relationship × state facet; the deduped union can contain more rows. */
|
|
322
|
+
itemsPerPage?: number;
|
|
323
|
+
forceSync?: boolean;
|
|
324
|
+
connectionId?: string;
|
|
325
|
+
/** Self-managed host domain fallback; see {@link ProviderSweepTarget.domain}. */
|
|
326
|
+
domain?: string;
|
|
327
|
+
}): Promise<ProviderPagedResult<PullRequestShape>>;
|
|
277
328
|
listIssuesPage(options: {
|
|
278
329
|
providerId: IntegrationIds;
|
|
279
330
|
repos?: ProviderRepositoriesInput;
|
|
@@ -3,7 +3,12 @@ import type { Account, UnidentifiedAuthor } from '../../../git/models/author.js'
|
|
|
3
3
|
import type { DefaultBranch } from '../../../git/models/defaultBranch.js';
|
|
4
4
|
import type { IssueSearchCriteria, IssueShape, IssueStateFilter } from '../../../git/models/issue.js';
|
|
5
5
|
import type { IssueOrPullRequestState as PullRequestState } from '../../../git/models/issueOrPullRequest.js';
|
|
6
|
-
import type {
|
|
6
|
+
import type {
|
|
7
|
+
PullRequest,
|
|
8
|
+
PullRequestMergeMethod,
|
|
9
|
+
PullRequestSearchCriteria,
|
|
10
|
+
PullRequestStateFilter,
|
|
11
|
+
} from '../../../git/models/pullRequest.js';
|
|
7
12
|
import type { RepositoryMetadata } from '../../../git/models/repositoryMetadata.js';
|
|
8
13
|
import type { ResourceDescriptor } from '../../../git/models/resourceDescriptor.js';
|
|
9
14
|
import type { PullRequestUrlIdentity } from '../../../git/utils/pullRequest.utils.js';
|
|
@@ -44,7 +49,12 @@ import {
|
|
|
44
49
|
} from '../providers/models.js';
|
|
45
50
|
import type { ProvidersApi } from '../providers/providersApi.js';
|
|
46
51
|
import { mergeCollectionMetadata } from '../providers/utils/providerPaging.js';
|
|
47
|
-
import type {
|
|
52
|
+
import type {
|
|
53
|
+
IntegrationResult,
|
|
54
|
+
IntegrationType,
|
|
55
|
+
ProviderIssueSearchPage,
|
|
56
|
+
ProviderPullRequestSearchPage,
|
|
57
|
+
} from './integration.js';
|
|
48
58
|
import { IntegrationBase } from './integration.js';
|
|
49
59
|
|
|
50
60
|
function isAzureDevOpsProvider(
|
|
@@ -1543,6 +1553,50 @@ export abstract class GitHostIntegration<
|
|
|
1543
1553
|
options?: SearchMyPullRequestsOptions,
|
|
1544
1554
|
): Promise<IntegrationResult<PullRequest[] | undefined>>;
|
|
1545
1555
|
|
|
1556
|
+
/**
|
|
1557
|
+
* Result-returning wrapper for the filtered pull-request search. Errors become the soft
|
|
1558
|
+
* `{ error }` branch so the public facade can preserve an empty account and a failed request as distinct
|
|
1559
|
+
* outcomes.
|
|
1560
|
+
*/
|
|
1561
|
+
async searchPullRequestsPageResult(
|
|
1562
|
+
options: {
|
|
1563
|
+
repos?: ProviderRepoInput[];
|
|
1564
|
+
org?: string;
|
|
1565
|
+
criteria?: PullRequestSearchCriteria;
|
|
1566
|
+
cursor?: string;
|
|
1567
|
+
pageSize?: number;
|
|
1568
|
+
},
|
|
1569
|
+
cancellation?: AbortSignal,
|
|
1570
|
+
connectionId?: string,
|
|
1571
|
+
): Promise<IntegrationResult<ProviderPullRequestSearchPage | undefined>> {
|
|
1572
|
+
const scope = getScopedLogger();
|
|
1573
|
+
const session = await this.resolveReadSession(connectionId, scope);
|
|
1574
|
+
if (session == null) return undefined;
|
|
1575
|
+
|
|
1576
|
+
const start = performance.now();
|
|
1577
|
+
try {
|
|
1578
|
+
const result = await this.searchProviderPullRequestsPage?.(session, options, cancellation);
|
|
1579
|
+
this.resetRequestExceptionCount('searchPullRequestsPage');
|
|
1580
|
+
return { value: result, duration: performance.now() - start };
|
|
1581
|
+
} catch (ex) {
|
|
1582
|
+
this.handleProviderException('searchPullRequestsPage', ex, { scope: scope, connectionId: connectionId });
|
|
1583
|
+
return { error: toError(ex), duration: performance.now() - start };
|
|
1584
|
+
}
|
|
1585
|
+
}
|
|
1586
|
+
|
|
1587
|
+
/** Must agree with `ProviderMetadata.supportedPullRequestSearch`. */
|
|
1588
|
+
protected searchProviderPullRequestsPage?(
|
|
1589
|
+
session: ProviderAuthenticationSession,
|
|
1590
|
+
options: {
|
|
1591
|
+
repos?: ProviderRepoInput[];
|
|
1592
|
+
org?: string;
|
|
1593
|
+
criteria?: PullRequestSearchCriteria;
|
|
1594
|
+
cursor?: string;
|
|
1595
|
+
pageSize?: number;
|
|
1596
|
+
},
|
|
1597
|
+
cancellation?: AbortSignal,
|
|
1598
|
+
): Promise<ProviderPullRequestSearchPage | undefined>;
|
|
1599
|
+
|
|
1546
1600
|
async searchPullRequests(
|
|
1547
1601
|
searchQuery: string,
|
|
1548
1602
|
repo?: T,
|
|
@@ -42,6 +42,7 @@ export type IntegrationType = 'git' | 'issues';
|
|
|
42
42
|
// The issue-read contracts live in their own module (pure data, and their relationship to each other is the
|
|
43
43
|
// point of reading them together); re-exported here so the providers that implement these reads keep one import.
|
|
44
44
|
export type { AccountWideIssuesResult, ProviderIssueSearchPage, SearchMyIssuesOptions } from './issueReads.js';
|
|
45
|
+
export type { ProviderPullRequestSearchPage } from './pullRequestReads.js';
|
|
45
46
|
|
|
46
47
|
export type IntegrationKey<T extends IntegrationIds = IntegrationIds> = T extends
|
|
47
48
|
| GitCloudHostIntegrationId
|
|
@@ -82,6 +83,7 @@ type SyncReqUsecase = Exclude<
|
|
|
82
83
|
| 'countIssues'
|
|
83
84
|
| 'mergePullRequest'
|
|
84
85
|
| 'searchIssuesPage'
|
|
86
|
+
| 'searchPullRequestsPage'
|
|
85
87
|
| 'searchMyIssues'
|
|
86
88
|
| 'searchMyPullRequests'
|
|
87
89
|
| 'searchPullRequests',
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { PullRequestShape } from '../../../git/models/pullRequest.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* One provider page of the filtered pull-request search, before the facade turns it into a neutral
|
|
5
|
+
* paged result with warnings.
|
|
6
|
+
*
|
|
7
|
+
* `totalCount` is the provider-reported pre-ceiling facet count, not the number of rows that remain reachable
|
|
8
|
+
* after a provider limit. It stays optional because absence means "not reported", never zero. Together with
|
|
9
|
+
* `truncated` it lets the facade distinguish GitHub's quantified ceiling from another unusable continuation.
|
|
10
|
+
*/
|
|
11
|
+
export type ProviderPullRequestSearchPage = {
|
|
12
|
+
values: PullRequestShape[];
|
|
13
|
+
truncated: boolean;
|
|
14
|
+
cursor?: string;
|
|
15
|
+
hasMore: boolean;
|
|
16
|
+
page: number;
|
|
17
|
+
totalCount?: number;
|
|
18
|
+
};
|
|
@@ -1,9 +1,4 @@
|
|
|
1
|
-
export
|
|
2
|
-
Author = 'author',
|
|
3
|
-
Assignee = 'assignee',
|
|
4
|
-
ReviewRequested = 'review-requested',
|
|
5
|
-
Mention = 'mention',
|
|
6
|
-
}
|
|
1
|
+
export { PullRequestFilter } from '../../git/models/pullRequest.js';
|
|
7
2
|
|
|
8
3
|
export enum IssueFilter {
|
|
9
4
|
Author = 'author',
|
|
@@ -25,3 +20,10 @@ export type {
|
|
|
25
20
|
IssueSearchCriteria,
|
|
26
21
|
IssueSearchRelationship,
|
|
27
22
|
} from '../../git/models/issue.js';
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* The filtered pull-request search criteria and its capability table. These live in `@gitkraken/core-gitlens/git` for the
|
|
26
|
+
* same dependency-direction reason as the issue-search model above: provider API clients translate them below
|
|
27
|
+
* this package and cannot import this facade.
|
|
28
|
+
*/
|
|
29
|
+
export type { PullRequestSearchCapabilities, PullRequestSearchCriteria } from '../../git/models/pullRequest.js';
|
|
@@ -5,6 +5,7 @@ import type { IssueOrPullRequest } from '../../../git/models/issueOrPullRequest.
|
|
|
5
5
|
import type {
|
|
6
6
|
PullRequest,
|
|
7
7
|
PullRequestMergeMethod,
|
|
8
|
+
PullRequestSearchCriteria,
|
|
8
9
|
PullRequestState,
|
|
9
10
|
PullRequestStateFilter,
|
|
10
11
|
} from '../../../git/models/pullRequest.js';
|
|
@@ -25,7 +26,11 @@ import { IntegrationReadUnavailableError } from '../errors.js';
|
|
|
25
26
|
import type { IntegrationConnectionChangeEvent } from '../integrationService.js';
|
|
26
27
|
import type { SearchMyPullRequestsOptions } from '../models/gitHostIntegration.js';
|
|
27
28
|
import { GitHostIntegration } from '../models/gitHostIntegration.js';
|
|
28
|
-
import type {
|
|
29
|
+
import type {
|
|
30
|
+
ProviderIssueSearchPage,
|
|
31
|
+
ProviderPullRequestSearchPage,
|
|
32
|
+
SearchMyIssuesOptions,
|
|
33
|
+
} from '../models/integration.js';
|
|
29
34
|
import type { GitHubIntegrationIds } from './github/github.utils.js';
|
|
30
35
|
import { getGitHubPullRequestIdentityFromMaybeUrl } from './github/github.utils.js';
|
|
31
36
|
import type {
|
|
@@ -625,6 +630,37 @@ abstract class GitHubIntegrationBase<ID extends GitHubIntegrationIds> extends Gi
|
|
|
625
630
|
);
|
|
626
631
|
}
|
|
627
632
|
|
|
633
|
+
/**
|
|
634
|
+
* Filtered pull-request search over a repository/organization or explicit current-user relationship scope. The
|
|
635
|
+
* API client owns criteria sanitizing, facet unioning, ordering, and the composite cursor that carries GitHub's
|
|
636
|
+
* result-ceiling signal across pages.
|
|
637
|
+
*/
|
|
638
|
+
protected override async searchProviderPullRequestsPage(
|
|
639
|
+
session: ProviderAuthenticationSession,
|
|
640
|
+
options: {
|
|
641
|
+
repos?: ProviderRepoInput[];
|
|
642
|
+
org?: string;
|
|
643
|
+
criteria?: PullRequestSearchCriteria;
|
|
644
|
+
cursor?: string;
|
|
645
|
+
pageSize?: number;
|
|
646
|
+
},
|
|
647
|
+
cancellation?: AbortSignal,
|
|
648
|
+
): Promise<ProviderPullRequestSearchPage | undefined> {
|
|
649
|
+
return (await this.authenticationService.apis.github)?.searchPullRequestsPage(
|
|
650
|
+
this,
|
|
651
|
+
toTokenWithInfo(this.id, session),
|
|
652
|
+
{
|
|
653
|
+
repos: options.repos?.map(r => `${r.namespace}/${r.name}`),
|
|
654
|
+
org: options.org,
|
|
655
|
+
criteria: options.criteria,
|
|
656
|
+
baseUrl: this.apiBaseUrl,
|
|
657
|
+
cursor: options.cursor,
|
|
658
|
+
pageSize: options.pageSize,
|
|
659
|
+
},
|
|
660
|
+
cancellation,
|
|
661
|
+
);
|
|
662
|
+
}
|
|
663
|
+
|
|
628
664
|
/**
|
|
629
665
|
* The filtered issue search: one GraphQL request per page, no forced relationship to the current user, and no
|
|
630
666
|
* route through the SDK's repo-scoped read (whose over-limit recovery walk can spend up to 128 requests).
|
|
@@ -75,11 +75,17 @@ import {
|
|
|
75
75
|
IssuesCloudHostIntegrationId,
|
|
76
76
|
} from '../constants.js';
|
|
77
77
|
import type { Integration, IntegrationType } from '../models/integration.js';
|
|
78
|
-
import type { IssueSearchCapabilities } from '../providerFilters.js';
|
|
78
|
+
import type { IssueSearchCapabilities, PullRequestSearchCapabilities } from '../providerFilters.js';
|
|
79
79
|
import { IssueFilter, PullRequestFilter } from '../providerFilters.js';
|
|
80
80
|
|
|
81
81
|
export { IssueFilter, PullRequestFilter } from '../providerFilters.js';
|
|
82
|
-
export type {
|
|
82
|
+
export type {
|
|
83
|
+
IssueSearchCapabilities,
|
|
84
|
+
IssueSearchCriteria,
|
|
85
|
+
IssueSearchRelationship,
|
|
86
|
+
PullRequestSearchCapabilities,
|
|
87
|
+
PullRequestSearchCriteria,
|
|
88
|
+
} from '../providerFilters.js';
|
|
83
89
|
import type { ProviderRepositoryShape } from '../results.js';
|
|
84
90
|
|
|
85
91
|
export type { ProviderOrganization, ProviderRepositoryShape } from '../results.js';
|
|
@@ -696,6 +702,16 @@ export interface ProviderMetadata {
|
|
|
696
702
|
* This is independent from `supportedPullRequestFilters`, which describes repo-scoped reads.
|
|
697
703
|
*/
|
|
698
704
|
supportedAccountWidePullRequestFilters?: PullRequestFilter[];
|
|
705
|
+
/**
|
|
706
|
+
* What the filtered pull-request search can express server-side. Absent means the provider has
|
|
707
|
+
* no such search and the facade refuses it rather than returning an unfiltered page.
|
|
708
|
+
*/
|
|
709
|
+
supportedPullRequestSearch?: PullRequestSearchCapabilities;
|
|
710
|
+
/**
|
|
711
|
+
* Maximum results the provider will serve for one pull-request search facet. Matches beyond it are unreachable
|
|
712
|
+
* and are reported as a succeeded omission carrying this limit plus the provider's pre-ceiling facet count.
|
|
713
|
+
*/
|
|
714
|
+
pullRequestSearchResultLimit?: number;
|
|
699
715
|
/** Filters the REPO-scoped issue read can express. */
|
|
700
716
|
supportedIssueFilters?: IssueFilter[];
|
|
701
717
|
/**
|
|
@@ -760,6 +776,21 @@ const githubIssueSearchCapabilities: IssueSearchCapabilities = {
|
|
|
760
776
|
states: true,
|
|
761
777
|
};
|
|
762
778
|
|
|
779
|
+
/** GitHub and GHE use the same filtered pull-request search syntax and result ceiling. */
|
|
780
|
+
const githubPullRequestSearchCapabilities: PullRequestSearchCapabilities = {
|
|
781
|
+
relationships: [
|
|
782
|
+
PullRequestFilter.Author,
|
|
783
|
+
PullRequestFilter.Assignee,
|
|
784
|
+
PullRequestFilter.ReviewRequested,
|
|
785
|
+
PullRequestFilter.Mention,
|
|
786
|
+
],
|
|
787
|
+
states: ['open', 'closed', 'merged', 'all'],
|
|
788
|
+
text: true,
|
|
789
|
+
includeArchived: true,
|
|
790
|
+
repositoryScope: true,
|
|
791
|
+
organizationScope: true,
|
|
792
|
+
};
|
|
793
|
+
|
|
763
794
|
export const providersMetadata: ProvidersMetadata = {
|
|
764
795
|
[GitCloudHostIntegrationId.GitHub]: {
|
|
765
796
|
domain: 'github.com',
|
|
@@ -782,6 +813,8 @@ export const providersMetadata: ProvidersMetadata = {
|
|
|
782
813
|
PullRequestFilter.ReviewRequested,
|
|
783
814
|
PullRequestFilter.Mention,
|
|
784
815
|
],
|
|
816
|
+
supportedPullRequestSearch: githubPullRequestSearchCapabilities,
|
|
817
|
+
pullRequestSearchResultLimit: githubSearchResultLimit,
|
|
785
818
|
// Use 'username' property on account for issue filters
|
|
786
819
|
supportedIssueFilters: [IssueFilter.Author, IssueFilter.Assignee, IssueFilter.Mention],
|
|
787
820
|
// The account-wide read is three independent searches (`author:@me`, `assignee:@me`, `mentions:@me`) behind
|
|
@@ -812,6 +845,8 @@ export const providersMetadata: ProvidersMetadata = {
|
|
|
812
845
|
PullRequestFilter.ReviewRequested,
|
|
813
846
|
PullRequestFilter.Mention,
|
|
814
847
|
],
|
|
848
|
+
supportedPullRequestSearch: githubPullRequestSearchCapabilities,
|
|
849
|
+
pullRequestSearchResultLimit: githubSearchResultLimit,
|
|
815
850
|
// Use 'username' property on account for issue filters
|
|
816
851
|
supportedIssueFilters: [IssueFilter.Author, IssueFilter.Assignee, IssueFilter.Mention],
|
|
817
852
|
// The account-wide read is three independent searches (`author:@me`, `assignee:@me`, `mentions:@me`) behind
|
|
@@ -7,7 +7,7 @@ import type { ProviderWarning } from '../results.js';
|
|
|
7
7
|
/**
|
|
8
8
|
* Everything the facade's provider reads need from the service that owns connection state — and nothing else.
|
|
9
9
|
*
|
|
10
|
-
* The reads (`listOrgs`, `listRepos`, the paged PR/issue reads, the sweeps, `broadenIssues`,
|
|
10
|
+
* The reads (`listOrgs`, `listRepos`, the paged PR/issue reads and searches, the sweeps, `broadenIssues`,
|
|
11
11
|
* `resolveRepository`) are otherwise stateless: given a resolved integration they only transform provider
|
|
12
12
|
* results into the neutral shapes consumers get. What they can't do themselves is decide WHICH integration
|
|
13
13
|
* instance and which host a request addresses, because that depends on the configured connections, the
|