protocheck

package module
v0.7.4 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 31, 2025 License: MIT Imports: 11 Imported by: 0

README

protocheck

Go GoDoc codecov

Lightweight solution to ProtocolBuffers message validation. protocheck-proto-gen is a protoc plugin that generates code.

features

  • 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

install

  go install github.com/emicklei/protocheck/cmd/protoc-gen-protocheck@latest

example

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.

generate Go
	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.

usage Go
p := &Person{
    Name:      "",
    BirthDate: &timestamppb.Timestamp{Seconds:1}
}
if err := p.Validate(); err != nil {
  for _ , each := range err {
    log.Println(each)
  }
}
generate Java
	protoc -I=../protos \
		--java_out=src/main/java \
		--protocheck_out=src/main/java \
		--protocheck_opt=paths=source_relative,lang=java \
		person.proto
usage Java
  Person p = Person.newBuilder()
          .setName("")
          .build();
  for (CheckError e : OuterClassName.validate(p)) {
      System.err.println(e);
  }
considerations

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

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	// repeated check.Check field = 20030916;
	E_Field = &file_check_proto_extTypes[1]
)

Extension fields to descriptorpb.FieldOptions.

View Source
var (
	// repeated check.Check message = 20030916;
	E_Message = &file_check_proto_extTypes[0]
)

Extension fields to descriptorpb.MessageOptions.

View Source
var File_check_proto protoreflect.FileDescriptor

Functions

func MakeProgram

func MakeProgram(env *cel.Env, expression string, container string) (cel.Program, error)

MakeProgram creates a cel Program for a given expression and environment.

Types

type Check

type Check struct {
	Id   string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`     // optional identifier for lookup purposes
	Fail string `protobuf:"bytes,2,opt,name=fail,proto3" json:"fail,omitempty"` // optional message to report when the check fails
	Cel  string `protobuf:"bytes,3,opt,name=cel,proto3" json:"cel,omitempty"`   // Google/CEL expression (https://github.com/google/cel-spec)
	// contains filtered or unexported fields
}

func (*Check) Descriptor deprecated

func (*Check) Descriptor() ([]byte, []int)

Deprecated: Use Check.ProtoReflect.Descriptor instead.

func (*Check) GetCel

func (x *Check) GetCel() string

func (*Check) GetFail

func (x *Check) GetFail() string

func (*Check) GetId

func (x *Check) GetId() string

func (*Check) ProtoMessage

func (*Check) ProtoMessage()

func (*Check) ProtoReflect

func (x *Check) ProtoReflect() protoreflect.Message

func (*Check) Reset

func (x *Check) Reset()

func (*Check) String

func (x *Check) String() string

type CheckError

type CheckError struct {
	Id   string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`     // optional id of the check that failed
	Fail string `protobuf:"bytes,2,opt,name=fail,proto3" json:"fail,omitempty"` // optional message to display
	Path string `protobuf:"bytes,3,opt,name=path,proto3" json:"path,omitempty"` // path to the field name that failed, if empty then it was a message check
	// contains filtered or unexported fields
}

func (*CheckError) Descriptor deprecated added in v0.6.0

func (*CheckError) Descriptor() ([]byte, []int)

Deprecated: Use CheckError.ProtoReflect.Descriptor instead.

func (*CheckError) GetFail added in v0.6.0

func (x *CheckError) GetFail() string

func (*CheckError) GetId added in v0.6.0

func (x *CheckError) GetId() string

func (*CheckError) GetPath added in v0.6.0

func (x *CheckError) GetPath() string

func (*CheckError) ProtoMessage added in v0.6.0

func (*CheckError) ProtoMessage()

func (*CheckError) ProtoReflect added in v0.6.0

func (x *CheckError) ProtoReflect() protoreflect.Message

func (*CheckError) Reset added in v0.6.0

func (x *CheckError) Reset()

func (*CheckError) String added in v0.6.0

func (x *CheckError) String() string

func (*CheckError) WithParentField

func (c *CheckError) WithParentField(parent string, key any) *CheckError

WithParentField returns a new CheckError with the parent field prepended to the path.

func (*CheckError) WithPath

func (c *CheckError) WithPath(path string) *CheckError

WithPath returns a new CheckError with the path set.

type Checker

type Checker struct {
	// contains filtered or unexported fields
}

Checker performs one check using a CEL program.

func NewChecker

func NewChecker(id string, fail string, cel string, fieldName string, isOptional bool, program cel.Program) Checker

NewChecker creates a Checker

func (Checker) WithEnabledFunc

func (c Checker) WithEnabledFunc(enabledFunc func(any) bool) Checker

func (Checker) WithIsSetFunc added in v0.2.0

func (c Checker) WithIsSetFunc(fun func(message any, fieldName string) bool) Checker

type MessageValidator

type MessageValidator struct {
	// contains filtered or unexported fields
}

MessageValidator holds a collection of checkers to validate a message.

func NewMessageValidator

func NewMessageValidator(messageCheckers, fieldCheckers []Checker) MessageValidator

NewMessageValidator creates a MessageValidator using two collections of checkers

func (MessageValidator) Validate

func (m MessageValidator) Validate(this any, options ...ValidationOption) (result ValidationError)

Validate runs all message and field checkers with the message. Always returns a ValidationError (error) which can be empty (no failed checks) With options you can control what to validations to skip.

type ValidationError

type ValidationError []*CheckError

ValidationError is a collection of *CheckError.

func AsValidationError added in v0.3.0

func AsValidationError(err error) ValidationError

AsValidationError converts an error or nil to a valid ValidationError (so could be empty).

func (ValidationError) Error

func (v ValidationError) Error() string

Error implements error

type ValidationOption added in v0.5.0

type ValidationOption byte
const FieldsSetOnly ValidationOption = 1

Only if a field is set (non-zero value for proto3) then validate its content.

type Validator

type Validator interface {
	Validate(options ...ValidationOption) ValidationError
}

Validator is an interface that can be implemented by a message to validate itself.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL