Fix Operation Hooks to propagate data changes
Before this change, many Operation Hooks were not correctly propagating changes made to `ctx.data` via reassigning ctx.data to a new object. This change modifies existing tests to account for this different scenario, adds few more tests for scenarios that were not covered by tests before and finally fixes the problem discovered.
This commit is contained in:
parent
fa0039c6a8
commit
9af79cf51a
21
lib/dao.js
21
lib/dao.js
|
@ -467,9 +467,10 @@ DataAccessObject.create = function(data, options, cb) {
|
||||||
hookState: hookState,
|
hookState: hookState,
|
||||||
options: options,
|
options: options,
|
||||||
};
|
};
|
||||||
Model.notifyObserversOf('persist', context, function(err) {
|
Model.notifyObserversOf('persist', context, function(err, ctx) {
|
||||||
if (err) return cb(err);
|
if (err) return cb(err);
|
||||||
invokeConnectorMethod(connector, 'create', Model, [obj.constructor._forDB(context.data)],
|
val = ctx.data;
|
||||||
|
invokeConnectorMethod(connector, 'create', Model, [obj.constructor._forDB(ctx.data)],
|
||||||
options, createCallback);
|
options, createCallback);
|
||||||
});
|
});
|
||||||
}, obj, cb);
|
}, obj, cb);
|
||||||
|
@ -1131,9 +1132,10 @@ DataAccessObject.findOrCreate = function findOrCreate(query, data, options, cb)
|
||||||
hookState: hookState,
|
hookState: hookState,
|
||||||
options: options,
|
options: options,
|
||||||
};
|
};
|
||||||
Model.notifyObserversOf('loaded', context, function(err) {
|
Model.notifyObserversOf('loaded', context, function(err, ctx) {
|
||||||
if (err) return cb(err);
|
if (err) return cb(err);
|
||||||
|
|
||||||
|
data = ctx.data;
|
||||||
var obj, Model = self.lookupModel(data);
|
var obj, Model = self.lookupModel(data);
|
||||||
|
|
||||||
if (data) {
|
if (data) {
|
||||||
|
@ -2547,9 +2549,10 @@ DataAccessObject.prototype.save = function(options, cb) {
|
||||||
hookState: hookState,
|
hookState: hookState,
|
||||||
options: options,
|
options: options,
|
||||||
};
|
};
|
||||||
Model.notifyObserversOf('loaded', context, function(err) {
|
Model.notifyObserversOf('loaded', context, function(err, ctx) {
|
||||||
if (err) return cb(err);
|
if (err) return cb(err);
|
||||||
|
|
||||||
|
data = ctx.data;
|
||||||
inst._initProperties(data, {persisted: true});
|
inst._initProperties(data, {persisted: true});
|
||||||
|
|
||||||
var context = {
|
var context = {
|
||||||
|
@ -2579,8 +2582,9 @@ DataAccessObject.prototype.save = function(options, cb) {
|
||||||
options: options,
|
options: options,
|
||||||
};
|
};
|
||||||
|
|
||||||
Model.notifyObserversOf('persist', context, function(err) {
|
Model.notifyObserversOf('persist', context, function(err, ctx) {
|
||||||
if (err) return cb(err);
|
if (err) return cb(err);
|
||||||
|
data = ctx.data;
|
||||||
invokeConnectorMethod(connector, 'save', Model, [Model._forDB(data)],
|
invokeConnectorMethod(connector, 'save', Model, [Model._forDB(data)],
|
||||||
options, saveCallback);
|
options, saveCallback);
|
||||||
});
|
});
|
||||||
|
@ -2756,6 +2760,7 @@ DataAccessObject.updateAll = function(where, data, options, cb) {
|
||||||
};
|
};
|
||||||
Model.notifyObserversOf('persist', context, function(err, ctx) {
|
Model.notifyObserversOf('persist', context, function(err, ctx) {
|
||||||
if (err) return cb(err);
|
if (err) return cb(err);
|
||||||
|
data = ctx.data;
|
||||||
invokeConnectorMethod(connector, 'update', Model, [where, data], options, updateCallback);
|
invokeConnectorMethod(connector, 'update', Model, [where, data], options, updateCallback);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -3084,6 +3089,7 @@ DataAccessObject.replaceById = function(id, data, options, cb) {
|
||||||
|
|
||||||
function replaceCallback(err, data) {
|
function replaceCallback(err, data) {
|
||||||
if (err) return cb(err);
|
if (err) return cb(err);
|
||||||
|
context.data = data;
|
||||||
if (typeof connector.generateContextData === 'function') {
|
if (typeof connector.generateContextData === 'function') {
|
||||||
context = connector.generateContextData(context, data);
|
context = connector.generateContextData(context, data);
|
||||||
}
|
}
|
||||||
|
@ -3132,7 +3138,7 @@ DataAccessObject.replaceById = function(id, data, options, cb) {
|
||||||
};
|
};
|
||||||
Model.notifyObserversOf('persist', ctx, function(err) {
|
Model.notifyObserversOf('persist', ctx, function(err) {
|
||||||
if (err) return cb(err);
|
if (err) return cb(err);
|
||||||
invokeConnectorMethod(connector, 'replaceById', Model, [id, Model._forDB(context.data)],
|
invokeConnectorMethod(connector, 'replaceById', Model, [id, Model._forDB(ctx.data)],
|
||||||
options, replaceCallback);
|
options, replaceCallback);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -3277,6 +3283,7 @@ function(data, options, cb) {
|
||||||
|
|
||||||
function updateAttributesCallback(err, data) {
|
function updateAttributesCallback(err, data) {
|
||||||
if (err) return cb(err);
|
if (err) return cb(err);
|
||||||
|
context.data = data;
|
||||||
if (typeof connector.generateContextData === 'function') {
|
if (typeof connector.generateContextData === 'function') {
|
||||||
context = connector.generateContextData(context, data);
|
context = connector.generateContextData(context, data);
|
||||||
}
|
}
|
||||||
|
@ -3330,7 +3337,7 @@ function(data, options, cb) {
|
||||||
Model.notifyObserversOf('persist', ctx, function(err) {
|
Model.notifyObserversOf('persist', ctx, function(err) {
|
||||||
if (err) return cb(err);
|
if (err) return cb(err);
|
||||||
invokeConnectorMethod(connector, 'updateAttributes', Model,
|
invokeConnectorMethod(connector, 'updateAttributes', Model,
|
||||||
[getIdValue(Model, inst), Model._forDB(context.data)],
|
[getIdValue(Model, inst), Model._forDB(ctx.data)],
|
||||||
options, updateAttributesCallback);
|
options, updateAttributesCallback);
|
||||||
});
|
});
|
||||||
}, data, cb);
|
}, data, cb);
|
||||||
|
|
|
@ -163,7 +163,8 @@ module.exports = function(dataSource, should, connectorCapabilities) {
|
||||||
|
|
||||||
it('applies updates from `loaded` hook when near filter is used', function(done) {
|
it('applies updates from `loaded` hook when near filter is used', function(done) {
|
||||||
GeoModel.observe('loaded', function(ctx, next) {
|
GeoModel.observe('loaded', function(ctx, next) {
|
||||||
ctx.data.name = 'Berlin';
|
// It's crucial to change `ctx.data` reference, not only data props
|
||||||
|
ctx.data = Object.assign({}, ctx.data, {name: 'Berlin'});
|
||||||
next();
|
next();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -181,8 +182,10 @@ module.exports = function(dataSource, should, connectorCapabilities) {
|
||||||
it('applies updates to one specific instance from `loaded` hook when near filter is used',
|
it('applies updates to one specific instance from `loaded` hook when near filter is used',
|
||||||
function(done) {
|
function(done) {
|
||||||
GeoModel.observe('loaded', function(ctx, next) {
|
GeoModel.observe('loaded', function(ctx, next) {
|
||||||
if (ctx.data.name === 'Rome')
|
if (ctx.data.name === 'Rome') {
|
||||||
ctx.data.name = 'Berlin';
|
// It's crucial to change `ctx.data` reference, not only data props
|
||||||
|
ctx.data = Object.assign({}, ctx.data, {name: 'Berlin'});
|
||||||
|
}
|
||||||
next();
|
next();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -199,7 +202,8 @@ module.exports = function(dataSource, should, connectorCapabilities) {
|
||||||
|
|
||||||
it('applies updates from `loaded` hook when near filter is not used', function(done) {
|
it('applies updates from `loaded` hook when near filter is not used', function(done) {
|
||||||
TestModel.observe('loaded', function(ctx, next) {
|
TestModel.observe('loaded', function(ctx, next) {
|
||||||
ctx.data.name = 'Paris';
|
// It's crucial to change `ctx.data` reference, not only data props
|
||||||
|
ctx.data = Object.assign({}, ctx.data, {name: 'Paris'});
|
||||||
next();
|
next();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -213,8 +217,10 @@ module.exports = function(dataSource, should, connectorCapabilities) {
|
||||||
it('applies updates to one specific instance from `loaded` hook when near filter is not used',
|
it('applies updates to one specific instance from `loaded` hook when near filter is not used',
|
||||||
function(done) {
|
function(done) {
|
||||||
TestModel.observe('loaded', function(ctx, next) {
|
TestModel.observe('loaded', function(ctx, next) {
|
||||||
if (ctx.data.name === 'first')
|
if (ctx.data.name === 'first') {
|
||||||
ctx.data.name = 'Paris';
|
// It's crucial to change `ctx.data` reference, not only data props
|
||||||
|
ctx.data = Object.assign({}, ctx.data, {name: 'Paris'});
|
||||||
|
}
|
||||||
next();
|
next();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -313,7 +319,8 @@ module.exports = function(dataSource, should, connectorCapabilities) {
|
||||||
|
|
||||||
it('applies updates from `loaded` hook', function(done) {
|
it('applies updates from `loaded` hook', function(done) {
|
||||||
TestModel.observe('loaded', ctxRecorder.recordAndNext(function(ctx) {
|
TestModel.observe('loaded', ctxRecorder.recordAndNext(function(ctx) {
|
||||||
ctx.data.extra = 'hook data';
|
// It's crucial to change `ctx.data` reference, not only data props
|
||||||
|
ctx.data = Object.assign({}, ctx.data, {extra: 'hook data'});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
TestModel.find(
|
TestModel.find(
|
||||||
|
@ -472,7 +479,8 @@ module.exports = function(dataSource, should, connectorCapabilities) {
|
||||||
|
|
||||||
it('applies updates from `persist` hook', function(done) {
|
it('applies updates from `persist` hook', function(done) {
|
||||||
TestModel.observe('persist', ctxRecorder.recordAndNext(function(ctx) {
|
TestModel.observe('persist', ctxRecorder.recordAndNext(function(ctx) {
|
||||||
ctx.data.extra = 'hook data';
|
// It's crucial to change `ctx.data` reference, not only data props
|
||||||
|
ctx.data = Object.assign({}, ctx.data, {extra: 'hook data'});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// By default, the instance passed to create callback is NOT updated
|
// By default, the instance passed to create callback is NOT updated
|
||||||
|
@ -536,7 +544,8 @@ module.exports = function(dataSource, should, connectorCapabilities) {
|
||||||
|
|
||||||
it('applies updates from `loaded` hook', function(done) {
|
it('applies updates from `loaded` hook', function(done) {
|
||||||
TestModel.observe('loaded', ctxRecorder.recordAndNext(function(ctx) {
|
TestModel.observe('loaded', ctxRecorder.recordAndNext(function(ctx) {
|
||||||
ctx.data.extra = 'hook data';
|
// It's crucial to change `ctx.data` reference, not only data props
|
||||||
|
ctx.data = Object.assign({}, ctx.data, {extra: 'hook data'});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// By default, the instance passed to create callback is NOT updated
|
// By default, the instance passed to create callback is NOT updated
|
||||||
|
@ -872,7 +881,8 @@ module.exports = function(dataSource, should, connectorCapabilities) {
|
||||||
if (dataSource.connector.findOrCreate) {
|
if (dataSource.connector.findOrCreate) {
|
||||||
it('applies updates from `persist` hook when found', function(done) {
|
it('applies updates from `persist` hook when found', function(done) {
|
||||||
TestModel.observe('persist', ctxRecorder.recordAndNext(function(ctx) {
|
TestModel.observe('persist', ctxRecorder.recordAndNext(function(ctx) {
|
||||||
ctx.data.extra = 'hook data';
|
// It's crucial to change `ctx.data` reference, not only data props
|
||||||
|
ctx.data = Object.assign({}, ctx.data, {extra: 'hook data'});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
TestModel.findOrCreate(
|
TestModel.findOrCreate(
|
||||||
|
@ -905,7 +915,8 @@ module.exports = function(dataSource, should, connectorCapabilities) {
|
||||||
|
|
||||||
it('applies updates from `persist` hook when not found', function(done) {
|
it('applies updates from `persist` hook when not found', function(done) {
|
||||||
TestModel.observe('persist', ctxRecorder.recordAndNext(function(ctx) {
|
TestModel.observe('persist', ctxRecorder.recordAndNext(function(ctx) {
|
||||||
ctx.data.extra = 'hook data';
|
// It's crucial to change `ctx.data` reference, not only data props
|
||||||
|
ctx.data = Object.assign({}, ctx.data, {extra: 'hook data'});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
TestModel.findOrCreate(
|
TestModel.findOrCreate(
|
||||||
|
@ -1003,7 +1014,8 @@ module.exports = function(dataSource, should, connectorCapabilities) {
|
||||||
if (dataSource.connector.findOrCreate) {
|
if (dataSource.connector.findOrCreate) {
|
||||||
it('applies updates from `loaded` hook when found', function(done) {
|
it('applies updates from `loaded` hook when found', function(done) {
|
||||||
TestModel.observe('loaded', ctxRecorder.recordAndNext(function(ctx) {
|
TestModel.observe('loaded', ctxRecorder.recordAndNext(function(ctx) {
|
||||||
ctx.data.extra = 'hook data';
|
// It's crucial to change `ctx.data` reference, not only data props
|
||||||
|
ctx.data = Object.assign({}, ctx.data, {extra: 'hook data'});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
TestModel.findOrCreate(
|
TestModel.findOrCreate(
|
||||||
|
@ -1021,7 +1033,8 @@ module.exports = function(dataSource, should, connectorCapabilities) {
|
||||||
|
|
||||||
it('applies updates from `loaded` hook when not found', function(done) {
|
it('applies updates from `loaded` hook when not found', function(done) {
|
||||||
TestModel.observe('loaded', ctxRecorder.recordAndNext(function(ctx) {
|
TestModel.observe('loaded', ctxRecorder.recordAndNext(function(ctx) {
|
||||||
ctx.data.extra = 'hook data';
|
// It's crucial to change `ctx.data` reference, not only data props
|
||||||
|
ctx.data = Object.assign({}, ctx.data, {extra: 'hook data'});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// Unoptimized connector gives a call to `create. But,
|
// Unoptimized connector gives a call to `create. But,
|
||||||
|
@ -1198,7 +1211,8 @@ module.exports = function(dataSource, should, connectorCapabilities) {
|
||||||
|
|
||||||
it('applies updates from `persist` hook', function(done) {
|
it('applies updates from `persist` hook', function(done) {
|
||||||
TestModel.observe('persist', ctxRecorder.recordAndNext(function(ctx) {
|
TestModel.observe('persist', ctxRecorder.recordAndNext(function(ctx) {
|
||||||
ctx.data.extra = 'hook data';
|
// It's crucial to change `ctx.data` reference, not only data props
|
||||||
|
ctx.data = Object.assign({}, ctx.data, {extra: 'hook data'});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
existingInstance.save(function(err, instance) {
|
existingInstance.save(function(err, instance) {
|
||||||
|
@ -1240,7 +1254,8 @@ module.exports = function(dataSource, should, connectorCapabilities) {
|
||||||
|
|
||||||
it('applies updates from `loaded` hook', function(done) {
|
it('applies updates from `loaded` hook', function(done) {
|
||||||
TestModel.observe('loaded', ctxRecorder.recordAndNext(function(ctx) {
|
TestModel.observe('loaded', ctxRecorder.recordAndNext(function(ctx) {
|
||||||
ctx.data.extra = 'hook data';
|
// It's crucial to change `ctx.data` reference, not only data props
|
||||||
|
ctx.data = Object.assign({}, ctx.data, {extra: 'hook data'});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
existingInstance.save(function(err, instance) {
|
existingInstance.save(function(err, instance) {
|
||||||
|
@ -1364,8 +1379,11 @@ module.exports = function(dataSource, should, connectorCapabilities) {
|
||||||
|
|
||||||
it('applies updates from `before save` hook', function(done) {
|
it('applies updates from `before save` hook', function(done) {
|
||||||
TestModel.observe('before save', function(ctx, next) {
|
TestModel.observe('before save', function(ctx, next) {
|
||||||
ctx.data.extra = 'extra data';
|
// It's crucial to change `ctx.data` reference, not only data props
|
||||||
ctx.data.name = 'hooked name';
|
ctx.data = Object.assign({}, ctx.data, {
|
||||||
|
extra: 'extra data',
|
||||||
|
name: 'hooked name',
|
||||||
|
});
|
||||||
next();
|
next();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1418,7 +1436,8 @@ module.exports = function(dataSource, should, connectorCapabilities) {
|
||||||
|
|
||||||
it('applies updates from `persist` hook', function(done) {
|
it('applies updates from `persist` hook', function(done) {
|
||||||
TestModel.observe('persist', ctxRecorder.recordAndNext(function(ctx) {
|
TestModel.observe('persist', ctxRecorder.recordAndNext(function(ctx) {
|
||||||
ctx.data.extra = 'hook data';
|
// It's crucial to change `ctx.data` reference, not only data props
|
||||||
|
ctx.data = Object.assign({}, ctx.data, {extra: 'hook data'});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// By default, the instance passed to updateAttributes callback is NOT updated
|
// By default, the instance passed to updateAttributes callback is NOT updated
|
||||||
|
@ -1429,7 +1448,11 @@ module.exports = function(dataSource, should, connectorCapabilities) {
|
||||||
existingInstance.updateAttributes({name: 'changed'}, function(err, instance) {
|
existingInstance.updateAttributes({name: 'changed'}, function(err, instance) {
|
||||||
if (err) return done(err);
|
if (err) return done(err);
|
||||||
instance.should.have.property('extra', 'hook data');
|
instance.should.have.property('extra', 'hook data');
|
||||||
done();
|
TestModel.findById(existingInstance.id, (err, found) => {
|
||||||
|
if (err) return done(err);
|
||||||
|
found.should.have.property('extra', 'hook data');
|
||||||
|
done();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1459,7 +1482,8 @@ module.exports = function(dataSource, should, connectorCapabilities) {
|
||||||
ctx.data.address.should.be.type('object');
|
ctx.data.address.should.be.type('object');
|
||||||
ctx.data.address.should.not.be.instanceOf(Address);
|
ctx.data.address.should.not.be.instanceOf(Address);
|
||||||
|
|
||||||
ctx.data.extra = 'hook data';
|
// It's crucial to change `ctx.data` reference, not only data props
|
||||||
|
ctx.data = Object.assign({}, ctx.data, {extra: 'hook data'});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// By default, the instance passed to updateAttributes callback is NOT updated
|
// By default, the instance passed to updateAttributes callback is NOT updated
|
||||||
|
@ -1505,7 +1529,7 @@ module.exports = function(dataSource, should, connectorCapabilities) {
|
||||||
if (err) return done(err);
|
if (err) return done(err);
|
||||||
|
|
||||||
ctxRecorder.records.should.eql(aCtxForModel(TestModel, {
|
ctxRecorder.records.should.eql(aCtxForModel(TestModel, {
|
||||||
data: {name: 'changed'},
|
data: {id: existingInstance.id, name: 'changed'},
|
||||||
isNewInstance: false,
|
isNewInstance: false,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
@ -1525,7 +1549,8 @@ module.exports = function(dataSource, should, connectorCapabilities) {
|
||||||
|
|
||||||
it('applies updates from `loaded` hook updateAttributes', function(done) {
|
it('applies updates from `loaded` hook updateAttributes', function(done) {
|
||||||
TestModel.observe('loaded', ctxRecorder.recordAndNext(function(ctx) {
|
TestModel.observe('loaded', ctxRecorder.recordAndNext(function(ctx) {
|
||||||
ctx.data.extra = 'hook data';
|
// It's crucial to change `ctx.data` reference, not only data props
|
||||||
|
ctx.data = Object.assign({}, ctx.data, {extra: 'hook data'});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// By default, the instance passed to updateAttributes callback is NOT updated
|
// By default, the instance passed to updateAttributes callback is NOT updated
|
||||||
|
@ -1583,7 +1608,7 @@ module.exports = function(dataSource, should, connectorCapabilities) {
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!dataSource.connector.replaceById) {
|
if (!dataSource.connector.replaceById) {
|
||||||
describe.skip('replaceById - not implemented', function() {});
|
describe.skip('replaceAttributes - not implemented', function() {});
|
||||||
} else {
|
} else {
|
||||||
describe('PersistedModel.prototype.replaceAttributes', function() {
|
describe('PersistedModel.prototype.replaceAttributes', function() {
|
||||||
it('triggers hooks in the correct order', function(done) {
|
it('triggers hooks in the correct order', function(done) {
|
||||||
|
@ -1722,7 +1747,8 @@ module.exports = function(dataSource, should, connectorCapabilities) {
|
||||||
ctx.data.address.should.be.type('object');
|
ctx.data.address.should.be.type('object');
|
||||||
ctx.data.address.should.not.be.instanceOf(Address);
|
ctx.data.address.should.not.be.instanceOf(Address);
|
||||||
|
|
||||||
ctx.data.extra = 'hook data';
|
// It's crucial to change `ctx.data` reference, not only data props
|
||||||
|
ctx.data = Object.assign({}, ctx.data, {extra: 'hook data'});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
existingUser.replaceAttributes(
|
existingUser.replaceAttributes(
|
||||||
|
@ -1775,7 +1801,8 @@ module.exports = function(dataSource, should, connectorCapabilities) {
|
||||||
|
|
||||||
it('applies updates from `loaded` hook replaceAttributes', function(done) {
|
it('applies updates from `loaded` hook replaceAttributes', function(done) {
|
||||||
TestModel.observe('loaded', ctxRecorder.recordAndNext(function(ctx) {
|
TestModel.observe('loaded', ctxRecorder.recordAndNext(function(ctx) {
|
||||||
ctx.data.name = 'changed in hook';
|
// It's crucial to change `ctx.data` reference, not only data props
|
||||||
|
ctx.data = Object.assign({}, ctx.data, {name: 'changed in hook'});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
existingInstance.replaceAttributes({name: 'changed'}, function(err, instance) {
|
existingInstance.replaceAttributes({name: 'changed'}, function(err, instance) {
|
||||||
|
@ -2025,7 +2052,8 @@ module.exports = function(dataSource, should, connectorCapabilities) {
|
||||||
|
|
||||||
it('applies updates from `before save` hook on update', function(done) {
|
it('applies updates from `before save` hook on update', function(done) {
|
||||||
TestModel.observe('before save', function(ctx, next) {
|
TestModel.observe('before save', function(ctx, next) {
|
||||||
ctx.data.name = 'hooked';
|
// It's crucial to change `ctx.data` reference, not only data props
|
||||||
|
ctx.data = Object.assign({}, ctx.data, {name: 'hooked'});
|
||||||
next();
|
next();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -2043,7 +2071,8 @@ module.exports = function(dataSource, should, connectorCapabilities) {
|
||||||
if (ctx.instance) {
|
if (ctx.instance) {
|
||||||
ctx.instance.name = 'hooked';
|
ctx.instance.name = 'hooked';
|
||||||
} else {
|
} else {
|
||||||
ctx.data.name = 'hooked';
|
// It's crucial to change `ctx.data` reference, not only data props
|
||||||
|
ctx.data = Object.assign({}, ctx.data, {name: 'hooked'});
|
||||||
}
|
}
|
||||||
next();
|
next();
|
||||||
});
|
});
|
||||||
|
@ -2556,6 +2585,53 @@ module.exports = function(dataSource, should, connectorCapabilities) {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('applies updates from `persist` hook on create', function(done) {
|
||||||
|
TestModel.observe('persist', (ctx, next) => {
|
||||||
|
// it's crucial to change `ctx.data` reference, not only data props
|
||||||
|
ctx.data = Object.assign({}, ctx.data, {extra: 'hook data'});
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
|
||||||
|
// By default, the instance passed to create callback is NOT updated
|
||||||
|
// with the changes made through persist/loaded hooks. To preserve
|
||||||
|
// backwards compatibility, we introduced a new setting updateOnLoad,
|
||||||
|
// which if set, will apply these changes to the model instance too.
|
||||||
|
TestModel.settings.updateOnLoad = true;
|
||||||
|
|
||||||
|
TestModel.replaceOrCreate(
|
||||||
|
{name: 'a name'},
|
||||||
|
function(err, instance) {
|
||||||
|
if (err) return done(err);
|
||||||
|
instance.should.have.property('extra', 'hook data');
|
||||||
|
TestModel.findById(instance.id, (err, found) => {
|
||||||
|
if (err) return done(err);
|
||||||
|
found.should.have.property('extra', 'hook data');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('applies updates from `persist` hook on update', function(done) {
|
||||||
|
TestModel.observe('persist', (ctx, next) => {
|
||||||
|
// It's crucial to change `ctx.data` reference, not only data props
|
||||||
|
ctx.data = Object.assign({}, ctx.data, {extra: 'hook data'});
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
|
||||||
|
existingInstance.name = 'changed';
|
||||||
|
const data = existingInstance.toObject();
|
||||||
|
|
||||||
|
TestModel.replaceOrCreate(data, function(err, instance) {
|
||||||
|
if (err) return done(err);
|
||||||
|
instance.should.have.property('extra', 'hook data');
|
||||||
|
TestModel.findById(existingInstance.id, (err, found) => {
|
||||||
|
if (err) return done(err);
|
||||||
|
found.should.have.property('extra', 'hook data');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('triggers `loaded` hook on create', function(done) {
|
it('triggers `loaded` hook on create', function(done) {
|
||||||
TestModel.observe('loaded', ctxRecorder.recordAndNext());
|
TestModel.observe('loaded', ctxRecorder.recordAndNext());
|
||||||
|
|
||||||
|
@ -2665,6 +2741,83 @@ module.exports = function(dataSource, should, connectorCapabilities) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!dataSource.connector.replaceById) {
|
||||||
|
describe.skip('replaceById - not implemented', function() {});
|
||||||
|
} else {
|
||||||
|
describe('PersistedModel.replaceById', function() {
|
||||||
|
it('triggers hooks in the correct order on create', function(done) {
|
||||||
|
monitorHookExecution();
|
||||||
|
|
||||||
|
existingInstance.name = 'replaced name';
|
||||||
|
TestModel.replaceById(
|
||||||
|
existingInstance.id,
|
||||||
|
existingInstance.toObject(),
|
||||||
|
function(err, record, created) {
|
||||||
|
if (err) return done(err);
|
||||||
|
hookMonitor.names.should.eql([
|
||||||
|
'before save',
|
||||||
|
'persist',
|
||||||
|
'loaded',
|
||||||
|
'after save',
|
||||||
|
]);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('triggers `persist` hook', function(done) {
|
||||||
|
TestModel.observe('persist', ctxRecorder.recordAndNext());
|
||||||
|
|
||||||
|
existingInstance.name = 'replaced name';
|
||||||
|
TestModel.replaceById(
|
||||||
|
existingInstance.id,
|
||||||
|
existingInstance.toObject(),
|
||||||
|
function(err, instance) {
|
||||||
|
if (err) return done(err);
|
||||||
|
|
||||||
|
const expected = {
|
||||||
|
where: {id: existingInstance.id},
|
||||||
|
data: {
|
||||||
|
id: existingInstance.id,
|
||||||
|
name: 'replaced name',
|
||||||
|
},
|
||||||
|
currentInstance: {
|
||||||
|
id: existingInstance.id,
|
||||||
|
name: 'replaced name',
|
||||||
|
extra: undefined,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const expectedContext = aCtxForModel(TestModel, expected);
|
||||||
|
expectedContext.isNewInstance = false;
|
||||||
|
|
||||||
|
ctxRecorder.records.should.eql(expectedContext);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('applies updates from `persist` hook', function(done) {
|
||||||
|
TestModel.observe('persist', ctxRecorder.recordAndNext(function(ctx) {
|
||||||
|
// It's crucial to change `ctx.data` reference, not only data props
|
||||||
|
ctx.data = Object.assign({}, ctx.data, {extra: 'hook data'});
|
||||||
|
}));
|
||||||
|
|
||||||
|
existingInstance.name = 'changed';
|
||||||
|
TestModel.replaceById(
|
||||||
|
existingInstance.id,
|
||||||
|
existingInstance.toObject(),
|
||||||
|
function(err, instance) {
|
||||||
|
if (err) return done(err);
|
||||||
|
instance.should.have.property('extra', 'hook data');
|
||||||
|
TestModel.findById(existingInstance.id, (err, found) => {
|
||||||
|
if (err) return done(err);
|
||||||
|
found.should.have.property('extra', 'hook data');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
describe('PersistedModel.deleteAll', function() {
|
describe('PersistedModel.deleteAll', function() {
|
||||||
it('triggers `access` hook with query', function(done) {
|
it('triggers `access` hook with query', function(done) {
|
||||||
TestModel.observe('access', ctxRecorder.recordAndNext());
|
TestModel.observe('access', ctxRecorder.recordAndNext());
|
||||||
|
@ -3037,7 +3190,8 @@ module.exports = function(dataSource, should, connectorCapabilities) {
|
||||||
|
|
||||||
it('applies updates from `persist` hook', function(done) {
|
it('applies updates from `persist` hook', function(done) {
|
||||||
TestModel.observe('persist', ctxRecorder.recordAndNext(function(ctx) {
|
TestModel.observe('persist', ctxRecorder.recordAndNext(function(ctx) {
|
||||||
ctx.data.extra = 'hook data';
|
// It's crucial to change `ctx.data` reference, not only data props
|
||||||
|
ctx.data = Object.assign({}, ctx.data, {extra: 'hook data'});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
TestModel.updateAll(
|
TestModel.updateAll(
|
||||||
|
@ -3282,7 +3436,8 @@ module.exports = function(dataSource, should, connectorCapabilities) {
|
||||||
|
|
||||||
it('applies updates from `before save` hook on update', function(done) {
|
it('applies updates from `before save` hook on update', function(done) {
|
||||||
TestModel.observe('before save', function(ctx, next) {
|
TestModel.observe('before save', function(ctx, next) {
|
||||||
ctx.data.name = 'hooked';
|
// It's crucial to change `ctx.data` reference, not only data props
|
||||||
|
ctx.data = Object.assign({}, ctx.data, {name: 'hooked'});
|
||||||
next();
|
next();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -3300,7 +3455,8 @@ module.exports = function(dataSource, should, connectorCapabilities) {
|
||||||
if (ctx.instance) {
|
if (ctx.instance) {
|
||||||
ctx.instance.name = 'hooked';
|
ctx.instance.name = 'hooked';
|
||||||
} else {
|
} else {
|
||||||
ctx.data.name = 'hooked';
|
// It's crucial to change `ctx.data` reference, not only data props
|
||||||
|
ctx.data = Object.assign({}, ctx.data, {name: 'hooked'});
|
||||||
}
|
}
|
||||||
next();
|
next();
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue