Merge pull request #3504 from zbarbuto/feature/share-method-glob

Allow glob-style patterns for remote options
This commit is contained in:
Miroslav Bajtoš 2017-08-14 17:25:43 +02:00 committed by GitHub
commit 06a2b6d86b
2 changed files with 173 additions and 8 deletions

View File

@ -533,9 +533,9 @@ 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);
setSharedMethodSharedProperties(ModelCtor, app, config);
} }
function setSharedMethodSharedProperties(model, app, modelConfigs) { function setSharedMethodSharedProperties(model, app, modelConfigs) {
@ -568,15 +568,38 @@ function setSharedMethodSharedProperties(model, app, modelConfigs) {
// set sharedMethod.shared using the merged settings // set sharedMethod.shared using the merged settings
var sharedMethods = model.sharedClass.methods({includeDisabled: true}); var sharedMethods = model.sharedClass.methods({includeDisabled: true});
// re-map glob style values to regular expressions
var tests = Object
.keys(settings)
.filter(function(setting) {
return settings.hasOwnProperty(setting) && setting.indexOf('*') >= 0;
})
.map(function(setting) {
// Turn * into an testable regexp string
var glob = escapeRegExp(setting).replace(/\*/g, '(.)*');
return {regex: new RegExp(glob), setting: settings[setting]};
}) || [];
sharedMethods.forEach(function(sharedMethod) { sharedMethods.forEach(function(sharedMethod) {
// use the specific setting if it exists // use the specific setting if it exists
var hasSpecificSetting = settings.hasOwnProperty(sharedMethod.name); var methodName = sharedMethod.isStatic ? sharedMethod.name : 'prototype.' + sharedMethod.name;
var hasSpecificSetting = settings.hasOwnProperty(methodName);
if (hasSpecificSetting) { if (hasSpecificSetting) {
sharedMethod.shared = settings[sharedMethod.name]; if (settings[methodName] === false) {
} else { // otherwise, use the default setting if it exists sharedMethod.sharedClass.disableMethodByName(methodName);
var hasDefaultSetting = settings.hasOwnProperty('*'); } else {
if (hasDefaultSetting) sharedMethod.shared = true;
sharedMethod.shared = settings['*']; }
} else {
tests.forEach(function(glob) {
if (glob.regex.test(methodName)) {
if (glob.setting === false) {
sharedMethod.sharedClass.disableMethodByName(methodName);
} else {
sharedMethod.shared = true;
}
}
});
} }
}); });
} }
@ -585,6 +608,11 @@ function clearHandlerCache(app) {
app._handlers = undefined; app._handlers = undefined;
} }
// Sanitize all RegExp reserved characters except * for pattern gobbing
function escapeRegExp(str) {
return str.replace(/[\-\[\]\/\{\}\(\)\+\?\.\\\^\$\|]/g, '\\$&');
}
/** /**
* Listen for connections and update the configured port. * Listen for connections and update the configured port.
* *

View File

@ -739,4 +739,141 @@ describe('loopback', function() {
}); });
} }
}); });
describe('Hiding shared methods', function() {
var app;
beforeEach(setupLoopback);
it('hides remote methods using fixed method names', function() {
var TestModel = app.registry.createModel(uniqueModelName);
app.model(TestModel, {
dataSource: null,
methods: {
staticMethod: {
isStatic: true,
http: {path: '/static'},
},
},
options: {
remoting: {
sharedMethods: {
staticMethod: false,
},
},
},
});
var publicMethods = getSharedMethods(TestModel);
expect(publicMethods).not.to.include.members([
'staticMethod',
]);
});
it('hides remote methods using a glob pattern', function() {
var TestModel = app.registry.createModel(uniqueModelName);
app.model(TestModel, {
dataSource: null,
methods: {
staticMethod: {
isStatic: true,
http: {path: '/static'},
},
instanceMethod: {
isStatic: false,
http: {path: '/instance'},
},
},
options: {
remoting: {
sharedMethods: {
'prototype.*': false,
},
},
},
});
var publicMethods = getSharedMethods(TestModel);
expect(publicMethods).to.include.members([
'staticMethod',
]);
expect(publicMethods).not.to.include.members([
'instanceMethod',
]);
});
it('hides all remote methods using *', function() {
var TestModel = app.registry.createModel(uniqueModelName);
app.model(TestModel, {
dataSource: null,
methods: {
staticMethod: {
isStatic: true,
http: {path: '/static'},
},
instanceMethod: {
isStatic: false,
http: {path: '/instance'},
},
},
options: {
remoting: {
sharedMethods: {
'*': false,
},
},
},
});
var publicMethods = getSharedMethods(TestModel);
expect(publicMethods).to.be.empty();
});
it('hides methods for related models using globs', function() {
var TestModel = app.registry.createModel(uniqueModelName);
var RelatedModel = app.registry.createModel(uniqueModelName);
app.dataSource('test', {connector: 'memory'});
app.model(RelatedModel, {dataSource: 'test'});
app.model(TestModel, {
dataSource: 'test',
relations: {
related: {
type: 'hasOne',
model: RelatedModel,
},
},
options: {
remoting: {
sharedMethods: {
'*__related': false,
},
},
},
});
var publicMethods = getSharedMethods(TestModel);
expect(publicMethods).to.not.include.members([
'prototype.__create__related',
]);
});
function setupLoopback() {
app = loopback({localRegistry: true});
}
function getSharedMethods(Model) {
return Model.sharedClass
.methods()
.filter(function(m) {
return m.shared === true;
})
.map(function(m) {
return m.stringName.replace(/^[^.]+\./, ''); // drop the class name
});
}
});
}); });