@aircall/http
Advanced tools
Comparing version 0.4.0 to 0.4.2
@@ -6,2 +6,21 @@ # Change Log | ||
## [0.4.2](http://bitbucket.org/aircall/front-end-modules/compare/@aircall/http@0.4.1...@aircall/http@0.4.2) (2021-05-10) | ||
### Bug Fixes | ||
* trigger numbers and https releases [PH-4732] ([dde8541](http://bitbucket.org/aircall/front-end-modules/commits/dde85417b6036f033359c04951106d476245ff95)) | ||
## [0.4.1](http://bitbucket.org/aircall/front-end-modules/compare/@aircall/http@0.4.0...@aircall/http@0.4.1) (2021-05-07) | ||
**Note:** Version bump only for package @aircall/http | ||
# [0.4.0](http://bitbucket.org/aircall/front-end-modules/compare/@aircall/http@0.3.0...@aircall/http@0.4.0) (2021-04-06) | ||
@@ -8,0 +27,0 @@ |
@@ -6,1 +6,2 @@ export declare enum ErrorCode { | ||
export declare const INVALID_API_RESPONSE_MESSAGE = "An internal error occured"; | ||
export declare const DEFAULT_MAX_RETRY = 3; |
@@ -11,2 +11,3 @@ "use strict"; | ||
exports.INVALID_API_RESPONSE_MESSAGE = 'An internal error occured'; | ||
exports.DEFAULT_MAX_RETRY = 3; | ||
//# sourceMappingURL=constants.js.map |
@@ -1,2 +0,2 @@ | ||
import { AxiosError, AxiosRequestConfig } from 'axios'; | ||
import { AxiosError, AxiosRequestConfig, AxiosResponse } from 'axios'; | ||
import { HttpServiceOptions, RequestLogPayload, HTTP_LOG_TYPE } from './typing/HttpService'; | ||
@@ -31,2 +31,3 @@ declare class HttpService { | ||
logErrorInterceptor: (error: AxiosError<any>) => Promise<AxiosError<any>>; | ||
retryStrategyInterceptor: (error: AxiosError<any>) => Promise<AxiosResponse<any>>; | ||
private handleResponse; | ||
@@ -33,0 +34,0 @@ get<T = any>(path: string, config?: AxiosRequestConfig): Promise<T>; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const axios_1 = require("axios"); | ||
const constants_1 = require("./constants"); | ||
/* istanbul ignore next */ | ||
function defaultRetryCondition() { | ||
return false; | ||
} | ||
/* istanbul ignore next */ | ||
function noop() { | ||
return; | ||
} | ||
class HttpService { | ||
@@ -12,3 +21,3 @@ constructor(options) { | ||
if (this.token) { | ||
config = Object.assign(Object.assign({}, config), { headers: Object.assign(Object.assign({}, config.headers), { common: Object.assign(Object.assign({}, config.headers.common), { Authorization: `Bearer ${this.token}` }) }) }); | ||
config = Object.assign(Object.assign({}, config), { headers: Object.assign(Object.assign({}, config.headers), { Authorization: `Bearer ${this.token}` }) }); | ||
} | ||
@@ -56,2 +65,19 @@ return config; | ||
}; | ||
this.retryStrategyInterceptor = async (error) => { | ||
var _a; | ||
const { retryStrategy } = this.options; | ||
if (!retryStrategy) { | ||
return Promise.reject(error); | ||
} | ||
const { onRetryAttempt = noop, retryCondition = defaultRetryCondition, maxRetry = constants_1.DEFAULT_MAX_RETRY } = retryStrategy; | ||
const { config } = error; | ||
config.retryCount = (_a = config.retryCount) !== null && _a !== void 0 ? _a : 0; | ||
const shouldRetry = retryCondition(error) && config.retryCount < maxRetry; | ||
if (shouldRetry) { | ||
config.retryCount++; | ||
await onRetryAttempt(error); | ||
return this.axiosInstance.request(error.config); | ||
} | ||
return Promise.reject(error); | ||
}; | ||
this.handleResponse = (response) => { | ||
@@ -78,4 +104,5 @@ return response.data; | ||
this.axiosInstance.interceptors.response.use(this.logResponseInterceptor, this.logErrorInterceptor); | ||
this.axiosInstance.interceptors.response.use(undefined, this.retryStrategyInterceptor); | ||
if (handleError) { | ||
this.axiosInstance.interceptors.response.use(value => value, handleError); | ||
this.axiosInstance.interceptors.response.use(undefined, handleError); | ||
} | ||
@@ -82,0 +109,0 @@ } |
@@ -14,2 +14,3 @@ import { AxiosRequestConfig, AxiosError } from 'axios'; | ||
export declare type HTTP_LOG_TYPE = 'REQUEST' | 'SUCCESS' | 'ERROR'; | ||
export declare type HttpError<T = any> = AxiosError<T>; | ||
export interface HttpServiceOptions { | ||
@@ -21,4 +22,8 @@ apiBaseUrl?: string; | ||
handleError?: (error: AxiosError) => Promise<AxiosError>; | ||
retryStrategy?: { | ||
maxRetry?: number; | ||
retryCondition?: <T extends HttpError>(error: T) => boolean; | ||
onRetryAttempt?: <T extends HttpError>(error: T) => Promise<any>; | ||
}; | ||
} | ||
export declare type HttpError<T> = AxiosError<T>; | ||
export {}; |
{ | ||
"name": "@aircall/http", | ||
"version": "0.4.0", | ||
"version": "0.4.2", | ||
"main": "dist/index.js", | ||
@@ -14,6 +14,6 @@ "types": "dist/index.d.ts", | ||
}, | ||
"gitHead": "4b7fc5f9dffe6ccabfffdf820128ead7c6dbcc6f", | ||
"gitHead": "3441071ad789b88af3127571212f34ac07c61067", | ||
"dependencies": { | ||
"@aircall/logger": "^2.5.2", | ||
"axios": "0.19.2" | ||
"axios": "^0.21.1" | ||
}, | ||
@@ -20,0 +20,0 @@ "devDependencies": { |
@@ -9,1 +9,3 @@ export enum ErrorCode { | ||
export const INVALID_API_RESPONSE_MESSAGE = 'An internal error occured'; | ||
export const DEFAULT_MAX_RETRY = 3; |
@@ -1,2 +0,2 @@ | ||
import axios, { AxiosError } from 'axios'; | ||
import axios, { AxiosError, AxiosRequestConfig } from 'axios'; | ||
import MockAdapter from 'axios-mock-adapter'; | ||
@@ -261,2 +261,67 @@ import { Logger } from '@aircall/logger'; | ||
}); | ||
describe('RetryStrategy', () => { | ||
it('should not run the logic if we did not add retryStrategy', done => { | ||
const retryStrategy = undefined; | ||
const service = new HttpService({ apiBaseUrl: BASE_URL, retryStrategy, logger }); | ||
const error = | ||
{ config: { method: 'get', retryCount: undefined } } as | ||
AxiosError & { config: AxiosRequestConfig & { retryCount?: number } }; | ||
service.retryStrategyInterceptor(error).catch(errorInt => { | ||
expect(errorInt).toBe(error); | ||
expect(error.config.retryCount).toBe(undefined); | ||
done(); | ||
}); | ||
}); | ||
it('should call the retry strategy', done => { | ||
const retryStrategy = { | ||
onRetryAttempt: jest.fn().mockReturnValue({}), | ||
retryCondition: jest.fn().mockReturnValue(true) | ||
}; | ||
const service = new HttpService({ apiBaseUrl: BASE_URL, retryStrategy, logger }); | ||
const error = | ||
{ config: { method: 'get', retryCount: undefined } } as | ||
AxiosError & { config: AxiosRequestConfig & { retryCount?: number } }; | ||
service.retryStrategyInterceptor(error).catch(() => { | ||
expect(retryStrategy.onRetryAttempt).toHaveBeenCalledTimes(1); | ||
expect(retryStrategy.retryCondition).toHaveBeenCalledTimes(1); | ||
expect(error.config.retryCount).toBe(1); | ||
done(); | ||
}); | ||
}); | ||
it('should not call the retry strategy because we execeed the max retry attemps', done => { | ||
const retryStrategy = { | ||
onRetryAttempt: jest.fn().mockReturnValue({}), | ||
retryCondition: jest.fn().mockReturnValue(true) | ||
}; | ||
const service = new HttpService({ apiBaseUrl: BASE_URL, retryStrategy, logger }); | ||
const error = | ||
{ config: { method: 'get', retryCount: 3 } } as | ||
AxiosError & { config: AxiosRequestConfig & { retryCount?: number } }; | ||
service.retryStrategyInterceptor(error).catch(() => { | ||
expect(retryStrategy.onRetryAttempt).toHaveBeenCalledTimes(0); | ||
done(); | ||
}); | ||
}); | ||
it('should not call the retry strategy because retryCondition is false', done => { | ||
const retryStrategy = { | ||
onRetryAttempt: jest.fn().mockReturnValue({}), | ||
retryCondition: jest.fn().mockReturnValue(false) | ||
}; | ||
const service = new HttpService({ apiBaseUrl: BASE_URL, retryStrategy, logger }); | ||
const error = | ||
{ config: { method: 'get', retryCount: 0 } } as | ||
AxiosError & { config: AxiosRequestConfig & { retryCount?: number } }; | ||
service.retryStrategyInterceptor(error).catch(errorInt => { | ||
expect(retryStrategy.onRetryAttempt).toHaveBeenCalledTimes(0); | ||
expect(errorInt.config.retryCount).toBe(0); | ||
expect(errorInt).toBe(error); | ||
done(); | ||
}); | ||
}); | ||
}); | ||
}); |
@@ -5,3 +5,13 @@ import axios, { AxiosError, AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios'; | ||
import { HttpServiceOptions, RequestLogPayload, HTTP_LOG_TYPE } from './typing/HttpService'; | ||
import { DEFAULT_MAX_RETRY } from './constants'; | ||
/* istanbul ignore next */ | ||
function defaultRetryCondition(): boolean { | ||
return false; | ||
} | ||
/* istanbul ignore next */ | ||
function noop(): undefined { | ||
return; | ||
} | ||
class HttpService { | ||
@@ -46,4 +56,6 @@ private token?: string; | ||
this.axiosInstance.interceptors.response.use(undefined, this.retryStrategyInterceptor); | ||
if (handleError) { | ||
this.axiosInstance.interceptors.response.use(value => value, handleError); | ||
this.axiosInstance.interceptors.response.use(undefined, handleError); | ||
} | ||
@@ -77,6 +89,3 @@ } | ||
...config.headers, | ||
common: { | ||
...config.headers.common, | ||
Authorization: `Bearer ${this.token}` | ||
} | ||
Authorization: `Bearer ${this.token}` | ||
} | ||
@@ -151,2 +160,28 @@ }; | ||
public retryStrategyInterceptor = async (error: AxiosError): Promise<AxiosResponse<any>> => { | ||
const { retryStrategy } = this.options; | ||
if (!retryStrategy) { | ||
return Promise.reject(error); | ||
} | ||
const { | ||
onRetryAttempt = noop, | ||
retryCondition = defaultRetryCondition, | ||
maxRetry = DEFAULT_MAX_RETRY | ||
} = retryStrategy; | ||
const { config }: { config: AxiosRequestConfig & { retryCount?: number } } = error; | ||
config.retryCount = config.retryCount ?? 0; | ||
const shouldRetry = retryCondition(error) && config.retryCount < maxRetry; | ||
if (shouldRetry) { | ||
config.retryCount++; | ||
await onRetryAttempt(error); | ||
return this.axiosInstance.request(error.config); | ||
} | ||
return Promise.reject(error); | ||
}; | ||
private handleResponse = <T = any>(response: AxiosResponse<T>): AxiosResponse<T>['data'] => { | ||
@@ -153,0 +188,0 @@ return response.data; |
@@ -17,3 +17,3 @@ import { AxiosRequestConfig, AxiosError } from 'axios'; | ||
export type HTTP_LOG_TYPE = 'REQUEST' | 'SUCCESS' | 'ERROR'; | ||
export type HttpError<T = any> = AxiosError<T>; | ||
export interface HttpServiceOptions { | ||
@@ -25,4 +25,7 @@ apiBaseUrl?: string; | ||
handleError?: (error: AxiosError) => Promise<AxiosError>; | ||
retryStrategy?: { | ||
maxRetry?: number; | ||
retryCondition?: <T extends HttpError>(error: T) => boolean; | ||
onRetryAttempt?: <T extends HttpError>(error: T) => Promise<any>; | ||
}; | ||
} | ||
export type HttpError<T> = AxiosError<T>; |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
38836
735
+ Addedaxios@0.21.4(transitive)
+ Addedfollow-redirects@1.15.9(transitive)
- Removedaxios@0.19.2(transitive)
- Removedfollow-redirects@1.5.10(transitive)
Updatedaxios@^0.21.1