From 6c14b406dab522bb48030f4af3959173fed9601e Mon Sep 17 00:00:00 2001 From: Amogh-Bharadwaj Date: Mon, 13 Nov 2023 08:46:13 -0500 Subject: [PATCH 1/5] add validation for mirror name --- ui/app/mirrors/create/handlers.ts | 25 ++++++++++++++++++------- ui/app/mirrors/create/schema.ts | 10 ++++++++++ 2 files changed, 28 insertions(+), 7 deletions(-) diff --git a/ui/app/mirrors/create/handlers.ts b/ui/app/mirrors/create/handlers.ts index 31ff45a66b..1e5e0d52b3 100644 --- a/ui/app/mirrors/create/handlers.ts +++ b/ui/app/mirrors/create/handlers.ts @@ -7,7 +7,12 @@ import { import { QRepConfig, QRepWriteType } from '@/grpc_generated/flow'; import { Dispatch, SetStateAction } from 'react'; import { CDCConfig, TableMapRow } from '../../dto/MirrorsDTO'; -import { cdcSchema, qrepSchema, tableMappingSchema } from './schema'; +import { + cdcSchema, + flowNameSchema, + qrepSchema, + tableMappingSchema, +} from './schema'; const validateCDCFields = ( tableMapping: TableMapRow[], @@ -85,10 +90,13 @@ export const handleCreateCDC = async ( setLoading: Dispatch>, route: RouteCallback ) => { - if (!flowJobName) { - setMsg({ ok: false, msg: 'Mirror name is required' }); - return; + const flowNameValid = flowNameSchema.safeParse(flowJobName); + if (!flowNameValid.success) { + const flowNameErr = flowNameValid.error.issues[0].message; + setMsg({ ok: false, msg: flowNameErr }); + return false; } + const isValid = validateCDCFields(rows, setMsg, config); if (!isValid) return; const tableNameMapping = reformattedTableMapping(rows); @@ -125,10 +133,13 @@ export const handleCreateQRep = async ( route: RouteCallback, xmin?: boolean ) => { - if (!flowJobName) { - setMsg({ ok: false, msg: 'Mirror name is required' }); - return; + const flowNameValid = flowNameSchema.safeParse(flowJobName); + if (!flowNameValid.success) { + const flowNameErr = flowNameValid.error.issues[0].message; + setMsg({ ok: false, msg: flowNameErr }); + return false; } + if (xmin == true) { config.watermarkColumn = 'xmin'; config.query = `SELECT * FROM ${config.watermarkTable} WHERE xmin::text::bigint BETWEEN {{.start}} AND {{.end}}`; diff --git a/ui/app/mirrors/create/schema.ts b/ui/app/mirrors/create/schema.ts index d7d03d45fa..1d14b02569 100644 --- a/ui/app/mirrors/create/schema.ts +++ b/ui/app/mirrors/create/schema.ts @@ -1,5 +1,15 @@ import * as z from 'zod'; +export const flowNameSchema = z + .string({ + invalid_type_error: 'Mirror name is invalid.', + required_error: 'Mirror name is required.', + }) + .min(1, { message: 'Mirror name cannot be empty.' }) + .regex(/^[\w]*$/, { + message: 'Mirror name must contain only letters, numbers and underscores', + }); + export const tableMappingSchema = z .array( z.object({ From 75595aa5596499cdf8f3a6e0e7f16bc5d5b703a2 Mon Sep 17 00:00:00 2001 From: Amogh-Bharadwaj Date: Mon, 13 Nov 2023 08:58:18 -0500 Subject: [PATCH 2/5] pretty --- ui/app/mirrors/create/handlers.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/app/mirrors/create/handlers.ts b/ui/app/mirrors/create/handlers.ts index 1e5e0d52b3..fad6160b81 100644 --- a/ui/app/mirrors/create/handlers.ts +++ b/ui/app/mirrors/create/handlers.ts @@ -139,7 +139,7 @@ export const handleCreateQRep = async ( setMsg({ ok: false, msg: flowNameErr }); return false; } - + if (xmin == true) { config.watermarkColumn = 'xmin'; config.query = `SELECT * FROM ${config.watermarkTable} WHERE xmin::text::bigint BETWEEN {{.start}} AND {{.end}}`; From 179c10dd42483eca63f64c3a3e24030582c621d4 Mon Sep 17 00:00:00 2001 From: Amogh-Bharadwaj Date: Mon, 13 Nov 2023 10:08:26 -0500 Subject: [PATCH 3/5] correct return types for handlers --- ui/app/mirrors/create/handlers.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ui/app/mirrors/create/handlers.ts b/ui/app/mirrors/create/handlers.ts index fad6160b81..20c13bbfb7 100644 --- a/ui/app/mirrors/create/handlers.ts +++ b/ui/app/mirrors/create/handlers.ts @@ -94,7 +94,7 @@ export const handleCreateCDC = async ( if (!flowNameValid.success) { const flowNameErr = flowNameValid.error.issues[0].message; setMsg({ ok: false, msg: flowNameErr }); - return false; + return; } const isValid = validateCDCFields(rows, setMsg, config); @@ -137,7 +137,7 @@ export const handleCreateQRep = async ( if (!flowNameValid.success) { const flowNameErr = flowNameValid.error.issues[0].message; setMsg({ ok: false, msg: flowNameErr }); - return false; + return; } if (xmin == true) { From 56b8d18755479950a69c554a883a94874ebf07f4 Mon Sep 17 00:00:00 2001 From: Amogh-Bharadwaj Date: Mon, 13 Nov 2023 10:11:12 -0500 Subject: [PATCH 4/5] pretty 2 --- ui/app/mirrors/create/qrep.tsx | 4 ++-- ui/app/peers/[peerName]/helpers.tsx | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/ui/app/mirrors/create/qrep.tsx b/ui/app/mirrors/create/qrep.tsx index 4e94b98d21..29922b4bb7 100644 --- a/ui/app/mirrors/create/qrep.tsx +++ b/ui/app/mirrors/create/qrep.tsx @@ -166,8 +166,8 @@ export default function QRepConfigForm({ setting.label === 'Create Destination Table' ? mirrorConfig.setupWatermarkTableOnDestination : setting.label === 'Initial Copy Only' - ? mirrorConfig.initialCopyOnly - : mirrorConfig.dstTableFullResync + ? mirrorConfig.initialCopyOnly + : mirrorConfig.dstTableFullResync } onCheckedChange={(state: boolean) => handleChange(state, setting) diff --git a/ui/app/peers/[peerName]/helpers.tsx b/ui/app/peers/[peerName]/helpers.tsx index 503455af7a..b219fe5c61 100644 --- a/ui/app/peers/[peerName]/helpers.tsx +++ b/ui/app/peers/[peerName]/helpers.tsx @@ -35,8 +35,8 @@ export const DurationDisplay = ({ duration }: { duration: number }) => { (duration % 3600) / 60 )} minutes` : duration >= 60 - ? `${Math.floor(duration / 60)} minute(s) ${Math.floor( - duration % 60 - )} seconds` - : `${duration.toFixed(2)} seconds`; + ? `${Math.floor(duration / 60)} minute(s) ${Math.floor( + duration % 60 + )} seconds` + : `${duration.toFixed(2)} seconds`; }; From a9ba67c24aa00b8f9f90da181dea751139ce8ee2 Mon Sep 17 00:00:00 2001 From: Amogh-Bharadwaj Date: Mon, 13 Nov 2023 10:35:53 -0500 Subject: [PATCH 5/5] pretty 3 --- ui/app/mirrors/create/qrep.tsx | 4 ++-- ui/app/peers/[peerName]/helpers.tsx | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/ui/app/mirrors/create/qrep.tsx b/ui/app/mirrors/create/qrep.tsx index 29922b4bb7..4e94b98d21 100644 --- a/ui/app/mirrors/create/qrep.tsx +++ b/ui/app/mirrors/create/qrep.tsx @@ -166,8 +166,8 @@ export default function QRepConfigForm({ setting.label === 'Create Destination Table' ? mirrorConfig.setupWatermarkTableOnDestination : setting.label === 'Initial Copy Only' - ? mirrorConfig.initialCopyOnly - : mirrorConfig.dstTableFullResync + ? mirrorConfig.initialCopyOnly + : mirrorConfig.dstTableFullResync } onCheckedChange={(state: boolean) => handleChange(state, setting) diff --git a/ui/app/peers/[peerName]/helpers.tsx b/ui/app/peers/[peerName]/helpers.tsx index b219fe5c61..503455af7a 100644 --- a/ui/app/peers/[peerName]/helpers.tsx +++ b/ui/app/peers/[peerName]/helpers.tsx @@ -35,8 +35,8 @@ export const DurationDisplay = ({ duration }: { duration: number }) => { (duration % 3600) / 60 )} minutes` : duration >= 60 - ? `${Math.floor(duration / 60)} minute(s) ${Math.floor( - duration % 60 - )} seconds` - : `${duration.toFixed(2)} seconds`; + ? `${Math.floor(duration / 60)} minute(s) ${Math.floor( + duration % 60 + )} seconds` + : `${duration.toFixed(2)} seconds`; };