Skip to content

Commit 8394748

Browse files
Fixes uncaught errors with cloud self hosted integrations
1 parent 5747f4c commit 8394748

File tree

10 files changed

+74
-65
lines changed

10 files changed

+74
-65
lines changed

src/constants.integrations.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@ export enum SelfHostedIntegrationId {
1212
GitLabSelfHosted = 'gitlab-self-hosted',
1313
}
1414

15+
export type CloudSelfHostedIntegrationId =
16+
| SelfHostedIntegrationId.CloudGitHubEnterprise
17+
| SelfHostedIntegrationId.CloudGitLabSelfHosted;
18+
1519
export enum IssueIntegrationId {
1620
Jira = 'jira',
1721
Trello = 'trello',

src/git/remotes/remoteProviders.ts

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import type { RemotesConfig } from '../../config';
22
import { SelfHostedIntegrationId } from '../../constants.integrations';
33
import type { Container } from '../../container';
44
import type { ConfiguredIntegrationDescriptor } from '../../plus/integrations/authentication/models';
5+
import { isCloudSelfHostedIntegrationId } from '../../plus/integrations/providers/models';
56
import { configuration } from '../../system/-webview/configuration';
67
import { Logger } from '../../system/logger';
78
import { AzureDevOpsRemote } from './azure-devops';
@@ -104,11 +105,7 @@ export function loadRemoteProviders(
104105

105106
if (configuredIntegrations?.length) {
106107
for (const ci of configuredIntegrations) {
107-
if (
108-
(ci.integrationId === SelfHostedIntegrationId.CloudGitHubEnterprise ||
109-
ci.integrationId === SelfHostedIntegrationId.CloudGitLabSelfHosted) &&
110-
ci.domain
111-
) {
108+
if (isCloudSelfHostedIntegrationId(ci.integrationId) && ci.domain) {
112109
const matcher = ci.domain.toLocaleLowerCase();
113110
const providerCreator = (_container: Container, domain: string, path: string) =>
114111
ci.integrationId === SelfHostedIntegrationId.CloudGitHubEnterprise

src/plus/integrations/authentication/integrationAuthentication.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,11 @@ import type { Container } from '../../../container';
99
import { gate } from '../../../system/decorators/-webview/gate';
1010
import { debug, log } from '../../../system/decorators/log';
1111
import type { DeferredEventExecutor } from '../../../system/event';
12-
import { isSelfHostedIntegrationId, supportedIntegrationIds } from '../providers/models';
12+
import {
13+
isCloudSelfHostedIntegrationId,
14+
isSelfHostedIntegrationId,
15+
supportedIntegrationIds,
16+
} from '../providers/models';
1317
import type { ConfiguredIntegrationDescriptor, ProviderAuthenticationSession } from './models';
1418
import { isSupportedCloudIntegrationId } from './models';
1519

@@ -375,10 +379,7 @@ export abstract class CloudIntegrationAuthenticationProvider<
375379
// Make an exception for GitHub because they always return 0
376380
if (
377381
session?.expiresIn === 0 &&
378-
(this.authProviderId === HostingIntegrationId.GitHub ||
379-
this.authProviderId === SelfHostedIntegrationId.CloudGitHubEnterprise ||
380-
// Note: added GitLab self managed here because the cloud token is always a PAT, and the api does not know when it expires, nor can it refresh it
381-
this.authProviderId === SelfHostedIntegrationId.CloudGitLabSelfHosted)
382+
(this.authProviderId === HostingIntegrationId.GitHub || isCloudSelfHostedIntegrationId(this.authProviderId))
382383
) {
383384
// It never expires so don't refresh it frequently:
384385
session.expiresIn = maxSmallIntegerV8; // maximum expiration length

src/plus/integrations/integration.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
11
import type { CancellationToken, Event, MessageItem } from 'vscode';
22
import { EventEmitter, window } from 'vscode';
33
import type { AutolinkReference, DynamicAutolinkReference } from '../../autolinks';
4-
import type { IntegrationId, IssueIntegrationId, SelfHostedIntegrationId } from '../../constants.integrations';
4+
import type {
5+
CloudSelfHostedIntegrationId,
6+
IntegrationId,
7+
IssueIntegrationId,
8+
SelfHostedIntegrationId,
9+
} from '../../constants.integrations';
510
import { HostingIntegrationId } from '../../constants.integrations';
611
import type { Sources } from '../../constants.telemetry';
712
import type { Container } from '../../container';
@@ -55,6 +60,7 @@ export type IntegrationResult<T> =
5560

5661
export type SupportedIntegrationIds = IntegrationId;
5762
export type SupportedHostingIntegrationIds = HostingIntegrationId;
63+
export type SupportedCloudSelfHostedIntegrationIds = CloudSelfHostedIntegrationId;
5864
export type SupportedIssueIntegrationIds = IssueIntegrationId;
5965
export type SupportedSelfHostedIntegrationIds = SelfHostedIntegrationId;
6066

src/plus/integrations/integrationService.ts

Lines changed: 32 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
import type { AuthenticationSessionsChangeEvent, CancellationToken, Event } from 'vscode';
22
import { authentication, Disposable, env, EventEmitter, ProgressLocation, Uri, window } from 'vscode';
33
import { isWeb } from '@env/platform';
4-
import type { IntegrationId, SupportedCloudIntegrationIds } from '../../constants.integrations';
4+
import type {
5+
CloudSelfHostedIntegrationId,
6+
IntegrationId,
7+
SupportedCloudIntegrationIds,
8+
} from '../../constants.integrations';
59
import { HostingIntegrationId, IssueIntegrationId, SelfHostedIntegrationId } from '../../constants.integrations';
610
import type { Source } from '../../constants.telemetry';
711
import { sourceToContext } from '../../constants.telemetry';
@@ -38,12 +42,13 @@ import type {
3842
IntegrationType,
3943
IssueIntegration,
4044
ResourceDescriptor,
45+
SupportedCloudSelfHostedIntegrationIds,
4146
SupportedHostingIntegrationIds,
4247
SupportedIntegrationIds,
4348
SupportedIssueIntegrationIds,
4449
SupportedSelfHostedIntegrationIds,
4550
} from './integration';
46-
import { isHostingIntegrationId, isSelfHostedIntegrationId } from './providers/models';
51+
import { isCloudSelfHostedIntegrationId, isHostingIntegrationId, isSelfHostedIntegrationId } from './providers/models';
4752
import type { ProvidersApi } from './providers/providersApi';
4853
import { isGitHubDotCom, isGitLabDotCom } from './providers/utils';
4954

@@ -136,20 +141,22 @@ export class IntegrationService implements Disposable {
136141

137142
private async *getSupportedCloudIntegrations(domainsById: Map<IntegrationId, string>): AsyncIterable<Integration> {
138143
for (const id of getSupportedCloudIntegrationIds()) {
139-
if (
140-
(id === SelfHostedIntegrationId.CloudGitHubEnterprise ||
141-
id === SelfHostedIntegrationId.CloudGitLabSelfHosted) &&
142-
!domainsById.has(id)
143-
) {
144+
if (isCloudSelfHostedIntegrationId(id) && !domainsById.has(id)) {
144145
try {
145146
// Try getting whatever we have now because we will need to disconnect
146-
yield this.get(id);
147+
const integration = await this.get(id, undefined);
148+
if (integration != null) {
149+
yield integration;
150+
}
147151
} catch {
148152
// Ignore this exception and continue,
149153
// because we probably haven't ever had an instance of this integration
150154
}
151155
} else {
152-
yield this.get(id, domainsById.get(id));
156+
const integration = await this.get(id, domainsById.get(id));
157+
if (integration != null) {
158+
yield integration;
159+
}
153160
}
154161
}
155162
}
@@ -245,6 +252,7 @@ export class IntegrationService implements Disposable {
245252
for (const integrationId of integrationIds) {
246253
try {
247254
const integration = await this.get(integrationId);
255+
if (integration == null) continue;
248256
if (integration.maybeConnected ?? (await integration.isConnected())) {
249257
connectedIntegrations.add(integrationId);
250258
}
@@ -368,6 +376,7 @@ export class IntegrationService implements Disposable {
368376
if (integrationIds != null) {
369377
for (const integrationId of integrationIds) {
370378
const integration = await this.get(integrationId);
379+
if (integration == null) continue;
371380
const connected = integration.maybeConnected ?? (await integration.isConnected());
372381
if (connected && !connectedIntegrations.has(integrationId)) {
373382
return true;
@@ -455,19 +464,18 @@ export class IntegrationService implements Disposable {
455464
return key == null ? this._connectedCache.size !== 0 : this._connectedCache.has(key);
456465
}
457466

458-
get(
459-
id:
460-
| SupportedHostingIntegrationIds
461-
| SelfHostedIntegrationId.CloudGitHubEnterprise
462-
| SelfHostedIntegrationId.CloudGitLabSelfHosted,
463-
): Promise<HostingIntegration>;
467+
get(id: SupportedHostingIntegrationIds): Promise<HostingIntegration>;
464468
get(id: SupportedIssueIntegrationIds): Promise<IssueIntegration>;
465-
get(id: SupportedSelfHostedIntegrationIds, domain: string): Promise<HostingIntegration>;
466-
get(id: SupportedIntegrationIds, domain?: string): Promise<Integration>;
469+
get(
470+
id: SupportedHostingIntegrationIds | SupportedCloudSelfHostedIntegrationIds,
471+
domain?: string,
472+
): Promise<HostingIntegration | undefined>;
473+
get(id: SupportedSelfHostedIntegrationIds, domain: string): Promise<HostingIntegration | undefined>;
474+
get(id: SupportedIntegrationIds, domain?: string): Promise<Integration | undefined>;
467475
async get(
468476
id: SupportedHostingIntegrationIds | SupportedIssueIntegrationIds | SupportedSelfHostedIntegrationIds,
469477
domain?: string,
470-
): Promise<Integration> {
478+
): Promise<Integration | undefined> {
471479
let integration = this.getCached(id, domain);
472480
if (integration == null) {
473481
switch (id) {
@@ -504,7 +512,7 @@ export class IntegrationService implements Disposable {
504512
break;
505513
}
506514

507-
throw new Error(`Domain is required for '${id}' integration`);
515+
return undefined;
508516
}
509517

510518
integration = new (
@@ -562,7 +570,7 @@ export class IntegrationService implements Disposable {
562570
break;
563571
}
564572

565-
throw new Error(`Domain is required for '${id}' integration`);
573+
return undefined;
566574
}
567575

568576
integration = new (
@@ -825,25 +833,9 @@ export class IntegrationService implements Disposable {
825833
args: { 0: integrationIds => (integrationIds?.length ? integrationIds.join(',') : '<undefined>') },
826834
})
827835
async getMyCurrentAccounts(
828-
integrationIds: (
829-
| HostingIntegrationId
830-
| SelfHostedIntegrationId.CloudGitHubEnterprise
831-
| SelfHostedIntegrationId.CloudGitLabSelfHosted
832-
)[],
833-
): Promise<
834-
Map<
835-
| HostingIntegrationId
836-
| SelfHostedIntegrationId.CloudGitHubEnterprise
837-
| SelfHostedIntegrationId.CloudGitLabSelfHosted,
838-
Account
839-
>
840-
> {
841-
const accounts = new Map<
842-
| HostingIntegrationId
843-
| SelfHostedIntegrationId.CloudGitHubEnterprise
844-
| SelfHostedIntegrationId.CloudGitLabSelfHosted,
845-
Account
846-
>();
836+
integrationIds: (HostingIntegrationId | CloudSelfHostedIntegrationId)[],
837+
): Promise<Map<HostingIntegrationId | CloudSelfHostedIntegrationId, Account>> {
838+
const accounts = new Map<HostingIntegrationId | CloudSelfHostedIntegrationId, Account>();
847839
await Promise.allSettled(
848840
integrationIds.map(async integrationId => {
849841
const integration = await this.get(integrationId);
@@ -862,11 +854,7 @@ export class IntegrationService implements Disposable {
862854
args: { 0: integrationIds => (integrationIds?.length ? integrationIds.join(',') : '<undefined>'), 1: false },
863855
})
864856
async getMyPullRequests(
865-
integrationIds?: (
866-
| HostingIntegrationId
867-
| SelfHostedIntegrationId.CloudGitHubEnterprise
868-
| SelfHostedIntegrationId.CloudGitLabSelfHosted
869-
)[],
857+
integrationIds?: (HostingIntegrationId | CloudSelfHostedIntegrationId)[],
870858
cancellation?: CancellationToken,
871859
silent?: boolean,
872860
): Promise<IntegrationResult<SearchedPullRequest[] | undefined>> {

src/plus/integrations/providers/models.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ import {
3232
GitPullRequestReviewState,
3333
GitPullRequestState,
3434
} from '@gitkraken/provider-apis';
35-
import type { IntegrationId } from '../../../constants.integrations';
35+
import type { CloudSelfHostedIntegrationId, IntegrationId } from '../../../constants.integrations';
3636
import { HostingIntegrationId, IssueIntegrationId, SelfHostedIntegrationId } from '../../../constants.integrations';
3737
import type { Account as UserAccount } from '../../../git/models/author';
3838
import type { IssueMember, SearchedIssue } from '../../../git/models/issue';
@@ -103,6 +103,10 @@ export function isHostingIntegrationId(id: IntegrationId): id is HostingIntegrat
103103
].includes(id as HostingIntegrationId);
104104
}
105105

106+
export function isCloudSelfHostedIntegrationId(id: IntegrationId): id is CloudSelfHostedIntegrationId {
107+
return id === SelfHostedIntegrationId.CloudGitHubEnterprise || id === SelfHostedIntegrationId.CloudGitLabSelfHosted;
108+
}
109+
106110
export enum PullRequestFilter {
107111
Author = 'author',
108112
Assignee = 'assignee',

src/plus/integrations/providers/utils.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import type { LaunchpadItem } from '../../launchpad/launchpadProvider';
1212
import type { IssueResourceDescriptor, RepositoryDescriptor } from '../integration';
1313
import { isIssueResourceDescriptor, isRepositoryDescriptor } from '../integration';
1414
import type { GitConfigEntityIdentifier } from './models';
15+
import { isCloudSelfHostedIntegrationId } from './models';
1516

1617
export function isGitHubDotCom(domain: string): boolean {
1718
return equalsIgnoreCase(domain, 'github.com');
@@ -134,9 +135,7 @@ export function encodeIssueOrPullRequestForGitConfig(
134135
id: entity.id,
135136
owner: encodedOwner,
136137
createdDate: new Date().toISOString(),
137-
isCloudEnterprise:
138-
entity.provider.id === SelfHostedIntegrationId.CloudGitHubEnterprise ||
139-
entity.provider.id === SelfHostedIntegrationId.CloudGitLabSelfHosted,
138+
isCloudEnterprise: isCloudSelfHostedIntegrationId(entity.provider.id as IntegrationId),
140139
},
141140
};
142141
}

src/plus/launchpad/launchpad.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,7 @@ export class LaunchpadCommand extends QuickCommand<State> {
199199

200200
private async ensureIntegrationConnected(id: IntegrationId) {
201201
const integration = await this.container.integrations.get(id);
202+
if (integration == null) return false;
202203
let connected = integration.maybeConnected ?? (await integration.isConnected());
203204
if (!connected) {
204205
connected = await integration.connect('launchpad');

src/plus/launchpad/launchpadProvider.ts

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import type { CancellationToken, ConfigurationChangeEvent } from 'vscode';
77
import { Disposable, env, EventEmitter, Uri, window } from 'vscode';
88
import { md5 } from '@env/crypto';
99
import { GlCommand } from '../../constants.commands';
10-
import type { IntegrationId } from '../../constants.integrations';
10+
import type { CloudSelfHostedIntegrationId, IntegrationId } from '../../constants.integrations';
1111
import { HostingIntegrationId, SelfHostedIntegrationId } from '../../constants.integrations';
1212
import type { Container } from '../../container';
1313
import { CancellationError } from '../../errors';
@@ -107,11 +107,7 @@ type PullRequestsWithSuggestionCounts = {
107107

108108
export type LaunchpadRefreshEvent = LaunchpadCategorizedResult;
109109

110-
export const supportedLaunchpadIntegrations: (
111-
| HostingIntegrationId
112-
| SelfHostedIntegrationId.CloudGitHubEnterprise
113-
| SelfHostedIntegrationId.CloudGitLabSelfHosted
114-
)[] = [
110+
export const supportedLaunchpadIntegrations: (HostingIntegrationId | CloudSelfHostedIntegrationId)[] = [
115111
HostingIntegrationId.GitHub,
116112
SelfHostedIntegrationId.CloudGitHubEnterprise,
117113
HostingIntegrationId.GitLab,
@@ -278,6 +274,7 @@ export class LaunchpadProvider implements Disposable {
278274
)
279275
.map(async (id: SupportedLaunchpadIntegrationIds) => {
280276
const integration = await this.container.integrations.get(id);
277+
if (integration == null) return;
281278
const searchResult = await searchIntegrationPRs(integration);
282279
const prs = searchResult?.value;
283280
if (prs) {
@@ -412,6 +409,7 @@ export class LaunchpadProvider implements Disposable {
412409
});
413410
if (confirm !== 'Merge') return;
414411
const integration = await this.container.integrations.get(integrationId);
412+
if (integration == null) return;
415413
const pr: PullRequest = fromProviderPullRequest(item, integration);
416414
await integration.mergePullRequest(pr);
417415
this.refresh();
@@ -604,6 +602,7 @@ export class LaunchpadProvider implements Disposable {
604602
for (const integrationId of supportedLaunchpadIntegrations) {
605603
if (connectedIntegrations.get(integrationId)) {
606604
const integration = await this.container.integrations.get(integrationId);
605+
if (integration == null) continue;
607606
const prIdentity = integration.getPullRequestIdentityFromMaybeUrl(search);
608607
if (prIdentity) {
609608
return prIdentity;
@@ -873,6 +872,7 @@ export class LaunchpadProvider implements Disposable {
873872
async hasConnectedIntegration(): Promise<boolean> {
874873
for (const integrationId of supportedLaunchpadIntegrations) {
875874
const integration = await this.container.integrations.get(integrationId);
875+
if (integration == null) continue;
876876
if (integration.maybeConnected ?? (await integration.isConnected())) {
877877
return true;
878878
}
@@ -887,6 +887,10 @@ export class LaunchpadProvider implements Disposable {
887887
await Promise.allSettled(
888888
supportedLaunchpadIntegrations.map(async integrationId => {
889889
const integration = await this.container.integrations.get(integrationId);
890+
if (integration == null) {
891+
connected.set(integrationId, false);
892+
return;
893+
}
890894
const isConnected = integration.maybeConnected ?? (await integration.isConnected());
891895
const hasAccess = isConnected && (await integration.access());
892896
connected.set(integrationId, hasAccess);

src/plus/startWork/startWork.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -316,6 +316,7 @@ export abstract class StartWorkBaseCommand extends QuickCommand<State> {
316316

317317
private async ensureIntegrationConnected(id: IntegrationId) {
318318
const integration = await this.container.integrations.get(id);
319+
if (integration == null) return false;
319320
let connected = integration.maybeConnected ?? (await integration.isConnected());
320321
if (!connected) {
321322
connected = await integration.connect(this.overrides?.ownSource ?? 'startWork');
@@ -726,6 +727,10 @@ async function getConnectedIntegrations(container: Container): Promise<Map<Suppo
726727
await Promise.allSettled(
727728
supportedStartWorkIntegrations.map(async integrationId => {
728729
const integration = await container.integrations.get(integrationId);
730+
if (integration == null) {
731+
connected.set(integrationId, false);
732+
return;
733+
}
729734
const isConnected = integration.maybeConnected ?? (await integration.isConnected());
730735
const hasAccess = isConnected && (await integration.access());
731736
connected.set(integrationId, hasAccess);

0 commit comments

Comments
 (0)