From db102b9bfe3b66cfeb9fab209002d027eca7689b Mon Sep 17 00:00:00 2001 From: Andrey Avtomonov Date: Wed, 13 May 2026 20:05:46 +0200 Subject: [PATCH] fix(cli): use public source labels in ingest reports --- packages/cli/src/ingest.test.ts | 72 +++++++++++++++++++++++++++++++-- packages/cli/src/ingest.ts | 25 +++++++++++- 2 files changed, 92 insertions(+), 5 deletions(-) diff --git a/packages/cli/src/ingest.test.ts b/packages/cli/src/ingest.test.ts index 12417043..5198cca2 100644 --- a/packages/cli/src/ingest.test.ts +++ b/packages/cli/src/ingest.test.ts @@ -103,6 +103,70 @@ describe('runKtxIngest', () => { expect(statusIo.stderr()).toBe(''); }); + it('labels internal database reports without adapter names in plain status output', async () => { + const projectDir = join(tempDir, 'project'); + await writeWarehouseConfig(projectDir); + const report = localFakeBundleReport('scan-job-1', { + id: 'report-scan-1', + runId: 'run-scan-1', + connectionId: 'warehouse', + sourceKey: 'live-database', + }); + const io = makeIo(); + + await expect( + runKtxIngest( + { + command: 'status', + projectDir, + reportFile: '/tmp/scan-report.json', + outputMode: 'plain', + }, + io.io, + { + readReportFile: vi.fn(async () => report), + }, + ), + ).resolves.toBe(0); + + expect(io.stdout()).toContain('Source: Database schema\n'); + expect(io.stdout()).not.toContain('Adapter:'); + expect(io.stdout()).not.toContain('live-database'); + expect(io.stderr()).toBe(''); + }); + + it('labels internal query-history reports without adapter names in plain status output', async () => { + const projectDir = join(tempDir, 'project'); + await writeWarehouseConfig(projectDir); + const report = localFakeBundleReport('query-history-job-1', { + id: 'report-query-history-1', + runId: 'run-query-history-1', + connectionId: 'warehouse', + sourceKey: 'historic-sql', + }); + const io = makeIo(); + + await expect( + runKtxIngest( + { + command: 'status', + projectDir, + reportFile: '/tmp/query-history-report.json', + outputMode: 'plain', + }, + io.io, + { + readReportFile: vi.fn(async () => report), + }, + ), + ).resolves.toBe(0); + + expect(io.stdout()).toContain('Source: Query history\n'); + expect(io.stdout()).not.toContain('Adapter:'); + expect(io.stdout()).not.toContain('historic-sql'); + expect(io.stderr()).toBe(''); + }); + it('emits structured progress for non-TTY local ingest runs', async () => { const projectDir = join(tempDir, 'project'); await writeWarehouseConfig(projectDir); @@ -654,7 +718,7 @@ describe('runKtxIngest', () => { ), ).resolves.toBe(0); expect(statusIo.stdout()).toContain('Job: metabase-child-1'); - expect(statusIo.stdout()).toContain('Adapter: metabase'); + expect(statusIo.stdout()).toContain('Source: Metabase'); expect(statusIo.stdout()).toContain('Connection: warehouse_a'); expect(statusIo.stderr()).toBe(''); }); @@ -879,7 +943,7 @@ describe('runKtxIngest', () => { ).resolves.toBe(0); expect(io.stderr()).toBe(''); - expect(io.stdout()).toContain('Adapter: historic-sql\n'); + expect(io.stdout()).toContain('Source: Query history\n'); expect(io.stdout()).toContain('Saved memory: 35 wiki, 57 SL\n'); }); @@ -1594,7 +1658,7 @@ describe('runKtxIngest', () => { expect(io.stderr()).toBe(''); expect(io.stdout()).toContain('Job: cli-looker-job'); - expect(io.stdout()).toContain('Adapter: looker'); + expect(io.stdout()).toContain('Source: Looker'); expect(io.stdout()).toContain('Connection: prod-looker'); expect(io.stdout()).toContain('Status: done'); expect(io.stdout()).toContain('Saved memory: 0 wiki, 1 SL'); @@ -1617,7 +1681,7 @@ describe('runKtxIngest', () => { ), ).resolves.toBe(0); expect(statusIo.stdout()).toContain('Job: cli-looker-job'); - expect(statusIo.stdout()).toContain('Adapter: looker'); + expect(statusIo.stdout()).toContain('Source: Looker'); expect(statusIo.stderr()).toBe(''); }); diff --git a/packages/cli/src/ingest.ts b/packages/cli/src/ingest.ts index b1321fde..4cf25b05 100644 --- a/packages/cli/src/ingest.ts +++ b/packages/cli/src/ingest.ts @@ -103,13 +103,36 @@ function reportStatus(report: IngestReportSnapshot): 'done' | 'error' { return report.body.failedWorkUnits.length > 0 ? 'error' : 'done'; } +const REPORT_SOURCE_LABELS = new Map([ + ['live-database', 'Database schema'], + ['historic-sql', 'Query history'], + ['dbt', 'dbt'], + ['metricflow', 'MetricFlow'], + ['lookml', 'LookML'], + ['looker', 'Looker'], + ['metabase', 'Metabase'], + ['notion', 'Notion'], +]); + +function reportSourceLabel(sourceKey: string): string { + const label = REPORT_SOURCE_LABELS.get(sourceKey); + if (label) { + return label; + } + return sourceKey + .split(/[-_]+/) + .filter((part) => part.length > 0) + .map((part) => `${part[0]?.toUpperCase() ?? ''}${part.slice(1)}`) + .join(' '); +} + function writeReportStatus(report: IngestReportSnapshot, io: KtxIngestIo): void { const counts = savedMemoryCountsForReport(report); io.stdout.write(`Report: ${report.id}\n`); io.stdout.write(`Run: ${report.runId}\n`); io.stdout.write(`Job: ${report.jobId}\n`); io.stdout.write(`Status: ${reportStatus(report)}\n`); - io.stdout.write(`Adapter: ${report.sourceKey}\n`); + io.stdout.write(`Source: ${reportSourceLabel(report.sourceKey)}\n`); io.stdout.write(`Connection: ${report.connectionId}\n`); io.stdout.write(`Sync: ${report.body.syncId}\n`); io.stdout.write(