diff --git a/apps/studio/components/interfaces/Database/Replication/Destinations.tsx b/apps/studio/components/interfaces/Database/Replication/Destinations.tsx index d9e58ccf4d8..462cb4c7125 100644 --- a/apps/studio/components/interfaces/Database/Replication/Destinations.tsx +++ b/apps/studio/components/interfaces/Database/Replication/Destinations.tsx @@ -19,7 +19,7 @@ import { TableHeader, TableRow, } from 'ui' -import { GenericSkeletonLoader } from 'ui-patterns' +import { Admonition, GenericSkeletonLoader } from 'ui-patterns' import { Input } from 'ui-patterns/DataInputs/Input' import { REPLICA_STATUS } from '../../Settings/Infrastructure/InfrastructureConfiguration/InstanceConfiguration.constants' @@ -43,6 +43,7 @@ import { replicationKeys } from '@/data/replication/keys' import { fetchReplicationPipelineVersion } from '@/data/replication/pipeline-version-query' import { useReplicationPipelinesQuery } from '@/data/replication/pipelines-query' import { useReplicationSourcesQuery } from '@/data/replication/sources-query' +import { checkLocalETLNotSetUp } from '@/data/replication/utils' import { useIsFeatureEnabled } from '@/hooks/misc/useIsFeatureEnabled' import { DOCS_URL } from '@/lib/constants' import { SHORTCUT_IDS } from '@/state/shortcuts/registry' @@ -144,7 +145,9 @@ export const Destinations = () => { pipelines.length === 0 const isLoading = isDestinationsLoading || isDatabasesLoading - const hasErrorsFetchingData = isDestinationsError || isDatabasesError + + const isLocalETLNotSetUp = checkLocalETLNotSetUp(destinationsError) + const hasErrorsFetchingData = (!isLocalETLNotSetUp && isDestinationsError) || isDatabasesError const openDestinationPanel = () => { if (!newDestinationDefaultType) return @@ -270,6 +273,13 @@ export const Destinations = () => { /> )} + {isLocalETLNotSetUp && ( + + )} + {isLoading ? ( ) : hasReplicas || hasDestinations ? ( diff --git a/apps/studio/components/interfaces/Database/Replication/ReplicationDiagram/index.tsx b/apps/studio/components/interfaces/Database/Replication/ReplicationDiagram/index.tsx index f90e8741bff..83a9555cee7 100644 --- a/apps/studio/components/interfaces/Database/Replication/ReplicationDiagram/index.tsx +++ b/apps/studio/components/interfaces/Database/Replication/ReplicationDiagram/index.tsx @@ -12,6 +12,7 @@ import { timeout } from '@/lib/helpers' import '@xyflow/react/dist/style.css' import { SmoothstepEdge } from './Edges' +import { checkLocalETLNotSetUp } from '@/data/replication/utils' export const ReplicationDiagram = () => { return ( @@ -42,10 +43,17 @@ const ReplicationDiagramContent = () => { [databases, projectRef] ) - const { data, isSuccess: isSuccessDestinations } = useReplicationDestinationsQuery({ + const { + data, + error: destinationsError, + isSuccess: isSuccessDestinations, + isError: isErrorDestinations, + } = useReplicationDestinationsQuery({ projectRef, }) const destinations = useMemo(() => data?.destinations ?? [], [data]) + const isLocalETLNotSetUp = checkLocalETLNotSetUp(destinationsError) + const skipRenderingDestinations = isErrorDestinations && isLocalETLNotSetUp const nodes = useMemo(() => { return [ @@ -103,10 +111,14 @@ const ReplicationDiagramContent = () => { } useEffect(() => { - if (nodes.length > 0 && isSuccessDestinations && isSuccessReplicas) { + if ( + nodes.length > 0 && + (isSuccessDestinations || skipRenderingDestinations) && + isSuccessReplicas + ) { setReactFlow() } - }, [nodes, isSuccessDestinations, isSuccessReplicas]) + }, [nodes, isSuccessDestinations, skipRenderingDestinations, isSuccessReplicas]) return (
diff --git a/apps/studio/data/replication/utils.ts b/apps/studio/data/replication/utils.ts index bf493328fab..31289a682e7 100644 --- a/apps/studio/data/replication/utils.ts +++ b/apps/studio/data/replication/utils.ts @@ -1,6 +1,20 @@ import { MAX_RETRY_FAILURE_COUNT } from '@/data/query-client' import { ResponseError } from '@/types' +const isLocal = + process.env.NEXT_PUBLIC_ENVIRONMENT !== 'prod' && + process.env.NEXT_PUBLIC_ENVIRONMENT !== 'staging' + +export const checkLocalETLNotSetUp = (error: ResponseError | null) => { + if (error === null) return false + + const isETLAPINotRunning = + error.code === undefined && error.message.includes('API error happened') + const isETLNotSetUp = + error.code === 503 && error.message.includes('replication API URL is not configured') + return isLocal && (isETLAPINotRunning || isETLNotSetUp) +} + export const checkReplicationFeatureFlagRetry = ( failureCount: number, error: ResponseError @@ -10,7 +24,9 @@ export const checkReplicationFeatureFlagRetry = ( error.code === 503 && error.message.includes('feature flag is required') - if (isFeatureFlagRequiredError) { + const isLocalETLNotSetUp = checkLocalETLNotSetUp(error) + + if (isFeatureFlagRequiredError || isLocalETLNotSetUp) { return false }