@querypanel/node-sdk 1.0.55 → 1.0.57
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 +9 -1
- package/dist/index.cjs +690 -35
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +459 -10
- package/dist/index.d.ts +459 -10
- package/dist/index.js +688 -35
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -326,29 +326,31 @@ var PostgresAdapter = class {
|
|
|
326
326
|
}
|
|
327
327
|
}
|
|
328
328
|
/**
|
|
329
|
-
* Convert
|
|
330
|
-
* PostgreSQL expects $1, $2, $3 in SQL and an array of values [val1, val2, val3]
|
|
329
|
+
* Convert params to positional array for PostgreSQL.
|
|
330
|
+
* PostgreSQL expects $1, $2, $3 in SQL and an array of values [val1, val2, val3].
|
|
331
|
+
* When the record has numeric keys ("1", "2", "3") — e.g. from mapGeneratedParams —
|
|
332
|
+
* use only those in order so placeholder order matches SQL. Otherwise fall back to
|
|
333
|
+
* named keys in alphabetical order for backward compatibility.
|
|
331
334
|
*/
|
|
332
335
|
convertNamedToPositionalParams(params) {
|
|
333
336
|
const numericKeys = Object.keys(params).filter((k) => /^\d+$/.test(k)).map((k) => Number.parseInt(k, 10)).sort((a, b) => a - b);
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
337
|
+
if (numericKeys.length > 0) {
|
|
338
|
+
const positionalParams = [];
|
|
339
|
+
for (const key of numericKeys) {
|
|
340
|
+
let val = params[String(key)];
|
|
341
|
+
if (typeof val === "string") {
|
|
342
|
+
const match = val.match(/^<([a-zA-Z0-9_]+)>$/);
|
|
343
|
+
const namedKey = match?.[1];
|
|
344
|
+
if (namedKey && namedKey in params) {
|
|
345
|
+
val = params[namedKey];
|
|
346
|
+
}
|
|
343
347
|
}
|
|
348
|
+
positionalParams.push(val);
|
|
344
349
|
}
|
|
345
|
-
positionalParams
|
|
346
|
-
}
|
|
347
|
-
for (const key of namedKeys) {
|
|
348
|
-
const val = params[key];
|
|
349
|
-
positionalParams.push(val);
|
|
350
|
+
return positionalParams;
|
|
350
351
|
}
|
|
351
|
-
|
|
352
|
+
const namedKeys = Object.keys(params).filter((k) => !/^\d+$/.test(k)).sort();
|
|
353
|
+
return namedKeys.map((key) => params[key]);
|
|
352
354
|
}
|
|
353
355
|
async validate(sql, params) {
|
|
354
356
|
let paramArray;
|
|
@@ -560,6 +562,13 @@ var ApiClient = class {
|
|
|
560
562
|
getDefaultTenantId() {
|
|
561
563
|
return this.defaultTenantId;
|
|
562
564
|
}
|
|
565
|
+
/**
|
|
566
|
+
* Create a JWT for the given tenant (and optional userId, scopes).
|
|
567
|
+
* Use this when you need to pass a token to the embed (e.g. frontend or demo).
|
|
568
|
+
*/
|
|
569
|
+
async createJwt(tenantId, userId, scopes) {
|
|
570
|
+
return await this.generateJWT(tenantId, userId, scopes);
|
|
571
|
+
}
|
|
563
572
|
async get(path, tenantId, userId, scopes, signal, sessionId) {
|
|
564
573
|
return await this.request(path, {
|
|
565
574
|
method: "GET",
|
|
@@ -772,6 +781,111 @@ var ApiClient = class {
|
|
|
772
781
|
}
|
|
773
782
|
};
|
|
774
783
|
|
|
784
|
+
// src/core/callback-client.ts
|
|
785
|
+
var CallbackApiClient = class {
|
|
786
|
+
requestHandler;
|
|
787
|
+
defaultTenantId;
|
|
788
|
+
constructor(requestHandler, options) {
|
|
789
|
+
this.requestHandler = requestHandler;
|
|
790
|
+
this.defaultTenantId = options?.defaultTenantId;
|
|
791
|
+
}
|
|
792
|
+
getDefaultTenantId() {
|
|
793
|
+
return this.defaultTenantId;
|
|
794
|
+
}
|
|
795
|
+
async request(opts) {
|
|
796
|
+
const { data, headers } = await this.requestHandler({
|
|
797
|
+
method: opts.method,
|
|
798
|
+
path: opts.path,
|
|
799
|
+
body: opts.body,
|
|
800
|
+
tenantId: opts.tenantId,
|
|
801
|
+
userId: opts.userId,
|
|
802
|
+
scopes: opts.scopes,
|
|
803
|
+
signal: opts.signal,
|
|
804
|
+
sessionId: opts.sessionId
|
|
805
|
+
});
|
|
806
|
+
return { data, headers };
|
|
807
|
+
}
|
|
808
|
+
async get(path, tenantId, userId, scopes, signal, sessionId) {
|
|
809
|
+
const { data } = await this.request({
|
|
810
|
+
method: "GET",
|
|
811
|
+
path,
|
|
812
|
+
tenantId,
|
|
813
|
+
userId,
|
|
814
|
+
scopes,
|
|
815
|
+
signal,
|
|
816
|
+
sessionId
|
|
817
|
+
});
|
|
818
|
+
return data;
|
|
819
|
+
}
|
|
820
|
+
async post(path, body, tenantId, userId, scopes, signal, sessionId) {
|
|
821
|
+
const { data } = await this.request({
|
|
822
|
+
method: "POST",
|
|
823
|
+
path,
|
|
824
|
+
body,
|
|
825
|
+
tenantId,
|
|
826
|
+
userId,
|
|
827
|
+
scopes,
|
|
828
|
+
signal,
|
|
829
|
+
sessionId
|
|
830
|
+
});
|
|
831
|
+
return data;
|
|
832
|
+
}
|
|
833
|
+
async postWithHeaders(path, body, tenantId, userId, scopes, signal, sessionId) {
|
|
834
|
+
const { data, headers } = await this.request({
|
|
835
|
+
method: "POST",
|
|
836
|
+
path,
|
|
837
|
+
body,
|
|
838
|
+
tenantId,
|
|
839
|
+
userId,
|
|
840
|
+
scopes,
|
|
841
|
+
signal,
|
|
842
|
+
sessionId
|
|
843
|
+
});
|
|
844
|
+
return {
|
|
845
|
+
data,
|
|
846
|
+
headers: headers ?? new Headers()
|
|
847
|
+
};
|
|
848
|
+
}
|
|
849
|
+
async put(path, body, tenantId, userId, scopes, signal, sessionId) {
|
|
850
|
+
const { data } = await this.request({
|
|
851
|
+
method: "PUT",
|
|
852
|
+
path,
|
|
853
|
+
body,
|
|
854
|
+
tenantId,
|
|
855
|
+
userId,
|
|
856
|
+
scopes,
|
|
857
|
+
signal,
|
|
858
|
+
sessionId
|
|
859
|
+
});
|
|
860
|
+
return data;
|
|
861
|
+
}
|
|
862
|
+
async patch(path, body, tenantId, userId, scopes, signal, sessionId) {
|
|
863
|
+
const { data } = await this.request({
|
|
864
|
+
method: "PATCH",
|
|
865
|
+
path,
|
|
866
|
+
body,
|
|
867
|
+
tenantId,
|
|
868
|
+
userId,
|
|
869
|
+
scopes,
|
|
870
|
+
signal,
|
|
871
|
+
sessionId
|
|
872
|
+
});
|
|
873
|
+
return data;
|
|
874
|
+
}
|
|
875
|
+
async delete(path, tenantId, userId, scopes, signal, sessionId) {
|
|
876
|
+
const { data } = await this.request({
|
|
877
|
+
method: "DELETE",
|
|
878
|
+
path,
|
|
879
|
+
tenantId,
|
|
880
|
+
userId,
|
|
881
|
+
scopes,
|
|
882
|
+
signal,
|
|
883
|
+
sessionId
|
|
884
|
+
});
|
|
885
|
+
return data;
|
|
886
|
+
}
|
|
887
|
+
};
|
|
888
|
+
|
|
775
889
|
// src/core/query-engine.ts
|
|
776
890
|
var QueryEngine = class {
|
|
777
891
|
databases = /* @__PURE__ */ new Map();
|
|
@@ -834,6 +948,12 @@ var QueryEngine = class {
|
|
|
834
948
|
return [];
|
|
835
949
|
}
|
|
836
950
|
}
|
|
951
|
+
/**
|
|
952
|
+
* Maps API params (array in SQL placeholder order $1, $2, $3) to ParamRecord.
|
|
953
|
+
* Sets both numeric keys ("1", "2", "3") and named keys so that:
|
|
954
|
+
* - Postgres adapter uses numeric order for positional params (matches SQL).
|
|
955
|
+
* - Named keys remain for tenant isolation / other lookups.
|
|
956
|
+
*/
|
|
837
957
|
mapGeneratedParams(params) {
|
|
838
958
|
const record = {};
|
|
839
959
|
params.forEach((param, index) => {
|
|
@@ -841,6 +961,7 @@ var QueryEngine = class {
|
|
|
841
961
|
if (value === void 0) {
|
|
842
962
|
return;
|
|
843
963
|
}
|
|
964
|
+
record[String(index + 1)] = value;
|
|
844
965
|
const nameCandidate = typeof param.name === "string" && param.name.trim() || typeof param.placeholder === "string" && param.placeholder.trim() || typeof param.position === "number" && String(param.position) || String(index + 1);
|
|
845
966
|
const key = nameCandidate.replace(/[{}:$]/g, "").trim();
|
|
846
967
|
record[key] = value;
|
|
@@ -1169,10 +1290,161 @@ function resolveTenantId2(client, tenantId) {
|
|
|
1169
1290
|
return resolved;
|
|
1170
1291
|
}
|
|
1171
1292
|
|
|
1293
|
+
// src/routes/dashboards.ts
|
|
1294
|
+
function resolveTenantId3(client, tenantId) {
|
|
1295
|
+
const resolved = tenantId ?? client.getDefaultTenantId();
|
|
1296
|
+
if (resolved === void 0) {
|
|
1297
|
+
throw new Error("tenantId is required (option or client default)");
|
|
1298
|
+
}
|
|
1299
|
+
return resolved;
|
|
1300
|
+
}
|
|
1301
|
+
async function createDashboard(client, body, options, signal) {
|
|
1302
|
+
const tenantId = resolveTenantId3(client, options?.tenantId);
|
|
1303
|
+
return await client.post(
|
|
1304
|
+
"/dashboards",
|
|
1305
|
+
body,
|
|
1306
|
+
tenantId,
|
|
1307
|
+
options?.userId,
|
|
1308
|
+
options?.scopes,
|
|
1309
|
+
signal
|
|
1310
|
+
);
|
|
1311
|
+
}
|
|
1312
|
+
async function listDashboards(client, options, signal) {
|
|
1313
|
+
const tenantId = resolveTenantId3(client, options?.tenantId);
|
|
1314
|
+
const params = new URLSearchParams();
|
|
1315
|
+
if (options?.pagination?.page) {
|
|
1316
|
+
params.set("page", options.pagination.page.toString());
|
|
1317
|
+
}
|
|
1318
|
+
if (options?.pagination?.limit) {
|
|
1319
|
+
params.set("limit", options.pagination.limit.toString());
|
|
1320
|
+
}
|
|
1321
|
+
if (options?.status) {
|
|
1322
|
+
params.set("status", options.status);
|
|
1323
|
+
}
|
|
1324
|
+
if (options?.sortBy) {
|
|
1325
|
+
params.set("sort_by", options.sortBy);
|
|
1326
|
+
}
|
|
1327
|
+
if (options?.sortDir) {
|
|
1328
|
+
params.set("sort_dir", options.sortDir);
|
|
1329
|
+
}
|
|
1330
|
+
return await client.get(
|
|
1331
|
+
`/dashboards?${params.toString()}`,
|
|
1332
|
+
tenantId,
|
|
1333
|
+
options?.userId,
|
|
1334
|
+
options?.scopes,
|
|
1335
|
+
signal
|
|
1336
|
+
);
|
|
1337
|
+
}
|
|
1338
|
+
async function getDashboard(client, id, options, signal) {
|
|
1339
|
+
const tenantId = resolveTenantId3(client, options?.tenantId);
|
|
1340
|
+
return await client.get(
|
|
1341
|
+
`/dashboards/${id}`,
|
|
1342
|
+
tenantId,
|
|
1343
|
+
options?.userId,
|
|
1344
|
+
options?.scopes,
|
|
1345
|
+
signal
|
|
1346
|
+
);
|
|
1347
|
+
}
|
|
1348
|
+
async function getDashboardForTenant(client, id, tenantId, options, signal) {
|
|
1349
|
+
const params = new URLSearchParams();
|
|
1350
|
+
params.set("tenant_id", tenantId);
|
|
1351
|
+
return await client.get(
|
|
1352
|
+
`/dashboards/${id}/for-tenant?${params.toString()}`,
|
|
1353
|
+
tenantId,
|
|
1354
|
+
options?.userId,
|
|
1355
|
+
options?.scopes,
|
|
1356
|
+
signal
|
|
1357
|
+
);
|
|
1358
|
+
}
|
|
1359
|
+
async function updateDashboard(client, id, body, options, signal) {
|
|
1360
|
+
const tenantId = resolveTenantId3(client, options?.tenantId);
|
|
1361
|
+
return await client.put(
|
|
1362
|
+
`/dashboards/${id}`,
|
|
1363
|
+
body,
|
|
1364
|
+
tenantId,
|
|
1365
|
+
options?.userId,
|
|
1366
|
+
options?.scopes,
|
|
1367
|
+
signal
|
|
1368
|
+
);
|
|
1369
|
+
}
|
|
1370
|
+
async function updateDashboardStatus(client, id, status, options, signal) {
|
|
1371
|
+
const tenantId = resolveTenantId3(client, options?.tenantId);
|
|
1372
|
+
return await client.patch(
|
|
1373
|
+
`/dashboards/${id}/status`,
|
|
1374
|
+
{ status },
|
|
1375
|
+
tenantId,
|
|
1376
|
+
options?.userId,
|
|
1377
|
+
options?.scopes,
|
|
1378
|
+
signal
|
|
1379
|
+
);
|
|
1380
|
+
}
|
|
1381
|
+
async function deleteDashboard(client, id, options, signal) {
|
|
1382
|
+
const tenantId = resolveTenantId3(client, options?.tenantId);
|
|
1383
|
+
await client.delete(
|
|
1384
|
+
`/dashboards/${id}`,
|
|
1385
|
+
tenantId,
|
|
1386
|
+
options?.userId,
|
|
1387
|
+
options?.scopes,
|
|
1388
|
+
signal
|
|
1389
|
+
);
|
|
1390
|
+
}
|
|
1391
|
+
async function forkDashboard(client, id, input, options, signal) {
|
|
1392
|
+
const tenantId = resolveTenantId3(client, options?.tenantId);
|
|
1393
|
+
return await client.post(
|
|
1394
|
+
`/dashboards/${id}/fork`,
|
|
1395
|
+
input,
|
|
1396
|
+
tenantId,
|
|
1397
|
+
options?.userId,
|
|
1398
|
+
options?.scopes,
|
|
1399
|
+
signal
|
|
1400
|
+
);
|
|
1401
|
+
}
|
|
1402
|
+
async function updateFork(client, forkId, input, options, signal) {
|
|
1403
|
+
const tenantId = resolveTenantId3(client, options?.tenantId);
|
|
1404
|
+
return await client.put(
|
|
1405
|
+
`/dashboards/forks/${forkId}`,
|
|
1406
|
+
input,
|
|
1407
|
+
tenantId,
|
|
1408
|
+
options?.userId,
|
|
1409
|
+
options?.scopes,
|
|
1410
|
+
signal
|
|
1411
|
+
);
|
|
1412
|
+
}
|
|
1413
|
+
async function rollbackFork(client, forkId, tenantId, options, signal) {
|
|
1414
|
+
return await client.post(
|
|
1415
|
+
`/dashboards/forks/${forkId}/rollback`,
|
|
1416
|
+
{ tenant_id: tenantId },
|
|
1417
|
+
tenantId,
|
|
1418
|
+
options?.userId,
|
|
1419
|
+
options?.scopes,
|
|
1420
|
+
signal
|
|
1421
|
+
);
|
|
1422
|
+
}
|
|
1423
|
+
async function deleteFork(client, forkId, tenantId, options, signal) {
|
|
1424
|
+
const params = new URLSearchParams();
|
|
1425
|
+
params.set("tenant_id", tenantId);
|
|
1426
|
+
await client.delete(
|
|
1427
|
+
`/dashboards/forks/${forkId}?${params.toString()}`,
|
|
1428
|
+
tenantId,
|
|
1429
|
+
options?.userId,
|
|
1430
|
+
options?.scopes,
|
|
1431
|
+
signal
|
|
1432
|
+
);
|
|
1433
|
+
}
|
|
1434
|
+
async function listForksForTenant(client, tenantId, options, signal) {
|
|
1435
|
+
return await client.get(
|
|
1436
|
+
`/dashboards/customer/${tenantId}`,
|
|
1437
|
+
tenantId,
|
|
1438
|
+
options?.userId,
|
|
1439
|
+
options?.scopes,
|
|
1440
|
+
signal
|
|
1441
|
+
);
|
|
1442
|
+
}
|
|
1443
|
+
|
|
1172
1444
|
// src/routes/ingest.ts
|
|
1173
1445
|
import crypto2 from "crypto";
|
|
1174
1446
|
async function syncSchema(client, queryEngine, databaseName, options, signal) {
|
|
1175
|
-
const tenantId =
|
|
1447
|
+
const tenantId = resolveTenantId4(client, options.tenantId);
|
|
1176
1448
|
const adapter = queryEngine.getDatabase(databaseName);
|
|
1177
1449
|
const metadata = queryEngine.getDatabaseMetadata(databaseName);
|
|
1178
1450
|
const introspection = await adapter.introspect(
|
|
@@ -1194,7 +1466,7 @@ async function syncSchema(client, queryEngine, databaseName, options, signal) {
|
|
|
1194
1466
|
);
|
|
1195
1467
|
return response;
|
|
1196
1468
|
}
|
|
1197
|
-
function
|
|
1469
|
+
function resolveTenantId4(client, tenantId) {
|
|
1198
1470
|
const resolved = tenantId ?? client.getDefaultTenantId();
|
|
1199
1471
|
if (!resolved) {
|
|
1200
1472
|
throw new Error(
|
|
@@ -1236,7 +1508,7 @@ import crypto4 from "crypto";
|
|
|
1236
1508
|
// src/routes/query.ts
|
|
1237
1509
|
import crypto3 from "crypto";
|
|
1238
1510
|
async function ask(client, queryEngine, question, options, signal) {
|
|
1239
|
-
const tenantId =
|
|
1511
|
+
const tenantId = resolveTenantId5(client, options.tenantId);
|
|
1240
1512
|
const sessionId = crypto3.randomUUID();
|
|
1241
1513
|
const querypanelSessionId = options.querypanelSessionId ?? sessionId;
|
|
1242
1514
|
const maxRetry = options.maxRetry ?? 0;
|
|
@@ -1391,7 +1663,7 @@ async function ask(client, queryEngine, question, options, signal) {
|
|
|
1391
1663
|
}
|
|
1392
1664
|
throw new Error("Unexpected error in ask retry loop");
|
|
1393
1665
|
}
|
|
1394
|
-
function
|
|
1666
|
+
function resolveTenantId5(client, tenantId) {
|
|
1395
1667
|
const resolved = tenantId ?? client.getDefaultTenantId();
|
|
1396
1668
|
if (!resolved) {
|
|
1397
1669
|
throw new Error(
|
|
@@ -1414,7 +1686,7 @@ function anonymizeResults(rows) {
|
|
|
1414
1686
|
}
|
|
1415
1687
|
|
|
1416
1688
|
// src/routes/modify.ts
|
|
1417
|
-
function buildModifiedQuestion(originalQuestion, modifications, pipeline) {
|
|
1689
|
+
function buildModifiedQuestion(originalQuestion, modifications, pipeline, hasSessionContext = false) {
|
|
1418
1690
|
const hints = [];
|
|
1419
1691
|
if (modifications.timeGranularity) {
|
|
1420
1692
|
hints.push(`group results by ${modifications.timeGranularity}`);
|
|
@@ -1449,7 +1721,7 @@ function buildModifiedQuestion(originalQuestion, modifications, pipeline) {
|
|
|
1449
1721
|
if (hints.length === 0) {
|
|
1450
1722
|
return originalQuestion;
|
|
1451
1723
|
}
|
|
1452
|
-
if (pipeline === "v2") {
|
|
1724
|
+
if (pipeline === "v2" && hasSessionContext) {
|
|
1453
1725
|
return hints.join(", ");
|
|
1454
1726
|
}
|
|
1455
1727
|
return `${originalQuestion} (${hints.join(", ")})`;
|
|
@@ -1547,7 +1819,7 @@ function stripVizSpecOnlyHints(hints) {
|
|
|
1547
1819
|
const { kind: _kind, ...rest } = hints;
|
|
1548
1820
|
return rest;
|
|
1549
1821
|
}
|
|
1550
|
-
function
|
|
1822
|
+
function resolveTenantId6(client, tenantId) {
|
|
1551
1823
|
const resolved = tenantId ?? client.getDefaultTenantId();
|
|
1552
1824
|
if (!resolved) {
|
|
1553
1825
|
throw new Error(
|
|
@@ -1557,9 +1829,10 @@ function resolveTenantId5(client, tenantId) {
|
|
|
1557
1829
|
return resolved;
|
|
1558
1830
|
}
|
|
1559
1831
|
async function modifyChart(client, queryEngine, input, options, signal) {
|
|
1560
|
-
const tenantId =
|
|
1832
|
+
const tenantId = resolveTenantId6(client, options?.tenantId);
|
|
1561
1833
|
const sessionId = crypto4.randomUUID();
|
|
1562
1834
|
const querypanelSessionId = options?.querypanelSessionId ?? sessionId;
|
|
1835
|
+
const hasQuerypanelSessionContext = !!options?.querypanelSessionId;
|
|
1563
1836
|
const chartType = options?.chartType ?? "vega-lite";
|
|
1564
1837
|
const hasSqlMods = !!input.sqlModifications;
|
|
1565
1838
|
const hasVizMods = !!input.vizModifications;
|
|
@@ -1596,7 +1869,8 @@ async function modifyChart(client, queryEngine, input, options, signal) {
|
|
|
1596
1869
|
const modifiedQuestion = buildModifiedQuestion(
|
|
1597
1870
|
input.question,
|
|
1598
1871
|
input.sqlModifications,
|
|
1599
|
-
options?.pipeline
|
|
1872
|
+
options?.pipeline,
|
|
1873
|
+
hasQuerypanelSessionContext
|
|
1600
1874
|
);
|
|
1601
1875
|
if (options?.pipeline === "v2") {
|
|
1602
1876
|
finalQuestion = modifiedQuestion;
|
|
@@ -1723,7 +1997,7 @@ async function modifyChart(client, queryEngine, input, options, signal) {
|
|
|
1723
1997
|
|
|
1724
1998
|
// src/routes/sessions.ts
|
|
1725
1999
|
async function listSessions(client, options, signal) {
|
|
1726
|
-
const tenantId =
|
|
2000
|
+
const tenantId = resolveTenantId7(client, options?.tenantId);
|
|
1727
2001
|
const params = new URLSearchParams();
|
|
1728
2002
|
if (options?.pagination?.page)
|
|
1729
2003
|
params.set("page", `${options.pagination.page}`);
|
|
@@ -1746,7 +2020,7 @@ async function listSessions(client, options, signal) {
|
|
|
1746
2020
|
);
|
|
1747
2021
|
}
|
|
1748
2022
|
async function getSession(client, sessionId, options, signal) {
|
|
1749
|
-
const tenantId =
|
|
2023
|
+
const tenantId = resolveTenantId7(client, options?.tenantId);
|
|
1750
2024
|
const params = new URLSearchParams();
|
|
1751
2025
|
if (options?.includeTurns !== void 0) {
|
|
1752
2026
|
params.set("include_turns", `${options.includeTurns}`);
|
|
@@ -1760,7 +2034,7 @@ async function getSession(client, sessionId, options, signal) {
|
|
|
1760
2034
|
);
|
|
1761
2035
|
}
|
|
1762
2036
|
async function updateSession(client, sessionId, body, options, signal) {
|
|
1763
|
-
const tenantId =
|
|
2037
|
+
const tenantId = resolveTenantId7(client, options?.tenantId);
|
|
1764
2038
|
return await client.patch(
|
|
1765
2039
|
`/sessions/${encodeURIComponent(sessionId)}`,
|
|
1766
2040
|
body,
|
|
@@ -1771,7 +2045,7 @@ async function updateSession(client, sessionId, body, options, signal) {
|
|
|
1771
2045
|
);
|
|
1772
2046
|
}
|
|
1773
2047
|
async function deleteSession(client, sessionId, options, signal) {
|
|
1774
|
-
const tenantId =
|
|
2048
|
+
const tenantId = resolveTenantId7(client, options?.tenantId);
|
|
1775
2049
|
await client.delete(
|
|
1776
2050
|
`/sessions/${encodeURIComponent(sessionId)}`,
|
|
1777
2051
|
tenantId,
|
|
@@ -1780,7 +2054,7 @@ async function deleteSession(client, sessionId, options, signal) {
|
|
|
1780
2054
|
signal
|
|
1781
2055
|
);
|
|
1782
2056
|
}
|
|
1783
|
-
function
|
|
2057
|
+
function resolveTenantId7(client, tenantId) {
|
|
1784
2058
|
const resolved = tenantId ?? client.getDefaultTenantId();
|
|
1785
2059
|
if (!resolved) {
|
|
1786
2060
|
throw new Error(
|
|
@@ -1793,7 +2067,7 @@ function resolveTenantId6(client, tenantId) {
|
|
|
1793
2067
|
// src/routes/vizspec.ts
|
|
1794
2068
|
import crypto5 from "crypto";
|
|
1795
2069
|
async function generateVizSpec(client, input, options, signal) {
|
|
1796
|
-
const tenantId =
|
|
2070
|
+
const tenantId = resolveTenantId8(client, options?.tenantId);
|
|
1797
2071
|
const sessionId = crypto5.randomUUID();
|
|
1798
2072
|
const response = await client.post(
|
|
1799
2073
|
"/vizspec",
|
|
@@ -1815,7 +2089,7 @@ async function generateVizSpec(client, input, options, signal) {
|
|
|
1815
2089
|
);
|
|
1816
2090
|
return response;
|
|
1817
2091
|
}
|
|
1818
|
-
function
|
|
2092
|
+
function resolveTenantId8(client, tenantId) {
|
|
1819
2093
|
const resolved = tenantId ?? client.getDefaultTenantId();
|
|
1820
2094
|
if (!resolved) {
|
|
1821
2095
|
throw new Error(
|
|
@@ -1826,13 +2100,62 @@ function resolveTenantId7(client, tenantId) {
|
|
|
1826
2100
|
}
|
|
1827
2101
|
|
|
1828
2102
|
// src/index.ts
|
|
1829
|
-
var QueryPanelSdkAPI = class {
|
|
2103
|
+
var QueryPanelSdkAPI = class _QueryPanelSdkAPI {
|
|
1830
2104
|
client;
|
|
1831
2105
|
queryEngine;
|
|
1832
2106
|
constructor(baseUrl, privateKey, organizationId, options) {
|
|
1833
|
-
|
|
2107
|
+
if (options?.api) {
|
|
2108
|
+
this.client = options.api;
|
|
2109
|
+
} else {
|
|
2110
|
+
if (!baseUrl) throw new Error("Base URL is required");
|
|
2111
|
+
if (!privateKey) throw new Error("Private key is required");
|
|
2112
|
+
if (!organizationId) throw new Error("Organization ID is required");
|
|
2113
|
+
this.client = new ApiClient(baseUrl, privateKey, organizationId, {
|
|
2114
|
+
defaultTenantId: options?.defaultTenantId,
|
|
2115
|
+
additionalHeaders: options?.additionalHeaders,
|
|
2116
|
+
fetch: options?.fetch
|
|
2117
|
+
});
|
|
2118
|
+
}
|
|
2119
|
+
if (!organizationId) throw new Error("Organization ID is required");
|
|
1834
2120
|
this.queryEngine = new QueryEngine();
|
|
1835
2121
|
}
|
|
2122
|
+
/**
|
|
2123
|
+
* Create an SDK instance that uses a request callback instead of HTTP.
|
|
2124
|
+
* Use this when calling the SDK from within your own API so that "API" calls
|
|
2125
|
+
* are handled in-process (e.g. by invoking querypanel-sdk services directly)
|
|
2126
|
+
* and do not cause recursion.
|
|
2127
|
+
*
|
|
2128
|
+
* @param organizationId - Organization identifier
|
|
2129
|
+
* @param requestHandler - Callback invoked for each logical API request (method, path, body, tenantId, etc.)
|
|
2130
|
+
* @param options - Optional defaultTenantId
|
|
2131
|
+
* @returns QueryPanelSdkAPI instance; attach databases and call ask(), syncSchema(), etc. as usual
|
|
2132
|
+
*
|
|
2133
|
+
* @example
|
|
2134
|
+
* ```ts
|
|
2135
|
+
* const qp = QueryPanelSdkAPI.withCallbacks(
|
|
2136
|
+
* process.env.ORGANIZATION_ID!,
|
|
2137
|
+
* async (opts) => {
|
|
2138
|
+
* // Call your in-process services (e.g. querypanel-sdk logic) instead of HTTP
|
|
2139
|
+
* if (opts.path === '/query' || opts.path === '/v2/query') {
|
|
2140
|
+
* const result = await yourSqlGenerator.generate(opts.body, opts.tenantId);
|
|
2141
|
+
* return { data: result, headers: new Headers({ 'x-querypanel-session-id': result.sessionId }) };
|
|
2142
|
+
* }
|
|
2143
|
+
* if (opts.path === '/chart') return { data: await yourChartService.generate(opts.body) };
|
|
2144
|
+
* // ... other paths
|
|
2145
|
+
* return { data: await yourProxy(opts) };
|
|
2146
|
+
* },
|
|
2147
|
+
* { defaultTenantId: process.env.DEFAULT_TENANT_ID }
|
|
2148
|
+
* );
|
|
2149
|
+
* qp.attachPostgres('db', createClient, { tenantFieldName: 'tenant_id' });
|
|
2150
|
+
* const result = await qp.ask('Top 10 orders', { tenantId: 't1', database: 'db' });
|
|
2151
|
+
* ```
|
|
2152
|
+
*/
|
|
2153
|
+
static withCallbacks(organizationId, requestHandler, options) {
|
|
2154
|
+
const api = new CallbackApiClient(requestHandler, {
|
|
2155
|
+
defaultTenantId: options?.defaultTenantId
|
|
2156
|
+
});
|
|
2157
|
+
return new _QueryPanelSdkAPI("", "", organizationId, { api });
|
|
2158
|
+
}
|
|
1836
2159
|
// Database attachment methods
|
|
1837
2160
|
attachClickhouse(name, clientFn, options) {
|
|
1838
2161
|
const adapter = new ClickHouseAdapter(clientFn, options);
|
|
@@ -1946,6 +2269,48 @@ var QueryPanelSdkAPI = class {
|
|
|
1946
2269
|
signal
|
|
1947
2270
|
);
|
|
1948
2271
|
}
|
|
2272
|
+
// Embedded dashboard / JWT
|
|
2273
|
+
/**
|
|
2274
|
+
* Creates a JWT for the given tenant (and optional userId, scopes).
|
|
2275
|
+
* Use this when you need to pass a token to the embed (e.g. frontend or demo).
|
|
2276
|
+
* Only available when using the HTTP client (ApiClient); not available when using withCallbacks.
|
|
2277
|
+
*/
|
|
2278
|
+
async createJwt(options) {
|
|
2279
|
+
const tenantId = options.tenantId?.trim();
|
|
2280
|
+
if (!tenantId) {
|
|
2281
|
+
throw new Error("tenantId is required");
|
|
2282
|
+
}
|
|
2283
|
+
if (!(this.client instanceof ApiClient)) {
|
|
2284
|
+
throw new Error(
|
|
2285
|
+
"createJwt is not available when using withCallbacks. Use the SDK with baseUrl + privateKey to create JWTs."
|
|
2286
|
+
);
|
|
2287
|
+
}
|
|
2288
|
+
return await this.client.createJwt(
|
|
2289
|
+
tenantId,
|
|
2290
|
+
options.userId,
|
|
2291
|
+
options.scopes
|
|
2292
|
+
);
|
|
2293
|
+
}
|
|
2294
|
+
/**
|
|
2295
|
+
* Runs SQL against an attached database with tenant isolation applied.
|
|
2296
|
+
* Used by embed run-sql (e.g. dashboard chart execution). Requires a database
|
|
2297
|
+
* to be attached (e.g. via attachPostgres) and options.tenantId.
|
|
2298
|
+
*/
|
|
2299
|
+
async runSqlForDashboard(input, options) {
|
|
2300
|
+
const tenantId = options.tenantId?.trim();
|
|
2301
|
+
if (!tenantId) {
|
|
2302
|
+
throw new Error("tenantId is required");
|
|
2303
|
+
}
|
|
2304
|
+
const databaseName = input.database ?? "db";
|
|
2305
|
+
const params = input.params ?? {};
|
|
2306
|
+
const result = await this.queryEngine.validateAndExecute(
|
|
2307
|
+
input.sql,
|
|
2308
|
+
params,
|
|
2309
|
+
databaseName,
|
|
2310
|
+
tenantId
|
|
2311
|
+
);
|
|
2312
|
+
return { rows: result.rows, fields: result.fields };
|
|
2313
|
+
}
|
|
1949
2314
|
// VizSpec generation
|
|
1950
2315
|
/**
|
|
1951
2316
|
* Generates a VizSpec visualization specification from query results.
|
|
@@ -2389,8 +2754,296 @@ var QueryPanelSdkAPI = class {
|
|
|
2389
2754
|
async deleteActiveChart(id, options, signal) {
|
|
2390
2755
|
await deleteActiveChart(this.client, id, options, signal);
|
|
2391
2756
|
}
|
|
2757
|
+
// Dashboard CRUD operations
|
|
2758
|
+
/**
|
|
2759
|
+
* Creates a new dashboard with BlockNote content.
|
|
2760
|
+
*
|
|
2761
|
+
* @param body - Dashboard configuration including name and BlockNote content
|
|
2762
|
+
* @param options - Tenant, user, and scope options
|
|
2763
|
+
* @param signal - Optional AbortSignal for cancellation
|
|
2764
|
+
* @returns Created dashboard
|
|
2765
|
+
*
|
|
2766
|
+
* @example
|
|
2767
|
+
* ```typescript
|
|
2768
|
+
* const dashboard = await qp.createDashboard({
|
|
2769
|
+
* name: "Sales Dashboard",
|
|
2770
|
+
* description: "Monthly sales metrics",
|
|
2771
|
+
* content_json: blockNoteContent,
|
|
2772
|
+
* editor_type: "blocknote",
|
|
2773
|
+
* }, { tenantId: "tenant_123" });
|
|
2774
|
+
* ```
|
|
2775
|
+
*/
|
|
2776
|
+
async createDashboard(body, options, signal) {
|
|
2777
|
+
return await createDashboard(
|
|
2778
|
+
this.client,
|
|
2779
|
+
body,
|
|
2780
|
+
options,
|
|
2781
|
+
signal
|
|
2782
|
+
);
|
|
2783
|
+
}
|
|
2784
|
+
/**
|
|
2785
|
+
* Lists dashboards with pagination and filtering.
|
|
2786
|
+
*
|
|
2787
|
+
* @param options - Filtering and pagination options
|
|
2788
|
+
* @param signal - Optional AbortSignal for cancellation
|
|
2789
|
+
* @returns Paginated list of dashboards
|
|
2790
|
+
*
|
|
2791
|
+
* @example
|
|
2792
|
+
* ```typescript
|
|
2793
|
+
* const dashboards = await qp.listDashboards({
|
|
2794
|
+
* tenantId: "tenant_123",
|
|
2795
|
+
* status: "deployed",
|
|
2796
|
+
* pagination: { page: 1, limit: 10 },
|
|
2797
|
+
* });
|
|
2798
|
+
* ```
|
|
2799
|
+
*/
|
|
2800
|
+
async listDashboards(options, signal) {
|
|
2801
|
+
return await listDashboards(this.client, options, signal);
|
|
2802
|
+
}
|
|
2803
|
+
/**
|
|
2804
|
+
* Gets a dashboard by ID.
|
|
2805
|
+
*
|
|
2806
|
+
* @param id - Dashboard ID
|
|
2807
|
+
* @param options - Tenant, user, and scope options
|
|
2808
|
+
* @param signal - Optional AbortSignal for cancellation
|
|
2809
|
+
* @returns Dashboard details
|
|
2810
|
+
*
|
|
2811
|
+
* @example
|
|
2812
|
+
* ```typescript
|
|
2813
|
+
* const dashboard = await qp.getDashboard("dash_123", {
|
|
2814
|
+
* tenantId: "tenant_123",
|
|
2815
|
+
* });
|
|
2816
|
+
* ```
|
|
2817
|
+
*/
|
|
2818
|
+
async getDashboard(id, options, signal) {
|
|
2819
|
+
return await getDashboard(
|
|
2820
|
+
this.client,
|
|
2821
|
+
id,
|
|
2822
|
+
options,
|
|
2823
|
+
signal
|
|
2824
|
+
);
|
|
2825
|
+
}
|
|
2826
|
+
/**
|
|
2827
|
+
* Gets a dashboard for a specific tenant.
|
|
2828
|
+
* Returns customer fork if exists, otherwise returns the original dashboard.
|
|
2829
|
+
*
|
|
2830
|
+
* @param id - Dashboard ID
|
|
2831
|
+
* @param tenantId - Tenant ID to check for fork
|
|
2832
|
+
* @param options - Additional auth options
|
|
2833
|
+
* @param signal - Optional AbortSignal for cancellation
|
|
2834
|
+
* @returns Dashboard (fork if exists, original otherwise)
|
|
2835
|
+
*
|
|
2836
|
+
* @example
|
|
2837
|
+
* ```typescript
|
|
2838
|
+
* const dashboard = await qp.getDashboardForTenant(
|
|
2839
|
+
* "dash_123",
|
|
2840
|
+
* "tenant_456",
|
|
2841
|
+
* );
|
|
2842
|
+
* ```
|
|
2843
|
+
*/
|
|
2844
|
+
async getDashboardForTenant(id, tenantId, options, signal) {
|
|
2845
|
+
return await getDashboardForTenant(
|
|
2846
|
+
this.client,
|
|
2847
|
+
id,
|
|
2848
|
+
tenantId,
|
|
2849
|
+
options,
|
|
2850
|
+
signal
|
|
2851
|
+
);
|
|
2852
|
+
}
|
|
2853
|
+
/**
|
|
2854
|
+
* Updates a dashboard.
|
|
2855
|
+
*
|
|
2856
|
+
* @param id - Dashboard ID to update
|
|
2857
|
+
* @param body - Fields to update
|
|
2858
|
+
* @param options - Tenant, user, and scope options
|
|
2859
|
+
* @param signal - Optional AbortSignal for cancellation
|
|
2860
|
+
* @returns Updated dashboard
|
|
2861
|
+
*
|
|
2862
|
+
* @example
|
|
2863
|
+
* ```typescript
|
|
2864
|
+
* const updated = await qp.updateDashboard("dash_123", {
|
|
2865
|
+
* name: "Updated Dashboard",
|
|
2866
|
+
* content_json: newBlockNoteContent,
|
|
2867
|
+
* }, { tenantId: "tenant_123" });
|
|
2868
|
+
* ```
|
|
2869
|
+
*/
|
|
2870
|
+
async updateDashboard(id, body, options, signal) {
|
|
2871
|
+
return await updateDashboard(
|
|
2872
|
+
this.client,
|
|
2873
|
+
id,
|
|
2874
|
+
body,
|
|
2875
|
+
options,
|
|
2876
|
+
signal
|
|
2877
|
+
);
|
|
2878
|
+
}
|
|
2879
|
+
/**
|
|
2880
|
+
* Updates dashboard status (deploy/undeploy).
|
|
2881
|
+
*
|
|
2882
|
+
* @param id - Dashboard ID
|
|
2883
|
+
* @param status - New status
|
|
2884
|
+
* @param options - Tenant, user, and scope options
|
|
2885
|
+
* @param signal - Optional AbortSignal for cancellation
|
|
2886
|
+
* @returns Updated dashboard
|
|
2887
|
+
*
|
|
2888
|
+
* @example
|
|
2889
|
+
* ```typescript
|
|
2890
|
+
* const deployed = await qp.updateDashboardStatus(
|
|
2891
|
+
* "dash_123",
|
|
2892
|
+
* "deployed",
|
|
2893
|
+
* { tenantId: "tenant_123" },
|
|
2894
|
+
* );
|
|
2895
|
+
* ```
|
|
2896
|
+
*/
|
|
2897
|
+
async updateDashboardStatus(id, status, options, signal) {
|
|
2898
|
+
return await updateDashboardStatus(
|
|
2899
|
+
this.client,
|
|
2900
|
+
id,
|
|
2901
|
+
status,
|
|
2902
|
+
options,
|
|
2903
|
+
signal
|
|
2904
|
+
);
|
|
2905
|
+
}
|
|
2906
|
+
/**
|
|
2907
|
+
* Deletes a dashboard.
|
|
2908
|
+
*
|
|
2909
|
+
* @param id - Dashboard ID to delete
|
|
2910
|
+
* @param options - Tenant, user, and scope options
|
|
2911
|
+
* @param signal - Optional AbortSignal for cancellation
|
|
2912
|
+
*
|
|
2913
|
+
* @example
|
|
2914
|
+
* ```typescript
|
|
2915
|
+
* await qp.deleteDashboard("dash_123", { tenantId: "tenant_123" });
|
|
2916
|
+
* ```
|
|
2917
|
+
*/
|
|
2918
|
+
async deleteDashboard(id, options, signal) {
|
|
2919
|
+
await deleteDashboard(this.client, id, options, signal);
|
|
2920
|
+
}
|
|
2921
|
+
// Dashboard Fork operations (Customer Customization)
|
|
2922
|
+
/**
|
|
2923
|
+
* Forks a dashboard for customer customization (copy-on-write).
|
|
2924
|
+
*
|
|
2925
|
+
* Creates a full copy of the dashboard that the customer can edit independently.
|
|
2926
|
+
* The original dashboard remains unchanged.
|
|
2927
|
+
*
|
|
2928
|
+
* @param id - Dashboard ID to fork
|
|
2929
|
+
* @param input - Fork configuration with tenant_id
|
|
2930
|
+
* @param options - Additional auth options
|
|
2931
|
+
* @param signal - Optional AbortSignal for cancellation
|
|
2932
|
+
* @returns Forked dashboard
|
|
2933
|
+
*
|
|
2934
|
+
* @example
|
|
2935
|
+
* ```typescript
|
|
2936
|
+
* const fork = await qp.forkDashboard("dash_123", {
|
|
2937
|
+
* tenant_id: "tenant_456",
|
|
2938
|
+
* name: "Customer's Custom Dashboard",
|
|
2939
|
+
* });
|
|
2940
|
+
* ```
|
|
2941
|
+
*/
|
|
2942
|
+
async forkDashboard(id, input, options, signal) {
|
|
2943
|
+
return await forkDashboard(
|
|
2944
|
+
this.client,
|
|
2945
|
+
id,
|
|
2946
|
+
input,
|
|
2947
|
+
options,
|
|
2948
|
+
signal
|
|
2949
|
+
);
|
|
2950
|
+
}
|
|
2951
|
+
/**
|
|
2952
|
+
* Updates a customer fork.
|
|
2953
|
+
*
|
|
2954
|
+
* @param forkId - Fork ID to update
|
|
2955
|
+
* @param input - Update data including tenant_id
|
|
2956
|
+
* @param options - Additional auth options
|
|
2957
|
+
* @param signal - Optional AbortSignal for cancellation
|
|
2958
|
+
* @returns Updated fork
|
|
2959
|
+
*
|
|
2960
|
+
* @example
|
|
2961
|
+
* ```typescript
|
|
2962
|
+
* const updated = await qp.updateFork("fork_123", {
|
|
2963
|
+
* tenant_id: "tenant_456",
|
|
2964
|
+
* content_json: newBlockNoteContent,
|
|
2965
|
+
* });
|
|
2966
|
+
* ```
|
|
2967
|
+
*/
|
|
2968
|
+
async updateFork(forkId, input, options, signal) {
|
|
2969
|
+
return await updateFork(
|
|
2970
|
+
this.client,
|
|
2971
|
+
forkId,
|
|
2972
|
+
input,
|
|
2973
|
+
options,
|
|
2974
|
+
signal
|
|
2975
|
+
);
|
|
2976
|
+
}
|
|
2977
|
+
/**
|
|
2978
|
+
* Rollbacks a fork to the original dashboard.
|
|
2979
|
+
* This deletes the fork and the customer will see the original again.
|
|
2980
|
+
*
|
|
2981
|
+
* @param forkId - Fork ID to rollback
|
|
2982
|
+
* @param tenantId - Tenant ID
|
|
2983
|
+
* @param options - Additional auth options
|
|
2984
|
+
* @param signal - Optional AbortSignal for cancellation
|
|
2985
|
+
* @returns Original dashboard
|
|
2986
|
+
*
|
|
2987
|
+
* @example
|
|
2988
|
+
* ```typescript
|
|
2989
|
+
* const original = await qp.rollbackFork("fork_123", "tenant_456");
|
|
2990
|
+
* ```
|
|
2991
|
+
*/
|
|
2992
|
+
async rollbackFork(forkId, tenantId, options, signal) {
|
|
2993
|
+
return await rollbackFork(
|
|
2994
|
+
this.client,
|
|
2995
|
+
forkId,
|
|
2996
|
+
tenantId,
|
|
2997
|
+
options,
|
|
2998
|
+
signal
|
|
2999
|
+
);
|
|
3000
|
+
}
|
|
3001
|
+
/**
|
|
3002
|
+
* Deletes a customer fork.
|
|
3003
|
+
*
|
|
3004
|
+
* @param forkId - Fork ID to delete
|
|
3005
|
+
* @param tenantId - Tenant ID
|
|
3006
|
+
* @param options - Additional auth options
|
|
3007
|
+
* @param signal - Optional AbortSignal for cancellation
|
|
3008
|
+
*
|
|
3009
|
+
* @example
|
|
3010
|
+
* ```typescript
|
|
3011
|
+
* await qp.deleteFork("fork_123", "tenant_456");
|
|
3012
|
+
* ```
|
|
3013
|
+
*/
|
|
3014
|
+
async deleteFork(forkId, tenantId, options, signal) {
|
|
3015
|
+
await deleteFork(
|
|
3016
|
+
this.client,
|
|
3017
|
+
forkId,
|
|
3018
|
+
tenantId,
|
|
3019
|
+
options,
|
|
3020
|
+
signal
|
|
3021
|
+
);
|
|
3022
|
+
}
|
|
3023
|
+
/**
|
|
3024
|
+
* Lists all customer forks for a tenant.
|
|
3025
|
+
*
|
|
3026
|
+
* @param tenantId - Tenant ID
|
|
3027
|
+
* @param options - Additional auth options
|
|
3028
|
+
* @param signal - Optional AbortSignal for cancellation
|
|
3029
|
+
* @returns Array of forks
|
|
3030
|
+
*
|
|
3031
|
+
* @example
|
|
3032
|
+
* ```typescript
|
|
3033
|
+
* const forks = await qp.listForksForTenant("tenant_456");
|
|
3034
|
+
* ```
|
|
3035
|
+
*/
|
|
3036
|
+
async listForksForTenant(tenantId, options, signal) {
|
|
3037
|
+
return await listForksForTenant(
|
|
3038
|
+
this.client,
|
|
3039
|
+
tenantId,
|
|
3040
|
+
options,
|
|
3041
|
+
signal
|
|
3042
|
+
);
|
|
3043
|
+
}
|
|
2392
3044
|
};
|
|
2393
3045
|
export {
|
|
3046
|
+
CallbackApiClient,
|
|
2394
3047
|
ClickHouseAdapter,
|
|
2395
3048
|
PostgresAdapter,
|
|
2396
3049
|
QueryErrorCode,
|