diff --git a/src/kernels/jupyter/connection/serverUriStorage.ts b/src/kernels/jupyter/connection/serverUriStorage.ts index 248a36f8138..edaece447a3 100644 --- a/src/kernels/jupyter/connection/serverUriStorage.ts +++ b/src/kernels/jupyter/connection/serverUriStorage.ts @@ -111,6 +111,7 @@ export class JupyterServerUriStorage extends Disposables implements IJupyterServ } public async get(id: string): Promise { this.hookupStorageEvents(); + await this.newStorage.migrateMRU(); const savedList = await this.getAll(); return savedList.find((item) => item.serverId === id); } @@ -119,6 +120,7 @@ export class JupyterServerUriStorage extends Disposables implements IJupyterServ options?: { time: number; displayName: string } ): Promise { this.hookupStorageEvents(); + await this.newStorage.migrateMRU(); traceInfoIfCI(`setUri: ${jupyterHandle.id}.${jupyterHandle.handle}`); const uri = generateUriFromRemoteProvider(jupyterHandle.id, jupyterHandle.handle); const serverId = await computeServerId(uri); @@ -142,10 +144,12 @@ export class JupyterServerUriStorage extends Disposables implements IJupyterServ } public async update(serverId: string) { this.hookupStorageEvents(); + await this.newStorage.migrateMRU(); await Promise.all([this.newStorage.update(serverId), this.oldStorage.update(serverId)]); } public async remove(serverId: string) { this.hookupStorageEvents(); + await this.newStorage.migrateMRU(); await Promise.all([this.newStorage.remove(serverId), this.oldStorage.remove(serverId)]); } } @@ -280,7 +284,7 @@ class OldStorage { } public async getAll(): Promise { if (this.lastSavedList) { - return this.lastSavedList; + return this.lastSavedList.then((items) => items.sort((a, b) => b.time - a.time)); } const promise = async () => { // List is in the global memento, URIs are in encrypted storage @@ -305,7 +309,7 @@ class OldStorage { return result.filter((item) => !!item) as IJupyterServerUriEntry[]; }; this.lastSavedList = promise(); - return this.lastSavedList; + return this.lastSavedList.then((items) => items.sort((a, b) => b.time - a.time)); } public async getAllRaw(): Promise { // List is in the global memento, URIs are in encrypted storage @@ -534,7 +538,7 @@ class NewStorage { .catch(noop)); } public async getAll(): Promise { - return this.getAllImpl(true); + return this.getAllImpl(true).then((items) => items.sort((a, b) => b.time - a.time)); } public async clear(): Promise { const all = await this.getAllImpl(false); @@ -577,6 +581,9 @@ class NewStorage { return entries; } private async getAllRaw(): Promise { + if (!(await this.fs.exists(this.storageFile))) { + return []; + } const json = await this.fs.readFile(this.storageFile); return JSON.parse(json) as StorageMRUItem[]; } diff --git a/src/kernels/jupyter/connection/serverUriStorage.unit.test.ts b/src/kernels/jupyter/connection/serverUriStorage.unit.test.ts index ad7f3af35be..61690725ecf 100644 --- a/src/kernels/jupyter/connection/serverUriStorage.unit.test.ts +++ b/src/kernels/jupyter/connection/serverUriStorage.unit.test.ts @@ -84,6 +84,7 @@ suite('Server Uri Storage', async () => { const itemsInNewStorage: StorageMRUItem[] = []; when(fs.writeFile(anything(), anything())).thenCall((_, data) => { itemsInNewStorage.push(...JSON.parse(data.toString())); + when(fs.exists(anything())).thenResolve(true); return Promise.resolve(); }); when(fs.readFile(anything())).thenCall(() => JSON.stringify(itemsInNewStorage)); @@ -122,6 +123,7 @@ suite('Server Uri Storage', async () => { const itemsInNewStorage: StorageMRUItem[] = []; when(fs.writeFile(anything(), anything())).thenCall((_, data) => { itemsInNewStorage.push(...JSON.parse(data.toString())); + when(fs.exists(anything())).thenResolve(true); return Promise.resolve(); }); when(fs.readFile(anything())).thenCall(() => JSON.stringify(itemsInNewStorage)); diff --git a/src/platform/common/cache.ts b/src/platform/common/cache.ts index 5871dc53d70..5f5f98e69af 100644 --- a/src/platform/common/cache.ts +++ b/src/platform/common/cache.ts @@ -36,7 +36,8 @@ export class OldCacheCleaner implements IExtensionSyncActivationService { 'JUPYTER_REMOTE_KERNELSPECS_V3', 'JUPYTER_LOCAL_KERNELSPECS_V4', 'LOCAL_KERNEL_SPECS_CACHE_KEY_V_2022_10', - 'LOCAL_KERNEL_PYTHON_AND_RELATED_SPECS_CACHE_KEY_V_2022_10' + 'LOCAL_KERNEL_PYTHON_AND_RELATED_SPECS_CACHE_KEY_V_2022_10', + 'user-jupyter-server-uri-list-v2' ] .filter((key) => this.globalState.get(key, undefined) !== undefined) .map((key) => this.globalState.update(key, undefined).then(noop, noop)) diff --git a/src/standalone/userJupyterServer/userServerUriProvider.unit.test.ts b/src/standalone/userJupyterServer/userServerUriProvider.unit.test.ts index d0aebb43bc8..7ad98a17d45 100644 --- a/src/standalone/userJupyterServer/userServerUriProvider.unit.test.ts +++ b/src/standalone/userJupyterServer/userServerUriProvider.unit.test.ts @@ -43,7 +43,7 @@ import { IJupyterPasswordConnectInfo, JupyterPasswordConnect } from './jupyterPa suite('User Uri Provider', () => { ['Old Password Manager', 'New Password Manager'].forEach((passwordManager) => { ['Old Storage Format', 'New Storage Format'].forEach((storageFormat) => { - suite(storageFormat, () => { + suite(`${passwordManager} - ${storageFormat}`, () => { const isNewPasswordManager = passwordManager === 'New Password Manager'; const isNewStorageFormat = storageFormat === 'New Storage Format'; let provider: UserJupyterServerUrlProvider; @@ -159,6 +159,13 @@ suite('User Uri Provider', () => { return Promise.resolve(); }); + when( + encryptedStorage.store( + Settings.JupyterServerRemoteLaunchService, + 'user-jupyter-server-uri-list-v2', + anything() + ) + ).thenResolve(); when( encryptedStorage.retrieve( Settings.JupyterServerRemoteLaunchService, diff --git a/src/standalone/userJupyterServer/userServerUrlProvider.ts b/src/standalone/userJupyterServer/userServerUrlProvider.ts index aa6e0b4fd54..0ba0fcaff9f 100644 --- a/src/standalone/userJupyterServer/userServerUrlProvider.ts +++ b/src/standalone/userJupyterServer/userServerUrlProvider.ts @@ -52,7 +52,7 @@ import { validateSelectJupyterURI } from '../../kernels/jupyter/connection/serve import { Deferred, createDeferred } from '../../platform/common/utils/async'; export const UserJupyterServerUriListKey = 'user-jupyter-server-uri-list'; -export const UserJupyterServerUriListKeyV2 = 'user-jupyter-server-uri-list-v2'; +export const UserJupyterServerUriListKeyV2 = 'user-jupyter-server-uri-list-version2'; export const UserJupyterServerUriListMementoKey = '_builtin.jupyterServerUrlProvider.uriList'; const GlobalStateUserAllowsInsecureConnections = 'DataScienceAllowInsecureConnections'; @@ -696,6 +696,39 @@ export class OldStorage { } } +type StorageItem = { + handle: string; + uri: string; +}; +function serverToStorageFormat( + servers: { + handle: string; + uri: string; + serverInfo: IJupyterServerUri; + }[] +): StorageItem[] { + return servers.map((s) => ({ handle: s.handle, uri: s.uri })); +} +function storageFormatToServers(items: StorageItem[]) { + const servers: { + handle: string; + uri: string; + serverInfo: IJupyterServerUri; + }[] = []; + items.forEach((s) => { + const server = parseUri(s.uri); + if (!server) { + return; + } + servers.push({ + handle: s.handle, + uri: s.uri, + serverInfo: server + }); + }); + return servers; +} + export class NewStorage { private readonly _migrationDone: Deferred; private updatePromise = Promise.resolve(); @@ -721,7 +754,13 @@ export class NewStorage { // Already migrated once before. return this._migrationDone.resolve(); } - + this.encryptedStorage + .store( + Settings.JupyterServerRemoteLaunchService, + 'user-jupyter-server-uri-list-v2', // Removed as this storage data is not in the best format. + undefined + ) + .catch(noop); await this.encryptedStorage.store( Settings.JupyterServerRemoteLaunchService, UserJupyterServerUriListKeyV2, @@ -738,7 +777,7 @@ export class NewStorage { return []; } try { - return JSON.parse(data); + return storageFormatToServers(JSON.parse(data)); } catch { return []; } @@ -751,7 +790,7 @@ export class NewStorage { await this.encryptedStorage.store( Settings.JupyterServerRemoteLaunchService, UserJupyterServerUriListKeyV2, - JSON.stringify(servers) + JSON.stringify(serverToStorageFormat(servers)) ); }) .catch(noop)); @@ -763,7 +802,7 @@ export class NewStorage { return this.encryptedStorage.store( Settings.JupyterServerRemoteLaunchService, UserJupyterServerUriListKeyV2, - JSON.stringify(servers) + JSON.stringify(serverToStorageFormat(servers)) ); }) .catch(noop));