Skip to content

Add backend validation for all canvas component types#9246

Open
djbarnwal wants to merge 9 commits intomainfrom
feat/canvas-component-validation
Open

Add backend validation for all canvas component types#9246
djbarnwal wants to merge 9 commits intomainfrom
feat/canvas-component-validation

Conversation

@djbarnwal
Copy link
Copy Markdown
Member

@djbarnwal djbarnwal commented Apr 15, 2026

Implements backend validation for all 18 canvas component renderer types in ValidateRendererProperties.

  • Validates required and optional fields for each renderer against the referenced metrics view (dimensions, measures)
  • Groups cartesian charts (line_chart, bar_chart, area_chart, stacked_bar, stacked_bar_normalized) and circular charts (donut_chart, pie_chart) into shared validators
  • Handles special cases: metrics_sql as an alternative to metrics_view, rill_measures/measures virtual color fields (type: value), plain-string color literals
  • Adds helpers: requireMetricsView, getPathStringSlice, validateOptionalDimensionField, validateOptionalMeasureField, validateOptionalColorDimensionField
  • Adds test functions covering valid specs, invalid field references, missing required fields, and edge cases

Checklist:

  • Covered by tests
  • Ran it and it works as intended
  • Reviewed the diff before requesting a review
  • Checked for unhandled edge cases
  • Linked the issues it closes
  • Checked if the docs need to be updated. If so, create a separate Linear DOCS issue
  • Intend to cherry-pick into the release branch
  • I'm proud of this work!

@djbarnwal
Copy link
Copy Markdown
Member Author

@begelundmuller The frontend canvas UI does not support metrics_sql as a renderer property for kpi or kpi_grid It is always configured with metrics_view.

However, the backend does handle metrics_sql as a generic renderer property: populateRendererRefs in reconcilers/canvas.go uses it to discover transitive metrics view references for security resolution, and canvases.go uses it during canvas resolution to extract referenced metrics views. Hence for now have added metrics_sql in the validation logic for those components.

Copy link
Copy Markdown
Contributor

@begelundmuller begelundmuller left a comment

Choose a reason for hiding this comment

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

What about validation for unknown/unsupported fields? For example, AI is likely to add random properties it think will do something, so it would be useful to tell it when a property doesn't have any effect.

Scratch that, I see you intend to do it as a follow up

return fmt.Errorf("referenced y.fields value %q is not a measure in metrics view %q", f, mvn)
}
}
}
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.

It doesn't handle the case when y.fields is present but malformed, e.g. if it is not an array or the array contains non-strings.

I know it's not realistic to tailor the error to every case, but when malformed, it would be nice to explicitly state what's expected, for example:

if !ok {
  return errors.New("renderer property 'y.fields' is malformed: must be an array of strings")
}

Comment thread runtime/canvas/component.go Outdated
Comment on lines +156 to +157
// Validate optional multi-field measures (measure.fields)
if fields, ok := getPathStringSlice(props, "measure.fields"); ok {
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.

Same comment: needs to handle the malformed case

Comment thread runtime/canvas/component.go Outdated
Comment on lines +263 to +266
measures, ok := getPathStringSlice(props, "measures")
if !ok || len(measures) == 0 {
return errors.New("renderer properties for kpi_grid must include a non-empty 'measures' array")
}
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 error should state the expected format of the array (e.g. maybe the user already passed an array, but it contains some non-string values)

Comment thread runtime/canvas/component.go Outdated
Comment on lines +283 to +286
columns, ok := getPathStringSlice(props, "columns")
if !ok || len(columns) == 0 {
return errors.New("renderer properties for table must include a non-empty 'columns' array")
}
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.

Same comment

Comment thread runtime/canvas/component.go Outdated
Comment on lines +303 to +309
measures, _ := getPathStringSlice(props, "measures")
rowDims, _ := getPathStringSlice(props, "row_dimensions")
colDims, _ := getPathStringSlice(props, "col_dimensions")

if len(measures) == 0 && len(rowDims) == 0 && len(colDims) == 0 {
return errors.New("renderer properties for pivot must include at least one of 'measures', 'row_dimensions', or 'col_dimensions'")
}
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.

Similar, it should handle malformed values (i.e. if measures is present but has an unexpected format)

Comment thread runtime/canvas/component.go Outdated
Comment on lines +337 to +342
measures, _ := getPathStringSlice(props, "measures")
dimensions, _ := getPathStringSlice(props, "dimensions")

if len(measures) == 0 && len(dimensions) == 0 {
return errors.New("renderer properties for leaderboard must include at least one 'measures' or 'dimensions' entry")
}
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.

Same comment

Comment on lines +373 to +377
func validateOptionalDimensionField(mv *runtimev1.MetricsViewSpec, mvName string, props map[string]any, path string) error {
field, ok := pathutil.GetPathString(props, path)
if !ok {
return nil
}
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.

Should error explicitly if the field is present but not a string

Comment on lines +385 to +389
func validateOptionalMeasureField(mv *runtimev1.MetricsViewSpec, mvName string, props map[string]any, path string) error {
field, ok := pathutil.GetPathString(props, path)
if !ok {
return nil
}
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.

Same comment: should error explicitly if the field is present but not a string

Comment on lines +419 to +425
// getPathStringSlice extracts a []string from a nested path in the props map.
// Returns false if the path doesn't exist or the value is not a []any of strings.
func getPathStringSlice(props map[string]any, path string) ([]string, bool) {
raw, ok := pathutil.GetPath(props, path)
if !ok {
return nil, false
}
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.

Not sure, but regarding earlier comments, it might help to return nil, true here (i.e. empty list but no type error). Then bool return type indicates if types are incorrect, while the []string result indicates values set if any.

@begelundmuller
Copy link
Copy Markdown
Contributor

begelundmuller commented Apr 21, 2026

@begelundmuller The frontend canvas UI does not support metrics_sql as a renderer property for kpi or kpi_grid It is always configured with metrics_view.

However, the backend does handle metrics_sql as a generic renderer property: populateRendererRefs in reconcilers/canvas.go uses it to discover transitive metrics view references for security resolution, and canvases.go uses it during canvas resolution to extract referenced metrics views. Hence for now have added metrics_sql in the validation logic for those components.

I'm not sure I understand this. If the frontend doesn't support metrics_sql for those renderers, why not just return an error from the renderer validation if it is used?

@djbarnwal
Copy link
Copy Markdown
Member Author

Made the suggested changes

I'm not sure I understand this. If the frontend doesn't support metrics_sql for those renderers, why not just return an error from the renderer validation if it is used?

Returning an error breaks the go test suite. Some internal test use the metrics_sql key and I believe they are used to test transitive access policy so didn't want to remove them before understand the intention behind it.

@begelundmuller
Copy link
Copy Markdown
Contributor

Returning an error breaks the go test suite. Some internal test use the metrics_sql key and I believe they are used to test transitive access policy so didn't want to remove them before understand the intention behind it.

Please add that validation and just break the Go test. There’s no deep thought behind the Go test, it probably didn’t consider if the renderer actually supported metrics SQL or not.

And then just update the Go test to use a renderer that does support metrics_sql, or alternatively ping me and I can fix it directly on the PR.

@djbarnwal djbarnwal requested a review from a team as a code owner April 26, 2026 07:28
@djbarnwal
Copy link
Copy Markdown
Member Author

Returning an error breaks the go test suite. Some internal test use the metrics_sql key and I believe they are used to test transitive access policy so didn't want to remove them before understand the intention behind it.

Please add that validation and just break the Go test. There’s no deep thought behind the Go test, it probably didn’t consider if the renderer actually supported metrics SQL or not.

And then just update the Go test to use a renderer that does support metrics_sql, or alternatively ping me and I can fix it directly on the PR.

Tightened validateKPI and validateKPIGrid per your suggestion. Breaks 7 tests in runtime/server/canvases_test.go (TestResolveCanvas, TestResolveCanvasWithInvalidSQL, TestResolveCanvasWithTemplatedSQL, TestResolveCanvasWithMultipleMetricsViewsReferences, TestResolveCanvasWithMetricsSQL, TestResolveCanvasWithCustomChart, TestCanvasAndTemplatedString) — all use kpi: metrics_sql: to seed resolver discovery.

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.

2 participants