docs: "dashboards as code" guide for programmatic dashboards (CUB-3521) - #11493
docs: "dashboards as code" guide for programmatic dashboards (CUB-3521)#11493mngr wants to merge 2 commits into
Conversation
|
Preview deployment for your docs. Learn more about Mintlify Previews.
💡 Tip: Enable Workflows to automatically generate PRs for you. |
…t API (CUB-3521)
The first draft documented a runtime-owned YAML-in-project model that never
shipped. Rewrite it to reflect what actually merged in cubejs-enterprise#13361:
the idempotent public REST upserts
PUT /deployments/{id}/workbooks/by-slug/{slug}
PUT /deployments/{id}/reports/by-public-id/{publicId}
keyed by portable slug / publicId. Covers the author→export→upsert→publish
apply flow, idempotency and the three 409 conflict codes, write-once publicId
adoption (incl. legacy synthesized ids), and cross-links to the generated
endpoint reference pages. Fix the dashboards index cross-link accordingly.
|
Claude finished @mngr's task in 3m 33s —— View job Docs review — 1 blocking, 3 medium, 2 low. Accuracy against the OpenAPI spec is otherwise excellent.Full review
What's goodThe technical accuracy here is unusually high for a docs PR. I checked every substantive claim against
The framing decision — document the REST primitives with an explicit Findings
Only #1 is genuinely blocking; the rest are worth a pass before merge. One editorial noteAt 200 lines this runs long against the "be concise, don't pad" guidance in
Neither blocks merge. |
| ```bash | ||
| curl -X PUT \ | ||
| "$CUBE_API_URL/api/v1/deployments/$DEPLOYMENT_ID/reports/by-public-id/revqZ1x8Kp0a" \ | ||
| -H "Authorization: $CUBE_API_TOKEN" \ | ||
| -H "Content-Type: application/json" \ | ||
| -d @report-revenue-by-month.json | ||
| ``` |
There was a problem hiding this comment.
The curl examples will 401 — the Bearer prefix is missing.
api-reference/authentication.mdx:11-18 is explicit: /api/v1/… requests take the token with the Bearer prefix (Api-Key is only accepted on the SCIM endpoint). Both curl blocks on this page send the bare token, so a reader copy-pasting either one gets 401 Unauthorized.
| ```bash | |
| curl -X PUT \ | |
| "$CUBE_API_URL/api/v1/deployments/$DEPLOYMENT_ID/reports/by-public-id/revqZ1x8Kp0a" \ | |
| -H "Authorization: $CUBE_API_TOKEN" \ | |
| -H "Content-Type: application/json" \ | |
| -d @report-revenue-by-month.json | |
| ``` | |
| ```bash | |
| curl -X PUT \ | |
| "$CUBE_API_URL/api/v1/deployments/$DEPLOYMENT_ID/reports/by-public-id/revqZ1x8Kp0a" \ | |
| -H "Authorization: Bearer $CUBE_API_TOKEN" \ | |
| -H "Content-Type: application/json" \ | |
| -d @report-revenue-by-month.json |
Same fix needed at line 117.
[Fix this →](https://claude.ai/code?q=On%20branch%20gleb%2Fcub-3521-dashboards-as-code%2C%20in%20docs-mintlify%2Fdocs%2Fexplore-analyze%2Fdashboards%2Fdashboards-as-code.mdx%2C%20both%20curl%20examples%20(around%20lines%2096%20and%20117)%20send%20-H%20%22Authorization%3A%20%24CUBE_API_TOKEN%22%20without%20the%20Bearer%20prefix.%20api-reference%2Fauthentication.mdx%20requires%20%22Authorization%3A%20Bearer%20%3Ctoken%3E%22%20for%20%2Fapi%2Fv1%20endpoints.%20Add%20the%20Bearer%20prefix%20to%20both.&repo=cube-js/cube)
| ### 4. Publish | ||
|
|
||
| Upserting the workbook writes the dashboard **draft**. Publish it to make it | ||
| visible to viewers with [`POST /workbooks/{workbookId}/publish`][ref-publish], | ||
| using the workbook id returned in step 3. Publishing is itself idempotent per | ||
| workbook, so it is safe to run on every apply. |
There was a problem hiding this comment.
The publish step needs a request body, and the idempotency claim isn't sourced.
PublishDashboardInput in api-reference/api.yaml:7323-7348 has required: [workbookId] — so workbookId must be in the body, not just the path, and config / title / dashboardId / allowEmbed are all accepted there. As written ("using the workbook id returned in step 3") a reader will POST with an empty body and get a validation error.
Two asks:
- Show the minimal body (a one-line
-d '{"workbookId": <id>}'curl, or at least say the body carriesworkbookId). - Say what
configmeans at publish time — if omitting it publishes the current draft, that's the key sentence for an as-code pipeline and it's the whole reason step 3 → step 4 works.
Also, "Publishing is itself idempotent per workbook" is the one claim on this page I couldn't corroborate — publishDashboard has no x-mint description in api.yaml, unlike the two upsert operations. Worth confirming against the handler before shipping, since pipelines will run it unconditionally on every apply.
| ### 2. Upsert each report | ||
|
|
||
| For every report, [upsert it by `publicId`][ref-upsert-report]. If a report with | ||
| that `publicId` already exists in the deployment it is updated with the fields you | ||
| send (same semantics as [`PUT /reports/{reportId}`][ref-update-report]); | ||
| otherwise it is created with that `publicId`. |
There was a problem hiding this comment.
Ordering gap: a report belongs to a workbook, but the workbook doesn't exist yet at step 2.
CreateReportInput (api.yaml:4953) carries workbookId — a per-deployment numeric id. On a fresh deployment (the staging → production case this page is written for), step 2 runs before the workbook exists, so there is no workbookId to send, and the reports land unattached.
The page never resolves this. Either the order should be workbook-first (upsert the workbook by slug → get its numeric id → upsert reports with that workbookId → re-upsert the workbook with the rewritten dashboardDraft → publish), or the flow should mention PUT /reports/{reportId}/connect-workbook as the attach step. Worth stating explicitly whether dashboard widgets can reference reports that aren't attached to the workbook at all — if they can, say so and this concern goes away.
| ## Authenticating | ||
|
|
||
| These are public REST endpoints. Authenticate with a deployment API key exactly | ||
| as for the rest of the [REST API][ref-api] — see [Authentication][ref-auth] for | ||
| how to create a key and pass it. The examples below assume: | ||
|
|
||
| ```bash | ||
| export CUBE_API_URL="https://<your-cube-cloud-host>" | ||
| export CUBE_API_TOKEN="<your-api-key>" | ||
| export DEPLOYMENT_ID="<your-deployment-id>" | ||
| ``` |
There was a problem hiding this comment.
Three smaller things in this block:
- "deployment API key" — there's no such thing per
authentication.mdx:20-26: it's an account API key (or OAuth access token) that may optionally be scoped to deployments. Suggest "Authenticate with a Cube API key generated in your account settings; a key scoped to specific deployments must include the target deployment." - Base URL placeholder — the rest of the API reference uses
https://<tenant>.cubecloud.dev. Matching it here saves the reader a lookup. - "These are public REST endpoints" reads as unauthenticated on first pass, and then the next sentence says to authenticate. Since the point is just "not internal/preview API", either drop the sentence or say "part of the public [REST API][ref-api]".
| [workbooks][ref-workbooks], their [dashboards][ref-dashboards], and the | ||
| [reports][ref-reports] the dashboard widgets render — from source control instead | ||
| of only through the UI. You keep each asset's definition in Git and apply it to a | ||
| deployment with the [Cube Cloud REST API][ref-api], the same way you might manage |
There was a problem hiding this comment.
docs-mintlify/CLAUDE.md lists Cube Cloud as legacy naming that shouldn't appear in new content. Use "Cube REST API" here (and https://<tenant>.cubecloud.dev rather than <your-cube-cloud-host> at line 61 — the hostname itself is a literal, so it's fine, but the prose label isn't).
| deployment with the [Cube Cloud REST API][ref-api], the same way you might manage | |
| deployment with the [Cube REST API][ref-api], the same way you might manage |
| [ref-upsert-workbook]: /api-reference/workbooks/create-or-update-a-workbook-by-slug | ||
| [ref-upsert-report]: /api-reference/reports/create-or-update-a-report-by-publicid | ||
| [ref-create-report]: /api-reference/reports/create-a-report | ||
| [ref-update-report]: /api-reference/reports/update-a-report | ||
| [ref-get-report]: /api-reference/reports/get-report | ||
| [ref-get-workbook]: /api-reference/workbooks/get-workbook | ||
| [ref-publish]: /api-reference/workbooks/publish-dashboard |
There was a problem hiding this comment.
All seven targets correspond to real operations in api-reference/api.yaml and the slugs match the summary: values (Create or update a workbook by slug, Get report, Publish dashboard, …), so these should resolve.
One to spot-check on the Mintlify preview before merging: create-or-update-a-report-by-publicid. Every other slug here comes from an all-lowercase summary, but this one slugifies publicId — depending on whether Mintlify splits camelCase, the real page could be …-by-public-id. Worth one click on the preview link since it's the page's most important reference.
Dashboards as code guide (CUB-3521)
Documents the idempotent REST upsert API for managing workbooks, dashboards, and reports as code — Phase 1 of programmatic dashboards, shipped in cubedevinc/cubejs-enterprise#13361.
New page:
docs-mintlify/docs/explore-analyze/dashboards/dashboards-as-code.mdx, in the Dashboards nav group, with a discovery cross-link from the dashboards index.What the merged API is
Two public REST endpoints, keyed by a portable identifier you choose (so a CI/CD pipeline can re-apply the same definitions across deployments without tracking per-environment numeric ids):
PUT /deployments/{deploymentId}/workbooks/by-slug/{slug}— upsert a workbook (and its dashboard draft inmeta.dashboardDraft, merged into existing metadata)PUT /deployments/{deploymentId}/reports/by-public-id/{publicId}— upsert a report keyed by its account-unique, write-oncepublicIdSupporting changes in the same PR:
POST /reportsaccepts a client-suppliedpublicId;PUT /reports/{reportId}accepts a write-oncepublicId(so a UI-authored report can be adopted into as-code management).Covers
publicId) / workbook (slug) / dashboard (meta.dashboardDraft, made visible by publishing)GET→ upsert each report → upsert the workbook → publish409conflict cases distinguished bycode(upsert_branch_changed= retry; cross-deployment = permanent;ambiguous_legacy_id= permanent)publicIdrules: 12-char[0-9A-Za-z], write-once, client-suppliable; reserved placeholder shape rejected with400publicId/api-reference/{reports,workbooks}/…What changed from the first draft
The original draft documented a runtime-owned YAML-in-project model (dashboards as
cube.dev/dashboard/v1YAML in adashboards/directory, read at build time, rendered read-only behinduseRuntimeDashboards). That feature did not ship in #13361 — it's a different, unmerged design. This revision replaces that page wholesale with documentation of the REST upsert API that actually merged, and drops the "preview / do not merge" gating since these endpoints are shipped public API.Accuracy
Every fact checked against the merged source and the public OpenAPI spec (
open-api-spec-public-v3.1.yaml) — endpoint paths, request bodies (CreateReportInput/CreateWorkbookInput), the409codevalues,publicIdvalidation, and that dashboard-draft widgets reference reports by per-deployment numeric id (hence the export-then-rewrite step). The endpoint reference pages already exist inapi-reference/api.yamlon master; all internal links resolve.Linear: CUB-3521