Merge pull request #427 from fabien/feature/relation-paths

Allow custom relation path (http) - enable hasOne remoting access
This commit is contained in:
Raymond Feng 2014-08-07 10:32:13 -07:00
commit 0affc65c67
2 changed files with 51 additions and 12 deletions

View File

@ -192,6 +192,8 @@ Model.setup = function () {
var relation = relations[relationName]; var relation = relations[relationName];
if (relation.type === 'belongsTo') { if (relation.type === 'belongsTo') {
ModelCtor.belongsToRemoting(relationName, relation, define) ModelCtor.belongsToRemoting(relationName, relation, define)
} else if (relation.type === 'hasOne') {
ModelCtor.hasOneRemoting(relationName, relation, define)
} else if ( } else if (
relation.type === 'hasMany' || relation.type === 'hasMany' ||
relation.type === 'embedsMany' || relation.type === 'embedsMany' ||
@ -343,21 +345,36 @@ Model.remoteMethod = function(name, options) {
Model.belongsToRemoting = function(relationName, relation, define) { Model.belongsToRemoting = function(relationName, relation, define) {
var fn = this.prototype[relationName]; var fn = this.prototype[relationName];
var pathName = (relation.options.http && relation.options.http.path) || relationName;
define('__get__' + relationName, { define('__get__' + relationName, {
isStatic: false, isStatic: false,
http: {verb: 'get', path: '/' + relationName}, http: {verb: 'get', path: '/' + pathName},
accepts: {arg: 'refresh', type: 'boolean', http: {source: 'query'}}, accepts: {arg: 'refresh', type: 'boolean', http: {source: 'query'}},
description: 'Fetches belongsTo relation ' + relationName, description: 'Fetches belongsTo relation ' + relationName,
returns: {arg: relationName, type: relation.modelTo.modelName, root: true} returns: {arg: relationName, type: relation.modelTo.modelName, root: true}
}, fn); }, 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) { Model.hasManyRemoting = function (relationName, relation, define) {
var pathName = (relation.options.http && relation.options.http.path) || relationName;
var toModelName = relation.modelTo.modelName; var toModelName = relation.modelTo.modelName;
var findByIdFunc = this.prototype['__findById__' + relationName]; var findByIdFunc = this.prototype['__findById__' + relationName];
define('__findById__' + relationName, { define('__findById__' + relationName, {
isStatic: false, isStatic: false,
http: {verb: 'get', path: '/' + relationName + '/:fk'}, http: {verb: 'get', path: '/' + pathName + '/:fk'},
accepts: {arg: 'fk', type: 'any', accepts: {arg: 'fk', type: 'any',
description: 'Foreign key for ' + relationName, required: true, description: 'Foreign key for ' + relationName, required: true,
http: {source: 'path'}}, http: {source: 'path'}},
@ -368,7 +385,7 @@ Model.hasManyRemoting = function (relationName, relation, define) {
var destroyByIdFunc = this.prototype['__destroyById__' + relationName]; var destroyByIdFunc = this.prototype['__destroyById__' + relationName];
define('__destroyById__' + relationName, { define('__destroyById__' + relationName, {
isStatic: false, isStatic: false,
http: {verb: 'delete', path: '/' + relationName + '/:fk'}, http: {verb: 'delete', path: '/' + pathName + '/:fk'},
accepts: {arg: 'fk', type: 'any', accepts: {arg: 'fk', type: 'any',
description: 'Foreign key for ' + relationName, required: true, description: 'Foreign key for ' + relationName, required: true,
http: {source: 'path'}}, http: {source: 'path'}},
@ -379,7 +396,7 @@ Model.hasManyRemoting = function (relationName, relation, define) {
var updateByIdFunc = this.prototype['__updateById__' + relationName]; var updateByIdFunc = this.prototype['__updateById__' + relationName];
define('__updateById__' + relationName, { define('__updateById__' + relationName, {
isStatic: false, isStatic: false,
http: {verb: 'put', path: '/' + relationName + '/:fk'}, http: {verb: 'put', path: '/' + pathName + '/:fk'},
accepts: [ accepts: [
{arg: 'fk', type: 'any', {arg: 'fk', type: 'any',
description: 'Foreign key for ' + relationName, required: true, description: 'Foreign key for ' + relationName, required: true,
@ -396,7 +413,7 @@ Model.hasManyRemoting = function (relationName, relation, define) {
var addFunc = this.prototype['__link__' + relationName]; var addFunc = this.prototype['__link__' + relationName];
define('__link__' + relationName, { define('__link__' + relationName, {
isStatic: false, isStatic: false,
http: {verb: 'put', path: '/' + relationName + '/rel/:fk'}, http: {verb: 'put', path: '/' + pathName + '/rel/:fk'},
accepts: {arg: 'fk', type: 'any', accepts: {arg: 'fk', type: 'any',
description: 'Foreign key for ' + relationName, required: true, description: 'Foreign key for ' + relationName, required: true,
http: {source: 'path'}}, http: {source: 'path'}},
@ -407,7 +424,7 @@ Model.hasManyRemoting = function (relationName, relation, define) {
var removeFunc = this.prototype['__unlink__' + relationName]; var removeFunc = this.prototype['__unlink__' + relationName];
define('__unlink__' + relationName, { define('__unlink__' + relationName, {
isStatic: false, isStatic: false,
http: {verb: 'delete', path: '/' + relationName + '/rel/:fk'}, http: {verb: 'delete', path: '/' + pathName + '/rel/:fk'},
accepts: {arg: 'fk', type: 'any', accepts: {arg: 'fk', type: 'any',
description: 'Foreign key for ' + relationName, required: true, description: 'Foreign key for ' + relationName, required: true,
http: {source: 'path'}}, http: {source: 'path'}},
@ -420,7 +437,7 @@ Model.hasManyRemoting = function (relationName, relation, define) {
var existsFunc = this.prototype['__exists__' + relationName]; var existsFunc = this.prototype['__exists__' + relationName];
define('__exists__' + relationName, { define('__exists__' + relationName, {
isStatic: false, isStatic: false,
http: {verb: 'head', path: '/' + relationName + '/rel/:fk'}, http: {verb: 'head', path: '/' + pathName + '/rel/:fk'},
accepts: {arg: 'fk', type: 'any', accepts: {arg: 'fk', type: 'any',
description: 'Foreign key for ' + relationName, required: true, description: 'Foreign key for ' + relationName, required: true,
http: {source: 'path'}}, http: {source: 'path'}},
@ -446,11 +463,12 @@ Model.hasManyRemoting = function (relationName, relation, define) {
}; };
Model.scopeRemoting = 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; var toModelName = relation.modelTo.modelName;
define('__get__' + relationName, { define('__get__' + relationName, {
isStatic: false, isStatic: false,
http: {verb: 'get', path: '/' + relationName}, http: {verb: 'get', path: '/' + pathName},
accepts: {arg: 'filter', type: 'object'}, accepts: {arg: 'filter', type: 'object'},
description: 'Queries ' + relationName + ' of ' + this.modelName + '.', description: 'Queries ' + relationName + ' of ' + this.modelName + '.',
returns: {arg: relationName, type: [toModelName], root: true} returns: {arg: relationName, type: [toModelName], root: true}
@ -458,7 +476,7 @@ Model.scopeRemoting = function(relationName, relation, define) {
define('__create__' + relationName, { define('__create__' + relationName, {
isStatic: false, isStatic: false,
http: {verb: 'post', path: '/' + relationName}, http: {verb: 'post', path: '/' + pathName},
accepts: {arg: 'data', type: toModelName, http: {source: 'body'}}, accepts: {arg: 'data', type: toModelName, http: {source: 'body'}},
description: 'Creates a new instance in ' + relationName + ' of this model.', description: 'Creates a new instance in ' + relationName + ' of this model.',
returns: {arg: 'data', type: toModelName, root: true} returns: {arg: 'data', type: toModelName, root: true}
@ -466,7 +484,7 @@ Model.scopeRemoting = function(relationName, relation, define) {
define('__delete__' + relationName, { define('__delete__' + relationName, {
isStatic: false, isStatic: false,
http: {verb: 'delete', path: '/' + relationName}, http: {verb: 'delete', path: '/' + pathName},
description: 'Deletes all ' + relationName + ' of this model.' description: 'Deletes all ' + relationName + ' of this model.'
}); });
} }

View File

@ -651,7 +651,15 @@ describe('relations - integration', function () {
'ingredient', 'ingredient',
{ properties: { name: 'string' }, dataSource: 'db' } { properties: { name: 'string' }, dataSource: 'db' }
); );
var photo = app.model(
'photo',
{ properties: { name: 'string' }, dataSource: 'db' }
);
recipe.referencesMany(ingredient); recipe.referencesMany(ingredient);
// contrived example for test:
recipe.hasOne(photo, { as: 'picture', options: {
http: { path: 'image' }
} });
}); });
before(function createRecipe(done) { before(function createRecipe(done) {
@ -664,7 +672,7 @@ describe('relations - integration', function () {
name: 'Chocolate' }, name: 'Chocolate' },
function(err, ing) { function(err, ing) {
test.ingredient1 = ing.id; test.ingredient1 = ing.id;
done(); recipe.picture.create({ name: 'Photo 1' }, done);
}); });
}); });
}); });
@ -680,7 +688,9 @@ describe('relations - integration', function () {
after(function(done) { after(function(done) {
var app = this.app; var app = this.app;
app.models.recipe.destroyAll(function() { 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 // TODO - this.head is undefined
// it.skip('checks if a referenced model exists - ok', function(done) { // it.skip('checks if a referenced model exists - ok', function(done) {