@querypanel/node-sdk 1.3.0-beta.2 → 1.3.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 CHANGED
@@ -286,9 +286,37 @@ const { data, missingIds } = await qp.getChartsByIds(
286
286
  Saved charts now include the original `prompt` so you can show the question
287
287
  alongside chart history or reuse it in follow-up workflows.
288
288
 
289
+ ## Referencing Queries by `queryId` (recommended)
290
+
291
+ `ask()` and `modifyChart()` return a server-issued `queryId`. The server stores the generated SQL and bind params for that id, scoped to the authenticated workspace and tenant. **Pass the id instead of SQL** so your clients never send executable SQL or bind params:
292
+
293
+ ```ts
294
+ const response = await qp.ask("revenue by country", { tenantId, database: "analytics" });
295
+
296
+ // Modify: SQL and params are loaded from the server, client `sql` / `params` are ignored
297
+ const modified = await qp.modifyChart(
298
+ { queryId: response.queryId, question: "revenue by country", database: "analytics",
299
+ vizModifications: { chartType: "bar" } },
300
+ { tenantId },
301
+ );
302
+
303
+ // Run a stored query (e.g. in a dashboard)
304
+ await qp.runSqlForDashboard({ queryId: response.queryId, database: "analytics" }, { tenantId });
305
+
306
+ // Save a chart: the server stores the SQL and params it holds for `query_id`
307
+ await qp.createChart({ title: "Revenue", query_id: modified.queryId, vega_lite_spec: spec }, { tenantId });
308
+ ```
309
+
310
+ - When `queryId` / `query_id` is present it **takes priority**: any `sql` / `sql_params` you also send is ignored.
311
+ - An id that belongs to another tenant, or does not exist, is rejected (`404`).
312
+ - `sql` is still accepted when no id is sent, for backward compatibility. Charts saved without a `query_id` (for example older charts) keep using their stored SQL.
313
+ - `modifyChart()` returns the input `queryId` when only visualization changes, and a new `queryId` when SQL is regenerated. Custom SQL (`customSql`) has no id.
314
+ - Generation fails if the server cannot record the query, so a successful `ask()` always returns a `queryId`.
315
+ - Requires an API that supports `GET /v2/queries/:id`; upgrade the API before the SDK.
316
+
289
317
  ## Modifying Charts
290
318
 
291
- The `modifyChart()` method allows you to edit SQL and/or visualization settings, then re-execute and regenerate charts. It works with both fresh `ask()` responses and saved charts.
319
+ The `modifyChart()` method allows you to edit SQL and/or visualization settings, then re-execute and regenerate charts. It works with both fresh `ask()` responses and saved charts. Prefer `queryId` over `sql` (see above).
292
320
 
293
321
  ### Changing Visualization Settings
294
322
 
package/dist/index.cjs CHANGED
@@ -2194,6 +2194,25 @@ function anonymizeResults(rows) {
2194
2194
  });
2195
2195
  }
2196
2196
 
2197
+ // src/routes/queries.ts
2198
+ async function resolveQuery(client, queryEngine, queryId, options, signal) {
2199
+ const response = await client.get(
2200
+ `/v2/queries/${encodeURIComponent(queryId)}`,
2201
+ options.tenantId,
2202
+ options.userId,
2203
+ options.scopes,
2204
+ signal
2205
+ );
2206
+ const paramMetadata = Array.isArray(response.params) ? response.params : [];
2207
+ return {
2208
+ queryId: response.queryId,
2209
+ sql: response.sql,
2210
+ params: queryEngine.mapGeneratedParams(paramMetadata),
2211
+ paramMetadata,
2212
+ dialect: response.dialect
2213
+ };
2214
+ }
2215
+
2197
2216
  // src/routes/modify.ts
2198
2217
  function buildModifiedQuestion(originalQuestion, modifications, pipeline, hasSessionContext = false) {
2199
2218
  const hints = [];
@@ -2348,11 +2367,28 @@ async function modifyChart(client, queryEngine, input, options, signal) {
2348
2367
  const hasCustomSql = !!input.sqlModifications?.customSql;
2349
2368
  const pipeline = resolveQueryPipeline(options?.pipeline);
2350
2369
  const queryEndpoint = pipeline === "v2" ? "/v2/query" : "/query";
2351
- let finalSql = input.sql;
2370
+ let currentSql = input.sql;
2352
2371
  let finalParams = input.params ?? {};
2353
2372
  let paramMetadata = [];
2354
2373
  let rationale;
2355
2374
  let queryId;
2375
+ if (input.queryId) {
2376
+ const resolved = await resolveQuery(
2377
+ client,
2378
+ queryEngine,
2379
+ input.queryId,
2380
+ { tenantId, userId: options?.userId, scopes: options?.scopes },
2381
+ signal
2382
+ );
2383
+ currentSql = resolved.sql;
2384
+ finalParams = resolved.params;
2385
+ paramMetadata = resolved.paramMetadata;
2386
+ queryId = resolved.queryId;
2387
+ }
2388
+ if (!currentSql) {
2389
+ throw new Error("modifyChart requires either queryId or sql.");
2390
+ }
2391
+ let finalSql = currentSql;
2356
2392
  let sqlChanged = false;
2357
2393
  let finalQuestion = input.question;
2358
2394
  const databaseName = input.database ?? queryEngine.getDefaultDatabase();
@@ -2380,6 +2416,7 @@ async function modifyChart(client, queryEngine, input, options, signal) {
2380
2416
  finalSql = input.sqlModifications.customSql;
2381
2417
  finalParams = {};
2382
2418
  paramMetadata = [];
2419
+ queryId = void 0;
2383
2420
  sqlChanged = true;
2384
2421
  } else if (hasSqlMods && !hasCustomSql) {
2385
2422
  const modifiedQuestion = buildModifiedQuestion(
@@ -2396,7 +2433,7 @@ async function modifyChart(client, queryEngine, input, options, signal) {
2396
2433
  {
2397
2434
  question: modifiedQuestion,
2398
2435
  session_id: querypanelSessionId,
2399
- previous_sql: input.sql,
2436
+ previous_sql: currentSql,
2400
2437
  ...options?.maxRetry ? { max_retry: options.maxRetry } : {},
2401
2438
  ...tenantSettings ? { tenant_settings: tenantSettings } : {},
2402
2439
  ...databaseName ? { database: databaseName } : {},
@@ -2421,7 +2458,7 @@ async function modifyChart(client, queryEngine, input, options, signal) {
2421
2458
  }
2422
2459
  rationale = queryResponse.rationale;
2423
2460
  queryId = queryResponse.queryId;
2424
- sqlChanged = finalSql !== input.sql;
2461
+ sqlChanged = finalSql !== currentSql;
2425
2462
  }
2426
2463
  const execution = await queryEngine.validateAndExecute(
2427
2464
  finalSql,
@@ -2866,9 +2903,23 @@ var QueryPanelSdkAPI = class _QueryPanelSdkAPI {
2866
2903
  throw new Error("tenantId is required");
2867
2904
  }
2868
2905
  const databaseName = input.database ?? "db";
2869
- const params = input.params ?? {};
2906
+ let sql = input.sql;
2907
+ let params = input.params ?? {};
2908
+ if (input.queryId) {
2909
+ const resolved = await resolveQuery(
2910
+ this.client,
2911
+ this.queryEngine,
2912
+ input.queryId,
2913
+ { tenantId }
2914
+ );
2915
+ sql = resolved.sql;
2916
+ params = resolved.params;
2917
+ }
2918
+ if (!sql) {
2919
+ throw new Error("Either queryId or sql is required");
2920
+ }
2870
2921
  const result = await this.queryEngine.validateAndExecute(
2871
- input.sql,
2922
+ sql,
2872
2923
  params,
2873
2924
  databaseName,
2874
2925
  tenantId