From 8da989f7b0d68439227f6e138c8a0f99cc622a6e Mon Sep 17 00:00:00 2001 From: Amogh-Bharadwaj Date: Tue, 7 Nov 2023 02:31:36 -0500 Subject: [PATCH 01/11] better state management in tablemapping and search --- ui/app/mirrors/create/tablemapping.tsx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ui/app/mirrors/create/tablemapping.tsx b/ui/app/mirrors/create/tablemapping.tsx index 65a77a8d8b..3161136765 100644 --- a/ui/app/mirrors/create/tablemapping.tsx +++ b/ui/app/mirrors/create/tablemapping.tsx @@ -117,6 +117,10 @@ const TableMapping = ({ return row.source.toLowerCase().includes(searchQuery.toLowerCase()); }); + const filteredRows = rows?.filter((row) => { + return row.source.toLowerCase().includes(searchQuery.toLowerCase()); + }); + useEffect(() => { fetchSchemas(sourcePeerName, setLoading).then((res) => setAllSchemas(res)); setSchema('public'); From 2a1a9b0c00e751528a66f8ee944177af0e515daf Mon Sep 17 00:00:00 2001 From: Amogh-Bharadwaj Date: Fri, 3 Nov 2023 17:46:55 -0500 Subject: [PATCH 02/11] ui polishging --- ui/app/mirrors/page.tsx | 2 - ui/app/peers/page.tsx | 2 - ui/app/peers/peerRows.tsx | 93 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 93 insertions(+), 4 deletions(-) create mode 100644 ui/app/peers/peerRows.tsx diff --git a/ui/app/mirrors/page.tsx b/ui/app/mirrors/page.tsx index 89368719a5..019e1bb66a 100644 --- a/ui/app/mirrors/page.tsx +++ b/ui/app/mirrors/page.tsx @@ -1,8 +1,6 @@ import { QRepConfig } from '@/grpc_generated/flow'; import { Button } from '@/lib/Button'; import { Header } from '@/lib/Header'; -import { Icon } from '@/lib/Icon'; -import { Label } from '@/lib/Label'; import { LayoutMain } from '@/lib/Layout'; import { Panel } from '@/lib/Panel'; import Link from 'next/link'; diff --git a/ui/app/peers/page.tsx b/ui/app/peers/page.tsx index 6ae7899e15..35ef322101 100644 --- a/ui/app/peers/page.tsx +++ b/ui/app/peers/page.tsx @@ -1,6 +1,4 @@ import { Button } from '@/lib/Button'; -import { Icon } from '@/lib/Icon'; -import { Label } from '@/lib/Label'; import { LayoutMain } from '@/lib/Layout'; import { Panel } from '@/lib/Panel'; import Link from 'next/link'; diff --git a/ui/app/peers/peerRows.tsx b/ui/app/peers/peerRows.tsx new file mode 100644 index 0000000000..5e0dc5214f --- /dev/null +++ b/ui/app/peers/peerRows.tsx @@ -0,0 +1,93 @@ +'use client'; +import { DropDialog } from '@/components/DropDialog'; +import PeerButton from '@/components/PeerComponent'; +import PeerTypeLabel from '@/components/PeerTypeComponent'; +import { Peer } from '@/grpc_generated/peers'; +import { Label } from '@/lib/Label'; +import { SearchField } from '@/lib/SearchField'; +import { Table, TableCell, TableRow } from '@/lib/Table'; +import { useEffect, useState } from 'react'; + +function PeerRow({ peer }: { peer: Peer }) { + return ( + + + + + + + + + + + + + ); +} + +function PeersTable({ title, peers }: { title: string; peers: Peer[] }) { + const [rows, setRows] = useState([]); + const [searchQuery, setSearchQuery] = useState(''); + + useEffect(() => { + if (searchQuery.length > 0) { + setRows( + peers.filter((peer) => { + return peer.name.toLowerCase().includes(searchQuery.toLowerCase()); + }) + ); + } + if (searchQuery.length == 0) { + setRows(peers); + } + }, [searchQuery]); + + return ( + {title}} + toolbar={{ + left: <>, + right: ( + ) => + setSearchQuery(e.target.value) + } + /> + ), + }} + header={ + + + + + + + + + + + + + } + > + {rows.map((row) => ( + + ))} +
+ ); +} + +export default PeersTable; From 1ca7e63c43df1f9d7a0f147fd831ebe9f993624e Mon Sep 17 00:00:00 2001 From: Amogh-Bharadwaj Date: Sun, 5 Nov 2023 14:53:44 -0600 Subject: [PATCH 03/11] better specific mirror page --- ui/app/mirrors/edit/[mirrorId]/syncStatusTable.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/app/mirrors/edit/[mirrorId]/syncStatusTable.tsx b/ui/app/mirrors/edit/[mirrorId]/syncStatusTable.tsx index 752b24e8c3..9675308530 100644 --- a/ui/app/mirrors/edit/[mirrorId]/syncStatusTable.tsx +++ b/ui/app/mirrors/edit/[mirrorId]/syncStatusTable.tsx @@ -8,7 +8,7 @@ import { Label } from '@/lib/Label'; import { ProgressCircle } from '@/lib/ProgressCircle'; import { Table, TableCell, TableRow } from '@/lib/Table'; import moment from 'moment'; -import { useState } from 'react'; +import { useEffect, useState } from 'react'; type SyncStatusRow = { batchId: number; From ac4ce5c825571e776adc7b67a9c4e8ca0d8d26d4 Mon Sep 17 00:00:00 2001 From: Amogh-Bharadwaj Date: Sun, 5 Nov 2023 17:28:09 -0600 Subject: [PATCH 04/11] refactor search bar comp --- .../edit/[mirrorId]/syncStatusTable.tsx | 2 +- ui/app/mirrors/page.tsx | 2 + ui/app/peers/page.tsx | 2 + ui/app/peers/peerRows.tsx | 93 ------------------- 4 files changed, 5 insertions(+), 94 deletions(-) delete mode 100644 ui/app/peers/peerRows.tsx diff --git a/ui/app/mirrors/edit/[mirrorId]/syncStatusTable.tsx b/ui/app/mirrors/edit/[mirrorId]/syncStatusTable.tsx index 9675308530..752b24e8c3 100644 --- a/ui/app/mirrors/edit/[mirrorId]/syncStatusTable.tsx +++ b/ui/app/mirrors/edit/[mirrorId]/syncStatusTable.tsx @@ -8,7 +8,7 @@ import { Label } from '@/lib/Label'; import { ProgressCircle } from '@/lib/ProgressCircle'; import { Table, TableCell, TableRow } from '@/lib/Table'; import moment from 'moment'; -import { useEffect, useState } from 'react'; +import { useState } from 'react'; type SyncStatusRow = { batchId: number; diff --git a/ui/app/mirrors/page.tsx b/ui/app/mirrors/page.tsx index 019e1bb66a..89368719a5 100644 --- a/ui/app/mirrors/page.tsx +++ b/ui/app/mirrors/page.tsx @@ -1,6 +1,8 @@ import { QRepConfig } from '@/grpc_generated/flow'; import { Button } from '@/lib/Button'; import { Header } from '@/lib/Header'; +import { Icon } from '@/lib/Icon'; +import { Label } from '@/lib/Label'; import { LayoutMain } from '@/lib/Layout'; import { Panel } from '@/lib/Panel'; import Link from 'next/link'; diff --git a/ui/app/peers/page.tsx b/ui/app/peers/page.tsx index 35ef322101..6ae7899e15 100644 --- a/ui/app/peers/page.tsx +++ b/ui/app/peers/page.tsx @@ -1,4 +1,6 @@ import { Button } from '@/lib/Button'; +import { Icon } from '@/lib/Icon'; +import { Label } from '@/lib/Label'; import { LayoutMain } from '@/lib/Layout'; import { Panel } from '@/lib/Panel'; import Link from 'next/link'; diff --git a/ui/app/peers/peerRows.tsx b/ui/app/peers/peerRows.tsx deleted file mode 100644 index 5e0dc5214f..0000000000 --- a/ui/app/peers/peerRows.tsx +++ /dev/null @@ -1,93 +0,0 @@ -'use client'; -import { DropDialog } from '@/components/DropDialog'; -import PeerButton from '@/components/PeerComponent'; -import PeerTypeLabel from '@/components/PeerTypeComponent'; -import { Peer } from '@/grpc_generated/peers'; -import { Label } from '@/lib/Label'; -import { SearchField } from '@/lib/SearchField'; -import { Table, TableCell, TableRow } from '@/lib/Table'; -import { useEffect, useState } from 'react'; - -function PeerRow({ peer }: { peer: Peer }) { - return ( - - - - - - - - - - - - - ); -} - -function PeersTable({ title, peers }: { title: string; peers: Peer[] }) { - const [rows, setRows] = useState([]); - const [searchQuery, setSearchQuery] = useState(''); - - useEffect(() => { - if (searchQuery.length > 0) { - setRows( - peers.filter((peer) => { - return peer.name.toLowerCase().includes(searchQuery.toLowerCase()); - }) - ); - } - if (searchQuery.length == 0) { - setRows(peers); - } - }, [searchQuery]); - - return ( - {title}} - toolbar={{ - left: <>, - right: ( - ) => - setSearchQuery(e.target.value) - } - /> - ), - }} - header={ - - - - - - - - - - - - - } - > - {rows.map((row) => ( - - ))} -
- ); -} - -export default PeersTable; From 51c4380b5d276478a7b3570c4d0a49748c18d400 Mon Sep 17 00:00:00 2001 From: Amogh-Bharadwaj Date: Mon, 6 Nov 2023 14:36:23 -0600 Subject: [PATCH 05/11] config page for bq --- ui/app/peers/create/[peerType]/helpers/bq.ts | 89 ++++++++++++++++++++ ui/app/peers/create/[peerType]/page.tsx | 3 + ui/components/SelectSource.tsx | 2 +- 3 files changed, 93 insertions(+), 1 deletion(-) create mode 100644 ui/app/peers/create/[peerType]/helpers/bq.ts diff --git a/ui/app/peers/create/[peerType]/helpers/bq.ts b/ui/app/peers/create/[peerType]/helpers/bq.ts new file mode 100644 index 0000000000..07b130839b --- /dev/null +++ b/ui/app/peers/create/[peerType]/helpers/bq.ts @@ -0,0 +1,89 @@ +import { BigqueryConfig } from '@/grpc_generated/peers'; +import { PeerSetting } from './common'; + +export const bigquerySetting: PeerSetting[] = [ + { + label: 'Auth Type', + stateHandler: (value, setter) => + setter((curr) => ({ ...curr, authType: value || 'service_account' })), + tips: 'The type of authentication to use when connecting to BigQuery.', + default: 'service_account', + helpfulLink: + 'https://cloud.google.com/bigquery/docs/authentication/service-account-file', + }, + { + label: 'Project ID', + stateHandler: (value, setter) => + setter((curr) => ({ ...curr, projectId: value })), + tips: 'The ID of the Google Cloud project that owns the BigQuery dataset.', + }, + { + label: 'Private Key ID', + stateHandler: (value, setter) => + setter((curr) => ({ ...curr, privateKeyId: value })), + tips: 'The ID of the private key used for authentication.', + }, + { + label: 'Private Key', + stateHandler: (value, setter) => + setter((curr) => ({ ...curr, privateKey: value })), + type: 'file', + tips: 'The contents of the private key used for authentication. This can be of any file extension.', + }, + { + label: 'Client Email', + stateHandler: (value, setter) => + setter((curr) => ({ ...curr, clientEmail: value })), + tips: 'The email address of the service account used for authentication.', + }, + { + label: 'Client ID', + stateHandler: (value, setter) => + setter((curr) => ({ ...curr, clientId: value })), + tips: 'The ID of the client used for authentication.', + }, + { + label: 'Auth URI', + stateHandler: (value, setter) => + setter((curr) => ({ ...curr, authUri: value })), + tips: 'The URI of the authorization server used for authentication.', + }, + { + label: 'Token URI', + stateHandler: (value, setter) => + setter((curr) => ({ ...curr, tokenUri: value })), + tips: 'The URI of the token server used for authentication.', + }, + { + label: 'Auth Provider X509 Cert URL', + stateHandler: (value, setter) => + setter((curr) => ({ ...curr, authProviderX509CertUrl: value })), + tips: 'The URL of the public x509 certificate used for authentication.', + }, + { + label: 'Client X509 Cert URL', + stateHandler: (value, setter) => + setter((curr) => ({ ...curr, clientX509CertUrl: value })), + tips: 'The URL of the public x509 certificate used for authentication.', + }, + { + label: 'Dataset ID', + stateHandler: (value, setter) => + setter((curr) => ({ ...curr, datasetId: value })), + tips: 'The ID of the BigQuery dataset to use.', + }, +]; + +export const blankBigquerySetting: BigqueryConfig = { + authType: 'service_account', + projectId: '', + privateKeyId: '', + privateKey: '', + clientEmail: '', + clientId: '', + authUri: '', + tokenUri: '', + authProviderX509CertUrl: '', + clientX509CertUrl: '', + datasetId: '', +}; diff --git a/ui/app/peers/create/[peerType]/page.tsx b/ui/app/peers/create/[peerType]/page.tsx index 01af2b0dd1..1ca33443bb 100644 --- a/ui/app/peers/create/[peerType]/page.tsx +++ b/ui/app/peers/create/[peerType]/page.tsx @@ -12,6 +12,7 @@ import { useRouter } from 'next/navigation'; import { useState } from 'react'; import ConfigForm from '../../../../components/ConfigForm'; import { handleCreate, handleValidate } from './handlers'; +import { bigquerySetting } from './helpers/bq'; import { PeerSetting, getBlankSetting } from './helpers/common'; import { postgresSetting } from './helpers/pg'; import { snowflakeSetting } from './helpers/sf'; @@ -42,6 +43,8 @@ export default function CreateConfig({ return configForm(postgresSetting); case 'SNOWFLAKE': return configForm(snowflakeSetting); + case 'BIGQUERY': + return configForm(bigquerySetting); default: return <>; } diff --git a/ui/components/SelectSource.tsx b/ui/components/SelectSource.tsx index 955cb09160..3ea6828860 100644 --- a/ui/components/SelectSource.tsx +++ b/ui/components/SelectSource.tsx @@ -12,7 +12,7 @@ export default function SelectSource({ setPeerType }: SelectSourceProps) { const dbTypes: string[] = Object.values(DBType).filter( (value): value is string => typeof value === 'string' && - (value === 'POSTGRES' || value === 'SNOWFLAKE') + (value === 'POSTGRES' || value === 'SNOWFLAKE' || value === 'BIGQUERY') ); return ( setPeerType(val)} > {dbTypes.map((dbType, id) => { + const peerLogo = DBTypeToImageMapping(dbType); return ( - {dbType} +
+ peer + +
{dbType}
+
); })} From 0db2cec4bdc01cba468a6acc732d42f3d8d0f0d7 Mon Sep 17 00:00:00 2001 From: Amogh-Bharadwaj Date: Mon, 6 Nov 2023 22:51:11 -0500 Subject: [PATCH 07/11] removing snapshot table --- ui/app/mirrors/page.tsx | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/ui/app/mirrors/page.tsx b/ui/app/mirrors/page.tsx index 89368719a5..3bb5c66db6 100644 --- a/ui/app/mirrors/page.tsx +++ b/ui/app/mirrors/page.tsx @@ -57,18 +57,9 @@ export default async function Mirrors() { return false; }); - let snapshotFlows = flows.filter((flow) => { - if (flow.config_proto && flow.query_string) { - let config = QRepConfig.decode(flow.config_proto); - return config.watermarkColumn.toLowerCase() === 'ctid'; - } - return false; - }); - stringifyConfig(cdcFlows); stringifyConfig(qrepFlows); stringifyConfig(xminFlows); - stringifyConfig(snapshotFlows); return ( @@ -105,9 +96,6 @@ export default async function Mirrors() { - - - ); } From 17ea679b0a27bfe00727fa000c5c1ebaf896b7e8 Mon Sep 17 00:00:00 2001 From: Amogh-Bharadwaj Date: Tue, 7 Nov 2023 03:44:10 -0500 Subject: [PATCH 08/11] fix: defaults for table map --- ui/app/mirrors/create/tablemapping.tsx | 42 ++++++++++++++++++++++++-- 1 file changed, 40 insertions(+), 2 deletions(-) diff --git a/ui/app/mirrors/create/tablemapping.tsx b/ui/app/mirrors/create/tablemapping.tsx index 248d0489c2..26d0dd1129 100644 --- a/ui/app/mirrors/create/tablemapping.tsx +++ b/ui/app/mirrors/create/tablemapping.tsx @@ -94,14 +94,22 @@ const TableMapping = ({ fetchTables(sourcePeerName, schemaName, setLoading).then((tableNames) => setRows((curr) => { const newRows = [...curr]; + tableNames.forEach((tableName) => { const row = newRows.find( (row) => row.source === `${schemaName}.${tableName}` ); if (!row) { + const dstName = + peerType != undefined && dBTypeToJSON(peerType) == 'BIGQUERY' + ? tableName + : `${schemaName}.${tableName}`; newRows.push({ source: `${schemaName}.${tableName}`, - destination: `${schemaName}.${tableName}`, + destination: + peerType != undefined && dBTypeToJSON(peerType) == 'BIGQUERY' + ? tableName + : `${schemaName}.${tableName}`, partitionKey: '', selected: false, }); @@ -111,7 +119,7 @@ const TableMapping = ({ }) ); }, - [sourcePeerName, setRows] + [sourcePeerName, setRows, peerType] ); const [searchQuery, setSearchQuery] = useState(''); @@ -124,6 +132,36 @@ const TableMapping = ({ return row.source.toLowerCase().includes(searchQuery.toLowerCase()); }); + useEffect(() => { + if (peerType != undefined && dBTypeToJSON(peerType) == 'BIGQUERY') { + setRows((rows) => { + const newRows = [...rows]; + newRows.forEach((_, i) => { + const row = newRows[i]; + newRows[i] = { + ...row, + destination: row.destination.split('.')[1], + }; + }); + return newRows; + }); + } else { + setRows((rows) => { + const newRows = [...rows]; + newRows.forEach((_, i) => { + const row = newRows[i]; + newRows[i] = { + ...row, + destination: `${schema}.${ + row.destination.split('.')[1] || row.destination + }`, + }; + }); + return newRows; + }); + } + }, [peerType, setRows, schema]); + useEffect(() => { fetchSchemas(sourcePeerName, setLoading).then((res) => setAllSchemas(res)); setSchema('public'); From a58f22f04db30c9a85b03338fba85e3ef29b7613 Mon Sep 17 00:00:00 2001 From: Amogh-Bharadwaj Date: Tue, 7 Nov 2023 11:18:06 -0500 Subject: [PATCH 09/11] fix issue from rebase --- ui/app/mirrors/create/tablemapping.tsx | 4 ---- 1 file changed, 4 deletions(-) diff --git a/ui/app/mirrors/create/tablemapping.tsx b/ui/app/mirrors/create/tablemapping.tsx index 26d0dd1129..7c789323ca 100644 --- a/ui/app/mirrors/create/tablemapping.tsx +++ b/ui/app/mirrors/create/tablemapping.tsx @@ -128,10 +128,6 @@ const TableMapping = ({ return row.source.toLowerCase().includes(searchQuery.toLowerCase()); }); - const filteredRows = rows?.filter((row) => { - return row.source.toLowerCase().includes(searchQuery.toLowerCase()); - }); - useEffect(() => { if (peerType != undefined && dBTypeToJSON(peerType) == 'BIGQUERY') { setRows((rows) => { From 6a9d4bdf8090313f723c0fb4dabf238ac562c952 Mon Sep 17 00:00:00 2001 From: Amogh-Bharadwaj Date: Tue, 7 Nov 2023 17:09:02 -0500 Subject: [PATCH 10/11] fixes tablemap, new bigquery UI, more robustness --- flow/connectors/bigquery/bigquery.go | 9 +- ui/app/api/peers/route.ts | 1 + ui/app/mirrors/create/cdc.tsx | 292 +++++++++--------- ui/app/mirrors/create/page.tsx | 6 + ui/app/mirrors/create/tablemapping.tsx | 295 +++++++++---------- ui/app/peers/create/[peerType]/helpers/bq.ts | 74 ----- ui/app/peers/create/[peerType]/page.tsx | 4 +- ui/app/peers/create/[peerType]/schema.ts | 6 +- ui/app/peers/peersTable.tsx | 2 +- ui/components/BigqueryConfig.tsx | 132 +++++++++ 10 files changed, 460 insertions(+), 361 deletions(-) create mode 100644 ui/components/BigqueryConfig.tsx diff --git a/flow/connectors/bigquery/bigquery.go b/flow/connectors/bigquery/bigquery.go index 7383bac2a6..1a3b7fab6d 100644 --- a/flow/connectors/bigquery/bigquery.go +++ b/flow/connectors/bigquery/bigquery.go @@ -165,6 +165,13 @@ func NewBigQueryConnector(ctx context.Context, config *protos.BigqueryConfig) (* return nil, fmt.Errorf("failed to create BigQuery client: %v", err) } + datasetID := config.GetDatasetId() + _, checkErr := client.Dataset(datasetID).Metadata(ctx) + if checkErr != nil { + log.Errorf("failed to get dataset metadata: %v", checkErr) + return nil, fmt.Errorf("failed to get dataset metadata: %v", checkErr) + } + storageClient, err := bqsa.CreateStorageClient(ctx) if err != nil { return nil, fmt.Errorf("failed to create Storage client: %v", err) @@ -174,7 +181,7 @@ func NewBigQueryConnector(ctx context.Context, config *protos.BigqueryConfig) (* ctx: ctx, bqConfig: config, client: client, - datasetID: config.GetDatasetId(), + datasetID: datasetID, storageClient: storageClient, }, nil } diff --git a/ui/app/api/peers/route.ts b/ui/app/api/peers/route.ts index 0a99717406..b1d1359e19 100644 --- a/ui/app/api/peers/route.ts +++ b/ui/app/api/peers/route.ts @@ -82,6 +82,7 @@ export async function POST(request: Request) { return new Response(JSON.stringify(response)); } else if (mode === 'create') { const req: CreatePeerRequest = { peer }; + console.log('/peer/create req:', req); const createStatus: CreatePeerResponse = await fetch( `${flowServiceAddr}/v1/peers/create`, { diff --git a/ui/app/mirrors/create/cdc.tsx b/ui/app/mirrors/create/cdc.tsx index d89072b6c7..98311238da 100644 --- a/ui/app/mirrors/create/cdc.tsx +++ b/ui/app/mirrors/create/cdc.tsx @@ -1,13 +1,13 @@ 'use client'; import { RequiredIndicator } from '@/components/RequiredIndicator'; import { QRepSyncMode } from '@/grpc_generated/flow'; -import { DBType } from '@/grpc_generated/peers'; +import { DBType, dBTypeToJSON } from '@/grpc_generated/peers'; import { Label } from '@/lib/Label'; import { RowWithSelect, RowWithSwitch, RowWithTextField } from '@/lib/Layout'; import { Select, SelectItem } from '@/lib/Select'; import { Switch } from '@/lib/Switch'; import { TextField } from '@/lib/TextField'; -import { Dispatch, SetStateAction } from 'react'; +import { Dispatch, SetStateAction, useEffect } from 'react'; import { InfoPopover } from '../../../components/InfoPopover'; import { CDCConfig, MirrorSetter, TableMapRow } from '../../dto/MirrorsDTO'; import { MirrorSetting } from './helpers/common'; @@ -21,6 +21,7 @@ interface MirrorConfigProps { setRows: Dispatch>; schema: string; setSchema: Dispatch>; + setValidSource: Dispatch>; } export const defaultSyncMode = (dtype: DBType | undefined) => { @@ -34,9 +35,18 @@ export const defaultSyncMode = (dtype: DBType | undefined) => { } }; -export default function CDCConfigForm(props: MirrorConfigProps) { +export default function CDCConfigForm({ + settings, + mirrorConfig, + setter, + rows, + setRows, + schema, + setSchema, + setValidSource, +}: MirrorConfigProps) { const setToDefault = (setting: MirrorSetting) => { - const destinationPeerType = props.mirrorConfig.destination?.type; + const destinationPeerType = mirrorConfig.destination?.type; return ( setting.label.includes('Sync') && (destinationPeerType === DBType.POSTGRES || @@ -51,149 +61,167 @@ export default function CDCConfigForm(props: MirrorConfigProps) { ? QRepSyncMode.QREP_SYNC_MODE_STORAGE_AVRO : QRepSyncMode.QREP_SYNC_MODE_MULTI_INSERT; } - setting.stateHandler(stateVal, props.setter); + setting.stateHandler(stateVal, setter); }; const paramDisplayCondition = (setting: MirrorSetting) => { const label = setting.label.toLowerCase(); if ( - (label.includes('snapshot') && - props.mirrorConfig.doInitialCopy !== true) || + (label.includes('snapshot') && mirrorConfig.doInitialCopy !== true) || (label.includes('snapshot staging') && - props.mirrorConfig.snapshotSyncMode?.toString() !== '1') || + mirrorConfig.snapshotSyncMode?.toString() !== '1') || (label.includes('cdc staging') && - props.mirrorConfig.cdcSyncMode?.toString() !== '1') + mirrorConfig.cdcSyncMode?.toString() !== '1') ) { return false; } return true; }; - return ( - <> - {props.mirrorConfig.source && ( + useEffect(() => { + if ( + mirrorConfig.source != undefined && + dBTypeToJSON(mirrorConfig.source?.type) === 'POSTGRES' + ) + setValidSource(true); + else { + setValidSource(false); + setRows([]); + } + }, [mirrorConfig.source, setValidSource, setRows]); + + if ( + mirrorConfig.source != undefined && + dBTypeToJSON(mirrorConfig.source?.type) === 'POSTGRES' + ) + return ( + <> - )} - {props.settings.map((setting, id) => { - return ( - paramDisplayCondition(setting) && - (setting.type === 'switch' ? ( - {setting.label}} - action={ -
- - handleChange(state, setting) - } - /> - {setting.tips && ( - - )} -
- } - /> - ) : setting.type === 'select' ? ( - - {setting.label} - {RequiredIndicator(setting.required)} - - } - action={ -
- - {setting.tips && ( - + handleChange(state, setting) + } /> - )} -
- } - /> - ) : ( - - {setting.label} - {RequiredIndicator(setting.required)} - - } - action={ -
- ) => - handleChange(e.target.value, setting) - } - /> - {setting.tips && ( - + )} +
+ } + /> + ) : setting.type === 'select' ? ( + + {setting.label} + {RequiredIndicator(setting.required)} + + } + action={ +
+ + {setting.tips && ( + + )} +
+ } + /> + ) : ( + + {setting.label} + {RequiredIndicator(setting.required)} + + } + action={ +
+ ) => + handleChange(e.target.value, setting) + } /> - )} -
- } - /> - )) - ); - })} - - ); + {setting.tips && ( + + )} + + } + /> + )) + ); + })} + + ); + else { + return mirrorConfig.source ? ( + + ) : ( + <> + ); + } } diff --git a/ui/app/mirrors/create/page.tsx b/ui/app/mirrors/create/page.tsx index ebb304da6d..f73b211037 100644 --- a/ui/app/mirrors/create/page.tsx +++ b/ui/app/mirrors/create/page.tsx @@ -42,6 +42,7 @@ export default function CreateMirrors() { const [config, setConfig] = useState(blankCDCSetting); const [peers, setPeers] = useState([]); const [rows, setRows] = useState([]); + const [validSource, setValidSource] = useState(false); const [sourceSchema, setSourceSchema] = useState('public'); const [qrepQuery, setQrepQuery] = useState(`-- Here's a sample template: @@ -167,6 +168,7 @@ export default function CreateMirrors() {