UNPKG

jest

Version:

JavaScriptational State Transfer. JS restful API layer with Mongoose based resources. Inspired by python Tastypie

219 lines (196 loc) 7.11 kB
var _ = require('underscore'), Class = require('sji'); var Api = module.exports = Class.extend({ init:function (path, app) { this.path = /^(\/+)?$/g.test(path) ? '' : _.chain([]) .push( path.replace( /^(\/)?(.+[^\/])?(\/)?$/, '$2' ) ) .push('/') .join('') .value(); this.app = app; //Default Settings For Api this.settings = { DEFAULT_LIMIT:20, MAX_LIMIT:500 }; //Copy From App Settings (api) Defaults _.extend(this.settings, this.app.settings.api); this.resources = []; this.resources_schemas = []; this.resources_by_name = {}; var self = this; this.app.get('/' + this.path, function(req, res){ res.json(self.resources); }); }, /** * Register Resource to the Api * @param name * @param resource */ register:function (name, resource) { //Get Default Settings For Api to Resource var self = this; this.resources_by_name[name] = resource; resource.settings = this.settings; resource.path = _.chain([]) .push(self.path) .push(name) .join('') .value(); resource.schema_path = _.chain([]) .push(self.path) .push('schema/') .push(name) .join('') .value(); this.resources.push({ name:name, url:resource.path, schema: resource.schema_path }); var resource_schema = { name:name, url:resource.path, allowed_methods: resource.get_allowed_methods_tree(), fields:resource.show_fields(), update_fields:resource.show_update_fields(), filtering : _.map(resource.filtering || {},function(value,key) { var filtering_field = { field : key }; if(value !== null && typeof(value) === "object") { filtering_field.usages = _.map(value, function(value2, operand) { var operand_str = operand === 'exact' ? '' : '__' + operand; return resource.path + '?' + key + operand_str + '=' + self.default_value_per_operand(operand); }); } else { filtering_field.usages = [ resource.path + '?' + key + '=<value>', resource.path + '?' + key + '__in=<value1>,<value2>']; } return filtering_field; }), sorting : resource.path + '?order_by=' + (resource.sorting ? _.map(resource.sorting,function(val,key) { return _.shuffle(['','-'])[0] + key; }).join('&') : "order_by=<field1>,-<field2>") }; resource_schema.allowed_methods = _.map(resource_schema.allowed_methods,function(usage,method) { var ret_usage = null; var has_usage = typeof(usage) == 'object'; switch(method) { case 'get': ret_usage= {}; if(!has_usage || usage.details) ret_usage.details = typeof(usage.details)=='object' || typeof(usage.details)=='string' ? usage.details : self.get_details_usage(resource_schema); if(!has_usage || usage.list) ret_usage.list = typeof(usage.list)=='object' || typeof(usage.list)=='string' ? usage.list : self.get_list_usage(resource_schema); break; default: ret_usage = has_usage || self[method + '_usage'](resource_schema); break; } return { method : method, usage:ret_usage }; }); if(!( 'get' in resource_schema.allowed_methods)) { delete resource_schema['filtering']; delete resource_schema['sorting']; } this.app.get('/' + resource.schema_path,function(req,res){ res.json(resource_schema); }); this.app.get('/' + resource.path, function(req, res){ resource.index(req, res); }); this.app.get('/' + resource.path + '/:id', function(req, res){ req._id = req.params.id; resource.show(req, res); }); this.app.post('/' + resource.path, function(req, res){ req._id = req.params.id; resource.create(req, res); }); this.app.delete('/' + resource.path + '/:id', function(req, res){ req._id = req.params.id; resource.destroy(req, res); }); this.app.put('/' + resource.path + '/:id', function(req, res){ req._id = req.params.id; resource.update(req, res); }); }, //Alias for register -Backword Compability register_resource:function () { this.register.apply(this, arguments); }, get_details_usage: function(schema) { return schema.url + '/<' + schema.name + '_id>'; }, get_list_usage : function(schema) { return _.chain(schema.filtering).map(function(field) { return field.usages; }).flatten().push(schema.url).push(schema.sorting).value(); }, put_usage : function(schema) { return { url : schema.url + '/<' + schema.name + '_id>', body: this.put_post_body(schema) }; }, delete_usage: function(schema) { return { url : schema.url + '/<' + schema.name + '_id>' }; }, post_usage : function(schema) { return { url : schema.url, body : this.put_post_body(schema) } }, put_post_body: function(schema) { var body = {}; var field_names = _.keys(schema.update_fields); var num_of_fields = Math.min(3,field_names.length); for(var i=0; i<num_of_fields; i++){ body[field_names[i]] = '<some value>'; } return body; }, default_value_per_operand: function(operand) { switch(operand){ case 'in': return '<value1>,<value2,<value3>'; case 'near': return '<lat>,<lng>'; case 'or': return '<field1__operand1>,<field2__operand2>&<field1__operand1>=<value1>&<field2__operand2>=<value2>'; default: return '<value>'; } } });