Add ability to add default values to workflow parameters (#865)
This commit is contained in:
@@ -150,7 +150,7 @@ function FileUpload({ value, onChange }: Props) {
|
||||
accept=".csv"
|
||||
className="hidden"
|
||||
/>
|
||||
<div className="flex max-w-full gap-2 truncate">
|
||||
<div className="flex max-w-full gap-2 px-2">
|
||||
{uploadFileMutation.isPending && (
|
||||
<ReloadIcon className="mr-2 h-4 w-4 animate-spin" />
|
||||
)}
|
||||
|
||||
@@ -3,6 +3,8 @@ import { FileInputValue, FileUpload } from "@/components/FileUpload";
|
||||
import { Checkbox } from "@/components/ui/checkbox";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { CodeEditor } from "./components/CodeEditor";
|
||||
import { AutoResizingTextarea } from "@/components/AutoResizingTextarea/AutoResizingTextarea";
|
||||
import { Label } from "@/components/ui/label";
|
||||
|
||||
type Props = {
|
||||
type: WorkflowParameterValueType;
|
||||
@@ -26,7 +28,7 @@ function WorkflowParameterInput({ type, value, onChange }: Props) {
|
||||
|
||||
if (type === "string") {
|
||||
return (
|
||||
<Input
|
||||
<AutoResizingTextarea
|
||||
value={value as string}
|
||||
onChange={(e) => onChange(e.target.value)}
|
||||
/>
|
||||
@@ -55,12 +57,16 @@ function WorkflowParameterInput({ type, value, onChange }: Props) {
|
||||
}
|
||||
|
||||
if (type === "boolean") {
|
||||
const checked = typeof value === "boolean" ? value : Boolean(value);
|
||||
return (
|
||||
<Checkbox
|
||||
checked={value as boolean}
|
||||
onCheckedChange={(checked) => onChange(checked)}
|
||||
className="block"
|
||||
/>
|
||||
<div className="flex items-center gap-2">
|
||||
<Checkbox
|
||||
checked={checked}
|
||||
onCheckedChange={(checked) => onChange(checked)}
|
||||
className="block"
|
||||
/>
|
||||
<Label>{value ? "True" : "False"}</Label>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -45,10 +45,6 @@ function WorkflowRunParameters() {
|
||||
? location.state.data
|
||||
: workflowParameters?.reduce(
|
||||
(acc, curr) => {
|
||||
if (curr.workflow_parameter_type === "file_url") {
|
||||
acc[curr.key] = null;
|
||||
return acc;
|
||||
}
|
||||
if (curr.workflow_parameter_type === "json") {
|
||||
if (typeof curr.default_value === "string") {
|
||||
acc[curr.key] = curr.default_value;
|
||||
@@ -59,6 +55,13 @@ function WorkflowRunParameters() {
|
||||
return acc;
|
||||
}
|
||||
}
|
||||
if (
|
||||
curr.default_value &&
|
||||
curr.workflow_parameter_type === "boolean"
|
||||
) {
|
||||
acc[curr.key] = Boolean(curr.default_value);
|
||||
return acc;
|
||||
}
|
||||
if (curr.default_value) {
|
||||
acc[curr.key] = curr.default_value;
|
||||
return acc;
|
||||
|
||||
@@ -73,7 +73,9 @@ function convertToParametersYAML(
|
||||
key: parameter.key,
|
||||
description: parameter.description || null,
|
||||
workflow_parameter_type: parameter.dataType,
|
||||
default_value: null,
|
||||
...(parameter.defaultValue === null
|
||||
? {}
|
||||
: { default_value: parameter.defaultValue }),
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
@@ -98,6 +100,7 @@ export type ParametersState = Array<
|
||||
parameterType: "workflow";
|
||||
dataType: WorkflowParameterValueType;
|
||||
description?: string;
|
||||
defaultValue: unknown;
|
||||
}
|
||||
| {
|
||||
key: string;
|
||||
|
||||
@@ -59,6 +59,7 @@ function WorkflowEditor() {
|
||||
key: parameter.key,
|
||||
parameterType: "workflow",
|
||||
dataType: parameter.workflow_parameter_type,
|
||||
defaultValue: parameter.default_value,
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
|
||||
@@ -43,7 +43,7 @@ function WorkflowHeader({
|
||||
inputClassName="text-3xl"
|
||||
/>
|
||||
</div>
|
||||
<div className="flex h-full w-1/3 items-center justify-end gap-4">
|
||||
<div className="flex h-full items-center justify-end gap-4">
|
||||
<TooltipProvider>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
|
||||
@@ -13,6 +13,10 @@ import {
|
||||
} from "@/components/ui/select";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { ParametersState } from "../FlowRenderer";
|
||||
import { WorkflowParameterInput } from "../../WorkflowParameterInput";
|
||||
import { Checkbox } from "@/components/ui/checkbox";
|
||||
import { getDefaultValueForParameterType } from "../workflowEditorUtils";
|
||||
import { toast } from "@/components/ui/use-toast";
|
||||
|
||||
type Props = {
|
||||
type: "workflow" | "credential";
|
||||
@@ -35,6 +39,13 @@ function WorkflowParameterAddPanel({ type, onClose, onSave }: Props) {
|
||||
const [collectionId, setCollectionId] = useState("");
|
||||
const [parameterType, setParameterType] =
|
||||
useState<WorkflowParameterValueType>("string");
|
||||
const [defaultValueState, setDefaultValueState] = useState<{
|
||||
hasDefaultValue: boolean;
|
||||
defaultValue: unknown;
|
||||
}>({
|
||||
hasDefaultValue: false,
|
||||
defaultValue: null,
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
@@ -56,28 +67,90 @@ function WorkflowParameterAddPanel({ type, onClose, onSave }: Props) {
|
||||
/>
|
||||
</div>
|
||||
{type === "workflow" && (
|
||||
<div className="space-y-1">
|
||||
<Label className="text-xs">Value Type</Label>
|
||||
<Select
|
||||
value={parameterType}
|
||||
onValueChange={(value) =>
|
||||
setParameterType(value as WorkflowParameterValueType)
|
||||
}
|
||||
>
|
||||
<SelectTrigger className="w-full">
|
||||
<SelectValue placeholder="Select a type" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectGroup>
|
||||
{workflowParameterTypeOptions.map((option) => (
|
||||
<SelectItem key={option.value} value={option.value}>
|
||||
{option.label}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectGroup>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
<>
|
||||
<div className="space-y-1">
|
||||
<Label className="text-xs">Value Type</Label>
|
||||
<Select
|
||||
value={parameterType}
|
||||
onValueChange={(value) => {
|
||||
setParameterType(value as WorkflowParameterValueType);
|
||||
setDefaultValueState((state) => {
|
||||
return {
|
||||
...state,
|
||||
defaultValue: getDefaultValueForParameterType(
|
||||
value as WorkflowParameterValueType,
|
||||
),
|
||||
};
|
||||
});
|
||||
}}
|
||||
>
|
||||
<SelectTrigger className="w-full">
|
||||
<SelectValue placeholder="Select a type" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectGroup>
|
||||
{workflowParameterTypeOptions.map((option) => (
|
||||
<SelectItem key={option.value} value={option.value}>
|
||||
{option.label}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectGroup>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
<div className="space-y-4">
|
||||
<div className="flex items-center gap-2">
|
||||
<Checkbox
|
||||
checked={defaultValueState.hasDefaultValue}
|
||||
onCheckedChange={(checked) => {
|
||||
if (!checked) {
|
||||
setDefaultValueState({
|
||||
hasDefaultValue: false,
|
||||
defaultValue: null,
|
||||
});
|
||||
return;
|
||||
}
|
||||
setDefaultValueState({
|
||||
hasDefaultValue: true,
|
||||
defaultValue:
|
||||
getDefaultValueForParameterType(parameterType),
|
||||
});
|
||||
}}
|
||||
/>
|
||||
<Label className="text-xs text-slate-300">
|
||||
Use Default Value
|
||||
</Label>
|
||||
</div>
|
||||
{defaultValueState.hasDefaultValue && (
|
||||
<WorkflowParameterInput
|
||||
onChange={(value) => {
|
||||
if (
|
||||
parameterType === "file_url" &&
|
||||
typeof value === "object" &&
|
||||
value &&
|
||||
"s3uri" in value
|
||||
) {
|
||||
setDefaultValueState((state) => {
|
||||
return {
|
||||
...state,
|
||||
defaultValue: value.s3uri,
|
||||
};
|
||||
});
|
||||
return;
|
||||
}
|
||||
setDefaultValueState((state) => {
|
||||
return {
|
||||
...state,
|
||||
defaultValue: value,
|
||||
};
|
||||
});
|
||||
}}
|
||||
type={parameterType}
|
||||
value={defaultValueState.defaultValue}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
{type === "credential" && (
|
||||
<>
|
||||
@@ -101,11 +174,32 @@ function WorkflowParameterAddPanel({ type, onClose, onSave }: Props) {
|
||||
<Button
|
||||
onClick={() => {
|
||||
if (type === "workflow") {
|
||||
if (
|
||||
parameterType === "json" &&
|
||||
typeof defaultValueState.defaultValue === "string"
|
||||
) {
|
||||
try {
|
||||
JSON.parse(defaultValueState.defaultValue);
|
||||
} catch (e) {
|
||||
toast({
|
||||
variant: "destructive",
|
||||
title: "Failed to save parameters",
|
||||
description: "Invalid JSON for default value",
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
const defaultValue =
|
||||
parameterType === "json" &&
|
||||
typeof defaultValueState.defaultValue === "string"
|
||||
? JSON.parse(defaultValueState.defaultValue)
|
||||
: defaultValueState.defaultValue;
|
||||
onSave({
|
||||
key,
|
||||
parameterType: "workflow",
|
||||
dataType: parameterType,
|
||||
description,
|
||||
defaultValue: defaultValue,
|
||||
});
|
||||
}
|
||||
if (type === "credential") {
|
||||
|
||||
@@ -13,6 +13,10 @@ import {
|
||||
} from "@/components/ui/select";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { ParametersState } from "../FlowRenderer";
|
||||
import { Checkbox } from "@/components/ui/checkbox";
|
||||
import { getDefaultValueForParameterType } from "../workflowEditorUtils";
|
||||
import { WorkflowParameterInput } from "../../WorkflowParameterInput";
|
||||
import { toast } from "@/components/ui/use-toast";
|
||||
|
||||
type Props = {
|
||||
type: "workflow" | "credential";
|
||||
@@ -56,6 +60,21 @@ function WorkflowParameterEditPanel({
|
||||
: "string",
|
||||
);
|
||||
|
||||
const [defaultValueState, setDefaultValueState] = useState<{
|
||||
hasDefaultValue: boolean;
|
||||
defaultValue: unknown;
|
||||
}>(
|
||||
initialValues.parameterType === "workflow"
|
||||
? {
|
||||
hasDefaultValue: initialValues.defaultValue !== null,
|
||||
defaultValue: initialValues.defaultValue ?? null,
|
||||
}
|
||||
: {
|
||||
hasDefaultValue: false,
|
||||
defaultValue: null,
|
||||
},
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
<header className="flex items-center justify-between">
|
||||
@@ -76,28 +95,90 @@ function WorkflowParameterEditPanel({
|
||||
/>
|
||||
</div>
|
||||
{type === "workflow" && (
|
||||
<div className="space-y-1">
|
||||
<Label className="text-xs">Value Type</Label>
|
||||
<Select
|
||||
value={parameterType}
|
||||
onValueChange={(value) =>
|
||||
setParameterType(value as WorkflowParameterValueType)
|
||||
}
|
||||
>
|
||||
<SelectTrigger className="w-full">
|
||||
<SelectValue placeholder="Select a type" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectGroup>
|
||||
{workflowParameterTypeOptions.map((option) => (
|
||||
<SelectItem key={option.value} value={option.value}>
|
||||
{option.label}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectGroup>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
<>
|
||||
<div className="space-y-1">
|
||||
<Label className="text-xs">Value Type</Label>
|
||||
<Select
|
||||
value={parameterType}
|
||||
onValueChange={(value) => {
|
||||
setParameterType(value as WorkflowParameterValueType);
|
||||
setDefaultValueState((state) => {
|
||||
return {
|
||||
...state,
|
||||
defaultValue: getDefaultValueForParameterType(
|
||||
value as WorkflowParameterValueType,
|
||||
),
|
||||
};
|
||||
});
|
||||
}}
|
||||
>
|
||||
<SelectTrigger className="w-full">
|
||||
<SelectValue placeholder="Select a type" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectGroup>
|
||||
{workflowParameterTypeOptions.map((option) => (
|
||||
<SelectItem key={option.value} value={option.value}>
|
||||
{option.label}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectGroup>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
<div className="space-y-4">
|
||||
<div className="flex items-center gap-2">
|
||||
<Checkbox
|
||||
checked={defaultValueState.hasDefaultValue}
|
||||
onCheckedChange={(checked) => {
|
||||
if (!checked) {
|
||||
setDefaultValueState({
|
||||
hasDefaultValue: false,
|
||||
defaultValue: null,
|
||||
});
|
||||
return;
|
||||
}
|
||||
setDefaultValueState({
|
||||
hasDefaultValue: true,
|
||||
defaultValue:
|
||||
getDefaultValueForParameterType(parameterType),
|
||||
});
|
||||
}}
|
||||
/>
|
||||
<Label className="text-xs text-slate-300">
|
||||
Use Default Value
|
||||
</Label>
|
||||
</div>
|
||||
{defaultValueState.hasDefaultValue && (
|
||||
<WorkflowParameterInput
|
||||
onChange={(value) => {
|
||||
if (
|
||||
parameterType === "file_url" &&
|
||||
typeof value === "object" &&
|
||||
value &&
|
||||
"s3uri" in value
|
||||
) {
|
||||
setDefaultValueState((state) => {
|
||||
return {
|
||||
...state,
|
||||
defaultValue: value,
|
||||
};
|
||||
});
|
||||
return;
|
||||
}
|
||||
setDefaultValueState((state) => {
|
||||
return {
|
||||
...state,
|
||||
defaultValue: value,
|
||||
};
|
||||
});
|
||||
}}
|
||||
type={parameterType}
|
||||
value={defaultValueState.defaultValue}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
{type === "credential" && (
|
||||
<>
|
||||
@@ -121,11 +202,32 @@ function WorkflowParameterEditPanel({
|
||||
<Button
|
||||
onClick={() => {
|
||||
if (type === "workflow") {
|
||||
if (
|
||||
parameterType === "json" &&
|
||||
typeof defaultValueState.defaultValue === "string"
|
||||
) {
|
||||
try {
|
||||
JSON.parse(defaultValueState.defaultValue);
|
||||
} catch (e) {
|
||||
toast({
|
||||
variant: "destructive",
|
||||
title: "Failed to save parameters",
|
||||
description: "Invalid JSON for default value",
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
const defaultValue =
|
||||
parameterType === "json" &&
|
||||
typeof defaultValueState.defaultValue === "string"
|
||||
? JSON.parse(defaultValueState.defaultValue)
|
||||
: defaultValueState.defaultValue;
|
||||
onSave({
|
||||
key,
|
||||
parameterType: "workflow",
|
||||
dataType: parameterType,
|
||||
description,
|
||||
defaultValue: defaultValue,
|
||||
});
|
||||
}
|
||||
if (type === "credential") {
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
import Dagre from "@dagrejs/dagre";
|
||||
import { Edge } from "@xyflow/react";
|
||||
import { nanoid } from "nanoid";
|
||||
import type { WorkflowBlock } from "../types/workflowTypes";
|
||||
import type {
|
||||
WorkflowBlock,
|
||||
WorkflowParameterValueType,
|
||||
} from "../types/workflowTypes";
|
||||
import { BlockYAML, ParameterYAML } from "../types/workflowYamlTypes";
|
||||
import {
|
||||
EMAIL_BLOCK_SENDER,
|
||||
@@ -764,6 +767,29 @@ function getLabelForExistingNode(label: string, existingLabels: Array<string>) {
|
||||
return label;
|
||||
}
|
||||
|
||||
function getDefaultValueForParameterType(
|
||||
parameterType: WorkflowParameterValueType,
|
||||
): unknown {
|
||||
switch (parameterType) {
|
||||
case "json": {
|
||||
return "{}";
|
||||
}
|
||||
case "string": {
|
||||
return "";
|
||||
}
|
||||
case "boolean": {
|
||||
return false;
|
||||
}
|
||||
case "float":
|
||||
case "integer": {
|
||||
return 0;
|
||||
}
|
||||
case "file_url": {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export {
|
||||
createNode,
|
||||
generateNodeData,
|
||||
@@ -778,4 +804,5 @@ export {
|
||||
getLabelForExistingNode,
|
||||
isOutputParameterKey,
|
||||
getBlockNameOfOutputParameterKey,
|
||||
getDefaultValueForParameterType,
|
||||
};
|
||||
|
||||
@@ -30,7 +30,7 @@ export type ParameterYAMLBase = {
|
||||
export type WorkflowParameterYAML = ParameterYAMLBase & {
|
||||
parameter_type: "workflow";
|
||||
workflow_parameter_type: string;
|
||||
default_value: string | null;
|
||||
default_value?: unknown;
|
||||
};
|
||||
|
||||
export type BitwardenLoginCredentialParameterYAML = ParameterYAMLBase & {
|
||||
|
||||
Reference in New Issue
Block a user