mirror of
https://github.com/Kaelio/ktx.git
synced 2026-06-07 07:55:13 +02:00
refactor: remove legacy compatibility shims (#208)
This commit is contained in:
parent
394a985d2a
commit
db09936085
17 changed files with 61 additions and 361 deletions
|
|
@ -121,10 +121,6 @@ describe('parseIngestSummary', () => {
|
||||||
expect(parseIngestSummary('Tasks: 5\nStatus: done')).toBe('5 tasks');
|
expect(parseIngestSummary('Tasks: 5\nStatus: done')).toBe('5 tasks');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('still parses the legacy "Work units:" wording for backward compat', () => {
|
|
||||||
expect(parseIngestSummary('Work units: 7\nStatus: done')).toBe('7 tasks');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('extracts saved memory alone when no task count', () => {
|
it('extracts saved memory alone when no task count', () => {
|
||||||
expect(parseIngestSummary('Saved memory: 3 wiki, 2 SL')).toBe('3 wiki, 2 SL');
|
expect(parseIngestSummary('Saved memory: 3 wiki, 2 SL')).toBe('3 wiki, 2 SL');
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -462,7 +462,7 @@ export function parseScanSummary(output: string): string | null {
|
||||||
export function parseIngestSummary(output: string): string | null {
|
export function parseIngestSummary(output: string): string | null {
|
||||||
const savedMemory = output.match(/Saved memory: (.+)/);
|
const savedMemory = output.match(/Saved memory: (.+)/);
|
||||||
if (savedMemory) return savedMemory[1];
|
if (savedMemory) return savedMemory[1];
|
||||||
const tasks = output.match(/(?:Tasks|Work units): (\d+)/);
|
const tasks = output.match(/Tasks: (\d+)/);
|
||||||
if (tasks) return `${tasks[1]} tasks`;
|
if (tasks) return `${tasks[1]} tasks`;
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,20 +1,9 @@
|
||||||
import type { HistoricSqlDialect } from './types.js';
|
import type { HistoricSqlDialect } from './types.js';
|
||||||
|
|
||||||
const KNOWN_DIALECTS = ['postgres', 'bigquery', 'snowflake'] as const;
|
|
||||||
|
|
||||||
function isKnownDialect(value: string): value is HistoricSqlDialect {
|
|
||||||
return (KNOWN_DIALECTS as readonly string[]).includes(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
function recordOrNull(value: unknown): Record<string, unknown> | null {
|
function recordOrNull(value: unknown): Record<string, unknown> | null {
|
||||||
return value && typeof value === 'object' && !Array.isArray(value) ? (value as Record<string, unknown>) : null;
|
return value && typeof value === 'object' && !Array.isArray(value) ? (value as Record<string, unknown>) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
function historicSqlRecord(connection: unknown): Record<string, unknown> | null {
|
|
||||||
const conn = recordOrNull(connection);
|
|
||||||
return conn ? recordOrNull(conn.historicSql) : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
function queryHistoryRecord(connection: unknown): Record<string, unknown> | null {
|
function queryHistoryRecord(connection: unknown): Record<string, unknown> | null {
|
||||||
const conn = recordOrNull(connection);
|
const conn = recordOrNull(connection);
|
||||||
const context = conn ? recordOrNull(conn.context) : null;
|
const context = conn ? recordOrNull(conn.context) : null;
|
||||||
|
|
@ -22,11 +11,7 @@ function queryHistoryRecord(connection: unknown): Record<string, unknown> | null
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isQueryHistoryEnabled(connection: unknown): boolean {
|
export function isQueryHistoryEnabled(connection: unknown): boolean {
|
||||||
const queryHistory = queryHistoryRecord(connection);
|
return queryHistoryRecord(connection)?.enabled === true;
|
||||||
if (queryHistory) {
|
|
||||||
return queryHistory.enabled === true;
|
|
||||||
}
|
|
||||||
return historicSqlRecord(connection)?.enabled === true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -43,6 +28,5 @@ export function queryHistoryDialectForConnection(connection: unknown): HistoricS
|
||||||
if (driver === 'postgres' || driver === 'postgresql') return 'postgres';
|
if (driver === 'postgres' || driver === 'postgresql') return 'postgres';
|
||||||
if (driver === 'bigquery') return 'bigquery';
|
if (driver === 'bigquery') return 'bigquery';
|
||||||
if (driver === 'snowflake') return 'snowflake';
|
if (driver === 'snowflake') return 'snowflake';
|
||||||
const legacy = String(historicSqlRecord(connection)?.dialect ?? '').toLowerCase();
|
return null;
|
||||||
return isKnownDialect(legacy) ? legacy : null;
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -152,10 +152,10 @@ async function writeHistoricSqlProject(project: KtxLocalProject): Promise<KtxLoc
|
||||||
'connections:',
|
'connections:',
|
||||||
' warehouse:',
|
' warehouse:',
|
||||||
' driver: postgres',
|
' driver: postgres',
|
||||||
' historicSql:',
|
' context:',
|
||||||
' enabled: true',
|
' queryHistory:',
|
||||||
' dialect: postgres',
|
' enabled: true',
|
||||||
' minExecutions: 2',
|
' minExecutions: 2',
|
||||||
'ingest:',
|
'ingest:',
|
||||||
' adapters:',
|
' adapters:',
|
||||||
' - historic-sql',
|
' - historic-sql',
|
||||||
|
|
|
||||||
|
|
@ -187,14 +187,15 @@ describe('local ingest adapters', () => {
|
||||||
warehouse: {
|
warehouse: {
|
||||||
driver: 'postgres',
|
driver: 'postgres',
|
||||||
url: 'env:WAREHOUSE_DATABASE_URL',
|
url: 'env:WAREHOUSE_DATABASE_URL',
|
||||||
historicSql: {
|
context: {
|
||||||
enabled: true,
|
queryHistory: {
|
||||||
dialect: 'postgres',
|
enabled: true,
|
||||||
minExecutions: 7,
|
minExecutions: 7,
|
||||||
maxTemplatesPerRun: 123,
|
maxTemplatesPerRun: 123,
|
||||||
filters: {
|
filters: {
|
||||||
serviceAccounts: { patterns: ['^svc_'], mode: 'exclude' },
|
serviceAccounts: { patterns: ['^svc_'], mode: 'exclude' },
|
||||||
dropTrivialProbes: true,
|
dropTrivialProbes: true,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -236,22 +237,6 @@ describe('local ingest adapters', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('prefers context.queryHistory over legacy historicSql', async () => {
|
|
||||||
const project = projectWithConnections({
|
|
||||||
warehouse: {
|
|
||||||
driver: 'postgres',
|
|
||||||
historicSql: { enabled: true, dialect: 'postgres', windowDays: 90 },
|
|
||||||
context: { queryHistory: { enabled: true, windowDays: 30 } },
|
|
||||||
},
|
|
||||||
});
|
|
||||||
const adapter = { source: 'historic-sql' } as never;
|
|
||||||
|
|
||||||
await expect(localPullConfigForAdapter(project, adapter, 'warehouse')).resolves.toMatchObject({
|
|
||||||
dialect: 'postgres',
|
|
||||||
minExecutions: 5,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('rejects local historic-sql pulls when the connection has not enabled historic SQL', async () => {
|
it('rejects local historic-sql pulls when the connection has not enabled historic SQL', async () => {
|
||||||
const historicSql = createDefaultLocalIngestAdapters(project, {
|
const historicSql = createDefaultLocalIngestAdapters(project, {
|
||||||
historicSql: {
|
historicSql: {
|
||||||
|
|
|
||||||
|
|
@ -247,16 +247,10 @@ export async function localPullConfigForAdapter(
|
||||||
return historicSqlUnifiedPullConfigSchema.parse(options.historicSqlPullConfigOverride);
|
return historicSqlUnifiedPullConfigSchema.parse(options.historicSqlPullConfigOverride);
|
||||||
}
|
}
|
||||||
const queryHistory = queryHistoryPullConfig(connection);
|
const queryHistory = queryHistoryPullConfig(connection);
|
||||||
if (queryHistory) {
|
if (!queryHistory) {
|
||||||
return historicSqlUnifiedPullConfigSchema.parse(queryHistory);
|
|
||||||
}
|
|
||||||
const historicSql = isRecord(connection?.historicSql) ? connection.historicSql : null;
|
|
||||||
if (historicSql?.enabled !== true) {
|
|
||||||
throw new Error(`Connection "${connectionId}" does not have context.queryHistory.enabled: true`);
|
throw new Error(`Connection "${connectionId}" does not have context.queryHistory.enabled: true`);
|
||||||
}
|
}
|
||||||
return historicSqlUnifiedPullConfigSchema.parse({
|
return historicSqlUnifiedPullConfigSchema.parse(queryHistory);
|
||||||
...historicSql,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
if (adapter.source === 'looker') {
|
if (adapter.source === 'looker') {
|
||||||
await seedLocalMappingStateFromKtxYaml(project, connectionId);
|
await seedLocalMappingStateFromKtxYaml(project, connectionId);
|
||||||
|
|
|
||||||
|
|
@ -369,7 +369,7 @@ ingest:
|
||||||
llm:
|
llm:
|
||||||
backend: anthropic
|
backend: anthropic
|
||||||
`),
|
`),
|
||||||
).toThrow('Unsupported ingest.llm: use top-level llm.provider, llm.models, and ingest.workUnits');
|
).toThrow('Unsupported ingest.llm: unknown field');
|
||||||
|
|
||||||
expect(() =>
|
expect(() =>
|
||||||
parseKtxProjectConfig(`
|
parseKtxProjectConfig(`
|
||||||
|
|
@ -377,7 +377,7 @@ scan:
|
||||||
enrichment:
|
enrichment:
|
||||||
backend: gateway
|
backend: gateway
|
||||||
`),
|
`),
|
||||||
).toThrow('Unsupported scan.enrichment.backend: use scan.enrichment.mode');
|
).toThrow('Unsupported scan.enrichment.backend: unknown field');
|
||||||
|
|
||||||
expect(() =>
|
expect(() =>
|
||||||
parseKtxProjectConfig(`
|
parseKtxProjectConfig(`
|
||||||
|
|
@ -387,7 +387,7 @@ scan:
|
||||||
llm:
|
llm:
|
||||||
backend: gateway
|
backend: gateway
|
||||||
`),
|
`),
|
||||||
).toThrow('Unsupported scan.enrichment.llm: use top-level llm.provider and llm.models');
|
).toThrow('Unsupported scan.enrichment.llm: unknown field');
|
||||||
|
|
||||||
expect(() =>
|
expect(() =>
|
||||||
parseKtxProjectConfig(`
|
parseKtxProjectConfig(`
|
||||||
|
|
@ -478,28 +478,6 @@ scan:
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('attaches migration hints for known deprecated keys', () => {
|
|
||||||
const result = validateKtxProjectConfig(`
|
|
||||||
ingest:
|
|
||||||
llm:
|
|
||||||
backend: anthropic
|
|
||||||
scan:
|
|
||||||
enrichment:
|
|
||||||
backend: none
|
|
||||||
`);
|
|
||||||
|
|
||||||
expect(result.ok).toBe(false);
|
|
||||||
const findIssue = (path: string) => result.issues.find((issue) => issue.path === path);
|
|
||||||
expect(findIssue('ingest.llm')).toMatchObject({
|
|
||||||
message: 'Unsupported ingest.llm: use top-level llm.provider, llm.models, and ingest.workUnits',
|
|
||||||
fix: 'use top-level llm.provider, llm.models, and ingest.workUnits',
|
|
||||||
});
|
|
||||||
expect(findIssue('scan.enrichment.backend')).toMatchObject({
|
|
||||||
message: 'Unsupported scan.enrichment.backend: use scan.enrichment.mode',
|
|
||||||
fix: 'use scan.enrichment.mode',
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('reports YAML parse errors as a root-level issue', () => {
|
it('reports YAML parse errors as a root-level issue', () => {
|
||||||
const result = validateKtxProjectConfig(': not valid yaml :\n');
|
const result = validateKtxProjectConfig(': not valid yaml :\n');
|
||||||
expect(result.ok).toBe(false);
|
expect(result.ok).toBe(false);
|
||||||
|
|
|
||||||
|
|
@ -11,15 +11,6 @@ const KTX_WORK_UNIT_FAILURE_MODES = ['abort', 'continue'] as const;
|
||||||
const KTX_STORAGE_STATES = ['sqlite', 'postgres'] as const;
|
const KTX_STORAGE_STATES = ['sqlite', 'postgres'] as const;
|
||||||
const KTX_SEARCH_BACKENDS = ['sqlite-fts5', 'postgres-hybrid'] as const;
|
const KTX_SEARCH_BACKENDS = ['sqlite-fts5', 'postgres-hybrid'] as const;
|
||||||
|
|
||||||
const DEPRECATED_KEY_HINTS: Record<string, string> = {
|
|
||||||
'llm.provider.provider': 'use llm.provider.backend',
|
|
||||||
'ingest.llm': 'use top-level llm.provider, llm.models, and ingest.workUnits',
|
|
||||||
'ingest.embeddings.provider': 'use ingest.embeddings.backend',
|
|
||||||
'scan.enrichment.backend': 'use scan.enrichment.mode',
|
|
||||||
'scan.enrichment.llm': 'use top-level llm.provider and llm.models',
|
|
||||||
'scan.enrichment.embeddings.provider': 'use scan.enrichment.embeddings.backend',
|
|
||||||
};
|
|
||||||
|
|
||||||
const apiCredentialsSchema = z
|
const apiCredentialsSchema = z
|
||||||
.strictObject({
|
.strictObject({
|
||||||
api_key: z.string().min(1).optional().describe('API key for the provider. Read from this value or the provider-specific environment variable.'),
|
api_key: z.string().min(1).optional().describe('API key for the provider. Read from this value or the provider-specific environment variable.'),
|
||||||
|
|
@ -189,12 +180,7 @@ const setupSchema = z
|
||||||
.array(z.string().min(1))
|
.array(z.string().min(1))
|
||||||
.default([])
|
.default([])
|
||||||
.describe('Connection IDs (keys of the top-level `connections` map) that the setup wizard treats as the project\'s primary databases.'),
|
.describe('Connection IDs (keys of the top-level `connections` map) that the setup wizard treats as the project\'s primary databases.'),
|
||||||
completed_steps: z
|
|
||||||
.unknown()
|
|
||||||
.optional()
|
|
||||||
.describe('Deprecated. Accepted for backward compatibility but ignored; KTX no longer tracks setup progress here.'),
|
|
||||||
})
|
})
|
||||||
.transform(({ database_connection_ids }) => ({ database_connection_ids }))
|
|
||||||
.describe('Setup-wizard state captured during `ktx setup`.');
|
.describe('Setup-wizard state captured during `ktx setup`.');
|
||||||
|
|
||||||
const storageGitSchema = z
|
const storageGitSchema = z
|
||||||
|
|
@ -309,10 +295,6 @@ function formatIssue(issue: z.core.$ZodIssue, input: unknown): KtxConfigIssue[]
|
||||||
const keys = (issue as { keys?: readonly string[] }).keys ?? [];
|
const keys = (issue as { keys?: readonly string[] }).keys ?? [];
|
||||||
return keys.map((key) => {
|
return keys.map((key) => {
|
||||||
const fullPath = basePath.length > 0 ? `${basePath}.${key}` : key;
|
const fullPath = basePath.length > 0 ? `${basePath}.${key}` : key;
|
||||||
const hint = DEPRECATED_KEY_HINTS[fullPath];
|
|
||||||
if (hint !== undefined) {
|
|
||||||
return { path: fullPath, message: `Unsupported ${fullPath}: ${hint}`, fix: hint };
|
|
||||||
}
|
|
||||||
return { path: fullPath, message: `Unsupported ${fullPath}: unknown field` };
|
return { path: fullPath, message: `Unsupported ${fullPath}: unknown field` };
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,12 +19,12 @@ describe('connectionConfigSchema (driver discriminated union)', () => {
|
||||||
const parsed = connectionConfigSchema.parse({
|
const parsed = connectionConfigSchema.parse({
|
||||||
driver: 'postgres',
|
driver: 'postgres',
|
||||||
url: 'postgres://x',
|
url: 'postgres://x',
|
||||||
historicSql: { enabled: true },
|
customField: { enabled: true },
|
||||||
context: { queryHistory: { enabled: false } },
|
context: { queryHistory: { enabled: false } },
|
||||||
});
|
});
|
||||||
expect(parsed).toMatchObject({
|
expect(parsed).toMatchObject({
|
||||||
driver: 'postgres',
|
driver: 'postgres',
|
||||||
historicSql: { enabled: true },
|
customField: { enabled: true },
|
||||||
context: { queryHistory: { enabled: false } },
|
context: { queryHistory: { enabled: false } },
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@ function warehouseConnectionSchema<const Driver extends WarehouseDriver>(driver:
|
||||||
),
|
),
|
||||||
})
|
})
|
||||||
.describe(
|
.describe(
|
||||||
`${driver} warehouse connection. Additional driver-tunable fields (e.g. historicSql, context.queryHistory) are accepted and passed through.`,
|
`${driver} warehouse connection. Additional driver-tunable fields (e.g. context.queryHistory) are accepted and passed through.`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -356,7 +356,7 @@ describe('runKtxDoctor', () => {
|
||||||
expect(out).toContain('KTX status');
|
expect(out).toContain('KTX status');
|
||||||
expect(out).toContain('Config');
|
expect(out).toContain('Config');
|
||||||
expect(out).toContain('Unsupported storrage: unknown field');
|
expect(out).toContain('Unsupported storrage: unknown field');
|
||||||
expect(out).toContain('Unsupported ingest.llm: use top-level llm.provider');
|
expect(out).toContain('Unsupported ingest.llm: unknown field');
|
||||||
expect(out).toContain('ktx.yaml');
|
expect(out).toContain('ktx.yaml');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -609,11 +609,6 @@ describe('runKtxDoctor', () => {
|
||||||
' driver: postgres',
|
' driver: postgres',
|
||||||
' url: env:WAREHOUSE_DATABASE_URL',
|
' url: env:WAREHOUSE_DATABASE_URL',
|
||||||
' readonly: true',
|
' readonly: true',
|
||||||
' historicSql:',
|
|
||||||
' enabled: true',
|
|
||||||
' dialect: postgres',
|
|
||||||
' windowDays: 30',
|
|
||||||
' concurrency: 4',
|
|
||||||
' local:',
|
' local:',
|
||||||
' driver: sqlite',
|
' driver: sqlite',
|
||||||
' file_path: ./warehouse.db',
|
' file_path: ./warehouse.db',
|
||||||
|
|
@ -651,8 +646,6 @@ describe('runKtxDoctor', () => {
|
||||||
const out = testIo.stdout();
|
const out = testIo.stdout();
|
||||||
expect(out).toContain('Warnings');
|
expect(out).toContain('Warnings');
|
||||||
expect(out).toContain('connections.warehouse.readonly is no longer used.');
|
expect(out).toContain('connections.warehouse.readonly is no longer used.');
|
||||||
expect(out).toContain('connections.warehouse.historicSql.concurrency is no longer used.');
|
|
||||||
expect(out).toContain('connections.warehouse.historicSql.windowDays does not constrain pg_stat_statements.');
|
|
||||||
expect(out).toContain('connections.local.file_path was removed.');
|
expect(out).toContain('connections.local.file_path was removed.');
|
||||||
expect(out).toContain('connections.docs.last_successful_cursor is local sync state.');
|
expect(out).toContain('connections.docs.last_successful_cursor is local sync state.');
|
||||||
delete process.env.ANTHROPIC_API_KEY;
|
delete process.env.ANTHROPIC_API_KEY;
|
||||||
|
|
@ -786,7 +779,7 @@ describe('runKtxDoctor', () => {
|
||||||
|
|
||||||
const out = testIo.stdout();
|
const out = testIo.stdout();
|
||||||
expect(out).toContain('Unsupported storrage: unknown field');
|
expect(out).toContain('Unsupported storrage: unknown field');
|
||||||
expect(out).toContain('Unsupported ingest.llm: use top-level llm.provider');
|
expect(out).toContain('Unsupported ingest.llm: unknown field');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('emits structured JSON issues when validation fails', async () => {
|
it('emits structured JSON issues when validation fails', async () => {
|
||||||
|
|
|
||||||
|
|
@ -1471,10 +1471,10 @@ describe('runKtxIngest', () => {
|
||||||
' warehouse:',
|
' warehouse:',
|
||||||
' driver: postgres',
|
' driver: postgres',
|
||||||
' url: env:WAREHOUSE_DATABASE_URL',
|
' url: env:WAREHOUSE_DATABASE_URL',
|
||||||
' historicSql:',
|
' context:',
|
||||||
' enabled: true',
|
' queryHistory:',
|
||||||
' dialect: postgres',
|
' enabled: true',
|
||||||
' minExecutions: 2',
|
' minExecutions: 2',
|
||||||
'ingest:',
|
'ingest:',
|
||||||
' adapters:',
|
' adapters:',
|
||||||
' - historic-sql',
|
' - historic-sql',
|
||||||
|
|
@ -1535,10 +1535,10 @@ describe('runKtxIngest', () => {
|
||||||
' warehouse:',
|
' warehouse:',
|
||||||
' driver: postgres',
|
' driver: postgres',
|
||||||
' url: env:WAREHOUSE_DATABASE_URL',
|
' url: env:WAREHOUSE_DATABASE_URL',
|
||||||
' historicSql:',
|
' context:',
|
||||||
' enabled: true',
|
' queryHistory:',
|
||||||
' dialect: postgres',
|
' enabled: true',
|
||||||
' minExecutions: 2',
|
' minExecutions: 2',
|
||||||
'ingest:',
|
'ingest:',
|
||||||
' adapters:',
|
' adapters:',
|
||||||
' - historic-sql',
|
' - historic-sql',
|
||||||
|
|
@ -1663,10 +1663,10 @@ describe('runKtxIngest', () => {
|
||||||
' warehouse:',
|
' warehouse:',
|
||||||
' driver: postgres',
|
' driver: postgres',
|
||||||
' url: env:WAREHOUSE_DATABASE_URL',
|
' url: env:WAREHOUSE_DATABASE_URL',
|
||||||
' historicSql:',
|
' context:',
|
||||||
' enabled: true',
|
' queryHistory:',
|
||||||
' dialect: postgres',
|
' enabled: true',
|
||||||
' minExecutions: 2',
|
' minExecutions: 2',
|
||||||
'ingest:',
|
'ingest:',
|
||||||
' adapters:',
|
' adapters:',
|
||||||
' - historic-sql',
|
' - historic-sql',
|
||||||
|
|
@ -1755,10 +1755,10 @@ describe('runKtxIngest', () => {
|
||||||
' warehouse:',
|
' warehouse:',
|
||||||
' driver: postgres',
|
' driver: postgres',
|
||||||
' url: env:WAREHOUSE_DATABASE_URL',
|
' url: env:WAREHOUSE_DATABASE_URL',
|
||||||
' historicSql:',
|
' context:',
|
||||||
' enabled: true',
|
' queryHistory:',
|
||||||
' dialect: postgres',
|
' enabled: true',
|
||||||
' minExecutions: 2',
|
' minExecutions: 2',
|
||||||
'ingest:',
|
'ingest:',
|
||||||
' adapters:',
|
' adapters:',
|
||||||
' - historic-sql',
|
' - historic-sql',
|
||||||
|
|
|
||||||
|
|
@ -39,36 +39,6 @@ describe('CLI local ingest adapters', () => {
|
||||||
await rm(tempDir, { recursive: true, force: true });
|
await rm(tempDir, { recursive: true, force: true });
|
||||||
});
|
});
|
||||||
|
|
||||||
it('registers Postgres historic SQL from the requested connection', async () => {
|
|
||||||
await writeProject(
|
|
||||||
tempDir,
|
|
||||||
[
|
|
||||||
'connections:',
|
|
||||||
' warehouse:',
|
|
||||||
' driver: postgres',
|
|
||||||
' url: env:WAREHOUSE_DATABASE_URL',
|
|
||||||
' historicSql:',
|
|
||||||
' enabled: true',
|
|
||||||
' dialect: postgres',
|
|
||||||
'ingest:',
|
|
||||||
' adapters:',
|
|
||||||
' - historic-sql',
|
|
||||||
'',
|
|
||||||
].join('\n'),
|
|
||||||
);
|
|
||||||
const project = await loadKtxProject({ projectDir: tempDir });
|
|
||||||
|
|
||||||
const adapters = createKtxCliLocalIngestAdapters(project, {
|
|
||||||
historicSqlConnectionId: 'warehouse',
|
|
||||||
sqlAnalysis: sqlAnalysisStub(),
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(adapters.find((adapter) => adapter.source === 'historic-sql')?.skillNames).toEqual([
|
|
||||||
'historic_sql_table_digest',
|
|
||||||
'historic_sql_patterns',
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('registers Postgres historic SQL from connection context query history', async () => {
|
it('registers Postgres historic SQL from connection context query history', async () => {
|
||||||
await writeProject(
|
await writeProject(
|
||||||
tempDir,
|
tempDir,
|
||||||
|
|
@ -110,9 +80,9 @@ describe('CLI local ingest adapters', () => {
|
||||||
' dataset_id: analytics',
|
' dataset_id: analytics',
|
||||||
' location: us',
|
' location: us',
|
||||||
' credentials_json: \'{"project_id":"demo-project"}\'',
|
' credentials_json: \'{"project_id":"demo-project"}\'',
|
||||||
' historicSql:',
|
' context:',
|
||||||
' enabled: true',
|
' queryHistory:',
|
||||||
' dialect: bigquery',
|
' enabled: true',
|
||||||
'ingest:',
|
'ingest:',
|
||||||
' adapters:',
|
' adapters:',
|
||||||
' - historic-sql',
|
' - historic-sql',
|
||||||
|
|
@ -145,9 +115,9 @@ describe('CLI local ingest adapters', () => {
|
||||||
' schema_name: PUBLIC',
|
' schema_name: PUBLIC',
|
||||||
' username: reader',
|
' username: reader',
|
||||||
' password: env:SNOWFLAKE_PASSWORD',
|
' password: env:SNOWFLAKE_PASSWORD',
|
||||||
' historicSql:',
|
' context:',
|
||||||
' enabled: true',
|
' queryHistory:',
|
||||||
' dialect: snowflake',
|
' enabled: true',
|
||||||
'ingest:',
|
'ingest:',
|
||||||
' adapters:',
|
' adapters:',
|
||||||
' - historic-sql',
|
' - historic-sql',
|
||||||
|
|
@ -179,9 +149,9 @@ describe('CLI local ingest adapters', () => {
|
||||||
' dataset_id: analytics',
|
' dataset_id: analytics',
|
||||||
' location: us',
|
' location: us',
|
||||||
` credentials_json: 'file:${credentialsPath}'`,
|
` credentials_json: 'file:${credentialsPath}'`,
|
||||||
' historicSql:',
|
' context:',
|
||||||
' enabled: true',
|
' queryHistory:',
|
||||||
' dialect: bigquery',
|
' enabled: true',
|
||||||
'ingest:',
|
'ingest:',
|
||||||
' adapters:',
|
' adapters:',
|
||||||
' - historic-sql',
|
' - historic-sql',
|
||||||
|
|
|
||||||
|
|
@ -2125,7 +2125,6 @@ describe('setup databases step', () => {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
expect(config.connections.snowflake.historicSql).toBeUndefined();
|
|
||||||
expect(configText).not.toContain('live-database');
|
expect(configText).not.toContain('live-database');
|
||||||
expect(configText).not.toContain('historic-sql');
|
expect(configText).not.toContain('historic-sql');
|
||||||
expect(configText).not.toMatch(/^\s+adapters:/m);
|
expect(configText).not.toMatch(/^\s+adapters:/m);
|
||||||
|
|
@ -2223,7 +2222,6 @@ describe('setup databases step', () => {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
expect(config.connections.warehouse.historicSql).toBeUndefined();
|
|
||||||
const warehouseContext =
|
const warehouseContext =
|
||||||
config.connections.warehouse.context &&
|
config.connections.warehouse.context &&
|
||||||
typeof config.connections.warehouse.context === 'object' &&
|
typeof config.connections.warehouse.context === 'object' &&
|
||||||
|
|
@ -2368,7 +2366,6 @@ describe('setup databases step', () => {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
expect(config.connections.analytics.historicSql).toBeUndefined();
|
|
||||||
expect(configText).not.toContain('live-database');
|
expect(configText).not.toContain('live-database');
|
||||||
expect(configText).not.toContain('historic-sql');
|
expect(configText).not.toContain('historic-sql');
|
||||||
expect(configText).not.toMatch(/^\s+adapters:/m);
|
expect(configText).not.toMatch(/^\s+adapters:/m);
|
||||||
|
|
@ -2420,80 +2417,6 @@ describe('setup databases step', () => {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
expect(config.connections.warehouse.historicSql).toBeUndefined();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('migrates legacy historicSql to context.queryHistory during database setup', async () => {
|
|
||||||
await writeFile(
|
|
||||||
join(tempDir, 'ktx.yaml'),
|
|
||||||
[
|
|
||||||
'connections:',
|
|
||||||
' warehouse:',
|
|
||||||
' driver: postgres',
|
|
||||||
' readonly: true',
|
|
||||||
' historicSql:',
|
|
||||||
' enabled: true',
|
|
||||||
' dialect: postgres',
|
|
||||||
' windowDays: 45',
|
|
||||||
' minExecutions: 9',
|
|
||||||
' concurrency: 3',
|
|
||||||
' staleArchiveAfterDays: 120',
|
|
||||||
' filters:',
|
|
||||||
' dropTrivialProbes: true',
|
|
||||||
' serviceAccounts:',
|
|
||||||
' mode: exclude',
|
|
||||||
' patterns:',
|
|
||||||
" - '^svc_'",
|
|
||||||
' orchestrators:',
|
|
||||||
' mode: exclude',
|
|
||||||
' patterns:',
|
|
||||||
' - airflow',
|
|
||||||
' dropFailedBelow: 2',
|
|
||||||
' redactionPatterns:',
|
|
||||||
" - '(?i)secret'",
|
|
||||||
'',
|
|
||||||
].join('\n'),
|
|
||||||
'utf-8',
|
|
||||||
);
|
|
||||||
|
|
||||||
const io = makeIo();
|
|
||||||
|
|
||||||
await expect(
|
|
||||||
runKtxSetupDatabasesStep(
|
|
||||||
{
|
|
||||||
projectDir: tempDir,
|
|
||||||
inputMode: 'disabled',
|
|
||||||
databaseConnectionIds: ['warehouse'],
|
|
||||||
databaseSchemas: [],
|
|
||||||
skipDatabases: false,
|
|
||||||
},
|
|
||||||
io.io,
|
|
||||||
{
|
|
||||||
testConnection: vi.fn(async () => 0),
|
|
||||||
scanConnection: vi.fn(async () => 0),
|
|
||||||
historicSqlProbe: vi.fn(async () => ({ ok: true, lines: [] })),
|
|
||||||
},
|
|
||||||
),
|
|
||||||
).resolves.toMatchObject({ status: 'ready' });
|
|
||||||
|
|
||||||
const config = parseKtxProjectConfig(await readFile(join(tempDir, 'ktx.yaml'), 'utf-8'));
|
|
||||||
expect(config.connections.warehouse.historicSql).toBeUndefined();
|
|
||||||
expect(config.connections.warehouse.context).toMatchObject({
|
|
||||||
queryHistory: {
|
|
||||||
enabled: true,
|
|
||||||
windowDays: 45,
|
|
||||||
minExecutions: 9,
|
|
||||||
concurrency: 3,
|
|
||||||
staleArchiveAfterDays: 120,
|
|
||||||
filters: {
|
|
||||||
dropTrivialProbes: true,
|
|
||||||
serviceAccounts: { mode: 'exclude', patterns: ['^svc_'] },
|
|
||||||
orchestrators: { mode: 'exclude', patterns: ['airflow'] },
|
|
||||||
dropFailedBelow: 2,
|
|
||||||
},
|
|
||||||
redactionPatterns: ['(?i)secret'],
|
|
||||||
},
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('prints a non-blocking Postgres query history probe failure after connection test succeeds', async () => {
|
it('prints a non-blocking Postgres query history probe failure after connection test succeeds', async () => {
|
||||||
|
|
|
||||||
|
|
@ -285,13 +285,6 @@ function numberConfigField(connection: KtxProjectConnectionConfig | undefined, f
|
||||||
return typeof value === 'number' && Number.isFinite(value) ? value : undefined;
|
return typeof value === 'number' && Number.isFinite(value) ? value : undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
function historicSqlConfigRecord(connection: KtxProjectConnectionConfig | undefined): Record<string, unknown> | null {
|
|
||||||
const historicSql = connection?.historicSql;
|
|
||||||
return historicSql && typeof historicSql === 'object' && !Array.isArray(historicSql)
|
|
||||||
? (historicSql as Record<string, unknown>)
|
|
||||||
: null;
|
|
||||||
}
|
|
||||||
|
|
||||||
function contextRecord(connection: KtxProjectConnectionConfig | undefined): Record<string, unknown> {
|
function contextRecord(connection: KtxProjectConnectionConfig | undefined): Record<string, unknown> {
|
||||||
const context = connection?.context;
|
const context = connection?.context;
|
||||||
return context && typeof context === 'object' && !Array.isArray(context) ? (context as Record<string, unknown>) : {};
|
return context && typeof context === 'object' && !Array.isArray(context) ? (context as Record<string, unknown>) : {};
|
||||||
|
|
@ -304,19 +297,12 @@ function queryHistoryConfigRecord(connection: KtxProjectConnectionConfig | undef
|
||||||
: null;
|
: null;
|
||||||
}
|
}
|
||||||
|
|
||||||
function stripLegacyHistoricSql(connection: KtxProjectConnectionConfig): KtxProjectConnectionConfig {
|
|
||||||
const { historicSql: _historicSql, ...rest } = connection as KtxProjectConnectionConfig & {
|
|
||||||
historicSql?: unknown;
|
|
||||||
};
|
|
||||||
return rest;
|
|
||||||
}
|
|
||||||
|
|
||||||
function withQueryHistoryConfig(
|
function withQueryHistoryConfig(
|
||||||
connection: KtxProjectConnectionConfig,
|
connection: KtxProjectConnectionConfig,
|
||||||
queryHistory: Record<string, unknown>,
|
queryHistory: Record<string, unknown>,
|
||||||
): KtxProjectConnectionConfig {
|
): KtxProjectConnectionConfig {
|
||||||
return {
|
return {
|
||||||
...stripLegacyHistoricSql(connection),
|
...connection,
|
||||||
context: {
|
context: {
|
||||||
...contextRecord(connection),
|
...contextRecord(connection),
|
||||||
queryHistory,
|
queryHistory,
|
||||||
|
|
@ -324,16 +310,6 @@ function withQueryHistoryConfig(
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function migrateLegacyHistoricSqlConnection(connection: KtxProjectConnectionConfig): KtxProjectConnectionConfig {
|
|
||||||
const existingQueryHistory = queryHistoryConfigRecord(connection);
|
|
||||||
const legacy = historicSqlConfigRecord(connection);
|
|
||||||
if (existingQueryHistory || !legacy) {
|
|
||||||
return existingQueryHistory ? stripLegacyHistoricSql(connection) : connection;
|
|
||||||
}
|
|
||||||
const { dialect: _dialect, ...queryHistory } = legacy;
|
|
||||||
return withQueryHistoryConfig(connection, queryHistory);
|
|
||||||
}
|
|
||||||
|
|
||||||
function historicSqlProbeFailureLines(error: unknown): string[] {
|
function historicSqlProbeFailureLines(error: unknown): string[] {
|
||||||
if (error instanceof Error && error.name === 'HistoricSqlExtensionMissingError') {
|
if (error instanceof Error && error.name === 'HistoricSqlExtensionMissingError') {
|
||||||
return [
|
return [
|
||||||
|
|
@ -1133,8 +1109,7 @@ async function maybeApplyHistoricSqlConfig(input: {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const existingRecord = queryHistoryConfigRecord(input.connection) ?? historicSqlConfigRecord(input.connection) ?? {};
|
const { dialect: _dialect, ...existing } = queryHistoryConfigRecord(input.connection) ?? {};
|
||||||
const { dialect: _dialect, ...existing } = existingRecord;
|
|
||||||
|
|
||||||
if (!enabled) {
|
if (!enabled) {
|
||||||
return withQueryHistoryConfig(input.connection, { ...existing, enabled: false });
|
return withQueryHistoryConfig(input.connection, { ...existing, enabled: false });
|
||||||
|
|
@ -1386,18 +1361,11 @@ async function writeConnectionConfig(input: {
|
||||||
io?: KtxCliIo;
|
io?: KtxCliIo;
|
||||||
}): Promise<void> {
|
}): Promise<void> {
|
||||||
const project = await loadKtxProject({ projectDir: input.projectDir });
|
const project = await loadKtxProject({ projectDir: input.projectDir });
|
||||||
const migratedConnections = Object.fromEntries(
|
|
||||||
Object.entries(project.config.connections).map(([connectionId, connection]) => [
|
|
||||||
connectionId,
|
|
||||||
migrateLegacyHistoricSqlConnection(connection),
|
|
||||||
]),
|
|
||||||
);
|
|
||||||
const nextConnection = migrateLegacyHistoricSqlConnection(input.connection);
|
|
||||||
const config = {
|
const config = {
|
||||||
...project.config,
|
...project.config,
|
||||||
connections: {
|
connections: {
|
||||||
...migratedConnections,
|
...project.config.connections,
|
||||||
[input.connectionId]: nextConnection,
|
[input.connectionId]: input.connection,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
await writeFile(project.configPath, serializeKtxProjectConfig(config), 'utf-8');
|
await writeFile(project.configPath, serializeKtxProjectConfig(config), 'utf-8');
|
||||||
|
|
@ -1407,13 +1375,13 @@ async function writeConnectionConfig(input: {
|
||||||
projectDir: input.projectDir,
|
projectDir: input.projectDir,
|
||||||
io: input.io,
|
io: input.io,
|
||||||
fields: {
|
fields: {
|
||||||
driver: String(nextConnection.driver ?? 'unknown').toLowerCase(),
|
driver: String(input.connection.driver ?? 'unknown').toLowerCase(),
|
||||||
isDemoConnection: isDemoConnection(input.connectionId, nextConnection),
|
isDemoConnection: isDemoConnection(input.connectionId, input.connection),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const queryHistory = queryHistoryConfigRecord(nextConnection);
|
const queryHistory = queryHistoryConfigRecord(input.connection);
|
||||||
if (queryHistory?.enabled === true) {
|
if (queryHistory?.enabled === true) {
|
||||||
await ensureHistoricSqlIngestDefaults(input.projectDir);
|
await ensureHistoricSqlIngestDefaults(input.projectDir);
|
||||||
}
|
}
|
||||||
|
|
@ -1745,18 +1713,7 @@ async function ensureHistoricSqlIngestDefaults(projectDir: string): Promise<void
|
||||||
|
|
||||||
async function markDatabasesComplete(projectDir: string, connectionIds: string[]): Promise<void> {
|
async function markDatabasesComplete(projectDir: string, connectionIds: string[]): Promise<void> {
|
||||||
const project = await loadKtxProject({ projectDir });
|
const project = await loadKtxProject({ projectDir });
|
||||||
const config = setKtxSetupDatabaseConnectionIds(
|
const config = setKtxSetupDatabaseConnectionIds(project.config, unique(connectionIds));
|
||||||
{
|
|
||||||
...project.config,
|
|
||||||
connections: Object.fromEntries(
|
|
||||||
Object.entries(project.config.connections).map(([connectionId, connection]) => [
|
|
||||||
connectionId,
|
|
||||||
migrateLegacyHistoricSqlConnection(connection),
|
|
||||||
]),
|
|
||||||
),
|
|
||||||
},
|
|
||||||
unique(connectionIds),
|
|
||||||
);
|
|
||||||
await writeFile(project.configPath, serializeKtxProjectConfig(config), 'utf-8');
|
await writeFile(project.configPath, serializeKtxProjectConfig(config), 'utf-8');
|
||||||
await markKtxSetupStateStepComplete(projectDir, 'databases');
|
await markKtxSetupStateStepComplete(projectDir, 'databases');
|
||||||
}
|
}
|
||||||
|
|
@ -1769,7 +1726,7 @@ async function maybeRunHistoricSqlSetupProbe(input: {
|
||||||
}): Promise<void> {
|
}): Promise<void> {
|
||||||
const project = await loadKtxProject({ projectDir: input.projectDir });
|
const project = await loadKtxProject({ projectDir: input.projectDir });
|
||||||
const connection = project.config.connections[input.connectionId];
|
const connection = project.config.connections[input.connectionId];
|
||||||
const queryHistory = queryHistoryConfigRecord(connection) ?? historicSqlConfigRecord(connection);
|
const queryHistory = queryHistoryConfigRecord(connection);
|
||||||
const driver = normalizeDriver(connection?.driver);
|
const driver = normalizeDriver(connection?.driver);
|
||||||
if (queryHistory?.enabled !== true) {
|
if (queryHistory?.enabled !== true) {
|
||||||
return;
|
return;
|
||||||
|
|
@ -2133,12 +2090,7 @@ async function runPrimarySourceFullEdit(input: {
|
||||||
connectionId: input.connectionId,
|
connectionId: input.connectionId,
|
||||||
connection: withExistingPrimaryEditPromptDefaults({
|
connection: withExistingPrimaryEditPromptDefaults({
|
||||||
previous: existing,
|
previous: existing,
|
||||||
next: {
|
next: withHistoricSql,
|
||||||
...withHistoricSql,
|
|
||||||
...(!Object.hasOwn(withHistoricSql, 'historicSql') && existing.historicSql !== undefined
|
|
||||||
? { historicSql: existing.historicSql }
|
|
||||||
: {}),
|
|
||||||
},
|
|
||||||
driver,
|
driver,
|
||||||
}),
|
}),
|
||||||
io: input.io,
|
io: input.io,
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,6 @@
|
||||||
import { mkdir, mkdtemp, readFile, rm, stat, writeFile } from 'node:fs/promises';
|
import { mkdir, mkdtemp, readFile, rm, stat, writeFile } from 'node:fs/promises';
|
||||||
import { tmpdir } from 'node:os';
|
import { tmpdir } from 'node:os';
|
||||||
import { join } from 'node:path';
|
import { join } from 'node:path';
|
||||||
import { initKtxProject } from './context/project/project.js';
|
|
||||||
import { parseKtxProjectConfig } from './context/project/config.js';
|
|
||||||
import { readKtxSetupState } from './context/project/setup-config.js';
|
import { readKtxSetupState } from './context/project/setup-config.js';
|
||||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
||||||
import { gray } from './io/symbols.js';
|
import { gray } from './io/symbols.js';
|
||||||
|
|
@ -76,36 +74,6 @@ describe('setup project step', () => {
|
||||||
expect(testIo.stderr()).toBe('');
|
expect(testIo.stderr()).toBe('');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('loads an existing project in auto mode and drops config setup progress', async () => {
|
|
||||||
const projectDir = join(tempDir, 'warehouse');
|
|
||||||
await initKtxProject({ projectDir });
|
|
||||||
await writeFile(
|
|
||||||
join(projectDir, 'ktx.yaml'),
|
|
||||||
[
|
|
||||||
'setup:',
|
|
||||||
' database_connection_ids:',
|
|
||||||
' - warehouse',
|
|
||||||
' completed_steps:',
|
|
||||||
' - llm',
|
|
||||||
'connections: {}',
|
|
||||||
].join('\n'),
|
|
||||||
'utf-8',
|
|
||||||
);
|
|
||||||
|
|
||||||
const result = await runKtxSetupProjectStep(
|
|
||||||
{ projectDir, mode: 'auto', inputMode: 'disabled', yes: false },
|
|
||||||
makeIo().io,
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(result.status).toBe('ready');
|
|
||||||
const config = parseKtxProjectConfig(await readFile(join(projectDir, 'ktx.yaml'), 'utf-8'));
|
|
||||||
expect(config.setup).toEqual({
|
|
||||||
database_connection_ids: ['warehouse'],
|
|
||||||
});
|
|
||||||
expect(await readFile(join(projectDir, 'ktx.yaml'), 'utf-8')).not.toContain('completed_steps:');
|
|
||||||
expect(await readKtxSetupState(projectDir)).toEqual({ completed_steps: ['project'] });
|
|
||||||
});
|
|
||||||
|
|
||||||
it('creates a missing auto-mode project only when --yes is present in no-input mode', async () => {
|
it('creates a missing auto-mode project only when --yes is present in no-input mode', async () => {
|
||||||
const projectDir = join(tempDir, 'warehouse');
|
const projectDir = join(tempDir, 'warehouse');
|
||||||
const rejectedIo = makeIo();
|
const rejectedIo = makeIo();
|
||||||
|
|
|
||||||
|
|
@ -92,10 +92,6 @@ type ClaudeCodeAuthProbe = (input: {
|
||||||
|
|
||||||
const PROJECT_READY_COMMANDS = KTX_NEXT_STEP_DIRECT_COMMANDS.map((step) => step.command);
|
const PROJECT_READY_COMMANDS = KTX_NEXT_STEP_DIRECT_COMMANDS.map((step) => step.command);
|
||||||
|
|
||||||
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
||||||
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
function hasOwnField(value: Record<string, unknown>, key: string): boolean {
|
function hasOwnField(value: Record<string, unknown>, key: string): boolean {
|
||||||
return Object.prototype.hasOwnProperty.call(value, key);
|
return Object.prototype.hasOwnProperty.call(value, key);
|
||||||
}
|
}
|
||||||
|
|
@ -766,27 +762,6 @@ function buildWarnings(
|
||||||
fix: 'Remove it from ktx.yaml. KTX stores the Notion cursor in .ktx/db.sqlite.',
|
fix: 'Remove it from ktx.yaml. KTX stores the Notion cursor in .ktx/db.sqlite.',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const historicSql = isRecord(connection.historicSql) ? connection.historicSql : null;
|
|
||||||
if (!historicSql) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (hasOwnField(historicSql, 'concurrency')) {
|
|
||||||
warnings.push({
|
|
||||||
message: `connections.${connectionId}.historicSql.concurrency is no longer used.`,
|
|
||||||
fix: `Remove connections.${connectionId}.historicSql.concurrency from ktx.yaml.`,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
const historicDialect = String(historicSql.dialect ?? driver).toLowerCase();
|
|
||||||
if (
|
|
||||||
(historicDialect === 'postgres' || historicDialect === 'postgresql') &&
|
|
||||||
hasOwnField(historicSql, 'windowDays')
|
|
||||||
) {
|
|
||||||
warnings.push({
|
|
||||||
message: `connections.${connectionId}.historicSql.windowDays does not constrain pg_stat_statements.`,
|
|
||||||
fix: `Remove connections.${connectionId}.historicSql.windowDays from ktx.yaml.`,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const adapter of config.ingest.adapters) {
|
for (const adapter of config.ingest.adapters) {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue