Skip conflict resolution for remote objects that can't be saved

E.g., if a local item has been modified in a way that conflicts with a
remote item that also has a new, unknown field, don't show the CR
window -- just add the item to the sync queue and show the
some-data-could-not-be-downloaded error.
This commit is contained in:
Dan Stillman 2021-02-07 16:45:08 -05:00
parent 254695f4b3
commit b919143630
2 changed files with 116 additions and 0 deletions

View file

@ -935,6 +935,22 @@ Zotero.Sync.Data.Local = {
if (objectType != 'item') {
throw new Error(`Unexpected conflict on ${objectType} object`);
}
// Skip conflict resolution if there are invalid fields
try {
let testObj = obj.clone();
testObj.fromJSON(jsonData, { strict: true });
}
catch (e) {
results.push({
key: objectKey,
processed: false,
error: e,
retry: false
});
throw e;
}
Zotero.debug("Conflict!", 2);
Zotero.debug(jsonDataLocal);
Zotero.debug(jsonData);

View file

@ -1736,6 +1736,106 @@ describe("Zotero.Sync.Data.Engine", function () {
assert.equal(spy.callCount, 4);
});
it("shouldn't show CR window if remote data contains unknown field", async function () {
({ engine, client, caller } = await setup({
stopOnError: false
}));
var library = Zotero.Libraries.userLibrary;
library.libraryVersion = 1;
await library.saveTx();
var item = createUnsavedDataObject('item', { title: 'a' });
item.version = 1;
await item.saveTx();
var json = item.toResponseJSON();
json.data.title = 'b';
json.data.invalidField = 'abcd';
var headers = {
"Last-Modified-Version": 2
};
server.respond(function (req) {
// Return 412, because item has changed remotely
if (req.method == "POST") {
if (!req.url.startsWith(baseURL + "users/1/items")) {
throw new Error("Unexpected POST");
}
req.respond(
412,
{
"Last-Modified-Version": 2
},
""
);
}
});
setResponse({
method: "GET",
url: "users/1/settings?since=1",
status: 200,
headers,
json: {}
});
setResponse({
method: "GET",
url: "users/1/collections?format=versions&since=1",
status: 200,
headers,
json: {}
});
setResponse({
method: "GET",
url: "users/1/searches?format=versions&since=1",
status: 200,
headers,
json: {}
});
setResponse({
method: "GET",
url: "users/1/items/top?format=versions&since=1&includeTrashed=1",
status: 200,
headers,
json: {
[item.key]: 2
}
});
setResponse({
method: "GET",
url: "users/1/items?format=versions&since=1&includeTrashed=1",
status: 200,
headers,
json: {
[item.key]: 2
}
});
setResponse({
method: "GET",
url: `users/1/items?format=json&itemKey=${item.key}&includeTrashed=1`,
status: 200,
headers,
json: [json]
});
setResponse({
method: "GET",
url: "users/1/deleted?since=1",
status: 200,
headers,
json: {}
});
var spy = sinon.spy(engine, "onError");
await engine.start();
var userLibraryID = Zotero.Libraries.userLibraryID;
// Library version should have been updated
assert.equal(Zotero.Libraries.getVersion(userLibraryID), 2);
var keys = await Zotero.Sync.Data.Local.getObjectsFromSyncQueue('item', userLibraryID);
assert.sameMembers(keys, [item.key]);
});
it("should delay on second upload conflict", function* () {
var library = Zotero.Libraries.userLibrary;
library.libraryVersion = 5;