diff --git a/package-lock.json b/package-lock.json index 4871fbe374b..6f9fd1ee31a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -21,7 +21,6 @@ "@sentry/browser": "^7.57.0", "@storybook/builder-vite": "^7.0.26", "@yaireo/ui-range": "^2.1.15", - "axios": "^1.4.0", "browser-image-compression": "^2.0.2", "cross-env": "^7.0.3", "date-fns": "^2.30.0", @@ -6622,29 +6621,6 @@ "node": ">=4" } }, - "node_modules/axios": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.4.0.tgz", - "integrity": "sha512-S4XCWMEmzvo64T9GfvQDOXgYRDJ/wsSZc7Jvdgx5u1sd0JwsuPLqb3SYmusag+edF6ziyMensPVqLTSc1PiSEA==", - "dependencies": { - "follow-redirects": "^1.15.0", - "form-data": "^4.0.0", - "proxy-from-env": "^1.1.0" - } - }, - "node_modules/axios/node_modules/form-data": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", - "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/axobject-query": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-3.2.1.tgz", @@ -9867,25 +9843,6 @@ "node": ">=0.4.0" } }, - "node_modules/follow-redirects": { - "version": "1.15.2", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", - "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==", - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/RubenVerborgh" - } - ], - "engines": { - "node": ">=4.0" - }, - "peerDependenciesMeta": { - "debug": { - "optional": true - } - } - }, "node_modules/for-each": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", @@ -17624,7 +17581,8 @@ "node_modules/proxy-from-env": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", - "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "dev": true }, "node_modules/psl": { "version": "1.9.0", diff --git a/package.json b/package.json index 246734dc3d4..f7c0556a6b2 100644 --- a/package.json +++ b/package.json @@ -61,7 +61,6 @@ "@sentry/browser": "^7.57.0", "@storybook/builder-vite": "^7.0.26", "@yaireo/ui-range": "^2.1.15", - "axios": "^1.4.0", "browser-image-compression": "^2.0.2", "cross-env": "^7.0.3", "date-fns": "^2.30.0", diff --git a/src/Common/hooks/useMSEplayer.ts b/src/Common/hooks/useMSEplayer.ts index b14f94a6a43..bb89818775a 100644 --- a/src/Common/hooks/useMSEplayer.ts +++ b/src/Common/hooks/useMSEplayer.ts @@ -1,5 +1,4 @@ import { useEffect, useRef } from "react"; -import axios from "axios"; export interface IAsset { middlewareHostname: string; @@ -59,10 +58,14 @@ const stopStream = (payload: { id: string }, options: IOptions) => { const { id } = payload; ws?.close(); - axios - .post(`https://${middlewareHostname}/stop`, { - id, - }) + fetch(`https://${middlewareHostname}/stop`, { + method: "POST", + headers: { + "Content-Type": "application/json", + Accept: "application/json", + }, + body: JSON.stringify({ id }), + }) .then((res) => options?.onSuccess && options.onSuccess(res)) .catch((err) => options.onError && options.onError(err)); }; diff --git a/src/Components/Assets/AssetType/ONVIFCamera.tsx b/src/Components/Assets/AssetType/ONVIFCamera.tsx index 4720c876010..2b857f2a753 100644 --- a/src/Components/Assets/AssetType/ONVIFCamera.tsx +++ b/src/Components/Assets/AssetType/ONVIFCamera.tsx @@ -8,7 +8,6 @@ import { } from "../../../Redux/actions"; import * as Notification from "../../../Utils/Notifications.js"; import { BedModel } from "../../Facility/models"; -import axios from "axios"; import { getCameraConfig } from "../../../Utils/transformUtils"; import CameraConfigure from "../configure/CameraConfigure"; import Loading from "../../Common/Loading"; @@ -107,13 +106,15 @@ const ONVIFCamera = ({ assetId, facilityId, asset, onUpdated }: Props) => { }; try { setLoadingAddPreset(true); - const presetData = await axios.get( - `https://${facilityMiddlewareHostname}/status?hostname=${config.hostname}&port=${config.port}&username=${config.username}&password=${config.password}` - ); + const preset = await fetch( + `https://${facilityMiddlewareHostname}/preset?hostname=${config.hostname}\ + &port=${config.port}&username=${config.username}&password=${config.password}\ + &preset_name=${newPreset}` + ).then((res) => res.json()); const res: any = await Promise.resolve( dispatch( createAssetBed( - { meta: { ...data, ...presetData.data } }, + { meta: { ...data, ...preset.data } }, assetId, bed?.id as string ) diff --git a/src/Components/CriticalCareRecording/CriticalCare__API.tsx b/src/Components/CriticalCareRecording/CriticalCare__API.tsx index 48b58faa167..423ee4eefd2 100644 --- a/src/Components/CriticalCareRecording/CriticalCare__API.tsx +++ b/src/Components/CriticalCareRecording/CriticalCare__API.tsx @@ -1,4 +1,4 @@ -import { fireRequestV2 } from "../../Redux/fireRequest"; +import { legacyFireRequest } from "../../Redux/fireRequest"; export const loadDailyRound = ( consultationId: string, @@ -6,7 +6,7 @@ export const loadDailyRound = ( successCB: any = () => null, errorCB: any = () => null ) => { - fireRequestV2("getDailyReport", [], {}, successCB, errorCB, { + legacyFireRequest("getDailyReport", [], {}, successCB, errorCB, { consultationId, id, }); @@ -19,7 +19,7 @@ export const updateDailyRound = ( successCB: any = () => null, errorCB: any = () => null ) => { - fireRequestV2("updateDailyRound", [], params, successCB, errorCB, { + legacyFireRequest("updateDailyRound", [], params, successCB, errorCB, { consultationId, id, }); diff --git a/src/Components/Facility/Consultations/LiveFeed.tsx b/src/Components/Facility/Consultations/LiveFeed.tsx index c6ba749b471..bf307a820ce 100644 --- a/src/Components/Facility/Consultations/LiveFeed.tsx +++ b/src/Components/Facility/Consultations/LiveFeed.tsx @@ -14,7 +14,6 @@ import { import { useFeedPTZ } from "../../../Common/hooks/useFeedPTZ"; import * as Notification from "../../../Utils/Notifications.js"; import { FeedCameraPTZHelpButton } from "./Feed"; -import { AxiosError } from "axios"; import { BedSelect } from "../../Common/BedSelect"; import { BedModel } from "../models"; import useWindowDimensions from "../../../Common/hooks/useWindowDimensions"; @@ -93,10 +92,9 @@ const LiveFeed = (props: any) => { setPresets(resp); }, onError: (resp) => { - resp instanceof AxiosError && - Notification.Error({ - msg: "Camera is offline", - }); + Notification.Error({ + msg: "Camera is offline", + }); }, }); diff --git a/src/Components/Facility/CoverImageEditModal.tsx b/src/Components/Facility/CoverImageEditModal.tsx index 988bc288f58..d365ed5aba9 100644 --- a/src/Components/Facility/CoverImageEditModal.tsx +++ b/src/Components/Facility/CoverImageEditModal.tsx @@ -1,4 +1,3 @@ -import axios from "axios"; import { ChangeEventHandler, useCallback, @@ -110,17 +109,18 @@ const CoverImageEditModal = ({ setIsUploading(true); try { - const response = await axios.post( + const response = await fetch( `/api/v1/facility/${facility.id}/cover_image/`, - formData, { + method: "POST", headers: { "Content-Type": "multipart/form-data", Authorization: "Bearer " + localStorage.getItem(LocalStorageKeys.accessToken), }, + body: formData, } - ); + ) if (response.status === 200) { Success({ msg: "Cover image updated." }); window.location.reload(); diff --git a/src/Components/Patient/FileUpload.tsx b/src/Components/Patient/FileUpload.tsx index fe0881e6994..38ea830d427 100644 --- a/src/Components/Patient/FileUpload.tsx +++ b/src/Components/Patient/FileUpload.tsx @@ -1,4 +1,3 @@ -import axios from "axios"; import CircularProgress from "../Common/components/CircularProgress"; import { useCallback, @@ -38,6 +37,7 @@ import { NonReadOnlyUsers } from "../../Utils/AuthorizeFor"; import AuthorizedChild from "../../CAREUI/misc/AuthorizedChild"; import Page from "../Common/components/Page"; import FilePreviewDialog from "../Common/FilePreviewDialog"; +import { uploadFile } from "../../Redux/fireRequest"; import useAuthUser from "../../Common/hooks/useAuthUser"; const Loading = lazy(() => import("../Common/Loading")); @@ -935,24 +935,10 @@ export const FileUpload = (props: FileUploadProps) => { if (!f) return; const newFile = new File([f], `${internal_name}`); - const config = { - headers: { - "Content-type": contentType, - "Content-disposition": "inline", - }, - onUploadProgress: (progressEvent: any) => { - const percentCompleted = Math.round( - (progressEvent.loaded * 100) / progressEvent.total - ); - setUploadPercent(percentCompleted); - }, - }; return new Promise((resolve, reject) => { - axios - .put(url, newFile, config) + uploadFile(url, newFile, contentType, setUploadPercent) .then(() => { setUploadStarted(false); - // setUploadSuccess(true); setFile(null); setUploadFileName(""); setReload(!reload); @@ -1056,8 +1042,7 @@ export const FileUpload = (props: FileUploadProps) => { }, }; - axios - .put(url, newFile, config) + uploadFile(url, newFile, "audio/mpeg", setUploadPercent) .then(() => { setAudioUploadStarted(false); // setUploadSuccess(true); diff --git a/src/Components/Patient/UpdateStatusDialog.tsx b/src/Components/Patient/UpdateStatusDialog.tsx index 4fb2ed43a7d..7867ac17431 100644 --- a/src/Components/Patient/UpdateStatusDialog.tsx +++ b/src/Components/Patient/UpdateStatusDialog.tsx @@ -1,5 +1,4 @@ import { useEffect, useState, useReducer } from "react"; -import axios from "axios"; import { SAMPLE_TEST_STATUS, SAMPLE_TEST_RESULT, @@ -18,6 +17,7 @@ import { FieldChangeEvent } from "../Form/FormFields/Utils"; import TextFormField from "../Form/FormFields/TextFormField"; import CheckBoxFormField from "../Form/FormFields/CheckBoxFormField"; import { useTranslation } from "react-i18next"; +import { uploadFile } from "../../Redux/fireRequest"; interface Props { sample: SampleTestModel; @@ -104,20 +104,7 @@ const UpdateStatusDialog = (props: Props) => { if (f === undefined) return; const newFile = new File([f], `${internal_name}`); - const config = { - headers: { - "Content-type": contentType, - "Content-disposition": "inline", - }, - onUploadProgress: (progressEvent: any) => { - const percentCompleted = Math.round( - (progressEvent.loaded * 100) / progressEvent.total - ); - setUploadPercent(percentCompleted); - }, - }; - axios - .put(url, newFile, config) + uploadFile(url, newFile, contentType, setUploadPercent) .then(() => { setUploadStarted(false); setUploadDone(true); diff --git a/src/Redux/actions.tsx b/src/Redux/actions.tsx index 1a5bd7e4fb1..bcacb6414b1 100644 --- a/src/Redux/actions.tsx +++ b/src/Redux/actions.tsx @@ -4,12 +4,12 @@ import { MedicineAdministrationRecord, Prescription, } from "../Components/Medicine/models"; -import { fireRequest, fireRequestForFiles } from "./fireRequest"; +import { fireRequest } from "./fireRequest"; import { ICreateHealthIdRequest } from "../Components/ABDM/models"; export const getConfig = () => { - return fireRequestForFiles("config"); + return fireRequest("config"); }; // User export const postLogin = (params: object) => { @@ -724,10 +724,6 @@ export const retrieveUpload = (params: object, fileId: string) => { return fireRequest("retrieveUpload", [], params, { fileId: fileId }); }; -export const retrieveUploadFilesURL = (params: object, fileId: string) => { - return fireRequestForFiles("retrieveUpload", [], params, { fileId: fileId }); -}; - export const editUpload = ( params: object, fileId: string, diff --git a/src/Redux/api.tsx b/src/Redux/api.tsx index 1aa06c7e1bc..a8ba0068e0b 100644 --- a/src/Redux/api.tsx +++ b/src/Redux/api.tsx @@ -35,12 +35,13 @@ const routes = { token_refresh: { path: "/api/v1/auth/token/refresh/", method: "POST", + noAuth: true, TRes: Type(), TBody: Type<{ refresh: string }>(), }, token_verify: { - path: "/api/v1/auth/token/verify", + path: "/api/v1/auth/token/verify/", method: "POST", }, @@ -57,6 +58,7 @@ const routes = { forgotPassword: { path: "/api/v1/password_reset/", method: "POST", + noAuth: true, }, updatePassword: { @@ -113,7 +115,7 @@ const routes = { }, updateUser: { - path: "/api/v1/users", + path: "/api/v1/users/", method: "PUT", }, @@ -123,7 +125,7 @@ const routes = { }, deleteUser: { - path: "/api/v1/users", + path: "/api/v1/users/", method: "DELETE", }, @@ -162,7 +164,7 @@ const routes = { }, getAllFacilities: { - path: "/api/v1/getallfacilities", + path: "/api/v1/getallfacilities/", }, createFacility: { @@ -179,12 +181,12 @@ const routes = { }, updateFacility: { - path: "/api/v1/facility", + path: "/api/v1/facility/", method: "PUT", }, partialUpdateFacility: { - path: "/api/v1/facility", + path: "/api/v1/facility/", method: "PATCH", }, @@ -298,7 +300,7 @@ const routes = { // Download Api deleteFacility: { - path: "/api/v1/facility", + path: "/api/v1/facility/", method: "DELETE", }, @@ -402,17 +404,17 @@ const routes = { }, updateCapacity: { - path: "/api/v1/facility/{facilityId}/capacity", + path: "/api/v1/facility/{facilityId}/capacity/", method: "PUT", }, updateDoctor: { - path: "/api/v1/facility/{facilityId}/hospital_doctor", + path: "/api/v1/facility/{facilityId}/hospital_doctor/", method: "PUT", }, deleteDoctor: { - path: "/api/v1/facility/{facilityId}/hospital_doctor", + path: "/api/v1/facility/{facilityId}/hospital_doctor/", method: "DELETE", }, @@ -429,16 +431,10 @@ const routes = { path: "/api/v1/facility/{facilityId}/patient_stats/{id}/", }, - // //Care Center - // createCenter: { - // path: "/api/v1/carecenter/", - // method: 'POST' - // } - // Patient searchPatient: { - path: "/api/v1/patient/search", + path: "/api/v1/patient/search/", }, patientList: { path: "/api/v1/patient/", @@ -494,7 +490,7 @@ const routes = { }, deleteExternalResult: { - path: "/api/v1/external_result", + path: "/api/v1/external_result/", method: "DELETE", }, @@ -562,7 +558,7 @@ const routes = { path: "/api/v1/test_sample/", }, getTestSample: { - path: "/api/v1/test_sample", + path: "/api/v1/test_sample/", }, patchSample: { path: "/api/v1/test_sample/{id}/", @@ -578,26 +574,26 @@ const routes = { method: "POST", }, getInventoryLog: { - path: "/api/v1/facility", + path: "/api/v1/facility/", }, setMinQuantity: { path: "/api/v1/facility/{facilityId}/min_quantity/", method: "POST", }, getMinQuantity: { - path: "/api/v1/facility", + path: "/api/v1/facility/", method: "GET", }, updateMinQuantity: { - path: "/api/v1/facility/{facilityId}/min_quantity/{inventoryId}", + path: "/api/v1/facility/{facilityId}/min_quantity/{inventoryId}/", method: "PATCH", }, getInventorySummary: { - path: "/api/v1/facility", + path: "/api/v1/facility/", method: "GET", }, getItemName: { - path: "/api/v1/items", + path: "/api/v1/items/", method: "GET", }, flagInventoryItem: { @@ -613,7 +609,7 @@ const routes = { method: "POST", }, dischargeSummaryPreview: { - path: "/api/v1/consultation/{external_id}/preview_discharge_summary", + path: "/api/v1/consultation/{external_id}/preview_discharge_summary/", method: "GET", }, dischargeSummaryEmail: { @@ -636,7 +632,7 @@ const routes = { method: "GET", }, updateUserDetails: { - path: "/api/v1/users", + path: "/api/v1/users/", method: "PUT", }, @@ -646,11 +642,11 @@ const routes = { method: "POST", }, updateShift: { - path: "/api/v1/shift", + path: "/api/v1/shift/", method: "PUT", }, deleteShiftRecord: { - path: "/api/v1/shift", + path: "/api/v1/shift/", method: "DELETE", }, listShiftRequests: { @@ -719,7 +715,7 @@ const routes = { method: "GET", }, listInvestigationGroups: { - path: "/api/v1/investigation/group", + path: "/api/v1/investigation/group/", method: "GET", }, createInvestigation: { @@ -758,11 +754,11 @@ const routes = { method: "POST", }, updateResource: { - path: "/api/v1/resource", + path: "/api/v1/resource/", method: "PUT", }, deleteResourceRecord: { - path: "/api/v1/resource", + path: "/api/v1/resource/", method: "DELETE", }, listResourceRequests: { @@ -788,7 +784,7 @@ const routes = { // Assets endpoints listAssets: { - path: "/api/v1/asset", + path: "/api/v1/asset/", method: "GET", }, createAsset: { @@ -828,7 +824,7 @@ const routes = { method: "GET", }, getAssetTransaction: { - path: "/api/v1/asset_transaction/{id}", + path: "/api/v1/asset_transaction/{id}/", method: "GET", }, @@ -839,11 +835,11 @@ const routes = { method: "GET", }, getAssetService: { - path: "/api/v1/asset/{asset_external_id}/service_records/{external_id}", + path: "/api/v1/asset/{asset_external_id}/service_records/{external_id}/", method: "GET", }, updateAssetService: { - path: "/api/v1/asset/{asset_external_id}/service_records/{external_id}", + path: "/api/v1/asset/{asset_external_id}/service_records/{external_id}/", method: "PUT", }, @@ -958,7 +954,7 @@ const routes = { method: "GET", }, getAssetAvailability: { - path: "/api/v1/asset_availability/{id}", + path: "/api/v1/asset_availability/{id}/", method: "GET", }, diff --git a/src/Redux/fireRequest.tsx b/src/Redux/fireRequest.tsx index 9859ea540b6..d570d8bdcbb 100644 --- a/src/Redux/fireRequest.tsx +++ b/src/Redux/fireRequest.tsx @@ -1,12 +1,8 @@ import * as Notification from "../Utils/Notifications.js"; -import { isEmpty, omitBy } from "lodash"; +import { LocalStorageKeys } from "../Common/constants.js"; +import routes from "./api.js"; -import { LocalStorageKeys } from "../Common/constants"; -import api from "./api"; -import axios from "axios"; - -const requestMap: any = api; export const actions = { FETCH_REQUEST: "FETCH_REQUEST", FETCH_REQUEST_SUCCESS: "FETCH_REQUEST_SUCCESS", @@ -14,6 +10,7 @@ export const actions = { SET_DATA: "SET_DATA", }; +// const isRunning: { [key: string]: AbortController } = {}; const isRunning: any = {}; export const setStoreData = (key: string, value: any) => { @@ -47,29 +44,34 @@ export const fetchResponseSuccess = (key: string, data: any) => { }; }; +export interface ResponseWrapper extends Response { + data: any; +} + export const fireRequest = ( key: string, path: any = [], params: any = {}, pathParam?: any, altKey?: string, - suppressNotif?: boolean + suppressNotif = false ) => { return (dispatch: any) => { // cancel previous api call - if (isRunning[altKey ? altKey : key]) { - isRunning[altKey ? altKey : key].cancel(); + const requestKey = altKey || key; + if (isRunning[requestKey]) { + isRunning[requestKey].abort(); } - isRunning[altKey ? altKey : key] = axios.CancelToken.source(); + const controller = new AbortController(); + isRunning[requestKey] = controller; + // get api url / method - const request = Object.assign({}, requestMap[key]); + const request = Object.assign({}, (routes as any)[key]); + if (path.length > 0) { - request.path += "/" + path.join("/"); - } - // add trailing slash to path before query paramaters - if (request.path.slice(-1) !== "/" && request.path.indexOf("?") === -1) { - request.path += "/"; + request.path += path.join("/") + "/"; } + if (request.method === undefined || request.method === "GET") { request.method = "GET"; let qString = ""; @@ -82,236 +84,131 @@ export const fireRequest = ( request.path += `?${qString}`; } } + // set dynamic params in the URL if (pathParam) { Object.keys(pathParam).forEach((param: any) => { request.path = request.path.replace(`{${param}}`, pathParam[param]); }); } - - // set authorization header in the request header - const config: any = { - headers: {}, + const headers: { [key: string]: string } = { + "Content-Type": "application/json", + Accept: "application/json", }; - if (!request.noAuth && localStorage.getItem(LocalStorageKeys.accessToken)) { - config.headers["Authorization"] = + if (!request.noAuth) { + headers["Authorization"] = "Bearer " + localStorage.getItem(LocalStorageKeys.accessToken); - } else { - // TODO: get access token } - const axiosApiCall: any = axios.create(config); dispatch(fetchDataRequest(key)); - return axiosApiCall[request.method.toLowerCase()](request.path, { - ...params, - cancelToken: isRunning[altKey ? altKey : key].token, + return fetch(request.path, { + method: request.method, + headers, + credentials: "include", + cache: "default", + redirect: "follow", + signal: controller.signal, + body: request.method === "GET" ? undefined : JSON.stringify(params), }) - .then((response: any) => { - dispatch(fetchResponseSuccess(key, response.data)); - return response; - }) - .catch((error: any) => { - dispatch(fetchDataRequestError(key, error)); - - if (!(suppressNotif ?? false) && error.response) { - // temporarily don't show invalid phone number error on duplicate patient check - if (error.response.status === 400 && key === "searchPatient") { - return; - } - - // deleteUser: 404 is for permission denied - if (error.response.status === 404 && key === "deleteUser") { - Notification.Error({ - msg: "Permission denied!", - }); - return; - } - - // currentUser is ignored because on the first page load - // 403 error is displayed for invalid credential. - if (error.response.status === 403 && key === "currentUser") { - if (localStorage.getItem(LocalStorageKeys.accessToken)) { - localStorage.removeItem(LocalStorageKeys.accessToken); - } - return; - } + .then(async (res: Response) => { + const response: ResponseWrapper = res as ResponseWrapper; + try { + response.data = await response.json(); + } catch (error) { + console.error(error); + response.data = {}; + } - // 400 Bad Request Error - if (error.response.status === 400 || error.response.status === 406) { - Notification.BadRequest({ - errs: error.response.data, - }); - return error.response; - } + if (response.ok) { + dispatch(fetchResponseSuccess(key, response.data)); + return response; + } - // 4xx Errors - if (error.response.status > 400 && error.response.status < 500) { - if (error.response.data && error.response.data.detail) { - if (error.response.data.code === "token_not_valid") { - window.location.href = "/session-expired"; - } - Notification.Error({ - msg: error.response.data.detail, - }); - } else { - Notification.Error({ - msg: "Something went wrong...!", - }); - } - if (error.response.status === 429) { - return error.response; - } - return; - } + dispatch(fetchDataRequestError(key, response)); + if (suppressNotif) { + return response; + } - // 5xx Errors - if (error.response.status >= 500 && error.response.status <= 599) { - Notification.Error({ - msg: "Something went wrong...!", - }); - return; - } - } else { - return error.response; + if (response.status === 400 || response.status === 406) { + Notification.BadRequest({ + errs: response.data, + }); + return response; } - }); - }; -}; -export const fireRequestV2 = ( - key: string, - path: any = [], - params: any = {}, - successCallback: any = () => undefined, - errorCallback: any = () => undefined, - pathParam?: any, - altKey?: string -) => { - // cancel previous api call - if (isRunning[altKey ? altKey : key]) { - isRunning[altKey ? altKey : key].cancel(); - } - isRunning[altKey ? altKey : key] = axios.CancelToken.source(); - // get api url / method - const request = Object.assign({}, requestMap[key]); - if (path.length > 0) { - request.path += "/" + path.join("/"); - } - if (request.method === undefined || request.method === "GET") { - request.method = "GET"; - const qs = new URLSearchParams(omitBy(params, isEmpty)).toString(); - if (qs !== "") { - request.path += `?${qs}`; - } - } - // set dynamic params in the URL - if (pathParam) { - Object.keys(pathParam).forEach((param: any) => { - request.path = request.path.replace(`{${param}}`, pathParam[param]); - }); - } - - // set authorization header in the request header - const config: any = { - headers: {}, - }; - if (!request.noAuth && localStorage.getItem(LocalStorageKeys.accessToken)) { - config.headers["Authorization"] = - "Bearer " + localStorage.getItem(LocalStorageKeys.accessToken); - } - const axiosApiCall: any = axios.create(config); - - fetchDataRequest(key); - return axiosApiCall[request.method.toLowerCase()](request.path, { - ...params, - cancelToken: isRunning[altKey ? altKey : key].token, - }) - .then((response: any) => { - successCallback(response.data); - }) - .catch((error: any) => { - errorCallback(error); - if (error.response) { - // temporarily don't show invalid phone number error on duplicate patient check - if (error.response.status === 400 && key === "searchPatient") { + if (response.status === 403 && key === "currentUser") { + localStorage.removeItem(LocalStorageKeys.accessToken); return; } - // deleteUser: 404 is for permission denied - if (error.response.status === 404 && key === "deleteUser") { + if (response.status === 404 && key === "deleteUser") { Notification.Error({ msg: "Permission denied!", }); + return response; } - // currentUser is ignored because on the first page load - // 403 error is displayed for invalid credential. - if (error.response.status === 403 && key === "currentUser") { - if (localStorage.getItem(LocalStorageKeys.accessToken)) { - localStorage.removeItem(LocalStorageKeys.accessToken); - } + if (response.status === 429) { + return response; } - // 400 Bad Request Error - if (error.response.status === 400 || error.response.status === 406) { - Notification.BadRequest({ - errs: error.response.data, - }); - } - - // 4xx Errors - if (error.response.status > 400 && error.response.status < 500) { - if (error.response.data && error.response.data.detail) { - Notification.Error({ - msg: error.response.data.detail, - }); - } else { - Notification.Error({ - msg: "Something went wrong...!", - }); - } - if (error.response.status === 429) { - return error.response; + if (response.status > 400 && response.status < 500) { + if (response?.data?.code === "token_not_valid") { + window.location.href = "/session-expired"; } - return; - } - - // 5xx Errors - if (error.response.status >= 500 && error.response.status <= 599) { Notification.Error({ - msg: "Something went wrong...!", + msg: response?.data?.detail || "Something went wrong...!", }); - return; } - } - }); + return response; + }) + .catch((error: any) => { + console.error(error); + dispatch(fetchDataRequestError(key, error)); + + return; + }); + }; }; -export const fireRequestForFiles = ( +export const legacyFireRequest = ( key: string, path: any = [], params: any = {}, + successCallback: any = () => undefined, + errorCallback: any = () => undefined, pathParam?: any, altKey?: string ) => { - return (dispatch: any) => { + return () => { // cancel previous api call - if (isRunning[altKey ? altKey : key]) { - isRunning[altKey ? altKey : key].cancel(); + const requestKey = altKey || key; + if (isRunning[requestKey]) { + isRunning[requestKey].abort(); } - isRunning[altKey ? altKey : key] = axios.CancelToken.source(); + const controller = new AbortController(); + isRunning[requestKey] = controller; + // get api url / method - const request = Object.assign({}, requestMap[key]); + const request = Object.assign({}, (routes as any)[key]); + if (path.length > 0) { - request.path += "/" + path.join("/"); + request.path += path.join("/") + "/"; } + if (request.method === undefined || request.method === "GET") { request.method = "GET"; - const qs = new URLSearchParams(omitBy(params, isEmpty)).toString(); - if (qs !== "") { - request.path += `?${qs}`; + let qString = ""; + Object.keys(params).forEach((param: any) => { + if (params[param] !== undefined && params[param] !== "") { + qString += `${param}=${encodeURIComponent(params[param])}&`; + } + }); + if (qString !== "") { + request.path += `?${qString}`; } } + // set dynamic params in the URL if (pathParam) { Object.keys(pathParam).forEach((param: any) => { @@ -319,80 +216,105 @@ export const fireRequestForFiles = ( }); } - // set authorization header in the request header - const config: any = { - headers: { - "Content-type": "application/pdf", - "Content-disposition": "inline", - }, + const headers: { [key: string]: string } = { + "Content-Type": "application/json", + Accept: "application/json", }; - // Content-Type: application/pdf - // Content-Disposition: inline; filename="filename.pdf" - if (!request.noAuth && localStorage.getItem(LocalStorageKeys.accessToken)) { - config.headers["Authorization"] = + if (!request.noAuth) { + headers["Authorization"] = "Bearer " + localStorage.getItem(LocalStorageKeys.accessToken); } - const axiosApiCall: any = axios.create(config); - dispatch(fetchDataRequest(key)); - return axiosApiCall[request.method.toLowerCase()](request.path, { - ...params, - cancelToken: isRunning[altKey ? altKey : key].token, + fetchDataRequest(key); + return fetch(request.path, { + method: request.method, + headers, + credentials: "include", + cache: "default", + redirect: "follow", + signal: controller.signal, + body: request.method === "POST" ? JSON.stringify(params) : undefined, }) - .then((response: any) => { - dispatch(fetchResponseSuccess(key, response.data)); - return response; - }) - .catch((error: any) => { - dispatch(fetchDataRequestError(key, error)); + .then(async (res: Response) => { + const response: ResponseWrapper = res as ResponseWrapper; + + try { + response.data = await response.json(); + } catch (error) { + console.error(error); + response.data = {}; + } - if (error.response) { - // temporarily don't show invalid phone number error on duplicate patient check - if (error.response.status === 400 && key === "searchPatient") { - return; - } + if (response.ok) { + successCallback(response.data); + return response; + } - // currentUser is ignored because on the first page load - // 403 error is displayed for invalid credential. - if (error.response.status === 403 && key === "currentUser") { - if (localStorage.getItem(LocalStorageKeys.accessToken)) { - localStorage.removeItem(LocalStorageKeys.accessToken); - } - return; - } + errorCallback(response); - // 400 Bad Request Error - if (error.response.status === 400 || error.response.status === 406) { - Notification.BadRequest({ - errs: error.response.data, - }); - return error.response; - } + if (response.status === 400 || response.status === 406) { + Notification.BadRequest({ + errs: response.data, + }); + return response; + } - // 4xx Errors - if (error.response.status > 400 && error.response.status < 500) { - if (error.response.status === 429) { - return error.response; - } else if (error.response.data && error.response.data.detail) { - Notification.Error({ - msg: error.response.data.detail, - }); - } else { - Notification.Error({ - msg: "Something went wrong...!", - }); - } - return; - } + if (response.status === 403 && key === "currentUser") { + localStorage.removeItem(LocalStorageKeys.accessToken); + return; + } + + if (response.status === 404 && key === "deleteUser") { + Notification.Error({ + msg: "Permission denied!", + }); + return response; + } + + if (response.status === 429) { + return response; + } - // 5xx Errors - if (error.response.status >= 500 && error.response.status <= 599) { - Notification.Error({ - msg: "Something went wrong...!", - }); - return; + if (response.status > 400 && response.status < 500) { + if (response?.data?.code === "token_not_valid") { + window.location.href = "/session-expired"; } + Notification.Error({ + msg: response?.data?.detail || "Something went wrong...!", + }); } + return response; + }) + .catch((error: any) => { + console.error(error); + errorCallback(error); + + return; }); }; }; + +export const uploadFile = async ( + url: string, + file: File, + contentType: string, + uploadProgressCB: (progress: number) => void +) => { + const xhr = new XMLHttpRequest(); + return new Promise((resolve, reject) => { + xhr.upload.onprogress = (event) => { + if (event.lengthComputable) { + uploadProgressCB((event.loaded / event.total) * 100); + } + }; + xhr.onloadend = () => { + if (xhr.readyState === 4 && xhr.status === 200) { + return resolve(xhr.response); + } + reject("failed to upload file"); + }; + xhr.open("PUT", url, true); + xhr.setRequestHeader("Content-Type", contentType); + xhr.send(file); + }); +};