Skip to content

Commit 1829c8c

Browse files
committed
Honors integration disconnect state & moves utils
- Ensure "maybe connect" honors disconnected storage state Moves integration change detection from compare results to autolinks - Avoids needless refresh since only autolinks need the integrations - Debounces integration changes
1 parent 7d85a6b commit 1829c8c

File tree

15 files changed

+152
-94
lines changed

15 files changed

+152
-94
lines changed

src/constants.storage.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import type { Environment } from './container';
88
import type { FeaturePreviews } from './features';
99
import type { GitRevisionRangeNotation } from './git/models/revision';
1010
import type { PaidSubscriptionPlanIds, Subscription } from './plus/gk/models/subscription';
11-
import type { Integration } from './plus/integrations/models/integration';
11+
import type { IntegrationConnectedKey } from './plus/integrations/models/integration';
1212
import type { DeepLinkServiceState } from './uris/deepLinks/deepLink';
1313

1414
export type SecretKeys =
@@ -156,7 +156,7 @@ export type WorkspaceStorage = {
156156
'views:searchAndCompare:pinned': StoredSearchAndCompareItems;
157157
'views:scm:grouped:selected': GroupableTreeViewTypes;
158158
} & {
159-
[key in `connected:${Integration['key']}`]: boolean;
159+
[key in IntegrationConnectedKey]: boolean;
160160
};
161161

162162
export interface Stored<T, SchemaVersion extends number = 1> {

src/constants.views.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ export const treeViewFileNodeTypes: TreeViewFileNodeTypes[] = [
109109
'uncommitted-file',
110110
];
111111
export type TreeViewSubscribableNodeTypes =
112+
| 'autolinks'
112113
| 'commits-current-branch'
113114
| 'compare-branch'
114115
| 'compare-results'
@@ -126,7 +127,6 @@ export type TreeViewNodeTypes =
126127
| TreeViewFileNodeTypes
127128
| TreeViewSubscribableNodeTypes
128129
| 'autolink'
129-
| 'autolinks'
130130
| 'branch-tag-folder'
131131
| 'branches'
132132
| 'compare-picker'

src/git/models/remote.ts

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,10 @@
22
import { GitCloudHostIntegrationId } from '../../constants.integrations';
33
import type { Container } from '../../container';
44
import type { GitHostIntegration } from '../../plus/integrations/models/gitHostIntegration';
5-
import { getIntegrationIdForRemote } from '../../plus/integrations/utils/-webview/integration.utils';
5+
import {
6+
getIntegrationConnectedKey,
7+
getIntegrationIdForRemote,
8+
} from '../../plus/integrations/utils/-webview/integration.utils';
69
import { memoize } from '../../system/decorators/-webview/memoize';
710
import { getLoggableName } from '../../system/logger';
811
import { equalsIgnoreCase } from '../../system/string';
@@ -54,7 +57,9 @@ export class GitRemote<TProvider extends RemoteProvider | undefined = RemoteProv
5457
// Special case for GitHub, since we support the legacy GitHub integration
5558
if (integrationId === GitCloudHostIntegrationId.GitHub) {
5659
const configured = this.container.integrations.getConfiguredLite(integrationId, { cloud: true });
57-
if (configured.length) return true;
60+
if (configured.length) {
61+
return this.container.storage.getWorkspace(getIntegrationConnectedKey(integrationId)) !== false;
62+
}
5863

5964
return undefined;
6065
}
@@ -63,7 +68,14 @@ export class GitRemote<TProvider extends RemoteProvider | undefined = RemoteProv
6368
integrationId,
6469
this.provider.custom ? { domain: this.provider.domain } : undefined,
6570
);
66-
return Boolean(configured.length);
71+
72+
if (configured.length) {
73+
return (
74+
this.container.storage.getWorkspace(getIntegrationConnectedKey(integrationId, this.provider.domain)) !==
75+
false
76+
);
77+
}
78+
return false;
6779
}
6880

6981
@memoize()

src/git/remotes/remoteProviders.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import type { CloudGitSelfManagedHostIntegrationIds } from '../../constants.inte
33
import { GitSelfManagedHostIntegrationId } from '../../constants.integrations';
44
import type { Container } from '../../container';
55
import type { ConfiguredIntegrationDescriptor } from '../../plus/integrations/authentication/models';
6-
import { isCloudSelfHostedIntegrationId } from '../../plus/integrations/providers/models';
6+
import { isCloudGitSelfManagedHostIntegrationId } from '../../plus/integrations/utils/-webview/integration.utils';
77
import { configuration } from '../../system/-webview/configuration';
88
import { Logger } from '../../system/logger';
99
import { AzureDevOpsRemote } from './azure-devops';
@@ -130,7 +130,7 @@ export function loadRemoteProviders(
130130
if (configuredIntegrations?.length) {
131131
for (const ci of configuredIntegrations) {
132132
const integrationId = ci.integrationId;
133-
if (isCloudSelfHostedIntegrationId(integrationId) && ci.domain) {
133+
if (isCloudGitSelfManagedHostIntegrationId(integrationId) && ci.domain) {
134134
const matcher = ci.domain.toLocaleLowerCase();
135135
const provider = {
136136
custom: false,

src/plus/integrations/authentication/configuredIntegrationService.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ import type { Container } from '../../../container';
1010
import { debounce } from '../../../system/function/debounce';
1111
import { flatten } from '../../../system/iterable';
1212
import { getBuiltInIntegrationSession } from '../../gk/utils/-webview/integrationAuthentication.utils';
13-
import { isSelfHostedIntegrationId, providersMetadata } from '../providers/models';
13+
import { providersMetadata } from '../providers/models';
14+
import { isGitSelfManagedHostIntegrationId } from '../utils/-webview/integration.utils';
1415
import type { IntegrationAuthenticationSessionDescriptor } from './integrationAuthenticationProvider';
1516
import type { ConfiguredIntegrationDescriptor, ProviderAuthenticationSession } from './models';
1617

@@ -289,12 +290,12 @@ export class ConfiguredIntegrationService implements Disposable {
289290

290291
await this.removeConfigured(id, {
291292
cloud: cloud,
292-
domain: isSelfHostedIntegrationId(id) ? sessionId : undefined,
293+
domain: isGitSelfManagedHostIntegrationId(id) ? sessionId : undefined,
293294
});
294295
}
295296

296297
async deleteAllSecrets(id: IntegrationIds, cloud?: boolean): Promise<void> {
297-
if (isSelfHostedIntegrationId(id)) {
298+
if (isGitSelfManagedHostIntegrationId(id)) {
298299
// Hack because session IDs are tied to domain. Update this when session ids are different
299300
const configuredDomains = this.configured.get(id)?.map(c => c.domain);
300301
if (configuredDomains != null) {
@@ -317,7 +318,7 @@ export class ConfiguredIntegrationService implements Disposable {
317318

318319
await this.addOrUpdateConfigured({
319320
integrationId: id,
320-
domain: isSelfHostedIntegrationId(id) ? session.domain : undefined,
321+
domain: isGitSelfManagedHostIntegrationId(id) ? session.domain : undefined,
321322
expiresAt: session.expiresAt,
322323
scopes: session.scopes.join(','),
323324
cloud: session.cloud ?? false,
@@ -336,7 +337,7 @@ export class ConfiguredIntegrationService implements Disposable {
336337
storedSession = JSON.parse(sessionJSON);
337338
if (storedSession != null) {
338339
const configured = this.configured.get(id);
339-
const domain = isSelfHostedIntegrationId(id) ? storedSession.id : undefined;
340+
const domain = isGitSelfManagedHostIntegrationId(id) ? storedSession.id : undefined;
340341
if (
341342
configured == null ||
342343
configured.length === 0 ||

src/plus/integrations/authentication/integrationAuthenticationProvider.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,10 @@ import type { Sources } from '../../../constants.telemetry';
66
import type { Container } from '../../../container';
77
import { debug } from '../../../system/decorators/log';
88
import { getBuiltInIntegrationSession } from '../../gk/utils/-webview/integrationAuthentication.utils';
9-
import { isCloudSelfHostedIntegrationId, isSelfHostedIntegrationId } from '../providers/models';
9+
import {
10+
isCloudGitSelfManagedHostIntegrationId,
11+
isGitSelfManagedHostIntegrationId,
12+
} from '../utils/-webview/integration.utils';
1013
import type { ConfiguredIntegrationService } from './configuredIntegrationService';
1114
import type { IntegrationAuthenticationService } from './integrationAuthenticationService';
1215
import type { ProviderAuthenticationSession } from './models';
@@ -65,7 +68,7 @@ abstract class IntegrationAuthenticationProviderBase<ID extends IntegrationIds =
6568
async deleteSession(descriptor: IntegrationAuthenticationSessionDescriptor): Promise<void> {
6669
const configured = await this.configuredIntegrationService.getConfigured(this.authProviderId, {
6770
cloud: this.cloud,
68-
domain: isSelfHostedIntegrationId(this.authProviderId) ? descriptor?.domain : undefined,
71+
domain: isGitSelfManagedHostIntegrationId(this.authProviderId) ? descriptor?.domain : undefined,
6972
});
7073

7174
await this.configuredIntegrationService.deleteStoredSessions(
@@ -265,7 +268,7 @@ export abstract class CloudIntegrationAuthenticationProvider<
265268
if (
266269
session?.expiresIn === 0 &&
267270
(this.authProviderId === GitCloudHostIntegrationId.GitHub ||
268-
isCloudSelfHostedIntegrationId(this.authProviderId))
271+
isCloudGitSelfManagedHostIntegrationId(this.authProviderId))
269272
) {
270273
// It never expires so don't refresh it frequently:
271274
session.expiresIn = maxSmallIntegerV8; // maximum expiration length

src/plus/integrations/authentication/integrationAuthenticationService.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import {
88
import type { Container } from '../../../container';
99
import { gate } from '../../../system/decorators/-webview/gate';
1010
import { log } from '../../../system/decorators/log';
11-
import { supportedIntegrationIds } from '../providers/models';
11+
import { supportedIntegrationIds } from '../utils/-webview/integration.utils';
1212
import type { ConfiguredIntegrationService } from './configuredIntegrationService';
1313
import type { IntegrationAuthenticationProvider } from './integrationAuthenticationProvider';
1414
import { BuiltInAuthenticationProvider } from './integrationAuthenticationProvider';

src/plus/integrations/integrationService.ts

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -51,9 +51,13 @@ import type {
5151
IntegrationResult,
5252
} from './models/integration';
5353
import type { IssuesIntegration } from './models/issuesIntegration';
54-
import { isCloudSelfHostedIntegrationId, isHostingIntegrationId, isSelfHostedIntegrationId } from './providers/models';
5554
import type { ProvidersApi } from './providers/providersApi';
56-
import { convertRemoteProviderIdToIntegrationId } from './utils/-webview/integration.utils';
55+
import {
56+
convertRemoteProviderIdToIntegrationId,
57+
isCloudGitSelfManagedHostIntegrationId,
58+
isGitCloudHostIntegrationId,
59+
isGitSelfManagedHostIntegrationId,
60+
} from './utils/-webview/integration.utils';
5761

5862
export interface ConnectionStateChangeEvent {
5963
key: string;
@@ -591,7 +595,7 @@ export class IntegrationService implements Disposable {
591595
const isInvalidIntegration =
592596
(options?.openRepositoriesOnly &&
593597
integrationId !== GitCloudHostIntegrationId.AzureDevOps &&
594-
(isHostingIntegrationId(integrationId) || isSelfHostedIntegrationId(integrationId)) &&
598+
(isGitCloudHostIntegrationId(integrationId) || isGitSelfManagedHostIntegrationId(integrationId)) &&
595599
!openRemotesByIntegrationId.has(integrationId)) ||
596600
(integrationId === GitCloudHostIntegrationId.AzureDevOps && !hasOpenAzureRepository);
597601
if (integration == null || isInvalidIntegration) {
@@ -970,11 +974,12 @@ export class IntegrationService implements Disposable {
970974
id: GitCloudHostIntegrationId | IssuesCloudHostIntegrationId | GitSelfManagedHostIntegrationId,
971975
domain?: string,
972976
): IntegrationKey {
973-
return isSelfHostedIntegrationId(id) ? (`${id}:${domain}` as const) : id;
977+
return isGitSelfManagedHostIntegrationId(id) ? (`${id}:${domain}` as const) : id;
974978
}
979+
975980
private async *getSupportedCloudIntegrations(domainsById: Map<IntegrationIds, string>): AsyncIterable<Integration> {
976981
for (const id of getSupportedCloudIntegrationIds()) {
977-
if (isCloudSelfHostedIntegrationId(id) && !domainsById.has(id)) {
982+
if (isCloudGitSelfManagedHostIntegrationId(id) && !domainsById.has(id)) {
978983
// Try getting whatever we have now because we will need to disconnect
979984
const integration = await this.get(id, undefined);
980985
if (integration != null) {

src/plus/integrations/models/integration.ts

Lines changed: 10 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,7 @@
22
import type { CancellationToken, Disposable, Event, MessageItem } from 'vscode';
33
import { EventEmitter, window } from 'vscode';
44
import type { AutolinkReference, DynamicAutolinkReference } from '../../../autolinks/models/autolinks';
5-
import type {
6-
GitSelfManagedHostIntegrationId,
7-
IntegrationIds,
8-
IssuesCloudHostIntegrationId,
9-
} from '../../../constants.integrations';
5+
import type { IntegrationIds, IssuesCloudHostIntegrationId } from '../../../constants.integrations';
106
import { GitCloudHostIntegrationId } from '../../../constants.integrations';
117
import type { Sources } from '../../../constants.telemetry';
128
import type { Container } from '../../../container';
@@ -41,15 +37,13 @@ export type IntegrationById<T extends IntegrationIds> = T extends IssuesCloudHos
4137
: GitHostIntegration;
4238
export type IntegrationType = 'git' | 'issues';
4339

44-
export type IntegrationKey =
45-
| `${GitCloudHostIntegrationId}`
46-
| `${IssuesCloudHostIntegrationId}`
47-
| `${GitSelfManagedHostIntegrationId}:${string}`;
48-
export type IntegrationKeyById<T extends IntegrationIds> = T extends IssuesCloudHostIntegrationId
49-
? `${IssuesCloudHostIntegrationId}`
50-
: T extends GitCloudHostIntegrationId
51-
? `${GitCloudHostIntegrationId}`
52-
: `${GitSelfManagedHostIntegrationId}:${string}`;
40+
export type IntegrationKey<T extends IntegrationIds = IntegrationIds> = T extends
41+
| GitCloudHostIntegrationId
42+
| IssuesCloudHostIntegrationId
43+
? `${T}`
44+
: `${T}:${string}`;
45+
46+
export type IntegrationConnectedKey<T extends IntegrationIds = IntegrationIds> = `connected:${IntegrationKey<T>}`;
5347

5448
export type IntegrationResult<T> =
5549
| { value: T; duration?: number; error?: never }
@@ -81,7 +75,7 @@ export abstract class IntegrationBase<
8175

8276
abstract get authProvider(): IntegrationAuthenticationProviderDescriptor;
8377
abstract get id(): ID;
84-
protected abstract get key(): IntegrationKeyById<ID>;
78+
protected abstract get key(): IntegrationKey<ID>;
8579
abstract get name(): string;
8680
abstract get domain(): string;
8781

@@ -104,7 +98,7 @@ export abstract class IntegrationBase<
10498
return [];
10599
}
106100

107-
private get connectedKey(): `connected:${Integration['key']}` {
101+
private get connectedKey(): IntegrationConnectedKey<ID> {
108102
return `connected:${this.key}`;
109103
}
110104

src/plus/integrations/providers/bitbucket-server.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import type { IntegrationAuthenticationService } from '../authentication/integra
1313
import type { ProviderAuthenticationSession } from '../authentication/models';
1414
import type { IntegrationConnectionChangeEvent } from '../integrationService';
1515
import { GitHostIntegration } from '../models/gitHostIntegration';
16+
import type { IntegrationKey } from '../models/integration';
1617
import type { BitbucketRepositoryDescriptor } from './bitbucket/models';
1718
import type { ProviderRepository } from './models';
1819
import { fromProviderPullRequest, providersMetadata } from './models';
@@ -28,7 +29,7 @@ export class BitbucketServerIntegration extends GitHostIntegration<
2829
readonly authProvider: IntegrationAuthenticationProviderDescriptor = authProvider;
2930
readonly id = GitSelfManagedHostIntegrationId.BitbucketServer;
3031
protected readonly key =
31-
`${this.id}:${this.domain}` satisfies `${GitSelfManagedHostIntegrationId.BitbucketServer}:${string}`;
32+
`${this.id}:${this.domain}` satisfies IntegrationKey<GitSelfManagedHostIntegrationId.BitbucketServer>;
3233
readonly name: string = 'Bitbucket Data Center';
3334

3435
constructor(

0 commit comments

Comments
 (0)