Implement `notify` for find method
This commit is contained in:
parent
de84a8bc7d
commit
a1a9bac9ba
63
lib/dao.js
63
lib/dao.js
|
@ -1633,15 +1633,25 @@ DataAccessObject.find = function find(query, options, cb) {
|
||||||
// using all documents
|
// using all documents
|
||||||
// TODO [fabien] use default scope here?
|
// TODO [fabien] use default scope here?
|
||||||
|
|
||||||
var context = {
|
if (options.notify === false) {
|
||||||
Model: self,
|
queryGeo(query);
|
||||||
query: query,
|
} else {
|
||||||
hookState: hookState,
|
withNotifyGeo();
|
||||||
options: options,
|
}
|
||||||
};
|
|
||||||
self.notifyObserversOf('access', context, function(err, ctx) {
|
|
||||||
if (err) return cb(err);
|
|
||||||
|
|
||||||
|
function withNotifyGeo() {
|
||||||
|
var context = {
|
||||||
|
Model: self,
|
||||||
|
query: query,
|
||||||
|
hookState: hookState,
|
||||||
|
options: options,
|
||||||
|
};
|
||||||
|
self.notifyObserversOf('access', context, function(err, ctx) {
|
||||||
|
if (err) return cb(err);
|
||||||
|
queryGeo(ctx.query);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function queryGeo(query) {
|
||||||
function geoCallback(err, data) {
|
function geoCallback(err, data) {
|
||||||
var memory = new Memory();
|
var memory = new Memory();
|
||||||
var modelName = self.modelName;
|
var modelName = self.modelName;
|
||||||
|
@ -1662,7 +1672,7 @@ DataAccessObject.find = function find(query, options, cb) {
|
||||||
});
|
});
|
||||||
|
|
||||||
// FIXME: apply "includes" and other transforms - see allCb below
|
// FIXME: apply "includes" and other transforms - see allCb below
|
||||||
memory.all(modelName, ctx.query, options, cb);
|
memory.all(modelName, query, options, cb);
|
||||||
} else {
|
} else {
|
||||||
cb(null, []);
|
cb(null, []);
|
||||||
}
|
}
|
||||||
|
@ -1673,10 +1683,9 @@ DataAccessObject.find = function find(query, options, cb) {
|
||||||
} else {
|
} else {
|
||||||
connector.all(self.modelName, {}, geoCallback);
|
connector.all(self.modelName, {}, geoCallback);
|
||||||
}
|
}
|
||||||
});
|
// already handled
|
||||||
|
return cb.promise;
|
||||||
// already handled
|
}
|
||||||
return cb.promise;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1717,19 +1726,23 @@ DataAccessObject.find = function find(query, options, cb) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (obj !== undefined) {
|
if (obj !== undefined) {
|
||||||
context = {
|
if (options.notify === false) {
|
||||||
Model: Model,
|
|
||||||
instance: obj,
|
|
||||||
isNewInstance: false,
|
|
||||||
hookState: hookState,
|
|
||||||
options: options,
|
|
||||||
};
|
|
||||||
|
|
||||||
Model.notifyObserversOf('loaded', context, function(err) {
|
|
||||||
if (err) return next(err);
|
|
||||||
|
|
||||||
next(null, obj);
|
next(null, obj);
|
||||||
});
|
} else {
|
||||||
|
context = {
|
||||||
|
Model: Model,
|
||||||
|
instance: obj,
|
||||||
|
isNewInstance: false,
|
||||||
|
hookState: hookState,
|
||||||
|
options: options,
|
||||||
|
};
|
||||||
|
|
||||||
|
Model.notifyObserversOf('loaded', context, function(err) {
|
||||||
|
if (err) return next(err);
|
||||||
|
|
||||||
|
next(null, obj);
|
||||||
|
});
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
next();
|
next();
|
||||||
}
|
}
|
||||||
|
|
|
@ -86,6 +86,45 @@ module.exports = function(dataSource, should, connectorCapabilities) {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should not trigger hooks, if notify is false', function(done) {
|
||||||
|
monitorHookExecution();
|
||||||
|
TestModel.find(
|
||||||
|
{ where: { id: '1' }},
|
||||||
|
{ notify: false },
|
||||||
|
function(err, list) {
|
||||||
|
if (err) return done(err);
|
||||||
|
hookMonitor.names.should.be.empty();
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not trigger hooks for geo queries, if notify is false',
|
||||||
|
function(done) {
|
||||||
|
monitorHookExecution();
|
||||||
|
|
||||||
|
TestModel.find(
|
||||||
|
{ where: { geo: [{ near: '10,20' }] }},
|
||||||
|
{ notify: false },
|
||||||
|
function(err, list) {
|
||||||
|
if (err) return done(err);
|
||||||
|
hookMonitor.names.should.be.empty();
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should apply updates from `access` hook', function(done) {
|
||||||
|
TestModel.observe('access', function(ctx, next) {
|
||||||
|
ctx.query = { where: { name: 'second' }};
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
|
||||||
|
TestModel.find({ name: 'first' }, function(err, list) {
|
||||||
|
if (err) return done(err);
|
||||||
|
list.map(get('name')).should.eql(['second']);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('triggers `access` hook', function(done) {
|
it('triggers `access` hook', function(done) {
|
||||||
TestModel.observe('access', ctxRecorder.recordAndNext());
|
TestModel.observe('access', ctxRecorder.recordAndNext());
|
||||||
|
|
||||||
|
@ -123,10 +162,10 @@ module.exports = function(dataSource, should, connectorCapabilities) {
|
||||||
it('triggers `access` hook for geo queries', function(done) {
|
it('triggers `access` hook for geo queries', function(done) {
|
||||||
TestModel.observe('access', ctxRecorder.recordAndNext());
|
TestModel.observe('access', ctxRecorder.recordAndNext());
|
||||||
|
|
||||||
TestModel.find({ where: { geo: { near: '10,20' }}}, function(err, list) {
|
TestModel.find({ where: { geo: [{ near: '10,20' }] }}, function(err, list) {
|
||||||
if (err) return done(err);
|
if (err) return done(err);
|
||||||
ctxRecorder.records.should.eql(aCtxForModel(TestModel, {
|
ctxRecorder.records.should.eql(aCtxForModel(TestModel, {
|
||||||
query: { where: { geo: { near: '10,20' }}},
|
query: { where: { geo: [{ near: '10,20' }] }},
|
||||||
}));
|
}));
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
@ -1664,24 +1703,13 @@ module.exports = function(dataSource, should, connectorCapabilities) {
|
||||||
{ id: existingInstance.id, name: 'new name' },
|
{ id: existingInstance.id, name: 'new name' },
|
||||||
function(err, record, created) {
|
function(err, record, created) {
|
||||||
if (err) return done(err);
|
if (err) return done(err);
|
||||||
if (dataSource.connector.updateOrCreate) {
|
hookMonitor.names.should.eql([
|
||||||
hookMonitor.names.should.eql([
|
'access',
|
||||||
'access',
|
'before save',
|
||||||
'before save',
|
'persist',
|
||||||
'persist',
|
'loaded',
|
||||||
'loaded',
|
'after save',
|
||||||
'after save',
|
]);
|
||||||
]);
|
|
||||||
} else {
|
|
||||||
hookMonitor.names.should.eql([
|
|
||||||
'access',
|
|
||||||
'loaded',
|
|
||||||
'before save',
|
|
||||||
'persist',
|
|
||||||
'loaded',
|
|
||||||
'after save',
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -2015,26 +2043,14 @@ module.exports = function(dataSource, should, connectorCapabilities) {
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
} else {
|
} else {
|
||||||
// For Unoptimized connector, the callback function `contextRecorder.recordAndNext`
|
ctxRecorder.records.should.eql(
|
||||||
// is called twice. As a result, contextRecorder.records
|
|
||||||
// returns an array and NOT a single instance.
|
|
||||||
ctxRecorder.records.should.eql([
|
|
||||||
aCtxForModel(TestModel, {
|
|
||||||
instance: {
|
|
||||||
id: existingInstance.id,
|
|
||||||
name: 'first',
|
|
||||||
extra: null,
|
|
||||||
},
|
|
||||||
isNewInstance: false,
|
|
||||||
options: { notify: false },
|
|
||||||
}),
|
|
||||||
aCtxForModel(TestModel, {
|
aCtxForModel(TestModel, {
|
||||||
data: {
|
data: {
|
||||||
id: existingInstance.id,
|
id: existingInstance.id,
|
||||||
name: 'updated name',
|
name: 'updated name',
|
||||||
},
|
},
|
||||||
}),
|
})
|
||||||
]);
|
);
|
||||||
}
|
}
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
@ -2118,33 +2134,13 @@ module.exports = function(dataSource, should, connectorCapabilities) {
|
||||||
{ id: existingInstance.id, name: 'new name' },
|
{ id: existingInstance.id, name: 'new name' },
|
||||||
function(err, record, created) {
|
function(err, record, created) {
|
||||||
if (err) return done(err);
|
if (err) return done(err);
|
||||||
if (dataSource.connector.replaceOrCreate) {
|
hookMonitor.names.should.eql([
|
||||||
hookMonitor.names.should.eql([
|
'access',
|
||||||
'access',
|
'before save',
|
||||||
'before save',
|
'persist',
|
||||||
'persist',
|
'loaded',
|
||||||
'loaded',
|
'after save',
|
||||||
'after save',
|
]);
|
||||||
]);
|
|
||||||
} else {
|
|
||||||
// TODO: Please see loopback-datasource-juggler/issues#836
|
|
||||||
//
|
|
||||||
// loaded hook is triggered twice in non-atomic version:
|
|
||||||
// 1) It gets triggered once by "find()" in this chain:
|
|
||||||
// "replaceORCreate()->findOne()->find()",
|
|
||||||
// which is a bug; Please see this ticket:
|
|
||||||
// loopback-datasource-juggler/issues#836.
|
|
||||||
// 2) It, also, gets triggered in "replaceAttributes()"
|
|
||||||
// in this chain replaceORCreate()->replaceAttributes()
|
|
||||||
hookMonitor.names.should.eql([
|
|
||||||
'access',
|
|
||||||
'loaded',
|
|
||||||
'before save',
|
|
||||||
'persist',
|
|
||||||
'loaded',
|
|
||||||
'after save',
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -2451,37 +2447,7 @@ module.exports = function(dataSource, should, connectorCapabilities) {
|
||||||
expected.isNewInstance =
|
expected.isNewInstance =
|
||||||
connectorCapabilities.replaceOrCreateReportsNewInstance ?
|
connectorCapabilities.replaceOrCreateReportsNewInstance ?
|
||||||
false : undefined;
|
false : undefined;
|
||||||
|
ctxRecorder.records.should.eql(aCtxForModel(TestModel, expected));
|
||||||
if (dataSource.connector.replaceOrCreate) {
|
|
||||||
ctxRecorder.records.should.eql(aCtxForModel(TestModel, expected));
|
|
||||||
} else {
|
|
||||||
// TODO: Please see loopback-datasource-juggler/issues#836
|
|
||||||
//
|
|
||||||
// loaded hook is triggered twice in non-atomic version:
|
|
||||||
// 1) It gets triggered once by "find()" in this chain:
|
|
||||||
// "replaceORCreate()->findOne()->find()",
|
|
||||||
// which is a bug; Please see this ticket:
|
|
||||||
// loopback-datasource-juggler/issues#836.
|
|
||||||
// 2) It, also, gets triggered in "replaceAttributes()"
|
|
||||||
// in this chain replaceORCreate()->replaceAttributes()
|
|
||||||
ctxRecorder.records.should.eql([
|
|
||||||
aCtxForModel(TestModel, {
|
|
||||||
data: {
|
|
||||||
id: existingInstance.id,
|
|
||||||
name: 'first',
|
|
||||||
},
|
|
||||||
isNewInstance: false,
|
|
||||||
options: { notify: false },
|
|
||||||
}),
|
|
||||||
aCtxForModel(TestModel, {
|
|
||||||
data: {
|
|
||||||
id: existingInstance.id,
|
|
||||||
name: 'replaced name',
|
|
||||||
},
|
|
||||||
isNewInstance: false,
|
|
||||||
}),
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue