Lightweight solution to ProtocolBuffers message validation.
protocheck-proto-gen
is a protoc
plugin that generates code.
- expressions with Google/CEL
- both message and field checks
- nested messages
- repeated, oneof and map fields
- syntax validation of CEL expressions at generation time
- supports proto3 and edition 2023 (default_api_level=API_OPAQUE)
- supported languages:
- Go
- Java
go install github.com/emicklei/protocheck/cmd/protoc-gen-protocheck@latest
package hr;
import "check.proto";
message Person {
// message cross-field checks
option (check.message) = {
cel:"size(this.name + this.surname) > 0" // cel is required
fail:"name and surname cannot be empty" // fail is optional
id:"person_invariant" }; // id is optional
// with per field state checks
string name = 1 [(check.field) = {
cel:"size(this.name) > 1"
fail:"name must be longer than 1" }];
optional string middle_name = 2 [(check.field) = {
cel:"size(this.middle_name) > 0"
fail:"middle name (if set) cannot be empty" }];
string surname = 3 [(check.field) = {
cel:"size(this.surname) > 1"
fail:"surname must be longer than 1" }];
google.protobuf.Timestamp birth_date = 4 [(check.field) = {
cel:"this.birth_date.getFullYear() > 2000"
id:"check_birth_date" }];
getFullYear
is one of the supported CEL macros.
See CEL language definition for creating CEL expressions.
protoc -I=../protos \
--go_out=. \
--go_opt=paths=source_relative \
--protocheck_out=. \
--protocheck_opt=paths=source_relative,lang=go \
person.proto
You can add the option v=true
to produce verbose logging of the protocheck tool.
p := &Person{
Name: "",
BirthDate: ×tamppb.Timestamp{Seconds:1}
}
if err := p.Validate(); err != nil {
for _ , each := range err {
log.Println(each)
}
}
protoc -I=../protos \
--java_out=src/main/java \
--protocheck_out=src/main/java \
--protocheck_opt=paths=source_relative,lang=java \
person.proto
Person p = Person.newBuilder()
.setName("")
.build();
for (CheckError e : OuterClassName.validate(p)) {
System.err.println(e);
}
The protocheck
package is inspired by bufbuild/protovalidate
which also uses the powerful CEL expression language. However, it differs by:
this
always refers to the message- minimal dependencies (cel and protobuf)
- syntax is more compact