mirror of
https://github.com/supabase/supabase.git
synced 2026-10-10 20:05:06 +03:00
add http parameters
This commit is contained in:
1 parent
ee4417422b
commit
df9d4b1814
5 files changed
+150
-124
No files matched your search
+51
@@ -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<ConfigParamsFormValues>({
|
||||
defaultValues: {
|
||||
config_params: [{ name: 'search_path', value: 'public' }],
|
||||
},
|
||||
})
|
||||
|
||||
return (
|
||||
<Form_Shadcn_ {...form}>
|
||||
<CreateFunctionConfigParamsSection />
|
||||
</Form_Shadcn_>
|
||||
)
|
||||
}
|
||||
|
||||
describe('CreateFunctionConfigParamsSection', () => {
|
||||
it('appends a new config parameter row with name and value fields', async () => {
|
||||
const user = userEvent.setup()
|
||||
|
||||
render(<CreateFunctionConfigParamsHarness />)
|
||||
|
||||
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(<CreateFunctionConfigParamsHarness />)
|
||||
|
||||
expect(screen.getByDisplayValue('search_path')).toBeInTheDocument()
|
||||
|
||||
await user.click(screen.getByRole('button', { name: 'Remove configuration parameter' }))
|
||||
|
||||
expect(screen.queryByDisplayValue('search_path')).not.toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
+28
@@ -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<CreateFunctionConfigParamsFormValues>()
|
||||
|
||||
return (
|
||||
<>
|
||||
<h5 className="text-base text-foreground">Configuration Parameters</h5>
|
||||
<KeyValueFieldArray
|
||||
control={form.control}
|
||||
name="config_params"
|
||||
keyFieldName="name"
|
||||
valueFieldName="value"
|
||||
createEmptyRow={() => ({ name: '', value: '' })}
|
||||
keyPlaceholder="parameter_name"
|
||||
valuePlaceholder="parameter_value"
|
||||
addLabel="Add a new config"
|
||||
removeLabel="Remove configuration parameter"
|
||||
rowsClassName="space-y-2 pt-4"
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -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 = ({
|
||||
</SheetSection>
|
||||
<Separator className={focusedEditor ? 'hidden' : ''} />
|
||||
<SheetSection className={focusedEditor ? 'hidden' : ''}>
|
||||
<FormFieldConfigParams readonly={isEditing} />
|
||||
<CreateFunctionConfigParamsSection />
|
||||
</SheetSection>
|
||||
<Separator className={focusedEditor ? 'hidden' : ''} />
|
||||
<SheetSection className={focusedEditor ? 'hidden' : ''}>
|
||||
@@ -505,75 +506,6 @@ const FormFieldArgs = ({ readonly }: FormFieldConfigParamsProps) => {
|
||||
)
|
||||
}
|
||||
|
||||
interface FormFieldConfigParamsProps {
|
||||
readonly?: boolean
|
||||
}
|
||||
|
||||
const FormFieldConfigParams = ({ readonly }: FormFieldConfigParamsProps) => {
|
||||
const { fields, append, remove } = useFieldArray<z.infer<typeof FormSchema>>({
|
||||
name: 'config_params',
|
||||
})
|
||||
|
||||
return (
|
||||
<>
|
||||
<h5 className="text-base text-foreground">Configuration Parameters</h5>
|
||||
<div className="space-y-2 pt-4">
|
||||
{readonly && isEmpty(fields) && (
|
||||
<span className="text-foreground-lighter">No argument for this function</span>
|
||||
)}
|
||||
{fields.map((field, index) => {
|
||||
return (
|
||||
<div className="flex flex-row space-x-1" key={field.id}>
|
||||
<FormField_Shadcn_
|
||||
name={`config_params.${index}.name`}
|
||||
render={({ field }) => (
|
||||
<FormItem_Shadcn_ className="flex-1">
|
||||
<FormControl_Shadcn_>
|
||||
<Input_Shadcn_ {...field} placeholder="parameter_name" />
|
||||
</FormControl_Shadcn_>
|
||||
<FormMessage_Shadcn_ />
|
||||
</FormItem_Shadcn_>
|
||||
)}
|
||||
/>
|
||||
<FormField_Shadcn_
|
||||
name={`config_params.${index}.value`}
|
||||
render={({ field }) => (
|
||||
<FormItem_Shadcn_ className="flex-1">
|
||||
<FormControl_Shadcn_>
|
||||
<Input_Shadcn_ {...field} placeholder="parameter_value" />
|
||||
</FormControl_Shadcn_>
|
||||
<FormMessage_Shadcn_ />
|
||||
</FormItem_Shadcn_>
|
||||
)}
|
||||
/>
|
||||
|
||||
{!readonly && (
|
||||
<Button
|
||||
type="danger"
|
||||
icon={<Trash size={12} />}
|
||||
onClick={() => remove(index)}
|
||||
className="h-[38px] w-[38px]"
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
|
||||
{!readonly && (
|
||||
<Button
|
||||
type="default"
|
||||
icon={<Plus size={12} />}
|
||||
onClick={() => append({ name: '', type: '' })}
|
||||
disabled={readonly}
|
||||
>
|
||||
Add a new config
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
const ALL_ALLOWED_LANGUAGES = ['plpgsql', 'sql', 'plcoffee', 'plv8', 'plls']
|
||||
|
||||
const FormFieldLanguage = () => {
|
||||
|
||||
@@ -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<WebhookFormValues>({
|
||||
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 (
|
||||
<Form_Shadcn_ {...form}>
|
||||
<HTTPParameters form={form} />
|
||||
</Form_Shadcn_>
|
||||
)
|
||||
}
|
||||
|
||||
describe('HTTPParameters', () => {
|
||||
it('appends a new parameter row', async () => {
|
||||
const user = userEvent.setup()
|
||||
|
||||
render(<HTTPParametersHarness />)
|
||||
|
||||
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(<HTTPParametersHarness />)
|
||||
|
||||
expect(screen.getByDisplayValue('tenant')).toBeInTheDocument()
|
||||
|
||||
await user.click(screen.getByRole('button', { name: 'Remove parameter' }))
|
||||
|
||||
expect(screen.queryByDisplayValue('tenant')).not.toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
@@ -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 (
|
||||
<FormSection
|
||||
header={<FormSectionLabel className="lg:!col-span-4">HTTP Parameters</FormSectionLabel>}
|
||||
>
|
||||
<FormSectionContent loading={false} className="lg:!col-span-8">
|
||||
<div className="space-y-2">
|
||||
{paramFields.map((field, index) => (
|
||||
<div key={field.id} className="flex items-center space-x-2">
|
||||
<FormField_Shadcn_
|
||||
control={form.control}
|
||||
name={`httpParameters.${index}.name`}
|
||||
render={({ field }) => (
|
||||
<FormControl_Shadcn_>
|
||||
<Input_Shadcn_ {...field} className="w-full" placeholder="Parameter name" />
|
||||
</FormControl_Shadcn_>
|
||||
)}
|
||||
/>
|
||||
<FormField_Shadcn_
|
||||
control={form.control}
|
||||
name={`httpParameters.${index}.value`}
|
||||
render={({ field }) => (
|
||||
<FormControl_Shadcn_>
|
||||
<Input_Shadcn_ {...field} className="w-full" placeholder="Parameter value" />
|
||||
</FormControl_Shadcn_>
|
||||
)}
|
||||
/>
|
||||
<ButtonTooltip
|
||||
type="text"
|
||||
className="py-4"
|
||||
icon={<Trash />}
|
||||
onClick={() => removeParam(index)}
|
||||
tooltip={{ content: { side: 'bottom', text: 'Remove parameter' } }}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
<div>
|
||||
<Button
|
||||
type="default"
|
||||
size="tiny"
|
||||
icon={<Plus />}
|
||||
onClick={() => appendParam({ id: uuidv4(), name: '', value: '' })}
|
||||
>
|
||||
Add a new parameter
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<KeyValueFieldArray
|
||||
control={form.control}
|
||||
name="httpParameters"
|
||||
keyFieldName="name"
|
||||
valueFieldName="value"
|
||||
createEmptyRow={() => ({ id: uuidv4(), name: '', value: '' })}
|
||||
keyPlaceholder="Parameter name"
|
||||
valuePlaceholder="Parameter value"
|
||||
addLabel="Add a new parameter"
|
||||
removeLabel="Remove parameter"
|
||||
/>
|
||||
</FormSectionContent>
|
||||
</FormSection>
|
||||
)
|
||||
|
||||
Reference in new issue
Block a user