@sussudio/platform
Version:
Internal APIs for VS Code's service injection the base services.
355 lines (354 loc) • 13.1 kB
JavaScript
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
var __decorate =
(this && this.__decorate) ||
function (decorators, target, key, desc) {
var c = arguments.length,
r = c < 3 ? target : desc === null ? (desc = Object.getOwnPropertyDescriptor(target, key)) : desc,
d;
if (typeof Reflect === 'object' && typeof Reflect.decorate === 'function')
r = Reflect.decorate(decorators, target, key, desc);
else
for (var i = decorators.length - 1; i >= 0; i--)
if ((d = decorators[i])) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __param =
(this && this.__param) ||
function (paramIndex, decorator) {
return function (target, key) {
decorator(target, key, paramIndex);
};
};
import { VSBuffer } from '@sussudio/base/common/buffer.mjs';
import { IConfigurationService } from '../../configuration/common/configuration.mjs';
import { IEnvironmentService } from '../../environment/common/environment.mjs';
import { IFileService } from '../../files/common/files.mjs';
import { IStorageService } from '../../storage/common/storage.mjs';
import { ITelemetryService } from '../../telemetry/common/telemetry.mjs';
import { IUriIdentityService } from '../../uriIdentity/common/uriIdentity.mjs';
import { IUserDataProfilesService } from '../../userDataProfile/common/userDataProfile.mjs';
import { AbstractFileSynchroniser, AbstractInitializer } from './abstractSynchronizer.mjs';
import {
IUserDataSyncBackupStoreService,
IUserDataSyncLogService,
IUserDataSyncEnablementService,
IUserDataSyncStoreService,
USER_DATA_SYNC_SCHEME,
} from './userDataSync.mjs';
export function getTasksContentFromSyncContent(syncContent, logService) {
try {
const parsed = JSON.parse(syncContent);
return parsed.tasks ?? null;
} catch (e) {
logService.error(e);
return null;
}
}
let TasksSynchroniser = class TasksSynchroniser extends AbstractFileSynchroniser {
version = 1;
previewResource = this.extUri.joinPath(this.syncPreviewFolder, 'tasks.json');
baseResource = this.previewResource.with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'base' });
localResource = this.previewResource.with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'local' });
remoteResource = this.previewResource.with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'remote' });
acceptedResource = this.previewResource.with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'accepted' });
constructor(
profile,
collection,
userDataSyncStoreService,
userDataSyncBackupStoreService,
logService,
configurationService,
userDataSyncEnablementService,
fileService,
environmentService,
storageService,
telemetryService,
uriIdentityService,
) {
super(
profile.tasksResource,
{ syncResource: 'tasks' /* SyncResource.Tasks */, profile },
collection,
fileService,
environmentService,
storageService,
userDataSyncStoreService,
userDataSyncBackupStoreService,
userDataSyncEnablementService,
telemetryService,
logService,
configurationService,
uriIdentityService,
);
}
async generateSyncPreview(
remoteUserData,
lastSyncUserData,
isRemoteDataFromCurrentMachine,
userDataSyncConfiguration,
) {
const remoteContent = remoteUserData.syncData
? getTasksContentFromSyncContent(remoteUserData.syncData.content, this.logService)
: null;
// Use remote data as last sync data if last sync data does not exist and remote data is from same machine
lastSyncUserData = lastSyncUserData === null && isRemoteDataFromCurrentMachine ? remoteUserData : lastSyncUserData;
const lastSyncContent = lastSyncUserData?.syncData
? getTasksContentFromSyncContent(lastSyncUserData.syncData.content, this.logService)
: null;
// Get file content last to get the latest
const fileContent = await this.getLocalFileContent();
let content = null;
let hasLocalChanged = false;
let hasRemoteChanged = false;
let hasConflicts = false;
if (remoteUserData.syncData) {
const localContent = fileContent ? fileContent.value.toString() : null;
if (
!lastSyncContent || // First time sync
lastSyncContent !== localContent || // Local has forwarded
lastSyncContent !== remoteContent // Remote has forwarded
) {
this.logService.trace(`${this.syncResourceLogLabel}: Merging remote tasks with local tasks...`);
const result = merge(localContent, remoteContent, lastSyncContent);
content = result.content;
hasConflicts = result.hasConflicts;
hasLocalChanged = result.hasLocalChanged;
hasRemoteChanged = result.hasRemoteChanged;
}
}
// First time syncing to remote
else if (fileContent) {
this.logService.trace(
`${this.syncResourceLogLabel}: Remote tasks does not exist. Synchronizing tasks for the first time.`,
);
content = fileContent.value.toString();
hasRemoteChanged = true;
}
const previewResult = {
content: hasConflicts ? lastSyncContent : content,
localChange: hasLocalChanged
? fileContent
? 2 /* Change.Modified */
: 1 /* Change.Added */
: 0 /* Change.None */,
remoteChange: hasRemoteChanged ? 2 /* Change.Modified */ : 0 /* Change.None */,
hasConflicts,
};
const localContent = fileContent ? fileContent.value.toString() : null;
return [
{
fileContent,
baseResource: this.baseResource,
baseContent: lastSyncContent,
localResource: this.localResource,
localContent,
localChange: previewResult.localChange,
remoteResource: this.remoteResource,
remoteContent,
remoteChange: previewResult.remoteChange,
previewResource: this.previewResource,
previewResult,
acceptedResource: this.acceptedResource,
},
];
}
async hasRemoteChanged(lastSyncUserData) {
const lastSyncContent = lastSyncUserData?.syncData
? getTasksContentFromSyncContent(lastSyncUserData.syncData.content, this.logService)
: null;
if (lastSyncContent === null) {
return true;
}
const fileContent = await this.getLocalFileContent();
const localContent = fileContent ? fileContent.value.toString() : null;
const result = merge(localContent, lastSyncContent, lastSyncContent);
return result.hasLocalChanged || result.hasRemoteChanged;
}
async getMergeResult(resourcePreview, token) {
return resourcePreview.previewResult;
}
async getAcceptResult(resourcePreview, resource, content, token) {
/* Accept local resource */
if (this.extUri.isEqual(resource, this.localResource)) {
return {
content: resourcePreview.fileContent ? resourcePreview.fileContent.value.toString() : null,
localChange: 0 /* Change.None */,
remoteChange: 2 /* Change.Modified */,
};
}
/* Accept remote resource */
if (this.extUri.isEqual(resource, this.remoteResource)) {
return {
content: resourcePreview.remoteContent,
localChange: 2 /* Change.Modified */,
remoteChange: 0 /* Change.None */,
};
}
/* Accept preview resource */
if (this.extUri.isEqual(resource, this.previewResource)) {
if (content === undefined) {
return {
content: resourcePreview.previewResult.content,
localChange: resourcePreview.previewResult.localChange,
remoteChange: resourcePreview.previewResult.remoteChange,
};
} else {
return {
content,
localChange: 2 /* Change.Modified */,
remoteChange: 2 /* Change.Modified */,
};
}
}
throw new Error(`Invalid Resource: ${resource.toString()}`);
}
async applyResult(remoteUserData, lastSyncUserData, resourcePreviews, force) {
const { fileContent } = resourcePreviews[0][0];
const { content, localChange, remoteChange } = resourcePreviews[0][1];
if (localChange === 0 /* Change.None */ && remoteChange === 0 /* Change.None */) {
this.logService.info(`${this.syncResourceLogLabel}: No changes found during synchronizing tasks.`);
}
if (localChange !== 0 /* Change.None */) {
this.logService.trace(`${this.syncResourceLogLabel}: Updating local tasks...`);
if (fileContent) {
await this.backupLocal(JSON.stringify(this.toTasksSyncContent(fileContent.value.toString())));
}
if (content) {
await this.updateLocalFileContent(content, fileContent, force);
} else {
await this.deleteLocalFile();
}
this.logService.info(`${this.syncResourceLogLabel}: Updated local tasks`);
}
if (remoteChange !== 0 /* Change.None */) {
this.logService.trace(`${this.syncResourceLogLabel}: Updating remote tasks...`);
const remoteContents = JSON.stringify(this.toTasksSyncContent(content));
remoteUserData = await this.updateRemoteUserData(remoteContents, force ? null : remoteUserData.ref);
this.logService.info(`${this.syncResourceLogLabel}: Updated remote tasks`);
}
// Delete the preview
try {
await this.fileService.del(this.previewResource);
} catch (e) {
/* ignore */
}
if (lastSyncUserData?.ref !== remoteUserData.ref) {
this.logService.trace(`${this.syncResourceLogLabel}: Updating last synchronized tasks...`);
await this.updateLastSyncUserData(remoteUserData);
this.logService.info(`${this.syncResourceLogLabel}: Updated last synchronized tasks`);
}
}
async hasLocalData() {
return this.fileService.exists(this.file);
}
async resolveContent(uri) {
if (
this.extUri.isEqual(this.remoteResource, uri) ||
this.extUri.isEqual(this.baseResource, uri) ||
this.extUri.isEqual(this.localResource, uri) ||
this.extUri.isEqual(this.acceptedResource, uri)
) {
return this.resolvePreviewContent(uri);
}
return null;
}
toTasksSyncContent(tasks) {
return tasks ? { tasks } : {};
}
};
TasksSynchroniser = __decorate(
[
__param(2, IUserDataSyncStoreService),
__param(3, IUserDataSyncBackupStoreService),
__param(4, IUserDataSyncLogService),
__param(5, IConfigurationService),
__param(6, IUserDataSyncEnablementService),
__param(7, IFileService),
__param(8, IEnvironmentService),
__param(9, IStorageService),
__param(10, ITelemetryService),
__param(11, IUriIdentityService),
],
TasksSynchroniser,
);
export { TasksSynchroniser };
let TasksInitializer = class TasksInitializer extends AbstractInitializer {
tasksResource = this.userDataProfilesService.defaultProfile.tasksResource;
constructor(
fileService,
userDataProfilesService,
environmentService,
logService,
storageService,
uriIdentityService,
) {
super(
'tasks' /* SyncResource.Tasks */,
userDataProfilesService,
environmentService,
logService,
fileService,
storageService,
uriIdentityService,
);
}
async doInitialize(remoteUserData) {
const tasksContent = remoteUserData.syncData
? getTasksContentFromSyncContent(remoteUserData.syncData.content, this.logService)
: null;
if (!tasksContent) {
this.logService.info('Skipping initializing tasks because remote tasks does not exist.');
return;
}
const isEmpty = await this.isEmpty();
if (!isEmpty) {
this.logService.info('Skipping initializing tasks because local tasks exist.');
return;
}
await this.fileService.writeFile(this.tasksResource, VSBuffer.fromString(tasksContent));
await this.updateLastSyncUserData(remoteUserData);
}
async isEmpty() {
return this.fileService.exists(this.tasksResource);
}
};
TasksInitializer = __decorate(
[
__param(0, IFileService),
__param(1, IUserDataProfilesService),
__param(2, IEnvironmentService),
__param(3, IUserDataSyncLogService),
__param(4, IStorageService),
__param(5, IUriIdentityService),
],
TasksInitializer,
);
export { TasksInitializer };
function merge(originalLocalContent, originalRemoteContent, baseContent) {
/* no changes */
if (originalLocalContent === null && originalRemoteContent === null && baseContent === null) {
return { content: null, hasLocalChanged: false, hasRemoteChanged: false, hasConflicts: false };
}
/* no changes */
if (originalLocalContent === originalRemoteContent) {
return { content: null, hasLocalChanged: false, hasRemoteChanged: false, hasConflicts: false };
}
const localForwarded = baseContent !== originalLocalContent;
const remoteForwarded = baseContent !== originalRemoteContent;
/* no changes */
if (!localForwarded && !remoteForwarded) {
return { content: null, hasLocalChanged: false, hasRemoteChanged: false, hasConflicts: false };
}
/* local has changed and remote has not */
if (localForwarded && !remoteForwarded) {
return { content: originalLocalContent, hasRemoteChanged: true, hasLocalChanged: false, hasConflicts: false };
}
/* remote has changed and local has not */
if (remoteForwarded && !localForwarded) {
return { content: originalRemoteContent, hasLocalChanged: true, hasRemoteChanged: false, hasConflicts: false };
}
return { content: originalLocalContent, hasLocalChanged: true, hasRemoteChanged: true, hasConflicts: true };
}