@querypanel/node-sdk 1.0.57 → 1.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/dist/index.cjs +231 -30
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +84 -6
- package/dist/index.d.ts +84 -6
- package/dist/index.js +230 -30
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,3 +1,183 @@
|
|
|
1
|
+
// src/adapters/bigquery.ts
|
|
2
|
+
var BigQueryAdapter = class {
|
|
3
|
+
constructor(clientFn, options) {
|
|
4
|
+
this.clientFn = clientFn;
|
|
5
|
+
this.projectId = options.projectId;
|
|
6
|
+
this.datasetProjectId = options.datasetProjectId ?? options.projectId;
|
|
7
|
+
this.defaultDataset = options.dataset;
|
|
8
|
+
this.databaseName = options.database ?? options.dataset;
|
|
9
|
+
this.kind = options.kind ?? "bigquery";
|
|
10
|
+
if (options.allowedTables) {
|
|
11
|
+
this.allowedTables = normalizeTableFilter(
|
|
12
|
+
options.allowedTables,
|
|
13
|
+
this.defaultDataset
|
|
14
|
+
);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
projectId;
|
|
18
|
+
datasetProjectId;
|
|
19
|
+
defaultDataset;
|
|
20
|
+
databaseName;
|
|
21
|
+
kind;
|
|
22
|
+
allowedTables;
|
|
23
|
+
async execute(sql, params) {
|
|
24
|
+
if (this.allowedTables) {
|
|
25
|
+
this.validateQueryTables(sql);
|
|
26
|
+
}
|
|
27
|
+
const result = await this.clientFn({
|
|
28
|
+
query: sql,
|
|
29
|
+
params,
|
|
30
|
+
dryRun: false
|
|
31
|
+
});
|
|
32
|
+
return { fields: result.fields, rows: result.rows };
|
|
33
|
+
}
|
|
34
|
+
async validate(sql, params) {
|
|
35
|
+
await this.clientFn({
|
|
36
|
+
query: sql,
|
|
37
|
+
params,
|
|
38
|
+
dryRun: true
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
getDialect() {
|
|
42
|
+
return "bigquery";
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Introspect using BigQuery INFORMATION_SCHEMA (TABLES and COLUMNS).
|
|
46
|
+
* Restricts to the default dataset (or allowedTables) and returns tables/columns only.
|
|
47
|
+
*/
|
|
48
|
+
async introspect(options) {
|
|
49
|
+
const tablesToIntrospect = options?.tables ? normalizeTableFilter(options.tables, this.defaultDataset) : this.allowedTables;
|
|
50
|
+
const normalizedTables = tablesToIntrospect ?? [];
|
|
51
|
+
const tablesSql = buildTablesQuery(
|
|
52
|
+
this.datasetProjectId,
|
|
53
|
+
this.defaultDataset,
|
|
54
|
+
normalizedTables
|
|
55
|
+
);
|
|
56
|
+
const tablesResult = await this.clientFn({
|
|
57
|
+
query: tablesSql,
|
|
58
|
+
dryRun: false
|
|
59
|
+
});
|
|
60
|
+
const tableRows = tablesResult.rows;
|
|
61
|
+
const columnsSql = buildColumnsQuery(
|
|
62
|
+
this.datasetProjectId,
|
|
63
|
+
this.defaultDataset,
|
|
64
|
+
normalizedTables
|
|
65
|
+
);
|
|
66
|
+
const columnsResult = await this.clientFn({
|
|
67
|
+
query: columnsSql,
|
|
68
|
+
dryRun: false
|
|
69
|
+
});
|
|
70
|
+
const columnRows = columnsResult.rows;
|
|
71
|
+
const tablesByKey = /* @__PURE__ */ new Map();
|
|
72
|
+
for (const row of tableRows) {
|
|
73
|
+
const key = tableKey(row.table_schema, row.table_name);
|
|
74
|
+
const table = {
|
|
75
|
+
name: row.table_name,
|
|
76
|
+
schema: row.table_schema,
|
|
77
|
+
type: asTableType(row.table_type),
|
|
78
|
+
columns: []
|
|
79
|
+
};
|
|
80
|
+
tablesByKey.set(key, table);
|
|
81
|
+
}
|
|
82
|
+
for (const row of columnRows) {
|
|
83
|
+
const key = tableKey(row.table_schema, row.table_name);
|
|
84
|
+
const table = tablesByKey.get(key);
|
|
85
|
+
if (!table) continue;
|
|
86
|
+
const column = {
|
|
87
|
+
name: row.column_name,
|
|
88
|
+
type: row.data_type,
|
|
89
|
+
isPrimaryKey: false
|
|
90
|
+
};
|
|
91
|
+
table.columns.push(column);
|
|
92
|
+
}
|
|
93
|
+
const tables = Array.from(tablesByKey.values()).sort((a, b) => {
|
|
94
|
+
if (a.schema === b.schema) {
|
|
95
|
+
return a.name.localeCompare(b.name);
|
|
96
|
+
}
|
|
97
|
+
return a.schema.localeCompare(b.schema);
|
|
98
|
+
});
|
|
99
|
+
return {
|
|
100
|
+
db: {
|
|
101
|
+
kind: this.kind,
|
|
102
|
+
name: this.databaseName
|
|
103
|
+
},
|
|
104
|
+
tables,
|
|
105
|
+
introspectedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
validateQueryTables(sql) {
|
|
109
|
+
if (!this.allowedTables || this.allowedTables.length === 0) {
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
const allowedSet = new Set(
|
|
113
|
+
this.allowedTables.map((t) => tableKey(t.dataset, t.table))
|
|
114
|
+
);
|
|
115
|
+
const tablePattern = /(?:FROM|JOIN)\s+`?(?:[\w-]+\.)?([\w-]+)\.([\w-]+)`?|(?:FROM|JOIN)\s+`?([\w-]+)`?/gi;
|
|
116
|
+
const matches = sql.matchAll(tablePattern);
|
|
117
|
+
for (const match of matches) {
|
|
118
|
+
const dataset = match[1] ?? match[3] ?? this.defaultDataset;
|
|
119
|
+
const table = match[2] ?? match[3];
|
|
120
|
+
if (table) {
|
|
121
|
+
const key = tableKey(dataset, table);
|
|
122
|
+
if (!allowedSet.has(key)) {
|
|
123
|
+
throw new Error(
|
|
124
|
+
`Query references table "${dataset}.${table}" which is not in the allowed tables list`
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
};
|
|
131
|
+
function normalizeTableFilter(tables, defaultDataset) {
|
|
132
|
+
if (!tables?.length) return [];
|
|
133
|
+
const normalized = [];
|
|
134
|
+
const seen = /* @__PURE__ */ new Set();
|
|
135
|
+
for (const raw of tables) {
|
|
136
|
+
if (!raw?.trim()) continue;
|
|
137
|
+
const trimmed = raw.trim();
|
|
138
|
+
const parts = trimmed.split(".");
|
|
139
|
+
const table = parts.pop() ?? "";
|
|
140
|
+
const dataset = parts.pop() ?? defaultDataset;
|
|
141
|
+
const key = tableKey(dataset, table);
|
|
142
|
+
if (seen.has(key)) continue;
|
|
143
|
+
seen.add(key);
|
|
144
|
+
normalized.push({ dataset, table });
|
|
145
|
+
}
|
|
146
|
+
return normalized;
|
|
147
|
+
}
|
|
148
|
+
function tableKey(dataset, table) {
|
|
149
|
+
return `${dataset}.${table}`;
|
|
150
|
+
}
|
|
151
|
+
function buildTablesQuery(datasetProjectId, dataset, tables) {
|
|
152
|
+
const qualifier = `\`${datasetProjectId}.${dataset}.INFORMATION_SCHEMA.TABLES\``;
|
|
153
|
+
const filter = tables.length > 0 ? ` AND table_name IN (${tables.map((t) => `'${escapeSql(t.table)}'`).join(", ")})` : "";
|
|
154
|
+
return `SELECT table_name, table_schema, table_type
|
|
155
|
+
FROM ${qualifier}
|
|
156
|
+
WHERE table_schema = '${escapeSql(dataset)}'${filter}
|
|
157
|
+
ORDER BY table_schema, table_name`;
|
|
158
|
+
}
|
|
159
|
+
function buildColumnsQuery(datasetProjectId, dataset, tables) {
|
|
160
|
+
const qualifier = `\`${datasetProjectId}.${dataset}.INFORMATION_SCHEMA.COLUMNS\``;
|
|
161
|
+
const filter = tables.length > 0 ? ` AND table_name IN (${tables.map((t) => `'${escapeSql(t.table)}'`).join(", ")})` : "";
|
|
162
|
+
return `SELECT table_name, table_schema, column_name, data_type, ordinal_position, is_nullable
|
|
163
|
+
FROM ${qualifier}
|
|
164
|
+
WHERE table_schema = '${escapeSql(dataset)}'${filter}
|
|
165
|
+
ORDER BY table_schema, table_name, ordinal_position`;
|
|
166
|
+
}
|
|
167
|
+
function escapeSql(value) {
|
|
168
|
+
return value.replace(/\\/g, "\\\\").replace(/'/g, "''");
|
|
169
|
+
}
|
|
170
|
+
function asTableType(tableType) {
|
|
171
|
+
const normalized = tableType?.toLowerCase() ?? "";
|
|
172
|
+
if (normalized.includes("view")) {
|
|
173
|
+
return normalized.includes("materialized") ? "materialized_view" : "view";
|
|
174
|
+
}
|
|
175
|
+
if (normalized.includes("external") || normalized.includes("snapshot") || normalized.includes("clone")) {
|
|
176
|
+
return "table";
|
|
177
|
+
}
|
|
178
|
+
return "table";
|
|
179
|
+
}
|
|
180
|
+
|
|
1
181
|
// src/utils/clickhouse.ts
|
|
2
182
|
var WRAPPER_REGEX = /^(Nullable|LowCardinality|SimpleAggregateFunction)\((.+)\)$/i;
|
|
3
183
|
function unwrapTypeModifiers(type) {
|
|
@@ -67,7 +247,7 @@ var ClickHouseAdapter = class {
|
|
|
67
247
|
this.defaultFormat = options.defaultFormat ?? "JSONEachRow";
|
|
68
248
|
this.kind = options.kind ?? "clickhouse";
|
|
69
249
|
if (options.allowedTables) {
|
|
70
|
-
this.allowedTables =
|
|
250
|
+
this.allowedTables = normalizeTableFilter2(options.allowedTables);
|
|
71
251
|
}
|
|
72
252
|
}
|
|
73
253
|
databaseName;
|
|
@@ -105,7 +285,7 @@ var ClickHouseAdapter = class {
|
|
|
105
285
|
* No indexes, constraints, or statistics
|
|
106
286
|
*/
|
|
107
287
|
async introspect(options) {
|
|
108
|
-
const tablesToIntrospect = options?.tables ?
|
|
288
|
+
const tablesToIntrospect = options?.tables ? normalizeTableFilter2(options.tables) : this.allowedTables;
|
|
109
289
|
const allowTables = tablesToIntrospect ?? [];
|
|
110
290
|
const hasFilter = allowTables.length > 0;
|
|
111
291
|
const queryParams = {
|
|
@@ -145,7 +325,7 @@ var ClickHouseAdapter = class {
|
|
|
145
325
|
const base = {
|
|
146
326
|
name: table.name,
|
|
147
327
|
schema: this.databaseName,
|
|
148
|
-
type:
|
|
328
|
+
type: asTableType2(table.engine),
|
|
149
329
|
columns: tableColumns
|
|
150
330
|
};
|
|
151
331
|
const comment = sanitize(table.comment);
|
|
@@ -223,7 +403,7 @@ function normalizePayload(payload) {
|
|
|
223
403
|
}
|
|
224
404
|
return [];
|
|
225
405
|
}
|
|
226
|
-
function
|
|
406
|
+
function normalizeTableFilter2(tables) {
|
|
227
407
|
if (!tables?.length) return [];
|
|
228
408
|
const seen = /* @__PURE__ */ new Set();
|
|
229
409
|
const normalized = [];
|
|
@@ -251,7 +431,7 @@ function transformColumnRow(row) {
|
|
|
251
431
|
if (comment !== void 0) column.comment = comment;
|
|
252
432
|
return column;
|
|
253
433
|
}
|
|
254
|
-
function
|
|
434
|
+
function asTableType2(engine) {
|
|
255
435
|
if (typeof engine === "string") {
|
|
256
436
|
const normalized = engine.toLowerCase();
|
|
257
437
|
if (normalized.includes("view")) {
|
|
@@ -280,7 +460,7 @@ var PostgresAdapter = class {
|
|
|
280
460
|
this.defaultSchema = options.defaultSchema ?? "public";
|
|
281
461
|
this.kind = options.kind ?? "postgres";
|
|
282
462
|
if (options.allowedTables) {
|
|
283
|
-
this.allowedTables =
|
|
463
|
+
this.allowedTables = normalizeTableFilter3(
|
|
284
464
|
options.allowedTables,
|
|
285
465
|
this.defaultSchema
|
|
286
466
|
);
|
|
@@ -307,7 +487,7 @@ var PostgresAdapter = class {
|
|
|
307
487
|
return;
|
|
308
488
|
}
|
|
309
489
|
const allowedSet = new Set(
|
|
310
|
-
this.allowedTables.map((t) =>
|
|
490
|
+
this.allowedTables.map((t) => tableKey2(t.schema, t.table))
|
|
311
491
|
);
|
|
312
492
|
const neutralizedSql = sql.replace(/EXTRACT\s*\([^)]*FROM\s+[^)]+\)/gi, "EXTRACT(/*neutralized*/)").replace(/SUBSTRING\s*\([^)]*FROM\s+[^)]+\)/gi, "SUBSTRING(/*neutralized*/)").replace(/TRIM\s*\([^)]*FROM\s+[^)]+\)/gi, "TRIM(/*neutralized*/)").replace(/POSITION\s*\([^)]*FROM\s+[^)]+\)/gi, "POSITION(/*neutralized*/)");
|
|
313
493
|
const tablePattern = /(?:FROM|JOIN)\s+(?:ONLY\s+)?(?:([a-zA-Z_][a-zA-Z0-9_]*)\.)?(["']?[a-zA-Z_][a-zA-Z0-9_]*["']?)/gi;
|
|
@@ -316,7 +496,7 @@ var PostgresAdapter = class {
|
|
|
316
496
|
const schema = match[1] ?? this.defaultSchema;
|
|
317
497
|
const table = match[2]?.replace(/['"]/g, "");
|
|
318
498
|
if (table) {
|
|
319
|
-
const key =
|
|
499
|
+
const key = tableKey2(schema, table);
|
|
320
500
|
if (!allowedSet.has(key)) {
|
|
321
501
|
throw new Error(
|
|
322
502
|
`Query references table "${schema}.${table}" which is not in the allowed tables list`
|
|
@@ -367,23 +547,23 @@ var PostgresAdapter = class {
|
|
|
367
547
|
* No indexes, constraints, or statistics
|
|
368
548
|
*/
|
|
369
549
|
async introspect(options) {
|
|
370
|
-
const tablesToIntrospect = options?.tables ?
|
|
550
|
+
const tablesToIntrospect = options?.tables ? normalizeTableFilter3(options.tables, this.defaultSchema) : this.allowedTables;
|
|
371
551
|
const normalizedTables = tablesToIntrospect ?? [];
|
|
372
552
|
const tablesResult = await this.clientFn(
|
|
373
|
-
|
|
553
|
+
buildTablesQuery2(normalizedTables)
|
|
374
554
|
);
|
|
375
555
|
const tableRows = tablesResult.rows;
|
|
376
556
|
const columnsResult = await this.clientFn(
|
|
377
|
-
|
|
557
|
+
buildColumnsQuery2(normalizedTables)
|
|
378
558
|
);
|
|
379
559
|
const columnRows = columnsResult.rows;
|
|
380
560
|
const tablesByKey = /* @__PURE__ */ new Map();
|
|
381
561
|
for (const row of tableRows) {
|
|
382
|
-
const key =
|
|
562
|
+
const key = tableKey2(row.schema_name, row.table_name);
|
|
383
563
|
const table = {
|
|
384
564
|
name: row.table_name,
|
|
385
565
|
schema: row.schema_name,
|
|
386
|
-
type:
|
|
566
|
+
type: asTableType3(row.table_type),
|
|
387
567
|
columns: []
|
|
388
568
|
};
|
|
389
569
|
const comment = sanitize2(row.comment);
|
|
@@ -393,7 +573,7 @@ var PostgresAdapter = class {
|
|
|
393
573
|
tablesByKey.set(key, table);
|
|
394
574
|
}
|
|
395
575
|
for (const row of columnRows) {
|
|
396
|
-
const key =
|
|
576
|
+
const key = tableKey2(row.table_schema, row.table_name);
|
|
397
577
|
const table = tablesByKey.get(key);
|
|
398
578
|
if (!table) continue;
|
|
399
579
|
const column = {
|
|
@@ -423,7 +603,7 @@ var PostgresAdapter = class {
|
|
|
423
603
|
};
|
|
424
604
|
}
|
|
425
605
|
};
|
|
426
|
-
function
|
|
606
|
+
function normalizeTableFilter3(tables, defaultSchema) {
|
|
427
607
|
if (!tables?.length) return [];
|
|
428
608
|
const normalized = [];
|
|
429
609
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -437,14 +617,14 @@ function normalizeTableFilter2(tables, defaultSchema) {
|
|
|
437
617
|
if (!isSafeIdentifier(schema) || !isSafeIdentifier(table)) {
|
|
438
618
|
continue;
|
|
439
619
|
}
|
|
440
|
-
const key =
|
|
620
|
+
const key = tableKey2(schema, table);
|
|
441
621
|
if (seen.has(key)) continue;
|
|
442
622
|
seen.add(key);
|
|
443
623
|
normalized.push({ schema, table });
|
|
444
624
|
}
|
|
445
625
|
return normalized;
|
|
446
626
|
}
|
|
447
|
-
function
|
|
627
|
+
function buildTablesQuery2(tables) {
|
|
448
628
|
const filter = buildFilterClause(tables, "n.nspname", "c.relname");
|
|
449
629
|
return `SELECT
|
|
450
630
|
c.relname AS table_name,
|
|
@@ -463,7 +643,7 @@ function buildTablesQuery(tables) {
|
|
|
463
643
|
${filter}
|
|
464
644
|
ORDER BY n.nspname, c.relname;`;
|
|
465
645
|
}
|
|
466
|
-
function
|
|
646
|
+
function buildColumnsQuery2(tables) {
|
|
467
647
|
const filter = buildFilterClause(
|
|
468
648
|
tables,
|
|
469
649
|
"cols.table_schema",
|
|
@@ -508,13 +688,13 @@ function buildFilterClause(tables, schemaExpr, tableExpr) {
|
|
|
508
688
|
});
|
|
509
689
|
return `AND (${clauses.join(" OR ")})`;
|
|
510
690
|
}
|
|
511
|
-
function
|
|
691
|
+
function tableKey2(schema, table) {
|
|
512
692
|
return `${schema}.${table}`;
|
|
513
693
|
}
|
|
514
694
|
function isSafeIdentifier(value) {
|
|
515
695
|
return /^[A-Za-z_][A-Za-z0-9_]*$/.test(value);
|
|
516
696
|
}
|
|
517
|
-
function
|
|
697
|
+
function asTableType3(value) {
|
|
518
698
|
const normalized = value.toLowerCase();
|
|
519
699
|
if (normalized.includes("view")) {
|
|
520
700
|
return normalized.includes("materialized") ? "materialized_view" : "view";
|
|
@@ -982,6 +1162,9 @@ var QueryEngine = class {
|
|
|
982
1162
|
const paramKey = tenantField;
|
|
983
1163
|
params[paramKey] = tenantId;
|
|
984
1164
|
tenantPredicate = `${tenantField} = {${tenantField}:${metadata.tenantFieldType ?? "String"}}`;
|
|
1165
|
+
} else if (metadata.dialect === "bigquery") {
|
|
1166
|
+
params[tenantField] = tenantId;
|
|
1167
|
+
tenantPredicate = `${tenantField} = @${tenantField}`;
|
|
985
1168
|
} else {
|
|
986
1169
|
const escapedId = tenantId.replace(/'/g, "''");
|
|
987
1170
|
tenantPredicate = `${tenantField} = '${escapedId}'`;
|
|
@@ -1538,7 +1721,8 @@ async function ask(client, queryEngine, question, options, signal) {
|
|
|
1538
1721
|
...options.maxRetry ? { max_retry: options.maxRetry } : {},
|
|
1539
1722
|
...tenantSettings ? { tenant_settings: tenantSettings } : {},
|
|
1540
1723
|
...databaseName ? { database: databaseName } : {},
|
|
1541
|
-
...metadata?.dialect ? { dialect: metadata.dialect } : {}
|
|
1724
|
+
...metadata?.dialect ? { dialect: metadata.dialect } : {},
|
|
1725
|
+
...options.model?.trim() ? { model: options.model.trim() } : {}
|
|
1542
1726
|
},
|
|
1543
1727
|
tenantId,
|
|
1544
1728
|
options.userId,
|
|
@@ -1562,7 +1746,7 @@ async function ask(client, queryEngine, question, options, signal) {
|
|
|
1562
1746
|
const dbName = queryResponse.data.database ?? options.database ?? queryEngine.getDefaultDatabase();
|
|
1563
1747
|
if (!dbName) {
|
|
1564
1748
|
throw new Error(
|
|
1565
|
-
"No database attached. Call attachPostgres
|
|
1749
|
+
"No database attached. Call attachPostgres, attachClickhouse, or attachBigQuery first."
|
|
1566
1750
|
);
|
|
1567
1751
|
}
|
|
1568
1752
|
const paramMetadata = Array.isArray(queryResponse.data.params) ? queryResponse.data.params : [];
|
|
@@ -2103,20 +2287,22 @@ function resolveTenantId8(client, tenantId) {
|
|
|
2103
2287
|
var QueryPanelSdkAPI = class _QueryPanelSdkAPI {
|
|
2104
2288
|
client;
|
|
2105
2289
|
queryEngine;
|
|
2106
|
-
|
|
2290
|
+
/**
|
|
2291
|
+
* @param workspaceId - Workspace UUID (same value as your org in the dashboard). Passed to the API as JWT claim `organizationId`.
|
|
2292
|
+
*/
|
|
2293
|
+
constructor(baseUrl, privateKey, workspaceId, options) {
|
|
2107
2294
|
if (options?.api) {
|
|
2108
2295
|
this.client = options.api;
|
|
2109
2296
|
} else {
|
|
2110
2297
|
if (!baseUrl) throw new Error("Base URL is required");
|
|
2111
2298
|
if (!privateKey) throw new Error("Private key is required");
|
|
2112
|
-
if (!
|
|
2113
|
-
this.client = new ApiClient(baseUrl, privateKey,
|
|
2299
|
+
if (!workspaceId) throw new Error("Workspace ID is required");
|
|
2300
|
+
this.client = new ApiClient(baseUrl, privateKey, workspaceId, {
|
|
2114
2301
|
defaultTenantId: options?.defaultTenantId,
|
|
2115
2302
|
additionalHeaders: options?.additionalHeaders,
|
|
2116
2303
|
fetch: options?.fetch
|
|
2117
2304
|
});
|
|
2118
2305
|
}
|
|
2119
|
-
if (!organizationId) throw new Error("Organization ID is required");
|
|
2120
2306
|
this.queryEngine = new QueryEngine();
|
|
2121
2307
|
}
|
|
2122
2308
|
/**
|
|
@@ -2125,7 +2311,7 @@ var QueryPanelSdkAPI = class _QueryPanelSdkAPI {
|
|
|
2125
2311
|
* are handled in-process (e.g. by invoking querypanel-sdk services directly)
|
|
2126
2312
|
* and do not cause recursion.
|
|
2127
2313
|
*
|
|
2128
|
-
* @param
|
|
2314
|
+
* @param workspaceId - Workspace identifier (sent as `organizationId` in JWTs and API auth)
|
|
2129
2315
|
* @param requestHandler - Callback invoked for each logical API request (method, path, body, tenantId, etc.)
|
|
2130
2316
|
* @param options - Optional defaultTenantId
|
|
2131
2317
|
* @returns QueryPanelSdkAPI instance; attach databases and call ask(), syncSchema(), etc. as usual
|
|
@@ -2133,7 +2319,7 @@ var QueryPanelSdkAPI = class _QueryPanelSdkAPI {
|
|
|
2133
2319
|
* @example
|
|
2134
2320
|
* ```ts
|
|
2135
2321
|
* const qp = QueryPanelSdkAPI.withCallbacks(
|
|
2136
|
-
* process.env.
|
|
2322
|
+
* process.env.QUERYPANEL_WORKSPACE_ID!,
|
|
2137
2323
|
* async (opts) => {
|
|
2138
2324
|
* // Call your in-process services (e.g. querypanel-sdk logic) instead of HTTP
|
|
2139
2325
|
* if (opts.path === '/query' || opts.path === '/v2/query') {
|
|
@@ -2150,11 +2336,11 @@ var QueryPanelSdkAPI = class _QueryPanelSdkAPI {
|
|
|
2150
2336
|
* const result = await qp.ask('Top 10 orders', { tenantId: 't1', database: 'db' });
|
|
2151
2337
|
* ```
|
|
2152
2338
|
*/
|
|
2153
|
-
static withCallbacks(
|
|
2339
|
+
static withCallbacks(workspaceId, requestHandler, options) {
|
|
2154
2340
|
const api = new CallbackApiClient(requestHandler, {
|
|
2155
2341
|
defaultTenantId: options?.defaultTenantId
|
|
2156
2342
|
});
|
|
2157
|
-
return new _QueryPanelSdkAPI("", "",
|
|
2343
|
+
return new _QueryPanelSdkAPI("", "", workspaceId, { api });
|
|
2158
2344
|
}
|
|
2159
2345
|
// Database attachment methods
|
|
2160
2346
|
attachClickhouse(name, clientFn, options) {
|
|
@@ -2183,6 +2369,19 @@ var QueryPanelSdkAPI = class _QueryPanelSdkAPI {
|
|
|
2183
2369
|
};
|
|
2184
2370
|
this.queryEngine.attachDatabase(name, adapter, metadata);
|
|
2185
2371
|
}
|
|
2372
|
+
attachBigQuery(name, clientFn, options) {
|
|
2373
|
+
const adapter = new BigQueryAdapter(clientFn, options);
|
|
2374
|
+
const metadata = {
|
|
2375
|
+
name,
|
|
2376
|
+
dialect: "bigquery",
|
|
2377
|
+
description: options?.description,
|
|
2378
|
+
tags: options?.tags,
|
|
2379
|
+
tenantFieldName: options?.tenantFieldName,
|
|
2380
|
+
tenantFieldType: options?.tenantFieldType ?? "String",
|
|
2381
|
+
enforceTenantIsolation: options?.tenantFieldName ? options?.enforceTenantIsolation ?? true : void 0
|
|
2382
|
+
};
|
|
2383
|
+
this.queryEngine.attachDatabase(name, adapter, metadata);
|
|
2384
|
+
}
|
|
2186
2385
|
attachDatabase(name, adapter) {
|
|
2187
2386
|
const metadata = {
|
|
2188
2387
|
name,
|
|
@@ -3043,6 +3242,7 @@ var QueryPanelSdkAPI = class _QueryPanelSdkAPI {
|
|
|
3043
3242
|
}
|
|
3044
3243
|
};
|
|
3045
3244
|
export {
|
|
3245
|
+
BigQueryAdapter,
|
|
3046
3246
|
CallbackApiClient,
|
|
3047
3247
|
ClickHouseAdapter,
|
|
3048
3248
|
PostgresAdapter,
|