fix: refactor get all to fix types not being fetched before checking for outdated components (#4762)

* Added use get types to fetch types from backend using tanstack

* Updated typesStore to use new set types

* Updated project to not use getTypes anymore

* deleted unused getTypes

* add tests

* [autofix.ci] apply automated fixes

* fix tests

---------

Co-authored-by: cristhianzl <cristhian.lousa@gmail.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: Gabriel Luiz Freitas Almeida <gabriel@langflow.org>
This commit is contained in:
Lucas Oliveira 2024-11-21 22:01:37 -03:00 committed by GitHub
commit b1a552fa9e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 1203 additions and 60 deletions

View file

@ -28,8 +28,8 @@ import useAlertStore from "@/stores/alertStore";
import useFlowsManagerStore from "@/stores/flowsManagerStore";
import useFlowStore from "@/stores/flowStore";
import { useShortcutsStore } from "@/stores/shortcuts";
import { useTypesStore } from "@/stores/typesStore";
import { cn } from "@/utils/utils";
import { useQueryClient } from "@tanstack/react-query";
export const MenuBar = ({}: {}): JSX.Element => {
const shortcuts = useShortcutsStore((state) => state.shortcuts);
@ -44,8 +44,8 @@ export const MenuBar = ({}: {}): JSX.Element => {
const uploadFlow = useUploadFlow();
const navigate = useCustomNavigate();
const isBuilding = useFlowStore((state) => state.isBuilding);
const getTypes = useTypesStore((state) => state.getTypes);
const saveFlow = useSaveFlow();
const queryClient = useQueryClient();
const autoSaving = useFlowsManagerStore((state) => state.autoSaving);
const currentFlow = useFlowStore((state) => state.currentFlow);
const currentSavedFlow = useFlowsManagerStore((state) => state.currentFlow);
@ -75,7 +75,7 @@ export const MenuBar = ({}: {}): JSX.Element => {
}
function handleReloadComponents() {
getTypes(true).then(() => {
queryClient.prefetchQuery({ queryKey: ["useGetTypes"] }).then(() => {
setSuccessData({ title: "Components reloaded successfully" });
});
}

View file

@ -22,6 +22,7 @@ export const URLs = {
CONFIG: `config`,
STARTER_PROJECTS: `starter-projects`,
SIDEBAR_CATEGORIES: `sidebar_categories`,
ALL: `all`,
} as const;
export function getURL(key: keyof typeof URLs, params: any = {}) {

View file

@ -0,0 +1,37 @@
import useFlowsManagerStore from "@/stores/flowsManagerStore";
import { useTypesStore } from "@/stores/typesStore";
import { APIObjectType, useQueryFunctionType } from "../../../../types/api";
import { api } from "../../api";
import { getURL } from "../../helpers/constants";
import { UseRequestProcessor } from "../../services/request-processor";
export const useGetTypes: useQueryFunctionType<undefined> = (options) => {
const { query } = UseRequestProcessor();
const setLoading = useFlowsManagerStore((state) => state.setIsLoading);
const setTypes = useTypesStore((state) => state.setTypes);
const getTypesFn = async () => {
try {
const response = await api.get<APIObjectType>(
`${getURL("ALL")}?force_refresh=true`,
);
const data = response?.data;
setTypes(data);
return data;
} catch {
(error) => {
console.error("An error has occurred while fetching types.");
console.log(error);
setLoading(false);
throw error;
};
}
};
const queryResult = query(["useGetTypes"], getTypesFn, {
refetchOnWindowFocus: false,
...options,
});
return queryResult;
};

View file

@ -1,4 +1,4 @@
import { DEFAULT_FOLDER, STARTER_FOLDER_NAME } from "@/constants/constants";
import { DEFAULT_FOLDER } from "@/constants/constants";
import { FolderType } from "@/pages/MainPage/entities";
import useAuthStore from "@/stores/authStore";
import { useFolderStore } from "@/stores/foldersStore";
@ -29,10 +29,9 @@ export const useGetFoldersQuery: useQueryFunctionType<
const myCollectionId = data?.find((f) => f.name === DEFAULT_FOLDER)?.id;
setMyCollectionId(myCollectionId);
setFolders(data);
const { getTypes, types } = useTypesStore.getState();
const { types } = useTypesStore.getState();
await refreshFlows({ get_all: true, header_flows: true });
if (!types || Object.keys(types).length === 0) await getTypes();
return data;
};

View file

@ -1,6 +1,7 @@
import { useGetAutoLogin } from "@/controllers/API/queries/auth";
import { useGetConfig } from "@/controllers/API/queries/config/use-get-config";
import { useGetBasicExamplesQuery } from "@/controllers/API/queries/flows/use-get-basic-examples";
import { useGetTypes } from "@/controllers/API/queries/flows/use-get-types";
import { useGetFoldersQuery } from "@/controllers/API/queries/folders/use-get-folders";
import { useGetTagsQuery } from "@/controllers/API/queries/store";
import { useGetGlobalVariables } from "@/controllers/API/queries/variables";
@ -23,17 +24,16 @@ export function AppInitPage() {
const { isFetched } = useGetAutoLogin({ enabled: isLoaded });
useGetVersionQuery({ enabled: isFetched });
useGetConfig({ enabled: isFetched });
useGetGlobalVariables({ enabled: isFetched });
useGetBasicExamplesQuery({ enabled: isFetched });
useGetTagsQuery({ enabled: isFetched });
const { refetch: refetchFolders } = useGetFoldersQuery({
enabled: isFetched,
});
const { isFetched: typesLoaded } = useGetTypes({ enabled: isFetched });
useGetGlobalVariables({ enabled: typesLoaded });
useGetBasicExamplesQuery({ enabled: typesLoaded });
useGetTagsQuery({ enabled: typesLoaded });
useGetFoldersQuery({ enabled: typesLoaded });
useEffect(() => {
if (isFetched) {
refreshStars();
refetchFolders();
}
}, [isFetched]);
@ -49,11 +49,11 @@ export function AppInitPage() {
//need parent component with width and height
<>
{isLoaded ? (
(isLoading || !isFetched) && <LoadingPage overlay />
(isLoading || !isFetched || !typesLoaded) && <LoadingPage overlay />
) : (
<CustomLoadingPage />
)}
{isFetched && <Outlet />}
{isFetched && typesLoaded && <Outlet />}
</>
);
}

View file

@ -42,7 +42,6 @@ export default function FlowPage({ view }: { view?: boolean }): JSX.Element {
const { mutateAsync: refreshFlows } = useGetRefreshFlows();
const setIsLoading = useFlowsManagerStore((state) => state.setIsLoading);
const getTypes = useTypesStore((state) => state.getTypes);
const types = useTypesStore((state) => state.types);
const updatedAt = currentSavedFlow?.updated_at;
@ -118,7 +117,6 @@ export default function FlowPage({ view }: { view?: boolean }): JSX.Element {
} else if (!flows) {
setIsLoading(true);
await refreshFlows({ get_all: true, header_flows: true });
if (!types || Object.keys(types).length === 0) await getTypes();
setIsLoading(false);
}
};

View file

@ -114,6 +114,7 @@ const ListComponent = ({ flowData }: { flowData: FlowType }) => {
className={`my-2 flex flex-row bg-background ${
isComponent ? "cursor-default" : "cursor-pointer"
} group justify-between rounded-lg border border-border p-4 hover:border-placeholder-foreground hover:shadow-sm`}
data-testid="list-card"
>
{/* left side */}
<div

View file

@ -1,21 +1,17 @@
import IconComponent from "../../../../components/genericIconComponent";
import { Button } from "../../../../components/ui/button";
import TableAutoCellRender from "@/components/tableComponent/components/tableAutoCellRender";
import {
useDeleteGlobalVariables,
useGetGlobalVariables,
} from "@/controllers/API/queries/variables";
import { useTypesStore } from "@/stores/typesStore";
import { GlobalVariable } from "@/types/global_variables";
import {
ColDef,
ColGroupDef,
RowClickedEvent,
RowDoubleClickedEvent,
SelectionChangedEvent,
} from "ag-grid-community";
import { useEffect, useRef, useState } from "react";
import { useRef, useState } from "react";
import GlobalVariableModal from "../../../../components/GlobalVariableModal/GlobalVariableModal";
import Dropdown from "../../../../components/dropdownComponent";
import ForwardedIconComponent from "../../../../components/genericIconComponent";
@ -27,7 +23,6 @@ export default function GlobalVariablesPage() {
const setErrorData = useAlertStore((state) => state.setErrorData);
const [openModal, setOpenModal] = useState(false);
const initialData = useRef<GlobalVariable | undefined>(undefined);
const getTypes = useTypesStore((state) => state.getTypes);
const BadgeRenderer = (props) => {
return props.value !== "" ? (
<div>
@ -40,11 +35,6 @@ export default function GlobalVariablesPage() {
);
};
useEffect(() => {
//get the components to build the Aplly To Fields dropdown
getTypes(true);
}, []);
const DropdownEditor = ({ options, value, onValueChange }) => {
return (
<Dropdown options={options} value={value} onSelect={onValueChange}>

View file

@ -16,7 +16,6 @@ export default function ViewPage() {
const currentFlowId = useFlowsManagerStore((state) => state.currentFlowId);
const { mutateAsync: refreshFlows } = useGetRefreshFlows();
const setIsLoading = useFlowsManagerStore((state) => state.setIsLoading);
const getTypes = useTypesStore((state) => state.getTypes);
const types = useTypesStore((state) => state.types);
// Set flow tab id
@ -34,7 +33,6 @@ export default function ViewPage() {
} else if (!flows) {
setIsLoading(true);
await refreshFlows({ get_all: true, header_flows: true });
if (!types || Object.keys(types).length === 0) await getTypes();
setIsLoading(false);
}
};

View file

@ -1,5 +1,4 @@
import { create } from "zustand";
import { getAll } from "../controllers/API";
import { APIDataType } from "../types/api";
import { TypesStoreType } from "../types/zustand/types";
import {
@ -7,7 +6,6 @@ import {
templatesGenerator,
typesGenerator,
} from "../utils/reactflowUtils";
import useFlowsManagerStore from "./flowsManagerStore";
export const useTypesStore = create<TypesStoreType>((set, get) => ({
ComponentFields: new Set(),
@ -20,33 +18,16 @@ export const useTypesStore = create<TypesStoreType>((set, get) => ({
types: {},
templates: {},
data: {},
getTypes: (force_refresh: boolean = true) => {
return new Promise<void>(async (resolve, reject) => {
const setLoading = useFlowsManagerStore.getState().setIsLoading;
getAll(force_refresh)
.then((response) => {
const data = response?.data;
set((old) => ({
types: typesGenerator(data),
data: { ...old.data, ...data },
ComponentFields: extractFieldsFromComponenents({
...old.data,
...data,
}),
templates: templatesGenerator(data),
}));
resolve();
})
.catch((error) => {
console.error("An error has occurred while fetching types.");
console.log(error);
setLoading(false);
reject();
});
});
},
setTypes: (newState: {}) => {
set({ types: newState });
setTypes: (data: APIDataType) => {
set((old) => ({
types: typesGenerator(data),
data: { ...old.data, ...data },
ComponentFields: extractFieldsFromComponenents({
...old.data,
...data,
}),
templates: templatesGenerator(data),
}));
},
setTemplates: (newState: {}) => {
set({ templates: newState });

View file

@ -7,7 +7,6 @@ export type TypesStoreType = {
setTemplates: (newState: {}) => void;
data: APIDataType;
setData: (newState: {}) => void;
getTypes: (force_refresh?: boolean) => Promise<void>;
ComponentFields: Set<string>;
setComponentFields: (fields: Set<string>) => void;
addComponentField: (field: string) => void;

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,58 @@
import { test } from "@playwright/test";
import { readFileSync } from "fs";
test("user must be able outdated message on error", async ({ page }) => {
await page.goto("/");
let modalCount = 0;
try {
const modalTitleElement = await page?.getByTestId("modal-title");
if (modalTitleElement) {
modalCount = await modalTitleElement.count();
}
} catch (error) {
modalCount = 0;
}
while (modalCount === 0) {
await page.getByText("New Flow", { exact: true }).click();
await page.waitForTimeout(3000);
modalCount = await page.getByTestId("modal-title")?.count();
}
await page.locator("span").filter({ hasText: "Close" }).first().click();
await page.locator("span").filter({ hasText: "My Collection" }).isVisible();
// Read your file into a buffer.
const jsonContent = readFileSync("tests/assets/outdated_flow.json", "utf-8");
// Create the DataTransfer and File
const dataTransfer = await page.evaluateHandle((data) => {
const dt = new DataTransfer();
// Convert the buffer to a hex array
const file = new File([data], "outdated_flow.json", {
type: "application/json",
});
dt.items.add(file);
return dt;
}, jsonContent);
// Now dispatch
await page.getByTestId("cards-wrapper").dispatchEvent("drop", {
dataTransfer,
});
await page.waitForTimeout(3000);
await page.getByTestId("list-card").first().click();
await page
.getByTestId("popover-anchor-input-api_key")
.fill("this is a test to crash");
await page.getByTestId("button_run_chat output").click();
await page.waitForSelector("text=there are outdated components in the flow", {
timeout: 30000,
state: "visible",
});
});