mirror of
https://github.com/Microsoft/vscode
synced 2024-08-27 21:09:43 +00:00
* fix #162380 - Handle 405 error - Clean up remote data when 405 occurs - Check for too many profiles locally - fix merging profiles - remove created collections when updaing profiles fail * fix tests
This commit is contained in:
parent
4b148a70a8
commit
d253ab2adc
|
@ -20,7 +20,7 @@ import { IHeaders } from 'vs/base/parts/request/common/request';
|
|||
import { localize } from 'vs/nls';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||
import { FileChangesEvent, FileOperationError, FileOperationResult, IFileContent, IFileService } from 'vs/platform/files/common/files';
|
||||
import { FileChangesEvent, FileOperationError, FileOperationResult, IFileContent, IFileService, toFileOperationResult } from 'vs/platform/files/common/files';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { getServiceMachineId } from 'vs/platform/externalServices/common/serviceMachineId';
|
||||
import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage';
|
||||
|
@ -540,8 +540,10 @@ export abstract class AbstractSynchroniser extends Disposable implements IUserDa
|
|||
this.storageService.remove(this.lastSyncUserDataStateKey, StorageScope.APPLICATION);
|
||||
try {
|
||||
await this.fileService.del(this.lastSyncResource);
|
||||
} catch (e) {
|
||||
this.logService.error(e);
|
||||
} catch (error) {
|
||||
if (toFileOperationResult(error) !== FileOperationResult.FILE_NOT_FOUND) {
|
||||
this.logService.error(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -267,6 +267,12 @@ export class UserDataAutoSyncService extends Disposable implements IUserDataAuto
|
|||
this.logService.info('Auto Sync: Turned off sync because of making too many requests to server');
|
||||
}
|
||||
|
||||
// Method Not Found
|
||||
else if (userDataSyncError.code === UserDataSyncErrorCode.MethodNotFound) {
|
||||
await this.turnOff(false, true /* force soft turnoff on error */);
|
||||
this.logService.info('Auto Sync: Turned off sync because current client is making requests to server that are not supported');
|
||||
}
|
||||
|
||||
// Upgrade Required or Gone
|
||||
else if (userDataSyncError.code === UserDataSyncErrorCode.UpgradeRequired || userDataSyncError.code === UserDataSyncErrorCode.Gone) {
|
||||
await this.turnOff(false, true /* force soft turnoff on error */,
|
||||
|
@ -458,89 +464,105 @@ class AutoSync extends Disposable {
|
|||
private async doSync(reason: string, disableCache: boolean, token: CancellationToken): Promise<void> {
|
||||
this.logService.info(`Auto Sync: Triggered by ${reason}`);
|
||||
this._onDidStartSync.fire();
|
||||
|
||||
let error: Error | undefined;
|
||||
try {
|
||||
this.syncTask = await this.userDataSyncService.createSyncTask(this.manifest, disableCache);
|
||||
if (token.isCancellationRequested) {
|
||||
return;
|
||||
}
|
||||
this.manifest = this.syncTask.manifest;
|
||||
|
||||
// Server has no data but this machine was synced before
|
||||
if (this.manifest === null && await this.userDataSyncService.hasPreviouslySynced()) {
|
||||
if (this.hasSyncServiceChanged()) {
|
||||
if (await this.hasDefaultServiceChanged()) {
|
||||
throw new UserDataAutoSyncError(localize('default service changed', "Cannot sync because default service has changed"), UserDataSyncErrorCode.DefaultServiceChanged);
|
||||
} else {
|
||||
throw new UserDataAutoSyncError(localize('service changed', "Cannot sync because sync service has changed"), UserDataSyncErrorCode.ServiceChanged);
|
||||
}
|
||||
} else {
|
||||
// Sync was turned off in the cloud
|
||||
throw new UserDataAutoSyncError(localize('turned off', "Cannot sync because syncing is turned off in the cloud"), UserDataSyncErrorCode.TurnedOff);
|
||||
}
|
||||
}
|
||||
|
||||
const sessionId = this.storageService.get(sessionIdKey, StorageScope.APPLICATION);
|
||||
// Server session is different from client session
|
||||
if (sessionId && this.manifest && sessionId !== this.manifest.session) {
|
||||
if (this.hasSyncServiceChanged()) {
|
||||
if (await this.hasDefaultServiceChanged()) {
|
||||
throw new UserDataAutoSyncError(localize('default service changed', "Cannot sync because default service has changed"), UserDataSyncErrorCode.DefaultServiceChanged);
|
||||
} else {
|
||||
throw new UserDataAutoSyncError(localize('service changed', "Cannot sync because sync service has changed"), UserDataSyncErrorCode.ServiceChanged);
|
||||
}
|
||||
} else {
|
||||
throw new UserDataAutoSyncError(localize('session expired', "Cannot sync because current session is expired"), UserDataSyncErrorCode.SessionExpired);
|
||||
}
|
||||
}
|
||||
|
||||
const machines = await this.userDataSyncMachinesService.getMachines(this.manifest || undefined);
|
||||
// Return if cancellation is requested
|
||||
if (token.isCancellationRequested) {
|
||||
return;
|
||||
}
|
||||
|
||||
const currentMachine = machines.find(machine => machine.isCurrent);
|
||||
// Check if sync was turned off from other machine
|
||||
if (currentMachine?.disabled) {
|
||||
// Throw TurnedOff error
|
||||
throw new UserDataAutoSyncError(localize('turned off machine', "Cannot sync because syncing is turned off on this machine from another machine."), UserDataSyncErrorCode.TurnedOff);
|
||||
}
|
||||
|
||||
await this.syncTask.run();
|
||||
|
||||
// After syncing, get the manifest if it was not available before
|
||||
if (this.manifest === null) {
|
||||
try {
|
||||
this.manifest = await this.userDataSyncStoreService.manifest(null);
|
||||
} catch (error) {
|
||||
throw new UserDataAutoSyncError(toErrorMessage(error), error instanceof UserDataSyncError ? error.code : UserDataSyncErrorCode.Unknown);
|
||||
}
|
||||
}
|
||||
|
||||
// Update local session id
|
||||
if (this.manifest && this.manifest.session !== sessionId) {
|
||||
this.storageService.store(sessionIdKey, this.manifest.session, StorageScope.APPLICATION, StorageTarget.MACHINE);
|
||||
}
|
||||
|
||||
// Return if cancellation is requested
|
||||
if (token.isCancellationRequested) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Add current machine
|
||||
if (!currentMachine) {
|
||||
await this.userDataSyncMachinesService.addCurrentMachine(this.manifest || undefined);
|
||||
}
|
||||
|
||||
await this.createAndRunSyncTask(disableCache, token);
|
||||
} catch (e) {
|
||||
this.logService.error(e);
|
||||
error = e;
|
||||
if (UserDataSyncError.toUserDataSyncError(e).code === UserDataSyncErrorCode.MethodNotFound) {
|
||||
try {
|
||||
this.logService.info('Auto Sync: Client is making invalid requests. Cleaning up data...');
|
||||
await this.userDataSyncService.cleanUpRemoteData();
|
||||
this.logService.info('Auto Sync: Retrying sync...');
|
||||
await this.createAndRunSyncTask(disableCache, token);
|
||||
error = undefined;
|
||||
} catch (e1) {
|
||||
this.logService.error(e1);
|
||||
error = e1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this._onDidFinishSync.fire(error);
|
||||
}
|
||||
|
||||
private async createAndRunSyncTask(disableCache: boolean, token: CancellationToken): Promise<void> {
|
||||
this.syncTask = await this.userDataSyncService.createSyncTask(this.manifest, disableCache);
|
||||
if (token.isCancellationRequested) {
|
||||
return;
|
||||
}
|
||||
this.manifest = this.syncTask.manifest;
|
||||
|
||||
// Server has no data but this machine was synced before
|
||||
if (this.manifest === null && await this.userDataSyncService.hasPreviouslySynced()) {
|
||||
if (this.hasSyncServiceChanged()) {
|
||||
if (await this.hasDefaultServiceChanged()) {
|
||||
throw new UserDataAutoSyncError(localize('default service changed', "Cannot sync because default service has changed"), UserDataSyncErrorCode.DefaultServiceChanged);
|
||||
} else {
|
||||
throw new UserDataAutoSyncError(localize('service changed', "Cannot sync because sync service has changed"), UserDataSyncErrorCode.ServiceChanged);
|
||||
}
|
||||
} else {
|
||||
// Sync was turned off in the cloud
|
||||
throw new UserDataAutoSyncError(localize('turned off', "Cannot sync because syncing is turned off in the cloud"), UserDataSyncErrorCode.TurnedOff);
|
||||
}
|
||||
}
|
||||
|
||||
const sessionId = this.storageService.get(sessionIdKey, StorageScope.APPLICATION);
|
||||
// Server session is different from client session
|
||||
if (sessionId && this.manifest && sessionId !== this.manifest.session) {
|
||||
if (this.hasSyncServiceChanged()) {
|
||||
if (await this.hasDefaultServiceChanged()) {
|
||||
throw new UserDataAutoSyncError(localize('default service changed', "Cannot sync because default service has changed"), UserDataSyncErrorCode.DefaultServiceChanged);
|
||||
} else {
|
||||
throw new UserDataAutoSyncError(localize('service changed', "Cannot sync because sync service has changed"), UserDataSyncErrorCode.ServiceChanged);
|
||||
}
|
||||
} else {
|
||||
throw new UserDataAutoSyncError(localize('session expired', "Cannot sync because current session is expired"), UserDataSyncErrorCode.SessionExpired);
|
||||
}
|
||||
}
|
||||
|
||||
const machines = await this.userDataSyncMachinesService.getMachines(this.manifest || undefined);
|
||||
// Return if cancellation is requested
|
||||
if (token.isCancellationRequested) {
|
||||
return;
|
||||
}
|
||||
|
||||
const currentMachine = machines.find(machine => machine.isCurrent);
|
||||
// Check if sync was turned off from other machine
|
||||
if (currentMachine?.disabled) {
|
||||
// Throw TurnedOff error
|
||||
throw new UserDataAutoSyncError(localize('turned off machine', "Cannot sync because syncing is turned off on this machine from another machine."), UserDataSyncErrorCode.TurnedOff);
|
||||
}
|
||||
|
||||
await this.syncTask.run();
|
||||
|
||||
// After syncing, get the manifest if it was not available before
|
||||
if (this.manifest === null) {
|
||||
try {
|
||||
this.manifest = await this.userDataSyncStoreService.manifest(null);
|
||||
} catch (error) {
|
||||
throw new UserDataAutoSyncError(toErrorMessage(error), error instanceof UserDataSyncError ? error.code : UserDataSyncErrorCode.Unknown);
|
||||
}
|
||||
}
|
||||
|
||||
// Update local session id
|
||||
if (this.manifest && this.manifest.session !== sessionId) {
|
||||
this.storageService.store(sessionIdKey, this.manifest.session, StorageScope.APPLICATION, StorageTarget.MACHINE);
|
||||
}
|
||||
|
||||
// Return if cancellation is requested
|
||||
if (token.isCancellationRequested) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Add current machine
|
||||
if (!currentMachine) {
|
||||
await this.userDataSyncMachinesService.addCurrentMachine(this.manifest || undefined);
|
||||
}
|
||||
}
|
||||
|
||||
register<T extends IDisposable>(t: T): T {
|
||||
return super._register(t);
|
||||
}
|
||||
|
|
|
@ -20,26 +20,20 @@ interface IUserDataProfileInfo {
|
|||
}
|
||||
|
||||
export function merge(local: IUserDataProfile[], remote: ISyncUserDataProfile[] | null, lastSync: ISyncUserDataProfile[] | null, ignored: string[]): IMergeResult {
|
||||
const result: IRelaxedMergeResult = {
|
||||
local: {
|
||||
added: [],
|
||||
removed: [],
|
||||
updated: [],
|
||||
}, remote: {
|
||||
added: [],
|
||||
removed: [],
|
||||
updated: [],
|
||||
}
|
||||
};
|
||||
const localResult: { added: ISyncUserDataProfile[]; removed: IUserDataProfile[]; updated: ISyncUserDataProfile[] } = { added: [], removed: [], updated: [] };
|
||||
let remoteResult: { added: IUserDataProfile[]; removed: ISyncUserDataProfile[]; updated: IUserDataProfile[] } | null = { added: [], removed: [], updated: [] };
|
||||
|
||||
if (!remote) {
|
||||
const added = local.filter(({ id }) => !ignored.includes(id));
|
||||
if (added.length) {
|
||||
result.remote!.added = added;
|
||||
remoteResult.added = added;
|
||||
} else {
|
||||
result.remote = null;
|
||||
remoteResult = null;
|
||||
}
|
||||
return result;
|
||||
return {
|
||||
local: localResult,
|
||||
remote: remoteResult
|
||||
};
|
||||
}
|
||||
|
||||
const localToRemote = compare(local, remote, ignored);
|
||||
|
@ -52,7 +46,7 @@ export function merge(local: IUserDataProfile[], remote: ISyncUserDataProfile[]
|
|||
for (const id of baseToRemote.removed) {
|
||||
const e = local.find(profile => profile.id === id);
|
||||
if (e) {
|
||||
result.local.removed.push(e);
|
||||
localResult.removed.push(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -64,24 +58,24 @@ export function merge(local: IUserDataProfile[], remote: ISyncUserDataProfile[]
|
|||
// Is different from local to remote
|
||||
if (localToRemote.updated.includes(id)) {
|
||||
// Remote wins always
|
||||
result.local.updated.push(remoteProfile);
|
||||
localResult.updated.push(remoteProfile);
|
||||
}
|
||||
} else {
|
||||
result.local.added.push(remoteProfile);
|
||||
localResult.added.push(remoteProfile);
|
||||
}
|
||||
}
|
||||
|
||||
// Remotely updated profiles
|
||||
for (const id of baseToRemote.updated) {
|
||||
// Remote wins always
|
||||
result.local.updated.push(remote.find(profile => profile.id === id)!);
|
||||
localResult.updated.push(remote.find(profile => profile.id === id)!);
|
||||
}
|
||||
|
||||
// Locally added profiles
|
||||
for (const id of baseToLocal.added) {
|
||||
// Not there in remote
|
||||
if (!baseToRemote.added.includes(id)) {
|
||||
result.remote!.added.push(local.find(profile => profile.id === id)!);
|
||||
remoteResult.added.push(local.find(profile => profile.id === id)!);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -94,21 +88,24 @@ export function merge(local: IUserDataProfile[], remote: ISyncUserDataProfile[]
|
|||
|
||||
// If not updated in remote
|
||||
if (!baseToRemote.updated.includes(id)) {
|
||||
result.remote!.updated.push(local.find(profile => profile.id === id)!);
|
||||
remoteResult.updated.push(local.find(profile => profile.id === id)!);
|
||||
}
|
||||
}
|
||||
|
||||
// Locally removed profiles
|
||||
for (const id of baseToLocal.removed) {
|
||||
result.remote!.removed.push(remote.find(profile => profile.id === id)!);
|
||||
const removedProfile = remote.find(profile => profile.id === id);
|
||||
if (removedProfile) {
|
||||
remoteResult.removed.push(removedProfile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (result.remote!.added.length === 0 && result.remote!.removed.length === 0 && result.remote!.updated.length === 0) {
|
||||
result.remote = null;
|
||||
if (remoteResult.added.length === 0 && remoteResult.removed.length === 0 && remoteResult.updated.length === 0) {
|
||||
remoteResult = null;
|
||||
}
|
||||
|
||||
return result;
|
||||
return { local: localResult, remote: remoteResult };
|
||||
}
|
||||
|
||||
function compare(from: IUserDataProfileInfo[] | null, to: IUserDataProfileInfo[], ignoredProfiles: string[]): { added: string[]; removed: string[]; updated: string[] } {
|
||||
|
|
|
@ -179,6 +179,11 @@ export class UserDataProfilesManifestSynchroniser extends AbstractSynchroniser i
|
|||
this.logService.info(`${this.syncResourceLogLabel}: No changes found during synchronizing profiles.`);
|
||||
}
|
||||
|
||||
const remoteProfiles = resourcePreviews[0][0].remoteProfiles || [];
|
||||
if (remoteProfiles.length + (remote?.added.length ?? 0) - (remote?.removed.length ?? 0) > 20) {
|
||||
throw new UserDataSyncError('Too many profiles to sync. Please remove some profiles and try again.', UserDataSyncErrorCode.LocalTooManyProfiles);
|
||||
}
|
||||
|
||||
if (localChange !== Change.None) {
|
||||
await this.backupLocal(stringifyLocalProfiles(this.getLocalUserDataProfiles(), false));
|
||||
const promises: Promise<any>[] = [];
|
||||
|
@ -212,11 +217,17 @@ export class UserDataProfilesManifestSynchroniser extends AbstractSynchroniser i
|
|||
}
|
||||
|
||||
if (remoteChange !== Change.None) {
|
||||
const remoteProfiles = resourcePreviews[0][0].remoteProfiles || [];
|
||||
this.logService.trace(`${this.syncResourceLogLabel}: Updating remote profiles...`);
|
||||
for (const profile of remote?.added || []) {
|
||||
const collection = await this.userDataSyncStoreService.createCollection(this.syncHeaders);
|
||||
remoteProfiles.push({ id: profile.id, name: profile.name, collection, shortName: profile.shortName });
|
||||
const addedCollections: string[] = [];
|
||||
const canAddRemoteProfiles = remoteProfiles.length + (remote?.added.length ?? 0) <= 20;
|
||||
if (canAddRemoteProfiles) {
|
||||
for (const profile of remote?.added || []) {
|
||||
const collection = await this.userDataSyncStoreService.createCollection(this.syncHeaders);
|
||||
addedCollections.push(collection);
|
||||
remoteProfiles.push({ id: profile.id, name: profile.name, collection, shortName: profile.shortName });
|
||||
}
|
||||
} else {
|
||||
this.logService.info(`${this.syncResourceLogLabel}: Could not create remote profiles as there are too many profiles.`);
|
||||
}
|
||||
for (const profile of remote?.removed || []) {
|
||||
remoteProfiles.splice(remoteProfiles.findIndex(({ id }) => profile.id === id), 1);
|
||||
|
@ -227,8 +238,19 @@ export class UserDataProfilesManifestSynchroniser extends AbstractSynchroniser i
|
|||
remoteProfiles.splice(remoteProfiles.indexOf(profileToBeUpdated), 1, { id: profile.id, name: profile.name, collection: profileToBeUpdated.collection, shortName: profile.shortName });
|
||||
}
|
||||
}
|
||||
remoteUserData = await this.updateRemoteUserData(this.stringifyRemoteProfiles(remoteProfiles), force ? null : remoteUserData.ref);
|
||||
this.logService.info(`${this.syncResourceLogLabel}: Updated remote profiles.${remote?.added.length ? ` Added: ${JSON.stringify(remote.added.map(e => e.name))}.` : ''}${remote?.updated.length ? ` Updated: ${JSON.stringify(remote.updated.map(e => e.name))}.` : ''}${remote?.removed.length ? ` Removed: ${JSON.stringify(remote.removed.map(e => e.name))}.` : ''}`);
|
||||
|
||||
try {
|
||||
remoteUserData = await this.updateRemoteProfiles(remoteProfiles, force ? null : remoteUserData.ref);
|
||||
this.logService.info(`${this.syncResourceLogLabel}: Updated remote profiles.${canAddRemoteProfiles && remote?.added.length ? ` Added: ${JSON.stringify(remote.added.map(e => e.name))}.` : ''}${remote?.updated.length ? ` Updated: ${JSON.stringify(remote.updated.map(e => e.name))}.` : ''}${remote?.removed.length ? ` Removed: ${JSON.stringify(remote.removed.map(e => e.name))}.` : ''}`);
|
||||
} catch (error) {
|
||||
if (addedCollections.length) {
|
||||
this.logService.info(`${this.syncResourceLogLabel}: Failed to update remote profiles. Cleaning up added collections...`);
|
||||
for (const collection of addedCollections) {
|
||||
await this.userDataSyncStoreService.deleteCollection(collection, this.syncHeaders);
|
||||
}
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
|
||||
for (const profile of remote?.removed || []) {
|
||||
await this.userDataSyncStoreService.deleteCollection(profile.collection, this.syncHeaders);
|
||||
|
@ -243,6 +265,10 @@ export class UserDataProfilesManifestSynchroniser extends AbstractSynchroniser i
|
|||
}
|
||||
}
|
||||
|
||||
async updateRemoteProfiles(profiles: ISyncUserDataProfile[], ref: string | null): Promise<IRemoteUserData> {
|
||||
return this.updateRemoteUserData(this.stringifyRemoteProfiles(profiles), ref);
|
||||
}
|
||||
|
||||
async hasLocalData(): Promise<boolean> {
|
||||
return this.getLocalUserDataProfiles().length > 0;
|
||||
}
|
||||
|
|
|
@ -238,6 +238,7 @@ export const enum UserDataSyncErrorCode {
|
|||
// Client Errors (>= 400 )
|
||||
Unauthorized = 'Unauthorized', /* 401 */
|
||||
NotFound = 'NotFound', /* 404 */
|
||||
MethodNotFound = 'MethodNotFound', /* 405 */
|
||||
Conflict = 'Conflict', /* 409 */
|
||||
Gone = 'Gone', /* 410 */
|
||||
PreconditionFailed = 'PreconditionFailed', /* 412 */
|
||||
|
@ -261,6 +262,7 @@ export const enum UserDataSyncErrorCode {
|
|||
SessionExpired = 'SessionExpired',
|
||||
ServiceChanged = 'ServiceChanged',
|
||||
DefaultServiceChanged = 'DefaultServiceChanged',
|
||||
LocalTooManyProfiles = 'LocalTooManyProfiles',
|
||||
LocalTooManyRequests = 'LocalTooManyRequests',
|
||||
LocalPreconditionFailed = 'LocalPreconditionFailed',
|
||||
LocalInvalidContent = 'LocalInvalidContent',
|
||||
|
@ -509,6 +511,7 @@ export interface IUserDataSyncService {
|
|||
|
||||
reset(): Promise<void>;
|
||||
resetRemote(): Promise<void>;
|
||||
cleanUpRemoteData(): Promise<void>;
|
||||
resetLocal(): Promise<void>;
|
||||
hasLocalData(): Promise<boolean>;
|
||||
hasPreviouslySynced(): Promise<boolean>;
|
||||
|
|
|
@ -167,7 +167,23 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ
|
|||
return that.sync(manifest, true, executionId, cancellableToken.token);
|
||||
},
|
||||
async apply(): Promise<void> {
|
||||
await that.applyManualSync(manifest, executionId, cancellableToken.token);
|
||||
try {
|
||||
try {
|
||||
await that.applyManualSync(manifest, executionId, cancellableToken.token);
|
||||
} catch (error) {
|
||||
if (UserDataSyncError.toUserDataSyncError(error).code === UserDataSyncErrorCode.MethodNotFound) {
|
||||
that.logService.info('Client is making invalid requests. Cleaning up data...');
|
||||
await that.cleanUpRemoteData();
|
||||
that.logService.info('Applying manual sync again...');
|
||||
await that.applyManualSync(manifest, executionId, cancellableToken.token);
|
||||
} else {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
that.logService.error(error);
|
||||
throw error;
|
||||
}
|
||||
that.logService.info(`Sync done. Took ${new Date().getTime() - startTime}ms`);
|
||||
that.updateLastSyncTime();
|
||||
},
|
||||
|
@ -395,6 +411,29 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ
|
|||
this.logService.info('Did reset the local sync state.');
|
||||
}
|
||||
|
||||
async cleanUpRemoteData(): Promise<void> {
|
||||
const remoteProfiles = await this.userDataSyncResourceProviderService.getRemoteSyncedProfiles();
|
||||
const remoteProfileCollections = remoteProfiles.map(profile => profile.collection);
|
||||
const allCollections = await this.userDataSyncStoreService.getAllCollections();
|
||||
const redundantCollections = allCollections.filter(c => !remoteProfileCollections.includes(c));
|
||||
if (redundantCollections.length) {
|
||||
this.logService.info(`Deleting ${redundantCollections.length} redundant collections on server`);
|
||||
await Promise.allSettled(redundantCollections.map(collectionId => this.userDataSyncStoreService.deleteCollection(collectionId)));
|
||||
this.logService.info(`Deleted redundant collections on server`);
|
||||
}
|
||||
const updatedRemoteProfiles = remoteProfiles.filter(profile => allCollections.includes(profile.collection));
|
||||
if (updatedRemoteProfiles.length !== remoteProfiles.length) {
|
||||
this.logService.info(`Updating remote profiles with invalid collections on server`);
|
||||
const profileManifestSynchronizer = this.instantiationService.createInstance(UserDataProfilesManifestSynchroniser, this.userDataProfilesService.defaultProfile, undefined);
|
||||
try {
|
||||
await profileManifestSynchronizer.updateRemoteProfiles(updatedRemoteProfiles, null);
|
||||
this.logService.info(`Updated remote profiles on server`);
|
||||
} finally {
|
||||
profileManifestSynchronizer.dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async performAction<T>(profile: IUserDataProfile, action: (synchroniser: IUserDataSynchroniser) => Promise<T | undefined>): Promise<T | null> {
|
||||
const disposables = new DisposableStore();
|
||||
try {
|
||||
|
@ -756,13 +795,13 @@ class ProfileSynchronizer extends Disposable {
|
|||
|
||||
private getOrder(syncResource: SyncResource): number {
|
||||
switch (syncResource) {
|
||||
case SyncResource.Profiles: return 0;
|
||||
case SyncResource.Settings: return 1;
|
||||
case SyncResource.Keybindings: return 2;
|
||||
case SyncResource.Snippets: return 3;
|
||||
case SyncResource.Tasks: return 4;
|
||||
case SyncResource.GlobalState: return 5;
|
||||
case SyncResource.Extensions: return 6;
|
||||
case SyncResource.Settings: return 0;
|
||||
case SyncResource.Keybindings: return 1;
|
||||
case SyncResource.Snippets: return 2;
|
||||
case SyncResource.Tasks: return 3;
|
||||
case SyncResource.GlobalState: return 4;
|
||||
case SyncResource.Extensions: return 5;
|
||||
case SyncResource.Profiles: return 6;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -771,10 +810,12 @@ class ProfileSynchronizer extends Disposable {
|
|||
function canBailout(e: any): boolean {
|
||||
if (e instanceof UserDataSyncError) {
|
||||
switch (e.code) {
|
||||
case UserDataSyncErrorCode.MethodNotFound:
|
||||
case UserDataSyncErrorCode.TooLarge:
|
||||
case UserDataSyncErrorCode.TooManyRequests:
|
||||
case UserDataSyncErrorCode.TooManyRequestsAndRetryAfter:
|
||||
case UserDataSyncErrorCode.LocalTooManyRequests:
|
||||
case UserDataSyncErrorCode.LocalTooManyProfiles:
|
||||
case UserDataSyncErrorCode.Gone:
|
||||
case UserDataSyncErrorCode.UpgradeRequired:
|
||||
case UserDataSyncErrorCode.IncompatibleRemoteContent:
|
||||
|
|
|
@ -82,6 +82,7 @@ export class UserDataSyncChannel implements IServerChannel {
|
|||
case 'replace': return this.service.replace(reviewSyncResourceHandle(args[0]));
|
||||
case 'getAssociatedResources': return this.service.getAssociatedResources(reviewSyncResourceHandle(args[0]));
|
||||
case 'getMachineId': return this.service.getMachineId(reviewSyncResourceHandle(args[0]));
|
||||
case 'cleanUpRemoteData': return this.service.cleanUpRemoteData();
|
||||
|
||||
case 'createManualSyncTask': return this.createManualSyncTask();
|
||||
}
|
||||
|
@ -95,7 +96,7 @@ export class UserDataSyncChannel implements IServerChannel {
|
|||
|
||||
switch (manualSyncTaskCommand) {
|
||||
case 'merge': return manualSyncTask.merge();
|
||||
case 'apply': return manualSyncTask.apply().finally(() => this.manualSyncTasks.delete(this.createKey(manualSyncTask.id)));
|
||||
case 'apply': return manualSyncTask.apply().then(() => this.manualSyncTasks.delete(this.createKey(manualSyncTask.id)));
|
||||
case 'stop': return manualSyncTask.stop().finally(() => this.manualSyncTasks.delete(this.createKey(manualSyncTask.id)));
|
||||
}
|
||||
}
|
||||
|
@ -246,6 +247,10 @@ export class UserDataSyncChannelClient extends Disposable implements IUserDataSy
|
|||
return this.channel.call<string | undefined>('getMachineId', [syncResourceHandle]);
|
||||
}
|
||||
|
||||
cleanUpRemoteData(): Promise<void> {
|
||||
return this.channel.call('cleanUpRemoteData');
|
||||
}
|
||||
|
||||
replace(syncResourceHandle: ISyncResourceHandle): Promise<void> {
|
||||
return this.channel.call('replace', [syncResourceHandle]);
|
||||
}
|
||||
|
|
|
@ -20,7 +20,7 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur
|
|||
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||
import { IFileService } from 'vs/platform/files/common/files';
|
||||
import { IProductService } from 'vs/platform/product/common/productService';
|
||||
import { asJson, asTextOrError, IRequestService, isSuccess as isSuccessContext } from 'vs/platform/request/common/request';
|
||||
import { asJson, asText, asTextOrError, IRequestService, isSuccess as isSuccessContext } from 'vs/platform/request/common/request';
|
||||
import { getServiceMachineId } from 'vs/platform/externalServices/common/serviceMachineId';
|
||||
import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage';
|
||||
import { CONFIGURATION_SYNC_STORE_KEY, HEADER_EXECUTION_ID, HEADER_OPERATION_ID, IAuthenticationProvider, IResourceRefHandle, IUserData, IUserDataManifest, IUserDataSyncLogService, IUserDataSyncStore, IUserDataSyncStoreManagementService, IUserDataSyncStoreService, ServerResource, SYNC_SERVICE_URL_TYPE, UserDataSyncErrorCode, UserDataSyncStoreError, UserDataSyncStoreType } from 'vs/platform/userDataSync/common/userDataSync';
|
||||
|
@ -243,7 +243,7 @@ export class UserDataSyncStoreClient extends Disposable {
|
|||
|
||||
const context = await this.request(url, { type: 'GET', headers }, [], CancellationToken.None);
|
||||
|
||||
return (await asJson<string[]>(context)) || [];
|
||||
return (await asJson<{ id: string }[]>(context))?.map(({ id }) => id) || [];
|
||||
}
|
||||
|
||||
async createCollection(headers: IHeaders = {}): Promise<string> {
|
||||
|
@ -448,8 +448,8 @@ export class UserDataSyncStoreClient extends Disposable {
|
|||
throw new Error('No settings sync store url configured.');
|
||||
}
|
||||
|
||||
await this.deleteResources();
|
||||
await this.deleteCollection();
|
||||
await this.deleteResources();
|
||||
|
||||
// clear cached session.
|
||||
this.clearSession();
|
||||
|
@ -529,10 +529,12 @@ export class UserDataSyncStoreClient extends Disposable {
|
|||
const operationId = context.res.headers[HEADER_OPERATION_ID];
|
||||
const requestInfo = { url, status: context.res.statusCode, 'execution-id': options.headers[HEADER_EXECUTION_ID], 'operation-id': operationId };
|
||||
const isSuccess = isSuccessContext(context) || (context.res.statusCode && successCodes.indexOf(context.res.statusCode) !== -1);
|
||||
let failureMessage = '';
|
||||
if (isSuccess) {
|
||||
this.logService.trace('Request succeeded', requestInfo);
|
||||
} else {
|
||||
this.logService.info('Request failed', requestInfo);
|
||||
failureMessage = await asText(context) || '';
|
||||
}
|
||||
|
||||
if (context.res.statusCode === 401) {
|
||||
|
@ -547,6 +549,10 @@ export class UserDataSyncStoreClient extends Disposable {
|
|||
throw new UserDataSyncStoreError(`${options.type} request '${url}' failed because the requested resource is not found (404).`, url, UserDataSyncErrorCode.NotFound, context.res.statusCode, operationId);
|
||||
}
|
||||
|
||||
if (context.res.statusCode === 405) {
|
||||
throw new UserDataSyncStoreError(`${options.type} request '${url}' failed because the requested endpoint is not found (405). ${failureMessage}`, url, UserDataSyncErrorCode.MethodNotFound, context.res.statusCode, operationId);
|
||||
}
|
||||
|
||||
if (context.res.statusCode === 409) {
|
||||
throw new UserDataSyncStoreError(`${options.type} request '${url}' failed because of Conflict (409). There is new data for this resource. Make the request again with latest data.`, url, UserDataSyncErrorCode.Conflict, context.res.statusCode, operationId);
|
||||
}
|
||||
|
|
|
@ -149,8 +149,6 @@ suite('UserDataAutoSyncService', () => {
|
|||
{ type: 'GET', url: `${target.url}/v1/manifest`, headers: {} },
|
||||
// Machines
|
||||
{ type: 'GET', url: `${target.url}/v1/resource/machines/latest`, headers: {} },
|
||||
// Profiles
|
||||
{ type: 'GET', url: `${target.url}/v1/resource/profiles/latest`, headers: {} },
|
||||
// Settings
|
||||
{ type: 'GET', url: `${target.url}/v1/resource/settings/latest`, headers: {} },
|
||||
{ type: 'POST', url: `${target.url}/v1/resource/settings`, headers: { 'If-Match': '0' } },
|
||||
|
@ -168,6 +166,8 @@ suite('UserDataAutoSyncService', () => {
|
|||
{ type: 'POST', url: `${target.url}/v1/resource/globalState`, headers: { 'If-Match': '0' } },
|
||||
// Extensions
|
||||
{ type: 'GET', url: `${target.url}/v1/resource/extensions/latest`, headers: {} },
|
||||
// Profiles
|
||||
{ type: 'GET', url: `${target.url}/v1/resource/profiles/latest`, headers: {} },
|
||||
// Manifest
|
||||
{ type: 'GET', url: `${target.url}/v1/manifest`, headers: {} },
|
||||
// Machines
|
||||
|
|
|
@ -173,4 +173,24 @@ suite('UserDataProfilesManifestMerge', () => {
|
|||
assert.strictEqual(actual.remote, null);
|
||||
});
|
||||
|
||||
test('merge when profile is removed locally, but not exists in remote', () => {
|
||||
const localProfiles: IUserDataProfile[] = [
|
||||
toUserDataProfile('1', '1', URI.file('1')),
|
||||
];
|
||||
const base: ISyncUserDataProfile[] = [
|
||||
{ id: '1', name: '1', collection: '1' },
|
||||
{ id: '2', name: '2', collection: '2' },
|
||||
];
|
||||
const remoteProfiles: ISyncUserDataProfile[] = [
|
||||
{ id: '1', name: '3', collection: '1' },
|
||||
];
|
||||
|
||||
const actual = merge(localProfiles, remoteProfiles, base, []);
|
||||
|
||||
assert.deepStrictEqual(actual.local.added, []);
|
||||
assert.deepStrictEqual(actual.local.removed, []);
|
||||
assert.deepStrictEqual(actual.local.updated, remoteProfiles);
|
||||
assert.strictEqual(actual.remote, null);
|
||||
});
|
||||
|
||||
});
|
||||
|
|
|
@ -32,8 +32,6 @@ suite('UserDataSyncService', () => {
|
|||
assert.deepStrictEqual(target.requests, [
|
||||
// Manifest
|
||||
{ type: 'GET', url: `${target.url}/v1/manifest`, headers: {} },
|
||||
// Profiles
|
||||
{ type: 'GET', url: `${target.url}/v1/resource/profiles/latest`, headers: {} },
|
||||
// Settings
|
||||
{ type: 'GET', url: `${target.url}/v1/resource/settings/latest`, headers: {} },
|
||||
{ type: 'POST', url: `${target.url}/v1/resource/settings`, headers: { 'If-Match': '0' } },
|
||||
|
@ -51,6 +49,8 @@ suite('UserDataSyncService', () => {
|
|||
{ type: 'POST', url: `${target.url}/v1/resource/globalState`, headers: { 'If-Match': '0' } },
|
||||
// Extensions
|
||||
{ type: 'GET', url: `${target.url}/v1/resource/extensions/latest`, headers: {} },
|
||||
// Profiles
|
||||
{ type: 'GET', url: `${target.url}/v1/resource/profiles/latest`, headers: {} },
|
||||
]);
|
||||
|
||||
});
|
||||
|
@ -69,8 +69,6 @@ suite('UserDataSyncService', () => {
|
|||
assert.deepStrictEqual(target.requests, [
|
||||
// Manifest
|
||||
{ type: 'GET', url: `${target.url}/v1/manifest`, headers: {} },
|
||||
// Profiles
|
||||
{ type: 'GET', url: `${target.url}/v1/resource/profiles/latest`, headers: {} },
|
||||
// Keybindings
|
||||
{ type: 'GET', url: `${target.url}/v1/resource/keybindings/latest`, headers: {} },
|
||||
{ type: 'POST', url: `${target.url}/v1/resource/keybindings`, headers: { 'If-Match': '0' } },
|
||||
|
@ -85,6 +83,8 @@ suite('UserDataSyncService', () => {
|
|||
{ type: 'POST', url: `${target.url}/v1/resource/globalState`, headers: { 'If-Match': '0' } },
|
||||
// Extensions
|
||||
{ type: 'GET', url: `${target.url}/v1/resource/extensions/latest`, headers: {} },
|
||||
// Profiles
|
||||
{ type: 'GET', url: `${target.url}/v1/resource/profiles/latest`, headers: {} },
|
||||
]);
|
||||
|
||||
});
|
||||
|
@ -102,8 +102,6 @@ suite('UserDataSyncService', () => {
|
|||
assert.deepStrictEqual(target.requests, [
|
||||
// Manifest
|
||||
{ type: 'GET', url: `${target.url}/v1/manifest`, headers: {} },
|
||||
// Profiles
|
||||
{ type: 'GET', url: `${target.url}/v1/resource/profiles/latest`, headers: {} },
|
||||
// Settings
|
||||
{ type: 'GET', url: `${target.url}/v1/resource/settings/latest`, headers: {} },
|
||||
// Keybindings
|
||||
|
@ -116,6 +114,8 @@ suite('UserDataSyncService', () => {
|
|||
{ type: 'GET', url: `${target.url}/v1/resource/globalState/latest`, headers: {} },
|
||||
// Extensions
|
||||
{ type: 'GET', url: `${target.url}/v1/resource/extensions/latest`, headers: {} },
|
||||
// Profiles
|
||||
{ type: 'GET', url: `${target.url}/v1/resource/profiles/latest`, headers: {} },
|
||||
]);
|
||||
|
||||
});
|
||||
|
@ -139,13 +139,13 @@ suite('UserDataSyncService', () => {
|
|||
|
||||
assert.deepStrictEqual(target.requests, [
|
||||
{ type: 'GET', url: `${target.url}/v1/manifest`, headers: {} },
|
||||
{ type: 'GET', url: `${target.url}/v1/resource/profiles/latest`, headers: {} },
|
||||
{ type: 'GET', url: `${target.url}/v1/resource/settings/latest`, headers: {} },
|
||||
{ type: 'GET', url: `${target.url}/v1/resource/keybindings/latest`, headers: {} },
|
||||
{ type: 'GET', url: `${target.url}/v1/resource/snippets/latest`, headers: {} },
|
||||
{ type: 'GET', url: `${target.url}/v1/resource/tasks/latest`, headers: {} },
|
||||
{ type: 'GET', url: `${target.url}/v1/resource/globalState/latest`, headers: {} },
|
||||
{ type: 'GET', url: `${target.url}/v1/resource/extensions/latest`, headers: {} },
|
||||
{ type: 'GET', url: `${target.url}/v1/resource/profiles/latest`, headers: {} },
|
||||
]);
|
||||
|
||||
});
|
||||
|
@ -177,7 +177,6 @@ suite('UserDataSyncService', () => {
|
|||
|
||||
assert.deepStrictEqual(target.requests, [
|
||||
{ type: 'GET', url: `${target.url}/v1/manifest`, headers: {} },
|
||||
{ type: 'GET', url: `${target.url}/v1/resource/profiles/latest`, headers: {} },
|
||||
{ type: 'GET', url: `${target.url}/v1/resource/settings/latest`, headers: {} },
|
||||
{ type: 'POST', url: `${target.url}/v1/resource/settings`, headers: { 'If-Match': '1' } },
|
||||
{ type: 'GET', url: `${target.url}/v1/resource/keybindings/latest`, headers: {} },
|
||||
|
@ -187,6 +186,7 @@ suite('UserDataSyncService', () => {
|
|||
{ type: 'GET', url: `${target.url}/v1/resource/tasks/latest`, headers: {} },
|
||||
{ type: 'GET', url: `${target.url}/v1/resource/globalState/latest`, headers: {} },
|
||||
{ type: 'GET', url: `${target.url}/v1/resource/extensions/latest`, headers: {} },
|
||||
{ type: 'GET', url: `${target.url}/v1/resource/profiles/latest`, headers: {} },
|
||||
]);
|
||||
|
||||
});
|
||||
|
@ -219,9 +219,6 @@ suite('UserDataSyncService', () => {
|
|||
|
||||
assert.deepStrictEqual(target.requests, [
|
||||
{ type: 'GET', url: `${target.url}/v1/manifest`, headers: {} },
|
||||
{ type: 'GET', url: `${target.url}/v1/resource/profiles/latest`, headers: {} },
|
||||
{ type: 'POST', url: `${target.url}/v1/collection`, headers: {} },
|
||||
{ type: 'POST', url: `${target.url}/v1/resource/profiles`, headers: { 'If-Match': '0' } },
|
||||
{ type: 'GET', url: `${target.url}/v1/resource/settings/latest`, headers: {} },
|
||||
{ type: 'POST', url: `${target.url}/v1/resource/settings`, headers: { 'If-Match': '1' } },
|
||||
{ type: 'GET', url: `${target.url}/v1/resource/keybindings/latest`, headers: {} },
|
||||
|
@ -231,6 +228,9 @@ suite('UserDataSyncService', () => {
|
|||
{ type: 'GET', url: `${target.url}/v1/resource/tasks/latest`, headers: {} },
|
||||
{ type: 'GET', url: `${target.url}/v1/resource/globalState/latest`, headers: {} },
|
||||
{ type: 'GET', url: `${target.url}/v1/resource/extensions/latest`, headers: {} },
|
||||
{ type: 'GET', url: `${target.url}/v1/resource/profiles/latest`, headers: {} },
|
||||
{ type: 'POST', url: `${target.url}/v1/collection`, headers: {} },
|
||||
{ type: 'POST', url: `${target.url}/v1/resource/profiles`, headers: { 'If-Match': '0' } },
|
||||
{ type: 'GET', url: `${target.url}/v1/collection/1/resource/settings/latest`, headers: {} },
|
||||
{ type: 'GET', url: `${target.url}/v1/collection/1/resource/keybindings/latest`, headers: {} },
|
||||
{ type: 'GET', url: `${target.url}/v1/collection/1/resource/snippets/latest`, headers: {} },
|
||||
|
@ -322,9 +322,6 @@ suite('UserDataSyncService', () => {
|
|||
assert.deepStrictEqual(target.requests, [
|
||||
// Manifest
|
||||
{ type: 'GET', url: `${target.url}/v1/manifest`, headers: {} },
|
||||
// Profiles
|
||||
{ type: 'POST', url: `${target.url}/v1/collection`, headers: {} },
|
||||
{ type: 'POST', url: `${target.url}/v1/resource/profiles`, headers: { 'If-Match': '0' } },
|
||||
// Settings
|
||||
{ type: 'POST', url: `${target.url}/v1/resource/settings`, headers: { 'If-Match': '1' } },
|
||||
// Keybindings
|
||||
|
@ -333,6 +330,9 @@ suite('UserDataSyncService', () => {
|
|||
{ type: 'POST', url: `${target.url}/v1/resource/snippets`, headers: { 'If-Match': '1' } },
|
||||
// Global state
|
||||
{ type: 'POST', url: `${target.url}/v1/resource/globalState`, headers: { 'If-Match': '1' } },
|
||||
// Profiles
|
||||
{ type: 'POST', url: `${target.url}/v1/collection`, headers: {} },
|
||||
{ type: 'POST', url: `${target.url}/v1/resource/profiles`, headers: { 'If-Match': '0' } },
|
||||
{ type: 'GET', url: `${target.url}/v1/collection/1/resource/settings/latest`, headers: {} },
|
||||
{ type: 'GET', url: `${target.url}/v1/collection/1/resource/keybindings/latest`, headers: {} },
|
||||
{ type: 'GET', url: `${target.url}/v1/collection/1/resource/snippets/latest`, headers: {} },
|
||||
|
@ -452,8 +452,6 @@ suite('UserDataSyncService', () => {
|
|||
assert.deepStrictEqual(target.requests, [
|
||||
// Manifest
|
||||
{ type: 'GET', url: `${target.url}/v1/manifest`, headers: {} },
|
||||
// Profiles
|
||||
{ type: 'GET', url: `${target.url}/v1/resource/profiles/latest`, headers: { 'If-None-Match': '0' } },
|
||||
// Settings
|
||||
{ type: 'GET', url: `${target.url}/v1/resource/settings/latest`, headers: { 'If-None-Match': '1' } },
|
||||
// Keybindings
|
||||
|
@ -462,6 +460,8 @@ suite('UserDataSyncService', () => {
|
|||
{ type: 'GET', url: `${target.url}/v1/resource/snippets/latest`, headers: { 'If-None-Match': '1' } },
|
||||
// Global state
|
||||
{ type: 'GET', url: `${target.url}/v1/resource/globalState/latest`, headers: { 'If-None-Match': '1' } },
|
||||
// Profiles
|
||||
{ type: 'GET', url: `${target.url}/v1/resource/profiles/latest`, headers: { 'If-None-Match': '0' } },
|
||||
{ type: 'GET', url: `${target.url}/v1/collection/1/resource/settings/latest`, headers: {} },
|
||||
{ type: 'GET', url: `${target.url}/v1/collection/1/resource/keybindings/latest`, headers: {} },
|
||||
{ type: 'GET', url: `${target.url}/v1/collection/1/resource/snippets/latest`, headers: {} },
|
||||
|
@ -487,8 +487,8 @@ suite('UserDataSyncService', () => {
|
|||
|
||||
assert.deepStrictEqual(target.requests, [
|
||||
// Manifest
|
||||
{ type: 'DELETE', url: `${target.url}/v1/resource`, headers: {} },
|
||||
{ type: 'DELETE', url: `${target.url}/v1/collection`, headers: {} },
|
||||
{ type: 'DELETE', url: `${target.url}/v1/resource`, headers: {} },
|
||||
]);
|
||||
|
||||
});
|
||||
|
@ -512,8 +512,6 @@ suite('UserDataSyncService', () => {
|
|||
assert.deepStrictEqual(target.requests, [
|
||||
// Manifest
|
||||
{ type: 'GET', url: `${target.url}/v1/manifest`, headers: {} },
|
||||
// Profiles
|
||||
{ type: 'GET', url: `${target.url}/v1/resource/profiles/latest`, headers: {} },
|
||||
// Settings
|
||||
{ type: 'GET', url: `${target.url}/v1/resource/settings/latest`, headers: {} },
|
||||
{ type: 'POST', url: `${target.url}/v1/resource/settings`, headers: { 'If-Match': '0' } },
|
||||
|
@ -531,6 +529,8 @@ suite('UserDataSyncService', () => {
|
|||
{ type: 'POST', url: `${target.url}/v1/resource/globalState`, headers: { 'If-Match': '0' } },
|
||||
// Extensions
|
||||
{ type: 'GET', url: `${target.url}/v1/resource/extensions/latest`, headers: {} },
|
||||
// Profiles
|
||||
{ type: 'GET', url: `${target.url}/v1/resource/profiles/latest`, headers: {} },
|
||||
]);
|
||||
|
||||
});
|
||||
|
|
|
@ -53,6 +53,7 @@ import { IHostService } from 'vs/workbench/services/host/browser/host';
|
|||
import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile';
|
||||
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
|
||||
import { ctxIsMergeResultEditor, ctxMergeBaseUri } from 'vs/workbench/contrib/mergeEditor/common/mergeEditor';
|
||||
import { IWorkbenchIssueService } from 'vs/workbench/services/issue/common/issue';
|
||||
|
||||
type ConfigureSyncQuickPickItem = { id: SyncResource; label: string; description?: string };
|
||||
|
||||
|
@ -115,7 +116,9 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo
|
|||
@IAuthenticationService private readonly authenticationService: IAuthenticationService,
|
||||
@IUserDataSyncStoreManagementService private readonly userDataSyncStoreManagementService: IUserDataSyncStoreManagementService,
|
||||
@IConfigurationService private readonly configurationService: IConfigurationService,
|
||||
@IHostService private readonly hostService: IHostService
|
||||
@IHostService private readonly hostService: IHostService,
|
||||
@ICommandService private readonly commandService: ICommandService,
|
||||
@IWorkbenchIssueService private readonly workbenchIssueService: IWorkbenchIssueService
|
||||
) {
|
||||
super();
|
||||
|
||||
|
@ -268,6 +271,10 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo
|
|||
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);
|
||||
}
|
||||
break;
|
||||
case UserDataSyncErrorCode.LocalTooManyProfiles:
|
||||
this.disableSync(SyncResource.Profiles);
|
||||
this.notificationService.error(localize('too many profiles', "Disabled syncing profiles because there are too many profiles to sync. Settings Sync supports syncing maximum 20 profiles. Please reduce the number of profiles and enable sync"));
|
||||
break;
|
||||
case UserDataSyncErrorCode.IncompatibleLocalContent:
|
||||
case UserDataSyncErrorCode.Gone:
|
||||
case UserDataSyncErrorCode.UpgradeRequired: {
|
||||
|
@ -279,6 +286,21 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo
|
|||
});
|
||||
break;
|
||||
}
|
||||
case UserDataSyncErrorCode.MethodNotFound: {
|
||||
const message = localize('method not found', "Settings sync is disabled because the client is making invalid requests. Please report an issue with the logs.");
|
||||
const operationId = error.operationId ? localize('operationId', "Operation Id: {0}", error.operationId) : undefined;
|
||||
this.notificationService.notify({
|
||||
severity: Severity.Error,
|
||||
message: operationId ? `${message} ${operationId}` : message,
|
||||
actions: {
|
||||
primary: [
|
||||
new Action('Show Sync Logs', localize('show sync logs', "Show Log"), undefined, true, () => this.commandService.executeCommand(SHOW_SYNC_LOG_COMMAND_ID)),
|
||||
new Action('Report Issue', localize('report issue', "Report Issue"), undefined, true, () => this.workbenchIssueService.openReporter())
|
||||
]
|
||||
}
|
||||
});
|
||||
break;
|
||||
}
|
||||
case UserDataSyncErrorCode.IncompatibleRemoteContent:
|
||||
this.notificationService.notify({
|
||||
severity: Severity.Error,
|
||||
|
@ -619,6 +641,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo
|
|||
case SyncResource.Tasks: return this.userDataSyncEnablementService.setResourceEnablement(SyncResource.Tasks, false);
|
||||
case SyncResource.Extensions: return this.userDataSyncEnablementService.setResourceEnablement(SyncResource.Extensions, false);
|
||||
case SyncResource.GlobalState: return this.userDataSyncEnablementService.setResourceEnablement(SyncResource.GlobalState, false);
|
||||
case SyncResource.Profiles: return this.userDataSyncEnablementService.setResourceEnablement(SyncResource.Profiles, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue