1
0
mirror of https://github.com/laurent22/joplin.git synced 2024-12-24 10:27:10 +02:00

OneDrive delta api

This commit is contained in:
Laurent Cozic 2017-07-18 19:57:49 +00:00
parent 980e4bded1
commit 927894e940
8 changed files with 169 additions and 103 deletions

View File

@ -91,7 +91,11 @@ class Command extends BaseCommand {
this.log(_('Starting synchronization...'));
await sync.start(options);
let context = Setting.value('sync.context');
context = context ? JSON.parse(context) : {};
options.context = context;
let newContext = await sync.start(options);
Setting.setValue('sync.context', JSON.stringify(newContext));
vorpalUtils.redrawDone();
await app().refreshCurrentFolder();

View File

@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: Joplin-CLI 1.0.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-07-18 16:33+0100\n"
"POT-Creation-Date: 2017-07-18 17:30+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@ -316,11 +316,11 @@ msgstr ""
msgid "Starting synchronization..."
msgstr ""
#: /media/veracrypt22/src/notes/CliClient/app/command-sync.js:99
#: /media/veracrypt22/src/notes/CliClient/app/command-sync.js:103
msgid "Done."
msgstr ""
#: /media/veracrypt22/src/notes/CliClient/app/command-sync.js:114
#: /media/veracrypt22/src/notes/CliClient/app/command-sync.js:118
#: /media/veracrypt22/src/notes/ReactNativeClient/lib/synchronizer.js:60
msgid "Cancelling..."
msgstr ""

View File

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: Joplin-CLI 1.0.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-07-18 16:32+0100\n"
"POT-Creation-Date: 2017-07-18 17:28+0100\n"
"PO-Revision-Date: 2017-07-18 13:27+0100\n"
"Last-Translator: \n"
"Language-Team: \n"
@ -342,11 +342,11 @@ msgstr "Impossible d'initialiser le synchroniseur."
msgid "Starting synchronization..."
msgstr "Commencement de la synchronisation..."
#: /media/veracrypt22/src/notes/CliClient/app/command-sync.js:99
#: /media/veracrypt22/src/notes/CliClient/app/command-sync.js:103
msgid "Done."
msgstr "Terminé."
#: /media/veracrypt22/src/notes/CliClient/app/command-sync.js:114
#: /media/veracrypt22/src/notes/CliClient/app/command-sync.js:118
#: /media/veracrypt22/src/notes/ReactNativeClient/lib/synchronizer.js:60
msgid "Cancelling..."
msgstr "Annulation..."

View File

@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: Joplin-CLI 1.0.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-07-18 16:33+0100\n"
"POT-Creation-Date: 2017-07-18 17:30+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@ -316,11 +316,11 @@ msgstr ""
msgid "Starting synchronization..."
msgstr ""
#: /media/veracrypt22/src/notes/CliClient/app/command-sync.js:99
#: /media/veracrypt22/src/notes/CliClient/app/command-sync.js:103
msgid "Done."
msgstr ""
#: /media/veracrypt22/src/notes/CliClient/app/command-sync.js:114
#: /media/veracrypt22/src/notes/CliClient/app/command-sync.js:118
#: /media/veracrypt22/src/notes/ReactNativeClient/lib/synchronizer.js:60
msgid "Cancelling..."
msgstr ""

View File

@ -166,10 +166,55 @@ class FileApiDriverOneDrive {
throw new Error('Not implemented');
}
// delta(path) {
// let response = await this.api_.exec('GET', this.makePath_(path) + ':/delta');
// console.info(response);
// }
async delta(path, options = null) {
let output = {
context: {},
items: [],
};
let context = options ? options.context : null;
let url = null;
let query = null;
if (context) {
url = context;
} else {
url = this.makePath_(path) + ':/delta';
query = this.itemFilter_();
}
while (true) {
let response = await this.api_.execJson('GET', url, query);
let items = this.makeItems_(response.value);
output.items = output.items.concat(items);
if (response['@odata.nextLink']) {
url = response['@odata.nextLink'];
} else {
if (!response['@odata.deltaLink']) {
throw new Error('Delta link missing: ' + JSON.stringify(response));
}
output.context = response['@odata.deltaLink'];
break;
}
}
// https://dev.onedrive.com/items/view_delta.htm
// The same item may appear more than once in a delta feed, for various reasons. You should use the last occurrence you see.
// So remove any duplicate item from the array.
let temp = [];
let seenPaths = [];
for (let i = output.items.length - 1; i >= 0; i--) {
let item = output.items[i];
if (seenPaths.indexOf(item.path) >= 0) continue;
temp.splice(0, 0, item);
seenPaths.push(item.path);
}
output.items = temp;
return output;
}
}

View File

@ -92,6 +92,11 @@ class FileApi {
return this.driver_.format();
}
delta(path, options = null) {
this.logger().debug('delta ' + this.fullPath_(path));
return this.driver_.delta(this.fullPath_(path), options);
}
}
export { FileApi };

View File

@ -148,6 +148,7 @@ Setting.defaults_ = {
'sync.onedrive.auth': { value: '', type: 'string', public: false },
'sync.filesystem.path': { value: '', type: 'string', public: true },
'sync.target': { value: 'onedrive', type: 'string', public: true },
'sync.context': { value: '', type: 'string', public: false },
'editor': { value: '', type: 'string', public: true },
'locale': { value: 'en_GB', type: 'string', public: true },
'aliases': { value: '', type: 'string', public: true },

View File

@ -147,6 +147,8 @@ class Synchronizer {
this.onProgress_ = options.onProgress ? options.onProgress : function(o) {};
this.progressReport_ = { errors: [] };
let lastContext = options.context;
const syncTargetId = this.api().driver().syncTargetId();
if (this.state() != 'idle') {
@ -164,6 +166,8 @@ class Synchronizer {
let synchronizationId = time.unixMs().toString();
let outputContext = {};
this.state_ = 'in_progress';
this.dispatch({ type: 'SYNC_STARTED' });
@ -315,116 +319,121 @@ class Synchronizer {
// At this point all the local items that have changed have been pushed to remote
// or handled as conflicts, so no conflict is possible after this.
let remoteIds = [];
let context = null;
let deltaOptions = {};
if (lastContext.delta) deltaOptions.context = lastContext.delta;
let listResult = await this.api().delta('', deltaOptions);
outputContext.delta = listResult.context;
while (true) {
if (this.cancelling()) break;
// let remoteIds = [];
// let context = null;
let listResult = await this.api().list('', { context: context });
let remotes = listResult.items;
for (let i = 0; i < remotes.length; i++) {
if (this.cancelling()) break;
// while (true) {
// if (this.cancelling()) break;
let remote = remotes[i];
let path = remote.path;
// let listResult = await this.api().list('', { context: context });
// let remotes = listResult.items;
// for (let i = 0; i < remotes.length; i++) {
// if (this.cancelling()) break;
remoteIds.push(BaseItem.pathToId(path));
if (donePaths.indexOf(path) > 0) continue;
// let remote = remotes[i];
// let path = remote.path;
let action = null;
let reason = '';
let local = await BaseItem.loadItemByPath(path);
if (!local) {
action = 'createLocal';
reason = 'remote exists but local does not';
} else {
if (remote.updated_time > local.updated_time) {
action = 'updateLocal';
reason = sprintf('remote is more recent than local');
}
}
// remoteIds.push(BaseItem.pathToId(path));
// if (donePaths.indexOf(path) > 0) continue;
if (!action) continue;
// let action = null;
// let reason = '';
// let local = await BaseItem.loadItemByPath(path);
// if (!local) {
// action = 'createLocal';
// reason = 'remote exists but local does not';
// } else {
// if (remote.updated_time > local.updated_time) {
// action = 'updateLocal';
// reason = sprintf('remote is more recent than local');
// }
// }
if (action == 'createLocal' || action == 'updateLocal') {
let content = await this.api().get(path);
if (content === null) {
this.logger().warn('Remote has been deleted between now and the list() call? In that case it will be handled during the next sync: ' + path);
continue;
}
content = await BaseItem.unserialize(content);
let ItemClass = BaseItem.itemClass(content);
// if (!action) continue;
let newContent = Object.assign({}, content);
let options = {
autoTimestamp: false,
applyMetadataChanges: true,
nextQueries: BaseItem.updateSyncTimeQueries(syncTargetId, newContent, time.unixMs()),
};
if (action == 'createLocal') options.isNew = true;
// if (action == 'createLocal' || action == 'updateLocal') {
// let content = await this.api().get(path);
// if (content === null) {
// this.logger().warn('Remote has been deleted between now and the list() call? In that case it will be handled during the next sync: ' + path);
// continue;
// }
// content = await BaseItem.unserialize(content);
// let ItemClass = BaseItem.itemClass(content);
if (newContent.type_ == BaseModel.TYPE_RESOURCE && action == 'createLocal') {
let localResourceContentPath = Resource.fullPath(newContent);
let remoteResourceContentPath = this.resourceDirName_ + '/' + newContent.id;
await this.api().get(remoteResourceContentPath, { path: localResourceContentPath, target: 'file' });
}
// let newContent = Object.assign({}, content);
// let options = {
// autoTimestamp: false,
// applyMetadataChanges: true,
// nextQueries: BaseItem.updateSyncTimeQueries(syncTargetId, newContent, time.unixMs()),
// };
// if (action == 'createLocal') options.isNew = true;
await ItemClass.save(newContent, options);
// if (newContent.type_ == BaseModel.TYPE_RESOURCE && action == 'createLocal') {
// let localResourceContentPath = Resource.fullPath(newContent);
// let remoteResourceContentPath = this.resourceDirName_ + '/' + newContent.id;
// await this.api().get(remoteResourceContentPath, { path: localResourceContentPath, target: 'file' });
// }
this.logSyncOperation(action, local, content, reason);
} else {
this.logSyncOperation(action, local, remote, reason);
}
}
// await ItemClass.save(newContent, options);
if (!listResult.hasMore) break;
context = listResult.context;
}
// this.logSyncOperation(action, local, content, reason);
// } else {
// this.logSyncOperation(action, local, remote, reason);
// }
// }
// ------------------------------------------------------------------------
// Search, among the local IDs, those that don't exist remotely, which
// means the item has been deleted.
// ------------------------------------------------------------------------
// if (!listResult.hasMore) break;
// context = listResult.context;
// }
if (this.randomFailure(options, 4)) return;
// // ------------------------------------------------------------------------
// // Search, among the local IDs, those that don't exist remotely, which
// // means the item has been deleted.
// // ------------------------------------------------------------------------
let localFoldersToDelete = [];
// if (this.randomFailure(options, 4)) return;
if (!this.cancelling()) {
let syncItems = await BaseItem.syncedItems(syncTargetId);
for (let i = 0; i < syncItems.length; i++) {
if (this.cancelling()) break;
// let localFoldersToDelete = [];
let syncItem = syncItems[i];
if (remoteIds.indexOf(syncItem.item_id) < 0) {
if (syncItem.item_type == Folder.modelType()) {
localFoldersToDelete.push(syncItem);
continue;
}
// if (!this.cancelling()) {
// let syncItems = await BaseItem.syncedItems(syncTargetId);
// for (let i = 0; i < syncItems.length; i++) {
// if (this.cancelling()) break;
this.logSyncOperation('deleteLocal', { id: syncItem.item_id }, null, 'remote has been deleted');
// let syncItem = syncItems[i];
// if (remoteIds.indexOf(syncItem.item_id) < 0) {
// if (syncItem.item_type == Folder.modelType()) {
// localFoldersToDelete.push(syncItem);
// continue;
// }
let ItemClass = BaseItem.itemClass(syncItem.item_type);
await ItemClass.delete(syncItem.item_id, { trackDeleted: false });
}
}
}
// this.logSyncOperation('deleteLocal', { id: syncItem.item_id }, null, 'remote has been deleted');
if (!this.cancelling()) {
for (let i = 0; i < localFoldersToDelete.length; i++) {
const syncItem = localFoldersToDelete[i];
const noteIds = await Folder.noteIds(syncItem.item_id);
if (noteIds.length) { // CONFLICT
await Folder.markNotesAsConflict(syncItem.item_id);
}
await Folder.delete(syncItem.item_id, { deleteChildren: false });
}
}
// let ItemClass = BaseItem.itemClass(syncItem.item_type);
// await ItemClass.delete(syncItem.item_id, { trackDeleted: false });
// }
// }
// }
if (!this.cancelling()) {
await BaseItem.deleteOrphanSyncItems();
}
// if (!this.cancelling()) {
// for (let i = 0; i < localFoldersToDelete.length; i++) {
// const syncItem = localFoldersToDelete[i];
// const noteIds = await Folder.noteIds(syncItem.item_id);
// if (noteIds.length) { // CONFLICT
// await Folder.markNotesAsConflict(syncItem.item_id);
// }
// await Folder.delete(syncItem.item_id, { deleteChildren: false });
// }
// }
// if (!this.cancelling()) {
// await BaseItem.deleteOrphanSyncItems();
// }
} catch (error) {
this.logger().error(error);
this.progressReport_.errors.push(error);
@ -447,6 +456,8 @@ class Synchronizer {
this.progressReport_ = {};
this.dispatch({ type: 'SYNC_COMPLETED' });
return outputContext;
}
}