1
0
mirror of https://github.com/laurent22/joplin.git synced 2025-12-20 23:30:05 +02:00

Move sync target info handling to separate class

This commit is contained in:
Laurent Cozic
2021-06-23 19:38:17 +01:00
parent 6cbdad0690
commit c8ad3ffaf0
8 changed files with 105 additions and 47 deletions

View File

@@ -1424,6 +1424,9 @@ packages/lib/services/synchronizer/LockHandler.js.map
packages/lib/services/synchronizer/MigrationHandler.d.ts
packages/lib/services/synchronizer/MigrationHandler.js
packages/lib/services/synchronizer/MigrationHandler.js.map
packages/lib/services/synchronizer/SyncTargetInfoHandler.d.ts
packages/lib/services/synchronizer/SyncTargetInfoHandler.js
packages/lib/services/synchronizer/SyncTargetInfoHandler.js.map
packages/lib/services/synchronizer/Synchronizer.basics.test.d.ts
packages/lib/services/synchronizer/Synchronizer.basics.test.js
packages/lib/services/synchronizer/Synchronizer.basics.test.js.map

3
.gitignore vendored
View File

@@ -1410,6 +1410,9 @@ packages/lib/services/synchronizer/LockHandler.js.map
packages/lib/services/synchronizer/MigrationHandler.d.ts
packages/lib/services/synchronizer/MigrationHandler.js
packages/lib/services/synchronizer/MigrationHandler.js.map
packages/lib/services/synchronizer/SyncTargetInfoHandler.d.ts
packages/lib/services/synchronizer/SyncTargetInfoHandler.js
packages/lib/services/synchronizer/SyncTargetInfoHandler.js.map
packages/lib/services/synchronizer/Synchronizer.basics.test.d.ts
packages/lib/services/synchronizer/Synchronizer.basics.test.js
packages/lib/services/synchronizer/Synchronizer.basics.test.js.map

View File

@@ -21,6 +21,7 @@ import ShareService from './services/share/ShareService';
import TaskQueue from './TaskQueue';
import ItemUploader from './services/synchronizer/ItemUploader';
import { FileApi } from './file-api';
import SyncTargetInfoHandler from './services/synchronizer/SyncTargetInfoHandler';
const { sprintf } = require('sprintf-js');
const { Dirnames } = require('./services/synchronizer/utils/types');
@@ -73,6 +74,7 @@ export default class Synchronizer {
private clientId_: string;
private lockHandler_: LockHandler;
private migrationHandler_: MigrationHandler;
private syncTargetInfoHandler_: SyncTargetInfoHandler;
private encryptionService_: EncryptionService = null;
private resourceService_: ResourceService = null;
private syncTargetIsLocked_: boolean = false;
@@ -125,18 +127,24 @@ export default class Synchronizer {
return this.logger_;
}
lockHandler() {
public lockHandler() {
if (this.lockHandler_) return this.lockHandler_;
this.lockHandler_ = new LockHandler(this.api());
return this.lockHandler_;
}
migrationHandler() {
private migrationHandler() {
if (this.migrationHandler_) return this.migrationHandler_;
this.migrationHandler_ = new MigrationHandler(this.api(), this.lockHandler(), this.appType_, this.clientId_);
this.migrationHandler_ = new MigrationHandler(this.api(), this.syncTargetInfoHandler(), this.lockHandler(), this.appType_, this.clientId_);
return this.migrationHandler_;
}
public syncTargetInfoHandler() {
if (this.syncTargetInfoHandler_) return this.syncTargetInfoHandler_;
this.syncTargetInfoHandler_ = new SyncTargetInfoHandler(this.api());
return this.syncTargetInfoHandler_;
}
maxResourceSize() {
if (this.maxResourceSize_ !== null) return this.maxResourceSize_;
return this.appType_ === 'mobile' ? 100 * 1000 * 1000 : Infinity;
@@ -415,7 +423,11 @@ export default class Synchronizer {
this.api().setTempDirName(Dirnames.Temp);
try {
const syncTargetInfo = await this.migrationHandler().checkCanSync();
const syncTargetInfoService = new SyncTargetInfoHandler(this.api());
await this.migrationHandler().checkCanSync();
const syncTargetInfo = await syncTargetInfoService.info();
this.logger().info('Sync target info:', syncTargetInfo);

View File

@@ -16,50 +16,29 @@ const migrations = [
import Setting from '../../models/Setting';
const { sprintf } = require('sprintf-js');
import JoplinError from '../../JoplinError';
interface SyncTargetInfo {
version: number;
}
import SyncTargetInfoHandler from './SyncTargetInfoHandler';
import { FileApi } from '../../file-api';
export default class MigrationHandler extends BaseService {
private api_: any = null;
private api_: FileApi = null;
private lockHandler_: LockHandler = null;
private syncTargetInfoHandler_: SyncTargetInfoHandler = null;
private clientType_: string;
private clientId_: string;
constructor(api: any, lockHandler: LockHandler, clientType: string, clientId: string) {
constructor(api: FileApi, syncTargetInfoHandler: SyncTargetInfoHandler, lockHandler: LockHandler, clientType: string, clientId: string) {
super();
this.api_ = api;
this.syncTargetInfoHandler_ = syncTargetInfoHandler;
this.lockHandler_ = lockHandler;
this.clientType_ = clientType;
this.clientId_ = clientId;
}
public async fetchSyncTargetInfo(): Promise<SyncTargetInfo> {
const syncTargetInfoText = await this.api_.get('info.json');
// Returns version 0 if the sync target is empty
let output: SyncTargetInfo = { version: 0 };
if (syncTargetInfoText) {
output = JSON.parse(syncTargetInfoText);
if (!output.version) throw new Error('Missing "version" field in info.json');
} else {
const oldVersion = await this.api_.get('.sync/version.txt');
if (oldVersion) output = { version: 1 };
}
return output;
}
private serializeSyncTargetInfo(info: SyncTargetInfo) {
return JSON.stringify(info);
}
async checkCanSync(): Promise<SyncTargetInfo> {
public async checkCanSync(): Promise<void> {
const supportedSyncTargetVersion = Setting.value('syncVersion');
const syncTargetInfo = await this.fetchSyncTargetInfo();
const syncTargetInfo = await this.syncTargetInfoHandler_.info();
if (syncTargetInfo.version) {
if (syncTargetInfo.version > supportedSyncTargetVersion) {
@@ -68,13 +47,11 @@ export default class MigrationHandler extends BaseService {
throw new JoplinError(sprintf('Sync version of the target (%d) is lower than the version supported by the client (%d). Please upgrade the sync target.', syncTargetInfo.version, supportedSyncTargetVersion), 'outdatedSyncTarget');
}
}
return syncTargetInfo;
}
async upgrade(targetVersion: number = 0) {
public async upgrade(targetVersion: number = 0) {
const supportedSyncTargetVersion = Setting.value('syncVersion');
const syncTargetInfo = await this.fetchSyncTargetInfo();
const syncTargetInfo = await this.syncTargetInfoHandler_.info();
if (syncTargetInfo.version > supportedSyncTargetVersion) {
throw new JoplinError(sprintf('Sync version of the target (%d) is greater than the version supported by the client (%d). Please upgrade your client.', syncTargetInfo.version, supportedSyncTargetVersion), 'outdatedClient');
@@ -122,10 +99,10 @@ export default class MigrationHandler extends BaseService {
await migration(this.api_);
if (autoLockError) throw autoLockError;
await this.api_.put('info.json', this.serializeSyncTargetInfo({
await this.syncTargetInfoHandler_.setInfo({
...syncTargetInfo,
version: newVersion,
}));
});
this.logger().info(`MigrationHandler: Done migrating from version ${fromVersion} to version ${newVersion}`);
} catch (error) {

View File

@@ -0,0 +1,52 @@
import { FileApi } from '../../file-api';
interface SyncTargetInfo {
version: number;
}
export default class SyncTargetInfoHandler {
private api_: FileApi = null;
private info_: SyncTargetInfo = null;
public constructor(api: FileApi) {
this.api_ = api;
}
public async info(): Promise<SyncTargetInfo> {
if (this.info_) return this.info_;
this.info_ = await this.fetchSyncTargetInfo();
return this.info_;
}
private serializeSyncTargetInfo(info: SyncTargetInfo): string {
return JSON.stringify(info);
}
private unserializeSyncTargetInfo(info: string): SyncTargetInfo {
return JSON.parse(info);
}
public async setInfo(info: SyncTargetInfo): Promise<void> {
this.info_ = info;
await this.api_.put('info.json', this.serializeSyncTargetInfo(info));
}
private async fetchSyncTargetInfo(): Promise<SyncTargetInfo> {
const syncTargetInfoText = await this.api_.get('info.json');
// Returns version 0 if the sync target is empty
let output: SyncTargetInfo = { version: 0 };
if (syncTargetInfoText) {
output = this.unserializeSyncTargetInfo(syncTargetInfoText);
if (!output.version) throw new Error('Missing "version" field in info.json');
} else {
const oldVersion = await this.api_.get('.sync/version.txt');
if (oldVersion) output = { version: 1 };
}
return output;
}
}

View File

@@ -3,6 +3,7 @@ import MigrationHandler from '../MigrationHandler';
const { useEffect, useState } = shim.react();
import Setting from '../../../models/Setting';
import { reg } from '../../../registry';
import Synchronizer from '../../../Synchronizer';
export interface SyncTargetUpgradeResult {
done: boolean;
@@ -21,11 +22,12 @@ export default function useSyncTargetUpgrade(): SyncTargetUpgradeResult {
let error = null;
try {
reg.logger().info('useSyncTargetUpgrade: Acquire synchronizer...');
const synchronizer = await reg.syncTarget().synchronizer();
const synchronizer: Synchronizer = await reg.syncTarget().synchronizer();
reg.logger().info('useSyncTargetUpgrade: Create migration handler...');
const migrationHandler = new MigrationHandler(
synchronizer.api(),
synchronizer.syncTargetInfoHandler(),
synchronizer.lockHandler(),
Setting.value('appType'),
Setting.value('clientId')

View File

@@ -8,15 +8,23 @@ import { Dirnames } from '../../services/synchronizer/utils/types';
// gulp buildTests -L && node tests-build/support/createSyncTargetSnapshot.js normal && node tests-build/support/createSyncTargetSnapshot.js e2ee
const { setSyncTargetName, fileApi, synchronizer, decryptionWorker, encryptionService, setupDatabaseAndSynchronizer, switchClient, expectThrow, expectNotThrow } = require('../../testing/test-utils.js');
import { setSyncTargetName, fileApi, synchronizer, decryptionWorker, encryptionService, setupDatabaseAndSynchronizer, switchClient, expectThrow, expectNotThrow } from '../../testing/test-utils';
const { deploySyncTargetSnapshot, testData, checkTestData } = require('../../testing/syncTargetUtils');
import Setting from '../../models/Setting';
import MasterKey from '../../models/MasterKey';
import SyncTargetInfoHandler from './SyncTargetInfoHandler';
const specTimeout = 60000 * 10; // Nextcloud tests can be slow
let lockHandler_: LockHandler = null;
let migrationHandler_: MigrationHandler = null;
let syncTargetInfo_: SyncTargetInfoHandler = null;
function syncTargetInfoHandler(): SyncTargetInfoHandler {
if (syncTargetInfo_) return syncTargetInfo_;
syncTargetInfo_ = new SyncTargetInfoHandler(fileApi());
return syncTargetInfo_;
}
function lockHandler(): LockHandler {
if (lockHandler_) return lockHandler_;
@@ -26,7 +34,7 @@ function lockHandler(): LockHandler {
function migrationHandler(clientId: string = 'abcd'): MigrationHandler {
if (migrationHandler_) return migrationHandler_;
migrationHandler_ = new MigrationHandler(fileApi(), lockHandler(), 'desktop', clientId);
migrationHandler_ = new MigrationHandler(fileApi(), syncTargetInfoHandler(), lockHandler(), 'desktop', clientId);
return migrationHandler_;
}
@@ -66,6 +74,7 @@ describe('synchronizer_MigrationHandler', function() {
previousSyncTargetName = setSyncTargetName('filesystem');
lockHandler_ = null;
migrationHandler_ = null;
syncTargetInfo_ = null;
await setupDatabaseAndSynchronizer(1);
await setupDatabaseAndSynchronizer(2);
await switchClient(1);
@@ -103,14 +112,14 @@ describe('synchronizer_MigrationHandler', function() {
it(`should migrate (${migrationVersion})`, (async () => {
await deploySyncTargetSnapshot('normal', migrationVersion - 1);
const info = await migrationHandler().fetchSyncTargetInfo();
const info = await syncTargetInfoHandler().info();
expect(info.version).toBe(migrationVersion - 1);
// Now, migrate to the new version
await migrationHandler().upgrade(migrationVersion);
// Verify that it has been upgraded
const newInfo = await migrationHandler().fetchSyncTargetInfo();
const newInfo = await syncTargetInfoHandler().info();
expect(newInfo.version).toBe(migrationVersion);
await migrationTests[migrationVersion]();
@@ -137,7 +146,7 @@ describe('synchronizer_MigrationHandler', function() {
await migrationHandler().upgrade(migrationVersion);
// Verify that it has been upgraded
const newInfo = await migrationHandler().fetchSyncTargetInfo();
const newInfo = await syncTargetInfoHandler().info();
expect(newInfo.version).toBe(migrationVersion);
await migrationTests[migrationVersion]();

View File

@@ -28,7 +28,6 @@ import NoteTag from '../models/NoteTag';
import Revision from '../models/Revision';
import MasterKey from '../models/MasterKey';
import BaseItem from '../models/BaseItem';
const { FileApi } = require('../file-api.js');
const FileApiDriverMemory = require('../file-api-driver-memory').default;
const { FileApiDriverLocal } = require('../file-api-driver-local.js');
const { FileApiDriverWebDav } = require('../file-api-driver-webdav.js');
@@ -52,6 +51,7 @@ import JoplinServerApi from '../JoplinServerApi';
import { FolderEntity } from '../services/database/types';
import { credentialFile, readCredentialFile } from '../utils/credentialFiles';
import SyncTargetJoplinCloud from '../SyncTargetJoplinCloud';
import { FileApi } from '../file-api';
const { loadKeychainServiceAndSettings } = require('../services/SettingUtils');
const md5 = require('md5');
const S3 = require('aws-sdk/clients/s3');
@@ -602,7 +602,7 @@ async function initFileApi() {
fileApis_[syncTargetId_] = fileApi;
}
function fileApi() {
function fileApi(): FileApi {
return fileApis_[syncTargetId_];
}