mirror of
https://github.com/Kaelio/ktx.git
synced 2026-06-07 07:55:13 +02:00
feat: pass telemetry project id to semantic daemon
This commit is contained in:
parent
1c6c6c853f
commit
2cbafa3df6
7 changed files with 71 additions and 1 deletions
|
|
@ -106,7 +106,10 @@ describe('createPythonSemanticLayerComputePort', () => {
|
|||
columns: [{ name: 'orders.order_count' }],
|
||||
plan: { sources_used: ['orders'] },
|
||||
}));
|
||||
const port = createPythonSemanticLayerComputePort({ runJson });
|
||||
const port = createPythonSemanticLayerComputePort({
|
||||
runJson,
|
||||
projectId: 'hashed-project-id',
|
||||
});
|
||||
|
||||
await expect(
|
||||
port.query({
|
||||
|
|
@ -125,6 +128,7 @@ describe('createPythonSemanticLayerComputePort', () => {
|
|||
sources: [source],
|
||||
dialect: 'postgres',
|
||||
query: { measures: ['orders.order_count'], dimensions: [] },
|
||||
projectId: 'hashed-project-id',
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -90,6 +90,7 @@ export interface PythonSemanticLayerComputeOptions {
|
|||
cwd?: string;
|
||||
env?: NodeJS.ProcessEnv;
|
||||
runJson?: KtxDaemonJsonRunner;
|
||||
projectId?: string;
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
|
|
@ -238,6 +239,7 @@ export function createPythonSemanticLayerComputePort(
|
|||
const command = options.command ?? 'python';
|
||||
const args = options.args ?? ['-m', 'ktx_daemon'];
|
||||
const runJson = options.runJson ?? runProcessJson({ command, args, cwd: options.cwd, env: options.env });
|
||||
const projectId = options.projectId;
|
||||
|
||||
return {
|
||||
async query(input) {
|
||||
|
|
@ -245,6 +247,7 @@ export function createPythonSemanticLayerComputePort(
|
|||
sources: input.sources,
|
||||
dialect: input.dialect,
|
||||
query: input.query,
|
||||
...(projectId ? { projectId } : {}),
|
||||
});
|
||||
return {
|
||||
sql: typeof raw.sql === 'string' ? raw.sql : '',
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import {
|
|||
type ManagedPythonRuntimeLayoutOptions,
|
||||
type ManagedPythonRuntimeStatus,
|
||||
} from './managed-python-runtime.js';
|
||||
import { readExistingTelemetryProjectId } from './telemetry/identity.js';
|
||||
|
||||
export type KtxManagedPythonInstallPolicy = 'prompt' | 'auto' | 'never';
|
||||
|
||||
|
|
@ -49,6 +50,7 @@ export interface ManagedPythonCommandOptions extends ManagedPythonCommandDeps {
|
|||
|
||||
export interface ManagedPythonSemanticLayerComputeOptions extends ManagedPythonCommandOptions {
|
||||
createPythonCompute?: typeof createPythonSemanticLayerComputePort;
|
||||
projectDir?: string;
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
|
|
@ -133,8 +135,12 @@ export async function createManagedPythonSemanticLayerComputePort(
|
|||
...(options.spinner ? { spinner: options.spinner } : {}),
|
||||
});
|
||||
const createPythonCompute = options.createPythonCompute ?? createPythonSemanticLayerComputePort;
|
||||
const projectId = options.projectDir
|
||||
? await readExistingTelemetryProjectId({ projectDir: options.projectDir })
|
||||
: undefined;
|
||||
return createPythonCompute({
|
||||
command: runtime.manifest.python.daemonExecutable,
|
||||
args: [],
|
||||
...(projectId ? { projectId } : {}),
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -452,6 +452,7 @@ joins: []
|
|||
cliVersion: '0.2.0',
|
||||
installPolicy: 'auto',
|
||||
io: { stdout, stderr },
|
||||
projectDir,
|
||||
});
|
||||
expect(stdout.write).toHaveBeenCalledWith('select count(*) as order_count from public.orders\n');
|
||||
});
|
||||
|
|
|
|||
|
|
@ -70,6 +70,7 @@ interface KtxSlDeps {
|
|||
cliVersion: string;
|
||||
installPolicy: KtxManagedPythonInstallPolicy;
|
||||
io: KtxSlIo;
|
||||
projectDir?: string;
|
||||
}) => Promise<KtxSemanticLayerComputePort>;
|
||||
createQueryExecutor?: () => KtxSqlQueryExecutorPort;
|
||||
}
|
||||
|
|
@ -277,6 +278,7 @@ export async function runKtxSl(args: KtxSlArgs, io: KtxSlIo = process, deps: Ktx
|
|||
cliVersion: args.cliVersion,
|
||||
installPolicy: args.runtimeInstallPolicy,
|
||||
io,
|
||||
projectDir: args.projectDir,
|
||||
});
|
||||
const queryExecutor = args.execute ? (deps.createQueryExecutor ?? createDefaultLocalQueryExecutor)() : undefined;
|
||||
const result = await compileLocalSlQuery(project as KtxLocalProject, {
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import { afterEach, beforeEach, describe, expect, it } from 'vitest';
|
|||
import {
|
||||
computeTelemetryProjectId,
|
||||
loadTelemetryIdentity,
|
||||
readExistingTelemetryProjectId,
|
||||
TELEMETRY_NOTICE,
|
||||
type TelemetryIdentityEnv,
|
||||
} from './identity.js';
|
||||
|
|
@ -156,4 +157,39 @@ describe('telemetry identity', () => {
|
|||
expect(computeTelemetryProjectId('00000000-0000-4000-8000-000000000000', projectDir)).toBe(projectId);
|
||||
expect(computeTelemetryProjectId('11111111-1111-4111-8111-111111111111', projectDir)).not.toBe(projectId);
|
||||
});
|
||||
|
||||
it('reads an existing project id for Python telemetry without creating identity', async () => {
|
||||
await mkdir(join(homeDir, '.ktx'), { recursive: true });
|
||||
await writeFile(
|
||||
join(homeDir, '.ktx', 'telemetry.json'),
|
||||
JSON.stringify(
|
||||
{
|
||||
installId: '00000000-0000-4000-8000-000000000000',
|
||||
enabled: true,
|
||||
noticeShownAt: '2026-05-22T14:33:02.000Z',
|
||||
noticeShownVersion: 1,
|
||||
createdAt: '2026-05-22T14:33:02.000Z',
|
||||
},
|
||||
null,
|
||||
2,
|
||||
) + '\n',
|
||||
'utf-8',
|
||||
);
|
||||
|
||||
await expect(
|
||||
readExistingTelemetryProjectId({
|
||||
homeDir,
|
||||
projectDir: '/tmp/acme-private-project',
|
||||
env: {},
|
||||
}),
|
||||
).resolves.toMatch(/^[a-f0-9]{64}$/);
|
||||
|
||||
await expect(
|
||||
readExistingTelemetryProjectId({
|
||||
homeDir,
|
||||
projectDir: '/tmp/acme-private-project',
|
||||
env: { KTX_TELEMETRY_DISABLED: '1' },
|
||||
}),
|
||||
).resolves.toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -124,3 +124,21 @@ export async function loadTelemetryIdentity(options: LoadTelemetryIdentityOption
|
|||
export function computeTelemetryProjectId(installId: string, projectDir: string): string {
|
||||
return createHash('sha256').update(`${installId}:${resolve(projectDir)}`).digest('hex');
|
||||
}
|
||||
|
||||
export async function readExistingTelemetryProjectId(options: {
|
||||
projectDir: string;
|
||||
homeDir?: string;
|
||||
env?: Pick<TelemetryIdentityEnv, 'KTX_TELEMETRY_DISABLED' | 'DO_NOT_TRACK'>;
|
||||
}): Promise<string | undefined> {
|
||||
const env = options.env ?? process.env;
|
||||
if (env.KTX_TELEMETRY_DISABLED || env.DO_NOT_TRACK) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const existing = await readTelemetryFile(telemetryPath(options.homeDir ?? homedir()));
|
||||
if (!existing?.enabled) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return computeTelemetryProjectId(existing.installId, options.projectDir);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue