From 38eb14ad42f486aa5081f1e793ddfc8341b0dcd1 Mon Sep 17 00:00:00 2001 From: Shuchang Zheng Date: Thu, 6 Mar 2025 12:12:13 -0800 Subject: [PATCH] Add new workflow parameter type in UI, credential (#1897) --- .../workflows/WorkflowParameterInput.tsx | 11 +++ .../CredentialParameterSourceSelector.tsx | 69 +++++++++++++++++++ ...r.tsx => LoginBlockCredentialSelector.tsx} | 4 +- .../editor/nodes/LoginNode/LoginNode.tsx | 5 +- .../panels/WorkflowParameterAddPanel.tsx | 35 +++++----- .../panels/WorkflowParameterEditPanel.tsx | 5 +- .../workflows/editor/workflowEditorUtils.ts | 3 + .../routes/workflows/types/workflowTypes.ts | 1 + 8 files changed, 109 insertions(+), 24 deletions(-) create mode 100644 skyvern-frontend/src/routes/workflows/components/CredentialParameterSourceSelector.tsx rename skyvern-frontend/src/routes/workflows/editor/nodes/LoginNode/{CredentialParameterSelector.tsx => LoginBlockCredentialSelector.tsx} (96%) diff --git a/skyvern-frontend/src/routes/workflows/WorkflowParameterInput.tsx b/skyvern-frontend/src/routes/workflows/WorkflowParameterInput.tsx index 629401ad..e9a81c47 100644 --- a/skyvern-frontend/src/routes/workflows/WorkflowParameterInput.tsx +++ b/skyvern-frontend/src/routes/workflows/WorkflowParameterInput.tsx @@ -5,6 +5,7 @@ import { CodeEditor } from "./components/CodeEditor"; import { AutoResizingTextarea } from "@/components/AutoResizingTextarea/AutoResizingTextarea"; import { Label } from "@/components/ui/label"; import { WorkflowParameterValueType } from "./types/workflowTypes"; +import { CredentialSelector } from "./components/CredentialSelector"; type Props = { type: WorkflowParameterValueType; @@ -80,6 +81,16 @@ function WorkflowParameterInput({ type, value, onChange }: Props) { /> ); } + + if (type === "credential_id") { + const credentialId = value as string | null; + return ( + onChange(value)} + /> + ); + } } export { WorkflowParameterInput }; diff --git a/skyvern-frontend/src/routes/workflows/components/CredentialParameterSourceSelector.tsx b/skyvern-frontend/src/routes/workflows/components/CredentialParameterSourceSelector.tsx new file mode 100644 index 00000000..dcb5eb65 --- /dev/null +++ b/skyvern-frontend/src/routes/workflows/components/CredentialParameterSourceSelector.tsx @@ -0,0 +1,69 @@ +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@/components/ui/select"; +import { Skeleton } from "@/components/ui/skeleton"; +import { useCredentialsQuery } from "../hooks/useCredentialsQuery"; +import { useWorkflowParametersState } from "../editor/useWorkflowParametersState"; +import { WorkflowParameterValueType } from "../types/workflowTypes"; + +type Props = { + value: string; + onChange: (value: string) => void; +}; + +function CredentialParameterSourceSelector({ value, onChange }: Props) { + const { data: credentials, isFetching } = useCredentialsQuery(); + const [workflowParameters] = useWorkflowParametersState(); + const workflowParametersOfTypeCredentialId = workflowParameters.filter( + (parameter) => + parameter.parameterType === "workflow" && + parameter.dataType === WorkflowParameterValueType.CredentialId, + ); + + if (isFetching) { + return ; + } + + if (!credentials) { + return null; + } + + const credentialOptions = credentials?.map((credential) => ({ + label: credential.name, + value: credential.credential_id, + type: "credential", + })); + + const workflowParameterOptionsOfTypeCredentialId = + workflowParametersOfTypeCredentialId.map((parameter) => ({ + label: parameter.key, + value: parameter.key, + type: "parameter", + })); + + const options = [ + ...credentialOptions, + ...workflowParameterOptionsOfTypeCredentialId, + ]; + + return ( + + ); +} + +export { CredentialParameterSourceSelector }; diff --git a/skyvern-frontend/src/routes/workflows/editor/nodes/LoginNode/CredentialParameterSelector.tsx b/skyvern-frontend/src/routes/workflows/editor/nodes/LoginNode/LoginBlockCredentialSelector.tsx similarity index 96% rename from skyvern-frontend/src/routes/workflows/editor/nodes/LoginNode/CredentialParameterSelector.tsx rename to skyvern-frontend/src/routes/workflows/editor/nodes/LoginNode/LoginBlockCredentialSelector.tsx index eb9dca24..79b3e823 100644 --- a/skyvern-frontend/src/routes/workflows/editor/nodes/LoginNode/CredentialParameterSelector.tsx +++ b/skyvern-frontend/src/routes/workflows/editor/nodes/LoginNode/LoginBlockCredentialSelector.tsx @@ -16,7 +16,7 @@ type Props = { onChange?: (value: string) => void; }; -function CredentialParameterSelector({ value, onChange }: Props) { +function LoginBlockCredentialSelector({ value, onChange }: Props) { const [workflowParameters, setWorkflowParameters] = useWorkflowParametersState(); const credentialParameters = workflowParameters.filter( @@ -104,4 +104,4 @@ function CredentialParameterSelector({ value, onChange }: Props) { ); } -export { CredentialParameterSelector }; +export { LoginBlockCredentialSelector }; diff --git a/skyvern-frontend/src/routes/workflows/editor/nodes/LoginNode/LoginNode.tsx b/skyvern-frontend/src/routes/workflows/editor/nodes/LoginNode/LoginNode.tsx index 4713c514..bf78f37a 100644 --- a/skyvern-frontend/src/routes/workflows/editor/nodes/LoginNode/LoginNode.tsx +++ b/skyvern-frontend/src/routes/workflows/editor/nodes/LoginNode/LoginNode.tsx @@ -29,13 +29,12 @@ import { EditableNodeTitle } from "../components/EditableNodeTitle"; import { NodeActionMenu } from "../NodeActionMenu"; import { errorMappingExampleValue } from "../types"; import { WorkflowBlockIcon } from "../WorkflowBlockIcon"; -import { CredentialParameterSelector } from "./CredentialParameterSelector"; import type { LoginNode } from "./types"; import { ParametersMultiSelect } from "../TaskNode/ParametersMultiSelect"; import { AppNode } from ".."; import { getAvailableOutputParameterKeys } from "../../workflowEditorUtils"; import { useIsFirstBlockInWorkflow } from "../../hooks/useIsFirstNodeInWorkflow"; - +import { LoginBlockCredentialSelector } from "./LoginBlockCredentialSelector"; function LoginNode({ id, data }: NodeProps) { const { updateNodeData } = useReactFlow(); const { editable } = data; @@ -152,7 +151,7 @@ function LoginNode({ id, data }: NodeProps) {
- 0 ? data.parameterKeys[0] diff --git a/skyvern-frontend/src/routes/workflows/editor/panels/WorkflowParameterAddPanel.tsx b/skyvern-frontend/src/routes/workflows/editor/panels/WorkflowParameterAddPanel.tsx index 219526fe..ff889f5a 100644 --- a/skyvern-frontend/src/routes/workflows/editor/panels/WorkflowParameterAddPanel.tsx +++ b/skyvern-frontend/src/routes/workflows/editor/panels/WorkflowParameterAddPanel.tsx @@ -1,11 +1,9 @@ -import { Cross2Icon } from "@radix-ui/react-icons"; -import { Label } from "@/components/ui/label"; +import { SwitchBar } from "@/components/SwitchBar"; +import { Button } from "@/components/ui/button"; +import { Checkbox } from "@/components/ui/checkbox"; import { Input } from "@/components/ui/input"; -import { useContext, useState } from "react"; -import { - WorkflowEditorParameterType, - WorkflowParameterValueType, -} from "../../types/workflowTypes"; +import { Label } from "@/components/ui/label"; +import { ScrollArea, ScrollAreaViewport } from "@/components/ui/scroll-area"; import { Select, SelectContent, @@ -14,17 +12,19 @@ import { SelectTrigger, SelectValue, } from "@/components/ui/select"; -import { Button } from "@/components/ui/button"; -import { ParametersState } from "../types"; -import { WorkflowParameterInput } from "../../WorkflowParameterInput"; -import { Checkbox } from "@/components/ui/checkbox"; -import { getDefaultValueForParameterType } from "../workflowEditorUtils"; import { toast } from "@/components/ui/use-toast"; -import { SourceParameterKeySelector } from "../../components/SourceParameterKeySelector"; -import { ScrollArea, ScrollAreaViewport } from "@/components/ui/scroll-area"; import CloudContext from "@/store/CloudContext"; -import { CredentialSelector } from "../../components/CredentialSelector"; -import { SwitchBar } from "@/components/SwitchBar"; +import { Cross2Icon } from "@radix-ui/react-icons"; +import { useContext, useState } from "react"; +import { CredentialParameterSourceSelector } from "../../components/CredentialParameterSourceSelector"; +import { SourceParameterKeySelector } from "../../components/SourceParameterKeySelector"; +import { + WorkflowEditorParameterType, + WorkflowParameterValueType, +} from "../../types/workflowTypes"; +import { WorkflowParameterInput } from "../../WorkflowParameterInput"; +import { ParametersState } from "../types"; +import { getDefaultValueForParameterType } from "../workflowEditorUtils"; import { validateBitwardenLoginCredential } from "./util"; type Props = { @@ -39,6 +39,7 @@ const workflowParameterTypeOptions = [ { label: "integer", value: WorkflowParameterValueType.Integer }, { label: "boolean", value: WorkflowParameterValueType.Boolean }, { label: "file", value: WorkflowParameterValueType.FileURL }, + { label: "credential", value: WorkflowParameterValueType.CredentialId }, { label: "JSON", value: WorkflowParameterValueType.JSON }, ]; @@ -299,7 +300,7 @@ function WorkflowParameterAddPanel({ type, onClose, onSave }: Props) { isCloud && (
- setCredentialId(value)} /> diff --git a/skyvern-frontend/src/routes/workflows/editor/panels/WorkflowParameterEditPanel.tsx b/skyvern-frontend/src/routes/workflows/editor/panels/WorkflowParameterEditPanel.tsx index cbf6b3f5..faaa94de 100644 --- a/skyvern-frontend/src/routes/workflows/editor/panels/WorkflowParameterEditPanel.tsx +++ b/skyvern-frontend/src/routes/workflows/editor/panels/WorkflowParameterEditPanel.tsx @@ -16,7 +16,7 @@ import { toast } from "@/components/ui/use-toast"; import CloudContext from "@/store/CloudContext"; import { Cross2Icon } from "@radix-ui/react-icons"; import { useContext, useState } from "react"; -import { CredentialSelector } from "../../components/CredentialSelector"; +import { CredentialParameterSourceSelector } from "../../components/CredentialParameterSourceSelector"; import { SourceParameterKeySelector } from "../../components/SourceParameterKeySelector"; import { WorkflowEditorParameterType, @@ -44,6 +44,7 @@ const workflowParameterTypeOptions = [ { label: "integer", value: WorkflowParameterValueType.Integer }, { label: "boolean", value: WorkflowParameterValueType.Boolean }, { label: "file", value: WorkflowParameterValueType.FileURL }, + { label: "credential", value: WorkflowParameterValueType.CredentialId }, { label: "JSON", value: WorkflowParameterValueType.JSON }, ]; @@ -353,7 +354,7 @@ function WorkflowParameterEditPanel({ isCloud && (
- setCredentialId(value)} /> diff --git a/skyvern-frontend/src/routes/workflows/editor/workflowEditorUtils.ts b/skyvern-frontend/src/routes/workflows/editor/workflowEditorUtils.ts index 68cc9cd8..0a5dd57e 100644 --- a/skyvern-frontend/src/routes/workflows/editor/workflowEditorUtils.ts +++ b/skyvern-frontend/src/routes/workflows/editor/workflowEditorUtils.ts @@ -1438,6 +1438,9 @@ function getDefaultValueForParameterType( case "file_url": { return null; } + case "credential_id": { + return null; + } } } diff --git a/skyvern-frontend/src/routes/workflows/types/workflowTypes.ts b/skyvern-frontend/src/routes/workflows/types/workflowTypes.ts index 58110298..8785481d 100644 --- a/skyvern-frontend/src/routes/workflows/types/workflowTypes.ts +++ b/skyvern-frontend/src/routes/workflows/types/workflowTypes.ts @@ -103,6 +103,7 @@ export const WorkflowParameterValueType = { Boolean: "boolean", JSON: "json", FileURL: "file_url", + CredentialId: "credential_id", } as const; export type WorkflowParameterValueType =