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:
parent
980e4bded1
commit
927894e940
@ -91,7 +91,11 @@ class Command extends BaseCommand {
|
|||||||
|
|
||||||
this.log(_('Starting synchronization...'));
|
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();
|
vorpalUtils.redrawDone();
|
||||||
|
|
||||||
await app().refreshCurrentFolder();
|
await app().refreshCurrentFolder();
|
||||||
|
@ -8,7 +8,7 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: Joplin-CLI 1.0.0\n"
|
"Project-Id-Version: Joplin-CLI 1.0.0\n"
|
||||||
"Report-Msgid-Bugs-To: \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"
|
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||||
@ -316,11 +316,11 @@ msgstr ""
|
|||||||
msgid "Starting synchronization..."
|
msgid "Starting synchronization..."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /media/veracrypt22/src/notes/CliClient/app/command-sync.js:99
|
#: /media/veracrypt22/src/notes/CliClient/app/command-sync.js:103
|
||||||
msgid "Done."
|
msgid "Done."
|
||||||
msgstr ""
|
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
|
#: /media/veracrypt22/src/notes/ReactNativeClient/lib/synchronizer.js:60
|
||||||
msgid "Cancelling..."
|
msgid "Cancelling..."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
@ -7,7 +7,7 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: Joplin-CLI 1.0.0\n"
|
"Project-Id-Version: Joplin-CLI 1.0.0\n"
|
||||||
"Report-Msgid-Bugs-To: \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"
|
"PO-Revision-Date: 2017-07-18 13:27+0100\n"
|
||||||
"Last-Translator: \n"
|
"Last-Translator: \n"
|
||||||
"Language-Team: \n"
|
"Language-Team: \n"
|
||||||
@ -342,11 +342,11 @@ msgstr "Impossible d'initialiser le synchroniseur."
|
|||||||
msgid "Starting synchronization..."
|
msgid "Starting synchronization..."
|
||||||
msgstr "Commencement de la synchronisation..."
|
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."
|
msgid "Done."
|
||||||
msgstr "Terminé."
|
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
|
#: /media/veracrypt22/src/notes/ReactNativeClient/lib/synchronizer.js:60
|
||||||
msgid "Cancelling..."
|
msgid "Cancelling..."
|
||||||
msgstr "Annulation..."
|
msgstr "Annulation..."
|
||||||
|
@ -8,7 +8,7 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: Joplin-CLI 1.0.0\n"
|
"Project-Id-Version: Joplin-CLI 1.0.0\n"
|
||||||
"Report-Msgid-Bugs-To: \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"
|
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||||
@ -316,11 +316,11 @@ msgstr ""
|
|||||||
msgid "Starting synchronization..."
|
msgid "Starting synchronization..."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /media/veracrypt22/src/notes/CliClient/app/command-sync.js:99
|
#: /media/veracrypt22/src/notes/CliClient/app/command-sync.js:103
|
||||||
msgid "Done."
|
msgid "Done."
|
||||||
msgstr ""
|
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
|
#: /media/veracrypt22/src/notes/ReactNativeClient/lib/synchronizer.js:60
|
||||||
msgid "Cancelling..."
|
msgid "Cancelling..."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
@ -166,10 +166,55 @@ class FileApiDriverOneDrive {
|
|||||||
throw new Error('Not implemented');
|
throw new Error('Not implemented');
|
||||||
}
|
}
|
||||||
|
|
||||||
// delta(path) {
|
async delta(path, options = null) {
|
||||||
// let response = await this.api_.exec('GET', this.makePath_(path) + ':/delta');
|
let output = {
|
||||||
// console.info(response);
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -92,6 +92,11 @@ class FileApi {
|
|||||||
return this.driver_.format();
|
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 };
|
export { FileApi };
|
@ -148,6 +148,7 @@ Setting.defaults_ = {
|
|||||||
'sync.onedrive.auth': { value: '', type: 'string', public: false },
|
'sync.onedrive.auth': { value: '', type: 'string', public: false },
|
||||||
'sync.filesystem.path': { value: '', type: 'string', public: true },
|
'sync.filesystem.path': { value: '', type: 'string', public: true },
|
||||||
'sync.target': { value: 'onedrive', 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 },
|
'editor': { value: '', type: 'string', public: true },
|
||||||
'locale': { value: 'en_GB', type: 'string', public: true },
|
'locale': { value: 'en_GB', type: 'string', public: true },
|
||||||
'aliases': { value: '', type: 'string', public: true },
|
'aliases': { value: '', type: 'string', public: true },
|
||||||
|
@ -147,6 +147,8 @@ class Synchronizer {
|
|||||||
this.onProgress_ = options.onProgress ? options.onProgress : function(o) {};
|
this.onProgress_ = options.onProgress ? options.onProgress : function(o) {};
|
||||||
this.progressReport_ = { errors: [] };
|
this.progressReport_ = { errors: [] };
|
||||||
|
|
||||||
|
let lastContext = options.context;
|
||||||
|
|
||||||
const syncTargetId = this.api().driver().syncTargetId();
|
const syncTargetId = this.api().driver().syncTargetId();
|
||||||
|
|
||||||
if (this.state() != 'idle') {
|
if (this.state() != 'idle') {
|
||||||
@ -164,6 +166,8 @@ class Synchronizer {
|
|||||||
|
|
||||||
let synchronizationId = time.unixMs().toString();
|
let synchronizationId = time.unixMs().toString();
|
||||||
|
|
||||||
|
let outputContext = {};
|
||||||
|
|
||||||
this.state_ = 'in_progress';
|
this.state_ = 'in_progress';
|
||||||
|
|
||||||
this.dispatch({ type: 'SYNC_STARTED' });
|
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
|
// 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.
|
// or handled as conflicts, so no conflict is possible after this.
|
||||||
|
|
||||||
let remoteIds = [];
|
let deltaOptions = {};
|
||||||
let context = null;
|
if (lastContext.delta) deltaOptions.context = lastContext.delta;
|
||||||
|
let listResult = await this.api().delta('', deltaOptions);
|
||||||
|
outputContext.delta = listResult.context;
|
||||||
|
|
||||||
while (true) {
|
// let remoteIds = [];
|
||||||
if (this.cancelling()) break;
|
// let context = null;
|
||||||
|
|
||||||
let listResult = await this.api().list('', { context: context });
|
// while (true) {
|
||||||
let remotes = listResult.items;
|
// if (this.cancelling()) break;
|
||||||
for (let i = 0; i < remotes.length; i++) {
|
|
||||||
if (this.cancelling()) break;
|
|
||||||
|
|
||||||
let remote = remotes[i];
|
// let listResult = await this.api().list('', { context: context });
|
||||||
let path = remote.path;
|
// let remotes = listResult.items;
|
||||||
|
// for (let i = 0; i < remotes.length; i++) {
|
||||||
|
// if (this.cancelling()) break;
|
||||||
|
|
||||||
remoteIds.push(BaseItem.pathToId(path));
|
// let remote = remotes[i];
|
||||||
if (donePaths.indexOf(path) > 0) continue;
|
// let path = remote.path;
|
||||||
|
|
||||||
let action = null;
|
// remoteIds.push(BaseItem.pathToId(path));
|
||||||
let reason = '';
|
// if (donePaths.indexOf(path) > 0) continue;
|
||||||
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) 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') {
|
// if (!action) continue;
|
||||||
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);
|
|
||||||
|
|
||||||
let newContent = Object.assign({}, content);
|
// if (action == 'createLocal' || action == 'updateLocal') {
|
||||||
let options = {
|
// let content = await this.api().get(path);
|
||||||
autoTimestamp: false,
|
// if (content === null) {
|
||||||
applyMetadataChanges: true,
|
// 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);
|
||||||
nextQueries: BaseItem.updateSyncTimeQueries(syncTargetId, newContent, time.unixMs()),
|
// continue;
|
||||||
};
|
// }
|
||||||
if (action == 'createLocal') options.isNew = true;
|
// content = await BaseItem.unserialize(content);
|
||||||
|
// let ItemClass = BaseItem.itemClass(content);
|
||||||
|
|
||||||
if (newContent.type_ == BaseModel.TYPE_RESOURCE && action == 'createLocal') {
|
// let newContent = Object.assign({}, content);
|
||||||
let localResourceContentPath = Resource.fullPath(newContent);
|
// let options = {
|
||||||
let remoteResourceContentPath = this.resourceDirName_ + '/' + newContent.id;
|
// autoTimestamp: false,
|
||||||
await this.api().get(remoteResourceContentPath, { path: localResourceContentPath, target: 'file' });
|
// 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);
|
// await ItemClass.save(newContent, options);
|
||||||
} else {
|
|
||||||
this.logSyncOperation(action, local, remote, reason);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!listResult.hasMore) break;
|
// this.logSyncOperation(action, local, content, reason);
|
||||||
context = listResult.context;
|
// } else {
|
||||||
}
|
// this.logSyncOperation(action, local, remote, reason);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
// ------------------------------------------------------------------------
|
// if (!listResult.hasMore) break;
|
||||||
// Search, among the local IDs, those that don't exist remotely, which
|
// context = listResult.context;
|
||||||
// means the item has been deleted.
|
// }
|
||||||
// ------------------------------------------------------------------------
|
|
||||||
|
|
||||||
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 localFoldersToDelete = [];
|
||||||
let syncItems = await BaseItem.syncedItems(syncTargetId);
|
|
||||||
for (let i = 0; i < syncItems.length; i++) {
|
|
||||||
if (this.cancelling()) break;
|
|
||||||
|
|
||||||
let syncItem = syncItems[i];
|
// if (!this.cancelling()) {
|
||||||
if (remoteIds.indexOf(syncItem.item_id) < 0) {
|
// let syncItems = await BaseItem.syncedItems(syncTargetId);
|
||||||
if (syncItem.item_type == Folder.modelType()) {
|
// for (let i = 0; i < syncItems.length; i++) {
|
||||||
localFoldersToDelete.push(syncItem);
|
// if (this.cancelling()) break;
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
// this.logSyncOperation('deleteLocal', { id: syncItem.item_id }, null, 'remote has been deleted');
|
||||||
await ItemClass.delete(syncItem.item_id, { trackDeleted: false });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this.cancelling()) {
|
// let ItemClass = BaseItem.itemClass(syncItem.item_type);
|
||||||
for (let i = 0; i < localFoldersToDelete.length; i++) {
|
// await ItemClass.delete(syncItem.item_id, { trackDeleted: false });
|
||||||
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()) {
|
// if (!this.cancelling()) {
|
||||||
await BaseItem.deleteOrphanSyncItems();
|
// 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) {
|
} catch (error) {
|
||||||
this.logger().error(error);
|
this.logger().error(error);
|
||||||
this.progressReport_.errors.push(error);
|
this.progressReport_.errors.push(error);
|
||||||
@ -447,6 +456,8 @@ class Synchronizer {
|
|||||||
this.progressReport_ = {};
|
this.progressReport_ = {};
|
||||||
|
|
||||||
this.dispatch({ type: 'SYNC_COMPLETED' });
|
this.dispatch({ type: 'SYNC_COMPLETED' });
|
||||||
|
|
||||||
|
return outputContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user