@gitkraken/core-gitlens 0.7.0 → 0.7.2
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 +28 -2
- package/dist/git/remotes/azure-devops.js +3 -3
- package/dist/git/remotes/azure-devops.js.map +1 -1
- package/dist/plus/integrations/authentication/configuredIntegrationService.js +9 -2
- package/dist/plus/integrations/authentication/configuredIntegrationService.js.map +1 -1
- package/dist/plus/integrations/authentication/integrationAuthenticationProvider.js +1 -1
- package/dist/plus/integrations/collectionMetadata.d.ts +48 -3
- package/dist/plus/integrations/collectionMetadata.js +138 -8
- package/dist/plus/integrations/collectionMetadata.js.map +1 -1
- package/dist/plus/integrations/errors.d.ts +3 -1
- package/dist/plus/integrations/errors.js +7 -3
- package/dist/plus/integrations/errors.js.map +1 -1
- package/dist/plus/integrations/index.d.ts +3 -3
- package/dist/plus/integrations/index.js.map +1 -1
- package/dist/plus/integrations/integrationService.d.ts +35 -1
- package/dist/plus/integrations/integrationService.js +90 -43
- package/dist/plus/integrations/integrationService.js.map +1 -1
- package/dist/plus/integrations/lite.js +5 -4
- package/dist/plus/integrations/lite.js.map +1 -1
- package/dist/plus/integrations/manager.d.ts +68 -23
- package/dist/plus/integrations/models/gitHostIntegration.d.ts +31 -15
- package/dist/plus/integrations/models/gitHostIntegration.js +71 -22
- package/dist/plus/integrations/models/gitHostIntegration.js.map +1 -1
- package/dist/plus/integrations/models/integration.d.ts +70 -12
- package/dist/plus/integrations/models/integration.js +56 -1
- package/dist/plus/integrations/models/integration.js.map +1 -1
- package/dist/plus/integrations/models/issueReads.d.ts +17 -0
- package/dist/plus/integrations/models/issuesIntegration.js +2 -0
- package/dist/plus/integrations/models/issuesIntegration.js.map +1 -1
- package/dist/plus/integrations/models/pullRequestReads.d.ts +26 -0
- package/dist/plus/integrations/providers/azure/azure.d.ts +66 -1
- package/dist/plus/integrations/providers/azure/azure.js +192 -12
- package/dist/plus/integrations/providers/azure/azure.js.map +1 -1
- package/dist/plus/integrations/providers/azure/search.d.ts +144 -0
- package/dist/plus/integrations/providers/azure/search.js +250 -0
- package/dist/plus/integrations/providers/azure/search.js.map +1 -0
- package/dist/plus/integrations/providers/azureDevOps.d.ts +73 -36
- package/dist/plus/integrations/providers/azureDevOps.js +191 -122
- package/dist/plus/integrations/providers/azureDevOps.js.map +1 -1
- package/dist/plus/integrations/providers/azureDevOpsServer.d.ts +176 -0
- package/dist/plus/integrations/providers/azureDevOpsServer.js +560 -0
- package/dist/plus/integrations/providers/azureDevOpsServer.js.map +1 -0
- package/dist/plus/integrations/providers/bitbucket-server/discovery.d.ts +12 -0
- package/dist/plus/integrations/providers/bitbucket-server/discovery.js +128 -0
- package/dist/plus/integrations/providers/bitbucket-server/discovery.js.map +1 -0
- package/dist/plus/integrations/providers/bitbucket-server/models.d.ts +10 -1
- package/dist/plus/integrations/providers/bitbucket-server/models.js +3 -2
- package/dist/plus/integrations/providers/bitbucket-server/models.js.map +1 -1
- package/dist/plus/integrations/providers/bitbucket-server/pullRequestSearch.d.ts +58 -0
- package/dist/plus/integrations/providers/bitbucket-server/pullRequestSearch.js +485 -0
- package/dist/plus/integrations/providers/bitbucket-server/pullRequestSearch.js.map +1 -0
- package/dist/plus/integrations/providers/bitbucket-server.d.ts +61 -3
- package/dist/plus/integrations/providers/bitbucket-server.js +114 -20
- package/dist/plus/integrations/providers/bitbucket-server.js.map +1 -1
- package/dist/plus/integrations/providers/bitbucket.d.ts +9 -1
- package/dist/plus/integrations/providers/bitbucket.js +13 -2
- package/dist/plus/integrations/providers/bitbucket.js.map +1 -1
- package/dist/plus/integrations/providers/github.d.ts +6 -6
- package/dist/plus/integrations/providers/github.js +35 -34
- package/dist/plus/integrations/providers/github.js.map +1 -1
- package/dist/plus/integrations/providers/gitlab.d.ts +6 -4
- package/dist/plus/integrations/providers/gitlab.js +32 -28
- package/dist/plus/integrations/providers/gitlab.js.map +1 -1
- package/dist/plus/integrations/providers/issueSorts.d.ts +1 -0
- package/dist/plus/integrations/providers/jira-server.d.ts +9 -0
- package/dist/plus/integrations/providers/jira-server.js +15 -7
- package/dist/plus/integrations/providers/jira-server.js.map +1 -1
- package/dist/plus/integrations/providers/jira.d.ts +9 -0
- package/dist/plus/integrations/providers/jira.js +14 -2
- package/dist/plus/integrations/providers/jira.js.map +1 -1
- package/dist/plus/integrations/providers/linear.js +2 -2
- package/dist/plus/integrations/providers/models.d.ts +10 -1
- package/dist/plus/integrations/providers/models.js +86 -0
- package/dist/plus/integrations/providers/models.js.map +1 -1
- package/dist/plus/integrations/providers/providersApi.d.ts +10 -0
- package/dist/plus/integrations/providers/providersApi.js +18 -3
- package/dist/plus/integrations/providers/providersApi.js.map +1 -1
- package/dist/plus/integrations/providers/utils.d.ts +12 -1
- package/dist/plus/integrations/providers/utils.js +36 -6
- package/dist/plus/integrations/providers/utils.js.map +1 -1
- package/dist/plus/integrations/reads/broaden.js +6 -6
- package/dist/plus/integrations/reads/broaden.js.map +1 -1
- package/dist/plus/integrations/reads/context.d.ts +1 -1
- package/dist/plus/integrations/reads/counts.d.ts +18 -4
- package/dist/plus/integrations/reads/counts.js +66 -15
- package/dist/plus/integrations/reads/counts.js.map +1 -1
- package/dist/plus/integrations/reads/cursors.js +1 -1
- package/dist/plus/integrations/reads/drains.d.ts +2 -1
- package/dist/plus/integrations/reads/drains.js +1 -1
- package/dist/plus/integrations/reads/filters.d.ts +1 -1
- package/dist/plus/integrations/reads/filters.js +17 -12
- package/dist/plus/integrations/reads/filters.js.map +1 -1
- package/dist/plus/integrations/reads/hierarchy.js +2 -2
- package/dist/plus/integrations/reads/hierarchy.js.map +1 -1
- package/dist/plus/integrations/reads/hierarchy.utils.js +1 -1
- package/dist/plus/integrations/reads/issueBatch.js +1 -1
- package/dist/plus/integrations/reads/issueTracker.js +2 -2
- package/dist/plus/integrations/reads/issues.js +2 -2
- package/dist/plus/integrations/reads/paging.js +1 -1
- package/dist/plus/integrations/reads/resolveRepository.d.ts +12 -11
- package/dist/plus/integrations/reads/resolveRepository.js +110 -19
- package/dist/plus/integrations/reads/resolveRepository.js.map +1 -1
- package/dist/plus/integrations/reads/searchIssues.js +4 -2
- package/dist/plus/integrations/reads/searchIssues.js.map +1 -1
- package/dist/plus/integrations/reads/searchPullRequests.js +9 -3
- package/dist/plus/integrations/reads/searchPullRequests.js.map +1 -1
- package/dist/plus/integrations/reads/sweeps.js +1 -1
- package/dist/plus/integrations/reads/trackerIssue.d.ts +9 -5
- package/dist/plus/integrations/reads/trackerIssue.js +15 -9
- package/dist/plus/integrations/reads/trackerIssue.js.map +1 -1
- package/dist/plus/integrations/reads/warnings.d.ts +6 -2
- package/dist/plus/integrations/reads/warnings.js +15 -19
- package/dist/plus/integrations/reads/warnings.js.map +1 -1
- package/dist/plus/integrations/results.d.ts +84 -6
- package/dist/plus/integrations/results.js +9 -2
- package/dist/plus/integrations/results.js.map +1 -1
- package/dist/plus/integrations/utils/domain.utils.d.ts +19 -0
- package/dist/plus/integrations/utils/domain.utils.js +36 -1
- package/dist/plus/integrations/utils/domain.utils.js.map +1 -1
- package/dist/plus/integrations/utils/integration.utils.d.ts +1 -0
- package/dist/plus/integrations/utils/integration.utils.js +12 -1
- package/dist/plus/integrations/utils/integration.utils.js.map +1 -1
- package/docs/integrations.md +263 -74
- package/docs/kepler-read-api-parity.md +19 -6
- package/package.json +1 -1
|
@@ -78,15 +78,16 @@ function stripScheme(domain) {
|
|
|
78
78
|
* GitHub's base URL, which `@gitkraken/provider-apis` derives BOTH the REST and the GraphQL endpoint from by
|
|
79
79
|
* appending GitHub Enterprise's paths (`/api/v3`, `/api/graphql`). Cloud must therefore pass nothing rather than
|
|
80
80
|
* `https://api.github.com`, which would build `https://api.github.com/api/graphql` and get a 404; omitting it is
|
|
81
|
-
* what selects the cloud endpoints. Mirrors the cloud `GitHubIntegration.
|
|
81
|
+
* what selects the cloud endpoints. Mirrors the cloud `GitHubIntegration.apiBaseUrlFor`.
|
|
82
82
|
*/
|
|
83
83
|
function resolveGitHubApiBaseUrl(id, host) {
|
|
84
84
|
return id === "github" ? void 0 : `https://${host}/api/v3`;
|
|
85
85
|
}
|
|
86
86
|
/**
|
|
87
|
-
* Mirrors the per-provider `
|
|
88
|
-
* base URL is always concrete (GitHub's is not — see
|
|
89
|
-
* scheme (only Azure DevOps Server honors it); `host` is
|
|
87
|
+
* Mirrors the per-provider `apiBaseUrlFor` methods on the `GitHostIntegration` subclasses for an installation at
|
|
88
|
+
* the host root. Covers every family whose base URL is always concrete (GitHub's is not — see
|
|
89
|
+
* {@link resolveGitHubApiBaseUrl}). `rawDomain` may carry a scheme (only Azure DevOps Server honors it); `host` is
|
|
90
|
+
* the bare host used everywhere else.
|
|
90
91
|
*/
|
|
91
92
|
function resolveApiBaseUrl(id, rawDomain, host) {
|
|
92
93
|
switch (id) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lite.js","names":[],"sources":["../../../../plus/integrations/src/lite.ts"],"sourcesContent":["// A lightweight, token-scoped entry point for stateless single-shot provider reads.\n//\n// Unlike `createIntegrationManager`, this does NOT build an `IntegrationServiceContext`\n// (no storage/account/config/cache/repositories/hooks) and runs no session/OAuth lifecycle.\n// A consumer that already holds a provider access token (obtained by its own means, e.g.\n// the GitKraken account backend's `v1/provider-tokens/{provider}` endpoint) can call the\n// per-provider API client directly and get back the same DTOs the session-managed\n// `GitHostIntegration` returns.\n//\n// Provider notes:\n// - Azure DevOps encodes the repository as the composite string `\"{project}/_git/{repoName}\"`\n// in the `repo` argument (matching the descriptor `GitHostIntegration` uses); `owner` is the\n// organization. Pass `repo` in that shape for Azure.\n// - Bitbucket Cloud `getDefaultBranch`/`getRepositoryMetadata` depend on `mainbranch`/`parent`\n// fields the API may omit for some repos; they resolve to `undefined` when absent.\n// - Bitbucket Server (`bitbucket-server`) is not supported here — its reads use a different\n// REST surface that this token-scoped path doesn't wire up.\n\nimport type { GitHubApiConfig } from '@gitlens/git-github/api/config.js';\nimport { GitHubApi } from '@gitlens/git-github/api/github.js';\nimport type { DefaultBranch } from '@gitlens/git/models/defaultBranch.js';\nimport type { Provider } from '@gitlens/git/models/remoteProvider.js';\nimport type { RepositoryMetadata } from '@gitlens/git/models/repositoryMetadata.js';\nimport type { TokenWithInfo } from './authentication/models.js';\nimport { toTokenInfo } from './authentication/models.js';\nimport { GitCloudHostIntegrationId, GitSelfManagedHostIntegrationId } from './constants.js';\nimport type { ProviderApiConfig } from './providers/apiConfig.js';\nimport { AzureDevOpsApi } from './providers/azure/azure.js';\nimport { BitbucketApi } from './providers/bitbucket/bitbucket.js';\nimport { GitLabApi } from './providers/gitlab/gitlab.js';\nimport { providersMetadata } from './providers/models.js';\n\n/** The git-hosting integrations that expose a token-scoped read path here. */\nexport type TokenScopedGitHostId =\n\t| GitCloudHostIntegrationId.GitHub\n\t| GitCloudHostIntegrationId.GitLab\n\t| GitCloudHostIntegrationId.Bitbucket\n\t| GitCloudHostIntegrationId.AzureDevOps\n\t| GitSelfManagedHostIntegrationId.CloudGitHubEnterprise\n\t| GitSelfManagedHostIntegrationId.CloudGitLabSelfHosted\n\t| GitSelfManagedHostIntegrationId.AzureDevOpsServer;\n\nexport interface TokenScopedGitHostToken {\n\t/** The provider access token the consumer already holds. */\n\treadonly accessToken: string;\n\t/**\n\t * Host domain for self-managed instances (e.g. `git.example.com`). Ignored for cloud ids. For an Azure\n\t * DevOps Server reachable over plain HTTP, include the scheme (e.g. `http://tfs.example.com`); otherwise\n\t * `https` is assumed.\n\t */\n\treadonly domain?: string;\n\t/** Optional scopes carried by the token, surfaced only in logging metadata. */\n\treadonly scopes?: readonly string[];\n\t/** Whether the token came from a GK-cloud connection (vs a raw PAT). Defaults to `true`. */\n\treadonly cloud?: boolean;\n}\n\nexport interface TokenScopedHttpConfig {\n\t/** The `fetch` implementation to use for HTTP requests. */\n\tfetch(input: string | URL, init?: RequestInit): Promise<Response>;\n\t/**\n\t * Wrap an async op so TLS validation is disabled when `ignoreSSLErrors` is `'force'`. Optional —\n\t * defaults to a pass-through, which is correct for the browser and for any consumer not toggling SSL.\n\t */\n\twrapForForcedInsecureSSL?<T>(ignoreSSLErrors: boolean | 'force', fn: () => Promise<T> | PromiseLike<T>): Promise<T>;\n\t/** Whether to ignore SSL certificate errors on requests. Defaults to `false`. */\n\tignoreSSLErrors?: boolean | 'force';\n\t/**\n\t * Whether the consumer runs in a web worker / browser (vs Node.js). GitHub uses this to drop the\n\t * `user-agent` header, which browsers reject and warn about. Defaults to `false`.\n\t */\n\tisWeb?: boolean;\n}\n\n/** The stateless reads a token-scoped integration exposes. */\nexport interface TokenScopedGitHostIntegration {\n\tgetRepositoryMetadata(\n\t\towner: string,\n\t\trepo: string,\n\t\tcancellation?: AbortSignal,\n\t): Promise<RepositoryMetadata | undefined>;\n\tgetDefaultBranch(owner: string, repo: string, cancellation?: AbortSignal): Promise<DefaultBranch | undefined>;\n}\n\n/**\n * Build a token-scoped git-host integration for a single provider, backed only by a `fetch` + a token —\n * no `IntegrationServiceContext`, no session/OAuth lifecycle. The returned DTOs match those from the\n * session-managed {@link GitHostIntegration}.\n */\nexport function createTokenScopedGitHostIntegration(\n\tid: TokenScopedGitHostId,\n\ttoken: TokenScopedGitHostToken,\n\thttp: TokenScopedHttpConfig,\n): TokenScopedGitHostIntegration {\n\t// `token.domain` only applies to self-managed ids; cloud ids always use their canonical domain.\n\tconst selfManaged =\n\t\tid === GitSelfManagedHostIntegrationId.CloudGitHubEnterprise ||\n\t\tid === GitSelfManagedHostIntegrationId.CloudGitLabSelfHosted ||\n\t\tid === GitSelfManagedHostIntegrationId.AzureDevOpsServer;\n\t// Self-managed ids carry no canonical domain (`providersMetadata[id].domain` is empty), so a missing\n\t// `token.domain` would silently build a malformed base URL (e.g. `https:///api/v3`). Fail fast instead.\n\tif (selfManaged && !token.domain) {\n\t\tthrow new Error(`A token-scoped integration for the self-managed host '${id}' requires 'token.domain'.`);\n\t}\n\n\tconst rawDomain = (selfManaged && token.domain) || providersMetadata[id].domain;\n\t// The provider/DTO domain drops the scheme (a subpath/collection is preserved for Azure Server and subpath\n\t// installs); only Azure DevOps Server's base URL honors an explicit scheme (per its doc). Stripping keeps a\n\t// scheme out of the DTO and prevents a malformed base URL (e.g. `https://http://host/api/v3`) if one is\n\t// accidentally passed for GHE/GitLab.\n\tconst host = stripScheme(rawDomain);\n\tconst provider = createProviderStub(id, host, http.ignoreSSLErrors ?? false);\n\tconst tokenInfo = buildTokenWithInfo(id, token);\n\n\tconst wrapForForcedInsecureSSL: NonNullable<TokenScopedHttpConfig['wrapForForcedInsecureSSL']> =\n\t\t// `Promise.resolve().then(fn)` (not `Promise.resolve(fn())`) so a synchronous throw in `fn` surfaces as\n\t\t// a rejected promise, honoring the `Promise<T>` contract.\n\t\thttp.wrapForForcedInsecureSSL ?? ((_ignore, fn) => Promise.resolve().then(fn));\n\n\t// GitHub has its own VS Code-free config type and a `getDefaultBranch` without a cancellation param.\n\tif (id === GitCloudHostIntegrationId.GitHub || id === GitSelfManagedHostIntegrationId.CloudGitHubEnterprise) {\n\t\tconst config: GitHubApiConfig = {\n\t\t\tisWeb: http.isWeb ?? false,\n\t\t\tfetch: (url, init) => http.fetch(url, init),\n\t\t\twrapForForcedInsecureSSL: wrapForForcedInsecureSSL,\n\t\t};\n\t\tconst api = new GitHubApi(config);\n\t\t// GitHub is the one family whose base URL can legitimately be absent — see `resolveGitHubApiBaseUrl`.\n\t\tconst baseUrl = resolveGitHubApiBaseUrl(id, host);\n\t\treturn {\n\t\t\tgetRepositoryMetadata: (owner, repo, cancellation) =>\n\t\t\t\tapi.getRepositoryMetadata(provider, tokenInfo, owner, repo, { baseUrl: baseUrl }, cancellation),\n\t\t\t// `GitHubApi.getDefaultBranch` has no cancellation param; accept and intentionally ignore it here so\n\t\t\t// the signature matches the other providers and the drop is explicit at the call site.\n\t\t\tgetDefaultBranch: (owner, repo, _cancellation) =>\n\t\t\t\tapi.getDefaultBranch(provider, tokenInfo, owner, repo, { baseUrl: baseUrl }),\n\t\t};\n\t}\n\n\t// GitLab, Bitbucket, and Azure DevOps share the `ProviderApiConfig` client shape and identical read signatures.\n\tconst baseUrl = resolveApiBaseUrl(id, rawDomain, host);\n\tconst config = toProviderApiConfig(http, wrapForForcedInsecureSSL);\n\tconst api =\n\t\tid === GitCloudHostIntegrationId.GitLab || id === GitSelfManagedHostIntegrationId.CloudGitLabSelfHosted\n\t\t\t? new GitLabApi(config)\n\t\t\t: id === GitCloudHostIntegrationId.Bitbucket\n\t\t\t\t? new BitbucketApi(config)\n\t\t\t\t: new AzureDevOpsApi(config);\n\treturn {\n\t\tgetRepositoryMetadata: (owner, repo, cancellation) =>\n\t\t\tapi.getRepositoryMetadata(provider, tokenInfo, owner, repo, { baseUrl: baseUrl }, cancellation),\n\t\tgetDefaultBranch: (owner, repo, cancellation) =>\n\t\t\tapi.getDefaultBranch(provider, tokenInfo, owner, repo, { baseUrl: baseUrl }, cancellation),\n\t};\n}\n\nfunction toProviderApiConfig(\n\thttp: TokenScopedHttpConfig,\n\twrapForForcedInsecureSSL: NonNullable<TokenScopedHttpConfig['wrapForForcedInsecureSSL']>,\n): ProviderApiConfig {\n\treturn {\n\t\tfetch: (input, init) => http.fetch(input, init),\n\t\twrapForForcedInsecureSSL: wrapForForcedInsecureSSL,\n\t};\n}\n\nfunction buildTokenWithInfo(id: TokenScopedGitHostId, token: TokenScopedGitHostToken): TokenWithInfo {\n\tconst cloud = token.cloud ?? true;\n\treturn {\n\t\t...toTokenInfo(id, token.accessToken, { cloud: cloud, type: undefined, scopes: token.scopes }),\n\t\taccessToken: token.accessToken,\n\t};\n}\n\n/** A minimal {@link Provider} for the API clients: identity + no-op reauth/telemetry, SSL from config. */\nfunction createProviderStub(id: TokenScopedGitHostId, domain: string, ignoreSSLErrors: boolean | 'force'): Provider {\n\tconst metadata = providersMetadata[id];\n\treturn {\n\t\tid: id,\n\t\tname: metadata.name,\n\t\tdomain: domain,\n\t\ticon: metadata.iconKey,\n\t\tgetIgnoreSSLErrors: () => ignoreSSLErrors,\n\t\treauthenticate: () => Promise.resolve(),\n\t\ttrackRequestException: () => {},\n\t};\n}\n\n/** Strips a leading `scheme://` and any trailing slashes; a subpath/collection (e.g. `host/tfs/Col`) is preserved. */\nfunction stripScheme(domain: string): string {\n\treturn domain.replace(/^[a-z][\\w+.-]*:\\/\\//i, '').replace(/\\/+$/, '');\n}\n\n/**\n * GitHub's base URL, which `@gitkraken/provider-apis` derives BOTH the REST and the GraphQL endpoint from by\n * appending GitHub Enterprise's paths (`/api/v3`, `/api/graphql`). Cloud must therefore pass nothing rather than\n * `https://api.github.com`, which would build `https://api.github.com/api/graphql` and get a 404; omitting it is\n * what selects the cloud endpoints. Mirrors the cloud `GitHubIntegration.apiBaseUrl` getter.\n */\nfunction resolveGitHubApiBaseUrl(\n\tid: GitCloudHostIntegrationId.GitHub | GitSelfManagedHostIntegrationId.CloudGitHubEnterprise,\n\thost: string,\n): string | undefined {\n\treturn id === GitCloudHostIntegrationId.GitHub ? undefined : `https://${host}/api/v3`;\n}\n\n/**\n * Mirrors the per-provider `apiBaseUrl` getters on the `GitHostIntegration` subclasses, for every family whose\n * base URL is always concrete (GitHub's is not — see {@link resolveGitHubApiBaseUrl}). `rawDomain` may carry a\n * scheme (only Azure DevOps Server honors it); `host` is the bare host used everywhere else.\n */\nfunction resolveApiBaseUrl(\n\tid: Exclude<\n\t\tTokenScopedGitHostId,\n\t\tGitCloudHostIntegrationId.GitHub | GitSelfManagedHostIntegrationId.CloudGitHubEnterprise\n\t>,\n\trawDomain: string,\n\thost: string,\n): string {\n\tswitch (id) {\n\t\tcase GitCloudHostIntegrationId.GitLab:\n\t\t\treturn 'https://gitlab.com/api';\n\t\tcase GitSelfManagedHostIntegrationId.CloudGitLabSelfHosted:\n\t\t\treturn `https://${host}/api`;\n\t\tcase GitCloudHostIntegrationId.Bitbucket:\n\t\t\treturn 'https://api.bitbucket.org/2.0';\n\t\tcase GitCloudHostIntegrationId.AzureDevOps:\n\t\t\treturn 'https://dev.azure.com';\n\t\tcase GitSelfManagedHostIntegrationId.AzureDevOpsServer:\n\t\t\t// Mirror the session-managed getter, which honors the connection protocol; default to https.\n\t\t\treturn rawDomain.includes('://') ? rawDomain.replace(/\\/+$/, '') : `https://${host}`;\n\t}\n}\n"],"mappings":";;;;;;;;;;;;;AAyFA,SAAgB,oCACf,IACA,OACA,MACgC;CAEhC,MAAM,cACL,OAAA,6BACA,OAAA,8BACA,OAAA;CAGD,IAAI,eAAe,CAAC,MAAM,QACzB,MAAM,IAAI,MAAM,yDAAyD,GAAG,2BAA2B;CAGxG,MAAM,YAAa,eAAe,MAAM,UAAW,kBAAkB,GAAG,CAAC;CAKzE,MAAM,OAAO,YAAY,SAAS;CAClC,MAAM,WAAW,mBAAmB,IAAI,MAAM,KAAK,mBAAmB,KAAK;CAC3E,MAAM,YAAY,mBAAmB,IAAI,KAAK;CAE9C,MAAM,2BAGL,KAAK,8BAA8B,SAAS,OAAO,QAAQ,QAAQ,CAAC,CAAC,KAAK,EAAE;CAG7E,IAAI,OAAA,YAA2C,OAAA,2BAA8D;EAC5G,MAAM,SAA0B;GAC/B,OAAO,KAAK,SAAS;GACrB,QAAQ,KAAK,SAAS,KAAK,MAAM,KAAK,IAAI;GAChB;EAC3B;EACA,MAAM,MAAM,IAAI,UAAU,MAAM;EAEhC,MAAM,UAAU,wBAAwB,IAAI,IAAI;EAChD,OAAO;GACN,wBAAwB,OAAO,MAAM,iBACpC,IAAI,sBAAsB,UAAU,WAAW,OAAO,MAAM,EAAW,QAAQ,GAAG,YAAY;GAG/F,mBAAmB,OAAO,MAAM,kBAC/B,IAAI,iBAAiB,UAAU,WAAW,OAAO,MAAM,EAAW,QAAQ,CAAC;EAC7E;CACD;CAGA,MAAM,UAAU,kBAAkB,IAAI,WAAW,IAAI;CACrD,MAAM,SAAS,oBAAoB,MAAM,wBAAwB;CACjE,MAAM,MACL,OAAA,YAA2C,OAAA,6BACxC,IAAI,UAAU,MAAM,IACpB,OAAA,cACC,IAAI,aAAa,MAAM,IACvB,IAAI,eAAe,MAAM;CAC9B,OAAO;EACN,wBAAwB,OAAO,MAAM,iBACpC,IAAI,sBAAsB,UAAU,WAAW,OAAO,MAAM,EAAW,QAAQ,GAAG,YAAY;EAC/F,mBAAmB,OAAO,MAAM,iBAC/B,IAAI,iBAAiB,UAAU,WAAW,OAAO,MAAM,EAAW,QAAQ,GAAG,YAAY;CAC3F;AACD;AAEA,SAAS,oBACR,MACA,0BACoB;CACpB,OAAO;EACN,QAAQ,OAAO,SAAS,KAAK,MAAM,OAAO,IAAI;EACpB;CAC3B;AACD;AAEA,SAAS,mBAAmB,IAA0B,OAA+C;CACpG,MAAM,QAAQ,MAAM,SAAS;CAC7B,OAAO;EACN,GAAG,YAAY,IAAI,MAAM,aAAa;GAAS;GAAO,MAAM,KAAA;GAAW,QAAQ,MAAM;EAAO,CAAC;EAC7F,aAAa,MAAM;CACpB;AACD;;AAGA,SAAS,mBAAmB,IAA0B,QAAgB,iBAA8C;CACnH,MAAM,WAAW,kBAAkB;CACnC,OAAO;EACF;EACJ,MAAM,SAAS;EACP;EACR,MAAM,SAAS;EACf,0BAA0B;EAC1B,sBAAsB,QAAQ,QAAQ;EACtC,6BAA6B,CAAC;CAC/B;AACD;;AAGA,SAAS,YAAY,QAAwB;CAC5C,OAAO,OAAO,QAAQ,wBAAwB,EAAE,CAAC,CAAC,QAAQ,QAAQ,EAAE;AACrE;;;;;;;AAQA,SAAS,wBACR,IACA,MACqB;CACrB,OAAO,OAAA,WAA0C,KAAA,IAAY,WAAW,KAAK;AAC9E;;;;;;AAOA,SAAS,kBACR,IAIA,WACA,MACS;CACT,QAAQ,IAAR;EACC,KAAA,UACC,OAAO;EACR,KAAA,4BACC,OAAO,WAAW,KAAK;EACxB,KAAA,aACC,OAAO;EACR,KAAA,eACC,OAAO;EACR,KAAA,uBAEC,OAAO,UAAU,SAAS,KAAK,IAAI,UAAU,QAAQ,QAAQ,EAAE,IAAI,WAAW;CAChF;AACD"}
|
|
1
|
+
{"version":3,"file":"lite.js","names":[],"sources":["../../../../plus/integrations/src/lite.ts"],"sourcesContent":["// A lightweight, token-scoped entry point for stateless single-shot provider reads.\n//\n// Unlike `createIntegrationManager`, this does NOT build an `IntegrationServiceContext`\n// (no storage/account/config/cache/repositories/hooks) and runs no session/OAuth lifecycle.\n// A consumer that already holds a provider access token (obtained by its own means, e.g.\n// the GitKraken account backend's `v1/provider-tokens/{provider}` endpoint) can call the\n// per-provider API client directly and get back the same DTOs the session-managed\n// `GitHostIntegration` returns.\n//\n// Provider notes:\n// - Azure DevOps encodes the repository as the composite string `\"{project}/_git/{repoName}\"`\n// in the `repo` argument (matching the descriptor `GitHostIntegration` uses); `owner` is the\n// organization. Pass `repo` in that shape for Azure.\n// - Bitbucket Cloud `getDefaultBranch`/`getRepositoryMetadata` depend on `mainbranch`/`parent`\n// fields the API may omit for some repos; they resolve to `undefined` when absent.\n// - Bitbucket Server (`bitbucket-server`) is not supported here — its reads use a different\n// REST surface that this token-scoped path doesn't wire up.\n\nimport type { GitHubApiConfig } from '@gitlens/git-github/api/config.js';\nimport { GitHubApi } from '@gitlens/git-github/api/github.js';\nimport type { DefaultBranch } from '@gitlens/git/models/defaultBranch.js';\nimport type { Provider } from '@gitlens/git/models/remoteProvider.js';\nimport type { RepositoryMetadata } from '@gitlens/git/models/repositoryMetadata.js';\nimport type { TokenWithInfo } from './authentication/models.js';\nimport { toTokenInfo } from './authentication/models.js';\nimport { GitCloudHostIntegrationId, GitSelfManagedHostIntegrationId } from './constants.js';\nimport type { ProviderApiConfig } from './providers/apiConfig.js';\nimport { AzureDevOpsApi } from './providers/azure/azure.js';\nimport { BitbucketApi } from './providers/bitbucket/bitbucket.js';\nimport { GitLabApi } from './providers/gitlab/gitlab.js';\nimport { providersMetadata } from './providers/models.js';\n\n/** The git-hosting integrations that expose a token-scoped read path here. */\nexport type TokenScopedGitHostId =\n\t| GitCloudHostIntegrationId.GitHub\n\t| GitCloudHostIntegrationId.GitLab\n\t| GitCloudHostIntegrationId.Bitbucket\n\t| GitCloudHostIntegrationId.AzureDevOps\n\t| GitSelfManagedHostIntegrationId.CloudGitHubEnterprise\n\t| GitSelfManagedHostIntegrationId.CloudGitLabSelfHosted\n\t| GitSelfManagedHostIntegrationId.AzureDevOpsServer;\n\nexport interface TokenScopedGitHostToken {\n\t/** The provider access token the consumer already holds. */\n\treadonly accessToken: string;\n\t/**\n\t * Host domain for self-managed instances (e.g. `git.example.com`). Ignored for cloud ids. For an Azure\n\t * DevOps Server reachable over plain HTTP, include the scheme (e.g. `http://tfs.example.com`); otherwise\n\t * `https` is assumed.\n\t */\n\treadonly domain?: string;\n\t/** Optional scopes carried by the token, surfaced only in logging metadata. */\n\treadonly scopes?: readonly string[];\n\t/** Whether the token came from a GK-cloud connection (vs a raw PAT). Defaults to `true`. */\n\treadonly cloud?: boolean;\n}\n\nexport interface TokenScopedHttpConfig {\n\t/** The `fetch` implementation to use for HTTP requests. */\n\tfetch(input: string | URL, init?: RequestInit): Promise<Response>;\n\t/**\n\t * Wrap an async op so TLS validation is disabled when `ignoreSSLErrors` is `'force'`. Optional —\n\t * defaults to a pass-through, which is correct for the browser and for any consumer not toggling SSL.\n\t */\n\twrapForForcedInsecureSSL?<T>(ignoreSSLErrors: boolean | 'force', fn: () => Promise<T> | PromiseLike<T>): Promise<T>;\n\t/** Whether to ignore SSL certificate errors on requests. Defaults to `false`. */\n\tignoreSSLErrors?: boolean | 'force';\n\t/**\n\t * Whether the consumer runs in a web worker / browser (vs Node.js). GitHub uses this to drop the\n\t * `user-agent` header, which browsers reject and warn about. Defaults to `false`.\n\t */\n\tisWeb?: boolean;\n}\n\n/** The stateless reads a token-scoped integration exposes. */\nexport interface TokenScopedGitHostIntegration {\n\tgetRepositoryMetadata(\n\t\towner: string,\n\t\trepo: string,\n\t\tcancellation?: AbortSignal,\n\t): Promise<RepositoryMetadata | undefined>;\n\tgetDefaultBranch(owner: string, repo: string, cancellation?: AbortSignal): Promise<DefaultBranch | undefined>;\n}\n\n/**\n * Build a token-scoped git-host integration for a single provider, backed only by a `fetch` + a token —\n * no `IntegrationServiceContext`, no session/OAuth lifecycle. The returned DTOs match those from the\n * session-managed {@link GitHostIntegration}.\n */\nexport function createTokenScopedGitHostIntegration(\n\tid: TokenScopedGitHostId,\n\ttoken: TokenScopedGitHostToken,\n\thttp: TokenScopedHttpConfig,\n): TokenScopedGitHostIntegration {\n\t// `token.domain` only applies to self-managed ids; cloud ids always use their canonical domain.\n\tconst selfManaged =\n\t\tid === GitSelfManagedHostIntegrationId.CloudGitHubEnterprise ||\n\t\tid === GitSelfManagedHostIntegrationId.CloudGitLabSelfHosted ||\n\t\tid === GitSelfManagedHostIntegrationId.AzureDevOpsServer;\n\t// Self-managed ids carry no canonical domain (`providersMetadata[id].domain` is empty), so a missing\n\t// `token.domain` would silently build a malformed base URL (e.g. `https:///api/v3`). Fail fast instead.\n\tif (selfManaged && !token.domain) {\n\t\tthrow new Error(`A token-scoped integration for the self-managed host '${id}' requires 'token.domain'.`);\n\t}\n\n\tconst rawDomain = (selfManaged && token.domain) || providersMetadata[id].domain;\n\t// The provider/DTO domain drops the scheme (a subpath/collection is preserved for Azure Server and subpath\n\t// installs); only Azure DevOps Server's base URL honors an explicit scheme (per its doc). Stripping keeps a\n\t// scheme out of the DTO and prevents a malformed base URL (e.g. `https://http://host/api/v3`) if one is\n\t// accidentally passed for GHE/GitLab.\n\tconst host = stripScheme(rawDomain);\n\tconst provider = createProviderStub(id, host, http.ignoreSSLErrors ?? false);\n\tconst tokenInfo = buildTokenWithInfo(id, token);\n\n\tconst wrapForForcedInsecureSSL: NonNullable<TokenScopedHttpConfig['wrapForForcedInsecureSSL']> =\n\t\t// `Promise.resolve().then(fn)` (not `Promise.resolve(fn())`) so a synchronous throw in `fn` surfaces as\n\t\t// a rejected promise, honoring the `Promise<T>` contract.\n\t\thttp.wrapForForcedInsecureSSL ?? ((_ignore, fn) => Promise.resolve().then(fn));\n\n\t// GitHub has its own VS Code-free config type and a `getDefaultBranch` without a cancellation param.\n\tif (id === GitCloudHostIntegrationId.GitHub || id === GitSelfManagedHostIntegrationId.CloudGitHubEnterprise) {\n\t\tconst config: GitHubApiConfig = {\n\t\t\tisWeb: http.isWeb ?? false,\n\t\t\tfetch: (url, init) => http.fetch(url, init),\n\t\t\twrapForForcedInsecureSSL: wrapForForcedInsecureSSL,\n\t\t};\n\t\tconst api = new GitHubApi(config);\n\t\t// GitHub is the one family whose base URL can legitimately be absent — see `resolveGitHubApiBaseUrl`.\n\t\tconst baseUrl = resolveGitHubApiBaseUrl(id, host);\n\t\treturn {\n\t\t\tgetRepositoryMetadata: (owner, repo, cancellation) =>\n\t\t\t\tapi.getRepositoryMetadata(provider, tokenInfo, owner, repo, { baseUrl: baseUrl }, cancellation),\n\t\t\t// `GitHubApi.getDefaultBranch` has no cancellation param; accept and intentionally ignore it here so\n\t\t\t// the signature matches the other providers and the drop is explicit at the call site.\n\t\t\tgetDefaultBranch: (owner, repo, _cancellation) =>\n\t\t\t\tapi.getDefaultBranch(provider, tokenInfo, owner, repo, { baseUrl: baseUrl }),\n\t\t};\n\t}\n\n\t// GitLab, Bitbucket, and Azure DevOps share the `ProviderApiConfig` client shape and identical read signatures.\n\tconst baseUrl = resolveApiBaseUrl(id, rawDomain, host);\n\tconst config = toProviderApiConfig(http, wrapForForcedInsecureSSL);\n\tconst api =\n\t\tid === GitCloudHostIntegrationId.GitLab || id === GitSelfManagedHostIntegrationId.CloudGitLabSelfHosted\n\t\t\t? new GitLabApi(config)\n\t\t\t: id === GitCloudHostIntegrationId.Bitbucket\n\t\t\t\t? new BitbucketApi(config)\n\t\t\t\t: new AzureDevOpsApi(config);\n\treturn {\n\t\tgetRepositoryMetadata: (owner, repo, cancellation) =>\n\t\t\tapi.getRepositoryMetadata(provider, tokenInfo, owner, repo, { baseUrl: baseUrl }, cancellation),\n\t\tgetDefaultBranch: (owner, repo, cancellation) =>\n\t\t\tapi.getDefaultBranch(provider, tokenInfo, owner, repo, { baseUrl: baseUrl }, cancellation),\n\t};\n}\n\nfunction toProviderApiConfig(\n\thttp: TokenScopedHttpConfig,\n\twrapForForcedInsecureSSL: NonNullable<TokenScopedHttpConfig['wrapForForcedInsecureSSL']>,\n): ProviderApiConfig {\n\treturn {\n\t\tfetch: (input, init) => http.fetch(input, init),\n\t\twrapForForcedInsecureSSL: wrapForForcedInsecureSSL,\n\t};\n}\n\nfunction buildTokenWithInfo(id: TokenScopedGitHostId, token: TokenScopedGitHostToken): TokenWithInfo {\n\tconst cloud = token.cloud ?? true;\n\treturn {\n\t\t...toTokenInfo(id, token.accessToken, { cloud: cloud, type: undefined, scopes: token.scopes }),\n\t\taccessToken: token.accessToken,\n\t};\n}\n\n/** A minimal {@link Provider} for the API clients: identity + no-op reauth/telemetry, SSL from config. */\nfunction createProviderStub(id: TokenScopedGitHostId, domain: string, ignoreSSLErrors: boolean | 'force'): Provider {\n\tconst metadata = providersMetadata[id];\n\treturn {\n\t\tid: id,\n\t\tname: metadata.name,\n\t\tdomain: domain,\n\t\ticon: metadata.iconKey,\n\t\tgetIgnoreSSLErrors: () => ignoreSSLErrors,\n\t\treauthenticate: () => Promise.resolve(),\n\t\ttrackRequestException: () => {},\n\t};\n}\n\n/** Strips a leading `scheme://` and any trailing slashes; a subpath/collection (e.g. `host/tfs/Col`) is preserved. */\nfunction stripScheme(domain: string): string {\n\treturn domain.replace(/^[a-z][\\w+.-]*:\\/\\//i, '').replace(/\\/+$/, '');\n}\n\n/**\n * GitHub's base URL, which `@gitkraken/provider-apis` derives BOTH the REST and the GraphQL endpoint from by\n * appending GitHub Enterprise's paths (`/api/v3`, `/api/graphql`). Cloud must therefore pass nothing rather than\n * `https://api.github.com`, which would build `https://api.github.com/api/graphql` and get a 404; omitting it is\n * what selects the cloud endpoints. Mirrors the cloud `GitHubIntegration.apiBaseUrlFor`.\n */\nfunction resolveGitHubApiBaseUrl(\n\tid: GitCloudHostIntegrationId.GitHub | GitSelfManagedHostIntegrationId.CloudGitHubEnterprise,\n\thost: string,\n): string | undefined {\n\treturn id === GitCloudHostIntegrationId.GitHub ? undefined : `https://${host}/api/v3`;\n}\n\n/**\n * Mirrors the per-provider `apiBaseUrlFor` methods on the `GitHostIntegration` subclasses for an installation at\n * the host root. Covers every family whose base URL is always concrete (GitHub's is not — see\n * {@link resolveGitHubApiBaseUrl}). `rawDomain` may carry a scheme (only Azure DevOps Server honors it); `host` is\n * the bare host used everywhere else.\n */\nfunction resolveApiBaseUrl(\n\tid: Exclude<\n\t\tTokenScopedGitHostId,\n\t\tGitCloudHostIntegrationId.GitHub | GitSelfManagedHostIntegrationId.CloudGitHubEnterprise\n\t>,\n\trawDomain: string,\n\thost: string,\n): string {\n\tswitch (id) {\n\t\tcase GitCloudHostIntegrationId.GitLab:\n\t\t\treturn 'https://gitlab.com/api';\n\t\tcase GitSelfManagedHostIntegrationId.CloudGitLabSelfHosted:\n\t\t\treturn `https://${host}/api`;\n\t\tcase GitCloudHostIntegrationId.Bitbucket:\n\t\t\treturn 'https://api.bitbucket.org/2.0';\n\t\tcase GitCloudHostIntegrationId.AzureDevOps:\n\t\t\treturn 'https://dev.azure.com';\n\t\tcase GitSelfManagedHostIntegrationId.AzureDevOpsServer:\n\t\t\t// Mirror the session-managed getter, which honors the connection protocol; default to https.\n\t\t\treturn rawDomain.includes('://') ? rawDomain.replace(/\\/+$/, '') : `https://${host}`;\n\t}\n}\n"],"mappings":";;;;;;;;;;;;;AAyFA,SAAgB,oCACf,IACA,OACA,MACgC;CAEhC,MAAM,cACL,OAAA,6BACA,OAAA,8BACA,OAAA;CAGD,IAAI,eAAe,CAAC,MAAM,QACzB,MAAM,IAAI,MAAM,yDAAyD,GAAG,2BAA2B;CAGxG,MAAM,YAAa,eAAe,MAAM,UAAW,kBAAkB,GAAG,CAAC;CAKzE,MAAM,OAAO,YAAY,SAAS;CAClC,MAAM,WAAW,mBAAmB,IAAI,MAAM,KAAK,mBAAmB,KAAK;CAC3E,MAAM,YAAY,mBAAmB,IAAI,KAAK;CAE9C,MAAM,2BAGL,KAAK,8BAA8B,SAAS,OAAO,QAAQ,QAAQ,CAAC,CAAC,KAAK,EAAE;CAG7E,IAAI,OAAA,YAA2C,OAAA,2BAA8D;EAC5G,MAAM,SAA0B;GAC/B,OAAO,KAAK,SAAS;GACrB,QAAQ,KAAK,SAAS,KAAK,MAAM,KAAK,IAAI;GAChB;EAC3B;EACA,MAAM,MAAM,IAAI,UAAU,MAAM;EAEhC,MAAM,UAAU,wBAAwB,IAAI,IAAI;EAChD,OAAO;GACN,wBAAwB,OAAO,MAAM,iBACpC,IAAI,sBAAsB,UAAU,WAAW,OAAO,MAAM,EAAW,QAAQ,GAAG,YAAY;GAG/F,mBAAmB,OAAO,MAAM,kBAC/B,IAAI,iBAAiB,UAAU,WAAW,OAAO,MAAM,EAAW,QAAQ,CAAC;EAC7E;CACD;CAGA,MAAM,UAAU,kBAAkB,IAAI,WAAW,IAAI;CACrD,MAAM,SAAS,oBAAoB,MAAM,wBAAwB;CACjE,MAAM,MACL,OAAA,YAA2C,OAAA,6BACxC,IAAI,UAAU,MAAM,IACpB,OAAA,cACC,IAAI,aAAa,MAAM,IACvB,IAAI,eAAe,MAAM;CAC9B,OAAO;EACN,wBAAwB,OAAO,MAAM,iBACpC,IAAI,sBAAsB,UAAU,WAAW,OAAO,MAAM,EAAW,QAAQ,GAAG,YAAY;EAC/F,mBAAmB,OAAO,MAAM,iBAC/B,IAAI,iBAAiB,UAAU,WAAW,OAAO,MAAM,EAAW,QAAQ,GAAG,YAAY;CAC3F;AACD;AAEA,SAAS,oBACR,MACA,0BACoB;CACpB,OAAO;EACN,QAAQ,OAAO,SAAS,KAAK,MAAM,OAAO,IAAI;EACpB;CAC3B;AACD;AAEA,SAAS,mBAAmB,IAA0B,OAA+C;CACpG,MAAM,QAAQ,MAAM,SAAS;CAC7B,OAAO;EACN,GAAG,YAAY,IAAI,MAAM,aAAa;GAAS;GAAO,MAAM,KAAA;GAAW,QAAQ,MAAM;EAAO,CAAC;EAC7F,aAAa,MAAM;CACpB;AACD;;AAGA,SAAS,mBAAmB,IAA0B,QAAgB,iBAA8C;CACnH,MAAM,WAAW,kBAAkB;CACnC,OAAO;EACF;EACJ,MAAM,SAAS;EACP;EACR,MAAM,SAAS;EACf,0BAA0B;EAC1B,sBAAsB,QAAQ,QAAQ;EACtC,6BAA6B,CAAC;CAC/B;AACD;;AAGA,SAAS,YAAY,QAAwB;CAC5C,OAAO,OAAO,QAAQ,wBAAwB,EAAE,CAAC,CAAC,QAAQ,QAAQ,EAAE;AACrE;;;;;;;AAQA,SAAS,wBACR,IACA,MACqB;CACrB,OAAO,OAAA,WAA0C,KAAA,IAAY,WAAW,KAAK;AAC9E;;;;;;;AAQA,SAAS,kBACR,IAIA,WACA,MACS;CACT,QAAQ,IAAR;EACC,KAAA,UACC,OAAO;EACR,KAAA,4BACC,OAAO,WAAW,KAAK;EACxB,KAAA,aACC,OAAO;EACR,KAAA,eACC,OAAO;EACR,KAAA,uBAEC,OAAO,UAAU,SAAS,KAAK,IAAI,UAAU,QAAQ,QAAQ,EAAE,IAAI,WAAW;CAChF;AACD"}
|
|
@@ -343,7 +343,8 @@ export interface IntegrationManager {
|
|
|
343
343
|
* scope.
|
|
344
344
|
*
|
|
345
345
|
* This is a separate read from {@link listPullRequestsPage}: free text reaches the provider instead of filtering
|
|
346
|
-
* whichever rows happened to be loaded, and every cursor-threaded page costs exactly one upstream
|
|
346
|
+
* whichever rows happened to be loaded, and on GitHub/GHE every cursor-threaded page costs exactly one upstream
|
|
347
|
+
* request (Bitbucket Data Center spends one per repository × relationship facet it is still reading).
|
|
347
348
|
* `criteria.relationships` and `criteria.states` are OR sets, so `[closed, merged]` expresses the complete
|
|
348
349
|
* terminal set and `[Author, Assignee, ReviewRequested]` matches the visible PR list without falling back to
|
|
349
350
|
* GitHub's mismatched `involves:@me`. Omit relationships only with a repository/organization scope to search
|
|
@@ -353,14 +354,24 @@ export interface IntegrationManager {
|
|
|
353
354
|
* criterion — a key not in `getSupportedFilters().pullRequestSearch.sorts` refuses the whole read rather than
|
|
354
355
|
* falling back, because at a bounded result window another order reaches another subset. The page is a union of
|
|
355
356
|
* the provider's relationship × state facets, so it is re-ordered as a whole rather than served as concatenated
|
|
356
|
-
* per-facet runs.
|
|
357
|
-
*
|
|
357
|
+
* per-facet runs. The order holds within a page, not across pages: each facet advances by its own continuation, so
|
|
358
|
+
* a later page can hold a row that sorts before the last row of an earlier one — sort the accumulated rows when a
|
|
359
|
+
* surface shows several pages as one list. Changing the sort invalidates a threaded cursor (it is part of the
|
|
360
|
+
* cursor's fingerprint, alongside the text and the scope); drop the cursor when you change the order.
|
|
358
361
|
*
|
|
359
362
|
* If the provider's result ceiling is reached, the request still succeeds and carries a warning omission with
|
|
360
363
|
* `totalCount`, `limit`, `sort`, and `recovery: 'none'`. `totalCount` is the largest provider-reported
|
|
361
364
|
* pre-ceiling facet count, not the reachable or returned row count; this mirrors the per-search ceiling's own
|
|
362
365
|
* unit.
|
|
363
366
|
*
|
|
367
|
+
* **Azure DevOps Server** differs in how it gets there, because its pull request API can't filter by text, date or
|
|
368
|
+
* draft: the first page drains every facet (up to 1,000 rows each) and later pages are served from that drain,
|
|
369
|
+
* so a first page can cost many requests and a continuation none. A continuation whose drain expired is refused;
|
|
370
|
+
* restart without the cursor. `itemsPerPage` sizes the whole page, not a
|
|
371
|
+
* facet. Past a facet's drain bound the page is `truncated` with no `totalCount`. Scope names with spaces or
|
|
372
|
+
* quotes are accepted there, since they reach the provider as encoded URL segments. See
|
|
373
|
+
* `docs/integrations.md` §9.
|
|
374
|
+
*
|
|
364
375
|
* Check `getSupportedFilters().pullRequestSearch` before calling. A provider that reports no relationships
|
|
365
376
|
* refuses the read rather than returning a page that never honored the criteria or scope.
|
|
366
377
|
*/
|
|
@@ -378,7 +389,8 @@ export interface IntegrationManager {
|
|
|
378
389
|
*
|
|
379
390
|
* Held to a STRICTER rule than the free-form `criteria.text`, which is sanitized: a scope name carrying a
|
|
380
391
|
* quote, or an inner space or control character, is REFUSED (warning + `fetchFailed`), and the refusal
|
|
381
|
-
* names the value
|
|
392
|
+
* names the value — except on a provider whose scope names never enter a query string (Azure DevOps
|
|
393
|
+
* Server), where only blank names and control characters are. Leading and trailing whitespace and control characters are stripped and accepted,
|
|
382
394
|
* since removing them cannot change which scope the query names. Sanitizing a scope would answer the wrong question — the sanitized value may name a real but
|
|
383
395
|
* DIFFERENT organization, whose result looks entirely normal. Pass the name exactly as the provider spells
|
|
384
396
|
* it; `''` means "no org supplied" and falls through to the other scopes.
|
|
@@ -394,7 +406,8 @@ export interface IntegrationManager {
|
|
|
394
406
|
* 2-state search returns up to `6 × itemsPerPage` items before deduplication, and fewer than that where
|
|
395
407
|
* the facets overlap. Deduplication does NOT bring the page back to this size: it removes only the rows the
|
|
396
408
|
* facets share. `page.itemsPerPage` reports what actually came back, so size the UI off that rather than
|
|
397
|
-
* off this. A provider may also cap it below what is asked for.
|
|
409
|
+
* off this. A provider may also cap it below what is asked for. Azure DevOps Server is the exception: it
|
|
410
|
+
* serves one merged page, so there this is the page size (at most 100).
|
|
398
411
|
*/
|
|
399
412
|
itemsPerPage?: number;
|
|
400
413
|
forceSync?: boolean;
|
|
@@ -497,7 +510,15 @@ export interface IntegrationManager {
|
|
|
497
510
|
* matched), `limit` (how many are reachable) and `sort` (the order that window was selected under) with
|
|
498
511
|
* `recovery: 'none'`, so a consumer can say "19.240 matched, showing the 1.000 most recent" — naming the
|
|
499
512
|
* order, since which 1.000 are reachable depends on it — and know not to offer a "load more". It never falls
|
|
500
|
-
* back to a per-repository recovery walk.
|
|
513
|
+
* back to a per-repository recovery walk. A provider that can prove the ceiling was reached without counting
|
|
514
|
+
* past it (Azure DevOps Server, at 20,000) omits `totalCount`, so render the limit alone. An Azure DevOps Server
|
|
515
|
+
* that refuses the match set even with the bounded query fails the page instead (warning + `fetchFailed`),
|
|
516
|
+
* while its count still reports `exceedsProviderLimit`; narrow the search.
|
|
517
|
+
*
|
|
518
|
+
* On **Azure DevOps Server** every relationship is one WIQL query over one collection (`org`, or the
|
|
519
|
+
* repositories' collection), so `itemsPerPage` sizes the whole page. Continuations page through the ids the
|
|
520
|
+
* first page queried while the pagination keeps reading; one whose ids have expired is refused, and the read
|
|
521
|
+
* must restart without the cursor. See `docs/integrations.md` §9.
|
|
501
522
|
*
|
|
502
523
|
* Check `getSupportedFilters().issueSearch` first: a provider with no filtered issue search reports empty
|
|
503
524
|
* relationships (and this read refuses), and a criterion or sort key it can't express refuses the whole read
|
|
@@ -517,7 +538,8 @@ export interface IntegrationManager {
|
|
|
517
538
|
*
|
|
518
539
|
* Held to a STRICTER rule than the free-form criteria, which are sanitized: a scope name carrying a quote,
|
|
519
540
|
* or an inner space or control character, is REFUSED (warning + `fetchFailed`), and the refusal names the
|
|
520
|
-
* value
|
|
541
|
+
* value — except on Azure DevOps Server, whose scope names never enter a query string, where only blank
|
|
542
|
+
* names and control characters are. Leading and trailing whitespace and control characters are stripped and accepted, since removing
|
|
521
543
|
* them cannot change which scope the query names.
|
|
522
544
|
* The sanitized value may name a real but DIFFERENT org, whose result looks entirely normal. `''` means
|
|
523
545
|
* "no org supplied" and falls through to the other scopes.
|
|
@@ -560,8 +582,8 @@ export interface IntegrationManager {
|
|
|
560
582
|
*
|
|
561
583
|
* `count: undefined` means the provider didn't report one — NOT zero, which is a real answer. Render the
|
|
562
584
|
* difference: showing an unknown count as 0 tells the user a filter matches nothing when it may match
|
|
563
|
-
* thousands. A provider that can't count at all (only GitHub/GHE can today) refuses the
|
|
564
|
-
* than returning fabricated numbers.
|
|
585
|
+
* thousands. A provider that can't count at all (only GitHub/GHE and Azure DevOps Server can today) refuses the
|
|
586
|
+
* probe outright rather than returning fabricated numbers.
|
|
565
587
|
*/
|
|
566
588
|
countIssues(options: {
|
|
567
589
|
providerId: IntegrationIds;
|
|
@@ -601,31 +623,54 @@ export interface IntegrationManager {
|
|
|
601
623
|
* {@link getIssuesBatch}, which cannot serve one.
|
|
602
624
|
*
|
|
603
625
|
* `issue: undefined` is a proven absence and may be cached. A failed read returns no item and sets
|
|
604
|
-
* `fetchFailed`. `resourceId` is required and trusted; Jira also requires the resource's site URL so the
|
|
605
|
-
* retains its browser link. The read performs no resource discovery.
|
|
626
|
+
* `fetchFailed`. `resourceId` is required and trusted; Jira Cloud also requires the resource's site URL so the
|
|
627
|
+
* result retains its browser link. The read performs no resource discovery.
|
|
628
|
+
*
|
|
629
|
+
* Jira (Cloud and Data Center) and Linear only. Trello refuses: its single-issue read falls back to a capped
|
|
630
|
+
* board scan for a numeric identifier, so it cannot prove absence.
|
|
606
631
|
*
|
|
607
|
-
* Jira
|
|
608
|
-
*
|
|
632
|
+
* Jira Data Center is addressed per host and, unlike the paged reads, this one never falls back to the primary
|
|
633
|
+
* connection: it requires a `domain` or a `connectionId` with a configured host and refuses otherwise, since two
|
|
634
|
+
* self-hosted instances routinely issue the same keys and a cached absence must name the host it was proven on.
|
|
609
635
|
*/
|
|
610
636
|
getTrackerIssue(options: {
|
|
611
637
|
providerId: IntegrationIds;
|
|
612
|
-
/**
|
|
638
|
+
/**
|
|
639
|
+
* Provider resource ID for the Atlassian site or Linear workspace. For Jira Data Center it is the host, the
|
|
640
|
+
* instance's single resource as `listOrgs` reports it, and must name the host the read resolves to.
|
|
641
|
+
*/
|
|
613
642
|
resourceId: string;
|
|
614
|
-
/**
|
|
643
|
+
/**
|
|
644
|
+
* Jira site URL from resource discovery. Required for Jira Cloud so the result retains a browser link; Jira
|
|
645
|
+
* Data Center builds it from the connection's own base URL.
|
|
646
|
+
*/
|
|
615
647
|
resourceUrl?: string;
|
|
616
648
|
/** The provider's own key, e.g. `ABC-123`. Not a number. */
|
|
617
649
|
key: string;
|
|
618
650
|
connectionId?: string;
|
|
651
|
+
/**
|
|
652
|
+
* Self-managed tracker host; see {@link ProviderSweepTarget.domain}. Ignored for the cloud trackers. Required
|
|
653
|
+
* for Jira Data Center unless `connectionId` names a configured host — see above.
|
|
654
|
+
*/
|
|
655
|
+
domain?: string;
|
|
619
656
|
}): Promise<ProviderResult<TrackerIssueResult>>;
|
|
620
657
|
/**
|
|
621
|
-
* How many pull requests match each scope
|
|
622
|
-
*
|
|
623
|
-
*
|
|
624
|
-
*
|
|
625
|
-
* The one PR-specific difference: a scope's `states` are counted as independent searches, so the
|
|
626
|
-
* is the LARGEST of them (the same total {@link searchPullRequestsPage} surfaces), not their sum.
|
|
627
|
-
* in one scope are therefore fine; only several relationships are refused — one relationship per
|
|
628
|
-
* {@link PullRequestCountScope}.
|
|
658
|
+
* How many pull requests match each scope — the PR twin of {@link countIssues}, behind a "this will fetch ~N pull
|
|
659
|
+
* requests" preview and a live count next to an unapplied filter. Same per-scope isolation, `key`-echo, and
|
|
660
|
+
* `count: undefined` ≠ zero rule as the issue count; GitHub/GHE, Bitbucket Data Center and Azure DevOps Server.
|
|
661
|
+
*
|
|
662
|
+
* The one PR-specific difference: on GitHub/GHE a scope's `states` are counted as independent searches, so the
|
|
663
|
+
* reported count is the LARGEST of them (the same total {@link searchPullRequestsPage} surfaces), not their sum.
|
|
664
|
+
* Several states in one scope are therefore fine; only several relationships are refused — one relationship per
|
|
665
|
+
* scope, see {@link PullRequestCountScope}.
|
|
666
|
+
*
|
|
667
|
+
* Bitbucket Data Center has no count query: it counts by reading each facet's first page, so a count there costs
|
|
668
|
+
* requests rather than none, and one past that page comes back as a floor with `lowerBound: true`. Reading lets it
|
|
669
|
+
* deduplicate the rows, so it counts the exact union of the states AND of several relationships in one scope,
|
|
670
|
+
* matching what the search it previews returns.
|
|
671
|
+
*
|
|
672
|
+
* Azure DevOps Server counts from the same drain its search pages through (reusing one its search read in the
|
|
673
|
+
* last minute), so its count is the union of the states — for disjoint states their sum — and costs requests too.
|
|
629
674
|
*/
|
|
630
675
|
countPullRequests(options: {
|
|
631
676
|
providerId: IntegrationIds;
|
|
@@ -9,10 +9,10 @@ import { RepositoryMetadata } from "../../../git/models/repositoryMetadata.js";
|
|
|
9
9
|
import { PullRequestUrlIdentity } from "../../../git/utils/pullRequest.utils.js";
|
|
10
10
|
import { ProviderAuthenticationSession } from "../authentication/models.js";
|
|
11
11
|
import { IntegrationIds } from "../constants.js";
|
|
12
|
-
import { MyIssuesForReposOptions, ProviderIssueSearchPage } from "./issueReads.js";
|
|
13
|
-
import { ProviderPullRequestSearchPage } from "./pullRequestReads.js";
|
|
14
|
-
import { IntegrationBase, IntegrationResult, IntegrationType } from "./integration.js";
|
|
15
12
|
import { ProviderOrganization } from "../results.js";
|
|
13
|
+
import { ProviderPullRequestCount, ProviderPullRequestSearchPage } from "./pullRequestReads.js";
|
|
14
|
+
import { MyIssuesForReposOptions, ProviderIssueSearchPage, ProviderSearchCount } from "./issueReads.js";
|
|
15
|
+
import { IntegrationBase, IntegrationResult, IntegrationType } from "./integration.js";
|
|
16
16
|
import { ProviderApiPagedResult, ProviderHierarchyResult, ProviderIssue, ProviderPullRequest, ProviderRepoInput, ProviderReposInput, ProviderRepository } from "../providers/models.js";
|
|
17
17
|
import { CollectionMetadata } from "@gitkraken/provider-apis";
|
|
18
18
|
//#region ../plus/integrations/src/models/gitHostIntegration.d.ts
|
|
@@ -45,6 +45,11 @@ type MyPullRequestsForReposOptions = {
|
|
|
45
45
|
};
|
|
46
46
|
export declare abstract class GitHostIntegration<ID extends IntegrationIds = IntegrationIds, T extends ResourceDescriptor = ResourceDescriptor> extends IntegrationBase<ID> {
|
|
47
47
|
readonly type: IntegrationType;
|
|
48
|
+
/** The self-managed installation `session` addresses, validated against this integration's host. */
|
|
49
|
+
protected getSelfManagedInstallationUrl(session: ProviderAuthenticationSession): string;
|
|
50
|
+
protected getSelfManagedApiBaseUrl(session: ProviderAuthenticationSession): string;
|
|
51
|
+
/** The base the repository-list reads hand the provider client for a self-managed host. */
|
|
52
|
+
protected getRepositoriesApiBaseUrl(session: ProviderAuthenticationSession, _repos: ProviderReposInput): string;
|
|
48
53
|
getAccountForEmail(repo: T, email: string, options?: {
|
|
49
54
|
avatarSize?: number;
|
|
50
55
|
}): Promise<Account$1 | undefined>;
|
|
@@ -86,7 +91,7 @@ export declare abstract class GitHostIntegration<ID extends IntegrationIds = Int
|
|
|
86
91
|
protected abstract getProviderRepositoryMetadata(session: ProviderAuthenticationSession, repo: T, cancellation?: AbortSignal): Promise<RepositoryMetadata | undefined>;
|
|
87
92
|
/**
|
|
88
93
|
* Whether this git host implements generic org discovery. False for providers that register no
|
|
89
|
-
* {@link getProviderOrganizationsForUser} hook
|
|
94
|
+
* {@link getProviderOrganizationsForUser} hook — the facade uses this to
|
|
90
95
|
* report `unsupported` instead of a silent empty list, which is indistinguishable from "has no orgs".
|
|
91
96
|
*/
|
|
92
97
|
get supportsOrganizationDiscovery(): boolean;
|
|
@@ -366,25 +371,26 @@ export declare abstract class GitHostIntegration<ID extends IntegrationIds = Int
|
|
|
366
371
|
* at all. Recovers thrown errors into `{ error }` like the reads around it.
|
|
367
372
|
*
|
|
368
373
|
* Counts come back POSITIONALLY — one per input scope, in order — because a caller's key must never reach the
|
|
369
|
-
* provider query.
|
|
374
|
+
* provider query. See {@link ProviderSearchCount} for what each slot can hold; `undefined` means the provider
|
|
375
|
+
* didn't report a count for it, never zero matches.
|
|
370
376
|
*/
|
|
371
377
|
countIssuesResult(scopes: readonly {
|
|
372
378
|
repos?: ProviderRepoInput[];
|
|
373
379
|
org?: string;
|
|
374
380
|
criteria?: IssueSearchCriteria;
|
|
375
|
-
}[], cancellation?: AbortSignal, connectionId?: string): Promise<IntegrationResult<
|
|
381
|
+
}[], cancellation?: AbortSignal, connectionId?: string): Promise<IntegrationResult<ProviderSearchCount[] | undefined>>;
|
|
376
382
|
/**
|
|
377
383
|
* OPTIONAL: only a provider that can answer "how many match" WITHOUT fetching the matches implements this.
|
|
378
|
-
* GitHub's search reports `issueCount` on a zero-node selection
|
|
379
|
-
*
|
|
380
|
-
* doesn't implement it and the facade refuses the probe, so a
|
|
381
|
-
* a fabricated one.
|
|
384
|
+
* GitHub's search reports `issueCount` on a zero-node selection, and Azure DevOps Server's WIQL returns the
|
|
385
|
+
* matching ids without their details; GitLab's REST exposes a total on some endpoints but not for a
|
|
386
|
+
* search-shaped query. A provider that can't answer doesn't implement it and the facade refuses the probe, so a
|
|
387
|
+
* consumer hides its count rather than being shown a fabricated one.
|
|
382
388
|
*/
|
|
383
389
|
protected countProviderIssues?(session: ProviderAuthenticationSession, scopes: readonly {
|
|
384
390
|
repos?: ProviderRepoInput[];
|
|
385
391
|
org?: string;
|
|
386
392
|
criteria?: IssueSearchCriteria;
|
|
387
|
-
}[], cancellation?: AbortSignal): Promise<
|
|
393
|
+
}[], cancellation?: AbortSignal): Promise<ProviderSearchCount[] | undefined>;
|
|
388
394
|
/**
|
|
389
395
|
* Result-returning wrapper for the BATCH issue read: resolves several `(owner, repo, number)` coordinates in
|
|
390
396
|
* one request. Recovers thrown errors into `{ error }` like the reads around it.
|
|
@@ -416,18 +422,28 @@ export declare abstract class GitHostIntegration<ID extends IntegrationIds = Int
|
|
|
416
422
|
repo: string;
|
|
417
423
|
number: number;
|
|
418
424
|
}[], cancellation?: AbortSignal): Promise<(IssueShape | undefined)[] | undefined>;
|
|
419
|
-
/**
|
|
425
|
+
/**
|
|
426
|
+
* The PR twin of {@link countIssuesResult}: counts each scope's pull requests, transferring none where the
|
|
427
|
+
* provider has a count query. Each slot is a {@link ProviderPullRequestCount}, so a provider that can only count
|
|
428
|
+
* by reading (Bitbucket Data Center) can mark a figure it stopped short of as a floor, or an `Error` refusing only
|
|
429
|
+
* that scope (Azure DevOps Server).
|
|
430
|
+
*/
|
|
420
431
|
countPullRequestsResult(scopes: readonly {
|
|
421
432
|
repos?: ProviderRepoInput[];
|
|
422
433
|
org?: string;
|
|
423
434
|
criteria?: PullRequestSearchCriteria;
|
|
424
|
-
}[], cancellation?: AbortSignal, connectionId?: string): Promise<IntegrationResult<(
|
|
425
|
-
/**
|
|
435
|
+
}[], cancellation?: AbortSignal, connectionId?: string): Promise<IntegrationResult<(ProviderPullRequestCount | Error)[] | undefined>>;
|
|
436
|
+
/**
|
|
437
|
+
* OPTIONAL, like {@link countProviderIssues}: only a provider that can answer "how many match" implements it —
|
|
438
|
+
* GitHub/GHE with a zero-node search, Bitbucket Data Center by reading within a budget and reporting a floor
|
|
439
|
+
* past it, Azure DevOps Server from the drain its search reads. Must agree with
|
|
440
|
+
* `ProviderMetadata.supportedPullRequestSearch`, which the facade validates against.
|
|
441
|
+
*/
|
|
426
442
|
protected countProviderPullRequests?(session: ProviderAuthenticationSession, scopes: readonly {
|
|
427
443
|
repos?: ProviderRepoInput[];
|
|
428
444
|
org?: string;
|
|
429
445
|
criteria?: PullRequestSearchCriteria;
|
|
430
|
-
}[], cancellation?: AbortSignal): Promise<(
|
|
446
|
+
}[], cancellation?: AbortSignal): Promise<(ProviderPullRequestCount | Error)[] | undefined>;
|
|
431
447
|
getPullRequestIdentityFromMaybeUrl(search: string): PullRequestUrlIdentity | undefined;
|
|
432
448
|
protected getProviderPullRequestIdentityFromMaybeUrl?(search: string): PullRequestUrlIdentity | undefined;
|
|
433
449
|
}
|
|
@@ -8,29 +8,29 @@ import __decorate from "../../../_virtual/@oxc-project+runtime/helpers/esm/decor
|
|
|
8
8
|
import { gate } from "../../../utils/decorators/gate.js";
|
|
9
9
|
import "../../../git/models/pullRequest.js";
|
|
10
10
|
import { toError } from "../errors.js";
|
|
11
|
-
import { throwIfCallerContractError, toCollectionFailureError, toCollectionScopeFailure } from "../collectionMetadata.js";
|
|
12
11
|
import "../constants.js";
|
|
12
|
+
import { throwIfCallerContractError, toCollectionFailureError, toCollectionScopeFailure } from "../collectionMetadata.js";
|
|
13
13
|
import { toTokenWithInfo } from "../authentication/models.js";
|
|
14
14
|
import "../providerFilters.js";
|
|
15
|
-
import { toIssueShape, toProviderIssueStates, toProviderPullRequestStates } from "../providers/models.js";
|
|
16
|
-
import { baseUrlFromDomain } from "../utils/domain.utils.js";
|
|
17
15
|
import { mergeCollectionMetadata, throwIfAllSettledFailed } from "../providers/utils/providerPaging.js";
|
|
16
|
+
import { areDomainsOnSameHost } from "../utils/domain.utils.js";
|
|
17
|
+
import { getSelfManagedBaseUrl, isGitSelfManagedHostIntegrationId } from "../utils/integration.utils.js";
|
|
18
|
+
import { toIssueShape, toProviderIssueStates, toProviderPullRequestStates } from "../providers/models.js";
|
|
18
19
|
import { IntegrationBase } from "./integration.js";
|
|
19
20
|
//#region ../plus/integrations/src/models/gitHostIntegration.ts
|
|
21
|
+
/**
|
|
22
|
+
* Whether an SDK read for `providerId` must send the session's secret as an HTTP Basic credential.
|
|
23
|
+
*
|
|
24
|
+
* Azure DevOps authenticates a PAT only as Basic — Azure DevOps Server refuses one sent as a bearer token outright —
|
|
25
|
+
* and the Azure integration's own reads already pass it that way (`AzureDevOpsIntegrationBase.getApiOptions`). The
|
|
26
|
+
* reads here are shared by every host, so they have to ask; every other provider keeps the SDK's default.
|
|
27
|
+
*/
|
|
28
|
+
function sendsBasicCredential(providerId, session) {
|
|
29
|
+
return isAzureDevOpsProvider(providerId) ? session.type !== "oauth" : void 0;
|
|
30
|
+
}
|
|
20
31
|
function isAzureDevOpsProvider(providerId) {
|
|
21
32
|
return providerId === "azureDevOps" || providerId === "azure-devops-server";
|
|
22
33
|
}
|
|
23
|
-
function getSelfManagedApiBaseUrl(providerId, domain, protocol) {
|
|
24
|
-
const baseUrl = baseUrlFromDomain(domain, protocol);
|
|
25
|
-
if (baseUrl == null) return void 0;
|
|
26
|
-
switch (providerId) {
|
|
27
|
-
case "cloud-github-enterprise": return `${baseUrl.replace(/\/api(?:\/v\d+)?$/, "")}/api/v3`;
|
|
28
|
-
case "cloud-gitlab-self-hosted": return baseUrl.replace(/\/api(?:\/v\d+)?$/, "");
|
|
29
|
-
case "bitbucket-server": return `${baseUrl.replace(/\/rest\/api\/1\.0$/, "")}/rest/api/1.0`;
|
|
30
|
-
case "azure-devops-server": return baseUrl;
|
|
31
|
-
default: return;
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
34
|
/**
|
|
35
35
|
* Rejects a read the provider can't serve as asked: logs the reason and returns it as the `{ error }` half of an
|
|
36
36
|
* {@link IntegrationResult}, timed from `start`.
|
|
@@ -58,6 +58,24 @@ const summaryPullRequestFields = {
|
|
|
58
58
|
};
|
|
59
59
|
var GitHostIntegration = class extends IntegrationBase {
|
|
60
60
|
type = "git";
|
|
61
|
+
/** The self-managed installation `session` addresses, validated against this integration's host. */
|
|
62
|
+
getSelfManagedInstallationUrl(session) {
|
|
63
|
+
const baseUrl = getSelfManagedBaseUrl(this.id, session.baseUrl ?? (session.domain || this.domain), session.protocol);
|
|
64
|
+
if (baseUrl == null || !areDomainsOnSameHost(baseUrl, this.domain)) throw new Error("Invalid self-managed integration base URL");
|
|
65
|
+
return baseUrl;
|
|
66
|
+
}
|
|
67
|
+
getSelfManagedApiBaseUrl(session) {
|
|
68
|
+
const baseUrl = this.getSelfManagedInstallationUrl(session);
|
|
69
|
+
switch (this.id) {
|
|
70
|
+
case "cloud-github-enterprise": return `${baseUrl}/api/v3`;
|
|
71
|
+
case "bitbucket-server": return `${baseUrl}/rest/api/1.0`;
|
|
72
|
+
default: return baseUrl;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
/** The base the repository-list reads hand the provider client for a self-managed host. */
|
|
76
|
+
getRepositoriesApiBaseUrl(session, _repos) {
|
|
77
|
+
return this.getSelfManagedApiBaseUrl(session);
|
|
78
|
+
}
|
|
61
79
|
async getAccountForEmail(repo, email, options) {
|
|
62
80
|
const scope = getScopedLogger();
|
|
63
81
|
if (!(this.maybeConnected ?? await this.isConnected())) return void 0;
|
|
@@ -138,7 +156,7 @@ var GitHostIntegration = class extends IntegrationBase {
|
|
|
138
156
|
}
|
|
139
157
|
/**
|
|
140
158
|
* Whether this git host implements generic org discovery. False for providers that register no
|
|
141
|
-
* {@link getProviderOrganizationsForUser} hook
|
|
159
|
+
* {@link getProviderOrganizationsForUser} hook — the facade uses this to
|
|
142
160
|
* report `unsupported` instead of a silent empty list, which is indistinguishable from "has no orgs".
|
|
143
161
|
*/
|
|
144
162
|
get supportsOrganizationDiscovery() {
|
|
@@ -192,7 +210,13 @@ var GitHostIntegration = class extends IntegrationBase {
|
|
|
192
210
|
const start = performance.now();
|
|
193
211
|
try {
|
|
194
212
|
const result = await this.getProviderOrganizationsForUser?.(session);
|
|
195
|
-
this.
|
|
213
|
+
await this.confirmScopedAuthFailures(session, result?.metadata);
|
|
214
|
+
const authFailure = result?.metadata?.failures?.find((failure) => failure.kind === "authentication" && failure.scope != null && failure.scope.providerId === this.id && Object.keys(failure.scope).length === 1);
|
|
215
|
+
if (authFailure != null) this.handleProviderException("getOrganizationsForUser", toCollectionFailureError(authFailure, toTokenWithInfo(this.id, session)), {
|
|
216
|
+
scope,
|
|
217
|
+
connectionId
|
|
218
|
+
});
|
|
219
|
+
else this.resetRequestExceptionCount("getOrganizationsForUser");
|
|
196
220
|
return {
|
|
197
221
|
value: result,
|
|
198
222
|
duration: performance.now() - start
|
|
@@ -223,6 +247,7 @@ var GitHostIntegration = class extends IntegrationBase {
|
|
|
223
247
|
const start = performance.now();
|
|
224
248
|
try {
|
|
225
249
|
const result = await this.getProviderProjectsForOrg(session, org);
|
|
250
|
+
await this.confirmScopedAuthFailures(session, result?.metadata);
|
|
226
251
|
this.resetRequestExceptionCount("getProjectsForOrg");
|
|
227
252
|
return {
|
|
228
253
|
value: result,
|
|
@@ -257,6 +282,7 @@ var GitHostIntegration = class extends IntegrationBase {
|
|
|
257
282
|
const start = performance.now();
|
|
258
283
|
try {
|
|
259
284
|
const result = await this.getProviderRepositoriesForOrg?.(session, org, options);
|
|
285
|
+
await this.confirmScopedAuthFailures(session, result?.metadata);
|
|
260
286
|
this.resetRequestExceptionCount("getRepositoriesForOrg");
|
|
261
287
|
return {
|
|
262
288
|
value: result,
|
|
@@ -288,6 +314,7 @@ var GitHostIntegration = class extends IntegrationBase {
|
|
|
288
314
|
const start = performance.now();
|
|
289
315
|
try {
|
|
290
316
|
const result = await this.getProviderRepositoriesForUser(session, options);
|
|
317
|
+
await this.confirmScopedAuthFailures(session, result?.metadata);
|
|
291
318
|
this.resetRequestExceptionCount("getRepositoriesForUser");
|
|
292
319
|
return {
|
|
293
320
|
value: result,
|
|
@@ -386,7 +413,10 @@ var GitHostIntegration = class extends IntegrationBase {
|
|
|
386
413
|
let pending = this._filterAccounts.get(key);
|
|
387
414
|
if (pending == null) {
|
|
388
415
|
const tokenWithInfo = toTokenWithInfo(this.authProvider.id, session);
|
|
389
|
-
pending = (organization != null ? api.getCurrentUserForInstance(tokenWithInfo, organization, {
|
|
416
|
+
pending = (organization != null ? api.getCurrentUserForInstance(tokenWithInfo, organization, {
|
|
417
|
+
baseUrl: customUrl,
|
|
418
|
+
isPAT: sendsBasicCredential(this.id, session)
|
|
419
|
+
}) : api.getCurrentUser(tokenWithInfo, { baseUrl: customUrl })).catch((ex) => {
|
|
390
420
|
this._filterAccounts.delete(key);
|
|
391
421
|
throw ex;
|
|
392
422
|
});
|
|
@@ -410,9 +440,10 @@ var GitHostIntegration = class extends IntegrationBase {
|
|
|
410
440
|
const session = await this.resolveReadSession(connectionId, scope);
|
|
411
441
|
if (session == null) return void 0;
|
|
412
442
|
const start = performance.now();
|
|
413
|
-
|
|
443
|
+
let customUrl;
|
|
414
444
|
let api;
|
|
415
445
|
try {
|
|
446
|
+
customUrl = options?.customUrl ?? (isGitSelfManagedHostIntegrationId(providerId) ? this.getRepositoriesApiBaseUrl(session, reposOrRepoIds) : void 0);
|
|
416
447
|
api = await this.getProvidersApi();
|
|
417
448
|
} catch (ex) {
|
|
418
449
|
this.handleProviderException("getIssuesForRepos", ex, {
|
|
@@ -481,6 +512,7 @@ var GitHostIntegration = class extends IntegrationBase {
|
|
|
481
512
|
...getIssuesOptions,
|
|
482
513
|
cursor: projectInput.cursor,
|
|
483
514
|
baseUrl: customUrl,
|
|
515
|
+
isPAT: sendsBasicCredential(providerId, session),
|
|
484
516
|
page: projectInput.cursor == null ? options?.page : void 0,
|
|
485
517
|
pageSize: options?.pageSize,
|
|
486
518
|
states,
|
|
@@ -518,6 +550,7 @@ var GitHostIntegration = class extends IntegrationBase {
|
|
|
518
550
|
}
|
|
519
551
|
}
|
|
520
552
|
if (options?.page != null) cursor.page = options.page;
|
|
553
|
+
await this.confirmScopedAuthFailures(session, metadata);
|
|
521
554
|
this.resetRequestExceptionCount("getIssuesForRepos");
|
|
522
555
|
return {
|
|
523
556
|
value: {
|
|
@@ -622,6 +655,7 @@ var GitHostIntegration = class extends IntegrationBase {
|
|
|
622
655
|
}
|
|
623
656
|
}
|
|
624
657
|
if (options?.page != null) cursor.page = options.page;
|
|
658
|
+
await this.confirmScopedAuthFailures(session, metadata);
|
|
625
659
|
this.resetRequestExceptionCount("getIssuesForRepos");
|
|
626
660
|
return {
|
|
627
661
|
value: {
|
|
@@ -653,6 +687,7 @@ var GitHostIntegration = class extends IntegrationBase {
|
|
|
653
687
|
...getIssuesOptions,
|
|
654
688
|
cursor: options?.cursor,
|
|
655
689
|
baseUrl: customUrl,
|
|
690
|
+
isPAT: sendsBasicCredential(providerId, session),
|
|
656
691
|
page: options?.page,
|
|
657
692
|
pageSize: options?.pageSize,
|
|
658
693
|
states,
|
|
@@ -670,6 +705,7 @@ var GitHostIntegration = class extends IntegrationBase {
|
|
|
670
705
|
duration: performance.now() - start
|
|
671
706
|
};
|
|
672
707
|
}
|
|
708
|
+
await this.confirmScopedAuthFailures(session, result.metadata);
|
|
673
709
|
this.resetRequestExceptionCount("getIssuesForRepos");
|
|
674
710
|
return {
|
|
675
711
|
value: result,
|
|
@@ -732,9 +768,10 @@ var GitHostIntegration = class extends IntegrationBase {
|
|
|
732
768
|
const session = await this.resolveReadSession(connectionId, scope);
|
|
733
769
|
if (session == null) return void 0;
|
|
734
770
|
const start = performance.now();
|
|
735
|
-
|
|
771
|
+
let customUrl;
|
|
736
772
|
let api;
|
|
737
773
|
try {
|
|
774
|
+
customUrl = options?.customUrl ?? (isGitSelfManagedHostIntegrationId(providerId) ? this.getRepositoriesApiBaseUrl(session, reposOrRepoIds) : void 0);
|
|
738
775
|
api = await this.getProvidersApi();
|
|
739
776
|
} catch (ex) {
|
|
740
777
|
this.handleProviderException("getPullRequestsForRepos", ex, {
|
|
@@ -840,7 +877,8 @@ var GitHostIntegration = class extends IntegrationBase {
|
|
|
840
877
|
results: await api.getPullRequestsForRepo(toTokenWithInfo(providerId, session), repoInput.repo, {
|
|
841
878
|
...getPullRequestsOptions,
|
|
842
879
|
cursor: repoInput.cursor,
|
|
843
|
-
|
|
880
|
+
isPAT: sendsBasicCredential(providerId, session),
|
|
881
|
+
baseUrl: options?.customUrl == null && isGitSelfManagedHostIntegrationId(providerId) ? this.getRepositoriesApiBaseUrl(session, [repoInput.repo]) : customUrl,
|
|
844
882
|
page: repoInput.cursor == null ? options?.page : void 0,
|
|
845
883
|
pageSize: options?.pageSize,
|
|
846
884
|
states,
|
|
@@ -875,6 +913,7 @@ var GitHostIntegration = class extends IntegrationBase {
|
|
|
875
913
|
failures
|
|
876
914
|
});
|
|
877
915
|
if (options?.page != null) cursor.page = options.page;
|
|
916
|
+
await this.confirmScopedAuthFailures(session, metadata);
|
|
878
917
|
this.resetRequestExceptionCount("getPullRequestsForRepos");
|
|
879
918
|
return {
|
|
880
919
|
value: {
|
|
@@ -904,6 +943,7 @@ var GitHostIntegration = class extends IntegrationBase {
|
|
|
904
943
|
const result = await api.getPullRequestsForRepos(toTokenWithInfo(providerId, session), reposOrRepoIds, {
|
|
905
944
|
...getPullRequestsOptions,
|
|
906
945
|
cursor: options?.cursor,
|
|
946
|
+
isPAT: sendsBasicCredential(providerId, session),
|
|
907
947
|
baseUrl: customUrl,
|
|
908
948
|
page: options?.page,
|
|
909
949
|
pageSize: options?.pageSize,
|
|
@@ -911,6 +951,7 @@ var GitHostIntegration = class extends IntegrationBase {
|
|
|
911
951
|
includeRemoteInfo: isAzureDevOpsProvider(providerId) ? true : void 0,
|
|
912
952
|
fields: options?.summary ? summaryPullRequestFields : void 0
|
|
913
953
|
});
|
|
954
|
+
await this.confirmScopedAuthFailures(session, result.metadata);
|
|
914
955
|
this.resetRequestExceptionCount("getPullRequestsForRepos");
|
|
915
956
|
return {
|
|
916
957
|
value: result,
|
|
@@ -968,6 +1009,7 @@ var GitHostIntegration = class extends IntegrationBase {
|
|
|
968
1009
|
const start = performance.now();
|
|
969
1010
|
try {
|
|
970
1011
|
const result = await this.getProviderMyPullRequestsForUser(session, options);
|
|
1012
|
+
await this.confirmScopedAuthFailures(session, result?.metadata);
|
|
971
1013
|
this.resetRequestExceptionCount("getMyPullRequestsForUser");
|
|
972
1014
|
return {
|
|
973
1015
|
value: result,
|
|
@@ -1010,6 +1052,7 @@ var GitHostIntegration = class extends IntegrationBase {
|
|
|
1010
1052
|
const start = performance.now();
|
|
1011
1053
|
try {
|
|
1012
1054
|
const result = await this.searchProviderPullRequestsPage?.(session, options, cancellation);
|
|
1055
|
+
await this.confirmScopedAuthFailures(session, result?.metadata);
|
|
1013
1056
|
this.resetRequestExceptionCount("searchPullRequestsPage");
|
|
1014
1057
|
return {
|
|
1015
1058
|
value: result,
|
|
@@ -1082,7 +1125,8 @@ var GitHostIntegration = class extends IntegrationBase {
|
|
|
1082
1125
|
* at all. Recovers thrown errors into `{ error }` like the reads around it.
|
|
1083
1126
|
*
|
|
1084
1127
|
* Counts come back POSITIONALLY — one per input scope, in order — because a caller's key must never reach the
|
|
1085
|
-
* provider query.
|
|
1128
|
+
* provider query. See {@link ProviderSearchCount} for what each slot can hold; `undefined` means the provider
|
|
1129
|
+
* didn't report a count for it, never zero matches.
|
|
1086
1130
|
*/
|
|
1087
1131
|
async countIssuesResult(scopes, cancellation, connectionId) {
|
|
1088
1132
|
const scope = getScopedLogger();
|
|
@@ -1144,7 +1188,12 @@ var GitHostIntegration = class extends IntegrationBase {
|
|
|
1144
1188
|
};
|
|
1145
1189
|
}
|
|
1146
1190
|
}
|
|
1147
|
-
/**
|
|
1191
|
+
/**
|
|
1192
|
+
* The PR twin of {@link countIssuesResult}: counts each scope's pull requests, transferring none where the
|
|
1193
|
+
* provider has a count query. Each slot is a {@link ProviderPullRequestCount}, so a provider that can only count
|
|
1194
|
+
* by reading (Bitbucket Data Center) can mark a figure it stopped short of as a floor, or an `Error` refusing only
|
|
1195
|
+
* that scope (Azure DevOps Server).
|
|
1196
|
+
*/
|
|
1148
1197
|
async countPullRequestsResult(scopes, cancellation, connectionId) {
|
|
1149
1198
|
const scope = getScopedLogger();
|
|
1150
1199
|
const session = await this.resolveReadSession(connectionId, scope);
|