From 3a408c814161c417a407a59e0f28ba6e813c5328 Mon Sep 17 00:00:00 2001 From: Lucas Oliveira <62335616+lucaseduoli@users.noreply.github.com> Date: Wed, 21 Aug 2024 12:21:19 -0300 Subject: [PATCH] refactor: add useGetRefreshFlows mutation and update code to use it (#3475) * Added refresh flows mutation * Changed places that used refreshFlows to use the mutation * removed old refreshFlows * removed readFlowsFromDatabase api call * Removed unused API calls from API.ts * Removed getFlowFromDatabase call --- src/frontend/src/controllers/API/index.ts | 151 ------------------ .../queries/flows/use-get-refresh-flows.ts | 67 ++++++++ .../API/queries/folders/use-get-folders.ts | 6 +- .../src/hooks/flows/use-upload-flow.ts | 6 +- src/frontend/src/pages/FlowPage/index.tsx | 5 +- src/frontend/src/pages/ViewPage/index.tsx | 5 +- src/frontend/src/stores/flowsManagerStore.ts | 45 ------ .../src/types/zustand/flowsManager/index.ts | 1 - 8 files changed, 79 insertions(+), 207 deletions(-) create mode 100644 src/frontend/src/controllers/API/queries/flows/use-get-refresh-flows.ts diff --git a/src/frontend/src/controllers/API/index.ts b/src/frontend/src/controllers/API/index.ts index 5faea9edf..41b1f8331 100644 --- a/src/frontend/src/controllers/API/index.ts +++ b/src/frontend/src/controllers/API/index.ts @@ -55,157 +55,6 @@ export async function sendAll(data: sendAllProps) { return await api.post(`${BASE_URL_API}predict`, data); } -/** - * Fetches a list of JSON files from a GitHub repository and returns their contents as an array of FlowType objects. - * - * @returns {Promise} A promise that resolves to an array of FlowType objects. - */ -export async function getExamples(): Promise { - const url = - "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) => { - return file.name.endsWith(".json"); - }); - - const contentsPromises = jsonFiles.map(async (file: any) => { - const contentResponse = await api.get(file.download_url); - return contentResponse.data; - }); - - return await Promise.all(contentsPromises); -} - -/** - * Saves a new flow to the database. - * - * @param {FlowType} newFlow - The flow data to save. - * @returns {Promise} The saved flow data. - * @throws Will throw an error if saving fails. - */ -export async function saveFlowToDatabase(newFlow: { - name: string; - id: string; - data: ReactFlowJsonObject | null; - description: string; - style?: FlowStyleType; - is_component?: boolean; - folder_id?: string; - endpoint_name?: string; -}): Promise { - try { - const response = await api.post(`${BASE_URL_API}flows/`, { - name: newFlow.name, - data: newFlow.data, - description: newFlow.description, - is_component: newFlow.is_component, - folder_id: newFlow.folder_id === "" ? null : newFlow.folder_id, - endpoint_name: newFlow.endpoint_name, - }); - - if (response?.status !== 201) { - throw new Error(`HTTP error! status: ${response?.status}`); - } - return response?.data; - } catch (error) { - console.error(error); - throw error; - } -} - -/** - * Reads all flows from the database. - * - * @returns {Promise} The flows data. - * @throws Will throw an error if reading fails. - */ -export async function readFlowsFromDatabase() { - try { - const response = await api.get(`${BASE_URL_API}flows/`); - if (response && response?.status !== 200) { - throw new Error(`HTTP error! status: ${response?.status}`); - } - return response?.data; - } catch (error) { - console.error(error); - throw error; - } -} - -export async function uploadFlowsToDatabase(flows: FormData) { - try { - const response = await api.post(`${BASE_URL_API}flows/upload/`, flows); - - if (response?.status !== 201) { - throw new Error(`HTTP error! status: ${response?.status}`); - } - return response?.data; - } catch (error) { - console.error(error); - throw error; - } -} - -/** - * Deletes a flow from the database. - * - * @param {string} flowId - The ID of the flow to delete. - * @returns {Promise} The deleted flow data. - * @throws Will throw an error if deletion fails. - */ -export async function deleteFlowFromDatabase(flowId: string) { - try { - const response = await api.delete(`${BASE_URL_API}flows/${flowId}`); - if (response.status !== 200) { - throw new Error(`HTTP error! status: ${response.status}`); - } - return response?.data; - } catch (error) { - console.error(error); - throw error; - } -} - -/** - * Fetches a flow from the database by ID. - * - * @param {number} flowId - The ID of the flow to fetch. - * @returns {Promise} The flow data. - * @throws Will throw an error if fetching fails. - */ -export async function getFlowFromDatabase(flowId: number) { - try { - const response = await api.get(`${BASE_URL_API}flows/${flowId}`); - if (response && response?.status !== 200) { - throw new Error(`HTTP error! status: ${response?.status}`); - } - return response?.data; - } catch (error) { - console.error(error); - throw error; - } -} - -/** - * Fetches flow styles from the database. - * - * @returns {Promise} The flow styles data. - * @throws Will throw an error if fetching fails. - */ -export async function getFlowStylesFromDatabase() { - try { - const response = await api.get(`${BASE_URL_API}flow_styles/`); - if (response.status !== 200) { - throw new Error(`HTTP error! status: ${response.status}`); - } - return response?.data; - } catch (error) { - console.error(error); - throw error; - } -} - /** * Fetches the version of the API. * diff --git a/src/frontend/src/controllers/API/queries/flows/use-get-refresh-flows.ts b/src/frontend/src/controllers/API/queries/flows/use-get-refresh-flows.ts new file mode 100644 index 000000000..c9c67443a --- /dev/null +++ b/src/frontend/src/controllers/API/queries/flows/use-get-refresh-flows.ts @@ -0,0 +1,67 @@ +import useAlertStore from "@/stores/alertStore"; +import useFlowsManagerStore from "@/stores/flowsManagerStore"; +import { useFolderStore } from "@/stores/foldersStore"; +import { useTypesStore } from "@/stores/typesStore"; +import { useMutationFunctionType } from "@/types/api"; +import { FlowType } from "@/types/flow"; +import { + extractFieldsFromComponenents, + processFlows, +} from "@/utils/reactflowUtils"; +import { UseMutationResult } from "@tanstack/react-query"; +import { api } from "../../api"; +import { getURL } from "../../helpers/constants"; +import { UseRequestProcessor } from "../../services/request-processor"; + +export const useGetRefreshFlows: useMutationFunctionType< + undefined, + undefined +> = (options?) => { + const { mutate } = UseRequestProcessor(); + + const setExamples = useFlowsManagerStore((state) => state.setExamples); + const setFlows = useFlowsManagerStore((state) => state.setFlows); + const setErrorData = useAlertStore((state) => state.setErrorData); + const starterProjectId = useFolderStore((state) => state.starterProjectId); + + const getRefreshFlowsFn = async (): Promise => { + try { + } catch (e) { + setErrorData({ + title: "Could not load flows from database", + }); + throw e; + } + const response = await api.get(`${getURL("FLOWS")}/`); + const dbData = response.data; + if (dbData) { + const { data, flows } = processFlows(dbData); + const examples = flows.filter( + (flow) => flow.folder_id === starterProjectId, + ); + setExamples(examples); + + const flowsWithoutStarterFolder = flows.filter( + (flow) => flow.folder_id !== starterProjectId, + ); + + setFlows(flowsWithoutStarterFolder); + useTypesStore.setState((state) => ({ + data: { ...state.data, ["saved_components"]: data }, + ComponentFields: extractFieldsFromComponenents({ + ...state.data, + ["saved_components"]: data, + }), + })); + return; + } + }; + + const mutation: UseMutationResult = mutate( + ["useGetRefreshFlows"], + getRefreshFlowsFn, + options, + ); + + return mutation; +}; diff --git a/src/frontend/src/controllers/API/queries/folders/use-get-folders.ts b/src/frontend/src/controllers/API/queries/folders/use-get-folders.ts index 51059a3be..1f63badbf 100644 --- a/src/frontend/src/controllers/API/queries/folders/use-get-folders.ts +++ b/src/frontend/src/controllers/API/queries/folders/use-get-folders.ts @@ -1,18 +1,19 @@ import { DEFAULT_FOLDER, STARTER_FOLDER_NAME } from "@/constants/constants"; import { FolderType } from "@/pages/MainPage/entities"; -import useFlowsManagerStore from "@/stores/flowsManagerStore"; import { useFolderStore } from "@/stores/foldersStore"; import { useTypesStore } from "@/stores/typesStore"; import { useQueryFunctionType } from "@/types/api"; import { api } from "../../api"; import { getURL } from "../../helpers/constants"; import { UseRequestProcessor } from "../../services/request-processor"; +import { useGetRefreshFlows } from "../flows/use-get-refresh-flows"; export const useGetFoldersQuery: useQueryFunctionType< undefined, FolderType[] > = (options) => { const { query } = UseRequestProcessor(); + const { mutateAsync: refreshFlows } = useGetRefreshFlows(); const setStarterProjectId = useFolderStore( (state) => state.setStarterProjectId, @@ -36,10 +37,9 @@ export const useGetFoldersQuery: useQueryFunctionType< const myCollectionId = data?.find((f) => f.name === DEFAULT_FOLDER)?.id; setMyCollectionId(myCollectionId); - const { refreshFlows } = useFlowsManagerStore.getState(); const { getTypes } = useTypesStore.getState(); - await refreshFlows(); + await refreshFlows(undefined); await getTypes(); return foldersWithoutStarterProjects; diff --git a/src/frontend/src/hooks/flows/use-upload-flow.ts b/src/frontend/src/hooks/flows/use-upload-flow.ts index 7d225e20c..609cb3e32 100644 --- a/src/frontend/src/hooks/flows/use-upload-flow.ts +++ b/src/frontend/src/hooks/flows/use-upload-flow.ts @@ -1,7 +1,7 @@ +import { useGetRefreshFlows } from "@/controllers/API/queries/flows/use-get-refresh-flows"; import { createFileUpload } from "@/helpers/create-file-upload"; import { getObjectsFromFilelist } from "@/helpers/get-objects-from-filelist"; import useFlowStore from "@/stores/flowStore"; -import useFlowsManagerStore from "@/stores/flowsManagerStore"; import { FlowType } from "@/types/flow"; import { processDataFromFlow } from "@/utils/reactflowUtils"; import useAddFlow from "./use-add-flow"; @@ -9,7 +9,7 @@ import useAddFlow from "./use-add-flow"; const useUploadFlow = () => { const addFlow = useAddFlow(); const paste = useFlowStore((state) => state.paste); - const refreshFlows = useFlowsManagerStore((state) => state.refreshFlows); + const { mutate: refreshFlows } = useGetRefreshFlows(); const getFlowsFromFiles = async ({ files, @@ -90,7 +90,7 @@ const useUploadFlow = () => { throw new Error("Invalid flow data"); } } - refreshFlows(); + refreshFlows(undefined); } } catch (e) { throw e; diff --git a/src/frontend/src/pages/FlowPage/index.tsx b/src/frontend/src/pages/FlowPage/index.tsx index bc9d711dd..ccfe33f29 100644 --- a/src/frontend/src/pages/FlowPage/index.tsx +++ b/src/frontend/src/pages/FlowPage/index.tsx @@ -1,4 +1,5 @@ import FeatureFlags from "@/../feature-config.json"; +import { useGetRefreshFlows } from "@/controllers/API/queries/flows/use-get-refresh-flows"; import { useGetGlobalVariables } from "@/controllers/API/queries/variables"; import useSaveFlow from "@/hooks/flows/use-save-flow"; import { SaveChangesModal } from "@/modals/saveChangesModal"; @@ -33,7 +34,7 @@ export default function FlowPage({ view }: { view?: boolean }): JSX.Element { const flows = useFlowsManagerStore((state) => state.flows); const currentFlowId = useFlowsManagerStore((state) => state.currentFlowId); - const refreshFlows = useFlowsManagerStore((state) => state.refreshFlows); + const { mutateAsync: refreshFlows } = useGetRefreshFlows(); const setIsLoading = useFlowsManagerStore((state) => state.setIsLoading); const getTypes = useTypesStore((state) => state.getTypes); @@ -74,7 +75,7 @@ export default function FlowPage({ view }: { view?: boolean }): JSX.Element { setCurrentFlow(isAnExistingFlow); } else if (!flows) { setIsLoading(true); - await refreshFlows(); + await refreshFlows(undefined); await getTypes(); setIsLoading(false); } diff --git a/src/frontend/src/pages/ViewPage/index.tsx b/src/frontend/src/pages/ViewPage/index.tsx index 1602122c3..1e7ad5b67 100644 --- a/src/frontend/src/pages/ViewPage/index.tsx +++ b/src/frontend/src/pages/ViewPage/index.tsx @@ -1,3 +1,4 @@ +import { useGetRefreshFlows } from "@/controllers/API/queries/flows/use-get-refresh-flows"; import { useGetGlobalVariables } from "@/controllers/API/queries/variables"; import useFlowStore from "@/stores/flowStore"; import { useTypesStore } from "@/stores/typesStore"; @@ -16,7 +17,7 @@ export default function ViewPage() { const flows = useFlowsManagerStore((state) => state.flows); const currentFlowId = useFlowsManagerStore((state) => state.currentFlowId); - const refreshFlows = useFlowsManagerStore((state) => state.refreshFlows); + const { mutateAsync: refreshFlows } = useGetRefreshFlows(); const setIsLoading = useFlowsManagerStore((state) => state.setIsLoading); const getTypes = useTypesStore((state) => state.getTypes); @@ -34,7 +35,7 @@ export default function ViewPage() { setCurrentFlow(isAnExistingFlow); } else if (!flows) { setIsLoading(true); - await refreshFlows(); + await refreshFlows(undefined); await getTypes(); setIsLoading(false); } diff --git a/src/frontend/src/stores/flowsManagerStore.ts b/src/frontend/src/stores/flowsManagerStore.ts index d33de6192..f18859e3b 100644 --- a/src/frontend/src/stores/flowsManagerStore.ts +++ b/src/frontend/src/stores/flowsManagerStore.ts @@ -1,19 +1,11 @@ import { cloneDeep } from "lodash"; import { create } from "zustand"; -import { readFlowsFromDatabase } from "../controllers/API"; import { FlowType } from "../types/flow"; import { FlowsManagerStoreType, UseUndoRedoOptions, } from "../types/zustand/flowsManager"; -import { - extractFieldsFromComponenents, - processFlows, -} from "../utils/reactflowUtils"; -import useAlertStore from "./alertStore"; import useFlowStore from "./flowStore"; -import { useFolderStore } from "./foldersStore"; -import { useTypesStore } from "./typesStore"; const defaultOptions: UseUndoRedoOptions = { maxHistorySize: 100, @@ -53,43 +45,6 @@ const useFlowsManagerStore = create((set, get) => ({ setSaveLoading: (saveLoading: boolean) => set({ saveLoading }), isLoading: false, setIsLoading: (isLoading: boolean) => set({ isLoading }), - refreshFlows: () => { - return new Promise((resolve, reject) => { - const starterFolderId = useFolderStore.getState().starterProjectId; - - readFlowsFromDatabase() - .then((dbData) => { - if (dbData) { - const { data, flows } = processFlows(dbData); - const examples = flows.filter( - (flow) => flow.folder_id === starterFolderId, - ); - get().setExamples(examples); - - const flowsWithoutStarterFolder = flows.filter( - (flow) => flow.folder_id !== starterFolderId, - ); - - get().setFlows(flowsWithoutStarterFolder); - useTypesStore.setState((state) => ({ - data: { ...state.data, ["saved_components"]: data }, - ComponentFields: extractFieldsFromComponenents({ - ...state.data, - ["saved_components"]: data, - }), - })); - resolve(); - } - }) - .catch((e) => { - set({ isLoading: false }); - useAlertStore.getState().setErrorData({ - title: "Could not load flows from database", - }); - reject(e); - }); - }); - }, takeSnapshot: () => { const currentFlowId = get().currentFlowId; // push the current graph to the past state diff --git a/src/frontend/src/types/zustand/flowsManager/index.ts b/src/frontend/src/types/zustand/flowsManager/index.ts index 397958feb..67cd4ac9f 100644 --- a/src/frontend/src/types/zustand/flowsManager/index.ts +++ b/src/frontend/src/types/zustand/flowsManager/index.ts @@ -12,7 +12,6 @@ export type FlowsManagerStoreType = { setSaveLoading: (saveLoading: boolean) => void; isLoading: boolean; setIsLoading: (isLoading: boolean) => void; - refreshFlows: () => Promise; undo: () => void; redo: () => void; takeSnapshot: () => void;