Merge credential parameters in the UI (#1853)

This commit is contained in:
Shuchang Zheng
2025-02-27 11:47:24 -08:00
committed by GitHub
parent ab2212c6fb
commit 5c5464b187
10 changed files with 172 additions and 129 deletions

View File

@@ -38,7 +38,6 @@ import {
WorkflowApiResponse, WorkflowApiResponse,
WorkflowEditorParameterTypes, WorkflowEditorParameterTypes,
WorkflowParameterTypes, WorkflowParameterTypes,
WorkflowParameterValueType,
WorkflowSettings, WorkflowSettings,
} from "../types/workflowTypes"; } from "../types/workflowTypes";
import { import {
@@ -83,6 +82,7 @@ import {
nodeAdderNode, nodeAdderNode,
startNode, startNode,
} from "./workflowEditorUtils"; } from "./workflowEditorUtils";
import { parameterIsBitwardenCredential, ParametersState } from "./types";
function convertToParametersYAML( function convertToParametersYAML(
parameters: ParametersState, parameters: ParametersState,
@@ -145,76 +145,33 @@ function convertToParametersYAML(
bitwarden_master_password_aws_secret_key: bitwarden_master_password_aws_secret_key:
BITWARDEN_MASTER_PASSWORD_AWS_SECRET_KEY, BITWARDEN_MASTER_PASSWORD_AWS_SECRET_KEY,
}; };
} else if (
parameter.parameterType === WorkflowEditorParameterTypes.Credential
) {
return {
parameter_type: WorkflowParameterTypes.Credential,
key: parameter.key,
description: parameter.description || null,
credential_id: parameter.credentialId,
};
} else { } else {
return { if (parameterIsBitwardenCredential(parameter)) {
parameter_type: WorkflowParameterTypes.Bitwarden_Login_Credential, return {
key: parameter.key, parameter_type: WorkflowParameterTypes.Bitwarden_Login_Credential,
description: parameter.description || null, key: parameter.key,
bitwarden_collection_id: parameter.collectionId, description: parameter.description || null,
url_parameter_key: parameter.urlParameterKey, bitwarden_collection_id: parameter.collectionId,
bitwarden_client_id_aws_secret_key: BITWARDEN_CLIENT_ID_AWS_SECRET_KEY, url_parameter_key: parameter.urlParameterKey,
bitwarden_client_secret_aws_secret_key: bitwarden_client_id_aws_secret_key:
BITWARDEN_CLIENT_SECRET_AWS_SECRET_KEY, BITWARDEN_CLIENT_ID_AWS_SECRET_KEY,
bitwarden_master_password_aws_secret_key: bitwarden_client_secret_aws_secret_key:
BITWARDEN_MASTER_PASSWORD_AWS_SECRET_KEY, BITWARDEN_CLIENT_SECRET_AWS_SECRET_KEY,
}; bitwarden_master_password_aws_secret_key:
BITWARDEN_MASTER_PASSWORD_AWS_SECRET_KEY,
};
} else {
return {
parameter_type: WorkflowParameterTypes.Credential,
key: parameter.key,
description: parameter.description || null,
credential_id: parameter.credentialId,
};
}
} }
}); });
} }
export type ParametersState = Array<
| {
key: string;
parameterType: "workflow";
dataType: WorkflowParameterValueType;
description?: string | null;
defaultValue: unknown;
}
| {
key: string;
parameterType: "bitwardenLoginCredential";
collectionId: string;
urlParameterKey: string;
description?: string | null;
}
| {
key: string;
parameterType: "context";
sourceParameterKey: string;
description?: string | null;
}
| {
key: string;
parameterType: "secret";
identityKey: string;
identityFields: Array<string>;
collectionId: string;
description?: string | null;
}
| {
key: string;
parameterType: "creditCardData";
itemId: string;
collectionId: string;
description?: string | null;
}
| {
key: string;
parameterType: "credential";
credentialId: string;
description?: string | null;
}
>;
type Props = { type Props = {
initialTitle: string; initialTitle: string;
initialNodes: Array<AppNode>; initialNodes: Array<AppNode>;

View File

@@ -129,8 +129,7 @@ function WorkflowEditor() {
} else { } else {
return { return {
key: parameter.key, key: parameter.key,
parameterType: parameterType: WorkflowEditorParameterTypes.Credential,
WorkflowEditorParameterTypes.BitwardenLoginCredential,
collectionId: parameter.bitwarden_collection_id, collectionId: parameter.bitwarden_collection_id,
urlParameterKey: parameter.url_parameter_key, urlParameterKey: parameter.url_parameter_key,
description: parameter.description, description: parameter.description,

View File

@@ -1,5 +1,5 @@
import { createContext } from "react"; import { createContext } from "react";
import { ParametersState } from "./FlowRenderer"; import { ParametersState } from "./types";
type WorkflowParametersState = [ type WorkflowParametersState = [
ParametersState, ParametersState,

View File

@@ -16,9 +16,7 @@ type Props = {
function CredentialParameterSelector({ value, onChange }: Props) { function CredentialParameterSelector({ value, onChange }: Props) {
const [workflowParameters] = useWorkflowParametersState(); const [workflowParameters] = useWorkflowParametersState();
const credentialParameters = workflowParameters.filter( const credentialParameters = workflowParameters.filter(
(parameter) => (parameter) => parameter.parameterType === "credential",
parameter.parameterType === "credential" ||
parameter.parameterType === "bitwardenLoginCredential",
); );
const noneItemValue = useId(); const noneItemValue = useId();

View File

@@ -15,7 +15,7 @@ import {
SelectValue, SelectValue,
} from "@/components/ui/select"; } from "@/components/ui/select";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { ParametersState } from "../FlowRenderer"; import { ParametersState } from "../types";
import { WorkflowParameterInput } from "../../WorkflowParameterInput"; import { WorkflowParameterInput } from "../../WorkflowParameterInput";
import { Checkbox } from "@/components/ui/checkbox"; import { Checkbox } from "@/components/ui/checkbox";
import { getDefaultValueForParameterType } from "../workflowEditorUtils"; import { getDefaultValueForParameterType } from "../workflowEditorUtils";
@@ -24,6 +24,7 @@ import { SourceParameterKeySelector } from "../../components/SourceParameterKeyS
import { ScrollArea, ScrollAreaViewport } from "@/components/ui/scroll-area"; import { ScrollArea, ScrollAreaViewport } from "@/components/ui/scroll-area";
import CloudContext from "@/store/CloudContext"; import CloudContext from "@/store/CloudContext";
import { CredentialSelector } from "../../components/CredentialSelector"; import { CredentialSelector } from "../../components/CredentialSelector";
import { SwitchBar } from "@/components/SwitchBar";
type Props = { type Props = {
type: WorkflowEditorParameterType; type: WorkflowEditorParameterType;
@@ -47,9 +48,6 @@ function header(type: WorkflowEditorParameterType) {
if (type === "credential") { if (type === "credential") {
return "Add Credential Parameter"; return "Add Credential Parameter";
} }
if (type === "bitwardenLoginCredential") {
return "Add Bitwarden Login Credential Parameter";
}
if (type === "secret") { if (type === "secret") {
return "Add Secret Parameter"; return "Add Secret Parameter";
} }
@@ -78,6 +76,10 @@ function WorkflowParameterAddPanel({ type, onClose, onSave }: Props) {
string | undefined string | undefined
>(undefined); >(undefined);
const [credentialType, setCredentialType] = useState<"bitwarden" | "skyvern">(
"skyvern",
);
const [identityKey, setIdentityKey] = useState(""); const [identityKey, setIdentityKey] = useState("");
const [identityFields, setIdentityFields] = useState(""); const [identityFields, setIdentityFields] = useState("");
const [itemId, setItemId] = useState(""); const [itemId, setItemId] = useState("");
@@ -189,7 +191,19 @@ function WorkflowParameterAddPanel({ type, onClose, onSave }: Props) {
</div> </div>
</> </>
)} )}
{type === "bitwardenLoginCredential" && ( {type === "credential" && (
<SwitchBar
value={credentialType}
onChange={(value) => {
setCredentialType(value as "bitwarden" | "skyvern");
}}
options={[
{ label: "Skyvern", value: "skyvern" },
{ label: "Bitwarden", value: "bitwarden" },
]}
/>
)}
{type === "credential" && credentialType === "bitwarden" && (
<> <>
<div className="space-y-1"> <div className="space-y-1">
<Label className="text-xs text-slate-300"> <Label className="text-xs text-slate-300">
@@ -265,15 +279,17 @@ function WorkflowParameterAddPanel({ type, onClose, onSave }: Props) {
)} )}
{ {
// temporarily cloud only // temporarily cloud only
type === "credential" && isCloud && ( type === "credential" &&
<div className="space-y-1"> credentialType === "skyvern" &&
<Label className="text-xs text-slate-300">Credential</Label> isCloud && (
<CredentialSelector <div className="space-y-1">
value={credentialId} <Label className="text-xs text-slate-300">Credential</Label>
onChange={(value) => setCredentialId(value)} <CredentialSelector
/> value={credentialId}
</div> onChange={(value) => setCredentialId(value)}
) />
</div>
)
} }
<div className="flex justify-end"> <div className="flex justify-end">
<Button <Button
@@ -318,7 +334,7 @@ function WorkflowParameterAddPanel({ type, onClose, onSave }: Props) {
}); });
} }
if ( if (
type === "bitwardenLoginCredential" || (type === "credential" && credentialType === "bitwarden") ||
type === "secret" || type === "secret" ||
type === "creditCardData" type === "creditCardData"
) { ) {
@@ -331,10 +347,10 @@ function WorkflowParameterAddPanel({ type, onClose, onSave }: Props) {
return; return;
} }
} }
if (type === "bitwardenLoginCredential") { if (type === "credential" && credentialType === "bitwarden") {
onSave({ onSave({
key, key,
parameterType: "bitwardenLoginCredential", parameterType: "credential",
collectionId, collectionId,
urlParameterKey, urlParameterKey,
description, description,
@@ -378,7 +394,7 @@ function WorkflowParameterAddPanel({ type, onClose, onSave }: Props) {
description, description,
}); });
} }
if (type === "credential") { if (type === "credential" && credentialType === "skyvern") {
if (!credentialId) { if (!credentialId) {
toast({ toast({
variant: "destructive", variant: "destructive",

View File

@@ -15,7 +15,6 @@ import {
SelectValue, SelectValue,
} from "@/components/ui/select"; } from "@/components/ui/select";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { ParametersState } from "../FlowRenderer";
import { Checkbox } from "@/components/ui/checkbox"; import { Checkbox } from "@/components/ui/checkbox";
import { getDefaultValueForParameterType } from "../workflowEditorUtils"; import { getDefaultValueForParameterType } from "../workflowEditorUtils";
import { WorkflowParameterInput } from "../../WorkflowParameterInput"; import { WorkflowParameterInput } from "../../WorkflowParameterInput";
@@ -24,6 +23,12 @@ import { SourceParameterKeySelector } from "../../components/SourceParameterKeyS
import { ScrollArea, ScrollAreaViewport } from "@/components/ui/scroll-area"; import { ScrollArea, ScrollAreaViewport } from "@/components/ui/scroll-area";
import CloudContext from "@/store/CloudContext"; import CloudContext from "@/store/CloudContext";
import { CredentialSelector } from "../../components/CredentialSelector"; import { CredentialSelector } from "../../components/CredentialSelector";
import { SwitchBar } from "@/components/SwitchBar";
import {
parameterIsBitwardenCredential,
parameterIsSkyvernCredential,
ParametersState,
} from "../types";
type Props = { type Props = {
type: WorkflowEditorParameterType; type: WorkflowEditorParameterType;
@@ -51,9 +56,6 @@ function header(type: WorkflowEditorParameterType) {
if (type === "secret") { if (type === "secret") {
return "Edit Secret Parameter"; return "Edit Secret Parameter";
} }
if (type === "bitwardenLoginCredential") {
return "Edit Bitwarden Login Credential Parameter";
}
if (type === "creditCardData") { if (type === "creditCardData") {
return "Edit Credit Card Data Parameter"; return "Edit Credit Card Data Parameter";
} }
@@ -68,16 +70,23 @@ function WorkflowParameterEditPanel({
}: Props) { }: Props) {
const isCloud = useContext(CloudContext); const isCloud = useContext(CloudContext);
const [key, setKey] = useState(initialValues.key); const [key, setKey] = useState(initialValues.key);
const isBitwardenCredential =
initialValues.parameterType === "credential" &&
parameterIsBitwardenCredential(initialValues);
const isSkyvernCredential =
initialValues.parameterType === "credential" &&
parameterIsSkyvernCredential(initialValues);
const [credentialType, setCredentialType] = useState<"bitwarden" | "skyvern">(
isBitwardenCredential ? "bitwarden" : "skyvern",
);
const [urlParameterKey, setUrlParameterKey] = useState( const [urlParameterKey, setUrlParameterKey] = useState(
initialValues.parameterType === "bitwardenLoginCredential" isBitwardenCredential ? initialValues.urlParameterKey : "",
? initialValues.urlParameterKey
: "",
); );
const [description, setDescription] = useState( const [description, setDescription] = useState(
initialValues.description ?? "", initialValues.description ?? "",
); );
const [collectionId, setCollectionId] = useState( const [collectionId, setCollectionId] = useState(
initialValues.parameterType === "bitwardenLoginCredential" || isBitwardenCredential ||
initialValues.parameterType === "secret" || initialValues.parameterType === "secret" ||
initialValues.parameterType === "creditCardData" initialValues.parameterType === "creditCardData"
? initialValues.collectionId ? initialValues.collectionId
@@ -130,9 +139,7 @@ function WorkflowParameterEditPanel({
); );
const [credentialId, setCredentialId] = useState( const [credentialId, setCredentialId] = useState(
initialValues.parameterType === "credential" isSkyvernCredential ? initialValues.credentialId : "",
? initialValues.credentialId
: "",
); );
return ( return (
@@ -240,7 +247,19 @@ function WorkflowParameterEditPanel({
</div> </div>
</> </>
)} )}
{type === "bitwardenLoginCredential" && ( {type === "credential" && (
<SwitchBar
value={credentialType}
onChange={(value) => {
setCredentialType(value as "bitwarden" | "skyvern");
}}
options={[
{ label: "Skyvern", value: "skyvern" },
{ label: "Bitwarden", value: "bitwarden" },
]}
/>
)}
{type === "credential" && credentialType === "bitwarden" && (
<> <>
<div className="space-y-1"> <div className="space-y-1">
<Label className="text-xs text-slate-300"> <Label className="text-xs text-slate-300">
@@ -316,15 +335,17 @@ function WorkflowParameterEditPanel({
)} )}
{ {
// temporarily cloud only // temporarily cloud only
type === "credential" && isCloud && ( type === "credential" &&
<div className="space-y-1"> credentialType === "skyvern" &&
<Label className="text-xs text-slate-300">Credential</Label> isCloud && (
<CredentialSelector <div className="space-y-1">
value={credentialId} <Label className="text-xs text-slate-300">Credential</Label>
onChange={(value) => setCredentialId(value)} <CredentialSelector
/> value={credentialId}
</div> onChange={(value) => setCredentialId(value)}
) />
</div>
)
} }
<div className="flex justify-end"> <div className="flex justify-end">
<Button <Button
@@ -369,7 +390,7 @@ function WorkflowParameterEditPanel({
}); });
} }
if ( if (
type === "bitwardenLoginCredential" || (type === "credential" && credentialType === "bitwarden") ||
type === "secret" || type === "secret" ||
type === "creditCardData" type === "creditCardData"
) { ) {
@@ -382,10 +403,10 @@ function WorkflowParameterEditPanel({
return; return;
} }
} }
if (type === "bitwardenLoginCredential") { if (type === "credential" && credentialType === "bitwarden") {
onSave({ onSave({
key, key,
parameterType: "bitwardenLoginCredential", parameterType: "credential",
urlParameterKey, urlParameterKey,
collectionId, collectionId,
description, description,
@@ -429,7 +450,7 @@ function WorkflowParameterEditPanel({
description, description,
}); });
} }
if (type === "credential") { if (type === "credential" && credentialType === "skyvern") {
if (!credentialId) { if (!credentialId) {
toast({ toast({
variant: "destructive", variant: "destructive",

View File

@@ -1,7 +1,7 @@
import { useState } from "react"; import { useState } from "react";
import { useWorkflowParametersState } from "../useWorkflowParametersState"; import { useWorkflowParametersState } from "../useWorkflowParametersState";
import { WorkflowParameterAddPanel } from "./WorkflowParameterAddPanel"; import { WorkflowParameterAddPanel } from "./WorkflowParameterAddPanel";
import { ParametersState } from "../FlowRenderer"; import { ParametersState } from "../types";
import { WorkflowParameterEditPanel } from "./WorkflowParameterEditPanel"; import { WorkflowParameterEditPanel } from "./WorkflowParameterEditPanel";
import { MixerVerticalIcon, PlusIcon } from "@radix-ui/react-icons"; import { MixerVerticalIcon, PlusIcon } from "@radix-ui/react-icons";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
@@ -96,17 +96,6 @@ function WorkflowParametersPanel() {
> >
Credential Parameter Credential Parameter
</DropdownMenuItem> </DropdownMenuItem>
<DropdownMenuItem
onClick={() => {
setOperationPanelState({
active: true,
operation: "add",
type: WorkflowEditorParameterTypes.BitwardenLoginCredential,
});
}}
>
Bitwarden Login Credential Parameter
</DropdownMenuItem>
<DropdownMenuItem <DropdownMenuItem
onClick={() => { onClick={() => {
setOperationPanelState({ setOperationPanelState({

View File

@@ -0,0 +1,64 @@
import { WorkflowParameterValueType } from "../types/workflowTypes";
export type BitwardenLoginCredential = {
key: string;
description?: string | null;
parameterType: "credential";
collectionId: string;
urlParameterKey: string;
};
export type SkyvernCredential = {
key: string;
description?: string | null;
parameterType: "credential";
credentialId: string;
};
export function parameterIsBitwardenCredential(
parameter: CredentialParameterState,
): parameter is BitwardenLoginCredential {
return "collectionId" in parameter;
}
export function parameterIsSkyvernCredential(
parameter: CredentialParameterState,
): parameter is SkyvernCredential {
return "credentialId" in parameter;
}
export type CredentialParameterState =
| BitwardenLoginCredential
| SkyvernCredential;
export type ParametersState = Array<
| {
key: string;
parameterType: "workflow";
dataType: WorkflowParameterValueType;
description?: string | null;
defaultValue: unknown;
}
| {
key: string;
parameterType: "context";
sourceParameterKey: string;
description?: string | null;
}
| {
key: string;
parameterType: "secret";
identityKey: string;
identityFields: Array<string>;
collectionId: string;
description?: string | null;
}
| {
key: string;
parameterType: "creditCardData";
itemId: string;
collectionId: string;
description?: string | null;
}
| CredentialParameterState
>;

View File

@@ -48,7 +48,7 @@ import {
SMTP_USERNAME_AWS_KEY, SMTP_USERNAME_AWS_KEY,
SMTP_USERNAME_PARAMETER_KEY, SMTP_USERNAME_PARAMETER_KEY,
} from "./constants"; } from "./constants";
import { ParametersState } from "./FlowRenderer"; import { ParametersState } from "./types";
import { AppNode, isWorkflowBlockNode, WorkflowBlockNode } from "./nodes"; import { AppNode, isWorkflowBlockNode, WorkflowBlockNode } from "./nodes";
import { codeBlockNodeDefaultData } from "./nodes/CodeBlockNode/types"; import { codeBlockNodeDefaultData } from "./nodes/CodeBlockNode/types";
import { downloadNodeDefaultData } from "./nodes/DownloadNode/types"; import { downloadNodeDefaultData } from "./nodes/DownloadNode/types";

View File

@@ -213,7 +213,6 @@ export type WorkflowBlockType =
export const WorkflowEditorParameterTypes = { export const WorkflowEditorParameterTypes = {
Workflow: "workflow", Workflow: "workflow",
BitwardenLoginCredential: "bitwardenLoginCredential",
Credential: "credential", Credential: "credential",
Secret: "secret", Secret: "secret",
Context: "context", Context: "context",