fix login block credential selection (#4458)
This commit is contained in:
@@ -8,7 +8,7 @@ import {
|
|||||||
import { Skeleton } from "@/components/ui/skeleton";
|
import { Skeleton } from "@/components/ui/skeleton";
|
||||||
import { useCredentialsQuery } from "@/routes/workflows/hooks/useCredentialsQuery";
|
import { useCredentialsQuery } from "@/routes/workflows/hooks/useCredentialsQuery";
|
||||||
import CloudContext from "@/store/CloudContext";
|
import CloudContext from "@/store/CloudContext";
|
||||||
import { useContext } from "react";
|
import { useContext, useMemo } from "react";
|
||||||
import { useWorkflowParametersStore } from "@/store/WorkflowParametersStore";
|
import { useWorkflowParametersStore } from "@/store/WorkflowParametersStore";
|
||||||
import { CredentialsModal } from "@/routes/credentials/CredentialsModal";
|
import { CredentialsModal } from "@/routes/credentials/CredentialsModal";
|
||||||
import { PlusIcon } from "@radix-ui/react-icons";
|
import { PlusIcon } from "@radix-ui/react-icons";
|
||||||
@@ -68,6 +68,17 @@ function LoginBlockCredentialSelector({ nodeId, value, onChange }: Props) {
|
|||||||
page_size: 100,
|
page_size: 100,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Determine which credential is currently selected (by credential_id)
|
||||||
|
// This must be before the early return to comply with React hooks rules
|
||||||
|
const selectedCredentialId = useMemo(() => {
|
||||||
|
if (!value) return undefined;
|
||||||
|
const parameter = credentialParameters.find((p) => p.key === value);
|
||||||
|
if (parameter && parameterIsSkyvernCredential(parameter)) {
|
||||||
|
return parameter.credentialId;
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
}, [value, credentialParameters]);
|
||||||
|
|
||||||
if (isCloud && isFetching) {
|
if (isCloud && isFetching) {
|
||||||
return <Skeleton className="h-8 w-full" />;
|
return <Skeleton className="h-8 w-full" />;
|
||||||
}
|
}
|
||||||
@@ -78,11 +89,26 @@ function LoginBlockCredentialSelector({ nodeId, value, onChange }: Props) {
|
|||||||
type: "credential",
|
type: "credential",
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const credentialParameterOptions = credentialParameters.map((parameter) => ({
|
// Get the set of credential IDs that are in the vault
|
||||||
label: parameter.key,
|
const credentialIdsInVault = new Set(credentials.map((c) => c.credential_id));
|
||||||
value: parameter.key,
|
|
||||||
type: "parameter",
|
// Filter credential parameters to only show those that reference credentials
|
||||||
}));
|
// NOT in the vault (e.g., Bitwarden, 1Password, Azure Vault credentials)
|
||||||
|
// Skyvern credential parameters are excluded because the actual credential is already shown
|
||||||
|
const filteredCredentialParameterOptions = credentialParameters
|
||||||
|
.filter((parameter) => {
|
||||||
|
if (parameterIsSkyvernCredential(parameter)) {
|
||||||
|
// Don't show Skyvern credential parameters if the credential is in the vault
|
||||||
|
return !credentialIdsInVault.has(parameter.credentialId);
|
||||||
|
}
|
||||||
|
// Show non-Skyvern credential parameters (Bitwarden, 1Password, etc.)
|
||||||
|
return true;
|
||||||
|
})
|
||||||
|
.map((parameter) => ({
|
||||||
|
label: parameter.key,
|
||||||
|
value: parameter.key,
|
||||||
|
type: "parameter",
|
||||||
|
}));
|
||||||
|
|
||||||
const credentialInputParameterOptions = credentialInputParameters.map(
|
const credentialInputParameterOptions = credentialInputParameters.map(
|
||||||
(parameter) => ({
|
(parameter) => ({
|
||||||
@@ -92,13 +118,6 @@ function LoginBlockCredentialSelector({ nodeId, value, onChange }: Props) {
|
|||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
const filteredCredentialParameterOptions = credentialParameterOptions.filter(
|
|
||||||
(option) =>
|
|
||||||
!credentialOptions.some(
|
|
||||||
(credential) => credential.value === option.value,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
const options = [
|
const options = [
|
||||||
...credentialOptions,
|
...credentialOptions,
|
||||||
...filteredCredentialParameterOptions,
|
...filteredCredentialParameterOptions,
|
||||||
@@ -108,7 +127,7 @@ function LoginBlockCredentialSelector({ nodeId, value, onChange }: Props) {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Select
|
<Select
|
||||||
value={value}
|
value={selectedCredentialId ?? value}
|
||||||
onValueChange={(newValue) => {
|
onValueChange={(newValue) => {
|
||||||
if (newValue === "new") {
|
if (newValue === "new") {
|
||||||
setIsOpen(true);
|
setIsOpen(true);
|
||||||
@@ -122,19 +141,20 @@ function LoginBlockCredentialSelector({ nodeId, value, onChange }: Props) {
|
|||||||
.filter((node) => node.id !== nodeId)
|
.filter((node) => node.id !== nodeId)
|
||||||
.filter(isLoginNode);
|
.filter(isLoginNode);
|
||||||
|
|
||||||
const thereIsAParameterWithThisValue = workflowParameters.some(
|
// Check if current value references a Skyvern credential
|
||||||
(parameter) =>
|
const currentParameter = workflowParameters.find((parameter) => {
|
||||||
parameter.parameterType === "credential" &&
|
if (parameter.parameterType !== "credential") return false;
|
||||||
parameterIsSkyvernCredential(parameter) &&
|
if (!parameterIsSkyvernCredential(parameter)) return false;
|
||||||
parameter.credentialId === value,
|
return parameter.key === value;
|
||||||
);
|
});
|
||||||
|
|
||||||
const isUsedInOtherLoginNodes =
|
const isUsedInOtherLoginNodes =
|
||||||
value &&
|
value &&
|
||||||
loginNodes.some((node) => node.data.parameterKeys.includes(value));
|
loginNodes.some((node) => node.data.parameterKeys.includes(value));
|
||||||
|
|
||||||
|
// Only delete old parameter if it's not used elsewhere
|
||||||
const deleteOldParameter =
|
const deleteOldParameter =
|
||||||
thereIsAParameterWithThisValue && !isUsedInOtherLoginNodes;
|
currentParameter && !isUsedInOtherLoginNodes;
|
||||||
|
|
||||||
if (deleteOldParameter) {
|
if (deleteOldParameter) {
|
||||||
newParameters = newParameters.filter(
|
newParameters = newParameters.filter(
|
||||||
@@ -142,21 +162,28 @@ function LoginBlockCredentialSelector({ nodeId, value, onChange }: Props) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const option = options.find((option) => option.value === newValue);
|
// Check if user selected an actual credential (by credential_id)
|
||||||
|
const selectedCredential = credentialOptions.find(
|
||||||
|
(option) => option.value === newValue,
|
||||||
|
);
|
||||||
|
|
||||||
let parameterKeyToUse = newValue;
|
let parameterKeyToUse = newValue;
|
||||||
if (option?.type === "credential") {
|
|
||||||
const existingCredential = workflowParameters.find((parameter) => {
|
if (selectedCredential) {
|
||||||
|
// User selected an actual credential
|
||||||
|
const existingParameter = newParameters.find((parameter) => {
|
||||||
return (
|
return (
|
||||||
parameter.parameterType === "credential" &&
|
parameter.parameterType === "credential" &&
|
||||||
"credentialId" in parameter &&
|
parameterIsSkyvernCredential(parameter) &&
|
||||||
parameter.credentialId === newValue
|
parameter.credentialId === newValue
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
if (existingCredential) {
|
|
||||||
// Use the existing parameter's key
|
if (existingParameter) {
|
||||||
parameterKeyToUse = existingCredential.key;
|
// Reuse the existing parameter
|
||||||
|
parameterKeyToUse = existingParameter.key;
|
||||||
} else {
|
} else {
|
||||||
// Generate a new parameter key based on existing keys
|
// Create a new parameter for this credential
|
||||||
const existingKeys = newParameters.map((param) => param.key);
|
const existingKeys = newParameters.map((param) => param.key);
|
||||||
const newKey =
|
const newKey =
|
||||||
generateDefaultCredentialParameterKey(existingKeys);
|
generateDefaultCredentialParameterKey(existingKeys);
|
||||||
@@ -171,17 +198,19 @@ function LoginBlockCredentialSelector({ nodeId, value, onChange }: Props) {
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
} else if (deleteOldParameter) {
|
|
||||||
newParameters = newParameters.filter(
|
|
||||||
(parameter) => parameter.key !== value,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
onChange?.(parameterKeyToUse);
|
// If user selected a parameter (non-Skyvern credential or input parameter)
|
||||||
|
// just use it directly (parameterKeyToUse is already set to newValue)
|
||||||
|
|
||||||
|
// Update Zustand store first, then call onChange
|
||||||
|
// This ensures workflowParameters is updated before the parent re-renders
|
||||||
|
// with the new value, so selectedCredentialId computes correctly
|
||||||
setWorkflowParameters(newParameters);
|
setWorkflowParameters(newParameters);
|
||||||
|
onChange?.(parameterKeyToUse);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<SelectTrigger className="w-full">
|
<SelectTrigger className="w-full">
|
||||||
<SelectValue placeholder="Select a credential parameter" />
|
<SelectValue placeholder="Select a credential" />
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent>
|
<SelectContent>
|
||||||
{options.map((option) => (
|
{options.map((option) => (
|
||||||
@@ -202,7 +231,7 @@ function LoginBlockCredentialSelector({ nodeId, value, onChange }: Props) {
|
|||||||
const existingKeys = workflowParameters.map((param) => param.key);
|
const existingKeys = workflowParameters.map((param) => param.key);
|
||||||
const newKey = generateDefaultCredentialParameterKey(existingKeys);
|
const newKey = generateDefaultCredentialParameterKey(existingKeys);
|
||||||
|
|
||||||
onChange?.(newKey);
|
// Update Zustand store first, then call onChange
|
||||||
setWorkflowParameters([
|
setWorkflowParameters([
|
||||||
...workflowParameters,
|
...workflowParameters,
|
||||||
{
|
{
|
||||||
@@ -211,6 +240,7 @@ function LoginBlockCredentialSelector({ nodeId, value, onChange }: Props) {
|
|||||||
key: newKey,
|
key: newKey,
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
onChange?.(newKey);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
|
|||||||
Reference in New Issue
Block a user