import { AxiosError } from "axios"; import _ from "lodash"; import { DTOModel, isDTO } from "./dto_model"; export interface FailureI { status?: number; message: string; key?: string; meta?: FailureMeta; } interface FailureMeta { isRequestFailure?: boolean; isNetworkFailure?: boolean; isPlainFailure?: boolean; data?: any; [prop: string]: any | undefined; } class Failure implements FailureI { message: string; key?: string | undefined; status?: number | undefined; meta?: FailureMeta; static isf = true; /** * * @param {Failure} o * @returns {boolean} */ static isFailure(o: any): o is Failure { if (!_.isObjectLike(o)) return false; if (_.has(o, "message")) return true; return false; } constructor({ status, message, key, meta }: FailureI) { this.status = status; this.message = message; this.key = key; this.meta = meta; console.error([key ?? "", message].filter((i) => i.length !== 0).join(":")); } static fromReason(reason: AxiosError, key?: string): Failure { try { if(!Failure.verifyContainsDTO(reason)) { throw reason; } if (reason.response) { return new Failure({ status: reason.response!.status, message: reason.response!.data.message ?? "Unknown response error model", key: key, meta: { isRequestFailure: true, data: reason.response.data, }, }); } if (reason.request) { return new Failure({ message: `Something went wrong while ${reason.config.url} had been called`, key: `unreachedResponse(${key})`, meta: { isNetworkFailure: true, }, }); } throw reason; } catch (error) { return new Failure({ message: reason.message, key: key, meta: { isPlainFailure: true }, }); } } /** * Verifies that passed object matches [DTOModel] structure * * Otherwise it will rethrow passed object */ static verifyContainsDTO( reason: AxiosError ): reason is AxiosError, any> { return [reason.response, reason.request].reduce((acc, obj) => { if (acc || isDTO(obj)) { return true; } return false; }, false); } toString(): string { return `${this.message} (${this.key ?? "key unknown"}) ${ this.status ?? "status unknown" }`; } mapKeyWith(transpiler: (key: string) => string) { if (!this.key) { return this.message; } return transpiler(this.key); } } export default Failure;