diff --git a/lib/models/model.js b/lib/models/model.js index 6f61d781..7fad476b 100644 --- a/lib/models/model.js +++ b/lib/models/model.js @@ -192,6 +192,8 @@ Model.setup = function () { var relation = relations[relationName]; if (relation.type === 'belongsTo') { ModelCtor.belongsToRemoting(relationName, relation, define) + } else if (relation.type === 'hasOne') { + ModelCtor.hasOneRemoting(relationName, relation, define) } else if ( relation.type === 'hasMany' || relation.type === 'embedsMany' || @@ -343,21 +345,36 @@ Model.remoteMethod = function(name, options) { Model.belongsToRemoting = function(relationName, relation, define) { var fn = this.prototype[relationName]; + var pathName = (relation.options.http && relation.options.http.path) || relationName; define('__get__' + relationName, { isStatic: false, - http: {verb: 'get', path: '/' + relationName}, + http: {verb: 'get', path: '/' + pathName}, accepts: {arg: 'refresh', type: 'boolean', http: {source: 'query'}}, description: 'Fetches belongsTo relation ' + relationName, returns: {arg: relationName, type: relation.modelTo.modelName, root: true} }, fn); } +Model.hasOneRemoting = function(relationName, relation, define) { + var fn = this.prototype[relationName]; + var pathName = (relation.options.http && relation.options.http.path) || relationName; + define('__get__' + relationName, { + isStatic: false, + http: {verb: 'get', path: '/' + pathName}, + accepts: {arg: 'refresh', type: 'boolean', http: {source: 'query'}}, + description: 'Fetches hasOne relation ' + relationName, + returns: {arg: relationName, type: relation.modelTo.modelName, root: true} + }, fn); +} + Model.hasManyRemoting = function (relationName, relation, define) { + var pathName = (relation.options.http && relation.options.http.path) || relationName; var toModelName = relation.modelTo.modelName; + var findByIdFunc = this.prototype['__findById__' + relationName]; define('__findById__' + relationName, { isStatic: false, - http: {verb: 'get', path: '/' + relationName + '/:fk'}, + http: {verb: 'get', path: '/' + pathName + '/:fk'}, accepts: {arg: 'fk', type: 'any', description: 'Foreign key for ' + relationName, required: true, http: {source: 'path'}}, @@ -368,7 +385,7 @@ Model.hasManyRemoting = function (relationName, relation, define) { var destroyByIdFunc = this.prototype['__destroyById__' + relationName]; define('__destroyById__' + relationName, { isStatic: false, - http: {verb: 'delete', path: '/' + relationName + '/:fk'}, + http: {verb: 'delete', path: '/' + pathName + '/:fk'}, accepts: {arg: 'fk', type: 'any', description: 'Foreign key for ' + relationName, required: true, http: {source: 'path'}}, @@ -379,7 +396,7 @@ Model.hasManyRemoting = function (relationName, relation, define) { var updateByIdFunc = this.prototype['__updateById__' + relationName]; define('__updateById__' + relationName, { isStatic: false, - http: {verb: 'put', path: '/' + relationName + '/:fk'}, + http: {verb: 'put', path: '/' + pathName + '/:fk'}, accepts: [ {arg: 'fk', type: 'any', description: 'Foreign key for ' + relationName, required: true, @@ -396,7 +413,7 @@ Model.hasManyRemoting = function (relationName, relation, define) { var addFunc = this.prototype['__link__' + relationName]; define('__link__' + relationName, { isStatic: false, - http: {verb: 'put', path: '/' + relationName + '/rel/:fk'}, + http: {verb: 'put', path: '/' + pathName + '/rel/:fk'}, accepts: {arg: 'fk', type: 'any', description: 'Foreign key for ' + relationName, required: true, http: {source: 'path'}}, @@ -407,7 +424,7 @@ Model.hasManyRemoting = function (relationName, relation, define) { var removeFunc = this.prototype['__unlink__' + relationName]; define('__unlink__' + relationName, { isStatic: false, - http: {verb: 'delete', path: '/' + relationName + '/rel/:fk'}, + http: {verb: 'delete', path: '/' + pathName + '/rel/:fk'}, accepts: {arg: 'fk', type: 'any', description: 'Foreign key for ' + relationName, required: true, http: {source: 'path'}}, @@ -420,7 +437,7 @@ Model.hasManyRemoting = function (relationName, relation, define) { var existsFunc = this.prototype['__exists__' + relationName]; define('__exists__' + relationName, { isStatic: false, - http: {verb: 'head', path: '/' + relationName + '/rel/:fk'}, + http: {verb: 'head', path: '/' + pathName + '/rel/:fk'}, accepts: {arg: 'fk', type: 'any', description: 'Foreign key for ' + relationName, required: true, http: {source: 'path'}}, @@ -446,11 +463,12 @@ Model.hasManyRemoting = function (relationName, relation, define) { }; Model.scopeRemoting = function(relationName, relation, define) { + var pathName = (relation.options.http && relation.options.http.path) || relationName; var toModelName = relation.modelTo.modelName; define('__get__' + relationName, { isStatic: false, - http: {verb: 'get', path: '/' + relationName}, + http: {verb: 'get', path: '/' + pathName}, accepts: {arg: 'filter', type: 'object'}, description: 'Queries ' + relationName + ' of ' + this.modelName + '.', returns: {arg: relationName, type: [toModelName], root: true} @@ -458,7 +476,7 @@ Model.scopeRemoting = function(relationName, relation, define) { define('__create__' + relationName, { isStatic: false, - http: {verb: 'post', path: '/' + relationName}, + http: {verb: 'post', path: '/' + pathName}, accepts: {arg: 'data', type: toModelName, http: {source: 'body'}}, description: 'Creates a new instance in ' + relationName + ' of this model.', returns: {arg: 'data', type: toModelName, root: true} @@ -466,7 +484,7 @@ Model.scopeRemoting = function(relationName, relation, define) { define('__delete__' + relationName, { isStatic: false, - http: {verb: 'delete', path: '/' + relationName}, + http: {verb: 'delete', path: '/' + pathName}, description: 'Deletes all ' + relationName + ' of this model.' }); } diff --git a/test/relations.integration.js b/test/relations.integration.js index 2c27cef9..ff7e36ff 100644 --- a/test/relations.integration.js +++ b/test/relations.integration.js @@ -651,7 +651,15 @@ describe('relations - integration', function () { 'ingredient', { properties: { name: 'string' }, dataSource: 'db' } ); + var photo = app.model( + 'photo', + { properties: { name: 'string' }, dataSource: 'db' } + ); recipe.referencesMany(ingredient); + // contrived example for test: + recipe.hasOne(photo, { as: 'picture', options: { + http: { path: 'image' } + } }); }); before(function createRecipe(done) { @@ -664,7 +672,7 @@ describe('relations - integration', function () { name: 'Chocolate' }, function(err, ing) { test.ingredient1 = ing.id; - done(); + recipe.picture.create({ name: 'Photo 1' }, done); }); }); }); @@ -680,7 +688,9 @@ describe('relations - integration', function () { after(function(done) { var app = this.app; app.models.recipe.destroyAll(function() { - app.models.ingredient.destroyAll(done); + app.models.ingredient.destroyAll(function() { + app.models.photo.destroyAll(done); + }); }); }); @@ -902,6 +912,17 @@ describe('relations - integration', function () { }); }); + it('uses a custom relation path', function(done) { + var url = '/api/recipes/' + this.recipe.id + '/image'; + + this.get(url) + .expect(200, function(err, res) { + expect(err).to.not.exist; + expect(res.body.name).to.equal('Photo 1'); + done(); + }); + }); + // TODO - this.head is undefined // it.skip('checks if a referenced model exists - ok', function(done) {