UNPKG

rest-methods

Version:

Declaratively publish functions for remote invocation.

253 lines (203 loc) 8.73 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } var _lodash = require("lodash"); var _lodash2 = _interopRequireDefault(_lodash); var _bodyParser = require("body-parser"); var _bodyParser2 = _interopRequireDefault(_bodyParser); var _ServerMethod = require("./ServerMethod"); var _ServerMethod2 = _interopRequireDefault(_ServerMethod); var _middleware = require("./middleware"); var _middleware2 = _interopRequireDefault(_middleware); var _connect = require("connect"); var _connect2 = _interopRequireDefault(_connect); var _const = require("../const"); var _http = require("http"); var _http2 = _interopRequireDefault(_http); var _chalk = require("chalk"); var _chalk2 = _interopRequireDefault(_chalk); var _jsUtil = require("js-util"); var _jsUtil2 = _interopRequireDefault(_jsUtil); var _url = require("../url"); /** * Generates a standard URL for a method. * * @param basePath: The base path to prefix the URL with. * @param path: The main part of the URL. * * @return string. */ var methodUrl = function methodUrl(basePath, path) { path = path.replace(/^\/*/, ""); var url = basePath + "/" + path; url = "/" + url.replace(/^\/*/, ""); return url; }; /** * Represents a REST API server. */ var Server = (function () { /** * Constructor * * @param connect: The connect app to apply the middleware to. * @param options: * - name: The name of the service. * - connect: The connect app to use. * If not specified a default Connect server is created. * - basePath: The base path to prepend URL"s with. * - version: The version number of the service. */ function Server() { var _this = this; var options = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0]; _classCallCheck(this, Server); // Store state. this.name = options.name || "Server Methods"; this.version = options.version || "0.0.0"; this[_const.METHODS] = {}; // Store base path. var path = options.basePath; if (_lodash2["default"].isString(path)) { path = path.replace(/^\/*/, "").replace(/\/*$/, ""); } else { path = ""; } this.basePath = "/" + path; // Register middleware. var connect = options.connect; if (!connect) { connect = (0, _connect2["default"])(); } connect.use(_bodyParser2["default"].json()); connect.use((0, _middleware2["default"])(this)); this.connect = connect; /** * Registers or retrieves the complete set of methods. * * @param definition: An object containing the method definitions. * @return an object containing the set of method definitions. */ this.methods = function (definition) { // Write: store method definitions if passed. if (definition) { (function () { var createUrl = function createUrl(urlPath, methodDef) { return methodUrl(_this.basePath, methodDef.url || urlPath); }; Object.keys(definition).forEach(function (key) { var methods = _this[_const.METHODS]; if (methods[key]) { throw new Error("Method \"" + key + "\" already exists."); } var value = definition[key]; var url = createUrl(key, value); var methodSet = undefined; if (_lodash2["default"].isFunction(value)) { // A single function was provided. // Use it for all the HTTP verbs. var func = value; methodSet = { get: new _ServerMethod2["default"](key, func, url, "GET"), put: new _ServerMethod2["default"](key, func, url, "PUT"), post: new _ServerMethod2["default"](key, func, url, "POST"), "delete": new _ServerMethod2["default"](key, func, url, "DELETE") }; } else if (_lodash2["default"].isObject(value)) { // Create individual methods for each verb. methodSet = {}; if (value.get) { methodSet.get = new _ServerMethod2["default"](key, value.get, url, "GET", value.docs); } if (value.put) { methodSet.put = new _ServerMethod2["default"](key, value.put, url, "PUT", value.docs); } if (value.post) { methodSet.post = new _ServerMethod2["default"](key, value.post, url, "POST", value.docs); } if (value["delete"]) { methodSet["delete"] = new _ServerMethod2["default"](key, value["delete"], url, "DELETE", value.docs); } } else { throw new Error("Type of value for method \"" + key + "\" not supported. Must be function or object."); } // Store an pointer to the method. // NOTE: This allows the server and client to behave isomorphically. // Server code can call the methods (directly) using the same // pathing/namespace object that the client uses, for example: // // server.methods.foo.put(123, "hello"); // var stub = _jsUtil2["default"].ns(_this.methods, key, { delimiter: "/" }); ["get", "put", "post", "delete"].forEach(function (verb) { var method = methodSet[verb]; if (method) { stub[verb] = function () { // Prepare the URL for the method. var route = method.route; var totalUrlParams = route.keys.length; for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) { args[_key] = arguments[_key]; } var invokeUrl = (0, _url.getMethodUrl)(method.name, null, route, args); if (totalUrlParams > 0) { args = _lodash2["default"].clone(args); // args.splice((args.length - totalUrlParams) , totalUrlParams); args.splice(0, totalUrlParams); } // Invoke the method. return method.invoke(args, invokeUrl); }; } }); // Store the values. _this[_const.METHODS][key] = methodSet; }); })(); } // Read. return _this[_const.METHODS]; }; // Finish up (Constructor). return this; } /** * Starts the server. * Only use this if you"re not passing in a connect server that * you are otherwise starting/managing independely for other purposes. * @param options: * - port: The HTTP port to use. * @return */ _createClass(Server, [{ key: "start", value: function start() { var options = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0]; var PORT = options.port || 3030; // Start the server. _http2["default"].createServer(this.connect).listen(PORT); // Output some helpful details to the console. var HR = _chalk2["default"].cyan(_lodash2["default"].repeat("-", 80)); var ADDRESS = "localhost:" + PORT; if (!this.basePath !== "/") { ADDRESS += this.basePath; } console.log(""); console.log(HR); console.log(_chalk2["default"].grey("Running") + " " + _chalk2["default"].black(this.name) + " " + _chalk2["default"].grey("(" + this.version + ") on") + " " + _chalk2["default"].cyan(ADDRESS)); console.log(HR); console.log(""); // Finish up. return this; } }]); return Server; })(); exports["default"] = function (options) { return new Server(options); }; module.exports = exports["default"];