Merge from vscode 3c6f6af7347d38e87bc6406024e8dcf9e9bce229 (#8962)

* Merge from vscode 3c6f6af7347d38e87bc6406024e8dcf9e9bce229

* skip failing tests

* update mac build image
This commit is contained in:
Anthony Dresser
2020-01-27 15:28:17 -08:00
committed by Karl Burtram
parent 0eaee18dc4
commit fefe1454de
481 changed files with 12764 additions and 7836 deletions

View File

@@ -3,22 +3,27 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IUserDataSyncService, SyncStatus, ISynchroniser, IUserDataSyncStoreService, SyncSource, ISettingsSyncService, IUserDataSyncLogService, IUserDataAuthTokenService } from 'vs/platform/userDataSync/common/userDataSync';
import { IUserDataSyncService, SyncStatus, ISynchroniser, IUserDataSyncStoreService, SyncSource, ISettingsSyncService, IUserDataSyncLogService, IUserDataAuthTokenService, IUserDataSynchroniser } from 'vs/platform/userDataSync/common/userDataSync';
import { Disposable } from 'vs/base/common/lifecycle';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { SettingsSynchroniser } from 'vs/platform/userDataSync/common/settingsSync';
import { Emitter, Event } from 'vs/base/common/event';
import { ExtensionsSynchroniser } from 'vs/platform/userDataSync/common/extensionsSync';
import { IExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
import { KeybindingsSynchroniser } from 'vs/platform/userDataSync/common/keybindingsSync';
import { GlobalStateSynchroniser } from 'vs/platform/userDataSync/common/globalStateSync';
import { toErrorMessage } from 'vs/base/common/errorMessage';
import { localize } from 'vs/nls';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
type SyncConflictsClassification = {
source?: { classification: 'SystemMetaData', purpose: 'FeatureInsight', isMeasurement: true };
};
export class UserDataSyncService extends Disposable implements IUserDataSyncService {
_serviceBrand: any;
private readonly synchronisers: ISynchroniser[];
private readonly synchronisers: IUserDataSynchroniser[];
private _status: SyncStatus = SyncStatus.Uninitialized;
get status(): SyncStatus { return this._status; }
@@ -40,6 +45,7 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ
@ISettingsSyncService private readonly settingsSynchroniser: ISettingsSyncService,
@IUserDataSyncLogService private readonly logService: IUserDataSyncLogService,
@IUserDataAuthTokenService private readonly userDataAuthTokenService: IUserDataAuthTokenService,
@ITelemetryService private readonly telemetryService: ITelemetryService,
) {
super();
this.keybindingsSynchroniser = this._register(this.instantiationService.createInstance(KeybindingsSynchroniser));
@@ -88,31 +94,57 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ
}
}
async sync(_continue?: boolean): Promise<boolean> {
async sync(): Promise<void> {
if (!this.userDataSyncStoreService.userDataSyncStore) {
throw new Error('Not enabled');
}
if (!(await this.userDataAuthTokenService.getToken())) {
throw new Error('Not Authenticated. Please sign in to start sync.');
}
if (this.status === SyncStatus.HasConflicts) {
throw new Error(localize('resolve conflicts', "Please resolve conflicts before resuming sync."));
}
for (const synchroniser of this.synchronisers) {
try {
if (!await synchroniser.sync(_continue)) {
return false;
await synchroniser.sync();
// do not continue if synchroniser has conflicts
if (synchroniser.status === SyncStatus.HasConflicts) {
return;
}
} catch (e) {
this.logService.error(`${this.getSyncSource(synchroniser)}: ${toErrorMessage(e)}`);
}
}
return true;
}
stop(): void {
async resolveConflictsAndContinueSync(content: string): Promise<void> {
const synchroniser = this.getSynchroniserInConflicts();
if (!synchroniser) {
throw new Error(localize('no synchroniser with conflicts', "No conflicts detected."));
}
await synchroniser.resolveConflicts(content);
if (synchroniser.status !== SyncStatus.HasConflicts) {
await this.sync();
}
}
async stop(): Promise<void> {
if (!this.userDataSyncStoreService.userDataSyncStore) {
throw new Error('Not enabled');
}
for (const synchroniser of this.synchronisers) {
synchroniser.stop();
await synchroniser.stop();
}
}
async restart(): Promise<void> {
const synchroniser = this.getSynchroniserInConflicts();
if (!synchroniser) {
throw new Error(localize('no synchroniser with conflicts', "No conflicts detected."));
}
await synchroniser.restart();
if (synchroniser.status !== SyncStatus.HasConflicts) {
await this.sync();
}
}
@@ -161,6 +193,15 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ
return false;
}
async getRemoteContent(source: SyncSource): Promise<string | null> {
for (const synchroniser of this.synchronisers) {
if (synchroniser.source === source) {
return synchroniser.getRemoteContent();
}
}
return null;
}
async isFirstTimeSyncAndHasUserData(): Promise<boolean> {
if (!this.userDataSyncStoreService.userDataSyncStore) {
throw new Error('Not enabled');
@@ -211,18 +252,21 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ
this.logService.info('Completed resetting local cache');
}
removeExtension(identifier: IExtensionIdentifier): Promise<void> {
return this.extensionsSynchroniser.removeExtension(identifier);
}
private updateStatus(): void {
this._conflictsSource = this.computeConflictsSource();
this.setStatus(this.computeStatus());
}
private setStatus(status: SyncStatus): void {
const status = this.computeStatus();
if (this._status !== status) {
const oldStatus = this._status;
const oldConflictsSource = this._conflictsSource;
this._conflictsSource = this.computeConflictsSource();
this._status = status;
if (status === SyncStatus.HasConflicts) {
// Log to telemetry when there is a sync conflict
this.telemetryService.publicLog2<{ source: string }, SyncConflictsClassification>('sync/conflictsDetected', { source: this._conflictsSource! });
}
if (oldStatus === SyncStatus.HasConflicts && status === SyncStatus.Idle) {
// Log to telemetry when conflicts are resolved
this.telemetryService.publicLog2<{ source: string }, SyncConflictsClassification>('sync/conflictsResolved', { source: oldConflictsSource! });
}
this._onDidChangeStatus.fire(status);
}
}
@@ -245,6 +289,11 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ
return synchroniser ? this.getSyncSource(synchroniser) : null;
}
private getSynchroniserInConflicts(): IUserDataSynchroniser | null {
const synchroniser = this.synchronisers.filter(s => s.status === SyncStatus.HasConflicts)[0];
return synchroniser || null;
}
private getSyncSource(synchroniser: ISynchroniser): SyncSource {
if (synchroniser instanceof SettingsSynchroniser) {
return SyncSource.Settings;
@@ -255,7 +304,7 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ
if (synchroniser instanceof ExtensionsSynchroniser) {
return SyncSource.Extensions;
}
return SyncSource.UIState;
return SyncSource.GlobalState;
}
private onDidChangeAuthTokenStatus(token: string | undefined): void {