Fix the model name for hasMany/through relation
This commit is contained in:
parent
b0fc88b333
commit
4c7c8901ff
13
lib/model.js
13
lib/model.js
|
@ -457,7 +457,7 @@ Model.hasManyRemoting = function(relationName, relation, define) {
|
|||
description: 'Foreign key for ' + relationName, required: true,
|
||||
http: {source: 'path'}},
|
||||
description: 'Delete a related item by id for ' + relationName,
|
||||
returns: {}
|
||||
returns: []
|
||||
}, destroyByIdFunc);
|
||||
|
||||
var updateByIdFunc = this.prototype['__updateById__' + relationName];
|
||||
|
@ -502,7 +502,7 @@ Model.hasManyRemoting = function(relationName, relation, define) {
|
|||
description: 'Foreign key for ' + relationName, required: true,
|
||||
http: {source: 'path'}},
|
||||
description: 'Remove the ' + relationName + ' relation to an item by id',
|
||||
returns: {}
|
||||
returns: []
|
||||
}, removeFunc);
|
||||
|
||||
// FIXME: [rfeng] How to map a function with callback(err, true|false) to HEAD?
|
||||
|
@ -542,6 +542,15 @@ Model.scopeRemoting = function(scopeName, scope, define) {
|
|||
var isStatic = scope.isStatic;
|
||||
var toModelName = scope.modelTo.modelName;
|
||||
|
||||
// https://github.com/strongloop/loopback/issues/811
|
||||
// Check if the scope is for a hasMany relation
|
||||
var relation = this.relations[scopeName];
|
||||
if (relation && relation.modelTo) {
|
||||
// For a relation with through model, the toModelName should be the one
|
||||
// from the target model
|
||||
toModelName = relation.modelTo.modelName;
|
||||
}
|
||||
|
||||
define('__get__' + scopeName, {
|
||||
isStatic: isStatic,
|
||||
http: {verb: 'get', path: '/' + pathName},
|
||||
|
|
|
@ -411,8 +411,8 @@ describe('relations - integration', function () {
|
|||
});
|
||||
|
||||
lt.describe.whenCalledRemotely('DELETE', '/api/physicians/:id/patients/rel/:fk', function () {
|
||||
it('should succeed with statusCode 200', function () {
|
||||
assert.equal(this.res.statusCode, 200);
|
||||
it('should succeed with statusCode 204', function () {
|
||||
assert.equal(this.res.statusCode, 204);
|
||||
});
|
||||
|
||||
it('should remove the record in appointment', function (done) {
|
||||
|
@ -469,8 +469,8 @@ describe('relations - integration', function () {
|
|||
});
|
||||
|
||||
lt.describe.whenCalledRemotely('DELETE', '/api/physicians/:id/patients/:fk', function () {
|
||||
it('should succeed with statusCode 200', function () {
|
||||
assert.equal(this.res.statusCode, 200);
|
||||
it('should succeed with statusCode 204', function () {
|
||||
assert.equal(this.res.statusCode, 204);
|
||||
});
|
||||
|
||||
it('should remove the record in appointment', function (done) {
|
||||
|
|
|
@ -66,33 +66,176 @@ describe('remoting - integration', function () {
|
|||
});
|
||||
});
|
||||
|
||||
describe('Model', function() {
|
||||
it('has expected remote methods', function() {
|
||||
var storeClass = app.handler('rest').adapter
|
||||
describe('Model shared classes', function() {
|
||||
function formatReturns(m) {
|
||||
var returns = m.returns;
|
||||
if (!returns || returns.length === 0) {
|
||||
return '';
|
||||
}
|
||||
var type = returns[0].type;
|
||||
return type ? ':' + type : '';
|
||||
}
|
||||
|
||||
function formatMethod(m) {
|
||||
return [
|
||||
m.name,
|
||||
'(',
|
||||
m.accepts.map(function(a) {
|
||||
return a.arg + ':' + a.type
|
||||
}).join(','),
|
||||
')',
|
||||
formatReturns(m),
|
||||
' ',
|
||||
m.getHttpMethod(),
|
||||
' ',
|
||||
m.getFullPath()
|
||||
].join('');
|
||||
}
|
||||
|
||||
function findClass(name) {
|
||||
return app.handler('rest').adapter
|
||||
.getClasses()
|
||||
.filter(function(c) { return c.name === 'store'; })[0];
|
||||
.filter(function(c) {
|
||||
return c.name === name;
|
||||
})[0];
|
||||
}
|
||||
|
||||
it('has expected remote methods', function() {
|
||||
var storeClass = findClass('store');
|
||||
var methods = storeClass.methods
|
||||
.filter(function(m) {
|
||||
return m.name.indexOf('__') === -1;
|
||||
})
|
||||
.map(function(m) {
|
||||
return [
|
||||
m.name + '()',
|
||||
m.getHttpMethod(),
|
||||
m.getFullPath()
|
||||
].join(' ');
|
||||
return formatMethod(m);
|
||||
});
|
||||
|
||||
var expectedMethods = [
|
||||
'create(data:object):store POST /stores',
|
||||
'upsert(data:object):store PUT /stores',
|
||||
'exists(id:any):boolean GET /stores/:id/exists',
|
||||
'findById(id:any):store GET /stores/:id',
|
||||
'find(filter:object):store GET /stores',
|
||||
'findOne(filter:object):store GET /stores/findOne',
|
||||
'updateAll(where:object,data:object) POST /stores/update',
|
||||
'deleteById(id:any) DELETE /stores/:id',
|
||||
'count(where:object):number GET /stores/count',
|
||||
'prototype.updateAttributes(data:object):store PUT /stores/:id'
|
||||
];
|
||||
|
||||
// The list of methods is from docs:
|
||||
// http://docs.strongloop.com/display/LB/Exposing+models+over+a+REST+API
|
||||
expect(methods).to.include.members([
|
||||
'create() POST /stores',
|
||||
'upsert() PUT /stores',
|
||||
'exists() GET /stores/:id/exists',
|
||||
'findById() GET /stores/:id',
|
||||
'find() GET /stores',
|
||||
'findOne() GET /stores/findOne',
|
||||
'deleteById() DELETE /stores/:id',
|
||||
'count() GET /stores/count',
|
||||
'prototype.updateAttributes() PUT /stores/:id',
|
||||
]);
|
||||
expect(methods).to.include.members(expectedMethods);
|
||||
});
|
||||
|
||||
it('has expected remote methods for scopes', function() {
|
||||
var storeClass = findClass('store');
|
||||
var methods = storeClass.methods
|
||||
.filter(function(m) {
|
||||
return m.name.indexOf('__') === 0;
|
||||
})
|
||||
.map(function(m) {
|
||||
return formatMethod(m);
|
||||
});
|
||||
|
||||
var expectedMethods = [
|
||||
'__get__superStores(filter:object):store GET /stores/superStores',
|
||||
'__create__superStores(data:store):store POST /stores/superStores',
|
||||
'__delete__superStores() DELETE /stores/superStores',
|
||||
'__count__superStores(where:object):number GET /stores/superStores/count'
|
||||
];
|
||||
|
||||
expect(methods).to.include.members(expectedMethods);
|
||||
});
|
||||
|
||||
|
||||
it('should have correct signatures for belongsTo methods',
|
||||
function() {
|
||||
|
||||
var widgetClass = findClass('widget');
|
||||
var methods = widgetClass.methods
|
||||
.filter(function(m) {
|
||||
return m.name.indexOf('prototype.__') === 0;
|
||||
})
|
||||
.map(function(m) {
|
||||
return formatMethod(m);
|
||||
});
|
||||
|
||||
var expectedMethods = [
|
||||
'prototype.__get__store(refresh:boolean):store ' +
|
||||
'GET /widgets/:id/store'
|
||||
];
|
||||
expect(methods).to.include.members(expectedMethods);
|
||||
});
|
||||
|
||||
|
||||
it('should have correct signatures for hasMany methods',
|
||||
function() {
|
||||
|
||||
var physicianClass = findClass('store');
|
||||
var methods = physicianClass.methods
|
||||
.filter(function(m) {
|
||||
return m.name.indexOf('prototype.__') === 0;
|
||||
})
|
||||
.map(function(m) {
|
||||
return formatMethod(m);
|
||||
});
|
||||
|
||||
var expectedMethods = [
|
||||
'prototype.__findById__widgets(fk:any):widget ' +
|
||||
'GET /stores/:id/widgets/:fk',
|
||||
'prototype.__destroyById__widgets(fk:any) ' +
|
||||
'DELETE /stores/:id/widgets/:fk',
|
||||
'prototype.__updateById__widgets(fk:any,data:widget):widget ' +
|
||||
'PUT /stores/:id/widgets/:fk',
|
||||
'prototype.__get__widgets(filter:object):widget ' +
|
||||
'GET /stores/:id/widgets',
|
||||
'prototype.__create__widgets(data:widget):widget ' +
|
||||
'POST /stores/:id/widgets',
|
||||
'prototype.__delete__widgets() ' +
|
||||
'DELETE /stores/:id/widgets',
|
||||
'prototype.__count__widgets(where:object):number ' +
|
||||
'GET /stores/:id/widgets/count'
|
||||
];
|
||||
expect(methods).to.include.members(expectedMethods);
|
||||
});
|
||||
|
||||
it('should have correct signatures for hasMany-through methods',
|
||||
function() {
|
||||
|
||||
var physicianClass = findClass('physician');
|
||||
var methods = physicianClass.methods
|
||||
.filter(function(m) {
|
||||
return m.name.indexOf('prototype.__') === 0;
|
||||
})
|
||||
.map(function(m) {
|
||||
return formatMethod(m);
|
||||
});
|
||||
|
||||
var expectedMethods = [
|
||||
'prototype.__findById__patients(fk:any):patient ' +
|
||||
'GET /physicians/:id/patients/:fk',
|
||||
'prototype.__destroyById__patients(fk:any) ' +
|
||||
'DELETE /physicians/:id/patients/:fk',
|
||||
'prototype.__updateById__patients(fk:any,data:patient):patient ' +
|
||||
'PUT /physicians/:id/patients/:fk',
|
||||
'prototype.__link__patients(fk:any,data:appointment):appointment ' +
|
||||
'PUT /physicians/:id/patients/rel/:fk',
|
||||
'prototype.__unlink__patients(fk:any) ' +
|
||||
'DELETE /physicians/:id/patients/rel/:fk',
|
||||
'prototype.__exists__patients(fk:any):boolean ' +
|
||||
'HEAD /physicians/:id/patients/rel/:fk',
|
||||
'prototype.__get__patients(filter:object):patient ' +
|
||||
'GET /physicians/:id/patients',
|
||||
'prototype.__create__patients(data:patient):patient ' +
|
||||
'POST /physicians/:id/patients',
|
||||
'prototype.__delete__patients() ' +
|
||||
'DELETE /physicians/:id/patients',
|
||||
'prototype.__count__patients(where:object):number ' +
|
||||
'GET /physicians/:id/patients/count'
|
||||
];
|
||||
expect(methods).to.include.members(expectedMethods);
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue