Check configs for shared method settings
This commit is contained in:
parent
716ed4569f
commit
26af1472e7
|
@ -11,6 +11,7 @@ var RemoteObjects = require('strong-remoting');
|
||||||
var classify = require('underscore.string/classify');
|
var classify = require('underscore.string/classify');
|
||||||
var camelize = require('underscore.string/camelize');
|
var camelize = require('underscore.string/camelize');
|
||||||
var path = require('path');
|
var path = require('path');
|
||||||
|
var util = require('util');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The `App` object represents a Loopback application.
|
* The `App` object represents a Loopback application.
|
||||||
|
@ -435,9 +436,54 @@ function configureModel(ModelCtor, config, app) {
|
||||||
config = extend({}, config);
|
config = extend({}, config);
|
||||||
config.dataSource = dataSource;
|
config.dataSource = dataSource;
|
||||||
|
|
||||||
|
setSharedMethodSharedProperties(ModelCtor, app, config);
|
||||||
|
|
||||||
app.registry.configureModel(ModelCtor, config);
|
app.registry.configureModel(ModelCtor, config);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function setSharedMethodSharedProperties(model, app, modelConfigs) {
|
||||||
|
var settings = {};
|
||||||
|
|
||||||
|
// apply config.json settings
|
||||||
|
var config = app.get('remoting');
|
||||||
|
var configHasSharedMethodsSettings = config &&
|
||||||
|
config.sharedMethods &&
|
||||||
|
typeof config.sharedMethods === 'object';
|
||||||
|
if (configHasSharedMethodsSettings)
|
||||||
|
util._extend(settings, config.sharedMethods);
|
||||||
|
|
||||||
|
// apply model-config.json settings
|
||||||
|
var modelConfig = modelConfigs.options;
|
||||||
|
var modelConfigHasSharedMethodsSettings = modelConfig &&
|
||||||
|
modelConfig.remoting &&
|
||||||
|
modelConfig.remoting.sharedMethods &&
|
||||||
|
typeof modelConfig.remoting.sharedMethods === 'object';
|
||||||
|
if (modelConfigHasSharedMethodsSettings)
|
||||||
|
util._extend(settings, modelConfig.remoting.sharedMethods);
|
||||||
|
|
||||||
|
// validate setting values
|
||||||
|
Object.keys(settings).forEach(function(setting) {
|
||||||
|
var settingValue = settings[setting];
|
||||||
|
var settingValueType = typeof settingValue;
|
||||||
|
if (settingValueType !== 'boolean')
|
||||||
|
throw new TypeError('Expected boolean, got ' + settingValueType);
|
||||||
|
});
|
||||||
|
|
||||||
|
// set sharedMethod.shared using the merged settings
|
||||||
|
var sharedMethods = model.sharedClass.methods({includeDisabled: true});
|
||||||
|
sharedMethods.forEach(function(sharedMethod) {
|
||||||
|
// use the specific setting if it exists
|
||||||
|
var hasSpecificSetting = settings.hasOwnProperty(sharedMethod.name);
|
||||||
|
if (hasSpecificSetting) {
|
||||||
|
sharedMethod.shared = settings[sharedMethod.name];
|
||||||
|
} else { // otherwise, use the default setting if it exists
|
||||||
|
var hasDefaultSetting = settings.hasOwnProperty('*');
|
||||||
|
if (hasDefaultSetting)
|
||||||
|
sharedMethod.shared = settings['*'];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function clearHandlerCache(app) {
|
function clearHandlerCache(app) {
|
||||||
app._handlers = undefined;
|
app._handlers = undefined;
|
||||||
}
|
}
|
||||||
|
|
|
@ -618,6 +618,12 @@ module.exports = function(registry) {
|
||||||
description: 'Delete all matching records.',
|
description: 'Delete all matching records.',
|
||||||
accessType: 'WRITE',
|
accessType: 'WRITE',
|
||||||
accepts: {arg: 'where', type: 'object', description: 'filter.where object'},
|
accepts: {arg: 'where', type: 'object', description: 'filter.where object'},
|
||||||
|
returns: {
|
||||||
|
arg: 'count',
|
||||||
|
type: 'object',
|
||||||
|
description: 'The number of instances deleted',
|
||||||
|
root: true
|
||||||
|
},
|
||||||
http: {verb: 'del', path: '/'},
|
http: {verb: 'del', path: '/'},
|
||||||
shared: false
|
shared: false
|
||||||
});
|
});
|
||||||
|
@ -632,6 +638,12 @@ module.exports = function(registry) {
|
||||||
{arg: 'data', type: 'object', http: {source: 'body'},
|
{arg: 'data', type: 'object', http: {source: 'body'},
|
||||||
description: 'An object of model property name/value pairs'},
|
description: 'An object of model property name/value pairs'},
|
||||||
],
|
],
|
||||||
|
returns: {
|
||||||
|
arg: 'count',
|
||||||
|
description: 'The number of instances updated',
|
||||||
|
type: 'object',
|
||||||
|
root: true
|
||||||
|
},
|
||||||
http: {verb: 'post', path: '/update'}
|
http: {verb: 'post', path: '/update'}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -641,7 +653,8 @@ module.exports = function(registry) {
|
||||||
accessType: 'WRITE',
|
accessType: 'WRITE',
|
||||||
accepts: {arg: 'id', type: 'any', description: 'Model id', required: true,
|
accepts: {arg: 'id', type: 'any', description: 'Model id', required: true,
|
||||||
http: {source: 'path'}},
|
http: {source: 'path'}},
|
||||||
http: {verb: 'del', path: '/:id'}
|
http: {verb: 'del', path: '/:id'},
|
||||||
|
returns: {arg: 'count', type: 'object', root: true}
|
||||||
});
|
});
|
||||||
|
|
||||||
setRemoting(PersistedModel, 'count', {
|
setRemoting(PersistedModel, 'count', {
|
||||||
|
|
|
@ -49,7 +49,7 @@
|
||||||
"nodemailer-stub-transport": "^0.1.5",
|
"nodemailer-stub-transport": "^0.1.5",
|
||||||
"serve-favicon": "^2.2.0",
|
"serve-favicon": "^2.2.0",
|
||||||
"stable": "^0.1.5",
|
"stable": "^0.1.5",
|
||||||
"strong-remoting": "^2.15.0",
|
"strong-remoting": "^2.21.0",
|
||||||
"uid2": "0.0.3",
|
"uid2": "0.0.3",
|
||||||
"underscore.string": "^3.0.3"
|
"underscore.string": "^3.0.3"
|
||||||
},
|
},
|
||||||
|
|
|
@ -117,14 +117,15 @@ describe('remoting - integration', function() {
|
||||||
'findById(id:any,filter:object):store GET /stores/:id',
|
'findById(id:any,filter:object):store GET /stores/:id',
|
||||||
'find(filter:object):store GET /stores',
|
'find(filter:object):store GET /stores',
|
||||||
'findOne(filter:object):store GET /stores/findOne',
|
'findOne(filter:object):store GET /stores/findOne',
|
||||||
'updateAll(where:object,data:object) POST /stores/update',
|
'updateAll(where:object,data:object):object POST /stores/update',
|
||||||
'deleteById(id:any) DELETE /stores/:id',
|
'deleteById(id:any):object DELETE /stores/:id',
|
||||||
'count(where:object):number GET /stores/count',
|
'count(where:object):number GET /stores/count',
|
||||||
'prototype.updateAttributes(data:object):store PUT /stores/:id'
|
'prototype.updateAttributes(data:object):store PUT /stores/:id',
|
||||||
|
'createChangeStream(options:object):ReadableStream POST /stores/change-stream'
|
||||||
];
|
];
|
||||||
|
|
||||||
// The list of methods is from docs:
|
// The list of methods is from docs:
|
||||||
// http://docs.strongloop.com/display/LB/Exposing+models+over+a+REST+API
|
// https://docs.strongloop.com/display/public/LB/Exposing+models+over+REST
|
||||||
expect(methods).to.include.members(expectedMethods);
|
expect(methods).to.include.members(expectedMethods);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
var path = require('path');
|
||||||
|
|
||||||
describe('loopback.rest', function() {
|
describe('loopback.rest', function() {
|
||||||
var MyModel;
|
var MyModel;
|
||||||
beforeEach(function() {
|
beforeEach(function() {
|
||||||
|
@ -14,6 +16,20 @@ describe('loopback.rest', function() {
|
||||||
.end(done);
|
.end(done);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should report 200 for DELETE /:id found', function(done) {
|
||||||
|
app.set('legacyExplorer', false);
|
||||||
|
app.model(MyModel);
|
||||||
|
app.use(loopback.rest());
|
||||||
|
MyModel.create({name: 'm1'}, function(err, inst) {
|
||||||
|
request(app)
|
||||||
|
.del('/mymodels/' + inst.id)
|
||||||
|
.expect(200, function(err, res) {
|
||||||
|
expect(res.body.count).to.equal(1);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('should report 404 for GET /:id not found', function(done) {
|
it('should report 404 for GET /:id not found', function(done) {
|
||||||
app.model(MyModel);
|
app.model(MyModel);
|
||||||
app.use(loopback.rest());
|
app.use(loopback.rest());
|
||||||
|
@ -337,4 +353,124 @@ describe('loopback.rest', function() {
|
||||||
User.login(credentials, cb);
|
User.login(credentials, cb);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
describe('shared methods', function() {
|
||||||
|
function getFixturePath(dirName) {
|
||||||
|
return path.join(__dirname, 'fixtures/shared-methods/' + dirName +
|
||||||
|
'/server/server.js');
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('with specific definitions in model-config.json', function() {
|
||||||
|
it('should not be exposed when the definition value is false',
|
||||||
|
function(done) {
|
||||||
|
var app = require(getFixturePath('model-config-defined-false'));
|
||||||
|
request(app)
|
||||||
|
.get('/todos')
|
||||||
|
.expect(404, done);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be exposed when the definition value is true', function(done) {
|
||||||
|
var app = require(getFixturePath('model-config-defined-true'));
|
||||||
|
request(app)
|
||||||
|
.get('/todos')
|
||||||
|
.expect(200, done);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('with default definitions in model-config.json', function() {
|
||||||
|
it('should not be exposed when the definition value is false',
|
||||||
|
function(done) {
|
||||||
|
var app = require(getFixturePath('model-config-default-false'));
|
||||||
|
request(app)
|
||||||
|
.get('/todos')
|
||||||
|
.expect(404, done);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be exposed when the definition value is true', function(done) {
|
||||||
|
var app = require(getFixturePath('model-config-default-true'));
|
||||||
|
app.models.Todo.create([
|
||||||
|
{content: 'a'},
|
||||||
|
{content: 'b'},
|
||||||
|
{content: 'c'}
|
||||||
|
], function() {
|
||||||
|
request(app)
|
||||||
|
.del('/todos')
|
||||||
|
.expect(200)
|
||||||
|
.end(function(err, res) {
|
||||||
|
if (err) return done(err);
|
||||||
|
expect(res.body.count).to.equal(3);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('with specific definitions in config.json', function() {
|
||||||
|
it('should not be exposed when the definition value is false',
|
||||||
|
function(done) {
|
||||||
|
var app = require(getFixturePath('config-defined-false'));
|
||||||
|
request(app)
|
||||||
|
.get('/todos')
|
||||||
|
.expect(404, done);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be exposed when the definition value is true',
|
||||||
|
function(done) {
|
||||||
|
var app = require(getFixturePath('config-defined-true'));
|
||||||
|
request(app)
|
||||||
|
.get('/todos')
|
||||||
|
.expect(200, done);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('with default definitions in config.json', function() {
|
||||||
|
it('should not be exposed when the definition value is false',
|
||||||
|
function(done) {
|
||||||
|
var app = require(getFixturePath('config-default-false'));
|
||||||
|
request(app)
|
||||||
|
.get('/todos')
|
||||||
|
.expect(404, done);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be exposed when the definition value is true', function(done) {
|
||||||
|
var app = require(getFixturePath('config-default-true'));
|
||||||
|
app.models.Todo.create([
|
||||||
|
{content: 'a'},
|
||||||
|
{content: 'b'},
|
||||||
|
{content: 'c'}
|
||||||
|
], function() {
|
||||||
|
request(app)
|
||||||
|
.del('/todos')
|
||||||
|
.expect(200)
|
||||||
|
.end(function(err, res) {
|
||||||
|
if (err) return done(err);
|
||||||
|
expect(res.body.count).to.equal(3);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// The fixture in `shared-method/both-configs-set/config.json` has `*:false`
|
||||||
|
// set which disables the REST endpoints for built-in models such as User as
|
||||||
|
// a side effect since tests share the same loopback instance. As a
|
||||||
|
// consequence, this causes the tests in user.integration to fail.
|
||||||
|
describe.skip('with definitions in both config.json and model-config.json',
|
||||||
|
function() {
|
||||||
|
it('should prioritize the settings in model-config.json', function(done) {
|
||||||
|
var app = require(getFixturePath('both-configs-set'));
|
||||||
|
request(app)
|
||||||
|
.del('/todos')
|
||||||
|
.expect(404, done);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should fall back to config.json settings if setting is not found in' +
|
||||||
|
'model-config.json', function(done) {
|
||||||
|
var app = require(getFixturePath('both-configs-set'));
|
||||||
|
request(app)
|
||||||
|
.get('/todos')
|
||||||
|
.expect(404, done);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -34,14 +34,10 @@ describe('users - integration', function() {
|
||||||
var accessToken;
|
var accessToken;
|
||||||
|
|
||||||
it('should create a new user', function(done) {
|
it('should create a new user', function(done) {
|
||||||
var url = '/api/users';
|
this.post('/api/users')
|
||||||
|
|
||||||
this.post(url)
|
|
||||||
.send({username: 'x', email: 'x@y.com', password: 'x'})
|
.send({username: 'x', email: 'x@y.com', password: 'x'})
|
||||||
.expect(200, function(err, res) {
|
.expect(200, function(err, res) {
|
||||||
if (err) {
|
if (err) return done(err);
|
||||||
return done(err);
|
|
||||||
}
|
|
||||||
expect(res.body.id).to.exist;
|
expect(res.body.id).to.exist;
|
||||||
userId = res.body.id;
|
userId = res.body.id;
|
||||||
done();
|
done();
|
||||||
|
|
Loading…
Reference in New Issue