Give warning if PK is changed in hooks

* Give warning if PK is changed in `before save` and `loaded`
 operation hooks for replaceById
This commit is contained in:
Amir Jafarian 2016-05-31 18:10:44 -04:00
parent a55bf40ee9
commit 7a8803cb38
3 changed files with 90 additions and 0 deletions

View File

@ -2671,6 +2671,13 @@ DataAccessObject.replaceById = function(id, data, options, cb) {
Model.notifyObserversOf('before save', context, function(err, ctx) { Model.notifyObserversOf('before save', context, function(err, ctx) {
if (err) return cb(err); if (err) return cb(err);
if (ctx.instance[pkName] !== id && !Model._warned.cannotOverwritePKInBeforeSaveHook) {
Model._warned.cannotOverwritePKInBeforeSaveHook = true;
console.warn('WARNING: id property cannot be changed from ' +
id + ' to ' + inst[pkName] + ' for model:' + Model.modelName +
' in \'before save\' operation hook');
}
data = inst.toObject(false); data = inst.toObject(false);
if (strict) { if (strict) {
@ -2722,7 +2729,15 @@ DataAccessObject.replaceById = function(id, data, options, cb) {
Model.notifyObserversOf('loaded', ctx, function(err) { Model.notifyObserversOf('loaded', ctx, function(err) {
if (err) return cb(err); if (err) return cb(err);
if (ctx.data[pkName] !== id && !Model._warned.cannotOverwritePKInLoadedHook) {
Model._warned.cannotOverwritePKInLoadedHook = true;
console.warn('WARNING: id property cannot be changed from ' +
id + ' to ' + ctx.data[pkName] + ' for model:' + Model.modelName +
' in \'loaded\' operation hook');
}
inst.__persisted = true; inst.__persisted = true;
ctx.data[pkName] = id;
inst.setAttributes(ctx.data); inst.setAttributes(ctx.data);
var context = { var context = {

View File

@ -218,6 +218,7 @@ ModelBuilder.prototype.define = function defineClass(className, properties, sett
hiddenProperty(ModelClass, 'http', { path: pathName }); hiddenProperty(ModelClass, 'http', { path: pathName });
hiddenProperty(ModelClass, 'base', ModelBaseClass); hiddenProperty(ModelClass, 'base', ModelBaseClass);
hiddenProperty(ModelClass, '_observers', {}); hiddenProperty(ModelClass, '_observers', {});
hiddenProperty(ModelClass, '_warned', {});
// inherit ModelBaseClass static methods // inherit ModelBaseClass static methods
for (var i in ModelBaseClass) { for (var i in ModelBaseClass) {

View File

@ -984,6 +984,73 @@ describe('manipulation', function() {
}); });
}); });
it('should ignore PK if it is set for `instance`' +
'in `before save` operation hook', function(done) {
Post.findById(postInstance.id, function(err, p) {
if (err) return done(err);
changePostIdInHook('before save');
p.replaceAttributes({ title: 'b' }, function(err, data) {
data.id.should.eql(postInstance.id);
if (err) return done(err);
Post.find(function(err, p) {
if (err) return done(err);
p[0].id.should.eql(postInstance.id);
done();
});
});
});
});
it('should set cannotOverwritePKInBeforeSaveHook flag, if `instance` in' +
'`before save` operation hook is set, so we report a warning just once',
function(done) {
Post.findById(postInstance.id, function(err, p) {
if (err) return done(err);
changePostIdInHook('before save');
p.replaceAttributes({ title: 'b' }, function(err, data) {
if (err) return done(err);
Post._warned.cannotOverwritePKInBeforeSaveHook.should.equal(true);
data.id.should.equal(postInstance.id);
done();
});
});
});
it('should ignore PK if it is set for `data`' +
'in `loaded` operation hook', function(done) {
Post.findById(postInstance.id, function(err, p) {
if (err) return done(err);
changePostIdInHook('loaded');
p.replaceAttributes({ title: 'b' }, function(err, data) {
data.id.should.eql(postInstance.id);
if (err) return done(err);
// clear observers to make sure `loaded`
// hook does not affect `find()` method
Post.clearObservers('loaded');
Post.find(function(err, p) {
if (err) return done(err);
p[0].id.should.eql(postInstance.id);
done();
});
});
});
});
it('should set cannotOverwritePKInLoadedHook flag, if `instance` in' +
'`before save` operation hook is set, so we report a warning just once',
function(done) {
Post.findById(postInstance.id, function(err, p) {
if (err) return done(err);
changePostIdInHook('loaded');
p.replaceAttributes({ title: 'b' }, function(err, data) {
if (err) return done(err);
Post._warned.cannotOverwritePKInLoadedHook.should.equal(true);
data.id.should.equal(postInstance.id);
done();
});
});
});
it('works without options(promise variant)', function(done) { it('works without options(promise variant)', function(done) {
Post.findById(postInstance.id) Post.findById(postInstance.id)
.then(function(p) { .then(function(p) {
@ -1059,6 +1126,13 @@ describe('manipulation', function() {
}); });
}); });
}); });
function changePostIdInHook(operationHook) {
Post.observe(operationHook, function(ctx, next) {
(ctx.data || ctx.instance).id = 99;
next();
});
}
}); });
} }