From f38ba59bed717cca0be10ffe1551fbe5b76cb5ea Mon Sep 17 00:00:00 2001 From: Shuchang Zheng Date: Sat, 1 Feb 2025 03:06:09 +0800 Subject: [PATCH] Change workflow queries to use API to get global workflows (#1688) --- .../src/routes/workflows/WorkflowPage.tsx | 37 +++--------- .../src/routes/workflows/WorkflowTitle.tsx | 12 +++- .../workflows/editor/WorkflowHeader.tsx | 26 +++++---- .../hooks/useGlobalWorkflowsQuery.ts | 24 ++++++++ .../workflows/hooks/useWorkflowQuery.ts | 11 ++-- .../workflows/hooks/useWorkflowRunQuery.ts | 9 ++- .../hooks/useWorkflowRunTimelineQuery.ts | 9 ++- .../workflows/hooks/useWorkflowRunsQuery.ts | 57 +++++++++++++++++++ skyvern-frontend/src/util/env.ts | 19 ------- 9 files changed, 136 insertions(+), 68 deletions(-) create mode 100644 skyvern-frontend/src/routes/workflows/hooks/useGlobalWorkflowsQuery.ts create mode 100644 skyvern-frontend/src/routes/workflows/hooks/useWorkflowRunsQuery.ts diff --git a/skyvern-frontend/src/routes/workflows/WorkflowPage.tsx b/skyvern-frontend/src/routes/workflows/WorkflowPage.tsx index 006ab86905..c22cc2b393 100644 --- a/skyvern-frontend/src/routes/workflows/WorkflowPage.tsx +++ b/skyvern-frontend/src/routes/workflows/WorkflowPage.tsx @@ -1,6 +1,6 @@ -import { getClient } from "@/api/AxiosClient"; -import { Status, WorkflowRunApiResponse } from "@/api/types"; +import { Status } from "@/api/types"; import { StatusBadge } from "@/components/StatusBadge"; +import { StatusFilterDropdown } from "@/components/StatusFilterDropdown"; import { Button } from "@/components/ui/button"; import { Pagination, @@ -19,50 +19,31 @@ import { TableHeader, TableRow, } from "@/components/ui/table"; -import { useCredentialGetter } from "@/hooks/useCredentialGetter"; import { basicLocalTimeFormat, basicTimeFormat } from "@/util/timeFormat"; import { cn } from "@/util/utils"; import { Pencil2Icon, PlayIcon } from "@radix-ui/react-icons"; -import { useQuery } from "@tanstack/react-query"; +import { useState } from "react"; import { Link, useNavigate, useParams, useSearchParams, } from "react-router-dom"; -import { WorkflowActions } from "./WorkflowActions"; -import { useState } from "react"; -import { StatusFilterDropdown } from "@/components/StatusFilterDropdown"; import { useWorkflowQuery } from "./hooks/useWorkflowQuery"; -import { globalWorkflowIds } from "@/util/env"; +import { useWorkflowRunsQuery } from "./hooks/useWorkflowRunsQuery"; +import { WorkflowActions } from "./WorkflowActions"; function WorkflowPage() { - const credentialGetter = useCredentialGetter(); const { workflowPermanentId } = useParams(); const [searchParams, setSearchParams] = useSearchParams(); const page = searchParams.get("page") ? Number(searchParams.get("page")) : 1; const [statusFilters, setStatusFilters] = useState>([]); const navigate = useNavigate(); - const { data: workflowRuns, isLoading } = useQuery< - Array - >({ - queryKey: ["workflowRuns", workflowPermanentId, { statusFilters }, page], - queryFn: async () => { - const client = await getClient(credentialGetter); - const isGlobalWorkflow = - workflowPermanentId && globalWorkflowIds.includes(workflowPermanentId); - const params = new URLSearchParams(); - if (isGlobalWorkflow) { - params.set("template", "true"); - } - params.append("page", String(page)); - return client - .get(`/workflows/${workflowPermanentId}/runs`, { - params, - }) - .then((response) => response.data); - }, + const { data: workflowRuns, isLoading } = useWorkflowRunsQuery({ + workflowPermanentId, + statusFilters, + page, refetchOnMount: "always", }); diff --git a/skyvern-frontend/src/routes/workflows/WorkflowTitle.tsx b/skyvern-frontend/src/routes/workflows/WorkflowTitle.tsx index b2b870b930..69499eee8b 100644 --- a/skyvern-frontend/src/routes/workflows/WorkflowTitle.tsx +++ b/skyvern-frontend/src/routes/workflows/WorkflowTitle.tsx @@ -4,6 +4,7 @@ import { useCredentialGetter } from "@/hooks/useCredentialGetter"; import { useQuery } from "@tanstack/react-query"; import { WorkflowApiResponse } from "./types/workflowTypes"; import { apiPathPrefix } from "@/util/env"; +import { useGlobalWorkflowsQuery } from "./hooks/useGlobalWorkflowsQuery"; type Props = { workflowPermanentId: string; @@ -11,6 +12,7 @@ type Props = { function WorkflowTitle({ workflowPermanentId }: Props) { const credentialGetter = useCredentialGetter(); + const { data: globalWorkflows } = useGlobalWorkflowsQuery(); const { data: workflow, @@ -20,10 +22,18 @@ function WorkflowTitle({ workflowPermanentId }: Props) { queryKey: ["workflow", workflowPermanentId], queryFn: async () => { const client = await getClient(credentialGetter); + const isGlobalWorkflow = globalWorkflows?.some( + (workflow) => workflow.workflow_permanent_id === workflowPermanentId, + ); + const params = new URLSearchParams(); + if (isGlobalWorkflow) { + params.set("template", "true"); + } return client - .get(`${apiPathPrefix}/workflows/${workflowPermanentId}`) + .get(`${apiPathPrefix}/workflows/${workflowPermanentId}`, { params }) .then((response) => response.data); }, + enabled: !!globalWorkflows && !!workflowPermanentId, }); if (isLoading) { diff --git a/skyvern-frontend/src/routes/workflows/editor/WorkflowHeader.tsx b/skyvern-frontend/src/routes/workflows/editor/WorkflowHeader.tsx index 23b5a135d8..cfa453e373 100644 --- a/skyvern-frontend/src/routes/workflows/editor/WorkflowHeader.tsx +++ b/skyvern-frontend/src/routes/workflows/editor/WorkflowHeader.tsx @@ -1,19 +1,19 @@ import { SaveIcon } from "@/components/icons/SaveIcon"; import { Button } from "@/components/ui/button"; +import { + Tooltip, + TooltipContent, + TooltipProvider, + TooltipTrigger, +} from "@/components/ui/tooltip"; import { ChevronDownIcon, ChevronUpIcon, PlayIcon, } from "@radix-ui/react-icons"; import { useNavigate, useParams } from "react-router-dom"; +import { useGlobalWorkflowsQuery } from "../hooks/useGlobalWorkflowsQuery"; import { EditableNodeTitle } from "./nodes/components/EditableNodeTitle"; -import { - Tooltip, - TooltipContent, - TooltipProvider, - TooltipTrigger, -} from "@/components/ui/tooltip"; -import { globalWorkflowIds } from "@/util/env"; type Props = { title: string; @@ -31,11 +31,17 @@ function WorkflowHeader({ onTitleChange, }: Props) { const { workflowPermanentId } = useParams(); - const isGlobalWorkflow = Boolean( - workflowPermanentId && globalWorkflowIds.includes(workflowPermanentId), - ); + const { data: globalWorkflows } = useGlobalWorkflowsQuery(); const navigate = useNavigate(); + if (!globalWorkflows) { + return null; // this should be loaded already by some other components + } + + const isGlobalWorkflow = globalWorkflows.some( + (workflow) => workflow.workflow_permanent_id === workflowPermanentId, + ); + return (
diff --git a/skyvern-frontend/src/routes/workflows/hooks/useGlobalWorkflowsQuery.ts b/skyvern-frontend/src/routes/workflows/hooks/useGlobalWorkflowsQuery.ts new file mode 100644 index 0000000000..9c6f6b2192 --- /dev/null +++ b/skyvern-frontend/src/routes/workflows/hooks/useGlobalWorkflowsQuery.ts @@ -0,0 +1,24 @@ +import { getClient } from "@/api/AxiosClient"; +import { useCredentialGetter } from "@/hooks/useCredentialGetter"; +import { useQuery } from "@tanstack/react-query"; +import { WorkflowApiResponse } from "../types/workflowTypes"; + +function useGlobalWorkflowsQuery() { + const credentialGetter = useCredentialGetter(); + return useQuery({ + queryKey: ["globalWorkflows"], + queryFn: async () => { + const client = await getClient(credentialGetter); + const params = new URLSearchParams(); + params.set("template", "true"); + params.set("page_size", "100"); + return client + .get>("/workflows", { + params, + }) + .then((response) => response.data); + }, + }); +} + +export { useGlobalWorkflowsQuery }; diff --git a/skyvern-frontend/src/routes/workflows/hooks/useWorkflowQuery.ts b/skyvern-frontend/src/routes/workflows/hooks/useWorkflowQuery.ts index 35e8e242f1..cb850790fd 100644 --- a/skyvern-frontend/src/routes/workflows/hooks/useWorkflowQuery.ts +++ b/skyvern-frontend/src/routes/workflows/hooks/useWorkflowQuery.ts @@ -2,20 +2,22 @@ import { getClient } from "@/api/AxiosClient"; import { useCredentialGetter } from "@/hooks/useCredentialGetter"; import { useQuery } from "@tanstack/react-query"; import { WorkflowApiResponse } from "../types/workflowTypes"; -import { globalWorkflowIds } from "@/util/env"; - +import { useGlobalWorkflowsQuery } from "./useGlobalWorkflowsQuery"; type Props = { workflowPermanentId?: string; }; function useWorkflowQuery({ workflowPermanentId }: Props) { + const { data: globalWorkflows } = useGlobalWorkflowsQuery(); const credentialGetter = useCredentialGetter(); + return useQuery({ queryKey: ["workflow", workflowPermanentId], queryFn: async () => { const client = await getClient(credentialGetter); - const isGlobalWorkflow = - workflowPermanentId && globalWorkflowIds.includes(workflowPermanentId); + const isGlobalWorkflow = globalWorkflows?.some( + (workflow) => workflow.workflow_permanent_id === workflowPermanentId, + ); const params = new URLSearchParams(); if (isGlobalWorkflow) { params.set("template", "true"); @@ -24,6 +26,7 @@ function useWorkflowQuery({ workflowPermanentId }: Props) { .get(`/workflows/${workflowPermanentId}`, { params }) .then((response) => response.data); }, + enabled: !!globalWorkflows && !!workflowPermanentId, }); } diff --git a/skyvern-frontend/src/routes/workflows/hooks/useWorkflowRunQuery.ts b/skyvern-frontend/src/routes/workflows/hooks/useWorkflowRunQuery.ts index f7e6ae68e2..5fad7d9551 100644 --- a/skyvern-frontend/src/routes/workflows/hooks/useWorkflowRunQuery.ts +++ b/skyvern-frontend/src/routes/workflows/hooks/useWorkflowRunQuery.ts @@ -5,20 +5,22 @@ import { statusIsNotFinalized, statusIsRunningOrQueued, } from "@/routes/tasks/types"; -import { globalWorkflowIds } from "@/util/env"; import { keepPreviousData, useQuery } from "@tanstack/react-query"; import { useParams } from "react-router-dom"; +import { useGlobalWorkflowsQuery } from "./useGlobalWorkflowsQuery"; function useWorkflowRunQuery() { const { workflowRunId, workflowPermanentId } = useParams(); const credentialGetter = useCredentialGetter(); + const { data: globalWorkflows } = useGlobalWorkflowsQuery(); return useQuery({ queryKey: ["workflowRun", workflowPermanentId, workflowRunId], queryFn: async () => { const client = await getClient(credentialGetter); - const isGlobalWorkflow = - workflowPermanentId && globalWorkflowIds.includes(workflowPermanentId); + const isGlobalWorkflow = globalWorkflows?.some( + (workflow) => workflow.workflow_permanent_id === workflowPermanentId, + ); const params = new URLSearchParams(); if (isGlobalWorkflow) { params.set("template", "true"); @@ -51,6 +53,7 @@ function useWorkflowRunQuery() { } return statusIsRunningOrQueued(query.state.data); }, + enabled: !!globalWorkflows && !!workflowPermanentId && !!workflowRunId, }); } diff --git a/skyvern-frontend/src/routes/workflows/hooks/useWorkflowRunTimelineQuery.ts b/skyvern-frontend/src/routes/workflows/hooks/useWorkflowRunTimelineQuery.ts index 5bc01b110f..92d5d632c3 100644 --- a/skyvern-frontend/src/routes/workflows/hooks/useWorkflowRunTimelineQuery.ts +++ b/skyvern-frontend/src/routes/workflows/hooks/useWorkflowRunTimelineQuery.ts @@ -5,19 +5,21 @@ import { keepPreviousData, useQuery } from "@tanstack/react-query"; import { useParams } from "react-router-dom"; import { WorkflowRunTimelineItem } from "../types/workflowRunTypes"; import { useWorkflowRunQuery } from "./useWorkflowRunQuery"; -import { globalWorkflowIds } from "@/util/env"; +import { useGlobalWorkflowsQuery } from "./useGlobalWorkflowsQuery"; function useWorkflowRunTimelineQuery() { const { workflowRunId, workflowPermanentId } = useParams(); const credentialGetter = useCredentialGetter(); + const { data: globalWorkflows } = useGlobalWorkflowsQuery(); const { data: workflowRun } = useWorkflowRunQuery(); return useQuery>({ queryKey: ["workflowRunTimeline", workflowPermanentId, workflowRunId], queryFn: async () => { const client = await getClient(credentialGetter); - const isGlobalWorkflow = - workflowPermanentId && globalWorkflowIds.includes(workflowPermanentId); + const isGlobalWorkflow = globalWorkflows?.some( + (workflow) => workflow.workflow_permanent_id === workflowPermanentId, + ); const params = new URLSearchParams(); if (isGlobalWorkflow) { params.set("template", "true"); @@ -36,6 +38,7 @@ function useWorkflowRunTimelineQuery() { workflowRun && statusIsNotFinalized(workflowRun) ? "always" : false, refetchOnWindowFocus: workflowRun && statusIsNotFinalized(workflowRun) ? "always" : false, + enabled: !!globalWorkflows && !!workflowPermanentId && !!workflowRunId, }); } diff --git a/skyvern-frontend/src/routes/workflows/hooks/useWorkflowRunsQuery.ts b/skyvern-frontend/src/routes/workflows/hooks/useWorkflowRunsQuery.ts new file mode 100644 index 0000000000..36aeae63ca --- /dev/null +++ b/skyvern-frontend/src/routes/workflows/hooks/useWorkflowRunsQuery.ts @@ -0,0 +1,57 @@ +import { getClient } from "@/api/AxiosClient"; +import { Status, WorkflowRunApiResponse } from "@/api/types"; +import { useCredentialGetter } from "@/hooks/useCredentialGetter"; +import { useQuery } from "@tanstack/react-query"; +import { useGlobalWorkflowsQuery } from "./useGlobalWorkflowsQuery"; + +type QueryReturnType = Array; +type UseQueryOptions = Omit< + Parameters>[0], + "queryKey" | "queryFn" | "enabled" +>; + +type Props = { + workflowPermanentId?: string; + statusFilters?: Array; + page: number; +} & UseQueryOptions; + +function useWorkflowRunsQuery({ + workflowPermanentId, + statusFilters, + page, + ...queryOptions +}: Props) { + const { data: globalWorkflows } = useGlobalWorkflowsQuery(); + const credentialGetter = useCredentialGetter(); + + return useQuery>({ + queryKey: ["workflowRuns", { statusFilters }, workflowPermanentId], + queryFn: async () => { + const client = await getClient(credentialGetter); + const params = new URLSearchParams(); + const isGlobalWorkflow = globalWorkflows?.some( + (workflow) => workflow.workflow_permanent_id === workflowPermanentId, + ); + params.append("page", String(page)); + if (isGlobalWorkflow) { + params.append("template", "true"); + } + if (statusFilters) { + statusFilters.forEach((status) => { + params.append("status", status); + }); + } + + return client + .get(`/workflows/${workflowPermanentId}/runs`, { + params, + }) + .then((response) => response.data); + }, + enabled: !!workflowPermanentId && !!globalWorkflows, + ...queryOptions, + }); +} + +export { useWorkflowRunsQuery }; diff --git a/skyvern-frontend/src/util/env.ts b/skyvern-frontend/src/util/env.ts index 84133707bd..2aec461744 100644 --- a/skyvern-frontend/src/util/env.ts +++ b/skyvern-frontend/src/util/env.ts @@ -21,29 +21,10 @@ if (!artifactApiBaseUrl) { const apiPathPrefix = import.meta.env.VITE_API_PATH_PREFIX ?? ""; -function getGlobalWorkflowIds(): Array { - const globalWorkflowIds = import.meta.env.VITE_GLOBAL_WORKFLOW_IDS; - if (!globalWorkflowIds) { - return []; - } - try { - const globalWorkflowIdsAsAList = JSON.parse(globalWorkflowIds); - if (Array.isArray(globalWorkflowIdsAsAList)) { - return globalWorkflowIdsAsAList; - } - return []; - } catch { - return []; - } -} - -const globalWorkflowIds = getGlobalWorkflowIds(); - export { apiBaseUrl, environment, envCredential, artifactApiBaseUrl, apiPathPrefix, - globalWorkflowIds, };