mirror of
https://github.com/supabase/supabase.git
synced 2026-10-11 04:15:04 +03:00
## Problem Add pipeline opens the creation sheet before users enable Pipelines. A pending or failed source lookup also lets creation open with an unknown enablement state. ## Solution Show the existing enablement dialog first when required, then open creation after successful enablement. Cancellation and failed enablement keep creation closed; enabling through the page menu does not open creation. Disable both Add pipeline buttons and their keyboard shortcut until the source lookup succeeds. Failed lookups show an error with Retry, including the local replication configuration message. Analytics Bucket keeps its existing creation path. | Before | After | | --- | --- | | <img width="1275" height="919" alt="Pipelines Database Shears Toolshed Supabase" src="https://github.com/user-attachments/assets/cf328732-4342-4758-bde2-98b393341d6a" /> | _No longer in sheet; dialog is shown conditionally before sheet._ | | <img width="1275" height="919" alt="Pipelines Database Shears Toolshed Supabase" src="https://github.com/user-attachments/assets/f593e271-d29c-4955-8849-0bb64946d4cc" /> | <img width="1275" height="919" alt="Pipelines Database Shears Toolshed Supabase" src="https://github.com/user-attachments/assets/3257d391-b892-44df-85d1-5a2108072312" />| | _“Enable Pipelines”_ | _“Enable”_ | | After | | --- | | <img width="1275" height="919" alt="Pipelines Database Shears Toolshed Supabase" src="https://github.com/user-attachments/assets/b11770a1-4785-49ee-950d-d821892b3245" /> | | _Loading_ | | <img width="1275" height="919" alt="Pipelines Database Shears Toolshed Supabase" src="https://github.com/user-attachments/assets/710f82be-ca6f-4337-a5ce-b65f9d7f6a32" /> | | _Lookup failed_ | | <img width="1275" height="919" alt="Pipelines Database Shears Toolshed Supabase" src="https://github.com/user-attachments/assets/68486ca8-37e8-4b27-89ae-8004f159c38a" /> | | _Plan-access loading, throttled_ | ## Review instructions Use a project with Pipelines access and a working replication API (which should work on [deploy preview](https://studio-staging-git-dnywh-fixpipeline-enable-create-supabase.vercel.app/)). Test the PR preview or locally ([instructions](https://app.notion.com/p/supabase/Danny-s-Local-ETL-Pipelines-Setup-3b25004b775f8058a108f8f67fc813e9?source=copy_link)). In DevTools Network, enable **Disable cache** before each reload. Analytics Bucket intentionally bypasses the source-status guard. 1. **Loading:** select **Slow 3G**, reload, and watch the request ending in `/replication/<ref>/sources`. While it is pending, both **Add pipeline** buttons must be disabled and **Shift+N** must open nothing. Both buttons replace the plus with a loading spinner and have no loading tooltip. Restore **No throttling** afterwards. 2. **Lookup failed:** right-click that source request and choose **Block request URL**, then reload. After retries finish, expect **Failed to retrieve pipeline enablement status** with **Retry**, disabled Add buttons whose tooltip matches the error title, and no sheet/dialog from **Shift+N**. An unconfigured local replication API instead shows **Replication unavailable locally**. Unblock the request before clicking **Retry**. 3. **Lookup succeeded:** on a disposable project with Pipelines disabled, successful Retry restores Add pipeline. Clicking it opens **Enable Pipelines**. Cancel stays on the list; **Enable** opens the sheet after successful enablement. On an already enabled project, Add pipeline opens the sheet directly. Enabling through the page's three-dot menu stays on the list. Analytics Bucket opens its sheet without ETL enablement. 4. **Plan-access loading is separate:** in Chrome 145 or newer, find `/organizations/<slug>/entitlements` in Network, right-click it and choose **Throttle request**. In the **Request conditions** drawer, select Slow 3G for that request only, leaving global throttling off. Reload on a Pro organisation with Pipelines disabled and open Add pipeline after the source lookup succeeds. While entitlements remain pending, expect body shimmers, the accessible “Checking Pipelines access…” status, a disabled loading **Enable** button, and no upgrade prompt. Remove the request condition afterwards. If your DevTools lacks per-request throttling, use the component tests for deterministic coverage.
146 lines
4.8 KiB
TypeScript
146 lines
4.8 KiB
TypeScript
import { useParams } from 'common'
|
|
import { useState } from 'react'
|
|
import { toast } from 'sonner'
|
|
import {
|
|
Button,
|
|
Dialog,
|
|
DialogContent,
|
|
DialogFooter,
|
|
DialogHeader,
|
|
DialogSection,
|
|
DialogSectionSeparator,
|
|
DialogTitle,
|
|
DialogTrigger,
|
|
} from 'ui'
|
|
import { Admonition } from 'ui-patterns/Admonition'
|
|
import { GenericSkeletonLoader } from 'ui-patterns/ShimmeringLoader'
|
|
|
|
import { DestinationType } from './DestinationPanel/DestinationPanel.types'
|
|
import { InlineLink } from '@/components/ui/InlineLink'
|
|
import { UpgradePlanButton } from '@/components/ui/UpgradePlanButton'
|
|
import { useCreateTenantSourceMutation } from '@/data/replication/create-tenant-source-mutation'
|
|
import { useCheckEntitlements } from '@/hooks/misc/useCheckEntitlements'
|
|
import { DOCS_URL } from '@/lib/constants'
|
|
|
|
type EnablePipelinesModalProps =
|
|
| { open: boolean; onOpenChange: (open: boolean) => void; onSuccess?: () => void }
|
|
| { open?: never; onOpenChange?: never; onSuccess?: () => void }
|
|
|
|
export const EnablePipelinesModal = ({
|
|
open: extOpen,
|
|
onOpenChange,
|
|
onSuccess,
|
|
}: EnablePipelinesModalProps) => {
|
|
const { ref: projectRef } = useParams()
|
|
const [_open, _setOpen] = useState(false)
|
|
|
|
const open = extOpen ?? _open
|
|
const setOpen = onOpenChange ?? _setOpen
|
|
const hideTrigger = extOpen !== undefined && onOpenChange !== undefined
|
|
|
|
const { hasAccess, isLoading } = useCheckEntitlements('replication.etl')
|
|
|
|
const { mutate: createTenantSource, isPending: creatingTenantSource } =
|
|
useCreateTenantSourceMutation({
|
|
onSuccess: () => {
|
|
toast.success('Pipelines enabled')
|
|
onSuccess?.()
|
|
setOpen(false)
|
|
},
|
|
onError: (error) => {
|
|
toast.error(`Failed to enable Pipelines: ${error.message}`)
|
|
},
|
|
})
|
|
|
|
const onEnablePipelines = async () => {
|
|
if (isLoading || !hasAccess) return
|
|
if (!projectRef) return console.error('Project ref is required')
|
|
createTenantSource({ projectRef })
|
|
}
|
|
|
|
return (
|
|
<Dialog open={open} onOpenChange={setOpen}>
|
|
{!hideTrigger && (
|
|
<DialogTrigger asChild>
|
|
<Button variant="primary">Enable</Button>
|
|
</DialogTrigger>
|
|
)}
|
|
<DialogContent size="small">
|
|
<DialogHeader>
|
|
<DialogTitle>Enable Pipelines</DialogTitle>
|
|
</DialogHeader>
|
|
<DialogSectionSeparator />
|
|
<DialogSection className="flex flex-col gap-y-3">
|
|
<p role="status" aria-live="polite" className="sr-only">
|
|
{isLoading ? 'Checking Pipelines access…' : ''}
|
|
</p>
|
|
{isLoading && (
|
|
<div aria-hidden="true">
|
|
<GenericSkeletonLoader />
|
|
</div>
|
|
)}
|
|
{!isLoading && hasAccess && (
|
|
<>
|
|
<p className="text-sm text-foreground-light">
|
|
Pipelines bills for configured pipeline hours and Postgres row data processed during
|
|
initial sync and ongoing replication. Review{' '}
|
|
<InlineLink href={`${DOCS_URL}/guides/platform/manage-your-usage/pipelines`}>
|
|
Pipelines pricing
|
|
</InlineLink>{' '}
|
|
before enabling.
|
|
</p>
|
|
<p className="text-sm text-foreground-light">
|
|
Pipelines is in public alpha and may change.
|
|
</p>
|
|
</>
|
|
)}
|
|
{!isLoading && !hasAccess && (
|
|
<p className="text-sm text-foreground-light">Pipelines requires the Pro plan.</p>
|
|
)}
|
|
</DialogSection>
|
|
<DialogFooter>
|
|
<Button disabled={creatingTenantSource} onClick={() => setOpen(false)}>
|
|
Cancel
|
|
</Button>
|
|
{isLoading || hasAccess ? (
|
|
<Button
|
|
variant="primary"
|
|
loading={isLoading || creatingTenantSource}
|
|
disabled={isLoading}
|
|
onClick={onEnablePipelines}
|
|
>
|
|
Enable
|
|
</Button>
|
|
) : (
|
|
<UpgradePlanButton source="replication" featureProposition="use replication" />
|
|
)}
|
|
</DialogFooter>
|
|
</DialogContent>
|
|
</Dialog>
|
|
)
|
|
}
|
|
|
|
export const EnablePipelinesCallout = ({ type }: { type?: DestinationType | null }) => {
|
|
const { hasAccess } = useCheckEntitlements('replication.etl')
|
|
|
|
return (
|
|
<Admonition
|
|
type="note"
|
|
layout="responsive"
|
|
title={hasAccess ? 'Enable Pipelines' : 'Upgrade to Pro for Pipelines'}
|
|
description={
|
|
hasAccess
|
|
? `Pipelines must be enabled before this project can replicate database changes to ${type ?? 'external destinations'}.`
|
|
: `The Pro plan is required to replicate database changes to ${type ?? 'external destinations'} with Pipelines.`
|
|
}
|
|
actions={
|
|
hasAccess ? (
|
|
<EnablePipelinesModal />
|
|
) : (
|
|
<UpgradePlanButton source="replication" featureProposition="use replication" />
|
|
)
|
|
}
|
|
/>
|
|
)
|
|
}
|