mirror of
https://github.com/supabase/supabase.git
synced 2026-10-05 09:25:06 +03:00
feat(studio): refine pipeline creation copy (#50751)
## Problem The pipeline creation sheet uses generic examples, inconsistent destination terminology, and Advanced settings copy that does not explain what is being overridden. Its Docs button also sends most destinations to the general Pipelines guide. This PR is stacked on #50719 so the shared copy builds on the focused Snowflake field improvements. ## Solution Adds destination-specific pipeline-name examples and field descriptions, clarifies destination summaries and Advanced settings, uses human-readable ClickHouse engine names, shortens the primary action to **Start pipeline**, and links the Docs button to the selected destination guide. ## Review instructions 1. Open **[Database Replication](https://studio-staging-git-dnywh-studiorefine-pipeline-1eaf59-supabase.vercel.app/dashboard/project/_/database/replication)** and click **Add pipeline**. 2. Switch between destination types and confirm the pipeline-name example, destination summary, and field descriptions update appropriately. 3. Open **Advanced settings** and confirm the batch wait-time default is shown in the description while the input placeholder is `10000`. 4. Select each supported destination and confirm **Docs** opens its matching destination guide. 5. Select ClickHouse and confirm the engine choices read **ReplacingMergeTree** and **MergeTree**. ## Checklist - [x] I have read [CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md) - [x] If I wrote a new docs topic or edited an existing topic, I used the `/write-the-docs` or `/edit-the-docs` skill, which references [WORD_LIST](https://github.com/supabase/supabase/blob/master/apps/docs/WORD_LIST.md) and the docs [CONTRIBUTING](https://github.com/supabase/supabase/blob/master/apps/docs/CONTRIBUTING.md) guide <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Updates** * Clarified replication settings, destination field descriptions, and batch wait-time guidance. * Added destination-specific pipeline name placeholders and updated the labels for ClickHouse engine options. * Clarified ClickHouse password guidance for new destinations. * Updated new-pipeline buttons to say “Start pipeline” or “Start pipeline anyway.” * Added destination-specific documentation links for BigQuery, ClickHouse, DuckLake, and Snowflake. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
1 parent
9fb173e827
commit
fd0918833f
11 files changed
+197
-73
No files matched your search
+3
-3
@@ -55,7 +55,7 @@ export const AdvancedSettings = ({
|
||||
<div className="flex flex-col items-start gap-0.5">
|
||||
<span className="text-sm font-medium">Advanced settings</span>
|
||||
<span className="text-sm text-foreground-lighter font-normal">
|
||||
Optional settings to control the pipeline in more depth
|
||||
Customize how the pipeline syncs and replicates data.
|
||||
</span>
|
||||
</div>
|
||||
</AccordionTrigger>
|
||||
@@ -67,7 +67,7 @@ export const AdvancedSettings = ({
|
||||
<FormItemLayout
|
||||
layout="horizontal"
|
||||
label="Batch wait time"
|
||||
description="How long the pipeline waits before sending a partially filled batch."
|
||||
description="Maximum time before sending a partially filled batch."
|
||||
>
|
||||
<FormControl>
|
||||
<InputGroup>
|
||||
@@ -78,7 +78,7 @@ export const AdvancedSettings = ({
|
||||
step={1}
|
||||
value={field.value ?? ''}
|
||||
onChange={handleNumberChange(field)}
|
||||
placeholder={`Default: ${DEFAULT_MAX_FILL_MS}`}
|
||||
placeholder={String(DEFAULT_MAX_FILL_MS)}
|
||||
/>
|
||||
<InputGroupAddon align="inline-end">
|
||||
<InputGroupText>milliseconds</InputGroupText>
|
||||
|
||||
+8
-4
@@ -25,6 +25,10 @@ import {
|
||||
STORED_SECRET_PLACEHOLDER,
|
||||
} from '../DestinationForm.constants'
|
||||
import type { DestinationPanelSchemaType } from '../DestinationForm.schema'
|
||||
import {
|
||||
ANALYTICS_BUCKET_BUCKET_FIELD_COPY,
|
||||
ANALYTICS_BUCKET_NAMESPACE_FIELD_COPY,
|
||||
} from '../DestinationFormFieldCopy'
|
||||
import {
|
||||
isMetadataListErrorVisible,
|
||||
isMetadataListLoading,
|
||||
@@ -155,9 +159,9 @@ export const AnalyticsBucketFields = ({
|
||||
name="warehouseName"
|
||||
render={({ field }) => (
|
||||
<FormItemLayout
|
||||
label="Bucket"
|
||||
label={ANALYTICS_BUCKET_BUCKET_FIELD_COPY.label}
|
||||
layout="horizontal"
|
||||
description="The Analytics Bucket where data will be stored"
|
||||
description={ANALYTICS_BUCKET_BUCKET_FIELD_COPY.description}
|
||||
>
|
||||
<FormControl>
|
||||
<Select
|
||||
@@ -207,9 +211,9 @@ export const AnalyticsBucketFields = ({
|
||||
name="namespace"
|
||||
render={({ field }) => (
|
||||
<FormItemLayout
|
||||
label="Namespace"
|
||||
label={ANALYTICS_BUCKET_NAMESPACE_FIELD_COPY.label}
|
||||
layout="horizontal"
|
||||
description="The namespace within the bucket where tables will be organized"
|
||||
description={ANALYTICS_BUCKET_NAMESPACE_FIELD_COPY.description}
|
||||
>
|
||||
<FormControl>
|
||||
<Select
|
||||
|
||||
+8
-4
@@ -6,6 +6,10 @@ import { FormItemLayout } from 'ui-patterns/form/FormItemLayout/FormItemLayout'
|
||||
|
||||
import { STORED_SECRET_PLACEHOLDER } from '../DestinationForm.constants'
|
||||
import type { DestinationPanelSchemaType } from '../DestinationForm.schema'
|
||||
import {
|
||||
BIGQUERY_DATASET_ID_FIELD_COPY,
|
||||
BIGQUERY_PROJECT_ID_FIELD_COPY,
|
||||
} from '../DestinationFormFieldCopy'
|
||||
import { MAX_SERVICE_ACCOUNT_KEY_LENGTH, readServiceAccountFile } from './BigQuery.utils'
|
||||
|
||||
export const BigQueryFields = ({
|
||||
@@ -60,8 +64,8 @@ export const BigQueryFields = ({
|
||||
render={({ field }) => (
|
||||
<FormItemLayout
|
||||
layout="horizontal"
|
||||
label="Project ID"
|
||||
description="The Google Cloud project ID where data will be sent"
|
||||
label={BIGQUERY_PROJECT_ID_FIELD_COPY.label}
|
||||
description={BIGQUERY_PROJECT_ID_FIELD_COPY.description}
|
||||
>
|
||||
<FormControl>
|
||||
<Input {...field} placeholder="my-gcp-project" />
|
||||
@@ -75,9 +79,9 @@ export const BigQueryFields = ({
|
||||
name="datasetId"
|
||||
render={({ field }) => (
|
||||
<FormItemLayout
|
||||
label="Dataset ID"
|
||||
label={BIGQUERY_DATASET_ID_FIELD_COPY.label}
|
||||
layout="horizontal"
|
||||
description="The BigQuery dataset where replicated tables will be created"
|
||||
description={BIGQUERY_DATASET_ID_FIELD_COPY.description}
|
||||
>
|
||||
<FormControl>
|
||||
<Input {...field} placeholder="my_dataset" />
|
||||
|
||||
+20
-12
@@ -16,6 +16,11 @@ import { FormItemLayout } from 'ui-patterns/form/FormItemLayout/FormItemLayout'
|
||||
|
||||
import { STORED_SECRET_PLACEHOLDER } from '../DestinationForm.constants'
|
||||
import type { DestinationPanelSchemaType } from '../DestinationForm.schema'
|
||||
import {
|
||||
CLICKHOUSE_DATABASE_FIELD_COPY,
|
||||
CLICKHOUSE_ENGINE_FIELD_COPY,
|
||||
CLICKHOUSE_URL_FIELD_COPY,
|
||||
} from '../DestinationFormFieldCopy'
|
||||
|
||||
export const ClickHouseFields = ({
|
||||
form,
|
||||
@@ -37,8 +42,8 @@ export const ClickHouseFields = ({
|
||||
render={({ field }) => (
|
||||
<FormItemLayout
|
||||
layout="horizontal"
|
||||
label="URL"
|
||||
description="HTTPS endpoint for your ClickHouse server, including port"
|
||||
label={CLICKHOUSE_URL_FIELD_COPY.label}
|
||||
description={CLICKHOUSE_URL_FIELD_COPY.description}
|
||||
>
|
||||
<FormControl>
|
||||
<Input
|
||||
@@ -58,7 +63,7 @@ export const ClickHouseFields = ({
|
||||
<FormItemLayout
|
||||
layout="horizontal"
|
||||
label="User"
|
||||
description="ClickHouse user with permission to write to the target database"
|
||||
description="ClickHouse user with permission to write to the target database."
|
||||
>
|
||||
<FormControl>
|
||||
<Input {...field} value={field.value ?? ''} placeholder="default" />
|
||||
@@ -74,17 +79,18 @@ export const ClickHouseFields = ({
|
||||
<FormItemLayout
|
||||
layout="horizontal"
|
||||
label="Password"
|
||||
labelOptional="Optional"
|
||||
description={
|
||||
editMode
|
||||
? 'Stored password is hidden. Enter a new password to replace it.'
|
||||
: 'Omit for passwordless access'
|
||||
: 'Leave blank for passwordless access.'
|
||||
}
|
||||
>
|
||||
<FormControl>
|
||||
<PasswordInput
|
||||
value={field.value ?? ''}
|
||||
type={showPassword ? 'text' : 'password'}
|
||||
placeholder={editMode ? STORED_SECRET_PLACEHOLDER : 'Optional'}
|
||||
placeholder={editMode ? STORED_SECRET_PLACEHOLDER : undefined}
|
||||
onChange={(event) => field.onChange(event.target.value)}
|
||||
actions={
|
||||
<div className="flex items-center justify-center">
|
||||
@@ -109,8 +115,8 @@ export const ClickHouseFields = ({
|
||||
render={({ field }) => (
|
||||
<FormItemLayout
|
||||
layout="horizontal"
|
||||
label="Database"
|
||||
description="The ClickHouse database where replicated tables will be created"
|
||||
label={CLICKHOUSE_DATABASE_FIELD_COPY.label}
|
||||
description={CLICKHOUSE_DATABASE_FIELD_COPY.description}
|
||||
>
|
||||
<FormControl>
|
||||
<Input {...field} value={field.value ?? ''} placeholder="default" />
|
||||
@@ -125,18 +131,20 @@ export const ClickHouseFields = ({
|
||||
render={({ field }) => (
|
||||
<FormItemLayout
|
||||
layout="horizontal"
|
||||
label="Table engine"
|
||||
description="Server defaults to replacing_merge_tree when unset"
|
||||
label={CLICKHOUSE_ENGINE_FIELD_COPY.label}
|
||||
description={CLICKHOUSE_ENGINE_FIELD_COPY.description}
|
||||
>
|
||||
<FormControl>
|
||||
<Select
|
||||
value={field.value ?? 'replacing_merge_tree'}
|
||||
onValueChange={field.onChange}
|
||||
>
|
||||
<SelectTrigger>{field.value ?? 'replacing_merge_tree'}</SelectTrigger>
|
||||
<SelectTrigger>
|
||||
{field.value === 'merge_tree' ? 'MergeTree' : 'ReplacingMergeTree'}
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="replacing_merge_tree">replacing_merge_tree</SelectItem>
|
||||
<SelectItem value="merge_tree">merge_tree</SelectItem>
|
||||
<SelectItem value="replacing_merge_tree">ReplacingMergeTree</SelectItem>
|
||||
<SelectItem value="merge_tree">MergeTree</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</FormControl>
|
||||
|
||||
+81
@@ -0,0 +1,81 @@
|
||||
export const PIPELINE_NAME_FIELD_COPY = {
|
||||
label: 'Pipeline name',
|
||||
description: 'Used to identify this pipeline in Supabase.',
|
||||
} as const
|
||||
|
||||
export const BIGQUERY_PROJECT_ID_FIELD_COPY = {
|
||||
label: 'Project ID',
|
||||
description: 'The Google Cloud project ID where data will be sent.',
|
||||
} as const
|
||||
|
||||
export const BIGQUERY_DATASET_ID_FIELD_COPY = {
|
||||
label: 'Dataset ID',
|
||||
description: 'The BigQuery dataset where replicated tables will be created.',
|
||||
} as const
|
||||
|
||||
export const ANALYTICS_BUCKET_BUCKET_FIELD_COPY = {
|
||||
label: 'Bucket',
|
||||
description: 'The Analytics Bucket where data will be stored.',
|
||||
} as const
|
||||
|
||||
export const ANALYTICS_BUCKET_NAMESPACE_FIELD_COPY = {
|
||||
label: 'Namespace',
|
||||
description: 'The namespace within the bucket where tables will be organized.',
|
||||
} as const
|
||||
|
||||
export const DUCKLAKE_CATALOG_PROJECT_FIELD_COPY = {
|
||||
label: 'Catalog project',
|
||||
description:
|
||||
"Pipelines connects to this project's Postgres instance to store the DuckLake catalog.",
|
||||
} as const
|
||||
|
||||
export const DUCKLAKE_STORAGE_PROJECT_FIELD_COPY = {
|
||||
label: 'Storage project',
|
||||
description: 'The project whose object storage holds the DuckLake data files.',
|
||||
} as const
|
||||
|
||||
export const DUCKLAKE_BUCKET_FIELD_COPY = {
|
||||
label: 'Bucket',
|
||||
description: 'The bucket in which DuckLake data files will be stored.',
|
||||
} as const
|
||||
|
||||
export const DUCKLAKE_CATALOG_URL_FIELD_COPY = {
|
||||
label: 'Catalog URL',
|
||||
createDescription: 'A Postgres connection string for the DuckLake catalog.',
|
||||
editDescription: 'Stored catalog URL is hidden. Enter a new URL to replace it.',
|
||||
} as const
|
||||
|
||||
export const DUCKLAKE_DATA_PATH_FIELD_COPY = {
|
||||
label: 'Data path',
|
||||
description: 'An S3 path where DuckLake data files will be written.',
|
||||
} as const
|
||||
|
||||
export const SNOWFLAKE_ACCOUNT_ID_FIELD_COPY = {
|
||||
label: 'Account ID',
|
||||
description: 'Snowflake organization and account identifiers joined with a hyphen.',
|
||||
} as const
|
||||
|
||||
export const SNOWFLAKE_DATABASE_FIELD_COPY = {
|
||||
label: 'Database',
|
||||
description: 'Snowflake database where replicated tables are created.',
|
||||
} as const
|
||||
|
||||
export const SNOWFLAKE_SCHEMA_FIELD_COPY = {
|
||||
label: 'Schema',
|
||||
description: 'An empty Snowflake schema where replicated tables are created.',
|
||||
} as const
|
||||
|
||||
export const CLICKHOUSE_URL_FIELD_COPY = {
|
||||
label: 'HTTPS URL',
|
||||
description: 'The HTTPS endpoint for your ClickHouse server, including port.',
|
||||
} as const
|
||||
|
||||
export const CLICKHOUSE_DATABASE_FIELD_COPY = {
|
||||
label: 'Database',
|
||||
description: 'The ClickHouse database where replicated tables will be created.',
|
||||
} as const
|
||||
|
||||
export const CLICKHOUSE_ENGINE_FIELD_COPY = {
|
||||
label: 'Table engine',
|
||||
description: 'Defaults to ReplacingMergeTree.',
|
||||
} as const
|
||||
+15
-4
@@ -2,28 +2,39 @@ import type { UseFormReturn } from 'react-hook-form'
|
||||
import { FormControl, FormField, Input } from 'ui'
|
||||
import { FormItemLayout } from 'ui-patterns/form/FormItemLayout/FormItemLayout'
|
||||
|
||||
import type { DestinationType } from '../DestinationPanel.types'
|
||||
import type { DestinationPanelSchemaType } from './DestinationForm.schema'
|
||||
import { PIPELINE_NAME_FIELD_COPY } from './DestinationFormFieldCopy'
|
||||
|
||||
const PIPELINE_NAME_PLACEHOLDERS: Record<DestinationType, string> = {
|
||||
BigQuery: 'Analytics Pipeline',
|
||||
'Analytics Bucket': 'Iceberg Pipeline',
|
||||
DuckLake: 'Lakehouse Pipeline',
|
||||
Snowflake: 'Data Warehouse Pipeline',
|
||||
ClickHouse: 'Live Analytics Pipeline',
|
||||
}
|
||||
|
||||
type DestinationNameInputProps = {
|
||||
form: UseFormReturn<DestinationPanelSchemaType>
|
||||
destinationType: DestinationType
|
||||
}
|
||||
|
||||
export const DestinationNameInput = ({ form }: DestinationNameInputProps) => {
|
||||
export const DestinationNameInput = ({ form, destinationType }: DestinationNameInputProps) => {
|
||||
return (
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="name"
|
||||
render={({ field }) => (
|
||||
<FormItemLayout
|
||||
label="Name"
|
||||
label={PIPELINE_NAME_FIELD_COPY.label}
|
||||
layout="horizontal"
|
||||
description="Used to identify this pipeline in Supabase."
|
||||
description={PIPELINE_NAME_FIELD_COPY.description}
|
||||
>
|
||||
<FormControl>
|
||||
<Input
|
||||
{...field}
|
||||
autoFocus
|
||||
placeholder="My destination"
|
||||
placeholder={PIPELINE_NAME_PLACEHOLDERS[destinationType]}
|
||||
data-1p-ignore
|
||||
data-lpignore="true"
|
||||
data-form-type="other"
|
||||
|
||||
+31
-27
@@ -28,6 +28,13 @@ import { SelectionListState } from 'ui-patterns/SelectionListState'
|
||||
|
||||
import { DEFAULT_DUCKLAKE_POOL_SIZE, STORED_SECRET_PLACEHOLDER } from '../DestinationForm.constants'
|
||||
import type { DestinationPanelSchemaType } from '../DestinationForm.schema'
|
||||
import {
|
||||
DUCKLAKE_BUCKET_FIELD_COPY,
|
||||
DUCKLAKE_CATALOG_PROJECT_FIELD_COPY,
|
||||
DUCKLAKE_CATALOG_URL_FIELD_COPY,
|
||||
DUCKLAKE_DATA_PATH_FIELD_COPY,
|
||||
DUCKLAKE_STORAGE_PROJECT_FIELD_COPY,
|
||||
} from '../DestinationFormFieldCopy'
|
||||
import {
|
||||
isMetadataListErrorVisible,
|
||||
isMetadataListLoading,
|
||||
@@ -57,7 +64,7 @@ const DUCKLAKE_MODE_OPTIONS = [
|
||||
value: DUCKLAKE_MODE_CUSTOM,
|
||||
icon: SlidersHorizontal,
|
||||
label: 'Custom parameters',
|
||||
description: 'Bring your own PostgreSQL catalog and S3-compatible object storage credentials.',
|
||||
description: 'Bring your own Postgres catalog and S3-compatible object storage credentials.',
|
||||
},
|
||||
] as const
|
||||
|
||||
@@ -187,7 +194,7 @@ const DuckLakeSupabaseFields = ({ form }: { form: UseFormReturn<DestinationPanel
|
||||
<div className="flex flex-col gap-y-1">
|
||||
<p className="text-sm font-medium text-foreground">Catalog</p>
|
||||
<p className="text-sm text-foreground-light">
|
||||
The selected project's Postgres database is used as the PostgreSQL DuckLake catalog
|
||||
The selected project's Postgres database is used as the DuckLake catalog.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -197,14 +204,11 @@ const DuckLakeSupabaseFields = ({ form }: { form: UseFormReturn<DestinationPanel
|
||||
render={({ field }) => (
|
||||
<FormItemLayout
|
||||
layout="horizontal"
|
||||
label="Catalog project"
|
||||
label={DUCKLAKE_CATALOG_PROJECT_FIELD_COPY.label}
|
||||
description={
|
||||
<div className="flex flex-col gap-y-2">
|
||||
{renderRegionWarning(field.value)}
|
||||
<span>
|
||||
Pipelines connects to this project's Postgres instance to store the DuckLake
|
||||
catalog
|
||||
</span>
|
||||
<span>{DUCKLAKE_CATALOG_PROJECT_FIELD_COPY.description}</span>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
@@ -226,7 +230,7 @@ const DuckLakeSupabaseFields = ({ form }: { form: UseFormReturn<DestinationPanel
|
||||
<FormItemLayout
|
||||
layout="horizontal"
|
||||
label="Pool size"
|
||||
description="Number of concurrent DuckDB connections to the catalog"
|
||||
description="Number of concurrent DuckDB connections to the catalog."
|
||||
>
|
||||
<FormControl>
|
||||
<Input
|
||||
@@ -251,7 +255,7 @@ const DuckLakeSupabaseFields = ({ form }: { form: UseFormReturn<DestinationPanel
|
||||
<FormItemLayout
|
||||
layout="horizontal"
|
||||
label="Metadata schema"
|
||||
description="Schema used for DuckLake metadata tables in the catalog's Postgres"
|
||||
description="Schema used for DuckLake metadata tables in the catalog's Postgres."
|
||||
>
|
||||
<FormControl>
|
||||
<Input {...field} placeholder="ducklake" value={field.value ?? ''} />
|
||||
@@ -273,11 +277,11 @@ const DuckLakeSupabaseFields = ({ form }: { form: UseFormReturn<DestinationPanel
|
||||
render={({ field }) => (
|
||||
<FormItemLayout
|
||||
layout="horizontal"
|
||||
label="Storage project"
|
||||
label={DUCKLAKE_STORAGE_PROJECT_FIELD_COPY.label}
|
||||
description={
|
||||
<div className="flex flex-col gap-y-2">
|
||||
{renderRegionWarning(field.value)}
|
||||
<span>The project whose object storage holds the DuckLake data files</span>
|
||||
<span>{DUCKLAKE_STORAGE_PROJECT_FIELD_COPY.description}</span>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
@@ -302,8 +306,8 @@ const DuckLakeSupabaseFields = ({ form }: { form: UseFormReturn<DestinationPanel
|
||||
render={({ field }) => (
|
||||
<FormItemLayout
|
||||
layout="horizontal"
|
||||
label="Bucket"
|
||||
description="The bucket in which DuckLake data files will be stored."
|
||||
label={DUCKLAKE_BUCKET_FIELD_COPY.label}
|
||||
description={DUCKLAKE_BUCKET_FIELD_COPY.description}
|
||||
>
|
||||
<div className="flex items-center gap-x-2">
|
||||
<div className="grow">
|
||||
@@ -380,7 +384,7 @@ const DuckLakeCustomFields = ({
|
||||
<div className="flex flex-col gap-y-1">
|
||||
<p className="text-sm font-medium text-foreground">Catalog</p>
|
||||
<p className="text-sm text-foreground-light">
|
||||
Configure the PostgreSQL DuckLake catalog and S3-compatible storage for replicated data
|
||||
Configure the Postgres DuckLake catalog and S3-compatible storage for replicated data.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -391,11 +395,11 @@ const DuckLakeCustomFields = ({
|
||||
render={({ field }) => (
|
||||
<FormItemLayout
|
||||
layout="horizontal"
|
||||
label="Catalog URL"
|
||||
label={DUCKLAKE_CATALOG_URL_FIELD_COPY.label}
|
||||
description={
|
||||
editMode
|
||||
? 'Stored catalog URL is hidden. Enter a new URL to replace it.'
|
||||
: 'A PostgreSQL connection string for the DuckLake catalog'
|
||||
? DUCKLAKE_CATALOG_URL_FIELD_COPY.editDescription
|
||||
: DUCKLAKE_CATALOG_URL_FIELD_COPY.createDescription
|
||||
}
|
||||
>
|
||||
<FormControl>
|
||||
@@ -429,8 +433,8 @@ const DuckLakeCustomFields = ({
|
||||
render={({ field }) => (
|
||||
<FormItemLayout
|
||||
layout="horizontal"
|
||||
label="Data path"
|
||||
description="An S3 path where DuckLake data files will be written"
|
||||
label={DUCKLAKE_DATA_PATH_FIELD_COPY.label}
|
||||
description={DUCKLAKE_DATA_PATH_FIELD_COPY.description}
|
||||
>
|
||||
<FormControl>
|
||||
<Input {...field} placeholder="s3://bucket/path" value={field.value ?? ''} />
|
||||
@@ -446,7 +450,7 @@ const DuckLakeCustomFields = ({
|
||||
<FormItemLayout
|
||||
layout="horizontal"
|
||||
label="Pool size"
|
||||
description="Number of concurrent DuckDB connections to use"
|
||||
description="Number of concurrent DuckDB connections to use."
|
||||
>
|
||||
<FormControl>
|
||||
<Input
|
||||
@@ -485,7 +489,7 @@ const DuckLakeCustomFields = ({
|
||||
description={
|
||||
editMode
|
||||
? 'Stored access key ID is hidden. Enter a new key ID to replace it.'
|
||||
: 'Required access key ID for the object storage provider'
|
||||
: 'Required access key ID for the object storage provider.'
|
||||
}
|
||||
>
|
||||
<FormControl>
|
||||
@@ -509,7 +513,7 @@ const DuckLakeCustomFields = ({
|
||||
description={
|
||||
editMode
|
||||
? 'Stored secret access key is hidden. Enter a new secret to replace it.'
|
||||
: 'Required secret access key for the object storage provider'
|
||||
: 'Required secret access key for the object storage provider.'
|
||||
}
|
||||
className="relative"
|
||||
>
|
||||
@@ -537,7 +541,7 @@ const DuckLakeCustomFields = ({
|
||||
<FormItemLayout
|
||||
layout="horizontal"
|
||||
label="S3 region"
|
||||
description="Required region for the object storage provider"
|
||||
description="Required region for the object storage provider."
|
||||
>
|
||||
<FormControl>
|
||||
<Input {...field} placeholder="us-east-1" value={field.value ?? ''} />
|
||||
@@ -553,7 +557,7 @@ const DuckLakeCustomFields = ({
|
||||
<FormItemLayout
|
||||
layout="horizontal"
|
||||
label="S3 endpoint"
|
||||
description="Required endpoint without the protocol scheme, for example `127.0.0.1:5000/s3`"
|
||||
description="Required endpoint without the protocol scheme, for example `127.0.0.1:5000/s3`."
|
||||
>
|
||||
<FormControl>
|
||||
<Input {...field} placeholder="127.0.0.1:5000/s3" value={field.value ?? ''} />
|
||||
@@ -569,7 +573,7 @@ const DuckLakeCustomFields = ({
|
||||
<FormItemLayout
|
||||
layout="horizontal"
|
||||
label="S3 URL style"
|
||||
description="Choose `path` for MinIO/Supabase-style endpoints or `vhost` for AWS-style virtual host addressing"
|
||||
description="Choose `path` for MinIO/Supabase-style endpoints or `vhost` for AWS-style virtual host addressing."
|
||||
>
|
||||
<FormControl>
|
||||
<Select value={field.value ?? 'path'} onValueChange={field.onChange}>
|
||||
@@ -591,7 +595,7 @@ const DuckLakeCustomFields = ({
|
||||
<FormItemLayout
|
||||
layout="horizontal"
|
||||
label="Use SSL"
|
||||
description="Whether to use SSL when connecting to the S3-compatible endpoint"
|
||||
description="Whether to use SSL when connecting to the S3-compatible endpoint."
|
||||
>
|
||||
<FormControl>
|
||||
<Select
|
||||
@@ -625,7 +629,7 @@ const DuckLakeCustomFields = ({
|
||||
<FormItemLayout
|
||||
layout="horizontal"
|
||||
label="Metadata schema"
|
||||
description="Schema used for DuckLake metadata tables in PostgreSQL"
|
||||
description="Schema used for DuckLake metadata tables in Postgres."
|
||||
>
|
||||
<FormControl>
|
||||
<Input {...field} placeholder="ducklake" value={field.value ?? ''} />
|
||||
|
||||
+11
-6
@@ -7,6 +7,11 @@ import { FormItemLayout } from 'ui-patterns/form/FormItemLayout/FormItemLayout'
|
||||
|
||||
import { STORED_SECRET_PLACEHOLDER } from '../DestinationForm.constants'
|
||||
import type { DestinationPanelSchemaType } from '../DestinationForm.schema'
|
||||
import {
|
||||
SNOWFLAKE_ACCOUNT_ID_FIELD_COPY,
|
||||
SNOWFLAKE_DATABASE_FIELD_COPY,
|
||||
SNOWFLAKE_SCHEMA_FIELD_COPY,
|
||||
} from '../DestinationFormFieldCopy'
|
||||
import { MAX_PRIVATE_KEY_LENGTH, readPrivateKeyFile } from './Snowflake.utils'
|
||||
|
||||
export const SnowflakeFields = ({
|
||||
@@ -70,8 +75,8 @@ export const SnowflakeFields = ({
|
||||
render={({ field }) => (
|
||||
<FormItemLayout
|
||||
layout="horizontal"
|
||||
label="Account ID"
|
||||
description="Snowflake organization and account identifiers joined with a hyphen."
|
||||
label={SNOWFLAKE_ACCOUNT_ID_FIELD_COPY.label}
|
||||
description={SNOWFLAKE_ACCOUNT_ID_FIELD_COPY.description}
|
||||
>
|
||||
<FormControl>
|
||||
<Input {...field} placeholder="MYORG-MYACCOUNT" value={field.value ?? ''} />
|
||||
@@ -111,8 +116,8 @@ export const SnowflakeFields = ({
|
||||
render={({ field }) => (
|
||||
<FormItemLayout
|
||||
layout="horizontal"
|
||||
label="Database"
|
||||
description="Snowflake database where replicated tables are created."
|
||||
label={SNOWFLAKE_DATABASE_FIELD_COPY.label}
|
||||
description={SNOWFLAKE_DATABASE_FIELD_COPY.description}
|
||||
>
|
||||
<FormControl>
|
||||
<Input {...field} placeholder="PIPELINES_DB" value={field.value ?? ''} />
|
||||
@@ -127,8 +132,8 @@ export const SnowflakeFields = ({
|
||||
render={({ field }) => (
|
||||
<FormItemLayout
|
||||
layout="horizontal"
|
||||
label="Schema"
|
||||
description="An empty Snowflake schema where replicated tables are created."
|
||||
label={SNOWFLAKE_SCHEMA_FIELD_COPY.label}
|
||||
description={SNOWFLAKE_SCHEMA_FIELD_COPY.description}
|
||||
>
|
||||
<FormControl>
|
||||
<Input {...field} placeholder="REPLICATED" value={field.value ?? ''} />
|
||||
|
||||
+3
-3
@@ -287,10 +287,10 @@ export const DestinationForm = ({
|
||||
return existingDestination?.enabled ? 'Apply and restart pipeline' : 'Apply changes'
|
||||
} else {
|
||||
if (hasRunValidation && validationWarnings.length > 0 && !hasValidationFailures) {
|
||||
return 'Create and start pipeline anyway'
|
||||
return 'Start pipeline anyway'
|
||||
}
|
||||
|
||||
return 'Create and start pipeline'
|
||||
return 'Start pipeline'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -474,7 +474,7 @@ export const DestinationForm = ({
|
||||
<p className="text-sm font-medium text-foreground">Destination details</p>
|
||||
|
||||
<div className="flex flex-col gap-y-4">
|
||||
<DestinationNameInput form={form} />
|
||||
<DestinationNameInput form={form} destinationType={selectedType} />
|
||||
<PublicationSelection
|
||||
form={form}
|
||||
onSelectNewPublication={() => setPublicationPanelVisible(true)}
|
||||
|
||||
+12
-4
@@ -31,6 +31,14 @@ import { checkLocalETLNotSetUp } from '@/data/replication/utils'
|
||||
import { useConfirmOnClose } from '@/hooks/ui/useConfirmOnClose'
|
||||
import { DOCS_URL } from '@/lib/constants'
|
||||
|
||||
const DESTINATION_DOCS_PATHS: Partial<Record<DestinationType, string>> = {
|
||||
BigQuery: '/guides/database/replication/pipelines/bigquery#configure-bigquery-as-a-destination',
|
||||
ClickHouse:
|
||||
'/guides/database/replication/pipelines/clickhouse#configure-clickhouse-as-a-destination',
|
||||
DuckLake: '/guides/database/replication/pipelines/ducklake#choose-a-configuration-mode',
|
||||
Snowflake: '/guides/database/replication/pipelines/snowflake#prepare-snowflake-resources',
|
||||
}
|
||||
|
||||
export const DestinationPanel = () => {
|
||||
const { ref: projectRef } = useParams()
|
||||
const enablePgReplicate = useIsETLPrivateAlpha()
|
||||
@@ -97,10 +105,10 @@ export const DestinationPanel = () => {
|
||||
onClose,
|
||||
})
|
||||
|
||||
const docsUrl =
|
||||
destinationType === 'BigQuery'
|
||||
? `${DOCS_URL}/guides/database/replication/bigquery#configure-bigquery-as-a-destination`
|
||||
: `${DOCS_URL}/guides/database/replication/pipelines#step-3-configure-a-destination`
|
||||
const docsUrl = `${DOCS_URL}${
|
||||
(destinationType && DESTINATION_DOCS_PATHS[destinationType]) ??
|
||||
'/guides/database/replication/pipelines#step-3-configure-a-destination'
|
||||
}`
|
||||
|
||||
useEffect(() => {
|
||||
if (edit !== null && invalidExistingDestination) {
|
||||
|
||||
+5
-6
@@ -74,7 +74,7 @@ export const DestinationTypeSelection = () => {
|
||||
{
|
||||
value: 'BigQuery',
|
||||
label: 'BigQuery',
|
||||
description: "Replicate changes to Google Cloud's data warehouse for analytics and BI",
|
||||
description: 'Replicate changes to BigQuery for analytics and BI',
|
||||
stage: 'Public Alpha',
|
||||
enabled: isOptionVisible('BigQuery', etlEnableBigQuery),
|
||||
},
|
||||
@@ -86,22 +86,21 @@ export const DestinationTypeSelection = () => {
|
||||
{
|
||||
value: 'DuckLake',
|
||||
label: 'DuckLake',
|
||||
description: 'Replicate changes to a DuckLake catalog backed by S3-compatible storage',
|
||||
description: 'Replicate changes to DuckLake for open lakehouse storage',
|
||||
stage: 'Early Access',
|
||||
enabled: isOptionVisible('DuckLake', etlEnableDucklake),
|
||||
},
|
||||
{
|
||||
value: 'Snowflake',
|
||||
label: 'Snowflake',
|
||||
description:
|
||||
'Replicate changes to Snowflake for warehouse analytics and downstream data workflows',
|
||||
description: 'Replicate changes to Snowflake for cloud data warehousing',
|
||||
stage: 'Early Access',
|
||||
enabled: isOptionVisible('Snowflake', etlEnableSnowflake),
|
||||
},
|
||||
{
|
||||
value: 'ClickHouse',
|
||||
label: 'ClickHouse',
|
||||
description: 'Stream changes to a ClickHouse cluster for fast columnar analytics',
|
||||
description: 'Replicate changes to ClickHouse for real-time analytics',
|
||||
stage: 'Early Access',
|
||||
enabled: isOptionVisible('ClickHouse', etlEnableClickHouse),
|
||||
},
|
||||
@@ -113,7 +112,7 @@ export const DestinationTypeSelection = () => {
|
||||
{
|
||||
value: 'Analytics Bucket',
|
||||
label: 'Analytics Bucket',
|
||||
description: 'Write Apache Iceberg tables to Supabase Storage for analytics workflows',
|
||||
description: 'Replicate changes to Supabase Storage as Apache Iceberg tables',
|
||||
stage: 'Deprecated',
|
||||
enabled: isOptionVisible('Analytics Bucket', etlEnableIceberg),
|
||||
},
|
||||
|
||||
Reference in new issue
Block a user