From 736db92b3fea5f8cc66c248268527c752766e6f2 Mon Sep 17 00:00:00 2001 From: Andrey Avtomonov Date: Thu, 14 May 2026 16:50:50 +0200 Subject: [PATCH] refactor(context): export and describe mapping shape schemas --- .../src/project/mappings-yaml-schema.test.ts | 16 ++++++ .../src/project/mappings-yaml-schema.ts | 50 ++++++++++++++----- 2 files changed, 53 insertions(+), 13 deletions(-) diff --git a/packages/context/src/project/mappings-yaml-schema.test.ts b/packages/context/src/project/mappings-yaml-schema.test.ts index 5497bba9..f7001a70 100644 --- a/packages/context/src/project/mappings-yaml-schema.test.ts +++ b/packages/context/src/project/mappings-yaml-schema.test.ts @@ -1,5 +1,8 @@ import { describe, expect, it } from 'vitest'; import { + lookerMappingsSchema, + lookmlMappingsSchema, + metabaseMappingsSchema, parseConnectionMappingBootstrap, parseLookmlMappingBootstrap, parseLookerMappingBootstrap, @@ -82,4 +85,17 @@ describe('ktx.yaml mapping bootstrap schema', () => { }), ).toMatchObject({ adapter: 'looker', connectionId: 'prod-looker' }); }); + + it('exports mapping shapes that parse documented examples', () => { + expect(metabaseMappingsSchema.parse({ databaseMappings: { '1': 'wh' } })).toMatchObject({ + databaseMappings: { '1': 'wh' }, + syncMode: 'ALL', + }); + expect(lookerMappingsSchema.parse({ connectionMappings: { x: 'wh' } })).toEqual({ + connectionMappings: { x: 'wh' }, + }); + expect(lookmlMappingsSchema.parse({ expectedLookerConnectionName: 'x' })).toEqual({ + expectedLookerConnectionName: 'x', + }); + }); }); diff --git a/packages/context/src/project/mappings-yaml-schema.ts b/packages/context/src/project/mappings-yaml-schema.ts index 67f14627..c20b0e69 100644 --- a/packages/context/src/project/mappings-yaml-schema.ts +++ b/packages/context/src/project/mappings-yaml-schema.ts @@ -11,24 +11,48 @@ const metabaseSelectionsSchema = z items: z.array(positiveIntegerValueSchema).default([]), }); -const metabaseMappingsSchema = z +export const metabaseMappingsSchema = z .object({ - databaseMappings: z.record(z.string(), stringTargetSchema).default({}), - syncEnabled: z.record(z.string(), z.boolean()).default({}), - syncMode: metabaseSyncModeSchema.default('ALL'), - selections: metabaseSelectionsSchema.default({ collections: [], items: [] }), - defaultTagNames: z.array(z.string().min(1)).default([]), - }); + databaseMappings: z + .record(z.string(), stringTargetSchema) + .default({}) + .describe('Map of Metabase database ID (positive integer string) to KTX connection ID. Use null to explicitly unmap.'), + syncEnabled: z + .record(z.string(), z.boolean()) + .default({}) + .describe('Per-Metabase-database sync toggle, keyed by Metabase database ID string.'), + syncMode: metabaseSyncModeSchema + .default('ALL') + .describe('Sync scope: ALL ingests every mapped DB; ONLY restricts to syncEnabled=true; EXCEPT excludes syncEnabled=true.'), + selections: metabaseSelectionsSchema + .default({ collections: [], items: [] }) + .describe('Optional Metabase collection and item IDs to scope ingest.'), + defaultTagNames: z + .array(z.string().min(1)) + .default([]) + .describe('Default tag names applied to ingested Metabase artifacts.'), + }) + .describe('Metabase database-to-warehouse mapping and sync configuration.'); -const lookerMappingsSchema = z +export const lookerMappingsSchema = z .object({ - connectionMappings: z.record(z.string().min(1), stringTargetSchema).default({}), - }); + connectionMappings: z + .record(z.string().min(1), stringTargetSchema) + .default({}) + .describe('Map of Looker connection name to KTX connection ID. Use null to explicitly unmap.'), + }) + .describe('Looker connection-to-warehouse mapping configuration.'); -const lookmlMappingsSchema = z +export const lookmlMappingsSchema = z .object({ - expectedLookerConnectionName: z.string().min(1).nullable().default(null), - }); + expectedLookerConnectionName: z + .string() + .min(1) + .nullable() + .default(null) + .describe('Looker connection name that LookML models must declare; mismatches block sl_write_source at ingest time.'), + }) + .describe('LookML connection-name expectation for ingest gating.'); export type MetabaseMappingBootstrap = { adapter: 'metabase';