fix(cli): resolve Looker client_secret_ref via the canonical config resolver

The local Looker adapter understood only env: references, so a client_secret_ref
written by `ktx setup` as a file: secret silently failed to resolve. Route it
through resolveKtxConfigReference so file: and env: refs both work, matching the
other connector paths.
This commit is contained in:
Andrey Avtomonov 2026-06-18 13:35:09 +02:00
parent 7692d92ef0
commit c439feb50f
2 changed files with 75 additions and 8 deletions

View file

@ -1,3 +1,4 @@
import { resolveKtxConfigReference } from '../../../core/config-reference.js';
import type { KtxLocalProject } from '../../../../context/project/project.js';
import type { KtxProjectConnectionConfig } from '../../../../context/project/config.js';
import {
@ -8,13 +9,6 @@ function stringField(value: unknown): string | null {
return typeof value === 'string' && value.trim().length > 0 ? value.trim() : null;
}
function resolveEnvReference(ref: string, env: NodeJS.ProcessEnv): string | null {
if (!ref.startsWith('env:')) {
return null;
}
return stringField(env[ref.slice('env:'.length)]);
}
export function lookerCredentialsFromLocalConnection(
connectionId: string,
connection: KtxProjectConnectionConfig | undefined,
@ -27,7 +21,8 @@ export function lookerCredentialsFromLocalConnection(
const clientId = stringField(connection.client_id);
const clientSecret =
stringField(connection.client_secret) ??
(stringField(connection.client_secret_ref) ? resolveEnvReference(String(connection.client_secret_ref), env) : null);
resolveKtxConfigReference(stringField(connection.client_secret_ref) ?? undefined, env) ??
null;
if (!baseUrl) {
throw new Error(`Connection "${connectionId}" is missing Looker base_url`);

View file

@ -0,0 +1,72 @@
import { mkdtemp, rm, writeFile } from 'node:fs/promises';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
import { lookerCredentialsFromLocalConnection } from '../../../../../src/context/ingest/adapters/looker/local-looker.adapter.js';
import type { KtxProjectConnectionConfig } from '../../../../../src/context/project/config.js';
const connectionId = '11111111-1111-4111-8111-111111111111';
function lookerConnection(overrides: Partial<KtxProjectConnectionConfig>): KtxProjectConnectionConfig {
return {
driver: 'looker',
base_url: 'https://looker.example.com',
client_id: 'client-123',
...overrides,
} as KtxProjectConnectionConfig;
}
describe('lookerCredentialsFromLocalConnection', () => {
let secretsDir: string;
beforeEach(async () => {
secretsDir = await mkdtemp(join(tmpdir(), 'looker-secret-'));
});
afterEach(async () => {
await rm(secretsDir, { recursive: true, force: true });
});
it('resolves client_secret_ref written as a local secret file (ktx setup default)', async () => {
const secretPath = join(secretsDir, 'looker-main-client-secret');
await writeFile(secretPath, 'file-secret\n', 'utf-8');
const credentials = lookerCredentialsFromLocalConnection(
connectionId,
lookerConnection({ client_secret_ref: `file:${secretPath}` }), // pragma: allowlist secret
{},
);
expect(credentials.client_secret).toBe('file-secret');
});
it('resolves client_secret_ref from the environment', () => {
const credentials = lookerCredentialsFromLocalConnection(
connectionId,
lookerConnection({ client_secret_ref: 'env:LOOKER_CLIENT_SECRET' }), // pragma: allowlist secret
{ LOOKER_CLIENT_SECRET: 'env-secret' }, // pragma: allowlist secret
);
expect(credentials.client_secret).toBe('env-secret');
});
it('prefers a literal client_secret over the reference', () => {
const credentials = lookerCredentialsFromLocalConnection(
connectionId,
lookerConnection({ client_secret: 'literal-secret', client_secret_ref: 'env:UNSET' }), // pragma: allowlist secret
{},
);
expect(credentials.client_secret).toBe('literal-secret');
});
it('throws when neither client_secret nor a resolvable client_secret_ref is present', () => {
expect(() =>
lookerCredentialsFromLocalConnection(
connectionId,
lookerConnection({ client_secret_ref: 'env:UNSET' }), // pragma: allowlist secret
{},
),
).toThrow(/missing Looker client_secret/);
});
});