Skip to content

Commit

Permalink
use if match and if not match headers
Browse files Browse the repository at this point in the history
  • Loading branch information
sandy081 committed Sep 18, 2019
1 parent 88e8f36 commit c0072b6
Show file tree
Hide file tree
Showing 3 changed files with 41 additions and 21 deletions.
20 changes: 10 additions & 10 deletions src/vs/platform/userDataSync/common/settingsSync.ts
Expand Up @@ -18,7 +18,7 @@ import { joinPath } from 'vs/base/common/resources';

interface ISyncPreviewResult {
readonly fileContent: IFileContent | null;
readonly remoteUserData: IUserData | null;
readonly remoteUserData: IUserData;
readonly hasLocalChanged: boolean;
readonly hasRemoteChanged: boolean;
readonly hasConflicts: boolean;
Expand Down Expand Up @@ -135,13 +135,13 @@ export class SettingsSynchroniser extends Disposable implements ISynchroniser {

let { fileContent, remoteUserData, hasLocalChanged, hasRemoteChanged } = await this.syncPreviewResultPromise;
if (hasRemoteChanged) {
const ref = await this.writeToRemote(content, remoteUserData ? remoteUserData.ref : null);
const ref = await this.writeToRemote(content, remoteUserData.ref);
remoteUserData = { ref, content };
}
if (hasLocalChanged) {
await this.writeToLocal(content, fileContent);
}
if (remoteUserData) {
if (remoteUserData.content) {
await this.updateLastSyncValue(remoteUserData);
}

Expand All @@ -167,33 +167,33 @@ export class SettingsSynchroniser extends Disposable implements ISynchroniser {
}

private async generatePreview(): Promise<ISyncPreviewResult> {
const remoteUserData = await this.userDataSyncStoreService.read(SettingsSynchroniser.EXTERNAL_USER_DATA_SETTINGS_KEY);
const lastSyncData = await this.getLastSyncUserData();
const remoteUserData = await this.userDataSyncStoreService.read(SettingsSynchroniser.EXTERNAL_USER_DATA_SETTINGS_KEY, lastSyncData);
const remoteContent: string | null = remoteUserData.content;
// Get file content last to get the latest
const fileContent = await this.getLocalFileContent();
let hasLocalChanged: boolean = false;
let hasRemoteChanged: boolean = false;
let hasConflicts: boolean = false;

// First time sync to remote
if (fileContent && !remoteUserData) {
if (fileContent && !remoteContent) {
this.logService.trace('Settings Sync: Remote contents does not exist. So sync with settings file.');
hasRemoteChanged = true;
await this.fileService.writeFile(this.environmentService.settingsSyncPreviewResource, VSBuffer.fromString(fileContent.value.toString()));
return { fileContent, remoteUserData, hasLocalChanged, hasRemoteChanged, hasConflicts };
}

// Settings file does not exist, so sync with remote contents.
if (remoteUserData && !fileContent) {
if (remoteContent && !fileContent) {
this.logService.trace('Settings Sync: Settings file does not exist. So sync with remote contents');
hasLocalChanged = true;
await this.fileService.writeFile(this.environmentService.settingsSyncPreviewResource, VSBuffer.fromString(remoteUserData.content));
await this.fileService.writeFile(this.environmentService.settingsSyncPreviewResource, VSBuffer.fromString(remoteContent));
return { fileContent, remoteUserData, hasLocalChanged, hasRemoteChanged, hasConflicts };
}

if (fileContent && remoteUserData) {
if (fileContent && remoteContent) {
const localContent: string = fileContent.value.toString();
const remoteContent: string = remoteUserData.content;
const lastSyncData = await this.getLastSyncUserData();
if (!lastSyncData // First time sync
|| lastSyncData.content !== localContent // Local has moved forwarded
|| lastSyncData.content !== remoteContent // Remote has moved forwarded
Expand Down
6 changes: 3 additions & 3 deletions src/vs/platform/userDataSync/common/userDataSync.ts
Expand Up @@ -8,7 +8,7 @@ import { Event } from 'vs/base/common/event';

export interface IUserData {
ref: string;
content: string;
content: string | null;
}

export enum UserDataSyncStoreErrorCode {
Expand Down Expand Up @@ -36,7 +36,7 @@ export interface IUserDataSyncStoreService {
login(): Promise<void>;
logout(): Promise<void>;

read(key: string, oldValue: IUserData | null): Promise<IUserData | null>;
read(key: string, oldValue: IUserData | null): Promise<IUserData>;
write(key: string, content: string, ref: string | null): Promise<string>;
}

Expand Down Expand Up @@ -74,6 +74,6 @@ export interface ISettingsMergeService {

_serviceBrand: undefined;

merge(localContent: string, remoteContent: string, baseContent: string | null): Promise<{mergeContent: string, hasChanges: boolean, hasConflicts: boolean}>;
merge(localContent: string, remoteContent: string, baseContent: string | null): Promise<{ mergeContent: string, hasChanges: boolean, hasConflicts: boolean }>;

}
36 changes: 28 additions & 8 deletions src/vs/platform/userDataSync/common/userDataSyncStoreService.ts
Expand Up @@ -4,10 +4,10 @@
*--------------------------------------------------------------------------------------------*/

import { Disposable, } from 'vs/base/common/lifecycle';
import { IUserData, IUserDataSyncStoreService } from 'vs/platform/userDataSync/common/userDataSync';
import { IUserData, IUserDataSyncStoreService, UserDataSyncStoreErrorCode, UserDataSyncStoreError } from 'vs/platform/userDataSync/common/userDataSync';
import { IProductService } from 'vs/platform/product/common/productService';
import { Emitter, Event } from 'vs/base/common/event';
import { IRequestService, asJson, asText } from 'vs/platform/request/common/request';
import { IRequestService, asText } 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';
Expand Down Expand Up @@ -37,31 +37,51 @@ export class UserDataSyncStoreService extends Disposable implements IUserDataSyn
async logout(): Promise<void> {
}

async read(key: string, oldValue: IUserData | null): Promise<IUserData | null> {
async read(key: string, oldValue: IUserData | null): Promise<IUserData> {
if (!this.enabled) {
return Promise.reject(new Error('No settings sync store url configured.'));
}

const url = joinPath(URI.parse(this.productService.settingsSyncStoreUrl!), key).toString();
const headers: IHeaders = {};
if (oldValue) {
headers['If-None-Match'] = oldValue.ref;
}

const context = await this.requestService.request({ type: 'GET', url, headers }, CancellationToken.None);
return asJson<IUserData>(context);

if (context.res.statusCode === 304) {
// There is no new value. Hence return the old value.
return oldValue!;
}

const ref = context.res.headers['etag'];
if (!ref) {
throw new Error('Server did not return the ref');
}
const content = await asText(context);
return { ref, content };
}

async write(key: string, content: string, ref: string | null): Promise<string> {
async write(key: string, data: string, ref: string | null): Promise<string> {
if (!this.enabled) {
return Promise.reject(new Error('No settings sync store url configured.'));
}

const url = joinPath(URI.parse(this.productService.settingsSyncStoreUrl!), key).toString();
const data = JSON.stringify({ content, ref });
const headers: IHeaders = { 'Content-Type': 'application/json' };
const headers: IHeaders = { 'Content-Type': 'text/plain' };
if (ref) {
headers['If-Match'] = ref;
}

const context = await this.requestService.request({ type: 'POST', url, data, headers }, CancellationToken.None);
const newRef = await asText(context);

if (context.res.statusCode === 412) {
// There is a new value. Throw Rejected Error
throw new UserDataSyncStoreError('New data exists', UserDataSyncStoreErrorCode.Rejected);
}

const newRef = context.res.headers['etag'];
if (!newRef) {
throw new Error('Server did not return the ref');
}
Expand Down

0 comments on commit c0072b6

Please sign in to comment.