From df9d4b1814666a490cfde3416aad2ff734be2d8d Mon Sep 17 00:00:00 2001 From: Danny White <3104761+dnywh@users.noreply.github.com> Date: Mon, 23 Mar 2026 11:21:54 +1100 Subject: [PATCH] add http parameters --- ...CreateFunctionConfigParamsSection.test.tsx | 51 +++++++++++++ .../CreateFunctionConfigParamsSection.tsx | 28 ++++++++ .../Functions/CreateFunction/index.tsx | 72 +------------------ .../Database/Hooks/HTTPParameters.test.tsx | 56 +++++++++++++++ .../Database/Hooks/HTTPParameters.tsx | 67 ++++------------- 5 files changed, 150 insertions(+), 124 deletions(-) create mode 100644 apps/studio/components/interfaces/Database/Functions/CreateFunction/CreateFunctionConfigParamsSection.test.tsx create mode 100644 apps/studio/components/interfaces/Database/Functions/CreateFunction/CreateFunctionConfigParamsSection.tsx create mode 100644 apps/studio/components/interfaces/Database/Hooks/HTTPParameters.test.tsx diff --git a/apps/studio/components/interfaces/Database/Functions/CreateFunction/CreateFunctionConfigParamsSection.test.tsx b/apps/studio/components/interfaces/Database/Functions/CreateFunction/CreateFunctionConfigParamsSection.test.tsx new file mode 100644 index 00000000000..f8cb0f439f7 --- /dev/null +++ b/apps/studio/components/interfaces/Database/Functions/CreateFunction/CreateFunctionConfigParamsSection.test.tsx @@ -0,0 +1,51 @@ +import { render, screen } from '@testing-library/react' +import userEvent from '@testing-library/user-event' +import { useForm } from 'react-hook-form' +import { describe, expect, it } from 'vitest' + +import { Form_Shadcn_ } from 'ui' + +import { CreateFunctionConfigParamsSection } from './CreateFunctionConfigParamsSection' + +type ConfigParamsFormValues = { + config_params: Array<{ name: string; value: string }> +} + +const CreateFunctionConfigParamsHarness = () => { + const form = useForm({ + defaultValues: { + config_params: [{ name: 'search_path', value: 'public' }], + }, + }) + + return ( + + + + ) +} + +describe('CreateFunctionConfigParamsSection', () => { + it('appends a new config parameter row with name and value fields', async () => { + const user = userEvent.setup() + + render() + + await user.click(screen.getByRole('button', { name: 'Add a new config' })) + + expect(screen.getAllByPlaceholderText('parameter_name')).toHaveLength(2) + expect(screen.getAllByPlaceholderText('parameter_value')).toHaveLength(2) + }) + + it('removes an existing config parameter row', async () => { + const user = userEvent.setup() + + render() + + expect(screen.getByDisplayValue('search_path')).toBeInTheDocument() + + await user.click(screen.getByRole('button', { name: 'Remove configuration parameter' })) + + expect(screen.queryByDisplayValue('search_path')).not.toBeInTheDocument() + }) +}) diff --git a/apps/studio/components/interfaces/Database/Functions/CreateFunction/CreateFunctionConfigParamsSection.tsx b/apps/studio/components/interfaces/Database/Functions/CreateFunction/CreateFunctionConfigParamsSection.tsx new file mode 100644 index 00000000000..51c40d1cfcf --- /dev/null +++ b/apps/studio/components/interfaces/Database/Functions/CreateFunction/CreateFunctionConfigParamsSection.tsx @@ -0,0 +1,28 @@ +import { useFormContext } from 'react-hook-form' +import { KeyValueFieldArray } from 'ui-patterns/form/KeyValueFieldArray/KeyValueFieldArray' + +type CreateFunctionConfigParamsFormValues = { + config_params: Array<{ name: string; value: string }> +} + +export const CreateFunctionConfigParamsSection = () => { + const form = useFormContext() + + return ( + <> +
Configuration Parameters
+ ({ name: '', value: '' })} + keyPlaceholder="parameter_name" + valuePlaceholder="parameter_value" + addLabel="Add a new config" + removeLabel="Remove configuration parameter" + rowsClassName="space-y-2 pt-4" + /> + + ) +} diff --git a/apps/studio/components/interfaces/Database/Functions/CreateFunction/index.tsx b/apps/studio/components/interfaces/Database/Functions/CreateFunction/index.tsx index 77727b3a454..47cb97a11b9 100644 --- a/apps/studio/components/interfaces/Database/Functions/CreateFunction/index.tsx +++ b/apps/studio/components/interfaces/Database/Functions/CreateFunction/index.tsx @@ -44,6 +44,7 @@ import { import { DiscardChangesConfirmationDialog } from 'components/ui-patterns/Dialogs/DiscardChangesConfirmationDialog' import { FormItemLayout } from 'ui-patterns/form/FormItemLayout/FormItemLayout' import { convertArgumentTypes, convertConfigParams } from '../Functions.utils' +import { CreateFunctionConfigParamsSection } from './CreateFunctionConfigParamsSection' import { CreateFunctionHeader } from './CreateFunctionHeader' import { FunctionEditor } from './FunctionEditor' @@ -331,7 +332,7 @@ export const CreateFunction = ({ - + @@ -505,75 +506,6 @@ const FormFieldArgs = ({ readonly }: FormFieldConfigParamsProps) => { ) } -interface FormFieldConfigParamsProps { - readonly?: boolean -} - -const FormFieldConfigParams = ({ readonly }: FormFieldConfigParamsProps) => { - const { fields, append, remove } = useFieldArray>({ - name: 'config_params', - }) - - return ( - <> -
Configuration Parameters
-
- {readonly && isEmpty(fields) && ( - No argument for this function - )} - {fields.map((field, index) => { - return ( -
- ( - - - - - - - )} - /> - ( - - - - - - - )} - /> - - {!readonly && ( -
- ) - })} - - {!readonly && ( - - )} -
- - ) -} - const ALL_ALLOWED_LANGUAGES = ['plpgsql', 'sql', 'plcoffee', 'plv8', 'plls'] const FormFieldLanguage = () => { diff --git a/apps/studio/components/interfaces/Database/Hooks/HTTPParameters.test.tsx b/apps/studio/components/interfaces/Database/Hooks/HTTPParameters.test.tsx new file mode 100644 index 00000000000..a6f15d6659e --- /dev/null +++ b/apps/studio/components/interfaces/Database/Hooks/HTTPParameters.test.tsx @@ -0,0 +1,56 @@ +import { render, screen } from '@testing-library/react' +import userEvent from '@testing-library/user-event' +import { useForm } from 'react-hook-form' +import { describe, expect, it } from 'vitest' + +import { Form_Shadcn_ } from 'ui' + +import { type WebhookFormValues } from './EditHookPanel.constants' +import { HTTPParameters } from './HTTPParameters' + +const HTTPParametersHarness = () => { + const form = useForm({ + defaultValues: { + name: 'test-hook', + table_id: 'public.messages', + http_method: 'POST', + timeout_ms: 1000, + events: ['INSERT'], + function_type: 'http_request', + http_url: 'https://hooks.example.com/webhook', + httpHeaders: [], + httpParameters: [{ id: 'param-1', name: 'tenant', value: 'prod' }], + }, + }) + + return ( + + + + ) +} + +describe('HTTPParameters', () => { + it('appends a new parameter row', async () => { + const user = userEvent.setup() + + render() + + await user.click(screen.getByRole('button', { name: 'Add a new parameter' })) + + expect(screen.getAllByPlaceholderText('Parameter name')).toHaveLength(2) + expect(screen.getAllByPlaceholderText('Parameter value')).toHaveLength(2) + }) + + it('removes an existing parameter row', async () => { + const user = userEvent.setup() + + render() + + expect(screen.getByDisplayValue('tenant')).toBeInTheDocument() + + await user.click(screen.getByRole('button', { name: 'Remove parameter' })) + + expect(screen.queryByDisplayValue('tenant')).not.toBeInTheDocument() + }) +}) diff --git a/apps/studio/components/interfaces/Database/Hooks/HTTPParameters.tsx b/apps/studio/components/interfaces/Database/Hooks/HTTPParameters.tsx index 7daeb981b66..af2402db0c7 100644 --- a/apps/studio/components/interfaces/Database/Hooks/HTTPParameters.tsx +++ b/apps/studio/components/interfaces/Database/Hooks/HTTPParameters.tsx @@ -1,9 +1,7 @@ -import { Plus, Trash } from 'lucide-react' -import { useFieldArray, UseFormReturn } from 'react-hook-form' -import { Button, FormControl_Shadcn_, FormField_Shadcn_, Input_Shadcn_, SidePanel } from 'ui' +import { UseFormReturn } from 'react-hook-form' +import { KeyValueFieldArray } from 'ui-patterns/form/KeyValueFieldArray/KeyValueFieldArray' import { WebhookFormValues } from './EditHookPanel.constants' -import { ButtonTooltip } from '@/components/ui/ButtonTooltip' import { FormSection, FormSectionContent, @@ -16,61 +14,22 @@ interface HTTPParametersProps { } export const HTTPParameters = ({ form }: HTTPParametersProps) => { - const { - fields: paramFields, - append: appendParam, - remove: removeParam, - } = useFieldArray({ - control: form.control, - name: 'httpParameters', - }) - return ( HTTP Parameters} > -
- {paramFields.map((field, index) => ( -
- ( - - - - )} - /> - ( - - - - )} - /> - } - onClick={() => removeParam(index)} - tooltip={{ content: { side: 'bottom', text: 'Remove parameter' } }} - /> -
- ))} -
- -
-
+ ({ id: uuidv4(), name: '', value: '' })} + keyPlaceholder="Parameter name" + valuePlaceholder="Parameter value" + addLabel="Add a new parameter" + removeLabel="Remove parameter" + />
)