globalStateSync.ts 8.3 KB
Newer Older
1 2 3 4 5
/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/

S
Sandeep Somavarapu 已提交
6
import { SyncStatus, IUserDataSyncStoreService, IUserDataSyncLogService, IGlobalState, SyncSource, IUserDataSynchroniser, ResourceKey, IUserDataSyncEnablementService } from 'vs/platform/userDataSync/common/userDataSync';
7
import { VSBuffer } from 'vs/base/common/buffer';
S
Sandeep Somavarapu 已提交
8
import { Event } from 'vs/base/common/event';
9
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
S
Sandeep Somavarapu 已提交
10
import { dirname } from 'vs/base/common/resources';
11 12 13 14
import { IFileService } from 'vs/platform/files/common/files';
import { IStringDictionary } from 'vs/base/common/collections';
import { edit } from 'vs/platform/userDataSync/common/content';
import { merge } from 'vs/platform/userDataSync/common/globalStateMerge';
S
Sandeep Somavarapu 已提交
15
import { parse } from 'vs/base/common/json';
S
Sandeep Somavarapu 已提交
16
import { AbstractSynchroniser, IRemoteUserData } from 'vs/platform/userDataSync/common/abstractSynchronizer';
17
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
S
Sandeep Somavarapu 已提交
18
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
19 20 21

const argvProperties: string[] = ['locale'];

22 23 24
interface ISyncPreviewResult {
	readonly local: IGlobalState | undefined;
	readonly remote: IGlobalState | undefined;
25
	readonly localUserData: IGlobalState;
S
Sandeep Somavarapu 已提交
26 27
	readonly remoteUserData: IRemoteUserData;
	readonly lastSyncUserData: IRemoteUserData | null;
28 29
}

30
export class GlobalStateSynchroniser extends AbstractSynchroniser implements IUserDataSynchroniser {
31

S
Sandeep Somavarapu 已提交
32
	readonly resourceKey: ResourceKey = 'globalState';
S
Sandeep Somavarapu 已提交
33
	protected readonly version: number = 1;
34

35
	constructor(
36
		@IFileService fileService: IFileService,
S
Sandeep Somavarapu 已提交
37
		@IUserDataSyncStoreService userDataSyncStoreService: IUserDataSyncStoreService,
38
		@IUserDataSyncLogService logService: IUserDataSyncLogService,
39
		@IEnvironmentService private readonly environmentService: IEnvironmentService,
S
Sandeep Somavarapu 已提交
40
		@IUserDataSyncEnablementService userDataSyncEnablementService: IUserDataSyncEnablementService,
41
		@ITelemetryService telemetryService: ITelemetryService,
S
Sandeep Somavarapu 已提交
42
		@IConfigurationService configurationService: IConfigurationService,
43
	) {
S
Sandeep Somavarapu 已提交
44
		super(SyncSource.GlobalState, fileService, environmentService, userDataSyncStoreService, userDataSyncEnablementService, telemetryService, logService, configurationService);
45
		this._register(this.fileService.watch(dirname(this.environmentService.argvResource)));
46
		this._register(Event.filter(this.fileService.onDidFilesChange, e => e.contains(this.environmentService.argvResource))(() => this._onDidChangeLocal.fire()));
47 48
	}

49
	async pull(): Promise<void> {
50
		if (!this.enabled) {
51 52 53 54 55 56 57 58 59 60
			this.logService.info('UI State: Skipped pulling ui state as it is disabled.');
			return;
		}

		this.stop();

		try {
			this.logService.info('UI State: Started pulling ui state...');
			this.setStatus(SyncStatus.Syncing);

61 62
			const lastSyncUserData = await this.getLastSyncUserData();
			const remoteUserData = await this.getRemoteUserData(lastSyncUserData);
63

S
Sandeep Somavarapu 已提交
64
			if (remoteUserData.syncData !== null) {
65
				const localUserData = await this.getLocalGlobalState();
S
Sandeep Somavarapu 已提交
66
				const local: IGlobalState = JSON.parse(remoteUserData.syncData.content);
67
				await this.apply({ local, remote: undefined, remoteUserData, localUserData, lastSyncUserData });
68 69 70 71 72 73 74 75 76 77 78 79 80 81
			}

			// No remote exists to pull
			else {
				this.logService.info('UI State: Remote UI state does not exist.');
			}

			this.logService.info('UI State: Finished pulling UI state.');
		} finally {
			this.setStatus(SyncStatus.Idle);
		}
	}

	async push(): Promise<void> {
82
		if (!this.enabled) {
83 84 85 86 87 88 89 90 91 92
			this.logService.info('UI State: Skipped pushing UI State as it is disabled.');
			return;
		}

		this.stop();

		try {
			this.logService.info('UI State: Started pushing UI State...');
			this.setStatus(SyncStatus.Syncing);

93
			const localUserData = await this.getLocalGlobalState();
94 95
			const lastSyncUserData = await this.getLastSyncUserData();
			const remoteUserData = await this.getRemoteUserData(lastSyncUserData);
96
			await this.apply({ local: undefined, remote: localUserData, remoteUserData, localUserData, lastSyncUserData }, true);
97

S
Sandeep Somavarapu 已提交
98
			this.logService.info('UI State: Finished pushing UI State.');
99 100 101 102 103 104
		} finally {
			this.setStatus(SyncStatus.Idle);
		}

	}

S
Sandeep Somavarapu 已提交
105 106
	async stop(): Promise<void> { }

S
Sandeep Somavarapu 已提交
107
	accept(content: string): Promise<void> {
S
Sandeep Somavarapu 已提交
108 109
		throw new Error('UI State: Conflicts should not occur');
	}
110

111 112 113 114 115 116 117 118 119 120 121 122
	async hasLocalData(): Promise<boolean> {
		try {
			const localGloablState = await this.getLocalGlobalState();
			if (localGloablState.argv['locale'] !== 'en') {
				return true;
			}
		} catch (error) {
			/* ignore error */
		}
		return false;
	}

123 124 125 126
	async getRemoteContent(): Promise<string | null> {
		return null;
	}

127 128 129 130
	protected async performSync(remoteUserData: IRemoteUserData, lastSyncUserData: IRemoteUserData | null): Promise<SyncStatus> {
		const result = await this.getPreview(remoteUserData, lastSyncUserData);
		await this.apply(result);
		return SyncStatus.Idle;
131 132
	}

M
Matt Bierner 已提交
133
	private async getPreview(remoteUserData: IRemoteUserData, lastSyncUserData: IRemoteUserData | null,): Promise<ISyncPreviewResult> {
S
Sandeep Somavarapu 已提交
134 135
		const remoteGlobalState: IGlobalState = remoteUserData.syncData ? JSON.parse(remoteUserData.syncData.content) : null;
		const lastSyncGlobalState = lastSyncUserData && lastSyncUserData.syncData ? JSON.parse(lastSyncUserData.syncData.content) : null;
136 137 138

		const localGloablState = await this.getLocalGlobalState();

S
Sandeep Somavarapu 已提交
139 140 141 142 143 144
		if (remoteGlobalState) {
			this.logService.trace('UI State: Merging remote ui state with local ui state...');
		} else {
			this.logService.trace('UI State: Remote ui state does not exist. Synchronizing ui state for the first time.');
		}

145 146
		const { local, remote } = merge(localGloablState, remoteGlobalState, lastSyncGlobalState);

147
		return { local, remote, remoteUserData, localUserData: localGloablState, lastSyncUserData };
148 149
	}

150
	private async apply({ local, remote, remoteUserData, lastSyncUserData, localUserData }: ISyncPreviewResult, forcePush?: boolean): Promise<void> {
S
Sandeep Somavarapu 已提交
151 152 153 154

		const hasChanges = local || remote;

		if (!hasChanges) {
155
			this.logService.info('UI State: No changes found during synchronizing ui state.');
S
Sandeep Somavarapu 已提交
156 157
		}

158 159
		if (local) {
			// update local
S
Sandeep Somavarapu 已提交
160
			this.logService.trace('UI State: Updating local ui state...');
S
Sandeep Somavarapu 已提交
161
			await this.backupLocal(VSBuffer.fromString(JSON.stringify(localUserData, null, '\t')));
162
			await this.writeLocalGlobalState(local);
S
Sandeep Somavarapu 已提交
163
			this.logService.info('UI State: Updated local ui state');
164 165 166 167
		}

		if (remote) {
			// update remote
S
Sandeep Somavarapu 已提交
168
			this.logService.trace('UI State: Updating remote ui state...');
S
Sandeep Somavarapu 已提交
169
			const content = JSON.stringify(remote);
S
Sandeep Somavarapu 已提交
170
			remoteUserData = await this.updateRemoteUserData(content, forcePush ? null : remoteUserData.ref);
S
Sandeep Somavarapu 已提交
171
			this.logService.info('UI State: Updated remote ui state');
172 173
		}

S
Sandeep Somavarapu 已提交
174
		if (lastSyncUserData?.ref !== remoteUserData.ref) {
175
			// update last sync
S
Sandeep Somavarapu 已提交
176
			this.logService.trace('UI State: Updating last synchronized ui state...');
S
Sandeep Somavarapu 已提交
177
			await this.updateLastSyncUserData(remoteUserData);
S
Sandeep Somavarapu 已提交
178
			this.logService.info('UI State: Updated last synchronized ui state');
179 180 181 182 183 184
		}
	}

	private async getLocalGlobalState(): Promise<IGlobalState> {
		const argv: IStringDictionary<any> = {};
		const storage: IStringDictionary<any> = {};
S
Sandeep Somavarapu 已提交
185 186 187 188 189 190 191 192 193 194 195
		const argvContent: string = await this.getLocalArgvContent();
		const argvValue: IStringDictionary<any> = parse(argvContent);
		for (const argvProperty of argvProperties) {
			if (argvValue[argvProperty] !== undefined) {
				argv[argvProperty] = argvValue[argvProperty];
			}
		}
		return { argv, storage };
	}

	private async getLocalArgvContent(): Promise<string> {
196 197
		try {
			const content = await this.fileService.readFile(this.environmentService.argvResource);
S
Sandeep Somavarapu 已提交
198
			return content.value.toString();
199
		} catch (error) { }
S
Sandeep Somavarapu 已提交
200
		return '{}';
201 202 203
	}

	private async writeLocalGlobalState(globalState: IGlobalState): Promise<void> {
S
Sandeep Somavarapu 已提交
204 205
		const argvContent = await this.getLocalArgvContent();
		let content = argvContent;
206
		for (const argvProperty of Object.keys(globalState.argv)) {
S
Sandeep Somavarapu 已提交
207
			content = edit(content, [argvProperty], globalState.argv[argvProperty], {});
208
		}
S
Sandeep Somavarapu 已提交
209 210
		if (argvContent !== content) {
			await this.fileService.writeFile(this.environmentService.argvResource, VSBuffer.fromString(content));
211 212 213 214
		}
	}

}