From f37317e28ae175bee3a8c495d25293b4288ffd5d Mon Sep 17 00:00:00 2001 From: yuneng Date: Fri, 25 Sep 2026 07:21:10 +0000 Subject: [PATCH] fix(ui): trim credential_alias in CredentialModal so whitespace-only input clears the alias Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- .../model_add/CredentialModal.test.tsx | 28 +++++++++++++++++++ .../components/model_add/CredentialModal.tsx | 6 +++- 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/ui/litellm-dashboard/src/components/model_add/CredentialModal.test.tsx b/ui/litellm-dashboard/src/components/model_add/CredentialModal.test.tsx index 6801531ce43..e68c1230b10 100644 --- a/ui/litellm-dashboard/src/components/model_add/CredentialModal.test.tsx +++ b/ui/litellm-dashboard/src/components/model_add/CredentialModal.test.tsx @@ -196,5 +196,33 @@ describe("CredentialModal", () => { await waitFor(() => expect(onSubmit).toHaveBeenCalled()); expect(onSubmit.mock.calls[0][0].credential_alias).toBeNull(); }); + + it("trims the alias before submitting in add mode", async () => { + const user = userEvent.setup({ pointerEventsCheck: PointerEventsCheckLevel.Never }); + const onSubmit = vi.fn(); + renderModal({ mode: "add", onSubmit }); + + await fillRequiredAddFields(user); + fireEvent.change(screen.getByLabelText("Alias:"), { target: { value: " Prod " } }); + fireEvent.click(screen.getByRole("button", { name: "Add Credential" })); + + await waitFor(() => expect(onSubmit).toHaveBeenCalled()); + expect(onSubmit.mock.calls[0][0]).toMatchObject({ credential_name: "new-cred", credential_alias: "Prod" }); + }); + + it("submits credential_alias: null when the alias is whitespace-only in edit mode", async () => { + const onSubmit = vi.fn(); + renderModal({ + mode: "edit", + onSubmit, + existingCredential: { ...mockCredential, credential_alias: "Prod OpenAI" }, + }); + + fireEvent.change(screen.getByLabelText("Alias:"), { target: { value: " " } }); + fireEvent.click(screen.getByRole("button", { name: "Update Credential" })); + + await waitFor(() => expect(onSubmit).toHaveBeenCalled()); + expect(onSubmit.mock.calls[0][0].credential_alias).toBeNull(); + }); }); }); diff --git a/ui/litellm-dashboard/src/components/model_add/CredentialModal.tsx b/ui/litellm-dashboard/src/components/model_add/CredentialModal.tsx index ef3875ee107..49371d921ae 100644 --- a/ui/litellm-dashboard/src/components/model_add/CredentialModal.tsx +++ b/ui/litellm-dashboard/src/components/model_add/CredentialModal.tsx @@ -72,7 +72,11 @@ export default function CredentialModal({ return; } const values = projectMountedValues(registry, form.getValues); - const filteredValues = Object.entries(values).reduce((acc, [key, value]) => { + const trimmedValues = + typeof values.credential_alias === "string" + ? { ...values, credential_alias: values.credential_alias.trim() } + : values; + const filteredValues = Object.entries(trimmedValues).reduce((acc, [key, value]) => { if (value !== "" && value !== undefined && value !== null) { acc[key] = value; }