userDataSyncService.ts 6.6 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 { IUserDataSyncService, SyncStatus, ISynchroniser, IUserDataSyncStoreService, SyncSource, IUserDataSyncLogService } from 'vs/platform/userDataSync/common/userDataSync';
7
import { Disposable } from 'vs/base/common/lifecycle';
8
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
9
import { SettingsSynchroniser } from 'vs/platform/userDataSync/common/settingsSync';
S
Sandeep Somavarapu 已提交
10
import { Emitter, Event } from 'vs/base/common/event';
11 12
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { timeout } from 'vs/base/common/async';
S
Sandeep Somavarapu 已提交
13
import { ExtensionsSynchroniser } from 'vs/platform/userDataSync/common/extensionsSync';
14
import { IExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
S
Sandeep Somavarapu 已提交
15
import { IAuthTokenService, AuthTokenStatus } from 'vs/platform/auth/common/auth';
16 17 18 19 20

export class UserDataSyncService extends Disposable implements IUserDataSyncService {

	_serviceBrand: any;

21
	private readonly synchronisers: ISynchroniser[];
S
Sandeep Somavarapu 已提交
22

23 24
	private _status: SyncStatus = SyncStatus.Uninitialized;
	get status(): SyncStatus { return this._status; }
25 26
	private _onDidChangeStatus: Emitter<SyncStatus> = this._register(new Emitter<SyncStatus>());
	readonly onDidChangeStatus: Event<SyncStatus> = this._onDidChangeStatus.event;
S
Sandeep Somavarapu 已提交
27

S
Sandeep Somavarapu 已提交
28 29
	readonly onDidChangeLocal: Event<void>;

30 31 32
	private _conflictsSource: SyncSource | null = null;
	get conflictsSource(): SyncSource | null { return this._conflictsSource; }

33 34 35
	private readonly settingsSynchroniser: SettingsSynchroniser;
	private readonly extensionsSynchroniser: ExtensionsSynchroniser;

36
	constructor(
S
Sandeep Somavarapu 已提交
37
		@IUserDataSyncStoreService private readonly userDataSyncStoreService: IUserDataSyncStoreService,
38
		@IInstantiationService private readonly instantiationService: IInstantiationService,
S
Sandeep Somavarapu 已提交
39
		@IAuthTokenService private readonly authTokenService: IAuthTokenService,
40 41
	) {
		super();
42 43 44
		this.settingsSynchroniser = this._register(this.instantiationService.createInstance(SettingsSynchroniser));
		this.extensionsSynchroniser = this._register(this.instantiationService.createInstance(ExtensionsSynchroniser));
		this.synchronisers = [this.settingsSynchroniser, this.extensionsSynchroniser];
45
		this.updateStatus();
46
		this._register(Event.any(...this.synchronisers.map(s => Event.map(s.onDidChangeStatus, () => undefined)))(() => this.updateStatus()));
S
Sandeep Somavarapu 已提交
47
		this.onDidChangeLocal = Event.any(...this.synchronisers.map(s => s.onDidChangeLocal));
48
		this._register(authTokenService.onDidChangeStatus(() => this.onDidChangeAuthTokenStatus()));
49 50
	}

51
	async sync(_continue?: boolean): Promise<boolean> {
52
		if (!this.userDataSyncStoreService.enabled) {
S
Sandeep Somavarapu 已提交
53 54
			throw new Error('Not enabled');
		}
55
		if (this.authTokenService.status === AuthTokenStatus.Inactive) {
S
Sandeep Somavarapu 已提交
56 57
			return Promise.reject('Not Authenticated. Please sign in to start sync.');
		}
58
		for (const synchroniser of this.synchronisers) {
59
			if (!await synchroniser.sync(_continue)) {
S
Sandeep Somavarapu 已提交
60
				return false;
61 62
			}
		}
S
Sandeep Somavarapu 已提交
63
		return true;
64 65
	}

S
Sandeep Somavarapu 已提交
66 67 68 69 70 71 72 73 74
	stop(): void {
		if (!this.userDataSyncStoreService.enabled) {
			throw new Error('Not enabled');
		}
		for (const synchroniser of this.synchronisers) {
			synchroniser.stop();
		}
	}

75 76 77 78
	removeExtension(identifier: IExtensionIdentifier): Promise<void> {
		return this.extensionsSynchroniser.removeExtension(identifier);
	}

79
	private updateStatus(): void {
80
		this._conflictsSource = this.computeConflictsSource();
81 82 83 84 85 86
		this.setStatus(this.computeStatus());
	}

	private setStatus(status: SyncStatus): void {
		if (this._status !== status) {
			this._status = status;
87
			this._onDidChangeStatus.fire(status);
88
		}
89 90
	}

91
	private computeStatus(): SyncStatus {
92
		if (!this.userDataSyncStoreService.enabled) {
93 94 95 96 97 98 99 100 101 102
			return SyncStatus.Uninitialized;
		}
		if (this.synchronisers.some(s => s.status === SyncStatus.HasConflicts)) {
			return SyncStatus.HasConflicts;
		}
		if (this.synchronisers.some(s => s.status === SyncStatus.Syncing)) {
			return SyncStatus.Syncing;
		}
		return SyncStatus.Idle;
	}
103

104 105 106 107 108 109 110 111 112 113
	private computeConflictsSource(): SyncSource | null {
		const source = this.synchronisers.filter(s => s.status === SyncStatus.HasConflicts)[0];
		if (source) {
			if (source instanceof SettingsSynchroniser) {
				return SyncSource.Settings;
			}
		}
		return null;
	}

114 115 116 117 118 119
	private onDidChangeAuthTokenStatus(): void {
		if (this.authTokenService.status === AuthTokenStatus.Inactive) {
			this.stop();
		}
	}

120 121 122 123
}

export class UserDataAutoSync extends Disposable {

124 125
	private enabled: boolean = false;

126 127 128 129
	constructor(
		@IConfigurationService private readonly configurationService: IConfigurationService,
		@IUserDataSyncService private readonly userDataSyncService: IUserDataSyncService,
		@IUserDataSyncStoreService userDataSyncStoreService: IUserDataSyncStoreService,
S
Sandeep Somavarapu 已提交
130
		@IUserDataSyncLogService private readonly userDataSyncLogService: IUserDataSyncLogService,
S
Sandeep Somavarapu 已提交
131
		@IAuthTokenService private readonly authTokenService: IAuthTokenService,
132 133
	) {
		super();
134 135 136
		this.updateEnablement(false);
		this._register(Event.any<any>(authTokenService.onDidChangeStatus, userDataSyncService.onDidChangeStatus)(() => this.updateEnablement(true)));
		this._register(Event.filter(this.configurationService.onDidChangeConfiguration, e => e.affectsConfiguration('configurationSync.enable'))(() => this.updateEnablement(true)));
137

S
Sandeep Somavarapu 已提交
138 139
		// Sync immediately if there is a local change.
		this._register(Event.debounce(this.userDataSyncService.onDidChangeLocal, () => undefined, 500)(() => this.sync(false)));
140 141
	}

142
	private updateEnablement(stopIfDisabled: boolean): void {
143
		const enabled = this.isSyncEnabled();
144 145 146 147 148 149 150 151 152 153 154
		if (this.enabled === enabled) {
			return;
		}

		this.enabled = enabled;
		if (this.enabled) {
			this.userDataSyncLogService.info('Syncing configuration started');
			this.sync(true);
			return;
		} else {
			if (stopIfDisabled) {
155 156 157 158
				this.userDataSyncService.stop();
				this.userDataSyncLogService.info('Syncing configuration stopped.');
			}
		}
159

160 161
	}

162
	private async sync(loop: boolean): Promise<void> {
163
		if (this.enabled) {
S
Sandeep Somavarapu 已提交
164 165 166 167
			try {
				await this.userDataSyncService.sync();
			} catch (e) {
				this.userDataSyncLogService.error(e);
168 169 170 171 172 173 174 175 176
			}
			if (loop) {
				await timeout(1000 * 5); // Loop sync for every 5s.
				this.sync(loop);
			}
		}
	}

	private isSyncEnabled(): boolean {
S
Sandeep Somavarapu 已提交
177 178 179
		return this.configurationService.getValue<boolean>('configurationSync.enable')
			&& this.userDataSyncService.status !== SyncStatus.Uninitialized
			&& this.authTokenService.status !== AuthTokenStatus.Inactive;
180 181
	}

182
}