@m92/express-utils
Version:
Utility Module for Express Framework
556 lines (463 loc) • 18.8 kB
JavaScript
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = HttpClientCreator;
var _axios = _interopRequireDefault(require("axios"));
var _axiosRetry = _interopRequireDefault(require("axios-retry"));
var _expressHttpContext = _interopRequireDefault(require("express-http-context"));
var _camaro = require("camaro");
var _nanoid = require("nanoid");
var _mimeTypes = _interopRequireDefault(require("mime-types"));
var _autoBind = _interopRequireDefault(require("auto-bind"));
var _HTTP_CLIENT_CONFIG = _interopRequireDefault(require("../defaults/HTTP_CLIENT_CONFIG"));
var _AXIOS_RETRY_CONFIG = _interopRequireDefault(require("../defaults/AXIOS_RETRY_CONFIG"));
var _excluded = ["retry"];
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } }
function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; }
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
function _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }
function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }
function HttpClientCreator(CONFIG, CONSTANTS, thisExpressUtils) {
var {
HTTP_CLIENT_LOG_ENABLED,
HTTP_CLIENT_BODY_LOG_ENABLED,
AAUTH_API_KEY
} = CONFIG;
var {
UID_CONTEXT,
UID_HEADER_KEY,
SESSION_ID_CONTEXT,
SESSION_ID_HEADER_KEY,
REQUEST_ID_CONTEXT,
REQUEST_ID_HEADER_KEY,
API_KEY_HEADER_KEY,
DEFAULT_SOAP_VERSION
} = CONSTANTS;
var {
CustomError,
logger,
ERROR_CLASSIFICATIONS
} = thisExpressUtils;
return class HttpClient {
constructor() {
var config = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
var overrideHeaders = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
var uid = overrideHeaders.uid || _expressHttpContext.default.get(UID_CONTEXT) || '';
var sessionId = overrideHeaders.sessionId || _expressHttpContext.default.get(SESSION_ID_CONTEXT) || '';
var requestId = overrideHeaders.requestId || _expressHttpContext.default.get(REQUEST_ID_CONTEXT) || '';
var headers = {
[UID_HEADER_KEY]: uid,
[SESSION_ID_HEADER_KEY]: sessionId,
[REQUEST_ID_HEADER_KEY]: requestId
};
var {
disableBodyLogging
} = config;
var customConfig = {
disableBodyLogging: !!disableBodyLogging,
overrideHeaders
}; // retry config
var {
retry: retryConfig = {}
} = config,
axiosConfig = _objectWithoutProperties(config, _excluded);
var axiosConf = _objectSpread(_objectSpread({}, _HTTP_CLIENT_CONFIG.default), {}, {
headers
}, axiosConfig);
this.client = _axios.default.create(axiosConf); // Request Interceptors
this.client.interceptors.request.use( // Interceptor for Request Success
HttpClient.requestLoggerSuccess(customConfig), // Interceptor for Request Failure
HttpClient.requestLoggerFailure(customConfig)); // Response Interceptors
this.client.interceptors.response.use( // Interceptor for Response Success
HttpClient.responseLoggerSuccess(customConfig), // Interceptor for Response Failure
HttpClient.responseLoggerFailure(customConfig)); // axios-retry
var axiosRetryConfig = _objectSpread(_objectSpread({}, _AXIOS_RETRY_CONFIG.default), retryConfig);
(0, _axiosRetry.default)(this.client, axiosRetryConfig);
(0, _autoBind.default)(this);
}
request(options, errorMap) {
var _this = this;
return _asyncToGenerator(function* () {
var {
client
} = _this;
var {
method,
url
} = options;
var classification = ERROR_CLASSIFICATIONS.API_CALL;
if (!method) {
var methodErrorMsg = 'HttpClientError: \'method\' cannot be empty';
var methodErrorParams = {
statusCode: 400,
message: methodErrorMsg,
classification
};
throw new CustomError(undefined, methodErrorParams);
}
if (!url) {
var urlErrorMsg = 'HttpClientError: \'url\' cannot be empty';
var urlErrorParams = {
statusCode: 400,
message: urlErrorMsg,
classification
};
throw new CustomError(undefined, urlErrorParams);
}
var sanitizedOptions = _this.sanitizeOptions(options);
return client.request(sanitizedOptions).catch(error => HttpClient.throwError(error, errorMap));
})();
}
requestSoap(options, errorMap) {
var _this2 = this;
return _asyncToGenerator(function* () {
var basicHeaders = {
'Content-Type': 'application/xml'
};
var requestOptions = _objectSpread(_objectSpread({}, options), {}, {
headers: _objectSpread(_objectSpread({}, basicHeaders), options.headers)
});
var {
method,
url
} = requestOptions;
var classification = ERROR_CLASSIFICATIONS.API_CALL;
if (!method) {
var methodErrorMsg = 'HttpClientError: \'method\' cannot be empty';
var methodErrorParams = {
statusCode: 400,
message: methodErrorMsg,
classification
};
throw new CustomError(undefined, methodErrorParams);
}
if (!url) {
var urlErrorMsg = 'HttpClientError: \'url\' cannot be empty';
var urlErrorParams = {
statusCode: 400,
message: urlErrorMsg,
classification
};
throw new CustomError(undefined, urlErrorParams);
}
return _this2.client.request(requestOptions).catch(error => HttpClient.throwError(error)).then(response => HttpClient.throwSoapError(response, errorMap));
})();
}
requestSoapWithAttachment(options) {
var _arguments = arguments,
_this3 = this;
return _asyncToGenerator(function* () {
var attachment = _arguments.length > 1 && _arguments[1] !== undefined ? _arguments[1] : {};
var errorMap = _arguments.length > 2 ? _arguments[2] : undefined;
var {
headers: optHeader = {},
data: optData = '',
soapVersion = DEFAULT_SOAP_VERSION
} = options;
var {
headers,
data
} = yield _this3._addAttachment(soapVersion, optData, attachment);
var requestHeaders = _objectSpread(_objectSpread({}, headers), optHeader);
var requestOptions = _objectSpread(_objectSpread({}, options), {}, {
headers: requestHeaders,
data
});
return _this3.requestSoap(requestOptions, errorMap);
})();
}
sanitizeOptions(options) {
var {
headers,
disableApiKey = false
} = options; // Handle Header for API Key
var apiKeyHeader = {
[API_KEY_HEADER_KEY]: AAUTH_API_KEY
};
var sanitizedHeaders = _objectSpread(_objectSpread({}, !disableApiKey && apiKeyHeader), headers);
var sanitizedOptions = _objectSpread(_objectSpread({}, options), {}, {
headers: sanitizedHeaders
});
return sanitizedOptions;
}
_addAttachment(soapVersion, soapData, attachment) {
var _this4 = this;
return _asyncToGenerator(function* () {
var {
file,
fileName,
type
} = attachment;
var mimeType = attachment.mimeType || _this4._getMimeType(fileName);
var {
contentType,
attachmentEncoding
} = _this4._getSoapVersionBasedProps(soapVersion);
var fileString = yield _this4._getFile(file, type, attachmentEncoding);
var boundary = "----=_Part_1_".concat((0, _nanoid.nanoid)());
var startContentId = "<rootpart.".concat((0, _nanoid.nanoid)(), ">");
var headers = {
'Content-Type': "multipart/related; type=\"".concat(contentType, "\"; start=\"").concat(startContentId, "\"; boundary=\"").concat(boundary, "\""),
SoapAction: ''
}; // Code is indent in such fashion due to the use of string interpolation
var startBoundary = "--".concat(boundary, "\nContent-Type: ").concat(contentType, "; charset=UTF-8\nContent-Transfer-Encoding: 8bit\nContent-ID: ").concat(startContentId, "\n");
var attachmentData = "--".concat(boundary, "\nContent-Type: ").concat(mimeType, "; name=").concat(fileName, "\nContent-Transfer-Encoding: ").concat(attachmentEncoding, "\nContent-ID: <").concat(fileName, "-").concat((0, _nanoid.nanoid)(), ">\n\n").concat(fileString);
var endBoundary = "--".concat(boundary, "--");
var data = "".concat(startBoundary, "\n").concat(soapData, "\n").concat(attachmentData, "\n").concat(endBoundary);
return {
headers,
data
};
})();
}
_getSoapVersionBasedProps(soapVersion) {
switch (soapVersion) {
case '1.1':
return {
contentType: 'text/xml',
attachmentEncoding: 'base64'
};
case '1.2':
return {
contentType: 'application/xop+xml',
attachmentEncoding: 'base64'
};
}
}
_getMimeType(fileName) {
return _mimeTypes.default.lookup(fileName);
}
_getFile(file, type, attachmentEncoding) {
var _this5 = this;
return _asyncToGenerator(function* () {
switch (type) {
case 'buffer':
{
return file.toString(attachmentEncoding);
}
case 'base64':
{
return attachmentEncoding === 'base64' && file || Buffer.from(file, 'base64').toString(attachmentEncoding);
}
case 'url':
{
var requestOptions = {
method: 'GET',
url: file,
responseType: 'arraybuffer'
};
try {
var response = yield _this5.request(requestOptions);
var {
data: body
} = response;
return attachmentEncoding === 'binary' && body || Buffer.from(body, 'binary').toString(attachmentEncoding);
} catch (e) {
var message = "Unable to download file from URL: ".concat(file);
var classification = ERROR_CLASSIFICATIONS.API_CALL;
var errorParams = {
message,
classification
};
var error = Buffer.from(e.error, 'binary').toString('utf8');
throw new CustomError(_objectSpread(_objectSpread({}, e), {}, {
error
}), errorParams);
}
}
default:
{
var _message = "Invalid Soap Attachment Type: ".concat(type);
var _classification = ERROR_CLASSIFICATIONS.VALIDATION;
var _errorParams = {
statusCode: 500,
message: _message,
classification: _classification
};
throw new CustomError(null, _errorParams);
}
}
})();
}
static throwError(error) {
var _arguments2 = arguments;
return _asyncToGenerator(function* () {
var errorMap = _arguments2.length > 1 && _arguments2[1] !== undefined ? _arguments2[1] : {};
var {
request,
response
} = error; // Handle Axios Response Error
if (response) {
var {
status = 500,
data: body
} = response;
var {
statusCode,
message,
data,
error: _error
} = body;
var statusErrorMap = errorMap[status] || {};
var classification = ERROR_CLASSIFICATIONS.API_CALL;
var {
name,
code,
message: errMapMsg,
statusCode: errMapStatusCode
} = statusErrorMap;
var e = _error || statusCode && message && data && undefined || body;
var errorParams = {
statusCode: errMapStatusCode || statusCode || status,
message: errMapMsg || message || undefined,
classification,
code,
name,
data
};
var _err = new CustomError(e, errorParams, false);
throw _err;
} // Handle Axios Request Error
if (request) {
var _classification2 = ERROR_CLASSIFICATIONS.CONNECTION;
var {
message: _message2
} = error;
var _errorParams2 = {
statusCode: 500,
message: _message2,
classification: _classification2
};
var _err2 = new CustomError(error, _errorParams2, false);
delete _err2.error.stack;
throw _err2;
} // Handle any other form of error
var err = new CustomError(error);
throw err;
})();
}
static throwSoapError(response, errorMap) {
return _asyncToGenerator(function* () {
if (!errorMap) {
return response;
}
var {
statusKey = '',
messageKey = '',
statusValueMap = {}
} = errorMap;
var errorTemplate = {
status: statusKey,
message: messageKey
};
var error = yield (0, _camaro.transform)(response, errorTemplate);
var mappedError = statusValueMap[error.status];
if (!mappedError) {
return response;
}
var {
statusCode,
message
} = mappedError;
var classification = ERROR_CLASSIFICATIONS.API_CALL;
var errorParams = {
statusCode,
message,
classification
};
throw new CustomError(error, errorParams, false);
})();
}
static requestLoggerSuccess(customConfig) {
return function (config) {
var isError = false;
HttpClient.logRequest(config, isError, customConfig);
return config;
};
}
static requestLoggerFailure(customConfig) {
return function (error) {
var isError = true;
HttpClient.logRequest(error, isError, customConfig);
return Promise.reject(error);
};
}
static responseLoggerSuccess(customConfig) {
return function (response) {
var isError = false;
HttpClient.logResponse(response, isError, customConfig);
return response;
};
}
static responseLoggerFailure(customConfig) {
return function (error) {
var isError = true;
HttpClient.logResponse(error, isError, customConfig);
return Promise.reject(error);
};
}
static logRequest(config, isError, customConfig) {
if (!HTTP_CLIENT_LOG_ENABLED) {
return;
}
var {
config: axiosConfig
} = config;
var {
method,
url,
data: body
} = axiosConfig || config;
var {
disableBodyLogging,
overrideHeaders = {}
} = customConfig;
var logMsg = "HttpClientRequest | ".concat(method, " ").concat(url);
var logData = _objectSpread({
url,
method,
body: HTTP_CLIENT_BODY_LOG_ENABLED && !disableBodyLogging && body || {}
}, overrideHeaders);
var logFunction = isError ? logger.error : logger.trace;
logFunction(logMsg, logData);
}
static logResponse(response, isError, customConfig) {
if (!HTTP_CLIENT_LOG_ENABLED) {
return;
}
var {
config = {},
code = '',
response: axiosResponse
} = response;
var {
status = 500,
statusText = '',
data: body
} = axiosResponse || response;
var {
method = '',
url = ''
} = config;
var {
disableBodyLogging,
overrideHeaders = {}
} = customConfig;
var logMsg = "HttpClientResponse | ".concat(method, " ").concat(url, " | ").concat(status, " ").concat(code || statusText);
var logData = _objectSpread({
url,
method,
code,
status,
statusText,
body: HTTP_CLIENT_BODY_LOG_ENABLED && !disableBodyLogging && body || {}
}, overrideHeaders);
var logFunction = isError ? logger.error : logger.info;
logFunction(logMsg, logData);
}
};
}