mirror of
https://github.com/supabase/supabase.git
synced 2026-10-09 03:15:06 +03:00
Fixes a batch of production Studio crashes from Sentry (all caught by the global error boundary). Most are missing array/null guards where an endpoint typed as an array — or with a nested array field — returned a non-array body in production; a few are one-off render crashes. Resolves FE-3748. ## Issues fixed | Sentry | Error | Fix | | --- | --- | --- | | [J7R](https://supabase.sentry.io/issues/7492997940/) | Maximum update depth exceeded | Disable RadialBar animation in disk-cooldown countdown | | [JR5](https://supabase.sentry.io/issues/7548484681/) | resourceWarnings.find is not a function | Guard in ResourceExhaustionWarningBanner | | [JCJ](https://supabase.sentry.io/issues/7506024989/) | resourceWarnings.find is not a function | Guard in ProjectLayout + normalize query | | [K1Y](https://supabase.sentry.io/issues/7584792331/) | snippet.name on undefined | Optional-chain SQL editor download filename | | [B3K](https://supabase.sentry.io/issues/7141649636/) | pagination.count on undefined | Guard pagination in projects infinite query | | [JVP](https://supabase.sentry.io/issues/7560437621/) | schemas.some / extensions.find | Coerce pg-meta lists to arrays in useInstalledIntegrations | | [JR2](https://supabase.sentry.io/issues/7548339272/) | extensions.find is not a function | (same fix as JVP) | | [JQR](https://supabase.sentry.io/issues/7547163939/) | lints.filter is not a function | Normalize project lints query | | [JR3](https://supabase.sentry.io/issues/7548433501/) | entitlements.find is not a function | Guard call sites + normalize entitlements query | | [JQS](https://supabase.sentry.io/issues/7547557098/) | selected_addons.find is not a function | Normalize addons query arrays | <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * Improved stability across several Studio screens by handling missing or unexpected data more safely. * Downloads now use a fallback name when a snippet name isn’t available. * Project, entitlement, schema, addon, warning, and extension views are less likely to break when data is missing or not in the expected format. * Pagination and countdown visuals now behave more consistently, with reduced chance of runtime errors or animation-related glitches. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Ivan Vasilov <vasilov.ivan@gmail.com>
133 lines
4.0 KiB
TypeScript
133 lines
4.0 KiB
TypeScript
import { useMemo } from 'react'
|
|
|
|
import {
|
|
hasMatchingWrapper,
|
|
hasRequiredExtensions,
|
|
isOAuthInstalled,
|
|
isStripeSyncEngineInstalled,
|
|
useProjectOAuthIntegrationData,
|
|
} from './Landing.utils'
|
|
import { useAvailableIntegrations } from './useAvailableIntegrations'
|
|
import { useDatabaseExtensionsQuery } from '@/data/database-extensions/database-extensions-query'
|
|
import { useSchemasQuery } from '@/data/database/schemas-query'
|
|
import { useFDWsQuery } from '@/data/fdw/fdws-query'
|
|
import { useSelectedProjectQuery } from '@/hooks/misc/useSelectedProject'
|
|
import { EMPTY_ARR } from '@/lib/void'
|
|
|
|
export const useInstalledIntegrations = () => {
|
|
const { data: project } = useSelectedProjectQuery()
|
|
|
|
const {
|
|
data: allIntegrations = EMPTY_ARR,
|
|
error: availableIntegrationsError,
|
|
isPending: isAvailableIntegrationsLoading,
|
|
isSuccess: isSuccessAvailableIntegrations,
|
|
isError: isErrorAvailableIntegrations,
|
|
} = useAvailableIntegrations()
|
|
|
|
const hasOAuthIntegration = useMemo(() => {
|
|
return allIntegrations.some((integration) => integration.type === 'oauth')
|
|
}, [allIntegrations])
|
|
|
|
const {
|
|
data: oauthData,
|
|
error: oauthDataError,
|
|
isError: isErrorOAuthData,
|
|
isLoading: isOAuthDataLoading,
|
|
isSuccess: isSuccessOAuthData,
|
|
} = useProjectOAuthIntegrationData(project?.ref, { enabled: hasOAuthIntegration })
|
|
|
|
const {
|
|
data: wrappers = EMPTY_ARR,
|
|
error: fdwError,
|
|
isError: isErrorFDWs,
|
|
isPending: isFDWLoading,
|
|
isSuccess: isSuccessFDWs,
|
|
} = useFDWsQuery({
|
|
projectRef: project?.ref,
|
|
connectionString: project?.connectionString,
|
|
})
|
|
const {
|
|
data: extensions = EMPTY_ARR,
|
|
error: extensionsError,
|
|
isError: isErrorExtensions,
|
|
isPending: isExtensionsLoading,
|
|
isSuccess: isSuccessExtensions,
|
|
} = useDatabaseExtensionsQuery({
|
|
projectRef: project?.ref,
|
|
connectionString: project?.connectionString,
|
|
})
|
|
|
|
const {
|
|
data: schemas = EMPTY_ARR,
|
|
error: schemasError,
|
|
isError: isErrorSchemas,
|
|
isPending: isSchemasLoading,
|
|
isSuccess: isSuccessSchemas,
|
|
} = useSchemasQuery({
|
|
projectRef: project?.ref,
|
|
connectionString: project?.connectionString,
|
|
})
|
|
|
|
const isHooksEnabled = schemas.some((schema) => schema.name === 'supabase_functions')
|
|
|
|
const installedIntegrations = useMemo(() => {
|
|
return allIntegrations
|
|
.filter((integration) => {
|
|
if (integration.id === 'webhooks') return isHooksEnabled
|
|
if (integration.id === 'data_api') return true
|
|
if (integration.id === 'stripe_sync_engine') {
|
|
return isStripeSyncEngineInstalled(schemas)
|
|
}
|
|
if (integration.type === 'wrapper') {
|
|
return hasMatchingWrapper({ meta: integration.meta, wrappers })
|
|
}
|
|
if (integration.type === 'postgres_extension') {
|
|
return hasRequiredExtensions({ integration, extensions })
|
|
}
|
|
if (integration.type === 'oauth') {
|
|
return isOAuthInstalled({
|
|
integration,
|
|
projectData: oauthData,
|
|
})
|
|
}
|
|
return false
|
|
})
|
|
.sort((a, b) => a.name.localeCompare(b.name))
|
|
}, [allIntegrations, wrappers, extensions, schemas, isHooksEnabled, oauthData])
|
|
|
|
const error =
|
|
fdwError ||
|
|
extensionsError ||
|
|
schemasError ||
|
|
availableIntegrationsError ||
|
|
(hasOAuthIntegration ? oauthDataError : null)
|
|
const isLoading =
|
|
isSchemasLoading ||
|
|
isFDWLoading ||
|
|
isExtensionsLoading ||
|
|
isAvailableIntegrationsLoading ||
|
|
(hasOAuthIntegration && isOAuthDataLoading)
|
|
const isError =
|
|
isErrorFDWs ||
|
|
isErrorExtensions ||
|
|
isErrorSchemas ||
|
|
isErrorAvailableIntegrations ||
|
|
(hasOAuthIntegration && isErrorOAuthData)
|
|
const isSuccess =
|
|
isSuccessFDWs &&
|
|
isSuccessExtensions &&
|
|
isSuccessSchemas &&
|
|
isSuccessAvailableIntegrations &&
|
|
(!hasOAuthIntegration || isSuccessOAuthData)
|
|
|
|
return {
|
|
// show all integrations at once instead of showing partial results
|
|
installedIntegrations: isLoading ? EMPTY_ARR : installedIntegrations,
|
|
error,
|
|
isError,
|
|
isLoading,
|
|
isSuccess,
|
|
}
|
|
}
|