@querypanel/node-sdk 1.0.57 → 1.2.0
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 +229 -29
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +80 -6
- package/dist/index.d.ts +80 -6
- package/dist/index.js +228 -29
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -21,7 +21,7 @@ import { Pool } from "pg";
|
|
|
21
21
|
const qp = new QueryPanelSdkAPI(
|
|
22
22
|
process.env.QUERYPANEL_URL!,
|
|
23
23
|
process.env.PRIVATE_KEY!,
|
|
24
|
-
process.env.
|
|
24
|
+
process.env.QUERYPANEL_WORKSPACE_ID!,
|
|
25
25
|
{
|
|
26
26
|
defaultTenantId: process.env.DEFAULT_TENANT_ID,
|
|
27
27
|
},
|
|
@@ -338,7 +338,7 @@ import { QueryPanelSdkAPI } from "https://esm.sh/@querypanel/sdk";
|
|
|
338
338
|
const qp = new QueryPanelSdkAPI(
|
|
339
339
|
Deno.env.get("QUERYPANEL_URL")!,
|
|
340
340
|
Deno.env.get("PRIVATE_KEY")!,
|
|
341
|
-
Deno.env.get("
|
|
341
|
+
Deno.env.get("QUERYPANEL_WORKSPACE_ID")!,
|
|
342
342
|
);
|
|
343
343
|
|
|
344
344
|
// Use the SDK as normal - JWT signing works automatically
|
package/dist/index.cjs
CHANGED
|
@@ -30,6 +30,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
30
30
|
// src/index.ts
|
|
31
31
|
var index_exports = {};
|
|
32
32
|
__export(index_exports, {
|
|
33
|
+
BigQueryAdapter: () => BigQueryAdapter,
|
|
33
34
|
CallbackApiClient: () => CallbackApiClient,
|
|
34
35
|
ClickHouseAdapter: () => ClickHouseAdapter,
|
|
35
36
|
PostgresAdapter: () => PostgresAdapter,
|
|
@@ -40,6 +41,186 @@ __export(index_exports, {
|
|
|
40
41
|
});
|
|
41
42
|
module.exports = __toCommonJS(index_exports);
|
|
42
43
|
|
|
44
|
+
// src/adapters/bigquery.ts
|
|
45
|
+
var BigQueryAdapter = class {
|
|
46
|
+
constructor(clientFn, options) {
|
|
47
|
+
this.clientFn = clientFn;
|
|
48
|
+
this.projectId = options.projectId;
|
|
49
|
+
this.datasetProjectId = options.datasetProjectId ?? options.projectId;
|
|
50
|
+
this.defaultDataset = options.dataset;
|
|
51
|
+
this.databaseName = options.database ?? options.dataset;
|
|
52
|
+
this.kind = options.kind ?? "bigquery";
|
|
53
|
+
if (options.allowedTables) {
|
|
54
|
+
this.allowedTables = normalizeTableFilter(
|
|
55
|
+
options.allowedTables,
|
|
56
|
+
this.defaultDataset
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
projectId;
|
|
61
|
+
datasetProjectId;
|
|
62
|
+
defaultDataset;
|
|
63
|
+
databaseName;
|
|
64
|
+
kind;
|
|
65
|
+
allowedTables;
|
|
66
|
+
async execute(sql, params) {
|
|
67
|
+
if (this.allowedTables) {
|
|
68
|
+
this.validateQueryTables(sql);
|
|
69
|
+
}
|
|
70
|
+
const result = await this.clientFn({
|
|
71
|
+
query: sql,
|
|
72
|
+
params,
|
|
73
|
+
dryRun: false
|
|
74
|
+
});
|
|
75
|
+
return { fields: result.fields, rows: result.rows };
|
|
76
|
+
}
|
|
77
|
+
async validate(sql, params) {
|
|
78
|
+
await this.clientFn({
|
|
79
|
+
query: sql,
|
|
80
|
+
params,
|
|
81
|
+
dryRun: true
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
getDialect() {
|
|
85
|
+
return "bigquery";
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Introspect using BigQuery INFORMATION_SCHEMA (TABLES and COLUMNS).
|
|
89
|
+
* Restricts to the default dataset (or allowedTables) and returns tables/columns only.
|
|
90
|
+
*/
|
|
91
|
+
async introspect(options) {
|
|
92
|
+
const tablesToIntrospect = options?.tables ? normalizeTableFilter(options.tables, this.defaultDataset) : this.allowedTables;
|
|
93
|
+
const normalizedTables = tablesToIntrospect ?? [];
|
|
94
|
+
const tablesSql = buildTablesQuery(
|
|
95
|
+
this.datasetProjectId,
|
|
96
|
+
this.defaultDataset,
|
|
97
|
+
normalizedTables
|
|
98
|
+
);
|
|
99
|
+
const tablesResult = await this.clientFn({
|
|
100
|
+
query: tablesSql,
|
|
101
|
+
dryRun: false
|
|
102
|
+
});
|
|
103
|
+
const tableRows = tablesResult.rows;
|
|
104
|
+
const columnsSql = buildColumnsQuery(
|
|
105
|
+
this.datasetProjectId,
|
|
106
|
+
this.defaultDataset,
|
|
107
|
+
normalizedTables
|
|
108
|
+
);
|
|
109
|
+
const columnsResult = await this.clientFn({
|
|
110
|
+
query: columnsSql,
|
|
111
|
+
dryRun: false
|
|
112
|
+
});
|
|
113
|
+
const columnRows = columnsResult.rows;
|
|
114
|
+
const tablesByKey = /* @__PURE__ */ new Map();
|
|
115
|
+
for (const row of tableRows) {
|
|
116
|
+
const key = tableKey(row.table_schema, row.table_name);
|
|
117
|
+
const table = {
|
|
118
|
+
name: row.table_name,
|
|
119
|
+
schema: row.table_schema,
|
|
120
|
+
type: asTableType(row.table_type),
|
|
121
|
+
columns: []
|
|
122
|
+
};
|
|
123
|
+
tablesByKey.set(key, table);
|
|
124
|
+
}
|
|
125
|
+
for (const row of columnRows) {
|
|
126
|
+
const key = tableKey(row.table_schema, row.table_name);
|
|
127
|
+
const table = tablesByKey.get(key);
|
|
128
|
+
if (!table) continue;
|
|
129
|
+
const column = {
|
|
130
|
+
name: row.column_name,
|
|
131
|
+
type: row.data_type,
|
|
132
|
+
isPrimaryKey: false
|
|
133
|
+
};
|
|
134
|
+
table.columns.push(column);
|
|
135
|
+
}
|
|
136
|
+
const tables = Array.from(tablesByKey.values()).sort((a, b) => {
|
|
137
|
+
if (a.schema === b.schema) {
|
|
138
|
+
return a.name.localeCompare(b.name);
|
|
139
|
+
}
|
|
140
|
+
return a.schema.localeCompare(b.schema);
|
|
141
|
+
});
|
|
142
|
+
return {
|
|
143
|
+
db: {
|
|
144
|
+
kind: this.kind,
|
|
145
|
+
name: this.databaseName
|
|
146
|
+
},
|
|
147
|
+
tables,
|
|
148
|
+
introspectedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
validateQueryTables(sql) {
|
|
152
|
+
if (!this.allowedTables || this.allowedTables.length === 0) {
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
const allowedSet = new Set(
|
|
156
|
+
this.allowedTables.map((t) => tableKey(t.dataset, t.table))
|
|
157
|
+
);
|
|
158
|
+
const tablePattern = /(?:FROM|JOIN)\s+`?(?:[\w-]+\.)?([\w-]+)\.([\w-]+)`?|(?:FROM|JOIN)\s+`?([\w-]+)`?/gi;
|
|
159
|
+
const matches = sql.matchAll(tablePattern);
|
|
160
|
+
for (const match of matches) {
|
|
161
|
+
const dataset = match[1] ?? match[3] ?? this.defaultDataset;
|
|
162
|
+
const table = match[2] ?? match[3];
|
|
163
|
+
if (table) {
|
|
164
|
+
const key = tableKey(dataset, table);
|
|
165
|
+
if (!allowedSet.has(key)) {
|
|
166
|
+
throw new Error(
|
|
167
|
+
`Query references table "${dataset}.${table}" which is not in the allowed tables list`
|
|
168
|
+
);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
};
|
|
174
|
+
function normalizeTableFilter(tables, defaultDataset) {
|
|
175
|
+
if (!tables?.length) return [];
|
|
176
|
+
const normalized = [];
|
|
177
|
+
const seen = /* @__PURE__ */ new Set();
|
|
178
|
+
for (const raw of tables) {
|
|
179
|
+
if (!raw?.trim()) continue;
|
|
180
|
+
const trimmed = raw.trim();
|
|
181
|
+
const parts = trimmed.split(".");
|
|
182
|
+
const table = parts.pop() ?? "";
|
|
183
|
+
const dataset = parts.pop() ?? defaultDataset;
|
|
184
|
+
const key = tableKey(dataset, table);
|
|
185
|
+
if (seen.has(key)) continue;
|
|
186
|
+
seen.add(key);
|
|
187
|
+
normalized.push({ dataset, table });
|
|
188
|
+
}
|
|
189
|
+
return normalized;
|
|
190
|
+
}
|
|
191
|
+
function tableKey(dataset, table) {
|
|
192
|
+
return `${dataset}.${table}`;
|
|
193
|
+
}
|
|
194
|
+
function buildTablesQuery(datasetProjectId, dataset, tables) {
|
|
195
|
+
const qualifier = `\`${datasetProjectId}.${dataset}.INFORMATION_SCHEMA.TABLES\``;
|
|
196
|
+
const filter = tables.length > 0 ? ` AND table_name IN (${tables.map((t) => `'${escapeSql(t.table)}'`).join(", ")})` : "";
|
|
197
|
+
return `SELECT table_name, table_schema, table_type
|
|
198
|
+
FROM ${qualifier}
|
|
199
|
+
WHERE table_schema = '${escapeSql(dataset)}'${filter}
|
|
200
|
+
ORDER BY table_schema, table_name`;
|
|
201
|
+
}
|
|
202
|
+
function buildColumnsQuery(datasetProjectId, dataset, tables) {
|
|
203
|
+
const qualifier = `\`${datasetProjectId}.${dataset}.INFORMATION_SCHEMA.COLUMNS\``;
|
|
204
|
+
const filter = tables.length > 0 ? ` AND table_name IN (${tables.map((t) => `'${escapeSql(t.table)}'`).join(", ")})` : "";
|
|
205
|
+
return `SELECT table_name, table_schema, column_name, data_type, ordinal_position, is_nullable
|
|
206
|
+
FROM ${qualifier}
|
|
207
|
+
WHERE table_schema = '${escapeSql(dataset)}'${filter}
|
|
208
|
+
ORDER BY table_schema, table_name, ordinal_position`;
|
|
209
|
+
}
|
|
210
|
+
function escapeSql(value) {
|
|
211
|
+
return value.replace(/\\/g, "\\\\").replace(/'/g, "''");
|
|
212
|
+
}
|
|
213
|
+
function asTableType(tableType) {
|
|
214
|
+
const normalized = tableType?.toLowerCase() ?? "";
|
|
215
|
+
if (normalized.includes("view")) {
|
|
216
|
+
return normalized.includes("materialized") ? "materialized_view" : "view";
|
|
217
|
+
}
|
|
218
|
+
if (normalized.includes("external") || normalized.includes("snapshot") || normalized.includes("clone")) {
|
|
219
|
+
return "table";
|
|
220
|
+
}
|
|
221
|
+
return "table";
|
|
222
|
+
}
|
|
223
|
+
|
|
43
224
|
// src/utils/clickhouse.ts
|
|
44
225
|
var WRAPPER_REGEX = /^(Nullable|LowCardinality|SimpleAggregateFunction)\((.+)\)$/i;
|
|
45
226
|
function unwrapTypeModifiers(type) {
|
|
@@ -109,7 +290,7 @@ var ClickHouseAdapter = class {
|
|
|
109
290
|
this.defaultFormat = options.defaultFormat ?? "JSONEachRow";
|
|
110
291
|
this.kind = options.kind ?? "clickhouse";
|
|
111
292
|
if (options.allowedTables) {
|
|
112
|
-
this.allowedTables =
|
|
293
|
+
this.allowedTables = normalizeTableFilter2(options.allowedTables);
|
|
113
294
|
}
|
|
114
295
|
}
|
|
115
296
|
databaseName;
|
|
@@ -147,7 +328,7 @@ var ClickHouseAdapter = class {
|
|
|
147
328
|
* No indexes, constraints, or statistics
|
|
148
329
|
*/
|
|
149
330
|
async introspect(options) {
|
|
150
|
-
const tablesToIntrospect = options?.tables ?
|
|
331
|
+
const tablesToIntrospect = options?.tables ? normalizeTableFilter2(options.tables) : this.allowedTables;
|
|
151
332
|
const allowTables = tablesToIntrospect ?? [];
|
|
152
333
|
const hasFilter = allowTables.length > 0;
|
|
153
334
|
const queryParams = {
|
|
@@ -187,7 +368,7 @@ var ClickHouseAdapter = class {
|
|
|
187
368
|
const base = {
|
|
188
369
|
name: table.name,
|
|
189
370
|
schema: this.databaseName,
|
|
190
|
-
type:
|
|
371
|
+
type: asTableType2(table.engine),
|
|
191
372
|
columns: tableColumns
|
|
192
373
|
};
|
|
193
374
|
const comment = sanitize(table.comment);
|
|
@@ -265,7 +446,7 @@ function normalizePayload(payload) {
|
|
|
265
446
|
}
|
|
266
447
|
return [];
|
|
267
448
|
}
|
|
268
|
-
function
|
|
449
|
+
function normalizeTableFilter2(tables) {
|
|
269
450
|
if (!tables?.length) return [];
|
|
270
451
|
const seen = /* @__PURE__ */ new Set();
|
|
271
452
|
const normalized = [];
|
|
@@ -293,7 +474,7 @@ function transformColumnRow(row) {
|
|
|
293
474
|
if (comment !== void 0) column.comment = comment;
|
|
294
475
|
return column;
|
|
295
476
|
}
|
|
296
|
-
function
|
|
477
|
+
function asTableType2(engine) {
|
|
297
478
|
if (typeof engine === "string") {
|
|
298
479
|
const normalized = engine.toLowerCase();
|
|
299
480
|
if (normalized.includes("view")) {
|
|
@@ -322,7 +503,7 @@ var PostgresAdapter = class {
|
|
|
322
503
|
this.defaultSchema = options.defaultSchema ?? "public";
|
|
323
504
|
this.kind = options.kind ?? "postgres";
|
|
324
505
|
if (options.allowedTables) {
|
|
325
|
-
this.allowedTables =
|
|
506
|
+
this.allowedTables = normalizeTableFilter3(
|
|
326
507
|
options.allowedTables,
|
|
327
508
|
this.defaultSchema
|
|
328
509
|
);
|
|
@@ -349,7 +530,7 @@ var PostgresAdapter = class {
|
|
|
349
530
|
return;
|
|
350
531
|
}
|
|
351
532
|
const allowedSet = new Set(
|
|
352
|
-
this.allowedTables.map((t) =>
|
|
533
|
+
this.allowedTables.map((t) => tableKey2(t.schema, t.table))
|
|
353
534
|
);
|
|
354
535
|
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*/)");
|
|
355
536
|
const tablePattern = /(?:FROM|JOIN)\s+(?:ONLY\s+)?(?:([a-zA-Z_][a-zA-Z0-9_]*)\.)?(["']?[a-zA-Z_][a-zA-Z0-9_]*["']?)/gi;
|
|
@@ -358,7 +539,7 @@ var PostgresAdapter = class {
|
|
|
358
539
|
const schema = match[1] ?? this.defaultSchema;
|
|
359
540
|
const table = match[2]?.replace(/['"]/g, "");
|
|
360
541
|
if (table) {
|
|
361
|
-
const key =
|
|
542
|
+
const key = tableKey2(schema, table);
|
|
362
543
|
if (!allowedSet.has(key)) {
|
|
363
544
|
throw new Error(
|
|
364
545
|
`Query references table "${schema}.${table}" which is not in the allowed tables list`
|
|
@@ -409,23 +590,23 @@ var PostgresAdapter = class {
|
|
|
409
590
|
* No indexes, constraints, or statistics
|
|
410
591
|
*/
|
|
411
592
|
async introspect(options) {
|
|
412
|
-
const tablesToIntrospect = options?.tables ?
|
|
593
|
+
const tablesToIntrospect = options?.tables ? normalizeTableFilter3(options.tables, this.defaultSchema) : this.allowedTables;
|
|
413
594
|
const normalizedTables = tablesToIntrospect ?? [];
|
|
414
595
|
const tablesResult = await this.clientFn(
|
|
415
|
-
|
|
596
|
+
buildTablesQuery2(normalizedTables)
|
|
416
597
|
);
|
|
417
598
|
const tableRows = tablesResult.rows;
|
|
418
599
|
const columnsResult = await this.clientFn(
|
|
419
|
-
|
|
600
|
+
buildColumnsQuery2(normalizedTables)
|
|
420
601
|
);
|
|
421
602
|
const columnRows = columnsResult.rows;
|
|
422
603
|
const tablesByKey = /* @__PURE__ */ new Map();
|
|
423
604
|
for (const row of tableRows) {
|
|
424
|
-
const key =
|
|
605
|
+
const key = tableKey2(row.schema_name, row.table_name);
|
|
425
606
|
const table = {
|
|
426
607
|
name: row.table_name,
|
|
427
608
|
schema: row.schema_name,
|
|
428
|
-
type:
|
|
609
|
+
type: asTableType3(row.table_type),
|
|
429
610
|
columns: []
|
|
430
611
|
};
|
|
431
612
|
const comment = sanitize2(row.comment);
|
|
@@ -435,7 +616,7 @@ var PostgresAdapter = class {
|
|
|
435
616
|
tablesByKey.set(key, table);
|
|
436
617
|
}
|
|
437
618
|
for (const row of columnRows) {
|
|
438
|
-
const key =
|
|
619
|
+
const key = tableKey2(row.table_schema, row.table_name);
|
|
439
620
|
const table = tablesByKey.get(key);
|
|
440
621
|
if (!table) continue;
|
|
441
622
|
const column = {
|
|
@@ -465,7 +646,7 @@ var PostgresAdapter = class {
|
|
|
465
646
|
};
|
|
466
647
|
}
|
|
467
648
|
};
|
|
468
|
-
function
|
|
649
|
+
function normalizeTableFilter3(tables, defaultSchema) {
|
|
469
650
|
if (!tables?.length) return [];
|
|
470
651
|
const normalized = [];
|
|
471
652
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -479,14 +660,14 @@ function normalizeTableFilter2(tables, defaultSchema) {
|
|
|
479
660
|
if (!isSafeIdentifier(schema) || !isSafeIdentifier(table)) {
|
|
480
661
|
continue;
|
|
481
662
|
}
|
|
482
|
-
const key =
|
|
663
|
+
const key = tableKey2(schema, table);
|
|
483
664
|
if (seen.has(key)) continue;
|
|
484
665
|
seen.add(key);
|
|
485
666
|
normalized.push({ schema, table });
|
|
486
667
|
}
|
|
487
668
|
return normalized;
|
|
488
669
|
}
|
|
489
|
-
function
|
|
670
|
+
function buildTablesQuery2(tables) {
|
|
490
671
|
const filter = buildFilterClause(tables, "n.nspname", "c.relname");
|
|
491
672
|
return `SELECT
|
|
492
673
|
c.relname AS table_name,
|
|
@@ -505,7 +686,7 @@ function buildTablesQuery(tables) {
|
|
|
505
686
|
${filter}
|
|
506
687
|
ORDER BY n.nspname, c.relname;`;
|
|
507
688
|
}
|
|
508
|
-
function
|
|
689
|
+
function buildColumnsQuery2(tables) {
|
|
509
690
|
const filter = buildFilterClause(
|
|
510
691
|
tables,
|
|
511
692
|
"cols.table_schema",
|
|
@@ -550,13 +731,13 @@ function buildFilterClause(tables, schemaExpr, tableExpr) {
|
|
|
550
731
|
});
|
|
551
732
|
return `AND (${clauses.join(" OR ")})`;
|
|
552
733
|
}
|
|
553
|
-
function
|
|
734
|
+
function tableKey2(schema, table) {
|
|
554
735
|
return `${schema}.${table}`;
|
|
555
736
|
}
|
|
556
737
|
function isSafeIdentifier(value) {
|
|
557
738
|
return /^[A-Za-z_][A-Za-z0-9_]*$/.test(value);
|
|
558
739
|
}
|
|
559
|
-
function
|
|
740
|
+
function asTableType3(value) {
|
|
560
741
|
const normalized = value.toLowerCase();
|
|
561
742
|
if (normalized.includes("view")) {
|
|
562
743
|
return normalized.includes("materialized") ? "materialized_view" : "view";
|
|
@@ -1024,6 +1205,9 @@ var QueryEngine = class {
|
|
|
1024
1205
|
const paramKey = tenantField;
|
|
1025
1206
|
params[paramKey] = tenantId;
|
|
1026
1207
|
tenantPredicate = `${tenantField} = {${tenantField}:${metadata.tenantFieldType ?? "String"}}`;
|
|
1208
|
+
} else if (metadata.dialect === "bigquery") {
|
|
1209
|
+
params[tenantField] = tenantId;
|
|
1210
|
+
tenantPredicate = `${tenantField} = @${tenantField}`;
|
|
1027
1211
|
} else {
|
|
1028
1212
|
const escapedId = tenantId.replace(/'/g, "''");
|
|
1029
1213
|
tenantPredicate = `${tenantField} = '${escapedId}'`;
|
|
@@ -1604,7 +1788,7 @@ async function ask(client, queryEngine, question, options, signal) {
|
|
|
1604
1788
|
const dbName = queryResponse.data.database ?? options.database ?? queryEngine.getDefaultDatabase();
|
|
1605
1789
|
if (!dbName) {
|
|
1606
1790
|
throw new Error(
|
|
1607
|
-
"No database attached. Call attachPostgres
|
|
1791
|
+
"No database attached. Call attachPostgres, attachClickhouse, or attachBigQuery first."
|
|
1608
1792
|
);
|
|
1609
1793
|
}
|
|
1610
1794
|
const paramMetadata = Array.isArray(queryResponse.data.params) ? queryResponse.data.params : [];
|
|
@@ -2145,20 +2329,22 @@ function resolveTenantId8(client, tenantId) {
|
|
|
2145
2329
|
var QueryPanelSdkAPI = class _QueryPanelSdkAPI {
|
|
2146
2330
|
client;
|
|
2147
2331
|
queryEngine;
|
|
2148
|
-
|
|
2332
|
+
/**
|
|
2333
|
+
* @param workspaceId - Workspace UUID (same value as your org in the dashboard). Passed to the API as JWT claim `organizationId`.
|
|
2334
|
+
*/
|
|
2335
|
+
constructor(baseUrl, privateKey, workspaceId, options) {
|
|
2149
2336
|
if (options?.api) {
|
|
2150
2337
|
this.client = options.api;
|
|
2151
2338
|
} else {
|
|
2152
2339
|
if (!baseUrl) throw new Error("Base URL is required");
|
|
2153
2340
|
if (!privateKey) throw new Error("Private key is required");
|
|
2154
|
-
if (!
|
|
2155
|
-
this.client = new ApiClient(baseUrl, privateKey,
|
|
2341
|
+
if (!workspaceId) throw new Error("Workspace ID is required");
|
|
2342
|
+
this.client = new ApiClient(baseUrl, privateKey, workspaceId, {
|
|
2156
2343
|
defaultTenantId: options?.defaultTenantId,
|
|
2157
2344
|
additionalHeaders: options?.additionalHeaders,
|
|
2158
2345
|
fetch: options?.fetch
|
|
2159
2346
|
});
|
|
2160
2347
|
}
|
|
2161
|
-
if (!organizationId) throw new Error("Organization ID is required");
|
|
2162
2348
|
this.queryEngine = new QueryEngine();
|
|
2163
2349
|
}
|
|
2164
2350
|
/**
|
|
@@ -2167,7 +2353,7 @@ var QueryPanelSdkAPI = class _QueryPanelSdkAPI {
|
|
|
2167
2353
|
* are handled in-process (e.g. by invoking querypanel-sdk services directly)
|
|
2168
2354
|
* and do not cause recursion.
|
|
2169
2355
|
*
|
|
2170
|
-
* @param
|
|
2356
|
+
* @param workspaceId - Workspace identifier (sent as `organizationId` in JWTs and API auth)
|
|
2171
2357
|
* @param requestHandler - Callback invoked for each logical API request (method, path, body, tenantId, etc.)
|
|
2172
2358
|
* @param options - Optional defaultTenantId
|
|
2173
2359
|
* @returns QueryPanelSdkAPI instance; attach databases and call ask(), syncSchema(), etc. as usual
|
|
@@ -2175,7 +2361,7 @@ var QueryPanelSdkAPI = class _QueryPanelSdkAPI {
|
|
|
2175
2361
|
* @example
|
|
2176
2362
|
* ```ts
|
|
2177
2363
|
* const qp = QueryPanelSdkAPI.withCallbacks(
|
|
2178
|
-
* process.env.
|
|
2364
|
+
* process.env.QUERYPANEL_WORKSPACE_ID!,
|
|
2179
2365
|
* async (opts) => {
|
|
2180
2366
|
* // Call your in-process services (e.g. querypanel-sdk logic) instead of HTTP
|
|
2181
2367
|
* if (opts.path === '/query' || opts.path === '/v2/query') {
|
|
@@ -2192,11 +2378,11 @@ var QueryPanelSdkAPI = class _QueryPanelSdkAPI {
|
|
|
2192
2378
|
* const result = await qp.ask('Top 10 orders', { tenantId: 't1', database: 'db' });
|
|
2193
2379
|
* ```
|
|
2194
2380
|
*/
|
|
2195
|
-
static withCallbacks(
|
|
2381
|
+
static withCallbacks(workspaceId, requestHandler, options) {
|
|
2196
2382
|
const api = new CallbackApiClient(requestHandler, {
|
|
2197
2383
|
defaultTenantId: options?.defaultTenantId
|
|
2198
2384
|
});
|
|
2199
|
-
return new _QueryPanelSdkAPI("", "",
|
|
2385
|
+
return new _QueryPanelSdkAPI("", "", workspaceId, { api });
|
|
2200
2386
|
}
|
|
2201
2387
|
// Database attachment methods
|
|
2202
2388
|
attachClickhouse(name, clientFn, options) {
|
|
@@ -2225,6 +2411,19 @@ var QueryPanelSdkAPI = class _QueryPanelSdkAPI {
|
|
|
2225
2411
|
};
|
|
2226
2412
|
this.queryEngine.attachDatabase(name, adapter, metadata);
|
|
2227
2413
|
}
|
|
2414
|
+
attachBigQuery(name, clientFn, options) {
|
|
2415
|
+
const adapter = new BigQueryAdapter(clientFn, options);
|
|
2416
|
+
const metadata = {
|
|
2417
|
+
name,
|
|
2418
|
+
dialect: "bigquery",
|
|
2419
|
+
description: options?.description,
|
|
2420
|
+
tags: options?.tags,
|
|
2421
|
+
tenantFieldName: options?.tenantFieldName,
|
|
2422
|
+
tenantFieldType: options?.tenantFieldType ?? "String",
|
|
2423
|
+
enforceTenantIsolation: options?.tenantFieldName ? options?.enforceTenantIsolation ?? true : void 0
|
|
2424
|
+
};
|
|
2425
|
+
this.queryEngine.attachDatabase(name, adapter, metadata);
|
|
2426
|
+
}
|
|
2228
2427
|
attachDatabase(name, adapter) {
|
|
2229
2428
|
const metadata = {
|
|
2230
2429
|
name,
|
|
@@ -3086,6 +3285,7 @@ var QueryPanelSdkAPI = class _QueryPanelSdkAPI {
|
|
|
3086
3285
|
};
|
|
3087
3286
|
// Annotate the CommonJS export names for ESM import in node:
|
|
3088
3287
|
0 && (module.exports = {
|
|
3288
|
+
BigQueryAdapter,
|
|
3089
3289
|
CallbackApiClient,
|
|
3090
3290
|
ClickHouseAdapter,
|
|
3091
3291
|
PostgresAdapter,
|