UNPKG

sleeprest

Version:

Resource driven REST server.

276 lines (248 loc) 7.9 kB
var Promise, Request, Resource, Response, formidable, http, path, querystring, _, __slice = [].slice; path = require('path'); _ = require('lodash'); Promise = require('bluebird'); http = require('http'); Request = require('./request'); Response = require('./response'); RegExp.quote = function(str) { return str.replace(/([.?*+^$[\]\\(){}|-])/g, "\\$1"); }; module.exports = Resource = (function() { function Resource(options) { this.resources = []; this.methods = {}; this.uses = []; this.options = options || {}; } Resource.prototype.method = function() { var fn, m, method, uses, _i; method = arguments[0], uses = 3 <= arguments.length ? __slice.call(arguments, 1, _i = arguments.length - 1) : (_i = 1, []), fn = arguments[_i++]; m = method.toUpperCase(); if (this.methods[m] != null) { throw new Error("Duplicate " + m + " handler set!"); } this.methods[m] = uses.concat([fn]); return this; }; Resource.prototype.use = function(fn) { this.uses.push(fn); return this; }; Resource.prototype.resource = function() { var PARAMREG, args, parameters, resource, uri, uriRegExp; args = 1 <= arguments.length ? __slice.call(arguments, 0) : []; if (args[0] instanceof RegExp) { uriRegExp = args[0], parameters = args[1]; if (parameters == null) { parameters = []; } if (!(parameters instanceof Array)) { parameters = [parameters]; } } else { if ((args[0] == null) || args[0] === '') { throw new Error("First argument of .resource must be a non-empty string!"); } uri = path.join.apply(path, args); if (uri.indexOf('/') === 0) { uri = uri.slice(1); } parameters = []; PARAMREG = /([^/]+)/; uri = '^\\/' + uri.split('/').map(function(segment) { if (segment.indexOf(':') === 0) { parameters.push(segment.slice(1)); return PARAMREG.source; } else { return RegExp.quote(segment); } }).join('\\/'); uriRegExp = new RegExp(uri); } resource = new Resource; this.resources.push({ resource: resource, uriRegExp: uriRegExp, paramNames: parameters }); return resource; }; Resource.prototype.res = Resource.prototype.resource; Resource.prototype._bubble = function(req, response) { var key, promise, url, value, _ref; _ref = this.options; for (key in _ref) { value = _ref[key]; response.options[key] = value; } url = req.url.match(/^([^?]*)\??.*$/)[1]; promise = Promise.bind(response)["return"](req); this.uses.forEach(function(use) { return promise = promise.then(use)["return"](req); }); return promise.then((function(_this) { return function() { var match, matching, paramname, params, _i, _len, _ref1; matching = _.find(_this.resources, function(resource) { return url.match(resource.uriRegExp) != null; }); if (matching == null) { if (['', '/'].indexOf(url) !== -1) { if (_this.methods[req.method] == null) { throw new Error("Method not allowed!"); } promise = Promise.bind(response); _this.methods[req.method].forEach(function(fn) { return promise = promise["return"](req).then(fn); }); return promise; } throw new Error("Resource not found!"); } match = url.match(matching.uriRegExp); params = req.params || (req.params = {}); _ref1 = matching.paramNames; for (key = _i = 0, _len = _ref1.length; _i < _len; key = ++_i) { paramname = _ref1[key]; params[paramname] = match[key + 1]; } req.url = url.slice(match[0].length); return matching.resource._bubble(req, response); }; })(this)); }; Resource.prototype._forgeRequest = function(req) { var response; req._url = req.url != null ? req.url : req.url = '/'; if (req.method == null) { req.method = 'GET'; } response = new Response(req, this.debug); return response.setBody(Promise["try"](this._bubble, [req, response], this)); }; Resource.prototype.set = function(option, value) { if (value == null) { return delete this.options[option]; } else { return this.options[option] = value; } }; Resource.prototype.listen = function() { var args, _ref; args = 1 <= arguments.length ? __slice.call(arguments, 0) : []; return this._server = (_ref = http.createServer((function(_this) { return function(req, res) { return _this._forgeRequest(req).then(function(response) { return response.applyTo(res); }); }; })(this))).listen.apply(_ref, args); }; return Resource; })(); ['get', 'post', 'put', 'delete'].forEach(function(method) { return Resource.prototype[method] = function() { var args; args = 1 <= arguments.length ? __slice.call(arguments, 0) : []; return this.method.apply(this, [method].concat(__slice.call(args))); }; }); formidable = require('formidable'); Resource.multipartBodyParser = function(opts) { if (opts == null) { opts = {}; } return function(req) { var form; if (req.contentType !== "multipart/form-data") { return; } form = new formidable.IncomingForm(); /* form.keepExtensions = (if opts.keepExtensions then true else false) form.onPart = onPart = (part) -> if part.filename and options.multipartFileHandler options.multipartFileHandler part, req else if not part.filename and options.multipartHandler options.multipartHandler part, req else form.handlePart part */ return new Promise(function(yell, cry) { return form.parse(req, function(err, fields, files) { if (err != null) { return cry(err); } return yell([fields, files]); }); }).spread(function(fields, files) { req.body = fields; return req.files = files; })["catch"](function(err) { throw err; }); }; }; querystring = require("querystring"); Resource.formBodyParser = function() { return function(req) { if (req.contentType !== 'application/x-www-form-urlencoded') { return; } return req.loadBody().then(function(body) { var params; params = querystring.parse(body.toString()); return req.body = params; })["catch"](function(e) { throw new Error("Invalid content"); }); }; }; Resource.jsonBodyParser = function() { return function(req) { if (req.contentType !== 'application/json') { return; } if (['POST', 'PUT'].indexOf(req.method) === -1) { return; } return req.loadBody().then(function(body) { var e; try { return req.body = JSON.parse(body); } catch (_error) { e = _error; return console.log('JSON ERROR :\'(', e); } }); }; }; Resource.bodyParser = function() { var form, json, multipart; json = Resource.jsonBodyParser(); multipart = Resource.multipartBodyParser(); form = Resource.formBodyParser(); return function(req) { return Promise.all([form(req), json(req), multipart(req)]); }; }; Resource.authorizationParser = function(options) { return function(req) { var credentials, scheme, _ref; req.authorization = {}; if (!req.headers.authorization) { return; } _ref = req.headers.authorization.split(" ", 2), scheme = _ref[0], credentials = _ref[1]; if (credentials == null) { throw new Error("HTTP:400 Invalid Authorization header!"); } return req.authorization = { scheme: scheme.toLowerCase(), credentials: credentials, raw: scheme.toLowerCase() + " " + credentials }; }; };