UNPKG

@m92/express-utils

Version:

Utility Module for Express Framework

556 lines (463 loc) 18.8 kB
'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); } }; }