mirror of
https://github.com/Kaelio/ktx.git
synced 2026-07-25 12:01:03 +02:00
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:
parent
7692d92ef0
commit
c439feb50f
2 changed files with 75 additions and 8 deletions
|
|
@ -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`);
|
||||
|
|
|
|||
|
|
@ -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/);
|
||||
});
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue