import axios from 'axios'; import { Dispatch } from '@reduxjs/toolkit'; import { setAuthData } from '../../reducer/userSlice'; import { toast } from '../../../RN-UI-LIB/src/components/toast'; import { navigateToScreen } from './navigationUtlis'; import { GLOBAL } from '../../constants/Global'; import { _map } from '../../../RN-UI-LIB/src/utlis/common'; import { BASE_AV_APP_URL } from '../../constants/config'; import { logError } from './errorUtils'; import { ClickstreamAPIToMonitor, CLICKSTREAM_EVENT_NAMES } from '../../common/Constants'; import { addClickstreamEvent } from '../../services/clickstreamEventService'; import { resetCasesData } from '../../reducer/allCasesSlice'; export enum ApiKeys { GENERATE_OTP, VERIFY_OTP, ALL_CASES, CASE_DETAIL, PINNED_CASES, LOGOUT, FEEDBACK, FILTERS, JANUS, GENERATE_PAYMENT_LINK, ADDRESSES_GEOLOCATION, NEW_ADDRESS, GET_SIGNED_URL, CASE_UNIFIED_DETAILS, PAST_FEEDBACK, } export const API_URLS: Record = {} as Record; API_URLS[ApiKeys.GENERATE_OTP] = '/auth/otp/generate'; API_URLS[ApiKeys.VERIFY_OTP] = '/auth/otp/verify'; API_URLS[ApiKeys.ALL_CASES] = '/cases/all-cases'; API_URLS[ApiKeys.CASE_DETAIL] = '/cases/get-cases'; API_URLS[ApiKeys.PINNED_CASES] = '/cases/pin'; API_URLS[ApiKeys.LOGOUT] = '/auth/logout'; API_URLS[ApiKeys.FEEDBACK] = '/cases/feedback'; API_URLS[ApiKeys.FILTERS] = '/cases/filters'; API_URLS[ApiKeys.JANUS] = '/events/json'; API_URLS[ApiKeys.GENERATE_PAYMENT_LINK] = '/send-payment-link'; API_URLS[ApiKeys.ADDRESSES_GEOLOCATION] = '/addresses-geolocations'; API_URLS[ApiKeys.NEW_ADDRESS] = '/addresses'; API_URLS[ApiKeys.GET_SIGNED_URL] = '/cases/get-signed-urls'; API_URLS[ApiKeys.CASE_UNIFIED_DETAILS] = '/collection-cases/unified-details/{loanAccountNumber}'; API_URLS[ApiKeys.PAST_FEEDBACK] = '/field-app/feedbacks'; export const API_STATUS_CODE = { OK: 200, CREATED: 201, BAD_REQUEST: 400, UNAUTHORIZED: 401, FORBIDDEN: 403, NOT_FOUND: 404, INTERNAL_SERVER_ERROR: 500 }; const API_TIMEOUT_INTERVAL = 1e4; let dispatch: Dispatch; export const getErrorMessage = (err: any) => { if (err?.response?.data?.title) { return err?.response?.data?.title; } const errorContent = err?.response?.data?.message ? JSON.parse(err?.response?.data?.message) : ''; return errorContent?.detail || errorContent?.message || errorContent || err; }; export function getApiUrl( apiKey: ApiKeys, params?: Record, queryParams?: Record, ) { let apiUrl = API_URLS[apiKey]; // replace all {placeholders} with their values in params if (params) { Object.keys(params).forEach(paramKey => { apiUrl = apiUrl.split(`{${paramKey}}`).join(`${params[paramKey]}`); // apiUrl = apiUrl.replaceAll(`{${paramKey}}`, `${params[paramKey]}`); }); } if (queryParams) { apiUrl += '?' + _map(queryParams, key => `${key}=${queryParams[key]}`).join('&'); } return `${apiUrl}`; } // status code to be retried on const errorsToRetry = [500, 503]; const axiosInstance = axios.create({timeout: API_TIMEOUT_INTERVAL}); axiosInstance.interceptors.request.use(request => { // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore request.retry = request?.retry < 4 ? request.retry : 3; // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore request.headers['X-Auth-Source'] = 'mjolnir'; // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore request.retry = request?.retry < 4 ? request.retry : 3; // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore request.headers['request-start-time'] = Date.now(); // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore request.headers['X-Session-Token'] = GLOBAL.SESSION_TOKEN || ''; // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore request.headers['deviceId'] = GLOBAL.DEVICE_ID || ''; return request; }); axiosInstance.interceptors.response.use( response => { if (response.config.headers) { const start = response.config.headers['request-start-time']; const end = Date.now(); const milliseconds = end - Number(start); response.headers['request-duration'] = String(milliseconds); if(response?.config?.url) { const url = response?.config?.url; if(ClickstreamAPIToMonitor[url]) { const eventName = ClickstreamAPIToMonitor[url] + '_SUCCESS' if(CLICKSTREAM_EVENT_NAMES[eventName]) { addClickstreamEvent(CLICKSTREAM_EVENT_NAMES[eventName], {timeTaken: milliseconds}) } } } } return response; }, error => { logError(error as Error, 'From API Helper'); const {config, response} = error; if(config.headers.donotHandleError) { return; } if (config?.headers) { const start = response.config.headers['request-start-time']; const end = Date.now(); const milliseconds = end - Number(start); if(config?.url) { const url = response?.config?.url; if(ClickstreamAPIToMonitor[url]) { const eventName = ClickstreamAPIToMonitor[url] + '_FAILED' if(CLICKSTREAM_EVENT_NAMES[eventName]) { addClickstreamEvent(CLICKSTREAM_EVENT_NAMES[eventName], {timeTaken: milliseconds, response}) } } } } if ( !config || config.retry <= 1 || !errorsToRetry.includes(error.response.status) ) { const errorString = getErrorMessage(error); if (!config.headers.donotHandleError) { toast({ type: 'error', text1: typeof errorString === 'string' ? errorString : 'Oops! something went wrong', }); } if ([API_STATUS_CODE.UNAUTHORIZED, API_STATUS_CODE.FORBIDDEN].includes(response.status)) { // Reset user info dispatch && dispatch( setAuthData({ sessionDetails: null, isLoggedIn: false, user: null, }), ); dispatch && dispatch(resetCasesData()); navigateToScreen('Login'); } return Promise.reject(error); } config.retry -= 1; const delayRetryRequest = new Promise(resolve => { setTimeout(() => { resolve(); }, 0); }); return delayRetryRequest.then(() => axiosInstance(config)); }, ); axiosInstance.defaults.headers.common['Content-Type'] = 'application/json'; axiosInstance.defaults.baseURL = BASE_AV_APP_URL; // TODO:: Ideally should happen through middlewares. export const registerNavigateAndDispatch = ( dispatchParam: Dispatch ) => ((dispatch = dispatchParam)); export default axiosInstance;