Skip to content

Commit

Permalink
Add validateResponse
Browse files Browse the repository at this point in the history
  • Loading branch information
obscurecat64 committed May 16, 2024
1 parent b48aa6c commit f25b520
Showing 1 changed file with 48 additions and 21 deletions.
69 changes: 48 additions & 21 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
import type { AxiosError, AxiosRequestConfig, AxiosInstance, AxiosStatic } from 'axios';
import type {
AxiosError,
AxiosRequestConfig,
AxiosInstance,
AxiosStatic,
AxiosResponse
} from 'axios';
import isRetryAllowed from 'is-retry-allowed';

export interface IAxiosRetryConfig {
Expand Down Expand Up @@ -34,6 +40,10 @@ export interface IAxiosRetryConfig {
* before throwing the error.
*/
onMaxRetryTimesExceeded?: (error: AxiosError, retryCount: number) => Promise<void> | void;
/**
* Defines whether a response should be resolved or rejected
*/
validateResponse?: ((response: AxiosResponse) => boolean) | null;
}

export interface IAxiosRetryConfigExtended extends IAxiosRetryConfig {
Expand Down Expand Up @@ -147,7 +157,8 @@ export const DEFAULT_OPTIONS: Required<IAxiosRetryConfig> = {
retryDelay: noDelay,
shouldResetTimeout: false,
onRetry: () => {},
onMaxRetryTimesExceeded: () => {}
onMaxRetryTimesExceeded: () => {},
validateResponse: null
};

function getRequestOptions(
Expand Down Expand Up @@ -201,6 +212,32 @@ async function shouldRetry(
}
return shouldRetryOrPromise;
}
async function handleRetry(
axiosInstance: AxiosInstance,
currentState: Required<IAxiosRetryConfigExtended>,
error: AxiosError,
config: AxiosRequestConfig
) {
currentState.retryCount += 1;
const { retryDelay, shouldResetTimeout, onRetry } = currentState;
const delay = retryDelay(currentState.retryCount, error);
// Axios fails merging this configuration to the default configuration because it has an issue
// with circular structures: https://github.com/mzabriskie/axios/issues/370
fixConfig(axiosInstance, config);
if (!shouldResetTimeout && config.timeout && currentState.lastRequestTime) {
const lastRequestDuration = Date.now() - currentState.lastRequestTime;
const timeout = config.timeout - lastRequestDuration - delay;
if (timeout <= 0) {
return Promise.reject(error);
}
config.timeout = timeout;
}
config.transformRequest = [(data) => data];
await onRetry(currentState.retryCount, error, config);
return new Promise((resolve) => {
setTimeout(() => resolve(axiosInstance(config)), delay);
});
}

async function handleMaxRetryTimesExceeded(
currentState: Required<IAxiosRetryConfigExtended>,
Expand All @@ -213,6 +250,10 @@ async function handleMaxRetryTimesExceeded(
const axiosRetry: AxiosRetry = (axiosInstance, defaultOptions) => {
const requestInterceptorId = axiosInstance.interceptors.request.use((config) => {
setCurrentState(config, defaultOptions);
if (config[namespace]?.validateResponse) {
// by setting this, all HTTP responses will be go through the error interceptor first
config.validateStatus = () => false;
}
return config;
});

Expand All @@ -223,26 +264,12 @@ const axiosRetry: AxiosRetry = (axiosInstance, defaultOptions) => {
return Promise.reject(error);
}
const currentState = setCurrentState(config, defaultOptions);
if (error.response && currentState.validateResponse?.(error.response)) {
// no issue with response
return error.response;
}
if (await shouldRetry(currentState, error)) {
currentState.retryCount += 1;
const { retryDelay, shouldResetTimeout, onRetry } = currentState;
const delay = retryDelay(currentState.retryCount, error);
// Axios fails merging this configuration to the default configuration because it has an issue
// with circular structures: https://github.com/mzabriskie/axios/issues/370
fixConfig(axiosInstance, config);
if (!shouldResetTimeout && config.timeout && currentState.lastRequestTime) {
const lastRequestDuration = Date.now() - currentState.lastRequestTime;
const timeout = config.timeout - lastRequestDuration - delay;
if (timeout <= 0) {
return Promise.reject(error);
}
config.timeout = timeout;
}
config.transformRequest = [(data) => data];
await onRetry(currentState.retryCount, error, config);
return new Promise((resolve) => {
setTimeout(() => resolve(axiosInstance(config)), delay);
});
return handleRetry(axiosInstance, currentState, error, config);
}

await handleMaxRetryTimesExceeded(currentState, error);
Expand Down

0 comments on commit f25b520

Please sign in to comment.