fix(frontend): fix auto select credential mechanism in new builder (#11171)

We’re currently facing two problems with credentials:

1. When we change the discriminator input value, the form data
credential field should be cleaned up completely.
2. When I select a different discriminator and if that provider has a
value, it should select the latest one.

So, in this PR, I’ve encountered both issues.

### Changes 🏗️
- Updated CredentialField to utilize a new setCredential function for
managing selected credentials.
- Implemented logic to auto-select the latest credential when none is
selected and clear the credential if the provider changes.
- Improved SelectCredential component with a defaultValue prop and
adjusted styling for better UI consistency.
- Removed unnecessary console logs from helper functions to clean up the
code.

<!-- Concisely describe all of the changes made in this pull request:
-->

### Checklist 📋

#### For code changes:
- [x] I have clearly listed my changes in the PR description
- [x] I have made a test plan
- [x] I have tested my changes according to the test plan:
- [x] Credential selection works perfectly with both the discriminator
and normal addition.
  - [x] Auto-select credential is also working.
This commit is contained in:
Abhimanyu Yadav
2025-10-15 14:09:05 +05:30
committed by GitHub
parent 22946f4617
commit 112c39f6a6
4 changed files with 83 additions and 20 deletions

View File

@@ -1,4 +1,4 @@
import React, { useEffect } from "react";
import React from "react";
import { FieldProps } from "@rjsf/utils";
import { useCredentialField } from "./useCredentialField";
import { SelectCredential } from "./SelectCredential";
@@ -24,22 +24,14 @@ export const CredentialsField = (props: FieldProps) => {
supportsUserPassword,
credentialsExists,
credentialProvider,
setCredential,
} = useCredentialField({
credentialSchema: schema as BlockIOCredentialsSubSchema,
formData,
nodeId: formContext.nodeId,
onChange,
});
const setField = (key: string, value: any) =>
onChange({ ...formData, [key]: value });
// This is to set the latest credential as the default one [currently, latest means last one in the list of credentials]
useEffect(() => {
if (!isCredentialListLoading && credentials.length > 0 && !formData.id) {
const latestCredential = credentials[credentials.length - 1];
setField("id", latestCredential.id);
}
}, [isCredentialListLoading, credentials, formData.id]);
if (isCredentialListLoading) {
return (
<div className="flex flex-col gap-2">
@@ -58,8 +50,8 @@ export const CredentialsField = (props: FieldProps) => {
{credentialsExists && (
<SelectCredential
credentials={credentials}
value={formData.id}
onChange={(value) => setField("id", value)}
value={formData.id || ""}
onChange={setCredential}
disabled={false}
label="Credential"
placeholder="Select credential"

View File

@@ -13,6 +13,7 @@ import { providerIcons } from "./helpers";
type SelectCredentialProps = {
credentials: CredentialsMetaResponse[];
value?: string;
defaultValue?: string;
onChange: (credentialId: string) => void;
disabled?: boolean;
label?: string;
@@ -67,7 +68,7 @@ export const SelectCredential: React.FC<SelectCredentialProps> = ({
<Select
label={label}
id="select-credential"
wrapperClassName="!mb-0 flex-1"
wrapperClassName="!mb-0 flex-1 !max-w-[90%]"
value={value}
onValueChange={onChange}
options={options}
@@ -77,8 +78,12 @@ export const SelectCredential: React.FC<SelectCredentialProps> = ({
hideLabel
/>
<Link href={`/profile/integrations`}>
<Button variant="outline" size="icon" className="h-8 w-8 p-0">
<ArrowSquareOutIcon className="h-4 w-4" />
<Button
variant="outline"
size="icon"
className="h-8 w-8 border-zinc-300 p-0"
>
<ArrowSquareOutIcon className="h-4 w-4 text-zinc-600" />
</Button>
</Link>
</div>

View File

@@ -15,8 +15,6 @@ export const filterCredentialsByProvider = (
credentials: CredentialsMetaResponse[] | undefined,
provider: string,
) => {
console.log("provider", provider);
console.log("credentials", credentials);
const filtered =
credentials?.filter((credential) => provider === credential.provider) ?? [];
return {
@@ -129,7 +127,6 @@ export const getCredentialProviderFromSchema = (
);
return null;
}
console.log("discriminatedProvider", discriminatedProvider);
return discriminatedProvider;
} else {
return providers[0];

View File

@@ -6,14 +6,21 @@ import {
getCredentialProviderFromSchema,
} from "./helpers";
import { useNodeStore } from "@/app/(platform)/build/stores/nodeStore";
import { useEffect, useRef } from "react";
export const useCredentialField = ({
credentialSchema,
formData,
nodeId,
onChange,
}: {
credentialSchema: BlockIOCredentialsSubSchema; // Here we are using manual typing, we need to fix it with automatic one
formData: Record<string, any>;
nodeId: string;
onChange: (value: Record<string, any>) => void;
}) => {
const previousProviderRef = useRef<string | null>(null);
// Fetch all the credentials from the backend
// We will save it in cache for 10 min, if user edits the credential, we will invalidate the cache
// Whenever user adds a block, we filter the credentials list and check if this block's provider is in the list
@@ -44,9 +51,71 @@ export const useCredentialField = ({
const { credentials: filteredCredentials, exists: credentialsExists } =
filterCredentialsByProvider(credentials, credentialProvider ?? "");
const setCredential = (credentialId: string) => {
const selectedCredential = filteredCredentials.find(
(c) => c.id === credentialId,
);
if (selectedCredential) {
onChange({
...formData,
id: selectedCredential.id,
provider: selectedCredential.provider,
title: selectedCredential.title,
type: selectedCredential.type,
});
}
};
// This side effect is used to clear the hardcoded value in credential formData when the provider changes
useEffect(() => {
if (!credentialProvider) return;
// If provider has changed and we have a credential selected
if (
previousProviderRef.current !== null &&
previousProviderRef.current !== credentialProvider &&
formData.id
) {
// Check if the current credential belongs to the new provider
const currentCredentialBelongsToProvider = filteredCredentials.some(
(c) => c.id === formData.id,
);
// If not, clear the credential
if (!currentCredentialBelongsToProvider) {
onChange({
id: "",
provider: "",
title: "",
type: "",
});
}
}
previousProviderRef.current = credentialProvider;
}, [credentialProvider, formData.id, credentials, onChange]);
// This side effect is used to auto-select the latest credential when none is selected [latest means last one in the list of credentials]
useEffect(() => {
if (
!isCredentialListLoading &&
filteredCredentials.length > 0 &&
!formData.id && // No credential currently selected
credentialProvider // Provider is set
) {
const latestCredential =
filteredCredentials[filteredCredentials.length - 1];
setCredential(latestCredential.id);
}
}, [
isCredentialListLoading,
filteredCredentials.length,
formData.id,
credentialProvider,
]);
return {
credentials: filteredCredentials,
isCredentialListLoading,
setCredential,
supportsApiKey,
supportsOAuth2,
supportsUserPassword,