rest-methods
Version:
Declaratively publish functions for remote invocation.
253 lines (203 loc) • 8.73 kB
JavaScript
;
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"];