From f93b69ed0ae60ac74ce837ffb70e7984c4aea7b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miroslav=20Bajto=C5=A1?= Date: Wed, 6 May 2015 10:04:06 +0200 Subject: [PATCH] Define remote methods via model settings/config Process `settings.methods` and `config.methods` as a key-value map where the key is the method name and the value is an object describing the method in the format expected by strong-remoting. Example: a static method `Model.create` "methods": { "create": { "isStatic": true, "accepts": { "arg": "data", "type": "Car", "http": { "source": "body" } }, "returns": { "arg": "data", "type": "Car", "root": true } } } This patch is based on the code proposed by @mrfelton in #1163. --- lib/application.js | 2 +- lib/registry.js | 26 ++++++++++++++++++++++ test/loopback.test.js | 50 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 77 insertions(+), 1 deletion(-) diff --git a/lib/application.js b/lib/application.js index 590c6584..3390b54b 100644 --- a/lib/application.js +++ b/lib/application.js @@ -122,7 +122,7 @@ app.model = function(Model, config) { Model = registry.createModel(modelConfig); // delete config options already applied - ['relations', 'base', 'acls', 'hidden'].forEach(function(prop) { + ['relations', 'base', 'acls', 'hidden', 'methods'].forEach(function(prop) { delete config[prop]; if (config.options) delete config.options[prop]; }); diff --git a/lib/registry.js b/lib/registry.js index 371cdf70..64d079f6 100644 --- a/lib/registry.js +++ b/lib/registry.js @@ -122,6 +122,8 @@ Registry.prototype.createModel = function(name, properties, options) { var model = BaseModel.extend(name, properties, options); model.registry = this; + this._defineRemoteMethods(model, options.methods); + // try to attach try { this.autoAttachModel(model); @@ -247,6 +249,30 @@ Registry.prototype.configureModel = function(ModelCtor, config) { 'Use `null` or `false` to mark models not attached to any data source.', modelName); } + + // Remote methods + this._defineRemoteMethods(ModelCtor, config.methods); +}; + +Registry.prototype._defineRemoteMethods = function(ModelCtor, methods) { + if (!methods) return; + if (typeof methods !== 'object') { + console.warn('Ignoring non-object "methods" setting of "%s".', + ModelCtor.modelName); + return; + } + + Object.keys(methods).forEach(function(key) { + var meta = methods[key]; + if (typeof meta.isStatic !== 'boolean') { + console.warn('Remoting metadata for "%s.%s" is missing "isStatic" ' + + 'flag, the method is registered as an instance method.', + ModelCtor.modelName, + key); + console.warn('This behaviour may change in the next major version.'); + } + ModelCtor.remoteMethod(key, meta); + }); }; /** diff --git a/test/loopback.test.js b/test/loopback.test.js index 95a54030..9b39709e 100644 --- a/test/loopback.test.js +++ b/test/loopback.test.js @@ -249,6 +249,30 @@ describe('loopback', function() { .to.throw(Error, new RegExp('Model not found: ' + uniqueModelName)); }); }); + + it('configures remote methods', function() { + var TestModel = loopback.createModel(uniqueModelName, {}, { + methods: { + staticMethod: { + isStatic: true, + http: { path: '/static' } + }, + instanceMethod: { + isStatic: false, + http: { path: '/instance' } + } + } + }); + + var methodNames = TestModel.sharedClass.methods().map(function(m) { + return m.stringName.replace(/^[^.]+\./, ''); // drop the class name + }); + + expect(methodNames).to.include.members([ + 'staticMethod', + 'prototype.instanceMethod' + ]); + }); }); describe('loopback.createModel(config)', function() { @@ -449,6 +473,32 @@ describe('loopback', function() { // configureModel MUST NOT change Model's base class expect(model.settings.base.name).to.equal(baseName); }); + + it('configures remote methods', function() { + var TestModel = loopback.createModel(uniqueModelName); + loopback.configureModel(TestModel, { + dataSource: null, + methods: { + staticMethod: { + isStatic: true, + http: { path: '/static' } + }, + instanceMethod: { + isStatic: false, + http: { path: '/instance' } + } + } + }); + + var methodNames = TestModel.sharedClass.methods().map(function(m) { + return m.stringName.replace(/^[^.]+\./, ''); // drop the class name + }); + + expect(methodNames).to.include.members([ + 'staticMethod', + 'prototype.instanceMethod' + ]); + }); }); describe('loopback object', function() {