From 6bb8daf3952a1472f1feaaea2d1c0cba90f02ed1 Mon Sep 17 00:00:00 2001 From: Daniel Falbel Date: Mon, 25 Nov 2024 16:15:07 -0300 Subject: [PATCH] Store connection metadata in the secret storage (#5456) Adresses https://github.com/posit-dev/positron/issues/5108 TODO: - [x] Make sure storage is per-project and that saved connection don't appear in separate projects. ### QA Notes Should be an invisible change. We should persist connections after closing the IDE and reopening. Connections should also be stored in a per-project basis. --- .../browser/positronConnectionsService.ts | 68 +++++++++++++------ 1 file changed, 46 insertions(+), 22 deletions(-) diff --git a/src/vs/workbench/services/positronConnections/browser/positronConnectionsService.ts b/src/vs/workbench/services/positronConnections/browser/positronConnectionsService.ts index 32707056619..bd2ac40bd34 100644 --- a/src/vs/workbench/services/positronConnections/browser/positronConnectionsService.ts +++ b/src/vs/workbench/services/positronConnections/browser/positronConnectionsService.ts @@ -11,13 +11,15 @@ import { IPositronConnectionsService, POSITRON_CONNECTIONS_VIEW_ID } from 'vs/wo import { DisconnectedPositronConnectionsInstance, PositronConnectionsInstance } from 'vs/workbench/services/positronConnections/browser/positronConnectionsInstance'; import { ILanguageRuntimeSession, IRuntimeSessionService, RuntimeClientType } from 'vs/workbench/services/runtimeSession/common/runtimeSessionService'; import { Event, Emitter } from 'vs/base/common/event'; -import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { POSITRON_CONNECTIONS_VIEW_ENABLED, USE_POSITRON_CONNECTIONS_KEY } from 'vs/workbench/services/positronConnections/browser/positronConnectionsFeatureFlag'; import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IConfigurationChangeEvent, IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IViewsService } from 'vs/workbench/services/views/common/viewsService'; import { ILogService } from 'vs/platform/log/common/log'; import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; +import { ISecretStorageService } from 'vs/platform/secrets/common/secrets'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; +import { generateUuid } from 'vs/base/common/uuid'; class PositronConnectionsService extends Disposable implements IPositronConnectionsService { @@ -35,6 +37,7 @@ class PositronConnectionsService extends Disposable implements IPositronConnecti constructor( @IRuntimeSessionService public readonly runtimeSessionService: IRuntimeSessionService, @IStorageService private readonly storageService: IStorageService, + @ISecretStorageService private readonly secretStorageService: ISecretStorageService, @IContextKeyService private readonly contextKeyService: IContextKeyService, @IConfigurationService private readonly configurationService: IConfigurationService, @IViewsService private readonly viewsService: IViewsService, @@ -52,21 +55,9 @@ class PositronConnectionsService extends Disposable implements IPositronConnecti this.attachRuntime(runtime); })); - const storedConnections: IConnectionMetadata[] = JSON.parse( - this.storageService.get('positron-connections', StorageScope.WORKSPACE, '[]') - ); - storedConnections.forEach((metadata) => { - if (metadata === null) { - return; - } - - const instance = new DisconnectedPositronConnectionsInstance( - new ConnectionMetadata(metadata), - this.runtimeSessionService, - this - ); - - this.addConnection(instance); + this.addStoredConnections().catch((e) => { + this.logService.error('Error while adding stored connections', e); + this.notificationService.error('Error while loading stored connections'); }); this._register(this.configurationService.onDidChangeConfiguration((e) => { @@ -91,6 +82,41 @@ class PositronConnectionsService extends Disposable implements IPositronConnecti initialize(): void { } + private getPrivateStorageId(): string { + let id = this.storageService.get('positron-connections-secret-id', StorageScope.WORKSPACE); + if (!id) { + id = generateUuid(); + this.storageService.store( + 'positron-connections-secret-id', + id, + StorageScope.WORKSPACE, + StorageTarget.MACHINE + ); + } + return id; + } + + private async addStoredConnections() { + const id = this.getPrivateStorageId(); + const storedConnections: IConnectionMetadata[] = JSON.parse( + await this.secretStorageService.get(id) || '[]' + ); + + storedConnections.forEach((metadata) => { + if (metadata === null) { + return; + } + + const instance = new DisconnectedPositronConnectionsInstance( + new ConnectionMetadata(metadata), + this.runtimeSessionService, + this + ); + + this.addConnection(instance); + }); + } + attachRuntime(session: ILanguageRuntimeSession) { this._register(session.onDidCreateClientInstance(async ({ message, client }) => { if (client.getClientType() !== RuntimeClientType.Connection) { @@ -219,13 +245,11 @@ class PositronConnectionsService extends Disposable implements IPositronConnecti } private saveConnectionsState() { - this.storageService.store( - 'positron-connections', - this.connections.map((con) => { + this.secretStorageService.set( + this.getPrivateStorageId(), + JSON.stringify(this.connections.map((con) => { return con.metadata; - }), - StorageScope.WORKSPACE, - StorageTarget.USER + })) ); } }