How to disable certain HTTP methods (like POST) for PersistedModel in LoopBack environment
When creating a model in the LoopBack environment, you can inherit from the PersistedModel class. All HTTP methods are generated this way. I am wondering how to disable some HTTP methods?
One option is to override functions from PersistedModel with empty logic, but wants the method to disappear from the Swagger API explorer.
source to share
Found the answer in the documentation. For example, this disables PersistedModel.deleteById:
var isStatic = true;
MyModel.disableRemoteMethod('deleteById', isStatic);
So it looks like you cannot disable all DELETE actions at the same time. For example, the persistedModel.deleteAll method remains available in this example.
The developer must disable each corresponding method from the PersistedModel class explicitly.
Relevant docs are here: http://docs.strongloop.com/display/public/LB/Exposing+models+over+REST
Sections:
- Hiding Methods and REST Endpoints
- Hiding endpoints for linked models
source to share
I did below in the model.js files as shown below. This makes the table read-only.
module.exports = function(model) {
var methodNames = ['create', 'upsert', 'deleteById','updateAll',
'updateAttributes','createChangeStream','replace','replaceById',
'upsertWithWhere','replaceOrCreate'
];
methodNames.forEach(function(methodName) {
disableMethods(model,methodName)
});
}
function disableMethods(model,methodName)
{
if(methodName!='updateAttributes')
model.disableRemoteMethod(methodName, true);
else
model.disableRemoteMethod(methodName, false);
}
source to share
The only thing that requires extra care is disabling methods on the user model (e.g. User.login). You need to call disableRemoteMethod before the explorer middleware: https://github.com/strongloop/loopback/issues/686
source to share
I had the same problem.
My first solution was to manually update the items "public":true
in server/model-configuration.json
, but it was overridden anytime I used the Swagger tool to update the LoopBack API (using a command slc loopback:swagger myswaggerfilename
from the project root).
Finally, I wrote the Grunt problem as a robust solution.
- Run it right after a generation
slc loopback:swagger
or just before running the realtime API. - you just need to provide the names of the paths I want to open in the javascript array
list_of_REST_path_to_EXPOSE
- and make sure you are happy with the backup folder for the original file
/server/model-config.json
.
I wanted to share it with you in case:
https://github.com/FranckVE/grunt-task-unexpose-rest-path-loopback-swagger
Basically:
module.exports = function (grunt) {
grunt.registerTask('unexpose_rest_path_for_swagger_models_v1', function (key, value) {
try {
// Change the list below depending on your API project :
// list of the REST paths to leave Exposed
var list_of_REST_path_to_EXPOSE =
[
"swagger_example-api_v1",
"write_here_the_paths_you_want_to_leave_exposed"
];
// Location of a bakup folder for modified model-config.json (change this according to your specific needs):
var backup_folder = "grunt-play-field/backups-model-config/";
var src_folder = "server/";
var dest_folder = "server/";
var src_file_extension = ".json";
var src_file_root_name = "model-config";
var src_filename = src_file_root_name + src_file_extension;
var dest_filename = src_file_root_name + src_file_extension;
var src = src_folder + src_filename;
var dest = dest_folder + dest_filename;
var free_backup_file = "";
if (!grunt.file.exists(src)) {
grunt.log.error("file " + src + " not found");
throw grunt.util.error("Source file 'model-config.json' does NOT exists in folder '" + src_folder + "'");
}
// timestamp for the backup file of model-config.json
var dateFormat = require('dateformat');
var now = new Date();
var ts = dateFormat(now, "yyyy-mm-dd_hh-MM-ss");
// backup model-config.json
var root_file_backup = src_file_root_name + "_bkp" + "_";
var root_backup = backup_folder + root_file_backup;
free_backup_file = root_backup + ts + src_file_extension;
if (!grunt.file.exists(root_file_backup + "*.*", backup_folder)) {
//var original_file = grunt.file.read(src);
grunt.file.write(free_backup_file, "// backup of " + src + " as of " + ts + "\n");
//grunt.file.write(free_backup_file, original_file);
grunt.log.write("Creating BACKUP"['green'] + " of '" + src + "' " + "to file : "['green'] + free_backup_file + " ").ok();
} else {
grunt.log.write("NO BACKUP created"['red'] + " of '" + src + "' " + "because file : " + free_backup_file + " ALREADY EXISTS ! "['red']).error();
throw grunt.util.error("Destination backup file already exists");
}
// load model-config.json
var project = grunt.file.readJSON(src);//get file as json object
// make modifications in model-config.json
for (var rest_path in project) {
if (rest_path.charAt(0) === "_") {
grunt.log.write("SKIPPING"['blue'] + " the JSON item '" + rest_path + "' belonging to the " + "SYSTEM"['blue'] + ". ").ok();
continue; // skip first level items that are system-related
}
if (list_of_REST_path_to_EXPOSE.indexOf(rest_path) > -1) { //
project[rest_path]["public"] = true;
grunt.log.write("KEEPING"['green'] + " the REST path '" + rest_path + "' " + "EXPOSED"['green'] + ". ").ok();
} else {
project[rest_path]["public"] = false;
grunt.log.writeln("HIDING"['yellow'] + " REST path '" + rest_path + "' : it will " + "NOT"['yellow'] + " be exposed.");
}
}
}
source to share
I wanted to hide the PATCH method, but when I tried to hide it I had to hide the PUT method, I used this line:
Equipment.disableRemoteMethod('updateAttributes', false);
but later I found a way to hide only the PATCH method, this line works fine for me.
Equipment.sharedClass.find('updateAttributes', false).http = [{verb: 'put', path: '/'}];
The above line overrides the original http which has an updateAttributes method.
[{verb: 'put', path: '/'}, {verb: 'patch', path: '/'}]
source to share
Update on Santhosh Hirekerur's answer to hide everything on LB3, stop using the deprecated method Model.disableRemoteMethod
as well as the smarter way to hide updateAttributes
and any other future methods that might work the same.
I am checking if this method is in prototype and if it is, prefix the name prototype.
before disableRemoteMethodByName
:
module.exports = function (model) {
var methodNames = [
'create',
'upsert',
'deleteById',
'updateAll',
'updateAttributes',
'patchAttributes',
'createChangeStream',
'findOne',
'find',
'findById',
'count',
'exists',
'replace',
'replaceById',
'upsertWithWhere',
'replaceOrCreate'
];
methodNames.forEach(function (methodName) {
if (!!model.prototype[methodName]) {
model.disableRemoteMethodByName('prototype.' + methodName);
} else {
model.disableRemoteMethodByName(methodName);
}
});
}
I put the above code in server/middleware/disable-methods.js
and call it from a model like this:
var disableMethods = require('../../server/middleware/disable-methods');
module.exports = function (Model) {
disableMethods(Model);
}
source to share