@salesforce/afv-skills 1.59.0 → 1.60.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/skills/agentforce-architecture-analyze/SKILL.md +66 -17
- package/skills/agentforce-architecture-analyze/assets/cli/show_access_token.yaml +4 -7
- package/skills/agentforce-architecture-analyze/references/contract.json +1 -1
- package/skills/agentforce-architecture-analyze/scripts/emit_env.py +2 -2
- package/skills/agentforce-architecture-analyze/scripts/main.py +40 -44
- package/skills/agentforce-architecture-analyze/scripts/rest_client.py +11 -3
- package/skills/agentforce-architecture-analyze/scripts/sf_cli.py +77 -10
- package/skills/agentforce-architecture-analyze/scripts/tests/test_end_to_end_fixture.py +1 -0
- package/skills/agentforce-architecture-analyze/scripts/tests/test_flow_children_inflation.py +1 -0
- package/skills/agentforce-architecture-analyze/scripts/tests/test_main_pipeline.py +53 -2
- package/skills/agentforce-architecture-analyze/scripts/tests/test_resolve_creds.py +184 -36
- package/skills/agentforce-architecture-analyze/scripts/tests/test_rest_client.py +17 -0
- package/skills/agentforce-architecture-analyze/scripts/tests/test_sf_cli.py +165 -11
- package/skills/agentforce-architecture-analyze/scripts/tests/test_skill_md_pipeline_block.py +319 -0
- package/skills/agentforce-d360-analyze/SKILL.md +28 -10
- package/skills/agentforce-d360-analyze/scripts/dc.py +200 -50
- package/skills/agentforce-d360-analyze/scripts/discover_sessions.py +10 -6
- package/skills/agentforce-d360-analyze/scripts/fetch_dc.py +22 -6
- package/skills/agentforce-d360-analyze/scripts/resolve_session.py +21 -13
- package/skills/agentforce-d360-analyze/scripts/tests/test_dc_and_resolve_session.py +298 -76
- package/skills/agentforce-d360-analyze/scripts/tests/test_default_target_org.py +432 -0
- package/skills/agentforce-d360-analyze/scripts/tests/test_discover_sessions.py +1 -1
- package/skills/agentforce-d360-analyze/scripts/tests/test_resolve_session_main.py +9 -3
- package/skills/agentforce-d360-analyze/scripts/tests/test_skill_md_prefix_block.py +176 -0
- package/skills/consumer-goods-accruals-datakit-deploy/SKILL.md +17 -27
- package/skills/consumer-goods-accruals-datakit-deploy/scripts/setup.js +7 -17
- package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/00-check-prerequisites.js +47 -63
- package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/01-download-static-resource.js +10 -13
- package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/02-replace-org-id.js +46 -46
- package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/03-deploy-metadata.js +4 -21
- package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/06-create-dataspace.js +58 -82
- package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/07-deploy-accruals-reports.js +16 -315
- package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/08-deploy-ui.js +9 -24
- package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/09-completion.js +44 -44
- package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/utils.js +11 -63
- package/skills/consumer-goods-rtr-datacloud-export-configure/SKILL.md +2 -1
- package/skills/consumer-goods-tpe-dashboard-configure/SKILL.md +2 -1
- package/skills/consumer-goods-tpe-dashboard-custom-kpi-configure/SKILL.md +2 -1
- package/skills/consumer-goods-tpe-datakit-deploy/SKILL.md +2 -1
- package/skills/data360-schema-get/SKILL.md +3 -3
- package/skills/design-systems-slds-validate/references/report-format.md +1 -1
- package/skills/dx-org-manage/SKILL.md +1 -0
- package/skills/dx-org-shape-manage/SKILL.md +1 -0
- package/skills/dx-org-snapshot-manage/SKILL.md +172 -0
- package/skills/dx-org-snapshot-manage/examples/create_error_output.json +16 -0
- package/skills/dx-org-snapshot-manage/examples/create_success_output.json +19 -0
- package/skills/dx-org-snapshot-manage/examples/delete_output.json +9 -0
- package/skills/dx-org-snapshot-manage/examples/get_output.json +19 -0
- package/skills/dx-org-snapshot-manage/examples/list_output.json +36 -0
- package/skills/dx-org-snapshot-manage/references/cli_flags.md +179 -0
- package/skills/dx-org-snapshot-manage/references/snapshot_usage.md +55 -0
- package/skills/dx-org-trial-expiration-check/SKILL.md +1 -1
- package/skills/dx-org-trial-expiration-check/scripts/check_expiration.sh +2 -2
- package/skills/education-cloud-student-recruitment-agent-configure/SKILL.md +7 -5
- package/skills/education-cloud-student-recruitment-agent-configure/references/prerequisites.md +11 -11
- package/skills/experience-content-media-image-generate/SKILL.md +83 -0
- package/skills/experience-content-media-image-generate/scripts/download-image.sh +84 -0
- package/skills/external-diagram-mermaid-generate/SKILL.md +12 -12
- package/skills/field-service-data-capture-migrate/references/component-mapping.md +1 -1
- package/skills/field-service-data-capture-migrate/references/fsm-dc-capability-catalog.md +1 -2
- package/skills/field-service-data-capture-migrate/references/known-deploy-errors.md +1 -0
- package/skills/field-service-data-capture-migrate/references/transformer-rules.md +1 -1
- package/skills/field-service-data-capture-migrate/scripts/transform_flow.py +12 -1
- package/skills/field-service-data-capture-migrate/scripts/vendor/build_flow.py +4 -1
- package/skills/field-service-setup-orchestrator-get/SKILL.md +0 -1
- package/skills/insurance-brokerage-agency-billing-configure/SKILL.md +233 -0
- package/skills/insurance-brokerage-agency-billing-configure/references/accounting-setup.md +117 -0
- package/skills/insurance-brokerage-agency-billing-configure/references/billing-rules-config.md +237 -0
- package/skills/insurance-brokerage-agency-billing-configure/references/billing-settings.md +271 -0
- package/skills/insurance-brokerage-agency-billing-configure/references/demo-policy-config.md +138 -0
- package/skills/insurance-brokerage-agency-billing-configure/references/org-defaults.md +64 -0
- package/skills/insurance-brokerage-agency-billing-configure/references/permissions-config.md +226 -0
- package/skills/insurance-brokerage-agency-billing-configure/references/prerequisites.md +142 -0
- package/skills/life-sciences-fieldsalesrep-coordinate/SKILL.md +3 -2
- package/skills/life-sciences-fieldsalesrep-coordinate/references/stage-5-visit-creation-execution-state-and-recovery.md +3 -3
- package/skills/life-sciences-fieldsalesrep-coordinate/references/stage-5-visit-creation-overview.md +68 -45
- package/skills/life-sciences-fieldsalesrep-coordinate/references/stage-5-visit-creation-visit-creation-data.md +17 -18
- package/skills/life-sciences-kam-coordinate/references/stage-5-data-and-plan-templates-overview.md +16 -3
- package/skills/life-sciences-kam-coordinate/references/stage-5-data-creation-data.md +16 -13
- package/skills/marketing-message-test/SKILL.md +194 -0
- package/skills/marketing-message-test/references/api-contract.md +333 -0
- package/skills/omnistudio-callable-apex-generate/SKILL.md +3 -3
- package/skills/omnistudio-datamapper-generate/SKILL.md +6 -6
- package/skills/omnistudio-datapacks-deploy/SKILL.md +11 -11
- package/skills/omnistudio-dependencies-analyze/SKILL.md +5 -5
- package/skills/omnistudio-epc-catalog-generate/SKILL.md +6 -6
- package/skills/omnistudio-flexcard-generate/SKILL.md +6 -6
- package/skills/omnistudio-integration-procedure-generate/SKILL.md +6 -6
- package/skills/omnistudio-omniscript-generate/SKILL.md +6 -5
- package/skills/platform-apex-logs-debug/SKILL.md +1 -0
- package/skills/platform-metadata-deploy/SKILL.md +1 -0
- package/skills/platform-models-api-configure/SKILL.md +5 -5
- package/skills/platform-soql-query/SKILL.md +1 -0
- package/skills/service-itsm-agentic-setup-cmdb-coordinate/SKILL.md +17 -6
- package/skills/service-itsm-agentic-setup-incident-sla-configure/SKILL.md +107 -192
- package/skills/service-itsm-agentic-setup-incident-sla-configure/assets/attach-milestone-action.json +6 -6
- package/skills/service-itsm-agentic-setup-incident-sla-configure/examples/milestone-patterns.md +49 -20
- package/skills/service-itsm-agentic-setup-incident-sla-configure/examples/output-templates.md +4 -4
- package/skills/service-itsm-agentic-setup-incident-sla-configure/references/mcp-invocation.md +224 -290
- package/skills/service-itsm-agentic-setup-incident-sla-configure/references/workflow.md +295 -0
- package/skills/service-itsm-incident-priority-configure/SKILL.md +1 -1
- package/skills/service-itsm-slack-itservice-configure/SKILL.md +2 -1
- package/skills/service-itsm-slack-itservice-configure/references/manage-slack-connection.md +2 -2
- package/skills/service-omni-channel-setup-coordinate/SKILL.md +20 -13
- package/skills/service-omni-channel-setup-coordinate/scripts/integration-driver.sh +5 -55
- package/skills/service-omni-channel-setup-coordinate/scripts/tests/test_omni_contracts.py +0 -38
- package/skills/service-omni-command-center-analyze/SKILL.md +2 -3
- package/skills/service-omni-command-center-analyze/scripts/analyze.sh +3 -3
- package/skills/tableau-next-asset-create/README.md +65 -0
- package/skills/tableau-next-asset-create/SKILL.md +409 -0
- package/skills/tableau-next-asset-create/references/admin-users.md +149 -0
- package/skills/tableau-next-asset-create/references/ai-readiness.md +355 -0
- package/skills/tableau-next-asset-create/references/alerts.md +111 -0
- package/skills/tableau-next-asset-create/references/build-workflow.md +819 -0
- package/skills/tableau-next-asset-create/references/dashboard-authoring.md +302 -0
- package/skills/tableau-next-asset-create/references/dashboard-design-principles.md +352 -0
- package/skills/tableau-next-asset-create/references/data-transform-gotchas.md +210 -0
- package/skills/tableau-next-asset-create/references/data-understanding.md +387 -0
- package/skills/tableau-next-asset-create/references/deep-reference-index.md +74 -0
- package/skills/tableau-next-asset-create/references/destructive-operations.md +62 -0
- package/skills/tableau-next-asset-create/references/edit-dashboard.md +273 -0
- package/skills/tableau-next-asset-create/references/edit-visualization.md +529 -0
- package/skills/tableau-next-asset-create/references/empty-source-handling.md +104 -0
- package/skills/tableau-next-asset-create/references/ingest-and-metric-gotchas.md +638 -0
- package/skills/tableau-next-asset-create/references/insight-bundle.md +69 -0
- package/skills/tableau-next-asset-create/references/large-flat-file-handling.md +331 -0
- package/skills/tableau-next-asset-create/references/schemas/edit-dashboard/_envelope.json +54 -0
- package/skills/tableau-next-asset-create/references/schemas/edit-dashboard/_shared-widget-fields.json +122 -0
- package/skills/tableau-next-asset-create/references/schemas/edit-dashboard/add_page.json +29 -0
- package/skills/tableau-next-asset-create/references/schemas/edit-dashboard/delete_page.json +27 -0
- package/skills/tableau-next-asset-create/references/schemas/edit-dashboard/discard_dashboard.json +11 -0
- package/skills/tableau-next-asset-create/references/schemas/edit-dashboard/move_widget.json +30 -0
- package/skills/tableau-next-asset-create/references/schemas/edit-dashboard/move_widget_to_page.json +30 -0
- package/skills/tableau-next-asset-create/references/schemas/edit-dashboard/place_widget_on_page.json +30 -0
- package/skills/tableau-next-asset-create/references/schemas/edit-dashboard/remove_widget_from_dashboard.json +16 -0
- package/skills/tableau-next-asset-create/references/schemas/edit-dashboard/remove_widget_from_page.json +26 -0
- package/skills/tableau-next-asset-create/references/schemas/edit-dashboard/rename_page.json +28 -0
- package/skills/tableau-next-asset-create/references/schemas/edit-dashboard/save_dashboard.json +18 -0
- package/skills/tableau-next-asset-create/references/schemas/edit-dashboard/undo.json +11 -0
- package/skills/tableau-next-asset-create/references/schemas/edit-dashboard/upsert_button_widget.json +69 -0
- package/skills/tableau-next-asset-create/references/schemas/edit-dashboard/upsert_container_widget.json +88 -0
- package/skills/tableau-next-asset-create/references/schemas/edit-dashboard/upsert_filter_widget.json +146 -0
- package/skills/tableau-next-asset-create/references/schemas/edit-dashboard/upsert_metric_widget.json +78 -0
- package/skills/tableau-next-asset-create/references/schemas/edit-dashboard/upsert_navigation_widget.json +80 -0
- package/skills/tableau-next-asset-create/references/schemas/edit-dashboard/upsert_parameter_widget.json +160 -0
- package/skills/tableau-next-asset-create/references/schemas/edit-dashboard/upsert_text_widget.json +103 -0
- package/skills/tableau-next-asset-create/references/schemas/edit-dashboard/upsert_visualization_widget.json +68 -0
- package/skills/tableau-next-asset-create/references/sdm-tool-reference.md +721 -0
- package/skills/tableau-next-asset-create/references/semantic-query-and-enrichment.md +355 -0
- package/skills/tableau-next-asset-create/references/shared-gates.md +322 -0
- package/skills/tableau-next-asset-create/references/sharing-and-promotion.md +306 -0
- package/skills/tableau-next-asset-create/references/tasks/ai-readiness-audit.md +51 -0
- package/skills/tableau-next-asset-create/references/tasks/analyze-data.md +48 -0
- package/skills/tableau-next-asset-create/references/tasks/build-dashboard.md +66 -0
- package/skills/tableau-next-asset-create/references/tasks/build-end-to-end.md +73 -0
- package/skills/tableau-next-asset-create/references/tasks/create-data-transform.md +51 -0
- package/skills/tableau-next-asset-create/references/tasks/create-viz.md +58 -0
- package/skills/tableau-next-asset-create/references/tasks/edit-dashboard.md +80 -0
- package/skills/tableau-next-asset-create/references/tasks/edit-sdm.md +106 -0
- package/skills/tableau-next-asset-create/references/tasks/edit-viz.md +40 -0
- package/skills/tableau-next-asset-create/references/tasks/enrich-model.md +68 -0
- package/skills/tableau-next-asset-create/references/tasks/ingest-flat-file.md +68 -0
- package/skills/tableau-next-asset-create/references/tasks/manage-alert.md +35 -0
- package/skills/tableau-next-asset-create/references/tasks/profile-dataset.md +47 -0
- package/skills/tableau-next-asset-create/references/tasks/promote-or-reuse.md +46 -0
- package/skills/tableau-next-asset-create/references/tasks/provision-user.md +39 -0
- package/skills/tableau-next-asset-create/references/tasks/query-model.md +92 -0
- package/skills/tableau-next-asset-create/references/tasks/review-verified-questions.md +42 -0
- package/skills/tableau-next-asset-create/references/tasks/share-asset.md +37 -0
- package/skills/tableau-next-asset-create/references/verified-questions.md +125 -0
- package/skills/tableau-next-asset-create/references/viz-authoring.md +992 -0
- package/skills/service-itsm-agentic-setup-incident-sla-configure/assets/predefined-incident-policy.json +0 -102
|
@@ -0,0 +1,992 @@
|
|
|
1
|
+
# Visualization Authoring — Tool Guide & Best Practices
|
|
2
|
+
|
|
3
|
+
Load this for **Step 10** (create visualizations). Covers tool selection,
|
|
4
|
+
chart-type best practices mapped to the available MCP tools, field binding rules,
|
|
5
|
+
aggregation handling, naming conventions, and verified payloads.
|
|
6
|
+
|
|
7
|
+
**Best-practice principles** for dashboard design live in
|
|
8
|
+
`references/dashboard-design-principles.md`; this file maps the chart-level
|
|
9
|
+
mechanics to what the tools actually support. For chart types not currently
|
|
10
|
+
supported by the tools, see §9.
|
|
11
|
+
|
|
12
|
+
> **⚠ Single-tool surface (verified 2026-07-09):** The MCP server exposes one
|
|
13
|
+
> viz creation tool: `create_visualization`. The formerly-documented specialized
|
|
14
|
+
> tools (`create_timeseries_viz`, `create_comparative_viz`,
|
|
15
|
+
> `create_compositional_viz`, `create_relationship_viz`) are **not available**.
|
|
16
|
+
> All chart types are built via `create_visualization` with the appropriate
|
|
17
|
+
> `marks.panes.type` and `visualSpecification`. Payloads in §11 are verified
|
|
18
|
+
> against the live server.
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## Contents
|
|
23
|
+
|
|
24
|
+
- 1. Data Presence Gate (prerequisite — hard stop)
|
|
25
|
+
- 2. Chart Type Parameters
|
|
26
|
+
- 3. Chart Type Decision Matrix
|
|
27
|
+
- 4. Naming Conventions
|
|
28
|
+
- 5. Field Selection Priorities
|
|
29
|
+
- 6. Color Encoding and Breakdown
|
|
30
|
+
- 7. Aggregation Handling
|
|
31
|
+
- 8. Year-over-Year Period Overlay Pattern
|
|
32
|
+
- 9. Unsupported Chart Types — Fallbacks
|
|
33
|
+
- §10 — Filter Security
|
|
34
|
+
- 10. Field Reference Shape (`create_visualization`)
|
|
35
|
+
- 11. Verified Payloads (`create_visualization`)
|
|
36
|
+
- 12. `create_visualization` Encoding Reference
|
|
37
|
+
- 13. Quality Checklist
|
|
38
|
+
- 14. Filters — WHERE vs. HAVING
|
|
39
|
+
- 15. Forecasts (Timeseries Only)
|
|
40
|
+
- 16. Calculated-Measure Level → Function Pairing
|
|
41
|
+
- 17. Reference Line Knob
|
|
42
|
+
- 18. Common Errors
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## 1. Data Presence Gate (prerequisite — hard stop)
|
|
47
|
+
|
|
48
|
+
**Never create a visualization on a source with zero rows.**
|
|
49
|
+
|
|
50
|
+
This is Rule 1 from SKILL.md: field-richness is NOT data-presence. Before
|
|
51
|
+
calling any viz creation tool, confirm that Step 9 (`run_semantic_query`)
|
|
52
|
+
returned real rows. If the query returned empty → **STOP**. Do not call
|
|
53
|
+
`create_visualization`. Communicate clearly that the source is empty and offer
|
|
54
|
+
alternatives (different data source, different metric).
|
|
55
|
+
|
|
56
|
+
---
|
|
57
|
+
|
|
58
|
+
## 2. Chart Type Parameters
|
|
59
|
+
|
|
60
|
+
**One tool handles all chart types:** `create_visualization`. Control the
|
|
61
|
+
chart type via `marks.panes.type` and `visualSpecification.layout`:
|
|
62
|
+
|
|
63
|
+
| Chart Type / Pattern | `marks.panes.type` | `layout` | Notes |
|
|
64
|
+
|---|---|---|---|
|
|
65
|
+
| **Line / Time Series** | `Line` | `Vizql` | Date on columns with `DateTruncMonth`; measure on rows |
|
|
66
|
+
| **Multi-series Line** | `Line` | `Vizql` | As above + Color encoding on breakdown dimension |
|
|
67
|
+
| **Bar (comparative, sorted)** | `Bar` | `Vizql` | Dim on columns, measure on rows; set `sortIntent: "MeasureDescending"` |
|
|
68
|
+
| **Stacked Bar** | `Bar` | `Vizql` | As above + `stack: {isAutomatic: false, isStacked: true}` + Color on second dim |
|
|
69
|
+
| **Side-by-Side Bar** | `Bar` | `Vizql` | Dim on columns; `MeasureValues` on rows + `MeasureNames` on Color; see `measureValues` |
|
|
70
|
+
| **Donut / Pie** | `Donut` | `Radial` | Measure on **`Angle` encoding** (NOT `rows`); dimension on Color; use when dim has < 5 unique values |
|
|
71
|
+
| **Scatter / Correlation** | `Circle` | `Vizql` | X-measure on columns, Y-measure on rows; point dimension on Color |
|
|
72
|
+
| **Heatmap** | `Square` | `Vizql` | Two dims on rows/columns; measure on Color encoding |
|
|
73
|
+
| **Table** | `Text` | `Table` | Dims in `groups`; measures in `rows`; `marks.panes.type` is ignored |
|
|
74
|
+
|
|
75
|
+
> **Donut gotcha:** Set `layout: "Radial"`, empty `rows`/`columns`. The measure
|
|
76
|
+
> goes on the `Angle` encoding, the dimension on `Color` — NOT on `rows` or
|
|
77
|
+
> `columns`. If you put the measure on `rows` the call will fail or render
|
|
78
|
+
> incorrectly. See §11 for the verified payload.
|
|
79
|
+
|
|
80
|
+
---
|
|
81
|
+
|
|
82
|
+
## 3. Chart Type Decision Matrix
|
|
83
|
+
|
|
84
|
+
| Business Question | Data Pattern | Chart Type |
|
|
85
|
+
|---|---|---|
|
|
86
|
+
| "How are we trending over time?" | 1 Date + 1 Measure | Line |
|
|
87
|
+
| "How do trends compare across categories?" | 1 Date + 1 Measure + 1 Dimension | Multi-series Line |
|
|
88
|
+
| "Which categories perform best?" | 1 Dimension + 1 Measure | Bar (sorted descending) |
|
|
89
|
+
| "What's our composition?" (< 5 categories) | 1 Dimension (< 5 vals) + 1 Measure | Donut |
|
|
90
|
+
| "What's our composition?" (≥ 5 categories) | 1 Dimension (≥ 5 vals) + 1 Measure | Bar — more readable beyond 4 slices |
|
|
91
|
+
| "What's the breakdown by two dimensions?" | 2 Dimensions + 1 Measure | Stacked Bar |
|
|
92
|
+
| "How do multiple measures compare?" | 1 Dimension + 2+ Measures | Side-by-Side Bar |
|
|
93
|
+
| "Where are the performance hotspots?" | 2 Dimensions + 1 Measure | Heatmap |
|
|
94
|
+
| "What correlates with success?" | 2 Continuous Measures + Grouping | Scatter |
|
|
95
|
+
| "What are the detailed rankings?" | Multiple Dimensions + Measures | Table |
|
|
96
|
+
| "How do 2024 vs 2023 compare month by month?" | Period + Measure + Year | Line (year-over-year — see §8) |
|
|
97
|
+
| "Predict next quarter revenue" | 1 Continuous date + 1 Measure | Line + `visualSpecification.forecasts` (HoltWinters / RidgeRegression) |
|
|
98
|
+
|
|
99
|
+
**Priority rules:**
|
|
100
|
+
1. Use the decision matrix first: data pattern → tool
|
|
101
|
+
2. When multiple patterns fit, prefer the tool that requires fewer manual
|
|
102
|
+
encoding overrides
|
|
103
|
+
3. Prefer diverse chart types over defaulting to bar for every request
|
|
104
|
+
|
|
105
|
+
**Analysis-intent → chart (do this before picking marks):**
|
|
106
|
+
|
|
107
|
+
| User intent (example) | Analysis type | Tool guidance | §reference |
|
|
108
|
+
|---|---|---|---|
|
|
109
|
+
| "revenue over time" | trend | Line (date on columns) | §11 Time Series |
|
|
110
|
+
| "top categories by sales" | comparison | Bar (dim on columns, sorted) | §11 Bar |
|
|
111
|
+
| "market-share breakdown" | part-to-whole | Donut (< 5 vals) or Bar (≥ 5) | §11 Donut |
|
|
112
|
+
| "discount vs profit relationship" | correlation | Scatter (2 measures, 1 color dim) | §11 Scatter |
|
|
113
|
+
| "predict next quarter revenue" | forecast | Line + `forecasts` map (timeseries only) | §11 Time Series (forecast) |
|
|
114
|
+
|
|
115
|
+
**Forecast** is supported on timeseries **Line** charts via
|
|
116
|
+
`visualSpecification.forecasts` (API 262+): continuous `DateTrunc*` date on the
|
|
117
|
+
axis, measure to forecast, `HoltWinters` or `RidgeRegression`, horizon under
|
|
118
|
+
`to.forecastInterval`. Non-Line / non-timeseries forecasts fail with
|
|
119
|
+
`INVALID_INPUT`.
|
|
120
|
+
|
|
121
|
+
---
|
|
122
|
+
|
|
123
|
+
## 4. Naming Conventions
|
|
124
|
+
|
|
125
|
+
Chart titles are **user-facing**. Every chart must answer a business question
|
|
126
|
+
and have a clear, non-technical title (use SDM labels over technical names;
|
|
127
|
+
strip suffixes).
|
|
128
|
+
|
|
129
|
+
**Rules:**
|
|
130
|
+
1. Title must be ≥ 3 words describing what the chart answers.
|
|
131
|
+
2. Strip technical suffixes from display labels: `_Clc`, `_mtc`, `_MTC`, `_CLC`.
|
|
132
|
+
3. Never expose raw field API names or suffixed names (`Region4`, `Amount7`).
|
|
133
|
+
4. Make titles actionable: "Sales Performance by Region" not "Sales by Region".
|
|
134
|
+
5. The `name` (apiName) returned by the tool must be meaningful if you control
|
|
135
|
+
it (e.g. specify `title`); the generated name (`monthly_sales_trend_…`) is
|
|
136
|
+
acceptable for dashboard placement but label the chart clearly.
|
|
137
|
+
|
|
138
|
+
✅ **Good:** `"Sales Performance by Region"`, `"Monthly Revenue Trend"`,
|
|
139
|
+
`"Discount vs Profit by Product"`
|
|
140
|
+
|
|
141
|
+
❌ **Bad:** `"Viz_1"`, `"Account_Industry_2"`, `"Pipeline_Generation_Clc_Trend"`,
|
|
142
|
+
`"Chart"`, `"Region4 by Amount7"`
|
|
143
|
+
|
|
144
|
+
---
|
|
145
|
+
|
|
146
|
+
## 5. Field Selection Priorities
|
|
147
|
+
|
|
148
|
+
**Always select meaningful dimensions — never ID fields.** CLC fields rank
|
|
149
|
+
highest (calculated, business-ready); ID fields are excluded (thousands of
|
|
150
|
+
unique values destroy chart readability).
|
|
151
|
+
|
|
152
|
+
Priority order (highest to lowest):
|
|
153
|
+
1. **CLC fields** (`*_clc`, `*_Clc`) — calculated, business-ready
|
|
154
|
+
2. Industry, Type, Stage, Status, Region, Segment, Category, Group, Class, Tier,
|
|
155
|
+
Level, Phase
|
|
156
|
+
3. Country, State, Territory, Department, Division, Account, Opportunity,
|
|
157
|
+
Product, Service
|
|
158
|
+
4. Name fields (Account_Name, Product_Name, etc.)
|
|
159
|
+
5. **Avoid:** Description, Comment, Note, Detail, Text (generic catch-all fields)
|
|
160
|
+
6. **Never:** ID fields (`_id`, `_ids` suffix) — thousands of unique values;
|
|
161
|
+
destroys chart readability
|
|
162
|
+
|
|
163
|
+
**Cardinality thresholds:**
|
|
164
|
+
- ≤ 4 unique values → Donut acceptable
|
|
165
|
+
- 5–50 values → Bar chart preferred dimension
|
|
166
|
+
- 50+ values → Apply a Top-N limit, or group to a higher-level category
|
|
167
|
+
(e.g., use `Category` instead of `Product_Name` with 200 values)
|
|
168
|
+
|
|
169
|
+
---
|
|
170
|
+
|
|
171
|
+
## 6. Color Encoding and Breakdown
|
|
172
|
+
|
|
173
|
+
**Color encodes meaning, not decoration.** When a second dimension is available,
|
|
174
|
+
always add it as a `breakdown` or Color encoding — this creates multi-series or
|
|
175
|
+
stacked visuals automatically.
|
|
176
|
+
|
|
177
|
+
| Chart type | How to add a color / second dimension |
|
|
178
|
+
|---|---|
|
|
179
|
+
| **All chart types** | `{ "type": "Color", "fieldKey": "<dim_field_key>" }` in `marks.panes.encodings` |
|
|
180
|
+
| **YoY line** | Color fieldKey = date field with `function: "DatePartYear"`, `displayCategory: "Discrete"` |
|
|
181
|
+
| **Stacked bar** | Color fieldKey = second dimension; also set `stack: { isAutomatic: false, isStacked: true }` |
|
|
182
|
+
| **Donut** | Color fieldKey = the category dimension (same field as Angle slice) |
|
|
183
|
+
|
|
184
|
+
**Rule:** When 2+ dimensions are available for a bar or line chart, add the
|
|
185
|
+
second dimension as a breakdown/Color encoding. Color should reference a
|
|
186
|
+
specific field, not be a static palette applied without data binding.
|
|
187
|
+
|
|
188
|
+
**Color vs. Detail by cardinality.** Use `Color` for series with ≤10 distinct
|
|
189
|
+
values, `Detail` for higher cardinality.
|
|
190
|
+
|
|
191
|
+
**Default stacking:** Bar → stacked, others → not stacked. Override with
|
|
192
|
+
`marks.panes.stack: { isAutomatic: false, isStacked: <bool> }`.
|
|
193
|
+
|
|
194
|
+
**Cross-chart color coordination** (same category = same color across multiple
|
|
195
|
+
charts) is a dashboard-level concern, not a per-chart rule.
|
|
196
|
+
|
|
197
|
+
---
|
|
198
|
+
|
|
199
|
+
## 7. Aggregation Handling
|
|
200
|
+
|
|
201
|
+
### UserAgg for CLC / Calculated Fields
|
|
202
|
+
|
|
203
|
+
CLC fields (e.g., `Win_Rate_Clc`, `Avg_Deal_Size_Clc`) contain expressions like
|
|
204
|
+
`SUM([Field]) / COUNT([Field])` that aggregate at their own level.
|
|
205
|
+
**Never override UserAgg with Sum, Average, or any other function.**
|
|
206
|
+
|
|
207
|
+
```jsonc
|
|
208
|
+
"fields": {
|
|
209
|
+
"F_clc": { "fieldName": "Win_Rate_Clc", "function": "UserAgg", "displayCategory": "Continuous" }
|
|
210
|
+
// objectName omitted — calc measures never carry objectName
|
|
211
|
+
}
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
Overriding with `Sum` or `Average` causes double-aggregation → mathematically
|
|
215
|
+
incorrect results (e.g., `SUM(Average_Deal_Size)` is meaningless).
|
|
216
|
+
|
|
217
|
+
### Aggregation Selection by Analytical Question
|
|
218
|
+
|
|
219
|
+
| Question | Aggregation |
|
|
220
|
+
|---|---|
|
|
221
|
+
| "Total revenue / sum" | `Sum` |
|
|
222
|
+
| "Average deal size / mean" | `Avg` |
|
|
223
|
+
| "Number of records / count" | `Count` |
|
|
224
|
+
| "Unique customer count" | `CountD` |
|
|
225
|
+
| "Calculated rate / ratio (CLC field)" | `UserAgg` |
|
|
226
|
+
| "Highest / lowest value" | `Max` / `Min` |
|
|
227
|
+
|
|
228
|
+
### Pre-Aggregated Field Handling
|
|
229
|
+
|
|
230
|
+
Fields identified as pre-aggregated (marked `[AGG]` or using `usr:` derivation
|
|
231
|
+
in the semantic model) must not be re-aggregated. Set `function: "UserAgg"` on
|
|
232
|
+
the field's entry in the `fields` map and pass the field directly — the
|
|
233
|
+
aggregation is already embedded in the field expression.
|
|
234
|
+
|
|
235
|
+
---
|
|
236
|
+
|
|
237
|
+
## 8. Year-over-Year Period Overlay Pattern
|
|
238
|
+
|
|
239
|
+
**Always use period overlay, NOT a continuous date timeline, for YoY comparison.**
|
|
240
|
+
|
|
241
|
+
| Approach | X-axis | Color | Result |
|
|
242
|
+
|---|---|---|---|
|
|
243
|
+
| ✅ Period overlay (correct) | Month/Quarter (`DatePartMonth`) | Year dimension | Multiple aligned lines; seasonal patterns obvious |
|
|
244
|
+
| ❌ Continuous timeline (wrong) | Full date range (Jan 2023–Dec 2024) | — | Hard to compare same period across years |
|
|
245
|
+
|
|
246
|
+
**Why period overlay works:** Each year's January lands on the same X position
|
|
247
|
+
(month 1), making the same-period comparison direct and vertical rather than
|
|
248
|
+
requiring horizontal eye movement across a 2-year timeline.
|
|
249
|
+
|
|
250
|
+
**Implementation with `create_visualization`:**
|
|
251
|
+
|
|
252
|
+
Two date fields from the same date column — one with `DatePartMonth` (X-axis)
|
|
253
|
+
and one with `DatePartYear` (Color). No calculated dimension is needed; the
|
|
254
|
+
date part functions split the field into the two analytical roles inline:
|
|
255
|
+
|
|
256
|
+
```jsonc
|
|
257
|
+
{
|
|
258
|
+
"workspaceId": "1Dy…", "workspaceName": "my_workspace",
|
|
259
|
+
"semanticModelName": "My_SDM", "semanticModelLabel": "My SDM",
|
|
260
|
+
"fields": {
|
|
261
|
+
"F_month": {
|
|
262
|
+
"objectName": "Orders", "fieldName": "Order_Date",
|
|
263
|
+
"function": "DatePartMonth", "displayCategory": "Discrete",
|
|
264
|
+
"label": "Month"
|
|
265
|
+
},
|
|
266
|
+
"F_year": {
|
|
267
|
+
"objectName": "Orders", "fieldName": "Order_Date",
|
|
268
|
+
"function": "DatePartYear", "displayCategory": "Discrete",
|
|
269
|
+
"label": "Year"
|
|
270
|
+
},
|
|
271
|
+
"F_meas": {
|
|
272
|
+
"objectName": "Orders", "fieldName": "Sales",
|
|
273
|
+
"function": "Sum", "displayCategory": "Continuous",
|
|
274
|
+
"numberFormat": "Currency"
|
|
275
|
+
}
|
|
276
|
+
},
|
|
277
|
+
"visualSpecification": {
|
|
278
|
+
"rows": ["F_meas"],
|
|
279
|
+
"columns": ["F_month"],
|
|
280
|
+
"layout": "Vizql",
|
|
281
|
+
"marks": {
|
|
282
|
+
"panes": {
|
|
283
|
+
"type": "Line",
|
|
284
|
+
"encodings": [{ "fieldKey": "F_year", "type": "Color" }]
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
},
|
|
288
|
+
"title": "Monthly Sales: Year-over-Year Comparison"
|
|
289
|
+
}
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
The `DatePartYear` Color encoding creates one line per year, all sharing the
|
|
293
|
+
same 12-month X-axis.
|
|
294
|
+
|
|
295
|
+
---
|
|
296
|
+
|
|
297
|
+
## 9. Unsupported Chart Types — Fallbacks
|
|
298
|
+
|
|
299
|
+
The following types appear in the universal visualization best practices guide
|
|
300
|
+
but are **not supported by the current MCP tools**. Use the recommended
|
|
301
|
+
fallback and communicate the limitation to the user:
|
|
302
|
+
|
|
303
|
+
| Unsupported Type | Reason | Recommended Fallback |
|
|
304
|
+
|---|---|---|
|
|
305
|
+
| **Funnel Chart** | No funnel tool | `create_visualization (Bar marks)`, sorted by stage order; communicate each stage's count |
|
|
306
|
+
| **Dot Matrix** | No dot matrix tool | `create_visualization (Circle marks)` for 2 measures, or `create_visualization (Bar marks)` for ranking |
|
|
307
|
+
| **Map (Point / Geographic)** | No map tool; lat/lon not routed | `create_visualization (Bar marks)` by Region or Territory dimension |
|
|
308
|
+
| **Flow / Sankey** | No flow tool | `create_visualization (Bar marks)` showing source → destination volumes as two separate charts |
|
|
309
|
+
| **Nightingale Rose** | Radial layout supports Donut marks only | `create_visualization (Bar marks)` (horizontal bar) |
|
|
310
|
+
| **Spoke Bars (Radial)** | Not available in current tools | `create_visualization (Bar marks)` |
|
|
311
|
+
| **Radial Heatmap** | Not available | `create_visualization` Square-mark heatmap (2D grid) |
|
|
312
|
+
|
|
313
|
+
**When the user explicitly requests an unsupported type:** say so clearly —
|
|
314
|
+
"This chart type is not currently available. I'll use [fallback] instead, which
|
|
315
|
+
answers the same question with [benefit]." Do not silently substitute without
|
|
316
|
+
explaining.
|
|
317
|
+
|
|
318
|
+
**Note:** Funnel, Dot Matrix, Map, Flow/Sankey, Nightingale Rose, and Spoke Bar
|
|
319
|
+
chart types cannot be verified via MCP tool call trace with the current toolset.
|
|
320
|
+
Validate the fallback communication behavior for these instead.
|
|
321
|
+
|
|
322
|
+
---
|
|
323
|
+
|
|
324
|
+
## §10 — Filter Security
|
|
325
|
+
|
|
326
|
+
### Hidden Filters Are Not a Security Control
|
|
327
|
+
|
|
328
|
+
When a user asks to hide a filter, lock a filter to a specific value, or restrict
|
|
329
|
+
which data users see by hiding a filter dimension, issue this warning before
|
|
330
|
+
proceeding:
|
|
331
|
+
|
|
332
|
+
> **Warning:** Hidden filters in dashboards are not a security control. Any user
|
|
333
|
+
> with direct API or URL access to the underlying data source can bypass a hidden
|
|
334
|
+
> filter and retrieve the full dataset. Do not rely on hidden filters to enforce
|
|
335
|
+
> data access restrictions or row-level security.
|
|
336
|
+
>
|
|
337
|
+
> **Recommendation:** For genuine data access restrictions, implement row-level
|
|
338
|
+
> security at the data source level (e.g., data source permissions,
|
|
339
|
+
> entitlement-based filtering in the semantic model). Hidden filters are
|
|
340
|
+
> appropriate only for UI/UX convenience (e.g., pre-selecting a default value
|
|
341
|
+
> the user can still change).
|
|
342
|
+
|
|
343
|
+
This warning applies when the user's intent is access restriction — not when they
|
|
344
|
+
simply want a pre-set default filter value that the end user can still override.
|
|
345
|
+
This applies regardless of whether the user says the restriction is "just for
|
|
346
|
+
convenience."
|
|
347
|
+
|
|
348
|
+
---
|
|
349
|
+
|
|
350
|
+
## 10. Field Reference Shape (`create_visualization`)
|
|
351
|
+
|
|
352
|
+
`create_visualization` uses a **`fields` map + `visualSpecification`** format —
|
|
353
|
+
NOT the `fieldRef` / `measureFunction` shape from older specialized tools.
|
|
354
|
+
|
|
355
|
+
```jsonc
|
|
356
|
+
"fields": {
|
|
357
|
+
"<fieldKey>": {
|
|
358
|
+
"objectName": "<SDM data-object apiName>", // OMIT for model-level calc measures
|
|
359
|
+
"fieldName": "<field apiName on that object>",
|
|
360
|
+
"displayCategory": "Discrete | Continuous",
|
|
361
|
+
"function": "<aggregation or date function>", // optional
|
|
362
|
+
"level": "Row | AggregateFunction | TableCalc | Lod", // required when calc + function
|
|
363
|
+
"label": "<display label override>", // optional
|
|
364
|
+
"numberFormat": "Currency | Percent | Abbreviated | Precise", // optional, measures only
|
|
365
|
+
"referenceLine": "Average | Sum | Min | Max | Target", // optional, measures only — see §17
|
|
366
|
+
"referenceLineValue": 1000000, // required iff referenceLine=Target
|
|
367
|
+
"colorScale": "Diverging" // optional, Color-encoded measure — see §11 Heatmap
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
**Server hydrates the rest — do NOT send these unless overriding:** `type:
|
|
373
|
+
"Field"` (auto-filled) and the field's inferred `role` (Measure when any
|
|
374
|
+
aggregation `function` is set, else Dimension); and at the spec level
|
|
375
|
+
`marks.headers` (default Text mark), `marks.fields` (one per continuous-axis
|
|
376
|
+
fieldKey on rows/columns), `legends` (auto-derived from Color encodings),
|
|
377
|
+
`referenceLines` (`{}`), `measureValues` (`[]`), `style` (full default
|
|
378
|
+
scaffold), `view` (empty), and `interactions` (`[]`).
|
|
379
|
+
|
|
380
|
+
**Critical rules:**
|
|
381
|
+
- `workspaceId` — workspace Id (e.g. `0Za...`). Get from `list_workspaces`
|
|
382
|
+
(`id` field). `workspaceName` — workspace API name (no spaces). Get from
|
|
383
|
+
`list_workspaces` (`name` field). **Do NOT pass the label-with-spaces.**
|
|
384
|
+
- `objectName` is the **SDM data-object apiName** (e.g., `Orders`, `Products`),
|
|
385
|
+
NOT the underlying DLO/DMO table name (`orders__dll`, `Orders__dlm`).
|
|
386
|
+
- `fieldName` is the field's apiName on that SDM object — use the **suffixed**
|
|
387
|
+
apiName for bulk-imported fields (e.g., `Category3` not `Category`; see
|
|
388
|
+
`shared-gates.md` G3 / `sdm-tool-reference.md` for the suffix read-back rule).
|
|
389
|
+
- **Calculated measures: omit `objectName`** entirely. Including `objectName`
|
|
390
|
+
for a calculated measure causes an `UNKNOWN_EXCEPTION`. When supplying
|
|
391
|
+
`function` on a calc, also set `level` from
|
|
392
|
+
`list_semantic_model_calculated_measures` (`UserAgg` only for
|
|
393
|
+
`AggregateFunction` / `TableCalc`).
|
|
394
|
+
- `displayCategory: "Continuous"` → measures and `DateTrunc*` date functions.
|
|
395
|
+
- `displayCategory: "Discrete"` → plain dimensions and `DatePart*` date functions.
|
|
396
|
+
- FieldKeys you define in `fields` must **exactly match** the strings used in
|
|
397
|
+
`visualSpecification.rows`, `columns`, and encoding `fieldKey` values
|
|
398
|
+
(these are the generate-viz field pointers).
|
|
399
|
+
- The tool returns the saved visualization's identifying triple (`id`, `label`,
|
|
400
|
+
`name`). **Capture `id` and `name`** for `edit_dashboard`'s
|
|
401
|
+
`upsert_visualization_widget`.
|
|
402
|
+
To inspect the full hydrated body (style, legends, marks.fields), GET the
|
|
403
|
+
visualization by id with `get_visualization`.
|
|
404
|
+
|
|
405
|
+
**`function` values:**
|
|
406
|
+
- Aggregations: `Sum`, `Avg`, `Median`, `Count`, `CountD`, `Min`, `Max`,
|
|
407
|
+
`UserAgg` (`UserAgg` only for model-level calc measures at
|
|
408
|
+
`AggregateFunction` / `TableCalc`)
|
|
409
|
+
- Continuous date trunc: `DateTruncYear`, `DateTruncQuarter`, `DateTruncMonth`,
|
|
410
|
+
`DateTruncWeek`, `DateTruncDay` (plus `FiscalDateTrunc*` variants)
|
|
411
|
+
- Discrete date part: `DatePartYear`, `DatePartQuarter`, `DatePartMonth`,
|
|
412
|
+
`DatePartWeek`, `DatePartDay`
|
|
413
|
+
- Fiscal-calendar variants (anchored to the org's fiscal year, no `Day`
|
|
414
|
+
granularity): `FiscalDateTruncYear/Quarter/Month/Week` (Continuous),
|
|
415
|
+
`FiscalDatePartYear/Quarter/Month/Week` (Discrete) — use only when the
|
|
416
|
+
request says "fiscal" explicitly; "this quarter" ≠ "this fiscal quarter".
|
|
417
|
+
- Plain dimensions: omit `function`
|
|
418
|
+
|
|
419
|
+
---
|
|
420
|
+
|
|
421
|
+
## 11. Verified Payloads (`create_visualization`)
|
|
422
|
+
|
|
423
|
+
All calls share: `workspaceId`, `workspaceName`, `semanticModelName`, `semanticModelLabel`, `label` (the viz display title).
|
|
424
|
+
|
|
425
|
+
### Time Series (Line chart)
|
|
426
|
+
|
|
427
|
+
```jsonc
|
|
428
|
+
{
|
|
429
|
+
"workspaceId": "1Dy…", "workspaceName": "my_workspace",
|
|
430
|
+
"semanticModelName": "My_SDM", "semanticModelLabel": "My SDM",
|
|
431
|
+
"label": "Monthly Sales Trend",
|
|
432
|
+
"fields": {
|
|
433
|
+
"F_date": {
|
|
434
|
+
"objectName": "Orders", "fieldName": "Order_Date",
|
|
435
|
+
"function": "DateTruncMonth", "displayCategory": "Continuous"
|
|
436
|
+
},
|
|
437
|
+
"F_meas": {
|
|
438
|
+
"objectName": "Orders", "fieldName": "Sales",
|
|
439
|
+
"function": "Sum", "displayCategory": "Continuous",
|
|
440
|
+
"numberFormat": "Currency"
|
|
441
|
+
}
|
|
442
|
+
},
|
|
443
|
+
"visualSpecification": {
|
|
444
|
+
"rows": ["F_meas"],
|
|
445
|
+
"columns": ["F_date"],
|
|
446
|
+
"layout": "Vizql",
|
|
447
|
+
"marks": { "panes": { "type": "Line" } }
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
```
|
|
451
|
+
|
|
452
|
+
With multi-series breakdown (Color = second dimension):
|
|
453
|
+
```jsonc
|
|
454
|
+
{
|
|
455
|
+
"fields": {
|
|
456
|
+
"F_date": { "objectName": "Orders", "fieldName": "Order_Date", "function": "DateTruncMonth", "displayCategory": "Continuous" },
|
|
457
|
+
"F_meas": { "objectName": "Orders", "fieldName": "Sales", "function": "Sum", "displayCategory": "Continuous" },
|
|
458
|
+
"F_dim": { "objectName": "Orders", "fieldName": "Region", "displayCategory": "Discrete" }
|
|
459
|
+
},
|
|
460
|
+
"visualSpecification": {
|
|
461
|
+
"rows": ["F_meas"], "columns": ["F_date"], "layout": "Vizql",
|
|
462
|
+
"marks": {
|
|
463
|
+
"panes": {
|
|
464
|
+
"type": "Line",
|
|
465
|
+
"encodings": [{ "fieldKey": "F_dim", "type": "Color" }]
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
},
|
|
469
|
+
"label": "Monthly Sales Trend by Region"
|
|
470
|
+
}
|
|
471
|
+
```
|
|
472
|
+
|
|
473
|
+
With year-over-year period overlay:
|
|
474
|
+
```jsonc
|
|
475
|
+
{
|
|
476
|
+
"fields": {
|
|
477
|
+
"F_month": { "objectName": "Orders", "fieldName": "Order_Date", "function": "DatePartMonth", "displayCategory": "Discrete", "label": "Month" },
|
|
478
|
+
"F_year": { "objectName": "Orders", "fieldName": "Order_Date", "function": "DatePartYear", "displayCategory": "Discrete", "label": "Year" },
|
|
479
|
+
"F_meas": { "objectName": "Orders", "fieldName": "Sales", "function": "Sum", "displayCategory": "Continuous", "numberFormat": "Currency" }
|
|
480
|
+
},
|
|
481
|
+
"visualSpecification": {
|
|
482
|
+
"rows": ["F_meas"], "columns": ["F_month"], "layout": "Vizql",
|
|
483
|
+
"marks": {
|
|
484
|
+
"panes": {
|
|
485
|
+
"type": "Line",
|
|
486
|
+
"encodings": [{ "fieldKey": "F_year", "type": "Color" }]
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
},
|
|
490
|
+
"label": "Monthly Sales: Year-over-Year Comparison"
|
|
491
|
+
}
|
|
492
|
+
```
|
|
493
|
+
|
|
494
|
+
With forecast (HoltWinters, 6-month horizon) — fieldKeys are pointers into
|
|
495
|
+
`fields`; both `dateDim.fieldKey` and `measureFieldKey` must match:
|
|
496
|
+
```jsonc
|
|
497
|
+
{
|
|
498
|
+
"label": "Sales Forecast — Next 6 Months",
|
|
499
|
+
"fields": {
|
|
500
|
+
"F_date": {
|
|
501
|
+
"objectName": "Orders", "fieldName": "Order_Date",
|
|
502
|
+
"function": "DateTruncMonth", "displayCategory": "Continuous"
|
|
503
|
+
},
|
|
504
|
+
"F_meas": {
|
|
505
|
+
"objectName": "Orders", "fieldName": "Sales",
|
|
506
|
+
"function": "Sum", "displayCategory": "Continuous",
|
|
507
|
+
"numberFormat": "Currency"
|
|
508
|
+
}
|
|
509
|
+
},
|
|
510
|
+
"visualSpecification": {
|
|
511
|
+
"rows": ["F_meas"],
|
|
512
|
+
"columns": ["F_date"],
|
|
513
|
+
"layout": "Vizql",
|
|
514
|
+
"marks": { "panes": { "type": "Line" } },
|
|
515
|
+
"forecasts": {
|
|
516
|
+
"FC1": {
|
|
517
|
+
"forecastModel": "HoltWinters",
|
|
518
|
+
"dimensionFields": [
|
|
519
|
+
{ "dateDim": { "fieldKey": "F_date", "granularity": "InferFromData" } }
|
|
520
|
+
],
|
|
521
|
+
"measureFieldKey": "F_meas",
|
|
522
|
+
"from": { "predefinedDate": "LatestData" },
|
|
523
|
+
"to": { "forecastInterval": { "value": 6, "timeUnit": "Months" } },
|
|
524
|
+
"includeBounds": true,
|
|
525
|
+
"confidenceLevel": 95
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
```
|
|
531
|
+
|
|
532
|
+
---
|
|
533
|
+
|
|
534
|
+
### Bar / Comparative (sorted descending)
|
|
535
|
+
|
|
536
|
+
```jsonc
|
|
537
|
+
{
|
|
538
|
+
"label": "Top Products by Revenue",
|
|
539
|
+
"fields": {
|
|
540
|
+
"F_dim": { "objectName": "Products", "fieldName": "Product_Name", "displayCategory": "Discrete" },
|
|
541
|
+
"F_meas": { "objectName": "Orders", "fieldName": "Sales", "function": "Sum", "displayCategory": "Continuous", "numberFormat": "Currency" }
|
|
542
|
+
},
|
|
543
|
+
"visualSpecification": {
|
|
544
|
+
"rows": ["F_meas"], "columns": ["F_dim"], "layout": "Vizql",
|
|
545
|
+
"marks": { "panes": { "type": "Bar" } }
|
|
546
|
+
},
|
|
547
|
+
"sortIntent": "MeasureDescending"
|
|
548
|
+
}
|
|
549
|
+
```
|
|
550
|
+
|
|
551
|
+
With Color on a second dimension (auto-adds color encoding):
|
|
552
|
+
```jsonc
|
|
553
|
+
{
|
|
554
|
+
"label": "Revenue by Region and Category",
|
|
555
|
+
"fields": {
|
|
556
|
+
"F_dim1": { "objectName": "Orders", "fieldName": "Region", "displayCategory": "Discrete" },
|
|
557
|
+
"F_dim2": { "objectName": "Products", "fieldName": "Category3", "displayCategory": "Discrete" },
|
|
558
|
+
"F_meas": { "objectName": "Orders", "fieldName": "Sales", "function": "Sum", "displayCategory": "Continuous" }
|
|
559
|
+
},
|
|
560
|
+
"visualSpecification": {
|
|
561
|
+
"rows": ["F_meas"], "columns": ["F_dim1"], "layout": "Vizql",
|
|
562
|
+
"marks": {
|
|
563
|
+
"panes": {
|
|
564
|
+
"type": "Bar",
|
|
565
|
+
"encodings": [{ "fieldKey": "F_dim2", "type": "Color" }]
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
},
|
|
569
|
+
"sortIntent": "MeasureDescending"
|
|
570
|
+
}
|
|
571
|
+
```
|
|
572
|
+
|
|
573
|
+
With a CLC calculated measure (omit `objectName`):
|
|
574
|
+
```jsonc
|
|
575
|
+
{
|
|
576
|
+
"label": "Win Rate by Stage",
|
|
577
|
+
"fields": {
|
|
578
|
+
"F_clc": { "fieldName": "Win_Rate_Clc", "function": "UserAgg", "displayCategory": "Continuous" },
|
|
579
|
+
"F_dim": { "objectName": "Opportunities", "fieldName": "Stage8", "displayCategory": "Discrete" }
|
|
580
|
+
},
|
|
581
|
+
"visualSpecification": {
|
|
582
|
+
"rows": ["F_clc"], "columns": ["F_dim"], "layout": "Vizql",
|
|
583
|
+
"marks": { "panes": { "type": "Bar" } }
|
|
584
|
+
},
|
|
585
|
+
"sortIntent": "MeasureDescending"
|
|
586
|
+
}
|
|
587
|
+
```
|
|
588
|
+
|
|
589
|
+
---
|
|
590
|
+
|
|
591
|
+
### Stacked Bar (2 dimensions + 1 measure)
|
|
592
|
+
|
|
593
|
+
```jsonc
|
|
594
|
+
{
|
|
595
|
+
"label": "Revenue by Region and Category",
|
|
596
|
+
"fields": {
|
|
597
|
+
"F_dim1": { "objectName": "Orders", "fieldName": "Region", "displayCategory": "Discrete" },
|
|
598
|
+
"F_dim2": { "objectName": "Products", "fieldName": "Category3", "displayCategory": "Discrete" },
|
|
599
|
+
"F_meas": { "objectName": "Orders", "fieldName": "Sales", "function": "Sum", "displayCategory": "Continuous" }
|
|
600
|
+
},
|
|
601
|
+
"visualSpecification": {
|
|
602
|
+
"rows": ["F_meas"], "columns": ["F_dim1"], "layout": "Vizql",
|
|
603
|
+
"marks": {
|
|
604
|
+
"panes": {
|
|
605
|
+
"type": "Bar",
|
|
606
|
+
"encodings": [{ "fieldKey": "F_dim2", "type": "Color" }],
|
|
607
|
+
"stack": { "isAutomatic": false, "isStacked": true }
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
},
|
|
611
|
+
"sortIntent": "MeasureDescending"
|
|
612
|
+
}
|
|
613
|
+
```
|
|
614
|
+
|
|
615
|
+
---
|
|
616
|
+
|
|
617
|
+
### Side-by-Side Bar (1 dimension + 2+ measures)
|
|
618
|
+
|
|
619
|
+
Uses `MeasureNames` / `MeasureValues` synthetic fields. Put `MeasureValues` on
|
|
620
|
+
rows, `MeasureNames` on Color. List the real measure fieldKeys in `measureValues`.
|
|
621
|
+
|
|
622
|
+
> **Required:** `displayCategory` is **not optional** for synthetic fields.
|
|
623
|
+
> Omitting it causes an `UNKNOWN_EXCEPTION` (HTTP 500). Always set:
|
|
624
|
+
> - `MeasureNames` → `"displayCategory": "Discrete"`
|
|
625
|
+
> - `MeasureValues` → `"displayCategory": "Continuous"`
|
|
626
|
+
|
|
627
|
+
```jsonc
|
|
628
|
+
{
|
|
629
|
+
"label": "Revenue and Units by Region",
|
|
630
|
+
"fields": {
|
|
631
|
+
"F_dim": { "objectName": "Orders", "fieldName": "Region", "displayCategory": "Discrete" },
|
|
632
|
+
"F_mv": { "type": "MeasureValues", "displayCategory": "Continuous" },
|
|
633
|
+
"F_mn": { "type": "MeasureNames", "displayCategory": "Discrete" },
|
|
634
|
+
"F_rev": { "objectName": "Orders", "fieldName": "Sales", "function": "Sum", "displayCategory": "Continuous", "numberFormat": "Currency" },
|
|
635
|
+
"F_units":{ "objectName": "Orders", "fieldName": "Quantity", "function": "Sum", "displayCategory": "Continuous" }
|
|
636
|
+
},
|
|
637
|
+
"visualSpecification": {
|
|
638
|
+
"rows": ["F_mv"], "columns": ["F_dim"], "layout": "Vizql",
|
|
639
|
+
"measureValues": ["F_rev", "F_units"],
|
|
640
|
+
"marks": {
|
|
641
|
+
"panes": {
|
|
642
|
+
"type": "Bar",
|
|
643
|
+
"encodings": [{ "fieldKey": "F_mn", "type": "Color" }]
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
```
|
|
649
|
+
|
|
650
|
+
---
|
|
651
|
+
|
|
652
|
+
### Multi-Measure (direct shelf listing — separate adjacent axes)
|
|
653
|
+
|
|
654
|
+
The simpler multi-measure form: just list two (or more) measure fieldKeys on
|
|
655
|
+
one shelf — no `MeasureNames`/`MeasureValues` needed. This renders the measures
|
|
656
|
+
as **separate adjacent axes** (distinct from the shared-axis Side-by-Side Bar
|
|
657
|
+
above, which pivots them onto one axis via the measure-names construct). Works
|
|
658
|
+
for Bar and Line.
|
|
659
|
+
|
|
660
|
+
Multi-measure bar (both measures in `columns`):
|
|
661
|
+
```jsonc
|
|
662
|
+
{
|
|
663
|
+
"label": "Sales and Profit by Category",
|
|
664
|
+
"fields": {
|
|
665
|
+
"F_dim": { "objectName": "Orders", "fieldName": "Category", "displayCategory": "Discrete" },
|
|
666
|
+
"F_sales": { "objectName": "Orders", "fieldName": "Sales", "function": "Sum", "displayCategory": "Continuous" },
|
|
667
|
+
"F_profit":{ "objectName": "Orders", "fieldName": "Profit", "function": "Sum", "displayCategory": "Continuous" }
|
|
668
|
+
},
|
|
669
|
+
"visualSpecification": {
|
|
670
|
+
"rows": ["F_dim"], "columns": ["F_sales", "F_profit"], "layout": "Vizql",
|
|
671
|
+
"marks": { "panes": { "type": "Bar" } }
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
```
|
|
675
|
+
|
|
676
|
+
Multi-measure line (two lines sharing the date axis — both measures in `columns`):
|
|
677
|
+
```jsonc
|
|
678
|
+
{
|
|
679
|
+
"label": "Sales and Profit over Time",
|
|
680
|
+
"fields": {
|
|
681
|
+
"F_date": { "objectName": "Orders", "fieldName": "Order_Date", "function": "DateTruncMonth", "displayCategory": "Continuous" },
|
|
682
|
+
"F_sales": { "objectName": "Orders", "fieldName": "Sales", "function": "Sum", "displayCategory": "Continuous" },
|
|
683
|
+
"F_profit":{ "objectName": "Orders", "fieldName": "Profit", "function": "Sum", "displayCategory": "Continuous" }
|
|
684
|
+
},
|
|
685
|
+
"visualSpecification": {
|
|
686
|
+
"rows": ["F_date"], "columns": ["F_sales", "F_profit"], "layout": "Vizql",
|
|
687
|
+
"marks": { "panes": { "type": "Line" } }
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
```
|
|
691
|
+
|
|
692
|
+
Prefer this direct form for a quick side-by-side of a few measures; use the
|
|
693
|
+
`MeasureNames`/`MeasureValues` Side-by-Side Bar above when the measures should
|
|
694
|
+
share a single axis.
|
|
695
|
+
|
|
696
|
+
---
|
|
697
|
+
|
|
698
|
+
### Donut / Compositional (Radial layout)
|
|
699
|
+
|
|
700
|
+
> **Gotcha:** Set `layout: "Radial"`, empty `rows`/`columns`. Place the measure
|
|
701
|
+
> on the **`Angle` encoding**, NOT on `rows`. Putting a measure on `rows` with
|
|
702
|
+
> Radial layout causes the call to fail or render incorrectly. Verified working
|
|
703
|
+
> shape:
|
|
704
|
+
|
|
705
|
+
```jsonc
|
|
706
|
+
{
|
|
707
|
+
"label": "Revenue Distribution by Region",
|
|
708
|
+
"fields": {
|
|
709
|
+
"F_meas": { "objectName": "Orders", "fieldName": "Sales", "function": "Sum", "displayCategory": "Continuous" },
|
|
710
|
+
"F_dim": { "objectName": "Orders", "fieldName": "Region", "displayCategory": "Discrete" }
|
|
711
|
+
},
|
|
712
|
+
"visualSpecification": {
|
|
713
|
+
"layout": "Radial",
|
|
714
|
+
"rows": [],
|
|
715
|
+
"columns": [],
|
|
716
|
+
"marks": {
|
|
717
|
+
"panes": {
|
|
718
|
+
"type": "Donut",
|
|
719
|
+
"encodings": [
|
|
720
|
+
{ "fieldKey": "F_meas", "type": "Angle" },
|
|
721
|
+
{ "fieldKey": "F_dim", "type": "Color" }
|
|
722
|
+
]
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
```
|
|
728
|
+
|
|
729
|
+
Use when dimension has **< 5 unique values**. For ≥ 5 values, use a Bar chart —
|
|
730
|
+
donut slices become unreadable beyond 4.
|
|
731
|
+
|
|
732
|
+
#### Part-to-whole / Donut — field-binding gotchas (`create_visualization`)
|
|
733
|
+
|
|
734
|
+
The Donut payload above is the current unified `create_visualization` shape
|
|
735
|
+
(`type: "Donut"`, `layout: "Radial"`). Older scripts may still name the retired
|
|
736
|
+
`create_compositional_viz` tool — that tool is **not** on the pilot MCP; always
|
|
737
|
+
use `create_visualization`.
|
|
738
|
+
|
|
739
|
+
**`objectName` / `level` for measures (matches generate-viz / IR-v2):**
|
|
740
|
+
|
|
741
|
+
- **Raw measure** (from `list_semantic_model_measures`): supply BOTH
|
|
742
|
+
`objectName` (SDM data-object apiName) and `fieldName`.
|
|
743
|
+
```jsonc
|
|
744
|
+
"F_meas": { "objectName": "Orders", "fieldName": "Sales", "function": "Sum", "displayCategory": "Continuous" }
|
|
745
|
+
```
|
|
746
|
+
- **Model-level calculated measure** (from
|
|
747
|
+
`list_semantic_model_calculated_measures`): supply ONLY `fieldName` — **omit
|
|
748
|
+
`objectName`**. Including `objectName` yields `UNKNOWN_EXCEPTION`. Copy the
|
|
749
|
+
calc's `level` from the list call whenever you set `function`:
|
|
750
|
+
- `AggregateFunction` / `TableCalc` → `function: "UserAgg"`
|
|
751
|
+
- `Row` / `Lod` → ordinary agg (`Avg` for Average, `CountD` for CountDistinct);
|
|
752
|
+
do **not** use `UserAgg`
|
|
753
|
+
```jsonc
|
|
754
|
+
"F_meas": { "fieldName": "Profit_Margin", "level": "AggregateFunction", "function": "UserAgg", "displayCategory": "Continuous" }
|
|
755
|
+
```
|
|
756
|
+
|
|
757
|
+
**FieldKeys are pointers:** every `rows` / `columns` / encoding `fieldKey` must
|
|
758
|
+
exactly match a key in the `fields` map — mismatches return
|
|
759
|
+
`INVALID_VISUALIZATION_METADATA`.
|
|
760
|
+
|
|
761
|
+
Validate dimensions/measures via `list_semantic_model_*` **before** calling
|
|
762
|
+
`create_visualization`. Donut is donut-only (no pie / treemap); for ≥ 5 unique
|
|
763
|
+
dimension values prefer §11 Bar.
|
|
764
|
+
|
|
765
|
+
---
|
|
766
|
+
|
|
767
|
+
### Scatter / Correlation (Circle marks)
|
|
768
|
+
|
|
769
|
+
```jsonc
|
|
770
|
+
{
|
|
771
|
+
"label": "Discount vs Profit by Product",
|
|
772
|
+
"fields": {
|
|
773
|
+
"F_x": { "objectName": "Orders", "fieldName": "Discount", "function": "Avg", "displayCategory": "Continuous" },
|
|
774
|
+
"F_y": { "objectName": "Orders", "fieldName": "Profit", "function": "Sum", "displayCategory": "Continuous" },
|
|
775
|
+
"F_dim": { "objectName": "Products", "fieldName": "Product_Name", "displayCategory": "Discrete" }
|
|
776
|
+
},
|
|
777
|
+
"visualSpecification": {
|
|
778
|
+
"rows": ["F_y"], "columns": ["F_x"], "layout": "Vizql",
|
|
779
|
+
"marks": {
|
|
780
|
+
"panes": {
|
|
781
|
+
"type": "Circle",
|
|
782
|
+
"encodings": [{ "fieldKey": "F_dim", "type": "Color" }]
|
|
783
|
+
}
|
|
784
|
+
}
|
|
785
|
+
}
|
|
786
|
+
}
|
|
787
|
+
```
|
|
788
|
+
|
|
789
|
+
---
|
|
790
|
+
|
|
791
|
+
### Heatmap (Square marks + Color(measure))
|
|
792
|
+
|
|
793
|
+
```jsonc
|
|
794
|
+
{
|
|
795
|
+
"label": "Sales Performance by Region and Quarter",
|
|
796
|
+
"fields": {
|
|
797
|
+
"F_dim1": { "objectName": "Orders", "fieldName": "Region", "displayCategory": "Discrete" },
|
|
798
|
+
"F_dim2": { "objectName": "Orders", "fieldName": "Quarter3", "displayCategory": "Discrete" },
|
|
799
|
+
"F_meas": { "objectName": "Orders", "fieldName": "Sales", "function": "Sum", "displayCategory": "Continuous" }
|
|
800
|
+
},
|
|
801
|
+
"visualSpecification": {
|
|
802
|
+
"rows": ["F_dim1"], "columns": ["F_dim2"], "layout": "Vizql",
|
|
803
|
+
"marks": {
|
|
804
|
+
"panes": {
|
|
805
|
+
"type": "Square",
|
|
806
|
+
"encodings": [{ "fieldKey": "F_meas", "type": "Color" }]
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
}
|
|
811
|
+
```
|
|
812
|
+
|
|
813
|
+
For diverging color scales (values above/below a threshold), add
|
|
814
|
+
`"colorScale": "Diverging"` to the measure's field binding — only for a measure
|
|
815
|
+
genuinely centered on zero/a target (profit, variance, YoY change); on
|
|
816
|
+
all-positive data a diverging scale misleads.
|
|
817
|
+
|
|
818
|
+
---
|
|
819
|
+
|
|
820
|
+
### Table
|
|
821
|
+
|
|
822
|
+
Use `groups` for the categorical grouping column(s) and `rows` for measures
|
|
823
|
+
(API version 262+). The `groups`/`rows` split is the recommended form — it
|
|
824
|
+
gives the grouping column merged-cell styling. Putting a dimension directly in
|
|
825
|
+
`rows` (alongside measures) also works and renders as a plain Text column;
|
|
826
|
+
prefer `groups` for the leftmost grouping dimension for cleaner output.
|
|
827
|
+
Every field renders as a discrete text column (`marks.panes.type` is ignored).
|
|
828
|
+
|
|
829
|
+
```jsonc
|
|
830
|
+
{
|
|
831
|
+
"label": "Top Products by Revenue",
|
|
832
|
+
"fields": {
|
|
833
|
+
"F_dim": { "objectName": "Products", "fieldName": "Product_Name", "displayCategory": "Discrete" },
|
|
834
|
+
"F_meas": { "objectName": "Orders", "fieldName": "Sales", "function": "Sum", "displayCategory": "Continuous" }
|
|
835
|
+
},
|
|
836
|
+
"visualSpecification": {
|
|
837
|
+
"groups": ["F_dim"],
|
|
838
|
+
"rows": ["F_meas"],
|
|
839
|
+
"layout": "Table",
|
|
840
|
+
"marks": { "panes": { "type": "Text" } }
|
|
841
|
+
},
|
|
842
|
+
"sortIntent": "MeasureDescending"
|
|
843
|
+
}
|
|
844
|
+
```
|
|
845
|
+
|
|
846
|
+
Sort the primary measure descending via `sortIntent: "MeasureDescending"` or by
|
|
847
|
+
pre-sorting the data query via `run_semantic_query` with `sort_orders: DESC`.
|
|
848
|
+
`sortIntent` is an input-only convenience field: the server does not persist it
|
|
849
|
+
verbatim — it is translated into an explicit `sortOrders` entry on the
|
|
850
|
+
dimension field (`{ byField: <measure>, order: "Descending", type: "Field" }`),
|
|
851
|
+
so `get_visualization` will show `sortOrders`, not `sortIntent`.
|
|
852
|
+
|
|
853
|
+
Top-level `sortIntent` values: `MeasureDescending` | `MeasureAscending` |
|
|
854
|
+
`DimensionAscending` | `DimensionDescending` | `None`. Use `DimensionAscending`
|
|
855
|
+
for lookup tables (A→Z is the point) and `None` to preserve data order — it
|
|
856
|
+
overrides the auto-sort described below.
|
|
857
|
+
|
|
858
|
+
---
|
|
859
|
+
|
|
860
|
+
## 12. `create_visualization` Encoding Reference
|
|
861
|
+
|
|
862
|
+
| Encoding type | Valid mark types | Purpose |
|
|
863
|
+
|---|---|---|
|
|
864
|
+
| `Color` | Bar, Line, Circle, Square, Donut | Category or measure heat gradient |
|
|
865
|
+
| `Label` | All | Text labels on marks |
|
|
866
|
+
| `Tooltip` | All | Hover detail |
|
|
867
|
+
| `Detail` | Bar, Line, Circle, Square | High-cardinality grouping without color |
|
|
868
|
+
| `Size` | Circle | Bubble size mapping (third measure) |
|
|
869
|
+
| `Range` | Line | Confidence interval band |
|
|
870
|
+
| `Angle` | Donut | Slice sizing (Radial layout) |
|
|
871
|
+
|
|
872
|
+
**`displayCategory` for field bindings:**
|
|
873
|
+
- `"Continuous"` → measures and `DateTrunc*` date functions
|
|
874
|
+
- `"Discrete"` → plain dimensions and `DatePart*` date functions
|
|
875
|
+
|
|
876
|
+
**Number formatting options:**
|
|
877
|
+
- `Currency` → `"$1.2M"` (assumes USD)
|
|
878
|
+
- `Percent` → `"45.2%"` (expects values stored as 0–1; do NOT use on a value
|
|
879
|
+
stored as `45.2`)
|
|
880
|
+
- `Abbreviated` → `"1.2K"` / `"1.2M"` (default for large numbers)
|
|
881
|
+
- `Precise` → `"1,234,567"` (full digits)
|
|
882
|
+
|
|
883
|
+
By default (no `numberFormat` set) the server applies `NumberShort` with K/M/B
|
|
884
|
+
units at **1 decimal place** — **0 decimals for `Count`/`CountD`** — so axes read
|
|
885
|
+
`$1.2M`-style, not `1234567.89`.
|
|
886
|
+
|
|
887
|
+
**Auto-sort only fires for the single-dim + single-measure case.** A bar chart
|
|
888
|
+
with exactly one categorical (non-date) dimension and one measure is sorted by
|
|
889
|
+
the measure descending automatically ("unsorted bars are nearly useless"). Date
|
|
890
|
+
axes stay chronological regardless. Charts with 2+ shelf dimensions or 2+
|
|
891
|
+
measures are left unsorted — the sort key is ambiguous — so use `sortIntent`
|
|
892
|
+
explicitly for those.
|
|
893
|
+
|
|
894
|
+
---
|
|
895
|
+
|
|
896
|
+
## 13. Quality Checklist
|
|
897
|
+
|
|
898
|
+
Before submitting any viz creation call:
|
|
899
|
+
|
|
900
|
+
- [ ] Step 9 confirmed real data (non-empty source) before calling `create_visualization`
|
|
901
|
+
- [ ] Title is ≥ 3 words, business-friendly, no raw API names or technical suffixes
|
|
902
|
+
- [ ] `fields.<key>.fieldName` uses the **suffixed** apiName from Step 5 (if bulk-imported)
|
|
903
|
+
- [ ] `objectName` is **omitted** for calculated measures
|
|
904
|
+
- [ ] `function: "UserAgg"` for CLC / calculated fields — not overridden
|
|
905
|
+
- [ ] Second dimension added as `breakdown` or Color encoding when available
|
|
906
|
+
- [ ] `MeasureNames` synthetic field has `displayCategory: "Discrete"` and `MeasureValues` has `displayCategory: "Continuous"` — both are required; omitting either causes HTTP 500
|
|
907
|
+
- [ ] Donut (Radial layout) only for dimension cardinality < 5; measure on `Angle` encoding, NOT `rows`
|
|
908
|
+
- [ ] Year-over-year uses `DatePartMonth` + Year breakdown, NOT continuous timeline
|
|
909
|
+
- [ ] Unsupported chart type → fallback communicated clearly to user
|
|
910
|
+
- [ ] Chart type matches the business question (decision matrix applied)
|
|
911
|
+
|
|
912
|
+
---
|
|
913
|
+
|
|
914
|
+
## 14. Filters — WHERE vs. HAVING
|
|
915
|
+
|
|
916
|
+
`create_visualization`'s optional `filters` array persists filter clauses at
|
|
917
|
+
create time (distinct from `edit_visualization`'s `addFilter` op —
|
|
918
|
+
`edit-visualization.md` — which mutates an already-saved viz). You don't
|
|
919
|
+
choose the bucket; it's derived from the target field's role:
|
|
920
|
+
|
|
921
|
+
- Filter on a **raw field** (no aggregation `function`) → rows/WHERE —
|
|
922
|
+
`Processing_Fee between 0 and 21`.
|
|
923
|
+
- Filter on an **aggregated measure** (field carries `Sum`/`Avg`/`Count`/…) →
|
|
924
|
+
aggregated groups/HAVING — `SUM(Refund_Amount) between 100 and 500`.
|
|
925
|
+
|
|
926
|
+
Shape: `{fieldKey, type: "Dimension"|"ValueRange"|"DateRange", ...}`.
|
|
927
|
+
Dimension filters take exactly one of `include`/`exclude` (never both/neither)
|
|
928
|
+
plus optional `includeNulls` (default false; ignored on range filters, which
|
|
929
|
+
always exclude nulls). `ValueRange`/`DateRange` need at least one of
|
|
930
|
+
`min`/`max` or `dateStart`/`dateEnd` — one-sided emits a GreaterThanOrEqualTo/
|
|
931
|
+
LessThanOrEqualTo, both emits Between (inclusive; `min` must be ≤ `max`) — and
|
|
932
|
+
only apply to `Continuous` fields; use a Dimension filter for a `Discrete`
|
|
933
|
+
(string) one. `DateRange` bounds must be real calendar dates (ISO 8601
|
|
934
|
+
`YYYY-MM-DD` or full timestamp) — a malformed value like `2025-99-99` is
|
|
935
|
+
rejected up front. Filters compose with `excludeNulls` (which adds its own
|
|
936
|
+
aggregated null filter) without clobbering each other.
|
|
937
|
+
|
|
938
|
+
**Sorted-dimension subtlety:** filtering the auto-sorted categorical
|
|
939
|
+
dimension of a bar chart keeps the auto-sort (filter runs on an internal
|
|
940
|
+
copy). Filtering the *measure* the chart sorts by drops the auto-sort — a
|
|
941
|
+
measure can't be both a sort key and a filter target.
|
|
942
|
+
|
|
943
|
+
## 15. Forecasts (Timeseries Only)
|
|
944
|
+
|
|
945
|
+
`visualSpecification.forecasts` is a map of `forecastKey -> config`, valid
|
|
946
|
+
**only** on a Line chart with a continuous `DateTrunc*` dimension on the
|
|
947
|
+
timeseries axis — a non-timeseries chart (e.g. Bar) fails `INVALID_INPUT`.
|
|
948
|
+
|
|
949
|
+
Required: `forecastModel` (`HoltWinters` — seasonality/trend — or
|
|
950
|
+
`RidgeRegression` — linear/L2); `dimensionFields` (one entry, `dateDim.
|
|
951
|
+
fieldKey` matching a `Continuous` `DateTrunc*` field in `fields`,
|
|
952
|
+
`granularity: "InferFromData"` only); `measureFieldKey` (a measure field);
|
|
953
|
+
`from: {predefinedDate: "LatestData"}`; `to.forecastInterval` (`value` > 0 +
|
|
954
|
+
`timeUnit` ∈ Minutes/Hours/Days/Weeks/Months/Quarters/Years).
|
|
955
|
+
|
|
956
|
+
Optional: `includeBounds` (default true), `confidenceLevel` (90/95/99,
|
|
957
|
+
default 95), `ignoreLastPeriods` (default 0), `fillNullsWithZero` (default
|
|
958
|
+
false). A `dateDim`/`measureFieldKey` missing from `fields`, or of the wrong
|
|
959
|
+
type, fails `INVALID_INPUT` naming the offending forecastKey.
|
|
960
|
+
|
|
961
|
+
## 16. Calculated-Measure Level → Function Pairing
|
|
962
|
+
|
|
963
|
+
When binding a model-level calculated measure (from
|
|
964
|
+
`list_semantic_model_calculated_measures`, omit `objectName` — §10), copy its
|
|
965
|
+
`level` into the field's `level` property whenever `function` is set, then
|
|
966
|
+
pick `function` by that level:
|
|
967
|
+
|
|
968
|
+
| `level` | `function` |
|
|
969
|
+
|---|---|
|
|
970
|
+
| `AggregateFunction` / `TableCalc` | `UserAgg` |
|
|
971
|
+
| `Row` / `Lod` (LOD) | ordinary aggregation matching `aggregationType` (`Average`→`Avg`, `CountDistinct`→`CountD`), or omit `function` when `aggregationType` is `Auto`/`None` |
|
|
972
|
+
|
|
973
|
+
A missing or mismatched level/function pair is rejected before the
|
|
974
|
+
visualization saves.
|
|
975
|
+
|
|
976
|
+
## 17. Reference Line Knob
|
|
977
|
+
|
|
978
|
+
A per-measure-field knob (`fields.<key>.referenceLine`), distinct from
|
|
979
|
+
`edit_visualization`'s `addReferenceLine`/`editReferenceLine` operations
|
|
980
|
+
(`edit-visualization.md` §6) which target an already-saved viz. Values:
|
|
981
|
+
`Average`/`Sum`/`Min`/`Max` (computed line) or `Target` with a required
|
|
982
|
+
`referenceLineValue` (constant quota/threshold) — no `Median`. Keep to at
|
|
983
|
+
most one per chart.
|
|
984
|
+
|
|
985
|
+
## 18. Common Errors
|
|
986
|
+
|
|
987
|
+
| Error | Root cause |
|
|
988
|
+
|---|---|
|
|
989
|
+
| `INVALID_INPUT` | Missing top-level required field (`workspaceId`/`workspaceName`/`semanticModelName`/`semanticModelLabel`/`label`/`fields`/`visualSpecification`); malformed filter (both `include`+`exclude`, `min`>`max`, empty date bounds); or a forecast referencing a missing/mistyped field, non-Line chart, `forecastInterval.value`≤0, or invalid `confidenceLevel`. |
|
|
990
|
+
| `INVALID_VISUALIZATION_METADATA` | A fieldKey used in `rows`/`columns`/an encoding/a filter isn't in the `fields` map, or an encoding type doesn't fit the mark (e.g. `Angle` outside `Donut`). |
|
|
991
|
+
| `JSON_PARSER_ERROR` | Body shape mismatch — `fields` must be a map (not an array); `marks.panes` is a single object (not an array of panes); `visualSpecification` must be a top-level object, not nested under `body`. |
|
|
992
|
+
| `UNKNOWN_EXCEPTION` | Most common fixable cause: binding a **calculated measure** with `objectName` set — drop it (§10/§16). Also the generic fallback for `MeasureNames`/`MeasureValues` missing `displayCategory`. |
|