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
|
// setRemoting for relation methods
|
||||||
sharedClass.resolve(function resolver(define) {
|
ModelCtor.setupRemotingRelation();
|
||||||
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 */
|
|
||||||
});
|
|
||||||
|
|
||||||
return ModelCtor;
|
return ModelCtor;
|
||||||
};
|
};
|
||||||
|
@ -444,131 +416,196 @@ module.exports = function(registry) {
|
||||||
this.sharedClass.disableMethod(name, isStatic || false);
|
this.sharedClass.disableMethod(name, isStatic || false);
|
||||||
this.emit('remoteMethodDisabled', this.sharedClass, name);
|
this.emit('remoteMethodDisabled', this.sharedClass, name);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
Model.belongsToRemoting = function(relationName, relation, define) {
|
Model.setupRemotingRelation = function() {
|
||||||
var modelName = relation.modelTo && relation.modelTo.modelName;
|
var ModelCtor = this;
|
||||||
modelName = modelName || 'PersistedModel';
|
var typeName = ModelCtor.modelName;
|
||||||
var fn = this.prototype[relationName];
|
var options = ModelCtor.settings;
|
||||||
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);
|
|
||||||
};
|
|
||||||
|
|
||||||
function convertNullToNotFoundError(toModelName, ctx, cb) {
|
var relations = ModelCtor.settings.relations || {};
|
||||||
if (ctx.result !== null) return cb();
|
|
||||||
|
|
||||||
var fk = ctx.getArgByName('fk');
|
// get the relations
|
||||||
var msg = g.f('Unknown "%s" id "%s".', toModelName, fk);
|
for (var relationName in relations) {
|
||||||
var error = new Error(msg);
|
var relation = relations[relationName];
|
||||||
error.statusCode = error.status = 404;
|
if (relation.type === 'belongsTo') {
|
||||||
error.code = 'MODEL_NOT_FOUND';
|
ModelCtor.prototype['__get__' + relationName] = function(data, callback) {
|
||||||
cb(error);
|
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 accepts = [];
|
||||||
var pathName = (relation.options.http && relation.options.http.path) || relationName;
|
if (relation.type === 'hasMany' && relation.through) {
|
||||||
var toModelName = relation.modelTo.modelName;
|
// 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, {
|
function setRemoting(name, options) {
|
||||||
isStatic: false,
|
var fn = ModelCtor.prototype[name];
|
||||||
http: { verb: 'get', path: '/' + pathName },
|
fn._delegate = true;
|
||||||
accepts: { arg: 'refresh', type: 'boolean', http: { source: 'query' }},
|
ModelCtor.remoteMethod(name, options);
|
||||||
description: format('Fetches hasOne relation %s.', relationName),
|
};
|
||||||
accessType: 'READ',
|
|
||||||
returns: { arg: relationName, type: relation.modelTo.modelName, root: true },
|
|
||||||
rest: { after: convertNullToNotFoundError.bind(null, toModelName) },
|
|
||||||
});
|
|
||||||
|
|
||||||
define('__create__' + relationName, {
|
function belongsToRemoting(relationName, relation) {
|
||||||
isStatic: false,
|
relation.options = relation.options || {};
|
||||||
http: { verb: 'post', path: '/' + pathName },
|
var modelName = relation.model;
|
||||||
accepts: { arg: 'data', type: toModelName, http: { source: 'body' }},
|
var pathName = (relation.options.http && relation.options.http.path) || relationName;
|
||||||
description: format('Creates a new instance in %s of this model.', relationName),
|
|
||||||
accessType: 'WRITE',
|
|
||||||
returns: { arg: 'data', type: toModelName, root: true },
|
|
||||||
});
|
|
||||||
|
|
||||||
define('__update__' + relationName, {
|
setRemoting('__get__' + relationName, {
|
||||||
isStatic: false,
|
isStatic: false,
|
||||||
http: { verb: 'put', path: '/' + pathName },
|
http: { verb: 'get', path: '/' + pathName },
|
||||||
accepts: { arg: 'data', type: toModelName, http: { source: 'body' }},
|
accepts: { arg: 'refresh', type: 'boolean', http: { source: 'query' }},
|
||||||
description: format('Update %s of this model.', relationName),
|
accessType: 'READ',
|
||||||
accessType: 'WRITE',
|
description: format('Fetches belongsTo relation %s.', relationName),
|
||||||
returns: { arg: 'data', type: toModelName, root: true },
|
returns: { arg: relationName, type: modelName, root: true },
|
||||||
});
|
});
|
||||||
|
};
|
||||||
|
|
||||||
define('__destroy__' + relationName, {
|
function convertNullToNotFoundError(toModelName, ctx, cb) {
|
||||||
isStatic: false,
|
if (ctx.result !== null) return cb();
|
||||||
http: { verb: 'delete', path: '/' + pathName },
|
|
||||||
description: format('Deletes %s of this model.', relationName),
|
|
||||||
accessType: 'WRITE',
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
Model.hasManyRemoting = function(relationName, relation, define) {
|
var fk = ctx.getArgByName('fk');
|
||||||
var pathName = (relation.options.http && relation.options.http.path) || relationName;
|
var msg = g.f('Unknown "%s" id "%s".', toModelName, fk);
|
||||||
var toModelName = relation.modelTo.modelName;
|
var error = new Error(msg);
|
||||||
|
error.statusCode = error.status = 404;
|
||||||
|
error.code = 'MODEL_NOT_FOUND';
|
||||||
|
cb(error);
|
||||||
|
};
|
||||||
|
|
||||||
var findByIdFunc = this.prototype['__findById__' + relationName];
|
function hasOneRemoting(relationName, relation) {
|
||||||
define('__findById__' + relationName, {
|
relation.options = relation.options || {};
|
||||||
isStatic: false,
|
var pathName = (relation.options.http && relation.options.http.path) || relationName;
|
||||||
http: { verb: 'get', path: '/' + pathName + '/:fk' },
|
var toModelName = relation.modelName;
|
||||||
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);
|
|
||||||
|
|
||||||
var destroyByIdFunc = this.prototype['__destroyById__' + relationName];
|
setRemoting('__get__' + relationName, {
|
||||||
define('__destroyById__' + relationName, {
|
isStatic: false,
|
||||||
isStatic: false,
|
http: { verb: 'get', path: '/' + pathName },
|
||||||
http: { verb: 'delete', path: '/' + pathName + '/:fk' },
|
accepts: { arg: 'refresh', type: 'boolean', http: { source: 'query' }},
|
||||||
accepts: { arg: 'fk', type: 'any',
|
description: format('Fetches hasOne relation %s.', relationName),
|
||||||
description: format('Foreign key for %s', relationName),
|
accessType: 'READ',
|
||||||
required: true,
|
returns: { arg: relationName, type: toModelName, root: true },
|
||||||
http: { source: 'path' }},
|
rest: { after: convertNullToNotFoundError.bind(null, toModelName) },
|
||||||
description: format('Delete a related item by id for %s.', relationName),
|
});
|
||||||
accessType: 'WRITE',
|
|
||||||
returns: [],
|
|
||||||
}, destroyByIdFunc);
|
|
||||||
|
|
||||||
var updateByIdFunc = this.prototype['__updateById__' + relationName];
|
setRemoting('__create__' + relationName, {
|
||||||
define('__updateById__' + relationName, {
|
isStatic: false,
|
||||||
isStatic: false,
|
http: { verb: 'post', path: '/' + pathName },
|
||||||
http: { verb: 'put', path: '/' + pathName + '/:fk' },
|
accepts: { arg: 'data', type: toModelName, http: { source: 'body' }},
|
||||||
accepts: [
|
description: format('Creates a new instance in %s of this model.', relationName),
|
||||||
{ arg: 'fk', type: 'any',
|
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),
|
description: format('Foreign key for %s', relationName),
|
||||||
required: true,
|
required: true,
|
||||||
http: { source: 'path' }},
|
http: { source: 'path' }},
|
||||||
{ arg: 'data', type: toModelName, http: { source: 'body' }},
|
description: format('Find a related item by id for %s.', relationName),
|
||||||
],
|
accessType: 'READ',
|
||||||
description: format('Update a related item by id for %s.', relationName),
|
returns: { arg: 'result', type: toModelName, root: true },
|
||||||
accessType: 'WRITE',
|
rest: { after: convertNullToNotFoundError.bind(null, toModelName) },
|
||||||
returns: { arg: 'result', type: toModelName, root: true },
|
});
|
||||||
}, updateByIdFunc);
|
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') {
|
function throughOrReferenceRemoting(relationName, relation) {
|
||||||
var modelThrough = relation.modelThrough || relation.modelTo;
|
setRemoting('__link__' + relationName, {
|
||||||
|
|
||||||
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, {
|
|
||||||
isStatic: false,
|
isStatic: false,
|
||||||
http: { verb: 'put', path: '/' + pathName + '/rel/:fk' },
|
http: { verb: 'put', path: '/' + pathName + '/rel/:fk' },
|
||||||
accepts: [{ arg: 'fk', type: 'any',
|
accepts: [{ arg: 'fk', type: 'any',
|
||||||
|
@ -578,10 +615,9 @@ module.exports = function(registry) {
|
||||||
description: format('Add a related item by id for %s.', relationName),
|
description: format('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 },
|
||||||
}, addFunc);
|
});
|
||||||
|
|
||||||
var removeFunc = this.prototype['__unlink__' + relationName];
|
setRemoting('__unlink__' + relationName, {
|
||||||
define('__unlink__' + relationName, {
|
|
||||||
isStatic: false,
|
isStatic: false,
|
||||||
http: { verb: 'delete', path: '/' + pathName + '/rel/:fk' },
|
http: { verb: 'delete', path: '/' + pathName + '/rel/:fk' },
|
||||||
accepts: { arg: 'fk', type: 'any',
|
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),
|
description: format('Remove the %s relation to an item by id.', relationName),
|
||||||
accessType: 'WRITE',
|
accessType: 'WRITE',
|
||||||
returns: [],
|
returns: [],
|
||||||
}, 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?
|
||||||
// true --> 200 and false --> 404?
|
// true --> 200 and false --> 404?
|
||||||
var existsFunc = this.prototype['__exists__' + relationName];
|
setRemoting('__exists__' + relationName, {
|
||||||
define('__exists__' + relationName, {
|
|
||||||
isStatic: false,
|
isStatic: false,
|
||||||
http: { verb: 'head', path: '/' + pathName + '/rel/:fk' },
|
http: { verb: 'head', path: '/' + pathName + '/rel/:fk' },
|
||||||
accepts: { arg: 'fk', type: 'any',
|
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) {
|
Model.scopeRemoting = function(scopeName, scope, define) {
|
||||||
|
|
Loading…
Reference in New Issue