fix: session error after retries + refactor on interceptor component (#2803)

* 📝 (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>
This commit is contained in:
Cristhian Zanforlin Lousa 2024-07-18 15:40:11 -03:00 committed by GitHub
commit 011ebc2a5c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 56 additions and 32 deletions

View file

@ -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<string, string>), // 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;
}

View file

@ -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<FlowType[]> {
"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,

View file

@ -78,6 +78,7 @@ const useFlowsManagerStore = create<FlowsManagerStoreType>((set, get) => ({
},
currentFlow: undefined,
saveLoading: false,
setSaveLoading: (saveLoading: boolean) => set({ saveLoading }),
isLoading: true,
setIsLoading: (isLoading: boolean) => set({ isLoading }),
refreshFlows: () => {

View file

@ -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<void>;