diff --git a/lib/persisted-model.js b/lib/persisted-model.js index 6c298e397..866d23cb8 100644 --- a/lib/persisted-model.js +++ b/lib/persisted-model.js @@ -1474,28 +1474,71 @@ module.exports = function(registry) { buildLookupOfAffectedModelData(Model, updates, function(err, currentMap) { if (err) return callback(err); - updates.forEach(function(update) { + + var deleteIds = []; + var deleteConflicts = []; + + updates.forEach(function (update) { var id = update.change.modelId; var current = currentMap[id]; switch (update.type) { case Change.UPDATE: - tasks.push(function(cb) { - applyUpdate(Model, id, current, update.data, update.change, conflicts, options, cb); + tasks.push(function (cb) { + applyUpdate( + Model, + id, + current, + update.data, + update.change, + conflicts, + options, + cb + ); }); break; case Change.CREATE: - tasks.push(function(cb) { - applyCreate(Model, id, current, update.data, update.change, conflicts, options, cb); + tasks.push(function (cb) { + applyCreate( + Model, + id, + current, + update.data, + update.change, + conflicts, + options, + cb + ); }); break; case Change.DELETE: - tasks.push(function(cb) { - applyDelete(Model, id, current, update.change, conflicts, options, cb); - }); + const conflictId = findDeleteConflicts( + Model, + id, + current, + update.change, + conflicts + ); + if (conflictId) { + deleteConflicts.push(conflictId); + } else { + deleteIds.push(id); + } + break; } }); + if (deleteConflicts.length) { + tasks.push(function (cb) { + rectifyDeleteChanges(Model, deleteConflicts, cb); + }); + } + + if (deleteIds.length) { + tasks.push(function (cb) { + applyDeletes(Model, deleteIds, update.change, conflicts, options, cb); + }); + } async.parallel(tasks, function(err) { if (err) return callback(err); @@ -1619,56 +1662,44 @@ module.exports = function(registry) { } } - function applyDelete(Model, id, current, change, conflicts, options, cb) { + /** + * Returns an id if there is a conflict, otherwise null + * @param {*} Model + * @param {*} id + * @param {*} current + * @param {*} change + * @param {*} conflicts + * @param {*} options + */ + function findDeleteConflicts(Model, id, current, change, conflicts) { if (!current) { // The instance was either already deleted or not created at all, // we are done. - return cb(); + return null; } var Change = Model.getChangeModel(); var rev = Change.revisionForInst(current); if (rev !== change.prev) { - debug('Detected non-rectified change of %s %j', - Model.modelName, id); + debug('Detected non-rectified change of %s %j', Model.modelName, id); debug('\tExpected revision: %s', change.rev); debug('\tActual revision: %s', rev); conflicts.push(change); - return Change.rectifyModelChanges(Model.modelName, [id], cb); + return id; } + } + function rectifyDeleteChanges(Model, ids, cb) { + return Change.rectifyModelChanges(Model.modelName, ids, cb); + } - Model.deleteById(id, options, function(err, result) { + function applyDeletes(Model, ids, change, conflicts, options, cb) { + var idName = this.getIdName(); + var where = {}; + where[idName] = { inq: ids }; + Model.deleteAll(where, options, function (err) { if (err) return cb(err); - - var count = result && result.count; - switch (count) { - case 1: - // The happy path, exactly one record was updated - return cb(); - - case 0: - debug('DeleteAll detected non-rectified change of %s %j', - Model.modelName, id); - conflicts.push(change); - // NOTE(bajtos) deleteAll triggers change rectification - // for all model instances, even when no records were updated, - // thus we don't need to rectify explicitly ourselves - return cb(); - - case undefined: - case null: - return cb(new Error( - g.f('Cannot apply bulk updates, ' + - 'the connector does not correctly report ' + - 'the number of deleted records.'))); - - default: - debug('%s.deleteAll modified unexpected number of instances: %j', - Model.modelName, count); - return cb(new Error( - g.f('Bulk update failed, the connector has deleted unexpected ' + - 'number of records: %s', JSON.stringify(count)))); - } + // all is good. exit gracefully, we deleted as much as we could! + return cb(); }); }