提交 c584c5c1 编写于 作者: S Sandeep Somavarapu

Implement getAllRefs and resolveContent for backup store

上级 7d17f6f9
......@@ -52,7 +52,7 @@ import { IProductService } from 'vs/platform/product/common/productService';
import { IUserDataSyncService, IUserDataSyncStoreService, registerConfiguration, IUserDataSyncLogService, IUserDataSyncUtilService, ISettingsSyncService, IUserDataSyncEnablementService, IUserDataSyncBackupStoreService } from 'vs/platform/userDataSync/common/userDataSync';
import { UserDataSyncService } from 'vs/platform/userDataSync/common/userDataSyncService';
import { UserDataSyncStoreService } from 'vs/platform/userDataSync/common/userDataSyncStoreService';
import { UserDataSyncChannel, UserDataSyncUtilServiceClient, SettingsSyncChannel, UserDataAutoSyncChannel, UserDataSyncStoreServiceChannel } from 'vs/platform/userDataSync/common/userDataSyncIpc';
import { UserDataSyncChannel, UserDataSyncUtilServiceClient, SettingsSyncChannel, UserDataAutoSyncChannel, UserDataSyncStoreServiceChannel, UserDataSyncBackupStoreServiceChannel } from 'vs/platform/userDataSync/common/userDataSyncIpc';
import { IElectronService } from 'vs/platform/electron/node/electron';
import { LoggerService } from 'vs/platform/log/node/loggerService';
import { UserDataSyncLogService } from 'vs/platform/userDataSync/common/userDataSyncLog';
......@@ -225,6 +225,10 @@ async function main(server: Server, initData: ISharedProcessInitData, configurat
const userDataSyncStoreServiceChannel = new UserDataSyncStoreServiceChannel(userDataSyncStoreService);
server.registerChannel('userDataSyncStoreService', userDataSyncStoreServiceChannel);
const userDataSyncBackupStoreService = accessor.get(IUserDataSyncBackupStoreService);
const userDataSyncBackupStoreServiceChannel = new UserDataSyncBackupStoreServiceChannel(userDataSyncBackupStoreService);
server.registerChannel('userDataSyncBackupStoreService', userDataSyncBackupStoreServiceChannel);
const settingsSyncService = accessor.get(ISettingsSyncService);
const settingsSyncChannel = new SettingsSyncChannel(settingsSyncService);
server.registerChannel('settingsSync', settingsSyncChannel);
......
......@@ -162,6 +162,10 @@ export abstract class AbstractSynchroniser extends Disposable {
return content;
}
async getLocalBackupContent(ref?: string): Promise<string | null> {
return this.userDataSyncBackupStoreService.resolveContent(this.resourceKey, ref);
}
async resetLocal(): Promise<void> {
try {
await this.fileService.del(this.lastSyncResource);
......
......@@ -123,18 +123,30 @@ export class ExtensionsSynchroniser extends AbstractSynchroniser implements IUse
async getRemoteContent(ref?: string, fragment?: string): Promise<string | null> {
const content = await super.getRemoteContent(ref);
if (content !== null && fragment) {
const syncData = this.parseSyncData(content);
if (syncData) {
switch (fragment) {
case 'extensions':
return syncData.content;
}
}
return null;
return this.getFragment(content, fragment);
}
return content;
}
async getLocalBackupContent(ref?: string, fragment?: string): Promise<string | null> {
let content = await super.getLocalBackupContent(ref);
if (content !== null && fragment) {
return this.getFragment(content, fragment);
}
return content;
}
private getFragment(content: string, fragment: string): string | null {
const syncData = this.parseSyncData(content);
if (syncData) {
switch (fragment) {
case 'extensions':
return syncData.content;
}
}
return null;
}
accept(content: string): Promise<void> {
throw new Error('Extensions: Conflicts should not occur');
}
......
......@@ -107,18 +107,30 @@ export class GlobalStateSynchroniser extends AbstractSynchroniser implements IUs
async getRemoteContent(ref?: string, fragment?: string): Promise<string | null> {
let content = await super.getRemoteContent(ref);
if (content !== null && fragment) {
const syncData = this.parseSyncData(content);
if (syncData) {
switch (fragment) {
case 'globalState':
return syncData.content;
}
}
return null;
return this.getFragment(content, fragment);
}
return content;
}
async getLocalBackupContent(ref?: string, fragment?: string): Promise<string | null> {
let content = await super.getLocalBackupContent(ref);
if (content !== null && fragment) {
return this.getFragment(content, fragment);
}
return content;
}
private getFragment(content: string, fragment: string): string | null {
const syncData = this.parseSyncData(content);
if (syncData) {
switch (fragment) {
case 'globalState':
return syncData.content;
}
}
return null;
}
accept(content: string): Promise<void> {
throw new Error('UI State: Conflicts should not occur');
}
......
......@@ -164,18 +164,30 @@ export class KeybindingsSynchroniser extends AbstractJsonFileSynchroniser implem
async getRemoteContent(ref?: string, fragment?: string): Promise<string | null> {
const content = await super.getRemoteContent(ref);
if (content !== null && fragment) {
const syncData = this.parseSyncData(content);
if (syncData) {
switch (fragment) {
case 'keybindings':
return this.getKeybindingsContentFromSyncContent(syncData.content);
}
}
return null;
return this.getFragment(content, fragment);
}
return content;
}
async getLocalBackupContent(ref?: string, fragment?: string): Promise<string | null> {
let content = await super.getLocalBackupContent(ref);
if (content !== null && fragment) {
return this.getFragment(content, fragment);
}
return content;
}
private getFragment(content: string, fragment: string): string | null {
const syncData = this.parseSyncData(content);
if (syncData) {
switch (fragment) {
case 'keybindings':
return this.getKeybindingsContentFromSyncContent(syncData.content);
}
}
return null;
}
protected async performSync(remoteUserData: IRemoteUserData, lastSyncUserData: IRemoteUserData | null): Promise<SyncStatus> {
try {
const result = await this.getPreview(remoteUserData, lastSyncUserData);
......
......@@ -205,19 +205,31 @@ export class SettingsSynchroniser extends AbstractJsonFileSynchroniser implement
async getRemoteContent(ref?: string, fragment?: string): Promise<string | null> {
let content = await super.getRemoteContent(ref);
if (content !== null && fragment) {
const syncData = this.parseSyncData(content);
if (syncData) {
const settingsSyncContent = this.parseSettingsSyncContent(syncData.content);
if (settingsSyncContent) {
switch (fragment) {
case 'settings':
return settingsSyncContent.settings;
}
return this.getFragment(content, fragment);
}
return content;
}
async getLocalBackupContent(ref?: string, fragment?: string): Promise<string | null> {
let content = await super.getLocalBackupContent(ref);
if (content !== null && fragment) {
return this.getFragment(content, fragment);
}
return content;
}
private getFragment(content: string, fragment: string): string | null {
const syncData = this.parseSyncData(content);
if (syncData) {
const settingsSyncContent = this.parseSettingsSyncContent(syncData.content);
if (settingsSyncContent) {
switch (fragment) {
case 'settings':
return settingsSyncContent.settings;
}
}
return null;
}
return content;
return null;
}
async accept(content: string): Promise<void> {
......
......@@ -165,6 +165,8 @@ export const IUserDataSyncBackupStoreService = createDecorator<IUserDataSyncBack
export interface IUserDataSyncBackupStoreService {
_serviceBrand: undefined;
backup(resourceKey: ResourceKey, content: string): Promise<void>;
getAllRefs(key: ResourceKey): Promise<IResourceRefHandle[]>;
resolveContent(key: ResourceKey, ref?: string): Promise<string | null>;
}
//#endregion
......@@ -261,6 +263,7 @@ export interface IUserDataSynchroniser {
getRemoteContentFromPreview(): Promise<string | null>;
getRemoteContent(ref?: string, fragment?: string): Promise<string | null>;
getLocalBackupContent(ref?: string, fragment?: string): Promise<string | null>;
accept(content: string): Promise<void>;
}
......@@ -354,6 +357,9 @@ export function toRemoteSyncResourceFromSource(source: SyncSource, ref?: string)
export function toRemoteSyncResource(resourceKey: ResourceKey, ref?: string): URI {
return URI.from({ scheme: USER_DATA_SYNC_SCHEME, authority: 'remote', path: `/${resourceKey}/${ref ? ref : 'latest'}` });
}
export function toLocalBackupSyncResource(resourceKey: ResourceKey, ref?: string): URI {
return URI.from({ scheme: USER_DATA_SYNC_SCHEME, authority: 'local-backup', path: `/${resourceKey}/${ref ? ref : 'latest'}` });
}
export function resolveSyncResource(resource: URI): { remote: boolean, resourceKey: ResourceKey, ref?: string } | null {
const remote = resource.authority === 'remote';
......
......@@ -4,10 +4,10 @@
*--------------------------------------------------------------------------------------------*/
import { Disposable, } from 'vs/base/common/lifecycle';
import { IUserDataSyncLogService, ResourceKey, ALL_RESOURCE_KEYS, IUserDataSyncBackupStoreService } from 'vs/platform/userDataSync/common/userDataSync';
import { IUserDataSyncLogService, ResourceKey, ALL_RESOURCE_KEYS, IUserDataSyncBackupStoreService, IResourceRefHandle } from 'vs/platform/userDataSync/common/userDataSync';
import { joinPath } from 'vs/base/common/resources';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IFileService } from 'vs/platform/files/common/files';
import { IFileService, IFileStat } from 'vs/platform/files/common/files';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { toLocalISOString } from 'vs/base/common/date';
import { VSBuffer } from 'vs/base/common/buffer';
......@@ -26,6 +26,34 @@ export class UserDataSyncBackupStoreService extends Disposable implements IUserD
ALL_RESOURCE_KEYS.forEach(resourceKey => this.cleanUpBackup(resourceKey));
}
async getAllRefs(resourceKey: ResourceKey): Promise<IResourceRefHandle[]> {
const folder = joinPath(this.environmentService.userDataSyncHome, resourceKey);
const stat = await this.fileService.resolve(folder);
if (stat.children) {
const all = stat.children.filter(stat => stat.isFile && /^\d{8}T\d{6}(\.json)?$/.test(stat.name)).sort().reverse();
return all.map(stat => ({
ref: stat.name,
created: this.getCreationTime(stat)
}));
}
return [];
}
async resolveContent(resourceKey: ResourceKey, ref?: string): Promise<string | null> {
if (!ref) {
const refs = await this.getAllRefs(resourceKey);
if (refs.length) {
ref = refs[refs.length - 1].ref;
}
}
if (ref) {
const file = joinPath(this.environmentService.userDataSyncHome, resourceKey, ref);
const content = await this.fileService.readFile(file);
return content.value.toString();
}
return null;
}
async backup(resourceKey: ResourceKey, content: string): Promise<void> {
const folder = joinPath(this.environmentService.userDataSyncHome, resourceKey);
const resource = joinPath(folder, `${toLocalISOString(new Date()).replace(/-|:|\.\d+Z$/g, '')}.json`);
......@@ -53,17 +81,7 @@ export class UserDataSyncBackupStoreService extends Disposable implements IUserD
if (stat.children) {
const all = stat.children.filter(stat => stat.isFile && /^\d{8}T\d{6}(\.json)?$/.test(stat.name)).sort();
const backUpMaxAge = 1000 * 60 * 60 * 24 * (this.configurationService.getValue<number>('sync.localBackupDuration') || 30 /* Default 30 days */);
let toDelete = all.filter(stat => {
const ctime = stat.ctime || new Date(
parseInt(stat.name.substring(0, 4)),
parseInt(stat.name.substring(4, 6)) - 1,
parseInt(stat.name.substring(6, 8)),
parseInt(stat.name.substring(9, 11)),
parseInt(stat.name.substring(11, 13)),
parseInt(stat.name.substring(13, 15))
).getTime();
return Date.now() - ctime > backUpMaxAge;
});
let toDelete = all.filter(stat => Date.now() - this.getCreationTime(stat) > backUpMaxAge);
const remaining = all.length - toDelete.length;
if (remaining < 10) {
toDelete = toDelete.slice(10 - remaining);
......@@ -77,4 +95,15 @@ export class UserDataSyncBackupStoreService extends Disposable implements IUserD
this.logService.error(e);
}
}
private getCreationTime(stat: IFileStat) {
return stat.ctime || new Date(
parseInt(stat.name.substring(0, 4)),
parseInt(stat.name.substring(4, 6)) - 1,
parseInt(stat.name.substring(6, 8)),
parseInt(stat.name.substring(9, 11)),
parseInt(stat.name.substring(11, 13)),
parseInt(stat.name.substring(13, 15))
).getTime();
}
}
......@@ -5,7 +5,7 @@
import { IServerChannel, IChannel } from 'vs/base/parts/ipc/common/ipc';
import { Event } from 'vs/base/common/event';
import { IUserDataSyncService, IUserDataSyncUtilService, ISettingsSyncService, IUserDataAutoSyncService, IUserDataSyncStoreService } from 'vs/platform/userDataSync/common/userDataSync';
import { IUserDataSyncService, IUserDataSyncUtilService, ISettingsSyncService, IUserDataAutoSyncService, IUserDataSyncStoreService, IUserDataSyncBackupStoreService } from 'vs/platform/userDataSync/common/userDataSync';
import { URI } from 'vs/base/common/uri';
import { IStringDictionary } from 'vs/base/common/collections';
import { FormattingOptions } from 'vs/base/common/jsonFormatter';
......@@ -69,6 +69,7 @@ export class SettingsSyncChannel implements IServerChannel {
case 'resolveSettingsConflicts': return this.service.resolveSettingsConflicts(args[0]);
case 'getRemoteContentFromPreview': return this.service.getRemoteContentFromPreview();
case 'getRemoteContent': return this.service.getRemoteContent(args[0], args[1]);
case 'getLocalBackupContent': return this.service.getLocalBackupContent(args[0], args[1]);
}
throw new Error('Invalid call');
}
......@@ -149,3 +150,20 @@ export class UserDataSyncStoreServiceChannel implements IServerChannel {
throw new Error('Invalid call');
}
}
export class UserDataSyncBackupStoreServiceChannel implements IServerChannel {
constructor(private readonly service: IUserDataSyncBackupStoreService) { }
listen(_: unknown, event: string): Event<any> {
throw new Error(`Event not found: ${event}`);
}
call(context: any, command: string, args?: any): Promise<any> {
switch (command) {
case 'getAllRefs': return this.service.getAllRefs(args[0]);
case 'resolveContent': return this.service.resolveContent(args[0], args[1]);
}
throw new Error('Invalid call');
}
}
......@@ -185,7 +185,7 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ
if (PREVIEW_QUERY === resource.query) {
return result.remote ? synchronizer.getRemoteContentFromPreview() : null;
}
return result.remote ? synchronizer.getRemoteContent(result.ref, resource.fragment) : null;
return result.remote ? synchronizer.getRemoteContent(result.ref, resource.fragment) : synchronizer.getLocalBackupContent(result.ref, resource.fragment);
}
}
return null;
......
......@@ -88,6 +88,10 @@ export class SettingsSyncService extends Disposable implements ISettingsSyncServ
return this.channel.call('getRemoteContent', [ref, fragment]);
}
getLocalBackupContent(ref?: string, fragment?: string): Promise<string | null> {
return this.channel.call('getLocalBackupContent', [ref, fragment]);
}
getRemoteContentFromPreview(): Promise<string | null> {
return this.channel.call('getRemoteContentFromPreview', []);
}
......
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ResourceKey, IResourceRefHandle, IUserDataSyncBackupStoreService } from 'vs/platform/userDataSync/common/userDataSync';
import { ISharedProcessService } from 'vs/platform/ipc/electron-browser/sharedProcessService';
import { IChannel } from 'vs/base/parts/ipc/common/ipc';
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
export class UserDataSyncBackupStoreService implements IUserDataSyncBackupStoreService {
_serviceBrand: undefined;
private readonly channel: IChannel;
constructor(
@ISharedProcessService sharedProcessService: ISharedProcessService,
) {
this.channel = sharedProcessService.getChannel('userDataSyncBackupStoreService');
}
backup(key: ResourceKey, content: string): Promise<void> {
return this.channel.call('backup', [key, content]);
}
getAllRefs(key: ResourceKey): Promise<IResourceRefHandle[]> {
return this.channel.call('getAllRefs', [key]);
}
resolveContent(key: ResourceKey, ref: string): Promise<string | null> {
return this.channel.call('resolveContent', [key, ref]);
}
}
registerSingleton(IUserDataSyncBackupStoreService, UserDataSyncBackupStoreService);
......@@ -53,6 +53,7 @@ import 'vs/workbench/services/userDataSync/electron-browser/userDataSyncService'
import 'vs/workbench/services/userDataSync/electron-browser/settingsSyncService';
import 'vs/workbench/services/userDataSync/electron-browser/userDataAutoSyncService';
import 'vs/workbench/services/userDataSync/electron-browser/userDataSyncStoreService';
import 'vs/workbench/services/userDataSync/electron-browser/userDataSyncBackupStoreService';
import 'vs/workbench/services/authentication/electron-browser/authenticationTokenService';
import 'vs/workbench/services/authentication/browser/authenticationService';
import 'vs/workbench/services/host/electron-browser/desktopHostService';
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册