Bugfix - Retry Mechanism for Failed Requests in Loop (#808)
This pull request addresses an issue with the retry mechanism for failed requests within a loop. The current implementation lacks proper handling of failed requests, leading to potential data loss and inconsistent behavior. This bugfix aims to improve the retry logic and ensure that requests are appropriately retried when they fail within a loop.
This commit is contained in:
commit
cb5114b9b7
9 changed files with 66 additions and 23 deletions
|
|
@ -9,10 +9,16 @@ import ErrorAlert from "./alerts/error";
|
|||
import NoticeAlert from "./alerts/notice";
|
||||
import SuccessAlert from "./alerts/success";
|
||||
import CrashErrorComponent from "./components/CrashErrorComponent";
|
||||
import FetchErrorComponent from "./components/fetchErrorComponent";
|
||||
import LoadingComponent from "./components/loadingComponent";
|
||||
import {
|
||||
FETCH_ERROR_DESCRIPION,
|
||||
FETCH_ERROR_MESSAGE,
|
||||
} from "./constants/constants";
|
||||
import { alertContext } from "./contexts/alertContext";
|
||||
import { locationContext } from "./contexts/locationContext";
|
||||
import { TabsContext } from "./contexts/tabsContext";
|
||||
import { typesContext } from "./contexts/typesContext";
|
||||
import Router from "./routes";
|
||||
|
||||
export default function App() {
|
||||
|
|
@ -38,6 +44,7 @@ export default function App() {
|
|||
setSuccessOpen,
|
||||
loading,
|
||||
} = useContext(alertContext);
|
||||
const { fetchError } = useContext(typesContext);
|
||||
|
||||
// Initialize state variable for the list of alerts
|
||||
const [alertsList, setAlertsList] = useState<
|
||||
|
|
@ -137,7 +144,14 @@ export default function App() {
|
|||
>
|
||||
{loading ? (
|
||||
<div className="loading-page-panel">
|
||||
<LoadingComponent remSize={50} />
|
||||
{fetchError ? (
|
||||
<FetchErrorComponent
|
||||
description={FETCH_ERROR_DESCRIPION}
|
||||
message={FETCH_ERROR_MESSAGE}
|
||||
></FetchErrorComponent>
|
||||
) : (
|
||||
<LoadingComponent remSize={50} />
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
|
|
|
|||
16
src/frontend/src/components/fetchErrorComponent/index.tsx
Normal file
16
src/frontend/src/components/fetchErrorComponent/index.tsx
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
import { fetchErrorComponentType } from "../../types/components";
|
||||
import IconComponent from "../genericIconComponent";
|
||||
|
||||
export default function FetchErrorComponent({
|
||||
message,
|
||||
description,
|
||||
}: fetchErrorComponentType) {
|
||||
return (
|
||||
<div role="status" className="m-auto flex flex-col items-center">
|
||||
<IconComponent className={`h-16 w-16`} name="Unplug"></IconComponent>
|
||||
<br></br>
|
||||
<span className="text-lg text-almost-medium-blue">{message}</span>
|
||||
<span className="text-lg text-almost-medium-blue">{description}</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -7,5 +7,11 @@ export default function IconComponent({
|
|||
iconColor,
|
||||
}: IconComponentProps): JSX.Element {
|
||||
const TargetIcon = nodeIconsLucide[name] ?? nodeIconsLucide["unknown"];
|
||||
return <TargetIcon className={className} style={{ color: iconColor }} />;
|
||||
return (
|
||||
<TargetIcon
|
||||
className={className}
|
||||
style={{ color: iconColor }}
|
||||
stroke-width={1.5}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,4 @@
|
|||
type LoadingComponentProps = {
|
||||
remSize: number;
|
||||
};
|
||||
import { LoadingComponentProps } from "../../types/components";
|
||||
|
||||
export default function LoadingComponent({ remSize }: LoadingComponentProps) {
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -511,3 +511,7 @@ export const URL_EXCLUDED_FROM_ERROR_RETRIES = [
|
|||
];
|
||||
|
||||
export const skipNodeUpdate = ["CustomComponent"];
|
||||
|
||||
export const FETCH_ERROR_MESSAGE = "Couldn't establish a connection.";
|
||||
export const FETCH_ERROR_DESCRIPION =
|
||||
"Check if everything is working properly and try again.";
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import {
|
|||
useState,
|
||||
} from "react";
|
||||
import { Node } from "reactflow";
|
||||
import { getAll } from "../controllers/API";
|
||||
import { getAll, getHealth } from "../controllers/API";
|
||||
import { APIKindType } from "../types/api";
|
||||
import { typesContextType } from "../types/typesContext";
|
||||
import { alertContext } from "./alertContext";
|
||||
|
|
@ -23,6 +23,8 @@ const initialValue: typesContextType = {
|
|||
setTemplates: () => {},
|
||||
data: {},
|
||||
setData: () => {},
|
||||
setFetchError: () => {},
|
||||
fetchError: false,
|
||||
};
|
||||
|
||||
export const typesContext = createContext<typesContextType>(initialValue);
|
||||
|
|
@ -32,14 +34,10 @@ export function TypesProvider({ children }: { children: ReactNode }) {
|
|||
const [reactFlowInstance, setReactFlowInstance] = useState(null);
|
||||
const [templates, setTemplates] = useState({});
|
||||
const [data, setData] = useState({});
|
||||
const [fetchError, setFetchError] = useState(false);
|
||||
const { setLoading } = useContext(alertContext);
|
||||
|
||||
useEffect(() => {
|
||||
let delay = 1000; // Start delay of 1 second
|
||||
let intervalId = null;
|
||||
let retryCount = 0; // Count of retry attempts
|
||||
const maxRetryCount = 5; // Max retry attempts
|
||||
|
||||
// We will keep a flag to handle the case where the component is unmounted before the API call resolves.
|
||||
let isMounted = true;
|
||||
|
||||
|
|
@ -47,7 +45,7 @@ export function TypesProvider({ children }: { children: ReactNode }) {
|
|||
try {
|
||||
const result = await getAll();
|
||||
// Make sure to only update the state if the component is still mounted.
|
||||
if (isMounted) {
|
||||
if (isMounted && result?.status === 200) {
|
||||
setLoading(false);
|
||||
setData(result.data);
|
||||
setTemplates(
|
||||
|
|
@ -77,22 +75,15 @@ export function TypesProvider({ children }: { children: ReactNode }) {
|
|||
}, {})
|
||||
);
|
||||
}
|
||||
// Clear the interval if successful.
|
||||
clearInterval(intervalId);
|
||||
} catch (error) {
|
||||
console.error("An error has occurred while fetching types.");
|
||||
await getHealth().catch((e) => {
|
||||
setFetchError(true);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Start the initial interval.
|
||||
intervalId = setInterval(getTypes, delay);
|
||||
|
||||
return () => {
|
||||
// This will clear the interval when the component unmounts, or when the dependencies of the useEffect hook change.
|
||||
clearInterval(intervalId);
|
||||
// Indicate that the component has been unmounted.
|
||||
isMounted = false;
|
||||
};
|
||||
getTypes();
|
||||
}, []);
|
||||
|
||||
function deleteNode(idx: string) {
|
||||
|
|
@ -117,6 +108,8 @@ export function TypesProvider({ children }: { children: ReactNode }) {
|
|||
templates,
|
||||
data,
|
||||
setData,
|
||||
fetchError,
|
||||
setFetchError,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
|
|
|
|||
|
|
@ -171,3 +171,11 @@ export type IconComponentProps = {
|
|||
export interface languageMap {
|
||||
[key: string]: string | undefined;
|
||||
}
|
||||
export type fetchErrorComponentType = {
|
||||
message: string;
|
||||
description: string;
|
||||
};
|
||||
|
||||
export type LoadingComponentProps = {
|
||||
remSize: number;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -15,4 +15,6 @@ export type typesContextType = {
|
|||
setTemplates: (newState: {}) => void;
|
||||
data: typeof data;
|
||||
setData: (newState: {}) => void;
|
||||
fetchError: boolean;
|
||||
setFetchError: (newState: boolean) => void;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -56,6 +56,7 @@ import {
|
|||
TerminalSquare,
|
||||
Trash2,
|
||||
Undo,
|
||||
Unplug,
|
||||
Upload,
|
||||
Users2,
|
||||
Variable,
|
||||
|
|
@ -275,4 +276,5 @@ export const nodeIconsLucide = {
|
|||
Upload,
|
||||
MessageSquare,
|
||||
MoreHorizontal,
|
||||
Unplug,
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue