omnipay-savings-sdk
Version:
Omnipay Savings SDK
325 lines (277 loc) • 9.13 kB
text/typescript
import dayjs from 'dayjs';
import {API_BASE_URL} from '../env.json';
import {looseObject} from './types';
import {
formDataRequests,
isValidDate,
parseQueryString,
waitFor,
} from '../utils';
import {useToast} from '../contexts/ToastProvider';
// Global token expiry handler
let globalTokenExpiryHandler: (() => void) | null = null;
export const setTokenExpiryHandler = (handler: (() => void) | null) => {
globalTokenExpiryHandler = handler;
};
const handleTokenExpiry = () => {
if (globalTokenExpiryHandler) {
globalTokenExpiryHandler();
}
};
let isRefreshingRequestToken = false;
type RequestContent = 'form-data' | 'json';
type RequestObject = {
requestUrl: string;
customHeaders?: looseObject;
};
interface RequestPostObject extends RequestObject {
requestData: looseObject;
requestType?: RequestContent;
}
export const POST = async (params: RequestPostObject & {apiKey: string}) => {
const {requestUrl, requestData, customHeaders, requestType, apiKey} = params;
try {
let data = formatRequestData(requestData, requestType);
const requestHeaders = await getRequestHeaders(apiKey, requestType);
const headers = {
...requestHeaders.headers,
...customHeaders,
};
const response = await fetch(`${API_BASE_URL}${requestUrl}`, {
method: 'POST',
headers,
body: JSON.stringify(data),
});
const responseData = await response.json();
if (!response.ok || responseData.error === true) {
// Handle 401 unauthorized responses
if (response.status === 401) {
handleTokenExpiry();
}
return {
error: extractDetailedErrorMessage(responseData),
status: responseData?.status,
};
}
return responseData;
} catch (error: any) {
const errorMessage = getErrorMessage(error);
const errorResponse = error?.response || {};
const errorData = errorResponse.data || {};
return Promise.reject(errorData);
}
};
export const PUT = async (params: RequestPostObject & {apiKey: string}) => {
const {requestUrl, requestData, customHeaders, requestType, apiKey} = params;
try {
let data = formatRequestData(requestData, requestType);
const requestHeaders = await getRequestHeaders(apiKey, requestType);
const headers = {
...requestHeaders.headers,
...customHeaders,
};
const response = await fetch(`${API_BASE_URL}${requestUrl}`, {
method: 'PUT',
headers,
body: JSON.stringify(data),
});
const responseData = await response.json();
if (!response.ok || responseData.error === true) {
// Handle 401 unauthorized responses
if (response.status === 401) {
handleTokenExpiry();
}
return {
error: extractDetailedErrorMessage(responseData),
status: responseData?.status,
};
}
return responseData;
} catch (error: any) {
const errorMessage = getErrorMessage(error);
const errorResponse = error?.response || {};
const errorData = errorResponse.data || {};
return Promise.reject(errorData);
}
};
export const GET = async (params: RequestObject & {apiKey: string}) => {
const {requestUrl, customHeaders, apiKey} = params;
try {
const requestHeaders = await getRequestHeaders(apiKey);
const headers = {
...requestHeaders.headers,
...customHeaders,
};
let modifiedDateUrl = modifyUrl(requestUrl);
const response = await fetch(`${API_BASE_URL}${modifiedDateUrl}`, {
method: 'GET',
headers,
});
// console.log(response, `response`);
if (!response.ok) {
// Handle 401 unauthorized responses
if (response.status === 401) {
handleTokenExpiry();
}
throw new Error(`HTTP error! status: ${response.status}`);
}
const responseData = await response.text();
console.log(responseData, 'response-data');
if (responseData.endsWith('.csv')) {
return responseData;
}
return JSON.parse(responseData).responseData || JSON.parse(responseData);
} catch (error: any) {
// Handle fetch errors that might also be 401
if (error?.message?.includes('401') || error?.response?.status === 401) {
handleTokenExpiry();
}
const errorMessage = getErrorMessage(error);
throw new Error(errorMessage);
}
};
function extractDetailedErrorMessage(responseData: any): string {
// Extract detailed validation errors if available
if (responseData.errors) {
// Handle validation errors object
if (typeof responseData.errors === 'object') {
const errorMessages = [];
// Check for array of errors
if (Array.isArray(responseData.errors)) {
errorMessages.push(...responseData.errors);
} else {
// Check for object with error properties
for (const [key, value] of Object.entries(responseData.errors)) {
if (Array.isArray(value)) {
errorMessages.push(...value);
} else if (typeof value === 'string') {
errorMessages.push(value);
}
}
}
if (errorMessages.length > 0) {
return errorMessages.join('. ');
}
}
}
// Fallback to message or title
return responseData.message ?? responseData.title ?? 'An error occurred. Please try again';
}
function getErrorMessage(error: any) {
const errorCode = error?.response?.status;
const errorData = error?.response?.data;
const errorMessage = errorData?.message;
if (error.message === 'Network Error') {
return 'Check your network connection';
}
if (errorCode === 500) {
return 'Ooops. Please try again';
}
if (error?.response?.message) {
if (typeof error.response.message === 'string') {
return error.response.message;
}
}
return typeof errorData === 'string' && errorData
? errorData
: errorMessage || error.message;
}
export async function getRequestHeaders(
apiKey: string,
requestType?: RequestContent,
) {
const token = apiKey;
return {
headers: {
...(!!token && {authorization: `Bearer ${token}`}),
'content-type':
requestType === 'form-data'
? 'multipart/form-data'
: 'application/json',
},
};
}
function modifyUrl(requestUrl: string) {
if (requestUrl.includes('?') && requestUrl.split('?')[1].includes('Date')) {
const currentParamsObject = parseQueryString(requestUrl.split('?')[1]);
let newRequestParamsObject = {};
for (const key in currentParamsObject) {
if (
key.includes('StartDate') ||
key.includes('EndDate') ||
(key === 'date' && currentParamsObject.StartDate === undefined)
) {
const isSingleDateKey = key === 'date';
if (key.includes('StartDate') || isSingleDateKey) {
const keySuffix = isSingleDateKey ? '' : key.split('StartDate')[0];
let endDateKey = `${keySuffix}EndDate`;
if (endDateKey === 'createenddate') {
endDateKey = 'createdenddate';
}
let startDateValue = currentParamsObject[key];
let endDateValue = currentParamsObject[endDateKey];
if (isValidDate(startDateValue)) {
startDateValue = dayjs
//@ts-ignore
.utc(dayjs(startDateValue, 'YYYY-MM-DD').startOf('day'))
.format();
}
if (isValidDate(endDateValue)) {
endDateValue = dayjs
//@ts-ignore
.utc(dayjs(endDateValue, 'YYYY-MM-DD').endOf('day'))
.format();
}
//@ts-ignore
newRequestParamsObject[key] = startDateValue;
//@ts-ignore
newRequestParamsObject[endDateKey] = endDateValue;
}
} else {
if (Array.isArray(currentParamsObject[key])) {
//@ts-ignore
newRequestParamsObject[key] = currentParamsObject[key][0];
} else {
//@ts-ignore
newRequestParamsObject[key] = currentParamsObject[key];
}
}
}
const newParamsUrl = Object.keys(newRequestParamsObject)
//@ts-ignore
.map(key => key + '=' + newRequestParamsObject[key])
.join('&');
let modifiedDateUrl = `${requestUrl.split('?')[0]}?${newParamsUrl}`;
return modifiedDateUrl;
}
return requestUrl;
}
export function formatRequestData(
requestData: looseObject,
requestType?: RequestContent,
) {
if (requestType === 'form-data') {
let fd = new FormData();
for (const el in requestData) {
let data = requestData[el];
if (formDataRequests.includes(el)) {
const uriParts = data?.split('.');
const fileType = uriParts?.[uriParts.length - 1];
if (data && uriParts && fileType) {
// @ts-ignore
fd.append(el, {
uri: data,
name: `${el}.${fileType}`,
type: fileType.toLowerCase().includes('pdf')
? `application/${fileType.toLowerCase()}`
: `image/${fileType.toLowerCase()}`,
} as any);
}
} else {
fd.append(el, data);
}
}
return fd;
}
return requestData;
}