Skip to content

Commit

Permalink
Fix remote method inheritance
Browse files Browse the repository at this point in the history
  • Loading branch information
0candy committed Sep 13, 2016
1 parent 3eb9009 commit d4b8cf6
Show file tree
Hide file tree
Showing 2 changed files with 142 additions and 1 deletion.
11 changes: 10 additions & 1 deletion lib/registry.js
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ Registry.prototype.createModel = function(name, properties, options) {
var model = BaseModel.extend(name, properties, options);
model.registry = this;

this._defineRemoteMethods(model, options.methods);
this._defineRemoteMethods(model, model.settings.methods);

return model;
};
Expand Down Expand Up @@ -244,6 +244,15 @@ Registry.prototype.configureModel = function(ModelCtor, config) {
modelName);
}

var newMethodNames = config.methods && Object.keys(config.methods);
var hasNewMethods = newMethodNames && newMethodNames.length;
var hasDescendants = this.getModelByType(ModelCtor) !== ModelCtor;
if (hasNewMethods && hasDescendants) {
g.warn(
'Child models of `%s` will not inherit newly defined remote methods %s.',
modelName, newMethodNames);
}

// Remote methods
this._defineRemoteMethods(ModelCtor, config.methods);
};
Expand Down
132 changes: 132 additions & 0 deletions test/loopback.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ var it = require('./util/it');
var describe = require('./util/describe');
var Domain = require('domain');
var EventEmitter = require('events').EventEmitter;
var loopback = require('../');
var expect = require('chai').expect;
var assert = require('assert');

describe('loopback', function() {
var nameCounter = 0;
Expand Down Expand Up @@ -608,4 +611,133 @@ describe('loopback', function() {
expect(methodNames).to.include('prototype.instanceMethod');
});
});

describe('Remote method inheritance', function() {
var app;

beforeEach(setupLoopback);

it('inherits remote methods defined via createModel', function() {
var Base = app.registry.createModel('Base', {}, {
methods: {
greet: {
http: { path: '/greet' },
},
},
});

var MyCustomModel = app.registry.createModel('MyCustomModel', {}, {
base: 'Base',
methods: {
hello: {
http: { path: '/hello' },
},
},
});
var methodNames = getAllMethodNamesWithoutClassName(MyCustomModel);

expect(methodNames).to.include('greet');
expect(methodNames).to.include('hello');
});

it('same remote method with different metadata should override parent', function() {
var Base = app.registry.createModel('Base', {}, {
methods: {
greet: {
http: { path: '/greet' },
},
},
});

var MyCustomModel = app.registry.createModel('MyCustomModel', {}, {
base: 'Base',
methods: {
greet: {
http: { path: '/hello' },
},
},
});
var methodNames = getAllMethodNamesWithoutClassName(MyCustomModel);
var baseMethod = Base.sharedClass.findMethodByName('greet');
var customMethod = MyCustomModel.sharedClass.findMethodByName('greet');

// Base Method
expect(baseMethod.http).to.eql({ path: '/greet' });
expect(baseMethod.http.path).to.equal('/greet');
expect(baseMethod.http.path).to.not.equal('/hello');

// Custom Method
expect(methodNames).to.include('greet');
expect(customMethod.http).to.eql({ path: '/hello' });
expect(customMethod.http.path).to.equal('/hello');
expect(customMethod.http.path).to.not.equal('/greet');
});

it('does not inherit remote methods defined via configureModel', function() {
var Base = app.registry.createModel('Base');
app.registry.configureModel(Base, {
dataSource: null,
methods: {
greet: {
http: { path: '/greet' },
},
},
});

var MyCustomModel = app.registry.createModel('MyCustomModel', {}, {
base: 'Base',
methods: {
hello: {
http: { path: '/hello' },
},
},
});
var methodNames = getAllMethodNamesWithoutClassName(MyCustomModel);

expect(methodNames).to.not.include('greet');
expect(methodNames).to.include('hello');
});

it('does not inherit remote methods defined via configureModel after child model ' +
'was created', function() {
var Base = app.registry.createModel('Base');
var MyCustomModel = app.registry.createModel('MyCustomModel', {}, {
base: 'Base',
});

app.registry.configureModel(Base, {
dataSource: null,
methods: {
greet: {
http: { path: '/greet' },
},
},
});

app.registry.configureModel(MyCustomModel, {
dataSource: null,
methods: {
hello: {
http: { path: '/hello' },
},
},
});
var baseMethodNames = getAllMethodNamesWithoutClassName(Base);
var methodNames = getAllMethodNamesWithoutClassName(MyCustomModel);

expect(baseMethodNames).to.include('greet');
expect(methodNames).to.not.include('greet');
expect(methodNames).to.include('hello');
});

function setupLoopback() {
app = loopback({ localRegistry: true });
}

function getAllMethodNamesWithoutClassName(Model) {
return Model.sharedClass.methods().map(function(m) {
return m.stringName.replace(/^[^.]+\./, ''); // drop the class name
});
}
});
});

0 comments on commit d4b8cf6

Please sign in to comment.