Files
supabase/apps/studio/components/interfaces/Database/Replication/RestartTableDialog.tsx
Danny WhiteandJoshen Lim a351a36e9b refactor(studio): centralise replication payload builders (#49842)
## What kind of change does this PR introduce?

Studio data-layer refactor.

## What is the current behavior?

Pipeline creation, editing, and validation build similar destination and
pipeline payloads separately. The duplicated mappings rely on type
assertions and can drift between actions.

## What is the new behavior?

Uses shared typed builders for create, update, and validation payloads
across the existing destinations. Update payloads continue to omit blank
secrets, while create payloads preserve their current values. This PR
does not add table partitioning configuration.

## To test

This is a data-layer refactor. No visible behaviour should change.

1. Open **Database > Replication** and click **Start a new pipeline**.
2. Select **BigQuery**, or any other enabled destination.
3. Edit a few non-secret fields and expand **Advanced settings**.
4. Confirm the form remains usable and no runtime errors appear.

Create, update, validation, and secret-handling behaviour is covered by
the focused tests and CI. Deploy previews and fresh local projects do
not have the existing destinations or credentials needed to exercise
those paths manually.

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

* **Bug Fixes**
* Improved replication destination configuration handling during
creation, updates, and validation.
* Applied consistent configuration mapping across supported destination
types.
* Ensured blank secret values are omitted during updates while retained
when creating destinations.
* Standardized table synchronization defaults when no specific setting
is provided.
* **Tests**
* Added coverage for BigQuery configuration mapping and secret handling.
  * Updated DuckLake tests for destination updates.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Joshen Lim <joshenlimek@gmail.com>
2026-09-07 15:02:09 +08:00

136 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 } from './TableSyncCopy.utils'
import { useRollbackTablesMutation } from '@/data/replication/rollback-tables-mutation'
import type { TableSyncCopyConfig } from '@/data/replication/types'
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> Existing source rows
will be synced again. Data successfully processed during this initial sync is
billed again.
</li>
) : (
<li>
<strong>The table will skip initial sync.</strong> Replication will resume with
new changes only, without syncing existing source rows. There is no additional
initial sync 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>
)
}