rest-methods
Version:
Declaratively publish functions for remote invocation.
167 lines (136 loc) • 5.8 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 _jsUtil = require("js-util");
var _jsUtil2 = _interopRequireDefault(_jsUtil);
var _pageJs = require("../page-js");
var _pageJs2 = _interopRequireDefault(_pageJs);
var _errors = require("../errors");
var _MethodDocs = require("./MethodDocs");
var _MethodDocs2 = _interopRequireDefault(_MethodDocs);
var _url = require("../url");
/**
* Represents a method.
*/
var ServerMethod = (function () {
function ServerMethod(name, func, routePattern, verb, docs) {
_classCallCheck(this, ServerMethod);
// Setup initial conditions.
if (_lodash2["default"].isEmpty(name)) {
throw new Error("Method name not specified.");
}
if (_jsUtil2["default"].isNumeric(name[0])) {
throw new Error("Method names cannot start with numbers. Given name: " + name);
}
if (name.match(/^[a-zA-Z0-9-\/]*$/) === null) {
throw new Error("A method name can only contain alpha-numeric characters and '/' or '-'. Given name: " + name);
}
if (!_lodash2["default"].isFunction(func)) {
throw new Error("Function not specified for the method \"" + name + "\".");
}
if (_lodash2["default"].isEmpty(routePattern)) {
throw new Error("URL pattern not specified for the method \"" + name + "\".");
}
if (_lodash2["default"].isEmpty(verb)) {
throw new Error("HTTP verb not specified for the method \"" + name + "\".");
}
// Store state.
this.name = name;
this.func = func;
this.verb = verb;
if (docs) {
this.docs = new _MethodDocs2["default"](docs);
}
// Store routes.
this.route = new _pageJs2["default"].Route(routePattern);
this.pathRoute = new _pageJs2["default"].Route(routePattern.split("?")[0]); // Route excluding the query-string pattern.
var routeKeys = this.route.keys;
// Calculate parameters.
var params = _jsUtil2["default"].functionParameters(func);
if (params.length > 0) {
this.params = params.map(function (paramName) {
return { name: paramName };
});
}
// If the URL route has parameters, ensure the function has enough parameters.
if (routeKeys.length > 0 && params.length < routeKeys.length) {
var routeParams = routeKeys.map(function (item) {
return item.name;
}).join(",");
throw new Error("The " + verb + " method \"" + name + "\" does not have enough parameters based on the URL route \"" + routePattern + "\". Should be: " + name + "(" + routeParams + ")");
}
// Ensure URL params are represented within the function.
if (routeKeys.length > 0) {
(function () {
var index = 0;
routeKeys.forEach(function (item) {
if (params[index] !== item.name) {
throw new Error("The \"" + name + "\" method has a function parameter (\"" + params[index] + "\") at index " + index + " that does not match the URL pattern. URL parameters come first. The parameter should be \"" + item.name + "\".");
}
index += 1;
});
})();
}
}
/**
* Invokes the method function.
*
* @param args: An array of arguments.
* @param url: The requesting URL.
* @return promise.
*/
_createClass(ServerMethod, [{
key: "invoke",
value: function invoke(args, url) {
var _this = this;
return new Promise(function (resolve, reject) {
var rejectWithError = function rejectWithError(err) {
err = new _errors.ServerMethodError(err.status, _this.name, args, err.message);
reject(err);
};
// Extract the URL parameters.
var urlParams = (0, _url.getUrlParams)(_this.route, url);
// Prepend the URL params to the arguments if there are any.
if (urlParams.length > 0) {
args = _lodash2["default"].flatten([urlParams, args]);
}
var thisContext = {
verb: _this.verb,
url: {
path: url,
params: urlParams
},
"throw": function _throw(status, message) {
throw new _errors.ServerMethodError(status, _this.name, args, message);
}
};
// Attempt to invoke the function.
try {
var result = _this.func.apply(thisContext, args);
if (result && _lodash2["default"].isFunction(result.then)) {
// A promise was returned.
result.then(function (asyncResult) {
resolve(asyncResult);
})["catch"](function (err) {
rejectWithError(err);
});
} else {
// A simple value was returned.
resolve(result);
}
} catch (err) {
rejectWithError(err);
}
});
}
}]);
return ServerMethod;
})();
exports["default"] = ServerMethod;
module.exports = exports["default"];