mirror of
https://github.com/supabase/supabase.git
synced 2026-07-28 02:12:49 -04:00
568a26899a
Previously, uncaught permission errors were bubbling up and preventing the marketplace UI from rendering at all. This is a problem because users might have access to the integrations tab, but not permissions to view all the connected resources on a particular integration. In that scenario, we want to degrade gracefully and show them only those resources they have access to.
164 lines
5.0 KiB
TypeScript
164 lines
5.0 KiB
TypeScript
import { useParams } from 'common'
|
|
import { useRouter } from 'next/router'
|
|
import { useEffect, useMemo } from 'react'
|
|
|
|
import { useProjectOAuthIntegrationData } from './Landing.utils'
|
|
import { useAvailableIntegrations } from './useAvailableIntegrations'
|
|
import { useInstalledIntegrations } from './useInstalledIntegrations'
|
|
import {
|
|
areRequiredExtensionsInstalledFor,
|
|
getFilteredNavItems,
|
|
getInstallActionType,
|
|
type InstallActionType,
|
|
} from './useIntegrationDetail.utils'
|
|
import { useIsMarketplaceEnabled } from '@/components/interfaces/App/FeaturePreview/FeaturePreviewContext'
|
|
import { useDatabaseExtensionsQuery } from '@/data/database-extensions/database-extensions-query'
|
|
import { useIsFeatureEnabled } from '@/hooks/misc/useIsFeatureEnabled'
|
|
import { useSelectedProjectQuery } from '@/hooks/misc/useSelectedProject'
|
|
|
|
export type IntegrationTab = {
|
|
label: string
|
|
href: string
|
|
active: boolean
|
|
}
|
|
|
|
export const useIntegrationDetail = () => {
|
|
const router = useRouter()
|
|
const { ref, id, pageId, childId } = useParams()
|
|
|
|
const { integrationsWrappers } = useIsFeatureEnabled(['integrations:wrappers'])
|
|
const isMarketplaceEnabled = useIsMarketplaceEnabled()
|
|
|
|
const { data: project } = useSelectedProjectQuery()
|
|
const { data: extensions } = useDatabaseExtensionsQuery({
|
|
projectRef: project?.ref,
|
|
connectionString: project?.connectionString,
|
|
})
|
|
|
|
const { data: allIntegrations, isPending: isAvailableLoading } = useAvailableIntegrations()
|
|
const { installedIntegrations, isLoading: isInstalledLoading } = useInstalledIntegrations()
|
|
|
|
// wrapped in useMemo to avoid UI resets when installing additional extensions like pg_net
|
|
const integration = useMemo(() => allIntegrations.find((i) => i.id === id), [allIntegrations, id])
|
|
const installation = useMemo(
|
|
() => installedIntegrations.find((i) => i.id === id),
|
|
[installedIntegrations, id]
|
|
)
|
|
|
|
// There does exist a mgmt-api endpoint to get a single status by integration-id, but the bulk results are likely already cached so go ahead and use them.
|
|
const { data: projectData, isLoading: isIntegrationStatusLoading } =
|
|
useProjectOAuthIntegrationData(project?.ref, { enabled: integration?.type === 'oauth' })
|
|
const integrationStatus = useMemo(
|
|
() => projectData.partnerIntegrations.find((i) => i.listing_slug === id),
|
|
[projectData, id]
|
|
)
|
|
|
|
const isInstalled = !!integration && !!installation
|
|
|
|
const areRequiredExtensionsInstalled = useMemo(
|
|
() => areRequiredExtensionsInstalledFor(integration, extensions),
|
|
[integration, extensions]
|
|
)
|
|
|
|
const navItems = useMemo(
|
|
() =>
|
|
getFilteredNavItems({
|
|
integration,
|
|
isInstalled,
|
|
isMarketplaceEnabled,
|
|
areRequiredExtensionsInstalled,
|
|
}),
|
|
[integration, isInstalled, isMarketplaceEnabled, areRequiredExtensionsInstalled]
|
|
)
|
|
|
|
const activeRoute = pageId ?? 'overview'
|
|
|
|
const activeNav = useMemo(
|
|
() => navItems.find((nav) => nav.route === activeRoute),
|
|
[navItems, activeRoute]
|
|
)
|
|
|
|
const isKnownRoute = !!activeNav
|
|
const layout = (activeNav?.layout ?? 'full') as 'full' | 'constrained'
|
|
|
|
const tabs: IntegrationTab[] = useMemo(
|
|
() =>
|
|
navItems.map((nav) => ({
|
|
label: nav.label,
|
|
href: `/project/${ref}/integrations/${id}/${nav.route}`,
|
|
active: nav.route === activeRoute,
|
|
})),
|
|
[navItems, ref, id, activeRoute]
|
|
)
|
|
|
|
const Component = useMemo(
|
|
() => integration?.navigate({ id, pageId, childId }),
|
|
[integration, id, pageId, childId]
|
|
)
|
|
|
|
const wrappersTabHref = useMemo(
|
|
() => tabs.find((tab) => tab.href.endsWith('/wrappers'))?.href,
|
|
[tabs]
|
|
)
|
|
|
|
const installActionType: InstallActionType = useMemo(
|
|
() =>
|
|
getInstallActionType({
|
|
integration,
|
|
isMarketplaceEnabled,
|
|
areRequiredExtensionsInstalled,
|
|
isInstalled,
|
|
}),
|
|
[integration, isMarketplaceEnabled, areRequiredExtensionsInstalled, isInstalled]
|
|
)
|
|
|
|
const isReady = !!router?.isReady
|
|
const isWrapperBlocked = !integrationsWrappers && !!id?.endsWith('_wrapper')
|
|
|
|
const pageTitle = integration?.name ?? 'Integration not found'
|
|
const pageSubTitle =
|
|
integration?.description ?? 'If you think this is an error, please contact support'
|
|
|
|
useEffect(() => {
|
|
if (
|
|
router?.isReady &&
|
|
!isAvailableLoading &&
|
|
!isInstalledLoading &&
|
|
!!integration &&
|
|
pageId &&
|
|
!isKnownRoute
|
|
) {
|
|
router.replace(`/project/${ref}/integrations/${id}/overview`)
|
|
}
|
|
}, [router, pageId, ref, id, integration, isKnownRoute, isAvailableLoading, isInstalledLoading])
|
|
|
|
return {
|
|
ref,
|
|
id,
|
|
pageId,
|
|
childId,
|
|
activeRoute,
|
|
activeNav,
|
|
isKnownRoute,
|
|
layout,
|
|
tabs,
|
|
isReady,
|
|
isWrapperBlocked,
|
|
pageTitle,
|
|
pageSubTitle,
|
|
integrationsWrappers,
|
|
integration,
|
|
installation,
|
|
integrationStatus,
|
|
isInstalled,
|
|
areRequiredExtensionsInstalled,
|
|
installActionType,
|
|
wrappersTabHref,
|
|
isAvailableLoading,
|
|
isInstalledLoading,
|
|
isIntegrationStatusLoading,
|
|
oauthIntegrationData: projectData,
|
|
Component,
|
|
}
|
|
}
|