diff --git a/ui/app/mirrors/create/handlers.ts b/ui/app/mirrors/create/handlers.ts index 31ff45a66b..20c13bbfb7 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' }); + const flowNameValid = flowNameSchema.safeParse(flowJobName); + if (!flowNameValid.success) { + const flowNameErr = flowNameValid.error.issues[0].message; + setMsg({ ok: false, msg: flowNameErr }); return; } + 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' }); + const flowNameValid = flowNameSchema.safeParse(flowJobName); + if (!flowNameValid.success) { + const flowNameErr = flowNameValid.error.issues[0].message; + setMsg({ ok: false, msg: flowNameErr }); return; } + 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({