From 751c212686f7a84a7e1b8a24337234a069ba6fae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dawid=20Zbi=C5=84ski?= Date: Fri, 28 Aug 2020 09:26:30 +0200 Subject: [PATCH] V3.0.0 (#111) v3.0.0: - deprecated `skipWhileRefresh` - renamed `skipWhileRefresh` flag to `pauseInstanceWhileRefreshing` - changed default behavior of `pauseInstanceWhileRefreshing` --- README.md | 29 +++++++++++++++++++++-------- package-lock.json | 2 +- package.json | 2 +- src/__tests__/index.spec.ts | 28 ++++++++++++++++++++++++---- src/index.ts | 12 ++++++++++-- src/utils.ts | 22 +++++++++++++--------- 6 files changed, 70 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index 347301a..f9acaa9 100644 --- a/README.md +++ b/README.md @@ -49,7 +49,7 @@ In order to activate the interceptors, you need to import a function from `axios which is *exported by default* and call it with the **axios instance** you want the interceptors for, as well as the **refresh authorization function** where you need to write the logic for refreshing the authorization. -The interceptors will then be bound onto the axios instance and the specified logic will be run whenever a [401 (Unauthorized)](https://httpstatuses.com/401) status code +The interceptors will then be bound onto the axios instance, and the specified logic will be run whenever a [401 (Unauthorized)](https://httpstatuses.com/401) status code is returned from a server (or any other status code you provide in options). All the new requests created while the refreshAuthLogic has been processing will be bound onto the Promise returned from the refreshAuthLogic function. This means that the requests will be resolved when a new access token has been fetched or when the refreshing logic faleid. @@ -144,17 +144,22 @@ stalled request is called with the request configuration object. } ``` -#### Unpause the instance while refreshing +#### Pause the instance while "refresh logic" is running -While your refresh logic is ran, the instance is marked as "to-be-skipped" -in order to prevent the "interceptors loop" when refreshing causes one of the statuses specified -in `options.statusCodes`. If that's behavior is not wanted, you can set the `skipWhileRefreshing` option to false, -but keep in mind that you need to implement skipping the requests by yourself using `skipAuthRefresh` flag -in request's configuration +While your refresh logic is running, the interceptor will be triggered for every request +which returns one of the `options.statusCodes` specified (HTTP 401 by default). + +In order to prevent the interceptors loop (when your refresh logic fails with any of the status +codes specified in `options.statusCodes`) you need to use a [`skipAuthRefresh`](#skipping-the-interceptor) +flag on your refreshing call inside the `refreshAuthLogic` function. + +In case your refresh logic does not make any calls, you should consider using the following flag +when initializing the interceptor to pause the whole axios instance while the refreshing is pending. +This prevents interceptor from running for each failed request. ```javascript { - skipWhileRefreshing: false // default: true + pauseInstanceWhileRefreshing: true // default: false } ``` @@ -168,6 +173,14 @@ have you used it for something else? Create a PR with your use case to share it. --- +### Changelog + +- **v3.0.0** + - `skipWhileRefresh` flag has been deprecated due to its unclear name and its logic has been moved to `pauseInstanceWhileRefreshing` flag + - `pauseInstanceWhileRefreshing` is set to `false` by default + +--- + #### Want to help? Check out [contribution guide](CONTRIBUTING.md) or my [patreon page!](https://www.patreon.com/dawidzbinski) diff --git a/package-lock.json b/package-lock.json index e8a7c57..103686a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "axios-auth-refresh", - "version": "2.2.8", + "version": "3.0.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 4814379..f039b06 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "axios-auth-refresh", - "version": "2.2.8", + "version": "3.0.0", "description": "Axios plugin which makes it very easy to automatically refresh the authorization tokens of your clients", "keywords": [ "axios", diff --git a/src/__tests__/index.spec.ts b/src/__tests__/index.spec.ts index d8c2b7f..0c01afa 100644 --- a/src/__tests__/index.spec.ts +++ b/src/__tests__/index.spec.ts @@ -77,10 +77,6 @@ describe('Merges configs', () => { }); }); -describe('Uses options correctly', () => { - -}); - describe('Determines if the response should be intercepted', () => { let cache: AxiosAuthRefreshCache = undefined; @@ -133,6 +129,30 @@ describe('Determines if the response should be intercepted', () => { }; expect(shouldInterceptError(error, options, axios, cache)).toBeTruthy(); }); + + it('when pauseInstanceWhileRefreshing flag is not provided', () => { + const error = { + response: { status: 401 }, + }; + expect(shouldInterceptError(error, options, axios, cache)).toBeTruthy(); + }); + + it('when pauseInstanceWhileRefreshing flag is set to true', () => { + const error = { + response: { status: 401 }, + }; + const newCache = { ...cache, skipInstances: [ axios ] }; + const newOptions = { ...options, pauseInstanceWhileRefreshing: true }; + expect(shouldInterceptError(error, newOptions, axios, newCache)).toBeFalsy(); + }); + + it('when pauseInstanceWhileRefreshing flag is set to false', () => { + const error = { + response: { status: 401 }, + }; + const newOptions = { ...options, pauseInstanceWhileRefreshing: false }; + expect(shouldInterceptError(error, newOptions, axios, cache)).toBeTruthy(); + }); }); describe('Creates refresh call', () => { diff --git a/src/index.ts b/src/index.ts index 1b09ec7..1187658 100644 --- a/src/index.ts +++ b/src/index.ts @@ -9,12 +9,18 @@ import { shouldInterceptError, AxiosAuthRefreshCache, createRequestQueueInterceptor, -} from "./utils"; +} from './utils'; export interface AxiosAuthRefreshOptions { statusCodes?: Array; retryInstance?: AxiosInstance; + /** + * @deprecated + * This flag has been deprecated in favor of `pauseInstanceWhileRefreshing` flag. + * Use `pauseInstanceWhileRefreshing` instead. + */ skipWhileRefreshing?: boolean; + pauseInstanceWhileRefreshing?: boolean; onRetry?: (requestConfig: AxiosRequestConfig) => AxiosRequestConfig } @@ -60,7 +66,9 @@ export default function createAuthRefreshInterceptor( return Promise.reject(error); } - cache.skipInstances.push(instance); + if (options.pauseInstanceWhileRefreshing) { + cache.skipInstances.push(instance); + } // If refresh call does not exist, create one const refreshing = createRefreshCall(error, refreshAuthCall, cache); diff --git a/src/utils.ts b/src/utils.ts index 0301d84..1908608 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,5 +1,5 @@ -import { AxiosAuthRefreshOptions } from "./index"; -import axios, { AxiosInstance, AxiosPromise } from "axios"; +import { AxiosAuthRefreshOptions } from './index'; +import axios, { AxiosInstance, AxiosPromise } from 'axios'; export interface AxiosAuthRefreshCache { skipInstances: AxiosInstance[]; @@ -9,19 +9,23 @@ export interface AxiosAuthRefreshCache { export const defaultOptions: AxiosAuthRefreshOptions = { statusCodes: [ 401 ], - skipWhileRefreshing: true, + pauseInstanceWhileRefreshing: false, }; /** - * Merges two options objects (source rewrites target). + * Merges two options objects (options overwrites defaults). * * @return {AxiosAuthRefreshOptions} */ export function mergeOptions( - target: AxiosAuthRefreshOptions, - source: AxiosAuthRefreshOptions, + defaults: AxiosAuthRefreshOptions, + options: AxiosAuthRefreshOptions, ): AxiosAuthRefreshOptions { - return { ...target, ...source }; + return { + ...defaults, + pauseInstanceWhileRefreshing: options.skipWhileRefreshing, + ...options, + }; } /** @@ -40,7 +44,7 @@ export function shouldInterceptError( return false; } - if (error.config && error.config.skipAuthRefresh) { + if (error.config?.skipAuthRefresh) { return false; } @@ -48,7 +52,7 @@ export function shouldInterceptError( return false; } - return !options.skipWhileRefreshing || !cache.skipInstances.includes(instance); + return !options.pauseInstanceWhileRefreshing || !cache.skipInstances.includes(instance); } /**