import { PermissionAction } from '@supabase/shared-types/out/constants' import { useQueryClient } from '@tanstack/react-query' import { isEmpty } from 'lodash' import { ArrowLeft, Edit, Trash } from 'lucide-react' import Link from 'next/link' import { useRouter } from 'next/router' import { useState } from 'react' import { toast } from 'sonner' import { useParams } from 'common' import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext' import { DocsButton } from 'components/ui/DocsButton' import { FormActions } from 'components/ui/Forms/FormActions' import { FormPanel } from 'components/ui/Forms/FormPanel' import { FormSection, FormSectionContent, FormSectionLabel } from 'components/ui/Forms/FormSection' import { FormsContainer } from 'components/ui/Forms/FormsContainer' import { useDatabaseExtensionsQuery } from 'data/database-extensions/database-extensions-query' import { invalidateSchemasQuery } from 'data/database/schemas-query' import { useFDWCreateMutation } from 'data/fdw/fdw-create-mutation' import { useCheckPermissions } from 'hooks/misc/useCheckPermissions' import { AlertDescription_Shadcn_, AlertTitle_Shadcn_, Alert_Shadcn_, Button, Form, Input, WarningIcon, cn, } from 'ui' import InputField from './InputField' import WrapperTableEditor from './WrapperTableEditor' import { WRAPPERS } from './Wrappers.constants' import { makeValidateRequired } from './Wrappers.utils' const CreateWrapper = () => { const formId = 'create-wrapper-form' const router = useRouter() const queryClient = useQueryClient() const { ref, type } = useParams() const { project } = useProjectContext() const canCreateWrapper = useCheckPermissions(PermissionAction.TENANT_SQL_ADMIN_WRITE, 'wrappers') const [newTables, setNewTables] = useState([]) const [isEditingTable, setIsEditingTable] = useState(false) const [selectedTableToEdit, setSelectedTableToEdit] = useState() const [formErrors, setFormErrors] = useState<{ [k: string]: string }>({}) const { mutate: createFDW, isLoading: isCreating } = useFDWCreateMutation({ onSuccess: () => { toast.success(`Successfully created ${wrapperMeta?.label} foreign data wrapper`) setNewTables([]) const hasNewSchema = newTables.some((table) => table.is_new_schema) if (hasNewSchema) invalidateSchemasQuery(queryClient, ref) router.push(`/project/${ref}/integrations/wrappers`) }, }) const wrapperMeta = WRAPPERS.find((wrapper) => wrapper.name === type) const initialValues = wrapperMeta !== undefined ? { wrapper_name: '', server_name: '', ...Object.fromEntries( wrapperMeta.server.options.map((option) => [option.name, option.defaultValue ?? '']) ), } : {} const { data } = useDatabaseExtensionsQuery({ projectRef: project?.ref, connectionString: project?.connectionString, }) const wrappersExtension = data?.find((ext) => ext.name === 'wrappers') const hasRequiredVersion = (wrappersExtension?.installed_version ?? '') >= (wrapperMeta?.minimumExtensionVersion ?? '') if (wrapperMeta === undefined) { return (

Unsupported wrapper type

The wrapper type {type} not supported by the dashboard. Head back to create a different wrapper.

) } const databaseNeedsUpgrading = wrappersExtension?.installed_version !== wrappersExtension?.default_version const onUpdateTable = (values: any) => { setNewTables((prev) => { // if the new values have tableIndex, we are editing an existing table if (values.tableIndex !== undefined) { const tableIndex = values.tableIndex const newTables = [...prev] delete values.tableIndex newTables[tableIndex] = values return newTables } return [...prev, values] }) setIsEditingTable(false) setSelectedTableToEdit(undefined) } const onSubmit = async (values: any) => { const validate = makeValidateRequired(wrapperMeta.server.options) const errors: any = validate(values) const { wrapper_name } = values if (wrapper_name.length === 0) errors.name = 'Please provide a name for your wrapper' if (newTables.length === 0) errors.tables = 'Please add at least one table' if (!isEmpty(errors)) return setFormErrors(errors) createFDW({ projectRef: project?.ref, connectionString: project?.connectionString, wrapperMeta, formState: { ...values, server_name: `${wrapper_name}_server` }, tables: newTables, }) } return (

Back

Create a {wrapperMeta.label} Wrapper

{hasRequiredVersion ? (
{({ handleReset, values, initialValues }: any) => { const hasChanges = JSON.stringify(values) !== JSON.stringify(initialValues) return ( } > Wrapper Configuration}> 0 ? ( <> Your wrapper's server name will be{' '} {values.wrapper_name}_server ) : ( '' ) } /> {wrapperMeta.label} Configuration} > {wrapperMeta.server.options .filter((option) => !option.hidden) .map((option) => ( ))}

Foreign Tables

You can query your data from these foreign tables after the wrapper is created

} > {newTables.length === 0 ? (
) : (
{newTables.map((table, i) => (

{table.schema_name}.{table.table_name}

Columns:{' '} {table.columns.map((column: any) => column.name).join(', ')}

))}
)} {newTables.length > 0 && (
)} {newTables.length === 0 && formErrors.tables && (

{formErrors.tables}

)}
) }}
) : ( Your extension version is outdated for this wrapper.

The {wrapperMeta.label} wrapper requires a minimum extension version of{' '} {wrapperMeta.minimumExtensionVersion}. You have version{' '} {wrappersExtension?.installed_version} installed. Please{' '} {databaseNeedsUpgrading && 'upgrade your database then '}update the extension by disabling and enabling the wrappers extension to create this wrapper.

Warning: Before reinstalling the wrapper extension, you must first remove all existing wrappers. Afterward, you can recreate the wrappers.

)} { setSelectedTableToEdit(undefined) setIsEditingTable(false) }} onSave={onUpdateTable} initialData={selectedTableToEdit} />
) } export default CreateWrapper