feat: change hasone relation error message

The current hasone error message is not appropriate one. So adds a better message.
This commit is contained in:
Sujesh T 2019-11-30 16:41:14 +05:30
parent 2db09a3d00
commit 2ecc3e5501
2 changed files with 1102 additions and 883 deletions

View File

@ -131,7 +131,8 @@ module.exports = function(registry) {
ModelCtor.sharedCtor = function(data, id, options, fn) { ModelCtor.sharedCtor = function(data, id, options, fn) {
const ModelCtor = this; const ModelCtor = this;
const isRemoteInvocationWithOptions = typeof data !== 'object' && const isRemoteInvocationWithOptions =
typeof data !== 'object' &&
typeof id === 'object' && typeof id === 'object' &&
typeof options === 'function'; typeof options === 'function';
if (isRemoteInvocationWithOptions) { if (isRemoteInvocationWithOptions) {
@ -187,15 +188,18 @@ module.exports = function(registry) {
const idDesc = ModelCtor.modelName + ' id'; const idDesc = ModelCtor.modelName + ' id';
ModelCtor.sharedCtor.accepts = [ ModelCtor.sharedCtor.accepts = [
{arg: 'id', type: 'any', required: true, http: {source: 'path'}, {
description: idDesc}, arg: 'id',
type: 'any',
required: true,
http: {source: 'path'},
description: idDesc,
},
// {arg: 'instance', type: 'object', http: {source: 'body'}} // {arg: 'instance', type: 'object', http: {source: 'body'}}
{arg: 'options', type: 'object', http: createOptionsViaModelMethod}, {arg: 'options', type: 'object', http: createOptionsViaModelMethod},
]; ];
ModelCtor.sharedCtor.http = [ ModelCtor.sharedCtor.http = [{path: '/:id'}];
{path: '/:id'},
];
ModelCtor.sharedCtor.returns = {root: true}; ModelCtor.sharedCtor.returns = {root: true};
@ -203,11 +207,11 @@ module.exports = function(registry) {
extend(remotingOptions, options.remoting || {}); extend(remotingOptions, options.remoting || {});
// create a sharedClass // create a sharedClass
const sharedClass = ModelCtor.sharedClass = new SharedClass( const sharedClass = (ModelCtor.sharedClass = new SharedClass(
ModelCtor.modelName, ModelCtor.modelName,
ModelCtor, ModelCtor,
remotingOptions, remotingOptions,
); ));
// before remote hook // before remote hook
ModelCtor.beforeRemote = function(name, fn) { ModelCtor.beforeRemote = function(name, fn) {
@ -248,15 +252,20 @@ module.exports = function(registry) {
}; };
if ('injectOptionsFromRemoteContext' in options) { if ('injectOptionsFromRemoteContext' in options) {
console.warn(g.f( console.warn(
'%s is using model setting %s which is no longer available.', g.f(
typeName, 'injectOptionsFromRemoteContext', '%s is using model setting %s which is no longer available.',
)); typeName,
console.warn(g.f( 'injectOptionsFromRemoteContext',
'Please rework your app to use the offical solution for injecting ' + ),
'"options" argument from request context,\nsee %s', );
'http://loopback.io/doc/en/lb3/Using-current-context.html', console.warn(
)); g.f(
'Please rework your app to use the offical solution for injecting ' +
'"options" argument from request context,\nsee %s',
'http://loopback.io/doc/en/lb3/Using-current-context.html',
),
);
} }
// resolve relation functions // resolve relation functions
@ -284,7 +293,8 @@ module.exports = function(registry) {
} else if ( } else if (
relation.type === 'hasMany' || relation.type === 'hasMany' ||
relation.type === 'embedsMany' || relation.type === 'embedsMany' ||
relation.type === 'referencesMany') { relation.type === 'referencesMany'
) {
ModelCtor.hasManyRemoting(relationName, relation, define); ModelCtor.hasManyRemoting(relationName, relation, define);
} }
// Automatically enable nestRemoting if the flag is set to true in the // Automatically enable nestRemoting if the flag is set to true in the
@ -345,19 +355,22 @@ module.exports = function(registry) {
ctx = {}; ctx = {};
} }
aclModel.checkAccessForContext({ aclModel.checkAccessForContext(
accessToken: token, {
model: this, accessToken: token,
property: sharedMethod.name, model: this,
method: sharedMethod.name, property: sharedMethod.name,
sharedMethod: sharedMethod, method: sharedMethod.name,
modelId: modelId, sharedMethod: sharedMethod,
accessType: this._getAccessTypeForMethod(sharedMethod), modelId: modelId,
remotingContext: ctx, accessType: this._getAccessTypeForMethod(sharedMethod),
}, function(err, accessRequest) { remotingContext: ctx,
if (err) return callback(err); },
callback(null, accessRequest.isAllowed()); function(err, accessRequest) {
}); if (err) return callback(err);
callback(null, accessRequest.isAllowed());
},
);
}; };
/*! /*!
@ -380,12 +393,15 @@ module.exports = function(registry) {
// Check the explicit setting of accessType // Check the explicit setting of accessType
if (method.accessType) { if (method.accessType) {
assert(method.accessType === ACL.READ || assert(
method.accessType === ACL.REPLICATE || method.accessType === ACL.READ ||
method.accessType === ACL.WRITE || method.accessType === ACL.REPLICATE ||
method.accessType === ACL.EXECUTE, 'invalid accessType ' + method.accessType === ACL.WRITE ||
method.accessType + method.accessType === ACL.EXECUTE,
'. It must be "READ", "REPLICATE", "WRITE", or "EXECUTE"'); 'invalid accessType ' +
method.accessType +
'. It must be "READ", "REPLICATE", "WRITE", or "EXECUTE"',
);
return method.accessType; return method.accessType;
} }
@ -478,8 +494,7 @@ module.exports = function(registry) {
}; };
function setupOptionsArgs(accepts, modelClass) { function setupOptionsArgs(accepts, modelClass) {
if (!Array.isArray(accepts)) if (!Array.isArray(accepts)) accepts = [accepts];
accepts = [accepts];
return accepts.map(function(arg) { return accepts.map(function(arg) {
if (arg.http && arg.http === 'optionsFromRequest') { if (arg.http && arg.http === 'optionsFromRequest') {
@ -500,7 +515,10 @@ module.exports = function(registry) {
*/ */
const DEFAULT_OPTIONS = { const DEFAULT_OPTIONS = {
// Default to `true` so that hidden properties cannot be used in query // Default to `true` so that hidden properties cannot be used in query
prohibitHiddenPropertiesInQuery: ModelCtor._getProhibitHiddenPropertiesInQuery({}, true), prohibitHiddenPropertiesInQuery: ModelCtor._getProhibitHiddenPropertiesInQuery(
{},
true,
),
// Default to `12` for the max depth of a query object // Default to `12` for the max depth of a query object
maxDepthOfQuery: ModelCtor._getMaxDepthOfQuery({}, 12), maxDepthOfQuery: ModelCtor._getMaxDepthOfQuery({}, 12),
// Default to `32` for the max depth of a data object // Default to `32` for the max depth of a data object
@ -510,8 +528,10 @@ module.exports = function(registry) {
if (typeof ModelCtor.createOptionsFromRemotingContext !== 'function') if (typeof ModelCtor.createOptionsFromRemotingContext !== 'function')
return DEFAULT_OPTIONS; return DEFAULT_OPTIONS;
debug('createOptionsFromRemotingContext for %s', ctx.method.stringName); debug('createOptionsFromRemotingContext for %s', ctx.method.stringName);
return Object.assign(DEFAULT_OPTIONS, return Object.assign(
ModelCtor.createOptionsFromRemotingContext(ctx)); DEFAULT_OPTIONS,
ModelCtor.createOptionsFromRemotingContext(ctx),
);
} }
/** /**
@ -524,8 +544,10 @@ module.exports = function(registry) {
*/ */
Model.disableRemoteMethod = function(name, isStatic) { Model.disableRemoteMethod = function(name, isStatic) {
deprecated('Model.disableRemoteMethod is deprecated. ' + deprecated(
'Use Model.disableRemoteMethodByName instead.'); 'Model.disableRemoteMethod is deprecated. ' +
'Use Model.disableRemoteMethodByName instead.',
);
const key = this.sharedClass.getKeyFromMethodNameAndTarget(name, isStatic); const key = this.sharedClass.getKeyFromMethodNameAndTarget(name, isStatic);
this.sharedClass.disableMethodByName(key); this.sharedClass.disableMethodByName(key);
this.emit('remoteMethodDisabled', this.sharedClass, key); this.emit('remoteMethodDisabled', this.sharedClass, key);
@ -547,7 +569,8 @@ module.exports = function(registry) {
let modelName = relation.modelTo && relation.modelTo.modelName; let modelName = relation.modelTo && relation.modelTo.modelName;
modelName = modelName || 'PersistedModel'; modelName = modelName || 'PersistedModel';
const fn = this.prototype[relationName]; const fn = this.prototype[relationName];
const pathName = (relation.options.http && relation.options.http.path) || relationName; const pathName =
(relation.options.http && relation.options.http.path) || relationName;
define('__get__' + relationName, { define('__get__' + relationName, {
isStatic: false, isStatic: false,
http: {verb: 'get', path: '/' + pathName}, http: {verb: 'get', path: '/' + pathName},
@ -565,7 +588,9 @@ module.exports = function(registry) {
if (ctx.result !== null) return cb(); if (ctx.result !== null) return cb();
const fk = ctx.getArgByName('fk'); const fk = ctx.getArgByName('fk');
const msg = g.f('Unknown "%s" id "%s".', toModelName, fk); const msg = fk ?
g.f('Unknown "%s" id "%s".', toModelName, fk) :
g.f('No "%s" instance(s) found', toModelName);
const error = new Error(msg); const error = new Error(msg);
error.statusCode = error.status = 404; error.statusCode = error.status = 404;
error.code = 'MODEL_NOT_FOUND'; error.code = 'MODEL_NOT_FOUND';
@ -573,7 +598,8 @@ module.exports = function(registry) {
} }
Model.hasOneRemoting = function(relationName, relation, define) { Model.hasOneRemoting = function(relationName, relation, define) {
const pathName = (relation.options.http && relation.options.http.path) || relationName; const pathName =
(relation.options.http && relation.options.http.path) || relationName;
const toModelName = relation.modelTo.modelName; const toModelName = relation.modelTo.modelName;
define('__get__' + relationName, { define('__get__' + relationName, {
@ -585,7 +611,11 @@ module.exports = function(registry) {
], ],
description: g.f('Fetches hasOne relation %s.', relationName), description: g.f('Fetches hasOne relation %s.', relationName),
accessType: 'READ', accessType: 'READ',
returns: {arg: relationName, type: relation.modelTo.modelName, root: true}, returns: {
arg: relationName,
type: relation.modelTo.modelName,
root: true,
},
rest: {after: convertNullToNotFoundError.bind(null, toModelName)}, rest: {after: convertNullToNotFoundError.bind(null, toModelName)},
}); });
@ -594,12 +624,17 @@ module.exports = function(registry) {
http: {verb: 'post', path: '/' + pathName}, http: {verb: 'post', path: '/' + pathName},
accepts: [ accepts: [
{ {
arg: 'data', type: 'object', model: toModelName, arg: 'data',
type: 'object',
model: toModelName,
http: {source: 'body'}, http: {source: 'body'},
}, },
{arg: 'options', type: 'object', http: 'optionsFromRequest'}, {arg: 'options', type: 'object', http: 'optionsFromRequest'},
], ],
description: g.f('Creates a new instance in %s of this model.', relationName), description: g.f(
'Creates a new instance in %s of this model.',
relationName,
),
accessType: 'WRITE', accessType: 'WRITE',
returns: {arg: 'data', type: toModelName, root: true}, returns: {arg: 'data', type: toModelName, root: true},
}); });
@ -609,7 +644,9 @@ module.exports = function(registry) {
http: {verb: 'put', path: '/' + pathName}, http: {verb: 'put', path: '/' + pathName},
accepts: [ accepts: [
{ {
arg: 'data', type: 'object', model: toModelName, arg: 'data',
type: 'object',
model: toModelName,
http: {source: 'body'}, http: {source: 'body'},
}, },
{arg: 'options', type: 'object', http: 'optionsFromRequest'}, {arg: 'options', type: 'object', http: 'optionsFromRequest'},
@ -622,16 +659,15 @@ module.exports = function(registry) {
define('__destroy__' + relationName, { define('__destroy__' + relationName, {
isStatic: false, isStatic: false,
http: {verb: 'delete', path: '/' + pathName}, http: {verb: 'delete', path: '/' + pathName},
accepts: [ accepts: [{arg: 'options', type: 'object', http: 'optionsFromRequest'}],
{arg: 'options', type: 'object', http: 'optionsFromRequest'},
],
description: g.f('Deletes %s of this model.', relationName), description: g.f('Deletes %s of this model.', relationName),
accessType: 'WRITE', accessType: 'WRITE',
}); });
}; };
Model.hasManyRemoting = function(relationName, relation, define) { Model.hasManyRemoting = function(relationName, relation, define) {
const pathName = (relation.options.http && relation.options.http.path) || relationName; const pathName =
(relation.options.http && relation.options.http.path) || relationName;
const toModelName = relation.modelTo.modelName; const toModelName = relation.modelTo.modelName;
const findByIdFunc = this.prototype['__findById__' + relationName]; const findByIdFunc = this.prototype['__findById__' + relationName];
@ -640,7 +676,8 @@ module.exports = function(registry) {
http: {verb: 'get', path: '/' + pathName + '/:fk'}, http: {verb: 'get', path: '/' + pathName + '/:fk'},
accepts: [ accepts: [
{ {
arg: 'fk', type: 'any', arg: 'fk',
type: 'any',
description: g.f('Foreign key for %s', relationName), description: g.f('Foreign key for %s', relationName),
required: true, required: true,
http: {source: 'path'}, http: {source: 'path'},
@ -659,7 +696,8 @@ module.exports = function(registry) {
http: {verb: 'delete', path: '/' + pathName + '/:fk'}, http: {verb: 'delete', path: '/' + pathName + '/:fk'},
accepts: [ accepts: [
{ {
arg: 'fk', type: 'any', arg: 'fk',
type: 'any',
description: g.f('Foreign key for %s', relationName), description: g.f('Foreign key for %s', relationName),
required: true, required: true,
http: {source: 'path'}, http: {source: 'path'},
@ -676,11 +714,19 @@ module.exports = function(registry) {
isStatic: false, isStatic: false,
http: {verb: 'put', path: '/' + pathName + '/:fk'}, http: {verb: 'put', path: '/' + pathName + '/:fk'},
accepts: [ accepts: [
{arg: 'fk', type: 'any', {
arg: 'fk',
type: 'any',
description: g.f('Foreign key for %s', relationName), description: g.f('Foreign key for %s', relationName),
required: true, required: true,
http: {source: 'path'}}, http: {source: 'path'},
{arg: 'data', type: 'object', model: toModelName, http: {source: 'body'}}, },
{
arg: 'data',
type: 'object',
model: toModelName,
http: {source: 'body'},
},
{arg: 'options', type: 'object', http: 'optionsFromRequest'}, {arg: 'options', type: 'object', http: 'optionsFromRequest'},
], ],
description: g.f('Update a related item by id for %s.', relationName), description: g.f('Update a related item by id for %s.', relationName),
@ -695,7 +741,9 @@ module.exports = function(registry) {
if (relation.type === 'hasMany' && relation.modelThrough) { if (relation.type === 'hasMany' && relation.modelThrough) {
// Restrict: only hasManyThrough relation can have additional properties // Restrict: only hasManyThrough relation can have additional properties
accepts.push({ accepts.push({
arg: 'data', type: 'object', model: modelThrough.modelName, arg: 'data',
type: 'object',
model: modelThrough.modelName,
http: {source: 'body'}, http: {source: 'body'},
}); });
} }
@ -704,13 +752,19 @@ module.exports = function(registry) {
define('__link__' + relationName, { define('__link__' + relationName, {
isStatic: false, isStatic: false,
http: {verb: 'put', path: '/' + pathName + '/rel/:fk'}, http: {verb: 'put', path: '/' + pathName + '/rel/:fk'},
accepts: [{arg: 'fk', type: 'any', accepts: [
description: g.f('Foreign key for %s', relationName), {
required: true, arg: 'fk',
http: {source: 'path'}}, type: 'any',
].concat(accepts).concat([ description: g.f('Foreign key for %s', relationName),
{arg: 'options', type: 'object', http: 'optionsFromRequest'}, required: true,
]), http: {source: 'path'},
},
]
.concat(accepts)
.concat([
{arg: 'options', type: 'object', http: 'optionsFromRequest'},
]),
description: g.f('Add a related item by id for %s.', relationName), description: g.f('Add a related item by id for %s.', relationName),
accessType: 'WRITE', accessType: 'WRITE',
returns: {arg: relationName, type: modelThrough.modelName, root: true}, returns: {arg: relationName, type: modelThrough.modelName, root: true},
@ -722,14 +776,18 @@ module.exports = function(registry) {
http: {verb: 'delete', path: '/' + pathName + '/rel/:fk'}, http: {verb: 'delete', path: '/' + pathName + '/rel/:fk'},
accepts: [ accepts: [
{ {
arg: 'fk', type: 'any', arg: 'fk',
type: 'any',
description: g.f('Foreign key for %s', relationName), description: g.f('Foreign key for %s', relationName),
required: true, required: true,
http: {source: 'path'}, http: {source: 'path'},
}, },
{arg: 'options', type: 'object', http: 'optionsFromRequest'}, {arg: 'options', type: 'object', http: 'optionsFromRequest'},
], ],
description: g.f('Remove the %s relation to an item by id.', relationName), description: g.f(
'Remove the %s relation to an item by id.',
relationName,
),
accessType: 'WRITE', accessType: 'WRITE',
returns: [], returns: [],
}, removeFunc); }, removeFunc);
@ -742,14 +800,18 @@ module.exports = function(registry) {
http: {verb: 'head', path: '/' + pathName + '/rel/:fk'}, http: {verb: 'head', path: '/' + pathName + '/rel/:fk'},
accepts: [ accepts: [
{ {
arg: 'fk', type: 'any', arg: 'fk',
type: 'any',
description: g.f('Foreign key for %s', relationName), description: g.f('Foreign key for %s', relationName),
required: true, required: true,
http: {source: 'path'}, http: {source: 'path'},
}, },
{arg: 'options', type: 'object', http: 'optionsFromRequest'}, {arg: 'options', type: 'object', http: 'optionsFromRequest'},
], ],
description: g.f('Check the existence of %s relation to an item by id.', relationName), description: g.f(
'Check the existence of %s relation to an item by id.',
relationName,
),
accessType: 'READ', accessType: 'READ',
returns: {arg: 'exists', type: 'boolean', root: true}, returns: {arg: 'exists', type: 'boolean', root: true},
rest: { rest: {
@ -774,7 +836,8 @@ module.exports = function(registry) {
Model.scopeRemoting = function(scopeName, scope, define) { Model.scopeRemoting = function(scopeName, scope, define) {
const pathName = const pathName =
(scope.options && scope.options.http && scope.options.http.path) || scopeName; (scope.options && scope.options.http && scope.options.http.path) ||
scopeName;
let modelTo = scope.modelTo; let modelTo = scope.modelTo;
@ -795,7 +858,8 @@ module.exports = function(registry) {
// createOnlyInstance flag in __create__ to indicate loopback-swagger // createOnlyInstance flag in __create__ to indicate loopback-swagger
// code to create a separate model instance for create operation only // code to create a separate model instance for create operation only
const updateOnlyProps = modelTo.getUpdateOnlyProperties ? const updateOnlyProps = modelTo.getUpdateOnlyProperties ?
modelTo.getUpdateOnlyProperties() : false; modelTo.getUpdateOnlyProperties() :
false;
const hasUpdateOnlyProps = updateOnlyProps && updateOnlyProps.length > 0; const hasUpdateOnlyProps = updateOnlyProps && updateOnlyProps.length > 0;
define('__get__' + scopeName, { define('__get__' + scopeName, {
@ -824,7 +888,10 @@ module.exports = function(registry) {
}, },
{arg: 'options', type: 'object', http: 'optionsFromRequest'}, {arg: 'options', type: 'object', http: 'optionsFromRequest'},
], ],
description: g.f('Creates a new instance in %s of this model.', scopeName), description: g.f(
'Creates a new instance in %s of this model.',
scopeName,
),
accessType: 'WRITE', accessType: 'WRITE',
returns: {arg: 'data', type: toModelName, root: true}, returns: {arg: 'data', type: toModelName, root: true},
}); });
@ -834,11 +901,14 @@ module.exports = function(registry) {
http: {verb: 'delete', path: '/' + pathName}, http: {verb: 'delete', path: '/' + pathName},
accepts: [ accepts: [
{ {
arg: 'where', type: 'object', arg: 'where',
type: 'object',
// The "where" argument is not exposed in the REST API // The "where" argument is not exposed in the REST API
// but we need to provide a value so that we can pass "options" // but we need to provide a value so that we can pass "options"
// as the third argument. // as the third argument.
http: function(ctx) { return undefined; }, http: function(ctx) {
return undefined;
},
}, },
{arg: 'options', type: 'object', http: 'optionsFromRequest'}, {arg: 'options', type: 'object', http: 'optionsFromRequest'},
], ],
@ -851,7 +921,8 @@ module.exports = function(registry) {
http: {verb: 'get', path: '/' + pathName + '/count'}, http: {verb: 'get', path: '/' + pathName + '/count'},
accepts: [ accepts: [
{ {
arg: 'where', type: 'object', arg: 'where',
type: 'object',
description: 'Criteria to match model instances', description: 'Criteria to match model instances',
}, },
{arg: 'options', type: 'object', http: 'optionsFromRequest'}, {arg: 'options', type: 'object', http: 'optionsFromRequest'},
@ -863,18 +934,18 @@ module.exports = function(registry) {
}; };
/** /**
* Enabled deeply-nested queries of related models via REST API. * Enabled deeply-nested queries of related models via REST API.
* *
* @param {String} relationName Name of the nested relation. * @param {String} relationName Name of the nested relation.
* @options {Object} [options] It is optional. See below. * @options {Object} [options] It is optional. See below.
* @param {String} pathName The HTTP path (relative to the model) at which your remote method is exposed. * @param {String} pathName The HTTP path (relative to the model) at which your remote method is exposed.
* @param {String} filterMethod The filter name. * @param {String} filterMethod The filter name.
* @param {String} paramName The argument name that the remote method accepts. * @param {String} paramName The argument name that the remote method accepts.
* @param {String} getterName The getter name. * @param {String} getterName The getter name.
* @param {Boolean} hooks Whether to inherit before/after hooks. * @param {Boolean} hooks Whether to inherit before/after hooks.
* @callback {Function} filterCallback The Optional filter function. * @callback {Function} filterCallback The Optional filter function.
* @param {Object} SharedMethod object. See [here](https://apidocs.strongloop.com/strong-remoting/#sharedmethod). * @param {Object} SharedMethod object. See [here](https://apidocs.strongloop.com/strong-remoting/#sharedmethod).
* @param {Object} RelationDefinition object which includes relation `type`, `ModelConstructor` of `modelFrom`, `modelTo`, `keyFrom`, `keyTo` and more relation definitions. * @param {Object} RelationDefinition object which includes relation `type`, `ModelConstructor` of `modelFrom`, `modelTo`, `keyFrom`, `keyTo` and more relation definitions.
*/ */
Model.nestRemoting = function(relationName, options, filterCallback) { Model.nestRemoting = function(relationName, options, filterCallback) {
@ -895,7 +966,8 @@ module.exports = function(registry) {
const sharedToClass = relation.modelTo.sharedClass; const sharedToClass = relation.modelTo.sharedClass;
const toModelName = relation.modelTo.modelName; const toModelName = relation.modelTo.modelName;
const pathName = options.pathName || relation.options.path || relationName; const pathName =
options.pathName || relation.options.path || relationName;
const paramName = options.paramName || 'nk'; const paramName = options.paramName || 'nk';
const http = [].concat(sharedToClass.http || [])[0]; const http = [].concat(sharedToClass.http || [])[0];
@ -905,7 +977,9 @@ module.exports = function(registry) {
httpPath = pathName + '/:' + paramName; httpPath = pathName + '/:' + paramName;
acceptArgs = [ acceptArgs = [
{ {
arg: paramName, type: 'any', http: {source: 'path'}, arg: paramName,
type: 'any',
http: {source: 'path'},
description: g.f('Foreign key for %s.', relation.name), description: g.f('Foreign key for %s.', relation.name),
required: true, required: true,
}, },
@ -921,18 +995,21 @@ module.exports = function(registry) {
// A method should return the method name to use, if it is to be // A method should return the method name to use, if it is to be
// included as a nested method - a falsy return value will skip. // included as a nested method - a falsy return value will skip.
const filter = filterCallback || options.filterMethod || function(method, relation) { const filter =
const matches = method.name.match(regExp); filterCallback ||
if (matches) { options.filterMethod ||
return '__' + matches[1] + '__' + relation.name + '__' + matches[2]; function(method, relation) {
} const matches = method.name.match(regExp);
}; if (matches) {
return '__' + matches[1] + '__' + relation.name + '__' + matches[2];
}
};
sharedToClass.methods().forEach(function(method) { sharedToClass.methods().forEach(function(method) {
let methodName; let methodName;
if (!method.isStatic && (methodName = filter(method, relation))) { if (!method.isStatic && (methodName = filter(method, relation))) {
const prefix = relation.multiple ? '__findById__' : '__get__'; const prefix = relation.multiple ? '__findById__' : '__get__';
const getterName = options.getterName || (prefix + relationName); const getterName = options.getterName || prefix + relationName;
const getterFn = relation.modelFrom.prototype[getterName]; const getterFn = relation.modelFrom.prototype[getterName];
if (typeof getterFn !== 'function') { if (typeof getterFn !== 'function') {
@ -966,46 +1043,57 @@ module.exports = function(registry) {
const lastArg = opts.accepts[opts.accepts.length - 1] || {}; const lastArg = opts.accepts[opts.accepts.length - 1] || {};
const hasOptionsFromContext = const hasOptionsFromContext =
(lastArg.arg || lastArg.name) === 'options' && (lastArg.arg || lastArg.name) === 'options' &&
lastArg.type === 'object' && lastArg.http; lastArg.type === 'object' &&
lastArg.http;
if (relation.multiple) { if (relation.multiple) {
sharedClass.defineMethod(methodName, opts, function(fkId) { sharedClass.defineMethod(
const args = Array.prototype.slice.call(arguments, 1); methodName,
const cb = args[args.length - 1]; opts,
const contextOptions = function(fkId) {
hasOptionsFromContext && args[args.length - 2] || {}; const args = Array.prototype.slice.call(arguments, 1);
this[getterName](fkId, contextOptions, function(err, inst) { const cb = args[args.length - 1];
if (err) return cb(err); const contextOptions =
if (inst instanceof relation.modelTo) { (hasOptionsFromContext && args[args.length - 2]) || {};
try { this[getterName](fkId, contextOptions, function(err, inst) {
nestedFn.apply(inst, args); if (err) return cb(err);
} catch (err) { if (inst instanceof relation.modelTo) {
return cb(err); try {
nestedFn.apply(inst, args);
} catch (err) {
return cb(err);
}
} else {
cb(err, null);
} }
} else { });
cb(err, null); },
} method.isStatic,
}); );
}, method.isStatic);
} else { } else {
sharedClass.defineMethod(methodName, opts, function() { sharedClass.defineMethod(
const args = Array.prototype.slice.call(arguments); methodName,
const cb = args[args.length - 1]; opts,
const contextOptions = function() {
hasOptionsFromContext && args[args.length - 2] || {}; const args = Array.prototype.slice.call(arguments);
this[getterName](contextOptions, function(err, inst) { const cb = args[args.length - 1];
if (err) return cb(err); const contextOptions =
if (inst instanceof relation.modelTo) { (hasOptionsFromContext && args[args.length - 2]) || {};
try { this[getterName](contextOptions, function(err, inst) {
nestedFn.apply(inst, args); if (err) return cb(err);
} catch (err) { if (inst instanceof relation.modelTo) {
return cb(err); try {
nestedFn.apply(inst, args);
} catch (err) {
return cb(err);
}
} else {
cb(err, null);
} }
} else { });
cb(err, null); },
} method.isStatic,
}); );
}, method.isStatic);
} }
} }
}); });
@ -1025,9 +1113,15 @@ module.exports = function(registry) {
sharedClass.methods().forEach(function(method) { sharedClass.methods().forEach(function(method) {
const delegateTo = method.rest && method.rest.delegateTo; const delegateTo = method.rest && method.rest.delegateTo;
if (delegateTo && delegateTo.ctor == relation.modelTo) { if (delegateTo && delegateTo.ctor == relation.modelTo) {
const before = method.isStatic ? beforeListeners : beforeListeners['prototype']; const before = method.isStatic ?
const after = method.isStatic ? afterListeners : afterListeners['prototype']; beforeListeners :
const m = method.isStatic ? method.name : 'prototype.' + method.name; beforeListeners['prototype'];
const after = method.isStatic ?
afterListeners :
afterListeners['prototype'];
const m = method.isStatic ?
method.name :
'prototype.' + method.name;
if (before && before[delegateTo.name]) { if (before && before[delegateTo.name]) {
self.beforeRemote(m, function(ctx, result, next) { self.beforeRemote(m, function(ctx, result, next) {
before[delegateTo.name]._listeners.call(null, ctx, next); before[delegateTo.name]._listeners.call(null, ctx, next);
@ -1042,7 +1136,11 @@ module.exports = function(registry) {
}); });
}); });
} else { } else {
const msg = g.f('Relation `%s` does not exist for model `%s`', relationName, this.modelName); const msg = g.f(
'Relation `%s` does not exist for model `%s`',
relationName,
this.modelName,
);
throw new Error(msg); throw new Error(msg);
} }
}; };

File diff suppressed because it is too large Load Diff