Skip to content

Commit af5e555

Browse files
authored
feat: Show alert to upgrade DB on queues integration (supabase#30925)
* List all integrations in the landing page. * Add a configuration in the integration definitions to show an alert if all their dependencies can't be installed. * Refactor the Integration Overview tab to show an integration specific alert if all its dependencies can't be met because of outdated image. * Fix smol misspell. * Update copy. * Fix copy.
1 parent 5f0a8ef commit af5e555

File tree

6 files changed

+74
-49
lines changed

6 files changed

+74
-49
lines changed

apps/studio/components/interfaces/Integrations/Integration/IntegrationOverviewTab.tsx

Lines changed: 23 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { Markdown } from 'components/interfaces/Markdown'
66
import { useDatabaseExtensionsQuery } from 'data/database-extensions/database-extensions-query'
77
import { useSelectedProject } from 'hooks/misc/useSelectedProject'
88
import { Badge, Separator } from 'ui'
9-
import { Admonition } from 'ui-patterns'
9+
import { Admonition } from 'ui-patterns/admonition'
1010
import { INTEGRATIONS } from '../Landing/Integrations.constants'
1111
import { BuiltBySection } from './BuildBySection'
1212
import { MarkdownContent } from './MarkdownContent'
@@ -26,23 +26,26 @@ export const IntegrationOverviewTab = ({
2626

2727
const integration = INTEGRATIONS.find((i) => i.id === id)
2828

29-
const dependsOnExtension = (integration?.requiredExtensions ?? []).length > 0
30-
3129
const { data: extensions } = useDatabaseExtensionsQuery({
3230
projectRef: project?.ref,
3331
connectionString: project?.connectionString,
3432
})
3533

36-
const neededExtensions = (extensions ?? []).filter((ext) =>
37-
(integration?.requiredExtensions ?? []).includes(ext.name)
38-
)
39-
40-
const hasMissingExtensions = neededExtensions.some((x) => !x.installed_version)
41-
4234
if (!integration) {
4335
return <div>Unsupported integration type</div>
4436
}
4537

38+
const dependsOnExtension = (integration.requiredExtensions ?? []).length > 0
39+
40+
const installableExtensions = (extensions ?? []).filter((ext) =>
41+
(integration.requiredExtensions ?? []).includes(ext.name)
42+
)
43+
const hasToInstallExtensions = installableExtensions.some((x) => !x.installed_version)
44+
45+
// The integration requires extensions that are not available to install on the current database image
46+
const hasMissingExtensions =
47+
installableExtensions.length !== integration.requiredExtensions.length
48+
4649
return (
4750
<div className="flex flex-col gap-8 py-10">
4851
<BuiltBySection integration={integration} />
@@ -65,19 +68,23 @@ export const IntegrationOverviewTab = ({
6568
className="max-w-full"
6669
content={`This integration uses the ${integration.requiredExtensions.map((x) => `\`${x}\``).join(', ')}
6770
extension${integration.requiredExtensions.length > 1 ? 's' : ''} directly in your Postgres database.
68-
${hasMissingExtensions ? `Install ${integration.requiredExtensions.length > 1 ? 'these' : 'this'} database extension${integration.requiredExtensions.length > 1 ? 's' : ''} to use ${integration.name} in your project.` : ''}
71+
${hasToInstallExtensions && !hasMissingExtensions ? `Install ${integration.requiredExtensions.length > 1 ? 'these' : 'this'} database extension${integration.requiredExtensions.length > 1 ? 's' : ''} to use ${integration.name} in your project.` : ''}
6972
`}
7073
/>
7174

72-
<div className="flex flex-row gap-x-2">
73-
{neededExtensions.map((extension) => (
74-
<MissingExtensionAlert key={extension.name} extension={extension} />
75-
))}
76-
</div>
75+
{hasMissingExtensions ? (
76+
integration.missingExtensionsAlert
77+
) : (
78+
<div className="flex flex-row gap-x-2">
79+
{installableExtensions.map((extension) => (
80+
<MissingExtensionAlert key={extension.name} extension={extension} />
81+
))}
82+
</div>
83+
)}
7784
</Admonition>
7885
</div>
7986
)}
80-
{!!actions && !hasMissingExtensions && <div className="px-10 max-w-4xl">{actions}</div>}
87+
{!!actions && !hasToInstallExtensions && <div className="px-10 max-w-4xl">{actions}</div>}
8188
<MarkdownContent key={integration.id} integrationId={integration.id} />
8289
<Separator />
8390
{children}

apps/studio/components/interfaces/Integrations/Landing/Integrations.constants.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { ComponentType, ReactNode } from 'react'
66
import { GenericSkeletonLoader } from 'components/ui/ShimmeringLoader'
77
import { BASE_PATH } from 'lib/constants'
88
import { cn } from 'ui'
9+
import { UpgradeDatabaseAlert } from '../Queues/UpgradeDatabaseAlert'
910
import { WRAPPERS } from '../Wrappers/Wrappers.constants'
1011
import { WrapperMeta } from '../Wrappers/Wrappers.types'
1112

@@ -35,6 +36,8 @@ export type IntegrationDefinition = {
3536
websiteUrl: string
3637
}
3738
requiredExtensions: string[]
39+
/** Optional component to render if the integration requires extensions that are not available on the current database image */
40+
missingExtensionsAlert?: ReactNode
3841
navigation?: Navigation[]
3942
navigate: (
4043
id: string,
@@ -53,6 +56,7 @@ const supabaseIntegrations: IntegrationDefinition[] = [
5356
id: 'queues',
5457
type: 'postgres_extension' as const,
5558
requiredExtensions: ['pgmq'],
59+
missingExtensionsAlert: <UpgradeDatabaseAlert />,
5660
name: `Queues`,
5761
icon: ({ className, ...props } = {}) => (
5862
<Layers className={cn('inset-0 p-2 text-black w-full h-full', className)} {...props} />

apps/studio/components/interfaces/Integrations/Landing/useInstalledIntegrations.tsx

Lines changed: 6 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -65,26 +65,12 @@ export const useInstalledIntegrations = () => {
6565
}).sort((a, b) => a.name.localeCompare(b.name))
6666
}, [wrappers, extensions, isHooksEnabled])
6767

68-
// available integrations are all integrations that can be installed. If an extension is on available to be installed
69-
// on the project, that integration is hidden.
70-
const availableIntegrations = useMemo(() => {
71-
return INTEGRATIONS.filter((i) => {
72-
// special handling for supabase webhooks
73-
if (i.id === 'webhooks') {
74-
return true
75-
}
76-
if (i.type === 'wrapper') {
77-
return true
78-
}
79-
if (i.type === 'postgres_extension') {
80-
return i.requiredExtensions.every((extName) => {
81-
const foundExtension = (extensions ?? []).find((ext) => ext.name === extName)
82-
return !!foundExtension
83-
})
84-
}
85-
return false
86-
}).sort((a, b) => a.name.localeCompare(b.name))
87-
}, [extensions])
68+
// available integrations are all integrations that can be installed. If an integration can't be installed (needed
69+
// extensions are not available on this DB image), the UI will provide a tooltip explaining why.
70+
const availableIntegrations = useMemo(
71+
() => INTEGRATIONS.sort((a, b) => a.name.localeCompare(b.name)),
72+
[]
73+
)
8874

8975
const error = fdwError || extensionsError || schemasError
9076
const isLoading = isSchemasLoading || isFDWLoading || isExtensionsLoading

apps/studio/components/interfaces/Integrations/Queues/OverviewTab.tsx

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
import { useSelectedProject } from 'hooks/misc/useSelectedProject'
2-
import { IntegrationOverviewTab } from '../Integration/IntegrationOverviewTab'
1+
import { useParams } from 'common'
32
import { useQueuesExposePostgrestStatusQuery } from 'data/database-queues/database-queues-expose-postgrest-status-query'
4-
import { Admonition } from 'ui-patterns'
5-
import { Button } from 'ui'
3+
import { useSelectedProject } from 'hooks/misc/useSelectedProject'
64
import Link from 'next/link'
7-
import { useParams } from 'common'
5+
import { Button } from 'ui'
6+
import { Admonition } from 'ui-patterns'
7+
import { IntegrationOverviewTab } from '../Integration/IntegrationOverviewTab'
88

99
export const QueuesOverviewTab = () => {
1010
const { ref } = useParams()
@@ -24,12 +24,12 @@ export const QueuesOverviewTab = () => {
2424
title="Queues can be managed via any Supabase client library or PostgREST endpoints"
2525
>
2626
<p>
27-
You may choose to toggle the exposure of Queues through PostgREST via the queues
27+
You may choose to toggle the exposure of Queues through Data APIs via the queues
2828
settings
2929
</p>
3030
<Button asChild type="default">
3131
<Link href={`/project/${ref}/integrations/queues/settings`}>
32-
Head to queues settings
32+
Manage queues settings
3333
</Link>
3434
</Button>
3535
</Admonition>

apps/studio/components/interfaces/Integrations/Queues/QueuesSettings.tsx

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,15 @@ import {
1212
FormPanelContent,
1313
FormPanelFooter,
1414
} from 'components/ui/Forms/FormPanel'
15+
import { useProjectPostgrestConfigQuery } from 'data/config/project-postgrest-config-query'
16+
import { useProjectPostgrestConfigUpdateMutation } from 'data/config/project-postgrest-config-update-mutation'
1517
import { useQueuesExposePostgrestStatusQuery } from 'data/database-queues/database-queues-expose-postgrest-status-query'
1618
import {
1719
QUEUES_SCHEMA,
1820
useDatabaseQueueToggleExposeMutation,
1921
} from 'data/database-queues/database-queues-toggle-postgrest-mutation'
22+
import { useTableUpdateMutation } from 'data/tables/table-update-mutation'
23+
import { useTablesQuery } from 'data/tables/tables-query'
2024
import { useCheckPermissions } from 'hooks/misc/useCheckPermissions'
2125
import { useSelectedProject } from 'hooks/misc/useSelectedProject'
2226
import {
@@ -28,12 +32,8 @@ import {
2832
Switch,
2933
} from 'ui'
3034
import { Admonition } from 'ui-patterns'
31-
import { FormItemLayout } from 'ui-patterns/form/FormItemLayout/FormItemLayout'
32-
import { useProjectPostgrestConfigUpdateMutation } from 'data/config/project-postgrest-config-update-mutation'
33-
import { useProjectPostgrestConfigQuery } from 'data/config/project-postgrest-config-query'
34-
import { useTablesQuery } from 'data/tables/tables-query'
3535
import ConfirmationModal from 'ui-patterns/Dialogs/ConfirmationModal'
36-
import { useTableUpdateMutation } from 'data/tables/table-update-mutation'
36+
import { FormItemLayout } from 'ui-patterns/form/FormItemLayout/FormItemLayout'
3737

3838
// [Joshen] Not convinced with the UI and layout but getting the functionality out first
3939

@@ -179,7 +179,7 @@ export const QueuesSettings = () => {
179179
<FormHeader
180180
className="mb-0"
181181
title="Settings"
182-
description="Manage your queues via any client library or PostgREST endpoints"
182+
description="Manage your queues via any client library or Data APIs endpoints"
183183
/>
184184
<Form_Shadcn_ {...form}>
185185
<form id="pgmq-postgrest" onSubmit={form.handleSubmit(onSubmit)}>
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import Link from 'next/link'
2+
3+
import { useSelectedProject } from 'hooks/misc/useSelectedProject'
4+
import { Button } from 'ui'
5+
import { Admonition } from 'ui-patterns/admonition'
6+
7+
export const UpgradeDatabaseAlert = () => {
8+
const project = useSelectedProject()
9+
10+
return (
11+
<Admonition
12+
type="warning"
13+
className="mt-4"
14+
title="Database Upgrade Needed"
15+
childProps={{ description: { className: 'flex flex-col gap-y-2' } }}
16+
>
17+
<div className="prose text-sm max-w-full">
18+
<p>
19+
This integration requires the <code>pgmq</code> extension which is not available on this
20+
version of Postgres. The extension is available on version 15.6.1.143 and higher.
21+
</p>
22+
</div>
23+
<Button color="primary" className="w-fit">
24+
<Link href={`/project/${project?.ref}/settings/infrastructure`}>Upgrade database</Link>
25+
</Button>
26+
</Admonition>
27+
)
28+
}

0 commit comments

Comments
 (0)