1
0
mirror of https://github.com/laurent22/joplin.git synced 2024-11-27 08:21:03 +02:00

Handle sync when resource has been edited

This commit is contained in:
Laurent Cozic 2020-05-31 00:31:29 +01:00
parent 0e5a0fdbe5
commit 1852d9291d
7 changed files with 63 additions and 8 deletions

View File

@ -3,7 +3,7 @@
require('app-module-path').addPath(__dirname);
const { time } = require('lib/time-utils.js');
const { setupDatabase, allSyncTargetItemsEncrypted, kvStore, revisionService, setupDatabaseAndSynchronizer, db, synchronizer, fileApi, sleep, clearDatabase, switchClient, syncTargetId, encryptionService, loadEncryptionMasterKey, fileContentEqual, decryptionWorker, checkThrowAsync, asyncTest } = require('test-utils.js');
const { setupDatabase, allSyncTargetItemsEncrypted, resourceFetcher, kvStore, revisionService, setupDatabaseAndSynchronizer, db, synchronizer, fileApi, sleep, clearDatabase, switchClient, syncTargetId, encryptionService, loadEncryptionMasterKey, fileContentEqual, decryptionWorker, checkThrowAsync, asyncTest } = require('test-utils.js');
const { shim } = require('lib/shim.js');
const fs = require('fs-extra');
const Folder = require('lib/models/Folder.js');
@ -991,6 +991,42 @@ describe('synchronizer', function() {
expect(fileContentEqual(resourcePath1, resourcePath1_2)).toBe(true);
}));
it('should sync resource blob changes', asyncTest(async () => {
const tempFile = `${__dirname}/tmp/test.txt`;
await shim.fsDriver().writeFile(tempFile, '1234', 'utf8');
const folder1 = await Folder.save({ title: 'folder1' });
const note1 = await Note.save({ title: 'ma note', parent_id: folder1.id });
await shim.attachFileToNote(note1, tempFile);
await synchronizer().start();
await switchClient(2);
await synchronizer().start();
await resourceFetcher().start();
await resourceFetcher().waitForAllFinished();
let resource1_2 = (await Resource.all())[0];
const modFile = `${__dirname}/tmp/test_mod.txt`;
await shim.fsDriver().writeFile(modFile, '1234 MOD', 'utf8');
await shim.updateResourceBlob(resource1_2.id, modFile);
const originalSize = resource1_2.size;
resource1_2 = (await Resource.all())[0];
const newSize = resource1_2.size;
expect(originalSize).toBe(4);
expect(newSize).toBe(8);
await synchronizer().start();
await switchClient(1);
await synchronizer().start();
await resourceFetcher().start();
await resourceFetcher().waitForAllFinished();
const resource1_1 = (await Resource.all())[0];
expect(resource1_1.size).toBe(newSize);
const resource1_1Path = Resource.fullPath(resource1_1);
const newContent = await shim.fsDriver().readFile(resource1_1Path, 'utf8');
expect(newContent).toBe('1234 MOD');
}));
it('should upload decrypted items to sync target after encryption disabled', asyncTest(async () => {
Setting.setValue('encryption.enabled', true);
const masterKey = await loadEncryptionMasterKey();

View File

@ -37,6 +37,7 @@ const EncryptionService = require('lib/services/EncryptionService.js');
const DecryptionWorker = require('lib/services/DecryptionWorker.js');
const ResourceService = require('lib/services/ResourceService.js');
const RevisionService = require('lib/services/RevisionService.js');
const ResourceFetcher = require('lib/services/ResourceFetcher.js');
const KvStore = require('lib/services/KvStore.js');
const WebDavApi = require('lib/WebDavApi');
const DropboxApi = require('lib/DropboxApi');
@ -47,6 +48,7 @@ const encryptionServices_ = [];
const revisionServices_ = [];
const decryptionWorkers_ = [];
const resourceServices_ = [];
const resourceFetchers_ = [];
const kvStores_ = [];
let fileApi_ = null;
let currentClient_ = 1;
@ -250,6 +252,7 @@ async function setupDatabaseAndSynchronizer(id = null) {
decryptionWorkers_[id] = new DecryptionWorker();
decryptionWorkers_[id].setEncryptionService(encryptionServices_[id]);
resourceServices_[id] = new ResourceService();
resourceFetchers_[id] = new ResourceFetcher(() => { return synchronizers_[id].api(); });
kvStores_[id] = new KvStore();
await fileApi().clearRoot();
@ -294,6 +297,11 @@ function resourceService(id = null) {
return resourceServices_[id];
}
function resourceFetcher(id = null) {
if (id === null) id = currentClient_;
return resourceFetchers_[id];
}
async function loadEncryptionMasterKey(id = null, useExisting = false) {
const service = encryptionService(id);
@ -546,4 +554,4 @@ class TestApp extends BaseApplication {
}
}
module.exports = { kvStore, resourceService, allSyncTargetItemsEncrypted, setupDatabase, revisionService, setupDatabaseAndSynchronizer, db, synchronizer, fileApi, sleep, clearDatabase, switchClient, syncTargetId, objectsEqual, checkThrowAsync, encryptionService, loadEncryptionMasterKey, fileContentEqual, decryptionWorker, asyncTest, currentClientId, id, ids, sortedIds, at, createNTestNotes, createNTestFolders, createNTestTags, TestApp };
module.exports = { kvStore, resourceService, resourceFetcher, allSyncTargetItemsEncrypted, setupDatabase, revisionService, setupDatabaseAndSynchronizer, db, synchronizer, fileApi, sleep, clearDatabase, switchClient, syncTargetId, objectsEqual, checkThrowAsync, encryptionService, loadEncryptionMasterKey, fileContentEqual, decryptionWorker, asyncTest, currentClientId, id, ids, sortedIds, at, createNTestNotes, createNTestFolders, createNTestTags, TestApp };

View File

@ -521,7 +521,7 @@ class BaseApplication {
DecryptionWorker.instance().scheduleStart();
}
if (this.hasGui() && action.type === 'SYNC_CREATED_RESOURCE') {
if (this.hasGui() && action.type === 'SYNC_CREATED_OR_UPDATED_RESOURCE') {
ResourceFetcher.instance().autoAddResources();
}

View File

@ -110,8 +110,16 @@ class Resource extends BaseItem {
}
static async isReady(resource) {
const r = await this.readyStatus(resource);
return r === 'ok';
}
static async readyStatus(resource) {
const ls = await this.localState(resource);
return resource && ls.fetch_status === Resource.FETCH_STATUS_DONE && !resource.encryption_blob_encrypted;
if (!resource) return 'notFound';
if (ls.fetch_status !== Resource.FETCH_STATUS_DONE) return 'notDownloaded';
if (resource.encryption_blob_encrypted) return 'encrypted';
return 'ok';
}
// For resources, we need to decrypt the item (metadata) and the resource binary blob.

View File

@ -207,6 +207,9 @@ function shimInit() {
shim.updateResourceBlob = async function(resourceId, newBlobFilePath) {
const resource = await Resource.load(resourceId);
const readyStatus = await Resource.readyStatus(resourceId);
if (readyStatus !== 'ok') throw new Error(`Cannot set resource blob because resource is not ready. Status: ${readyStatus}`);
const fileStat = await shim.fsDriver().stat(newBlobFilePath);
await shim.fsDriver().copy(newBlobFilePath, Resource.fullPath(resource));

View File

@ -718,9 +718,9 @@ class Synchronizer {
if (action == 'createLocal') options.isNew = true;
if (action == 'updateLocal') options.oldItem = local;
const creatingNewResource = content.type_ == BaseModel.TYPE_RESOURCE && action == 'createLocal';
const creatingOrUpdatingResource = content.type_ == BaseModel.TYPE_RESOURCE && (action == 'createLocal' || action == 'updateLocal');
if (creatingNewResource) {
if (creatingOrUpdatingResource) {
if (content.size >= this.maxResourceSize()) {
await handleCannotSyncItem(ItemClass, syncTargetId, content, `File "${content.title}" is larger than allowed ${this.maxResourceSize()} bytes. Beyond this limit, the mobile app would crash.`, BaseItem.SYNC_ITEM_LOCATION_REMOTE);
continue;
@ -731,7 +731,7 @@ class Synchronizer {
await ItemClass.save(content, options);
if (creatingNewResource) this.dispatch({ type: 'SYNC_CREATED_RESOURCE', id: content.id });
if (creatingOrUpdatingResource) this.dispatch({ type: 'SYNC_CREATED_OR_UPDATED_RESOURCE', id: content.id });
if (!hasAutoEnabledEncryption && content.type_ === BaseModel.TYPE_MASTER_KEY && !masterKeysBefore) {
hasAutoEnabledEncryption = true;

View File

@ -151,7 +151,7 @@ const generalMiddleware = store => next => async (action) => {
DecryptionWorker.instance().scheduleStart();
}
if (action.type === 'SYNC_CREATED_RESOURCE') {
if (action.type === 'SYNC_CREATED_OR_UPDATED_RESOURCE') {
ResourceFetcher.instance().autoAddResources();
}