diff --git a/src/vs/platform/userDataSync/common/abstractSynchronizer.ts b/src/vs/platform/userDataSync/common/abstractSynchronizer.ts index 0e438099b62..964644304d6 100644 --- a/src/vs/platform/userDataSync/common/abstractSynchronizer.ts +++ b/src/vs/platform/userDataSync/common/abstractSynchronizer.ts @@ -667,14 +667,14 @@ export abstract class AbstractSynchroniser extends Disposable implements IUserDa return { ref: refOrLastSyncData, content }; } else { const lastSyncUserData: IUserData | null = refOrLastSyncData ? { ref: refOrLastSyncData.ref, content: refOrLastSyncData.syncData ? JSON.stringify(refOrLastSyncData.syncData) : null } : null; - return this.userDataSyncStoreService.read(this.resource, lastSyncUserData, this.syncHeaders); + return this.userDataSyncStoreService.read(this.resource, lastSyncUserData, undefined, this.syncHeaders); } } protected async updateRemoteUserData(content: string, ref: string | null): Promise { const machineId = await this.currentMachineIdPromise; const syncData: ISyncData = { version: this.version, machineId, content }; - ref = await this.userDataSyncStoreService.write(this.resource, JSON.stringify(syncData), ref, this.syncHeaders); + ref = await this.userDataSyncStoreService.write(this.resource, JSON.stringify(syncData), ref, undefined, this.syncHeaders); return { ref, syncData }; } diff --git a/src/vs/platform/userDataSync/common/globalStateSync.ts b/src/vs/platform/userDataSync/common/globalStateSync.ts index e902d4774f2..d244e00671a 100644 --- a/src/vs/platform/userDataSync/common/globalStateSync.ts +++ b/src/vs/platform/userDataSync/common/globalStateSync.ts @@ -480,7 +480,7 @@ export class UserDataSyncStoreTypeSynchronizer { private async doSync(userDataSyncStoreType: UserDataSyncStoreType, syncHeaders: IHeaders): Promise { // Read the global state from remote - const globalStateUserData = await this.userDataSyncStoreClient.read(SyncResource.GlobalState, null, syncHeaders); + const globalStateUserData = await this.userDataSyncStoreClient.readResource(SyncResource.GlobalState, null, syncHeaders); const remoteGlobalState = this.parseGlobalState(globalStateUserData) || { storage: {} }; // Update the sync store type @@ -489,7 +489,7 @@ export class UserDataSyncStoreTypeSynchronizer { // Write the global state to remote const machineId = await getServiceMachineId(this.environmentService, this.fileService, this.storageService); const syncDataToUpdate: ISyncData = { version: GLOBAL_STATE_DATA_VERSION, machineId, content: stringify(remoteGlobalState, false) }; - await this.userDataSyncStoreClient.write(SyncResource.GlobalState, JSON.stringify(syncDataToUpdate), globalStateUserData.ref, syncHeaders); + await this.userDataSyncStoreClient.writeResource(SyncResource.GlobalState, JSON.stringify(syncDataToUpdate), globalStateUserData.ref, syncHeaders); } private parseGlobalState({ content }: IUserData): IGlobalState | null { diff --git a/src/vs/platform/userDataSync/common/userDataSync.ts b/src/vs/platform/userDataSync/common/userDataSync.ts index 6f6bf364d98..17ed95577de 100644 --- a/src/vs/platform/userDataSync/common/userDataSync.ts +++ b/src/vs/platform/userDataSync/common/userDataSync.ts @@ -156,7 +156,7 @@ export interface IResourceRefHandle { created: number; } -export type ServerResource = SyncResource | 'machines' | 'editSessions'; +export type ServerResource = SyncResource | 'machines' | 'editSessions' | 'profiles'; export type UserDataSyncStoreType = 'insiders' | 'stable'; export const IUserDataSyncStoreManagementService = createDecorator('IUserDataSyncStoreManagementService'); @@ -168,7 +168,9 @@ export interface IUserDataSyncStoreManagementService { getPreviousUserDataSyncStore(): Promise; } -export interface IUserDataSyncStoreClient { +export const IUserDataSyncStoreService = createDecorator('IUserDataSyncStoreService'); +export interface IUserDataSyncStoreService { + readonly _serviceBrand: undefined; readonly onDidChangeDonotMakeRequestsUntil: Event; readonly donotMakeRequestsUntil: Date | undefined; @@ -176,20 +178,13 @@ export interface IUserDataSyncStoreClient { readonly onTokenSucceed: Event; setAuthToken(token: string, type: string): void; - // Sync requests manifest(oldValue: IUserDataManifest | null, headers?: IHeaders): Promise; - read(resource: ServerResource, oldValue: IUserData | null, headers?: IHeaders): Promise; - write(resource: ServerResource, content: string, ref: string | null, headers?: IHeaders): Promise; + read(resource: ServerResource, oldValue: IUserData | null, profile?: string, headers?: IHeaders): Promise; + write(resource: ServerResource, content: string, ref: string | null, profile?: string, headers?: IHeaders): Promise; + delete(resource: ServerResource, ref: string | null, profile?: string): Promise; + getAllRefs(resource: ServerResource, profile?: string): Promise; + resolveContent(resource: ServerResource, ref: string, profile?: string, headers?: IHeaders): Promise; clear(): Promise; - delete(resource: ServerResource, ref: string | null): Promise; - - getAllRefs(resource: ServerResource): Promise; - resolveContent(resource: ServerResource, ref: string, headers?: IHeaders): Promise; -} - -export const IUserDataSyncStoreService = createDecorator('IUserDataSyncStoreService'); -export interface IUserDataSyncStoreService extends IUserDataSyncStoreClient { - readonly _serviceBrand: undefined; } export const IUserDataSyncBackupStoreService = createDecorator('IUserDataSyncBackupStoreService'); diff --git a/src/vs/platform/userDataSync/common/userDataSyncStoreService.ts b/src/vs/platform/userDataSync/common/userDataSyncStoreService.ts index c3fe6a3db6c..4104c13c6ad 100644 --- a/src/vs/platform/userDataSync/common/userDataSyncStoreService.ts +++ b/src/vs/platform/userDataSync/common/userDataSyncStoreService.ts @@ -12,6 +12,7 @@ import { Mimes } from 'vs/base/common/mime'; import { isWeb } from 'vs/base/common/platform'; import { ConfigurationSyncStore } from 'vs/base/common/product'; import { joinPath, relativePath } from 'vs/base/common/resources'; +import { join } from 'vs/base/common/path'; import { isObject, isString } from 'vs/base/common/types'; import { URI } from 'vs/base/common/uri'; import { generateUuid } from 'vs/base/common/uuid'; @@ -23,7 +24,7 @@ import { IProductService } from 'vs/platform/product/common/productService'; import { asJson, asTextOrError, IRequestService, isSuccess as isSuccessContext } from 'vs/platform/request/common/request'; import { getServiceMachineId } from 'vs/platform/externalServices/common/serviceMachineId'; import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; -import { CONFIGURATION_SYNC_STORE_KEY, HEADER_EXECUTION_ID, HEADER_OPERATION_ID, IAuthenticationProvider, IResourceRefHandle, IUserData, IUserDataManifest, IUserDataSyncLogService, IUserDataSyncStore, IUserDataSyncStoreClient, IUserDataSyncStoreManagementService, IUserDataSyncStoreService, ServerResource, SYNC_SERVICE_URL_TYPE, UserDataSyncErrorCode, UserDataSyncStoreError, UserDataSyncStoreType } from 'vs/platform/userDataSync/common/userDataSync'; +import { CONFIGURATION_SYNC_STORE_KEY, HEADER_EXECUTION_ID, HEADER_OPERATION_ID, IAuthenticationProvider, IResourceRefHandle, IUserData, IUserDataManifest, IUserDataSyncLogService, IUserDataSyncStore, IUserDataSyncStoreManagementService, IUserDataSyncStoreService, ServerResource, SYNC_SERVICE_URL_TYPE, UserDataSyncErrorCode, UserDataSyncStoreError, UserDataSyncStoreType } from 'vs/platform/userDataSync/common/userDataSync'; const SYNC_PREVIOUS_STORE = 'sync.previous.store'; const DONOT_MAKE_REQUESTS_UNTIL_KEY = 'sync.donot-make-requests-until'; @@ -140,7 +141,7 @@ export class UserDataSyncStoreManagementService extends AbstractUserDataSyncStor } } -export class UserDataSyncStoreClient extends Disposable implements IUserDataSyncStoreClient { +export class UserDataSyncStoreClient extends Disposable { private userDataSyncStoreUrl: URI | undefined; @@ -230,12 +231,12 @@ export class UserDataSyncStoreClient extends Disposable implements IUserDataSync } } - async getAllRefs(resource: ServerResource): Promise { + async getAllResourceRefs(path: string): Promise { if (!this.userDataSyncStoreUrl) { throw new Error('No settings sync store url configured.'); } - const uri = joinPath(this.userDataSyncStoreUrl, 'resource', resource); + const uri = joinPath(this.userDataSyncStoreUrl, 'resource', path); const headers: IHeaders = {}; const context = await this.request(uri.toString(), { type: 'GET', headers }, [], CancellationToken.None); @@ -244,12 +245,12 @@ export class UserDataSyncStoreClient extends Disposable implements IUserDataSync return result.map(({ url, created }) => ({ ref: relativePath(uri, uri.with({ path: url }))!, created: created * 1000 /* Server returns in seconds */ })); } - async resolveContent(resource: ServerResource, ref: string, headers: IHeaders = {}): Promise { + async resolveResourceContent(path: string, ref: string, headers: IHeaders = {}): Promise { if (!this.userDataSyncStoreUrl) { throw new Error('No settings sync store url configured.'); } - const url = joinPath(this.userDataSyncStoreUrl, 'resource', resource, ref).toString(); + const url = joinPath(this.userDataSyncStoreUrl, 'resource', path, ref).toString(); headers = { ...headers }; headers['Cache-Control'] = 'no-cache'; @@ -258,23 +259,23 @@ export class UserDataSyncStoreClient extends Disposable implements IUserDataSync return content; } - async delete(resource: ServerResource, ref: string | null): Promise { + async deleteResource(path: string, ref: string | null): Promise { if (!this.userDataSyncStoreUrl) { throw new Error('No settings sync store url configured.'); } - const url = ref !== null ? joinPath(this.userDataSyncStoreUrl, 'resource', resource, ref).toString() : joinPath(this.userDataSyncStoreUrl, 'resource', resource).toString(); + const url = ref !== null ? joinPath(this.userDataSyncStoreUrl, 'resource', path, ref).toString() : joinPath(this.userDataSyncStoreUrl, 'resource', path).toString(); const headers: IHeaders = {}; await this.request(url, { type: 'DELETE', headers }, [], CancellationToken.None); } - async read(resource: ServerResource, oldValue: IUserData | null, headers: IHeaders = {}): Promise { + async readResource(path: string, oldValue: IUserData | null, headers: IHeaders = {}): Promise { if (!this.userDataSyncStoreUrl) { throw new Error('No settings sync store url configured.'); } - const url = joinPath(this.userDataSyncStoreUrl, 'resource', resource, 'latest').toString(); + const url = joinPath(this.userDataSyncStoreUrl, 'resource', path, 'latest').toString(); headers = { ...headers }; // Disable caching as they are cached by synchronisers headers['Cache-Control'] = 'no-cache'; @@ -306,12 +307,12 @@ export class UserDataSyncStoreClient extends Disposable implements IUserDataSync return userData; } - async write(resource: ServerResource, data: string, ref: string | null, headers: IHeaders = {}): Promise { + async writeResource(path: string, data: string, ref: string | null, headers: IHeaders = {}): Promise { if (!this.userDataSyncStoreUrl) { throw new Error('No settings sync store url configured.'); } - const url = joinPath(this.userDataSyncStoreUrl, 'resource', resource).toString(); + const url = joinPath(this.userDataSyncStoreUrl, 'resource', path).toString(); headers = { ...headers }; headers['Content-Type'] = Mimes.text; if (ref) { @@ -549,6 +550,33 @@ export class UserDataSyncStoreService extends UserDataSyncStoreClient implements super(userDataSyncStoreManagementService.userDataSyncStore?.url, productService, requestService, logService, environmentService, fileService, storageService); this._register(userDataSyncStoreManagementService.onDidChangeUserDataSyncStore(() => this.updateUserDataSyncStoreUrl(userDataSyncStoreManagementService.userDataSyncStore?.url))); } + + getAllRefs(resource: ServerResource, profile?: string): Promise { + return this.getAllResourceRefs(profile ? this.getProfileResource(resource, profile) : resource); + } + + read(resource: ServerResource, oldValue: IUserData | null, profile?: string, headers?: IHeaders): Promise { + return this.readResource(profile ? this.getProfileResource(resource, profile) : resource, oldValue, headers); + } + + write(resource: ServerResource, content: string, ref: string | null, profile?: string, headers?: IHeaders): Promise { + return this.writeResource(profile ? this.getProfileResource(resource, profile) : resource, content, ref, headers); + } + + delete(resource: ServerResource, ref: string | null, profile?: string): Promise { + return this.deleteResource(profile ? this.getProfileResource(resource, profile) : resource, ref); + } + + resolveContent(resource: ServerResource, ref: string, profile?: string, headers?: IHeaders): Promise { + return this.resolveResourceContent(profile ? this.getProfileResource(resource, profile) : resource, ref, headers); + } + + private getProfileResource(resource: ServerResource, profile: string): string { + if (resource === 'profiles') { + throw new Error(`Invalid Resource Argument: ${resource}`); + } + return join('profiles', profile, resource); + } } export class RequestsSession { diff --git a/src/vs/workbench/contrib/editSessions/browser/editSessionsWorkbenchService.ts b/src/vs/workbench/contrib/editSessions/browser/editSessionsWorkbenchService.ts index c8c9b8f437f..e4564cf9958 100644 --- a/src/vs/workbench/contrib/editSessions/browser/editSessionsWorkbenchService.ts +++ b/src/vs/workbench/contrib/editSessions/browser/editSessionsWorkbenchService.ts @@ -77,7 +77,7 @@ export class EditSessionsWorkbenchService extends Disposable implements IEditSes throw new Error('Please sign in to store your edit session.'); } - return this.storeClient!.write('editSessions', JSON.stringify(editSession), null, createSyncHeaders(generateUuid())); + return this.storeClient!.writeResource('editSessions', JSON.stringify(editSession), null, createSyncHeaders(generateUuid())); } /** @@ -96,9 +96,9 @@ export class EditSessionsWorkbenchService extends Disposable implements IEditSes const headers = createSyncHeaders(generateUuid()); try { if (ref !== undefined) { - content = await this.storeClient?.resolveContent('editSessions', ref, headers); + content = await this.storeClient?.resolveResourceContent('editSessions', ref, headers); } else { - const result = await this.storeClient?.read('editSessions', null, headers); + const result = await this.storeClient?.readResource('editSessions', null, headers); content = result?.content; ref = result?.ref; } @@ -117,7 +117,7 @@ export class EditSessionsWorkbenchService extends Disposable implements IEditSes } try { - await this.storeClient?.delete('editSessions', ref); + await this.storeClient?.deleteResource('editSessions', ref); } catch (ex) { this.logService.error(ex); } @@ -130,7 +130,7 @@ export class EditSessionsWorkbenchService extends Disposable implements IEditSes } try { - return this.storeClient?.getAllRefs('editSessions') ?? []; + return this.storeClient?.getAllResourceRefs('editSessions') ?? []; } catch (ex) { this.logService.error(ex); } @@ -400,7 +400,7 @@ export class EditSessionsWorkbenchService extends Disposable implements IEditSes }); if (result.confirmed) { if (result.checkboxChecked) { - that.storeClient?.delete('editSessions', null); + that.storeClient?.deleteResource('editSessions', null); } that.clearAuthenticationPreference(); } diff --git a/src/vs/workbench/contrib/extensions/electron-sandbox/remoteExtensionsInit.ts b/src/vs/workbench/contrib/extensions/electron-sandbox/remoteExtensionsInit.ts index 7209af8ccaf..851e2d0c3f3 100644 --- a/src/vs/workbench/contrib/extensions/electron-sandbox/remoteExtensionsInit.ts +++ b/src/vs/workbench/contrib/extensions/electron-sandbox/remoteExtensionsInit.ts @@ -83,7 +83,7 @@ export class RemoteExtensionsInitializerContribution implements IWorkbenchContri const userDataSyncStoreClient = this.instantiationService.createInstance(UserDataSyncStoreClient, this.userDataSyncStoreManagementService.userDataSyncStore.url); userDataSyncStoreClient.setAuthToken(session.accessToken, resolvedAuthority.options.authenticationSession.providerId); - const userData = await userDataSyncStoreClient.read(SyncResource.Extensions, null); + const userData = await userDataSyncStoreClient.readResource(SyncResource.Extensions, null); const serviceCollection = new ServiceCollection(); serviceCollection.set(IExtensionManagementService, remoteExtensionManagementServer.extensionManagementService); diff --git a/src/vs/workbench/services/userData/browser/userDataInit.ts b/src/vs/workbench/services/userData/browser/userDataInit.ts index 1d7504a2656..07ef427a949 100644 --- a/src/vs/workbench/services/userData/browser/userDataInit.ts +++ b/src/vs/workbench/services/userData/browser/userDataInit.ts @@ -16,7 +16,7 @@ import { ILogService } from 'vs/platform/log/common/log'; import { UserDataSyncStoreClient } from 'vs/platform/userDataSync/common/userDataSyncStoreService'; import { IProductService } from 'vs/platform/product/common/productService'; import { IRequestService } from 'vs/platform/request/common/request'; -import { IRemoteUserData, IUserData, IUserDataInitializer, IUserDataSyncLogService, IUserDataSyncStoreClient, IUserDataSyncStoreManagementService, SyncResource } from 'vs/platform/userDataSync/common/userDataSync'; +import { IRemoteUserData, IUserData, IUserDataInitializer, IUserDataSyncLogService, IUserDataSyncStoreManagementService, SyncResource } from 'vs/platform/userDataSync/common/userDataSync'; import { AuthenticationSessionInfo, getCurrentAuthenticationSessionInfo } from 'vs/workbench/services/authentication/browser/authenticationService'; import { getSyncAreaLabel } from 'vs/workbench/services/userDataSync/common/userDataSync'; import { IWorkbenchContribution, IWorkbenchContributionsRegistry, Extensions } from 'vs/workbench/common/contributions'; @@ -77,10 +77,10 @@ export class UserDataInitializationService implements IUserDataInitializationSer }); } - private _userDataSyncStoreClientPromise: Promise | undefined; - private createUserDataSyncStoreClient(): Promise { + private _userDataSyncStoreClientPromise: Promise | undefined; + private createUserDataSyncStoreClient(): Promise { if (!this._userDataSyncStoreClientPromise) { - this._userDataSyncStoreClientPromise = (async (): Promise => { + this._userDataSyncStoreClientPromise = (async (): Promise => { try { if (!isWeb) { this.logService.trace(`Skipping initializing user data in desktop`); @@ -151,7 +151,7 @@ export class UserDataInitializationService implements IUserDataInitializationSer userDataSyncStoreClient.setAuthToken(authenticationSession.accessToken, authenticationSession.providerId); // Cache global state data for global state initialization - this.globalStateUserData = await userDataSyncStoreClient.read(SyncResource.GlobalState, null); + this.globalStateUserData = await userDataSyncStoreClient.readResource(SyncResource.GlobalState, null); if (this.globalStateUserData) { const userDataSyncStoreType = new UserDataSyncStoreTypeSynchronizer(userDataSyncStoreClient, this.storageService, this.environmentService, this.fileService, this.logService).getSyncStoreType(this.globalStateUserData); @@ -238,7 +238,7 @@ export class UserDataInitializationService implements IUserDataInitializationSer if (!userDataSyncStoreClient) { return null; } - const userData = await userDataSyncStoreClient.read(SyncResource.Extensions, null); + const userData = await userDataSyncStoreClient.readResource(SyncResource.Extensions, null); return instantiationService.createInstance(ExtensionsPreviewInitializer, userData); })(); } @@ -260,7 +260,7 @@ export class UserDataInitializationService implements IUserDataInitializationSer this.initialized.push(syncResource); this.logService.trace(`Initializing ${getSyncAreaLabel(syncResource)}`); const initializer = this.createSyncResourceInitializer(syncResource); - const userData = await userDataSyncStoreClient.read(syncResource, syncResource === SyncResource.GlobalState ? this.globalStateUserData : null); + const userData = await userDataSyncStoreClient.readResource(syncResource, syncResource === SyncResource.GlobalState ? this.globalStateUserData : null); await initializer.initialize(userData); this.logService.info(`Initialized ${getSyncAreaLabel(syncResource)}`); } catch (error) {