Skip to content

Commit fd455c3

Browse files
committed
1 parent cf0094f commit fd455c3

10 files changed

Lines changed: 115 additions & 37 deletions

File tree

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -311,7 +311,7 @@ export abstract class AbstractSynchroniser extends Disposable {
311311
if (remoteUserData.syncData && remoteUserData.syncData.version > this.version) {
312312
// current version is not compatible with cloud version
313313
this.telemetryService.publicLog2<{ source: string }, SyncSourceClassification>('sync/incompatible', { source: this.resource });
314-
throw new UserDataSyncError(localize({ key: 'incompatible', comment: ['This is an error while syncing a resource that its local version is not compatible with its remote version.'] }, "Cannot sync {0} as its local version {1} is not compatible with its remote version {2}", this.resource, this.version, remoteUserData.syncData.version), UserDataSyncErrorCode.Incompatible, this.resource);
314+
throw new UserDataSyncError(localize({ key: 'incompatible', comment: ['This is an error while syncing a resource that its local version is not compatible with its remote version.'] }, "Cannot sync {0} as its local version {1} is not compatible with its remote version {2}", this.resource, this.version, remoteUserData.syncData.version), UserDataSyncErrorCode.IncompatibleLocalContent, this.resource);
315315
}
316316

317317
try {
@@ -626,7 +626,7 @@ export abstract class AbstractSynchroniser extends Disposable {
626626
} catch (error) {
627627
this.logService.error(error);
628628
}
629-
throw new UserDataSyncError(localize('incompatible sync data', "Cannot parse sync data as it is not compatible with current version."), UserDataSyncErrorCode.Incompatible, this.resource);
629+
throw new UserDataSyncError(localize('incompatible sync data', "Cannot parse sync data as it is not compatible with current version."), UserDataSyncErrorCode.IncompatibleRemoteContent, this.resource);
630630
}
631631

632632
private async getUserData(refOrLastSyncData: string | IRemoteUserData | null): Promise<IUserData> {

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

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,26 @@ export class UserDataAutoSyncService extends UserDataAutoSyncEnablementService i
218218
this.logService.info('Auto Sync: Turned off sync because of making too many requests to server');
219219
}
220220

221+
// Upgrade Required or Gone
222+
else if (userDataSyncError.code === UserDataSyncErrorCode.UpgradeRequired || userDataSyncError.code === UserDataSyncErrorCode.Gone) {
223+
await this.turnOff(false, true /* force soft turnoff on error */,
224+
true /* do not disable machine because disabling a machine makes request to server and can fail with upgrade required or gone */);
225+
this.disableMachineEventually();
226+
this.logService.info('Auto Sync: Turned off sync because current client is not compatible with server. Requires client upgrade.');
227+
}
228+
229+
// Incompatible Local Content
230+
else if (userDataSyncError.code === UserDataSyncErrorCode.IncompatibleLocalContent) {
231+
await this.turnOff(false, true /* force soft turnoff on error */);
232+
this.logService.info('Auto Sync: Turned off sync because server has {0} content with newer version than of client. Requires client upgrade.', userDataSyncError.resource);
233+
}
234+
235+
// Incompatible Remote Content
236+
else if (userDataSyncError.code === UserDataSyncErrorCode.IncompatibleRemoteContent) {
237+
await this.turnOff(false, true /* force soft turnoff on error */);
238+
this.logService.info('Auto Sync: Turned off sync because server has {0} content with older version than of client. Requires server reset.', userDataSyncError.resource);
239+
}
240+
221241
else {
222242
this.logService.error(userDataSyncError);
223243
this.successiveFailures++;

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -217,7 +217,8 @@ export enum UserDataSyncErrorCode {
217217
LocalPreconditionFailed = 'LocalPreconditionFailed',
218218
LocalInvalidContent = 'LocalInvalidContent',
219219
LocalError = 'LocalError',
220-
Incompatible = 'Incompatible',
220+
IncompatibleLocalContent = 'IncompatibleLocalContent',
221+
IncompatibleRemoteContent = 'IncompatibleRemoteContent',
221222
UnresolvedConflicts = 'UnresolvedConflicts',
222223

223224
Unknown = 'Unknown',
@@ -415,6 +416,7 @@ export interface IUserDataSyncService {
415416
pull(): Promise<void>;
416417
replace(uri: URI): Promise<void>;
417418
reset(): Promise<void>;
419+
resetRemote(): Promise<void>;
418420
resetLocal(): Promise<void>;
419421

420422
hasLocalData(): Promise<boolean>;

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ export class UserDataSyncChannel implements IServerChannel {
4949
case 'pull': return this.service.pull();
5050
case 'replace': return this.service.replace(URI.revive(args[0]));
5151
case 'reset': return this.service.reset();
52+
case 'resetRemote': return this.service.resetRemote();
5253
case 'resetLocal': return this.service.resetLocal();
5354
case 'hasPreviouslySynced': return this.service.hasPreviouslySynced();
5455
case 'hasLocalData': return this.service.hasLocalData();

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

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,16 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ
313313
await this.resetLocal();
314314
}
315315

316+
async resetRemote(): Promise<void> {
317+
await this.checkEnablement();
318+
try {
319+
await this.userDataSyncStoreService.clear();
320+
this.logService.info('Cleared data on server');
321+
} catch (e) {
322+
this.logService.error(e);
323+
}
324+
}
325+
316326
async resetLocal(): Promise<void> {
317327
await this.checkEnablement();
318328
this.storageService.remove(LAST_SYNC_TIME_KEY, StorageScope.GLOBAL);
@@ -336,16 +346,6 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ
336346
return false;
337347
}
338348

339-
private async resetRemote(): Promise<void> {
340-
await this.checkEnablement();
341-
try {
342-
await this.userDataSyncStoreService.clear();
343-
this.logService.info('Cleared data on server');
344-
} catch (e) {
345-
this.logService.error(e);
346-
}
347-
}
348-
349349
private setStatus(status: SyncStatus): void {
350350
const oldStatus = this._status;
351351
if (this._status !== status) {
@@ -402,7 +402,8 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ
402402
case UserDataSyncErrorCode.LocalTooManyRequests:
403403
case UserDataSyncErrorCode.Gone:
404404
case UserDataSyncErrorCode.UpgradeRequired:
405-
case UserDataSyncErrorCode.Incompatible:
405+
case UserDataSyncErrorCode.IncompatibleRemoteContent:
406+
case UserDataSyncErrorCode.IncompatibleLocalContent:
406407
throw e;
407408
}
408409
}

src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo
119119
@IStorageService private readonly storageService: IStorageService,
120120
@IOpenerService private readonly openerService: IOpenerService,
121121
@IAuthenticationService private readonly authenticationService: IAuthenticationService,
122+
@ICommandService private readonly commandService: ICommandService,
122123
) {
123124
super();
124125

@@ -294,17 +295,28 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo
294295
this.handleTooLargeError(error.resource, localize('too large', "Disabled syncing {0} because size of the {1} file to sync is larger than {2}. Please open the file and reduce the size and enable sync", sourceArea.toLowerCase(), sourceArea.toLowerCase(), '100kb'), error);
295296
}
296297
break;
297-
case UserDataSyncErrorCode.Incompatible:
298+
case UserDataSyncErrorCode.IncompatibleLocalContent:
298299
case UserDataSyncErrorCode.Gone:
299300
case UserDataSyncErrorCode.UpgradeRequired:
300-
this.userDataSyncWorkbenchService.turnoff(false);
301301
const message = localize('error upgrade required', "Preferences sync is disabled because the current version ({0}, {1}) is not compatible with the sync service. Please update before turning on sync.", this.productService.version, this.productService.commit);
302302
const operationId = error.operationId ? localize('operationId', "Operation Id: {0}", error.operationId) : undefined;
303303
this.notificationService.notify({
304304
severity: Severity.Error,
305305
message: operationId ? `${message} ${operationId}` : message,
306306
});
307307
break;
308+
case UserDataSyncErrorCode.IncompatibleRemoteContent:
309+
this.notificationService.notify({
310+
severity: Severity.Error,
311+
message: localize('error reset required', "Preferences sync is disabled because your data in the cloud is older than that of in the client. Please reset your data in the cloud before turning on sync."),
312+
actions: {
313+
primary: [
314+
new Action('reset', localize('reset', "Reset Synced Data"), undefined, true, () => this.userDataSyncWorkbenchService.resetSyncedData()),
315+
new Action('show synced data', localize('show synced data action', "Show Synced Data"), undefined, true, () => this.commandService.executeCommand(SHOW_SYNCED_DATA_COMMAND_ID))
316+
]
317+
}
318+
});
319+
return;
308320
}
309321
}
310322

@@ -439,7 +451,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo
439451
return;
440452
}
441453
break;
442-
case UserDataSyncErrorCode.Incompatible:
454+
case UserDataSyncErrorCode.IncompatibleLocalContent:
443455
case UserDataSyncErrorCode.Gone:
444456
case UserDataSyncErrorCode.UpgradeRequired:
445457
const message = localize('error upgrade required while starting sync', "Preferences sync cannot be turned on because the current version ({0}, {1}) is not compatible with the sync service. Please update before turning on sync.", this.productService.version, this.productService.commit);
@@ -449,6 +461,18 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo
449461
message: operationId ? `${message} ${operationId}` : message,
450462
});
451463
return;
464+
case UserDataSyncErrorCode.IncompatibleRemoteContent:
465+
this.notificationService.notify({
466+
severity: Severity.Error,
467+
message: localize('error reset required while starting sync', "Preferences sync cannot be turned on because your data in the cloud is older than that of in the client. Please reset your data in the cloud before turning on sync."),
468+
actions: {
469+
primary: [
470+
new Action('reset', localize('reset', "Reset Synced Data"), undefined, true, () => this.userDataSyncWorkbenchService.resetSyncedData()),
471+
new Action('show synced data', localize('show synced data action', "Show Synced Data"), undefined, true, () => this.commandService.executeCommand(SHOW_SYNCED_DATA_COMMAND_ID))
472+
]
473+
}
474+
});
475+
return;
452476
}
453477
}
454478
this.notificationService.error(localize('turn on failed', "Error while starting Sync: {0}", toErrorMessage(e)));
@@ -946,7 +970,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo
946970
that.viewsEnablementContext.set(true);
947971
const viewDescriptorService = accessor.get(IViewDescriptorService);
948972
const viewsService = accessor.get(IViewsService);
949-
const viewContainer = viewDescriptorService.getViewContainerByViewId(viewContainerId);
973+
const viewContainer = viewDescriptorService.getViewContainerById(viewContainerId);
950974
if (viewContainer) {
951975
const model = viewDescriptorService.getViewContainerModel(viewContainer);
952976
if (model.activeViewDescriptors.length) {

src/vs/workbench/contrib/userDataSync/browser/userDataSyncViews.ts

Lines changed: 22 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import { localize } from 'vs/nls';
99
import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
1010
import { TreeViewPane } from 'vs/workbench/browser/parts/views/treeView';
1111
import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
12-
import { ALL_SYNC_RESOURCES, SyncResource, IUserDataSyncService, ISyncResourceHandle as IResourceHandle, SyncStatus, IUserDataSyncResourceEnablementService, IUserDataAutoSyncService, Change } from 'vs/platform/userDataSync/common/userDataSync';
12+
import { ALL_SYNC_RESOURCES, SyncResource, IUserDataSyncService, ISyncResourceHandle as IResourceHandle, SyncStatus, IUserDataSyncResourceEnablementService, IUserDataAutoSyncService, Change, UserDataSyncError, UserDataSyncErrorCode } from 'vs/platform/userDataSync/common/userDataSync';
1313
import { registerAction2, Action2, MenuId } from 'vs/platform/actions/common/actions';
1414
import { ContextKeyExpr, ContextKeyEqualsExpr } from 'vs/platform/contextkey/common/contextkey';
1515
import { URI } from 'vs/base/common/uri';
@@ -33,7 +33,7 @@ import { IAction, Action } from 'vs/base/common/actions';
3333
import { IUserDataSyncWorkbenchService, CONTEXT_SYNC_STATE, getSyncAreaLabel, CONTEXT_ACCOUNT_STATE, AccountStatus, CONTEXT_ENABLE_VIEWS, SHOW_SYNC_LOG_COMMAND_ID, CONFIGURE_SYNC_COMMAND_ID, CONTEXT_SHOW_MANUAL_SYNC_VIEW, IUserDataSyncPreview, IUserDataSyncResourceGroup } from 'vs/workbench/services/userDataSync/common/userDataSync';
3434
import { IUserDataSyncMachinesService, IUserDataSyncMachine } from 'vs/platform/userDataSync/common/userDataSyncMachines';
3535
import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput';
36-
import { INotificationService } from 'vs/platform/notification/common/notification';
36+
import { INotificationService, Severity } from 'vs/platform/notification/common/notification';
3737
import { TreeView } from 'vs/workbench/contrib/views/browser/treeView';
3838
import { flatten } from 'vs/base/common/arrays';
3939
import { isEqual, basename } from 'vs/base/common/resources';
@@ -42,7 +42,6 @@ export class UserDataSyncViewPaneContainer extends ViewPaneContainer {
4242

4343
constructor(
4444
containerId: string,
45-
@IDialogService private readonly dialogService: IDialogService,
4645
@IUserDataSyncWorkbenchService private readonly userDataSyncWorkbenchService: IUserDataSyncWorkbenchService,
4746
@ICommandService private readonly commandService: ICommandService,
4847
@IWorkbenchLayoutService layoutService: IWorkbenchLayoutService,
@@ -68,22 +67,10 @@ export class UserDataSyncViewPaneContainer extends ViewPaneContainer {
6867

6968
getSecondaryActions(): IAction[] {
7069
return [
71-
new Action('workbench.actions.syncData.reset', localize('workbench.actions.syncData.reset', "Reset Synced Data"), undefined, true, () => this.reset()),
70+
new Action('workbench.actions.syncData.reset', localize('workbench.actions.syncData.reset', "Reset Synced Data"), undefined, true, () => this.userDataSyncWorkbenchService.resetSyncedData()),
7271
];
7372
}
7473

75-
private async reset(): Promise<void> {
76-
const result = await this.dialogService.confirm({
77-
message: localize('reset', "This will clear your synced data from the cloud and stop sync on all your devices."),
78-
title: localize('reset title', "Reset Synced Data"),
79-
type: 'info',
80-
primaryButton: localize('reset button', "Reset"),
81-
});
82-
if (result.confirmed) {
83-
await this.userDataSyncWorkbenchService.turnoff(true);
84-
}
85-
}
86-
8774
}
8875

8976
export class UserDataSyncDataViews extends Disposable {
@@ -626,6 +613,7 @@ abstract class UserDataSyncActivityViewDataProvider implements ITreeViewDataProv
626613
constructor(
627614
@IUserDataSyncService protected readonly userDataSyncService: IUserDataSyncService,
628615
@IUserDataAutoSyncService protected readonly userDataAutoSyncService: IUserDataAutoSyncService,
616+
@IUserDataSyncWorkbenchService private readonly userDataSyncWorkbenchService: IUserDataSyncWorkbenchService,
629617
@INotificationService private readonly notificationService: INotificationService,
630618
) { }
631619

@@ -639,7 +627,22 @@ abstract class UserDataSyncActivityViewDataProvider implements ITreeViewDataProv
639627
}
640628
return [];
641629
} catch (error) {
642-
this.notificationService.error(error);
630+
if (!(error instanceof UserDataSyncError)) {
631+
error = UserDataSyncError.toUserDataSyncError(error);
632+
}
633+
if (error instanceof UserDataSyncError && error.code === UserDataSyncErrorCode.IncompatibleRemoteContent) {
634+
this.notificationService.notify({
635+
severity: Severity.Error,
636+
message: error.message,
637+
actions: {
638+
primary: [
639+
new Action('reset', localize('reset', "Reset Synced Data"), undefined, true, () => this.userDataSyncWorkbenchService.resetSyncedData()),
640+
]
641+
}
642+
});
643+
} else {
644+
this.notificationService.error(error);
645+
}
643646
throw error;
644647
}
645648
}
@@ -705,9 +708,10 @@ class RemoteUserDataSyncActivityViewDataProvider extends UserDataSyncActivityVie
705708
@IUserDataSyncService userDataSyncService: IUserDataSyncService,
706709
@IUserDataAutoSyncService userDataAutoSyncService: IUserDataAutoSyncService,
707710
@IUserDataSyncMachinesService private readonly userDataSyncMachinesService: IUserDataSyncMachinesService,
711+
@IUserDataSyncWorkbenchService userDataSyncWorkbenchService: IUserDataSyncWorkbenchService,
708712
@INotificationService notificationService: INotificationService,
709713
) {
710-
super(userDataSyncService, userDataAutoSyncService, notificationService);
714+
super(userDataSyncService, userDataAutoSyncService, userDataSyncWorkbenchService, notificationService);
711715
}
712716

713717
async getChildren(element?: ITreeItem): Promise<ITreeItem[]> {

src/vs/workbench/services/userDataSync/browser/userDataSyncWorkbenchService.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -357,6 +357,18 @@ export class UserDataSyncWorkbenchService extends Disposable implements IUserDat
357357
}
358358
}
359359

360+
async resetSyncedData(): Promise<void> {
361+
const result = await this.dialogService.confirm({
362+
message: localize('reset', "This will clear your synced data from the cloud and stop sync on all your devices."),
363+
title: localize('reset title', "Reset Synced Data"),
364+
type: 'info',
365+
primaryButton: localize('reset button', "Reset"),
366+
});
367+
if (result.confirmed) {
368+
await this.userDataSyncService.resetRemote();
369+
}
370+
}
371+
360372
private isSupportedAuthenticationProviderId(authenticationProviderId: string): boolean {
361373
return this.authenticationProviders.some(({ id }) => id === authenticationProviderId);
362374
}

src/vs/workbench/services/userDataSync/common/userDataSync.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ export interface IUserDataSyncWorkbenchService {
5454
turnOn(): Promise<void>;
5555
turnoff(everyWhere: boolean): Promise<void>;
5656
signIn(): Promise<void>;
57+
resetSyncedData(): Promise<void>;
5758
}
5859

5960
export function getSyncAreaLabel(source: SyncResource): string {

src/vs/workbench/services/userDataSync/electron-browser/userDataSyncService.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,10 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ
8686
return this.channel.call('reset');
8787
}
8888

89+
resetRemote(): Promise<void> {
90+
return this.channel.call('resetRemote');
91+
}
92+
8993
resetLocal(): Promise<void> {
9094
return this.channel.call('resetLocal');
9195
}
@@ -165,7 +169,16 @@ class ManualSyncTask implements IManualSyncTask {
165169
readonly manifest: IUserDataManifest | null,
166170
sharedProcessService: ISharedProcessService,
167171
) {
168-
this.channel = sharedProcessService.getChannel(`manualSyncTask-${id}`);
172+
const manualSyncTaskChannel = sharedProcessService.getChannel(`manualSyncTask-${id}`);
173+
this.channel = {
174+
call<T>(command: string, arg?: any, cancellationToken?: CancellationToken): Promise<T> {
175+
return manualSyncTaskChannel.call(command, arg, cancellationToken)
176+
.then(null, error => { throw UserDataSyncError.toUserDataSyncError(error); });
177+
},
178+
listen<T>(event: string, arg?: any): Event<T> {
179+
return manualSyncTaskChannel.listen(event, arg);
180+
}
181+
};
169182
}
170183

171184
async preview(): Promise<[SyncResource, ISyncResourcePreview][]> {

0 commit comments

Comments
 (0)