Merge from vscode 9bc92b48d945144abb405b9e8df05e18accb9148

This commit is contained in:
ADS Merger
2020-02-19 03:11:35 +00:00
parent 98584d32a7
commit 1e308639e5
253 changed files with 6414 additions and 2296 deletions

View File

@@ -174,7 +174,7 @@ export abstract class AbstractFileSynchroniser extends AbstractSynchroniser {
) {
super(source, fileService, environmentService, userDataSyncStoreService, userDataSyncEnablementService, telemetryService, logService);
this._register(this.fileService.watch(dirname(file)));
this._register(this.fileService.onFileChanges(e => this.onFileChanges(e)));
this._register(this.fileService.onDidFilesChange(e => this.onFileChanges(e)));
}
async stop(): Promise<void> {

View File

@@ -39,7 +39,7 @@ export class GlobalStateSynchroniser extends AbstractSynchroniser implements IUs
) {
super(SyncSource.GlobalState, fileService, environmentService, userDataSyncStoreService, userDataSyncEnablementService, telemetryService, logService);
this._register(this.fileService.watch(dirname(this.environmentService.argvResource)));
this._register(Event.filter(this.fileService.onFileChanges, e => e.contains(this.environmentService.argvResource))(() => this._onDidChangeLocal.fire()));
this._register(Event.filter(this.fileService.onDidFilesChange, e => e.contains(this.environmentService.argvResource))(() => this._onDidChangeLocal.fire()));
}
async pull(): Promise<void> {

View File

@@ -14,6 +14,9 @@ export class UserDataAuthTokenService extends Disposable implements IUserDataAut
private _onDidChangeToken: Emitter<string | undefined> = this._register(new Emitter<string | undefined>());
readonly onDidChangeToken: Event<string | undefined> = this._onDidChangeToken.event;
private _onTokenFailed: Emitter<void> = this._register(new Emitter<void>());
readonly onTokenFailed: Event<void> = this._onTokenFailed.event;
private _token: string | undefined;
constructor() {
@@ -30,4 +33,8 @@ export class UserDataAuthTokenService extends Disposable implements IUserDataAut
this._onDidChangeToken.fire(token);
}
}
sendTokenFailed(): void {
this._onTokenFailed.fire();
}
}

View File

@@ -72,6 +72,13 @@ export class UserDataAutoSyncService extends Disposable implements IUserDataAuto
return this.sync(loop, auto);
}
}
if (e instanceof UserDataSyncError && e.code === UserDataSyncErrorCode.SessionExpired) {
this.logService.info('Auto Sync: Cloud has new session');
this.logService.info('Auto Sync: Resetting the local sync state.');
await this.userDataSyncService.resetLocal();
this.logService.info('Auto Sync: Completed resetting the local sync state.');
return this.sync(loop, auto);
}
this.logService.error(e);
this.successiveFailures++;
this._onError.fire(e instanceof UserDataSyncError ? { code: e.code, source: e.source } : { code: UserDataSyncErrorCode.Unknown });

View File

@@ -18,7 +18,7 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur
import { IStringDictionary } from 'vs/base/common/collections';
import { FormattingOptions } from 'vs/base/common/jsonFormatter';
import { URI } from 'vs/base/common/uri';
import { isEqual } from 'vs/base/common/resources';
import { isEqual, joinPath } from 'vs/base/common/resources';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
export const CONFIGURATION_SYNC_STORE_KEY = 'configurationSync.store';
@@ -120,23 +120,27 @@ export interface IUserData {
}
export interface IUserDataSyncStore {
url: string;
url: URI;
authenticationProviderId: string;
}
export function getUserDataSyncStore(configurationService: IConfigurationService): IUserDataSyncStore | undefined {
const value = configurationService.getValue<IUserDataSyncStore>(CONFIGURATION_SYNC_STORE_KEY);
return value && value.url && value.authenticationProviderId ? value : undefined;
const value = configurationService.getValue<{ url: string, authenticationProviderId: string }>(CONFIGURATION_SYNC_STORE_KEY);
if (value && value.url && value.authenticationProviderId) {
return {
url: joinPath(URI.parse(value.url), 'v1'),
authenticationProviderId: value.authenticationProviderId
};
}
return undefined;
}
export const ALL_RESOURCE_KEYS: ResourceKey[] = ['settings', 'keybindings', 'extensions', 'globalState'];
export type ResourceKey = 'settings' | 'keybindings' | 'extensions' | 'globalState';
export interface IUserDataManifest {
settings: string;
keybindings: string;
extensions: string;
globalState: string;
latest?: Record<ResourceKey, string>
session: string;
}
export const IUserDataSyncStoreService = createDecorator<IUserDataSyncStoreService>('IUserDataSyncStoreService');
@@ -162,6 +166,7 @@ export enum UserDataSyncErrorCode {
TooLarge = 'TooLarge',
NoRef = 'NoRef',
TurnedOff = 'TurnedOff',
SessionExpired = 'SessionExpired',
// Local Errors
LocalPreconditionFailed = 'LocalPreconditionFailed',
@@ -303,9 +308,11 @@ export interface IUserDataAuthTokenService {
_serviceBrand: undefined;
readonly onDidChangeToken: Event<string | undefined>;
readonly onTokenFailed: Event<void>;
getToken(): Promise<string | undefined>;
setToken(accessToken: string | undefined): Promise<void>;
sendTokenFailed(): void;
}
export const IUserDataSyncLogService = createDecorator<IUserDataSyncLogService>('IUserDataSyncLogService');

View File

@@ -96,6 +96,7 @@ export class UserDataAuthTokenServiceChannel implements IServerChannel {
listen(_: unknown, event: string): Event<any> {
switch (event) {
case 'onDidChangeToken': return this.service.onDidChangeToken;
case 'onTokenFailed': return this.service.onTokenFailed;
}
throw new Error(`Event not found: ${event}`);
}

View File

@@ -14,11 +14,14 @@ import { toErrorMessage } from 'vs/base/common/errorMessage';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { equals } from 'vs/base/common/arrays';
import { localize } from 'vs/nls';
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
type SyncErrorClassification = {
source: { classification: 'SystemMetaData', purpose: 'FeatureInsight', isMeasurement: true };
};
const SESSION_ID_KEY = 'sync.sessionId';
export class UserDataSyncService extends Disposable implements IUserDataSyncService {
_serviceBrand: any;
@@ -48,6 +51,7 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ
@IUserDataSyncLogService private readonly logService: IUserDataSyncLogService,
@IUserDataAuthTokenService private readonly userDataAuthTokenService: IUserDataAuthTokenService,
@ITelemetryService private readonly telemetryService: ITelemetryService,
@IStorageService private readonly storageService: IStorageService,
) {
super();
this.keybindingsSynchroniser = this._register(this.instantiationService.createInstance(KeybindingsSynchroniser));
@@ -96,7 +100,7 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ
this.setStatus(SyncStatus.Syncing);
}
const manifest = await this.userDataSyncStoreService.manifest();
let manifest = await this.userDataSyncStoreService.manifest();
// Server has no data but this machine was synced before
if (manifest === null && await this.hasPreviouslySynced()) {
@@ -104,14 +108,30 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ
throw new UserDataSyncError(localize('turned off', "Cannot sync because syncing is turned off in the cloud"), UserDataSyncErrorCode.TurnedOff);
}
const sessionId = this.storageService.get(SESSION_ID_KEY, StorageScope.GLOBAL);
// Server session is different from client session
if (sessionId && manifest && sessionId !== manifest.session) {
throw new UserDataSyncError(localize('session expired', "Cannot sync because current session is expired"), UserDataSyncErrorCode.SessionExpired);
}
for (const synchroniser of this.synchronisers) {
try {
await synchroniser.sync(manifest ? manifest[synchroniser.resourceKey] : undefined);
await synchroniser.sync(manifest && manifest.latest ? manifest.latest[synchroniser.resourceKey] : undefined);
} catch (e) {
this.handleSyncError(e, synchroniser.source);
}
}
// After syncing, get the manifest if it was not available before
if (manifest === null) {
manifest = await this.userDataSyncStoreService.manifest();
}
// Update local session id
if (manifest && manifest.session !== sessionId) {
this.storageService.store(SESSION_ID_KEY, manifest.session, StorageScope.GLOBAL);
}
this.logService.info(`Sync done. Took ${new Date().getTime() - startTime}ms`);
} finally {
@@ -140,26 +160,6 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ
return synchroniser.accept(content);
}
private async hasPreviouslySynced(): Promise<boolean> {
await this.checkEnablement();
for (const synchroniser of this.synchronisers) {
if (await synchroniser.hasPreviouslySynced()) {
return true;
}
}
return false;
}
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) {
@@ -189,6 +189,7 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ
async resetLocal(): Promise<void> {
await this.checkEnablement();
this.storageService.remove(SESSION_ID_KEY, StorageScope.GLOBAL);
for (const synchroniser of this.synchronisers) {
try {
synchroniser.resetLocal();
@@ -199,6 +200,26 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ
}
}
private async hasPreviouslySynced(): Promise<boolean> {
await this.checkEnablement();
for (const synchroniser of this.synchronisers) {
if (await synchroniser.hasPreviouslySynced()) {
return true;
}
}
return false;
}
private async hasLocalData(): Promise<boolean> {
await this.checkEnablement();
for (const synchroniser of this.synchronisers) {
if (await synchroniser.hasLocalData()) {
return true;
}
}
return false;
}
private async resetRemote(): Promise<void> {
await this.checkEnablement();
try {

View File

@@ -6,7 +6,6 @@
import { Disposable, } from 'vs/base/common/lifecycle';
import { IUserData, IUserDataSyncStoreService, UserDataSyncErrorCode, IUserDataSyncStore, getUserDataSyncStore, IUserDataAuthTokenService, SyncSource, UserDataSyncStoreError, IUserDataSyncLogService, IUserDataManifest } from 'vs/platform/userDataSync/common/userDataSync';
import { IRequestService, asText, isSuccess, asJson } from 'vs/platform/request/common/request';
import { URI } from 'vs/base/common/uri';
import { joinPath } from 'vs/base/common/resources';
import { CancellationToken } from 'vs/base/common/cancellation';
import { IHeaders, IRequestOptions, IRequestContext } from 'vs/base/parts/request/common/request';
@@ -33,7 +32,7 @@ export class UserDataSyncStoreService extends Disposable implements IUserDataSyn
throw new Error('No settings sync store url configured.');
}
const url = joinPath(URI.parse(this.userDataSyncStore.url), 'resource', key, 'latest').toString();
const url = joinPath(this.userDataSyncStore.url, 'resource', key, 'latest').toString();
const headers: IHeaders = {};
// Disable caching as they are cached by synchronisers
headers['Cache-Control'] = 'no-cache';
@@ -65,7 +64,7 @@ export class UserDataSyncStoreService extends Disposable implements IUserDataSyn
throw new Error('No settings sync store url configured.');
}
const url = joinPath(URI.parse(this.userDataSyncStore.url), 'resource', key).toString();
const url = joinPath(this.userDataSyncStore.url, 'resource', key).toString();
const headers: IHeaders = { 'Content-Type': 'text/plain' };
if (ref) {
headers['If-Match'] = ref;
@@ -89,7 +88,7 @@ export class UserDataSyncStoreService extends Disposable implements IUserDataSyn
throw new Error('No settings sync store url configured.');
}
const url = joinPath(URI.parse(this.userDataSyncStore.url), 'resource', 'latest').toString();
const url = joinPath(this.userDataSyncStore.url, 'manifest').toString();
const headers: IHeaders = { 'Content-Type': 'application/json' };
const context = await this.request({ type: 'GET', url, headers }, undefined, CancellationToken.None);
@@ -105,7 +104,7 @@ export class UserDataSyncStoreService extends Disposable implements IUserDataSyn
throw new Error('No settings sync store url configured.');
}
const url = joinPath(URI.parse(this.userDataSyncStore.url), 'resource').toString();
const url = joinPath(this.userDataSyncStore.url, 'resource').toString();
const headers: IHeaders = { 'Content-Type': 'text/plain' };
const context = await this.request({ type: 'DELETE', url, headers }, undefined, CancellationToken.None);
@@ -134,6 +133,7 @@ export class UserDataSyncStoreService extends Disposable implements IUserDataSyn
}
if (context.res.statusCode === 401) {
this.authTokenService.sendTokenFailed();
throw new UserDataSyncStoreError(`Request '${options.url?.toString()}' failed because of Unauthorized (401).`, UserDataSyncErrorCode.Unauthorized, source);
}