Fix overflow of default value in parameters (#1156)

This commit is contained in:
Shuchang Zheng
2024-11-07 07:22:24 -08:00
committed by GitHub
parent 61d1c24ffc
commit 2252148c5a
2 changed files with 492 additions and 474 deletions

View File

@@ -18,6 +18,7 @@ import { Checkbox } from "@/components/ui/checkbox";
import { getDefaultValueForParameterType } from "../workflowEditorUtils"; import { getDefaultValueForParameterType } from "../workflowEditorUtils";
import { toast } from "@/components/ui/use-toast"; import { toast } from "@/components/ui/use-toast";
import { SourceParameterKeySelector } from "../../components/SourceParameterKeySelector"; import { SourceParameterKeySelector } from "../../components/SourceParameterKeySelector";
import { ScrollArea, ScrollAreaViewport } from "@/components/ui/scroll-area";
type Props = { type Props = {
type: "workflow" | "credential" | "context" | "secret"; type: "workflow" | "credential" | "context" | "secret";
@@ -69,254 +70,262 @@ function WorkflowParameterAddPanel({ type, onClose, onSave }: Props) {
const [identityFields, setIdentityFields] = useState(""); const [identityFields, setIdentityFields] = useState("");
return ( return (
<div className="space-y-4"> <ScrollArea>
<header className="flex items-center justify-between"> <ScrollAreaViewport className="max-h-[500px]">
<span>{header(type)}</span> <div className="space-y-4 p-1">
<Cross2Icon className="h-6 w-6 cursor-pointer" onClick={onClose} /> <header className="flex items-center justify-between">
</header> <span>{header(type)}</span>
<div className="space-y-1"> <Cross2Icon className="h-6 w-6 cursor-pointer" onClick={onClose} />
<Label className="text-xs text-slate-300">Key</Label> </header>
<Input value={key} onChange={(e) => setKey(e.target.value)} />
</div>
<div className="space-y-1">
<Label className="text-xs text-slate-300">Description</Label>
<Input
value={description}
onChange={(e) => setDescription(e.target.value)}
/>
</div>
{type === "workflow" && (
<>
<div className="space-y-1"> <div className="space-y-1">
<Label className="text-xs">Value Type</Label> <Label className="text-xs text-slate-300">Key</Label>
<Select <Input value={key} onChange={(e) => setKey(e.target.value)} />
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>
<div className="space-y-4"> <div className="space-y-1">
<div className="flex items-center gap-2"> <Label className="text-xs text-slate-300">Description</Label>
<Checkbox <Input
checked={defaultValueState.hasDefaultValue} value={description}
onCheckedChange={(checked) => { onChange={(e) => setDescription(e.target.value)}
if (!checked) { />
setDefaultValueState({ </div>
hasDefaultValue: false, {type === "workflow" && (
defaultValue: null, <>
}); <div className="space-y-1">
return; <Label className="text-xs">Value Type</Label>
} <Select
setDefaultValueState({ value={parameterType}
hasDefaultValue: true, onValueChange={(value) => {
defaultValue: setParameterType(value as WorkflowParameterValueType);
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) => { setDefaultValueState((state) => {
return { return {
...state, ...state,
defaultValue: value.s3uri, 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" && (
<>
<div className="space-y-1">
<Label className="text-xs text-slate-300">
URL Parameter Key
</Label>
<Input
value={urlParameterKey}
onChange={(e) => setUrlParameterKey(e.target.value)}
/>
</div>
<div className="space-y-1">
<Label className="text-xs text-slate-300">Collection ID</Label>
<Input
value={collectionId}
onChange={(e) => setCollectionId(e.target.value)}
/>
</div>
</>
)}
{type === "context" && (
<div className="space-y-1">
<Label className="text-xs text-slate-300">Source Parameter</Label>
<SourceParameterKeySelector
value={sourceParameterKey}
onChange={setSourceParameterKey}
/>
</div>
)}
{type === "secret" && (
<>
<div className="space-y-1">
<Label className="text-xs text-slate-300">Identity Key</Label>
<Input
value={identityKey}
onChange={(e) => setIdentityKey(e.target.value)}
/>
</div>
<div className="space-y-1">
<Label className="text-xs text-slate-300">
Identity Fields
</Label>
<Input
value={identityFields}
onChange={(e) => setIdentityFields(e.target.value)}
/>
</div>
<div className="space-y-1">
<Label className="text-xs text-slate-300">Collection ID</Label>
<Input
value={collectionId}
onChange={(e) => setCollectionId(e.target.value)}
/>
</div>
</>
)}
<div className="flex justify-end">
<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 add parameter",
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: defaultValueState.hasDefaultValue
? defaultValue
: null,
});
}
if (type === "credential") {
if (!collectionId) {
toast({
variant: "destructive",
title: "Failed to add parameter",
description: "Collection ID is required",
});
return; return;
} }
setDefaultValueState((state) => { onSave({
return { key,
...state, parameterType: "credential",
defaultValue: value, collectionId,
}; urlParameterKey,
description,
}); });
}}
type={parameterType}
value={defaultValueState.defaultValue}
/>
)}
</div>
</>
)}
{type === "credential" && (
<>
<div className="space-y-1">
<Label className="text-xs text-slate-300">URL Parameter Key</Label>
<Input
value={urlParameterKey}
onChange={(e) => setUrlParameterKey(e.target.value)}
/>
</div>
<div className="space-y-1">
<Label className="text-xs text-slate-300">Collection ID</Label>
<Input
value={collectionId}
onChange={(e) => setCollectionId(e.target.value)}
/>
</div>
</>
)}
{type === "context" && (
<div className="space-y-1">
<Label className="text-xs text-slate-300">Source Parameter</Label>
<SourceParameterKeySelector
value={sourceParameterKey}
onChange={setSourceParameterKey}
/>
</div>
)}
{type === "secret" && (
<>
<div className="space-y-1">
<Label className="text-xs text-slate-300">Identity Key</Label>
<Input
value={identityKey}
onChange={(e) => setIdentityKey(e.target.value)}
/>
</div>
<div className="space-y-1">
<Label className="text-xs text-slate-300">Identity Fields</Label>
<Input
value={identityFields}
onChange={(e) => setIdentityFields(e.target.value)}
/>
</div>
<div className="space-y-1">
<Label className="text-xs text-slate-300">Collection ID</Label>
<Input
value={collectionId}
onChange={(e) => setCollectionId(e.target.value)}
/>
</div>
</>
)}
<div className="flex justify-end">
<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 add parameter",
description: "Invalid JSON for default value",
});
return;
} }
} if (type === "secret") {
const defaultValue = if (!collectionId) {
parameterType === "json" && toast({
typeof defaultValueState.defaultValue === "string" variant: "destructive",
? JSON.parse(defaultValueState.defaultValue) title: "Failed to add parameter",
: defaultValueState.defaultValue; description: "Collection ID is required",
onSave({ });
key, return;
parameterType: "workflow", }
dataType: parameterType, onSave({
description, key,
defaultValue: defaultValueState.hasDefaultValue parameterType: "secret",
? defaultValue collectionId,
: null, identityFields: identityFields
}); .split(",")
} .filter((s) => s.length > 0)
if (type === "credential") { .map((field) => field.trim()),
if (!collectionId) { identityKey,
toast({ description,
variant: "destructive", });
title: "Failed to add parameter", }
description: "Collection ID is required", if (type === "context") {
}); if (!sourceParameterKey) {
return; toast({
} variant: "destructive",
onSave({ title: "Failed to add parameter",
key, description: "Source parameter key is required",
parameterType: "credential", });
collectionId, return;
urlParameterKey, }
description, onSave({
}); key,
} parameterType: "context",
if (type === "secret") { sourceParameterKey: sourceParameterKey,
if (!collectionId) { description,
toast({ });
variant: "destructive", }
title: "Failed to add parameter", }}
description: "Collection ID is required", >
}); Save
return; </Button>
} </div>
onSave({ </div>
key, </ScrollAreaViewport>
parameterType: "secret", </ScrollArea>
collectionId,
identityFields: identityFields
.split(",")
.filter((s) => s.length > 0)
.map((field) => field.trim()),
identityKey,
description,
});
}
if (type === "context") {
if (!sourceParameterKey) {
toast({
variant: "destructive",
title: "Failed to add parameter",
description: "Source parameter key is required",
});
return;
}
onSave({
key,
parameterType: "context",
sourceParameterKey: sourceParameterKey,
description,
});
}
}}
>
Save
</Button>
</div>
</div>
); );
} }

View File

@@ -18,6 +18,7 @@ import { getDefaultValueForParameterType } from "../workflowEditorUtils";
import { WorkflowParameterInput } from "../../WorkflowParameterInput"; import { WorkflowParameterInput } from "../../WorkflowParameterInput";
import { toast } from "@/components/ui/use-toast"; import { toast } from "@/components/ui/use-toast";
import { SourceParameterKeySelector } from "../../components/SourceParameterKeySelector"; import { SourceParameterKeySelector } from "../../components/SourceParameterKeySelector";
import { ScrollArea, ScrollAreaViewport } from "@/components/ui/scroll-area";
type Props = { type Props = {
type: "workflow" | "credential" | "context" | "secret"; type: "workflow" | "credential" | "context" | "secret";
@@ -110,254 +111,262 @@ function WorkflowParameterEditPanel({
); );
return ( return (
<div className="space-y-4"> <ScrollArea>
<header className="flex items-center justify-between"> <ScrollAreaViewport className="max-h-[500px]">
<span>{header(type)}</span> <div className="space-y-4 p-1">
<Cross2Icon className="h-6 w-6 cursor-pointer" onClick={onClose} /> <header className="flex items-center justify-between">
</header> <span>{header(type)}</span>
<div className="space-y-1"> <Cross2Icon className="h-6 w-6 cursor-pointer" onClick={onClose} />
<Label className="text-xs text-slate-300">Key</Label> </header>
<Input value={key} onChange={(e) => setKey(e.target.value)} />
</div>
<div className="space-y-1">
<Label className="text-xs text-slate-300">Description</Label>
<Input
value={description}
onChange={(e) => setDescription(e.target.value)}
/>
</div>
{type === "workflow" && (
<>
<div className="space-y-1"> <div className="space-y-1">
<Label className="text-xs">Value Type</Label> <Label className="text-xs text-slate-300">Key</Label>
<Select <Input value={key} onChange={(e) => setKey(e.target.value)} />
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>
<div className="space-y-4"> <div className="space-y-1">
<div className="flex items-center gap-2"> <Label className="text-xs text-slate-300">Description</Label>
<Checkbox <Input
checked={defaultValueState.hasDefaultValue} value={description}
onCheckedChange={(checked) => { onChange={(e) => setDescription(e.target.value)}
if (!checked) { />
setDefaultValueState({ </div>
hasDefaultValue: false, {type === "workflow" && (
defaultValue: null, <>
}); <div className="space-y-1">
return; <Label className="text-xs">Value Type</Label>
} <Select
setDefaultValueState({ value={parameterType}
hasDefaultValue: true, onValueChange={(value) => {
defaultValue: setParameterType(value as WorkflowParameterValueType);
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) => { setDefaultValueState((state) => {
return { return {
...state, ...state,
defaultValue: value, 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" && (
<>
<div className="space-y-1">
<Label className="text-xs text-slate-300">
URL Parameter Key
</Label>
<Input
value={urlParameterKey}
onChange={(e) => setUrlParameterKey(e.target.value)}
/>
</div>
<div className="space-y-1">
<Label className="text-xs text-slate-300">Collection ID</Label>
<Input
value={collectionId}
onChange={(e) => setCollectionId(e.target.value)}
/>
</div>
</>
)}
{type === "context" && (
<div className="space-y-1">
<Label className="text-xs text-slate-300">Source Parameter</Label>
<SourceParameterKeySelector
value={sourceParameterKey}
onChange={setSourceParameterKey}
/>
</div>
)}
{type === "secret" && (
<>
<div className="space-y-1">
<Label className="text-xs text-slate-300">Identity Key</Label>
<Input
value={identityKey}
onChange={(e) => setIdentityKey(e.target.value)}
/>
</div>
<div className="space-y-1">
<Label className="text-xs text-slate-300">
Identity Fields
</Label>
<Input
value={identityFields}
onChange={(e) => setIdentityFields(e.target.value)}
/>
</div>
<div className="space-y-1">
<Label className="text-xs text-slate-300">Collection ID</Label>
<Input
value={collectionId}
onChange={(e) => setCollectionId(e.target.value)}
/>
</div>
</>
)}
<div className="flex justify-end">
<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 parameter",
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: defaultValueState.hasDefaultValue
? defaultValue
: null,
});
}
if (type === "credential") {
if (!collectionId) {
toast({
variant: "destructive",
title: "Failed to save parameter",
description: "Collection ID is required",
});
return; return;
} }
setDefaultValueState((state) => { onSave({
return { key,
...state, parameterType: "credential",
defaultValue: value, urlParameterKey,
}; collectionId,
description,
}); });
}}
type={parameterType}
value={defaultValueState.defaultValue}
/>
)}
</div>
</>
)}
{type === "credential" && (
<>
<div className="space-y-1">
<Label className="text-xs text-slate-300">URL Parameter Key</Label>
<Input
value={urlParameterKey}
onChange={(e) => setUrlParameterKey(e.target.value)}
/>
</div>
<div className="space-y-1">
<Label className="text-xs text-slate-300">Collection ID</Label>
<Input
value={collectionId}
onChange={(e) => setCollectionId(e.target.value)}
/>
</div>
</>
)}
{type === "context" && (
<div className="space-y-1">
<Label className="text-xs text-slate-300">Source Parameter</Label>
<SourceParameterKeySelector
value={sourceParameterKey}
onChange={setSourceParameterKey}
/>
</div>
)}
{type === "secret" && (
<>
<div className="space-y-1">
<Label className="text-xs text-slate-300">Identity Key</Label>
<Input
value={identityKey}
onChange={(e) => setIdentityKey(e.target.value)}
/>
</div>
<div className="space-y-1">
<Label className="text-xs text-slate-300">Identity Fields</Label>
<Input
value={identityFields}
onChange={(e) => setIdentityFields(e.target.value)}
/>
</div>
<div className="space-y-1">
<Label className="text-xs text-slate-300">Collection ID</Label>
<Input
value={collectionId}
onChange={(e) => setCollectionId(e.target.value)}
/>
</div>
</>
)}
<div className="flex justify-end">
<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 parameter",
description: "Invalid JSON for default value",
});
return;
} }
} if (type === "secret") {
const defaultValue = if (!collectionId) {
parameterType === "json" && toast({
typeof defaultValueState.defaultValue === "string" variant: "destructive",
? JSON.parse(defaultValueState.defaultValue) title: "Failed to add parameter",
: defaultValueState.defaultValue; description: "Collection ID is required",
onSave({ });
key, return;
parameterType: "workflow", }
dataType: parameterType, onSave({
description, key,
defaultValue: defaultValueState.hasDefaultValue parameterType: "secret",
? defaultValue collectionId,
: null, identityFields: identityFields
}); .split(",")
} .filter((s) => s.length > 0)
if (type === "credential") { .map((field) => field.trim()),
if (!collectionId) { identityKey,
toast({ description,
variant: "destructive", });
title: "Failed to save parameter", }
description: "Collection ID is required", if (type === "context") {
}); if (!sourceParameterKey) {
return; toast({
} variant: "destructive",
onSave({ title: "Failed to save parameter",
key, description: "Source parameter key is required",
parameterType: "credential", });
urlParameterKey, return;
collectionId, }
description, onSave({
}); key,
} parameterType: "context",
if (type === "secret") { sourceParameterKey,
if (!collectionId) { description,
toast({ });
variant: "destructive", }
title: "Failed to add parameter", }}
description: "Collection ID is required", >
}); Save
return; </Button>
} </div>
onSave({ </div>
key, </ScrollAreaViewport>
parameterType: "secret", </ScrollArea>
collectionId,
identityFields: identityFields
.split(",")
.filter((s) => s.length > 0)
.map((field) => field.trim()),
identityKey,
description,
});
}
if (type === "context") {
if (!sourceParameterKey) {
toast({
variant: "destructive",
title: "Failed to save parameter",
description: "Source parameter key is required",
});
return;
}
onSave({
key,
parameterType: "context",
sourceParameterKey,
description,
});
}
}}
>
Save
</Button>
</div>
</div>
); );
} }