diff --git a/packages/client-common/__tests__/integration/auth.test.ts b/packages/client-common/__tests__/integration/auth.test.ts index 3a734cc3..9e014617 100644 --- a/packages/client-common/__tests__/integration/auth.test.ts +++ b/packages/client-common/__tests__/integration/auth.test.ts @@ -1,18 +1,21 @@ import { type ClickHouseClient } from '@clickhouse/client-common' -import { createTestClient } from '../utils' +import { createSimpleTable } from '@test/fixtures/simple_table' +import { getAuthFromEnv } from '@test/utils/env' +import { createTestClient, guid } from '../utils' describe('authentication', () => { let client: ClickHouseClient - afterEach(async () => { - await client.close() - }) - - it('provides authentication error details', async () => { + beforeEach(() => { client = createTestClient({ username: 'gibberish', password: 'gibberish', }) + }) + afterEach(async () => { + await client.close() + }) + it('provides authentication error details', async () => { await expectAsync( client.query({ query: 'SELECT number FROM system.numbers LIMIT 3', @@ -25,4 +28,70 @@ describe('authentication', () => { }), ) }) + + describe('auth override', () => { + let defaultClient: ClickHouseClient + beforeAll(() => { + defaultClient = createTestClient() + }) + afterAll(async () => { + await defaultClient.close() + }) + + let tableName: string + const values = [ + { + id: '1', + name: 'foo', + sku: [3, 4], + }, + ] + const auth = getAuthFromEnv() + + it('should with with insert and select', async () => { + tableName = `simple_table_${guid()}` + await createSimpleTable(defaultClient, tableName) + await client.insert({ + table: tableName, + format: 'JSONEachRow', + values, + auth, + }) + const rs = await client.query({ + query: `SELECT * FROM ${tableName} ORDER BY id ASC`, + format: 'JSONEachRow', + auth, + }) + expect(await rs.json()).toEqual(values) + }) + + it('should work with command and select', async () => { + tableName = `simple_table_${guid()}` + await createSimpleTable(defaultClient, tableName) + await client.command({ + query: `INSERT INTO ${tableName} VALUES (1, 'foo', [3, 4])`, + auth, + }) + const rs = await client.query({ + query: `SELECT * FROM ${tableName} ORDER BY id ASC`, + format: 'JSONEachRow', + auth, + }) + expect(await rs.json()).toEqual(values) + }) + + it('should work with exec', async () => { + const { stream } = await client.exec({ + query: 'SELECT 42, 144 FORMAT CSV', + auth, + }) + let result = '' + const textDecoder = new TextDecoder() + // @ts-expect-error - ReadableStream (Web) or Stream.Readable (Node.js); same API. + for await (const chunk of stream) { + result += textDecoder.decode(chunk, { stream: true }) + } + expect(result).toEqual('42,144\n') + }) + }) }) diff --git a/packages/client-common/__tests__/utils/client.ts b/packages/client-common/__tests__/utils/client.ts index f3e906d7..a29bd5dd 100644 --- a/packages/client-common/__tests__/utils/client.ts +++ b/packages/client-common/__tests__/utils/client.ts @@ -4,7 +4,7 @@ import type { ClickHouseClient, ClickHouseSettings, } from '@clickhouse/client-common' -import { getFromEnv } from './env' +import { EnvKeys, getFromEnv } from './env' import { guid } from './guid' import { getClickHouseTestEnvironment, @@ -55,8 +55,8 @@ export function createTestClient( } if (isCloudTestEnv()) { const cloudConfig: BaseClickHouseClientConfigOptions = { - url: `https://${getFromEnv('CLICKHOUSE_CLOUD_HOST')}:8443`, - password: getFromEnv('CLICKHOUSE_CLOUD_PASSWORD'), + url: `https://${getFromEnv(EnvKeys.host)}:8443`, + password: getFromEnv(EnvKeys.password), database: databaseName, ...logging, ...config, diff --git a/packages/client-common/__tests__/utils/env.ts b/packages/client-common/__tests__/utils/env.ts index 981897bc..c1d7955c 100644 --- a/packages/client-common/__tests__/utils/env.ts +++ b/packages/client-common/__tests__/utils/env.ts @@ -1,3 +1,9 @@ +export const EnvKeys = { + host: 'CLICKHOUSE_CLOUD_HOST', + username: 'CLICKHOUSE_CLOUD_USERNAME', + password: 'CLICKHOUSE_CLOUD_PASSWORD', +} + export function getFromEnv(key: string): string { const value = process.env[key] if (value === undefined) { @@ -5,3 +11,9 @@ export function getFromEnv(key: string): string { } return value } + +export function getAuthFromEnv() { + const username = process.env[EnvKeys.username] + const password = process.env[EnvKeys.password] + return { username: username ?? 'default', password: password ?? '' } +} diff --git a/packages/client-web/src/connection/web_connection.ts b/packages/client-web/src/connection/web_connection.ts index 9a9214d1..d91085d3 100644 --- a/packages/client-web/src/connection/web_connection.ts +++ b/packages/client-web/src/connection/web_connection.ts @@ -168,7 +168,13 @@ export class WebConnection implements Connection { try { const headers = withCompressionHeaders({ - headers: this.defaultHeaders, + headers: + params?.auth !== undefined + ? { + ...this.defaultHeaders, + Authorization: `Basic ${btoa(`${params.auth.username}:${params.auth.password}`)}`, + } + : this.defaultHeaders, compress_request: false, decompress_response: this.params.compression.decompress_response, })