From 011ebc2a5ca8a954e31df1a39a044e011db16520 Mon Sep 17 00:00:00 2001 From: Cristhian Zanforlin Lousa <72977554+Cristhianzl@users.noreply.github.com> Date: Thu, 18 Jul 2024 15:40:11 -0300 Subject: [PATCH] fix: session error after retries + refactor on interceptor component (#2803) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 📝 (API): Add AxiosRequestConfig to import statement in api.tsx for better type checking 🔧 (API): Add useFlowsManagerStore to import statement in api.tsx for managing saveLoading state ♻️ (API): Refactor ApiInterceptor function in api.tsx to improve error handling and flow control 📝 (API): Update comments in api.tsx for better code readability and understanding 🔧 (API): Add setSaveLoading function to FlowsManagerStoreType in flowsManagerStore.ts for managing saveLoading state * 🐛 (frontend): fix potential error when api response is null or undefined by adding optional chaining to data property access * [autofix.ci] apply automated fixes --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> --- src/frontend/src/controllers/API/api.tsx | 42 +++++++++++++----- src/frontend/src/controllers/API/index.ts | 44 +++++++++---------- src/frontend/src/stores/flowsManagerStore.ts | 1 + .../src/types/zustand/flowsManager/index.ts | 1 + 4 files changed, 56 insertions(+), 32 deletions(-) diff --git a/src/frontend/src/controllers/API/api.tsx b/src/frontend/src/controllers/API/api.tsx index 3545a639c..11dcf971d 100644 --- a/src/frontend/src/controllers/API/api.tsx +++ b/src/frontend/src/controllers/API/api.tsx @@ -2,7 +2,8 @@ import { LANGFLOW_ACCESS_TOKEN, LANGFLOW_AUTO_LOGIN_OPTION, } from "@/constants/constants"; -import axios, { AxiosError, AxiosInstance } from "axios"; +import useFlowsManagerStore from "@/stores/flowsManagerStore"; +import axios, { AxiosError, AxiosInstance, AxiosRequestConfig } from "axios"; import { useContext, useEffect } from "react"; import { Cookies } from "react-cookie"; import { renewAccessToken } from "."; @@ -19,9 +20,10 @@ const api: AxiosInstance = axios.create({ function ApiInterceptor() { const setErrorData = useAlertStore((state) => state.setErrorData); - let { accessToken, login, logout, authenticationErrorCount, autoLogin } = + let { accessToken, logout, authenticationErrorCount, autoLogin } = useContext(AuthContext); const cookies = new Cookies(); + const setSaveLoading = useFlowsManagerStore((state) => state.setSaveLoading); useEffect(() => { const interceptor = api.interceptors.response.use( @@ -39,7 +41,8 @@ function ApiInterceptor() { if (!stillRefresh) { return Promise.reject(error); } - const acceptedRequest = await tryToRenewAccessToken(error); + + await tryToRenewAccessToken(error); const accessToken = cookies.get(LANGFLOW_ACCESS_TOKEN); @@ -47,11 +50,12 @@ function ApiInterceptor() { return Promise.reject(error); } - return acceptedRequest; + await remakeRequest(error); + setSaveLoading(false); + authenticationErrorCount = 0; } } await clearBuildVerticesState(error); - return Promise.reject(error); }, ); @@ -67,7 +71,6 @@ function ApiInterceptor() { try { const parsedURL = new URL(url); - const isDomainAllowed = authorizedDomains.some( (domain) => parsedURL.origin === new URL(domain).origin, ); @@ -131,10 +134,7 @@ function ApiInterceptor() { async function tryToRenewAccessToken(error: AxiosError) { try { if (window.location.pathname.includes("/login")) return; - const res = await renewAccessToken(); - if (res?.data?.access_token && res?.data?.refresh_token) { - login(res?.data?.access_token, cookies.get(LANGFLOW_AUTO_LOGIN_OPTION)); - } + await renewAccessToken(); } catch (error) { clearBuildVerticesState(error); logout(); @@ -152,6 +152,28 @@ function ApiInterceptor() { } } + async function remakeRequest(error: AxiosError) { + const originalRequest = error.config as AxiosRequestConfig; + + try { + const accessToken = cookies.get(LANGFLOW_ACCESS_TOKEN); + if (!accessToken) { + throw new Error("Access token not found in cookies"); + } + + // Modify headers in originalRequest + originalRequest.headers = { + ...(originalRequest.headers as Record), // Cast to suppress TypeScript error + Authorization: `Bearer ${accessToken}`, + }; + + const response = await axios.request(originalRequest); + return response.data; // Or handle the response as needed + } catch (err) { + throw err; // Throw the error if request fails again + } + } + return null; } diff --git a/src/frontend/src/controllers/API/index.ts b/src/frontend/src/controllers/API/index.ts index 6805d0f96..cf1c0f43e 100644 --- a/src/frontend/src/controllers/API/index.ts +++ b/src/frontend/src/controllers/API/index.ts @@ -49,7 +49,7 @@ const GITHUB_API_URL = "https://api.github.com"; export async function getRepoStars(owner: string, repo: string) { try { const response = await api.get(`${GITHUB_API_URL}/repos/${owner}/${repo}`); - return response.data.stargazers_count; + return response?.data.stargazers_count; } catch (error) { console.error("Error fetching repository data:", error); return null; @@ -101,7 +101,7 @@ export async function getExamples(): Promise { "https://api.github.com/repos/langflow-ai/langflow_examples/contents/examples?ref=main"; const response = await api.get(url); - const jsonFiles = response.data.filter((file: any) => { + const jsonFiles = response?.data.filter((file: any) => { return file.name.endsWith(".json"); }); @@ -143,7 +143,7 @@ export async function saveFlowToDatabase(newFlow: { if (response?.status !== 201) { throw new Error(`HTTP error! status: ${response?.status}`); } - return response.data; + return response?.data; } catch (error) { console.error(error); throw error; @@ -168,10 +168,10 @@ export async function updateFlowInDatabase( endpoint_name: updatedFlow.endpoint_name, }); - if (response?.status !== 200) { + if (response && response?.status !== 200) { throw new Error(`HTTP error! status: ${response?.status}`); } - return response.data; + return response?.data; } catch (error) { console.error(error); throw error; @@ -187,10 +187,10 @@ export async function updateFlowInDatabase( export async function readFlowsFromDatabase() { try { const response = await api.get(`${BASE_URL_API}flows/`); - if (response?.status !== 200) { + if (response && response?.status !== 200) { throw new Error(`HTTP error! status: ${response?.status}`); } - return response.data; + return response?.data; } catch (error) { console.error(error); throw error; @@ -200,10 +200,10 @@ export async function readFlowsFromDatabase() { export async function downloadFlowsFromDatabase() { try { const response = await api.get(`${BASE_URL_API}flows/download/`); - if (response?.status !== 200) { + if (response && response?.status !== 200) { throw new Error(`HTTP error! status: ${response?.status}`); } - return response.data; + return response?.data; } catch (error) { console.error(error); throw error; @@ -217,7 +217,7 @@ export async function uploadFlowsToDatabase(flows: FormData) { if (response?.status !== 201) { throw new Error(`HTTP error! status: ${response?.status}`); } - return response.data; + return response?.data; } catch (error) { console.error(error); throw error; @@ -237,7 +237,7 @@ export async function deleteFlowFromDatabase(flowId: string) { if (response.status !== 200) { throw new Error(`HTTP error! status: ${response.status}`); } - return response.data; + return response?.data; } catch (error) { console.error(error); throw error; @@ -254,10 +254,10 @@ export async function deleteFlowFromDatabase(flowId: string) { export async function getFlowFromDatabase(flowId: number) { try { const response = await api.get(`${BASE_URL_API}flows/${flowId}`); - if (response?.status !== 200) { + if (response && response?.status !== 200) { throw new Error(`HTTP error! status: ${response?.status}`); } - return response.data; + return response?.data; } catch (error) { console.error(error); throw error; @@ -276,7 +276,7 @@ export async function getFlowStylesFromDatabase() { if (response.status !== 200) { throw new Error(`HTTP error! status: ${response.status}`); } - return response.data; + return response?.data; } catch (error) { console.error(error); throw error; @@ -302,7 +302,7 @@ export async function saveFlowStyleToDatabase(flowStyle: FlowStyleType) { if (response.status !== 201) { throw new Error(`HTTP error! status: ${response.status}`); } - return response.data; + return response?.data; } catch (error) { console.error(error); throw error; @@ -316,7 +316,7 @@ export async function saveFlowStyleToDatabase(flowStyle: FlowStyleType) { */ export async function getVersion() { const response = await api.get(`${BASE_URL_API}version`); - return response.data; + return response?.data; } /** @@ -416,7 +416,7 @@ export async function onLogin(user: LoginType) { ); if (response.status === 200) { - const data = response.data; + const data = response?.data; return data; } } catch (error) { @@ -431,7 +431,7 @@ export async function autoLogin(abortSignal) { }); if (response.status === 200) { - const data = response.data; + const data = response?.data; return data; } } catch (error) { @@ -607,7 +607,7 @@ export async function saveFlowStore( if (response.status !== 201) { throw new Error(`HTTP error! status: ${response.status}`); } - return response.data; + return response?.data; } catch (error) { console.error(error); throw error; @@ -862,7 +862,7 @@ export async function updateFlowStore( if (response.status !== 201) { throw new Error(`HTTP error! status: ${response.status}`); } - return response.data; + return response?.data; } catch (error) { console.error(error); throw error; @@ -872,7 +872,7 @@ export async function updateFlowStore( export async function requestLogout() { try { const response = await api.post(`${BASE_URL_API}logout`); - return response.data; + return response?.data; } catch (error) { console.error(error); throw error; @@ -883,7 +883,7 @@ export async function getGlobalVariables(): Promise<{ [key: string]: { id: string; type: string; default_fields: string[] }; }> { const globalVariables = {}; - (await api.get(`${BASE_URL_API}variables/`)).data.forEach((element) => { + (await api.get(`${BASE_URL_API}variables/`))?.data?.forEach((element) => { globalVariables[element.name] = { id: element.id, type: element.type, diff --git a/src/frontend/src/stores/flowsManagerStore.ts b/src/frontend/src/stores/flowsManagerStore.ts index f9ef070b0..fe77f4072 100644 --- a/src/frontend/src/stores/flowsManagerStore.ts +++ b/src/frontend/src/stores/flowsManagerStore.ts @@ -78,6 +78,7 @@ const useFlowsManagerStore = create((set, get) => ({ }, currentFlow: undefined, saveLoading: false, + setSaveLoading: (saveLoading: boolean) => set({ saveLoading }), isLoading: true, setIsLoading: (isLoading: boolean) => set({ isLoading }), refreshFlows: () => { diff --git a/src/frontend/src/types/zustand/flowsManager/index.ts b/src/frontend/src/types/zustand/flowsManager/index.ts index 6e7b03194..234e76ba0 100644 --- a/src/frontend/src/types/zustand/flowsManager/index.ts +++ b/src/frontend/src/types/zustand/flowsManager/index.ts @@ -11,6 +11,7 @@ export type FlowsManagerStoreType = { currentFlowId: string; setCurrentFlowId: (currentFlowId: string) => void; saveLoading: boolean; + setSaveLoading: (saveLoading: boolean) => void; isLoading: boolean; setIsLoading: (isLoading: boolean) => void; refreshFlows: () => Promise;