Giter Club home page Giter Club logo

protovalidate-go's Introduction

The Buf logo protovalidate-go

CI Conformance Report Card GoDoc BSR

protovalidate-go is the Go language implementation of protovalidate designed to validate Protobuf messages at runtime based on user-defined validation constraints. Powered by Google's Common Expression Language (CEL), it provides a flexible and efficient foundation for defining and evaluating custom validation rules. The primary goal of protovalidate is to help developers ensure data consistency and integrity across the network without requiring generated code.

The protovalidate project

Head over to the core protovalidate repository for:

Other protovalidate runtime implementations:

And others coming soon:

  • TypeScript: protovalidate-ts

For Connect see connectrpc/validate-go.

Installation

To install the package, use the go get command from within your Go module:

go get github.com/bufbuild/protovalidate-go

Import the package into your Go project:

import "github.com/bufbuild/protovalidate-go"

Remember to always check for the latest version of protovalidate-go on the project's GitHub releases page to ensure you're using the most up-to-date version.

Usage

Implementing validation constraints

Validation constraints are defined directly within .proto files. Documentation for adding constraints can be found in the protovalidate project README and its comprehensive docs.

syntax = "proto3";

package my.package;

import "google/protobuf/timestamp.proto";
import "buf/validate/validate.proto";

message Transaction {
  uint64 id = 1 [(buf.validate.field).uint64.gt = 999];
  google.protobuf.Timestamp purchase_date = 2;
  google.protobuf.Timestamp delivery_date = 3;
  
  string price = 4 [(buf.validate.field).cel = {
    id: "transaction.price",
    message: "price must be positive and include a valid currency symbol ($ or £)",
    expression: "(this.startsWith('$') || this.startsWith('£')) && double(this.substring(1)) > 0"
  }];

  option (buf.validate.message).cel = {
    id: "transaction.delivery_date",
    message: "delivery date must be after purchase date",
    expression: "this.delivery_date > this.purchase_date"
  };
}

Buf managed mode

protovalidate-go assumes the constraint extensions are imported into the generated code via buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go.

If you are using Buf managed mode to augment Go code generation, ensure that the protovalidate module is excluded in your buf.gen.yaml:

version: v1
# <snip>
managed:
  enabled: true
  go_package_prefix:
    except:
      - buf.build/bufbuild/protovalidate
# <snip>

Example

package main

import (
	"fmt"
	"time"
	
	pb "github.com/path/to/generated/protos"
	"github.com/bufbuild/protovalidate-go"
	"google.golang.org/protobuf/types/known/timestamppb"
)

func main() {
	msg := &pb.Transaction{
		Id:           1234,
		Price:        "$5.67",
		PurchaseDate: timestamppb.New(time.Now()),
		DeliveryDate: timestamppb.New(time.Now().Add(time.Hour)),
	}

	v, err := protovalidate.New()
	if err != nil {
		fmt.Println("failed to initialize validator:", err)
	}

	if err = v.Validate(msg); err != nil {
		fmt.Println("validation failed:", err)
	} else {
		fmt.Println("validation succeeded")
	}
}

Lazy mode

protovalidate-go defaults to lazily construct validation logic for Protobuf message types the first time they are encountered. A validator's internal cache can be pre-warmed with the WithMessages or WithDescriptors options during initialization:

validator, err := protovalidate.New(
  protovalidate.WithMessages(
    &pb.MyFoo{}, 
    &pb.MyBar{}, 
  ),
)

Lazy mode uses a copy on write cache stategy to reduce the required locking. While performance is sub-microsecond, the overhead can be further reduced by disabling lazy mode with the WithDisableLazy option. Note that all expected messages must be provided during initialization of the validator:

validator, err := protovalidate.New(
  protovalidate.WithDisableLazy(true),
  protovalidate.WithMessages(
    &pb.MyFoo{},
    &pb.MyBar{},
  ),
)

Support legacy protoc-gen-validate constraints

The protovalidate-go module comes with a legacy package which adds opt-in support for existing protoc-gen-validate constraints. Provide thelegacy.WithLegacySupport option when initializing the validator:

validator, err := protovalidate.New(
  legacy.WithLegacySupport(legacy.ModeMerge),
)

protoc-gen-validate code generation is not used by protovalidate-go. The legacy package assumes the protoc-gen-validate extensions are imported into the generated code via github.com/envoyproxy/protoc-gen-validate/validate.

A migration tool is also available to incrementally upgrade legacy constraints in .proto files.

Performance

Benchmarks are provided to test a variety of use-cases. Generally, after the initial cold start, validation on a message is sub-microsecond and only allocates in the event of a validation error.

[circa 14 September 2023]
goos: darwin
goarch: arm64
pkg: github.com/bufbuild/protovalidate-go
BenchmarkValidator
BenchmarkValidator/ColdStart-10              4192  246278 ns/op  437698 B/op  5955 allocs/op
BenchmarkValidator/Lazy/Valid-10         11816635   95.08 ns/op       0 B/op     0 allocs/op
BenchmarkValidator/Lazy/Invalid-10        2983478   380.5 ns/op     649 B/op    15 allocs/op
BenchmarkValidator/Lazy/FailFast-10      12268683   98.22 ns/op     168 B/op     3 allocs/op
BenchmarkValidator/PreWarmed/Valid-10    12209587   90.36 ns/op       0 B/op     0 allocs/op
BenchmarkValidator/PreWarmed/Invalid-10   3098940   394.1 ns/op     649 B/op    15 allocs/op
BenchmarkValidator/PreWarmed/FailFast-10 12291523   99.27 ns/op     168 B/op     3 allocs/op
PASS

Ecosystem

Legal

Offered under the Apache 2 license.

protovalidate-go's People

Contributors

akshayjshah avatar alfus avatar chrispine avatar dependabot[bot] avatar derekperkins avatar dragon3 avatar elliotmjackson avatar emcfarlane avatar higebu avatar jhump avatar marekbuild avatar nicksnyder avatar oliversun9 avatar pkwarren avatar rodaine avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

protovalidate-go's Issues

Trouble to build for ` no field or method errors` in cel-go

Description

I'm using protovalidate-go, but I found than I can't build my project using this library.

If there are solutions of this trouble, I want to know them.

Expected Behavior

Success to build.

Actual Behavior

Failed to build with entry.GetOptionalEntry undefined (type *expr.Expr_CreateStruct_Entry has no field or method GetOptionalEntry) errors in cel-go.

Screenshots/Logs

# github.com/google/cel-go/common/debug
../../../../go/pkg/mod/github.com/google/[email protected]/common/debug/debug.go:173:13: entry.GetOptionalEntry undefined (type *expr.Expr_CreateStruct_Entry has no field or method GetOptionalEntry)
../../../../go/pkg/mod/github.com/google/[email protected]/common/debug/debug.go:197:13: entry.GetOptionalEntry undefined (type *expr.Expr_CreateStruct_Entry has no field or method GetOptionalEntry)
# github.com/google/cel-go/parser
../../../../go/pkg/mod/github.com/google/[email protected]/parser/helper.go:125:4: unknown field OptionalIndices in struct literal of type expr.Expr_CreateList
../../../../go/pkg/mod/github.com/google/[email protected]/parser/helper.go:142:3: unknown field OptionalEntry in struct literal of type expr.Expr_CreateStruct_Entry
../../../../go/pkg/mod/github.com/google/[email protected]/parser/helper.go:162:3: unknown field OptionalEntry in struct literal of type expr.Expr_CreateStruct_Entry
../../../../go/pkg/mod/github.com/google/[email protected]/parser/helper.go:263:6: unknown field OptionalIndices in struct literal of type expr.Expr_CreateList
../../../../go/pkg/mod/github.com/google/[email protected]/parser/helper.go:263:32: listExpr.GetOptionalIndices undefined (type *expr.Expr_CreateList has no field or method GetOptionalIndices)
../../../../go/pkg/mod/github.com/google/[email protected]/parser/unparser.go:310:24: l.GetOptionalIndices undefined (type *expr.Expr_CreateList has no field or method GetOptionalIndices)
../../../../go/pkg/mod/github.com/google/[email protected]/parser/unparser.go:378:12: entry.GetOptionalEntry undefined (type *expr.Expr_CreateStruct_Entry has no field or method GetOptionalEntry)
../../../../go/pkg/mod/github.com/google/[email protected]/parser/unparser.go:402:12: entry.GetOptionalEntry undefined (type *expr.Expr_CreateStruct_Entry has no field or method GetOptionalEntry)
# github.com/google/cel-go/common/ast
../../../../go/pkg/mod/github.com/google/[email protected]/common/ast/expr.go:509:34: l.ToExpr().GetListExpr().GetOptionalIndices undefined (type *expr.Expr_CreateList has no field or method GetOptionalIndices)
../../../../go/pkg/mod/github.com/google/[email protected]/common/ast/expr.go:527:13: e.GetOptionalEntry undefined (type *expr.Expr_CreateStruct_Entry has no field or method GetOptionalEntry)
../../../../go/pkg/mod/github.com/google/[email protected]/common/ast/expr.go:588:13: f.GetOptionalEntry undefined (type *expr.Expr_CreateStruct_Entry has no field or method GetOptionalEntry)

Environment

Additional Context

I created an issue(google/cel-go#811) in cel-go project first.
There are some sample code in this issue comments.

Make evaluator threadsafe without mutex

Based on benchmarks, about 50% of CPU time is spent obtaining a read lock on this mutex. Ideally, this can be reworked to be thread-safe while minimizing the need to obtain a lock.

example

Fails to validate a simple message

Description

Calling the validate method on a proto message used to work but since v0.3.3 it fails.

Steps to Reproduce

I have a simple message test written like this:

syntax = "proto3";

package test.v1;

import "buf/validate/validate.proto";

message SmallMessage {
  string code = 1 [(buf.validate.field).required = true];
  uint32 decimal_places = 2 [(buf.validate.field).uint32 = {gte: 0, lte: 4}];
}

and a unit test like this:

package validator

import (
	"testing"

	pv "github.com/bufbuild/protovalidate-go"
	testv1 "github.com/ennismore/go-buf-connect/v2/internal/test/proto/gen/test/v1"
	"github.com/stretchr/testify/assert"
)

func TestValidatorError(t *testing.T) {
	msg := &testv1.SmallMessage{
		Code:          "",
		DecimalPlaces: 5,
	}

	v, _ := pv.New()
	err := v.Validate(msg)

	assert.Error(t, err)
	assert.Regexp(t, `code: value is required`, err)
	assert.Regexp(t, `decimal_places: value must be greater than or equal to 0 and less than or equal to 4`, err)
}

Expected Behavior

To pass. This worked fine with v0.3.2, I expect it to keep working in later releases.

Actual Behavior

Output

=== RUN   TestValidatorError
    validate_test.go:20: 
        	Error Trace:	/home/gary/Projects/go-buf-connect/service/request/validator/validate_test.go:20
        	Error:      	An error is expected but got nil.
        	Test:       	TestValidatorError
    validate_test.go:21: 
        	Error Trace:	/home/gary/Projects/go-buf-connect/service/request/validator/validate_test.go:21
        	Error:      	Expect "<nil>" to match "code: value is required"
        	Test:       	TestValidatorError
    validate_test.go:22: 
        	Error Trace:	/home/gary/Projects/go-buf-connect/service/request/validator/validate_test.go:22
        	Error:      	Expect "<nil>" to match "decimal_places: value must be greater than or equal to 0 and less than or equal to 4"
        	Test:       	TestValidatorError
--- FAIL: TestValidatorError (0.00s)
FAIL
FAIL	github.com/ennismore/go-buf-connect/v2/service/request/validator	0.004s
FAIL

Screenshots/Logs

buf.gen.yaml

version: v1
managed:
  enabled: true
  go_package_prefix:
    default: github.com/ennismore/go-buf-connect/v2/internal/test/proto/gen
    except:
      - buf.build/bufbuild/protovalidate
plugins:
  - plugin: buf.build/protocolbuffers/go
    opt:
      - paths=source_relative
    out: gen
  - plugin: buf.build/bufbuild/connect-go
    opt:
      - paths=source_relative
    out: gen

Environment

  • Operating System: Linux
  • Version: Ubuntu 22.04
  • Compiler/Toolchain: gcc (Ubuntu 11.4.0-1ubuntu1~22.04) 11.4.0
  • Protobuf Compiler & Version: buf 1.17.0
  • Protoc-gen-validate Version:
  • Protovalidate Version: 0.3.3 - 0.4.3

Possible Solution

n/a

Additional Context

n/a

[BUG] error evaluating repeated.unique: internal error: runtime error: invalid memory address or nil pointer dereference

Description

We encountered this in our Sentry error reporting from our connect-go validation interceptor (so the proto is probably valid):
runtime error: error evaluating repeated.unique: internal error: runtime error: invalid memory address or nil pointer dereference

Steps to Reproduce

The relevant proto message has a member which is of type ExperimentConfig, here is what it roughly looks like:

message ExperimentConfig {
  repeated ExperimentKey a = 1 [(buf.validate.field).repeated.unique = true];
  repeated ExperimentKey b = 1 [(buf.validate.field).repeated.unique = true];
}

// buf:lint:ignore ENUM_VALUE_PREFIX
enum ExperimentKey {
  // buf:lint:ignore ENUM_ZERO_VALUE_SUFFIX
  UNSPECIFIED = 0;
  ...
  reserved "foo", "bar", ...;
  reserved 10, 11, ...;
}

Expected Behavior

I expect no runtime error.

Actual Behavior

Some kind of runtime error.

Screenshots/Logs

I don't have additional information as this is on a client machine.

Environment

  • Operating System: Linux
  • Version: unknown
  • Compiler/Toolchain: Go 1.21.4
  • Protobuf Compiler & Version: protoc v3.19.4
  • Protoc-gen-validate Version: N/A
  • Protovalidate Version: github.com/bufbuild/protovalidate-go v0.4.3 and buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.31.0-20231115204500-e097f827e652.2

Possible Solution

N/A

Additional Context

N/A

gen proto issue

Which protoc extension is used for protovalidate-go generation? The github.com/envoyproxy/protoc-gen-validate/validate tool no longer exists

[BUG] Processing a message that contains multiple unique constraints causes a runtime error.

Description

If a single message contains multiple repeated fields, each of different types, and each type has a unique constraint, a runtime error occurs.

Steps to Reproduce

  1. Uncomment the following line:
  2. Run buf generate.
  3. Execute go test -run TestValidator_ValidateRepeatedFoo.

Expected Behavior

The test passes without a runtime error.

Actual Behavior

A runtime error occurs.

Screenshots/Logs

--- FAIL: TestValidator_ValidateRepeatedFoo (0.01s)
    validator_test.go:111:
                Error Trace:    /Users/kaz/Desktop/protovalidate-go/validator_test.go:111
                Error:          Received unexpected error:
                                runtime error: error evaluating repeated.unique: no such overload: unique(list)
                Test:           TestValidator_ValidateRepeatedFoo
FAIL
exit status 1
FAIL    github.com/bufbuild/protovalidate-go    0.254s

Environment

  • Operating System: macOS
  • Version: macOS Ventura 13.4.1
  • Compiler/Toolchain: go1.21.0 darwin/arm64
  • Protobuf Compiler & Version: buf v1.26.1
  • Protovalidate Version: v0.4.0

Possible Solution

As a workaround, it's suggested to implement the unique constraint as a custom constraint.
It seems to work when dynamically dispatched using dyn.

message MsgHasRepeated {
  repeated float x = 1 [
    (buf.validate.field).repeated = {
      max_items: 3,
      min_items: 1,
      items: {
        cel: {
          expression: "true",
          message: "intentional false"
        }
        float: {gt: 0}
      }
    },
    (buf.validate.field).cel = {
      /* workaround */
      expression: "dyn(this).unique()",
      message: "repeated value must contain unique items"
    }
  ];
  repeated string y = 2 [(buf.validate.field).cel = {
    /* workaround */
    expression: "dyn(this).unique()",
    message: "repeated value must contain unique items"
  }];
  repeated HasMsgExprs z = 3 [(buf.validate.field).repeated = {max_items: 2}];
}

Additional Context

The method loadOrCompileStandardConstraint in *github.com/bufbuild/protovalidate-go/internal/constraints.Cache uses only protoreflect FieldDescriptor as the cache key and does not consider the type of the field being constrained.
As a result, it seems that it might return an expression.ASTSet that calls the incorrect overload of the unique.

For instance, in the example above, looking inside the AST of the unique constraint for the y field reveals that the overload double_unique_bool has been chosen. (It's expected to select string_unique_bool.)

I think that this issue could be resolved by either making the implementation of the Standard constraint dynamically dispatched or by including type information when caching the compile results of the Standard constraint.

Part of the validation code is not generated

Description

I am testing the code in the 'example' directory. After using the 'buf generate proto' command, in the 'example.pb.validate.go' file, validation code has been generated for only a few fields. The validation code for fields like 'Id', 'Email', and 'Name' is empty. and the 'Home' field has not generated the primary validation code.

func (m *Person) validate(all bool) error {
	if m == nil {
		return nil
	}

	var errors []error

	// no validation rules for Id

	// no validation rules for Email

	// no validation rules for Name

	if all {
		switch v := interface{}(m.GetHome()).(type) {
		case interface{ ValidateAll() error }:
			if err := v.ValidateAll(); err != nil {
				errors = append(errors, PersonValidationError{
					field:  "Home",
					reason: "embedded message failed validation",
					cause:  err,
				})
			}
		case interface{ Validate() error }:
			if err := v.Validate(); err != nil {
				errors = append(errors, PersonValidationError{
					field:  "Home",
					reason: "embedded message failed validation",
					cause:  err,
				})
			}
		}
	} else if v, ok := interface{}(m.GetHome()).(interface{ Validate() error }); ok {
		if err := v.Validate(); err != nil {
			return PersonValidationError{
				field:  "Home",
				reason: "embedded message failed validation",
				cause:  err,
			}
		}
	}

	if len(errors) > 0 {
		return PersonMultiError(errors)
	}

	return nil
}

this is my buf.gen.yaml :

version: v1
managed:
  enabled: true
  go_package_prefix:
    default: tests.example.v1

plugins:
  - plugin: buf.build/protocolbuffers/go
    out: gen
    opt: paths=source_relative

  - plugin: buf.build/bufbuild/validate-go:v1.0.2
    out: gen
    opt:
      - paths=source_relative

Environment

  • Operating System: Windows 11
  • Compiler/Toolchain: go version go1.20.7 windows/amd64
  • Protobuf Compiler & Version: buf v1.27.1, protoc libprotoc 22.0-rc2
  • Protoc-gen-validate Version: 1.0.2
  • Protovalidate Version: v1.0.2

TestCode

validatetest.zip

Cannot build with bazel

Description

Build with bazel after adding protovalidate-go. Go builds succeed, but bazel builds fail on cel-go references. No proto compilation happens inside bazel, generated files are committed.

Screenshots/Logs

'@com_github_google_cel_go//cel:cel': target 'cel' not declared in package 'cel' defined by /external/com_github_google_cel_go/cel/BUILD.bazel and referenced by '@com_github_bufbuild_protovalidate_go//internal/celext:celext'
'@com_github_google_cel_go//common/overloads:overloads': target 'overloads' not declared in package 'common/overloads' defined by /external/com_github_google_cel_go/common/overloads/BUILD.bazel and referenced by '@com_github_bufbuild_protovalidate_go//internal/celext:celext'
'@com_github_google_cel_go//common/types:types': target 'types' not declared in package 'common/types' defined by /external/com_github_google_cel_go/common/types/BUILD.bazel and referenced by '@com_github_bufbuild_protovalidate_go//internal/celext:celext'
'@com_github_google_cel_go//common/types/ref:ref': target 'ref' not declared in package 'common/types/ref' defined by /external/com_github_google_cel_go/common/types/ref/BUILD.bazel and referenced by '@com_github_bufbuild_protovalidate_go//internal/celext:celext'
'@com_github_google_cel_go//common/types/traits:traits': target 'traits' not declared in package 'common/types/traits' defined by /external/com_github_google_cel_go/common/types/traits/BUILD.bazel and referenced by '@com_github_bufbuild_protovalidate_go//internal/celext:celext'
'@com_github_google_cel_go//ext:ext': target 'ext' not declared in package 'ext' defined by /external/com_github_google_cel_go/ext/BUILD.bazel and referenced by '@com_github_bufbuild_protovalidate_go//internal/celext:celext'
'@com_github_google_cel_go//cel:cel': target 'cel' not declared in package 'cel' defined by /external/com_github_google_cel_go/cel/BUILD.bazel and referenced by '@com_github_bufbuild_protovalidate_go//internal/expression:expression'
'@com_github_google_cel_go//interpreter:interpreter': target 'interpreter' not declared in package 'interpreter' defined by /external/com_github_google_cel_go/interpreter/BUILD.bazel and referenced by '@com_github_bufbuild_protovalidate_go//internal/expression:expression'
'@com_github_google_cel_go//cel:cel': target 'cel' not declared in package 'cel' defined by /external/com_github_google_cel_go/cel/BUILD.bazel and referenced by '@com_github_bufbuild_protovalidate_go//internal/constraints:constraints'
'@com_github_google_cel_go//cel:cel': target 'cel' not declared in package 'cel' defined by /external/com_github_google_cel_go/cel/BUILD.bazel and referenced by '@com_github_bufbuild_protovalidate_go//internal/evaluator:evaluator'

Environment

  • protovalidate-go v0.2.1
  • bazel v6.3.2
  • go v1.21.0
  • rules_go v0.41.0
  • gazelle v0.32.0
  • github.com/google/cel-go v0.17.1

Additional Context

[BUG] Cannot build with Bzlmod

Description

I'm using the upcoming bzlmod support in rules_buf and I've encountered this error.

ERROR: /private/var/tmp/_bazel_debkanchan/969f704a68afbe6aa6f76e61ecfa9c99/external/gazelle~0.34.0~go_deps~com_github_bufbuild_protovalidate_go/celext/BUILD.bazel:3:11: no such target '@gazelle~0.34.0~go_deps~com_github_google_cel_go//cel:cel': target 'cel' not declared in package 'cel' defined by /private/var/tmp/_bazel_debkanchan/969f704a68afbe6aa6f76e61ecfa9c99/external/gazelle~0.34.0~go_deps~com_github_google_cel_go/cel/BUILD.bazel (Tip: use `query "@@gazelle~0.34.0~go_deps~com_github_google_cel_go//cel:*"` to see all the targets in that package) and referenced by '@gazelle~0.34.0~go_deps~com_github_bufbuild_protovalidate_go//celext:celext'
ERROR: /private/var/tmp/_bazel_debkanchan/969f704a68afbe6aa6f76e61ecfa9c99/external/gazelle~0.34.0~go_deps~com_github_bufbuild_protovalidate_go/celext/BUILD.bazel:3:11: no such target '@gazelle~0.34.0~go_deps~com_github_google_cel_go//common/overloads:overloads': target 'overloads' not declared in package 'common/overloads' defined by /private/var/tmp/_bazel_debkanchan/969f704a68afbe6aa6f76e61ecfa9c99/external/gazelle~0.34.0~go_deps~com_github_google_cel_go/common/overloads/BUILD.bazel (did you mean 'overloads.go'? Tip: use `query "@@gazelle~0.34.0~go_deps~com_github_google_cel_go//common/overloads:*"` to see all the targets in that package) and referenced by '@gazelle~0.34.0~go_deps~com_github_bufbuild_protovalidate_go//celext:celext'
ERROR: /private/var/tmp/_bazel_debkanchan/969f704a68afbe6aa6f76e61ecfa9c99/external/gazelle~0.34.0~go_deps~com_github_bufbuild_protovalidate_go/celext/BUILD.bazel:3:11: no such target '@gazelle~0.34.0~go_deps~com_github_google_cel_go//common/types:types': target 'types' not declared in package 'common/types' defined by /private/var/tmp/_bazel_debkanchan/969f704a68afbe6aa6f76e61ecfa9c99/external/gazelle~0.34.0~go_deps~com_github_google_cel_go/common/types/BUILD.bazel (Tip: use `query "@@gazelle~0.34.0~go_deps~com_github_google_cel_go//common/types:*"` to see all the targets in that package) and referenced by '@gazelle~0.34.0~go_deps~com_github_bufbuild_protovalidate_go//celext:celext'
ERROR: /private/var/tmp/_bazel_debkanchan/969f704a68afbe6aa6f76e61ecfa9c99/external/gazelle~0.34.0~go_deps~com_github_bufbuild_protovalidate_go/celext/BUILD.bazel:3:11: no such target '@gazelle~0.34.0~go_deps~com_github_google_cel_go//common/types/ref:ref': target 'ref' not declared in package 'common/types/ref' defined by /private/var/tmp/_bazel_debkanchan/969f704a68afbe6aa6f76e61ecfa9c99/external/gazelle~0.34.0~go_deps~com_github_google_cel_go/common/types/ref/BUILD.bazel (Tip: use `query "@@gazelle~0.34.0~go_deps~com_github_google_cel_go//common/types/ref:*"` to see all the targets in that package) and referenced by '@gazelle~0.34.0~go_deps~com_github_bufbuild_protovalidate_go//celext:celext'
ERROR: /private/var/tmp/_bazel_debkanchan/969f704a68afbe6aa6f76e61ecfa9c99/external/gazelle~0.34.0~go_deps~com_github_bufbuild_protovalidate_go/celext/BUILD.bazel:3:11: no such target '@gazelle~0.34.0~go_deps~com_github_google_cel_go//common/types/traits:traits': target 'traits' not declared in package 'common/types/traits' defined by /private/var/tmp/_bazel_debkanchan/969f704a68afbe6aa6f76e61ecfa9c99/external/gazelle~0.34.0~go_deps~com_github_google_cel_go/common/types/traits/BUILD.bazel (Tip: use `query "@@gazelle~0.34.0~go_deps~com_github_google_cel_go//common/types/traits:*"` to see all the targets in that package) and referenced by '@gazelle~0.34.0~go_deps~com_github_bufbuild_protovalidate_go//celext:celext'
ERROR: /private/var/tmp/_bazel_debkanchan/969f704a68afbe6aa6f76e61ecfa9c99/external/gazelle~0.34.0~go_deps~com_github_bufbuild_protovalidate_go/celext/BUILD.bazel:3:11: no such target '@gazelle~0.34.0~go_deps~com_github_google_cel_go//ext:ext': target 'ext' not declared in package 'ext' defined by /private/var/tmp/_bazel_debkanchan/969f704a68afbe6aa6f76e61ecfa9c99/external/gazelle~0.34.0~go_deps~com_github_google_cel_go/ext/BUILD.bazel (Tip: use `query "@@gazelle~0.34.0~go_deps~com_github_google_cel_go//ext:*"` to see all the targets in that package) and referenced by '@gazelle~0.34.0~go_deps~com_github_bufbuild_protovalidate_go//celext:celext'

Steps to Reproduce

Environment

  • Operating System: macOS
  • Version: macOS 14.1.2 23B92 arm64
  • Compiler/Toolchain: Bazel
  • Protobuf Compiler & Version: buf 1.28.1
  • Protovalidate Version: v0.4.3

Possible Solution

in #27 a possible solution includes manually changing go_repository arguments in go_deps.bzl. Since bzlmod support means there's no go_deps.bazel file anymore where I could manually change settings I'm stuck as it seems.

Additional Context

MODULE

"""
This is the MODULE.bazel file. It defines the dependencies for the Bazel build system.
"""
bazel_dep(name = "rules_go", version = "0.42.0")

bazel_dep(name = "gazelle", version = "0.34.0", dev_dependency = True)

bazel_dep(name = "rules_pkg", version = "0.9.1")
bazel_dep(name = "rules_oci", version = "1.5.1")

# TODO: remove this later. See https://github.com/bazel-contrib/rules_oci/issues/425#issuecomment-1860211483
bazel_dep(name = "aspect_bazel_lib", version = "2.0.3")
bazel_dep(name = "container_structure_test", version = "1.16.0")

go_deps = use_extension("@gazelle//:extensions.bzl", "go_deps")
go_deps.from_file(go_mod = "//:go.mod")

use_repo(go_deps, "build_buf_gen_go_bufbuild_protovalidate_protocolbuffers_go", "build_buf_gen_go_ride_wallet_connectrpc_go", "build_buf_gen_go_ride_wallet_protocolbuffers_go", "com_connectrpc_connect", "com_github_bufbuild_protovalidate_go", "com_github_golang_jwt_jwt_v5", "com_github_google_wire", "com_github_ilyakaznacheev_cleanenv", "com_github_micahparks_keyfunc_v2", "com_github_mmcloughlin_geohash", "com_github_onsi_ginkgo_v2", "com_github_onsi_gomega", "com_google_cloud_go_firestore", "com_google_firebase_go_v4", "org_golang_google_genproto", "org_golang_google_genproto_googleapis_api", "org_golang_google_grpc", "org_golang_google_protobuf", "org_golang_x_net", "org_uber_go_mock", "org_uber_go_zap")

oci = use_extension("@rules_oci//oci:extensions.bzl", "oci")
oci.pull(
    name = "distroless",
    digest = "sha256:112a87f19e83c83711cc81ce8ed0b4d79acd65789682a6a272df57c4a0858534",
    image = "gcr.io/distroless/static",
    platforms = [
        "linux/amd64",
        "linux/arm64/v8",
    ],
)

# For each oci.pull call, repeat the "name" here to expose them as dependencies.
use_repo(oci, "distroless")

BUILD

load("@com_github_bazelbuild_buildtools//buildifier:def.bzl", "buildifier")
load("@gazelle//:def.bzl", "gazelle")

# gazelle:prefix github.com/ride-app/driver-service
# gazelle:build_file_name BUILD.bazel
# gazelle:exclude infra
# gazelle:proto disable_global
gazelle(
    name = "gazelle",
)

buildifier(
    name = "buildifier",
)

[BUG] Some red wavy line about importing when VSCode/Goland displaying generated code

Hi everyone!

I'm new to this repo, so it's maybe a foolish question. In fact, this issue may not a bug. Because my app work good with generated code.

Description

In generated code, there're some lines of importing:

import(
    _ "buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go/buf/validate"
)

VSCode marked it with red wavy line, and tell me there's an error here

could not import buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go/buf/validate (no required module provides package "buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go/buf/validate") compiler BrokenImport

But my app work good when I using go build.

I'm using .proto files like following:

syntax = "proto3";

package xxx;

import "api/grpc/entities/error.proto";
import "xxxxxx"; // some other proto files defined by me
import "buf/validate/validate.proto";
import "google/api/annotations.proto";
import "openapiv3/annotations.proto";

option go_package = "xxx";

service XXX{
    ......
}

......

I hope VSCode does not display red wavy lines

Steps to Reproduce

  1. Write proto files
  2. Use protoc to generate some pb code

Expected Behavior

No red wavy lines.

Actual Behavior

Screenshots/Logs

Environment

  • Operating System:
  • Version:
  • Compiler/Toolchain:
  • Protobuf Compiler & Version:
  • Protoc-gen-validate Version:
  • Protovalidate Version:

Possible Solution

Additional Context

[BUG] resolveDeprecatedIndex() isn't functioning.

Description

resolveDeprecatedIndex() isn't functioning.
In my project, the version of the buf module in protovalidate was fixed to commit 998a6885b3f74d08847b9c24571fdf08 in buf.lock.
Commit 998a6885b3f74d08847b9c24571fdf08 has an old extension field number of 51071.
Even so, protovalidate-go was functioning up to v0.3.2, but stopped working from v0.3.3.
I suspect the cause is in #57.

Steps to Reproduce

  1. In buf.yaml, you would include the following:
deps:
  - buf.build/bufbuild/protovalidate:998a6885b3f74d08847b9c24571fdf08
  1. buf mod update
  2. buf generate
  3. protovalidate-go functions with v0.3.2 but does not function with v0.3.3.

Expected Behavior

Even if the buf module version of protovalidate is old, [email protected] and later will function.

Actual Behavior

When the version of the buf module in protovalidate is commit 998a6885b3f74d08847b9c24571fdf08 and protovalidate-go is at v0.3.3, protovalidate-go does not function.

Screenshots/Logs

Nothing.

Environment

  • Operating System: macOS
  • Version: macOS 14.3.1
  • Compiler/Toolchain: Go1.22.2
  • Protobuf Compiler & Version: buf v1.28.1
  • Protoc-gen-validate Version: v0.3.3 and later
  • Protovalidate Version: commit 998a6885b3f74d08847b9c24571fdf08

Possible Solution

I suspect the cause is in #57.

Additional Context

Nothing.

[Question] What's the correct way to write cel expressions for validating a single item in a repeated field?

Description

Hello, I'm updating protovalidate used in my Go/Java/Python applications to the latest versions, and encountered an inconsistent behavior, but I couldn't determine if it's a bug or it's an expected behavior as I can't find any example that resembles my use case.

My use case of cel validation is to apply string functions to validate a list of strings. For example, I used an expression similar to the message below to achieve what I need:

message TestRepeatedOne {
  repeated string paths = 1 [ (buf.validate.field).repeated = {
    items : {
      string : {min_len : 1},
      cel : {
        message : "leading spaces are not allowed",
        expression : "!this.startsWith(' ')",
      },
    },
  } ];
}

My understanding was that the variable this refers to each single item in a repeated field, so in this case I supposed it should be a string. This works well in protovalidate for Java/Python even with the latest releases, but not with protovalidate-go. The change happened in #80 is the cause of my issue here. With protovalidate-go >= v0.4.2, the following code produces an error on the type of the string function I want to apply.

bufValidator, _ := protovalidate.New()
testRepeatedOneMsg := &pb.TestRepeatedOne{Paths: []string{" mypath"}}
err := bufValidator.Validate(testRepeatedOneMsg)

The error is

compilation error: failed to compile items constraints for repeated mytest.TestRepeatedOne.paths: compilation error: failed to compile expression : ERROR: <input>:1:17: found no matching overload for 'startsWith' applied to 'list(string).(string)'

I briefly checked underlying code and thought the this variable here now resolves to the list itself rather than individual items, so tried this:

message TestRepeatedTwo {
  repeated string paths = 1 [ (buf.validate.field).repeated = {
    items : {
      string : {min_len : 1},
      cel : {
        message : "leading spaces are not allowed",
        expression : "this.all(x, !x.startsWith(' '))",
      },
    },
  } ];
}

However, it also gives error:

runtime error: error evaluating : got 'types.String', expected iterable type

so my assumption about the passed in type is wrong.

I know I can always use a message level validator like this:

message TestRepeatedPass {
  repeated string paths = 1 [ (buf.validate.field).repeated = {
    items : {
      string : {min_len : 1},
    },
  } ];
  option (buf.validate.message).cel = {
    id : "paths.spaces",
    message : "leading spaces are not allowed",
    expression : "this.paths.all(x, !x.startsWith(' '))"
  };
}

for the same validation. The validation errors produces from this approach however don't contain the information about which item in the list fails, while items.cel could hint it. As such, I'd still like to know what's the correct way to compose such expressions. I ask this question here instead of bufbuild/protovalidate-go is that I'm not sure this is a spec problem or a bug in the Go implementation. If my understanding in TestRepeatedOne is correct, then probably this counts a bug in the Go implementation, and I can move this issue there.

Environment

  • Operating System: macOS/Linux
  • Version: macOS 14.2.1 Sonoma/Ubuntu 22.04
  • Compiler/Toolchain: Go 1.21.6
  • Protobuf Compiler & Version: protoc-gen-go v1.32.0, protoc v4.25.2
  • Protovalidate Version: protovalidate-go >= v0.4.2 (tried v0.5.0)

Additional Context

The test results for the same messages on protovalidate-python and protovalidate-java are attached by the way:

  • [email protected]: All three messages passed, although it looks like that the expression in TestRepeatedTwo resolved this as a single string and the x in the all macro is actually characters in the string.
  • [email protected]: TestRepeatedTwo failed to compile(Failed to compile expression), but I guess that's expected. TestRepeatedOne works as expected.

field mask validation at message level is not working

Here is my message

message MyMessage {
  ParentObj obj = 1;
  google.protobuf.FieldMask field_mask = 2;
 
  option (buf.validate.message).cel = {
    id: "obj.code",
    message: "code should be present"
    expression: "this.field_mask.paths.exists(p, p  == 'obj.code')"
  };
}

reason I was trying to perform this check at message level is to validate a nested field of ParentObj only when it is present in field mask. i.e, perform validation when it is specified in field mask. I was trying to use nested ternary operation to acheive it but in vain.
After some tests, realized that validation for field mask paths is not working at message level.

Even with or without obj.code in field mask, I see the error message for this validation error that code should be present.

Environment

  • Operating System: Windows
  • Version: windows 11
  • Compiler/Toolchain:
  • Protobuf Compiler & Version:
buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.33.0-20240401165935-b983156c5e99.1
connectrpc.com/validate v0.1.0
github.com/bufbuild/protovalidate-go v0.6.2 // indirect
google.golang.org/protobuf v1.33.0

Possible Solution

Additional Context

[BUG] undefined: atomic.Pointer v0.3.3

Description

Steps to Reproduce

  1. go get github.com/bufbuild/[email protected]
  2. kratos run

GO_PATH/pkg/mod/github.com/bufbuild/[email protected]/internal/evaluator/builder.go:34:21: undefined: atomic.Pointer

Expected Behavior

Actual Behavior

Program terminates

Screenshots/Logs

Environment

  • Operating System: linux>
  • Version: Ubuntu 20.04
  • Compiler/Toolchain: GCC 9.4.0
  • Protobuf Compiler & Version: protoc 24.x
  • Protoc-gen-validate Version: NA
  • Protovalidate Version: v1.31.0

Possible Solution

Additional Context

Works when download protovalidate-go to v0.2.0

[BUG] *.pb.validate.go Validate AND ValidateAll cannot use.

Description

when i use - plugin: buf.build/bufbuild/validate-go to generate validation, a *.pb.validate.go is generated, but no validation rule in function Validate or ValidateAll. if i want to use validate, i need to new a protovalidateor to validate the struct. is it excessive?

Steps to Reproduce

buf.gen.yaml

version: v1
managed:
  enabled: false
plugins:
  # generate go struct code
  - plugin: buf.build/protocolbuffers/go
    out: .
    opt:
      - paths=source_relative
  # generate message validator code
  - plugin: buf.build/bufbuild/validate-go
    out: .
    opt:
      - paths=source_relative
message Task {
  int64 id = 1;
  string name = 2 [
    (buf.validate.field).required = true,
    (buf.validate.field).string = {min_len: 1}
  ];
  string description = 3 [
    (buf.validate.field).ignore_empty = true,
    (buf.validate.field).string = {min_len: 1}
  ];
}

task.pb.validate.go

// Validate checks the field values on Task with the rules defined in the proto
// definition for this message. If any rules are violated, the first error
// encountered is returned, or nil if there are no violations.
func (m *Task) Validate() error {
	return m.validate(false)
}

// ValidateAll checks the field values on Task with the rules defined in the
// proto definition for this message. If any rules are violated, the result is
// a list of violation errors wrapped in TaskMultiError, or nil if none found.
func (m *Task) ValidateAll() error {
	return m.validate(true)
}

func (m *Task) validate(all bool) error {
	if m == nil {
		return nil
	}

	var errors []error

	// no validation rules for Id

	// no validation rules for Name

	// no validation rules for Description

	if len(errors) > 0 {
		return TaskMultiError(errors)
	}

	return nil
}

Expected Behavior

i can use task.Validate() or task.ValidateAll() to validate fields

Actual Behavior

all pass directly

Screenshots/Logs

Environment

  • Operating System: Linux
  • Version: Ubuntu 22.04
  • Compiler/Toolchain: Buf
  • Protobuf Compiler & Version:
  • Protoc-gen-validate Version:
  • Protovalidate Version: e097f827e65240ac9fd4b1158849a8fc
  • BufVersion: 1.28.1

Possible Solution

Additional Context

[BUG] Non-deterministic error values when using `string.uuid`

Description

After upgrading from 0.5 to 0.6 I get a non-deterministic behaviour when validating strings with the UUID validator when the string is empty. Half of the time I get value is empty, which is not a valid UUID, and half of the time I get value must be a valid UUID. I guess the first one might be better, but it would be good (especially for testing) if either one or the other is used.

Steps to Reproduce

  1. Use 0.6.0 of this library
  2. Use the constraint [(buf.validate.field).string.uuid = true];
  3. Set the field to an empty string

Expected Behavior

I would probably expect the new value is empty, which is not a valid UUID error string

Actual Behavior

Half of the time I get value is empty, which is not a valid UUID, and half of the time I get value must be a valid UUID

Environment

  • Operating System: maOS
  • Protobuf Compiler & Version: buf 1.29.0
  • Protovalidate Version: v0.6.0

StandardConstraintResolver interface needs to return errors

The current StandardConstraintResolver interface does not support returning errors, preferring the implementation to fail silently if it cannot resolve the constraints. But in practice, resolving the extension is fallible (very apparent in #57). This will be a breaking change to the interface and a behavioral change to the library (instead of failing silently and not producing a compilation error, the validator would return a compilation error caused by the resolver's error).

[BUG] Trying to set a zero value for an enum field which has a required rule

Description

what I was trying to accomplish

I was trying to set a zero value for an enum field which has a required rule.

Steps to Reproduce

  1. define a enum Status.
enum Status {
   STATUS_UNSPECIFIED = 0;
   STATUS_NORMAL = 1;
   STATUS_FORBIDDEN = 2;
   STATUS_UNVERIFIED = 3;
 }
  1. define a message with the Status filed and a required rule.
message ChangeUserStatusRequest{
  uint64 id = 1;
  Status status = 2[
    (buf.validate.field).required  = true
  ];
}
  1. call the grpc service
reqChangeStatus := &pb.ChangeUserStatusRequest{
	Id:     6,
	Status: pb.User_STATUS_UNSPECIFIED,
}
return cli.ChangeUserStatus(context.Background(), reqChangeStatus)

Expected Behavior

I expect that the call will pass the validation.

Actual Behavior

The validation will be failed.

Screenshots/Logs

{
  "code":  3,
  "message":  "request contains invalid arguments",
  "details":  [
    {
      "@type":  "type.googleapis.com/google.rpc.BadRequest",
      "field_violations":  [
        {
          "field":  "status",
          "description":  "value is required[required]"
        }
      ]
    }
  ]
}

Environment

  • Operating System: Windows
  • Version: Windows 10
  • Compiler/Toolchain:
  • Protobuf Compiler & Version: libprotoc 22.2
  • Protoc-gen-validate Version:
  • Protovalidate Version: v0.1.1

Possible Solution

Additional Context

[Feature Request] Allow extending default cel.Env created by protovalidate.New

Feature description:

It would be great if we could extend the default env with custom options, while using the builtin Validator.

Problem it solves or use case:

I'd like to add my own cel functions and/or modify existing ones, and have them be available when validating using a validator created by protovalidate.New(...).

Proposed implementation or solution:

One way could be to add a ValidatorOption like WithEnvOptions(...cel.EnvOption), which would do the following:

func New(options ...ValidatorOption) (*Validator, error) {
	cfg := config{resolver: resolver.DefaultResolver{}}
	for _, opt := range options {
		opt(&cfg)
	}

	env, err := celext.DefaultEnv(cfg.useUTC)
	if err != nil {
		return nil, fmt.Errorf(
			"failed to construct CEL environment: %w", err)
	}

+	if len(cfg.envOpts) > 0 {
+		env, err = env.Extend(cfg.envOpts...)
+		if err != nil {
+			return nil, fmt.Errorf(
+				"failed to extend CEL environment: %w", err)
+		}
+	}

	bldr := evaluator.NewBuilder(
		env,
		cfg.disableLazy,
		cfg.resolver,
		cfg.desc...,
	)

	return &Validator{
		failFast: cfg.failFast,
		builder:  bldr,
	}, nil
}

Then,

v, err := protovalidate.New(protovalidate.WithEnvOptions(...))
...

Contribution:

I would be willing to contribute this.

Examples or references:

Additional context:

Thanks! 😃

[QUESTION] validate.proto file not found after running "go get -u github.com/bufbuild/protovalidate-go"

Description

I am trying to use protovalidate-go in my go project in an effort to add input validation to a variable in my "api.proto" file. I am also using protovalidate interceptor from go-grpc-middleware to intercept any cases where there is invalid input.

Steps to Reproduce

  1. Run go get -u github.com/bufbuild/protovalidate-go
  2. Run protoc --proto_path=. --proto_path=../../../../go/pkg/mod/github.com/bufbuild/protovali
    [email protected] --go_out=. --go_opt=paths=source_relative --protovalidate_go_out=. api.proto [[go directory in my root folder]]
  3. Get the following errors:
    buf/validate/validate.proto: File not found.
    api.proto:6:1: Import "buf/validate/validate.proto" was not found or had errors.

Expected Behavior

Expected for it to find the validate.proto file but it doesn't. Please let me know if I am doing something wrong

Screenshots/Logs

image

Environment

  • Operating System: WSL 2
  • Version: Ubuntu 22.04.3 LTS

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.