Skip to content

docs: fill gaps found in a routine doc-coverage audit (SQL API, embedding, billing, usage analytics) - #11701

Open
keydunov wants to merge 5 commits into
masterfrom
claude/gallant-ramanujan-re1j3p
Open

docs: fill gaps found in a routine doc-coverage audit (SQL API, embedding, billing, usage analytics)#11701
keydunov wants to merge 5 commits into
masterfrom
claude/gallant-ramanujan-re1j3p

Conversation

@keydunov

Copy link
Copy Markdown
Member

Summary

Routine audit cross-checking recent merged PRs in cube-js/cube and cubedevinc/cubejs-enterprise against docs-mintlify, filtered through the customer-facing criteria in cubejs-enterprise's .claude/shared/customer-facing-criteria.md. These are small, surgical additions to existing pages for shipped (not flagged-off) behavior that had no documentation at all.

  • SQL API reference (reference/core-data-apis/sql-api/reference.mdx):
    • Document UNION/UNION ALL pushdown to the data source (previously ran only as post-processing; could silently drop rows before this shipped)
    • Document the ranking/value window functions (ROW_NUMBER, RANK, DENSE_RANK, PERCENT_RANK, CUME_DIST, FIRST_VALUE, LAST_VALUE, NTH_VALUE) — only LAG/LEAD were previously documented
    • Document APPROX_PERCENTILE_CONT, including its use for APPROX_MEDIAN on data sources with no exact median function (Athena, Trino, Presto)
  • Querying data APIs (reference/core-data-apis/queries.mdx): note that the timezone query option is now validated against real IANA time zone names and rejected early if invalid
  • Creator Mode embedding (embedding/iframe/creator-mode.mdx): mention that embed users can now create folders, not just workbooks/dashboards/reports
  • AI Tokens (admin/account-billing/ai-tokens.mdx): document the AI Usage / AI Requests tab split, and free-tier token enforcement + the mid-month-downgrade drawdown behavior
  • Usage Analytics (admin/monitoring/usage-analytics.mdx): document the new POST /api/v1/usage-analytics/token endpoint for pulling usage data into external BI tools

Not included here

A few larger, genuinely undocumented features surfaced by the same audit (Funnel chart, Dashboard comments, the Alerts feature, and the dbt chat-driven workspace-authoring capability) are tracked as separate GitHub issues rather than bundled into this docs PR, since they likely need their own new page(s)/sections rather than a small edit.

Test plan

  • Docs build (cd docs-mintlify && yarn dev) renders each changed page without MDX/broken-link errors
  • Spot-check the new SQL API tables and the UNION example render correctly

Generated by Claude Code

…s, and APPROX_PERCENTILE_CONT

- UNION/UNION ALL now push down to a single data source when all
  unioned queries target the same source
- Window functions: document ROW_NUMBER, RANK, DENSE_RANK,
  PERCENT_RANK, CUME_DIST, FIRST_VALUE, LAST_VALUE, NTH_VALUE (only
  LAG/LEAD were previously documented)
- Aggregate functions: document APPROX_PERCENTILE_CONT, including its
  use for APPROX_MEDIAN on data sources with no exact median function
…d against IANA zone names

The timezone value is now checked against real IANA time zone names
and rejected early with an error if invalid, instead of being handled
inconsistently.
Creator Mode users can now create, rename, and delete folders, not
just workbooks/dashboards/reports.
…ier enforcement

- The AI token tab is now split into "AI Usage" (aggregate spend) and
  "AI Requests" (raw per-request log with live tail)
- Free-tier token usage is now correctly drawn down and enforced, and
  a mid-month downgrade draws that month's spend against the new free
  allowance immediately
New POST /api/v1/usage-analytics/token endpoint lets account admins
get a scoped Cube API token to query their own usage/billing data
from external BI tools.
@mintlify

mintlify Bot commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

Preview deployment for your docs. Learn more about Mintlify Previews.

Project Status Preview Updated (UTC)
cubed3 🟢 Ready View Preview Aug 31, 2026, 12:34 AM

💡 Tip: Enable Workflows to automatically generate PRs for you.

@claude

claude Bot commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

Claude finished @keydunov's task in 3m 7s —— View job


Docs review — 1 factual error, 2 medium, 3 low. Verified every claim I could reach from this repo; the SQL API additions are otherwise accurate.

Full review — findings, verification notes, and checklist

Checklist

  • Read the diff
  • Verify internal links and anchors resolve
  • Verify SQL function + UNION pushdown claims against rust/cubesql
  • Verify timezone validation claim against cubejs-api-gateway
  • Post review

What I verified

The SQL API claims hold up well against the source — this is more carefully researched than most docs PRs.

Claim Verdict
UNION/UNION ALL pushes down when all queries share a data source; cross-source falls back to post-processing ✅ Matches wrapper-pull-up-union in rust/cubesql/.../rules/wrapper/union.rs, which binds ?input_data_source as a single top-level var
UNION dedupes via a Distinct folded into the set operation wrapper-push-down-distinct-to-union
union is a base SQL template (so no dialect caveat needed) BaseQuery.js:4622
Ranking/value window function list ✅ Exactly matches the case list in test_wrapper_built_in_window_functions — including correctly omitting NTILE
timezone rejected unless a valid IANA name timezoneSchema in packages/cubejs-api-gateway/src/query.js:62
APPROX_MEDIANAPPROX_PERCENTILE_CONT(expr, 0.5); Athena/Trino/Presto framing ✅ Matches the doc comment on test_wrapper_multi_arg_aggregate_function
All new link refs and anchors resolve ref-qpd, ref-qpp, ref-data-sources, ref-core-data-apis, #build-your-own-dashboards, #when-limits-are-reached all valid

Findings

1. APPROX_PERCENTILE_CONT syntax is wrong (sql-api/reference.mdx:545) — the row says it's "used with WITHIN GROUP (ORDER BY ...)", copied from the PERCENTILE_CONT row above. It isn't an ordered-set aggregate; it takes the percentile positionally, and the cell contradicts itself by giving the correct form one sentence later. Suggested replacement inline, folding in the two supporting caveats (pushdown is conditional on the dialect defining functions/APPROXPERCENTILECONT — without it the query hard-fails with Can't detect Cube query rather than degrading to post-processing; and the weighted 3-arg variant is rejected).

2. NTILE isn't marked unsupported (sql-api/reference.mdx:610) — now that the page reads as a complete ranking-function list, a reader will try NTILE(4) and get an opaque DataFusion coercion error with no hint it's a known gap (CORE-831).

3. usage-analytics.mdx:72–75 — auth scheme may be wrong. Every other documented /api/v1/ endpoint uses Authorization: Bearer; this sample uses the Embed APIs' Api-Key prefix. I can't verify it — the handler is in cubedevinc/cubejs-enterprise — so please confirm against the implementation. Same comment also flags the missing response shape and asks whether the admin-only restriction is enforced or conventional.

4. sql-api/reference.mdx:76 — link target nit. [ref-data-sources] points at the connection-setup catalog; multiple-data-sources is the page that explains the data_source concept the rule actually turns on.

5. sql-api/reference.mdx:84–89 — the UNION example uses COUNT(*), which this same page warns (~line 404) requires a count measure to exist. Suggested a variant using a named measure with a period label.

Not verifiable from this repo

The ai-tokens.mdx and creator-mode.mdx changes describe enterprise-side behavior I have no access to. The free-tier mid-month-downgrade drawdown in particular is a specific billing behavior worth a second pair of eyes from whoever shipped it — getting that wrong in docs has direct customer-trust cost.

Style

Consistent with docs-mintlify/CLAUDE.md: surgical edits to existing pages, no padding, root-relative links, correct heading levels. No new pages, which is the right call for changes this size.

· branch `claude/gallant-ramanujan-re1j3p`

| `MEASURE` | Works with measures of [any type][ref-sql-api-aggregate-functions] | ✅ Yes | <nobr>❌ Outer</nobr><br/><nobr>✅ Inner (selections)</nobr><br/><nobr>✅ Inner (projections)</nobr> |
| `STRING_AGG` | Concatenates the input values into a string, separated by a delimiter (supports `DISTINCT`) | ✅ Yes | ❌ No |
| `PERCENTILE_CONT` | Computes a continuous percentile, used with `WITHIN GROUP (ORDER BY ...)` | ✅ Yes | ❌ No |
| `APPROX_PERCENTILE_CONT` | Computes an approximate continuous percentile, used with `WITHIN GROUP (ORDER BY ...)`. Also how `APPROX_MEDIAN` is expressed (as `APPROX_PERCENTILE_CONT(expr, 0.5)`), which is useful for computing a median on data sources with no exact median function, such as Athena, Trino, and Presto | ✅ Yes | ❌ No |

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Incorrect syntax: APPROX_PERCENTILE_CONT is not an ordered-set aggregate in CubeSQL — it takes the percentile as a positional argument, not WITHIN GROUP (ORDER BY ...). The row copies that clause from the PERCENTILE_CONT row above it, but the two have different call shapes, and the cell then contradicts itself by giving the correct form (APPROX_PERCENTILE_CONT(expr, 0.5)) a sentence later.

From rust/cubesql/cubesql/src/compile/test/test_wrapper.rs:4079 the pushed-down call is APPROX_PERCENTILE_CONT(taxful_total_price, 0.5), and test_wrapper_approx_percentile_cont_is_binary (line 4156) pins that a third argument is rejected outright.

Two more things worth folding in while editing this row:

  • Pushdown is conditional on the dialect defining the functions/APPROXPERCENTILECONT template. Per test_wrapper_multi_arg_aggregate_function_without_template, a dialect without it doesn't degrade to post-processing — the query fails with Can't detect Cube query. The bare ✅ Yes / ❌ No pair reads like graceful fallback.
  • The weighted three-argument variant is a different function and is not supported.
Suggested change
| `APPROX_PERCENTILE_CONT` | Computes an approximate continuous percentile, used with `WITHIN GROUP (ORDER BY ...)`. Also how `APPROX_MEDIAN` is expressed (as `APPROX_PERCENTILE_CONT(expr, 0.5)`), which is useful for computing a median on data sources with no exact median function, such as Athena, Trino, and Presto | ✅ Yes | ❌ No |
| `APPROX_PERCENTILE_CONT` | Computes an approximate continuous percentile, called as `APPROX_PERCENTILE_CONT(expr, fraction)` (not as an ordered-set aggregate with `WITHIN GROUP`). `APPROX_MEDIAN(expr)` is evaluated as `APPROX_PERCENTILE_CONT(expr, 0.5)`, which is how a median is computed on data sources with no exact median function, such as Athena, Trino, and Presto. Only supported on data sources that provide an approximate percentile function; on other data sources the query fails rather than falling back to post-processing. The weighted three-argument variant is not supported | ✅ Yes | ❌ No |

Fix this →

| `CUME_DIST` | Returns the cumulative distribution: the number of partition rows preceding or peer with the current row, divided by total rows in the partition | ✅ Yes | ❌ No |
| `FIRST_VALUE` | Returns the value evaluated at the first row of the window frame | ✅ Yes | ❌ No |
| `LAST_VALUE` | Returns the value evaluated at the last row of the window frame | ✅ Yes | ❌ No |
| `NTH_VALUE` | Returns the value evaluated at the `n`-th row of the window frame, counting from 1 | ✅ Yes | ❌ No |

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new table enumerates the ranking/value window functions, and the list matches test_wrapper_built_in_window_functions exactly — except that the one built-in the test deliberately excludes, NTILE, isn't mentioned as unsupported anywhere.

That omission is the one a reader is most likely to trip over: now that the page reads as a complete list of ranking functions, someone will reach for NTILE(4) and get a DataFusion coercion error (Exact([UInt64])), which gives no hint that it's a known gap rather than a syntax mistake. See test_wrapper_ntile_does_not_plan (rust/cubesql/cubesql/src/compile/test/test_wrapper.rs:4120) — it's tracked as CORE-831.

Suggest adding a row so the failure is at least self-explanatory:

Suggested change
| `NTH_VALUE` | Returns the value evaluated at the `n`-th row of the window frame, counting from 1 | ✅ Yes | ❌ No |
| `NTH_VALUE` | Returns the value evaluated at the `n`-th row of the window frame, counting from 1 | ✅ Yes | ❌ No |
| `NTILE` | Not supported; a query using it fails to plan | ❌ No | ❌ No |

Fix this →

Comment on lines +84 to +89
SELECT status, COUNT(*) AS count
FROM orders
WHERE created_at >= '2024-01-01'
GROUP BY 1
UNION ALL
SELECT status, COUNT(*) AS count

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor: COUNT(*) here fights with the warning further down this same page (~line 404), which says COUNT(*) requires a measure named count to be defined on the cube or view. A reader copying this example against a cube without that measure gets an error from the example rather than from their own query. Using a named measure would sidestep it, and the two branches only differ by the created_at predicate anyway.

Also, as written the example produces two rows per status, which is a slightly confusing first demonstration of UNION ALL — adding a literal period label would make the intent obvious:

Suggested change
SELECT status, COUNT(*) AS count
FROM orders
WHERE created_at >= '2024-01-01'
GROUP BY 1
UNION ALL
SELECT status, COUNT(*) AS count
SELECT 'current' AS period, status, count
FROM orders
WHERE created_at >= '2024-01-01'
GROUP BY 1, 2
UNION ALL
SELECT 'previous' AS period, status, count
FROM orders
WHERE created_at < '2024-01-01'
GROUP BY 1, 2;

`UNION` combines the result sets of two or more `SELECT` statements. `UNION ALL`
keeps duplicate rows in the combined result; plain `UNION` removes them.

When every unioned query targets the same [data source][ref-data-sources],

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit on the link target: [ref-data-sources] resolves to /admin/connect-to-data/data-sources, which is the catalog of connection setup pages (Athena, Snowflake, …). What matters for this rule is the cube's data_source assignment — i.e. whether two cubes route to the same configured source — so /admin/connect-to-data/multiple-data-sources is the page that actually explains the concept a reader needs here.

(The claim itself checks out — wrapper-pull-up-union in rust/cubesql/cubesql/src/compile/rewrite/rules/wrapper/union.rs binds ?input_data_source as a single top-level var across every unioned query, so a cross-source union falls through to post-processing, exactly as described.)

Comment on lines +72 to +75
```bash
curl -X POST https://your-account.cubecloud.dev/api/v1/usage-analytics/token \
-H "Authorization: Api-Key ${API_KEY}"
```

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Worth double-checking the auth scheme against the actual handler — I can't verify it from this repo, since the endpoint lives in cubedevinc/cubejs-enterprise.

This is a /api/v1/ path, and every other documented /api/v1/ endpoint (Control Plane API, reference/control-plane-api.mdx:26) authenticates with Authorization: Bearer YOUR_API_KEY. The Api-Key prefix used here is the Embed APIs convention (reference/embed-apis/generate-session.mdx:358). If the enterprise handler shares the Control Plane API's middleware, this sample won't work as written — and an auth-header mistake is the kind of docs bug that costs a reader a support ticket rather than a re-read.

Two smaller things in the same block:

  • Other curl samples on /api/v1/ use the literal placeholder YOUR_API_KEY rather than ${API_KEY}; matching that keeps the page copy-pasteable in the same way as its neighbors.
  • The endpoint returns a token, but the snippet doesn't show the response shape or how the token is then passed to a Core Data API request. Since the whole point is feeding it to Power BI, one line showing the response field name would make this actionable.

Also: "Only account administrators can request this token" — is that enforced by the endpoint, or a convention? Worth being precise, as readers will plan access around it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant