Skip to content

Commit 51cdb49

Browse files
committed
1 parent c24d232 commit 51cdb49

17 files changed

Lines changed: 403 additions & 85 deletions

File tree

src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,10 +49,10 @@ import { IFileService } from 'vs/platform/files/common/files';
4949
import { DiskFileSystemProvider } from 'vs/platform/files/node/diskFileSystemProvider';
5050
import { Schemas } from 'vs/base/common/network';
5151
import { IProductService } from 'vs/platform/product/common/productService';
52-
import { IUserDataSyncService, IUserDataSyncStoreService, registerConfiguration, IUserDataSyncLogService, IUserDataSyncUtilService, IUserDataSyncResourceEnablementService, IUserDataSyncBackupStoreService } from 'vs/platform/userDataSync/common/userDataSync';
52+
import { IUserDataSyncService, IUserDataSyncStoreService, registerConfiguration, IUserDataSyncLogService, IUserDataSyncUtilService, IUserDataSyncResourceEnablementService, IUserDataSyncBackupStoreService, IUserDataSyncStoreManagementService } from 'vs/platform/userDataSync/common/userDataSync';
5353
import { UserDataSyncService } from 'vs/platform/userDataSync/common/userDataSyncService';
54-
import { UserDataSyncStoreService } from 'vs/platform/userDataSync/common/userDataSyncStoreService';
55-
import { UserDataSyncChannel, UserDataSyncUtilServiceClient, UserDataAutoSyncChannel, StorageKeysSyncRegistryChannelClient, UserDataSyncMachinesServiceChannel, UserDataSyncAccountServiceChannel } from 'vs/platform/userDataSync/common/userDataSyncIpc';
54+
import { UserDataSyncStoreService, UserDataSyncStoreManagementService } from 'vs/platform/userDataSync/common/userDataSyncStoreService';
55+
import { UserDataSyncChannel, UserDataSyncUtilServiceClient, UserDataAutoSyncChannel, StorageKeysSyncRegistryChannelClient, UserDataSyncMachinesServiceChannel, UserDataSyncAccountServiceChannel, UserDataSyncStoreManagementServiceChannel } from 'vs/platform/userDataSync/common/userDataSyncIpc';
5656
import { IElectronService } from 'vs/platform/electron/electron-sandbox/electron';
5757
import { LoggerService } from 'vs/platform/log/node/loggerService';
5858
import { UserDataSyncLogService } from 'vs/platform/userDataSync/common/userDataSyncLog';
@@ -202,6 +202,7 @@ async function main(server: Server, initData: ISharedProcessInitData, configurat
202202
services.set(IUserDataSyncLogService, new SyncDescriptor(UserDataSyncLogService));
203203
services.set(IUserDataSyncUtilService, new UserDataSyncUtilServiceClient(server.getChannel('userDataSyncUtil', client => client.ctx !== 'main')));
204204
services.set(IGlobalExtensionEnablementService, new SyncDescriptor(GlobalExtensionEnablementService));
205+
services.set(IUserDataSyncStoreManagementService, new SyncDescriptor(UserDataSyncStoreManagementService));
205206
services.set(IUserDataSyncStoreService, new SyncDescriptor(UserDataSyncStoreService));
206207
services.set(IUserDataSyncMachinesService, new SyncDescriptor(UserDataSyncMachinesService));
207208
services.set(IUserDataSyncBackupStoreService, new SyncDescriptor(UserDataSyncBackupStoreService));
@@ -237,6 +238,10 @@ async function main(server: Server, initData: ISharedProcessInitData, configurat
237238
const authTokenChannel = new UserDataSyncAccountServiceChannel(authTokenService);
238239
server.registerChannel('userDataSyncAccount', authTokenChannel);
239240

241+
const userDataSyncStoreManagementService = accessor.get(IUserDataSyncStoreManagementService);
242+
const userDataSyncStoreManagementChannel = new UserDataSyncStoreManagementServiceChannel(userDataSyncStoreManagementService);
243+
server.registerChannel('userDataSyncStoreManagement', userDataSyncStoreManagementChannel);
244+
240245
const userDataSyncService = accessor.get(IUserDataSyncService);
241246
const userDataSyncChannel = new UserDataSyncChannel(server, userDataSyncService, logService);
242247
server.registerChannel('userDataSync', userDataSyncChannel);

src/vs/platform/product/common/productService.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,12 @@ export interface IBuiltInExtension {
2222
readonly metadata: any;
2323
}
2424

25-
export type ConfigurationSyncStore = { url: string, authenticationProviders: IStringDictionary<{ scopes: string[] }> };
25+
export type ConfigurationSyncStore = {
26+
url: string,
27+
insidersUrl?: string,
28+
stableUrl?: string,
29+
authenticationProviders: IStringDictionary<{ scopes: string[] }>
30+
};
2631

2732
export interface IProductConfiguration {
2833
readonly version: string;

src/vs/platform/userDataSync/common/userDataAutoSyncService.ts

Lines changed: 49 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
import { Delayer, disposableTimeout, CancelablePromise, createCancelablePromise, timeout } from 'vs/base/common/async';
77
import { Event, Emitter } from 'vs/base/common/event';
88
import { Disposable, toDisposable, MutableDisposable, IDisposable } from 'vs/base/common/lifecycle';
9-
import { IUserDataSyncLogService, IUserDataSyncService, IUserDataAutoSyncService, UserDataSyncError, UserDataSyncErrorCode, IUserDataSyncResourceEnablementService, IUserDataSyncStoreService, UserDataAutoSyncError, ISyncTask } from 'vs/platform/userDataSync/common/userDataSync';
9+
import { IUserDataSyncLogService, IUserDataSyncService, IUserDataAutoSyncService, UserDataSyncError, UserDataSyncErrorCode, IUserDataSyncResourceEnablementService, IUserDataSyncStoreService, UserDataAutoSyncError, ISyncTask, IUserDataSyncStoreManagementService } from 'vs/platform/userDataSync/common/userDataSync';
1010
import { IUserDataSyncAccountService } from 'vs/platform/userDataSync/common/userDataSyncAccount';
1111
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
1212
import { isPromiseCanceledError } from 'vs/base/common/errors';
@@ -16,6 +16,8 @@ import { IEnvironmentService } from 'vs/platform/environment/common/environment'
1616
import { IUserDataSyncMachinesService } from 'vs/platform/userDataSync/common/userDataSyncMachines';
1717
import { localize } from 'vs/nls';
1818
import { toLocalISOString } from 'vs/base/common/date';
19+
import { URI } from 'vs/base/common/uri';
20+
import { isEqual } from 'vs/base/common/resources';
1921

2022
type AutoSyncClassification = {
2123
sources: { classification: 'SystemMetaData', purpose: 'FeatureInsight', isMeasurement: true };
@@ -41,7 +43,8 @@ export class UserDataAutoSyncEnablementService extends Disposable {
4143

4244
constructor(
4345
@IStorageService protected readonly storageService: IStorageService,
44-
@IEnvironmentService private readonly environmentService: IEnvironmentService
46+
@IEnvironmentService private readonly environmentService: IEnvironmentService,
47+
@IUserDataSyncStoreManagementService protected readonly userDataSyncStoreManagementService: IUserDataSyncStoreManagementService
4548
) {
4649
super();
4750
this._register(storageService.onDidChangeStorage(e => this.onDidStorageChange(e)));
@@ -58,7 +61,7 @@ export class UserDataAutoSyncEnablementService extends Disposable {
5861
}
5962

6063
canToggleEnablement(): boolean {
61-
return this.environmentService.sync === undefined;
64+
return this.userDataSyncStoreManagementService.userDataSyncStore !== undefined && this.environmentService.sync === undefined;
6265
}
6366

6467
private onDidStorageChange(workspaceStorageChangeEvent: IWorkspaceStorageChangeEvent): void {
@@ -83,7 +86,21 @@ export class UserDataAutoSyncService extends UserDataAutoSyncEnablementService i
8386
private readonly _onError: Emitter<UserDataSyncError> = this._register(new Emitter<UserDataSyncError>());
8487
readonly onError: Event<UserDataSyncError> = this._onError.event;
8588

89+
private lastSyncUrl: URI | undefined;
90+
private get syncUrl(): URI | undefined {
91+
const value = this.storageService.get(storeUrlKey, StorageScope.GLOBAL);
92+
return value ? URI.parse(value) : undefined;
93+
}
94+
private set syncUrl(syncUrl: URI | undefined) {
95+
if (syncUrl) {
96+
this.storageService.store(storeUrlKey, syncUrl.toString(), StorageScope.GLOBAL);
97+
} else {
98+
this.storageService.remove(storeUrlKey, StorageScope.GLOBAL);
99+
}
100+
}
101+
86102
constructor(
103+
@IUserDataSyncStoreManagementService userDataSyncStoreManagementService: IUserDataSyncStoreManagementService,
87104
@IUserDataSyncStoreService private readonly userDataSyncStoreService: IUserDataSyncStoreService,
88105
@IUserDataSyncResourceEnablementService private readonly userDataSyncResourceEnablementService: IUserDataSyncResourceEnablementService,
89106
@IUserDataSyncService private readonly userDataSyncService: IUserDataSyncService,
@@ -94,13 +111,12 @@ export class UserDataAutoSyncService extends UserDataAutoSyncEnablementService i
94111
@IStorageService storageService: IStorageService,
95112
@IEnvironmentService environmentService: IEnvironmentService
96113
) {
97-
super(storageService, environmentService);
114+
super(storageService, environmentService, userDataSyncStoreManagementService);
98115
this.syncTriggerDelayer = this._register(new Delayer<void>(0));
116+
this.lastSyncUrl = this.syncUrl;
117+
this.syncUrl = userDataSyncStoreManagementService.userDataSyncStore?.url;
99118

100-
if (userDataSyncStoreService.userDataSyncStore) {
101-
102-
storageService.store(storeUrlKey, userDataSyncStoreService.userDataSyncStore.url.toString(), StorageScope.GLOBAL);
103-
119+
if (userDataSyncStoreManagementService.userDataSyncStore) {
104120
if (this.isEnabled()) {
105121
this.logService.info('Auto Sync is enabled.');
106122
} else {
@@ -123,7 +139,7 @@ export class UserDataAutoSyncService extends UserDataAutoSyncEnablementService i
123139
const { enabled, message } = this.isAutoSyncEnabled();
124140
if (enabled) {
125141
if (this.autoSync.value === undefined) {
126-
this.autoSync.value = new AutoSync(1000 * 60 * 5 /* 5 miutes */, this.userDataSyncStoreService, this.userDataSyncService, this.userDataSyncMachinesService, this.logService, this.storageService);
142+
this.autoSync.value = new AutoSync(this.lastSyncUrl, 1000 * 60 * 5 /* 5 miutes */, this.userDataSyncStoreManagementService, this.userDataSyncStoreService, this.userDataSyncService, this.userDataSyncMachinesService, this.logService, this.storageService);
127143
this.autoSync.value.register(this.autoSync.value.onDidStartSync(() => this.lastSyncTriggerTime = new Date().getTime()));
128144
this.autoSync.value.register(this.autoSync.value.onDidFinishSync(e => this.onDidFinishSync(e)));
129145
if (this.startAutoSync()) {
@@ -164,6 +180,7 @@ export class UserDataAutoSyncService extends UserDataAutoSyncEnablementService i
164180

165181
async turnOn(): Promise<void> {
166182
this.stopDisableMachineEventually();
183+
this.lastSyncUrl = this.syncUrl;
167184
this.setEnablement(true);
168185
}
169186

@@ -228,7 +245,7 @@ export class UserDataAutoSyncService extends UserDataAutoSyncEnablementService i
228245
}
229246

230247
// Turned off from another device
231-
if (userDataSyncError.code === UserDataSyncErrorCode.TurnedOff) {
248+
else if (userDataSyncError.code === UserDataSyncErrorCode.TurnedOff) {
232249
await this.turnOff(false, true /* force soft turnoff on error */);
233250
this.logService.info('Auto Sync: Turned off sync because sync is turned off in the cloud');
234251
}
@@ -261,6 +278,13 @@ export class UserDataAutoSyncService extends UserDataAutoSyncEnablementService i
261278
this.logService.info('Auto Sync: Turned off sync because server has {0} content with older version than of client. Requires server reset.', userDataSyncError.resource);
262279
}
263280

281+
// Endpoint changed
282+
else if (userDataSyncError.code === UserDataSyncErrorCode.ServiceChanged) {
283+
await this.turnOff(false, true /* force soft turnoff on error */, true /* do not disable machine */);
284+
await this.turnOn();
285+
this.logService.info('Auto Sync: Endpoint changed. Turned off auto sync, reset local state and turned on auto sync.');
286+
}
287+
264288
else {
265289
this.logService.error(userDataSyncError);
266290
this.successiveFailures++;
@@ -342,7 +366,9 @@ class AutoSync extends Disposable {
342366
private syncPromise: CancelablePromise<void> | undefined;
343367

344368
constructor(
369+
private readonly lastSyncUrl: URI | undefined,
345370
private readonly interval: number /* in milliseconds */,
371+
private readonly userDataSyncStoreManagementService: IUserDataSyncStoreManagementService,
346372
private readonly userDataSyncStoreService: IUserDataSyncStoreService,
347373
private readonly userDataSyncService: IUserDataSyncService,
348374
private readonly userDataSyncMachinesService: IUserDataSyncMachinesService,
@@ -407,14 +433,24 @@ class AutoSync extends Disposable {
407433

408434
// Server has no data but this machine was synced before
409435
if (manifest === null && await this.userDataSyncService.hasPreviouslySynced()) {
410-
// Sync was turned off in the cloud
411-
throw new UserDataAutoSyncError(localize('turned off', "Cannot sync because syncing is turned off in the cloud"), UserDataSyncErrorCode.TurnedOff);
436+
if (!this.lastSyncUrl || isEqual(this.lastSyncUrl, this.userDataSyncStoreManagementService.userDataSyncStore?.url)) {
437+
// Sync was turned off in the cloud
438+
throw new UserDataAutoSyncError(localize('turned off', "Cannot sync because syncing is turned off in the cloud"), UserDataSyncErrorCode.TurnedOff);
439+
} else {
440+
// Sync endpoint changed
441+
throw new UserDataAutoSyncError(localize('endpoint changed', "Cannot sync because sync endpoint is changed"), UserDataSyncErrorCode.ServiceChanged);
442+
}
412443
}
413444

414445
const sessionId = this.storageService.get(sessionIdKey, StorageScope.GLOBAL);
415446
// Server session is different from client session
416447
if (sessionId && manifest && sessionId !== manifest.session) {
417-
throw new UserDataAutoSyncError(localize('session expired', "Cannot sync because current session is expired"), UserDataSyncErrorCode.SessionExpired);
448+
if (!this.lastSyncUrl || isEqual(this.lastSyncUrl, this.userDataSyncStoreManagementService.userDataSyncStore?.url)) {
449+
throw new UserDataAutoSyncError(localize('session expired', "Cannot sync because current session is expired"), UserDataSyncErrorCode.SessionExpired);
450+
} else {
451+
// Sync endpoint changed
452+
throw new UserDataAutoSyncError(localize('endpoint changed', "Cannot sync because sync endpoint is changed"), UserDataSyncErrorCode.ServiceChanged);
453+
}
418454
}
419455

420456
const machines = await this.userDataSyncMachinesService.getMachines(manifest || undefined);

src/vs/platform/userDataSync/common/userDataSync.ts

Lines changed: 17 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,11 @@ import { IDisposable } from 'vs/base/common/lifecycle';
1313
import { IJSONContributionRegistry, Extensions as JSONExtensions } from 'vs/platform/jsonschemas/common/jsonContributionRegistry';
1414
import { IJSONSchema } from 'vs/base/common/jsonSchema';
1515
import { ILogService } from 'vs/platform/log/common/log';
16-
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
1716
import { IStringDictionary } from 'vs/base/common/collections';
1817
import { FormattingOptions } from 'vs/base/common/jsonFormatter';
1918
import { URI } from 'vs/base/common/uri';
2019
import { joinPath, isEqualOrParent } from 'vs/base/common/resources';
2120
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
22-
import { IProductService, ConfigurationSyncStore } from 'vs/platform/product/common/productService';
2321
import { distinct } from 'vs/base/common/arrays';
2422
import { isArray, isString, isObject } from 'vs/base/common/types';
2523
import { IHeaders } from 'vs/base/parts/request/common/request';
@@ -110,8 +108,11 @@ export interface IUserData {
110108
export type IAuthenticationProvider = { id: string, scopes: string[] };
111109

112110
export interface IUserDataSyncStore {
113-
url: URI;
114-
authenticationProviders: IAuthenticationProvider[];
111+
readonly url: URI;
112+
readonly defaultUrl: URI;
113+
readonly stableUrl: URI | undefined;
114+
readonly insidersUrl: URI | undefined;
115+
readonly authenticationProviders: IAuthenticationProvider[];
115116
}
116117

117118
export function isAuthenticationProvider(thing: any): thing is IAuthenticationProvider {
@@ -121,27 +122,6 @@ export function isAuthenticationProvider(thing: any): thing is IAuthenticationPr
121122
&& isArray(thing.scopes);
122123
}
123124

124-
export function getUserDataSyncStore(productService: IProductService, configurationService: IConfigurationService): IUserDataSyncStore | undefined {
125-
const value = {
126-
...(productService[CONFIGURATION_SYNC_STORE_KEY] || {}),
127-
...(configurationService.getValue<ConfigurationSyncStore>(CONFIGURATION_SYNC_STORE_KEY) || {})
128-
};
129-
if (value
130-
&& isString(value.url)
131-
&& isObject(value.authenticationProviders)
132-
&& Object.keys(value.authenticationProviders).every(authenticationProviderId => isArray(value.authenticationProviders[authenticationProviderId].scopes))
133-
) {
134-
return {
135-
url: joinPath(URI.parse(value.url), 'v1'),
136-
authenticationProviders: Object.keys(value.authenticationProviders).reduce<IAuthenticationProvider[]>((result, id) => {
137-
result.push({ id, scopes: value.authenticationProviders[id].scopes });
138-
return result;
139-
}, [])
140-
};
141-
}
142-
return undefined;
143-
}
144-
145125
export const enum SyncResource {
146126
Settings = 'settings',
147127
Keybindings = 'keybindings',
@@ -161,11 +141,20 @@ export interface IResourceRefHandle {
161141
created: number;
162142
}
163143

164-
export const IUserDataSyncStoreService = createDecorator<IUserDataSyncStoreService>('IUserDataSyncStoreService');
165144
export type ServerResource = SyncResource | 'machines';
166-
export interface IUserDataSyncStoreService {
145+
export type UserDataSyncStoreType = 'insiders' | 'stable';
146+
147+
export const IUserDataSyncStoreManagementService = createDecorator<IUserDataSyncStoreManagementService>('IUserDataSyncStoreManagementService');
148+
export interface IUserDataSyncStoreManagementService {
167149
readonly _serviceBrand: undefined;
168150
readonly userDataSyncStore: IUserDataSyncStore | undefined;
151+
switch(type: UserDataSyncStoreType): Promise<void>;
152+
getPreviousUserDataSyncStore(): Promise<IUserDataSyncStore | undefined>;
153+
}
154+
155+
export const IUserDataSyncStoreService = createDecorator<IUserDataSyncStoreService>('IUserDataSyncStoreService');
156+
export interface IUserDataSyncStoreService {
157+
readonly _serviceBrand: undefined;
169158

170159
readonly onDidChangeDonotMakeRequestsUntil: Event<void>;
171160
readonly donotMakeRequestsUntil: Date | undefined;
@@ -220,6 +209,7 @@ export enum UserDataSyncErrorCode {
220209
NoRef = 'NoRef',
221210
TurnedOff = 'TurnedOff',
222211
SessionExpired = 'SessionExpired',
212+
ServiceChanged = 'ServiceChanged',
223213
LocalTooManyRequests = 'LocalTooManyRequests',
224214
LocalPreconditionFailed = 'LocalPreconditionFailed',
225215
LocalInvalidContent = 'LocalInvalidContent',

src/vs/platform/userDataSync/common/userDataSyncIpc.ts

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
import { IServerChannel, IChannel, IPCServer } from 'vs/base/parts/ipc/common/ipc';
77
import { Event, Emitter } from 'vs/base/common/event';
8-
import { IUserDataSyncService, IUserDataSyncUtilService, IUserDataAutoSyncService, IManualSyncTask, IUserDataManifest } from 'vs/platform/userDataSync/common/userDataSync';
8+
import { IUserDataSyncService, IUserDataSyncUtilService, IUserDataAutoSyncService, IManualSyncTask, IUserDataManifest, IUserDataSyncStoreManagementService } from 'vs/platform/userDataSync/common/userDataSync';
99
import { URI } from 'vs/base/common/uri';
1010
import { IStringDictionary } from 'vs/base/common/collections';
1111
import { FormattingOptions } from 'vs/base/common/jsonFormatter';
@@ -260,3 +260,18 @@ export class UserDataSyncAccountServiceChannel implements IServerChannel {
260260
}
261261
}
262262

263+
export class UserDataSyncStoreManagementServiceChannel implements IServerChannel {
264+
constructor(private readonly service: IUserDataSyncStoreManagementService) { }
265+
266+
listen(_: unknown, event: string): Event<any> {
267+
throw new Error(`Event not found: ${event}`);
268+
}
269+
270+
call(context: any, command: string, args?: any): Promise<any> {
271+
switch (command) {
272+
case 'switch': return this.service.switch(args[0]);
273+
case 'getPreviousUserDataSyncStore': return this.service.getPreviousUserDataSyncStore();
274+
}
275+
throw new Error('Invalid call');
276+
}
277+
}

0 commit comments

Comments
 (0)