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:
parent
226453a606
commit
b1a552fa9e
13 changed files with 1203 additions and 60 deletions
|
|
@ -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" });
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 = {}) {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
|
|
@ -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;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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 />}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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}>
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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 });
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
1081
src/frontend/tests/assets/outdated_flow.json
Normal file
1081
src/frontend/tests/assets/outdated_flow.json
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -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",
|
||||
});
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue