import rawAxios, { AxiosError, AxiosResponse } from "axios"; import { Base64 } from "js-base64"; import { BehaviorSubject, Observable } from "rxjs"; export const apiBaseUrl = "/api"; export const axios = rawAxios.create(); function convertToNetworkError(error: AxiosError): never { if (error.isAxiosError && error.response == null) { throw new HttpNetworkError(error); } else { throw error; } } function convertToForbiddenError(error: AxiosError): never { if ( error.isAxiosError && error.response != null && (error.response.status == 401 || error.response.status == 403) ) { throw new HttpForbiddenError(error); } else { throw error; } } function convertToNotFoundError(error: AxiosError): never { if ( error.isAxiosError && error.response != null && error.response.status == 404 ) { throw new HttpNotFoundError(error); } else { throw error; } } rawAxios.interceptors.response.use(undefined, convertToNetworkError); rawAxios.interceptors.response.use(undefined, convertToForbiddenError); rawAxios.interceptors.response.use(undefined, convertToNotFoundError); axios.interceptors.response.use(undefined, convertToNetworkError); axios.interceptors.response.use(undefined, convertToForbiddenError); axios.interceptors.response.use(undefined, convertToNotFoundError); const tokenSubject = new BehaviorSubject(null); export function getHttpToken(): string | null { return tokenSubject.value; } export function setHttpToken(token: string | null): void { tokenSubject.next(token); if (token == null) { // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access delete axios.defaults.headers.common["Authorization"]; } else { // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access axios.defaults.headers.common["Authorization"] = `Bearer ${token}`; } } export const token$: Observable = tokenSubject.asObservable(); export function base64(blob: Blob | string): Promise { if (typeof blob === "string") { return Promise.resolve(Base64.encode(blob)); } return new Promise((resolve) => { const reader = new FileReader(); reader.onload = function () { resolve((reader.result as string).replace(/^data:.*;base64,/, "")); }; reader.readAsDataURL(blob); }); } export function extractStatusCode(error: AxiosError): number | null { if (error.isAxiosError) { const code = error?.response?.status; if (typeof code === "number") { return code; } } return null; } export interface CommonErrorResponse { code: number; message: string; } export function extractErrorCode( error: AxiosError ): number | null { if (error.isAxiosError) { const code = error.response?.data?.code; if (typeof code === "number") { return code; } } return null; } export class HttpNetworkError extends Error { constructor(public innerError?: AxiosError) { super(); } } export class HttpForbiddenError extends Error { constructor(public innerError?: AxiosError) { super(); } } export class HttpNotFoundError extends Error { constructor(public innerError?: AxiosError) { super(); } } export class NotModified {} export interface BlobWithEtag { data: Blob; etag: string; } export function extractResponseData(res: AxiosResponse): T { return res.data; } export function catchIfStatusCodeIs< TResult, TErrorHandlerResult extends TResult | PromiseLike | null | undefined >( statusCode: number, errorHandler: (error: AxiosError) => TErrorHandlerResult ): (error: AxiosError) => TErrorHandlerResult { return (error: AxiosError) => { if (extractStatusCode(error) == statusCode) { return errorHandler(error); } else { throw error; } }; } export function convertToIfStatusCodeIs( statusCode: number, newErrorType: { new (innerError: AxiosError): NewError; } ): (error: AxiosError) => never { return catchIfStatusCodeIs(statusCode, (error) => { throw new newErrorType(error); }); } export function catchIfErrorCodeIs< TResult, TErrorHandlerResult extends TResult | PromiseLike | null | undefined >( errorCode: number, errorHandler: (error: AxiosError) => TErrorHandlerResult ): (error: AxiosError) => TErrorHandlerResult { return (error: AxiosError) => { if (extractErrorCode(error) == errorCode) { return errorHandler(error); } else { throw error; } }; } export function convertToIfErrorCodeIs( errorCode: number, newErrorType: { new (innerError: AxiosError): NewError; } ): (error: AxiosError) => never { return catchIfErrorCodeIs(errorCode, (error) => { throw new newErrorType(error); }); } export function convertToNotModified( error: AxiosError ): NotModified { if ( error.isAxiosError && error.response != null && error.response.status == 304 ) { return new NotModified(); } else { throw error; } } export function convertToBlobWithEtag(res: AxiosResponse): BlobWithEtag { return { data: res.data, etag: (res.headers as Record<"etag", string>)["etag"], }; } export function extractEtag(res: AxiosResponse): string { return (res.headers as Record<"etag", string>)["etag"]; } export interface Page { pageNumber: number; pageSize: number; totalPageCount: number; totalCount: number; items: T[]; }