@servicetitan/ajax-handlers
Advanced tools
Comparing version 22.14.0 to 22.15.0
@@ -97,3 +97,3 @@ import axios from 'axios'; | ||
}, error => { | ||
var _a, _b, _c, _d, _e, _f, _g, _h, _j; | ||
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k; | ||
if (!((_a = error.config) === null || _a === void 0 ? void 0 : _a.hideGlobalLoader)) { | ||
@@ -111,5 +111,7 @@ loader.hide(); | ||
(Array.isArray((_d = error.config) === null || _d === void 0 ? void 0 : _d.suppressShowError) && | ||
!((_e = error.config) === null || _e === void 0 ? void 0 : _e.suppressShowError.includes((_f = error.response) === null || _f === void 0 ? void 0 : _f.status)))) { | ||
!((_e = error.config) === null || _e === void 0 ? void 0 : _e.suppressShowError.includes((_f = error.response) === null || _f === void 0 ? void 0 : _f.status))) || | ||
(typeof ((_g = error.config) === null || _g === void 0 ? void 0 : _g.suppressShowError) === 'function' && | ||
!error.config.suppressShowError(error))) { | ||
const isFiltered = errorFilteredUrls.some(url => { var _a, _b; return (_b = (_a = error.config) === null || _a === void 0 ? void 0 : _a.url) === null || _b === void 0 ? void 0 : _b.startsWith(url); }); | ||
const isCodedException = !!((_j = (_h = (_g = error.response) === null || _g === void 0 ? void 0 : _g.data) === null || _h === void 0 ? void 0 : _h.Error) === null || _j === void 0 ? void 0 : _j.Code); | ||
const isCodedException = !!((_k = (_j = (_h = error.response) === null || _h === void 0 ? void 0 : _h.data) === null || _j === void 0 ? void 0 : _j.Error) === null || _k === void 0 ? void 0 : _k.Code); | ||
const isAborted = typeof error === 'string' && error === 'abort'; | ||
@@ -116,0 +118,0 @@ if (!isFiltered && !isCodedException && !isAborted) { |
@@ -7,4 +7,6 @@ declare module 'axios' { | ||
* Example: [401] disables error notification for Unauthorized response. | ||
* A function can also be passed. It receives the error as a parameter and returns | ||
* a boolean value indicating whether the error notification should be suppressed or not. | ||
*/ | ||
suppressShowError?: boolean | number[]; | ||
suppressShowError?: boolean | number[] | ((error: AxiosError) => boolean); | ||
hideGlobalLoader?: boolean; | ||
@@ -11,0 +13,0 @@ } |
@@ -10,2 +10,7 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { | ||
}; | ||
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) { | ||
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter"); | ||
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it"); | ||
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver); | ||
}; | ||
import { jsx as _jsx } from "react/jsx-runtime"; | ||
@@ -16,11 +21,6 @@ import { injectable, provide, useDependencies } from '@servicetitan/react-ioc'; | ||
export function withMicroservice(baseURL, authURL, tokens, UnwrappedComponent, extraHeaders) { | ||
var _MicroserviceAuthStore_addAuthHeaders, _MicroserviceAuthStore_baseURLMatchesConfig; | ||
// TODO: Use disposable store as soon as this feature is implemented | ||
let MicroserviceAuthStore = class MicroserviceAuthStore { | ||
constructor() { | ||
Object.defineProperty(this, "isMounted", { | ||
enumerable: true, | ||
configurable: true, | ||
writable: true, | ||
value: true | ||
}); | ||
Object.defineProperty(this, "authToken", { | ||
@@ -44,2 +44,20 @@ enumerable: true, | ||
}); | ||
Object.defineProperty(this, "isFetching", { | ||
enumerable: true, | ||
configurable: true, | ||
writable: true, | ||
value: false | ||
}); | ||
Object.defineProperty(this, "isMounted", { | ||
enumerable: true, | ||
configurable: true, | ||
writable: true, | ||
value: true | ||
}); | ||
Object.defineProperty(this, "requestQueue", { | ||
enumerable: true, | ||
configurable: true, | ||
writable: true, | ||
value: [] | ||
}); | ||
Object.defineProperty(this, "destructor", { | ||
@@ -78,5 +96,5 @@ enumerable: true, | ||
this.axiosRequestInterceptor = axios.interceptors.request.use(config => { | ||
if (config.baseURL && config.baseURL === baseURL) { | ||
if (__classPrivateFieldGet(this, _MicroserviceAuthStore_baseURLMatchesConfig, "f").call(this, config)) { | ||
config.suppressShowError = [401]; | ||
config.headers = Object.assign(Object.assign({}, config.headers), this.getAuthHeaders(this.authToken)); | ||
config.headers = __classPrivateFieldGet(this, _MicroserviceAuthStore_addAuthHeaders, "f").call(this, config); | ||
} | ||
@@ -93,13 +111,33 @@ return config; | ||
this.axiosResponseInterceptor = axios.interceptors.response.use(response => response, error => { | ||
const { config } = error.response; | ||
// Reject promise if usual error | ||
if (error.response.status !== 401 || baseURL !== config.baseURL) { | ||
var _a, _b; | ||
if (!this.isMounted) { | ||
return Promise.reject(error); | ||
} | ||
const axiosError = error; | ||
const response = axiosError.response; | ||
const config = (_a = response === null || response === void 0 ? void 0 : response.config) !== null && _a !== void 0 ? _a : {}; | ||
/* | ||
* When response code is 401, try to refresh the token. | ||
* Eject the interceptor so it doesn't loop in case | ||
* token refresh causes the 401 response | ||
* Only intercept uncanceled 401 responses from same baseURL, and ignore errors from authURL to prevent a loop. | ||
*/ | ||
this.removeResponseInterceptor(); | ||
if (axios.isCancel(error) || | ||
!__classPrivateFieldGet(this, _MicroserviceAuthStore_baseURLMatchesConfig, "f").call(this, config) || | ||
config.url === authURL || | ||
(response === null || response === void 0 ? void 0 : response.status) !== 401) { | ||
return Promise.reject(axiosError); | ||
} | ||
if (this.isFetching) { | ||
// Queue up request while the auth token is being fetched by another request | ||
return new Promise(resolve => { | ||
this.requestQueue.push(() => { | ||
resolve(axios(config)); | ||
}); | ||
}); | ||
} | ||
// Check if auth token got updated while this request was in flight | ||
const newHeaders = __classPrivateFieldGet(this, _MicroserviceAuthStore_addAuthHeaders, "f").call(this, config); | ||
if (newHeaders.Authorization !== ((_b = config.headers) === null || _b === void 0 ? void 0 : _b.Authorization) && | ||
this.authToken !== '') { | ||
return axios(config); | ||
} | ||
this.isFetching = true; | ||
return axios | ||
@@ -109,3 +147,10 @@ .get(authURL) | ||
this.authToken = response.data; | ||
config.headers = Object.assign(Object.assign({}, config.headers), this.getAuthHeaders(response.data)); | ||
this.isFetching = false; | ||
/* | ||
* If any other auth requests were attempting to be made | ||
* while this request was running, they were queued and | ||
* will now be run now that the auth token has been fetched. | ||
*/ | ||
this.requestQueue.forEach(request => request()); | ||
this.requestQueue = []; | ||
return axios(config); | ||
@@ -115,9 +160,4 @@ }) | ||
this.authToken = ''; | ||
this.isFetching = false; | ||
return Promise.reject(error); | ||
}) | ||
.finally(() => { | ||
if (!this.isMounted) { | ||
return; | ||
} | ||
this.addResponseInterceptor(); | ||
}); | ||
@@ -149,7 +189,6 @@ }); | ||
}); | ||
Object.defineProperty(this, "getAuthHeaders", { | ||
enumerable: true, | ||
configurable: true, | ||
writable: true, | ||
value: (authToken) => (Object.assign({ Authorization: `Bearer ${authToken}` }, extraHeaders)) | ||
_MicroserviceAuthStore_addAuthHeaders.set(this, (config) => (Object.assign(Object.assign(Object.assign({}, config.headers), { Authorization: `Bearer ${this.authToken}` }), extraHeaders))); | ||
_MicroserviceAuthStore_baseURLMatchesConfig.set(this, (config) => { | ||
var _a; | ||
return ((config.baseURL && config.baseURL === baseURL) || ((_a = config.url) === null || _a === void 0 ? void 0 : _a.startsWith(baseURL))); | ||
}); | ||
@@ -159,2 +198,3 @@ this.addInterceptors(); | ||
}; | ||
_MicroserviceAuthStore_addAuthHeaders = new WeakMap(), _MicroserviceAuthStore_baseURLMatchesConfig = new WeakMap(); | ||
MicroserviceAuthStore = __decorate([ | ||
@@ -161,0 +201,0 @@ injectable(), |
{ | ||
"name": "@servicetitan/ajax-handlers", | ||
"version": "22.14.0", | ||
"version": "22.15.0", | ||
"description": "", | ||
@@ -21,3 +21,7 @@ "repository": { | ||
"devDependencies": { | ||
"@servicetitan/react-ioc": "22.14.0", | ||
"@servicetitan/react-ioc": "22.15.0", | ||
"@testing-library/dom": "^8.20.1", | ||
"@testing-library/jest-dom": "^6.1.4", | ||
"@testing-library/react": "^12.1.5", | ||
"@testing-library/react-hooks": "^8.0.1", | ||
"@types/jquery": "~2.0.57", | ||
@@ -27,6 +31,7 @@ "@types/js-cookie": "~3.0.3", | ||
"jquery": "~2.1.4", | ||
"msw": "^1.3.1", | ||
"react": "~17.0.2" | ||
}, | ||
"peerDependencies": { | ||
"@servicetitan/react-ioc": "22.14.0", | ||
"@servicetitan/react-ioc": "22.15.0", | ||
"axios": "~0.27.2", | ||
@@ -42,3 +47,3 @@ "jquery": "~2.1.4", | ||
}, | ||
"gitHead": "e0064d9f47c180d6b128f1b19f92c5f46d4e0320" | ||
"gitHead": "1ad445e4befe0d229644be24b0d4ed9cf18a9d11" | ||
} |
@@ -135,7 +135,8 @@ import axios, { AxiosError } from 'axios'; | ||
} | ||
if ( | ||
!error.config?.suppressShowError || | ||
(Array.isArray(error.config?.suppressShowError) && | ||
!error.config?.suppressShowError.includes(error.response?.status)) | ||
!error.config?.suppressShowError.includes(error.response?.status)) || | ||
(typeof error.config?.suppressShowError === 'function' && | ||
!error.config.suppressShowError(error)) | ||
) { | ||
@@ -142,0 +143,0 @@ const isFiltered = errorFilteredUrls.some(url => |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
81793
47
1457
11