blob: 7f316a74672138baade07c78b95e17177a53ef44 [file]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import axios, { AxiosRequestConfig, AxiosResponse } from "axios";
import { globalSessionData } from "../../utils/Enum";
import { toast } from "react-toastify";
import { serverErrorHandler } from "@utils/Utils";
/** Keep 403 toasts readable (Atlas authorization message). */
const FORBIDDEN_ERROR_TOAST_MS = 5_000;
/**
* Callers often `toast.dismiss(ref.current)` in catch when `ref.current` is null;
* react-toastify then clears all toasts and removes the 403 toast we just showed.
* Queue the toast as a macrotask so it runs after that dismiss.
*/
const FETCH_API_FORBIDDEN_TOAST_ID = "fetch-api-http-403";
const showForbiddenToastLater = (
responseData: unknown,
defaultMessage: string
) => {
setTimeout(() => {
let message = defaultMessage;
if (responseData && typeof responseData === "object") {
const d = responseData as {
errorMessage?: unknown;
message?: unknown;
error?: unknown;
};
message =
(d.errorMessage as string | undefined) ||
(d.message as string | undefined) ||
(d.error as string | undefined) ||
message;
}
toast.error(message, {
toastId: FETCH_API_FORBIDDEN_TOAST_ID,
autoClose: FORBIDDEN_ERROR_TOAST_MS
});
}, 0);
};
let prevNetworkErrorTime = 0;
function errorHandelingForAbortAndStatus0() {
const diffTime = new Date().getTime() - prevNetworkErrorTime;
if (diffTime > 3000) {
prevNetworkErrorTime = new Date().getTime();
toast.error(
"Network Connection Failure : " +
"It seems you are not connected to the internet. Please check your internet connection and try again"
);
}
}
const fetchApi = async (url: string, config: AxiosRequestConfig) => {
const configs: AxiosRequestConfig = {
url: url,
method: config.method,
params: config.params,
data: config.data,
onUploadProgress: config.onUploadProgress,
headers: {
[globalSessionData.restCrsfHeader]: globalSessionData.crsfToken
? [globalSessionData.crsfToken]
: '""'
}
};
try {
const resp: AxiosResponse = await axios(configs as AxiosRequestConfig);
return resp;
} catch (error: any) {
if (axios.isAxiosError(error)) {
if(error.response?.status){
switch (Number(error.response.status)) {
case 0:
errorHandelingForAbortAndStatus0();
break;
case 401:
window.location.replace("login.jsp");
break;
case 403:
// Match classic UI: toast only, no redirect. Defer toast so callers
// that dismiss all toasts in catch (e.g. toast.dismiss(null ref)) do
// not remove this notification before it is shown — see
// showForbiddenToastLater.
showForbiddenToastLater(
error.response?.data,
"You are not authorized"
);
break;
case 404:
serverErrorHandler(
{ responseJSON: error.response?.data },
"Resource not found"
);
break;
case 419:
toast.warning("Session Time Out !!");
window.location.replace("login.jsp");
break;
case 500:
serverErrorHandler(
{ responseJSON: error.response?.data },
"Internal Server Error"
);
break;
case 503:
serverErrorHandler(
{ responseJSON: error.response?.data },
"Service Unavailable"
);
break;
case 504:
serverErrorHandler(
{ responseJSON: error.response?.data },
"Gateway Timeout"
);
break;
default:
break;
}
}
// Only treat as offline / connection failure when there is no HTTP
// response (or status 0). Do not run this for 403/404/5xx — those are
// handled above and would wrongly show a network toast.
const res = error.response;
const isAbort =
error.code === "ERR_CANCELED" || res?.statusText === "abort";
if (!isAbort && (!res || Number(res.status) === 0)) {
errorHandelingForAbortAndStatus0();
}
}
throw error;
}
};
export { fetchApi };