Files
supabase/apps/studio/components/interfaces/Database/Replication/RestartTableDialog.tsx
T
b2b150fa3c feat(pipelines): Add UI selector for choosing which tables to skip copy of (#47808)
## Summary

Adds initial-copy scoping to Pipelines in Studio. Users can copy all
existing rows, skip all initial copies, copy only selected publication
tables, or skip selected table copies. All publication tables continue
streaming new changes regardless of the initial-copy policy.

The policy now round-trips through create, edit, validation, and the
generated Management API contract. Initial-copy estimates and
table-restart confirmations use the same scope. Edit requests also
preserve redacted credentials and pipeline settings that Studio does not
own.

This completes the Studio layer of the [ETL API
change](https://github.com/supabase/etl/pull/897) and [Management API
change](https://github.com/supabase/platform/pull/35479).

## Screenshots

### Selector

<img width="1153" height="465" alt="image"
src="https://github.com/user-attachments/assets/bf615e82-ee61-4222-979d-a8695a957e82"
/>

### Select certain tables only

<img width="1153" height="465" alt="image"
src="https://github.com/user-attachments/assets/28adaa24-f239-4d1d-8fb8-fdb1988320cd"
/>

### Confirm copy costs

As the final step before the pipeline is created:

<img width="597" height="619" alt="image"
src="https://github.com/user-attachments/assets/a660bd87-bfb8-41c5-8099-4cdbdef943bf"
/>


### Policy-aware initial-copy estimate

#### Copy no table is selected

<img width="407" height="464" alt="image"
src="https://github.com/user-attachments/assets/99d859ec-2ec3-452a-ab69-11924a8db260"
/>

#### Some tables are selected

<img width="407" height="464" alt="image"
src="https://github.com/user-attachments/assets/e68aedf4-66bc-4372-98ef-0dd7fecef324"
/>


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

- **New Features**
- Added configurable “initial table copy” policies (copy/skip all and
copy/skip selected) during replication setup, including table-picker
behavior, pruning of stale selections, and updated restart/cost
estimates.
- **Bug Fixes**
- Improved restart flows to consistently use `schema.table` identity and
simplified “errored tables” targeting to match error-state tables.
- Reduced unnecessary loading by gating publication/table fetches to
when panels are visible; improved validation/toast handling when
publication tables are unavailable.
- **Tests**
- Added/expanded coverage for destination form submission, table-copy
selection, restart/cost dialogs, and copy-estimate summarization.
- **Style**
- Refreshed warning/label text for clearer configuration and
confirmation messaging.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Victor Farazdagi <simple.square@gmail.com>
Co-authored-by: Joshen Lim <joshenlimek@gmail.com>
2026-07-24 10:29:46 +03:00

139 lines
4.6 KiB
TypeScript

import { useParams } from 'common'
import { toast } from 'sonner'
import {
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle,
} from 'ui'
import { PipelineStatusName } from './Replication.constants'
import { RestartCostEstimate } from './RestartCostEstimate'
import {
shouldCopyTable,
type ReplicationTableIdentity,
type TableSyncCopyConfig,
} from './TableSyncCopy.utils'
import { useRollbackTablesMutation } from '@/data/replication/rollback-tables-mutation'
interface RestartTableDialogProps {
open: boolean
onOpenChange: (open: boolean) => void
table: ReplicationTableIdentity
tableSyncCopy?: TableSyncCopyConfig
sourceId?: number
publicationName?: string
pipelineStatusName?: PipelineStatusName
onRestartStart?: () => void
onRestartComplete?: () => void
}
export const RestartTableDialog = ({
open,
onOpenChange,
table,
tableSyncCopy,
sourceId,
publicationName,
pipelineStatusName,
onRestartStart,
onRestartComplete,
}: RestartTableDialogProps) => {
const { ref: projectRef, pipelineId: _pipelineId } = useParams()
const pipelineId = Number(_pipelineId)
const tableName = `${table.schema}.${table.name}`
const willCopyTable = shouldCopyTable(tableSyncCopy, table.id)
const { mutate: rollbackTables, isPending: isResetting } = useRollbackTablesMutation({
onSuccess: () => {
toast.success(
`Restarting replication for "${tableName}". Pipeline will ${pipelineStatusName === PipelineStatusName.STOPPED ? 'start' : 'restart'} automatically.`
)
},
onSettled: () => {
onRestartComplete?.()
onOpenChange(false)
},
onError: (error) => {
toast.error(`Failed to restart replication: ${error.message}`)
},
})
const handleReset = () => {
if (!projectRef) return toast.error('Project ref is required')
if (!pipelineId) return toast.error('Pipeline ID is required')
onRestartStart?.()
rollbackTables({
projectRef,
pipelineId,
target: { type: 'single_table', table_id: table.id },
rollbackType: 'full',
pipelineStatusName,
})
}
return (
<AlertDialog open={open} onOpenChange={onOpenChange}>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>
Restart replication for <code className="text-code-inline">{tableName}</code>
</AlertDialogTitle>
<AlertDialogDescription asChild>
<div className="space-y-3 text-sm">
<p>
This will restart replication for{' '}
<code className="text-code-inline">{tableName}</code> from scratch:
</p>
<ul className="list-disc list-inside space-y-1.5 pl-2">
{willCopyTable ? (
<li>
<strong>The table's initial sync will restart.</strong> All existing data will
be copied again from the source. This initial sync is billed in addition to
previous initial syncs.
</li>
) : (
<li>
<strong>No initial copy will run.</strong> New changes will resume streaming
without backfilling existing source rows. There is no additional initial-copy
charge.
</li>
)}
<li>
<strong>Existing downstream data will be deleted.</strong> Any replicated data for
this table will be removed.
</li>
<li>
<strong>All other tables remain untouched.</strong> Only this table is affected.
</li>
<li>
<strong>The pipeline will restart automatically.</strong> This is required to
apply this change.
</li>
</ul>
</div>
</AlertDialogDescription>
</AlertDialogHeader>
<RestartCostEstimate
open={open}
projectRef={projectRef}
sourceId={sourceId}
publicationName={publicationName}
tables={willCopyTable ? [table] : []}
/>
<AlertDialogFooter>
<AlertDialogCancel disabled={isResetting}>Cancel</AlertDialogCancel>
<AlertDialogAction disabled={isResetting} onClick={handleReset} variant="warning">
{isResetting ? 'Restarting replication...' : 'Restart replication'}
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
)
}