From b5b900e0ff8a90d86212eecea7aabc71d5bc2c59 Mon Sep 17 00:00:00 2001 From: Candy <ngcandy@ca.ibm.com> Date: Mon, 28 Mar 2016 11:11:22 -0400 Subject: [PATCH] Remove constraint making isStatic required --- 3.0-RELEASE-NOTES.md | 32 +++++++++++++++- lib/model.js | 4 +- lib/registry.js | 18 ++++++--- test/loopback.test.js | 88 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 135 insertions(+), 7 deletions(-) diff --git a/3.0-RELEASE-NOTES.md b/3.0-RELEASE-NOTES.md index a1a1681c..f70b1185 100644 --- a/3.0-RELEASE-NOTES.md +++ b/3.0-RELEASE-NOTES.md @@ -32,4 +32,34 @@ via `global.Promise`, you will have to check all places where you are using non-standard promise API and update them to use Bluebird API instead. -Please see [Related code change](https://github.com/strongloop/loopback/pull/1896) here. \ No newline at end of file +Please see [Related code change](https://github.com/strongloop/loopback/pull/1896) here. + +## new method of defining remoting metadata + +In 2.0, remote methods were defined as: +``` +methods: { + staticMethod: { + isStatic: true, + http: { path: '/static' } + }, + instanceMethod: { + isStatic: false, + http: { path: '/instance' } + } +} +``` + +For 3.0, the isStatic flag is no longer required and will be determined from the method name. +Method name starting with "prototype." will be the same as having isStatic flag set to false. +``` +methods: { + staticMethod: { + http: { path: '/static' } + }, + 'prototype.instanceMethod': { + http: { path: '/instance' } +} +``` + +Please see [related code change](https://github.com/strongloop/loopback/pull/2174) here. \ No newline at end of file diff --git a/lib/model.js b/lib/model.js index f3ec47b2..b766ded3 100644 --- a/lib/model.js +++ b/lib/model.js @@ -415,7 +415,9 @@ module.exports = function(registry) { Model.remoteMethod = function(name, options) { if (options.isStatic === undefined) { - options.isStatic = true; + var m = name.match(/^prototype\.(.*)$/); + options.isStatic = !m; + name = options.isStatic ? name : m[1]; } this.sharedClass.defineMethod(name, options); }; diff --git a/lib/registry.js b/lib/registry.js index 7fe66ad3..5ee4ce85 100644 --- a/lib/registry.js +++ b/lib/registry.js @@ -4,6 +4,7 @@ var juggler = require('loopback-datasource-juggler'); var debug = require('debug')('loopback:registry'); var DataSource = juggler.DataSource; var ModelBuilder = juggler.ModelBuilder; +var deprecated = require('depd')('strong-remoting'); module.exports = Registry; @@ -258,12 +259,19 @@ Registry.prototype._defineRemoteMethods = function(ModelCtor, methods) { Object.keys(methods).forEach(function(key) { var meta = methods[key]; + var m = key.match(/^prototype\.(.*)$/); + var isStatic = !m; + 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.'); + key = isStatic ? key : m[1]; + meta.isStatic = isStatic; + } else if (meta.isStatic && m) { + throw new Error('Remoting metadata for ' + ModelCtor.modelName + '.' + + key + ' "isStatic" does not match new method name-based style.'); + } else { + key = isStatic ? key : m[1]; + deprecated('Remoting metadata "isStatic" is deprecated. Please ' + + 'specify "prototype.name" in method name instead for isStatic=false.'); } ModelCtor.remoteMethod(key, meta); }); diff --git a/test/loopback.test.js b/test/loopback.test.js index 05fac520..dca31228 100644 --- a/test/loopback.test.js +++ b/test/loopback.test.js @@ -631,4 +631,92 @@ describe('loopback', function() { }); }); }); + + describe('new remote method configuration', function() { + function getAllMethodNamesWithoutClassName(TestModel) { + return TestModel.sharedClass.methods().map(function(m) { + return m.stringName.replace(/^[^.]+\./, ''); // drop the class name + }); + } + + it('treats method names that don\'t start with "prototype." as "isStatic:true"', function() { + var TestModel = loopback.createModel(uniqueModelName); + loopback.configureModel(TestModel, { + dataSource: null, + methods: { + staticMethod: { + http: { path: '/static' } + } + } + }); + + var methodNames = getAllMethodNamesWithoutClassName(TestModel); + + expect(methodNames).to.include('staticMethod'); + }); + + it('treats method names starting with "prototype." as "isStatic:false"', function() { + var TestModel = loopback.createModel(uniqueModelName); + loopback.configureModel(TestModel, { + dataSource: null, + methods: { + 'prototype.instanceMethod': { + http: { path: '/instance' } + } + } + }); + + var methodNames = getAllMethodNamesWithoutClassName(TestModel); + + expect(methodNames).to.include('prototype.instanceMethod'); + }); + + it('throws an error when "isStatic:true" and method name starts with "prototype."', function() { + var TestModel = loopback.createModel(uniqueModelName); + expect(function() { loopback.configureModel(TestModel, { + dataSource: null, + methods: { + 'prototype.instanceMethod': { + isStatic: true, + http: { path: '/instance' } + } + } + });}).to.throw(Error, new Error('Remoting metadata for' + TestModel.modelName + + ' "isStatic" does not match new method name-based style.')); + }); + + it('use "isStatic:true" if method name does not start with "prototype."', function() { + var TestModel = loopback.createModel(uniqueModelName); + loopback.configureModel(TestModel, { + dataSource: null, + methods: { + staticMethod: { + isStatic: true, + http: { path: '/static' } + } + } + }); + + var methodNames = getAllMethodNamesWithoutClassName(TestModel); + + expect(methodNames).to.include('staticMethod'); + }); + + it('use "isStatic:false" if method name starts with "prototype."', function() { + var TestModel = loopback.createModel(uniqueModelName); + loopback.configureModel(TestModel, { + dataSource: null, + methods: { + 'prototype.instanceMethod': { + isStatic: false, + http: { path: '/instance' } + } + } + }); + + var methodNames = getAllMethodNamesWithoutClassName(TestModel); + + expect(methodNames).to.include('prototype.instanceMethod'); + }); + }); });