Promisify Model Change

* Change.diff
* Change.findOrCreateChange
* Change.rectifyModelChanges
* Change.prototype.currentRevision
* Change.prototype.rectify
This commit is contained in:
Jue Hou 2016-01-27 14:42:47 -05:00
parent 524058d8fc
commit d26d6ff3ed
2 changed files with 126 additions and 4 deletions

View File

@ -4,6 +4,7 @@
var PersistedModel = require('../../lib/loopback').PersistedModel; var PersistedModel = require('../../lib/loopback').PersistedModel;
var loopback = require('../../lib/loopback'); var loopback = require('../../lib/loopback');
var utils = require('../../lib/utils');
var crypto = require('crypto'); var crypto = require('crypto');
var CJSON = {stringify: require('canonical-json')}; var CJSON = {stringify: require('canonical-json')};
var async = require('async'); var async = require('async');
@ -77,6 +78,8 @@ module.exports = function(Change) {
var Change = this; var Change = this;
var errors = []; var errors = [];
callback = callback || utils.createPromiseCallback();
var tasks = modelIds.map(function(id) { var tasks = modelIds.map(function(id) {
return function(cb) { return function(cb) {
Change.findOrCreateChange(modelName, id, function(err, change) { Change.findOrCreateChange(modelName, id, function(err, change) {
@ -111,6 +114,7 @@ module.exports = function(Change) {
} }
callback(); callback();
}); });
return callback.promise;
}; };
/** /**
@ -138,6 +142,7 @@ module.exports = function(Change) {
Change.findOrCreateChange = function(modelName, modelId, callback) { Change.findOrCreateChange = function(modelName, modelId, callback) {
assert(loopback.findModel(modelName), modelName + ' does not exist'); assert(loopback.findModel(modelName), modelName + ' does not exist');
callback = callback || utils.createPromiseCallback();
var id = this.idForModel(modelName, modelId); var id = this.idForModel(modelName, modelId);
var Change = this; var Change = this;
@ -155,6 +160,7 @@ module.exports = function(Change) {
Change.updateOrCreate(ch, callback); Change.updateOrCreate(ch, callback);
} }
}); });
return callback.promise;
}; };
/** /**
@ -171,9 +177,7 @@ module.exports = function(Change) {
change.debug('rectify change'); change.debug('rectify change');
cb = cb || function(err) { cb = cb || utils.createPromiseCallback();
if (err) throw new Error(err);
};
change.currentRevision(function(err, rev) { change.currentRevision(function(err, rev) {
if (err) return cb(err); if (err) return cb(err);
@ -194,6 +198,7 @@ module.exports = function(Change) {
} }
); );
}); });
return cb.promise;
function doRectify(checkpoint, rev) { function doRectify(checkpoint, rev) {
if (rev) { if (rev) {
@ -248,6 +253,7 @@ module.exports = function(Change) {
*/ */
Change.prototype.currentRevision = function(cb) { Change.prototype.currentRevision = function(cb) {
cb = cb || utils.createPromiseCallback();
var model = this.getModelCtor(); var model = this.getModelCtor();
var id = this.getModelId(); var id = this.getModelId();
model.findById(id, function(err, inst) { model.findById(id, function(err, inst) {
@ -258,6 +264,7 @@ module.exports = function(Change) {
cb(null, null); cb(null, null);
} }
}); });
return cb.promise;
}; };
/** /**
@ -390,8 +397,11 @@ module.exports = function(Change) {
*/ */
Change.diff = function(modelName, since, remoteChanges, callback) { Change.diff = function(modelName, since, remoteChanges, callback) {
callback = callback || utils.createPromiseCallback();
if (!Array.isArray(remoteChanges) || remoteChanges.length === 0) { if (!Array.isArray(remoteChanges) || remoteChanges.length === 0) {
return callback(null, {deltas: [], conflicts: []}); callback(null, {deltas: [], conflicts: []});
return callback.promise;
} }
var remoteChangeIndex = {}; var remoteChangeIndex = {};
var modelIds = []; var modelIds = [];
@ -455,6 +465,7 @@ module.exports = function(Change) {
conflicts: conflicts conflicts: conflicts
}); });
}); });
return callback.promise;
}; };
/** /**

View File

@ -82,6 +82,38 @@ describe('Change', function() {
}); });
}); });
describe('Change.rectifyModelChanges - promise variant', function() {
describe('using an existing untracked model', function() {
beforeEach(function(done) {
var test = this;
Change.rectifyModelChanges(this.modelName, [this.modelId])
.then(function(trackedChanges) {
done();
})
.catch(done);
});
it('should create an entry', function(done) {
var test = this;
Change.find()
.then(function(trackedChanges) {
assert.equal(trackedChanges[0].modelId, test.modelId.toString());
done();
})
.catch(done);
});
it('should only create one change', function(done) {
Change.count()
.then(function(count) {
assert.equal(count, 1);
done();
})
.catch(done);
});
});
});
describe('Change.findOrCreateChange(modelName, modelId, callback)', function() { describe('Change.findOrCreateChange(modelName, modelId, callback)', function() {
describe('when a change doesnt exist', function() { describe('when a change doesnt exist', function() {
@ -104,6 +136,27 @@ describe('Change', function() {
}); });
}); });
describe('when a change doesnt exist - promise variant', function() {
beforeEach(function(done) {
var test = this;
Change.findOrCreateChange(this.modelName, this.modelId)
.then(function(result) {
test.result = result;
done();
})
.catch(done);
});
it('should create an entry', function(done) {
var test = this;
Change.findById(this.result.id, function(err, change) {
if (err) return done(err);
assert.equal(change.id, test.result.id);
done();
});
});
});
describe('when a change does exist', function() { describe('when a change does exist', function() {
beforeEach(function(done) { beforeEach(function(done) {
var test = this; var test = this;
@ -219,6 +272,28 @@ describe('Change', function() {
}); });
}); });
describe('change.rectify - promise variant', function() {
var change;
beforeEach(function(done) {
Change.findOrCreateChange(this.modelName, this.modelId)
.then(function(ch) {
change = ch;
done();
})
.catch(done);
});
it('should create a new change with the correct revision', function(done) {
var test = this;
change.rectify()
.then(function(ch) {
assert.equal(ch.rev, test.revisionForModel);
done();
})
.catch(done);
});
});
describe('change.currentRevision(callback)', function() { describe('change.currentRevision(callback)', function() {
it('should get the correct revision', function(done) { it('should get the correct revision', function(done) {
var test = this; var test = this;
@ -234,6 +309,23 @@ describe('Change', function() {
}); });
}); });
describe('change.currentRevision - promise variant', function() {
it('should get the correct revision', function(done) {
var test = this;
var change = new Change({
modelName: this.modelName,
modelId: this.modelId
});
change.currentRevision()
.then(function(rev) {
assert.equal(rev, test.revisionForModel);
done();
})
.catch(done);
});
});
describe('Change.hash(str)', function() { describe('Change.hash(str)', function() {
// todo(ritch) test other hashing algorithms // todo(ritch) test other hashing algorithms
it('should hash the given string', function() { it('should hash the given string', function() {
@ -374,6 +466,25 @@ describe('Change', function() {
}); });
}); });
it('should return delta and conflict lists - promise variant', function(done) {
var remoteChanges = [
// an update => should result in a delta
{rev: 'foo2', prev: 'foo', modelName: this.modelName, modelId: 9, checkpoint: 1},
// no change => should not result in a delta / conflict
{rev: 'bar', prev: 'bar', modelName: this.modelName, modelId: 10, checkpoint: 1},
// a conflict => should result in a conflict
{rev: 'bat2', prev: 'bat0', modelName: this.modelName, modelId: 11, checkpoint: 1},
];
Change.diff(this.modelName, 0, remoteChanges)
.then(function(diff) {
assert.equal(diff.deltas.length, 1);
assert.equal(diff.conflicts.length, 1);
done();
})
.catch(done);
});
it('should set "prev" to local revision in non-conflicting delta', function(done) { it('should set "prev" to local revision in non-conflicting delta', function(done) {
var updateRecord = { var updateRecord = {
rev: 'foo-new', rev: 'foo-new',