mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-01-29 09:35:38 -05:00
Merge from vscode 8a997f7321ae6612fc0e6eb3eac4f358a6233bfb
This commit is contained in:
@@ -3,7 +3,7 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { IUserDataSyncService, SyncStatus, IUserDataSyncStoreService, SyncSource, ISettingsSyncService, IUserDataSyncLogService, IUserDataAuthTokenService, IUserDataSynchroniser, UserDataSyncStoreError, UserDataSyncErrorCode } from 'vs/platform/userDataSync/common/userDataSync';
|
||||
import { IUserDataSyncService, SyncStatus, IUserDataSyncStoreService, SyncSource, ISettingsSyncService, IUserDataSyncLogService, IUserDataAuthTokenService, IUserDataSynchroniser, UserDataSyncStoreError, UserDataSyncErrorCode, UserDataSyncError } from 'vs/platform/userDataSync/common/userDataSync';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
@@ -11,12 +11,9 @@ import { ExtensionsSynchroniser } from 'vs/platform/userDataSync/common/extensio
|
||||
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 };
|
||||
};
|
||||
import { equals } from 'vs/base/common/arrays';
|
||||
import { localize } from 'vs/nls';
|
||||
|
||||
type SyncErrorClassification = {
|
||||
source: { classification: 'SystemMetaData', purpose: 'FeatureInsight', isMeasurement: true };
|
||||
@@ -35,8 +32,10 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ
|
||||
|
||||
readonly onDidChangeLocal: Event<void>;
|
||||
|
||||
private _conflictsSource: SyncSource | null = null;
|
||||
get conflictsSource(): SyncSource | null { return this._conflictsSource; }
|
||||
private _conflictsSources: SyncSource[] = [];
|
||||
get conflictsSources(): SyncSource[] { return this._conflictsSources; }
|
||||
private _onDidChangeConflicts: Emitter<SyncSource[]> = this._register(new Emitter<SyncSource[]>());
|
||||
readonly onDidChangeConflicts: Event<SyncSource[]> = this._onDidChangeConflicts.event;
|
||||
|
||||
private readonly keybindingsSynchroniser: KeybindingsSynchroniser;
|
||||
private readonly extensionsSynchroniser: ExtensionsSynchroniser;
|
||||
@@ -66,12 +65,7 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ
|
||||
}
|
||||
|
||||
async pull(): 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.');
|
||||
}
|
||||
await this.checkEnablement();
|
||||
for (const synchroniser of this.synchronisers) {
|
||||
try {
|
||||
await synchroniser.pull();
|
||||
@@ -82,12 +76,7 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ
|
||||
}
|
||||
|
||||
async push(): 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.');
|
||||
}
|
||||
await this.checkEnablement();
|
||||
for (const synchroniser of this.synchronisers) {
|
||||
try {
|
||||
await synchroniser.push();
|
||||
@@ -98,105 +87,79 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ
|
||||
}
|
||||
|
||||
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."));
|
||||
}
|
||||
const startTime = new Date().getTime();
|
||||
this.logService.trace('Started Syncing...');
|
||||
for (const synchroniser of this.synchronisers) {
|
||||
try {
|
||||
await synchroniser.sync();
|
||||
// do not continue if synchroniser has conflicts
|
||||
if (synchroniser.status === SyncStatus.HasConflicts) {
|
||||
break;
|
||||
}
|
||||
} catch (e) {
|
||||
this.handleSyncError(e, synchroniser.source);
|
||||
}
|
||||
}
|
||||
this.logService.trace(`Finished Syncing. Took ${new Date().getTime() - startTime}ms`);
|
||||
}
|
||||
await this.checkEnablement();
|
||||
|
||||
async accept(source: SyncSource, content: string): Promise<void> {
|
||||
const synchroniser = this.getSynchroniser(source);
|
||||
await synchroniser.accept(content);
|
||||
if (synchroniser.status !== SyncStatus.HasConflicts) {
|
||||
await this.sync();
|
||||
const startTime = new Date().getTime();
|
||||
try {
|
||||
this.logService.trace('Sync started.');
|
||||
if (this.status !== SyncStatus.HasConflicts) {
|
||||
this.setStatus(SyncStatus.Syncing);
|
||||
}
|
||||
|
||||
const manifest = await this.userDataSyncStoreService.manifest();
|
||||
|
||||
// Server has no data but this machine was synced before
|
||||
if (manifest === null && await this.hasPreviouslySynced()) {
|
||||
// Sync was turned off from other machine
|
||||
throw new UserDataSyncError(localize('turned off', "Cannot sync because syncing is turned off in the cloud"), UserDataSyncErrorCode.TurnedOff);
|
||||
}
|
||||
|
||||
for (const synchroniser of this.synchronisers) {
|
||||
try {
|
||||
await synchroniser.sync(manifest ? manifest[synchroniser.resourceKey] : undefined);
|
||||
} catch (e) {
|
||||
this.handleSyncError(e, synchroniser.source);
|
||||
}
|
||||
}
|
||||
|
||||
this.logService.info(`Sync done. Took ${new Date().getTime() - startTime}ms`);
|
||||
|
||||
} finally {
|
||||
this.updateStatus();
|
||||
}
|
||||
}
|
||||
|
||||
async stop(): Promise<void> {
|
||||
if (!this.userDataSyncStoreService.userDataSyncStore) {
|
||||
throw new Error('Not enabled');
|
||||
if (this.status === SyncStatus.Idle) {
|
||||
return;
|
||||
}
|
||||
for (const synchroniser of this.synchronisers) {
|
||||
await synchroniser.stop();
|
||||
try {
|
||||
if (synchroniser.status !== SyncStatus.Idle) {
|
||||
await synchroniser.stop();
|
||||
}
|
||||
} catch (e) {
|
||||
this.logService.error(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
async accept(source: SyncSource, content: string): Promise<void> {
|
||||
await this.checkEnablement();
|
||||
const synchroniser = this.getSynchroniser(source);
|
||||
return synchroniser.accept(content);
|
||||
}
|
||||
|
||||
async hasPreviouslySynced(): Promise<boolean> {
|
||||
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.');
|
||||
}
|
||||
await this.checkEnablement();
|
||||
for (const synchroniser of this.synchronisers) {
|
||||
if (await synchroniser.hasPreviouslySynced()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
async hasRemoteData(): Promise<boolean> {
|
||||
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.');
|
||||
}
|
||||
for (const synchroniser of this.synchronisers) {
|
||||
if (await synchroniser.hasRemoteData()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
async hasLocalData(): Promise<boolean> {
|
||||
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.');
|
||||
}
|
||||
private async hasLocalData(): Promise<boolean> {
|
||||
await this.checkEnablement();
|
||||
for (const synchroniser of this.synchronisers) {
|
||||
if (await synchroniser.hasLocalData()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
async getRemoteContent(source: SyncSource, preview: boolean): Promise<string | null> {
|
||||
await this.checkEnablement();
|
||||
for (const synchroniser of this.synchronisers) {
|
||||
if (synchroniser.source === source) {
|
||||
return synchroniser.getRemoteContent(preview);
|
||||
@@ -205,12 +168,10 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ
|
||||
return null;
|
||||
}
|
||||
|
||||
async isFirstTimeSyncAndHasUserData(): Promise<boolean> {
|
||||
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.');
|
||||
async isFirstTimeSyncWithMerge(): Promise<boolean> {
|
||||
await this.checkEnablement();
|
||||
if (!await this.userDataSyncStoreService.manifest()) {
|
||||
return false;
|
||||
}
|
||||
if (await this.hasPreviouslySynced()) {
|
||||
return false;
|
||||
@@ -219,60 +180,46 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ
|
||||
}
|
||||
|
||||
async reset(): Promise<void> {
|
||||
await this.checkEnablement();
|
||||
await this.resetRemote();
|
||||
await this.resetLocal();
|
||||
}
|
||||
|
||||
private async resetRemote(): 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.');
|
||||
}
|
||||
try {
|
||||
await this.userDataSyncStoreService.clear();
|
||||
this.logService.info('Completed clearing remote data');
|
||||
} catch (e) {
|
||||
this.logService.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
async resetLocal(): 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.');
|
||||
}
|
||||
await this.checkEnablement();
|
||||
for (const synchroniser of this.synchronisers) {
|
||||
try {
|
||||
await synchroniser.resetLocal();
|
||||
} catch (e) {
|
||||
this.logService.error(`${synchroniser.source}: ${toErrorMessage(e)}`);
|
||||
this.logService.error(e);
|
||||
}
|
||||
}
|
||||
this.logService.info('Completed resetting local cache');
|
||||
}
|
||||
|
||||
private async resetRemote(): Promise<void> {
|
||||
await this.checkEnablement();
|
||||
try {
|
||||
await this.userDataSyncStoreService.clear();
|
||||
} catch (e) {
|
||||
this.logService.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
private setStatus(status: SyncStatus): void {
|
||||
if (this._status !== status) {
|
||||
this._status = status;
|
||||
this._onDidChangeStatus.fire(status);
|
||||
}
|
||||
}
|
||||
|
||||
private updateStatus(): 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);
|
||||
const conflictsSources = this.computeConflictsSources();
|
||||
if (!equals(this._conflictsSources, conflictsSources)) {
|
||||
this._conflictsSources = this.computeConflictsSources();
|
||||
this._onDidChangeConflicts.fire(conflictsSources);
|
||||
}
|
||||
const status = this.computeStatus();
|
||||
this.setStatus(status);
|
||||
}
|
||||
|
||||
private computeStatus(): SyncStatus {
|
||||
@@ -300,14 +247,8 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ
|
||||
this.logService.error(`${source}: ${toErrorMessage(e)}`);
|
||||
}
|
||||
|
||||
private computeConflictsSource(): SyncSource | null {
|
||||
const synchroniser = this.synchronisers.filter(s => s.status === SyncStatus.HasConflicts)[0];
|
||||
return synchroniser ? synchroniser.source : null;
|
||||
}
|
||||
|
||||
private getSynchroniserInConflicts(): IUserDataSynchroniser | null {
|
||||
const synchroniser = this.synchronisers.filter(s => s.status === SyncStatus.HasConflicts)[0];
|
||||
return synchroniser || null;
|
||||
private computeConflictsSources(): SyncSource[] {
|
||||
return this.synchronisers.filter(s => s.status === SyncStatus.HasConflicts).map(s => s.source);
|
||||
}
|
||||
|
||||
private getSynchroniser(source: SyncSource): IUserDataSynchroniser {
|
||||
@@ -319,6 +260,15 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ
|
||||
}
|
||||
}
|
||||
|
||||
private async checkEnablement(): Promise<void> {
|
||||
if (!this.userDataSyncStoreService.userDataSyncStore) {
|
||||
throw new Error('Not enabled');
|
||||
}
|
||||
if (!(await this.userDataAuthTokenService.getToken())) {
|
||||
throw new UserDataSyncError('Not Authenticated. Please sign in to start sync.', UserDataSyncErrorCode.Unauthorized);
|
||||
}
|
||||
}
|
||||
|
||||
private onDidChangeAuthTokenStatus(token: string | undefined): void {
|
||||
if (!token) {
|
||||
this.stop();
|
||||
|
||||
Reference in New Issue
Block a user