Skip to content

Commit

Permalink
Support extensions using indexes reserved for internal organization (#17
Browse files Browse the repository at this point in the history
)

Supplies a fallback mechanism for the deprecated extension index.
Essentially we search for the index that we want, if its not found we
look for the old one.

depends on bufbuild/protovalidate#43
  • Loading branch information
elliotmjackson authored Jul 11, 2023
1 parent 35189a0 commit eb52d52
Show file tree
Hide file tree
Showing 4 changed files with 81 additions and 18 deletions.
4 changes: 4 additions & 0 deletions .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -78,3 +78,7 @@ issues:
- path: internal/constraints/lookups.go
linters:
- gochecknoglobals
- path: internal/evaluator/resolver.go
linters:
# uses deprecated fields on protoimpl.ExtensionInfo but its the only way
- staticcheck
5 changes: 4 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ module github.com/bufbuild/protovalidate-go
go 1.18

require (
buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.31.0-20230609233005-3757a25ff0b9.1
buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.31.0-20230711201221-890d7e3584ce.1
github.com/envoyproxy/protoc-gen-validate v1.0.2
github.com/google/cel-go v0.16.0
github.com/stretchr/testify v1.8.4
Expand All @@ -13,11 +13,14 @@ require (
require (
github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230512164433-5d1fd1a340c9 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/kr/pretty v0.1.0 // indirect
github.com/kr/text v0.2.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/stoewer/go-strcase v1.3.0 // indirect
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 // indirect
golang.org/x/text v0.10.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20230530153820-e85fd2cbaebc // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc // indirect
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
14 changes: 11 additions & 3 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.31.0-20230609233005-3757a25ff0b9.1 h1:0nKm28ysQfpk1VP76OOcv33yyI92L2EQMtvhzCd7EXE=
buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.31.0-20230609233005-3757a25ff0b9.1/go.mod h1:xafc+XIsTxTy76GJQ1TKgvJWsSugFBqMaN27WhUblew=
buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.31.0-20230711201221-890d7e3584ce.1 h1:LtMTVxNFGQ/AkswvA3AQgSMBjZw9bvu642Mww5rn5Tg=
buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.31.0-20230711201221-890d7e3584ce.1/go.mod h1:xafc+XIsTxTy76GJQ1TKgvJWsSugFBqMaN27WhUblew=
github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230512164433-5d1fd1a340c9 h1:goHVqTbFX3AIo0tzGr14pgfAW2ZfPChKO21Z9MGf/gk=
github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230512164433-5d1fd1a340c9/go.mod h1:pSwJ0fSY5KhvocuWSx4fz3BA8OrA1bQn+K1Eli3BRwM=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
Expand All @@ -12,6 +13,12 @@ github.com/google/cel-go v0.16.0 h1:DG9YQ8nFCFXAs/FDDwBxmL1tpKNrdlGUM9U3537bX/Y=
github.com/google/cel-go v0.16.0/go.mod h1:HXZKzB0LXqer5lHHgfWAnlYwJaQBDKMjxjulNQzhwhY=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stoewer/go-strcase v1.3.0 h1:g0eASXYtp+yvN9fK8sH94oCIk0fau9uV1/ZdJ0AVEzs=
Expand All @@ -36,8 +43,9 @@ google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc/go.
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
76 changes: 62 additions & 14 deletions internal/evaluator/resolver.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,18 @@
package evaluator

import (
"strings"

"buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go/buf/validate"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/reflect/protoreflect"
"google.golang.org/protobuf/reflect/protoregistry"
"google.golang.org/protobuf/runtime/protoimpl"
)

const (
newExtensionIndex = "1159"
previousExtensionIndex = "51071"
)

type StandardConstraintResolver interface {
Expand All @@ -29,46 +38,85 @@ type StandardConstraintResolver interface {
type DefaultResolver struct{}

func (r DefaultResolver) ResolveMessageConstraints(desc protoreflect.MessageDescriptor) *validate.MessageConstraints {
return resolveExt[protoreflect.MessageDescriptor, *validate.MessageConstraints](desc, validate.E_Message)
constraints := resolveExt[protoreflect.MessageDescriptor, *validate.MessageConstraints](desc, validate.E_Message)
if constraints == nil {
constraints = resolveDeprecatedIndex[protoreflect.MessageDescriptor, *validate.MessageConstraints](desc, validate.E_Message)
}
return constraints
}

func (r DefaultResolver) ResolveOneofConstraints(desc protoreflect.OneofDescriptor) *validate.OneofConstraints {
return resolveExt[protoreflect.OneofDescriptor, *validate.OneofConstraints](desc, validate.E_Oneof)
constraints := resolveExt[protoreflect.OneofDescriptor, *validate.OneofConstraints](desc, validate.E_Oneof)
if constraints == nil {
constraints = resolveDeprecatedIndex[protoreflect.OneofDescriptor, *validate.OneofConstraints](desc, validate.E_Oneof)
}
return constraints
}

func (r DefaultResolver) ResolveFieldConstraints(desc protoreflect.FieldDescriptor) *validate.FieldConstraints {
return resolveExt[protoreflect.FieldDescriptor, *validate.FieldConstraints](desc, validate.E_Field)
constraints := resolveExt[protoreflect.FieldDescriptor, *validate.FieldConstraints](desc, validate.E_Field)
if constraints == nil {
constraints = resolveDeprecatedIndex[protoreflect.FieldDescriptor, *validate.FieldConstraints](desc, validate.E_Field)
}
return constraints
}

// resolveExt does not use proto.GetExtension in the event the underlying type
// of the extension is not the concrete type expected by the library. In some
// circumstances, particularly in dynamic or runtime contexts, the underlying
// extension value's type may be a dynamicpb.Message. In this case, we fall back
// through a proto.[Un]Marshal cycle to get it into the concrete type we expect.
func resolveExt[
D protoreflect.Descriptor,
C proto.Message,
](
func resolveExt[D protoreflect.Descriptor, C proto.Message](
desc D,
extType protoreflect.ExtensionType,
) (constraints C) {
opts := desc.Options().ProtoReflect()
fDesc := extType.TypeDescriptor()

if !opts.Has(fDesc) {
if opts.Has(fDesc) {
msg := opts.Get(fDesc).Message().Interface()
if m, ok := msg.(C); ok {
return m
}
}

unknown := opts.GetUnknown()
if len(unknown) == 0 {
return constraints
}

opts = opts.Type().New()
var resolver protoregistry.Types
if err := resolver.RegisterExtension(extType); err != nil {
return constraints
}
_ = proto.UnmarshalOptions{Resolver: &resolver}.Unmarshal(unknown, opts.Interface())

if opts.Has(fDesc) {
msg := opts.Get(fDesc).Message().Interface()
if m, ok := msg.(C); ok {
return m
}
}
msg := opts.Get(fDesc).Message().Interface()
if m, ok := msg.(C); ok {
return m
}

b, _ := proto.Marshal(msg)
constraints, ok := extType.New().Message().Interface().(C)
if !ok {
return constraints
}
_ = proto.Unmarshal(b, constraints)
return constraints
}

// resolveDeprecatedIndex is a fallback for the deprecated extension index.
func resolveDeprecatedIndex[D protoreflect.Descriptor, C proto.Message](
desc D,
ext *protoimpl.ExtensionInfo,
) C {
return resolveExt[D, C](desc, &protoimpl.ExtensionInfo{
ExtendedType: ext.ExtendedType,
ExtensionType: ext.ExtensionType,
Field: 51071,
Name: ext.Name,
Tag: strings.Replace(ext.Tag, newExtensionIndex, previousExtensionIndex, 1),
Filename: ext.Filename,
})
}

0 comments on commit eb52d52

Please sign in to comment.