Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Expand buf lint PROTOVALIDATE rule to lint example and predefined rules #3317

Merged
merged 39 commits into from
Oct 3, 2024
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
d56a84b
validate.Constraint -> shared.Constraint
oliversun9 Sep 5, 2024
5779a5e
add comment todos
oliversun9 Sep 5, 2024
c3d13e5
wip
oliversun9 Sep 10, 2024
ce41891
add comments and prints (to be removed); still need to get map to work
oliversun9 Sep 10, 2024
89e7440
checkpoint; works
oliversun9 Sep 11, 2024
cf88e02
checkpoint; seems to work
oliversun9 Sep 12, 2024
1ca775b
clean up shared rules
oliversun9 Sep 12, 2024
c664274
move
oliversun9 Sep 12, 2024
49452e0
small cleanup
oliversun9 Sep 12, 2024
e7f0f34
rearrange for less diff
oliversun9 Sep 13, 2024
0bfd6ff
rename
oliversun9 Sep 13, 2024
452d1af
add back reparsing, which was dropped by mistake; cleanup
oliversun9 Sep 13, 2024
b726162
add commit info
oliversun9 Sep 13, 2024
74c86d2
comment
oliversun9 Sep 13, 2024
c16b0bd
update protovalidate files in test
oliversun9 Sep 13, 2024
6d7f0a5
Merge branch 'main' into osun/lint-examples
oliversun9 Sep 13, 2024
558bbf9
Merge remote-tracking branch 'origin/main' into osun/lint-examples
doriable Sep 25, 2024
f50049d
Update shared rules -> predefined rules.
doriable Sep 25, 2024
282e462
Fix CI
doriable Sep 25, 2024
c6486f4
Fix CI for real
doriable Sep 25, 2024
bae26dd
Small refactors and address in-line TODOs
doriable Sep 25, 2024
46f4d81
Merge remote-tracking branch 'origin/main' into osun/lint-examples
doriable Sep 26, 2024
3ddf6c2
Remove the use of proto.{Marshal, Unmarshal}
doriable Sep 26, 2024
c36ef8f
Merge remote-tracking branch 'origin/main' into osun/lint-examples
doriable Sep 26, 2024
75898ac
Refactor again
doriable Sep 26, 2024
eae0426
Add test cases for examples and fix bugs found
doriable Sep 27, 2024
e1bb066
Handle type casting
doriable Sep 27, 2024
2bdb9ff
Address last todo
doriable Sep 27, 2024
e3c766a
Add predefined rules tests (WIP)
doriable Sep 30, 2024
b4c08ef
Merge remote-tracking branch 'origin/main' into osun/lint-examples
doriable Sep 30, 2024
48c393a
Merge remote-tracking branch 'origin/main' into osun/lint-examples
doriable Sep 30, 2024
97f260f
Upgrade fix from protovalidate-go and update some tests
doriable Oct 1, 2024
d0ec45a
Merge remote-tracking branch 'origin/main' into osun/lint-examples
doriable Oct 1, 2024
6019ed1
Fix CI
doriable Oct 1, 2024
49da005
Address comments
doriable Oct 1, 2024
4f73316
Update comment
doriable Oct 2, 2024
0571da6
Merge branch 'main' into osun/lint-examples
bufdev Oct 3, 2024
02d8762
Update CHANGELOG.md
bufdev Oct 3, 2024
a053f42
Merge branch 'osun/lint-examples' of https://github.com/bufbuild/buf …
bufdev Oct 3, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -120,3 +120,9 @@ require (
google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 // indirect
google.golang.org/grpc v1.66.0 // indirect
)

// TODO: remove
replace github.com/bufbuild/protovalidate-go => ../pvgojohn // commit e474f9b456a57181c0c8ad6785be9c9c2ab0a0be

// TODO: remove
replace buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go => ../pv-gen // commit 4fd7a369f04577fc71d8ff33ed733105aae84894
4 changes: 0 additions & 4 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
buf.build/gen/go/bufbuild/bufplugin/protocolbuffers/go v1.34.2-20240904181154-a0be11449112.2 h1:X9qBPcvWGOJs/CeRVLoxxLJwC/eKyWDS/G4nj+3KGMY=
buf.build/gen/go/bufbuild/bufplugin/protocolbuffers/go v1.34.2-20240904181154-a0be11449112.2/go.mod h1:B+9TKHRYqoAUW57pLjhkLOnBCu0DQYMV+f7imQ9nXwI=
buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.34.2-20240717164558-a6c49f84cc0f.2 h1:SZRVx928rbYZ6hEKUIN+vtGDkl7uotABRWGY4OAg5gM=
buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.34.2-20240717164558-a6c49f84cc0f.2/go.mod h1:ylS4c28ACSI59oJrOdW4pHS4n0Hw4TgSPHn8rpHl4Yw=
buf.build/gen/go/bufbuild/registry/connectrpc/go v1.16.2-20240821192916-45ba72cdd479.1 h1:QaJ6UkpvlGo4dBXR41vLRfPiKungbg7brjmbBC/k6Ig=
buf.build/gen/go/bufbuild/registry/connectrpc/go v1.16.2-20240821192916-45ba72cdd479.1/go.mod h1:oQsMFNU3YzxxjRS6O68UkcF/A+pXdXqQNcUfQEBTWcw=
buf.build/gen/go/bufbuild/registry/protocolbuffers/go v1.34.2-20240821192916-45ba72cdd479.2 h1:C3CTZTucEUm7i0O2tAM8GSlg23GnQYcljX1b1Jcpsro=
Expand Down Expand Up @@ -30,8 +28,6 @@ github.com/bufbuild/protocompile v0.14.1 h1:iA73zAf/fyljNjQKwYzUHD6AD4R8KMasmwa/
github.com/bufbuild/protocompile v0.14.1/go.mod h1:ppVdAIhbr2H8asPk6k4pY7t9zB1OU5DoEw9xY/FUi1c=
github.com/bufbuild/protoplugin v0.0.0-20240323223605-e2735f6c31ee h1:E6ET8YUcYJ1lAe6ctR3as7yqzW2BNItDFnaB5zQq/8M=
github.com/bufbuild/protoplugin v0.0.0-20240323223605-e2735f6c31ee/go.mod h1:HjGFxsck9RObrTJp2hXQZfWhPgZqnR6sR1U5fCA/Kus=
github.com/bufbuild/protovalidate-go v0.6.5 h1:WucDKXIbK22WjkO8A8J6Yyxxy0jl91Oe9LSMduq3YEE=
github.com/bufbuild/protovalidate-go v0.6.5/go.mod h1:LHDiGCWSM3GagZEnyEZ1sPtFwi6Ja4tVTi/DCc+iDFI=
github.com/bufbuild/protoyaml-go v0.1.12 h1:tIJrwvGxumVpNwLsw/AevT1QnkPDBuAObBSuBAdmAWY=
github.com/bufbuild/protoyaml-go v0.1.12/go.mod h1:Xmz3wct+08Va+g9gjIuLTAmxW2w6sre5Wrgw7K3gn0I=
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import (
"github.com/bufbuild/buf/private/pkg/stringutil"
"github.com/bufbuild/bufplugin-go/check"
"google.golang.org/protobuf/reflect/protoreflect"
"google.golang.org/protobuf/reflect/protoregistry"
"google.golang.org/protobuf/types/descriptorpb"
)

Expand Down Expand Up @@ -935,56 +936,90 @@ func handleLintPackageVersionSuffix(
}

// HandleLintProtovalidate is a handle function.
var HandleLintProtovalidate = bufcheckserverutil.NewMultiHandler(
// NOTE: Oneofs also have protovalidate support, but they only have a "required" field, so nothing to lint.
bufcheckserverutil.NewLintMessageRuleHandler(handleLintMessageProtovalidate),
bufcheckserverutil.NewLintFieldRuleHandler(handleLintFieldProtovalidate),
var HandleLintProtovalidate = bufcheckserverutil.NewRuleHandler(
func(
ctx context.Context,
responseWriter bufcheckserverutil.ResponseWriter,
request bufcheckserverutil.Request,
) error {
// TODO: refactor so that add func is no longer needed
oliversun9 marked this conversation as resolved.
Show resolved Hide resolved
addAnnotationFunc := func(
_ bufprotosource.Descriptor,
location bufprotosource.Location,
_ []bufprotosource.Location,
format string,
args ...interface{},
) {
responseWriter.AddProtosourceAnnotation(
location,
nil,
format,
args...,
)
}
extensionTypesFromRequest := new(protoregistry.Types)
// This for-loop checks that shared rules' cel expressions compile and add
// those that compile to the extension types, as a side effect. These types
// will be useful later on when example values are checked. Therefore, this
// loop must run before field and messages are checked.
for _, file := range request.ProtosourceFiles() {
// Regardless whether its file is an import, we want to add a shared rule
// extension to the types registry, as long as the extension's cel
// expressions compile.
// TODO: pass a nop addFunc the file is an import,
if err := bufprotosource.ForEachMessage(
func(message bufprotosource.Message) error {
for _, extension := range message.Extensions() {
if err := buflintvalidate.CheckAndRegisterSharedRuleExtension(
addAnnotationFunc,
extension,
extensionTypesFromRequest,
); err != nil {
return nil
}
}
return nil
},
file,
); err != nil {
return err
}
for _, extension := range file.Extensions() {
if err := buflintvalidate.CheckAndRegisterSharedRuleExtension(
addAnnotationFunc,
extension,
extensionTypesFromRequest,
); err != nil {
return nil
}
}
}
if err := bufcheckserverutil.NewLintMessageRuleHandler(
func(
_ bufcheckserverutil.ResponseWriter,
_ bufcheckserverutil.Request,
message bufprotosource.Message,
) error {
return buflintvalidate.CheckMessage(addAnnotationFunc, message)
}).Handle(ctx, responseWriter, request); err != nil {
return err
}
// At this point the extension types are already populated.
if err := bufcheckserverutil.NewLintFieldRuleHandler(
func(
_ bufcheckserverutil.ResponseWriter,
_ bufcheckserverutil.Request,
field bufprotosource.Field,
) error {
return buflintvalidate.CheckField(addAnnotationFunc, field, extensionTypesFromRequest)
}).Handle(ctx, responseWriter, request); err != nil {
return err
}
// NOTE: Oneofs also have protovalidate support, but they only have a "required" field, so nothing to lint.
return nil
},
)

func handleLintMessageProtovalidate(
responseWriter bufcheckserverutil.ResponseWriter,
_ bufcheckserverutil.Request,
message bufprotosource.Message,
) error {
addAnnotationFunc := func(
_ bufprotosource.Descriptor,
location bufprotosource.Location,
_ []bufprotosource.Location,
format string,
args ...interface{},
) {
responseWriter.AddProtosourceAnnotation(
location,
nil,
format,
args...,
)
}
return buflintvalidate.CheckMessage(addAnnotationFunc, message)
}

func handleLintFieldProtovalidate(
responseWriter bufcheckserverutil.ResponseWriter,
_ bufcheckserverutil.Request,
field bufprotosource.Field,
) error {
addAnnotationFunc := func(
_ bufprotosource.Descriptor,
location bufprotosource.Location,
_ []bufprotosource.Location,
format string,
args ...interface{},
) {
responseWriter.AddProtosourceAnnotation(
location,
nil,
format,
args...,
)
}
return buflintvalidate.CheckField(addAnnotationFunc, field)
}

// HandleLintRPCNoClientStreaming is a handle function.
var HandleLintRPCNoClientStreaming = bufcheckserverutil.NewLintMethodRuleHandler(handleLintRPCNoClientStreaming)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,31 @@
import (
"buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go/buf/validate"
"github.com/bufbuild/buf/private/bufpkg/bufprotosource"
"github.com/bufbuild/protovalidate-go/resolver"

Check failure on line 20 in private/bufpkg/bufcheck/bufcheckserver/internal/buflintvalidate/buflintvalidate.go

View workflow job for this annotation

GitHub Actions / buf-binary-size

github.com/bufbuild/protovalidate-go@v0.6.5: replacement directory ../pvgojohn does not exist

Check failure on line 20 in private/bufpkg/bufcheck/bufcheckserver/internal/buflintvalidate/buflintvalidate.go

View workflow job for this annotation

GitHub Actions / lint

github.com/bufbuild/protovalidate-go@v0.6.5: replacement directory ../pvgojohn does not exist

Check failure on line 20 in private/bufpkg/bufcheck/bufcheckserver/internal/buflintvalidate/buflintvalidate.go

View workflow job for this annotation

GitHub Actions / test

github.com/bufbuild/protovalidate-go@v0.6.5: replacement directory ../pvgojohn does not exist
"google.golang.org/protobuf/reflect/protoreflect"
"google.golang.org/protobuf/reflect/protoregistry"
)

// https://buf.build/bufbuild/protovalidate/docs/v0.5.1:buf.validate#buf.validate.MessageConstraints
const disabledFieldNumberInMesageConstraints = 1

// ExtensionTypeResolver is an extension resolver, the same type as the Resolver in proto.UnmarshalOptions.
type ExtensionTypeResolver interface {
FindExtensionByName(field protoreflect.FullName) (protoreflect.ExtensionType, error)
FindExtensionByNumber(message protoreflect.FullName, field protoreflect.FieldNumber) (protoreflect.ExtensionType, error)
}

// CheckAndRegisterSharedRuleExtension checks whether an extension extending a protovalidate rule
// is valid, checking that all of its CEL expressionus compile. If so, the extension type is added to
// the extension types passed in.
func CheckAndRegisterSharedRuleExtension(
addAnnotationFunc func(bufprotosource.Descriptor, bufprotosource.Location, []bufprotosource.Location, string, ...interface{}),
field bufprotosource.Field,
extensionTypesToPopulate *protoregistry.Types,
) error {
return checkAndRegisterSharedRuleExtension(addAnnotationFunc, field, extensionTypesToPopulate)
}

// CheckMessage validates that all rules on the message are valid, and any CEL expressions compile.
//
// addAnnotationFunc adds an annotation with the descriptor and location for check results.
Expand Down Expand Up @@ -62,6 +81,7 @@
func CheckField(
addAnnotationFunc func(bufprotosource.Descriptor, bufprotosource.Location, []bufprotosource.Location, string, ...interface{}),
field bufprotosource.Field,
extensionTypeResolver ExtensionTypeResolver,
) error {
return checkField(addAnnotationFunc, field)
return checkField(addAnnotationFunc, field, extensionTypeResolver)
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@

"buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go/buf/validate"
"github.com/bufbuild/buf/private/bufpkg/bufprotosource"
"github.com/bufbuild/protovalidate-go/celext"

Check failure on line 23 in private/bufpkg/bufcheck/bufcheckserver/internal/buflintvalidate/cel.go

View workflow job for this annotation

GitHub Actions / buf-binary-size

github.com/bufbuild/protovalidate-go@v0.6.5: replacement directory ../pvgojohn does not exist

Check failure on line 23 in private/bufpkg/bufcheck/bufcheckserver/internal/buflintvalidate/cel.go

View workflow job for this annotation

GitHub Actions / lint

github.com/bufbuild/protovalidate-go@v0.6.5: replacement directory ../pvgojohn does not exist

Check failure on line 23 in private/bufpkg/bufcheck/bufcheckserver/internal/buflintvalidate/cel.go

View workflow job for this annotation

GitHub Actions / test

github.com/bufbuild/protovalidate-go@v0.6.5: replacement directory ../pvgojohn does not exist
"github.com/google/cel-go/cel"
"github.com/google/cel-go/common/types"
"google.golang.org/protobuf/reflect/protoreflect"
Expand Down Expand Up @@ -109,14 +109,16 @@
return nil
}

// Returns true only if all cel expressions compile
func checkCEL(
celEnv *cel.Env,
celConstraints []*validate.Constraint,
parentName string,
parentNameCapitalized string,
celName string,
add func(int, string, ...interface{}),
) {
) bool {
allCelExpressionsCompile := true
idToConstraintIndices := make(map[string][]int, len(celConstraints))
for i, celConstraint := range celConstraints {
if celID := celConstraint.GetId(); celID != "" {
Expand All @@ -142,14 +144,14 @@
} else {
add(i, "%s has an empty %s.id. IDs should always be specified.", parentNameCapitalized, celName)
}
if len(strings.TrimSpace(celConstraint.Expression)) == 0 {
if len(strings.TrimSpace(celConstraint.GetExpression())) == 0 {
add(i, "%s has an empty %s.expression. Expressions should always be specified.", parentNameCapitalized, celName)
continue
}
ast, compileIssues := celEnv.Compile(celConstraint.Expression)
ast, compileIssues := celEnv.Compile(celConstraint.GetExpression())
switch {
case ast.OutputType().IsAssignableType(cel.BoolType):
if celConstraint.Message == "" {
if celConstraint.GetMessage() == "" {
add(
i,
"%s has an empty %s.message for an expression that evaluates to a boolean. If an expression evaluates to a boolean, a message should always be specified.",
Expand All @@ -158,7 +160,7 @@
)
}
case ast.OutputType().IsAssignableType(cel.StringType):
if celConstraint.Message != "" {
if celConstraint.GetMessage() != "" {
add(
i,
"%s has a %s with an expression that evaluates to a string, and also has a message. The message is redundant - since the expression evaluates to a string, its result will be printed instead of the message, so the message should be removed.",
Expand All @@ -179,6 +181,7 @@
)
}
if compileIssues.Err() != nil {
allCelExpressionsCompile = false
for _, parsedIssue := range parseCelIssuesText(compileIssues.Err().Error()) {
add(
i,
Expand All @@ -204,6 +207,7 @@
)
}
}
return allCelExpressionsCompile
}

// this depends on the undocumented behavior of cel-go's error message
Expand Down
Loading
Loading