Merge pull request #1306 from strongloop/feature/allow-filter-on-findById

Expose the `filter` argument for findById
This commit is contained in:
Raymond Feng 2015-04-16 15:12:50 -07:00
commit 3dcde7994b
3 changed files with 87 additions and 12 deletions

View File

@ -142,15 +142,20 @@ module.exports = function(registry) {
}; };
/** /**
* Find object by ID. * Find object by ID with an optional filter for include/fields.
* *
* @param {*} id Primary key value * @param {*} id Primary key value
* @options {Object} [filter] Optional Filter JSON object; see below.
* @property {String|Object|Array} fields Identify fields to include in return result.
* <br/>See [Fields filter](http://docs.strongloop.com/display/LB/Fields+filter).
* @property {String|Object|Array} include See PersistedModel.include documentation.
* <br/>See [Include filter](http://docs.strongloop.com/display/LB/Include+filter).
* @callback {Function} callback Callback function called with `(err, instance)` arguments. Required. * @callback {Function} callback Callback function called with `(err, instance)` arguments. Required.
* @param {Error} err Error object; see [Error object](http://docs.strongloop.com/display/LB/Error+object). * @param {Error} err Error object; see [Error object](http://docs.strongloop.com/display/LB/Error+object).
* @param {Object} instance Model instance matching the specified ID or null if no instance matches. * @param {Object} instance Model instance matching the specified ID or null if no instance matches.
*/ */
PersistedModel.findById = function find(id, cb) { PersistedModel.findById = function find(id, filter, cb) {
throwNotAttached(this.modelName, 'findById'); throwNotAttached(this.modelName, 'findById');
}; };
@ -180,7 +185,7 @@ module.exports = function(registry) {
* @param {Array} models Model instances matching the filter, or null if none found. * @param {Array} models Model instances matching the filter, or null if none found.
*/ */
PersistedModel.find = function find(params, cb) { PersistedModel.find = function find(filter, cb) {
throwNotAttached(this.modelName, 'find'); throwNotAttached(this.modelName, 'find');
}; };
@ -209,7 +214,7 @@ module.exports = function(registry) {
* @param {Array} model First model instance that matches the filter or null if none found. * @param {Array} model First model instance that matches the filter or null if none found.
*/ */
PersistedModel.findOne = function findOne(params, cb) { PersistedModel.findOne = function findOne(filter, cb) {
throwNotAttached(this.modelName, 'findOne'); throwNotAttached(this.modelName, 'findOne');
}; };
@ -573,10 +578,12 @@ module.exports = function(registry) {
setRemoting(PersistedModel, 'findById', { setRemoting(PersistedModel, 'findById', {
description: 'Find a model instance by id from the data source.', description: 'Find a model instance by id from the data source.',
accessType: 'READ', accessType: 'READ',
accepts: { accepts: [
arg: 'id', type: 'any', description: 'Model id', required: true, { arg: 'id', type: 'any', description: 'Model id', required: true,
http: {source: 'path'} http: {source: 'path'}},
}, { arg: 'filter', type: 'object',
description: 'Filter defining fields and include'}
],
returns: {arg: 'data', type: typeName, root: true}, returns: {arg: 'data', type: typeName, root: true},
http: {verb: 'get', path: '/:id'}, http: {verb: 'get', path: '/:id'},
rest: {after: convertNullToNotFoundError} rest: {after: convertNullToNotFoundError}
@ -585,7 +592,7 @@ module.exports = function(registry) {
setRemoting(PersistedModel, 'find', { setRemoting(PersistedModel, 'find', {
description: 'Find all instances of the model matched by filter from the data source.', description: 'Find all instances of the model matched by filter from the data source.',
accessType: 'READ', accessType: 'READ',
accepts: {arg: 'filter', type: 'object', description: 'Filter defining fields, where, order, offset, and limit'}, accepts: {arg: 'filter', type: 'object', description: 'Filter defining fields, where, include, order, offset, and limit'},
returns: {arg: 'data', type: [typeName], root: true}, returns: {arg: 'data', type: [typeName], root: true},
http: {verb: 'get', path: '/'} http: {verb: 'get', path: '/'}
}); });
@ -593,7 +600,7 @@ module.exports = function(registry) {
setRemoting(PersistedModel, 'findOne', { setRemoting(PersistedModel, 'findOne', {
description: 'Find first instance of the model matched by filter from the data source.', description: 'Find first instance of the model matched by filter from the data source.',
accessType: 'READ', accessType: 'READ',
accepts: {arg: 'filter', type: 'object', description: 'Filter defining fields, where, order, offset, and limit'}, accepts: {arg: 'filter', type: 'object', description: 'Filter defining fields, where, include, order, offset, and limit'},
returns: {arg: 'data', type: typeName, root: true}, returns: {arg: 'data', type: typeName, root: true},
http: {verb: 'get', path: '/findOne'}, http: {verb: 'get', path: '/findOne'},
rest: {after: convertNullToNotFoundError} rest: {after: convertNullToNotFoundError}

View File

@ -66,7 +66,7 @@ describe('Model / PersistedModel', function() {
describe.onServer('Remote Methods', function() { describe.onServer('Remote Methods', function() {
var User; var User, Post;
var dataSource; var dataSource;
var app; var app;
@ -84,11 +84,22 @@ describe.onServer('Remote Methods', function() {
trackChanges: true trackChanges: true
}); });
Post = PersistedModel.extend('post', {
id: { id: true, type: String, defaultFn: 'guid' },
title: String,
content: String
}, {
trackChanges: true
});
dataSource = loopback.createDataSource({ dataSource = loopback.createDataSource({
connector: loopback.Memory connector: loopback.Memory
}); });
User.attachTo(dataSource); User.attachTo(dataSource);
Post.attachTo(dataSource);
User.hasMany(Post);
User.login = function(username, password, fn) { User.login = function(username, password, fn) {
if (username === 'foo' && password === 'bar') { if (username === 'foo' && password === 'bar') {
@ -163,6 +174,63 @@ describe.onServer('Remote Methods', function() {
done(); done();
}); });
}); });
it('Call the findById with filter.fields using HTTP / REST', function(done) {
request(app)
.post('/users')
.send({first: 'x', last: 'y'})
.expect('Content-Type', /json/)
.expect(200)
.end(function(err, res) {
if (err) return done(err);
var userId = res.body.id;
assert(userId);
request(app)
.get('/users/' + userId + '?filter[fields]=first')
.expect('Content-Type', /json/)
.expect(200)
.end(function(err, res) {
if (err) return done(err);
assert.equal(res.body.first, 'x', 'first should be x');
assert(res.body.last === undefined, 'last should not be present');
done();
});
});
});
it('Call the findById with filter.include using HTTP / REST', function(done) {
request(app)
.post('/users')
.send({first: 'x', last: 'y'})
.expect('Content-Type', /json/)
.expect(200)
.end(function(err, res) {
if (err) return done(err);
var userId = res.body.id;
assert(userId);
request(app)
.post('/users/' + userId + '/posts')
.send({title: 'T1', content: 'C1'})
.expect('Content-Type', /json/)
.expect(200)
.end(function(err, res) {
if (err) return done(err);
var post = res.body;
request(app)
.get('/users/' + userId + '?filter[include]=posts')
.expect('Content-Type', /json/)
.expect(200)
.end(function(err, res) {
if (err) return done(err);
assert.equal(res.body.first, 'x', 'first should be x');
assert.equal(res.body.last, 'y', 'last should be y');
assert.deepEqual(post, res.body.posts[0]);
done();
});
});
});
});
}); });
describe('Model.beforeRemote(name, fn)', function() { describe('Model.beforeRemote(name, fn)', function() {

View File

@ -114,7 +114,7 @@ describe('remoting - integration', function() {
'create(data:object):store POST /stores', 'create(data:object):store POST /stores',
'upsert(data:object):store PUT /stores', 'upsert(data:object):store PUT /stores',
'exists(id:any):boolean GET /stores/:id/exists', 'exists(id:any):boolean GET /stores/:id/exists',
'findById(id:any):store GET /stores/:id', 'findById(id:any,filter:object):store GET /stores/:id',
'find(filter:object):store GET /stores', 'find(filter:object):store GET /stores',
'findOne(filter:object):store GET /stores/findOne', 'findOne(filter:object):store GET /stores/findOne',
'updateAll(where:object,data:object) POST /stores/update', 'updateAll(where:object,data:object) POST /stores/update',