Skip to content

validate: integrate protovalidate for CEL validation#378

Merged
wenchy merged 5 commits intomasterfrom
buf-validate
Apr 10, 2026
Merged

validate: integrate protovalidate for CEL validation#378
wenchy merged 5 commits intomasterfrom
buf-validate

Conversation

@Kybxd
Copy link
Copy Markdown
Collaborator

@Kybxd Kybxd commented Apr 3, 2026

Overview

Integrates protovalidate (buf's proto validation framework) into the tableau pipeline, enabling users to declare validation rules directly in spreadsheet field/worksheet properties, which are then compiled into generated proto files and enforced at config generation time.


Proto Schema Changes (tableau.proto)

Added new fields to support validation rule declarations:

  • FieldProp.validate — field-level rules for scalar and well-known types (text-format buf.validate.FieldRules, e.g. "string:{max_len:10}")
  • FieldProp.validate_complex — field-level rules for complex types (list/map), e.g. "repeated:{min_items:1}"
  • FieldProp.validate_message — message-level rules for the nested message of a field (text-format buf.validate.MessageRules)
  • WorksheetOptions.validate — message-level rules at the worksheet/messager level

Proto Generation (protogen)

  • exporter.go: During proto file export, the three validate* fields are parsed from text-format into validate.FieldRules / validate.MessageRules, then emitted as (buf.validate.field) / (buf.validate.message) options in the generated .proto file. The raw string fields are cleared afterward to avoid leaking them into (tableau.field) options.
  • marshalToText / unmarshalFromText: Converted from package-level functions to bookExporter methods, using a ProtoRegistryTypes resolver to correctly handle custom predefined rule extensions.
  • genFieldOptionsString: Now accepts fieldRules and appends (buf.validate.field) when present.
  • addMessageExtensionImports: New helper that recursively walks a proto message to collect and register import paths for all extension fields (needed for custom rule definitions).
  • field_prop.go: ExtractMapFieldProp, ExtractListFieldProp, ExtractStructFieldProp, and ExtractScalarFieldProp now propagate the new validate* fields to their respective extracted prop copies.

Config Generation (confgen)

  • confgen.go: A protovalidate.Validator is created with WithExtensionTypeResolver(dynamicpb.NewTypes(...)) to support custom predefined rules, and passed down to SheetExporter.
  • util.go: A new validate() helper runs protovalidate on each generated config message. Violations are collected and wrapped as E2027 errors (with violation description and the offending field value), then joined and returned.
  • storeMessage: Now accepts a validator and calls validate() before writing output files.

Error Reporting (xerrors / i18n)

  • New error code E2027: protovalidate violation — reports {{ quote .FieldValue }} violates rule: {{.Violation}} with the violation description from v.String() and the actual field value.
  • i18n.FuncMap exported so the ecode code-generator tool can reuse it (fixing a quote function not defined panic during go generate).

Tests & Testdata

Added comprehensive functional test cases covering:

  • Field-level validation (scalar, well-known types)
  • Complex-type validation (list/map)
  • Message-level validation (CEL expressions)
  • Predefined types validation
  • Custom rules (via proto extensions)
  • Worksheet-level validation

@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 3, 2026

Codecov Report

❌ Patch coverage is 82.24852% with 30 lines in your changes missing coverage. Please review.
✅ Project coverage is 74.26%. Comparing base (05396e8) to head (241a42c).
⚠️ Report is 1 commits behind head on master.

Files with missing lines Patch % Lines
internal/protogen/exporter.go 82.55% 10 Missing and 5 partials ⚠️
internal/confgen/confgen.go 53.33% 5 Missing and 2 partials ⚠️
internal/protogen/protogen.go 66.66% 2 Missing and 2 partials ⚠️
internal/confgen/parser.go 71.42% 2 Missing ⚠️
internal/confgen/util.go 86.66% 2 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master     #378      +/-   ##
==========================================
+ Coverage   74.11%   74.26%   +0.14%     
==========================================
  Files          87       87              
  Lines        8727     8836     +109     
==========================================
+ Hits         6468     6562      +94     
- Misses       1698     1707       +9     
- Partials      561      567       +6     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Comment thread go.mod Outdated
Comment thread internal/protogen/exporter.go Outdated
Comment thread proto/tableau/protobuf/tableau.proto Outdated
Comment thread internal/confgen/util.go Outdated
Comment on lines +279 to +285
if validator != nil {
if err := validator.Validate(msg); err != nil {
return xerrors.Wrap(err)
}
} else if err := protovalidate.Validate(msg); err != nil {
return xerrors.Wrap(err)
}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Discussion: validate on each parsed cell? for the exact cell position when printing errors.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Another solution is #381

message ValidateStructType {
option (tableau.struct) = {name:"ValidateStructType"};
option (buf.validate.message) = {cel:{id:"validate_struct.num_positive" message:"num must be positive when id > 0" expression:"this.id == 0u || this.num > 0"}};
option (buf.validate.message) = {cel:{id:"validate_struct.range" expression:"this.min > this.max ? 'min(%d) must be less than or equal to max(%d)'.format([this.min, this.max]) : ''"}};
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

  1. Should also support assigning validate to MODE_STRUCT_TYPE_MULTI and MODE_UNION_TYPE_MULTI sheets
  2. Improve readability, change less than or equal to -> <=
  3. Add new struct Timespan, and validate the begin and end timestamp fields.

Comment thread proto/tableau/protobuf/tableau.proto Outdated
@wenchy wenchy changed the title feat: integrate buf/validate (protovalidate) for field-level validation validate: integrate protovalidate for CEL validation Apr 9, 2026
@wenchy wenchy added the validate confgen: enforce constraints on messages and fields. label Apr 9, 2026
Comment thread internal/protogen/exporter_test.go Outdated
Comment thread proto/tableau/protobuf/tableau.proto Outdated
Comment thread proto/tableau/protobuf/tableau.proto Outdated
Comment thread internal/protogen/exporter.go Outdated
Comment thread internal/protogen/exporter.go
feat: Add validate field to FieldProp for protovalidate

feat: Add protovalidate message-level validation support

refactor: Align field validation naming and Go version to 1.24.x

refactor: Refactor worksheet validation to use item_map structure

refactor: Unify protovalidate usage and imports

refactor: Simplify proto export and improve naming clarity

feat: Add validation support for struct and union types

feat: Integrate protovalidate for custom rule validation
The code introduces protovalidate integration to support custom predefined validation rules. It adds validator initialization, modifies message exporting to use the validator, and includes tests for custom rule validation.

chore: Add go_package and trim top blank line

refactor: Improve error handling and update dependencies

chore: Remove unused bufValidateProtoPath constant

feat: Add validation tests and proto for well-known and predefined types

refactor: Migrate range validations to timespan with CEL

refactor: Rename FieldValidate to Validate, constraints to rules

refactor: Rename message validation and split field validation into Validate/ValidateComplex

feat: Add uint32/gt and map min_pairs validations

refactor: Simplify FieldOptions init in test

refactor(exporter): Update comments and enhance validate usage
@wenchy wenchy merged commit 7639e00 into master Apr 10, 2026
7 checks passed
@wenchy wenchy deleted the buf-validate branch April 10, 2026 06:05
@wenchy wenchy mentioned this pull request Apr 10, 2026
42 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

validate confgen: enforce constraints on messages and fields.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

validate: support configuring in-place validate on repeated and map fields fieldprop(check): support CEL to write custom rules

2 participants