Repository navigation
Conversation
The comprehension 'if mode == "categorical"' on the undefaulted disjunction of #palette cannot be resolved once the plugin schema is merged into the complete dashboard schema (/api/v1/schemas/dashboards): 'cue vet' then fails with 'unresolved disjunction' on every TimeSeriesChart panel. Use a disjunction of closed structs instead, which keeps the same rules: 'name' is only allowed (and optional) in categorical mode. Signed-off-by: Akshay Iyyadurai Balasundaram <akshay.iyyadurai.balasundaram@sap.com>
There was a problem hiding this comment.
🟡 Changes recommended
The added fixture passes under the old schema and therefore does not protect against the reported merged-schema regression.
1 open finding
What changed in this PR
Rewrites the TimeSeriesChart palette schema to remain resolvable when embedded in Perses dashboard schemas.
Changes:
- Replaces conditional palette validation with explicit struct variants.
- Adds an auto-palette schema fixture.
| File | Description |
|---|---|
timeserieschart/schemas/time-series.cue |
Expresses palette modes as a resolvable disjunction. |
timeserieschart/schemas/tests/valid/time-series-palette-auto.json |
Adds an auto-mode validation fixture. |
🧠 Review effort: Balanced
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
The plugin schema tests validate the plugin schema alone, where the previous palette definition was valid. Add a CUE test that embeds the plugin schema in a panel plugin disjunction, as Perses does when it builds the dashboard schema, and run it from test-schemas-plugins. Signed-off-by: Akshay Iyyadurai Balasundaram <akshay.iyyadurai.balasundaram@sap.com>
| // dashboardTestDir contains an optional CUE package defining a "#Dashboard" that embeds the plugin schema the way | ||
| // Perses does when merging all plugin schemas, along with JSON dashboards that must validate against it. | ||
| const dashboardTestDir = "schemas/tests/dashboard" |
There was a problem hiding this comment.
this makes me wonder why we have this discrepancy. It feels wrong to me to tackle it this way. I think I did a mistake originally in the panel schemas here, e.g TimeSeriesChart is defined as such:
plugins/timeserieschart/schemas/time-series.cue
Lines 20 to 28 in 04a103f
the close() call is closing the spec struct only at the level it is used, it doesnt recursively apply to all nested structs, while wrapping the panel in a #Dashboard definition performs a recursive close & that's probably why you got this "valid dashboards fail cue vet against the generated schema, although the same server accepts them". To verify that we should tweak the TimeSeriesChart to this:
kind: "TimeSeriesChart"
spec: #spec
#spec: {
legend?: common.#legendWithValues
tooltip?: #tooltip
yAxis?: #yAxis
thresholds?: common.#thresholds
visual?: #visual
querySettings?: #querySettings
})Could you do this & verify that for the same situation as in perses/perses#4574 this time it makes both cue vet & server validation fail? If yes we should update this schema (and all the other similar ones) to this structure, this would be more accurate in that case & we wouldn't need this dashboardTestDir-related test logic to cover a gap.
There was a problem hiding this comment.
Thank you for the pointer. @AntoineThebaud
I tried it with the Perses v0.55.0-rc.0. I loaded TimeSeriesChart, BarChart and StatChart, generated the schema with GenerateDashboardSchema(), and ran cue vet -d '#Dashboard'.
Below are the results:
| TimeSeriesChart schema | server validation | cue vet |
|---|---|---|
spec: close({...}) + old #palette (main) |
OK | FAIL: #palette: unresolved disjunction |
spec: #spec, without close() + old #palette |
OK | FAIL (same) |
spec: {...}, without close() + old #palette |
OK | FAIL (same) |
spec: close({...}) + new #palette (this PR) |
OK | OK |
So removing close(), with or without your #spec definition, doesn't change the outcome: the #palette error stays.
Your point about close() is a separate issue. With a plain spec: {...} and no close(), the server accepts an unknown field like spec.foo while cue vet rejects it. With close() or #spec, both reject it. A nested unknown field like spec.visual.foo is rejected by both in every variant, because the nested types are definitions.
The difference comes from how the dashboard schema is generated. Each plugin schema, including helper definitions like #palette, is copied into the panel plugin disjunction. Inside that disjunction, CUE can't resolve the if mode == "categorical" check. The server validates each plugin schema on its own, so it never hits this. perses/perses#4577 fixes the generator and adds a regression test for exactly this case.
Given that, I'm happy to remove the test logic from this PR and keep only the schema change. WDYT?
There was a problem hiding this comment.
Ok indeed I forgot to consider that all the fields within spec are definitions.. I still don't fully understand why this issue occur but let's follow up on perses/perses#4577 side, for this PR yes please keep only the schema change.

Description
Part of perses/perses#4574
The TimeSeriesChart palette schema breaks validation against the dashboard CUE schema from Perses: every dashboard with a TimeSeriesChart panel fails. This PR writes the same rule in a form that CUE can resolve. The allowed values don't change.
perses/perses#4577 also fixes this for all plugins in the Perses schema generator.
AI Usage Disclosure
GitHub Copilot was used to investigate the issue and write the change. I reviewed and tested it.