diff options
author | 2017-08-07 11:39:39 +0800 | |
---|---|---|
committer | 2017-08-07 11:39:39 +0800 | |
commit | 21d72c4a80fe2937d0c4ddd20624b27adbcd989b (patch) | |
tree | e5013ee12f74f8452e01cbff16e7b0158bc456cb /common/src/main/webapp/usageguide/appserver/node_modules/node-restful/lib/model.js | |
parent | f533e73e2ae32e010b16abdcf7985abaf31ab843 (diff) |
Upload the ESR GUI seed code
Issue-ID: AAI-68
Change-Id: Ia50ce0570c2fabecd77199d4e8454f56fe587c4e
Signed-off-by: lizi00164331 <li.zi30@zte.com.cn>
Diffstat (limited to 'common/src/main/webapp/usageguide/appserver/node_modules/node-restful/lib/model.js')
-rw-r--r-- | common/src/main/webapp/usageguide/appserver/node_modules/node-restful/lib/model.js | 479 |
1 files changed, 479 insertions, 0 deletions
diff --git a/common/src/main/webapp/usageguide/appserver/node_modules/node-restful/lib/model.js b/common/src/main/webapp/usageguide/appserver/node_modules/node-restful/lib/model.js new file mode 100644 index 0000000..1531498 --- /dev/null +++ b/common/src/main/webapp/usageguide/appserver/node_modules/node-restful/lib/model.js @@ -0,0 +1,479 @@ +var mongoose = require('mongoose'), + _ = require('underscore'), + Model = mongoose.Model, + handlers = require('./handlers'); + +exports = module.exports = model; + +var methods = ['get', 'post', 'put', 'delete'], // All HTTP methods, PATCH not currently supported + endpoints = ['get', 'post', 'put', 'delete', 'getDetail'], + defaultroutes = ['schema'], + lookup = { + 'get': 'index', + 'getDetail': 'show', + 'put': 'updated', + 'post': 'created', + 'delete': 'deleted' + }, + valid_alterables = filterable({ + 'populate': query('populate'), + }, {}); + valid_filters = filterable({ + 'limit': query('limit'), + 'skip': query('skip'), + 'offset': query('offset'), + 'select': query('select'), + 'sort': query('sort'), + }, { + 'equals': query('equals'), + 'gte': query('gte'), + 'gt': query('gt'), + 'lt': query('lt'), + 'lte': query('lte'), + 'ne': query('ne'), + 'regex': function(val, query) { + var regParts = val.match(/^\/(.*?)\/([gim]*)$/); + if (regParts) { + // the parsed pattern had delimiters and modifiers. handle them. + val = new RegExp(regParts[1], regParts[2]); + } else { + // we got pattern string without delimiters + val = new RegExp(val); + } + + return query.regex(val); + }, + 'in': query('in'), + 'nin': query('nin'), + }); + defaults = function() { + return { + routes: {}, + allowed_methods: { + get: { detail: false } + }, + update_options: {}, + remove_options: {}, + templateRoot: '', + shouldIncludeSchema: true, + shouldUseAtomicUpdate: true + }; + }; + +/** + * Returns the model associated with the given name or + * registers the model with mongoose + */ +function model() { + var result = mongoose.model.apply(mongoose, arguments), + default_properties = defaults(); + if (1 === arguments.length) return result; + + for (var key in default_properties) { + result[key] = default_properties[key]; + } + + return result; +} + +Model.includeSchema = function(shouldIncludeSchema) { + this.shouldIncludeSchema = shouldIncludeSchema; + return this; +}; + +Model.methods = function(newmethods) { + var self = this, + get = contains(newmethods, 'get'); + + methods.forEach(function(method) { + delete self.routes[method]; + }); + + this.allowed_methods = []; + if (!Array.isArray(newmethods)) { + newmethods = [newmethods]; + } + if (get && !contains(newmethods, 'getDetail')) { + newmethods.push({ + method: 'getDetail', + before: (typeof get !== 'string') ? get.before : null, + after: (typeof get !== 'string') ? get.after : null + }); + } + newmethods.forEach(function(meth) { + var method = meth.method; + if ('string' === typeof meth) { + method = meth; + meth = {}; + } + if (!method) throw new Error("Method object must have a method property"); + self.allowed_methods.push(method); + + meth.handler = handlers[method]; + meth.detail = (method !== 'get' && method !== 'post'); + self.route(method, meth); + }); + return this; +}; + +Model.updateOptions = function(options) { + this['update_options'] = options; + return this; +}; + +Model.removeOptions = function(options) { + this['remove_options'] = options; + return this; +}; + +Model.template = function(templatePath) { + if (templatePath.substr(-1) == '/') { + templatePath = templatePath.substr(0, templatePath.length - 1); + } + this.templateRoot = templatePath; + return this; +}; + +/** + * Adds the default routes for the HTTP methods and one to get the schema + */ +Model.addDefaultRoutes = function() { + if (this.shouldIncludeSchema) { + this.route('schema', handlers.schema); + } + this.addSchemaRoutes(); +}; + +Model.addSchemaRoutes = function() { + var self = this; + this.schema.eachPath(function(pathName, schemaType) { + if (pathName.indexOf('_id') === -1 && schemaType.instance === 'ObjectID') { + // Right now, getting nested models is the only operation supported + ['get'].forEach(function(method) { + self.route(pathName, method , { + handler: handlers[method + 'Path'].call(self, pathName), + detail: true + }); + }); + } + }); +}; + +/** + * Adds an internal route for a path and method or methods to a function + * + * @param {String|Object} path: absolute path (including method) or object of routes + * @param {String|Function} method: the method to route to or the handler function + * @param {Function} fn: The handler function + * @return {Model} for chaining + * @api public + */ +Model.route = function(path, method, fn) { + var route = getRoute(this.routes, path), + meths = methods, // Default to all methods + lastPath = path.substr(path.lastIndexOf('.') + 1); + + if (2 === arguments.length) { + fn = method; + if (!fn.methods && endpoints.indexOf(lastPath) > -1) { + meths = [lastPath]; + } else if (fn.methods) { + meths = fn.methods; + } + } else { + meths = [method]; + } + + if (fn) { + fn = normalizeHandler(fn); + + meths.forEach(function(meth) { + route[meth] = merge(route[meth], fn); + }); + } + return this; +}; + +Model.before = function(path, method, fn) { + if (2 == arguments.length) { + arguments[1] = { before: arguments[1] }; + } + return this.route.apply(this, arguments); +}; + +Model.after = function(path, method, fn) { + if (2 == arguments.length) { + arguments[1] = { after: arguments[1] }; + } + return this.route.apply(this, arguments); +}; + +/** + * Registers all of the routes in routeObj to the given app + * + * TODO(baugarten): refactor to make less ugly + * + * if (isEndpoint(routeObj, path)) { handleRegistration(app, prefix, path, routeObj); } + * else { + * for (var key in routeObj) { recurse } + * } + */ +Model.registerRoutes = function(app, prefix, path, routeObj) { + var self = this; + for (var key in routeObj) { + if (isEndpoint(routeObj, key)) { + var route = routeObj[key]; + var routehandlers = _.isArray(route.handler) ? route.handler : [route.handler]; + routehandlers = _.map(routehandlers, function(handler) { return handler.bind(self); }); + var detailGet = !route.detail && !path && key === 'get', + handlerlist = route.before.concat( + [preprocess.bind(self)], + routehandlers, + route.after, + [handlers.last] + ); + /** + * TODO(baugarten): Add an enum type-thing to specify detail route, detail optional or list + * aka prettify this + */ + if (route.detail) { + app[key](prefix + '/:id([0-9a-fA-F]{0,24})' + path , handlerlist); + } else if (detailGet) { + app[key](prefix + '/:id([0-9a-fA-F]{0,24}$)?', handlerlist); + } else { + app[key](prefix + path, handlerlist); + } + } else { + this.registerRoutes(app, prefix, path + '/' + key, routeObj[key]); + } + } +}; + +/** + * Registers this model to the given app + * + * This includes registering endpoints for all the methods desired + * in the model definition + * + */ +Model.register = function(app, url) { + this.addDefaultRoutes(); + app.getDetail = app.get; + this.registerRoutes(app, url, '', this.routes); +}; + +// Will I still support handle()? I think maybe for default routes it might be nice, but +// exposed via model.get, model.post, etc. +/*Model.prototype.handle = function(route, filters, data, callback) { + if (arguments.length === 3) { + callback = data; + data = {}; + } else if (arguments.length === 2) { + callback = filters; + filters = []; + data = {}; + } + route = route.replace(/\//g, /\./); + data.format = 'js'; + var req = { + url: route, + filters: filters, + body: data, + format: 'js', + } + var res = { + writeHeader: function() { }, + write: function(ret) { callback(ret); }, + send: function() {}, + }; + this.send(route.split(/\./), req, res); +} + +Model.prototype.send = function(routes, req, res, next) { + var handler = this.routes; + req.quer = this.filter(req.filters, req.body, req.query, this.Model.find({})); + req.templatePath = this.template(routes, req.filters); + routes.forEach(function(route) { + if (route in handler) handler = handler[route]; + else if (!('all' in handler)) { + handlers.respond(res, 404, handlers.respond404()); + handlers.last(req, res); + } + }); + if ('all' in handler) handler = handler.all; + + if ('function' === typeof handler) { + return handler.call(this, req, res, next); + } + + handlers.respond(res, 404, handlers.respond404()); + handlers.last(req, res); +}*/ + +/** + * Returns a query filtered by the data in the request + * Looks in req.body and req.query to get the filterable data + * Filters the query based on functions in valid_filters + */ +Model.filter = function(req, quer) { + var detail = false; // detail route + // filter by id + if (req.params.id) { + quer = this.findById(req.params.id); + detail = true + } + + [req.body, req.query, req.headers].forEach(function(alterableResponse) { + Object.keys(alterableResponse).filter(function(potential) { + return valid_alterables.contains(potential, quer); + }).forEach(function(valid_key) { + query = valid_alterables.filter(valid_key, alterableResponse[valid_key], quer); + }); + }); + + if (!detail) { + [req.body, req.query, req.headers].forEach(function(filterableData) { + Object.keys(filterableData).filter(function(potential_filter) { + return valid_filters.contains(potential_filter, quer); + }).forEach(function(valid_key) { + quer = valid_filters.filter(valid_key, filterableData[valid_key], quer); + }); + }); + } + return quer; +} + +function preprocess(req, res, next) { + req.body = req.body || {}; + req.query = req.query || {}; + req.quer = this.filter(req, this.find({})); + if (!('locals' in res)) { + res.locals = {}; + } + res.locals.bundle = {}; + + req.templatePath = resolveTemplate(req); + next(); +} + +function query(key) { + return function(val, query) { + return query[key](val); + }; +} + +function haveOneModel(req) { + return !!req.params.id; +} + +function resolveTemplate(req) { + var method = req.method.toLowerCase(), + tmplName; + if (methods.indexOf(method) > -1) { + if (haveOneModel(req) && method === 'get') { + method += 'Detail'; + } + tmplName = lookup[method]; + } + return tmplName; +} + +/** + * Merges a route with another function object + * fn.before is called after the old before + * fn.after is called before the old after + * If fn.handler is specified, then route.handler is overwritten + */ +function merge(route, fn) { + if (!route) return fn; + + route.before = route.before.concat(fn.before); + route.handler = fn.handler || route.handler; + route.after = fn.after.concat(route.after); + return route; +} + +function getRoute(routes, path) { + path = path.replace(/\//g, /\./).split(/\./); + if (1 === path.length && '' === path[0]) { // we got the empty string + path = []; + } + if (endpoints.indexOf(path[path.length - 1]) > -1) { + path.splice(path.length - 1, 1); + } + path.forEach(function(sub, i) { + if (!routes[sub]) routes[sub] = {}; + routes = routes[sub]; + }); + return routes; +} + +function normalizeHandler(fn) { + var result = {}; + result.handler = fn.handler; + result.detail = fn.detail; + if ({}.toString.call(fn) == '[object Function]') { + result = { + handler: fn + }; + } + ['before', 'after'].forEach(function(hook) { + result[hook] = fn[hook] || []; + if (!Array.isArray(result[hook])) { + result[hook] = [ result[hook] ]; + } + }); + return result; +} + +function isEndpoint(route, method) { + return endpoints.indexOf(method) > -1 && route[method].handler; +} + +function contains(arr, key) { + if (arr.indexOf(key) > -1) return true; + for (var obj in arr) { + if (obj.method === key) { + return true; + } + } + return false; +}; + +function coerceData(filter_func, data) { + // Assume data is a string + if (data && data.toLowerCase && data.toLowerCase() === 'true') { + return true; + } else if (data && data.toLowerCase && data.toLowerCase() === 'false') { + return false; + } else if (filter_func === 'limit' || filter_func === 'skip') { + return parseInt(data); + } + return data; +}; + +function filterable(props, subfilters) { + return { + filter: function(key, val, quer) { + if (props[key]) { + return props[key](val, quer); + } + var field = key.split('__'), + filter_func = field[1] || 'equals', + data = coerceData(filter_func, val); + + // Turn data into array for $in and $nin clause + if (filter_func === 'in' || filter_func === 'nin') { + data = data.split(','); + } + + return subfilters[filter_func](data, quer.where(field[0])); + }, + contains: function(key, quer) { + if (key in props) return true; + var field = key.split('__'); + var filter_func = field[1] || 'equals'; + return field[0] in quer.model.schema.paths && filter_func in subfilters; + } + } +} |