diff --git a/studio/components/interfaces/Database/Hooks/CreateHookPanel/CreateHookPanel.tsx b/studio/components/interfaces/Database/Hooks/CreateHookPanel/CreateHookPanel.tsx
index 301ac766149..c51ab1721c7 100644
--- a/studio/components/interfaces/Database/Hooks/CreateHookPanel/CreateHookPanel.tsx
+++ b/studio/components/interfaces/Database/Hooks/CreateHookPanel/CreateHookPanel.tsx
@@ -8,6 +8,7 @@ import HTTPRequestFields from './HTTPRequestFields'
import { FormSection, FormSectionLabel, FormSectionContent } from 'components/ui/Forms'
import { useDatabaseTriggerCreateMutation } from 'data/database-triggers/database-trigger-create-mutation'
import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext'
+import { isValidHttpUrl } from '../Hooks.utils'
export interface CreateHookPanelProps {
visible: boolean
@@ -80,6 +81,8 @@ const CreateHookPanel = ({ visible, onClose }: CreateHookPanelProps) => {
errors['http_url'] = 'Please provide a URL'
} else if (!values.http_url.startsWith('http')) {
errors['http_url'] = 'Please include HTTP/HTTPs to your URL'
+ } else if (!isValidHttpUrl(values.http_url)) {
+ errors['http_url'] = 'Please provide a valid URL'
}
} else if (values.function_name === 'supabase_function') {
// For Supabase Edge Functions
@@ -117,14 +120,18 @@ const CreateHookPanel = ({ visible, onClose }: CreateHookPanelProps) => {
if (values.function_name === 'http_request') {
const serviceTimeoutMs = '1000'
- const headers = httpHeaders.reduce((a: any, b: any) => {
- a[b.name] = b.value
- return a
- }, {})
- const parameters = httpParameters.reduce((a: any, b: any) => {
- a[b.name] = b.value
- return a
- }, {})
+ const headers = httpHeaders
+ .filter((header) => header.name && header.value)
+ .reduce((a: any, b: any) => {
+ a[b.name] = b.value
+ return a
+ }, {})
+ const parameters = httpParameters
+ .filter((param) => param.name && param.value)
+ .reduce((a: any, b: any) => {
+ a[b.name] = b.value
+ return a
+ }, {})
payload.function_args = [
values.http_url,
values.http_method,
diff --git a/studio/components/interfaces/Database/Hooks/CreateHookPanel/HTTPRequestFields.tsx b/studio/components/interfaces/Database/Hooks/CreateHookPanel/HTTPRequestFields.tsx
index 3be1e661089..2d6f40eccce 100644
--- a/studio/components/interfaces/Database/Hooks/CreateHookPanel/HTTPRequestFields.tsx
+++ b/studio/components/interfaces/Database/Hooks/CreateHookPanel/HTTPRequestFields.tsx
@@ -110,7 +110,7 @@ const HTTPRequestFields = ({
}
+ icon={}
className="px-[10px] py-[9px]"
onClick={() => onRemoveParameter(idx)}
/>
diff --git a/studio/components/interfaces/Database/Hooks/DeleteHook.tsx b/studio/components/interfaces/Database/Hooks/DeleteHook.tsx
index 14efa7f8f5b..6de1eab2e9e 100644
--- a/studio/components/interfaces/Database/Hooks/DeleteHook.tsx
+++ b/studio/components/interfaces/Database/Hooks/DeleteHook.tsx
@@ -1,32 +1,41 @@
-import * as React from 'react'
+import { useState } from 'react'
import { observer } from 'mobx-react-lite'
import { useStore } from 'hooks'
import TextConfirmModal from 'components/ui/Modals/TextConfirmModal'
+import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext'
+import { useDatabaseTriggerDeleteMutation } from 'data/database-triggers/database-trigger-delete-mutation'
-type DeleteHookProps = {
+interface DeleteHookProps {
hook?: any
visible: boolean
- setVisible: (value: boolean) => void
-} & any
+ onClose: () => void
+}
-const DeleteHook: React.FC = ({ hook, visible, setVisible }) => {
- const { ui, meta } = useStore()
- const [loading, setLoading] = React.useState(false)
+const DeleteHook = ({ hook, visible, onClose }: DeleteHookProps) => {
+ const { ui } = useStore()
+ const [loading, setLoading] = useState(false)
const { id, name, schema } = hook ?? {}
+ const { project } = useProjectContext()
+ const { mutateAsync: deleteDatabaseTrigger } = useDatabaseTriggerDeleteMutation()
+
async function handleDelete() {
+ if (!project) {
+ return console.error('Project ref is required')
+ }
+ if (!id) {
+ return ui.setNotification({ category: 'error', message: 'Unable find selected hook' })
+ }
+
try {
setLoading(true)
- if (!id) {
- throw Error('Invalid hook info')
- }
- const response: any = await meta.hooks.del(id)
- if (response.error) {
- throw response.error
- } else {
- ui.setNotification({ category: 'success', message: `Successfully removed ${name}` })
- setVisible(false)
- }
+ await deleteDatabaseTrigger({
+ id,
+ projectRef: project.ref,
+ connectionString: project.connectionString,
+ })
+ ui.setNotification({ category: 'success', message: `Successfully deleted ${name}` })
+ onClose()
} catch (error: any) {
ui.setNotification({
category: 'error',
@@ -40,15 +49,16 @@ const DeleteHook: React.FC = ({ hook, visible, setVisible }) =>
return (
setVisible(!visible)}
+ size="medium"
+ onCancel={() => onClose()}
onConfirm={handleDelete}
- title="Delete this hook"
+ title="Delete database webhook"
loading={loading}
- confirmLabel={`Delete hook ${name}`}
- confirmPlaceholder="Type in name of hook"
+ confirmLabel={`Delete ${name}`}
+ confirmPlaceholder="Type in name of webhook"
confirmString={name}
- text={`This will delete your hook called ${name} of schema ${schema}.`}
- alert="You cannot recover this hook once it is deleted!"
+ text={`This will delete the webhook "${name}" from the schema "${schema}".`}
+ alert="You cannot recover this webhook once it is deleted!"
/>
)
}
diff --git a/studio/data/database-triggers/database-trigger-create-mutation.ts b/studio/data/database-triggers/database-trigger-create-mutation.ts
index bf5bb4db9d9..294c50c0deb 100644
--- a/studio/data/database-triggers/database-trigger-create-mutation.ts
+++ b/studio/data/database-triggers/database-trigger-create-mutation.ts
@@ -10,7 +10,7 @@ export type DatabaseTriggerCreateVariables = {
payload: any
}
-type createDatabaseTriggerResponse = PostgresTrigger & { error?: any }
+type CreateDatabaseTriggerResponse = PostgresTrigger & { error?: any }
export async function createDatabaseTrigger({
projectRef,
@@ -25,7 +25,7 @@ export async function createDatabaseTrigger({
const response = (await post(`${API_URL}/pg-meta/${projectRef}/triggers`, payload, {
headers: Object.fromEntries(headers),
- })) as createDatabaseTriggerResponse
+ })) as CreateDatabaseTriggerResponse
if (response?.error) throw response.error
return response as PostgresTrigger
diff --git a/studio/data/database-triggers/database-trigger-delete-mutation.ts b/studio/data/database-triggers/database-trigger-delete-mutation.ts
new file mode 100644
index 00000000000..05bbcfdc402
--- /dev/null
+++ b/studio/data/database-triggers/database-trigger-delete-mutation.ts
@@ -0,0 +1,56 @@
+import { PostgresTrigger } from '@supabase/postgres-meta'
+import { useMutation, UseMutationOptions, useQueryClient } from '@tanstack/react-query'
+import { delete_ } from 'lib/common/fetch'
+import { API_URL } from 'lib/constants'
+import { databaseTriggerKeys } from './keys'
+
+export type DatabaseTriggerDeleteVariables = {
+ id: number
+ projectRef: string
+ connectionString?: string
+}
+
+type DeleteDatabaseTriggerResponse = PostgresTrigger & { error?: any }
+
+export async function deleteDatabaseTrigger({
+ id,
+ projectRef,
+ connectionString,
+}: DatabaseTriggerDeleteVariables) {
+ if (!projectRef) throw new Error('projectRef is required')
+ if (!connectionString) throw new Error('connectionString is required')
+
+ let headers = new Headers()
+ headers.set('x-connection-encrypted', connectionString)
+
+ const response = (await delete_(`${API_URL}/pg-meta/${projectRef}/triggers?id=${id}`, undefined, {
+ headers: Object.fromEntries(headers),
+ })) as DeleteDatabaseTriggerResponse
+
+ if (response?.error) throw response.error
+ return response as PostgresTrigger
+}
+
+type DatabaseTriggerDeleteData = Awaited>
+
+export const useDatabaseTriggerDeleteMutation = ({
+ onSuccess,
+ ...options
+}: Omit<
+ UseMutationOptions,
+ 'mutationFn'
+> = {}) => {
+ const queryClient = useQueryClient()
+
+ return useMutation(
+ (vars) => deleteDatabaseTrigger(vars),
+ {
+ async onSuccess(data, variables, context) {
+ const { projectRef } = variables
+ await queryClient.invalidateQueries(databaseTriggerKeys.list(projectRef))
+ await onSuccess?.(data, variables, context)
+ },
+ ...options,
+ }
+ )
+}
diff --git a/studio/data/database-triggers/database-trigger-update-mutation.ts b/studio/data/database-triggers/database-trigger-update-mutation.ts
index 4ea811bd85c..5a183fc80f5 100644
--- a/studio/data/database-triggers/database-trigger-update-mutation.ts
+++ b/studio/data/database-triggers/database-trigger-update-mutation.ts
@@ -24,7 +24,7 @@ export async function updateDatabaseTrigger({
let headers = new Headers()
headers.set('x-connection-encrypted', connectionString)
- const response = await patch(`${API_URL}/pg-meta/${projectRef}/triggers/?id=${id}`, { payload })
+ const response = await patch(`${API_URL}/pg-meta/${projectRef}/triggers?id=${id}`, { payload })
if (response.error) throw response.error
return response as PostgresTrigger
diff --git a/studio/pages/project/[ref]/database/hooks/index.tsx b/studio/pages/project/[ref]/database/hooks/index.tsx
index ab1ffe7d6b4..d51f8bc7678 100644
--- a/studio/pages/project/[ref]/database/hooks/index.tsx
+++ b/studio/pages/project/[ref]/database/hooks/index.tsx
@@ -95,7 +95,7 @@ const HooksPage: NextPageWithLayout = () => {
setShowDeleteHookForm(false)}
/>
)