From 2ac33884e64dd51b0bc04e86eed144350688edf6 Mon Sep 17 00:00:00 2001 From: Alex Vasilev Date: Thu, 27 Aug 2026 16:08:11 -0400 Subject: [PATCH 1/8] docs: fix five stale documentation sections (#11671) * docs: fix five stale documentation sections - default_ui_filters now seed queries in the Google Sheets / Excel add-in as well as workbooks and React Embed SDK views - document one exploration placed on many sheets/workbooks in the Excel/Sheets add-ins - document the measure position and order controls in the Excel/Sheets pivot builder - document runQuery's branchName parameter and the branch-verification step in the MCP server workflow - allowExport in embedded dashboards now covers PNG/PDF, not just CSV * docs: fix contradictory wording flagged in review - allowExport: replace the confusing "opt-in ... otherwise opt-out" framing with a plain list of what stays hidden - saved reports: clarify that an anchor is a fixed cell reference, so row/column inserts above it break the placement instead of shifting with it --------- Co-authored-by: Claude --- .../workbooks/querying-data.mdx | 4 +- .../docs/integrations/google-sheets.mdx | 51 +++++++++++++++++-- .../docs/integrations/mcp-server.mdx | 13 ++++- .../docs/integrations/microsoft-excel.mdx | 47 ++++++++++++++++- docs-mintlify/embedding/iframe/dashboards.mdx | 21 +++++--- docs-mintlify/embedding/iframe/events.mdx | 12 ++--- .../reference/data-modeling/view.mdx | 19 +++---- 7 files changed, 137 insertions(+), 30 deletions(-) diff --git a/docs-mintlify/docs/explore-analyze/workbooks/querying-data.mdx b/docs-mintlify/docs/explore-analyze/workbooks/querying-data.mdx index a49462a39596f..8fe236eca2726 100644 --- a/docs-mintlify/docs/explore-analyze/workbooks/querying-data.mdx +++ b/docs-mintlify/docs/explore-analyze/workbooks/querying-data.mdx @@ -105,7 +105,9 @@ above cannot express. ### Default filters A data model author can define default workbook filters on a semantic view with -the [`meta.default_ui_filters`][ref-default-ui-filters] parameter: +the [`meta.default_ui_filters`][ref-default-ui-filters] parameter. The same +defaults are also seeded when a query on that view is started in the Google +Sheets / Excel add-in. ```yaml views: diff --git a/docs-mintlify/docs/integrations/google-sheets.mdx b/docs-mintlify/docs/integrations/google-sheets.mdx index 54f03dae5493d..9ae74899c7501 100644 --- a/docs-mintlify/docs/integrations/google-sheets.mdx +++ b/docs-mintlify/docs/integrations/google-sheets.mdx @@ -73,10 +73,15 @@ looks and feels like [Playground][ref-playground]. -Google Cloud for Sheets works only with [views][ref-views], not cubes. +Cube Cloud for Sheets works only with [views][ref-views], not cubes. +If the view defines [`default_ui_filters`][ref-default-ui-filters], those +filters are pre-populated as soon as you select the view — the same way they +are in workbooks. They are a starting point, not enforcement: you can change +their values, switch operators, or remove them. + Click on members to add them to **Rows** and **Measures**. If needed, drag dimensions from **Rows** to **Columns**. Click on the funnel buttons to add members to **Filters**. Click on **×** to @@ -100,6 +105,31 @@ With every change to your query, Cube Cloud for Sheets will update the report on the sheet after a slight delay. If you'd like to minimize it, consider implementing [pre-aggregations][ref-pre-aggs]. +### Measure position and order + +By default, measures nest under each value of the dimension they're paired +with — every measure for the first column value, then every measure for the +next. Open the **Display** tab to change **Measure position** to **Before +columns** to get the opposite layout: each measure spans every column value, +with all values of one measure together before moving to the next. + +For example, with `Forecast Sales Units` and `Forecast Net Sales` on Measures +and a `season` dimension on Columns: + +- **After columns** (default): `Q1: [Sales Units, Net Sales] | Q2: [Sales + Units, Net Sales]` +- **Before columns**: `Sales Units: [Q1, Q2] | Net Sales: [Q1, Q2]` + +{/* TODO: screenshot — side-by-side of the two output layouts (After columns vs Before columns) */} + +The same choice applies when measures are placed on **Rows** instead (set +with **Measures on**, in the same tab) — there it's labeled **After rows** / +**Before rows**. + +To change the order measures appear in within their group, drag them within +the **Measures** pane on the **Pivot** tab. Position and order are saved with +the report and survive **Refresh**. + When your report is ready, you can optionally move it to [saved reports](#work-with-saved-reports) by clicking **Save**. @@ -111,8 +141,20 @@ exploration by name across your whole deployment, not just the current folder — results are grouped by type and show each item's folder location, and selecting one navigates you straight to it. -Click **Refresh** to manually refresh the data in the report's location. -Click **Edit** to change the query or the location. +An exploration can be placed more than once — on different sheets or at +different anchors in the same spreadsheet, and in more than one document at +once (a Google Sheets spreadsheet and an Excel workbook simultaneously). +Hovering a row in this list shows every placement it has in the **current** +document under **Location** / **Locations**. Click **Refresh** to update all +of that exploration's placements in the current document at once; click its +title to open it and change the query, which applies to every placement. + +A placement survives renaming the sheet it's on or the spreadsheet it's in — +it's tracked by the sheet's and spreadsheet's own stable ids, not by name. A +placement's anchor is a fixed cell reference, though, so it does **not** +survive rows or columns inserted above it: the visible data shifts down with +the insert, but the stored anchor doesn't move with it, so the next refresh +targets the wrong cell. @@ -133,5 +175,6 @@ in Cube Cloud. [ref-playground]: /docs/explore-analyze/playground [ref-views]: /docs/data-modeling/views [ref-pre-aggs]: /docs/pre-aggregations/using-pre-aggregations +[ref-default-ui-filters]: /reference/data-modeling/view#default_ui_filters [ref-sql-api-enabled]: /reference/core-data-apis/sql-api#cube-cloud -[ref-saved-reports]: /docs/workspace/saved-reports \ No newline at end of file +[ref-saved-reports]: /docs/workspace/saved-reports diff --git a/docs-mintlify/docs/integrations/mcp-server.mdx b/docs-mintlify/docs/integrations/mcp-server.mdx index e69485ee866b4..a329c8001bb6f 100644 --- a/docs-mintlify/docs/integrations/mcp-server.mdx +++ b/docs-mintlify/docs/integrations/mcp-server.mdx @@ -239,11 +239,17 @@ work together. | Tool | Description | Access | | --- | --- | --- | | `searchDataModel` | Searches the semantic model by similarity — views and their measures and dimensions — to discover what is queryable. Returns compact records (name, title, description, type) to reference in `runQuery`. | Read-only | -| `runQuery` | Runs a Cube SQL query (PostgreSQL dialect) against the [SQL API][ref-sql-api]. Returns a schema, a page of rows, and `hasMore` / `totalRows` for pagination via `offset`. | Read-only | +| `runQuery` | Runs a Cube SQL query (PostgreSQL dialect) against the [SQL API][ref-sql-api]. Returns a schema, a page of rows, and `hasMore` / `totalRows` for pagination via `offset`. Pass the dev `branchName` from `startDataModelEdit` to query that branch instead of the deployed model. | Read-only | Call `searchDataModel` before `runQuery` to find exact view and member names rather than guessing them. +Omitting `branchName` queries the deployed model, exactly as before this parameter existed. +Passing a `branchName` that isn't an active dev branch — for example the deploy branch's own +name, or a feature branch with no dev worker running — fails with an error rather than +silently answering from the deployed model, so a query aimed at the wrong branch never +looks like a query that just returned no rows. + ### Dashboard authoring These tools build [workbooks][ref-workbooks] and [dashboards][ref-dashboards] @@ -320,6 +326,9 @@ into the MCP server: model-editing tools, `getBranchDiff`, `getDeploymentEnv`, and both pre-aggregation tools — is offered only to users whose role allows editing the semantic model. A Viewer never sees them at all. +- **Verification happens on the branch, before a merge, not after.** Pass the dev + `branchName` to `runQuery` to check that an edit returns the right data — `valid: true` + from `writeDataModelFile` only means the model compiles, not that it's correct. Review pending work with `getDataModelChanges` before you commit. @@ -400,6 +409,8 @@ Call `startDataModelEdit` to enter development mode and get a dev `branchName`. the current source with `listDataModelFiles` and `readDataModelFile`, then apply changes with `writeDataModelFile` or `deleteDataModelFile`, passing that `branchName`. Each write recompiles the model and reports validation errors, so you can iterate until it compiles. +Before committing, verify the change returns the right data: call `runQuery` with the same +`branchName` and check the numbers, since a compiling model can still return wrong results. Review the result with `getDataModelChanges`, then commit the branch from the Cube UI to publish it. diff --git a/docs-mintlify/docs/integrations/microsoft-excel.mdx b/docs-mintlify/docs/integrations/microsoft-excel.mdx index cb658fb1303b7..4024888e8402d 100644 --- a/docs-mintlify/docs/integrations/microsoft-excel.mdx +++ b/docs-mintlify/docs/integrations/microsoft-excel.mdx @@ -85,6 +85,11 @@ Cube Cloud for Excel works only with [views][ref-views], not cubes. +If the view defines [`default_ui_filters`][ref-default-ui-filters], those +filters are pre-populated as soon as you select the view — the same way they +are in workbooks. They are a starting point, not enforcement: you can change +their values, switch operators, or remove them. + Click on members to add them to **Rows** and **Measures**. If needed, drag dimensions from **Rows** to **Columns**. Click on the funnel buttons to add members to **Filters**. Click on **×** to @@ -108,6 +113,31 @@ With every change to your query, Cube Cloud for Excel will update the report on the sheet after a slight delay. If you'd like to minimize it, consider implementing [pre-aggregations][ref-pre-aggs]. +### Measure position and order + +By default, measures nest under each value of the dimension they're paired +with — every measure for the first column value, then every measure for the +next. Open the **Display** tab to change **Measure position** to **Before +columns** to get the opposite layout: each measure spans every column value, +with all values of one measure together before moving to the next. + +For example, with `Forecast Sales Units` and `Forecast Net Sales` on Measures +and a `season` dimension on Columns: + +- **After columns** (default): `Q1: [Sales Units, Net Sales] | Q2: [Sales + Units, Net Sales]` +- **Before columns**: `Sales Units: [Q1, Q2] | Net Sales: [Q1, Q2]` + +{/* TODO: screenshot — side-by-side of the two output layouts (After columns vs Before columns) */} + +The same choice applies when measures are placed on **Rows** instead (set +with **Measures on**, in the same tab) — there it's labeled **After rows** / +**Before rows**. + +To change the order measures appear in within their group, drag them within +the **Measures** pane on the **Pivot** tab. Position and order are saved with +the report and survive **Refresh**. + When your report is ready, you can optionally move it to [saved reports](#work-with-saved-reports) by clicking **Save**. @@ -119,8 +149,20 @@ exploration by name across your whole deployment, not just the current folder — results are grouped by type and show each item's folder location, and selecting one navigates you straight to it. -Click **Refresh** to manually refresh the data in the report's location. -Click **Edit** to change the query or the location. +An exploration can be placed more than once — on different sheets or at +different anchors in the same workbook, and in more than one document at +once (a Google Sheets spreadsheet and an Excel workbook simultaneously). +Hovering a row in this list shows every placement it has in the **current** +document under **Location** / **Locations**. Click **Refresh** to update all +of that exploration's placements in the current document at once; click its +title to open it and change the query, which applies to every placement. + +A placement survives renaming the sheet it's on or the workbook it's in — +it's tracked by the sheet's and workbook's own stable ids, not by name. A +placement's anchor is a fixed cell reference, though, so it does **not** +survive rows or columns inserted above it: the visible data shifts down with +the insert, but the stored anchor doesn't move with it, so the next refresh +targets the wrong cell. @@ -141,6 +183,7 @@ in Cube Cloud. [ref-playground]: /docs/explore-analyze/playground [ref-views]: /docs/data-modeling/views [ref-pre-aggs]: /docs/pre-aggregations/using-pre-aggregations +[ref-default-ui-filters]: /reference/data-modeling/view#default_ui_filters [ref-sql-api-enabled]: /reference/core-data-apis/sql-api#cube-cloud [link-excel-addins]: https://support.microsoft.com/en-us/office/add-or-remove-add-ins-in-excel-0af570c4-5cf3-4fa9-9b88-403625a0b460 [link-ms-appsource]: https://appsource.microsoft.com/en-us/product/office/WA200008486 diff --git a/docs-mintlify/embedding/iframe/dashboards.mdx b/docs-mintlify/embedding/iframe/dashboards.mdx index 905fd42e34e74..3bf630d168ef9 100644 --- a/docs-mintlify/embedding/iframe/dashboards.mdx +++ b/docs-mintlify/embedding/iframe/dashboards.mdx @@ -77,20 +77,27 @@ This works on both regular and published (embedded) dashboards. The filter is only applied if a matching filter widget for that dimension already exists on the dashboard. -## Allow CSV export +## Allow chart export {#allow-csv-export} By default, embedded dashboards do not expose a download action on individual -widgets. To let viewers download a chart widget's data as a CSV file, add the -`allowExport=true` query parameter to the embed URL: +widgets. To let viewers download a chart widget as a **CSV**, **PNG**, or +**PDF** file, add the `allowExport=true` query parameter to the embed URL: ```text https://your-tenant.cubecloud.dev/embed/dashboard/YOUR_DASHBOARD_PUBLIC_ID?session=YOUR_SESSION_ID&allowExport=true ``` -When enabled, each chart widget's ⋮ menu shows a **Download as CSV** action. The -CSV is generated client-side from the data already loaded into the widget, so no -additional query is issued. The parameter is opt-in — omit it (the default) to -keep the download action hidden. +When enabled, each chart widget's ⋮ menu shows **Download as CSV**, **Download +as PNG**, and **Download as PDF** actions. `allowExport` is the single switch +for all three formats — there is no way to allow one format while blocking +another. Only the exact literal string `true` opts in: `allowExport=1`, +`allowExport=TRUE`, a bare `?allowExport`, and omitting the parameter (the +default) all leave every download action hidden. + +The CSV is generated client-side from the data already loaded into the +widget, so no additional query is issued. Inside an embed, `allowExport` — +not the viewer's own Cube role — is the authority on whether the download +actions appear. ## Show or hide the AI chat diff --git a/docs-mintlify/embedding/iframe/events.mdx b/docs-mintlify/embedding/iframe/events.mdx index 066816c5c9964..25c78576972f9 100644 --- a/docs-mintlify/embedding/iframe/events.mdx +++ b/docs-mintlify/embedding/iframe/events.mdx @@ -186,9 +186,9 @@ painted" signal (distinct from `ready`, which fires at mount, before data loads) #### `cube:event:download` {#cube-event-download} -Emitted when a viewer exports something — a widget's data as CSV, or (in future) -a dashboard image. Reports _that_ an export happened and its shape — never the -exported rows themselves. +Emitted when a viewer exports something — a widget's data as CSV, or a widget +as a PNG or PDF image. Reports _that_ an export happened and its shape — never +the exported rows themselves. | Field | Type | Description | | --- | --- | --- | @@ -209,10 +209,10 @@ exported rows themselves. ``` - The CSV download action on a dashboard widget only appears when the embed URL - includes `allowExport=true` (see [Dashboards → Allow CSV + A dashboard widget's download actions (CSV, PNG, PDF) only appear when the + embed URL includes `allowExport=true` (see [Dashboards → Allow chart export](/embedding/iframe/dashboards#allow-csv-export)). The event fires when a - viewer uses it. + viewer uses one of them. #### `cube:event:drilldown` {#cube-event-drilldown} diff --git a/docs-mintlify/reference/data-modeling/view.mdx b/docs-mintlify/reference/data-modeling/view.mdx index 29920e8e454a7..f5a3731f04bb9 100644 --- a/docs-mintlify/reference/data-modeling/view.mdx +++ b/docs-mintlify/reference/data-modeling/view.mdx @@ -287,10 +287,11 @@ over the view's default. When `auto_run` is not set, auto-run is on. #### `default_ui_filters` In the Cube cloud platform, you can use the `default_ui_filters` key in a -view's `meta` to define default filters for [workbooks][ref-workbook-filtering] -and views embedded via the React Embed SDK. When the view is selected, these -filters are pre-populated as regular, fully editable filters — data consumers -can change their values, switch operators, or remove them entirely. +view's `meta` to define default filters for [workbooks][ref-workbook-filtering], +views embedded via the React Embed SDK, and the Google Sheets / Excel add-in. +When the view is selected, these filters are pre-populated as regular, fully +editable filters — data consumers can change their values, switch operators, +or remove them entirely. Each entry has a `member` (a dimension or measure exposed by the view), an `operator`, and a list of `values` — or a single `value`, which is natural for @@ -418,11 +419,11 @@ bounds, drops that filter entry with a browser-console warning — the remaining entries still apply, and view selection never breaks. Unlike [`default_filters`](#default_filters), which are enforced on every query -against the view, `default_ui_filters` only provide a starting point for the -workbook UI: they don't apply to queries made through APIs or other -visualization tools, and workbook users can freely modify or remove them. -They also only apply to new queries — defining or changing them in the data -model does not add them to existing queries. +against the view, `default_ui_filters` only provide a starting point: they +don't apply to queries made through APIs or other visualization tools, and +data consumers can freely modify or remove them. They also only apply to new +queries — defining or changing them in the data model does not add them to +existing queries or saved explorations. ### `cubes` From b82c58aa245919a36e3d20f79105b639d0c16863 Mon Sep 17 00:00:00 2001 From: Pavel Tiunov Date: Thu, 27 Aug 2026 13:25:10 -0700 Subject: [PATCH 2/8] fix(server-core): release orchestrators evicted from the LRU (#11661) * fix(cubestore-driver): make close() actually close the connection `close()` called `webSocket.close()` and left it there, which does not release anything: the 'close' handler re-sends whatever was in flight over a fresh connection, and the 5s heartbeat on that one keeps the socket -- and everything reachable from the interval, including the orchestrator holding the driver -- alive for the life of the process. A query arriving after the close re-opened the connection just as readily. A single `closed` flag makes the close terminal. `initWebSocket()` refuses once it is set, which covers both the late query and the re-send path, whose `catch` then fails the messages that were in flight instead of re-opening for them. Messages already in flight are still given the chance to be answered -- an eviction can land mid-query and Cube Store may already be working on the answer -- so the socket goes away once the last of them settles, or immediately if there are none. A query Cube Store never answers is bounded by the existing no-heartbeat timeout, which closes the socket and lands on the same path. Alongside it, two things that leaked a connection nobody was waiting for: - The heartbeat interval is now stopped through `teardown()` wherever the socket dies, not only in the 'close' handler. It is the timer, not the socket, that keeps the graph reachable. - The 'error' handler no longer reconnects once the socket has been established. Retrying is only useful while somebody is still waiting for `readyPromise`; after that it raised a connection no caller asked for and kept it alive on its own heartbeat, while what was in flight is re-sent by the 'close' handler anyway. The re-send itself now also bails out when everything it was scheduled for has settled in the meantime. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01HF1JQbLKkZ4XLd1hLXYVmA * fix(server-core): release orchestrators evicted from the LRU `OrchestratorStorage` keeps `OrchestratorApi` instances in an `LRUCache` with no `dispose` handler, so an entry pushed out by `max` -- or replaced, deleted, expired -- is dropped without `release()` ever being called. Its drivers stay open, and with them the Cube Store WebSocket. Only `releaseConnections()` at shutdown released anything. Nothing else closes that socket: the driver pings it every 5s, so neither side times it out, and that live timer keeps the whole orchestrator reachable from a GC root. A collected socket would not be closed anyway -- V8 runs no destructors and neither `ws` nor `net` registers a finalizer. In Node a socket is closed explicitly or never. So a deployment whose `contextToOrchestratorId` has high cardinality (per-user ids, or ids that rotate on a timer) leaked one Cube Store connection per eviction. Measured in production: ~450 new sockets/minute per API pod (429 on a pod 43s after restart, 5,198 at 16 min; a pod serving no traffic held 1), 65,551 established sockets on the router they pointed at, and that region's ingress OOM-killed 38-46 times in six hours. Reproduced on deployments pinned to v1.7.2 and v1.7.7, so it is not version-specific. `disposeAfter` covers every removal reason. It is `disposeAfter` rather than `dispose` because `release()` is async and calls into the drivers, while `dispose` runs synchronously inside `set()`/`delete()`. The scheduled releases are tracked in `pendingReleases` so `releaseConnections()` can await them: `clear()` only schedules them, so without that shutdown could return with the connections still open. A release that fails is swallowed rather than left as an unhandled rejection, and each entry removes itself from the set when it finishes. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01HF1JQbLKkZ4XLd1hLXYVmA * fix(cubestore-driver): bound the drain a close waits out Review follow-ups on the `closed` flag. The drain was unbounded and re-checked only from the 'message' handler, so a message Cube Store never answers kept the socket -- and the heartbeat interval that makes it reachable -- for the life of the process. The no-heartbeat close does not save it: Cube Store keeps answering the pings of a connection whose query simply never completes, so nothing would call `closeIfDrained()` again. That is the leak this branch exists to close, behind a narrower door. `close()` now arms a bound; when it fires, whatever is left is rejected naming the count, and the socket is torn down. A stuck message costs one query instead. `closeIfDrained()` also took the socket the answer arrived on rather than reading `this.webSocket`: a late answer on a socket the re-send path has already superseded was being read as the current connection having drained. Dropped the `established` flag as well. It existed to stop the 'error' handler reconnecting a socket nobody was waiting for any more, but with `closed` making the close terminal that reconnect is no longer orphaned -- it is assigned to `this.webSocket`, so it is reachable and the close reaps it. The heartbeat is still stopped there, since a 'close' does not always follow an 'error'. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01HF1JQbLKkZ4XLd1hLXYVmA * fix(server-core): log an orchestrator release that fails `OrchestratorStorage` swallows the rejection so that one orchestrator failing to close cannot fail shutdown -- which also means nothing said so. On the eviction path there was never a caller to tell, and at shutdown `releaseConnections()` used to propagate the error and no longer can. A driver that fails to close takes its connection with it, and that is the one signal an operator has that this fix is not working in their deployment, so `release()` logs it through the api's own logger before rethrowing. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01HF1JQbLKkZ4XLd1hLXYVmA * fix(cubestore-driver): bound the close in the heartbeat, not a second timer The bound on how long a closed connection waits for its in-flight messages was its own `setTimeout`. The heartbeat interval is already the timer that owns "this socket has waited long enough", and it is already the thing keeping the socket reachable, so the check belongs there: one timer to reason about, and no chance of a stray one outliving the socket it was bounding. The interval keeps pinging while a closed connection drains, so one that is draining legitimately is not dropped for inactivity, and the closed branch returns before the ordinary no-heartbeat check -- that check cannot bound this wait, since Cube Store keeps answering the pings of a connection whose query is never completed. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01HF1JQbLKkZ4XLd1hLXYVmA * fix(server-core): don't build the external driver just to close it `release()` called `releaseDriver(this.options.externalDriverFactory)` unconditionally, and that factory *creates* the connection on first call -- `server.ts` builds the driver and runs `testConnection()` inside it. Harmless while release only happened at shutdown; with a release on every LRU eviction it means an orchestrator that never touched Cube Store opens a connection purely to close it, which is churn against exactly the router this branch exists to relieve, at the eviction rate the PR describes. The factory is wrapped in the constructor -- before `QueryOrchestrator` captures it off `options` -- so the release can tell whether anything built the driver, and the flag is set on fulfilment: `server.ts` releases the driver and clears its memo when `testConnection()` fails, so a build that threw left nothing open. The release closes through the unwrapped factory, or it would set the flag again on the way out. The data-source loop was already guarded this way by `seenDataSources`. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01HF1JQbLKkZ4XLd1hLXYVmA * fix(cubestore-driver): don't leave an ownerless rejection on the retry path Dropping the `established` flag left a fatal hole, and the reasoning in that commit message was incomplete: it covered a live connection, where the retry's reconnect lands in `this.webSocket` and a later close reaps it, and missed the closed one, where there is no reconnect to reap -- only a rejection with no owner. `initWebSocket()` refuses once `closed` is set, and the 'error' handler handed that rejected promise to `resolve`. On a socket whose `readyPromise` has already settled, `resolve` returns without adopting what it was given, so nothing ever observes the rejection -- and an unhandled rejection is fatal under Node's default `--unhandled-rejections=throw`, which would take the API pod with it. Two changes, each standing on its own: - The handler returns early when the connection is closed. There is nothing to retry towards, and this also stops arming a pointless timer per evicted connection. - The retry uses `.then(resolve, reject)` rather than `resolve(promise)`, so any rejection is observed whether or not `readyPromise` has settled. The early return covers the case above, but a close landing between the timer being armed and firing does not go through it, and neither would anything else `initWebSocket()` throws. The test emits the 'error' rather than provoking one: `ws` reports a dying socket as 'close' against the mock, and the errors that do reach this handler in the wild -- a protocol error on an established socket, or a connect retry still pending from before the close -- are not reproducible there. The handler's contract is what is being pinned, and the test fails against the previous commit with exactly the fatal rejection. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01HF1JQbLKkZ4XLd1hLXYVmA * Revert "fix(server-core): don't build the external driver just to close it" This reverts commit c22a475d, keeping only the release logging. The guard is not worth its complexity. For any orchestrator that served a query the external driver has already been built -- the Cube Store queue and cache go through the same factory -- so the guard only saves an open/close for one that was created and never used, and the wrapper it needs drew two defects of its own in review. Master's unconditional release stays. For the record, since it is the reason the guard existed: the operation it skips is not free. `CubeStoreDriver.testConnection()` runs `SELECT 1`, which opens the socket, so releasing a never-built external driver does connect + query + close. That cost is now accepted for the minority of evictions where it applies. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01HF1JQbLKkZ4XLd1hLXYVmA * fix(cubestore-driver): settle readyPromise when a closed connection errors The early return added in 52abc597 skips every path that settles `readyPromise`: `'open'` will not fire, `teardown()` above has stopped the interval, the 'close' handler does not touch it, and the retry that used to settle it -- by adoption, since `resolve(promise)` on an unsettled promise adopts -- is exactly what the early return skips. So a socket still CONNECTING when the connection is closed left the `await this.initWebSocket()` in `sendMessage` pending for good. That is an eviction landing while an orchestrator's first query is connecting, and it turned the rejection that path used to produce into a request hung on nothing, which is worse than the failure it replaced. Rejecting before the return restores it, and is a no-op once the socket has opened. Also corrected the comment above it: what was written to the socket is rejected by the 'close' handler that `ws` emits after an 'error', and *not* by the drain bound, which cannot fire any more because `teardown()` has stopped the interval that evaluates it. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01HF1JQbLKkZ4XLd1hLXYVmA * refactor(cubestore-driver): make two close-path fields mean what they say Neither is a defect; both were noted in review as harmless and are cheaper to remove than to keep explaining. `currentConnectionTry` was incremented above the closed branch, i.e. on a path that returns without attempting a connection. It now counts only real tries, which is what paces the retries and spends the `maxConnectRetries` budget a cold connect needs. `closedAt` was re-stamped by every `close()`, so a second call on a draining connection restarted the bound. That is reachable: `release()` closes the data-source and external drivers separately, which is the same connection twice when one `CubeStoreDriver` backs both. It now records when the close was first asked for. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01HF1JQbLKkZ4XLd1hLXYVmA --------- Co-authored-by: Claude --- .../src/WebSocketConnection.ts | 166 ++++++++++++++++- .../test/websocket-connection.test.ts | 167 +++++++++++++++++- .../src/core/OrchestratorApi.ts | 22 ++- .../src/core/OrchestratorStorage.ts | 27 ++- .../test/unit/OrchestratorStorage.test.ts | 160 +++++++++++++++++ 5 files changed, 530 insertions(+), 12 deletions(-) create mode 100644 packages/cubejs-server-core/test/unit/OrchestratorStorage.test.ts diff --git a/packages/cubejs-cubestore-driver/src/WebSocketConnection.ts b/packages/cubejs-cubestore-driver/src/WebSocketConnection.ts index 362fc11cf6ff8..f20ef98cfaeac 100644 --- a/packages/cubejs-cubestore-driver/src/WebSocketConnection.ts +++ b/packages/cubejs-cubestore-driver/src/WebSocketConnection.ts @@ -67,6 +67,9 @@ interface CubeStoreWebSocket extends WebSocket { // A failure that killed this socket and that re-sending can't fix, so the // message that caused it is rejected instead of being re-sent. fatalError: Error | null; + // Stops the heartbeat interval, which otherwise keeps a reference to this socket + // (and the socket itself alive) even when nothing else points to the connection. + teardown: () => void; } export class WebSocketConnection { @@ -88,6 +91,17 @@ export class WebSocketConnection { private cubeStoreVersion: string | null = null; + // Set by close(), and never unset: a closed connection stays open only for the + // messages already in flight and never establishes another socket. Without it + // close() does not actually close anything -- the 'close' handler re-sends + // whatever was pending over a fresh connection, and the heartbeat on that one + // keeps it (and the orchestrator holding it) alive for the life of the process. + private closed: boolean = false; + + // When close() was asked for, so the heartbeat can bound the wait for the + // messages that were still in flight at that point. + private closedAt: Date | null = null; + public constructor(url: string) { this.url = url; this.messageCounter = 1; @@ -99,6 +113,13 @@ export class WebSocketConnection { } protected async initWebSocket(): Promise { + if (this.closed) { + // Refusing here is what makes close() terminal: it covers a query issued + // after the release as well as the re-send path, whose `catch` then + // rejects the messages that were in flight instead of re-opening for them. + throw new ConnectionError('Cube Store connection is closed'); + } + if (!this.webSocket) { const headers: Record = {}; headers['x-process-id'] = getProcessUid(); @@ -115,11 +136,32 @@ export class WebSocketConnection { webSocket.ping(); } + if (this.closed) { + // A closed connection is kept only for the messages already in + // flight, and this is where that wait is both re-checked and + // bounded. It has to be bounded here rather than by the heartbeat + // check below: Cube Store keeps answering the pings of a connection + // whose query is simply never completed, so that check never fires + // and a single wedged message would keep this socket -- and this + // interval, which is what makes it reachable -- for the life of the + // process. Pinging continues meanwhile, so a connection that is + // draining legitimately is not dropped for inactivity. + if (this.closedAt && new Date().getTime() - this.closedAt.getTime() > this.noHeartBeatTimeout * 1000) { + this.abandonClose(webSocket); + } else { + this.closeIfDrained(webSocket); + } + + return; + } + if (new Date().getTime() - webSocket.lastHeartBeat.getTime() > this.noHeartBeatTimeout * 1000) { webSocket.close(); } }, 5000); + webSocket.teardown = () => clearInterval(pingInterval); + webSocket.sendAsync = async (message: Uint8Array) => new Promise((resolveSend) => { // If socket is closing this message should be resent if (webSocket.readyState !== WebSocket.OPEN) { @@ -164,11 +206,48 @@ export class WebSocketConnection { return; } + // The socket is done either way, so stop its heartbeat now rather than + // relying on a 'close' that may not follow: the interval is what keeps + // the socket, and everything reachable from it, alive. + webSocket.teardown(); + + if (this.closed) { + // Nothing to retry towards: `initWebSocket()` refuses once closed. + // Whatever was written to the socket is rejected by the 'close' + // handler, which `ws` emits after an 'error' -- the drain bound + // cannot catch it any more, since `teardown()` above has already + // stopped the interval that evaluates it. + if (webSocket === this.webSocket) { + this.webSocket = null; + } + + // A no-op once this socket has opened, and the only thing left that + // can settle `readyPromise` if it has not: `teardown()` has stopped + // the interval, the 'close' handler does not touch it, and the retry + // that used to settle it by adoption is skipped just above. A socket + // closed while still CONNECTING would otherwise leave the + // `sendMessage` awaiting it pending for good -- a request hung on + // nothing, which is worse than the failure it replaced. + reject(new ConnectionError('Cube Store connection is closed')); + + return; + } + + // Counted here rather than above the branch: the closed path does not + // attempt a connection, and this counter both paces the retries and + // spends the `maxConnectRetries` budget a genuinely cold connect needs. this.currentConnectionTry += 1; if (this.currentConnectionTry < this.maxConnectRetries) { setTimeout(async () => { - resolve(this.initWebSocket()); + // `.then(resolve, reject)` rather than `resolve(promise)`: once + // this socket's `readyPromise` has settled, `resolve` returns + // without adopting -- so a rejection handed to it is never + // observed, and an unhandled rejection is fatal under Node's + // default. The retry above cannot reject for `closed` any more, + // but a close landing between this timer being armed and firing + // still can, as can anything else `initWebSocket()` throws. + this.initWebSocket().then(resolve, reject); }, this.retryWaitTime()); } else { reject(new ConnectionError( @@ -188,7 +267,7 @@ export class WebSocketConnection { webSocket.lastHeartBeat = new Date(); }); webSocket.on('close', (code: number, reason: Buffer) => { - clearInterval(pingInterval); + webSocket.teardown(); const pending = Object.keys(webSocket.sentMessages); @@ -259,6 +338,13 @@ export class WebSocketConnection { setTimeout(async () => { try { + // Everything this re-send was scheduled for has been settled in + // the meantime, so there is nothing left to carry over and a new + // connection would just stay open on its heartbeat. + if (!Object.keys(webSocket.sentMessages).length) { + return; + } + const nextWebSocket = await this.initWebSocket(); const resent = Object.keys(webSocket.sentMessages); @@ -302,6 +388,7 @@ export class WebSocketConnection { if (httpMessage.commandType() === HttpCommand.HttpError) { resolver.reject(new QueryError(`${httpMessage.command(new HttpError())?.error()}`)); + this.closeIfDrained(webSocket); return; } @@ -311,6 +398,8 @@ export class WebSocketConnection { } catch (e) { resolver.reject(e); } + + this.closeIfDrained(webSocket); }); }); @@ -530,9 +619,78 @@ export class WebSocketConnection { return this.cubeStoreVersion ?? '0.0.0'; } + /** + * Closes the connection for good. Messages already in flight are given the + * chance to be answered -- an eviction can happen mid-query, and failing one + * that Cube Store is already working on would fail the request -- so the + * socket goes away once the last of them settles, or right away if there are + * none. + */ public close() { - if (this.webSocket) { - this.webSocket.close(); + if (!this.closed) { + this.closed = true; + // First asked for, not most recently: `release()` closes the data-source + // and external drivers separately, which is the same connection twice when + // one `CubeStoreDriver` backs both, and re-stamping would restart the bound + // each time. + this.closedAt = new Date(); + } + + // Straight away when there is nothing in flight; otherwise the heartbeat + // takes it from here, both to notice the drain finishing and to bound it. + this.closeIfDrained(this.webSocket); + } + + /** + * Gives up on the messages a closed connection was waiting for, so that one + * Cube Store never answers costs a query rather than a permanently open + * socket. + */ + private abandonClose(socket: CubeStoreWebSocket) { + const unanswered = Object.keys(socket.sentMessages); + + socket.teardown(); + + if (socket === this.webSocket) { + this.webSocket = null; } + + const error = new ConnectionError( + `Cube Store connection was closed with ${unanswered.length} message(s) still unanswered` + ); + + // eslint-disable-next-line no-restricted-syntax + for (const key of unanswered) { + const message = socket.sentMessages[key]; + delete socket.sentMessages[key]; + message.reject(error); + } + + socket.terminate(); + } + + /** + * Closes `socket` once nothing is in flight on it. A no-op until close() has + * been called, so an ordinary idle connection is left alone. + * + * Takes the socket rather than reading `this.webSocket`: a late answer can + * arrive on a socket the re-send path has already superseded, and that must + * not be read as the current connection having drained. + */ + private closeIfDrained(socket: CubeStoreWebSocket | null) { + if (!this.closed || !socket || socket !== this.webSocket) { + return; + } + + if (Object.keys(socket.sentMessages).length) { + return; + } + + // Stop the heartbeat before closing rather than leaving it to the 'close' + // handler: it is the timer, not the socket, that keeps this whole graph + // reachable, so it should not outlive the decision to close by even a tick. + socket.teardown(); + this.webSocket = null; + socket.close(); } } diff --git a/packages/cubejs-cubestore-driver/test/websocket-connection.test.ts b/packages/cubejs-cubestore-driver/test/websocket-connection.test.ts index 33c51c6867249..ddff629837074 100644 --- a/packages/cubejs-cubestore-driver/test/websocket-connection.test.ts +++ b/packages/cubejs-cubestore-driver/test/websocket-connection.test.ts @@ -1,7 +1,8 @@ import { Socket } from 'net'; +import WebSocket from 'ws'; import { WebSocketConnection } from '../src/WebSocketConnection'; -import { MessageTooLargeError, QueryError } from '../src/errors'; +import { ConnectionError, MessageTooLargeError, QueryError } from '../src/errors'; import { QueryResultFormat } from '../codegen'; import { answeredBy, @@ -48,6 +49,22 @@ const waitForBufferedWrite = async (socket: Socket) => { } }; +/** + * Polls until `condition` holds, for assertions about a socket that is torn down + * a few ticks after the call that asked for it. + */ +const waitUntil = async (condition: () => boolean, description: string) => { + const deadline = Date.now() + 5000; + + while (!condition()) { + if (Date.now() > deadline) { + throw new Error(`Timed out waiting for ${description}`); + } + + await new Promise((resolve) => { setTimeout(resolve, 25); }); + } +}; + describe('WebSocketConnection', () => { let server: MockCubeStoreServer; let connection: WebSocketConnection | null = null; @@ -168,6 +185,154 @@ describe('WebSocketConnection', () => { await expectAnsweredBy(query('SELECT 1'), 1); }, JEST_TIMEOUT); + describe('close', () => { + /** Whether Cube Store has seen the connection go away. */ + const closedOnTheServer = (index: number) => server.connection(index).ws.readyState === WebSocket.CLOSED; + + it('closes the socket when nothing is in flight', async () => { + connection = new WebSocketConnection(server.url); + + await expectAnsweredBy(query('SELECT 1'), 0); + + connection.close(); + + await waitUntil(() => closedOnTheServer(0), 'Cube Store to see the connection close'); + expect((connection as any).webSocket).toBeNull(); + }, JEST_TIMEOUT); + + it('keeps the connection open until an in-flight query is answered', async () => { + connection = new WebSocketConnection(server.url); + + let answer: () => void = () => undefined; + server.handler = (message, mockConnection) => { + answer = () => mockConnection.ws.send( + buildErrorMessage(message.messageId, answeredBy(mockConnection.index)) + ); + }; + + const promise = query('SELECT 1'); + await server.waitForMessages(1); + + // An eviction can land mid-query, and Cube Store is already working on the + // answer, so closing must not drop it: the socket stays OPEN rather than + // going into CLOSING out from under the query. + connection.close(); + expect((connection as any).webSocket.readyState).toBe(WebSocket.OPEN); + + answer(); + await expectAnsweredBy(promise, 0); + + await waitUntil(() => closedOnTheServer(0), 'the connection to close once drained'); + expect(server.connections.length).toBe(1); + }, JEST_TIMEOUT); + + it('does not establish a new connection for a query issued after close', async () => { + connection = new WebSocketConnection(server.url); + + await expectAnsweredBy(query('SELECT 1'), 0); + connection.close(); + + // The released driver is still reachable from whoever held it, and this is + // the call that used to re-open the socket the release had just closed. + await expect(query('SELECT 2')).rejects.toThrow(ConnectionError); + expect(server.connections.length).toBe(1); + }, JEST_TIMEOUT); + + it('gives up on a message Cube Store never answers instead of holding the socket', async () => { + // The bound the close waits out. Cube Store keeps answering the pings of a + // connection whose query never completes, so without it nothing would ever + // close this socket again. + process.env.CUBEJS_CUBESTORE_NO_HEART_BEAT_TIMEOUT = '1'; + + try { + connection = new WebSocketConnection(server.url); + + server.handler = () => undefined; + + const promise = query('SELECT 1'); + await server.waitForMessages(1); + + connection.close(); + + await expect(promise).rejects.toThrow(ConnectionError); + await expect(promise).rejects.toThrow('1 message(s) still unanswered'); + + await waitUntil(() => closedOnTheServer(0), 'the connection to be closed anyway'); + expect((connection as any).webSocket).toBeNull(); + } finally { + delete process.env.CUBEJS_CUBESTORE_NO_HEART_BEAT_TIMEOUT; + } + }, JEST_TIMEOUT); + + it('rejects a query that is still connecting when the connection is closed', async () => { + connection = new WebSocketConnection(server.url); + + // `initWebSocket()` creates the socket synchronously, so this close lands + // while it is still CONNECTING -- an eviction while the very first query + // of an orchestrator is connecting. `ws` reports closing a CONNECTING + // socket as an 'error', and nothing else left would settle the promise + // the query is awaiting. + const promise = query('SELECT 1'); + connection.close(); + + await expect(promise).rejects.toThrow(ConnectionError); + await expect(promise).rejects.toThrow('Cube Store connection is closed'); + }, JEST_TIMEOUT); + + it('does not leave an unhandled rejection when a closed connection errors', async () => { + const unhandled: unknown[] = []; + const onUnhandled = (reason: unknown) => { unhandled.push(reason); }; + process.on('unhandledRejection', onUnhandled); + + try { + connection = new WebSocketConnection(server.url); + + // Establish it, so this socket's `readyPromise` is already settled. + await expectAnsweredBy(query('SELECT 1'), 0); + + const socket = (connection as any).webSocket; + connection.close(); + + // A post-open 'error' on a closed connection. Emitted rather than + // provoked because `ws` reports a dying socket as 'close' here, while + // the errors that do reach this handler in the wild -- a protocol error, + // or a connect retry still pending from before the close -- are not + // reproducible against the mock. The handler's contract is the subject: + // it schedules a retry, `initWebSocket()` refuses once closed, and + // handing that rejection to an already-settled `resolve` leaves nobody + // to observe it, which is fatal under Node's default + // --unhandled-rejections=throw. + socket.emit('error', Object.assign(new Error('read ECONNRESET'), { code: 'ECONNRESET' })); + + // Past the retry the handler schedules. + await new Promise((resolve) => { setTimeout(resolve, 2500); }); + + expect(unhandled).toEqual([]); + } finally { + process.off('unhandledRejection', onUnhandled); + } + }, JEST_TIMEOUT); + + it('does not re-open when the socket dies while draining', async () => { + connection = new WebSocketConnection(server.url); + + // Cube Store never answers, so the query is still in flight at close. + server.handler = () => undefined; + + const promise = query('SELECT 1'); + await server.waitForMessages(1); + + connection.close(); + clientSocket().destroy(epipe()); + + // The re-send path would ordinarily raise a fresh connection here, whose + // heartbeat is exactly what kept an evicted orchestrator alive. Failing + // the query is the honest outcome once the connection is closed. + await expect(promise).rejects.toThrow(ConnectionError); + expect(server.connections.length).toBe(1); + }, JEST_TIMEOUT); + }); + describe('message size limit', () => { const MAX_MESSAGE_SIZE = 1024 * 1024; diff --git a/packages/cubejs-server-core/src/core/OrchestratorApi.ts b/packages/cubejs-server-core/src/core/OrchestratorApi.ts index 12e1da161c4c9..2d99755616e44 100644 --- a/packages/cubejs-server-core/src/core/OrchestratorApi.ts +++ b/packages/cubejs-server-core/src/core/OrchestratorApi.ts @@ -240,11 +240,23 @@ export class OrchestratorApi { } public async release() { - return Promise.all([ - ...Object.keys(this.seenDataSources).map(ds => this.releaseDriver(this.driverFactory, ds)), - this.releaseDriver(this.options.externalDriverFactory), - this.orchestrator.cleanup() - ]); + try { + return await Promise.all([ + ...Object.keys(this.seenDataSources).map(ds => this.releaseDriver(this.driverFactory, ds)), + this.releaseDriver(this.options.externalDriverFactory), + this.orchestrator.cleanup() + ]); + } catch (error) { + // `OrchestratorStorage` swallows this so that one orchestrator failing to + // close cannot fail shutdown, which leaves this as the only place an + // operator learns that a connection was not closed after all. + this.logger('Orchestrator Release Error', { + orchestratorId: this.options.redisPrefix, + error: (error as Error).stack || String(error), + }); + + throw error; + } } protected async releaseDriver(driverFn?: DriverFactoryByDataSource, dataSource: string = 'default') { diff --git a/packages/cubejs-server-core/src/core/OrchestratorStorage.ts b/packages/cubejs-server-core/src/core/OrchestratorStorage.ts index cc1f6de078b00..9b1b80c7947fe 100644 --- a/packages/cubejs-server-core/src/core/OrchestratorStorage.ts +++ b/packages/cubejs-server-core/src/core/OrchestratorStorage.ts @@ -4,14 +4,35 @@ import type { OrchestratorApi } from './OrchestratorApi'; export class OrchestratorStorage { protected readonly storage: LRUCache; + protected readonly pendingReleases: Set> = new Set(); + public constructor(options: { compilerCacheSize?: number, maxCompilerCacheKeepAlive?: number, updateCompilerCacheKeepAlive?: boolean } = { compilerCacheSize: 100 }) { this.storage = new LRUCache({ max: options.compilerCacheSize, ttl: options.maxCompilerCacheKeepAlive, - updateAgeOnGet: options.updateCompilerCacheKeepAlive + updateAgeOnGet: options.updateCompilerCacheKeepAlive, + // Any removal reason ('evict' | 'set' | 'delete' | 'expire') means the api is not + // reachable through the cache anymore, so its connections have to be closed. + // Without it an evicted api keeps its Cube Store web socket open forever: the heartbeat + // interval holds a reference to the socket, so it is never garbage collected either. + // disposeAfter, not dispose: release() is async and calls into the drivers, while + // dispose runs synchronously inside set()/delete(). + disposeAfter: (api: OrchestratorApi) => { + this.release(api); + }, }); } + protected release(api: OrchestratorApi) { + const pending: Promise = api.release() + .then(() => undefined, () => undefined) + .then(() => { + this.pendingReleases.delete(pending); + }); + + this.pendingReleases.add(pending); + } + public has(orchestratorId: string) { return this.storage.has(orchestratorId); } @@ -37,7 +58,9 @@ export class OrchestratorStorage { } public async releaseConnections() { - await Promise.all([...this.storage.values()].map(api => api.release())); + // clear() disposes every entry, which schedules release() for each of them. this.storage.clear(); + + await Promise.all([...this.pendingReleases]); } } diff --git a/packages/cubejs-server-core/test/unit/OrchestratorStorage.test.ts b/packages/cubejs-server-core/test/unit/OrchestratorStorage.test.ts new file mode 100644 index 0000000000000..4a2cae2ed6a36 --- /dev/null +++ b/packages/cubejs-server-core/test/unit/OrchestratorStorage.test.ts @@ -0,0 +1,160 @@ +import { OrchestratorApi } from '../../src/core/OrchestratorApi'; +import { OrchestratorStorage } from '../../src/core/OrchestratorStorage'; + +/** + * `OrchestratorStorage` only ever calls `release()` on what it holds, so the + * entry is stubbed down to that: the release lifecycle inside `OrchestratorApi` + * is its own unit. + */ +function createApi(release: () => Promise = async () => undefined) { + const api = { release: jest.fn(release) }; + + return { api: api as unknown as OrchestratorApi, release: api.release }; +} + +/** + * A real `OrchestratorApi` with a stubbed `QueryOrchestrator`, for the one case + * that is about what the api logs rather than what the storage does with it. + */ +function createRealApi(driverError: Error) { + const logger = jest.fn(); + const driver = { release: async () => { throw driverError; } }; + + const api = new OrchestratorApi( + async () => driver as any, + logger, + { + cacheAndQueueDriver: 'memory', + contextToDbType: async () => 'postgres', + contextToExternalDbType: () => 'cubestore', + redisPrefix: 'tenant-1', + } + ); + + (api as any).orchestrator = { cleanup: async () => undefined }; + + // A data source the orchestrator has touched, so the release has a driver to + // close -- and this one fails to close. + api.addDataSeenSource('default'); + + return { api, logger }; +} + +// lru-cache runs `disposeAfter` once the operation that removed the entry has +// finished, and the release it schedules is asynchronous. +const flush = () => new Promise(resolve => setImmediate(resolve)); + +describe('OrchestratorStorage', () => { + test('releases an orchestrator the LRU evicts, and keeps the one still cached', async () => { + const storage = new OrchestratorStorage({ compilerCacheSize: 1 }); + const evicted = createApi(); + const kept = createApi(); + + storage.set('evicted', evicted.api); + storage.set('kept', kept.api); + await flush(); + + expect(storage.has('evicted')).toBe(false); + expect(evicted.release).toHaveBeenCalledTimes(1); + + expect(storage.has('kept')).toBe(true); + expect(kept.release).not.toHaveBeenCalled(); + }); + + test('releases an orchestrator replaced under the same id', async () => { + const storage = new OrchestratorStorage({ compilerCacheSize: 10 }); + const first = createApi(); + const second = createApi(); + + storage.set('same-id', first.api); + storage.set('same-id', second.api); + await flush(); + + expect(first.release).toHaveBeenCalledTimes(1); + expect(second.release).not.toHaveBeenCalled(); + }); + + test('setting the same instance again does not release it', async () => { + const storage = new OrchestratorStorage({ compilerCacheSize: 10 }); + const { api, release } = createApi(); + + storage.set('same-id', api); + storage.set('same-id', api); + await flush(); + + expect(storage.has('same-id')).toBe(true); + expect(release).not.toHaveBeenCalled(); + }); + + test('releaseConnections waits for the releases it schedules', async () => { + const storage = new OrchestratorStorage({ compilerCacheSize: 10 }); + let finishRelease: () => void = () => undefined; + const { api, release } = createApi(() => new Promise(resolve => { + finishRelease = () => resolve(); + })); + + storage.set('a', api); + + let settled = false; + const releasing = storage.releaseConnections().then(() => { settled = true; }); + await flush(); + + // `clear()` only schedules the releases, so shutdown has to wait for them: + // returning here would let the process exit with the connections still open. + expect(release).toHaveBeenCalledTimes(1); + expect(settled).toBe(false); + + finishRelease(); + await releasing; + + expect(settled).toBe(true); + expect(storage.has('a')).toBe(false); + }); + + test('releaseConnections resolves and clears even when a release fails', async () => { + const storage = new OrchestratorStorage({ compilerCacheSize: 10 }); + const failing = createApi(async () => { throw new Error('driver is gone'); }); + const ok = createApi(); + + storage.set('failing', failing.api); + storage.set('ok', ok.api); + + await expect(storage.releaseConnections()).resolves.toBeUndefined(); + + expect(ok.release).toHaveBeenCalledTimes(1); + expect(storage.has('failing')).toBe(false); + expect(storage.has('ok')).toBe(false); + }); + + test('a release that fails is logged rather than lost', async () => { + const storage = new OrchestratorStorage({ compilerCacheSize: 1 }); + const { api, logger } = createRealApi(new Error('driver is gone')); + + storage.set('failing', api); + storage.set('next', createApi().api); + await flush(); + + // The storage swallows the rejection so shutdown cannot fail on it, which + // leaves the log as the only signal that a connection stayed open. + expect(logger).toHaveBeenCalledWith( + 'Orchestrator Release Error', + expect.objectContaining({ + orchestratorId: 'tenant-1', + error: expect.stringContaining('driver is gone'), + }) + ); + }); + + test('forgets a release once it has finished', async () => { + const storage = new OrchestratorStorage({ compilerCacheSize: 1 }); + + storage.set('a', createApi().api); + storage.set('b', createApi().api); + await storage.releaseConnections(); + await flush(); + + // Otherwise the set grows by one entry per eviction -- a smaller leak in + // place of the one being fixed. + expect((storage as any).pendingReleases.size).toBe(0); + }); +}); From 3198a905aacfb3fde621c5046dab0326cf51af7b Mon Sep 17 00:00:00 2001 From: Dmitry Patsura Date: Thu, 27 Aug 2026 22:26:32 +0200 Subject: [PATCH 3/8] refactor(query-orchestrator): Rework QueryCache.cacheQueryResult (#11672) --- .../src/orchestrator/QueryCache.ts | 393 +++++++++++------- .../test/unit/QueryCache.test.ts | 285 +++++++++++++ 2 files changed, 526 insertions(+), 152 deletions(-) diff --git a/packages/cubejs-query-orchestrator/src/orchestrator/QueryCache.ts b/packages/cubejs-query-orchestrator/src/orchestrator/QueryCache.ts index 70d2c35f86958..c500df4681d8f 100644 --- a/packages/cubejs-query-orchestrator/src/orchestrator/QueryCache.ts +++ b/packages/cubejs-query-orchestrator/src/orchestrator/QueryCache.ts @@ -121,13 +121,30 @@ export type CacheKey = [CacheKeyItem, CacheKeyItem, CacheKeyItem] | [CacheKeyItem, CacheKeyItem, CacheKeyItem, CacheKeyItem]; -type CacheEntry = { +export type CacheEntry = { time: number; result: any; - renewalKey: string; + renewalKey?: string; requestId?: string; }; +export enum CacheAction { + ServeCached = 'serve-cached', + RefreshSameRequest = 'refresh-same-request', + RefreshBackground = 'refresh-background', + WaitForRenew = 'wait-for-renew', +} + +type CacheOperationContext = { + cacheKey: CacheKey; + redisKey: string; + renewalKey?: string; + expiration: number; + spanId: string; + options: CacheQueryResultOptions; + log: (message: string, extra?: Record) => void; +}; + export interface QueryCacheOptions { refreshKeyRenewalThreshold?: number; externalQueueOptions?: any; @@ -156,6 +173,8 @@ export class QueryCache { protected memoryCache: LRUCache; + protected static readonly IN_MEMORY_CACHE_DISABLE_PERIOD = 5 * 60 * 1000; + public constructor( protected readonly cachePrefix: string, protected readonly driverFactory: DriverFactoryByDataSource, @@ -888,174 +907,244 @@ export class QueryCache { callback: () => MaybeCancelablePromise, ) => this.cacheDriver.withLock(`lock:${key}`, callback, ttl, true); - public async cacheQueryResult( - query: string | QueryWithParams, - values: string[], + protected static decideCacheAction( + entry: CacheEntry, + renewedAgo: number, + options: CacheQueryResultOptions, + renewalKey?: string, + ): CacheAction { + const { renewalThreshold } = options; + const isExpired = !renewalThreshold || !entry.time || renewedAgo > renewalThreshold * 1000; + const isKeyMismatch = !!renewalKey && entry.renewalKey !== renewalKey; + + if (!isExpired && !isKeyMismatch) { + return CacheAction.ServeCached; + } + + const isSameRequest = options.requestId && entry.requestId && + QueryCache.extractRequestUUID(entry.requestId) === QueryCache.extractRequestUUID(options.requestId); + + // A client polling through continue-wait re-enters with the same requestId, so rejecting + // the result it just wrote would restart the fetch on every poll and never converge while + // the refreshKey keeps moving. Background renew opts out: fresh data is all it exists for. + if (isSameRequest && !options.renewCycle) { + return CacheAction.RefreshSameRequest; + } + + // Without a refreshKey there is nothing to refresh against, so an elapsed threshold alone never triggers a fetch. + if (!renewalKey) { + return CacheAction.ServeCached; + } + + return options.waitForRenew ? CacheAction.WaitForRenew : CacheAction.RefreshBackground; + } + + protected static isMemoryEntryUsable( + entry: CacheEntry, + renewedAgo: number, + expiration: number, + renewalThreshold?: number, + renewalKey?: string, + ): boolean { + if (renewedAgo > expiration * 1000 || renewedAgo > QueryCache.IN_MEMORY_CACHE_DISABLE_PERIOD) { + return false; + } + + if (!renewalKey) { + return true; + } + + // Near expiry an in-memory entry races with refreshes carrying a different refreshKey value. + return !!renewalThreshold && + !!entry.time && + renewedAgo + QueryCache.IN_MEMORY_CACHE_DISABLE_PERIOD <= renewalThreshold * 1000 && + entry.renewalKey === renewalKey; + } + + protected cacheOperationContext( cacheKey: CacheKey, expiration: number, options: CacheQueryResultOptions, - ) { + ): CacheOperationContext { const spanId = crypto.randomBytes(16).toString('hex'); - options = options || { dataSource: 'default' }; - const { renewalThreshold, primaryQuery, renewCycle } = options; - const renewalKey = options.renewalKey && this.queryRedisKey(options.renewalKey); - const redisKey = this.queryRedisKey(cacheKey); - const fetchNew = () => ( - this.queryWithRetryAndRelease(query, values, { - cacheKey, - priority: options.priority, - external: options.external, + const logContext = { + cacheKey, + requestId: options.requestId, + spanId, + primaryQuery: options.primaryQuery, + renewCycle: options.renewCycle, + }; + + return { + cacheKey, + redisKey: this.queryRedisKey(cacheKey), + renewalKey: options.renewalKey && this.queryRedisKey(options.renewalKey), + expiration, + spanId, + options, + log: (message, extra) => this.logger(message, extra ? { ...logContext, ...extra } : logContext), + }; + } + + protected fetchAndCacheQuery( + query: string | QueryWithParams, + values: string[], + ctx: CacheOperationContext, + ) { + const { cacheKey, redisKey, renewalKey, expiration, spanId, options } = ctx; + + return this.queryWithRetryAndRelease(query, values, { + cacheKey, + priority: options.priority, + external: options.external, + requestId: options.requestId, + spanId, + persistent: options.persistent, + dataSource: options.dataSource, + useCsvQuery: options.useCsvQuery, + lambdaTypes: options.lambdaTypes, + }).then(res => { + const entry = { + time: (new Date()).getTime(), + result: res, + renewalKey, requestId: options.requestId, - spanId, - persistent: options.persistent, - dataSource: options.dataSource, - useCsvQuery: options.useCsvQuery, - lambdaTypes: options.lambdaTypes, - }).then(res => { - const result = { - time: (new Date()).getTime(), - result: res, - renewalKey, - requestId: options.requestId, - }; - return this - .cacheDriver - .set(redisKey, result, expiration) - .then(({ bytes }) => { - this.logger('Renewed', { cacheKey, requestId: options.requestId, spanId, primaryQuery, renewCycle }); - this.logger('Outgoing network usage', { - service: 'cache', - requestId: options.requestId, - spanId, - bytes, - cacheKey, - }); - return res; - }); - }).catch(e => { - if (!(e instanceof ContinueWaitError)) { - this.logger('Dropping Cache', { - cacheKey, - error: e.stack || e, + }; + + return this + .cacheDriver + .set(redisKey, entry, expiration) + .then(({ bytes }) => { + ctx.log('Renewed'); + this.logger('Outgoing network usage', { + service: 'cache', requestId: options.requestId, spanId, - primaryQuery, - renewCycle + bytes, + cacheKey, }); - this.cacheDriver.remove(redisKey) - .catch(err => this.logger('Error removing key', { - cacheKey, - spanId, - error: err.stack || err, - requestId: options.requestId - })); - } - throw e; - }) - ); + return res; + }); + }).catch(e => { + if (!(e instanceof ContinueWaitError)) { + ctx.log('Dropping Cache', { error: e.stack || e }); + this.cacheDriver.remove(redisKey) + .catch(err => this.logger('Error removing key', { + cacheKey, + spanId, + error: err.stack || err, + requestId: options.requestId + })); + } + throw e; + }); + } + + protected fetchAndCacheQueryInBackground( + query: string | QueryWithParams, + values: string[], + ctx: CacheOperationContext, + ): void { + this.fetchAndCacheQuery(query, values, ctx).catch(e => { + if (!(e instanceof ContinueWaitError)) { + ctx.log('Error renewing', { error: e.stack || e }); + } + }); + } + + protected getFromMemoryCache(ctx: CacheOperationContext): CacheEntry | null { + const { redisKey, renewalKey, expiration, options } = ctx; + const entry = this.memoryCache.get(redisKey); + + if (!entry) { + return null; + } + + const renewedAgo = (new Date()).getTime() - entry.time; + + if (!QueryCache.isMemoryEntryUsable(entry, renewedAgo, expiration, options.renewalThreshold, renewalKey)) { + this.memoryCache.delete(redisKey); + return null; + } + + ctx.log('Found in memory cache entry', { + time: entry.time, + renewedAgo, + renewalKey: entry.renewalKey, + newRenewalKey: renewalKey, + renewalThreshold: options.renewalThreshold, + }); + + return entry; + } + + protected storeInMemoryCache(entry: CacheEntry, renewedAgo: number, ctx: CacheOperationContext): void { + const { useInMemory, renewalThreshold } = ctx.options; + + if (useInMemory && !!renewalThreshold && + renewedAgo + QueryCache.IN_MEMORY_CACHE_DISABLE_PERIOD <= renewalThreshold * 1000) { + this.memoryCache.set(ctx.redisKey, entry); + } + } + + public async cacheQueryResult( + query: string | QueryWithParams, + values: string[], + cacheKey: CacheKey, + expiration: number, + options: CacheQueryResultOptions, + ) { + options = options || { dataSource: 'default' }; + + const ctx = this.cacheOperationContext(cacheKey, expiration, options); + const { renewalThreshold } = options; if (options.forceNoCache) { - this.logger('Force no cache for', { cacheKey, requestId: options.requestId, spanId, primaryQuery, renewCycle }); - return fetchNew(); + ctx.log('Force no cache for'); + return this.fetchAndCacheQuery(query, values, ctx); } - let res; - - const inMemoryCacheDisablePeriod = 5 * 60 * 1000; - - if (options.useInMemory) { - const inMemoryValue = this.memoryCache.get(redisKey); - if (inMemoryValue) { - const renewedAgo = (new Date()).getTime() - inMemoryValue.time; - - if ( - renewalKey && ( - !renewalThreshold || - !inMemoryValue.time || - // Do not cache in memory in last 5 minutes of expiry. - // Most likely it'll cause race condition of refreshing data with different refreshKey values. - renewedAgo + inMemoryCacheDisablePeriod > renewalThreshold * 1000 || - inMemoryValue.renewalKey !== renewalKey - ) || renewedAgo > expiration * 1000 || renewedAgo > inMemoryCacheDisablePeriod - ) { - this.memoryCache.delete(redisKey); - } else { - this.logger('Found in memory cache entry', { - cacheKey, - time: inMemoryValue.time, - renewedAgo, - renewalKey: inMemoryValue.renewalKey, - newRenewalKey: renewalKey, - renewalThreshold, - requestId: options.requestId, - spanId, - primaryQuery, - renewCycle - }); - res = inMemoryValue; - } - } + let entry: CacheEntry | null = options.useInMemory ? this.getFromMemoryCache(ctx) : null; + + if (!entry) { + entry = await this.cacheDriver.get(ctx.redisKey); } - if (!res) { - res = await this.cacheDriver.get(redisKey); + if (!entry) { + ctx.log('Missing cache for'); + return this.fetchAndCacheQuery(query, values, ctx); } - if (res) { - const parsedResult = res; - const renewedAgo = (new Date()).getTime() - parsedResult.time; - this.logger('Found cache entry', { - cacheKey, - time: parsedResult.time, - renewedAgo, - renewalKey: parsedResult.renewalKey, - newRenewalKey: renewalKey, - renewalThreshold, - requestId: options.requestId, - spanId, - primaryQuery, - renewCycle - }); - - const isExpired = !renewalThreshold || !parsedResult.time || renewedAgo > renewalThreshold * 1000; - const isKeyMismatch = renewalKey && parsedResult.renewalKey !== renewalKey; - const isSameRequest = options.requestId && parsedResult.requestId && - QueryCache.extractRequestUUID(parsedResult.requestId) === QueryCache.extractRequestUUID(options.requestId); - - // Continue-wait cycle: result was produced by our request, - // refreshKey changed during execution — return cached, refresh in background. - // Skip for renewCycle — it must always fetch fresh data to keep cache up-to-date. - if (isSameRequest && !renewCycle && (isExpired || isKeyMismatch)) { - this.logger('Same request cache hit (background refresh)', { cacheKey, renewalThreshold, requestId: options.requestId, spanId, primaryQuery, renewCycle }); - fetchNew().catch(e => { - if (!(e instanceof ContinueWaitError)) { - this.logger('Error renewing', { cacheKey, error: e.stack || e, requestId: options.requestId, spanId, primaryQuery, renewCycle }); - } - }); - } else if (renewalKey && (isExpired || isKeyMismatch)) { - // Cache expired or refreshKey changed — need to refresh - if (options.waitForRenew) { - this.logger('Waiting for renew', { cacheKey, renewalThreshold, requestId: options.requestId, spanId, primaryQuery, renewCycle }); - return fetchNew(); - } else { - this.logger('Renewing existing key', { cacheKey, renewalThreshold, requestId: options.requestId, spanId, primaryQuery, renewCycle }); - fetchNew().catch(e => { - if (!(e instanceof ContinueWaitError)) { - this.logger('Error renewing', { cacheKey, error: e.stack || e, requestId: options.requestId, spanId, primaryQuery, renewCycle }); - } - }); - } - } + const renewedAgo = (new Date()).getTime() - entry.time; - this.logger('Using cache for', { cacheKey, requestId: options.requestId, spanId, primaryQuery, renewCycle }); - if (options.useInMemory && renewedAgo + inMemoryCacheDisablePeriod <= renewalThreshold * 1000) { - this.memoryCache.set(redisKey, parsedResult); - } - return parsedResult.result; - } else { - this.logger('Missing cache for', { cacheKey, requestId: options.requestId, spanId, primaryQuery, renewCycle }); - return fetchNew(); + ctx.log('Found cache entry', { + time: entry.time, + renewedAgo, + renewalKey: entry.renewalKey, + newRenewalKey: ctx.renewalKey, + renewalThreshold, + }); + + switch (QueryCache.decideCacheAction(entry, renewedAgo, options, ctx.renewalKey)) { + case CacheAction.WaitForRenew: + ctx.log('Waiting for renew', { renewalThreshold }); + return this.fetchAndCacheQuery(query, values, ctx); + case CacheAction.RefreshSameRequest: + ctx.log('Same request cache hit (background refresh)', { renewalThreshold }); + this.fetchAndCacheQueryInBackground(query, values, ctx); + break; + case CacheAction.RefreshBackground: + ctx.log('Renewing existing key', { renewalThreshold }); + this.fetchAndCacheQueryInBackground(query, values, ctx); + break; + default: + break; } + + ctx.log('Using cache for'); + this.storeInMemoryCache(entry, renewedAgo, ctx); + + return entry.result; } protected async lastRefreshTime(cacheKey) { diff --git a/packages/cubejs-query-orchestrator/test/unit/QueryCache.test.ts b/packages/cubejs-query-orchestrator/test/unit/QueryCache.test.ts index 6cdbdb684da2d..578ebc3d89de0 100644 --- a/packages/cubejs-query-orchestrator/test/unit/QueryCache.test.ts +++ b/packages/cubejs-query-orchestrator/test/unit/QueryCache.test.ts @@ -1,5 +1,290 @@ +import { CacheAction, CacheEntry, CacheQueryResultOptions, QueryCache } from '../../src'; import { QueryCacheTest } from './QueryCache.abstract'; QueryCacheTest('Local', { cacheAndQueueDriver: 'memory', }); + +class QueryCacheOpened extends QueryCache { + public static get disablePeriod(): number { + return this.IN_MEMORY_CACHE_DISABLE_PERIOD; + } + + public static decideCacheAction( + entry: Partial, + renewedAgo: number, + options: Partial, + renewalKey?: string, + ): CacheAction { + return super.decideCacheAction( + entry as CacheEntry, + renewedAgo, + options as CacheQueryResultOptions, + renewalKey, + ); + } + + public static isMemoryEntryUsable( + entry: Partial, + renewedAgo: number, + expiration: number, + renewalThreshold?: number, + renewalKey?: string, + ): boolean { + return super.isMemoryEntryUsable( + entry as CacheEntry, + renewedAgo, + expiration, + renewalThreshold, + renewalKey, + ); + } +} + +describe('QueryCache.decideCacheAction', () => { + const THRESHOLD = 600; + const NOT_EXPIRED = 100 * 1000; + const EXPIRED = 700 * 1000; + + type Case = { + name: string, + entry: Partial, + renewedAgo: number, + options: Partial, + renewalKey?: string, + expected: CacheAction, + }; + + const cases: Case[] = [ + { + name: 'not expired and renewal key matches: serves cache even for the same request', + entry: { time: 1, renewalKey: 'rk', requestId: 'req-1-span-1' }, + renewedAgo: NOT_EXPIRED, + options: { renewalThreshold: THRESHOLD, requestId: 'req-1-span-2', waitForRenew: true }, + renewalKey: 'rk', + expected: CacheAction.ServeCached, + }, + { + name: 'expired with renewal key and waitForRenew: blocks on fetch', + entry: { time: 1, renewalKey: 'rk', requestId: 'req-1' }, + renewedAgo: EXPIRED, + options: { renewalThreshold: THRESHOLD, requestId: 'req-2', waitForRenew: true }, + renewalKey: 'rk', + expected: CacheAction.WaitForRenew, + }, + { + name: 'expired with renewal key without waitForRenew: refreshes in background', + entry: { time: 1, renewalKey: 'rk', requestId: 'req-1' }, + renewedAgo: EXPIRED, + options: { renewalThreshold: THRESHOLD, requestId: 'req-2', waitForRenew: false }, + renewalKey: 'rk', + expected: CacheAction.RefreshBackground, + }, + { + name: 'renewal key mismatch while not expired and waitForRenew: blocks on fetch', + entry: { time: 1, renewalKey: 'old', requestId: 'req-1' }, + renewedAgo: NOT_EXPIRED, + options: { renewalThreshold: THRESHOLD, requestId: 'req-2', waitForRenew: true }, + renewalKey: 'new', + expected: CacheAction.WaitForRenew, + }, + { + name: 'renewal key mismatch while not expired without waitForRenew: refreshes in background', + entry: { time: 1, renewalKey: 'old', requestId: 'req-1' }, + renewedAgo: NOT_EXPIRED, + options: { renewalThreshold: THRESHOLD, requestId: 'req-2', waitForRenew: false }, + renewalKey: 'new', + expected: CacheAction.RefreshBackground, + }, + { + name: 'same request (different span) and expired: serves stale, refreshes in background', + entry: { time: 1, renewalKey: 'rk', requestId: 'req-1-span-1' }, + renewedAgo: EXPIRED, + options: { renewalThreshold: THRESHOLD, requestId: 'req-1-span-7', waitForRenew: true }, + renewalKey: 'rk', + expected: CacheAction.RefreshSameRequest, + }, + { + name: 'same request and renewal key mismatch: serves stale, refreshes in background', + entry: { time: 1, renewalKey: 'old', requestId: 'req-1-span-1' }, + renewedAgo: NOT_EXPIRED, + options: { renewalThreshold: THRESHOLD, requestId: 'req-1-span-7', waitForRenew: true }, + renewalKey: 'new', + expected: CacheAction.RefreshSameRequest, + }, + { + name: 'renew cycle never serves stale: expired same request blocks on fetch', + entry: { time: 1, renewalKey: 'rk', requestId: 'req-1-span-1' }, + renewedAgo: EXPIRED, + options: { renewalThreshold: THRESHOLD, requestId: 'req-1-span-7', waitForRenew: true, renewCycle: true }, + renewalKey: 'rk', + expected: CacheAction.WaitForRenew, + }, + { + name: 'renew cycle never serves stale: key mismatch without waitForRenew refreshes in background', + entry: { time: 1, renewalKey: 'old', requestId: 'req-1-span-1' }, + renewedAgo: NOT_EXPIRED, + options: { renewalThreshold: THRESHOLD, requestId: 'req-1-span-7', waitForRenew: false, renewCycle: true }, + renewalKey: 'new', + expected: CacheAction.RefreshBackground, + }, + { + name: 'expired without a renewal key: keeps serving cache', + entry: { time: 1, requestId: 'req-1' }, + renewedAgo: EXPIRED, + options: { renewalThreshold: THRESHOLD, requestId: 'req-2', waitForRenew: true }, + expected: CacheAction.ServeCached, + }, + { + name: 'expired without a renewal key but same request: refreshes in background', + entry: { time: 1, requestId: 'req-1-span-1' }, + renewedAgo: EXPIRED, + options: { renewalThreshold: THRESHOLD, requestId: 'req-1-span-7', waitForRenew: true }, + expected: CacheAction.RefreshSameRequest, + }, + { + name: 'missing renewal threshold counts as expired', + entry: { time: 1, renewalKey: 'rk', requestId: 'req-1' }, + renewedAgo: 0, + options: { requestId: 'req-2', waitForRenew: true }, + renewalKey: 'rk', + expected: CacheAction.WaitForRenew, + }, + { + name: 'entry without a timestamp counts as expired', + entry: { time: 0, renewalKey: 'rk', requestId: 'req-1' }, + renewedAgo: 0, + options: { renewalThreshold: THRESHOLD, requestId: 'req-2', waitForRenew: false }, + renewalKey: 'rk', + expected: CacheAction.RefreshBackground, + }, + { + name: 'entry without a request id is never treated as the same request', + entry: { time: 1, renewalKey: 'rk' }, + renewedAgo: EXPIRED, + options: { renewalThreshold: THRESHOLD, requestId: 'req-1', waitForRenew: true }, + renewalKey: 'rk', + expected: CacheAction.WaitForRenew, + }, + ]; + + cases.forEach(({ name, entry, renewedAgo, options, renewalKey, expected }) => { + it(name, () => { + expect(QueryCacheOpened.decideCacheAction(entry, renewedAgo, options, renewalKey)).toEqual(expected); + }); + }); +}); + +describe('QueryCache.isMemoryEntryUsable', () => { + const WINDOW = QueryCacheOpened.disablePeriod; + const HOUR = 3600; + + type Case = { + name: string, + entry: Partial, + renewedAgo: number, + expiration: number, + renewalThreshold?: number, + renewalKey?: string, + expected: boolean, + }; + + const cases: Case[] = [ + { + name: 'past expiration but still inside the in-memory window: unusable', + entry: { time: 1 }, + renewedAgo: 61 * 1000, + expiration: 60, + expected: false, + }, + { + name: 'past the in-memory window but nowhere near expiration: unusable', + entry: { time: 1 }, + renewedAgo: WINDOW + 1, + expiration: HOUR, + expected: false, + }, + { + name: 'exactly at the in-memory window: usable', + entry: { time: 1 }, + renewedAgo: WINDOW, + expiration: HOUR, + expected: true, + }, + { + name: 'exactly at expiration: usable', + entry: { time: 1 }, + renewedAgo: 60 * 1000, + expiration: 60, + expected: true, + }, + { + name: 'without a renewal key the renewal checks are skipped: no threshold and no timestamp still usable', + entry: { time: 0 }, + renewedAgo: 1000, + expiration: HOUR, + expected: true, + }, + { + name: 'renewal key without a renewal threshold: unusable', + entry: { time: 1, renewalKey: 'rk' }, + renewedAgo: 1000, + expiration: HOUR, + renewalKey: 'rk', + expected: false, + }, + { + name: 'renewal key with an entry that has no timestamp: unusable', + entry: { time: 0, renewalKey: 'rk' }, + renewedAgo: 1000, + expiration: HOUR, + renewalThreshold: HOUR, + renewalKey: 'rk', + expected: false, + }, + { + name: 'threshold below twice the window, entry inside the last window before expiry: unusable', + entry: { time: 1, renewalKey: 'rk' }, + renewedAgo: 100 * 1000, + expiration: HOUR, + renewalThreshold: 300, + renewalKey: 'rk', + expected: false, + }, + { + name: 'threshold below twice the window, entry exactly at the last window before expiry: usable', + entry: { time: 1, renewalKey: 'rk' }, + renewedAgo: 100 * 1000, + expiration: HOUR, + renewalThreshold: 400, + renewalKey: 'rk', + expected: true, + }, + { + name: 'renewal key mismatch: unusable', + entry: { time: 1, renewalKey: 'old' }, + renewedAgo: 1000, + expiration: HOUR, + renewalThreshold: HOUR, + renewalKey: 'new', + expected: false, + }, + { + name: 'every check satisfied: usable', + entry: { time: 1, renewalKey: 'rk' }, + renewedAgo: 1000, + expiration: HOUR, + renewalThreshold: HOUR, + renewalKey: 'rk', + expected: true, + }, + ]; + + cases.forEach(({ name, entry, renewedAgo, expiration, renewalThreshold, renewalKey, expected }) => { + it(name, () => { + expect( + QueryCacheOpened.isMemoryEntryUsable(entry, renewedAgo, expiration, renewalThreshold, renewalKey) + ).toEqual(expected); + }); + }); +}); From c726236ce9b2650c1d9301ea833b027b79b9ef10 Mon Sep 17 00:00:00 2001 From: Alex Vasilev Date: Thu, 27 Aug 2026 16:52:20 -0400 Subject: [PATCH 4/8] docs: document embed header controls and standalone explorations (#11673) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * docs: document embed header controls and standalone explorations - Add URL parameters for hiding individual embedded-dashboard header controls (title, back button, edit, duplicate, or the whole bar), with a pointer from the Creator Mode page. - Clarify that a saved item without a workbook is a standalone exploration (the same object as a workbook report, just unfiled), documented consistently across Analytics Chat, Explore, Workbooks, Python analysis, and the MCP server's createReport tool. * docs: tighten wording and drop an unverified add-in claim - Simplify the allowExport comparison and drop CSS-level detail in favor of the actual precedence rule between showDashboardHeader and the per-control parameters. - Call out that the header-control parameters are URL-only, not part of the Generate Session settings object, with a matching pointer from the API reference. - Remove a claim that a saved Python analysis can be opened in Excel or Google Sheets — the add-ins render a report's raw SQL result, not a Python script's output, so that combination doesn't work. - Trim a features-list bullet that had grown a how-to prompt inside it. * docs: align the Python analysis feature bullet with the exploration terminology Same list on the same page already said saved results can be a report or a standalone exploration; the Python bullet still said "workbook" only. * docs: fix a dangling pronoun reference in the Python save sentence * docs: consolidate two duplicated callouts into one * docs: fix a pronoun reaching past its intended antecedent --------- Co-authored-by: Claude --- .../docs/explore-analyze/analytics-chat.mdx | 4 +- .../docs/explore-analyze/explore.mdx | 6 ++- .../docs/explore-analyze/workbooks/index.mdx | 3 ++ .../workbooks/python-analysis.mdx | 7 +-- .../docs/integrations/mcp-server.mdx | 2 +- .../embedding/iframe/creator-mode.mdx | 7 +++ docs-mintlify/embedding/iframe/dashboards.mdx | 43 +++++++++++++++++++ .../reference/embed-apis/generate-session.mdx | 4 ++ 8 files changed, 69 insertions(+), 7 deletions(-) diff --git a/docs-mintlify/docs/explore-analyze/analytics-chat.mdx b/docs-mintlify/docs/explore-analyze/analytics-chat.mdx index a5c4671f82d56..d92407675d616 100644 --- a/docs-mintlify/docs/explore-analyze/analytics-chat.mdx +++ b/docs-mintlify/docs/explore-analyze/analytics-chat.mdx @@ -13,10 +13,10 @@ The AI agent interprets your questions, generates queries against your semantic - **Natural language queries** – Ask questions in plain language without knowing SQL - **Multi-query reasoning** – The AI creates and synthesizes multiple queries for complex questions -- **Python analysis** – For work SQL can't express — forecasting, cohort analysis, statistical tests — the agent can run [Python](/docs/explore-analyze/workbooks/python-analysis) and render the result inline, then save it to a workbook as a re-runnable report (in preview) +- **Python analysis** – For work SQL can't express — forecasting, cohort analysis, statistical tests — the agent can run [Python](/docs/explore-analyze/workbooks/python-analysis) and render the result inline, then save it as a re-runnable report or exploration (in preview) - **Semantic model integration** – All queries run against your semantic model with proper access control and security, honoring the active [security context](/docs/explore-analyze/workbooks/querying-data#applying-a-security-context)—including an override applied by a developer or admin - **Queued messages** – Send follow-up messages while the agent is still processing -- **Save to Workbook** – Export insights to [Workbooks](/docs/explore-analyze/workbooks) for further analysis +- **Save your results** – Ask the agent to save a result as a [report](/docs/explore-analyze/workbooks) inside a workbook, or as a standalone [exploration](/docs/explore-analyze/explore#saving-explorations) when you don't want to create a workbook ## Discover available fields diff --git a/docs-mintlify/docs/explore-analyze/explore.mdx b/docs-mintlify/docs/explore-analyze/explore.mdx index 44e29e7216cc6..44078c278f8b0 100644 --- a/docs-mintlify/docs/explore-analyze/explore.mdx +++ b/docs-mintlify/docs/explore-analyze/explore.mdx @@ -50,7 +50,11 @@ Explore results carry the same [freshness and pre-aggregation indicators](/docs/ ## Saving explorations You can save an exploration so you can return to it later without -re-building your query. +re-building your query. An exploration is the same kind of object as a +workbook [report](/docs/explore-analyze/workbooks) — the only difference is +whether it belongs to a workbook. [Analytics Chat](/docs/explore-analyze/analytics-chat) +and the Cube MCP `createReport` tool save a standalone exploration the same +way: by leaving the workbook unset. - **Save** — For an unsaved exploration, click **Save** to open the **Save Exploration** dialog. Give your exploration a name and click diff --git a/docs-mintlify/docs/explore-analyze/workbooks/index.mdx b/docs-mintlify/docs/explore-analyze/workbooks/index.mdx index 390915ad0af6b..e1f43bc5bc698 100644 --- a/docs-mintlify/docs/explore-analyze/workbooks/index.mdx +++ b/docs-mintlify/docs/explore-analyze/workbooks/index.mdx @@ -6,6 +6,9 @@ title: Workbooks Workbooks allow you to build reports and explore data with AI agents, organize the results of your analysis, and share trusted insights with your team. +A report inside a workbook and a standalone [exploration](/docs/explore-analyze/explore#saving-explorations) +are the same kind of object — the only difference is whether it's filed inside a workbook. + ## Tabs Workbooks can contain one or more tabs, each providing a different way to query and analyze your data. Tabs enable you to organize multiple analyses within a single workbook, making it easy to explore different aspects of your data or combine insights from different sources. diff --git a/docs-mintlify/docs/explore-analyze/workbooks/python-analysis.mdx b/docs-mintlify/docs/explore-analyze/workbooks/python-analysis.mdx index 74daa648ae53f..2d54566a87f78 100644 --- a/docs-mintlify/docs/explore-analyze/workbooks/python-analysis.mdx +++ b/docs-mintlify/docs/explore-analyze/workbooks/python-analysis.mdx @@ -32,9 +32,10 @@ Ask for the analysis in natural language — "forecast next quarter's revenue", result inline in the [chat thread](/docs/explore-analyze/analytics-chat). This result is ephemeral by default. -Ask to **save it to a workbook** and Cube persists both the code and the run result -onto a report, which then renders the saved output without re-running. Saving -re-executes the analysis. +Ask to **save it** — to a workbook, or as a standalone +[exploration](/docs/explore-analyze/explore#saving-explorations) — and Cube persists +both the code and the run result. Opening the saved copy renders that output without +re-running; saving re-executes the analysis. diff --git a/docs-mintlify/docs/integrations/mcp-server.mdx b/docs-mintlify/docs/integrations/mcp-server.mdx index a329c8001bb6f..8db7500f06543 100644 --- a/docs-mintlify/docs/integrations/mcp-server.mdx +++ b/docs-mintlify/docs/integrations/mcp-server.mdx @@ -259,7 +259,7 @@ programmatically. Creating and editing workbooks requires the Explorer role or h | --- | --- | --- | | `readWorkbook` | Reads a workbook — its name and its current dashboard draft and published configs. | Read-only | | `createWorkbook` | Creates a new empty workbook, the container that holds reports and a dashboard. | Write | -| `createReport` | Saves a query plus its visualization as a report inside a workbook, and returns the `reportId` a chart widget references. | Write | +| `createReport` | Saves a query plus its visualization as a report. Pass `workbookId` to file it inside a workbook and get the `reportId` a chart widget references; omit `workbookId` to save a standalone [exploration](/docs/explore-analyze/explore#saving-explorations) instead. | Write | | `readReport` | Reads one saved report by id — its title, SQL, chart spec, placement, and a shareable URL. | Read-only | | `updateReport` | Edits an existing report in place, keeping its `reportId`. Send only the fields you want to change. | Destructive — prompts | | `deleteReport` | Deletes a report. | Destructive — prompts | diff --git a/docs-mintlify/embedding/iframe/creator-mode.mdx b/docs-mintlify/embedding/iframe/creator-mode.mdx index d4d5f49736c96..07694a34c25c6 100644 --- a/docs-mintlify/embedding/iframe/creator-mode.mdx +++ b/docs-mintlify/embedding/iframe/creator-mode.mdx @@ -15,6 +15,13 @@ To enable it, pass `creatorMode: true` to the [Generate Session API][ref-generat Creator Mode requires [Signed embedding](/embedding/iframe/auth/signed). Private embedding is not supported. +## Dashboard header controls + +When a user opens a dashboard inside Creator Mode, its header renders the +same as in a standalone dashboard embed. To hide any of its controls (title, +back button, Edit, Duplicate) for your embed, see +[Show or hide header controls](/embedding/iframe/dashboards#show-or-hide-header-controls). + ## Embed tenant scoping In Creator Mode, content (workbooks, dashboards) and the groups/user attributes referenced by the session are scoped to an **embed tenant**. Pass `embedTenantName` to isolate content per customer; omit it to use the current tenant. diff --git a/docs-mintlify/embedding/iframe/dashboards.mdx b/docs-mintlify/embedding/iframe/dashboards.mdx index 3bf630d168ef9..955f950b23a79 100644 --- a/docs-mintlify/embedding/iframe/dashboards.mdx +++ b/docs-mintlify/embedding/iframe/dashboards.mdx @@ -129,6 +129,49 @@ This applies to embedded published dashboards; it does not affect the standalone [Analytics Chat](/embedding/iframe/analytics-chat) surface, where embedding the chat is itself the opt-in. +## Show or hide header controls + +By default, an embedded dashboard's header shows its title, back button, and +— when the viewer has permission — the Edit and Duplicate actions. Hide any +of them individually with URL parameters on the embed iframe `src`: + +| Parameter | Hides | +| --- | --- | +| `showDashboardHeader=false` | The entire header bar | +| `showDashboardBackButton=false` | The back button | +| `showDashboardTitle=false` | The dashboard title | +| `showDashboardEditButton=false` | The Edit action | +| `showDashboardDuplicateButton=false` | The Duplicate action | + +```text +https://your-tenant.cubecloud.dev/embed/dashboard/YOUR_DASHBOARD_PUBLIC_ID?session=YOUR_SESSION_ID&showDashboardBackButton=false&showDashboardTitle=false +``` + +Every one of these defaults to shown. Only the exact literal string `false` +hides a control — `=0`, `=False`, `=FALSE`, and a bare `?showDashboardTitle` +(no value) all leave it visible. Note this is the inverse of `allowExport` +above, where only the literal string `true` opts in. + + +These parameters are URL-only — they aren't accepted in the [Generate +Session](/reference/embed-apis/generate-session#session-settings) `settings` +object, unlike `showDashboardChat`, and they have no account-wide default +under **Embed → Settings**. + + +These parameters apply to the dashboard header only — the workbook header +(which carries Publish and Share) is unaffected. + +Setting `showDashboardHeader=false` overrides the other four: the whole bar +disappears regardless of their values. `showDashboardEditButton` can only +hide the Edit action; it can never show one to a viewer who lacks edit +permission. + +These parameters are read from the URL the host loaded the iframe with and +stay pinned for the life of the embed, so they survive in-app navigation +(for example, from the home-page dashboard card, or after publishing a +draft). + ## Set the language Embedded dashboards render their UI in the account's default language, which you can diff --git a/docs-mintlify/reference/embed-apis/generate-session.mdx b/docs-mintlify/reference/embed-apis/generate-session.mdx index e0f1bc0c73782..bb7454defa8e4 100644 --- a/docs-mintlify/reference/embed-apis/generate-session.mdx +++ b/docs-mintlify/reference/embed-apis/generate-session.mdx @@ -79,6 +79,10 @@ over the account-wide setting. |----------|------|----------|-------| | `showDashboardChat` | boolean | No | Show or hide the AI chat (agent panel and launcher bubble) on embedded dashboards for this session. Omit to inherit the account-wide **Show dashboard chat** toggle (**Embed → Settings**; shown by default); `false` hides it even if enabled account-wide, `true` shows it even if disabled. Affects embedded **published dashboards** only — not the standalone [embedded chat][ref-analytics-chat] surface. See [Hiding the AI chat][ref-signed-embedding]. | + +The dashboard header's title, back button, Edit, and Duplicate controls are hidden with URL parameters on the embed iframe instead — they aren't part of this `settings` object. See [Show or hide header controls](/embedding/iframe/dashboards#show-or-hide-header-controls). + + ## User profile `userProfile` lets you attach a human-readable display name and avatar to an external user so they render with a recognizable identity inside embedded surfaces (workbook owners, dashboard headers, etc.) instead of the raw `externalId`. From 6b7f4d688c4726a60e9a08a51c4e6cf62b822949 Mon Sep 17 00:00:00 2001 From: Alex Vasilev Date: Thu, 27 Aug 2026 17:27:45 -0400 Subject: [PATCH 5/8] docs(api-reference): sync Platform API docs for platform-client v0.4.0 (#11668) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * docs(api-reference): sync Platform API docs for platform-client v0.4.0 Regenerates api.yaml, docs.json and introduction.mdx from the cubejs-enterprise public OpenAPI spec, and changelog.mdx from the platform-client CHANGELOG.md, via scripts/extract-api.mjs and extract-changelog.mjs. Adds the new Region Private Links group to TAG_ORDER so it's placed after Regions instead of appended A-Z. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01XdLcEJjqSB78Zu4zdbo5yn * fix(api-reference): pick up the corrected platform-client v0.4.0 changelog Re-syncs changelog.mdx after cubedevinc/cubejs-enterprise#14466 fixed three inaccuracies in the release notes flagged by review: users/groups already had cursor pagination in 0.3.0 (search is what's new), the DeploymentEnvironment(Tokens)ListResponse.pagination narrowing is a TS break, and the report-placement field additions were unlisted. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01XdLcEJjqSB78Zu4zdbo5yn * fix(api-reference): exclude Cube-staff-only Regions/PrivateLinks ops from public docs Per review: every new Regions/PrivateLinks operation (create/get/update/ destroy/apply/provisioning-status/cloud-provider-catalog on regions, and all 5 PrivateLink ops) is gated "Cube super admin only" — no reader of these docs can call any of them. EXCLUDE_OPERATIONS already exists for exactly this class of route ("Account-level / internal admin APIs kept out of the public docs"), and no prior operation in this spec carried that banner, so this adds all 12 to it rather than leaving the sidebar's first customer-uncallable pages in by default. GET /api/v1/regions (listRegions) is untouched — any tenant can call it. The Region Private Links tag now has zero operations left, so it drops out of the nav automatically; reverts the TAG_ORDER entry added for it. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01XdLcEJjqSB78Zu4zdbo5yn * fix(api-reference): pick up the second platform-client v0.4.0 changelog correction Re-syncs after cubedevinc/cubejs-enterprise#14466's follow-up fix: the report-input field list is now split per schema (Connect/Create/Update gained different subsets), and ResourceGroupPolicyDto joins its sibling InheritedGroupPolicyDto for the name/userCount addition. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01XdLcEJjqSB78Zu4zdbo5yn * fix(api-reference): pick up the isStagingEnvironmentEnabled changelog note Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01XdLcEJjqSB78Zu4zdbo5yn * fix(api-reference): fail the sync when an EXCLUDE_OPERATIONS entry matches nothing Per review: the exclusion lookup silently no-ops if an entry's method+path stops matching (upstream rename/move), and --check can't catch that either — both the committed and freshly generated output would contain the leak. That's low-stakes for "stray/incomplete admin routes" but more load-bearing now that 12 entries carry a privacy intent (keeping Cube-staff-only Regions/PrivateLinks operations out of the public docs). Track which entries matched during the path loop and abort with the unmatched ones listed if any didn't. This immediately caught a real stale entry: `GET /api/v1/ai-engineer/ active-region` was removed from the source spec as a breaking change in platform-client 0.3.0, so its exclusion had already gone dead — removed. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01XdLcEJjqSB78Zu4zdbo5yn * fix(api-reference): pick up the Regions/PrivateLinks exclusion note Re-syncs after cubedevinc/cubejs-enterprise#14466 added a note that these SDK-typed operations are excluded from this reference (EXCLUDE_OPERATIONS). Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01XdLcEJjqSB78Zu4zdbo5yn * fix(api-reference): pick up the GET /api/v1/regions/ clarification Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01XdLcEJjqSB78Zu4zdbo5yn * fix(api-reference): pick up removal of the Regions/PrivateLinks changelog section Re-syncs after cubedevinc/cubejs-enterprise#14466 dropped the section per direction not to surface or mention Cube-staff-only endpoints anywhere, including the SDK changelog. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01XdLcEJjqSB78Zu4zdbo5yn * fix(api-reference): auto-detect and exclude Cube-staff-only operations from the routing Per direction: instead of hand-maintaining a list of super-admin-gated paths in EXCLUDE_OPERATIONS (which only protects endpoints someone remembered to add, and only re-validates the ones already listed), detect them structurally at generation time. console-server prefixes every super-admin-gated operation's OpenAPI description with SUPER_ADMIN_ONLY_DOC_MARKER (see cubedevinc/ cubejs-enterprise#14467 for the cross-reference comment on that constant). Matching on that exact text in the path loop — before applyDescription() moves the description into x-mint.content — catches any current or future staff-only operation automatically, with no per-endpoint bookkeeping in this repo. Removes the 12 manually-listed Regions/PrivateLinks entries from EXCLUDE_OPERATIONS (now redundant and would trip the fail-closed assertion from d24cdd8, since the auto-detection deletes them first). --check confirms byte-identical output to the hand-maintained list. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01XdLcEJjqSB78Zu4zdbo5yn * fix(api-reference): fail closed if the super-admin marker stops matching Per review: the marker-based auto-exclusion added in ab7fc3a is an exact-substring match against text authored in a different repo, so it fails OPEN (not closed) if that text drifts upstream — the opposite of what d24cdd8 just bought for the old hand-maintained list. Two guards restore fail-closed without depending on the marker staying exact: abort if the marker matched zero operations (today's spec always has staff-only ones), and a residual scan over every kept operation's rendered content for "super admin" (looser than the marker itself, specifically excluding the unrelated-but-similar 🔒 "Admin only" marker, which is legitimately public). Verified both actually fire: a marker that matches nothing aborts, and the real output is unaffected (byte-identical api.yaml/docs.json/introduction.mdx). Also narrowed the marker itself to a short, stable anchor ("Cube super admin only") rather than the full two-sentence constant, which is more exposed to upstream rewrapping — safe now that the residual scan is the actual backstop either way. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01XdLcEJjqSB78Zu4zdbo5yn * fix(api-reference): close partial-drift gap in the super-admin residual scan Widen the residual-scan regex from /super admin/i to /super[-\s]?admins?\b/i so a hyphenated "super-admin" rewording on a newly-added operation still trips the guard even when autoExcludedCount stays non-zero (the floor guard alone can't catch a partial drift, only a total reword). --------- Co-authored-by: Claude --- docs-mintlify/api-reference/api.yaml | 1146 ++++++++++++++++-- docs-mintlify/api-reference/changelog.mdx | 57 + docs-mintlify/api-reference/introduction.mdx | 2 +- docs-mintlify/docs.json | 5 + docs-mintlify/scripts/extract-api.mjs | 85 +- 5 files changed, 1204 insertions(+), 91 deletions(-) diff --git a/docs-mintlify/api-reference/api.yaml b/docs-mintlify/api-reference/api.yaml index f7b62151099cc..ef1d9aa05230e 100644 --- a/docs-mintlify/api-reference/api.yaml +++ b/docs-mintlify/api-reference/api.yaml @@ -105,19 +105,14 @@ paths: type: integer minimum: 0 - in: query - name: first - required: false + name: after schema: - type: integer - minimum: 1 - description: Page size for cursor-based pagination + type: string - in: query - name: after - required: false + name: first schema: minimum: 1 - type: string - description: Cursor for fetching the next page + type: integer responses: '200': content: @@ -272,6 +267,45 @@ paths: tags: - Deployments /api/v1/deployments/{deploymentId}/dbt-sync: + get: + operationId: listDbtSyncs + parameters: + - in: path + name: deploymentId + required: true + schema: + type: integer + - in: query + name: status + schema: + oneOf: + - $ref: '#/components/schemas/DbtSyncRunsQueryStatus' + - type: 'null' + - in: query + name: trigger + schema: + oneOf: + - $ref: '#/components/schemas/DbtSyncRunsQueryTrigger' + - type: 'null' + - in: query + name: after + schema: + type: string + - in: query + name: first + schema: + minimum: 1 + type: integer + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/DbtSyncRunListResponse' + description: '' + summary: List dbt syncs for a deployment + tags: + - dbt Sync post: operationId: startDbtSync parameters: @@ -344,6 +378,30 @@ paths: summary: Get the status of a dbt sync tags: - dbt Sync + /api/v1/deployments/{deploymentId}/dbt-sync/{syncJobId}/logs: + get: + operationId: getDbtSyncLogs + parameters: + - in: path + name: deploymentId + required: true + schema: + type: integer + - in: path + name: syncJobId + required: true + schema: + type: string + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/DbtSyncLogEntriesListResponse' + description: '' + summary: Get the log of a dbt sync + tags: + - dbt Sync /api/v1/deployments/{deploymentId}/dbt-sync/{syncJobId}/result: get: operationId: getDbtSyncResult @@ -377,6 +435,19 @@ paths: required: true schema: type: integer + - in: query + name: after + schema: + oneOf: + - type: string + - type: 'null' + - in: query + name: first + schema: + oneOf: + - minimum: 1 + type: integer + - type: 'null' responses: '200': content: @@ -389,8 +460,9 @@ paths: - Env Variables x-mint: content: >- - Lists the deployment environment variables. Values of secret-named variables (containing - PASS, SECRET, TOKEN, KEY, or CREDENTIAL) are returned as `[ENCRYPTED]`. + Lists the deployment environment variables, cursor-paginated via `first`/`after`. Values + of secret-named variables (containing PASS, SECRET, TOKEN, KEY, or CREDENTIAL) are + returned as `[ENCRYPTED]`. put: operationId: setEnvVariables parameters: @@ -464,6 +536,15 @@ paths: - type: 'null' type: integer minimum: 0 + - in: query + name: after + schema: + type: string + - in: query + name: first + schema: + minimum: 1 + type: integer responses: '200': content: @@ -508,6 +589,15 @@ paths: - type: 'null' type: integer minimum: 0 + - in: query + name: after + schema: + type: string + - in: query + name: first + schema: + minimum: 1 + type: integer responses: '200': content: @@ -822,6 +912,15 @@ paths: oneOf: - $ref: '#/components/schemas/DeploymentLogsQueryContainer' - type: 'null' + - in: query + name: after + schema: + type: string + - in: query + name: first + schema: + minimum: 1 + type: integer responses: '200': content: @@ -847,7 +946,6 @@ paths: description: Numeric id of the deployment that owns the notification. - in: query name: dashboardId - required: false schema: oneOf: - minimum: 1 @@ -856,14 +954,8 @@ paths: Optional filter: only return notifications for this numeric dashboard id. Provide this OR dashboardPublicId, not both. - type: 'null' - type: integer - minimum: 1 - description: >- - Optional filter: only return notifications for this numeric dashboard id. Provide this - OR `dashboardPublicId`, not both. - in: query name: dashboardPublicId - required: false schema: oneOf: - type: string @@ -871,13 +963,8 @@ paths: Optional filter: only return notifications for this dashboard public id. Provide this OR dashboardId, not both. - type: 'null' - type: string - description: >- - Optional filter: only return notifications for this dashboard public id. Provide this OR - `dashboardId`, not both. - in: query name: recipientUserId - required: false schema: oneOf: - minimum: 1 @@ -886,14 +973,8 @@ paths: Optional filter: only return notifications received by this user, by user id. Mutually exclusive with recipientEmail and the embed-user filter. - type: 'null' - type: integer - minimum: 1 - description: >- - Optional filter: only return notifications received by this user, by user id. Mutually - exclusive with `recipientEmail` and the embed-user filter. - in: query name: recipientEmail - required: false schema: oneOf: - type: string @@ -901,13 +982,8 @@ paths: Optional filter: only return notifications received by this user, by email. Mutually exclusive with recipientUserId and the embed-user filter. - type: 'null' - type: string - description: >- - Optional filter: only return notifications received by this user, by email. Mutually - exclusive with `recipientUserId` and the embed-user filter. - in: query name: recipientEmbedTenantName - required: false schema: oneOf: - type: string @@ -915,13 +991,8 @@ paths: Optional filter: only return notifications received by this embed user. Required together with recipientExternalId; mutually exclusive with the user filters. - type: 'null' - type: string - description: >- - Optional filter: only return notifications received by this embed user. Required - together with `recipientExternalId`; mutually exclusive with the user filters. - in: query name: recipientExternalId - required: false schema: oneOf: - type: string @@ -929,25 +1000,15 @@ paths: Optional filter: only return notifications received by this embed user. Required together with recipientEmbedTenantName; mutually exclusive with the user filters. - type: 'null' - type: string - description: >- - Optional filter: only return notifications received by this embed user. Required - together with `recipientEmbedTenantName`; mutually exclusive with the user filters. - in: query - name: first - required: false + name: after schema: - type: integer - minimum: 1 - maximum: 200 - description: Page size for cursor pagination (default 100, max 200). + type: string - in: query - name: after - required: false + name: first schema: minimum: 1 - type: string - description: Opaque cursor for the next page; pass the previous response's `pageInfo.endCursor`. + type: integer responses: '200': content: @@ -1184,26 +1245,18 @@ paths: type: integer description: Numeric id of the notification (scheduled run). - in: query - name: first - required: false + name: after schema: oneOf: - type: string - type: 'null' - type: integer - minimum: 1 - maximum: 200 - description: Page size for cursor pagination (default 100, max 200). - in: query - name: after - required: false + name: first schema: oneOf: - minimum: 1 type: integer - type: 'null' - type: string - description: Opaque cursor for the next page; pass the previous response's `pageInfo.endCursor`. responses: '200': content: @@ -1283,6 +1336,19 @@ paths: required: true schema: type: integer + - in: query + name: after + schema: + oneOf: + - type: string + - type: 'null' + - in: query + name: first + schema: + oneOf: + - minimum: 1 + type: integer + - type: 'null' responses: '200': content: @@ -1597,6 +1663,64 @@ paths: Requires **edit** access to the report. /api/v1/deployments/{deploymentId}/reports/{reportId}/connect-workbook: + delete: + operationId: disconnectReportFromWorkbook + parameters: + - in: path + name: deploymentId + required: true + schema: + type: number + - in: path + name: reportId + required: true + schema: + type: number + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/DisconnectReportFromWorkbookInput' + description: DisconnectReportFromWorkbookInput + required: false + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/Report' + description: '' + summary: Disconnect a report from one spot in a spreadsheet + tags: + - Reports + x-mint: + content: |- + Remove a single placement of a report — one sheet + anchor in one + spreadsheet — leaving every other placement, in this workbook and in others, alone. + + Address the placement by `placementId`, or (for placements recorded before ids + existed) by `externalWorkbookId` plus `sheetName` and `anchorCell` — or a + `resultLocation` naming both. Neither the sheet name nor the anchor is optional in + that form: matching is by sheet + anchor, and a target missing either addresses + nothing. + + `sheetId` is an optional extra, not a substitute for `sheetName`: a placement + written before that field existed has only a name, and no id the server could + resolve to it, so an id-only address would match nothing while looking correct. + + Send a `sheetId` only when you know it is the one the placement was stored with. + It NARROWS the target — a placement recorded under a different id is not matched, + even when the sheet name and anchor agree, because the stored name may belong to a + tab that has since been renamed and the id is the only stable identity. If your id + may have moved on (a tab deleted and recreated under the same name reports a new + one), address the placement by `placementId`, or by sheet name with no + `sheetId` at all. + + A target that matches no placement succeeds and changes nothing, so retrying is + safe. + + Like connecting, this records nothing about the report's definition and so requires + only read access: unplacing a report from your own sheet is not an edit to the report. put: operationId: connectReportToWorkbook parameters: @@ -1650,6 +1774,13 @@ paths: required: true schema: type: number + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/RefreshReportInput' + description: RefreshReportInput + required: false responses: '200': content: @@ -1793,6 +1924,19 @@ paths: required: true schema: type: integer + - in: query + name: after + schema: + oneOf: + - type: string + - type: 'null' + - in: query + name: first + schema: + oneOf: + - minimum: 1 + type: integer + - type: 'null' responses: '200': content: @@ -3163,6 +3307,20 @@ paths: /api/v1/github/installations: get: operationId: installations + parameters: + - in: query + name: after + schema: + oneOf: + - type: string + - type: 'null' + - in: query + name: first + schema: + oneOf: + - minimum: 1 + type: integer + - type: 'null' responses: '200': content: @@ -3181,7 +3339,20 @@ paths: name: installationId required: true schema: - type: number + type: integer + - in: query + name: after + schema: + oneOf: + - type: string + - type: 'null' + - in: query + name: first + schema: + oneOf: + - minimum: 1 + type: integer + - type: 'null' responses: '200': content: @@ -3196,11 +3367,11 @@ paths: get: operationId: branches parameters: - - in: query - name: installationId + - in: path + name: owner required: true schema: - type: integer + type: string - in: path name: repo required: true @@ -3208,9 +3379,22 @@ paths: type: string - in: query name: installationId - required: false + required: true schema: - type: number + type: integer + - in: query + name: after + schema: + oneOf: + - type: string + - type: 'null' + - in: query + name: first + schema: + oneOf: + - minimum: 1 + type: integer + - type: 'null' responses: '200': content: @@ -3425,9 +3609,23 @@ paths: /api/v1/regions: get: operationId: listRegions - responses: - '200': - content: + parameters: + - in: query + name: after + schema: + oneOf: + - type: string + - type: 'null' + - in: query + name: first + schema: + oneOf: + - minimum: 1 + type: integer + - type: 'null' + responses: + '200': + content: application/json: schema: $ref: '#/components/schemas/RegionsListResponse' @@ -3435,6 +3633,11 @@ paths: summary: List regions tags: - Regions + x-mint: + content: >- + Lists the regions available to the account, cursor-paginated via `first`/`after`. The + legacy `data` field returns the full, unpaginated list and is deprecated — use `items` + + `pageInfo` instead. /api/v1/spec: get: operationId: getSpec @@ -3555,6 +3758,15 @@ paths: oneOf: - $ref: '#/components/schemas/GetUserAttributesQueryType' - type: 'null' + - in: query + name: after + schema: + type: string + - in: query + name: first + schema: + minimum: 1 + type: integer responses: '200': content: @@ -3565,6 +3777,11 @@ paths: summary: Get user attributes tags: - User Attributes + x-mint: + content: >- + List user attributes, newest first, cursor-paginated via `first`/`after`. The legacy + `offset`/`limit` query params and the `data`/`count` response fields are deprecated — use + `items` + `pageInfo` instead. post: operationId: createUserAttribute requestBody: @@ -3829,6 +4046,53 @@ paths: `github`, no starter project is scaffolded — connect a repository afterwards via POST /build/api/v1/deployments/:deploymentId/github/connect. /build/api/v1/deployments/{deploymentId}/branches: + delete: + operationId: removeBranch + parameters: + - in: path + name: deploymentId + required: true + schema: + type: integer + - in: query + name: branchName + required: true + schema: + type: string + - in: query + name: removeOnUpstream + required: false + schema: + type: boolean + default: false + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/DataModelWriteResponse' + description: '' + summary: Delete a branch + tags: + - Data Model + x-mint: + content: >- + Deletes the branch and its git ref — the API-side counterpart of pruning a branch in Cube + Cloud, for callers that create branches per run (a CI job syncing dbt on every pull + request, say) and would otherwise accumulate them forever. Child branches are re-parented + onto the deleted branch’s own parent, and anyone viewing the branch in dev mode is moved + back to the default branch, so a branch that is currently open is deleted rather than + refused. The deploy branch cannot be deleted (400) and an unknown branch is a 404. + + + `removeOnUpstream=true` additionally deletes the branch on the connected git provider + (GitHub/GitLab). It defaults to `false`, so by default the branch is removed from Cube + Cloud only and the ref on your own remote is left alone — a CI job cleaning up branches it + created itself should opt in explicitly. + + + `branchName` is a query parameter rather than a path segment because branch names contain + slashes (`dbt-sync/main-20260817120000-a1b2c3d4`). get: operationId: listBranches parameters: @@ -3837,6 +4101,19 @@ paths: required: true schema: type: integer + - in: query + name: after + schema: + oneOf: + - type: string + - type: 'null' + - in: query + name: first + schema: + oneOf: + - minimum: 1 + type: integer + - type: 'null' responses: '200': content: @@ -4019,6 +4296,19 @@ paths: required: false schema: type: boolean + - in: query + name: after + schema: + oneOf: + - type: string + - type: 'null' + - in: query + name: first + schema: + oneOf: + - minimum: 1 + type: integer + - type: 'null' responses: '200': content: @@ -4029,6 +4319,12 @@ paths: summary: List the data-model source files for a deployment tags: - Data Model + x-mint: + content: >- + Returns the data-model file tree. `first`/`after` (from the shared cursor params) paginate + the **top-level** tree nodes only — a single node can carry a large subtree, so this + bounds the number of roots returned, not the total file count. Omit `first` to get the + whole tree. put: operationId: updateFiles parameters: @@ -4207,6 +4503,43 @@ paths: summary: Start a data-model upload transaction tags: - Data Model Uploads + /build/api/v1/deployments/{deploymentId}/data-model/validate: + get: + operationId: validateDataModel + parameters: + - in: path + name: deploymentId + required: true + schema: + type: integer + - in: query + name: branchName + required: false + schema: + type: string + - in: query + name: devMode + required: false + schema: + type: boolean + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/DataModelValidationResponse' + description: '' + summary: Compile a deployment's data model and report compilation errors + tags: + - Data Model + x-mint: + content: >- + Asks the branch's own Cube runtime for `GET /cubejs-api/v1/meta` — the same call the + console makes in dev mode — so the verdict is the one that branch's API would give. With + neither parameter it validates the deploy branch (production). `branchName` validates a + named branch, and `devMode=true` validates the caller's active dev-mode branch; the two + are mutually exclusive. Answers 200 with `valid: false` and the compiler's errors when the + model doesn't compile — a broken model is a result, not a request error. /build/api/v1/deployments/{deploymentId}/dev-mode: delete: operationId: stopDevMode @@ -4491,10 +4824,21 @@ components: BranchesListResponse: properties: data: + deprecated: true + description: 'Deprecated: use `items` instead. Kept for backward compatibility.' + items: + $ref: '#/components/schemas/BranchResponse' + type: array + items: items: $ref: '#/components/schemas/BranchResponse' type: array + pageInfo: + oneOf: + - $ref: '#/components/schemas/PageInfo' + - type: 'null' required: + - items - data type: object BuildStatusResponse: @@ -4715,18 +5059,47 @@ components: type: object ConnectReportToWorkbookInput: properties: + anchorCell: + oneOf: + - type: string + - type: 'null' endResultCell: oneOf: - type: string - type: 'null' externalWorkbookId: type: string + host: + oneOf: + - $ref: '#/components/schemas/ConnectReportToWorkbookInputHost' + - type: 'null' + placementId: + oneOf: + - type: string + - type: 'null' resultLocation: type: string + sheetId: + oneOf: + - type: string + - type: 'null' + sheetName: + oneOf: + - type: string + - type: 'null' + workbookName: + oneOf: + - type: string + - type: 'null' required: - externalWorkbookId - resultLocation type: object + ConnectReportToWorkbookInputHost: + enum: + - GOOGLE_SHEETS + - EXCEL + type: string ControlThemeBorderSectionInput: properties: color: @@ -5076,6 +5449,10 @@ components: oneOf: - type: integer - type: 'null' + host: + oneOf: + - $ref: '#/components/schemas/CreateReportInputHost' + - type: 'null' jsonQuery: oneOf: - type: string @@ -5104,6 +5481,10 @@ components: oneOf: - type: string - type: 'null' + sheetId: + oneOf: + - type: string + - type: 'null' source: oneOf: - $ref: '#/components/schemas/CreateReportInputSource' @@ -5120,7 +5501,16 @@ components: oneOf: - type: integer - type: 'null' + workbookName: + oneOf: + - type: string + - type: 'null' type: object + CreateReportInputHost: + enum: + - GOOGLE_SHEETS + - EXCEL + type: string CreateReportInputSource: enum: - GOOGLE_SHEETS @@ -5928,6 +6318,7 @@ components: - FILTER - AI - TIME_GRAIN + - PARENT - SPACER - DIVIDER - CONTAINER @@ -5967,6 +6358,7 @@ components: - FILTER - AI - TIME_GRAIN + - PARENT - SPACER - DIVIDER - CONTAINER @@ -6030,6 +6422,47 @@ components: - path - newPath type: object + DataModelValidationError: + properties: + fileName: + oneOf: + - type: string + - type: 'null' + message: + type: string + required: + - message + type: object + DataModelValidationResponse: + properties: + branchName: + type: string + cubesCount: + oneOf: + - type: integer + - type: 'null' + deploymentId: + type: integer + errors: + items: + $ref: '#/components/schemas/DataModelValidationError' + type: array + mode: + $ref: '#/components/schemas/DataModelValidationResponseMode' + valid: + type: boolean + required: + - deploymentId + - branchName + - mode + - valid + - errors + type: object + DataModelValidationResponseMode: + enum: + - production + - dev_mode + type: string DataModelWriteResponse: properties: success: @@ -6052,6 +6485,49 @@ components: required: - path type: object + DbtSyncLogEntriesListResponse: + properties: + items: + items: + $ref: '#/components/schemas/DbtSyncLogEntry' + type: array + pageInfo: + $ref: '#/components/schemas/PageInfo' + required: + - items + - pageInfo + type: object + DbtSyncLogEntry: + properties: + durationMs: + oneOf: + - type: integer + description: For a phase line, how long the phase took, in milliseconds. + - type: 'null' + level: + description: info or error. + type: string + message: + type: string + phase: + oneOf: + - type: string + description: The phase this line belongs to, e.g. dbt-compile. + - type: 'null' + stream: + description: >- + Always "system" in this version. "stdout" and "stderr" join it once dbt’s own output is + captured, so treat this as an open set. + type: string + timestamp: + description: When the line was produced, as an ISO 8601 timestamp. + type: string + required: + - timestamp + - level + - stream + - message + type: object DbtSyncManifestStats: properties: macros: @@ -6125,6 +6601,189 @@ components: - completedAt - durationMs type: object + DbtSyncRun: + properties: + branchName: + description: The Cube branch this sync wrote its generated cubes to. + type: string + completedAt: + oneOf: + - type: string + description: >- + When the sync finished, as an ISO 8601 timestamp, or null while it is still running. + This is stamped by the process that ran the sync whereas startedAt is stamped by the + process that launched it — two different clocks — so for a run that failed moments + after starting, completedAt can even precede startedAt. Use durationMs to show how + long a run took rather than subtracting these. + - type: 'null' + deploymentId: + type: integer + durationMs: + oneOf: + - type: integer + description: >- + How long the sync took, in milliseconds, and the value to use when showing a + duration. Measured from a single clock, so it is never negative — prefer it over + subtracting startedAt from completedAt, which can disagree with it by a small skew. + - type: 'null' + errorMessage: + oneOf: + - type: string + description: Why the sync stopped. Present only for a failed run. + - type: 'null' + failedPhase: + oneOf: + - type: string + description: The phase that failed, e.g. dbt-compile. + - type: 'null' + gitRef: + oneOf: + - type: string + description: The dbt-repository ref this sync was run against, when pinned. + - type: 'null' + lastStage: + oneOf: + - type: string + description: The pipeline stage the run reached, e.g. COMPILING_DBT. + - type: 'null' + phases: + oneOf: + - items: + $ref: '#/components/schemas/DbtSyncRunPhase' + type: array + description: Per-phase timings, in the order they ran. Absent while the sync is running. + - type: 'null' + startedAt: + description: >- + When the sync started, as an ISO 8601 timestamp. See completedAt before comparing the + two. + type: string + stats: + oneOf: + - $ref: '#/components/schemas/DbtSyncRunStats' + - type: 'null' + status: + description: >- + RUNNING, COMPLETED, FAILED, CANCELLED or UNKNOWN. UNKNOWN means the outcome could not be + established (the run’s record was lost and the workflow engine no longer knows the + workflow) and is deliberately distinct from FAILED. Treat an unrecognized value as + non-terminal. + type: string + syncJobId: + type: string + trigger: + description: >- + What started the sync: manual (the Cube Cloud UI), api (this REST API), webhook (a push + to the dbt repository), agent (Cube AI), or unknown. + type: string + triggerContext: + oneOf: + - $ref: '#/components/schemas/DbtSyncRunTriggerContext' + - type: 'null' + userId: + oneOf: + - type: integer + description: The Cube user who started the sync, when a user started it. + - type: 'null' + required: + - syncJobId + - deploymentId + - status + - trigger + - branchName + - startedAt + type: object + DbtSyncRunListResponse: + properties: + items: + items: + $ref: '#/components/schemas/DbtSyncRun' + type: array + pageInfo: + $ref: '#/components/schemas/PageInfo' + required: + - items + - pageInfo + type: object + DbtSyncRunPhase: + properties: + durationMs: + description: How long the phase took, in milliseconds. + type: integer + outcome: + description: ok or failed. + type: string + phase: + description: Phase token, e.g. repo-clone, dbt-deps, dbt-compile. + type: string + startedOffsetMs: + description: When the phase began, in milliseconds after the sync started. + type: integer + required: + - phase + - startedOffsetMs + - durationMs + - outcome + type: object + DbtSyncRunStats: + properties: + cubeCount: + oneOf: + - type: integer + description: How many cubes the sync generated. + - type: 'null' + generatedFileCount: + oneOf: + - type: integer + description: How many files the sync wrote to the branch it created. + - type: 'null' + macros: + oneOf: + - type: integer + description: dbt macros found in the manifest. + - type: 'null' + models: + oneOf: + - type: integer + description: dbt models found in the manifest. + - type: 'null' + sources: + oneOf: + - type: integer + description: dbt sources found in the manifest. + - type: 'null' + type: object + DbtSyncRunTriggerContext: + properties: + headSha: + oneOf: + - type: string + - type: 'null' + pushedBranch: + oneOf: + - type: string + - type: 'null' + targetBranch: + oneOf: + - type: string + - type: 'null' + type: object + DbtSyncRunsQueryStatus: + enum: + - RUNNING + - COMPLETED + - FAILED + - CANCELLED + - UNKNOWN + type: string + DbtSyncRunsQueryTrigger: + enum: + - manual + - api + - webhook + - agent + - unknown + type: string DbtSyncStatusResponse: properties: error: @@ -6258,14 +6917,28 @@ components: DeploymentEnvironmentTokensListResponse: properties: data: + deprecated: true + description: 'Deprecated: use `items` instead. Kept for backward compatibility.' items: $ref: '#/components/schemas/DeploymentEnvironmentToken' type: array + items: + items: + $ref: '#/components/schemas/DeploymentEnvironmentToken' + type: array + pageInfo: + oneOf: + - $ref: '#/components/schemas/PageInfo' + - type: 'null' pagination: - $ref: '#/components/schemas/DeploymentsPagination' + oneOf: + - $ref: '#/components/schemas/DeploymentsPagination' + deprecated: true + description: 'Deprecated: use `pageInfo` instead. Kept for backward compatibility.' + - type: 'null' required: + - items - data - - pagination type: object DeploymentEnvironmentType: enum: @@ -6276,14 +6949,28 @@ components: DeploymentEnvironmentsListResponse: properties: data: + deprecated: true + description: 'Deprecated: use `items` instead. Kept for backward compatibility.' items: $ref: '#/components/schemas/DeploymentEnvironment' type: array + items: + items: + $ref: '#/components/schemas/DeploymentEnvironment' + type: array + pageInfo: + oneOf: + - $ref: '#/components/schemas/PageInfo' + - type: 'null' pagination: - $ref: '#/components/schemas/DeploymentsPagination' + oneOf: + - $ref: '#/components/schemas/DeploymentsPagination' + deprecated: true + description: 'Deprecated: use `pageInfo` instead. Kept for backward compatibility.' + - type: 'null' required: + - items - data - - pagination type: object DeploymentLogLine: properties: @@ -6317,6 +7004,10 @@ components: items: $ref: '#/components/schemas/DeploymentLogLine' type: array + pageInfo: + oneOf: + - $ref: '#/components/schemas/PageInfo' + - type: 'null' required: - items type: object @@ -6355,6 +7046,10 @@ components: items: $ref: '#/components/schemas/DeploymentPod' type: array + pageInfo: + oneOf: + - $ref: '#/components/schemas/PageInfo' + - type: 'null' required: - items type: object @@ -6371,9 +7066,7 @@ components: - $ref: '#/components/schemas/DeploymentSettingsCreationMethod' - type: 'null' creationStep: - oneOf: - - $ref: '#/components/schemas/CreationStep' - - type: 'null' + $ref: '#/components/schemas/CreationStep' cspsConfig: oneOf: - $ref: '#/components/schemas/CspsConfig' @@ -6407,9 +7100,7 @@ components: deployProjectRoot: type: string deploymentUrl: - oneOf: - - type: string - - type: 'null' + type: string id: type: integer isCold: @@ -6466,6 +7157,8 @@ components: - name - template - cloudProvider + - creationStep + - deploymentUrl - releaseChannel - deployBranchName - deployBranchReadOnly @@ -6541,12 +7234,16 @@ components: type: string DeploymentVersionsResponse: properties: - data: + items: items: $ref: '#/components/schemas/DeploymentVersion' type: array + pageInfo: + oneOf: + - $ref: '#/components/schemas/PageInfo' + - type: 'null' required: - - data + - items type: object DeploymentsListResponse: properties: @@ -6554,8 +7251,14 @@ components: oneOf: - minimum: 0 type: integer + deprecated: true + description: >- + Deprecated: total number of accessible deployments, ignoring pagination. Kept for + backward compatibility. - type: 'null' data: + deprecated: true + description: 'Deprecated: use `items` instead. Kept for backward compatibility.' items: $ref: '#/components/schemas/Deployment' type: array @@ -6569,14 +7272,18 @@ components: - type: 'null' pagination: $ref: '#/components/schemas/DeploymentsPagination' + deprecated: true + description: 'Deprecated: use `pageInfo` instead. Kept for backward compatibility.' totalCount: oneOf: - minimum: 0 type: integer + deprecated: true + description: 'Deprecated: use `count`. Kept for backward compatibility.' - type: 'null' required: - - data - items + - data - pagination type: object DeploymentsPagination: @@ -6610,6 +7317,33 @@ components: required: - active type: object + DisconnectReportFromWorkbookInput: + properties: + anchorCell: + oneOf: + - type: string + - type: 'null' + externalWorkbookId: + oneOf: + - type: string + - type: 'null' + placementId: + oneOf: + - type: string + - type: 'null' + resultLocation: + oneOf: + - type: string + - type: 'null' + sheetId: + oneOf: + - type: string + - type: 'null' + sheetName: + oneOf: + - type: string + - type: 'null' + type: object DuplicateWorkbookInput: properties: shared: @@ -6727,6 +7461,20 @@ components: type: object EmbedSessionSettings: properties: + allowChatWorkspaceAuthoring: + oneOf: + - type: boolean + description: >- + Whether AI Chat authenticated with this session may create or modify persistent Cube + Workspace content. Set to `false` for headless chat integrations that render answers + in their own UI: ad-hoc data analysis and inline tables/charts remain available, but + the agent cannot save or update standalone explorations/reports, create or modify + workbooks, create or publish dashboards, or direct users to those Cube UI surfaces. + Omit or set to `true` to preserve the session user's role-derived authoring + capabilities; `true` never grants access the user does not already have. This + setting changes only AI Chat tools and instructions. It does not change user roles + or permissions for direct API calls. + - type: 'null' showDashboardChat: oneOf: - type: boolean @@ -6923,10 +7671,21 @@ components: EnvVariablesListResponse: properties: data: + deprecated: true + description: 'Deprecated: use `items` instead. Kept for backward compatibility.' + items: + $ref: '#/components/schemas/EnvVariableItem' + type: array + items: items: $ref: '#/components/schemas/EnvVariableItem' type: array + pageInfo: + oneOf: + - $ref: '#/components/schemas/PageInfo' + - type: 'null' required: + - items - data type: object FileHash: @@ -7157,9 +7916,9 @@ components: - $ref: '#/components/schemas/EmbedSessionSettings' type: object description: >- - Per-session overrides for embed behavior. Each key is tri-state: omit it to inherit - the account-wide setting, or set `true`/`false` to force the behavior for every - embed viewed with this session, taking precedence over the account-wide setting. + Per-session overrides for embed behavior, stored in the signed embed token. Each + documented key applies only to this session; omitted keys preserve their existing + default behavior. - type: 'null' tenantGroupDefinitions: oneOf: @@ -7268,10 +8027,21 @@ components: GitHubBranchesListResponse: properties: data: + deprecated: true + description: 'Deprecated: use `items` instead. Kept for backward compatibility.' + items: + $ref: '#/components/schemas/GitHubBranchResponse' + type: array + items: items: $ref: '#/components/schemas/GitHubBranchResponse' type: array + pageInfo: + oneOf: + - $ref: '#/components/schemas/PageInfo' + - type: 'null' required: + - items - data type: object GitHubConnectStatusResponse: @@ -7306,10 +8076,21 @@ components: GitHubInstallationsListResponse: properties: data: + deprecated: true + description: 'Deprecated: use `items` instead. Kept for backward compatibility.' + items: + $ref: '#/components/schemas/GitHubInstallationResponse' + type: array + items: items: $ref: '#/components/schemas/GitHubInstallationResponse' type: array + pageInfo: + oneOf: + - $ref: '#/components/schemas/PageInfo' + - type: 'null' required: + - items - data type: object GitHubRepoResponse: @@ -7328,10 +8109,21 @@ components: GitHubReposListResponse: properties: data: + deprecated: true + description: 'Deprecated: use `items` instead. Kept for backward compatibility.' + items: + $ref: '#/components/schemas/GitHubRepoResponse' + type: array + items: items: $ref: '#/components/schemas/GitHubRepoResponse' type: array + pageInfo: + oneOf: + - $ref: '#/components/schemas/PageInfo' + - type: 'null' required: + - items - data type: object GroupDefinition: @@ -7793,6 +8585,14 @@ components: items: type: string type: array + measuresAxis: + oneOf: + - $ref: '#/components/schemas/PivotItemsMeasuresAxis' + - type: 'null' + measuresPosition: + oneOf: + - $ref: '#/components/schemas/PivotItemsMeasuresPosition' + - type: 'null' rows: items: type: string @@ -7817,6 +8617,14 @@ components: items: type: string type: array + measuresAxis: + oneOf: + - $ref: '#/components/schemas/PivotItemsInputMeasuresAxis' + - type: 'null' + measuresPosition: + oneOf: + - $ref: '#/components/schemas/PivotItemsInputMeasuresPosition' + - type: 'null' rows: items: type: string @@ -7827,6 +8635,26 @@ components: - rows - filters type: object + PivotItemsInputMeasuresAxis: + enum: + - columns + - rows + type: string + PivotItemsInputMeasuresPosition: + enum: + - before + - after + type: string + PivotItemsMeasuresAxis: + enum: + - columns + - rows + type: string + PivotItemsMeasuresPosition: + enum: + - before + - after + type: string PostTokenBySessionIdInput: properties: sessionId: @@ -7899,6 +8727,33 @@ components: - stderr - inputRowCount type: object + RefreshReportInput: + properties: + anchorCell: + oneOf: + - type: string + - type: 'null' + externalWorkbookId: + oneOf: + - type: string + - type: 'null' + placementId: + oneOf: + - type: string + - type: 'null' + resultLocation: + oneOf: + - type: string + - type: 'null' + sheetId: + oneOf: + - type: string + - type: 'null' + sheetName: + oneOf: + - type: string + - type: 'null' + type: object RegionResponse: properties: id: @@ -7936,10 +8791,21 @@ components: RegionsListResponse: properties: data: + deprecated: true + description: 'Deprecated: use `items` instead. Kept for backward compatibility.' + items: + $ref: '#/components/schemas/RegionResponse' + type: array + items: items: $ref: '#/components/schemas/RegionResponse' type: array + pageInfo: + oneOf: + - $ref: '#/components/schemas/PageInfo' + - type: 'null' required: + - items - data type: object RemoveDataModelFilesInput: @@ -8151,18 +9017,63 @@ components: type: string ReportPlacement: properties: + anchorCell: + oneOf: + - type: string + - type: 'null' endResultCell: oneOf: - type: string - type: 'null' + host: + oneOf: + - $ref: '#/components/schemas/ReportPlacementHost' + - type: 'null' + id: + oneOf: + - type: string + - type: 'null' + placedAt: + oneOf: + - type: string + - type: 'null' + placedByUserId: + oneOf: + - type: integer + - type: 'null' + refreshedAt: + oneOf: + - type: string + - type: 'null' + refreshedByUserId: + oneOf: + - type: integer + - type: 'null' resultLocation: type: string + sheetId: + oneOf: + - type: string + - type: 'null' + sheetName: + oneOf: + - type: string + - type: 'null' workbookId: type: string + workbookName: + oneOf: + - type: string + - type: 'null' required: - workbookId - resultLocation type: object + ReportPlacementHost: + enum: + - GOOGLE_SHEETS + - EXCEL + type: string ReportSnapshot: properties: description: @@ -8202,6 +9113,10 @@ components: oneOf: - $ref: '#/components/schemas/ReportSnapshotPreferences' - type: 'null' + pythonCode: + oneOf: + - type: string + - type: 'null' reportId: type: integer spec: @@ -8398,11 +9313,23 @@ components: SourceTreeResponse: properties: data: + deprecated: true + description: 'Deprecated: use `items` instead. Kept for backward compatibility.' + items: + additionalProperties: true + type: object + type: array + items: items: additionalProperties: true type: object type: array + pageInfo: + oneOf: + - $ref: '#/components/schemas/PageInfo' + - type: 'null' required: + - items - data type: object StartDbtSyncInput: @@ -8870,6 +9797,10 @@ components: oneOf: - type: integer - type: 'null' + host: + oneOf: + - $ref: '#/components/schemas/UpdateReportInputHost' + - type: 'null' jsonQuery: oneOf: - type: string @@ -8887,6 +9818,10 @@ components: oneOf: - $ref: '#/components/schemas/PivotItems' - type: 'null' + placementId: + oneOf: + - type: string + - type: 'null' publicId: oneOf: - pattern: ^[0-9A-Za-z]+$ @@ -8898,6 +9833,10 @@ components: oneOf: - type: string - type: 'null' + sheetId: + oneOf: + - type: string + - type: 'null' sqlQuery: oneOf: - type: string @@ -8910,7 +9849,16 @@ components: oneOf: - type: integer - type: 'null' + workbookName: + oneOf: + - type: string + - type: 'null' type: object + UpdateReportInputHost: + enum: + - GOOGLE_SHEETS + - EXCEL + type: string UpdateWorkbookInput: properties: folderId: @@ -9271,12 +10219,28 @@ components: UserAttributesListResponse: properties: count: + deprecated: true + description: >- + Deprecated: total number of user attributes, ignoring pagination. Kept for backward + compatibility. + minimum: 0 type: integer data: + deprecated: true + description: 'Deprecated: use `items` instead. Kept for backward compatibility.' + items: + $ref: '#/components/schemas/UserAttribute' + type: array + items: items: $ref: '#/components/schemas/UserAttribute' type: array + pageInfo: + oneOf: + - $ref: '#/components/schemas/PageInfo' + - type: 'null' required: + - items - data - count type: object @@ -9690,8 +10654,14 @@ components: oneOf: - minimum: 0 type: integer + deprecated: true + description: >- + Deprecated: total number of accessible workbooks, ignoring pagination. Kept for + backward compatibility. - type: 'null' data: + deprecated: true + description: 'Deprecated: use `items` instead. Kept for backward compatibility.' items: $ref: '#/components/schemas/Workbook' type: array diff --git a/docs-mintlify/api-reference/changelog.mdx b/docs-mintlify/api-reference/changelog.mdx index d9815203b6e03..d99963b7378a0 100644 --- a/docs-mintlify/api-reference/changelog.mdx +++ b/docs-mintlify/api-reference/changelog.mdx @@ -7,6 +7,63 @@ rss: true {/* GENERATED FILE — do not edit by hand. */} {/* Run scripts/extract-changelog.js against the platform client CHANGELOG.md. */} + + ### Added + + #### dbt Sync — status, results and history + + - `GET /api/v1/deployments/{deploymentId}/dbt-sync` (`DbtSyncPublicController.listDbtSyncs`) — sync run history, filterable by `status`/`trigger`, cursor-paginated. New schemas: `DbtSyncRun`, `DbtSyncRunListResponse`, `DbtSyncRunPhase`, `DbtSyncRunStats`, `DbtSyncRunTriggerContext`, `DbtSyncRunsQueryStatus`, `DbtSyncRunsQueryTrigger`. + - `GET /api/v1/deployments/{deploymentId}/dbt-sync/{syncJobId}` (`getDbtSyncStatus`) — poll a running sync. New schemas: `DbtSyncStatusResponse`, `DbtSyncProgress`. + - `GET .../dbt-sync/{syncJobId}/result` (`getDbtSyncResult`) and `GET .../dbt-sync/{syncJobId}/logs` (`getDbtSyncLogs`). New schemas: `DbtSyncResultResponse`, `DbtSyncGeneratedFile`, `DbtSyncManifestStats`, `DbtSyncLogEntry`, `DbtSyncLogEntriesListResponse`. + - `DELETE .../dbt-sync/{syncJobId}` (`cancelDbtSync`) — cancel a running sync. New schema: `DbtSyncCancelResponse`. + + #### Data model — branch cleanup, staging environments and validation + + - `DELETE /build/api/v1/deployments/{deploymentId}/branches` (`DataModelPublicController.removeBranch`, `branchName` as a query param since branch names contain slashes) — deletes a branch and its git ref, re-parenting any children onto the deleted branch's own parent; `removeOnUpstream` additionally deletes the ref on the connected GitHub/GitLab remote. New schema: `DataModelWriteResponse` (shared with the existing file-write endpoints). + - `PUT .../branches/staging-environment` (`setBranchStagingEnvironment`) — enable or disable a branch's always-on staging environment (`/dev-mode/{branchName}/cubejs-api/v1`). New schemas: `SetBranchStagingEnvironmentRequest`, `SetBranchStagingEnvironmentResponse`. `BranchResponse` (from `listBranches`) reads the flag back via a new `isStagingEnvironmentEnabled`. + - `GET .../data-model/validate` (`validateDataModel`) — compiles the deploy branch, a named branch, or the caller's dev-mode branch and reports compilation errors as a 200 response (`valid: false`), not a request error. New schemas: `DataModelValidationResponse`, `DataModelValidationError`, `DataModelValidationResponseMode`. + + #### Deployment settings, versions and the spec endpoint + + - `GET /api/v1/deployments/{deploymentId}/settings` (`DeploymentsPublicController.getDeploymentSettings`) — every deployment setting in one payload (secrets excluded); writes go through the existing `PUT /:deploymentId`. New schema: `DeploymentSettings` (+ its `cloudProvider`/`creationMethod`/`deployMode`/`releaseChannel`/`repoType`/`template` enums). + - `GET /api/v1/deployments/{deploymentId}/versions` (`listDeploymentVersions`) — the Cube versions this deployment can switch to (release-channel heads plus prior versions run before). New schemas: `DeploymentVersion`, `DeploymentVersionsResponse`, `DeploymentVersionReleaseChannel`. + - `PUT /api/v1/deployments/{deploymentId}` (`updateDeployment`) / `UpdateDeploymentInput` now accepts `cloudProvider`, `region`, `releaseChannel`, `releaseChannelVersion`, `releaseChannelVersionHold`, `template`, `templateVariables`, `defaultLaunchpadViewGroup`, and `launchpadTabs` — the same settings surface `GET .../settings` now reads back. `releaseChannelVersion` must be one of the versions `GET .../versions` lists for the target channel. + - `GET /api/v1/spec` (`OpenApiSpecPublicController.getSpec`) — the full OpenAPI 3.1 document served by the build handling the request, for runtime discovery by clients and agents. + + #### Reports + + - `DELETE /api/v1/deployments/{deploymentId}/reports/{reportId}/connect-workbook` (`ReportsPublicController.disconnectReportFromWorkbook`) — removes a single sheet+anchor placement of a report from a spreadsheet, leaving every other placement (in this workbook and others) alone. Addressed by `placementId`, or by `externalWorkbookId` + `sheetName` + `anchorCell`. New schema: `DisconnectReportFromWorkbookInput`. + - `PUT .../reports/{reportId}/refresh` now takes a body: new schema `RefreshReportInput` (`placementId`, or `externalWorkbookId` + `sheetName`/`sheetId`/`anchorCell`), addressing the same way as disconnect. + - Report placements are now individually addressable and trackable (new `ConnectReportToWorkbookInputHost` / `CreateReportInputHost` / `UpdateReportInputHost` enums, `"GOOGLE_SHEETS" | "EXCEL"`): `ConnectReportToWorkbookInput` gained `placementId`, `sheetId`, `sheetName`, `anchorCell`, `workbookName` and `host`; `CreateReportInput` gained `host`, `sheetId` and `workbookName`; `UpdateReportInput` gained `host`, `placementId`, `sheetId` and `workbookName`. `ReportPlacement` gained the matching `id`, `host`, `sheetId`, `sheetName`, `anchorCell`, `workbookName`, plus `placedAt`/`placedByUserId` and `refreshedAt`/`refreshedByUserId` (new `ReportPlacementHost` enum). `ReportSnapshot` gained `isSourceReport` and `pythonCode`. + + #### Cursor pagination on previously offset-only or unpaginated lists + + - `first`/`after` → `items` + `pageInfo` cursor pagination added to: deployment env variables (`GET .../env-vars`), GitHub branches/installations/repositories (`GitHubPublicController.*`), user attributes (previously `offset`/`limit`), `GET /api/v1/regions/`, report folders, data-model branches and files (`listBranches`/`listFiles`, `BranchesListResponse`/`SourceTreeResponse`), and deployment logs/pods (`DeploymentLogsResponse`/`DeploymentPodsResponse` gain `pageInfo`). Newly-paginated responses keep the legacy `data`/`count`/`pagination` fields, marked `@deprecated`. + - `UserGroupsPublicController.listGroups` and `UsersPublicController.getUsers` already had `first`/`after`; what's new on both is a `search` query param (case-insensitive substring match on group name / first name, username, email). + - `DashboardFilterOperator` / `DashboardFilterInputOperator` gained `is_empty` / `is_not_empty`. + + #### Other new fields + + - New `TimezoneSettings` schema, plus `timezoneSettings` on `AppConfigResponse` and `resolvedTimezone` / `deployBranchName` / `timezoneSettings` on `AIEngineerSettings`. + - New `CspsConfig` schema (customer-storage-provider config, mirrored on the existing `CspsConfigInput`). + - `scopeDeploymentId` added to the agent-space config schemas. + - `measuresAxis` / `measuresPosition` added to `PivotItems` / `PivotItemsInput` (new `PivotItemsMeasuresAxis` / `PivotItemsMeasuresPosition` / `PivotItemsInputMeasuresAxis` / `PivotItemsInputMeasuresPosition` enums, `"columns" | "rows"` and `"before" | "after"`). + - `name` / `userCount` added to `InheritedGroupPolicyDto` and `ResourceGroupPolicyDto`. + - `allowChatWorkspaceAuthoring` added to `EmbedSessionSettings`. + - `ref` added to `StartDbtSyncInput`. + + ### Changed + + - **BREAKING (TypeScript):** `DeploymentEnvironmentsListResponse.pagination` and `DeploymentEnvironmentTokensListResponse.pagination` moved from required to `pagination?: DeploymentsPagination | null` now that both responses also carry `items`/`pageInfo`. Code under `strictNullChecks` reading `res.pagination.currentPage` without a null check no longer compiles. + - **BREAKING (TypeScript):** `NotificationsPublicController.getRecipients`'s cursor params were narrowed — `first?: number | string | null` → `first?: number | null` and `after?: string | number | null` → `after?: string | null` — cleaning up a hand-declared `@OpenAPI({ parameters })` that had merged positionally with the real cursor-param types. A caller passing a numeric `after` or a stringified `first` (both accepted, if oddly typed, before) no longer compiles. + - `DashboardWidgetDtoType` / `DashboardWidgetInputType` no longer advertise `TABS_CONTAINER` in the type — replaced by `PARENT`, `SPACER`, `DIVIDER`, `CONTAINER` (a `CONTAINER` with `config.layout: 'tabs'` is the current shape for a tabbed container). Existing stored or sent `TABS_CONTAINER` values are still healed server-side for backward compatibility (CUB-3634), so this narrows the advertised type rather than breaking existing integrations at runtime — new code should target `CONTAINER` + `layout`. + - `PUT /api/v1/deployments/{deploymentId}` (`updateDeployment`) description clarified: writes are a partial merge (omitted fields are left alone, `templateVariables` is merged key-by-key), and `releaseChannelVersion` is validated against `GET .../versions`. + + ### Removed + + - **BREAKING:** `POST /api/v1/deployments/{deploymentId}/workbooks/{workbookId}/dashboard/ai-widget-thread` (`WorkbooksPublicController.updatePublishedDashboardAiWidgetThread`) and its `UpdatePublishedAiWidgetThreadInput` schema (added in `0.2.0`). It persisted an AI-analysis thread id + checksum into the published dashboard config — the mechanism that caused read-only/anonymous viewers to hit `WorkbookEdit`/403s and let one viewer's filter state clobber the shared config (CUB-3898). AI-analysis results are now cached server-side keyed by dashboard state, so no client-facing endpoint replaces it. This is a deliberate breaking removal; SDK consumers referencing the operation or schema should drop those references. + + > Supersedes `0.2.1`, which never reached npm because its publish job failed. > Upgrading from `0.2.0` picks up both releases, so the `0.2.1` entries are diff --git a/docs-mintlify/api-reference/introduction.mdx b/docs-mintlify/api-reference/introduction.mdx index d7f69f6cb67cb..33c7d132f9412 100644 --- a/docs-mintlify/api-reference/introduction.mdx +++ b/docs-mintlify/api-reference/introduction.mdx @@ -79,7 +79,7 @@ Resources by entity: | [Data Model Uploads](/api-reference/data-model-uploads/content-hashes-of-the-current-data-model-files-for-upload-diffing) | `/build/api/v1/deployments/{deploymentId}/data-model` | v1 | | [GitHub](/api-reference/github/list-repositories-accessible-to-a-github-app-installation) | `/api/v1/github` | v1 | | [GitHub Connection](/api-reference/github-connection/connect-a-deployment-to-a-github-repository-and-trigger-a-build) | `/build/api/v1/deployments/{deploymentId}/github/connect` | v1 | -| [dbt Sync](/api-reference/dbt-sync/start-a-dbt-sync-for-a-deployment) | `/api/v1/deployments/{deploymentId}/dbt-sync` | v1 | +| [dbt Sync](/api-reference/dbt-sync/list-dbt-syncs-for-a-deployment) | `/api/v1/deployments/{deploymentId}/dbt-sync` | v1 | | [Folders](/api-reference/folders/list-folders) | `/api/v1/deployments/{deploymentId}/folders` | v1 | | [Reports](/api-reference/reports/list-reports) | `/api/v1/deployments/{deploymentId}/reports` | v1 | | [Workbooks](/api-reference/workbooks/get-workbooks) | `/api/v1/deployments/{deploymentId}/workbooks` | v1 | diff --git a/docs-mintlify/docs.json b/docs-mintlify/docs.json index 90bed16172475..afe7a92a17791 100644 --- a/docs-mintlify/docs.json +++ b/docs-mintlify/docs.json @@ -808,12 +808,14 @@ "pages": [ "GET /build/api/v1/deployments/{deploymentId}/branches", "POST /build/api/v1/deployments/{deploymentId}/branches", + "DELETE /build/api/v1/deployments/{deploymentId}/branches", "PUT /build/api/v1/deployments/{deploymentId}/branches/staging-environment", "POST /build/api/v1/deployments/{deploymentId}/commit", "GET /build/api/v1/deployments/{deploymentId}/data-model/files", "PUT /build/api/v1/deployments/{deploymentId}/data-model/files", "DELETE /build/api/v1/deployments/{deploymentId}/data-model/files", "POST /build/api/v1/deployments/{deploymentId}/data-model/files/rename", + "GET /build/api/v1/deployments/{deploymentId}/data-model/validate", "POST /build/api/v1/deployments/{deploymentId}/dev-mode", "DELETE /build/api/v1/deployments/{deploymentId}/dev-mode", "POST /build/api/v1/deployments/{deploymentId}/merge", @@ -852,9 +854,11 @@ "group": "dbt Sync", "openapi": "/api-reference/api.yaml", "pages": [ + "GET /api/v1/deployments/{deploymentId}/dbt-sync", "POST /api/v1/deployments/{deploymentId}/dbt-sync", "GET /api/v1/deployments/{deploymentId}/dbt-sync/{syncJobId}", "DELETE /api/v1/deployments/{deploymentId}/dbt-sync/{syncJobId}", + "GET /api/v1/deployments/{deploymentId}/dbt-sync/{syncJobId}/logs", "GET /api/v1/deployments/{deploymentId}/dbt-sync/{syncJobId}/result" ] }, @@ -880,6 +884,7 @@ "PUT /api/v1/deployments/{deploymentId}/reports/{reportId}", "DELETE /api/v1/deployments/{deploymentId}/reports/{reportId}", "PUT /api/v1/deployments/{deploymentId}/reports/{reportId}/connect-workbook", + "DELETE /api/v1/deployments/{deploymentId}/reports/{reportId}/connect-workbook", "PUT /api/v1/deployments/{deploymentId}/reports/{reportId}/refresh" ] }, diff --git a/docs-mintlify/scripts/extract-api.mjs b/docs-mintlify/scripts/extract-api.mjs index e6929d74b470c..028419e20a03f 100644 --- a/docs-mintlify/scripts/extract-api.mjs +++ b/docs-mintlify/scripts/extract-api.mjs @@ -168,12 +168,27 @@ const EXCLUDE_OPERATIONS = new Set([ 'PUT /api/v1/resource-policies/group', 'PUT /api/v1/resource-policies/user', 'GET /api/v1/app-theme', - 'GET /api/v1/ai-engineer/active-region', 'GET /api/v1/ai-engineer/settings', // Report folders listing — not part of the public docs surface. 'GET /api/v1/deployments/{deploymentId}/report-folders', ]); +// Cube-staff-only operations (provisioning real cloud infrastructure — Regions, +// PrivateLinks, and anything else gated the same way) are excluded automatically +// rather than via a hand-maintained EXCLUDE_OPERATIONS list: console-server's +// `superAdminOnlyDescription()` (packages/console-server/src/api/middlewares/ +// validate-admin-access.ts in cubejs-enterprise) prefixes every such operation's +// OpenAPI `description` with this exact marker text before ANY other processing +// touches it, so matching on it here catches staff-only operations that didn't +// exist yet when this list was last updated — not just the ones someone +// remembered to add. No reader of these docs can call them (tenant admins are +// rejected server-side), so nothing customer-facing is lost. +// +// A short, stable anchor rather than the full marker sentence — less brittle against +// upstream rewrapping/rewording, and the residual scan below (which matches even more +// loosely) is the real safety net if this ever stops matching. +const SUPER_ADMIN_ONLY_MARKER = 'Cube super admin only'; + // Explicit display names for tags whose auto-cleaned form would be unclear or // collide. Everything else is cleaned by cleanTag() below. const TAG_MAP = { @@ -267,6 +282,8 @@ const src = yaml.load(fs.readFileSync(SRC, 'utf8')); // trailing slash — a trailing slash breaks Mintlify dev), and clean tags + // operationIds. const paths = {}; +const matchedExcludes = new Set(); +let autoExcludedCount = 0; for (const [key, val] of Object.entries(src.paths)) { if (!INCLUDE_PREFIXES.some((prefix) => key.startsWith(prefix))) continue; let newKey = key.length > 1 ? key.replace(/\/$/, '') : key; // drop trailing slash @@ -274,7 +291,15 @@ for (const [key, val] of Object.entries(src.paths)) { for (const m of METHODS) { if (!val[m]) continue; // Drop explicitly hidden operations before they reach the spec or nav. - if (EXCLUDE_OPERATIONS.has(`${m.toUpperCase()} ${newKey}`)) { + const excludeKey = `${m.toUpperCase()} ${newKey}`; + if (EXCLUDE_OPERATIONS.has(excludeKey)) { + matchedExcludes.add(excludeKey); + delete val[m]; + continue; + } + // Drop Cube-staff-only operations automatically — see SUPER_ADMIN_ONLY_MARKER above. + if (typeof val[m].description === 'string' && val[m].description.includes(SUPER_ADMIN_ONLY_MARKER)) { + autoExcludedCount++; delete val[m]; continue; } @@ -310,6 +335,59 @@ if (!Object.keys(paths).length) { process.exit(1); } +// An EXCLUDE_OPERATIONS entry that matches nothing was likely renamed or moved +// upstream — silently letting it through would re-publish whatever it was meant +// to hide (some entries there exist specifically to keep staff-only operations +// out of the public docs), and `--check` can't catch this: both the committed +// and freshly generated output would contain the leak. +const unmatchedExcludes = [...EXCLUDE_OPERATIONS].filter((op) => !matchedExcludes.has(op)); +if (unmatchedExcludes.length) { + console.error( + 'Aborting: EXCLUDE_OPERATIONS entries matched nothing (renamed upstream?):\n ' + + unmatchedExcludes.join('\n ') + ); + process.exit(1); +} + +// The SUPER_ADMIN_ONLY_MARKER auto-detection above is an exact-substring match against +// text authored in a different repo — it fails OPEN, not closed, if that text drifts +// (rewording, a dropped 🔒, a punctuation change). Two guards restore a fail-closed +// default without depending on the marker staying exact: +// +// Floor: the marker matched nothing at all. Today's spec always has staff-only +// operations, so zero is almost certainly "the marker stopped matching," not "there +// are none" — and if that ever becomes a real, deliberate zero, dropping this guard +// is a one-line edit made by a human looking at exactly this message. +if (!autoExcludedCount) { + console.error( + 'Aborting: SUPER_ADMIN_ONLY_MARKER matched no operation. Did the marker text change upstream ' + + '(packages/console-server/src/api/middlewares/validate-admin-access.ts in cubejs-enterprise)?' + ); + process.exit(1); +} +// Residual scan: catch staff-only prose that slipped past the exact marker match — +// worded more loosely than the marker itself (just "super admin"/"super-admin", not +// the full sentence) so it still fires even when SUPER_ADMIN_ONLY_MARKER no longer +// matches. Covers a *partial* drift too: if only a newly-added operation is reworded +// while existing ones keep today's wording, autoExcludedCount stays non-zero and the +// floor guard above can't catch it — this scan is what does. +// NB: "super admin"/"super-admin" specifically, not the bare 🔒 — the same lock emoji +// also opens the unrelated (and legitimately public) ADMIN_ONLY_DOC_MARKER ("🔒 Admin only."). +const leakedStaffOnly = []; +for (const [p, ops] of Object.entries(paths)) { + for (const m of METHODS) { + const text = ops[m]?.['x-mint']?.content ?? ops[m]?.description ?? ''; + if (/super[-\s]?admins?\b/i.test(text)) leakedStaffOnly.push(`${m.toUpperCase()} ${p}`); + } +} +if (leakedStaffOnly.length) { + console.error( + 'Aborting: staff-only prose survived exclusion (SUPER_ADMIN_ONLY_MARKER stopped matching?):\n ' + + leakedStaffOnly.join('\n ') + ); + process.exit(1); +} + // 2. Transitive $ref schema closure. function collectRefs(node, acc) { if (Array.isArray(node)) { node.forEach((n) => collectRefs(n, acc)); return; } @@ -403,6 +481,9 @@ const out = { writeOrCheck(OUT, yaml.dump(out, { lineWidth: 100, noRefs: true })); console.log('paths:', Object.keys(paths).length, '| schemas:', Object.keys(schemas).length, '| tags:', orderedTags.length); +if (autoExcludedCount) { + console.log(`(${autoExcludedCount} Cube-staff-only operation(s) auto-excluded via SUPER_ADMIN_ONLY_MARKER)`); +} // 5. Group operations by tag (pages in source order within a tag) and capture, // per tag, its paths + the first operation's summary — used to build both the From e5ef1633692ed2f708e059a7e2bb810453173bb0 Mon Sep 17 00:00:00 2001 From: Pavel Tiunov Date: Thu, 27 Aug 2026 14:36:39 -0700 Subject: [PATCH 6/8] v1.7.29 --- CHANGELOG.md | 12 ++++ lerna.json | 2 +- packages/cubejs-api-gateway/CHANGELOG.md | 6 ++ packages/cubejs-api-gateway/package.json | 10 ++-- packages/cubejs-athena-driver/CHANGELOG.md | 4 ++ packages/cubejs-athena-driver/package.json | 10 ++-- packages/cubejs-backend-cloud/CHANGELOG.md | 4 ++ packages/cubejs-backend-cloud/package.json | 6 +- packages/cubejs-backend-maven/CHANGELOG.md | 4 ++ packages/cubejs-backend-maven/package.json | 6 +- packages/cubejs-backend-native/CHANGELOG.md | 4 ++ packages/cubejs-backend-native/package.json | 8 +-- packages/cubejs-backend-shared/CHANGELOG.md | 4 ++ packages/cubejs-backend-shared/package.json | 4 +- packages/cubejs-base-driver/CHANGELOG.md | 4 ++ packages/cubejs-base-driver/package.json | 6 +- packages/cubejs-bigquery-driver/CHANGELOG.md | 4 ++ packages/cubejs-bigquery-driver/package.json | 8 +-- packages/cubejs-cli/CHANGELOG.md | 4 ++ packages/cubejs-cli/package.json | 12 ++-- .../cubejs-clickhouse-driver/CHANGELOG.md | 4 ++ .../cubejs-clickhouse-driver/package.json | 10 ++-- packages/cubejs-client-core/CHANGELOG.md | 4 ++ packages/cubejs-client-core/package.json | 4 +- packages/cubejs-client-dx/CHANGELOG.md | 4 ++ packages/cubejs-client-dx/package.json | 2 +- packages/cubejs-client-ngx/CHANGELOG.md | 4 ++ packages/cubejs-client-ngx/package.json | 2 +- packages/cubejs-client-react/CHANGELOG.md | 4 ++ packages/cubejs-client-react/package.json | 4 +- packages/cubejs-client-vue3/CHANGELOG.md | 4 ++ packages/cubejs-client-vue3/package.json | 4 +- .../cubejs-client-ws-transport/CHANGELOG.md | 4 ++ .../cubejs-client-ws-transport/package.json | 6 +- packages/cubejs-crate-driver/CHANGELOG.md | 4 ++ packages/cubejs-crate-driver/package.json | 10 ++-- packages/cubejs-cubestore-driver/CHANGELOG.md | 6 ++ packages/cubejs-cubestore-driver/package.json | 12 ++-- .../CHANGELOG.md | 4 ++ .../package.json | 12 ++-- .../cubejs-dbt-schema-extension/CHANGELOG.md | 4 ++ .../cubejs-dbt-schema-extension/package.json | 8 +-- packages/cubejs-docker/CHANGELOG.md | 4 ++ packages/cubejs-docker/package.json | 58 +++++++++---------- packages/cubejs-dremio-driver/CHANGELOG.md | 4 ++ packages/cubejs-dremio-driver/package.json | 12 ++-- packages/cubejs-druid-driver/CHANGELOG.md | 4 ++ packages/cubejs-druid-driver/package.json | 10 ++-- packages/cubejs-duckdb-driver/CHANGELOG.md | 4 ++ packages/cubejs-duckdb-driver/package.json | 12 ++-- packages/cubejs-firebolt-driver/CHANGELOG.md | 4 ++ packages/cubejs-firebolt-driver/package.json | 12 ++-- packages/cubejs-hive-driver/CHANGELOG.md | 4 ++ packages/cubejs-hive-driver/package.json | 8 +-- packages/cubejs-jdbc-driver/CHANGELOG.md | 4 ++ packages/cubejs-jdbc-driver/package.json | 8 +-- packages/cubejs-ksql-driver/CHANGELOG.md | 4 ++ packages/cubejs-ksql-driver/package.json | 10 ++-- packages/cubejs-linter/CHANGELOG.md | 4 ++ packages/cubejs-linter/package.json | 2 +- .../cubejs-materialize-driver/CHANGELOG.md | 4 ++ .../cubejs-materialize-driver/package.json | 12 ++-- packages/cubejs-mongobi-driver/CHANGELOG.md | 4 ++ packages/cubejs-mongobi-driver/package.json | 8 +-- packages/cubejs-mssql-driver/CHANGELOG.md | 4 ++ packages/cubejs-mssql-driver/package.json | 6 +- .../CHANGELOG.md | 4 ++ .../package.json | 8 +-- packages/cubejs-mysql-driver/CHANGELOG.md | 4 ++ packages/cubejs-mysql-driver/package.json | 10 ++-- packages/cubejs-oracle-driver/CHANGELOG.md | 4 ++ packages/cubejs-oracle-driver/package.json | 6 +- packages/cubejs-pinot-driver/CHANGELOG.md | 4 ++ packages/cubejs-pinot-driver/package.json | 10 ++-- packages/cubejs-playground/CHANGELOG.md | 4 ++ packages/cubejs-playground/package.json | 6 +- packages/cubejs-postgres-driver/CHANGELOG.md | 4 ++ packages/cubejs-postgres-driver/package.json | 10 ++-- packages/cubejs-prestodb-driver/CHANGELOG.md | 4 ++ packages/cubejs-prestodb-driver/package.json | 8 +-- .../cubejs-query-orchestrator/CHANGELOG.md | 7 +++ .../cubejs-query-orchestrator/package.json | 10 ++-- packages/cubejs-questdb-driver/CHANGELOG.md | 4 ++ packages/cubejs-questdb-driver/package.json | 12 ++-- packages/cubejs-redshift-driver/CHANGELOG.md | 4 ++ packages/cubejs-redshift-driver/package.json | 10 ++-- packages/cubejs-schema-compiler/CHANGELOG.md | 6 ++ packages/cubejs-schema-compiler/package.json | 12 ++-- packages/cubejs-server-core/CHANGELOG.md | 7 +++ packages/cubejs-server-core/package.json | 24 ++++---- packages/cubejs-server/CHANGELOG.md | 4 ++ packages/cubejs-server/package.json | 14 ++--- packages/cubejs-snowflake-driver/CHANGELOG.md | 4 ++ packages/cubejs-snowflake-driver/package.json | 8 +-- packages/cubejs-sqlite-driver/CHANGELOG.md | 4 ++ packages/cubejs-sqlite-driver/package.json | 8 +-- packages/cubejs-templates/CHANGELOG.md | 4 ++ packages/cubejs-templates/package.json | 6 +- packages/cubejs-testing-drivers/CHANGELOG.md | 4 ++ packages/cubejs-testing-drivers/package.json | 46 +++++++-------- packages/cubejs-testing-shared/CHANGELOG.md | 4 ++ packages/cubejs-testing-shared/package.json | 10 ++-- packages/cubejs-testing/CHANGELOG.md | 4 ++ packages/cubejs-testing/package.json | 22 +++---- packages/cubejs-trino-driver/CHANGELOG.md | 4 ++ packages/cubejs-trino-driver/package.json | 12 ++-- packages/cubejs-vertica-driver/CHANGELOG.md | 4 ++ packages/cubejs-vertica-driver/package.json | 14 ++--- rust/cubesql/CHANGELOG.md | 4 ++ rust/cubesql/package.json | 2 +- rust/cubestore/CHANGELOG.md | 6 ++ rust/cubestore/Cargo.lock | 2 +- rust/cubestore/cubestore/Cargo.toml | 2 +- rust/cubestore/package.json | 6 +- 114 files changed, 537 insertions(+), 291 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 194650103f508..e8f1037491cc7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,18 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.7.29](https://github.com/cube-js/cube/compare/v1.7.28...v1.7.29) (2026-08-27) + +### Bug Fixes + +- **api-gateway:** Make pre-agg build job status per-entry and data-source aware ([#11666](https://github.com/cube-js/cube/issues/11666)) ([694955f](https://github.com/cube-js/cube/commit/694955f864375515d84d463e9e7ee89a156a4a2b)) +- **query-orchestrator:** initialize missing data source queue in isPartitionExist, thanks [@dochernyshov](https://github.com/dochernyshov) ([#11617](https://github.com/cube-js/cube/issues/11617)) ([ab48039](https://github.com/cube-js/cube/commit/ab48039f1a3d46d890367b0fc898d9ebf1322786)) +- **server-core:** release orchestrators evicted from the LRU ([#11661](https://github.com/cube-js/cube/issues/11661)) ([b82c58a](https://github.com/cube-js/cube/commit/b82c58aa245919a36e3d20f79105b639d0c16863)) + +### Features + +- **cubestore:** cap concurrent websocket connections per user ([#11667](https://github.com/cube-js/cube/issues/11667)) ([111eb3e](https://github.com/cube-js/cube/commit/111eb3ec049b9be1fa2f31186a7f83f58b5a0291)) + ## [1.7.28](https://github.com/cube-js/cube/compare/v1.7.27...v1.7.28) (2026-08-26) ### Bug Fixes diff --git a/lerna.json b/lerna.json index 6dd0bd7ff55f7..187c51197e30f 100644 --- a/lerna.json +++ b/lerna.json @@ -1,5 +1,5 @@ { - "version": "1.7.28", + "version": "1.7.29", "npmClient": "yarn", "command": { "bootstrap": { diff --git a/packages/cubejs-api-gateway/CHANGELOG.md b/packages/cubejs-api-gateway/CHANGELOG.md index e48a266fb4417..53a4870c28c5d 100644 --- a/packages/cubejs-api-gateway/CHANGELOG.md +++ b/packages/cubejs-api-gateway/CHANGELOG.md @@ -3,6 +3,12 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.7.29](https://github.com/cube-js/cube/compare/v1.7.28...v1.7.29) (2026-08-27) + +### Bug Fixes + +- **api-gateway:** Make pre-agg build job status per-entry and data-source aware ([#11666](https://github.com/cube-js/cube/issues/11666)) ([694955f](https://github.com/cube-js/cube/commit/694955f864375515d84d463e9e7ee89a156a4a2b)) + ## [1.7.28](https://github.com/cube-js/cube/compare/v1.7.27...v1.7.28) (2026-08-26) **Note:** Version bump only for package @cubejs-backend/api-gateway diff --git a/packages/cubejs-api-gateway/package.json b/packages/cubejs-api-gateway/package.json index 5b3553858ac10..9e78837e07933 100644 --- a/packages/cubejs-api-gateway/package.json +++ b/packages/cubejs-api-gateway/package.json @@ -2,7 +2,7 @@ "name": "@cubejs-backend/api-gateway", "description": "Cube API Gateway", "author": "Cube Dev, Inc.", - "version": "1.7.28", + "version": "1.7.29", "repository": { "type": "git", "url": "https://github.com/cube-js/cube.git", @@ -27,9 +27,9 @@ "dist/src/*" ], "dependencies": { - "@cubejs-backend/native": "1.7.28", - "@cubejs-backend/query-orchestrator": "1.7.28", - "@cubejs-backend/shared": "1.7.28", + "@cubejs-backend/native": "1.7.29", + "@cubejs-backend/query-orchestrator": "1.7.29", + "@cubejs-backend/shared": "1.7.29", "@ungap/structured-clone": "^0.3.4", "assert-never": "^1.4.0", "body-parser": "^1.19.0", @@ -53,7 +53,7 @@ "zod": "^4.1.13" }, "devDependencies": { - "@cubejs-backend/linter": "1.7.28", + "@cubejs-backend/linter": "1.7.29", "@types/express": "^4.17.21", "@types/jest": "^29", "@types/jsonwebtoken": "^9.0.2", diff --git a/packages/cubejs-athena-driver/CHANGELOG.md b/packages/cubejs-athena-driver/CHANGELOG.md index cc8ac76fe2c1d..1abbe31e6416e 100644 --- a/packages/cubejs-athena-driver/CHANGELOG.md +++ b/packages/cubejs-athena-driver/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.7.29](https://github.com/cube-js/cube/compare/v1.7.28...v1.7.29) (2026-08-27) + +**Note:** Version bump only for package @cubejs-backend/athena-driver + ## [1.7.28](https://github.com/cube-js/cube/compare/v1.7.27...v1.7.28) (2026-08-26) **Note:** Version bump only for package @cubejs-backend/athena-driver diff --git a/packages/cubejs-athena-driver/package.json b/packages/cubejs-athena-driver/package.json index 1c49026cf01fb..41cc1a9f39a2d 100644 --- a/packages/cubejs-athena-driver/package.json +++ b/packages/cubejs-athena-driver/package.json @@ -2,7 +2,7 @@ "name": "@cubejs-backend/athena-driver", "description": "Cube.js Athena database driver", "author": "Cube Dev, Inc.", - "version": "1.7.28", + "version": "1.7.29", "repository": { "type": "git", "url": "https://github.com/cube-js/cube.git", @@ -31,12 +31,12 @@ "dependencies": { "@aws-sdk/client-athena": "^3.22.0", "@aws-sdk/credential-providers": "^3.22.0", - "@cubejs-backend/base-driver": "1.7.28", - "@cubejs-backend/shared": "1.7.28" + "@cubejs-backend/base-driver": "1.7.29", + "@cubejs-backend/shared": "1.7.29" }, "devDependencies": { - "@cubejs-backend/linter": "1.7.28", - "@cubejs-backend/testing-shared": "1.7.28", + "@cubejs-backend/linter": "1.7.29", + "@cubejs-backend/testing-shared": "1.7.29", "@types/ramda": "^0.27.40", "typescript": "~5.2.2" }, diff --git a/packages/cubejs-backend-cloud/CHANGELOG.md b/packages/cubejs-backend-cloud/CHANGELOG.md index 54bd3a3d49f02..c8ad1358b8b31 100644 --- a/packages/cubejs-backend-cloud/CHANGELOG.md +++ b/packages/cubejs-backend-cloud/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.7.29](https://github.com/cube-js/cube/compare/v1.7.28...v1.7.29) (2026-08-27) + +**Note:** Version bump only for package @cubejs-backend/cloud + ## [1.7.28](https://github.com/cube-js/cube/compare/v1.7.27...v1.7.28) (2026-08-26) **Note:** Version bump only for package @cubejs-backend/cloud diff --git a/packages/cubejs-backend-cloud/package.json b/packages/cubejs-backend-cloud/package.json index d6dca569fa3cb..ed6f4baca3cdc 100644 --- a/packages/cubejs-backend-cloud/package.json +++ b/packages/cubejs-backend-cloud/package.json @@ -1,6 +1,6 @@ { "name": "@cubejs-backend/cloud", - "version": "1.7.28", + "version": "1.7.29", "description": "Cube Cloud package", "main": "dist/src/index.js", "typings": "dist/src/index.d.ts", @@ -30,7 +30,7 @@ "devDependencies": { "@babel/core": "^7.24.5", "@babel/preset-env": "^7.24.5", - "@cubejs-backend/linter": "1.7.28", + "@cubejs-backend/linter": "1.7.29", "@types/fs-extra": "^9.0.8", "@types/jest": "^29", "jest": "^29", @@ -38,7 +38,7 @@ }, "dependencies": { "@cubejs-backend/dotenv": "^9.0.2", - "@cubejs-backend/shared": "1.7.28", + "@cubejs-backend/shared": "1.7.29", "chokidar": "^3.5.1", "env-var": "^6.3.0", "form-data": "^4.0.0", diff --git a/packages/cubejs-backend-maven/CHANGELOG.md b/packages/cubejs-backend-maven/CHANGELOG.md index 83480ad76b2a3..09c764af28175 100644 --- a/packages/cubejs-backend-maven/CHANGELOG.md +++ b/packages/cubejs-backend-maven/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.7.29](https://github.com/cube-js/cube/compare/v1.7.28...v1.7.29) (2026-08-27) + +**Note:** Version bump only for package @cubejs-backend/maven + ## [1.7.28](https://github.com/cube-js/cube/compare/v1.7.27...v1.7.28) (2026-08-26) **Note:** Version bump only for package @cubejs-backend/maven diff --git a/packages/cubejs-backend-maven/package.json b/packages/cubejs-backend-maven/package.json index af7564ecae986..4e6091b29e754 100644 --- a/packages/cubejs-backend-maven/package.json +++ b/packages/cubejs-backend-maven/package.json @@ -2,7 +2,7 @@ "name": "@cubejs-backend/maven", "description": "Cube.js Maven Wrapper for java dependencies downloading", "author": "Cube Dev, Inc.", - "version": "1.7.28", + "version": "1.7.29", "license": "Apache-2.0", "repository": { "type": "git", @@ -31,12 +31,12 @@ "dist/src/*" ], "dependencies": { - "@cubejs-backend/shared": "1.7.28", + "@cubejs-backend/shared": "1.7.29", "source-map-support": "^0.5.19", "xmlbuilder2": "^2.4.0" }, "devDependencies": { - "@cubejs-backend/linter": "1.7.28", + "@cubejs-backend/linter": "1.7.29", "@types/jest": "^29", "@types/node": "^22", "jest": "^29", diff --git a/packages/cubejs-backend-native/CHANGELOG.md b/packages/cubejs-backend-native/CHANGELOG.md index ec097487f0957..006a786d3ec99 100644 --- a/packages/cubejs-backend-native/CHANGELOG.md +++ b/packages/cubejs-backend-native/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.7.29](https://github.com/cube-js/cube/compare/v1.7.28...v1.7.29) (2026-08-27) + +**Note:** Version bump only for package @cubejs-backend/native + ## [1.7.28](https://github.com/cube-js/cube/compare/v1.7.27...v1.7.28) (2026-08-26) **Note:** Version bump only for package @cubejs-backend/native diff --git a/packages/cubejs-backend-native/package.json b/packages/cubejs-backend-native/package.json index c190847902b5e..d7ecbf0f007b2 100644 --- a/packages/cubejs-backend-native/package.json +++ b/packages/cubejs-backend-native/package.json @@ -1,6 +1,6 @@ { "name": "@cubejs-backend/native", - "version": "1.7.28", + "version": "1.7.29", "author": "Cube Dev, Inc.", "description": "Native module for Cube.js (binding to Rust codebase)", "main": "dist/js/index.js", @@ -39,7 +39,7 @@ "dist/js" ], "devDependencies": { - "@cubejs-backend/linter": "1.7.28", + "@cubejs-backend/linter": "1.7.29", "@types/jest": "^29", "@types/node": "^22", "cargo-cp-artifact": "^0.1.9", @@ -50,8 +50,8 @@ "uuid": "^11.1.1" }, "dependencies": { - "@cubejs-backend/cubesql": "1.7.28", - "@cubejs-backend/shared": "1.7.28", + "@cubejs-backend/cubesql": "1.7.29", + "@cubejs-backend/shared": "1.7.29", "@cubejs-infra/post-installer": "^0.1.2" }, "resources": { diff --git a/packages/cubejs-backend-shared/CHANGELOG.md b/packages/cubejs-backend-shared/CHANGELOG.md index aafab09388ab8..602e4f96aa936 100644 --- a/packages/cubejs-backend-shared/CHANGELOG.md +++ b/packages/cubejs-backend-shared/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.7.29](https://github.com/cube-js/cube/compare/v1.7.28...v1.7.29) (2026-08-27) + +**Note:** Version bump only for package @cubejs-backend/shared + ## [1.7.28](https://github.com/cube-js/cube/compare/v1.7.27...v1.7.28) (2026-08-26) ### Bug Fixes diff --git a/packages/cubejs-backend-shared/package.json b/packages/cubejs-backend-shared/package.json index 9bc2af1be01cf..88722abbea5c5 100644 --- a/packages/cubejs-backend-shared/package.json +++ b/packages/cubejs-backend-shared/package.json @@ -1,6 +1,6 @@ { "name": "@cubejs-backend/shared", - "version": "1.7.28", + "version": "1.7.29", "description": "Shared code for Cube.js backend packages", "main": "dist/src/index.js", "typings": "dist/src/index.d.ts", @@ -27,7 +27,7 @@ }, "license": "Apache-2.0", "devDependencies": { - "@cubejs-backend/linter": "1.7.28", + "@cubejs-backend/linter": "1.7.29", "@types/bytes": "^3.1.5", "@types/cli-progress": "^3.9.1", "@types/jest": "^29", diff --git a/packages/cubejs-base-driver/CHANGELOG.md b/packages/cubejs-base-driver/CHANGELOG.md index 351d937c8f07f..13df2b18a20fb 100644 --- a/packages/cubejs-base-driver/CHANGELOG.md +++ b/packages/cubejs-base-driver/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.7.29](https://github.com/cube-js/cube/compare/v1.7.28...v1.7.29) (2026-08-27) + +**Note:** Version bump only for package @cubejs-backend/base-driver + ## [1.7.28](https://github.com/cube-js/cube/compare/v1.7.27...v1.7.28) (2026-08-26) **Note:** Version bump only for package @cubejs-backend/base-driver diff --git a/packages/cubejs-base-driver/package.json b/packages/cubejs-base-driver/package.json index f9eae0553b85a..45ecd5cd90988 100644 --- a/packages/cubejs-base-driver/package.json +++ b/packages/cubejs-base-driver/package.json @@ -2,7 +2,7 @@ "name": "@cubejs-backend/base-driver", "description": "Cube.js Base Driver", "author": "Cube Dev, Inc.", - "version": "1.7.28", + "version": "1.7.29", "repository": { "type": "git", "url": "https://github.com/cube-js/cube.git", @@ -33,11 +33,11 @@ "@aws-sdk/s3-request-presigner": "^3.49.0", "@azure/identity": "^4.4.1", "@azure/storage-blob": "^12.9.0", - "@cubejs-backend/shared": "1.7.28", + "@cubejs-backend/shared": "1.7.29", "@google-cloud/storage": "^7.13.0" }, "devDependencies": { - "@cubejs-backend/linter": "1.7.28", + "@cubejs-backend/linter": "1.7.29", "@types/jest": "^29", "@types/node": "^22", "jest": "^29", diff --git a/packages/cubejs-bigquery-driver/CHANGELOG.md b/packages/cubejs-bigquery-driver/CHANGELOG.md index 7babf1af08460..9e05c45b9cd73 100644 --- a/packages/cubejs-bigquery-driver/CHANGELOG.md +++ b/packages/cubejs-bigquery-driver/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.7.29](https://github.com/cube-js/cube/compare/v1.7.28...v1.7.29) (2026-08-27) + +**Note:** Version bump only for package @cubejs-backend/bigquery-driver + ## [1.7.28](https://github.com/cube-js/cube/compare/v1.7.27...v1.7.28) (2026-08-26) **Note:** Version bump only for package @cubejs-backend/bigquery-driver diff --git a/packages/cubejs-bigquery-driver/package.json b/packages/cubejs-bigquery-driver/package.json index 65a5e9f97cc50..756f3a93aa793 100644 --- a/packages/cubejs-bigquery-driver/package.json +++ b/packages/cubejs-bigquery-driver/package.json @@ -2,7 +2,7 @@ "name": "@cubejs-backend/bigquery-driver", "description": "Cube.js BigQuery database driver", "author": "Cube Dev, Inc.", - "version": "1.7.28", + "version": "1.7.29", "repository": { "type": "git", "url": "https://github.com/cube-js/cube.git", @@ -29,15 +29,15 @@ "main": "index.js", "types": "dist/src/index.d.ts", "dependencies": { - "@cubejs-backend/base-driver": "1.7.28", + "@cubejs-backend/base-driver": "1.7.29", "@cubejs-backend/dotenv": "^9.0.2", - "@cubejs-backend/shared": "1.7.28", + "@cubejs-backend/shared": "1.7.29", "@google-cloud/bigquery": "^7.7.0", "@google-cloud/storage": "^7.13.0", "ramda": "^0.27.2" }, "devDependencies": { - "@cubejs-backend/testing-shared": "1.7.28", + "@cubejs-backend/testing-shared": "1.7.29", "@types/big.js": "^6.2.2", "@types/dedent": "^0.7.0", "@types/jest": "^29", diff --git a/packages/cubejs-cli/CHANGELOG.md b/packages/cubejs-cli/CHANGELOG.md index 2e1a5e567a580..acd7b7a5be87e 100644 --- a/packages/cubejs-cli/CHANGELOG.md +++ b/packages/cubejs-cli/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.7.29](https://github.com/cube-js/cube/compare/v1.7.28...v1.7.29) (2026-08-27) + +**Note:** Version bump only for package cubejs-cli + ## [1.7.28](https://github.com/cube-js/cube/compare/v1.7.27...v1.7.28) (2026-08-26) **Note:** Version bump only for package cubejs-cli diff --git a/packages/cubejs-cli/package.json b/packages/cubejs-cli/package.json index d27f6576158c6..8d77be17508bd 100644 --- a/packages/cubejs-cli/package.json +++ b/packages/cubejs-cli/package.json @@ -2,7 +2,7 @@ "name": "cubejs-cli", "description": "Cube.js Command Line Interface", "author": "Cube Dev, Inc.", - "version": "1.7.28", + "version": "1.7.29", "repository": { "type": "git", "url": "https://github.com/cube-js/cube.git", @@ -30,10 +30,10 @@ "LICENSE" ], "dependencies": { - "@cubejs-backend/cloud": "1.7.28", + "@cubejs-backend/cloud": "1.7.29", "@cubejs-backend/dotenv": "^9.0.2", - "@cubejs-backend/schema-compiler": "1.7.28", - "@cubejs-backend/shared": "1.7.28", + "@cubejs-backend/schema-compiler": "1.7.29", + "@cubejs-backend/shared": "1.7.29", "chalk": "^2.4.2", "cli-progress": "^3.10", "commander": "^2.19.0", @@ -50,8 +50,8 @@ "colors": "1.4.0" }, "devDependencies": { - "@cubejs-backend/linter": "1.7.28", - "@cubejs-backend/server": "1.7.28", + "@cubejs-backend/linter": "1.7.29", + "@cubejs-backend/server": "1.7.29", "@oclif/command": "^1.8.0", "@types/cli-progress": "^3.8.0", "@types/cross-spawn": "^6.0.2", diff --git a/packages/cubejs-clickhouse-driver/CHANGELOG.md b/packages/cubejs-clickhouse-driver/CHANGELOG.md index fb1aa319a78f2..6a740d925fee0 100644 --- a/packages/cubejs-clickhouse-driver/CHANGELOG.md +++ b/packages/cubejs-clickhouse-driver/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.7.29](https://github.com/cube-js/cube/compare/v1.7.28...v1.7.29) (2026-08-27) + +**Note:** Version bump only for package @cubejs-backend/clickhouse-driver + ## [1.7.28](https://github.com/cube-js/cube/compare/v1.7.27...v1.7.28) (2026-08-26) **Note:** Version bump only for package @cubejs-backend/clickhouse-driver diff --git a/packages/cubejs-clickhouse-driver/package.json b/packages/cubejs-clickhouse-driver/package.json index ab4f5da8dd6ae..dd1f05b71589f 100644 --- a/packages/cubejs-clickhouse-driver/package.json +++ b/packages/cubejs-clickhouse-driver/package.json @@ -2,7 +2,7 @@ "name": "@cubejs-backend/clickhouse-driver", "description": "Cube.js ClickHouse database driver", "author": "Cube Dev, Inc.", - "version": "1.7.28", + "version": "1.7.29", "repository": { "type": "git", "url": "https://github.com/cube-js/cube.git", @@ -29,16 +29,16 @@ }, "dependencies": { "@clickhouse/client": "^1.12.0", - "@cubejs-backend/base-driver": "1.7.28", - "@cubejs-backend/shared": "1.7.28", + "@cubejs-backend/base-driver": "1.7.29", + "@cubejs-backend/shared": "1.7.29", "moment": "^2.24.0", "sqlstring": "^2.3.1", "uuid": "^11.1.1" }, "license": "Apache-2.0", "devDependencies": { - "@cubejs-backend/linter": "1.7.28", - "@cubejs-backend/testing-shared": "1.7.28", + "@cubejs-backend/linter": "1.7.29", + "@cubejs-backend/testing-shared": "1.7.29", "@types/jest": "^29", "jest": "^29", "typescript": "~5.2.2" diff --git a/packages/cubejs-client-core/CHANGELOG.md b/packages/cubejs-client-core/CHANGELOG.md index 328b69dd3fb2f..9cbd8695e28aa 100644 --- a/packages/cubejs-client-core/CHANGELOG.md +++ b/packages/cubejs-client-core/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.7.29](https://github.com/cube-js/cube/compare/v1.7.28...v1.7.29) (2026-08-27) + +**Note:** Version bump only for package @cubejs-client/core + ## [1.7.28](https://github.com/cube-js/cube/compare/v1.7.27...v1.7.28) (2026-08-26) **Note:** Version bump only for package @cubejs-client/core diff --git a/packages/cubejs-client-core/package.json b/packages/cubejs-client-core/package.json index 1865c8a7dc5d2..60d127c4f3a72 100644 --- a/packages/cubejs-client-core/package.json +++ b/packages/cubejs-client-core/package.json @@ -1,6 +1,6 @@ { "name": "@cubejs-client/core", - "version": "1.7.28", + "version": "1.7.29", "engines": {}, "type": "module", "repository": { @@ -58,7 +58,7 @@ ], "license": "MIT", "devDependencies": { - "@cubejs-backend/linter": "1.7.28", + "@cubejs-backend/linter": "1.7.29", "@types/d3-format": "^3", "@types/d3-time-format": "^4", "@types/moment-range": "^4.0.0", diff --git a/packages/cubejs-client-dx/CHANGELOG.md b/packages/cubejs-client-dx/CHANGELOG.md index 1067446ed856f..5d99dca457a55 100644 --- a/packages/cubejs-client-dx/CHANGELOG.md +++ b/packages/cubejs-client-dx/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.7.29](https://github.com/cube-js/cube/compare/v1.7.28...v1.7.29) (2026-08-27) + +**Note:** Version bump only for package @cubejs-client/dx + ## [1.7.28](https://github.com/cube-js/cube/compare/v1.7.27...v1.7.28) (2026-08-26) **Note:** Version bump only for package @cubejs-client/dx diff --git a/packages/cubejs-client-dx/package.json b/packages/cubejs-client-dx/package.json index 4a9017e66120e..dc8ee50644e45 100644 --- a/packages/cubejs-client-dx/package.json +++ b/packages/cubejs-client-dx/package.json @@ -1,6 +1,6 @@ { "name": "@cubejs-client/dx", - "version": "1.7.28", + "version": "1.7.29", "engines": {}, "repository": { "type": "git", diff --git a/packages/cubejs-client-ngx/CHANGELOG.md b/packages/cubejs-client-ngx/CHANGELOG.md index 3c8f41be681f7..a953a5363ef19 100644 --- a/packages/cubejs-client-ngx/CHANGELOG.md +++ b/packages/cubejs-client-ngx/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.7.29](https://github.com/cube-js/cube/compare/v1.7.28...v1.7.29) (2026-08-27) + +**Note:** Version bump only for package @cubejs-client/ngx + ## [1.7.28](https://github.com/cube-js/cube/compare/v1.7.27...v1.7.28) (2026-08-26) **Note:** Version bump only for package @cubejs-client/ngx diff --git a/packages/cubejs-client-ngx/package.json b/packages/cubejs-client-ngx/package.json index 21ac9a14d43c7..bebfbd5f60c59 100644 --- a/packages/cubejs-client-ngx/package.json +++ b/packages/cubejs-client-ngx/package.json @@ -1,6 +1,6 @@ { "name": "@cubejs-client/ngx", - "version": "1.7.28", + "version": "1.7.29", "author": "Cube Dev, Inc.", "engines": {}, "repository": { diff --git a/packages/cubejs-client-react/CHANGELOG.md b/packages/cubejs-client-react/CHANGELOG.md index 665061d2f0f91..5358adc9d3404 100644 --- a/packages/cubejs-client-react/CHANGELOG.md +++ b/packages/cubejs-client-react/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.7.29](https://github.com/cube-js/cube/compare/v1.7.28...v1.7.29) (2026-08-27) + +**Note:** Version bump only for package @cubejs-client/react + ## [1.7.28](https://github.com/cube-js/cube/compare/v1.7.27...v1.7.28) (2026-08-26) **Note:** Version bump only for package @cubejs-client/react diff --git a/packages/cubejs-client-react/package.json b/packages/cubejs-client-react/package.json index f266d1ae12ffe..8d371c8bb0fd3 100644 --- a/packages/cubejs-client-react/package.json +++ b/packages/cubejs-client-react/package.json @@ -1,6 +1,6 @@ { "name": "@cubejs-client/react", - "version": "1.7.28", + "version": "1.7.29", "author": "Cube Dev, Inc.", "license": "MIT", "engines": {}, @@ -26,7 +26,7 @@ ], "dependencies": { "@babel/runtime": "^7.1.2", - "@cubejs-client/core": "1.7.28", + "@cubejs-client/core": "1.7.29", "core-js": "^3.6.5", "ramda": "^0.27.2" }, diff --git a/packages/cubejs-client-vue3/CHANGELOG.md b/packages/cubejs-client-vue3/CHANGELOG.md index 36375c32cf04f..cd7817677efd6 100644 --- a/packages/cubejs-client-vue3/CHANGELOG.md +++ b/packages/cubejs-client-vue3/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.7.29](https://github.com/cube-js/cube/compare/v1.7.28...v1.7.29) (2026-08-27) + +**Note:** Version bump only for package @cubejs-client/vue3 + ## [1.7.28](https://github.com/cube-js/cube/compare/v1.7.27...v1.7.28) (2026-08-26) **Note:** Version bump only for package @cubejs-client/vue3 diff --git a/packages/cubejs-client-vue3/package.json b/packages/cubejs-client-vue3/package.json index bd4bc226b7454..b980de9651a36 100644 --- a/packages/cubejs-client-vue3/package.json +++ b/packages/cubejs-client-vue3/package.json @@ -1,6 +1,6 @@ { "name": "@cubejs-client/vue3", - "version": "1.7.28", + "version": "1.7.29", "engines": {}, "repository": { "type": "git", @@ -27,7 +27,7 @@ "src" ], "dependencies": { - "@cubejs-client/core": "1.7.28", + "@cubejs-client/core": "1.7.29", "ramda": "^0.27.0" }, "devDependencies": { diff --git a/packages/cubejs-client-ws-transport/CHANGELOG.md b/packages/cubejs-client-ws-transport/CHANGELOG.md index 2f971fcb9096c..711b52b2ab5f5 100644 --- a/packages/cubejs-client-ws-transport/CHANGELOG.md +++ b/packages/cubejs-client-ws-transport/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.7.29](https://github.com/cube-js/cube/compare/v1.7.28...v1.7.29) (2026-08-27) + +**Note:** Version bump only for package @cubejs-client/ws-transport + ## [1.7.28](https://github.com/cube-js/cube/compare/v1.7.27...v1.7.28) (2026-08-26) **Note:** Version bump only for package @cubejs-client/ws-transport diff --git a/packages/cubejs-client-ws-transport/package.json b/packages/cubejs-client-ws-transport/package.json index c9494f0ea742d..0d97dd5ae88b7 100644 --- a/packages/cubejs-client-ws-transport/package.json +++ b/packages/cubejs-client-ws-transport/package.json @@ -1,6 +1,6 @@ { "name": "@cubejs-client/ws-transport", - "version": "1.7.28", + "version": "1.7.29", "engines": {}, "repository": { "type": "git", @@ -20,7 +20,7 @@ }, "dependencies": { "@babel/runtime": "^7.1.2", - "@cubejs-client/core": "1.7.28", + "@cubejs-client/core": "1.7.29", "core-js": "^3.6.5", "isomorphic-ws": "^4.0.1", "ws": "^7.3.1" @@ -33,7 +33,7 @@ "@babel/core": "^7.3.3", "@babel/preset-env": "^7.3.1", "@babel/preset-typescript": "^7.12.1", - "@cubejs-backend/linter": "1.7.28", + "@cubejs-backend/linter": "1.7.29", "@types/ws": "^7.2.9", "typescript": "~5.2.2" }, diff --git a/packages/cubejs-crate-driver/CHANGELOG.md b/packages/cubejs-crate-driver/CHANGELOG.md index 5e11febd61d87..a345375c60d97 100644 --- a/packages/cubejs-crate-driver/CHANGELOG.md +++ b/packages/cubejs-crate-driver/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.7.29](https://github.com/cube-js/cube/compare/v1.7.28...v1.7.29) (2026-08-27) + +**Note:** Version bump only for package @cubejs-backend/crate-driver + ## [1.7.28](https://github.com/cube-js/cube/compare/v1.7.27...v1.7.28) (2026-08-26) **Note:** Version bump only for package @cubejs-backend/crate-driver diff --git a/packages/cubejs-crate-driver/package.json b/packages/cubejs-crate-driver/package.json index 1daaeb392c711..c57761ab2335d 100644 --- a/packages/cubejs-crate-driver/package.json +++ b/packages/cubejs-crate-driver/package.json @@ -2,7 +2,7 @@ "name": "@cubejs-backend/crate-driver", "description": "Cube.js Crate database driver", "author": "Cube Dev, Inc.", - "version": "1.7.28", + "version": "1.7.29", "repository": { "type": "git", "url": "https://github.com/cube-js/cube.git", @@ -29,13 +29,13 @@ "lint:fix": "eslint --fix src/* --ext .ts" }, "dependencies": { - "@cubejs-backend/postgres-driver": "1.7.28", - "@cubejs-backend/shared": "1.7.28" + "@cubejs-backend/postgres-driver": "1.7.29", + "@cubejs-backend/shared": "1.7.29" }, "license": "Apache-2.0", "devDependencies": { - "@cubejs-backend/linter": "1.7.28", - "@cubejs-backend/testing-shared": "1.7.28", + "@cubejs-backend/linter": "1.7.29", + "@cubejs-backend/testing-shared": "1.7.29", "testcontainers": "^10.28.0", "typescript": "~5.2.2" }, diff --git a/packages/cubejs-cubestore-driver/CHANGELOG.md b/packages/cubejs-cubestore-driver/CHANGELOG.md index e0dab773a26e3..6f96fd950a826 100644 --- a/packages/cubejs-cubestore-driver/CHANGELOG.md +++ b/packages/cubejs-cubestore-driver/CHANGELOG.md @@ -3,6 +3,12 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.7.29](https://github.com/cube-js/cube/compare/v1.7.28...v1.7.29) (2026-08-27) + +### Bug Fixes + +- **server-core:** release orchestrators evicted from the LRU ([#11661](https://github.com/cube-js/cube/issues/11661)) ([b82c58a](https://github.com/cube-js/cube/commit/b82c58aa245919a36e3d20f79105b639d0c16863)) + ## [1.7.28](https://github.com/cube-js/cube/compare/v1.7.27...v1.7.28) (2026-08-26) ### Bug Fixes diff --git a/packages/cubejs-cubestore-driver/package.json b/packages/cubejs-cubestore-driver/package.json index 6c06ae3a9cbb5..8d5cc4ce1ae86 100644 --- a/packages/cubejs-cubestore-driver/package.json +++ b/packages/cubejs-cubestore-driver/package.json @@ -2,7 +2,7 @@ "name": "@cubejs-backend/cubestore-driver", "description": "Cube Store driver", "author": "Cube Dev, Inc.", - "version": "1.7.28", + "version": "1.7.29", "repository": { "type": "git", "url": "https://github.com/cube-js/cube.git", @@ -27,10 +27,10 @@ "unit": "jest --coverage" }, "dependencies": { - "@cubejs-backend/base-driver": "1.7.28", - "@cubejs-backend/cubestore": "1.7.28", - "@cubejs-backend/native": "1.7.28", - "@cubejs-backend/shared": "1.7.28", + "@cubejs-backend/base-driver": "1.7.29", + "@cubejs-backend/cubestore": "1.7.29", + "@cubejs-backend/native": "1.7.29", + "@cubejs-backend/shared": "1.7.29", "csv-write-stream": "^2.0.0", "flatbuffers": "25.9.23", "fs-extra": "^9.1.0", @@ -41,7 +41,7 @@ "ws": "^7.4.3" }, "devDependencies": { - "@cubejs-backend/linter": "1.7.28", + "@cubejs-backend/linter": "1.7.29", "@types/csv-write-stream": "^2.0.0", "@types/jest": "^29", "@types/node": "^22", diff --git a/packages/cubejs-databricks-jdbc-driver/CHANGELOG.md b/packages/cubejs-databricks-jdbc-driver/CHANGELOG.md index 49e46739ddcc1..911f91903c4b5 100644 --- a/packages/cubejs-databricks-jdbc-driver/CHANGELOG.md +++ b/packages/cubejs-databricks-jdbc-driver/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.7.29](https://github.com/cube-js/cube/compare/v1.7.28...v1.7.29) (2026-08-27) + +**Note:** Version bump only for package @cubejs-backend/databricks-jdbc-driver + ## [1.7.28](https://github.com/cube-js/cube/compare/v1.7.27...v1.7.28) (2026-08-26) **Note:** Version bump only for package @cubejs-backend/databricks-jdbc-driver diff --git a/packages/cubejs-databricks-jdbc-driver/package.json b/packages/cubejs-databricks-jdbc-driver/package.json index 2922707950ae2..6931578c0871e 100644 --- a/packages/cubejs-databricks-jdbc-driver/package.json +++ b/packages/cubejs-databricks-jdbc-driver/package.json @@ -2,7 +2,7 @@ "name": "@cubejs-backend/databricks-jdbc-driver", "description": "Cube.js Databricks database driver", "author": "Cube Dev, Inc.", - "version": "1.7.28", + "version": "1.7.29", "license": "Apache-2.0", "repository": { "type": "git", @@ -30,17 +30,17 @@ "bin" ], "dependencies": { - "@cubejs-backend/base-driver": "1.7.28", - "@cubejs-backend/jdbc-driver": "1.7.28", - "@cubejs-backend/schema-compiler": "1.7.28", - "@cubejs-backend/shared": "1.7.28", + "@cubejs-backend/base-driver": "1.7.29", + "@cubejs-backend/jdbc-driver": "1.7.29", + "@cubejs-backend/schema-compiler": "1.7.29", + "@cubejs-backend/shared": "1.7.29", "node-fetch": "^2.6.1", "ramda": "^0.27.2", "source-map-support": "^0.5.19", "uuid": "^11.1.1" }, "devDependencies": { - "@cubejs-backend/linter": "1.7.28", + "@cubejs-backend/linter": "1.7.29", "@types/jest": "^29", "@types/node": "^22", "@types/ramda": "^0.27.34", diff --git a/packages/cubejs-dbt-schema-extension/CHANGELOG.md b/packages/cubejs-dbt-schema-extension/CHANGELOG.md index 6498bcdaf56e4..21f26c87a1bc5 100644 --- a/packages/cubejs-dbt-schema-extension/CHANGELOG.md +++ b/packages/cubejs-dbt-schema-extension/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.7.29](https://github.com/cube-js/cube/compare/v1.7.28...v1.7.29) (2026-08-27) + +**Note:** Version bump only for package @cubejs-backend/dbt-schema-extension + ## [1.7.28](https://github.com/cube-js/cube/compare/v1.7.27...v1.7.28) (2026-08-26) **Note:** Version bump only for package @cubejs-backend/dbt-schema-extension diff --git a/packages/cubejs-dbt-schema-extension/package.json b/packages/cubejs-dbt-schema-extension/package.json index c7b5570c335d5..040f701ed5ec2 100644 --- a/packages/cubejs-dbt-schema-extension/package.json +++ b/packages/cubejs-dbt-schema-extension/package.json @@ -2,7 +2,7 @@ "name": "@cubejs-backend/dbt-schema-extension", "description": "Cube.js dbt Schema Extension", "author": "Cube Dev, Inc.", - "version": "1.7.28", + "version": "1.7.29", "repository": { "type": "git", "url": "https://github.com/cube-js/cube.git", @@ -25,14 +25,14 @@ "lint:fix": "eslint --fix src/* --ext .ts,.js" }, "dependencies": { - "@cubejs-backend/schema-compiler": "1.7.28", + "@cubejs-backend/schema-compiler": "1.7.29", "fs-extra": "^9.1.0", "inflection": "^1.12.0", "node-fetch": "^2.6.1" }, "devDependencies": { - "@cubejs-backend/linter": "1.7.28", - "@cubejs-backend/testing": "1.7.28", + "@cubejs-backend/linter": "1.7.29", + "@cubejs-backend/testing": "1.7.29", "@types/jest": "^29", "jest": "^29", "stream-to-array": "^2.3.0", diff --git a/packages/cubejs-docker/CHANGELOG.md b/packages/cubejs-docker/CHANGELOG.md index dd686784a926c..f0c1c8b58ad1a 100644 --- a/packages/cubejs-docker/CHANGELOG.md +++ b/packages/cubejs-docker/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.7.29](https://github.com/cube-js/cube/compare/v1.7.28...v1.7.29) (2026-08-27) + +**Note:** Version bump only for package @cubejs-backend/docker + ## [1.7.28](https://github.com/cube-js/cube/compare/v1.7.27...v1.7.28) (2026-08-26) **Note:** Version bump only for package @cubejs-backend/docker diff --git a/packages/cubejs-docker/package.json b/packages/cubejs-docker/package.json index f91df7753bfb6..9c2fa30da73d7 100644 --- a/packages/cubejs-docker/package.json +++ b/packages/cubejs-docker/package.json @@ -1,6 +1,6 @@ { "name": "@cubejs-backend/docker", - "version": "1.7.28", + "version": "1.7.29", "description": "Cube.js In Docker (virtual package)", "author": "Cube Dev, Inc.", "license": "Apache-2.0", @@ -9,34 +9,34 @@ "node": ">=20.0.0" }, "dependencies": { - "@cubejs-backend/athena-driver": "1.7.28", - "@cubejs-backend/bigquery-driver": "1.7.28", - "@cubejs-backend/clickhouse-driver": "1.7.28", - "@cubejs-backend/crate-driver": "1.7.28", - "@cubejs-backend/databricks-jdbc-driver": "1.7.28", - "@cubejs-backend/dbt-schema-extension": "1.7.28", - "@cubejs-backend/dremio-driver": "1.7.28", - "@cubejs-backend/druid-driver": "1.7.28", - "@cubejs-backend/duckdb-driver": "1.7.28", - "@cubejs-backend/firebolt-driver": "1.7.28", - "@cubejs-backend/hive-driver": "1.7.28", - "@cubejs-backend/ksql-driver": "1.7.28", - "@cubejs-backend/materialize-driver": "1.7.28", - "@cubejs-backend/mongobi-driver": "1.7.28", - "@cubejs-backend/mssql-driver": "1.7.28", - "@cubejs-backend/mysql-driver": "1.7.28", - "@cubejs-backend/oracle-driver": "1.7.28", - "@cubejs-backend/pinot-driver": "1.7.28", - "@cubejs-backend/postgres-driver": "1.7.28", - "@cubejs-backend/prestodb-driver": "1.7.28", - "@cubejs-backend/questdb-driver": "1.7.28", - "@cubejs-backend/redshift-driver": "1.7.28", - "@cubejs-backend/server": "1.7.28", - "@cubejs-backend/snowflake-driver": "1.7.28", - "@cubejs-backend/sqlite-driver": "1.7.28", - "@cubejs-backend/trino-driver": "1.7.28", - "@cubejs-backend/vertica-driver": "1.7.28", - "cubejs-cli": "1.7.28", + "@cubejs-backend/athena-driver": "1.7.29", + "@cubejs-backend/bigquery-driver": "1.7.29", + "@cubejs-backend/clickhouse-driver": "1.7.29", + "@cubejs-backend/crate-driver": "1.7.29", + "@cubejs-backend/databricks-jdbc-driver": "1.7.29", + "@cubejs-backend/dbt-schema-extension": "1.7.29", + "@cubejs-backend/dremio-driver": "1.7.29", + "@cubejs-backend/druid-driver": "1.7.29", + "@cubejs-backend/duckdb-driver": "1.7.29", + "@cubejs-backend/firebolt-driver": "1.7.29", + "@cubejs-backend/hive-driver": "1.7.29", + "@cubejs-backend/ksql-driver": "1.7.29", + "@cubejs-backend/materialize-driver": "1.7.29", + "@cubejs-backend/mongobi-driver": "1.7.29", + "@cubejs-backend/mssql-driver": "1.7.29", + "@cubejs-backend/mysql-driver": "1.7.29", + "@cubejs-backend/oracle-driver": "1.7.29", + "@cubejs-backend/pinot-driver": "1.7.29", + "@cubejs-backend/postgres-driver": "1.7.29", + "@cubejs-backend/prestodb-driver": "1.7.29", + "@cubejs-backend/questdb-driver": "1.7.29", + "@cubejs-backend/redshift-driver": "1.7.29", + "@cubejs-backend/server": "1.7.29", + "@cubejs-backend/snowflake-driver": "1.7.29", + "@cubejs-backend/sqlite-driver": "1.7.29", + "@cubejs-backend/trino-driver": "1.7.29", + "@cubejs-backend/vertica-driver": "1.7.29", + "cubejs-cli": "1.7.29", "typescript": "~5.2.2" }, "resolutions": { diff --git a/packages/cubejs-dremio-driver/CHANGELOG.md b/packages/cubejs-dremio-driver/CHANGELOG.md index a059d13f61e03..4c2d50f3a98e8 100644 --- a/packages/cubejs-dremio-driver/CHANGELOG.md +++ b/packages/cubejs-dremio-driver/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.7.29](https://github.com/cube-js/cube/compare/v1.7.28...v1.7.29) (2026-08-27) + +**Note:** Version bump only for package @cubejs-backend/dremio-driver + ## [1.7.28](https://github.com/cube-js/cube/compare/v1.7.27...v1.7.28) (2026-08-26) **Note:** Version bump only for package @cubejs-backend/dremio-driver diff --git a/packages/cubejs-dremio-driver/package.json b/packages/cubejs-dremio-driver/package.json index ee512d1cf0323..6b29078c87ff8 100644 --- a/packages/cubejs-dremio-driver/package.json +++ b/packages/cubejs-dremio-driver/package.json @@ -2,7 +2,7 @@ "name": "@cubejs-backend/dremio-driver", "description": "Cube.js Dremio driver", "author": "Cube Dev, Inc.", - "version": "1.7.28", + "version": "1.7.29", "repository": { "type": "git", "url": "https://github.com/cube-js/cube.git", @@ -23,14 +23,14 @@ "lint:fix": "eslint driver/*.js" }, "dependencies": { - "@cubejs-backend/base-driver": "1.7.28", - "@cubejs-backend/schema-compiler": "1.7.28", - "@cubejs-backend/shared": "1.7.28", + "@cubejs-backend/base-driver": "1.7.29", + "@cubejs-backend/schema-compiler": "1.7.29", + "@cubejs-backend/shared": "1.7.29", "axios": "^1.8.3" }, "devDependencies": { - "@cubejs-backend/linter": "1.7.28", - "@cubejs-backend/testing-shared": "1.7.28", + "@cubejs-backend/linter": "1.7.29", + "@cubejs-backend/testing-shared": "1.7.29", "jest": "^29" }, "license": "Apache-2.0", diff --git a/packages/cubejs-druid-driver/CHANGELOG.md b/packages/cubejs-druid-driver/CHANGELOG.md index 6801d2383e144..67c75300f4805 100644 --- a/packages/cubejs-druid-driver/CHANGELOG.md +++ b/packages/cubejs-druid-driver/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.7.29](https://github.com/cube-js/cube/compare/v1.7.28...v1.7.29) (2026-08-27) + +**Note:** Version bump only for package @cubejs-backend/druid-driver + ## [1.7.28](https://github.com/cube-js/cube/compare/v1.7.27...v1.7.28) (2026-08-26) ### Features diff --git a/packages/cubejs-druid-driver/package.json b/packages/cubejs-druid-driver/package.json index 9117e25e3847a..d03a18a18b7e6 100644 --- a/packages/cubejs-druid-driver/package.json +++ b/packages/cubejs-druid-driver/package.json @@ -2,7 +2,7 @@ "name": "@cubejs-backend/druid-driver", "description": "Cube.js Druid database driver", "author": "Cube Dev, Inc.", - "version": "1.7.28", + "version": "1.7.29", "license": "Apache-2.0", "repository": { "type": "git", @@ -28,13 +28,13 @@ "dist/src/*" ], "dependencies": { - "@cubejs-backend/base-driver": "1.7.28", - "@cubejs-backend/schema-compiler": "1.7.28", - "@cubejs-backend/shared": "1.7.28", + "@cubejs-backend/base-driver": "1.7.29", + "@cubejs-backend/schema-compiler": "1.7.29", + "@cubejs-backend/shared": "1.7.29", "axios": "^1.8.3" }, "devDependencies": { - "@cubejs-backend/linter": "1.7.28", + "@cubejs-backend/linter": "1.7.29", "@types/jest": "^29", "@types/node": "^22", "jest": "^29", diff --git a/packages/cubejs-duckdb-driver/CHANGELOG.md b/packages/cubejs-duckdb-driver/CHANGELOG.md index 21e7f6bcb754a..1546c9bb0980c 100644 --- a/packages/cubejs-duckdb-driver/CHANGELOG.md +++ b/packages/cubejs-duckdb-driver/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.7.29](https://github.com/cube-js/cube/compare/v1.7.28...v1.7.29) (2026-08-27) + +**Note:** Version bump only for package @cubejs-backend/duckdb-driver + ## [1.7.28](https://github.com/cube-js/cube/compare/v1.7.27...v1.7.28) (2026-08-26) **Note:** Version bump only for package @cubejs-backend/duckdb-driver diff --git a/packages/cubejs-duckdb-driver/package.json b/packages/cubejs-duckdb-driver/package.json index 023c6fca31241..1c71360f9b773 100644 --- a/packages/cubejs-duckdb-driver/package.json +++ b/packages/cubejs-duckdb-driver/package.json @@ -2,7 +2,7 @@ "name": "@cubejs-backend/duckdb-driver", "description": "Cube DuckDB database driver", "author": "Cube Dev, Inc.", - "version": "1.7.28", + "version": "1.7.29", "repository": { "type": "git", "url": "https://github.com/cube-js/cube.git", @@ -28,15 +28,15 @@ "lint:fix": "eslint --fix src/* --ext .ts" }, "dependencies": { - "@cubejs-backend/base-driver": "1.7.28", - "@cubejs-backend/schema-compiler": "1.7.28", - "@cubejs-backend/shared": "1.7.28", + "@cubejs-backend/base-driver": "1.7.29", + "@cubejs-backend/schema-compiler": "1.7.29", + "@cubejs-backend/shared": "1.7.29", "duckdb": "^1.4.1" }, "license": "Apache-2.0", "devDependencies": { - "@cubejs-backend/linter": "1.7.28", - "@cubejs-backend/testing-shared": "1.7.28", + "@cubejs-backend/linter": "1.7.29", + "@cubejs-backend/testing-shared": "1.7.29", "@types/jest": "^29", "@types/node": "^22", "jest": "^29", diff --git a/packages/cubejs-firebolt-driver/CHANGELOG.md b/packages/cubejs-firebolt-driver/CHANGELOG.md index 6d5e5bcca144a..f5ca7b1f210ac 100644 --- a/packages/cubejs-firebolt-driver/CHANGELOG.md +++ b/packages/cubejs-firebolt-driver/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.7.29](https://github.com/cube-js/cube/compare/v1.7.28...v1.7.29) (2026-08-27) + +**Note:** Version bump only for package @cubejs-backend/firebolt-driver + ## [1.7.28](https://github.com/cube-js/cube/compare/v1.7.27...v1.7.28) (2026-08-26) **Note:** Version bump only for package @cubejs-backend/firebolt-driver diff --git a/packages/cubejs-firebolt-driver/package.json b/packages/cubejs-firebolt-driver/package.json index 3edb279d6ce72..9b6d95577747c 100644 --- a/packages/cubejs-firebolt-driver/package.json +++ b/packages/cubejs-firebolt-driver/package.json @@ -2,7 +2,7 @@ "name": "@cubejs-backend/firebolt-driver", "description": "Cube.js Firebolt database driver", "author": "Cube Dev, Inc.", - "version": "1.7.28", + "version": "1.7.29", "repository": { "type": "git", "url": "https://github.com/cube-js/cube.git", @@ -28,15 +28,15 @@ "lint:fix": "eslint --fix src/* --ext .ts" }, "dependencies": { - "@cubejs-backend/base-driver": "1.7.28", - "@cubejs-backend/schema-compiler": "1.7.28", - "@cubejs-backend/shared": "1.7.28", + "@cubejs-backend/base-driver": "1.7.29", + "@cubejs-backend/schema-compiler": "1.7.29", + "@cubejs-backend/shared": "1.7.29", "firebolt-sdk": "1.10.0" }, "license": "Apache-2.0", "devDependencies": { - "@cubejs-backend/linter": "1.7.28", - "@cubejs-backend/testing-shared": "1.7.28", + "@cubejs-backend/linter": "1.7.29", + "@cubejs-backend/testing-shared": "1.7.29", "typescript": "~5.2.2" }, "publishConfig": { diff --git a/packages/cubejs-hive-driver/CHANGELOG.md b/packages/cubejs-hive-driver/CHANGELOG.md index 113b054d3599c..ebb2eb808632b 100644 --- a/packages/cubejs-hive-driver/CHANGELOG.md +++ b/packages/cubejs-hive-driver/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.7.29](https://github.com/cube-js/cube/compare/v1.7.28...v1.7.29) (2026-08-27) + +**Note:** Version bump only for package @cubejs-backend/hive-driver + ## [1.7.28](https://github.com/cube-js/cube/compare/v1.7.27...v1.7.28) (2026-08-26) **Note:** Version bump only for package @cubejs-backend/hive-driver diff --git a/packages/cubejs-hive-driver/package.json b/packages/cubejs-hive-driver/package.json index fd22801d8d870..bd8526a025c80 100644 --- a/packages/cubejs-hive-driver/package.json +++ b/packages/cubejs-hive-driver/package.json @@ -2,7 +2,7 @@ "name": "@cubejs-backend/hive-driver", "description": "Cube.js Hive database driver", "author": "Cube Dev, Inc.", - "version": "1.7.28", + "version": "1.7.29", "repository": { "type": "git", "url": "https://github.com/cube-js/cube.git", @@ -17,8 +17,8 @@ "lint:fix": "eslint --fix src/* --ext .ts" }, "dependencies": { - "@cubejs-backend/base-driver": "1.7.28", - "@cubejs-backend/shared": "1.7.28", + "@cubejs-backend/base-driver": "1.7.29", + "@cubejs-backend/shared": "1.7.29", "jshs2": "^0.4.4", "sasl-plain": "^0.1.0", "saslmechanisms": "^0.1.1", @@ -27,7 +27,7 @@ }, "license": "Apache-2.0", "devDependencies": { - "@cubejs-backend/linter": "1.7.28" + "@cubejs-backend/linter": "1.7.29" }, "publishConfig": { "access": "public" diff --git a/packages/cubejs-jdbc-driver/CHANGELOG.md b/packages/cubejs-jdbc-driver/CHANGELOG.md index 2ac4c9f5ff3dd..96e8537b2b83e 100644 --- a/packages/cubejs-jdbc-driver/CHANGELOG.md +++ b/packages/cubejs-jdbc-driver/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.7.29](https://github.com/cube-js/cube/compare/v1.7.28...v1.7.29) (2026-08-27) + +**Note:** Version bump only for package @cubejs-backend/jdbc-driver + ## [1.7.28](https://github.com/cube-js/cube/compare/v1.7.27...v1.7.28) (2026-08-26) **Note:** Version bump only for package @cubejs-backend/jdbc-driver diff --git a/packages/cubejs-jdbc-driver/package.json b/packages/cubejs-jdbc-driver/package.json index 1e65bbe900350..50e99318b7e66 100644 --- a/packages/cubejs-jdbc-driver/package.json +++ b/packages/cubejs-jdbc-driver/package.json @@ -2,7 +2,7 @@ "name": "@cubejs-backend/jdbc-driver", "description": "Cube.js JDBC database driver", "author": "Cube Dev, Inc.", - "version": "1.7.28", + "version": "1.7.29", "repository": { "type": "git", "url": "https://github.com/cube-js/cube.git", @@ -26,9 +26,9 @@ "index.js" ], "dependencies": { - "@cubejs-backend/base-driver": "1.7.28", + "@cubejs-backend/base-driver": "1.7.29", "@cubejs-backend/node-java-maven": "^0.1.3", - "@cubejs-backend/shared": "1.7.28" + "@cubejs-backend/shared": "1.7.29" }, "optionalDependencies": { "@cubejs-backend/jdbc": "^0.9.0", @@ -42,7 +42,7 @@ "testEnvironment": "node" }, "devDependencies": { - "@cubejs-backend/linter": "1.7.28", + "@cubejs-backend/linter": "1.7.29", "@types/node": "^22", "typescript": "~5.2.2" } diff --git a/packages/cubejs-ksql-driver/CHANGELOG.md b/packages/cubejs-ksql-driver/CHANGELOG.md index eb0a5314cd698..d33341218ca76 100644 --- a/packages/cubejs-ksql-driver/CHANGELOG.md +++ b/packages/cubejs-ksql-driver/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.7.29](https://github.com/cube-js/cube/compare/v1.7.28...v1.7.29) (2026-08-27) + +**Note:** Version bump only for package @cubejs-backend/ksql-driver + ## [1.7.28](https://github.com/cube-js/cube/compare/v1.7.27...v1.7.28) (2026-08-26) ### Features diff --git a/packages/cubejs-ksql-driver/package.json b/packages/cubejs-ksql-driver/package.json index 9808f03761b60..cd6bce6c1b8f9 100644 --- a/packages/cubejs-ksql-driver/package.json +++ b/packages/cubejs-ksql-driver/package.json @@ -2,7 +2,7 @@ "name": "@cubejs-backend/ksql-driver", "description": "Cube.js ksql database driver", "author": "Cube Dev, Inc.", - "version": "1.7.28", + "version": "1.7.29", "repository": { "type": "git", "url": "https://github.com/cube-js/cube.git", @@ -26,9 +26,9 @@ "lint:fix": "eslint --fix src/* --ext .ts" }, "dependencies": { - "@cubejs-backend/base-driver": "1.7.28", - "@cubejs-backend/schema-compiler": "1.7.28", - "@cubejs-backend/shared": "1.7.28", + "@cubejs-backend/base-driver": "1.7.29", + "@cubejs-backend/schema-compiler": "1.7.29", + "@cubejs-backend/shared": "1.7.29", "async-mutex": "0.3.2", "axios": "^1.8.3", "kafkajs": "^2.2.3" @@ -41,7 +41,7 @@ "extends": "../cubejs-linter" }, "devDependencies": { - "@cubejs-backend/linter": "1.7.28", + "@cubejs-backend/linter": "1.7.29", "typescript": "~5.2.2" } } diff --git a/packages/cubejs-linter/CHANGELOG.md b/packages/cubejs-linter/CHANGELOG.md index 49d676961bfd7..c0f0eca4c0094 100644 --- a/packages/cubejs-linter/CHANGELOG.md +++ b/packages/cubejs-linter/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.7.29](https://github.com/cube-js/cube/compare/v1.7.28...v1.7.29) (2026-08-27) + +**Note:** Version bump only for package @cubejs-backend/linter + ## [1.7.28](https://github.com/cube-js/cube/compare/v1.7.27...v1.7.28) (2026-08-26) **Note:** Version bump only for package @cubejs-backend/linter diff --git a/packages/cubejs-linter/package.json b/packages/cubejs-linter/package.json index 6325b24d02c89..d5547783f4c36 100644 --- a/packages/cubejs-linter/package.json +++ b/packages/cubejs-linter/package.json @@ -2,7 +2,7 @@ "name": "@cubejs-backend/linter", "description": "Cube.js ESLint (virtual package) for linting code", "author": "Cube Dev, Inc.", - "version": "1.7.28", + "version": "1.7.29", "repository": { "type": "git", "url": "https://github.com/cube-js/cube.git", diff --git a/packages/cubejs-materialize-driver/CHANGELOG.md b/packages/cubejs-materialize-driver/CHANGELOG.md index 09dfa15906788..4859b15f48624 100644 --- a/packages/cubejs-materialize-driver/CHANGELOG.md +++ b/packages/cubejs-materialize-driver/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.7.29](https://github.com/cube-js/cube/compare/v1.7.28...v1.7.29) (2026-08-27) + +**Note:** Version bump only for package @cubejs-backend/materialize-driver + ## [1.7.28](https://github.com/cube-js/cube/compare/v1.7.27...v1.7.28) (2026-08-26) **Note:** Version bump only for package @cubejs-backend/materialize-driver diff --git a/packages/cubejs-materialize-driver/package.json b/packages/cubejs-materialize-driver/package.json index a35bfd95fd763..46ebc306c9cf5 100644 --- a/packages/cubejs-materialize-driver/package.json +++ b/packages/cubejs-materialize-driver/package.json @@ -2,7 +2,7 @@ "name": "@cubejs-backend/materialize-driver", "description": "Cube.js Materialize database driver", "author": "Cube Dev, Inc.", - "version": "1.7.28", + "version": "1.7.29", "repository": { "type": "git", "url": "https://github.com/cube-js/cube.git", @@ -27,15 +27,15 @@ "lint:fix": "eslint --fix src/* --ext .ts" }, "dependencies": { - "@cubejs-backend/base-driver": "1.7.28", - "@cubejs-backend/postgres-driver": "1.7.28", - "@cubejs-backend/shared": "1.7.28", + "@cubejs-backend/base-driver": "1.7.29", + "@cubejs-backend/postgres-driver": "1.7.29", + "@cubejs-backend/shared": "1.7.29", "semver": "^7.6.3" }, "license": "Apache-2.0", "devDependencies": { - "@cubejs-backend/linter": "1.7.28", - "@cubejs-backend/testing": "1.7.28", + "@cubejs-backend/linter": "1.7.29", + "@cubejs-backend/testing": "1.7.29", "typescript": "~5.2.2" }, "publishConfig": { diff --git a/packages/cubejs-mongobi-driver/CHANGELOG.md b/packages/cubejs-mongobi-driver/CHANGELOG.md index 36ea9dbef5570..65012a88cf6bb 100644 --- a/packages/cubejs-mongobi-driver/CHANGELOG.md +++ b/packages/cubejs-mongobi-driver/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.7.29](https://github.com/cube-js/cube/compare/v1.7.28...v1.7.29) (2026-08-27) + +**Note:** Version bump only for package @cubejs-backend/mongobi-driver + ## [1.7.28](https://github.com/cube-js/cube/compare/v1.7.27...v1.7.28) (2026-08-26) **Note:** Version bump only for package @cubejs-backend/mongobi-driver diff --git a/packages/cubejs-mongobi-driver/package.json b/packages/cubejs-mongobi-driver/package.json index deec376e37ebd..099409fc5e8c4 100644 --- a/packages/cubejs-mongobi-driver/package.json +++ b/packages/cubejs-mongobi-driver/package.json @@ -2,7 +2,7 @@ "name": "@cubejs-backend/mongobi-driver", "description": "Cube.js MongoBI driver", "author": "krunalsabnis@gmail.com", - "version": "1.7.28", + "version": "1.7.29", "repository": { "type": "git", "url": "https://github.com/cube-js/cube.git", @@ -27,8 +27,8 @@ "integration:mongobi": "jest dist/test" }, "dependencies": { - "@cubejs-backend/base-driver": "1.7.28", - "@cubejs-backend/shared": "1.7.28", + "@cubejs-backend/base-driver": "1.7.29", + "@cubejs-backend/shared": "1.7.29", "@types/node": "^22", "moment": "^2.29.1", "mysql2": "^3.11.5" @@ -38,7 +38,7 @@ "access": "public" }, "devDependencies": { - "@cubejs-backend/linter": "1.7.28", + "@cubejs-backend/linter": "1.7.29", "testcontainers": "^10.28.0", "typescript": "~5.2.2" }, diff --git a/packages/cubejs-mssql-driver/CHANGELOG.md b/packages/cubejs-mssql-driver/CHANGELOG.md index d13c7056c13a2..0f9cdfc6ed851 100644 --- a/packages/cubejs-mssql-driver/CHANGELOG.md +++ b/packages/cubejs-mssql-driver/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.7.29](https://github.com/cube-js/cube/compare/v1.7.28...v1.7.29) (2026-08-27) + +**Note:** Version bump only for package @cubejs-backend/mssql-driver + ## [1.7.28](https://github.com/cube-js/cube/compare/v1.7.27...v1.7.28) (2026-08-26) **Note:** Version bump only for package @cubejs-backend/mssql-driver diff --git a/packages/cubejs-mssql-driver/package.json b/packages/cubejs-mssql-driver/package.json index ac18b8d7c6a5e..6d171a5f101b5 100644 --- a/packages/cubejs-mssql-driver/package.json +++ b/packages/cubejs-mssql-driver/package.json @@ -2,7 +2,7 @@ "name": "@cubejs-backend/mssql-driver", "description": "Cube.js MS SQL database driver", "author": "Cube Dev, Inc.", - "version": "1.7.28", + "version": "1.7.29", "repository": { "type": "git", "url": "https://github.com/cube-js/cube.git", @@ -25,8 +25,8 @@ "lint:fix": "eslint --fix src/* --ext .ts,.js" }, "dependencies": { - "@cubejs-backend/base-driver": "1.7.28", - "@cubejs-backend/shared": "1.7.28", + "@cubejs-backend/base-driver": "1.7.29", + "@cubejs-backend/shared": "1.7.29", "mssql": "^11.0.1" }, "devDependencies": { diff --git a/packages/cubejs-mysql-aurora-serverless-driver/CHANGELOG.md b/packages/cubejs-mysql-aurora-serverless-driver/CHANGELOG.md index bac945f8ce0b3..500c5fba057e9 100644 --- a/packages/cubejs-mysql-aurora-serverless-driver/CHANGELOG.md +++ b/packages/cubejs-mysql-aurora-serverless-driver/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.7.29](https://github.com/cube-js/cube/compare/v1.7.28...v1.7.29) (2026-08-27) + +**Note:** Version bump only for package @cubejs-backend/mysql-aurora-serverless-driver + ## [1.7.28](https://github.com/cube-js/cube/compare/v1.7.27...v1.7.28) (2026-08-26) **Note:** Version bump only for package @cubejs-backend/mysql-aurora-serverless-driver diff --git a/packages/cubejs-mysql-aurora-serverless-driver/package.json b/packages/cubejs-mysql-aurora-serverless-driver/package.json index ad32f02783745..39902c25528dd 100644 --- a/packages/cubejs-mysql-aurora-serverless-driver/package.json +++ b/packages/cubejs-mysql-aurora-serverless-driver/package.json @@ -2,7 +2,7 @@ "name": "@cubejs-backend/mysql-aurora-serverless-driver", "description": "Cube.js Aurora Serverless Mysql database driver", "author": "Cube Dev, Inc.", - "version": "1.7.28", + "version": "1.7.29", "repository": { "type": "git", "url": "https://github.com/cube-js/cube.git", @@ -21,14 +21,14 @@ "lint": "eslint driver/*.js test/*.js" }, "dependencies": { - "@cubejs-backend/base-driver": "1.7.28", - "@cubejs-backend/shared": "1.7.28", + "@cubejs-backend/base-driver": "1.7.29", + "@cubejs-backend/shared": "1.7.29", "@types/mysql": "^2.15.15", "aws-sdk": "^2.787.0", "data-api-client": "^1.1.0" }, "devDependencies": { - "@cubejs-backend/linter": "1.7.28", + "@cubejs-backend/linter": "1.7.29", "@types/data-api-client": "^1.2.1", "@types/jest": "^29", "jest": "^29", diff --git a/packages/cubejs-mysql-driver/CHANGELOG.md b/packages/cubejs-mysql-driver/CHANGELOG.md index 07b42b5b95027..6e4d408bdef6b 100644 --- a/packages/cubejs-mysql-driver/CHANGELOG.md +++ b/packages/cubejs-mysql-driver/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.7.29](https://github.com/cube-js/cube/compare/v1.7.28...v1.7.29) (2026-08-27) + +**Note:** Version bump only for package @cubejs-backend/mysql-driver + ## [1.7.28](https://github.com/cube-js/cube/compare/v1.7.27...v1.7.28) (2026-08-26) **Note:** Version bump only for package @cubejs-backend/mysql-driver diff --git a/packages/cubejs-mysql-driver/package.json b/packages/cubejs-mysql-driver/package.json index 6c0712b1710c4..43380cb9c2fe2 100644 --- a/packages/cubejs-mysql-driver/package.json +++ b/packages/cubejs-mysql-driver/package.json @@ -2,7 +2,7 @@ "name": "@cubejs-backend/mysql-driver", "description": "Cube.js Mysql database driver", "author": "Cube Dev, Inc.", - "version": "1.7.28", + "version": "1.7.29", "repository": { "type": "git", "url": "https://github.com/cube-js/cube.git", @@ -27,13 +27,13 @@ "lint:fix": "eslint --fix src/* test/* --ext .ts,.js" }, "dependencies": { - "@cubejs-backend/base-driver": "1.7.28", - "@cubejs-backend/shared": "1.7.28", + "@cubejs-backend/base-driver": "1.7.29", + "@cubejs-backend/shared": "1.7.29", "mysql2": "^3.16.1" }, "devDependencies": { - "@cubejs-backend/linter": "1.7.28", - "@cubejs-backend/testing-shared": "1.7.28", + "@cubejs-backend/linter": "1.7.29", + "@cubejs-backend/testing-shared": "1.7.29", "@types/jest": "^29", "jest": "^29", "stream-to-array": "^2.3.0", diff --git a/packages/cubejs-oracle-driver/CHANGELOG.md b/packages/cubejs-oracle-driver/CHANGELOG.md index 1e600ebc94a1b..e501d633f729e 100644 --- a/packages/cubejs-oracle-driver/CHANGELOG.md +++ b/packages/cubejs-oracle-driver/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.7.29](https://github.com/cube-js/cube/compare/v1.7.28...v1.7.29) (2026-08-27) + +**Note:** Version bump only for package @cubejs-backend/oracle-driver + ## [1.7.28](https://github.com/cube-js/cube/compare/v1.7.27...v1.7.28) (2026-08-26) **Note:** Version bump only for package @cubejs-backend/oracle-driver diff --git a/packages/cubejs-oracle-driver/package.json b/packages/cubejs-oracle-driver/package.json index 680ac47c6de65..810a0419f388c 100644 --- a/packages/cubejs-oracle-driver/package.json +++ b/packages/cubejs-oracle-driver/package.json @@ -2,7 +2,7 @@ "name": "@cubejs-backend/oracle-driver", "description": "Cube.js oracle database driver", "author": "Cube Dev, Inc.", - "version": "1.7.28", + "version": "1.7.29", "repository": { "type": "git", "url": "https://github.com/cube-js/cube.git", @@ -13,8 +13,8 @@ }, "main": "driver/OracleDriver.js", "dependencies": { - "@cubejs-backend/base-driver": "1.7.28", - "@cubejs-backend/shared": "1.7.28", + "@cubejs-backend/base-driver": "1.7.29", + "@cubejs-backend/shared": "1.7.29", "ramda": "^0.27.0" }, "devDependencies": { diff --git a/packages/cubejs-pinot-driver/CHANGELOG.md b/packages/cubejs-pinot-driver/CHANGELOG.md index fdc893e539d8d..69cc30ee8382c 100644 --- a/packages/cubejs-pinot-driver/CHANGELOG.md +++ b/packages/cubejs-pinot-driver/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.7.29](https://github.com/cube-js/cube/compare/v1.7.28...v1.7.29) (2026-08-27) + +**Note:** Version bump only for package @cubejs-backend/pinot-driver + ## [1.7.28](https://github.com/cube-js/cube/compare/v1.7.27...v1.7.28) (2026-08-26) **Note:** Version bump only for package @cubejs-backend/pinot-driver diff --git a/packages/cubejs-pinot-driver/package.json b/packages/cubejs-pinot-driver/package.json index 98e8deaa4b33d..ee8796faf305b 100644 --- a/packages/cubejs-pinot-driver/package.json +++ b/packages/cubejs-pinot-driver/package.json @@ -2,7 +2,7 @@ "name": "@cubejs-backend/pinot-driver", "description": "Cube.js Pinot database driver", "author": "Julian Ronsse, InTheMemory, Cube Dev, Inc.", - "version": "1.7.28", + "version": "1.7.29", "repository": { "type": "git", "url": "https://github.com/cube-js/cube.git", @@ -28,9 +28,9 @@ "lint:fix": "eslint --fix src/* --ext .ts" }, "dependencies": { - "@cubejs-backend/base-driver": "1.7.28", - "@cubejs-backend/schema-compiler": "1.7.28", - "@cubejs-backend/shared": "1.7.28", + "@cubejs-backend/base-driver": "1.7.29", + "@cubejs-backend/schema-compiler": "1.7.29", + "@cubejs-backend/shared": "1.7.29", "node-fetch": "^2.6.1", "ramda": "^0.27.2" }, @@ -39,7 +39,7 @@ "access": "public" }, "devDependencies": { - "@cubejs-backend/linter": "1.7.28", + "@cubejs-backend/linter": "1.7.29", "@types/jest": "^29", "jest": "^29", "should": "^13.2.3", diff --git a/packages/cubejs-playground/CHANGELOG.md b/packages/cubejs-playground/CHANGELOG.md index 7116424ca53f6..af1d2e9337020 100644 --- a/packages/cubejs-playground/CHANGELOG.md +++ b/packages/cubejs-playground/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.7.29](https://github.com/cube-js/cube/compare/v1.7.28...v1.7.29) (2026-08-27) + +**Note:** Version bump only for package @cubejs-client/playground + ## [1.7.28](https://github.com/cube-js/cube/compare/v1.7.27...v1.7.28) (2026-08-26) **Note:** Version bump only for package @cubejs-client/playground diff --git a/packages/cubejs-playground/package.json b/packages/cubejs-playground/package.json index 9c87f3853a47b..0c2cdbd104cc0 100644 --- a/packages/cubejs-playground/package.json +++ b/packages/cubejs-playground/package.json @@ -1,7 +1,7 @@ { "name": "@cubejs-client/playground", "author": "Cube Dev, Inc.", - "version": "1.7.28", + "version": "1.7.29", "engines": {}, "repository": { "type": "git", @@ -68,8 +68,8 @@ "@ant-design/compatible": "^1.0.1", "@ant-design/icons": "^5.3.5", "@cube-dev/ui-kit": "0.52.3", - "@cubejs-client/core": "1.7.28", - "@cubejs-client/react": "1.7.28", + "@cubejs-client/core": "1.7.29", + "@cubejs-client/react": "1.7.29", "@types/flexsearch": "^0.7.3", "@types/node": "^22", "@types/react": "^18.3.4", diff --git a/packages/cubejs-postgres-driver/CHANGELOG.md b/packages/cubejs-postgres-driver/CHANGELOG.md index 196a07cb318ce..c26014a063d87 100644 --- a/packages/cubejs-postgres-driver/CHANGELOG.md +++ b/packages/cubejs-postgres-driver/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.7.29](https://github.com/cube-js/cube/compare/v1.7.28...v1.7.29) (2026-08-27) + +**Note:** Version bump only for package @cubejs-backend/postgres-driver + ## [1.7.28](https://github.com/cube-js/cube/compare/v1.7.27...v1.7.28) (2026-08-26) **Note:** Version bump only for package @cubejs-backend/postgres-driver diff --git a/packages/cubejs-postgres-driver/package.json b/packages/cubejs-postgres-driver/package.json index 57d341c6b6f21..f9e595998d8c4 100644 --- a/packages/cubejs-postgres-driver/package.json +++ b/packages/cubejs-postgres-driver/package.json @@ -2,7 +2,7 @@ "name": "@cubejs-backend/postgres-driver", "description": "Cube.js Postgres database driver", "author": "Cube Dev, Inc.", - "version": "1.7.28", + "version": "1.7.29", "repository": { "type": "git", "url": "https://github.com/cube-js/cube.git", @@ -27,8 +27,8 @@ "lint:fix": "eslint --fix src/* --ext .ts" }, "dependencies": { - "@cubejs-backend/base-driver": "1.7.28", - "@cubejs-backend/shared": "1.7.28", + "@cubejs-backend/base-driver": "1.7.29", + "@cubejs-backend/shared": "1.7.29", "@types/pg": "^8.16.0", "@types/pg-query-stream": "^1.0.3", "pg": "^8.18.0", @@ -36,8 +36,8 @@ }, "license": "Apache-2.0", "devDependencies": { - "@cubejs-backend/linter": "1.7.28", - "@cubejs-backend/testing-shared": "1.7.28", + "@cubejs-backend/linter": "1.7.29", + "@cubejs-backend/testing-shared": "1.7.29", "testcontainers": "^10.28.0", "typescript": "~5.2.2" }, diff --git a/packages/cubejs-prestodb-driver/CHANGELOG.md b/packages/cubejs-prestodb-driver/CHANGELOG.md index 3e288fad3ad90..e97f38e12cf7f 100644 --- a/packages/cubejs-prestodb-driver/CHANGELOG.md +++ b/packages/cubejs-prestodb-driver/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.7.29](https://github.com/cube-js/cube/compare/v1.7.28...v1.7.29) (2026-08-27) + +**Note:** Version bump only for package @cubejs-backend/prestodb-driver + ## [1.7.28](https://github.com/cube-js/cube/compare/v1.7.27...v1.7.28) (2026-08-26) **Note:** Version bump only for package @cubejs-backend/prestodb-driver diff --git a/packages/cubejs-prestodb-driver/package.json b/packages/cubejs-prestodb-driver/package.json index e59b168c37d20..838bac240b0c6 100644 --- a/packages/cubejs-prestodb-driver/package.json +++ b/packages/cubejs-prestodb-driver/package.json @@ -2,7 +2,7 @@ "name": "@cubejs-backend/prestodb-driver", "description": "Cube.js Presto database driver", "author": "Cube Dev, Inc.", - "version": "1.7.28", + "version": "1.7.29", "repository": { "type": "git", "url": "https://github.com/cube-js/cube.git", @@ -28,8 +28,8 @@ "lint:fix": "eslint --fix src/* --ext .ts" }, "dependencies": { - "@cubejs-backend/base-driver": "1.7.28", - "@cubejs-backend/shared": "1.7.28", + "@cubejs-backend/base-driver": "1.7.29", + "@cubejs-backend/shared": "1.7.29", "presto-client": "1.2.0", "ramda": "^0.27.0" }, @@ -38,7 +38,7 @@ "access": "public" }, "devDependencies": { - "@cubejs-backend/linter": "1.7.28", + "@cubejs-backend/linter": "1.7.29", "@types/jest": "^29", "jest": "^29", "should": "^13.2.3", diff --git a/packages/cubejs-query-orchestrator/CHANGELOG.md b/packages/cubejs-query-orchestrator/CHANGELOG.md index d1a8c51c62e2a..08906729b2ace 100644 --- a/packages/cubejs-query-orchestrator/CHANGELOG.md +++ b/packages/cubejs-query-orchestrator/CHANGELOG.md @@ -3,6 +3,13 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.7.29](https://github.com/cube-js/cube/compare/v1.7.28...v1.7.29) (2026-08-27) + +### Bug Fixes + +- **api-gateway:** Make pre-agg build job status per-entry and data-source aware ([#11666](https://github.com/cube-js/cube/issues/11666)) ([694955f](https://github.com/cube-js/cube/commit/694955f864375515d84d463e9e7ee89a156a4a2b)) +- **query-orchestrator:** initialize missing data source queue in isPartitionExist, thanks [@dochernyshov](https://github.com/dochernyshov) ([#11617](https://github.com/cube-js/cube/issues/11617)) ([ab48039](https://github.com/cube-js/cube/commit/ab48039f1a3d46d890367b0fc898d9ebf1322786)) + ## [1.7.28](https://github.com/cube-js/cube/compare/v1.7.27...v1.7.28) (2026-08-26) **Note:** Version bump only for package @cubejs-backend/query-orchestrator diff --git a/packages/cubejs-query-orchestrator/package.json b/packages/cubejs-query-orchestrator/package.json index 9d1c663d82f18..de328f49fa74b 100644 --- a/packages/cubejs-query-orchestrator/package.json +++ b/packages/cubejs-query-orchestrator/package.json @@ -2,7 +2,7 @@ "name": "@cubejs-backend/query-orchestrator", "description": "Cube.js Query Orchestrator and Cache", "author": "Cube Dev, Inc.", - "version": "1.7.28", + "version": "1.7.29", "repository": { "type": "git", "url": "https://github.com/cube-js/cube.git", @@ -30,15 +30,15 @@ "dist/src/*" ], "dependencies": { - "@cubejs-backend/base-driver": "1.7.28", - "@cubejs-backend/cubestore-driver": "1.7.28", - "@cubejs-backend/shared": "1.7.28", + "@cubejs-backend/base-driver": "1.7.29", + "@cubejs-backend/cubestore-driver": "1.7.29", + "@cubejs-backend/shared": "1.7.29", "csv-write-stream": "^2.0.0", "lru-cache": "^11.1.0", "ramda": "^0.27.2" }, "devDependencies": { - "@cubejs-backend/linter": "1.7.28", + "@cubejs-backend/linter": "1.7.29", "@types/jest": "^29", "@types/node": "^22", "@types/ramda": "^0.27.32", diff --git a/packages/cubejs-questdb-driver/CHANGELOG.md b/packages/cubejs-questdb-driver/CHANGELOG.md index e80040a05a7dc..6f41928fda545 100644 --- a/packages/cubejs-questdb-driver/CHANGELOG.md +++ b/packages/cubejs-questdb-driver/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.7.29](https://github.com/cube-js/cube/compare/v1.7.28...v1.7.29) (2026-08-27) + +**Note:** Version bump only for package @cubejs-backend/questdb-driver + ## [1.7.28](https://github.com/cube-js/cube/compare/v1.7.27...v1.7.28) (2026-08-26) **Note:** Version bump only for package @cubejs-backend/questdb-driver diff --git a/packages/cubejs-questdb-driver/package.json b/packages/cubejs-questdb-driver/package.json index 8fd04fda1a6c9..dcbf3eb0e7137 100644 --- a/packages/cubejs-questdb-driver/package.json +++ b/packages/cubejs-questdb-driver/package.json @@ -2,7 +2,7 @@ "name": "@cubejs-backend/questdb-driver", "description": "Cube.js QuestDB database driver", "author": "Cube Dev, Inc.", - "version": "1.7.28", + "version": "1.7.29", "repository": { "type": "git", "url": "https://github.com/cube-js/cube.git", @@ -27,9 +27,9 @@ "lint:fix": "eslint --fix src/* --ext .ts" }, "dependencies": { - "@cubejs-backend/base-driver": "1.7.28", - "@cubejs-backend/schema-compiler": "1.7.28", - "@cubejs-backend/shared": "1.7.28", + "@cubejs-backend/base-driver": "1.7.29", + "@cubejs-backend/schema-compiler": "1.7.29", + "@cubejs-backend/shared": "1.7.29", "@types/pg": "^8.6.0", "moment": "^2.24.0", "pg": "^8.7.0", @@ -37,8 +37,8 @@ }, "license": "Apache-2.0", "devDependencies": { - "@cubejs-backend/linter": "1.7.28", - "@cubejs-backend/testing-shared": "1.7.28", + "@cubejs-backend/linter": "1.7.29", + "@cubejs-backend/testing-shared": "1.7.29", "testcontainers": "^10.28.0", "typescript": "~5.2.2" }, diff --git a/packages/cubejs-redshift-driver/CHANGELOG.md b/packages/cubejs-redshift-driver/CHANGELOG.md index bdd37a47b0bb2..bc8b92cebc0ef 100644 --- a/packages/cubejs-redshift-driver/CHANGELOG.md +++ b/packages/cubejs-redshift-driver/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.7.29](https://github.com/cube-js/cube/compare/v1.7.28...v1.7.29) (2026-08-27) + +**Note:** Version bump only for package @cubejs-backend/redshift-driver + ## [1.7.28](https://github.com/cube-js/cube/compare/v1.7.27...v1.7.28) (2026-08-26) **Note:** Version bump only for package @cubejs-backend/redshift-driver diff --git a/packages/cubejs-redshift-driver/package.json b/packages/cubejs-redshift-driver/package.json index 25a6ba696b00d..b2f4aad2915f4 100644 --- a/packages/cubejs-redshift-driver/package.json +++ b/packages/cubejs-redshift-driver/package.json @@ -2,7 +2,7 @@ "name": "@cubejs-backend/redshift-driver", "description": "Cube.js Redshift database driver", "author": "Cube Dev, Inc.", - "version": "1.7.28", + "version": "1.7.29", "repository": { "type": "git", "url": "https://github.com/cube-js/cube.git", @@ -27,13 +27,13 @@ "dependencies": { "@aws-sdk/client-redshift": "^3.22.0", "@aws-sdk/credential-providers": "^3.22.0", - "@cubejs-backend/base-driver": "1.7.28", - "@cubejs-backend/postgres-driver": "1.7.28", - "@cubejs-backend/shared": "1.7.28" + "@cubejs-backend/base-driver": "1.7.29", + "@cubejs-backend/postgres-driver": "1.7.29", + "@cubejs-backend/shared": "1.7.29" }, "license": "Apache-2.0", "devDependencies": { - "@cubejs-backend/linter": "1.7.28", + "@cubejs-backend/linter": "1.7.29", "typescript": "~5.2.2" }, "publishConfig": { diff --git a/packages/cubejs-schema-compiler/CHANGELOG.md b/packages/cubejs-schema-compiler/CHANGELOG.md index 4f6f2f2362022..dc27743aabde5 100644 --- a/packages/cubejs-schema-compiler/CHANGELOG.md +++ b/packages/cubejs-schema-compiler/CHANGELOG.md @@ -3,6 +3,12 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.7.29](https://github.com/cube-js/cube/compare/v1.7.28...v1.7.29) (2026-08-27) + +### Bug Fixes + +- **api-gateway:** Make pre-agg build job status per-entry and data-source aware ([#11666](https://github.com/cube-js/cube/issues/11666)) ([694955f](https://github.com/cube-js/cube/commit/694955f864375515d84d463e9e7ee89a156a4a2b)) + ## [1.7.28](https://github.com/cube-js/cube/compare/v1.7.27...v1.7.28) (2026-08-26) ### Features diff --git a/packages/cubejs-schema-compiler/package.json b/packages/cubejs-schema-compiler/package.json index 7d3e68b78148e..c1b0f19e20f30 100644 --- a/packages/cubejs-schema-compiler/package.json +++ b/packages/cubejs-schema-compiler/package.json @@ -2,7 +2,7 @@ "name": "@cubejs-backend/schema-compiler", "description": "Cube schema compiler", "author": "Cube Dev, Inc.", - "version": "1.7.28", + "version": "1.7.29", "repository": { "type": "git", "url": "https://github.com/cube-js/cube.git", @@ -40,8 +40,8 @@ "@babel/standalone": "^7.24", "@babel/traverse": "^7.24", "@babel/types": "^7.24", - "@cubejs-backend/native": "1.7.28", - "@cubejs-backend/shared": "1.7.28", + "@cubejs-backend/native": "1.7.29", + "@cubejs-backend/shared": "1.7.29", "antlr4": "^4.13.2", "camelcase": "^6.2.0", "cron-parser": "^4.9.0", @@ -60,9 +60,9 @@ }, "devDependencies": { "@clickhouse/client": "^1.12.0", - "@cubejs-backend/linter": "1.7.28", - "@cubejs-backend/mssql-driver": "1.7.28", - "@cubejs-backend/query-orchestrator": "1.7.28", + "@cubejs-backend/linter": "1.7.29", + "@cubejs-backend/mssql-driver": "1.7.29", + "@cubejs-backend/query-orchestrator": "1.7.29", "@types/babel__code-frame": "^7.0.6", "@types/babel__generator": "^7.6.8", "@types/babel__traverse": "^7.20.5", diff --git a/packages/cubejs-server-core/CHANGELOG.md b/packages/cubejs-server-core/CHANGELOG.md index ffed88e0060f2..6dc896fc73089 100644 --- a/packages/cubejs-server-core/CHANGELOG.md +++ b/packages/cubejs-server-core/CHANGELOG.md @@ -3,6 +3,13 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.7.29](https://github.com/cube-js/cube/compare/v1.7.28...v1.7.29) (2026-08-27) + +### Bug Fixes + +- **api-gateway:** Make pre-agg build job status per-entry and data-source aware ([#11666](https://github.com/cube-js/cube/issues/11666)) ([694955f](https://github.com/cube-js/cube/commit/694955f864375515d84d463e9e7ee89a156a4a2b)) +- **server-core:** release orchestrators evicted from the LRU ([#11661](https://github.com/cube-js/cube/issues/11661)) ([b82c58a](https://github.com/cube-js/cube/commit/b82c58aa245919a36e3d20f79105b639d0c16863)) + ## [1.7.28](https://github.com/cube-js/cube/compare/v1.7.27...v1.7.28) (2026-08-26) **Note:** Version bump only for package @cubejs-backend/server-core diff --git a/packages/cubejs-server-core/package.json b/packages/cubejs-server-core/package.json index e0e6fbca5a388..b4cd09f13adc9 100644 --- a/packages/cubejs-server-core/package.json +++ b/packages/cubejs-server-core/package.json @@ -2,7 +2,7 @@ "name": "@cubejs-backend/server-core", "description": "Cube.js base component to wire all backend components together", "author": "Cube Dev, Inc.", - "version": "1.7.28", + "version": "1.7.29", "repository": { "type": "git", "url": "https://github.com/cube-js/cube.git", @@ -29,16 +29,16 @@ "unit": "jest --runInBand --forceExit --coverage dist/test" }, "dependencies": { - "@cubejs-backend/api-gateway": "1.7.28", - "@cubejs-backend/base-driver": "1.7.28", - "@cubejs-backend/cloud": "1.7.28", - "@cubejs-backend/cubestore-driver": "1.7.28", + "@cubejs-backend/api-gateway": "1.7.29", + "@cubejs-backend/base-driver": "1.7.29", + "@cubejs-backend/cloud": "1.7.29", + "@cubejs-backend/cubestore-driver": "1.7.29", "@cubejs-backend/dotenv": "^9.0.2", - "@cubejs-backend/native": "1.7.28", - "@cubejs-backend/query-orchestrator": "1.7.28", - "@cubejs-backend/schema-compiler": "1.7.28", - "@cubejs-backend/shared": "1.7.28", - "@cubejs-backend/templates": "1.7.28", + "@cubejs-backend/native": "1.7.29", + "@cubejs-backend/query-orchestrator": "1.7.29", + "@cubejs-backend/schema-compiler": "1.7.29", + "@cubejs-backend/shared": "1.7.29", + "@cubejs-backend/templates": "1.7.29", "codesandbox-import-utils": "^2.1.12", "cross-spawn": "^7.0.1", "fs-extra": "^8.1.0", @@ -62,8 +62,8 @@ "ws": "^7.5.3" }, "devDependencies": { - "@cubejs-backend/linter": "1.7.28", - "@cubejs-client/playground": "1.7.28", + "@cubejs-backend/linter": "1.7.29", + "@cubejs-client/playground": "1.7.29", "@types/cross-spawn": "^6.0.2", "@types/express": "^4.17.21", "@types/fs-extra": "^9.0.8", diff --git a/packages/cubejs-server/CHANGELOG.md b/packages/cubejs-server/CHANGELOG.md index 5ed77caf533f5..6e525c596e8ee 100644 --- a/packages/cubejs-server/CHANGELOG.md +++ b/packages/cubejs-server/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.7.29](https://github.com/cube-js/cube/compare/v1.7.28...v1.7.29) (2026-08-27) + +**Note:** Version bump only for package @cubejs-backend/server + ## [1.7.28](https://github.com/cube-js/cube/compare/v1.7.27...v1.7.28) (2026-08-26) **Note:** Version bump only for package @cubejs-backend/server diff --git a/packages/cubejs-server/package.json b/packages/cubejs-server/package.json index 118cf4da9aaf8..5abd908f2655a 100644 --- a/packages/cubejs-server/package.json +++ b/packages/cubejs-server/package.json @@ -2,7 +2,7 @@ "name": "@cubejs-backend/server", "description": "Cube.js all-in-one server", "author": "Cube Dev, Inc.", - "version": "1.7.28", + "version": "1.7.29", "types": "index.d.ts", "repository": { "type": "git", @@ -40,11 +40,11 @@ "jest:shapshot": "jest --updateSnapshot test" }, "dependencies": { - "@cubejs-backend/cubestore-driver": "1.7.28", + "@cubejs-backend/cubestore-driver": "1.7.29", "@cubejs-backend/dotenv": "^9.0.2", - "@cubejs-backend/native": "1.7.28", - "@cubejs-backend/server-core": "1.7.28", - "@cubejs-backend/shared": "1.7.28", + "@cubejs-backend/native": "1.7.29", + "@cubejs-backend/server-core": "1.7.29", + "@cubejs-backend/shared": "1.7.29", "@oclif/color": "^1.0.0", "@oclif/command": "^1.8.13", "@oclif/config": "^1.18.2", @@ -61,8 +61,8 @@ "ws": "^7.1.2" }, "devDependencies": { - "@cubejs-backend/linter": "1.7.28", - "@cubejs-backend/query-orchestrator": "1.7.28", + "@cubejs-backend/linter": "1.7.29", + "@cubejs-backend/query-orchestrator": "1.7.29", "@oclif/dev-cli": "^1.23.1", "@types/body-parser": "^1.19.0", "@types/cors": "^2.8.8", diff --git a/packages/cubejs-snowflake-driver/CHANGELOG.md b/packages/cubejs-snowflake-driver/CHANGELOG.md index 52a016fa182a2..816c0454152bb 100644 --- a/packages/cubejs-snowflake-driver/CHANGELOG.md +++ b/packages/cubejs-snowflake-driver/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.7.29](https://github.com/cube-js/cube/compare/v1.7.28...v1.7.29) (2026-08-27) + +**Note:** Version bump only for package @cubejs-backend/snowflake-driver + ## [1.7.28](https://github.com/cube-js/cube/compare/v1.7.27...v1.7.28) (2026-08-26) **Note:** Version bump only for package @cubejs-backend/snowflake-driver diff --git a/packages/cubejs-snowflake-driver/package.json b/packages/cubejs-snowflake-driver/package.json index bb1a62e7d40d7..608f99513d5b6 100644 --- a/packages/cubejs-snowflake-driver/package.json +++ b/packages/cubejs-snowflake-driver/package.json @@ -2,7 +2,7 @@ "name": "@cubejs-backend/snowflake-driver", "description": "Cube.js Snowflake database driver", "author": "Cube Dev, Inc.", - "version": "1.7.28", + "version": "1.7.29", "repository": { "type": "git", "url": "https://github.com/cube-js/cube.git", @@ -29,8 +29,8 @@ }, "dependencies": { "@aws-sdk/client-s3": "^3.726.0", - "@cubejs-backend/base-driver": "1.7.28", - "@cubejs-backend/shared": "1.7.28", + "@cubejs-backend/base-driver": "1.7.29", + "@cubejs-backend/shared": "1.7.29", "snowflake-sdk": "^2.4.0" }, "license": "Apache-2.0", @@ -41,7 +41,7 @@ "extends": "../cubejs-linter" }, "devDependencies": { - "@cubejs-backend/linter": "1.7.28", + "@cubejs-backend/linter": "1.7.29", "typescript": "~5.2.2", "vitest": "^4" } diff --git a/packages/cubejs-sqlite-driver/CHANGELOG.md b/packages/cubejs-sqlite-driver/CHANGELOG.md index 9d47520b90747..6457e155c5590 100644 --- a/packages/cubejs-sqlite-driver/CHANGELOG.md +++ b/packages/cubejs-sqlite-driver/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.7.29](https://github.com/cube-js/cube/compare/v1.7.28...v1.7.29) (2026-08-27) + +**Note:** Version bump only for package @cubejs-backend/sqlite-driver + ## [1.7.28](https://github.com/cube-js/cube/compare/v1.7.27...v1.7.28) (2026-08-26) **Note:** Version bump only for package @cubejs-backend/sqlite-driver diff --git a/packages/cubejs-sqlite-driver/package.json b/packages/cubejs-sqlite-driver/package.json index 56d90e0fb9588..8c3111f63a37d 100644 --- a/packages/cubejs-sqlite-driver/package.json +++ b/packages/cubejs-sqlite-driver/package.json @@ -2,7 +2,7 @@ "name": "@cubejs-backend/sqlite-driver", "description": "Cube.js Sqlite database driver", "author": "Cube Dev, Inc.", - "version": "1.7.28", + "version": "1.7.29", "repository": { "type": "git", "url": "https://github.com/cube-js/cube.git", @@ -18,13 +18,13 @@ "unit": "jest" }, "dependencies": { - "@cubejs-backend/base-driver": "1.7.28", - "@cubejs-backend/shared": "1.7.28", + "@cubejs-backend/base-driver": "1.7.29", + "@cubejs-backend/shared": "1.7.29", "sqlite3": "^5.1.7" }, "license": "Apache-2.0", "devDependencies": { - "@cubejs-backend/linter": "1.7.28", + "@cubejs-backend/linter": "1.7.29", "jest": "^29" }, "publishConfig": { diff --git a/packages/cubejs-templates/CHANGELOG.md b/packages/cubejs-templates/CHANGELOG.md index b6be3ec1726bd..9f737c1a82367 100644 --- a/packages/cubejs-templates/CHANGELOG.md +++ b/packages/cubejs-templates/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.7.29](https://github.com/cube-js/cube/compare/v1.7.28...v1.7.29) (2026-08-27) + +**Note:** Version bump only for package @cubejs-backend/templates + ## [1.7.28](https://github.com/cube-js/cube/compare/v1.7.27...v1.7.28) (2026-08-26) **Note:** Version bump only for package @cubejs-backend/templates diff --git a/packages/cubejs-templates/package.json b/packages/cubejs-templates/package.json index 489d65f155895..4d5ef4d341f09 100644 --- a/packages/cubejs-templates/package.json +++ b/packages/cubejs-templates/package.json @@ -1,6 +1,6 @@ { "name": "@cubejs-backend/templates", - "version": "1.7.28", + "version": "1.7.29", "description": "Cube.js Templates helpers", "author": "Cube Dev, Inc.", "repository": { @@ -31,7 +31,7 @@ "extends": "../cubejs-linter" }, "dependencies": { - "@cubejs-backend/shared": "1.7.28", + "@cubejs-backend/shared": "1.7.29", "cross-spawn": "^7.0.3", "fs-extra": "^9.1.0", "node-fetch": "^2.6.1", @@ -40,7 +40,7 @@ "tar": "^7.5.22" }, "devDependencies": { - "@cubejs-backend/linter": "1.7.28", + "@cubejs-backend/linter": "1.7.29", "typescript": "~5.2.2" } } diff --git a/packages/cubejs-testing-drivers/CHANGELOG.md b/packages/cubejs-testing-drivers/CHANGELOG.md index 0f4588f3e03a5..67afdd0e66e9b 100644 --- a/packages/cubejs-testing-drivers/CHANGELOG.md +++ b/packages/cubejs-testing-drivers/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.7.29](https://github.com/cube-js/cube/compare/v1.7.28...v1.7.29) (2026-08-27) + +**Note:** Version bump only for package @cubejs-backend/testing-drivers + ## [1.7.28](https://github.com/cube-js/cube/compare/v1.7.27...v1.7.28) (2026-08-26) **Note:** Version bump only for package @cubejs-backend/testing-drivers diff --git a/packages/cubejs-testing-drivers/package.json b/packages/cubejs-testing-drivers/package.json index 7fc12e08e3f20..6cf199f254894 100644 --- a/packages/cubejs-testing-drivers/package.json +++ b/packages/cubejs-testing-drivers/package.json @@ -1,6 +1,6 @@ { "name": "@cubejs-backend/testing-drivers", - "version": "1.7.28", + "version": "1.7.29", "description": "Cube.js drivers test suite", "author": "Cube Dev, Inc.", "repository": { @@ -87,29 +87,29 @@ "dist/src" ], "dependencies": { - "@cubejs-backend/athena-driver": "1.7.28", - "@cubejs-backend/base-driver": "1.7.28", - "@cubejs-backend/bigquery-driver": "1.7.28", - "@cubejs-backend/clickhouse-driver": "1.7.28", - "@cubejs-backend/crate-driver": "1.7.28", - "@cubejs-backend/cubestore-driver": "1.7.28", - "@cubejs-backend/databricks-jdbc-driver": "1.7.28", + "@cubejs-backend/athena-driver": "1.7.29", + "@cubejs-backend/base-driver": "1.7.29", + "@cubejs-backend/bigquery-driver": "1.7.29", + "@cubejs-backend/clickhouse-driver": "1.7.29", + "@cubejs-backend/crate-driver": "1.7.29", + "@cubejs-backend/cubestore-driver": "1.7.29", + "@cubejs-backend/databricks-jdbc-driver": "1.7.29", "@cubejs-backend/dotenv": "^9.0.2", - "@cubejs-backend/linter": "1.7.28", - "@cubejs-backend/mssql-driver": "1.7.28", - "@cubejs-backend/mysql-driver": "1.7.28", - "@cubejs-backend/oracle-driver": "1.7.28", - "@cubejs-backend/pinot-driver": "1.7.28", - "@cubejs-backend/postgres-driver": "1.7.28", - "@cubejs-backend/query-orchestrator": "1.7.28", - "@cubejs-backend/questdb-driver": "1.7.28", - "@cubejs-backend/server-core": "1.7.28", - "@cubejs-backend/shared": "1.7.28", - "@cubejs-backend/snowflake-driver": "1.7.28", - "@cubejs-backend/testing-shared": "1.7.28", - "@cubejs-backend/trino-driver": "1.7.28", - "@cubejs-client/core": "1.7.28", - "@cubejs-client/ws-transport": "1.7.28", + "@cubejs-backend/linter": "1.7.29", + "@cubejs-backend/mssql-driver": "1.7.29", + "@cubejs-backend/mysql-driver": "1.7.29", + "@cubejs-backend/oracle-driver": "1.7.29", + "@cubejs-backend/pinot-driver": "1.7.29", + "@cubejs-backend/postgres-driver": "1.7.29", + "@cubejs-backend/query-orchestrator": "1.7.29", + "@cubejs-backend/questdb-driver": "1.7.29", + "@cubejs-backend/server-core": "1.7.29", + "@cubejs-backend/shared": "1.7.29", + "@cubejs-backend/snowflake-driver": "1.7.29", + "@cubejs-backend/testing-shared": "1.7.29", + "@cubejs-backend/trino-driver": "1.7.29", + "@cubejs-client/core": "1.7.29", + "@cubejs-client/ws-transport": "1.7.29", "@jest/globals": "^29", "@types/jest": "^29", "@types/node": "^22", diff --git a/packages/cubejs-testing-shared/CHANGELOG.md b/packages/cubejs-testing-shared/CHANGELOG.md index 1e2c0946dba4c..ae15a6c6955e6 100644 --- a/packages/cubejs-testing-shared/CHANGELOG.md +++ b/packages/cubejs-testing-shared/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.7.29](https://github.com/cube-js/cube/compare/v1.7.28...v1.7.29) (2026-08-27) + +**Note:** Version bump only for package @cubejs-backend/testing-shared + ## [1.7.28](https://github.com/cube-js/cube/compare/v1.7.27...v1.7.28) (2026-08-26) **Note:** Version bump only for package @cubejs-backend/testing-shared diff --git a/packages/cubejs-testing-shared/package.json b/packages/cubejs-testing-shared/package.json index 0332a59ef0e21..4ada7f9c5bbe7 100644 --- a/packages/cubejs-testing-shared/package.json +++ b/packages/cubejs-testing-shared/package.json @@ -1,6 +1,6 @@ { "name": "@cubejs-backend/testing-shared", - "version": "1.7.28", + "version": "1.7.29", "description": "Cube.js Testing Helpers", "author": "Cube Dev, Inc.", "repository": { @@ -26,16 +26,16 @@ ], "dependencies": { "@cubejs-backend/dotenv": "^9.0.2", - "@cubejs-backend/query-orchestrator": "1.7.28", - "@cubejs-backend/schema-compiler": "1.7.28", - "@cubejs-backend/shared": "1.7.28", + "@cubejs-backend/query-orchestrator": "1.7.29", + "@cubejs-backend/schema-compiler": "1.7.29", + "@cubejs-backend/shared": "1.7.29", "@testcontainers/kafka": "~10.28.0", "dedent": "^0.7.0", "node-fetch": "^2.6.7", "testcontainers": "^10.28.0" }, "devDependencies": { - "@cubejs-backend/linter": "1.7.28", + "@cubejs-backend/linter": "1.7.29", "@jest/globals": "^29", "@types/dedent": "^0.7.0", "@types/jest": "^29", diff --git a/packages/cubejs-testing/CHANGELOG.md b/packages/cubejs-testing/CHANGELOG.md index 03ae39ee3f006..a5ac78f7845fc 100644 --- a/packages/cubejs-testing/CHANGELOG.md +++ b/packages/cubejs-testing/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.7.29](https://github.com/cube-js/cube/compare/v1.7.28...v1.7.29) (2026-08-27) + +**Note:** Version bump only for package @cubejs-backend/testing + ## [1.7.28](https://github.com/cube-js/cube/compare/v1.7.27...v1.7.28) (2026-08-26) **Note:** Version bump only for package @cubejs-backend/testing diff --git a/packages/cubejs-testing/package.json b/packages/cubejs-testing/package.json index eab5892e7a018..78c79290a296e 100644 --- a/packages/cubejs-testing/package.json +++ b/packages/cubejs-testing/package.json @@ -1,6 +1,6 @@ { "name": "@cubejs-backend/testing", - "version": "1.7.28", + "version": "1.7.29", "description": "Cube.js e2e tests", "author": "Cube Dev, Inc.", "repository": { @@ -92,15 +92,15 @@ "birdbox-fixtures" ], "dependencies": { - "@cubejs-backend/cubestore-driver": "1.7.28", + "@cubejs-backend/cubestore-driver": "1.7.29", "@cubejs-backend/dotenv": "^9.0.2", - "@cubejs-backend/ksql-driver": "1.7.28", - "@cubejs-backend/postgres-driver": "1.7.28", - "@cubejs-backend/query-orchestrator": "1.7.28", - "@cubejs-backend/schema-compiler": "1.7.28", - "@cubejs-backend/shared": "1.7.28", - "@cubejs-backend/testing-shared": "1.7.28", - "@cubejs-client/ws-transport": "1.7.28", + "@cubejs-backend/ksql-driver": "1.7.29", + "@cubejs-backend/postgres-driver": "1.7.29", + "@cubejs-backend/query-orchestrator": "1.7.29", + "@cubejs-backend/schema-compiler": "1.7.29", + "@cubejs-backend/shared": "1.7.29", + "@cubejs-backend/testing-shared": "1.7.29", + "@cubejs-client/ws-transport": "1.7.29", "dedent": "^0.7.0", "fs-extra": "^8.1.0", "http-proxy": "^1.18.1", @@ -111,8 +111,8 @@ }, "devDependencies": { "@4tw/cypress-drag-drop": "^1.6.0", - "@cubejs-backend/linter": "1.7.28", - "@cubejs-client/core": "1.7.28", + "@cubejs-backend/linter": "1.7.29", + "@cubejs-client/core": "1.7.29", "@jest/globals": "^29", "@types/dedent": "^0.7.0", "@types/http-proxy": "^1.17.5", diff --git a/packages/cubejs-trino-driver/CHANGELOG.md b/packages/cubejs-trino-driver/CHANGELOG.md index 75a49efbf1744..1bc0bc4c4871e 100644 --- a/packages/cubejs-trino-driver/CHANGELOG.md +++ b/packages/cubejs-trino-driver/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.7.29](https://github.com/cube-js/cube/compare/v1.7.28...v1.7.29) (2026-08-27) + +**Note:** Version bump only for package @cubejs-backend/trino-driver + ## [1.7.28](https://github.com/cube-js/cube/compare/v1.7.27...v1.7.28) (2026-08-26) **Note:** Version bump only for package @cubejs-backend/trino-driver diff --git a/packages/cubejs-trino-driver/package.json b/packages/cubejs-trino-driver/package.json index 52574ce732d24..ac22e78d41f7c 100644 --- a/packages/cubejs-trino-driver/package.json +++ b/packages/cubejs-trino-driver/package.json @@ -2,7 +2,7 @@ "name": "@cubejs-backend/trino-driver", "description": "Cube.js Trino database driver", "author": "Cube Dev, Inc.", - "version": "1.7.28", + "version": "1.7.29", "repository": { "type": "git", "url": "https://github.com/cube-js/cube.git", @@ -28,10 +28,10 @@ "lint:fix": "eslint --fix src/* --ext .ts" }, "dependencies": { - "@cubejs-backend/base-driver": "1.7.28", - "@cubejs-backend/prestodb-driver": "1.7.28", - "@cubejs-backend/schema-compiler": "1.7.28", - "@cubejs-backend/shared": "1.7.28", + "@cubejs-backend/base-driver": "1.7.29", + "@cubejs-backend/prestodb-driver": "1.7.29", + "@cubejs-backend/schema-compiler": "1.7.29", + "@cubejs-backend/shared": "1.7.29", "node-fetch": "^2.6.1", "presto-client": "^1.2.0" }, @@ -40,7 +40,7 @@ "access": "public" }, "devDependencies": { - "@cubejs-backend/linter": "1.7.28", + "@cubejs-backend/linter": "1.7.29", "@types/jest": "^29", "jest": "^29", "testcontainers": "^10.28.0", diff --git a/packages/cubejs-vertica-driver/CHANGELOG.md b/packages/cubejs-vertica-driver/CHANGELOG.md index 7c1942b544787..24339799235d2 100644 --- a/packages/cubejs-vertica-driver/CHANGELOG.md +++ b/packages/cubejs-vertica-driver/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.7.29](https://github.com/cube-js/cube/compare/v1.7.28...v1.7.29) (2026-08-27) + +**Note:** Version bump only for package @cubejs-backend/vertica-driver + ## [1.7.28](https://github.com/cube-js/cube/compare/v1.7.27...v1.7.28) (2026-08-26) **Note:** Version bump only for package @cubejs-backend/vertica-driver diff --git a/packages/cubejs-vertica-driver/package.json b/packages/cubejs-vertica-driver/package.json index ae1dabf1ec283..c5283b97ebae8 100644 --- a/packages/cubejs-vertica-driver/package.json +++ b/packages/cubejs-vertica-driver/package.json @@ -2,7 +2,7 @@ "name": "@cubejs-backend/vertica-driver", "description": "Cube.js Vertica database driver", "author": "Eduard Karacharov, Tim Brown, Cube Dev, Inc.", - "version": "1.7.28", + "version": "1.7.29", "repository": { "type": "git", "url": "https://github.com/cube-js/cube.git", @@ -19,16 +19,16 @@ "lint:fix": "eslint --fix **/*.js" }, "dependencies": { - "@cubejs-backend/base-driver": "1.7.28", - "@cubejs-backend/query-orchestrator": "1.7.28", - "@cubejs-backend/schema-compiler": "1.7.28", - "@cubejs-backend/shared": "1.7.28", + "@cubejs-backend/base-driver": "1.7.29", + "@cubejs-backend/query-orchestrator": "1.7.29", + "@cubejs-backend/schema-compiler": "1.7.29", + "@cubejs-backend/shared": "1.7.29", "vertica-nodejs": "^1.0.3" }, "license": "Apache-2.0", "devDependencies": { - "@cubejs-backend/linter": "1.7.28", - "@cubejs-backend/testing-shared": "1.7.28", + "@cubejs-backend/linter": "1.7.29", + "@cubejs-backend/testing-shared": "1.7.29", "@types/jest": "^29", "jest": "^29", "testcontainers": "^10.28.0" diff --git a/rust/cubesql/CHANGELOG.md b/rust/cubesql/CHANGELOG.md index 0c8e2df1763b1..e99357516190b 100644 --- a/rust/cubesql/CHANGELOG.md +++ b/rust/cubesql/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.7.29](https://github.com/cube-js/cube/compare/v1.7.28...v1.7.29) (2026-08-27) + +**Note:** Version bump only for package @cubejs-backend/cubesql + ## [1.7.28](https://github.com/cube-js/cube/compare/v1.7.27...v1.7.28) (2026-08-26) ### Features diff --git a/rust/cubesql/package.json b/rust/cubesql/package.json index bb8245128fb5b..ccfd3be4127ea 100644 --- a/rust/cubesql/package.json +++ b/rust/cubesql/package.json @@ -1,6 +1,6 @@ { "name": "@cubejs-backend/cubesql", - "version": "1.7.28", + "version": "1.7.29", "description": "SQL API for Cube as proxy over MySQL protocol.", "engines": { "node": "^12.0.0 || ^14.0.0 || >=16.0.0" diff --git a/rust/cubestore/CHANGELOG.md b/rust/cubestore/CHANGELOG.md index 986e4dfbca598..82a693ecb526a 100644 --- a/rust/cubestore/CHANGELOG.md +++ b/rust/cubestore/CHANGELOG.md @@ -3,6 +3,12 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.7.29](https://github.com/cube-js/cube/compare/v1.7.28...v1.7.29) (2026-08-27) + +### Features + +- **cubestore:** cap concurrent websocket connections per user ([#11667](https://github.com/cube-js/cube/issues/11667)) ([111eb3e](https://github.com/cube-js/cube/commit/111eb3ec049b9be1fa2f31186a7f83f58b5a0291)) + ## [1.7.28](https://github.com/cube-js/cube/compare/v1.7.27...v1.7.28) (2026-08-26) ### Bug Fixes diff --git a/rust/cubestore/Cargo.lock b/rust/cubestore/Cargo.lock index 34d2321545297..73cb99f8d5e29 100644 --- a/rust/cubestore/Cargo.lock +++ b/rust/cubestore/Cargo.lock @@ -1445,7 +1445,7 @@ dependencies = [ [[package]] name = "cubestore" -version = "1.7.28" +version = "1.7.29" dependencies = [ "actix-rt", "anyhow", diff --git a/rust/cubestore/cubestore/Cargo.toml b/rust/cubestore/cubestore/Cargo.toml index bed8b87f6f0c0..7d87c8fa35a2f 100644 --- a/rust/cubestore/cubestore/Cargo.toml +++ b/rust/cubestore/cubestore/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cubestore" -version = "1.7.28" +version = "1.7.29" authors = ["Cube Dev, Inc."] edition = "2021" license = "Apache-2.0" diff --git a/rust/cubestore/package.json b/rust/cubestore/package.json index 19cb3cf9850ea..7d39be393e2cb 100644 --- a/rust/cubestore/package.json +++ b/rust/cubestore/package.json @@ -1,6 +1,6 @@ { "name": "@cubejs-backend/cubestore", - "version": "1.7.28", + "version": "1.7.29", "description": "Cube.js pre-aggregation storage layer.", "main": "dist/src/index.js", "typings": "dist/src/index.d.ts", @@ -33,7 +33,7 @@ }, "license": "Apache-2.0", "devDependencies": { - "@cubejs-backend/linter": "1.7.28", + "@cubejs-backend/linter": "1.7.29", "@types/jest": "^29", "@types/node": "^18", "jest": "^29", @@ -43,7 +43,7 @@ "access": "public" }, "dependencies": { - "@cubejs-backend/shared": "1.7.28", + "@cubejs-backend/shared": "1.7.29", "@octokit/core": "^3.2.5", "source-map-support": "^0.5.19" }, From f0c591b531634dcaa53bcdc555bf75ec735e098e Mon Sep 17 00:00:00 2001 From: Igor Lukanin Date: Fri, 28 Aug 2026 01:06:24 +0200 Subject: [PATCH 7/8] docs: document the sankey chart type (#11669) * docs: document the sankey chart type * docs: correct the sankey tooltip default and register sankey in shared chart lists --------- Co-authored-by: igorlukanin <3852894+igorlukanin@users.noreply.github.com> --- docs-mintlify/docs.json | 1 + .../charts/chart-types/index.mdx | 5 +- .../charts/chart-types/sankey.mdx | 75 +++++++++++++++++++ .../charts/configuration/series-mapping.mdx | 2 + .../charts/configuration/small-multiples.mdx | 2 +- .../charts/configuration/tooltips.mdx | 4 +- 6 files changed, 85 insertions(+), 4 deletions(-) create mode 100644 docs-mintlify/docs/explore-analyze/charts/chart-types/sankey.mdx diff --git a/docs-mintlify/docs.json b/docs-mintlify/docs.json index afe7a92a17791..f748f9ab776ad 100644 --- a/docs-mintlify/docs.json +++ b/docs-mintlify/docs.json @@ -71,6 +71,7 @@ "docs/explore-analyze/charts/chart-types/area", "docs/explore-analyze/charts/chart-types/scatter", "docs/explore-analyze/charts/chart-types/pie", + "docs/explore-analyze/charts/chart-types/sankey", "docs/explore-analyze/charts/chart-types/heatmap", "docs/explore-analyze/charts/chart-types/boxplot", "docs/explore-analyze/charts/chart-types/table", diff --git a/docs-mintlify/docs/explore-analyze/charts/chart-types/index.mdx b/docs-mintlify/docs/explore-analyze/charts/chart-types/index.mdx index 1783f2f1b7c42..caeb9d3fdbd4d 100644 --- a/docs-mintlify/docs/explore-analyze/charts/chart-types/index.mdx +++ b/docs-mintlify/docs/explore-analyze/charts/chart-types/index.mdx @@ -10,6 +10,7 @@ Cube includes a library of built-in chart types covering the most common visuali - [Area](/docs/explore-analyze/charts/chart-types/area) - [Scatter](/docs/explore-analyze/charts/chart-types/scatter) - [Pie & donut](/docs/explore-analyze/charts/chart-types/pie) +- [Sankey](/docs/explore-analyze/charts/chart-types/sankey) - [Heatmap](/docs/explore-analyze/charts/chart-types/heatmap) - [Boxplot](/docs/explore-analyze/charts/chart-types/boxplot) - [Table](/docs/explore-analyze/charts/chart-types/table) @@ -79,9 +80,9 @@ Otherwise Cube checks these in order and takes the first that matches: The two [bar](/docs/explore-analyze/charts/chart-types/bar) rows are variants of the same chart type, not separate ones. -Pie, area, boxplot and HTML are never recommended — pick them yourself. Pie's shape, one measure +Pie, sankey, area, boxplot and HTML are never recommended — pick them yourself. Pie's shape, one measure across a few categories, is the one Bar already answers, and a bar chart compares those values more -accurately. All four stay available in the picker like any other chart type. +accurately. All five stay available in the picker like any other chart type. ### When nothing is recommended diff --git a/docs-mintlify/docs/explore-analyze/charts/chart-types/sankey.mdx b/docs-mintlify/docs/explore-analyze/charts/chart-types/sankey.mdx new file mode 100644 index 0000000000000..86b439cdf11fb --- /dev/null +++ b/docs-mintlify/docs/explore-analyze/charts/chart-types/sankey.mdx @@ -0,0 +1,75 @@ +--- +title: Sankey +description: Show flows between nodes, such as order status to year or channel to conversion. +--- + +Sankey diagrams draw one node per distinct endpoint and one ribbon per flow, with each ribbon's thickness proportional to its value. Best for showing how a quantity divides and moves between two sets of things: order statuses across years, traffic channels into outcomes, or one step of a user journey into the next. + +{/* Screenshot: sankey chart — order counts flowing from four statuses on the left to years on the right, with node labels on both sides. Place directly below this paragraph, full width. (hidden — replace this comment with when image is ready) */} + +## Fields + +A sankey needs exactly three fields, picked on the Fields tab: + +- **Source** — a dimension (a time dimension works too, labelled by its grain). The node a flow leaves. +- **Target** — a dimension. The node a flow enters. +- **Value** — a measure. Each row's value becomes one ribbon's thickness. + +Each result row is read as one flow, so a query grouped by two dimensions with one measure draws directly. **Swap** exchanges the source and target, which reverses every ribbon's direction. + +{/* Screenshot: Fields tab with the Source, Target and Value selectors bound, and the Swap button below them. Place inline, 50% width, right-aligned. (hidden — replace this comment with when image is ready) */} + +Stages come from shared node names rather than from a stage field: rows `A → B` and `B → C` draw as a three-stage diagram, because `B` appears as both a target and a source. Two dimensions that share no values always draw two stages. + +A row with a blank endpoint, or a value of zero or less, is not a flow and is left out — a sankey cannot draw a ribbon with no thickness. + +Cyclic data is rejected rather than drawn. A flow that returns to a node it already passed through has no left-to-right layout, so the chart names the loop and asks you to remove one hop. + +## Node grouping + +A wide breakdown can return more flows than a diagram can carry, and the small ones become unreadable first. Node grouping folds them into a single **Other** node per side, so the tail stays represented as an aggregate instead of vanishing. It is off until you pick a mode, in the Node grouping section of the Fields tab: + +- **Minimal share** — fold every node below a percentage of its side's total. A node sitting exactly on the threshold is kept. +- **Top N nodes** — keep the N largest nodes per side. Tied nodes at the cutoff are kept together, so asking for the top 3 can keep more than 3 when third place is shared. + +The two sides fold independently, each against its own total, because a node that is small as a target may be large as a source. The bucket's name is yours to set; leave it blank for **Other**. When both sides fold at once, the two buckets are named apart — **Other (source)** and **Other (target)** — so one is not read as the other. + +{/* Screenshot: Node grouping section with Minimal share selected and a threshold entered. Place inline, 50% width, right-aligned. (hidden — replace this comment with when image is ready) */} + +A single node is never folded on its own — replacing one named node with a same-sized **Other** would say less than leaving it alone. A node whose own value already matches the bucket's name is also left out of the bucket, so a real category called "Other" is never merged into it. + +The bucket stands for a set of members rather than one, so it has no query behind it: clicking a ribbon that touches it does not drill down. + +## Truncation + +Independently of grouping, a sankey draws at most 50 flows, keeping the largest. This is a backstop against a runaway result, not a legibility control — when it removes anything, a warning appears beside the Flows heading on the Fields tab saying how many of the total it is showing, pointing at node grouping, which is the real answer for a diagram too wide to read. + +## Data labels + +Each node carries its name and, optionally, its value. Both are independent buttons in the Data labels section of the Style tab: **Name** is on by default, **Value** is off. With both on, the value follows the name in parentheses; with only the value on, the node carries a bare number; with both off the diagram draws no labels. + +A node's value is the larger of its inflow and its outflow, and takes the value field's own number format. + +Labels sit to the right of their node, except in the final stage, where they flip to the left so they stay inside the frame. + +## Color + +Each node takes its own color from the palette selected in the **Palette** dropdown on the Style tab. The color follows the node's name rather than its position, so filtering or re-sorting the query does not repaint the diagram. See [Palettes](/docs/explore-analyze/charts/configuration/color-and-stacking#palettes) for the built-in palettes and how to define a custom one. + +The **Flows** control sets where a ribbon takes its color from: + +- **Source** — the color of the node the ribbon leaves. The default. +- **Neutral** — one muted color for every ribbon, leaving the nodes to carry the palette. +- **Target** — the color of the node the ribbon enters. + +{/* Screenshot: Style tab Color section with the Palette dropdown and the Flows buttons. Place inline, 50% width, right-aligned. (hidden — replace this comment with when image is ready) */} + +## Tooltips + +Hovering a ribbon shows the result columns named in **Tooltip fields** on the Fields tab, which starts as every column in the query except the three the diagram already draws — source, target and value. Add them back like any other field if you want them repeated. Hovering a node shows its name and value. + +See [Tooltips](/docs/explore-analyze/charts/configuration/tooltips) for adding, removing and reordering fields. + +## Drill down + +Clicking a ribbon drills into the row behind that flow. Ribbons touching a grouped **Other** node are not clickable, since that node covers several members at once. diff --git a/docs-mintlify/docs/explore-analyze/charts/configuration/series-mapping.mdx b/docs-mintlify/docs/explore-analyze/charts/configuration/series-mapping.mdx index 4489b16d23a1a..33a5c2caea8dd 100644 --- a/docs-mintlify/docs/explore-analyze/charts/configuration/series-mapping.mdx +++ b/docs-mintlify/docs/explore-analyze/charts/configuration/series-mapping.mdx @@ -18,6 +18,8 @@ Series mapping controls which query columns are assigned to which chart channels | **Split by** | Repeats the chart once per unique value, as a grid of panels — see [small multiples](/docs/explore-analyze/charts/configuration/small-multiples) | Bar, line, area, scatter | | **Tooltip** | Fields shown on hover | All types | | **Theta** (pie) | The measure that determines slice size | Pie | +| **Source** / **Target** (sankey) | The dimensions holding the node each flow leaves and enters | Sankey | +| **Value** (sankey) | The measure that determines ribbon thickness | Sankey | ## Assigning fields diff --git a/docs-mintlify/docs/explore-analyze/charts/configuration/small-multiples.mdx b/docs-mintlify/docs/explore-analyze/charts/configuration/small-multiples.mdx index 71b2a4ee07ada..14a1506a50607 100644 --- a/docs-mintlify/docs/explore-analyze/charts/configuration/small-multiples.mdx +++ b/docs-mintlify/docs/explore-analyze/charts/configuration/small-multiples.mdx @@ -55,6 +55,6 @@ A legend is shared across the grid rather than repeated per panel. Clicking a le ## Limitations - **One split dimension.** One dimension fills the grid, panel by panel. Splitting by two dimensions at once — one down the rows and another across the columns — is not supported. -- **Cartesian charts only.** Bar, line, area, and scatter. Pie, table, KPI, heatmap, boxplot, map, and HTML charts cannot be split. +- **Cartesian charts only.** Bar, line, area, and scatter. Pie, sankey, table, KPI, heatmap, boxplot, map, and HTML charts cannot be split. - **Panel labels are not configurable.** Each panel is labeled with its dimension value; the font, size, and color are fixed. - **The split is enabled on a single-view chart.** A chart that already carries data labels, a reference line, or a second Y axis series cannot be split — turn the split on first. The order is the only constraint: once a chart is split, data labels and reference lines can be added freely and are drawn in every panel. diff --git a/docs-mintlify/docs/explore-analyze/charts/configuration/tooltips.mdx b/docs-mintlify/docs/explore-analyze/charts/configuration/tooltips.mdx index 1f4ec0b5956d6..33db7523c45b1 100644 --- a/docs-mintlify/docs/explore-analyze/charts/configuration/tooltips.mdx +++ b/docs-mintlify/docs/explore-analyze/charts/configuration/tooltips.mdx @@ -9,7 +9,9 @@ Tooltips appear when a user hovers over a data point in a chart. By default, Cub ## Default behavior -When a chart is first created, all fields assigned to the chart (X, Y, color, size) are included in the tooltip automatically. The tooltip is enabled by default on all Vega-based chart types (bar, line, area, scatter, heatmap, boxplot) and on map charts. +When a chart is first created, all fields assigned to the chart (X, Y, color, size) are included in the tooltip automatically. The tooltip is enabled by default on all Vega-based chart types (bar, line, area, scatter, heatmap, boxplot), on map charts, and on sankey charts. + +[Sankey](/docs/explore-analyze/charts/chart-types/sankey) is the exception to the first rule: it seeds the tooltip with every column *except* the source, target and value it already draws. ## Configuring tooltip fields From 486e1f288e5daab8a8fbb50b81e11dec461b9ac3 Mon Sep 17 00:00:00 2001 From: Igor Lukanin Date: Fri, 28 Aug 2026 01:40:17 +0200 Subject: [PATCH 8/8] docs: recipe for cross-data-source UNION ALL queries via the SQL API (#11676) * docs: recipe for cross-data-source UNION ALL queries via the SQL API * docs: correct cube-name resolution note, flag non-additive measures, fix recipe count * docs: rewrap the pg_ prefix note so no inline code span breaks across lines --------- Co-authored-by: igorlukanin <3852894+igorlukanin@users.noreply.github.com> --- .../connect-to-data/multiple-data-sources.mdx | 4 + docs-mintlify/docs.json | 1 + .../data-modeling/concepts/data-blending.mdx | 5 + .../cross-data-source-queries.mdx | 368 ++++++++++++++++++ docs-mintlify/recipes/index.mdx | 5 +- 5 files changed, 382 insertions(+), 1 deletion(-) create mode 100644 docs-mintlify/recipes/data-modeling/cross-data-source-queries.mdx diff --git a/docs-mintlify/admin/connect-to-data/multiple-data-sources.mdx b/docs-mintlify/admin/connect-to-data/multiple-data-sources.mdx index 89f66bc334857..42d56014832c1 100644 --- a/docs-mintlify/admin/connect-to-data/multiple-data-sources.mdx +++ b/docs-mintlify/admin/connect-to-data/multiple-data-sources.mdx @@ -64,6 +64,10 @@ Cubes that don't set `data_source` use the default source — there's no need to write `data_source: default` explicitly, though you can if you prefer to be explicit. +A single query can also span sources: see [querying across data +sources](/recipes/data-modeling/cross-data-source-queries) for appending rows +from cubes in different databases into one result set. + For multitenancy scenarios where the data source is selected dynamically per request, see [`driver_factory`](/reference/configuration/config#driver_factory) and the [multitenancy guide][ref-config-multitenancy]. diff --git a/docs-mintlify/docs.json b/docs-mintlify/docs.json index f748f9ab776ad..b86750bd9413f 100644 --- a/docs-mintlify/docs.json +++ b/docs-mintlify/docs.json @@ -675,6 +675,7 @@ "recipes/data-modeling/dbt", "recipes/data-modeling/using-dynamic-measures", "recipes/data-modeling/dynamic-union-tables", + "recipes/data-modeling/cross-data-source-queries", "recipes/data-modeling/polymorphic-cubes", "recipes/data-modeling/custom-order" ] diff --git a/docs-mintlify/docs/data-modeling/concepts/data-blending.mdx b/docs-mintlify/docs/data-modeling/concepts/data-blending.mdx index 044535d218ea9..f7bf3e6c5c35b 100644 --- a/docs-mintlify/docs/data-modeling/concepts/data-blending.mdx +++ b/docs-mintlify/docs/data-modeling/concepts/data-blending.mdx @@ -18,6 +18,11 @@ efficient for large volumes of data. The other situation in which data blending could be a better approach than joining on date is when the two tables have mostly the same columns, such as in the example below. +This pattern builds one cube over a union of the underlying cubes' SQL, so all of +them must live in the same data source. To append rows from cubes in *different* +databases, see [querying across data +sources](/recipes/data-modeling/cross-data-source-queries). + For an example, consider an omnichannel store which has both online and offline sales. Let's calculate summary metrics for revenue, customer count, etc. We have a `retail_orders` cube for offline sales: diff --git a/docs-mintlify/recipes/data-modeling/cross-data-source-queries.mdx b/docs-mintlify/recipes/data-modeling/cross-data-source-queries.mdx new file mode 100644 index 0000000000000..52016aa151268 --- /dev/null +++ b/docs-mintlify/recipes/data-modeling/cross-data-source-queries.mdx @@ -0,0 +1,368 @@ +--- +title: Querying across data sources +description: Append rows from cubes that live in different databases into one result set with a UNION ALL query through the SQL API. +--- + +## Use case + +Some datasets are spread across more than one database. Product events, for +example, are often split by age: the last few months stay in a fast analytical +database that serves live dashboards, while everything older is moved to +cheaper storage. Both tables describe the same events and carry the same +columns. + +The goal is to report on them together — one result set with the rows of both +databases appended, plus a dimension that says which database each row came +from. + +This is a *union*: it adds rows. It is a different problem from a +[`rollup_join`](/reference/data-modeling/pre-aggregations#rollup_join), which +adds columns by relating entities that live in different databases. It is also +different from [data blending](/docs/data-modeling/concepts/data-blending), +which unions cubes inside a single database. + +The [SQL API](/reference/core-data-apis/sql-api) can do this at query time, +against live data and without pre-aggregations. Each cube is queried on its own +data source, and Cube appends the results. + +## Configuration + +Define the two connections as [multiple data +sources](/admin/connect-to-data/multiple-data-sources). The default source needs +no name; every other source gets one, and the full list goes in +`CUBEJS_DATASOURCES`: + +```dotenv +CUBEJS_DATASOURCES=default,recent + +CUBEJS_DB_TYPE=postgres +CUBEJS_DB_HOST=archive.example.com +CUBEJS_DB_NAME=analytics +# ... + +CUBEJS_DS_RECENT_DB_TYPE=clickhouse +CUBEJS_DS_RECENT_DB_HOST=clickhouse.example.com +CUBEJS_DS_RECENT_DB_NAME=analytics +# ... +``` + +## Data modeling + +Model each table as its own cube, and point one of them at the named data source +with [`data_source`](/reference/data-modeling/cube#data_source). The cube without +a `data_source` uses the default one. + +Give both cubes a matching set of members, and add a constant dimension that +identifies the origin of each row. That dimension is what makes the two halves +of the union distinguishable once they sit in the same result set: + + + +```yaml title="YAML" +cubes: + - name: archived_events + sql_table: events + + dimensions: + - name: id + sql: id + type: number + primary_key: true + + - name: created_at + sql: created_at + type: time + + - name: tier + sql: tier + type: string + + - name: storage + sql: "'archive'" + type: string + + measures: + - name: event_count + type: count + + - name: recent_events + sql_table: events + data_source: recent + + dimensions: + - name: id + sql: id + type: number + primary_key: true + + - name: created_at + sql: created_at + type: time + + - name: tier + sql: tier + type: string + + - name: storage + sql: "'recent'" + type: string + + measures: + - name: event_count + type: count +``` + +```javascript title="JavaScript" +cube(`archived_events`, { + sql_table: `events`, + + dimensions: { + id: { + sql: `id`, + type: `number`, + primary_key: true + }, + + created_at: { + sql: `created_at`, + type: `time` + }, + + tier: { + sql: `tier`, + type: `string` + }, + + storage: { + sql: `'archive'`, + type: `string` + } + }, + + measures: { + event_count: { + type: `count` + } + } +}) + +cube(`recent_events`, { + sql_table: `events`, + data_source: `recent`, + + dimensions: { + id: { + sql: `id`, + type: `number`, + primary_key: true + }, + + created_at: { + sql: `created_at`, + type: `time` + }, + + tier: { + sql: `tier`, + type: `string` + }, + + storage: { + sql: `'recent'`, + type: `string` + } + }, + + measures: { + event_count: { + type: `count` + } + } +}) +``` + + + +## Querying + +Connect to the [SQL API](/reference/core-data-apis/sql-api) and append the two +cubes with `UNION ALL`: + +```sql +SELECT storage, tier, MEASURE(event_count) AS events +FROM archived_events +GROUP BY 1, 2 +UNION ALL +SELECT storage, tier, MEASURE(event_count) AS events +FROM recent_events +GROUP BY 1, 2 +``` + +``` + storage | tier | events +---------+------------+-------- + archive | free | 412508 + archive | enterprise | 95012 + recent | free | 38471 + recent | enterprise | 9930 +``` + +Each half of the union is evaluated against its own database, in that database's +own dialect, and Cube appends the two results. Filters reach the databases +rather than being applied afterwards, so a `WHERE` clause on either side limits +what that database scans: + +```sql +SELECT storage, tier, MEASURE(event_count) AS events +FROM archived_events +WHERE tier = 'enterprise' +GROUP BY 1, 2 +UNION ALL +SELECT storage, tier, MEASURE(event_count) AS events +FROM recent_events +WHERE tier = 'enterprise' +GROUP BY 1, 2 +``` + +Add a time dimension to both halves to line the databases up on a common grain: + +```sql +SELECT storage, tier, DATE_TRUNC('day', created_at) AS date, MEASURE(event_count) AS events +FROM archived_events +GROUP BY 1, 2, 3 +UNION ALL +SELECT storage, tier, DATE_TRUNC('day', created_at) AS date, MEASURE(event_count) AS events +FROM recent_events +GROUP BY 1, 2, 3 +ORDER BY 3, 1 +``` + + + +A cube whose name starts with `pg_` cannot be referenced by that name alone. The +SQL API routes such a name to `pg_catalog`, where no cube is ever found, and the +query fails with `Table or CTE with name 'pg_...' not found`. Qualify it with the +schema that holds cubes, as in `FROM public.pg_costs`, or avoid the prefix. + + + +### Aggregating across the union + +Wrap the union in a CTE to aggregate over both databases at once. Here the +per-tier totals combine events from both databases: + +```sql +WITH blended AS ( + SELECT storage, tier, MEASURE(event_count) AS events + FROM archived_events + GROUP BY 1, 2 + UNION ALL + SELECT storage, tier, MEASURE(event_count) AS events + FROM recent_events + GROUP BY 1, 2 +) +SELECT tier, SUM(events) AS total, COUNT(DISTINCT storage) AS databases +FROM blended +GROUP BY 1 +ORDER BY 2 DESC +``` + +``` + tier | total | databases +------------+--------+----------- + free | 450979 | 2 + enterprise | 104942 | 2 +``` + +`UNION` also works where duplicate rows should collapse, as do an outer +`ORDER BY` and `LIMIT` over the union. + + + +Aggregating over the union like this is only correct for additive measures such +as `count` and `sum`. Non-additive measures — `count_distinct`, `avg`, +percentiles — cannot be combined from per-source results: summing distinct +counts double-counts anything present in both databases, and averaging averages +ignores how many rows each database contributed. No error is raised, so report +these measures per data source instead. + + + +### Inspecting how a query is split + +Run `EXPLAIN` on any of these queries to see the plan. It puts a `Union` over one +`CubeScan` per cube, and each scan carries its own filter — the `WHERE` clause is +part of the request sent for that cube, not a step applied after the results are +appended: + +```sql +EXPLAIN SELECT storage, tier, MEASURE(event_count) AS events +FROM archived_events +WHERE tier = 'enterprise' +GROUP BY 1, 2 +UNION ALL +SELECT storage, tier, MEASURE(event_count) AS events +FROM recent_events +WHERE tier = 'enterprise' +GROUP BY 1, 2 +``` + +``` +Union + CubeScan: request={ + "measures": [ + "archived_events.event_count" + ], + "dimensions": [ + "archived_events.storage", + "archived_events.tier" + ], + "segments": [], + "order": [], + "filters": [ + { + "member": "archived_events.tier", + "operator": "equals", + "values": [ + "enterprise" + ] + } + ] +} + CubeScan: request={ + "measures": [ + "recent_events.event_count" + ], + "dimensions": [ + "recent_events.storage", + "recent_events.tier" + ], + "segments": [], + "order": [], + "filters": [ + { + "member": "recent_events.tier", + "operator": "equals", + "values": [ + "enterprise" + ] + } + ] +} +``` + +The plan identifies each scan by cube, not by data source, so read it together +with the `data_source` of each cube to see which database serves which half. + +## Limitations + +Unions combine rows across data sources; joins do not. A query that joins two +cubes on different data sources is rejected, and relating entities across +databases needs a +[`rollup_join`](/reference/data-modeling/pre-aggregations#rollup_join) instead. + +Each half of the union is subject to the [maximum row +limit](/docs/data-modeling/configuration#maximum-row-limit) on its own, and the +cap applies before the results are appended. Aggregate inside each half of the +union, as in the examples above, rather than unioning raw rows and aggregating +afterwards. diff --git a/docs-mintlify/recipes/index.mdx b/docs-mintlify/recipes/index.mdx index 469eb137d4509..4462abde37e77 100644 --- a/docs-mintlify/recipes/index.mdx +++ b/docs-mintlify/recipes/index.mdx @@ -4,7 +4,7 @@ description: Step-by-step tutorials and best practices for getting the most out mode: wide --- -Explore **39 recipes** across data modeling, calculations, analytics patterns, +Explore **41 recipes** across data modeling, calculations, analytics patterns, pre-aggregations, configuration, APIs, and AI. ## Data Modeling @@ -28,6 +28,9 @@ pre-aggregations, configuration, APIs, and AI. Combine multiple database tables that relate to the same entity into a single cube. + + Append rows from cubes in different databases into one result set with a UNION ALL query. + Define a custom sort order for categorical values like pipeline stages that don't sort alphabetically.