UNPKG

@elastic.io/component-commons-library

Version:
98 lines (87 loc) 3.83 kB
import axios, { AxiosResponse, AxiosInstance, AxiosRequestConfig } from 'axios'; import { getLogger } from '../logger/logger'; export interface RetryOptions { retriesCount?: number; // values are validated with API_RETRIES_COUNT const below requestTimeout?: number; // values are validated with API_REQUEST_TIMEOUT const below } export const API_RETRIES_COUNT = { minValue: 0, defaultValue: 3, maxValue: 5 } as const; const ENV_API_RETRIES_COUNT = process.env.API_RETRIES_COUNT ? parseInt(process.env.API_RETRIES_COUNT, 10) : API_RETRIES_COUNT.defaultValue; export const API_REQUEST_TIMEOUT = { minValue: 500, defaultValue: 15000, maxValue: 120000 } as const; /** * if values are higher or lower the limit - they'll be overwritten. * returns valid values for RetryOptions */ export const getRetryOptions = (): RetryOptions => { const ENV_API_REQUEST_TIMEOUT = process.env.API_REQUEST_TIMEOUT ? parseInt(process.env.API_REQUEST_TIMEOUT, 10) : API_REQUEST_TIMEOUT.defaultValue; return { retriesCount: (ENV_API_RETRIES_COUNT > API_RETRIES_COUNT.maxValue || ENV_API_RETRIES_COUNT < API_RETRIES_COUNT.minValue) ? API_RETRIES_COUNT.defaultValue : ENV_API_RETRIES_COUNT, requestTimeout: (ENV_API_REQUEST_TIMEOUT > API_REQUEST_TIMEOUT.maxValue || ENV_API_REQUEST_TIMEOUT < API_REQUEST_TIMEOUT.minValue) ? API_REQUEST_TIMEOUT.defaultValue : ENV_API_REQUEST_TIMEOUT }; }; export const exponentialDelay = (currentRetries: number) => { const maxBackoff = 15000; const delay = (2 ** currentRetries) * 100; const randomSum = delay * 0.2 * Math.random(); // 0-20% of the delay return Math.min(delay + randomSum, maxBackoff); }; export const sleep = async (ms: number) => new Promise((resolve) => { setTimeout(resolve, ms); }); export const exponentialSleep = async (currentRetries: number) => sleep(exponentialDelay(currentRetries)); export const getErrMsg = (errResponse: AxiosResponse) => { const statusText = errResponse?.statusText || 'unknown'; const status = errResponse?.status || 'unknown'; const data = errResponse?.data || 'no body found'; return `Got error "${statusText}", status - "${status}", body: ${JSON.stringify(data)}`; }; export const axiosReqWithRetryOnServerError = async function (options: AxiosRequestConfig, axiosInstance: AxiosInstance = axios, logger = getLogger()) { const { retriesCount, requestTimeout } = getRetryOptions(); let response: AxiosResponse; let currentRetry = 0; let error; const loggerInstance = this?.logger || logger; while (currentRetry < retriesCount) { try { response = await axiosInstance.request({ ...options, timeout: requestTimeout, validateStatus: (status) => (status >= 200 && status < 300) || (status === 404 && this?.cfg?.doNotThrow404) }); return response; } catch (err) { loggerInstance.error(err.response ? getErrMsg(err.response) : err.message); error = err; if (err.response?.status < 500) { throw error; } loggerInstance.info(`URL: "${options.url}", method: ${options.method}, Error message: "${err.message}"`); loggerInstance.info(`Request failed, retrying(${1 + currentRetry})`); await exponentialSleep(currentRetry); currentRetry++; } } throw error; }; export const getFacelessRetriesCount = () => { const FACELESS_RETRIES_COUNT = { minValue: 0, defaultValue: 3, maxValue: 5, }; const ENV_FACELESS_RETRIES_COUNT = process.env.FACELESS_RETRIES_COUNT ? parseInt(process.env.FACELESS_RETRIES_COUNT, 10) : FACELESS_RETRIES_COUNT.defaultValue; return (ENV_FACELESS_RETRIES_COUNT > FACELESS_RETRIES_COUNT.maxValue || ENV_FACELESS_RETRIES_COUNT < FACELESS_RETRIES_COUNT.minValue) ? FACELESS_RETRIES_COUNT.defaultValue : ENV_FACELESS_RETRIES_COUNT; };