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,
|
description: 'Foreign key for ' + relationName, required: true,
|
||||||
http: {source: 'path'}},
|
http: {source: 'path'}},
|
||||||
description: 'Delete a related item by id for ' + relationName,
|
description: 'Delete a related item by id for ' + relationName,
|
||||||
returns: {}
|
returns: []
|
||||||
}, destroyByIdFunc);
|
}, destroyByIdFunc);
|
||||||
|
|
||||||
var updateByIdFunc = this.prototype['__updateById__' + relationName];
|
var updateByIdFunc = this.prototype['__updateById__' + relationName];
|
||||||
|
@ -502,7 +502,7 @@ Model.hasManyRemoting = function(relationName, relation, define) {
|
||||||
description: 'Foreign key for ' + relationName, required: true,
|
description: 'Foreign key for ' + relationName, required: true,
|
||||||
http: {source: 'path'}},
|
http: {source: 'path'}},
|
||||||
description: 'Remove the ' + relationName + ' relation to an item by id',
|
description: 'Remove the ' + relationName + ' relation to an item by id',
|
||||||
returns: {}
|
returns: []
|
||||||
}, removeFunc);
|
}, removeFunc);
|
||||||
|
|
||||||
// FIXME: [rfeng] How to map a function with callback(err, true|false) to HEAD?
|
// 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 isStatic = scope.isStatic;
|
||||||
var toModelName = scope.modelTo.modelName;
|
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, {
|
define('__get__' + scopeName, {
|
||||||
isStatic: isStatic,
|
isStatic: isStatic,
|
||||||
http: {verb: 'get', path: '/' + pathName},
|
http: {verb: 'get', path: '/' + pathName},
|
||||||
|
|
|
@ -411,8 +411,8 @@ describe('relations - integration', function () {
|
||||||
});
|
});
|
||||||
|
|
||||||
lt.describe.whenCalledRemotely('DELETE', '/api/physicians/:id/patients/rel/:fk', function () {
|
lt.describe.whenCalledRemotely('DELETE', '/api/physicians/:id/patients/rel/:fk', function () {
|
||||||
it('should succeed with statusCode 200', function () {
|
it('should succeed with statusCode 204', function () {
|
||||||
assert.equal(this.res.statusCode, 200);
|
assert.equal(this.res.statusCode, 204);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should remove the record in appointment', function (done) {
|
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 () {
|
lt.describe.whenCalledRemotely('DELETE', '/api/physicians/:id/patients/:fk', function () {
|
||||||
it('should succeed with statusCode 200', function () {
|
it('should succeed with statusCode 204', function () {
|
||||||
assert.equal(this.res.statusCode, 200);
|
assert.equal(this.res.statusCode, 204);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should remove the record in appointment', function (done) {
|
it('should remove the record in appointment', function (done) {
|
||||||
|
|
|
@ -66,33 +66,176 @@ describe('remoting - integration', function () {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Model', function() {
|
describe('Model shared classes', function() {
|
||||||
it('has expected remote methods', function() {
|
function formatReturns(m) {
|
||||||
var storeClass = app.handler('rest').adapter
|
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()
|
.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
|
var methods = storeClass.methods
|
||||||
|
.filter(function(m) {
|
||||||
|
return m.name.indexOf('__') === -1;
|
||||||
|
})
|
||||||
.map(function(m) {
|
.map(function(m) {
|
||||||
return [
|
return formatMethod(m);
|
||||||
m.name + '()',
|
|
||||||
m.getHttpMethod(),
|
|
||||||
m.getFullPath()
|
|
||||||
].join(' ');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
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:
|
// The list of methods is from docs:
|
||||||
// http://docs.strongloop.com/display/LB/Exposing+models+over+a+REST+API
|
// http://docs.strongloop.com/display/LB/Exposing+models+over+a+REST+API
|
||||||
expect(methods).to.include.members([
|
expect(methods).to.include.members(expectedMethods);
|
||||||
'create() POST /stores',
|
});
|
||||||
'upsert() PUT /stores',
|
|
||||||
'exists() GET /stores/:id/exists',
|
it('has expected remote methods for scopes', function() {
|
||||||
'findById() GET /stores/:id',
|
var storeClass = findClass('store');
|
||||||
'find() GET /stores',
|
var methods = storeClass.methods
|
||||||
'findOne() GET /stores/findOne',
|
.filter(function(m) {
|
||||||
'deleteById() DELETE /stores/:id',
|
return m.name.indexOf('__') === 0;
|
||||||
'count() GET /stores/count',
|
})
|
||||||
'prototype.updateAttributes() PUT /stores/:id',
|
.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