1
0
mirror of https://github.com/laurent22/joplin.git synced 2025-12-23 23:33:01 +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.d.ts
packages/lib/services/synchronizer/MigrationHandler.js packages/lib/services/synchronizer/MigrationHandler.js
packages/lib/services/synchronizer/MigrationHandler.js.map 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.d.ts
packages/lib/services/synchronizer/Synchronizer.basics.test.js packages/lib/services/synchronizer/Synchronizer.basics.test.js
packages/lib/services/synchronizer/Synchronizer.basics.test.js.map 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.d.ts
packages/lib/services/synchronizer/MigrationHandler.js packages/lib/services/synchronizer/MigrationHandler.js
packages/lib/services/synchronizer/MigrationHandler.js.map 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.d.ts
packages/lib/services/synchronizer/Synchronizer.basics.test.js packages/lib/services/synchronizer/Synchronizer.basics.test.js
packages/lib/services/synchronizer/Synchronizer.basics.test.js.map 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 TaskQueue from './TaskQueue';
import ItemUploader from './services/synchronizer/ItemUploader'; import ItemUploader from './services/synchronizer/ItemUploader';
import { FileApi } from './file-api'; import { FileApi } from './file-api';
import SyncTargetInfoHandler from './services/synchronizer/SyncTargetInfoHandler';
const { sprintf } = require('sprintf-js'); const { sprintf } = require('sprintf-js');
const { Dirnames } = require('./services/synchronizer/utils/types'); const { Dirnames } = require('./services/synchronizer/utils/types');
@@ -73,6 +74,7 @@ export default class Synchronizer {
private clientId_: string; private clientId_: string;
private lockHandler_: LockHandler; private lockHandler_: LockHandler;
private migrationHandler_: MigrationHandler; private migrationHandler_: MigrationHandler;
private syncTargetInfoHandler_: SyncTargetInfoHandler;
private encryptionService_: EncryptionService = null; private encryptionService_: EncryptionService = null;
private resourceService_: ResourceService = null; private resourceService_: ResourceService = null;
private syncTargetIsLocked_: boolean = false; private syncTargetIsLocked_: boolean = false;
@@ -125,18 +127,24 @@ export default class Synchronizer {
return this.logger_; return this.logger_;
} }
lockHandler() { public lockHandler() {
if (this.lockHandler_) return this.lockHandler_; if (this.lockHandler_) return this.lockHandler_;
this.lockHandler_ = new LockHandler(this.api()); this.lockHandler_ = new LockHandler(this.api());
return this.lockHandler_; return this.lockHandler_;
} }
migrationHandler() { private migrationHandler() {
if (this.migrationHandler_) return this.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_; return this.migrationHandler_;
} }
public syncTargetInfoHandler() {
if (this.syncTargetInfoHandler_) return this.syncTargetInfoHandler_;
this.syncTargetInfoHandler_ = new SyncTargetInfoHandler(this.api());
return this.syncTargetInfoHandler_;
}
maxResourceSize() { maxResourceSize() {
if (this.maxResourceSize_ !== null) return this.maxResourceSize_; if (this.maxResourceSize_ !== null) return this.maxResourceSize_;
return this.appType_ === 'mobile' ? 100 * 1000 * 1000 : Infinity; return this.appType_ === 'mobile' ? 100 * 1000 * 1000 : Infinity;
@@ -415,7 +423,11 @@ export default class Synchronizer {
this.api().setTempDirName(Dirnames.Temp); this.api().setTempDirName(Dirnames.Temp);
try { 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); this.logger().info('Sync target info:', syncTargetInfo);

View File

@@ -16,50 +16,29 @@ const migrations = [
import Setting from '../../models/Setting'; import Setting from '../../models/Setting';
const { sprintf } = require('sprintf-js'); const { sprintf } = require('sprintf-js');
import JoplinError from '../../JoplinError'; import JoplinError from '../../JoplinError';
import SyncTargetInfoHandler from './SyncTargetInfoHandler';
interface SyncTargetInfo { import { FileApi } from '../../file-api';
version: number;
}
export default class MigrationHandler extends BaseService { export default class MigrationHandler extends BaseService {
private api_: any = null; private api_: FileApi = null;
private lockHandler_: LockHandler = null; private lockHandler_: LockHandler = null;
private syncTargetInfoHandler_: SyncTargetInfoHandler = null;
private clientType_: string; private clientType_: string;
private clientId_: 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(); super();
this.api_ = api; this.api_ = api;
this.syncTargetInfoHandler_ = syncTargetInfoHandler;
this.lockHandler_ = lockHandler; this.lockHandler_ = lockHandler;
this.clientType_ = clientType; this.clientType_ = clientType;
this.clientId_ = clientId; this.clientId_ = clientId;
} }
public async fetchSyncTargetInfo(): Promise<SyncTargetInfo> { public async checkCanSync(): Promise<void> {
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> {
const supportedSyncTargetVersion = Setting.value('syncVersion'); const supportedSyncTargetVersion = Setting.value('syncVersion');
const syncTargetInfo = await this.fetchSyncTargetInfo(); const syncTargetInfo = await this.syncTargetInfoHandler_.info();
if (syncTargetInfo.version) { if (syncTargetInfo.version) {
if (syncTargetInfo.version > supportedSyncTargetVersion) { 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'); 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 supportedSyncTargetVersion = Setting.value('syncVersion');
const syncTargetInfo = await this.fetchSyncTargetInfo(); const syncTargetInfo = await this.syncTargetInfoHandler_.info();
if (syncTargetInfo.version > supportedSyncTargetVersion) { 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'); 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_); await migration(this.api_);
if (autoLockError) throw autoLockError; if (autoLockError) throw autoLockError;
await this.api_.put('info.json', this.serializeSyncTargetInfo({ await this.syncTargetInfoHandler_.setInfo({
...syncTargetInfo, ...syncTargetInfo,
version: newVersion, version: newVersion,
})); });
this.logger().info(`MigrationHandler: Done migrating from version ${fromVersion} to version ${newVersion}`); this.logger().info(`MigrationHandler: Done migrating from version ${fromVersion} to version ${newVersion}`);
} catch (error) { } 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(); const { useEffect, useState } = shim.react();
import Setting from '../../../models/Setting'; import Setting from '../../../models/Setting';
import { reg } from '../../../registry'; import { reg } from '../../../registry';
import Synchronizer from '../../../Synchronizer';
export interface SyncTargetUpgradeResult { export interface SyncTargetUpgradeResult {
done: boolean; done: boolean;
@@ -21,11 +22,12 @@ export default function useSyncTargetUpgrade(): SyncTargetUpgradeResult {
let error = null; let error = null;
try { try {
reg.logger().info('useSyncTargetUpgrade: Acquire synchronizer...'); 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...'); reg.logger().info('useSyncTargetUpgrade: Create migration handler...');
const migrationHandler = new MigrationHandler( const migrationHandler = new MigrationHandler(
synchronizer.api(), synchronizer.api(),
synchronizer.syncTargetInfoHandler(),
synchronizer.lockHandler(), synchronizer.lockHandler(),
Setting.value('appType'), Setting.value('appType'),
Setting.value('clientId') 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 // 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'); const { deploySyncTargetSnapshot, testData, checkTestData } = require('../../testing/syncTargetUtils');
import Setting from '../../models/Setting'; import Setting from '../../models/Setting';
import MasterKey from '../../models/MasterKey'; import MasterKey from '../../models/MasterKey';
import SyncTargetInfoHandler from './SyncTargetInfoHandler';
const specTimeout = 60000 * 10; // Nextcloud tests can be slow const specTimeout = 60000 * 10; // Nextcloud tests can be slow
let lockHandler_: LockHandler = null; let lockHandler_: LockHandler = null;
let migrationHandler_: MigrationHandler = 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 { function lockHandler(): LockHandler {
if (lockHandler_) return lockHandler_; if (lockHandler_) return lockHandler_;
@@ -26,7 +34,7 @@ function lockHandler(): LockHandler {
function migrationHandler(clientId: string = 'abcd'): MigrationHandler { function migrationHandler(clientId: string = 'abcd'): MigrationHandler {
if (migrationHandler_) return migrationHandler_; if (migrationHandler_) return migrationHandler_;
migrationHandler_ = new MigrationHandler(fileApi(), lockHandler(), 'desktop', clientId); migrationHandler_ = new MigrationHandler(fileApi(), syncTargetInfoHandler(), lockHandler(), 'desktop', clientId);
return migrationHandler_; return migrationHandler_;
} }
@@ -66,6 +74,7 @@ describe('synchronizer_MigrationHandler', function() {
previousSyncTargetName = setSyncTargetName('filesystem'); previousSyncTargetName = setSyncTargetName('filesystem');
lockHandler_ = null; lockHandler_ = null;
migrationHandler_ = null; migrationHandler_ = null;
syncTargetInfo_ = null;
await setupDatabaseAndSynchronizer(1); await setupDatabaseAndSynchronizer(1);
await setupDatabaseAndSynchronizer(2); await setupDatabaseAndSynchronizer(2);
await switchClient(1); await switchClient(1);
@@ -103,14 +112,14 @@ describe('synchronizer_MigrationHandler', function() {
it(`should migrate (${migrationVersion})`, (async () => { it(`should migrate (${migrationVersion})`, (async () => {
await deploySyncTargetSnapshot('normal', migrationVersion - 1); await deploySyncTargetSnapshot('normal', migrationVersion - 1);
const info = await migrationHandler().fetchSyncTargetInfo(); const info = await syncTargetInfoHandler().info();
expect(info.version).toBe(migrationVersion - 1); expect(info.version).toBe(migrationVersion - 1);
// Now, migrate to the new version // Now, migrate to the new version
await migrationHandler().upgrade(migrationVersion); await migrationHandler().upgrade(migrationVersion);
// Verify that it has been upgraded // Verify that it has been upgraded
const newInfo = await migrationHandler().fetchSyncTargetInfo(); const newInfo = await syncTargetInfoHandler().info();
expect(newInfo.version).toBe(migrationVersion); expect(newInfo.version).toBe(migrationVersion);
await migrationTests[migrationVersion](); await migrationTests[migrationVersion]();
@@ -137,7 +146,7 @@ describe('synchronizer_MigrationHandler', function() {
await migrationHandler().upgrade(migrationVersion); await migrationHandler().upgrade(migrationVersion);
// Verify that it has been upgraded // Verify that it has been upgraded
const newInfo = await migrationHandler().fetchSyncTargetInfo(); const newInfo = await syncTargetInfoHandler().info();
expect(newInfo.version).toBe(migrationVersion); expect(newInfo.version).toBe(migrationVersion);
await migrationTests[migrationVersion](); await migrationTests[migrationVersion]();

View File

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