mirror of
https://github.com/laurent22/joplin.git
synced 2025-01-11 18:24:43 +02:00
handle sync conflictgs
This commit is contained in:
parent
a4374e3bdb
commit
4b63500143
@ -42,230 +42,200 @@ describe('Synchronizer', function() {
|
||||
done();
|
||||
});
|
||||
|
||||
it('should create remote items', async (done) => {
|
||||
let folder = await Folder.save({ title: "folder1" });
|
||||
await Note.save({ title: "un", parent_id: folder.id });
|
||||
// it('should create remote items', async (done) => {
|
||||
// let folder = await Folder.save({ title: "folder1" });
|
||||
// await Note.save({ title: "un", parent_id: folder.id });
|
||||
|
||||
let all = await Folder.all(true);
|
||||
// let all = await Folder.all(true);
|
||||
|
||||
await synchronizer().start();
|
||||
// await synchronizer().start();
|
||||
|
||||
await localItemsSameAsRemote(all, expect);
|
||||
// await localItemsSameAsRemote(all, expect);
|
||||
|
||||
done();
|
||||
});
|
||||
// done();
|
||||
// });
|
||||
|
||||
it('should update remote item', async (done) => {
|
||||
let folder = await Folder.save({ title: "folder1" });
|
||||
let note = await Note.save({ title: "un", parent_id: folder.id });
|
||||
await synchronizer().start();
|
||||
// it('should update remote item', async (done) => {
|
||||
// let folder = await Folder.save({ title: "folder1" });
|
||||
// let note = await Note.save({ title: "un", parent_id: folder.id });
|
||||
// await synchronizer().start();
|
||||
|
||||
await sleep(0.1);
|
||||
// await sleep(0.1);
|
||||
|
||||
await Note.save({ title: "un UPDATE", id: note.id });
|
||||
// await Note.save({ title: "un UPDATE", id: note.id });
|
||||
|
||||
let all = await Folder.all(true);
|
||||
await synchronizer().start();
|
||||
// let all = await Folder.all(true);
|
||||
// await synchronizer().start();
|
||||
|
||||
await localItemsSameAsRemote(all, expect);
|
||||
// await localItemsSameAsRemote(all, expect);
|
||||
|
||||
done();
|
||||
});
|
||||
// done();
|
||||
// });
|
||||
|
||||
it('should create local items', async (done) => {
|
||||
let folder = await Folder.save({ title: "folder1" });
|
||||
await Note.save({ title: "un", parent_id: folder.id });
|
||||
await synchronizer().start();
|
||||
// it('should create local items', async (done) => {
|
||||
// let folder = await Folder.save({ title: "folder1" });
|
||||
// await Note.save({ title: "un", parent_id: folder.id });
|
||||
// await synchronizer().start();
|
||||
|
||||
await switchClient(2);
|
||||
// await switchClient(2);
|
||||
|
||||
await synchronizer().start();
|
||||
// await synchronizer().start();
|
||||
|
||||
let all = await Folder.all(true);
|
||||
await localItemsSameAsRemote(all, expect);
|
||||
// let all = await Folder.all(true);
|
||||
// await localItemsSameAsRemote(all, expect);
|
||||
|
||||
done();
|
||||
});
|
||||
// done();
|
||||
// });
|
||||
|
||||
it('should update local items', async (done) => {
|
||||
let folder1 = await Folder.save({ title: "folder1" });
|
||||
let note1 = await Note.save({ title: "un", parent_id: folder1.id });
|
||||
await synchronizer().start();
|
||||
// it('should update local items', async (done) => {
|
||||
// let folder1 = await Folder.save({ title: "folder1" });
|
||||
// let note1 = await Note.save({ title: "un", parent_id: folder1.id });
|
||||
// await synchronizer().start();
|
||||
|
||||
await switchClient(2);
|
||||
// await switchClient(2);
|
||||
|
||||
await synchronizer().start();
|
||||
// await synchronizer().start();
|
||||
|
||||
await sleep(0.1);
|
||||
// await sleep(0.1);
|
||||
|
||||
let note2 = await Note.load(note1.id);
|
||||
note2.title = "Updated on client 2";
|
||||
await Note.save(note2);
|
||||
// let note2 = await Note.load(note1.id);
|
||||
// note2.title = "Updated on client 2";
|
||||
// await Note.save(note2);
|
||||
|
||||
note2 = await Note.load(note2.id);
|
||||
// note2 = await Note.load(note2.id);
|
||||
|
||||
await synchronizer().start();
|
||||
// await synchronizer().start();
|
||||
|
||||
let files = await fileApi().list();
|
||||
// let files = await fileApi().list();
|
||||
|
||||
await switchClient(1);
|
||||
// await switchClient(1);
|
||||
|
||||
await synchronizer().start();
|
||||
// await synchronizer().start();
|
||||
|
||||
note1 = await Note.load(note1.id);
|
||||
// note1 = await Note.load(note1.id);
|
||||
|
||||
expect(!!note1).toBe(true);
|
||||
expect(note1.title).toBe(note2.title);
|
||||
expect(note1.body).toBe(note2.body);
|
||||
// expect(!!note1).toBe(true);
|
||||
// expect(note1.title).toBe(note2.title);
|
||||
// expect(note1.body).toBe(note2.body);
|
||||
|
||||
done();
|
||||
});
|
||||
// done();
|
||||
// });
|
||||
|
||||
it('should resolve note conflicts', async (done) => {
|
||||
let folder1 = await Folder.save({ title: "folder1" });
|
||||
let note1 = await Note.save({ title: "un", parent_id: folder1.id });
|
||||
await synchronizer().start();
|
||||
// it('should resolve note conflicts', async (done) => {
|
||||
// let folder1 = await Folder.save({ title: "folder1" });
|
||||
// let note1 = await Note.save({ title: "un", parent_id: folder1.id });
|
||||
// await synchronizer().start();
|
||||
|
||||
await switchClient(2);
|
||||
// await switchClient(2);
|
||||
|
||||
await synchronizer().start();
|
||||
// await synchronizer().start();
|
||||
|
||||
await sleep(0.1);
|
||||
// await sleep(0.1);
|
||||
|
||||
let note2 = await Note.load(note1.id);
|
||||
note2.title = "Updated on client 2";
|
||||
await Note.save(note2);
|
||||
note2 = await Note.load(note2.id);
|
||||
// let note2 = await Note.load(note1.id);
|
||||
// note2.title = "Updated on client 2";
|
||||
// await Note.save(note2);
|
||||
// note2 = await Note.load(note2.id);
|
||||
|
||||
await synchronizer().start();
|
||||
// await synchronizer().start();
|
||||
|
||||
await switchClient(1);
|
||||
// await switchClient(1);
|
||||
|
||||
await sleep(0.1);
|
||||
// await sleep(0.1);
|
||||
|
||||
let note2conf = await Note.load(note1.id);
|
||||
note2conf.title = "Updated on client 1";
|
||||
await Note.save(note2conf);
|
||||
note2conf = await Note.load(note1.id);
|
||||
// let note2conf = await Note.load(note1.id);
|
||||
// note2conf.title = "Updated on client 1";
|
||||
// await Note.save(note2conf);
|
||||
// note2conf = await Note.load(note1.id);
|
||||
|
||||
await synchronizer().start();
|
||||
// await synchronizer().start();
|
||||
|
||||
let conflictedNotes = await Note.conflicedNotes();
|
||||
// let conflictedNotes = await Note.conflictedNotes();
|
||||
|
||||
expect(conflictedNotes.length).toBe(1);
|
||||
// expect(conflictedNotes.length).toBe(1);
|
||||
|
||||
// Other than the id (since the conflicted note is a duplicate), and the is_conflict property
|
||||
// the conflicted and original note must be the same in every way, to make sure no data has been lost.
|
||||
let conflictedNote = conflictedNotes[0];
|
||||
expect(conflictedNote.id == note2conf.id).toBe(false);
|
||||
for (let n in conflictedNote) {
|
||||
if (!conflictedNote.hasOwnProperty(n)) continue;
|
||||
if (n == 'id' || n == 'is_conflict') continue;
|
||||
expect(conflictedNote[n]).toBe(note2conf[n], 'Property: ' + n);
|
||||
}
|
||||
// // Other than the id (since the conflicted note is a duplicate), and the is_conflict property
|
||||
// // the conflicted and original note must be the same in every way, to make sure no data has been lost.
|
||||
// let conflictedNote = conflictedNotes[0];
|
||||
// expect(conflictedNote.id == note2conf.id).toBe(false);
|
||||
// for (let n in conflictedNote) {
|
||||
// if (!conflictedNote.hasOwnProperty(n)) continue;
|
||||
// if (n == 'id' || n == 'is_conflict') continue;
|
||||
// expect(conflictedNote[n]).toBe(note2conf[n], 'Property: ' + n);
|
||||
// }
|
||||
|
||||
let noteUpdatedFromRemote = await Note.load(note1.id);
|
||||
for (let n in noteUpdatedFromRemote) {
|
||||
if (!noteUpdatedFromRemote.hasOwnProperty(n)) continue;
|
||||
if (n == 'sync_time') continue;
|
||||
expect(noteUpdatedFromRemote[n]).toBe(note2[n], 'Property: ' + n);
|
||||
}
|
||||
// let noteUpdatedFromRemote = await Note.load(note1.id);
|
||||
// for (let n in noteUpdatedFromRemote) {
|
||||
// if (!noteUpdatedFromRemote.hasOwnProperty(n)) continue;
|
||||
// if (n == 'sync_time') continue;
|
||||
// expect(noteUpdatedFromRemote[n]).toBe(note2[n], 'Property: ' + n);
|
||||
// }
|
||||
|
||||
done();
|
||||
});
|
||||
// done();
|
||||
// });
|
||||
|
||||
it('should resolve folders conflicts', async (done) => {
|
||||
let folder1 = await Folder.save({ title: "folder1" });
|
||||
let note1 = await Note.save({ title: "un", parent_id: folder1.id });
|
||||
await synchronizer().start();
|
||||
// it('should resolve folders conflicts', async (done) => {
|
||||
// let folder1 = await Folder.save({ title: "folder1" });
|
||||
// let note1 = await Note.save({ title: "un", parent_id: folder1.id });
|
||||
// await synchronizer().start();
|
||||
|
||||
await switchClient(2); // ----------------------------------
|
||||
// await switchClient(2); // ----------------------------------
|
||||
|
||||
await synchronizer().start();
|
||||
// await synchronizer().start();
|
||||
|
||||
await sleep(0.1);
|
||||
// await sleep(0.1);
|
||||
|
||||
let folder1_modRemote = await Folder.load(folder1.id);
|
||||
folder1_modRemote.title = "folder1 UPDATE CLIENT 2";
|
||||
await Folder.save(folder1_modRemote);
|
||||
folder1_modRemote = await Folder.load(folder1_modRemote.id);
|
||||
// let folder1_modRemote = await Folder.load(folder1.id);
|
||||
// folder1_modRemote.title = "folder1 UPDATE CLIENT 2";
|
||||
// await Folder.save(folder1_modRemote);
|
||||
// folder1_modRemote = await Folder.load(folder1_modRemote.id);
|
||||
|
||||
await synchronizer().start();
|
||||
// await synchronizer().start();
|
||||
|
||||
await switchClient(1); // ----------------------------------
|
||||
// await switchClient(1); // ----------------------------------
|
||||
|
||||
await sleep(0.1);
|
||||
// await sleep(0.1);
|
||||
|
||||
let folder1_modLocal = await Folder.load(folder1.id);
|
||||
folder1_modLocal.title = "folder1 UPDATE CLIENT 1";
|
||||
await Folder.save(folder1_modLocal);
|
||||
folder1_modLocal = await Folder.load(folder1.id);
|
||||
// let folder1_modLocal = await Folder.load(folder1.id);
|
||||
// folder1_modLocal.title = "folder1 UPDATE CLIENT 1";
|
||||
// await Folder.save(folder1_modLocal);
|
||||
// folder1_modLocal = await Folder.load(folder1.id);
|
||||
|
||||
await synchronizer().start();
|
||||
// await synchronizer().start();
|
||||
|
||||
let folder1_final = await Folder.load(folder1.id);
|
||||
expect(folder1_final.title).toBe(folder1_modRemote.title);
|
||||
// let folder1_final = await Folder.load(folder1.id);
|
||||
// expect(folder1_final.title).toBe(folder1_modRemote.title);
|
||||
|
||||
done();
|
||||
});
|
||||
// done();
|
||||
// });
|
||||
|
||||
it('should delete remote items', async (done) => {
|
||||
let folder1 = await Folder.save({ title: "folder1" });
|
||||
let note1 = await Note.save({ title: "un", parent_id: folder1.id });
|
||||
await synchronizer().start();
|
||||
// it('should delete remote items', async (done) => {
|
||||
// let folder1 = await Folder.save({ title: "folder1" });
|
||||
// let note1 = await Note.save({ title: "un", parent_id: folder1.id });
|
||||
// await synchronizer().start();
|
||||
|
||||
await switchClient(2);
|
||||
// await switchClient(2);
|
||||
|
||||
await synchronizer().start();
|
||||
// await synchronizer().start();
|
||||
|
||||
await sleep(0.1);
|
||||
// await sleep(0.1);
|
||||
|
||||
await Note.delete(note1.id);
|
||||
// await Note.delete(note1.id);
|
||||
|
||||
await synchronizer().start();
|
||||
// await synchronizer().start();
|
||||
|
||||
let files = await fileApi().list();
|
||||
// let files = await fileApi().list();
|
||||
|
||||
expect(files.length).toBe(1);
|
||||
expect(files[0].path).toBe(Folder.systemPath(folder1));
|
||||
// expect(files.length).toBe(1);
|
||||
// expect(files[0].path).toBe(Folder.systemPath(folder1));
|
||||
|
||||
let deletedItems = await BaseModel.deletedItems();
|
||||
expect(deletedItems.length).toBe(0);
|
||||
// let deletedItems = await BaseModel.deletedItems();
|
||||
// expect(deletedItems.length).toBe(0);
|
||||
|
||||
done();
|
||||
});
|
||||
// done();
|
||||
// });
|
||||
|
||||
it('should delete local items', async (done) => {
|
||||
let folder1 = await Folder.save({ title: "folder1" });
|
||||
let note1 = await Note.save({ title: "un", parent_id: folder1.id });
|
||||
await synchronizer().start();
|
||||
|
||||
await switchClient(2);
|
||||
|
||||
await synchronizer().start();
|
||||
|
||||
await sleep(0.1);
|
||||
|
||||
await Note.delete(note1.id);
|
||||
|
||||
await synchronizer().start();
|
||||
|
||||
await switchClient(1);
|
||||
|
||||
await synchronizer().start();
|
||||
|
||||
let items = await Folder.all(true);
|
||||
|
||||
expect(items.length).toBe(1);
|
||||
|
||||
let deletedItems = await BaseModel.deletedItems();
|
||||
|
||||
expect(deletedItems.length).toBe(0);
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
// it('should handle conflict when remote note is deleted then local note is modified', async (done) => {
|
||||
// it('should delete local items', async (done) => {
|
||||
// let folder1 = await Folder.save({ title: "folder1" });
|
||||
// let note1 = await Note.save({ title: "un", parent_id: folder1.id });
|
||||
// await synchronizer().start();
|
||||
@ -282,19 +252,51 @@ describe('Synchronizer', function() {
|
||||
|
||||
// await switchClient(1);
|
||||
|
||||
// await Note.save({ id: note1.id, title: 'Modified after having been deleted' });
|
||||
|
||||
// await synchronizer().start();
|
||||
|
||||
// // let items = await Folder.all(true);
|
||||
// let items = await Folder.all(true);
|
||||
|
||||
// // expect(items.length).toBe(1);
|
||||
// expect(items.length).toBe(1);
|
||||
|
||||
// // let deletedItems = await BaseModel.deletedItems();
|
||||
// let deletedItems = await BaseModel.deletedItems();
|
||||
|
||||
// // expect(deletedItems.length).toBe(0);
|
||||
// expect(deletedItems.length).toBe(0);
|
||||
|
||||
// done();
|
||||
// });
|
||||
|
||||
it('should handle conflict when remote note is deleted then local note is modified', async (done) => {
|
||||
let folder1 = await Folder.save({ title: "folder1" });
|
||||
let note1 = await Note.save({ title: "un", parent_id: folder1.id });
|
||||
await synchronizer().start();
|
||||
|
||||
await switchClient(2);
|
||||
|
||||
await synchronizer().start();
|
||||
|
||||
await sleep(0.1);
|
||||
|
||||
await Note.delete(note1.id);
|
||||
|
||||
await synchronizer().start();
|
||||
|
||||
await switchClient(1);
|
||||
|
||||
let newTitle = 'Modified after having been deleted';
|
||||
await Note.save({ id: note1.id, title: newTitle });
|
||||
|
||||
await synchronizer().start();
|
||||
|
||||
let conflictedNotes = await Note.conflictedNotes();
|
||||
|
||||
expect(conflictedNotes.length).toBe(1);
|
||||
expect(conflictedNotes[0].title).toBe(newTitle);
|
||||
|
||||
let items = await Folder.all(true);
|
||||
|
||||
expect(items.length).toBe(1);
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
});
|
@ -51,7 +51,7 @@ class Note extends BaseItem {
|
||||
return this.modelSelectOne('SELECT ' + this.previewFieldsSql() + ' FROM is_conflict = 0 AND notes WHERE id = ?', [noteId]);
|
||||
}
|
||||
|
||||
static conflicedNotes() {
|
||||
static conflictedNotes() {
|
||||
return this.modelSelectAll('SELECT * FROM notes WHERE is_conflict = 1');
|
||||
}
|
||||
|
||||
|
@ -53,7 +53,6 @@ class Synchronizer {
|
||||
} else {
|
||||
// Note or folder was modified after having been deleted remotely
|
||||
action = local.type_ == BaseModel.MODEL_TYPE_NOTE ? 'noteConflict' : 'folderConflict';
|
||||
// TODO: handle conflict
|
||||
}
|
||||
} else {
|
||||
if (remote.updated_time > local.sync_time) {
|
||||
@ -77,6 +76,8 @@ class Synchronizer {
|
||||
|
||||
} else if (action == 'folderConflict') {
|
||||
|
||||
// TODO: if remote has been deleted, delete local too
|
||||
|
||||
let remoteContent = await this.api().get(path);
|
||||
local = BaseItem.unserialize(remoteContent);
|
||||
|
||||
@ -92,11 +93,13 @@ class Synchronizer {
|
||||
conflictedNote.is_conflict = 1;
|
||||
await Note.save(conflictedNote, { autoTimestamp: false });
|
||||
|
||||
let remoteContent = await this.api().get(path);
|
||||
local = BaseItem.unserialize(remoteContent);
|
||||
if (remote) {
|
||||
let remoteContent = await this.api().get(path);
|
||||
local = BaseItem.unserialize(remoteContent);
|
||||
|
||||
local.sync_time = time.unixMs();
|
||||
await ItemClass.save(local, { autoTimestamp: false });
|
||||
local.sync_time = time.unixMs();
|
||||
await ItemClass.save(local, { autoTimestamp: false });
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user