If parent item is missing remotely, mark as unsynced and add to queue

This shouldn't happen, but there've been some reports of it.
This commit is contained in:
Dan Stillman 2017-02-16 18:01:53 -05:00
parent cbed716424
commit 34c90fd156
2 changed files with 95 additions and 0 deletions

View file

@ -1113,6 +1113,32 @@ Zotero.Sync.Data.Engine.prototype._uploadObjects = Zotero.Promise.coroutine(func
Zotero.logError("Error for " + objectType + " " + jsonBatch[index].key + " in "
+ this.library.name + ":\n\n" + e);
// If parent item is missing remotely and it isn't in the queue (which shouldn't happen),
// mark it as unsynced and add to queue
if (e.code == 400 && objectType == 'item' && data && data.parentItem) {
let parentItem = Zotero.Items.getByLibraryAndKey(this.libraryID, data.parentItem);
if (!parentItem) {
throw new Error(`Item ${this.libraryID}/${jsonBatch[index].key} references parent `
+ `item ${data.parentItem}, which doesn't exist`);
}
let id = parentItem.id;
// If parent item isn't already in queue, mark it as unsynced and add it
if (!queue.find(o => o.id == id) && !batch.find(o => o.id == id)) {
yield Zotero.Sync.Data.Local.markObjectAsUnsynced(parentItem);
Zotero.logError(`Adding parent item ${data.parentItem} to upload queue`);
queue.push({
id,
json: null,
tries: 0,
failed: false
});
// Pretend that we were successful so syncing continues
numSuccessful++;
continue;
}
}
// This shouldn't happen, because the upload request includes a library version and should
// prevent an outdated upload before the object version is checked. If it does, we need to
// do a full sync. This error is checked in handleUploadError().

View file

@ -2175,6 +2175,75 @@ describe("Zotero.Sync.Data.Engine", function () {
var result = yield engine._startUpload();
assert.equal(result, engine.UPLOAD_RESULT_OBJECT_CONFLICT);
});
it("should mark local parent item as unsynced if it doesn't exist when uploading child", function* () {
({ engine, client, caller } = yield setup());
var library = Zotero.Libraries.userLibrary;
var libraryID = library.id;
var lastLibraryVersion = 5;
library.libraryVersion = lastLibraryVersion;
yield library.saveTx();
var item = createUnsavedDataObject('item');
// Set the parent item as synced (though this shouldn't happen)
item.synced = true;
yield item.saveTx();
var note = yield createDataObject('item', { itemType: 'note', parentID: item.id });
var called = 0;
server.respond(function (req) {
let requestJSON = JSON.parse(req.requestBody);
if (called == 0) {
assert.lengthOf(requestJSON, 1);
assert.equal(requestJSON[0].key, note.key);
req.respond(
200,
{
"Last-Modified-Version": lastLibraryVersion
},
JSON.stringify({
successful: {},
unchanged: {},
failed: {
0: {
code: 400,
message: `Parent item '${item.key}' doesn't exist`,
data: {
parentItem: item.key
}
}
}
})
);
}
else if (called == 1) {
assert.lengthOf(requestJSON, 2);
assert.sameMembers(requestJSON.map(o => o.key), [item.key, note.key]);
req.respond(
200,
{
"Last-Modified-Version": ++lastLibraryVersion
},
JSON.stringify({
successful: {
0: item.toResponseJSON(),
1: note.toResponseJSON()
},
unchanged: {},
failed: {}
})
);
}
called++;
});
var result = yield engine._startUpload();
assert.equal(result, engine.UPLOAD_RESULT_SUCCESS);
assert.equal(called, 2);
});
});