lumen-react-javascript
Version:
Lumen React bridge
256 lines (227 loc) • 10.6 kB
text/typescript
import {AxiosError, AxiosInstance, AxiosRequestConfig, AxiosResponse, Cancel, default as axios} from 'axios';
import {History} from 'history';
import AuthUtils from './auth-utils';
import * as _ from 'lodash';
import {getGlobal, setGlobal} from './window-utils';
import {makeId} from './string-utils';
import {parseHeadersToApiResponseHeaders} from '..';
export function createCancelToken(id: string, config = null) {
let source = makeCancelToken();
storeCancelToken(id, source, config);
return source;
}
export function makeCancelToken() {
let source: any = axios.CancelToken.source();
source.id = makeId();
return source;
}
export function storeCancelToken(id: string, source, config = null) {
let storedCancelTokens = getGlobal('storedCancelTokens', []);
storedCancelTokens.push({id, source, config});
setGlobal('storedCancelTokens', storedCancelTokens);
}
export function cancelRequest(tokenOrId: string, message: string = null) {
let storedCancelTokens = getGlobal('storedCancelTokens', []);
let lastIndex = -1;
let sourceIndex = storedCancelTokens.findIndex(
s => s.source.id == tokenOrId || s.id == tokenOrId
);
while (sourceIndex >= 0) {
lastIndex = sourceIndex;
storedCancelTokens[sourceIndex].source.cancel(
JSON.stringify({id: storedCancelTokens[sourceIndex].id, message}));
sourceIndex = storedCancelTokens.findIndex(
(s, index) => index > lastIndex && (s.source.id == tokenOrId || s.id == tokenOrId)
);
}
}
export interface AxiosRequestConfigExtended extends AxiosRequestConfig {
onResponse?: { [code: number]: () => void };
onExpired?: () => void;
skipCancel?: boolean;
cancelTokenId?: string;
cancelMessage?: string;
globalHandlers?: {
onRequest?: (config: AxiosRequestConfigExtended, originalConfig: AxiosRequestConfigExtended) => void;
onRequestError?: (error: AxiosError, originalConfig: AxiosRequestConfigExtended) => void;
onResponse?: (response: AxiosResponse, originalConfig: AxiosRequestConfigExtended) => void;
onResponseError?: (error: AxiosError | Cancel, originalConfig: AxiosRequestConfigExtended) => void;
}
}
export function bypassGlobalResponseHandlers(bypassGlobalResponseHandlersOption: (status: number,
response: any,
expired: boolean) => boolean | boolean | Array<number>,
status: number, response: any, expired: boolean) {
if (_.isBoolean(bypassGlobalResponseHandlersOption)) {
return bypassGlobalResponseHandlersOption;
}
else if (_.isArray(bypassGlobalResponseHandlersOption)) {
return bypassGlobalResponseHandlersOption.findIndex(h => h == status) >= 0;
}
else {
return bypassGlobalResponseHandlersOption(status, response, expired);
}
}
export function setupAxiosInstance(instance: AxiosInstance, config: AxiosRequestConfigExtended = {}) {
instance.interceptors.request.use(
(_config: AxiosRequestConfigExtended) => {
_config = {..._config, ...(config || {})};
let source = makeCancelToken();
let tokenId: string = _config.cancelToken ? _config.cancelToken as any : `${_config.method} ${_config.url}`.toLowerCase();
_config.cancelToken = source.token;
_config.cancelTokenId = source.id;
if (!(_config as any).skipCancel) {
cancelRequest(tokenId, _config.cancelMessage || 'Skipped because of ' + source.id);
}
if (AuthUtils.isLoggedIn()) {
_config.headers = {..._config.headers, ...{token: AuthUtils.getToken()}};
}
for (let key in _config.headers) {
let header = _config.headers[key];
if (_.isFunction(header)) {
_config.headers[key] = header();
}
}
storeCancelToken(tokenId, source, _config);
if (config.globalHandlers && config.globalHandlers.onRequest) {
config.globalHandlers.onRequest(_config, config);
}
return Promise.resolve(_config);
},
(error: AxiosError) => {
console.error(error);
if (config.globalHandlers && config.globalHandlers.onRequest) {
config.globalHandlers.onRequestError(error, config);
}
return Promise.reject(error);
}
);
instance.interceptors.response.use(
(response: AxiosResponse) => {
if (response.config.cancelToken) {
let storedCancelTokens = getGlobal('storedCancelTokens', []);
let index = storedCancelTokens.findIndex(s => s.source.id == (response.config as any).cancelTokenId);
if (index >= 0) {
storedCancelTokens.splice(index, 1);
setGlobal('storedCancelTokens', storedCancelTokens);
}
}
let config: any = (response.config as AxiosRequestConfigExtended);
let headers = parseHeadersToApiResponseHeaders(response.headers);
let bypass = 'bypassGlobalResponseHandlers' in config &&
bypassGlobalResponseHandlers(config.bypassGlobalResponseHandlers, response.status, response.data,
!!headers.expired);
if (!bypass) {
if (config.onResponse && response.status in config.onResponse) {
config.onResponse[response.status]();
}
if (headers.expired && config.onExpired) {
config.onExpired();
}
}
if (config.globalHandlers && config.globalHandlers.onResponse) {
config.globalHandlers.onResponse(response, config);
}
return response;
},
(error: AxiosError | Cancel) => {
if (axios.isCancel(error)) {
try {
let cancelPayload = JSON.parse((error as Cancel).message);
let storedCancelTokens = getGlobal('storedCancelTokens', []);
let sourceIndex = storedCancelTokens.findIndex(s => s.id == cancelPayload.id);
if (sourceIndex >= 0) {
console.warn(cancelPayload.message, storedCancelTokens[sourceIndex]);
if (config.globalHandlers && config.globalHandlers.onResponseError) {
config.globalHandlers.onResponseError({
message: cancelPayload.message,
config: storedCancelTokens[sourceIndex]
}, config);
}
storedCancelTokens.splice(sourceIndex, 1);
}
else {
console.warn(error);
if (config.globalHandlers && config.globalHandlers.onResponseError) {
config.globalHandlers.onResponseError(error as Cancel, config);
}
}
}
catch {
console.warn(error);
if (config.globalHandlers && config.globalHandlers.onResponseError) {
config.globalHandlers.onResponseError(error as Cancel, config);
}
}
}
else {
if ((error as AxiosError).config.cancelToken) {
let storedCancelTokens = getGlobal('storedCancelTokens', []);
let index = storedCancelTokens.findIndex(s => s.source.id == (error as any).config.cancelTokenId);
if (index >= 0) {
storedCancelTokens.splice(index, 1);
setGlobal('storedCancelTokens', storedCancelTokens);
}
}
console.log('ERROR RESPONSE', error);
let config: any = ((error as AxiosError).config as AxiosRequestConfigExtended);
let response = (error as AxiosError).response;
let headers = parseHeadersToApiResponseHeaders(response.headers);
let bypass = 'bypassGlobalResponseHandlers' in config &&
bypassGlobalResponseHandlers(config.bypassGlobalResponseHandlers, response.status, response.data,
!!headers.expired);
if (!bypass) {
if (config.onResponse && response.status in config.onResponse) {
config.onResponse[response.status]();
}
if (headers.expired && config.onExpired) {
config.onExpired();
}
}
if (config.globalHandlers && config.globalHandlers.onResponseError) {
config.globalHandlers.onResponseError(error as AxiosError, config);
}
}
return Promise.reject(error);
}
);
}
export function redirectOnFailConnect(history: History, redirects = {
401: (history: History) => history.push('/login'),
403: (history: History) => history.push('/login'),
404: (history: History) => history.push('/notfound'),
}): { catch: (e: any) => any } {
return {
catch: redirectOnFail(history, redirects),
};
}
export function redirectOnFail(history: History, redirects = {
401: (history: History) => history.push('/login'),
403: (history: History) => history.push('/login'),
404: (history: History) => history.push('/notfound'),
}): (e: any) => any {
return e => {
if (e.response && e.response.status && redirects[e.response.status]) {
if (e.response.status == 401) {
AuthUtils.logout();
setTimeout(() => window.document.location.reload(true), 500);
}
redirects[e.response.status](history);
}
};
}
export function backOnSuccessConnect(history: History, to = null): { then: (r: any) => any } {
return {
then: backOnSuccess(history, to),
};
}
export function backOnSuccess(history: History, to = null): (r: any) => any {
return response => {
if (to) {
history.push(to);
}
else {
history.goBack();
}
};
}