Add new workflow parameter type in UI, credential (#1897)

This commit is contained in:
Shuchang Zheng
2025-03-06 12:12:13 -08:00
committed by GitHub
parent 67e50e0d0e
commit 38eb14ad42
8 changed files with 109 additions and 24 deletions

View File

@@ -5,6 +5,7 @@ import { CodeEditor } from "./components/CodeEditor";
import { AutoResizingTextarea } from "@/components/AutoResizingTextarea/AutoResizingTextarea"; import { AutoResizingTextarea } from "@/components/AutoResizingTextarea/AutoResizingTextarea";
import { Label } from "@/components/ui/label"; import { Label } from "@/components/ui/label";
import { WorkflowParameterValueType } from "./types/workflowTypes"; import { WorkflowParameterValueType } from "./types/workflowTypes";
import { CredentialSelector } from "./components/CredentialSelector";
type Props = { type Props = {
type: WorkflowParameterValueType; type: WorkflowParameterValueType;
@@ -80,6 +81,16 @@ function WorkflowParameterInput({ type, value, onChange }: Props) {
/> />
); );
} }
if (type === "credential_id") {
const credentialId = value as string | null;
return (
<CredentialSelector
value={credentialId ?? ""}
onChange={(value) => onChange(value)}
/>
);
}
} }
export { WorkflowParameterInput }; export { WorkflowParameterInput };

View File

@@ -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 <Skeleton className="h-10 w-full" />;
}
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 (
<Select value={value} onValueChange={onChange}>
<SelectTrigger>
<SelectValue placeholder="Select a credential" />
</SelectTrigger>
<SelectContent>
{options.map((option) => (
<SelectItem key={option.value} value={option.value}>
{option.label}
</SelectItem>
))}
</SelectContent>
</Select>
);
}
export { CredentialParameterSourceSelector };

View File

@@ -16,7 +16,7 @@ type Props = {
onChange?: (value: string) => void; onChange?: (value: string) => void;
}; };
function CredentialParameterSelector({ value, onChange }: Props) { function LoginBlockCredentialSelector({ value, onChange }: Props) {
const [workflowParameters, setWorkflowParameters] = const [workflowParameters, setWorkflowParameters] =
useWorkflowParametersState(); useWorkflowParametersState();
const credentialParameters = workflowParameters.filter( const credentialParameters = workflowParameters.filter(
@@ -104,4 +104,4 @@ function CredentialParameterSelector({ value, onChange }: Props) {
); );
} }
export { CredentialParameterSelector }; export { LoginBlockCredentialSelector };

View File

@@ -29,13 +29,12 @@ import { EditableNodeTitle } from "../components/EditableNodeTitle";
import { NodeActionMenu } from "../NodeActionMenu"; import { NodeActionMenu } from "../NodeActionMenu";
import { errorMappingExampleValue } from "../types"; import { errorMappingExampleValue } from "../types";
import { WorkflowBlockIcon } from "../WorkflowBlockIcon"; import { WorkflowBlockIcon } from "../WorkflowBlockIcon";
import { CredentialParameterSelector } from "./CredentialParameterSelector";
import type { LoginNode } from "./types"; import type { LoginNode } from "./types";
import { ParametersMultiSelect } from "../TaskNode/ParametersMultiSelect"; import { ParametersMultiSelect } from "../TaskNode/ParametersMultiSelect";
import { AppNode } from ".."; import { AppNode } from "..";
import { getAvailableOutputParameterKeys } from "../../workflowEditorUtils"; import { getAvailableOutputParameterKeys } from "../../workflowEditorUtils";
import { useIsFirstBlockInWorkflow } from "../../hooks/useIsFirstNodeInWorkflow"; import { useIsFirstBlockInWorkflow } from "../../hooks/useIsFirstNodeInWorkflow";
import { LoginBlockCredentialSelector } from "./LoginBlockCredentialSelector";
function LoginNode({ id, data }: NodeProps<LoginNode>) { function LoginNode({ id, data }: NodeProps<LoginNode>) {
const { updateNodeData } = useReactFlow(); const { updateNodeData } = useReactFlow();
const { editable } = data; const { editable } = data;
@@ -152,7 +151,7 @@ function LoginNode({ id, data }: NodeProps<LoginNode>) {
</div> </div>
<div className="space-y-2"> <div className="space-y-2">
<Label className="text-xs text-slate-300">Credential</Label> <Label className="text-xs text-slate-300">Credential</Label>
<CredentialParameterSelector <LoginBlockCredentialSelector
value={ value={
data.parameterKeys.length > 0 data.parameterKeys.length > 0
? data.parameterKeys[0] ? data.parameterKeys[0]

View File

@@ -1,11 +1,9 @@
import { Cross2Icon } from "@radix-ui/react-icons"; import { SwitchBar } from "@/components/SwitchBar";
import { Label } from "@/components/ui/label"; import { Button } from "@/components/ui/button";
import { Checkbox } from "@/components/ui/checkbox";
import { Input } from "@/components/ui/input"; import { Input } from "@/components/ui/input";
import { useContext, useState } from "react"; import { Label } from "@/components/ui/label";
import { import { ScrollArea, ScrollAreaViewport } from "@/components/ui/scroll-area";
WorkflowEditorParameterType,
WorkflowParameterValueType,
} from "../../types/workflowTypes";
import { import {
Select, Select,
SelectContent, SelectContent,
@@ -14,17 +12,19 @@ import {
SelectTrigger, SelectTrigger,
SelectValue, SelectValue,
} from "@/components/ui/select"; } 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 { 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 CloudContext from "@/store/CloudContext";
import { CredentialSelector } from "../../components/CredentialSelector"; import { Cross2Icon } from "@radix-ui/react-icons";
import { SwitchBar } from "@/components/SwitchBar"; 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"; import { validateBitwardenLoginCredential } from "./util";
type Props = { type Props = {
@@ -39,6 +39,7 @@ const workflowParameterTypeOptions = [
{ label: "integer", value: WorkflowParameterValueType.Integer }, { label: "integer", value: WorkflowParameterValueType.Integer },
{ label: "boolean", value: WorkflowParameterValueType.Boolean }, { label: "boolean", value: WorkflowParameterValueType.Boolean },
{ label: "file", value: WorkflowParameterValueType.FileURL }, { label: "file", value: WorkflowParameterValueType.FileURL },
{ label: "credential", value: WorkflowParameterValueType.CredentialId },
{ label: "JSON", value: WorkflowParameterValueType.JSON }, { label: "JSON", value: WorkflowParameterValueType.JSON },
]; ];
@@ -299,7 +300,7 @@ function WorkflowParameterAddPanel({ type, onClose, onSave }: Props) {
isCloud && ( isCloud && (
<div className="space-y-1"> <div className="space-y-1">
<Label className="text-xs text-slate-300">Credential</Label> <Label className="text-xs text-slate-300">Credential</Label>
<CredentialSelector <CredentialParameterSourceSelector
value={credentialId} value={credentialId}
onChange={(value) => setCredentialId(value)} onChange={(value) => setCredentialId(value)}
/> />

View File

@@ -16,7 +16,7 @@ import { toast } from "@/components/ui/use-toast";
import CloudContext from "@/store/CloudContext"; import CloudContext from "@/store/CloudContext";
import { Cross2Icon } from "@radix-ui/react-icons"; import { Cross2Icon } from "@radix-ui/react-icons";
import { useContext, useState } from "react"; import { useContext, useState } from "react";
import { CredentialSelector } from "../../components/CredentialSelector"; import { CredentialParameterSourceSelector } from "../../components/CredentialParameterSourceSelector";
import { SourceParameterKeySelector } from "../../components/SourceParameterKeySelector"; import { SourceParameterKeySelector } from "../../components/SourceParameterKeySelector";
import { import {
WorkflowEditorParameterType, WorkflowEditorParameterType,
@@ -44,6 +44,7 @@ const workflowParameterTypeOptions = [
{ label: "integer", value: WorkflowParameterValueType.Integer }, { label: "integer", value: WorkflowParameterValueType.Integer },
{ label: "boolean", value: WorkflowParameterValueType.Boolean }, { label: "boolean", value: WorkflowParameterValueType.Boolean },
{ label: "file", value: WorkflowParameterValueType.FileURL }, { label: "file", value: WorkflowParameterValueType.FileURL },
{ label: "credential", value: WorkflowParameterValueType.CredentialId },
{ label: "JSON", value: WorkflowParameterValueType.JSON }, { label: "JSON", value: WorkflowParameterValueType.JSON },
]; ];
@@ -353,7 +354,7 @@ function WorkflowParameterEditPanel({
isCloud && ( isCloud && (
<div className="space-y-1"> <div className="space-y-1">
<Label className="text-xs text-slate-300">Credential</Label> <Label className="text-xs text-slate-300">Credential</Label>
<CredentialSelector <CredentialParameterSourceSelector
value={credentialId} value={credentialId}
onChange={(value) => setCredentialId(value)} onChange={(value) => setCredentialId(value)}
/> />

View File

@@ -1438,6 +1438,9 @@ function getDefaultValueForParameterType(
case "file_url": { case "file_url": {
return null; return null;
} }
case "credential_id": {
return null;
}
} }
} }

View File

@@ -103,6 +103,7 @@ export const WorkflowParameterValueType = {
Boolean: "boolean", Boolean: "boolean",
JSON: "json", JSON: "json",
FileURL: "file_url", FileURL: "file_url",
CredentialId: "credential_id",
} as const; } as const;
export type WorkflowParameterValueType = export type WorkflowParameterValueType =