Skip to content

Commit 5225915

Browse files
committed
microsoft#90218 Handle local errors
1 parent 194dae5 commit 5225915

6 files changed

Lines changed: 64 additions & 7 deletions

File tree

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,7 @@ export enum UserDataSyncErrorCode {
187187
// Local Errors
188188
LocalPreconditionFailed = 'LocalPreconditionFailed',
189189
LocalInvalidContent = 'LocalInvalidContent',
190+
LocalError = 'LocalError',
190191
Incompatible = 'Incompatible',
191192

192193
Unknown = 'Unknown',
@@ -293,6 +294,7 @@ export interface IUserDataSyncService {
293294
readonly onDidChangeConflicts: Event<SyncSource[]>;
294295

295296
readonly onDidChangeLocal: Event<void>;
297+
readonly onSyncErrors: Event<[SyncSource, UserDataSyncError][]>;
296298

297299
readonly lastSyncTime: number | undefined;
298300
readonly onDidChangeLastSyncTime: Event<number>;

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ export class UserDataSyncChannel implements IServerChannel {
2020
case 'onDidChangeConflicts': return this.service.onDidChangeConflicts;
2121
case 'onDidChangeLocal': return this.service.onDidChangeLocal;
2222
case 'onDidChangeLastSyncTime': return this.service.onDidChangeLastSyncTime;
23+
case 'onSyncErrors': return this.service.onSyncErrors;
2324
}
2425
throw new Error(`Event not found: ${event}`);
2526
}

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,10 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ
4141
private _onDidChangeConflicts: Emitter<SyncSource[]> = this._register(new Emitter<SyncSource[]>());
4242
readonly onDidChangeConflicts: Event<SyncSource[]> = this._onDidChangeConflicts.event;
4343

44+
private _syncErrors: [SyncSource, UserDataSyncError][] = [];
45+
private _onSyncErrors: Emitter<[SyncSource, UserDataSyncError][]> = this._register(new Emitter<[SyncSource, UserDataSyncError][]>());
46+
readonly onSyncErrors: Event<[SyncSource, UserDataSyncError][]> = this._onSyncErrors.event;
47+
4448
private _lastSyncTime: number | undefined = undefined;
4549
get lastSyncTime(): number | undefined { return this._lastSyncTime; }
4650
private _onDidChangeLastSyncTime: Emitter<number> = this._register(new Emitter<number>());
@@ -101,6 +105,7 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ
101105
await this.checkEnablement();
102106

103107
const startTime = new Date().getTime();
108+
this._syncErrors = [];
104109
try {
105110
this.logService.trace('Sync started.');
106111
if (this.status !== SyncStatus.HasConflicts) {
@@ -126,6 +131,7 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ
126131
await synchroniser.sync(manifest && manifest.latest ? manifest.latest[synchroniser.resourceKey] : undefined);
127132
} catch (e) {
128133
this.handleSyncError(e, synchroniser.source);
134+
this._syncErrors.push([synchroniser.source, UserDataSyncError.toUserDataSyncError(e)]);
129135
}
130136
}
131137

@@ -144,6 +150,7 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ
144150

145151
} finally {
146152
this.updateStatus();
153+
this._onSyncErrors.fire(this._syncErrors);
147154
}
148155
}
149156

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

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,11 +37,11 @@ class UserDataSyncSettingsMigrationContribution implements IWorkbenchContributio
3737
}
3838

3939
private async removeFromConfiguration(): Promise<void> {
40-
await this.configurationService.updateValue('sync.enable', undefined, ConfigurationTarget.USER);
41-
await this.configurationService.updateValue('sync.enableSettings', undefined, ConfigurationTarget.USER);
42-
await this.configurationService.updateValue('sync.enableKeybindings', undefined, ConfigurationTarget.USER);
43-
await this.configurationService.updateValue('sync.enableUIState', undefined, ConfigurationTarget.USER);
44-
await this.configurationService.updateValue('sync.enableExtensions', undefined, ConfigurationTarget.USER);
40+
await this.configurationService.updateValue('sync.enable', undefined, {}, ConfigurationTarget.USER, true);
41+
await this.configurationService.updateValue('sync.enableSettings', undefined, {}, ConfigurationTarget.USER, true);
42+
await this.configurationService.updateValue('sync.enableKeybindings', undefined, {}, ConfigurationTarget.USER, true);
43+
await this.configurationService.updateValue('sync.enableUIState', undefined, {}, ConfigurationTarget.USER, true);
44+
await this.configurationService.updateValue('sync.enableExtensions', undefined, {}, ConfigurationTarget.USER, true);
4545
}
4646
}
4747

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

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo
109109
private readonly syncStatusContext: IContextKey<string>;
110110
private readonly authenticationState: IContextKey<string>;
111111
private readonly conflictsSources: IContextKey<string>;
112-
private readonly conflictsDisposables = new Map<SyncSource, IDisposable>();
112+
113113
private readonly badgeDisposable = this._register(new MutableDisposable());
114114
private readonly signInNotificationDisposable = this._register(new MutableDisposable());
115115
private _activeAccount: AuthenticationSession | undefined;
@@ -149,6 +149,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo
149149
this.onDidChangeEnablement(this.userDataSyncEnablementService.isEnabled());
150150
this._register(Event.debounce(userDataSyncService.onDidChangeStatus, () => undefined, 500)(() => this.onDidChangeSyncStatus(this.userDataSyncService.status)));
151151
this._register(userDataSyncService.onDidChangeConflicts(() => this.onDidChangeConflicts(this.userDataSyncService.conflictsSources)));
152+
this._register(userDataSyncService.onSyncErrors(errors => this.onSyncErrors(errors)));
152153
this._register(this.authTokenService.onTokenFailed(_ => this.authenticationService.getSessions(this.userDataSyncStore!.authenticationProviderId)));
153154
this._register(this.userDataSyncEnablementService.onDidChangeEnablement(enabled => this.onDidChangeEnablement(enabled)));
154155
this._register(this.authenticationService.onDidRegisterAuthenticationProvider(e => this.onDidRegisterAuthenticationProvider(e)));
@@ -268,6 +269,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo
268269
this.updateBadge();
269270
}
270271

272+
private readonly conflictsDisposables = new Map<SyncSource, IDisposable>();
271273
private onDidChangeConflicts(conflicts: SyncSource[]) {
272274
this.updateBadge();
273275
if (conflicts.length) {
@@ -405,7 +407,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo
405407
severity: Severity.Error,
406408
message: localize('too large', "Disabled sync {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, sourceArea, '100kb'),
407409
actions: {
408-
primary: [new Action('open sync file', localize('open file', "Show {0} file", sourceArea), undefined, true,
410+
primary: [new Action('open sync file', localize('open file', "Open {0} file", sourceArea), undefined, true,
409411
() => error.source === SyncSource.Settings ? this.preferencesService.openGlobalSettings(true) : this.preferencesService.openGlobalKeybindingSettings(true))]
410412
}
411413
});
@@ -421,6 +423,47 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo
421423
}
422424
}
423425

426+
private readonly invalidContentErrorDisposables = new Map<SyncSource, IDisposable>();
427+
private onSyncErrors(errors: [SyncSource, UserDataSyncError][]): void {
428+
if (errors.length) {
429+
for (const [source, error] of errors) {
430+
switch (error.code) {
431+
case UserDataSyncErrorCode.LocalInvalidContent:
432+
this.handleInvalidContentError(source);
433+
break;
434+
default:
435+
const disposable = this.invalidContentErrorDisposables.get(source);
436+
if (disposable) {
437+
disposable.dispose();
438+
this.invalidContentErrorDisposables.delete(source);
439+
}
440+
}
441+
}
442+
} else {
443+
this.invalidContentErrorDisposables.forEach(disposable => disposable.dispose());
444+
this.invalidContentErrorDisposables.clear();
445+
}
446+
}
447+
448+
private handleInvalidContentError(source: SyncSource): void {
449+
if (!this.invalidContentErrorDisposables.has(source)) {
450+
const errorArea = getSyncAreaLabel(source);
451+
const handle = this.notificationService.notify({
452+
severity: Severity.Error,
453+
message: localize('errorInvalidConfiguration', "Unable to sync {0} because there are some errors/warnings in the file. Please open the file to correct errors/warnings in it.", errorArea),
454+
actions: {
455+
primary: [new Action('open sync file', localize('open file', "Open {0} file", errorArea), undefined, true,
456+
() => source === SyncSource.Settings ? this.preferencesService.openGlobalSettings(true) : this.preferencesService.openGlobalKeybindingSettings(true))]
457+
}
458+
});
459+
this.invalidContentErrorDisposables.set(source, toDisposable(() => {
460+
// close the error warning notification
461+
handle.close();
462+
this.invalidContentErrorDisposables.delete(source);
463+
}));
464+
}
465+
}
466+
424467
private async updateBadge(): Promise<void> {
425468
this.badgeDisposable.clear();
426469

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,9 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ
3434
private _onDidChangeLastSyncTime: Emitter<number> = this._register(new Emitter<number>());
3535
readonly onDidChangeLastSyncTime: Event<number> = this._onDidChangeLastSyncTime.event;
3636

37+
private _onSyncErrors: Emitter<[SyncSource, UserDataSyncError][]> = this._register(new Emitter<[SyncSource, UserDataSyncError][]>());
38+
readonly onSyncErrors: Event<[SyncSource, UserDataSyncError][]> = this._onSyncErrors.event;
39+
3740
constructor(
3841
@ISharedProcessService sharedProcessService: ISharedProcessService
3942
) {
@@ -58,6 +61,7 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ
5861
this._register(this.channel.listen<number>('onDidChangeLastSyncTime')(lastSyncTime => this.updateLastSyncTime(lastSyncTime)));
5962
});
6063
this._register(this.channel.listen<SyncSource[]>('onDidChangeConflicts')(conflicts => this.updateConflicts(conflicts)));
64+
this._register(this.channel.listen<[SyncSource, Error][]>('onSyncErrors')(errors => this._onSyncErrors.fire(errors.map(([source, error]) => ([source, UserDataSyncError.toUserDataSyncError(error)])))));
6165
}
6266

6367
pull(): Promise<void> {

0 commit comments

Comments
 (0)