route4me-node
Version:
Access Route4Me's logistics-as-a-service API using our Node.js SDK
280 lines (238 loc) • 7.89 kB
JavaScript
"use strict";
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 _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
var request = require("superagent");
var errors = require("./errors");
var ResponseHandler = function () {
function ResponseHandler(PromiseConstructor, logger, validate, validateContext, callback) {
_classCallCheck(this, ResponseHandler);
var cb = "function" !== typeof callback ? function (x) {
return x;
} : callback;
this._logger = logger;
this._validate = validate;
this._validateContext = validateContext;
if (PromiseConstructor) {
var self = this;
this._p = new PromiseConstructor(function (res, rej) {
self._res = res;
self._rej = rej;
});
} else {
this._p = undefined;
this._res = function (res) {
return cb(null, res);
};
this._rej = function (err) {
return cb(err);
};
}
}
_createClass(ResponseHandler, [{
key: "callback",
value: function callback(err, res) {
if (err) {
return this._handleError(err, res);
}
return this._handleOk(res);
}
}, {
key: "fail",
value: function fail(err) {
return this._rej(err);
}
}, {
key: "getPromise",
value: function getPromise() {
return this._p;
}
}, {
key: "_handleOk",
value: function _handleOk(res) {
this.logger.debug({
src: "route4me:request-manager:ResponseHandler:_handleOk",
msg: "response ok"
});
var data = this._validate(res.body, this._validateContext, res);
if (data instanceof errors.Route4MeError) {
// TODO: include url and method to the log message
this._logger.warn({ "msg": "response validation error", "err": data });
return this.fail(data);
} else if (data instanceof Error) {
// TODO: include url and method to the log message
this._logger.error({ "msg": "Unhandled error during validation", "err": data, "fatal": true });
return this.fail(data);
}
// TODO: include url and method to the log message
this._logger.info({ "msg": "response ok" });
return this._res(data);
}
}, {
key: "_handleError",
value: function _handleError(err, res) {
this.logger.debug({
src: "route4me:request-manager:ResponseHandler:_handleError",
msg: "response error"
});
var e = new errors.Route4MeApiError(err.message, res, err);
// TODO: include url and method to the log message
this._logger.warn({ "msg": "response error", "err": e });
return this.fail(e);
}
}, {
key: "logger",
get: function get() {
return this._logger;
}
}]);
return ResponseHandler;
}();
/**
* Request manager, provides
* * simple API for sending HTTP requests
* * a way to handle HTTP responses
*
* @since 0.1.0
*
* @protected
*/
var RequestManager = function () {
/**
* Creates new RequestManager. All parameters are inherited from {Route4Me}
*
* @param {object} apiKey - see {Route4Me}
* @param {object} options - see {Route4Me}
* @return {RequestManager} - New Request Manager
*/
function RequestManager(apiKey, options) {
_classCallCheck(this, RequestManager);
var opt = options;
this._apiKey = apiKey;
this._baseUrl = opt["baseUrl"];
this._userAgent = opt["userAgent"];
this._logger = opt["logger"];
this._validate = "function" === typeof opt["validate"] ? opt["validate"] : function (ix) {
return ix;
};
if (true === opt["promise"]) {
this.logger.debug({
src: "route4me:request-manager:RequestManager",
msg: "promises: global Promise"
});
this._promiseConstructor = Promise;
} else if ("function" === typeof opt["promise"]) {
this.logger.debug({
src: "route4me:request-manager:RequestManager",
msg: "promises: explicitly defined promise-lib"
});
this._promiseConstructor = opt["promise"];
} else {
this.logger.debug({
src: "route4me:request-manager:RequestManager",
msg: "promises: off"
});
this._promiseConstructor = null;
}
}
_createClass(RequestManager, [{
key: "_makeRequest",
/**
* Wrapper around {@link external:superagent} with all options applied.
*
* @todo TODO: rename this method!!!
* @protected
*
* @param {object} options Request options
* @param {string} options.method HTTP method
* @param {string} options.path Server path
* @param {object} [options.qs] Query string
* @param {object} [options.body] Body
* @param {null|string|function} [options.validationContext=null]
* * `null` cause validation disabled (TODO: test this case)
* * `string` is threated as the name of JSON Schema
* * `function` will be used for validation.
* @param {module:route4me-node~RequestCallback} [callback]
*/
value: function _makeRequest(options, callback) {
var qs = options.qs || {}; /* query string */
var body = options.body || null;
var form = options.form || null;
var timeouts = {
response: 5000, // Wait 5 seconds for the server to start sending,
deadline: 10000 };
var method = options.method.toLowerCase();
if ("delete" === method) {
method = "del";
}
var apiUrl = void 0;
if (options.url) {
this.logger.debug({
src: "route4me:request-manager:RequestManager:_makeRequest",
msg: "WARNING: _makeRequest called with FULL url, but MUST be called only for partial path",
url: options.url
});
apiUrl = options.url;
} else {
apiUrl = `${this._baseUrl}${options.path}`;
}
qs["api_key"] = this._apiKey;
if (undefined === options.validationContext) {
// this is just a protective wall
throw new errors.Route4MeError("validationContext should not be undefined");
}
var v = this._validate;
var c = options.validationContext || null;
if ("function" === typeof c) {
v = c;
c = null;
}
this.logger.info({
src: "route4me:request-manager:RequestManager:_makeRequest",
msg: "sending request",
method,
url: apiUrl,
queryString: qs
});
var resHandler = new ResponseHandler(this._promiseConstructor, this._logger, v, c, callback);
// debug only!
// qs["oldUrl"] = apiUrl
// apiUrl = "https://httpbin.org/get"
var req = request[method](apiUrl).set("Route4Me-User-Agent", this._userAgent).timeout(timeouts).redirects(1000) // unlimited number of redirects
.accept("application/json").query(qs);
if (form) {
req.type("multipart/form-data").field(form);
} else {
req.type("application/json").send(body);
}
req.end(function (err, res) {
return resHandler.callback(err, res);
});
return resHandler.getPromise();
}
/**
* Early cancel request
*
* @todo TODO: rename this method!!!
* @todo TODO: write documentation
*
* @param {Error} error The reason the request was cancelled.
* @param {module:route4me-node~RequestCallback} [callback]
*/
}, {
key: "_makeError",
value: function _makeError(error, callback) {
var resHandler = new ResponseHandler(this._promiseConstructor, this._logger, this._validate, null, callback);
setTimeout(function () {
resHandler.fail(error);
}, 0);
return resHandler.getPromise();
}
}, {
key: "logger",
get: function get() {
return this._logger;
}
}]);
return RequestManager;
}();
module.exports = RequestManager;