setRemoting for relation methods when setup
This commit is contained in:
parent
cc95860c68
commit
63e7a01fcf
344
lib/model.js
344
lib/model.js
|
@ -233,36 +233,8 @@ module.exports = function(registry) {
|
|||
});
|
||||
};
|
||||
|
||||
// resolve relation functions
|
||||
sharedClass.resolve(function resolver(define) {
|
||||
var relations = ModelCtor.relations || {};
|
||||
|
||||
// get the relations
|
||||
for (var relationName in relations) {
|
||||
var relation = relations[relationName];
|
||||
if (relation.type === 'belongsTo') {
|
||||
ModelCtor.belongsToRemoting(relationName, relation, define);
|
||||
} else if (
|
||||
relation.type === 'hasOne' ||
|
||||
relation.type === 'embedsOne'
|
||||
) {
|
||||
ModelCtor.hasOneRemoting(relationName, relation, define);
|
||||
} else if (
|
||||
relation.type === 'hasMany' ||
|
||||
relation.type === 'embedsMany' ||
|
||||
relation.type === 'referencesMany') {
|
||||
ModelCtor.hasManyRemoting(relationName, relation, define);
|
||||
}
|
||||
}
|
||||
|
||||
// handle scopes
|
||||
var scopes = ModelCtor.scopes || {};
|
||||
/* eslint-disable one-var */
|
||||
for (var scopeName in scopes) {
|
||||
ModelCtor.scopeRemoting(scopeName, scopes[scopeName], define);
|
||||
}
|
||||
/* eslint-enable one-var */
|
||||
});
|
||||
// setRemoting for relation methods
|
||||
ModelCtor.setupRemotingRelation();
|
||||
|
||||
return ModelCtor;
|
||||
};
|
||||
|
@ -444,131 +416,196 @@ module.exports = function(registry) {
|
|||
this.sharedClass.disableMethod(name, isStatic || false);
|
||||
this.emit('remoteMethodDisabled', this.sharedClass, name);
|
||||
};
|
||||
|
||||
|
||||
Model.belongsToRemoting = function(relationName, relation, define) {
|
||||
var modelName = relation.modelTo && relation.modelTo.modelName;
|
||||
modelName = modelName || 'PersistedModel';
|
||||
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' }},
|
||||
accessType: 'READ',
|
||||
description: format('Fetches belongsTo relation %s.', relationName),
|
||||
returns: { arg: relationName, type: modelName, root: true },
|
||||
}, fn);
|
||||
};
|
||||
Model.setupRemotingRelation = function() {
|
||||
var ModelCtor = this;
|
||||
var typeName = ModelCtor.modelName;
|
||||
var options = ModelCtor.settings;
|
||||
|
||||
function convertNullToNotFoundError(toModelName, ctx, cb) {
|
||||
if (ctx.result !== null) return cb();
|
||||
var relations = ModelCtor.settings.relations || {};
|
||||
|
||||
var fk = ctx.getArgByName('fk');
|
||||
var msg = g.f('Unknown "%s" id "%s".', toModelName, fk);
|
||||
var error = new Error(msg);
|
||||
error.statusCode = error.status = 404;
|
||||
error.code = 'MODEL_NOT_FOUND';
|
||||
cb(error);
|
||||
}
|
||||
// get the relations
|
||||
for (var relationName in relations) {
|
||||
var relation = relations[relationName];
|
||||
if (relation.type === 'belongsTo') {
|
||||
ModelCtor.prototype['__get__' + relationName] = function(data, callback) {
|
||||
throwNotAttached(this.modelName, relationName);
|
||||
};
|
||||
belongsToRemoting(relationName, relation);
|
||||
} else if (
|
||||
relation.type === 'hasOne' ||
|
||||
relation.type === 'embedsOne'
|
||||
) {
|
||||
ModelCtor.prototype['__get__' + relationName] = function(data, callback) {
|
||||
throwNotAttached(this.modelName, '__get__' + relationName);
|
||||
};
|
||||
ModelCtor.prototype['__create__' + relationName] = function(data, callback) {
|
||||
throwNotAttached(this.modelName, '__create__' + relationName);
|
||||
};
|
||||
ModelCtor.prototype['__update__' + relationName] = function(data, callback) {
|
||||
throwNotAttached(this.modelName, '__update__' + relationName);
|
||||
};
|
||||
ModelCtor.prototype['__destroy__' + relationName] = function(data, callback) {
|
||||
throwNotAttached(this.modelName, '__destory__' + relationName);
|
||||
};
|
||||
hasOneRemoting(relationName, relation);
|
||||
} else if (
|
||||
relation.type === 'hasMany' ||
|
||||
relation.type === 'embedsMany' ||
|
||||
relation.type === 'referencesMany'
|
||||
) {
|
||||
ModelCtor.prototype['__findById__' + relationName] = function(data, callback) {
|
||||
throwNotAttached(this.modelName, relationName);
|
||||
};
|
||||
ModelCtor.prototype['__destroyById__' + relationName] = function(data, callback) {
|
||||
throwNotAttached(this.modelName, relationName);
|
||||
};
|
||||
ModelCtor.prototype['__updateById__' + relationName] = function(data, callback) {
|
||||
throwNotAttached(this.modelName, relationName);
|
||||
};
|
||||
hasManyRemoting(relationName, relation);
|
||||
|
||||
if (relation.through || relation.type === 'referencesMany') {
|
||||
var modelThrough = relation.through || relation.model;
|
||||
|
||||
Model.hasOneRemoting = function(relationName, relation, define) {
|
||||
var pathName = (relation.options.http && relation.options.http.path) || relationName;
|
||||
var toModelName = relation.modelTo.modelName;
|
||||
var accepts = [];
|
||||
if (relation.type === 'hasMany' && relation.through) {
|
||||
// Restrict: only hasManyThrough relation can have additional properties
|
||||
accepts.push({ arg: 'data', type: modelThrough.modelName, http: { source: 'body' }});
|
||||
}
|
||||
ModelCtor.prototype['__link__' + relationName] = function(data, callback) {
|
||||
throwNotAttached(this.modelName, relationName);
|
||||
};
|
||||
ModelCtor.prototype['__unlink__' + relationName] = function(data, callback) {
|
||||
throwNotAttached(this.modelName, relationName);
|
||||
};
|
||||
ModelCtor.prototype['__exists__' + relationName] = function(data, callback) {
|
||||
throwNotAttached(this.modelName, relationName);
|
||||
};
|
||||
throughOrReferencesRemoting(relationName, relation);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
define('__get__' + relationName, {
|
||||
isStatic: false,
|
||||
http: { verb: 'get', path: '/' + pathName },
|
||||
accepts: { arg: 'refresh', type: 'boolean', http: { source: 'query' }},
|
||||
description: format('Fetches hasOne relation %s.', relationName),
|
||||
accessType: 'READ',
|
||||
returns: { arg: relationName, type: relation.modelTo.modelName, root: true },
|
||||
rest: { after: convertNullToNotFoundError.bind(null, toModelName) },
|
||||
});
|
||||
function setRemoting(name, options) {
|
||||
var fn = ModelCtor.prototype[name];
|
||||
fn._delegate = true;
|
||||
ModelCtor.remoteMethod(name, options);
|
||||
};
|
||||
|
||||
define('__create__' + relationName, {
|
||||
isStatic: false,
|
||||
http: { verb: 'post', path: '/' + pathName },
|
||||
accepts: { arg: 'data', type: toModelName, http: { source: 'body' }},
|
||||
description: format('Creates a new instance in %s of this model.', relationName),
|
||||
accessType: 'WRITE',
|
||||
returns: { arg: 'data', type: toModelName, root: true },
|
||||
});
|
||||
function belongsToRemoting(relationName, relation) {
|
||||
relation.options = relation.options || {};
|
||||
var modelName = relation.model;
|
||||
var pathName = (relation.options.http && relation.options.http.path) || relationName;
|
||||
|
||||
define('__update__' + relationName, {
|
||||
isStatic: false,
|
||||
http: { verb: 'put', path: '/' + pathName },
|
||||
accepts: { arg: 'data', type: toModelName, http: { source: 'body' }},
|
||||
description: format('Update %s of this model.', relationName),
|
||||
accessType: 'WRITE',
|
||||
returns: { arg: 'data', type: toModelName, root: true },
|
||||
});
|
||||
setRemoting('__get__' + relationName, {
|
||||
isStatic: false,
|
||||
http: { verb: 'get', path: '/' + pathName },
|
||||
accepts: { arg: 'refresh', type: 'boolean', http: { source: 'query' }},
|
||||
accessType: 'READ',
|
||||
description: format('Fetches belongsTo relation %s.', relationName),
|
||||
returns: { arg: relationName, type: modelName, root: true },
|
||||
});
|
||||
};
|
||||
|
||||
define('__destroy__' + relationName, {
|
||||
isStatic: false,
|
||||
http: { verb: 'delete', path: '/' + pathName },
|
||||
description: format('Deletes %s of this model.', relationName),
|
||||
accessType: 'WRITE',
|
||||
});
|
||||
};
|
||||
function convertNullToNotFoundError(toModelName, ctx, cb) {
|
||||
if (ctx.result !== null) return cb();
|
||||
|
||||
Model.hasManyRemoting = function(relationName, relation, define) {
|
||||
var pathName = (relation.options.http && relation.options.http.path) || relationName;
|
||||
var toModelName = relation.modelTo.modelName;
|
||||
var fk = ctx.getArgByName('fk');
|
||||
var msg = g.f('Unknown "%s" id "%s".', toModelName, fk);
|
||||
var error = new Error(msg);
|
||||
error.statusCode = error.status = 404;
|
||||
error.code = 'MODEL_NOT_FOUND';
|
||||
cb(error);
|
||||
};
|
||||
|
||||
var findByIdFunc = this.prototype['__findById__' + relationName];
|
||||
define('__findById__' + relationName, {
|
||||
isStatic: false,
|
||||
http: { verb: 'get', path: '/' + pathName + '/:fk' },
|
||||
accepts: { arg: 'fk', type: 'any',
|
||||
description: format('Foreign key for %s', relationName),
|
||||
required: true,
|
||||
http: { source: 'path' }},
|
||||
description: format('Find a related item by id for %s.', relationName),
|
||||
accessType: 'READ',
|
||||
returns: { arg: 'result', type: toModelName, root: true },
|
||||
rest: { after: convertNullToNotFoundError.bind(null, toModelName) },
|
||||
}, findByIdFunc);
|
||||
function hasOneRemoting(relationName, relation) {
|
||||
relation.options = relation.options || {};
|
||||
var pathName = (relation.options.http && relation.options.http.path) || relationName;
|
||||
var toModelName = relation.modelName;
|
||||
|
||||
var destroyByIdFunc = this.prototype['__destroyById__' + relationName];
|
||||
define('__destroyById__' + relationName, {
|
||||
isStatic: false,
|
||||
http: { verb: 'delete', path: '/' + pathName + '/:fk' },
|
||||
accepts: { arg: 'fk', type: 'any',
|
||||
description: format('Foreign key for %s', relationName),
|
||||
required: true,
|
||||
http: { source: 'path' }},
|
||||
description: format('Delete a related item by id for %s.', relationName),
|
||||
accessType: 'WRITE',
|
||||
returns: [],
|
||||
}, destroyByIdFunc);
|
||||
setRemoting('__get__' + relationName, {
|
||||
isStatic: false,
|
||||
http: { verb: 'get', path: '/' + pathName },
|
||||
accepts: { arg: 'refresh', type: 'boolean', http: { source: 'query' }},
|
||||
description: format('Fetches hasOne relation %s.', relationName),
|
||||
accessType: 'READ',
|
||||
returns: { arg: relationName, type: toModelName, root: true },
|
||||
rest: { after: convertNullToNotFoundError.bind(null, toModelName) },
|
||||
});
|
||||
|
||||
var updateByIdFunc = this.prototype['__updateById__' + relationName];
|
||||
define('__updateById__' + relationName, {
|
||||
isStatic: false,
|
||||
http: { verb: 'put', path: '/' + pathName + '/:fk' },
|
||||
accepts: [
|
||||
{ arg: 'fk', type: 'any',
|
||||
setRemoting('__create__' + relationName, {
|
||||
isStatic: false,
|
||||
http: { verb: 'post', path: '/' + pathName },
|
||||
accepts: { arg: 'data', type: toModelName, http: { source: 'body' }},
|
||||
description: format('Creates a new instance in %s of this model.', relationName),
|
||||
accessType: 'WRITE',
|
||||
returns: { arg: 'data', type: toModelName, root: true },
|
||||
});
|
||||
|
||||
setRemoting('__update__' + relationName, {
|
||||
isStatic: false,
|
||||
http: { verb: 'put', path: '/' + pathName },
|
||||
accepts: { arg: 'data', type: toModelName, http: { source: 'body' }},
|
||||
description: format('Update %s of this model.', relationName),
|
||||
accessType: 'WRITE',
|
||||
returns: { arg: 'data', type: toModelName, root: true },
|
||||
});
|
||||
|
||||
setRemoting('__destroy__' + relationName, {
|
||||
isStatic: false,
|
||||
http: { verb: 'delete', path: '/' + pathName },
|
||||
description: format('Deletes %s of this model.', relationName),
|
||||
accessType: 'WRITE',
|
||||
});
|
||||
};
|
||||
function hasManyRemoting(relationName, relation) {
|
||||
relation.options = relation.options || {};
|
||||
var pathName = (relation.options.http && relation.options.http.path) || relationName;
|
||||
var toModelName = relation.modelName;
|
||||
|
||||
setRemoting('__findById__' + relationName, {
|
||||
isStatic: false,
|
||||
http: { verb: 'get', path: '/' + pathName + '/:fk' },
|
||||
accepts: { arg: 'fk', type: 'any',
|
||||
description: format('Foreign key for %s', relationName),
|
||||
required: true,
|
||||
http: { source: 'path' }},
|
||||
{ arg: 'data', type: toModelName, http: { source: 'body' }},
|
||||
],
|
||||
description: format('Update a related item by id for %s.', relationName),
|
||||
accessType: 'WRITE',
|
||||
returns: { arg: 'result', type: toModelName, root: true },
|
||||
}, updateByIdFunc);
|
||||
description: format('Find a related item by id for %s.', relationName),
|
||||
accessType: 'READ',
|
||||
returns: { arg: 'result', type: toModelName, root: true },
|
||||
rest: { after: convertNullToNotFoundError.bind(null, toModelName) },
|
||||
});
|
||||
setRemoting('__destroyById__' + relationName, {
|
||||
isStatic: false,
|
||||
http: { verb: 'delete', path: '/' + pathName + '/:fk' },
|
||||
accepts: { arg: 'fk', type: 'any',
|
||||
description: format('Foreign key for %s', relationName),
|
||||
required: true,
|
||||
http: { source: 'path' }},
|
||||
description: format('Delete a related item by id for %s.', relationName),
|
||||
accessType: 'WRITE',
|
||||
returns: [],
|
||||
});
|
||||
setRemoting('__updateById__' + relationName, {
|
||||
isStatic: false,
|
||||
http: { verb: 'put', path: '/' + pathName + '/:fk' },
|
||||
accepts: [
|
||||
{ arg: 'fk', type: 'any',
|
||||
description: format('Foreign key for %s', relationName),
|
||||
required: true,
|
||||
http: { source: 'path' }},
|
||||
{ arg: 'data', type: toModelName, http: { source: 'body' }},
|
||||
],
|
||||
description: format('Update a related item by id for %s.', relationName),
|
||||
accessType: 'WRITE',
|
||||
returns: { arg: 'result', type: toModelName, root: true },
|
||||
});
|
||||
};
|
||||
|
||||
if (relation.modelThrough || relation.type === 'referencesMany') {
|
||||
var modelThrough = relation.modelThrough || relation.modelTo;
|
||||
|
||||
var accepts = [];
|
||||
if (relation.type === 'hasMany' && relation.modelThrough) {
|
||||
// Restrict: only hasManyThrough relation can have additional properties
|
||||
accepts.push({ arg: 'data', type: modelThrough.modelName, http: { source: 'body' }});
|
||||
}
|
||||
|
||||
var addFunc = this.prototype['__link__' + relationName];
|
||||
define('__link__' + relationName, {
|
||||
function throughOrReferenceRemoting(relationName, relation) {
|
||||
setRemoting('__link__' + relationName, {
|
||||
isStatic: false,
|
||||
http: { verb: 'put', path: '/' + pathName + '/rel/:fk' },
|
||||
accepts: [{ arg: 'fk', type: 'any',
|
||||
|
@ -578,10 +615,9 @@ module.exports = function(registry) {
|
|||
description: format('Add a related item by id for %s.', relationName),
|
||||
accessType: 'WRITE',
|
||||
returns: { arg: relationName, type: modelThrough.modelName, root: true },
|
||||
}, addFunc);
|
||||
});
|
||||
|
||||
var removeFunc = this.prototype['__unlink__' + relationName];
|
||||
define('__unlink__' + relationName, {
|
||||
setRemoting('__unlink__' + relationName, {
|
||||
isStatic: false,
|
||||
http: { verb: 'delete', path: '/' + pathName + '/rel/:fk' },
|
||||
accepts: { arg: 'fk', type: 'any',
|
||||
|
@ -591,12 +627,11 @@ module.exports = function(registry) {
|
|||
description: format('Remove the %s relation to an item by id.', relationName),
|
||||
accessType: 'WRITE',
|
||||
returns: [],
|
||||
}, removeFunc);
|
||||
});
|
||||
|
||||
// FIXME: [rfeng] How to map a function with callback(err, true|false) to HEAD?
|
||||
// true --> 200 and false --> 404?
|
||||
var existsFunc = this.prototype['__exists__' + relationName];
|
||||
define('__exists__' + relationName, {
|
||||
setRemoting('__exists__' + relationName, {
|
||||
isStatic: false,
|
||||
http: { verb: 'head', path: '/' + pathName + '/rel/:fk' },
|
||||
accepts: { arg: 'fk', type: 'any',
|
||||
|
@ -622,8 +657,23 @@ module.exports = function(registry) {
|
|||
}
|
||||
},
|
||||
},
|
||||
}, existsFunc);
|
||||
}
|
||||
});
|
||||
};
|
||||
// // setRemoting for scope methods
|
||||
// var scopes = ModelCtor.scopes || {};
|
||||
// /* eslint-disable one-var */
|
||||
// for (var scopeName in scopes) {
|
||||
// ModelCtor.scopeRemoting(scopeName, scopes[scopeName], define);
|
||||
// }
|
||||
// /* eslint-enable one-var */
|
||||
function throwNotAttached(modelName, methodName) {
|
||||
throw new Error(
|
||||
g.f('Cannot call %s.%s().' +
|
||||
' The %s method has not been setup.' +
|
||||
' The {{Model}} has not been correctly attached to a {{DataSource}}!',
|
||||
modelName, methodName, methodName)
|
||||
);
|
||||
};
|
||||
};
|
||||
|
||||
Model.scopeRemoting = function(scopeName, scope, define) {
|
||||
|
|
Loading…
Reference in New Issue