diff --git a/packages/context/src/project/driver-schemas.test.ts b/packages/context/src/project/driver-schemas.test.ts new file mode 100644 index 00000000..2cb6eac4 --- /dev/null +++ b/packages/context/src/project/driver-schemas.test.ts @@ -0,0 +1,35 @@ +import { describe, expect, it } from 'vitest'; +import { connectionConfigSchema } from './driver-schemas.js'; + +describe('connectionConfigSchema (driver discriminated union)', () => { + it.each([ + ['postgres', 'postgres://user:pass@host:5432/db'], + ['postgresql', 'postgresql://user:pass@host:5432/db'], + ['mysql', 'mysql://user:pass@host:3306/db'], + ['snowflake', 'snowflake://account/db'], + ['bigquery', 'bigquery://project/dataset'], + ['sqlite', 'sqlite:///tmp/db.sqlite'], + ['clickhouse', 'clickhouse://host:8123/db'], + ['sqlserver', 'sqlserver://host:1433;database=db'], + ])('parses %s warehouse connection', (driver, url) => { + expect(connectionConfigSchema.parse({ driver, url })).toMatchObject({ driver, url }); + }); + + it('preserves unknown warehouse fields via looseObject passthrough', () => { + const parsed = connectionConfigSchema.parse({ + driver: 'postgres', + url: 'postgres://x', + historicSql: { enabled: true }, + context: { queryHistory: { enabled: false } }, + }); + expect(parsed).toMatchObject({ + driver: 'postgres', + historicSql: { enabled: true }, + context: { queryHistory: { enabled: false } }, + }); + }); + + it('rejects an unknown driver', () => { + expect(() => connectionConfigSchema.parse({ driver: 'nope', url: 'x' })).toThrow(); + }); +}); diff --git a/packages/context/src/project/driver-schemas.ts b/packages/context/src/project/driver-schemas.ts new file mode 100644 index 00000000..a584fc7c --- /dev/null +++ b/packages/context/src/project/driver-schemas.ts @@ -0,0 +1,44 @@ +import * as z from 'zod'; + +const warehouseDrivers = [ + 'postgres', + 'postgresql', + 'mysql', + 'snowflake', + 'bigquery', + 'sqlite', + 'clickhouse', + 'sqlserver', +] as const; + +type WarehouseDriver = (typeof warehouseDrivers)[number]; + +function warehouseConnectionSchema(driver: Driver) { + return z + .looseObject({ + driver: z.literal(driver), + url: z + .string() + .min(1) + .optional() + .describe('Warehouse connection URL or DSN; may contain environment-variable references like env:DATABASE_URL.'), + }) + .describe( + `${driver} warehouse connection. Additional driver-tunable fields (e.g. historicSql, context.queryHistory) are accepted and passed through.`, + ); +} + +const warehouseConnectionSchemas = [ + warehouseConnectionSchema('postgres'), + warehouseConnectionSchema('postgresql'), + warehouseConnectionSchema('mysql'), + warehouseConnectionSchema('snowflake'), + warehouseConnectionSchema('bigquery'), + warehouseConnectionSchema('sqlite'), + warehouseConnectionSchema('clickhouse'), + warehouseConnectionSchema('sqlserver'), +] as const; + +export const connectionConfigSchema = z.discriminatedUnion('driver', warehouseConnectionSchemas); + +export type KtxConnectionConfig = z.infer;