UNPKG

@mcma/client

Version:

Node module with classes and functions used to access services in an MCMA environment

158 lines (157 loc) 6.16 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.HttpClient = void 0; const core_1 = require("@mcma/core"); const axios_1 = require("axios"); const headers_1 = require("./headers"); class HttpClient { authenticator; config; client; constructor(authenticator, config) { this.authenticator = authenticator; this.config = config; if (authenticator) { if (typeof authenticator.sign !== "function") { throw new core_1.McmaException("HttpClient: Provided authenticator does not define the required sign() function."); } } this.config = Object.assign({}, config); if (isNaN(this.config.maxAttempts) || this.config.maxAttempts < 1) { this.config.maxAttempts = 3; } if (isNaN(this.config.retryInterval) || this.config.retryInterval < 0) { this.config.retryInterval = 5000; } this.client = axios_1.default.create(Object.assign({}, this.config.axiosConfig)); } async get(urlOrConfig, config) { return await this.request(this.prepareRequest("GET", urlOrConfig, config)); } ; async post(body, urlOrConfig, config) { return await this.request(this.prepareRequest("POST", urlOrConfig, config, body)); } ; async put(body, urlOrConfig, config) { return await this.request(this.prepareRequest("PUT", urlOrConfig, config, body)); } ; async patch(body, urlOrConfig, config) { return await this.request(this.prepareRequest("PATCH", urlOrConfig, config, body)); } ; async delete(urlOrConfig, config) { return await this.request(this.prepareRequest("DELETE", urlOrConfig, config)); } ; prepareRequest(method, urlOrConfig, config, body) { let url; if (typeof urlOrConfig === "string") { url = urlOrConfig; } else if (!config) { config = urlOrConfig; url = ""; } if (!url && body?.id) { url = body.id; } url = url || ""; config = Object.assign({}, config); config.method = method; if (!config.baseURL && this.config?.axiosConfig?.baseURL) { config.baseURL = this.config?.axiosConfig?.baseURL; } config.url = url; config.data = body; config.transformResponse = (data) => { if (data) { try { return JSON.parse(data, core_1.Utils.reviver); } catch { } } return data; }; return config; } async request(config) { if (!config) { throw new core_1.McmaException("HttpClient: Missing configuration for making HTTP request"); } if (config.method === undefined) { config.method = "GET"; } if (config.baseURL) { if (!config.url) { config.url = config.baseURL; } else if (config.url.indexOf("http://") !== 0 && config.url.indexOf("https://") !== 0) { config.url = config.baseURL.replace(/\/?\/$/, "") + "/" + config.url.replace(/^\/+/, ""); } else if (!config.url.startsWith(config.baseURL)) { throw new core_1.McmaException("HttpClient: Making " + config.method + " request to URL '" + config.url + "' which does not match baseURL '" + config.baseURL + "'"); } } if (!config.url) { throw new core_1.McmaException("HttpClient: Missing url in request config"); } // add tracker header, if a tracker is present if (config.tracker) { if (!config.headers) { config.headers = {}; } config.headers[headers_1.McmaHeaders.tracker] = core_1.Utils.toBase64(JSON.stringify(config.tracker)); delete config.tracker; } // try copying original headers so we can safely run the authentication signer multiple times let headers = config.headers; if (headers) { try { headers = JSON.parse(JSON.stringify(headers)); } catch (error) { console.error("HttpClient: Failed to copy headers due to:"); console.error(error); } } // send request using axios for (let attempts = 0; attempts < this.config.maxAttempts; attempts++) { try { config.headers = headers; if (this.authenticator) { await this.authenticator.sign(config); } return await this.client.request(config); } catch (error) { // retrying 404 errors as well as 5xx errors. 404 errors can be caused by eventual consistency issues as a resource might be created but not yet available. if ((error?.response?.status === 404 || error?.response?.status >= 500) && attempts < this.config.maxAttempts - 1) { await core_1.Utils.sleep(this.config.retryInterval); } else { let response; if (error?.response?.data) { response = error.response.data; } else if (error?.response) { response = error.response; } else { response = "none"; } throw new core_1.McmaException("HttpClient: " + config.method + " request to " + config.url + " failed!", error, { config, response }); } } } // Though it never arrives here compiler complains about "TS7030: Not all code paths return a value." throw new core_1.McmaException("HttpClient: " + config.method + " request to " + config.url + " failed!"); } ; } exports.HttpClient = HttpClient;