mirror of
https://github.com/supabase/supabase.git
synced 2026-10-06 09:55:06 +03:00
## Context PR here refactors the way we manage Foreign Data Wrappers in the dashboard (Under Project -> Integrations), as there's some DX problems with the current behaviour. Currently whenever a user creates a new wrapper, the dashboard is creating both the Foreign Data Wrapper (`create foreign data wrapper...`) + server (`create server ...`). The former is **_redundant_** to create multiples of given that it just handles the `handler` and `validator`, whereas what matters more is the server which holds the connection credentials. Hence standard practice is usually one Foreign Data Wrapper with multiple servers. (The former just needs to be created once if not done yet) This also led to some problems as well when users created their own wrappers via SQL and tried to manage them through the dashboard GUI, leading to us having to add some guard rails to prevent managing wrappers sharing the same Foreign Data Wrapper ([ref](https://github.com/supabase/supabase/pull/50785)) ## Changes involved - When creating a wrapper, if the Foreign Data Wrapper has yet to be set up for the wrapper type, the dashboard will initialize one and subsequently use that same Foreign Data Wrapper for any new wrappers - When creating / editing a wrapper, users will name the **server** instead of the **wrapper** <img width="500" alt="image" src="https://github.com/user-attachments/assets/b3e61204-0e16-4599-84ac-af2aab5b93c2" /> - When deleting a wrapper, the clean up for vault secrets are now deterministic by referencing the wrapper's server options - RE backwards compatibility: Existing wrappers will _not_ be affected by the changes here - they can be edited / deleted as per normal ## Unrelated fixes + UI refactors added - Fix Iceberg Wrapper not showing the right form when adding new wrapper - Adjust form layouts in side panel to be horizontal instead of vertical (Follows Database -> Pipelines) - Clean up to use newer UI components like `ButtonTooltip` - Opt to hide Docs + Create CTA under `WrappersTab` if marketplace feature preview is enabled (Since these actions are already in the header, will be duplicates) - Consolidate foreign tables configuration for create + edit wrapper sheet into one component `ForeignTablesSelector` ## To test - [ ] Verify that existing wrappers with their own Foreign Data Wrapper can be edited correctly - [ ] Verify that existing wrappers with their own Foreign Data Wrapper can be deleted - [ ] Verify that existing wrappers with shared Foreign Data Wrapper can be edited correctly - [ ] Verify that existing wrappers with shared Foreign Data Wrapper can be deleted - [ ] Verify that new wrappers can be created - [ ] Verify that newly created wrappers can be edited correctly - [ ] Verify that newly created wrappers can be deleted
133 lines
4.9 KiB
TypeScript
133 lines
4.9 KiB
TypeScript
import { useParams } from 'common'
|
|
import Link from 'next/link'
|
|
import { Button } from 'ui'
|
|
import { Admonition } from 'ui-patterns/Admonition'
|
|
|
|
import { IntegrationOverviewTab } from '../Integration/IntegrationOverviewTab'
|
|
import { RequiredExtensionsSection } from '../Integration/RequiredExtensionsSection'
|
|
import { useAvailableIntegrations } from '../Landing/useAvailableIntegrations'
|
|
import { AddWrapperButton } from './AddWrapperButton'
|
|
import { WRAPPERS } from './Wrappers.constants'
|
|
import { WrapperTable } from './WrapperTable'
|
|
import { useIsMarketplaceEnabled } from '@/components/interfaces/App/FeaturePreview/FeaturePreviewContext'
|
|
import { getServiceVersionsPath } from '@/components/interfaces/Settings/General/ServiceVersions/ServiceVersions.utils'
|
|
import { ScaffoldContainer, ScaffoldSection } from '@/components/layouts/Scaffold'
|
|
import { useDatabaseExtensionsQuery } from '@/data/database-extensions/database-extensions-query'
|
|
import { useSelectedProjectQuery } from '@/hooks/misc/useSelectedProject'
|
|
|
|
const WrapperOverviewContent = () => {
|
|
return (
|
|
<div className="flex flex-col gap-y-5 max-w-5xl">
|
|
<div className="flex items-center justify-between">
|
|
<p>Recent wrappers</p>
|
|
<AddWrapperButton variant="primary" />
|
|
</div>
|
|
<WrapperTable />
|
|
</div>
|
|
)
|
|
}
|
|
|
|
const AddNewWrapperCTA = () => {
|
|
const { id } = useParams()
|
|
const { data: project } = useSelectedProjectQuery()
|
|
|
|
const { data } = useDatabaseExtensionsQuery({
|
|
projectRef: project?.ref,
|
|
connectionString: project?.connectionString,
|
|
})
|
|
|
|
const wrapperMeta = WRAPPERS.find((w) => w.name === id)
|
|
const wrappersExtension = data?.find((ext) => ext.name === 'wrappers')
|
|
const isWrappersExtensionInstalled = !!wrappersExtension?.installed_version
|
|
const hasRequiredVersion =
|
|
(wrappersExtension?.installed_version ?? '') >= (wrapperMeta?.minimumExtensionVersion ?? '')
|
|
// [Joshen] Default version is what's on the DB, so if the installed version is already the default version
|
|
// but still doesnt meet the minimum extension version, then DB upgrade is required
|
|
const databaseNeedsUpgrading =
|
|
wrappersExtension?.installed_version === wrappersExtension?.default_version
|
|
|
|
if (!!wrapperMeta && isWrappersExtensionInstalled && !hasRequiredVersion) {
|
|
return (
|
|
<Admonition type="warning" title="Your extension version is outdated for this wrapper">
|
|
<div className="flex flex-col gap-y-2 [&>p]:mb-0!">
|
|
<p>
|
|
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 <code className="text-code-inline">wrappers</code> extension
|
|
to create this wrapper.
|
|
</p>
|
|
<p className="text-warning">
|
|
Warning: Before reinstalling the wrapper extension, you must first remove all existing
|
|
wrappers. Afterward, you can recreate the wrappers.
|
|
</p>
|
|
</div>
|
|
<Button asChild className="w-min mt-3">
|
|
<Link
|
|
href={
|
|
databaseNeedsUpgrading
|
|
? getServiceVersionsPath(project?.ref)
|
|
: `/project/${project?.ref}/database/extensions?filter=wrappers`
|
|
}
|
|
>
|
|
{databaseNeedsUpgrading ? 'Upgrade database' : 'View wrappers extension'}
|
|
</Link>
|
|
</Button>
|
|
</Admonition>
|
|
)
|
|
}
|
|
|
|
return null
|
|
}
|
|
|
|
export const WrapperContent = () => {
|
|
const { id } = useParams()
|
|
const { data: project } = useSelectedProjectQuery()
|
|
|
|
const { data: integrations = [] } = useAvailableIntegrations()
|
|
const integration = integrations.find((i) => i.id === id)
|
|
const wrapperMeta = WRAPPERS.find((w) => w.name === id)
|
|
|
|
const { data: extensions } = useDatabaseExtensionsQuery({
|
|
projectRef: project?.ref,
|
|
connectionString: project?.connectionString,
|
|
})
|
|
const installableExtensions = (extensions ?? []).filter((ext) =>
|
|
(integration?.requiredExtensions ?? []).includes(ext.name)
|
|
)
|
|
const isInstalled = installableExtensions.every((x) => x.installed_version)
|
|
|
|
if (!wrapperMeta) {
|
|
return (
|
|
<ScaffoldContainer>
|
|
<ScaffoldSection isFullWidth>
|
|
<p className="text-sm text-foreground-light">Unsupported integration type</p>
|
|
</ScaffoldSection>
|
|
</ScaffoldContainer>
|
|
)
|
|
}
|
|
|
|
return (
|
|
<>
|
|
<RequiredExtensionsSection />
|
|
<AddNewWrapperCTA />
|
|
{isInstalled && <WrapperOverviewContent />}
|
|
</>
|
|
)
|
|
}
|
|
|
|
export const WrapperOverviewTab = () => {
|
|
const isMarketplaceEnabled = useIsMarketplaceEnabled()
|
|
|
|
if (isMarketplaceEnabled) return <RequiredExtensionsSection />
|
|
|
|
return (
|
|
<IntegrationOverviewTab actions={<AddNewWrapperCTA />}>
|
|
<div className="mx-10">
|
|
<WrapperOverviewContent />
|
|
</div>
|
|
</IntegrationOverviewTab>
|
|
)
|
|
}
|