@mcma/client
Version:
Node module with classes and functions used to access services in an MCMA environment
158 lines (157 loc) • 6.16 kB
JavaScript
;
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;