From 0c403b246c4d451770aec57d2f060e8f00d1bbb4 Mon Sep 17 00:00:00 2001 From: Igor Tsiglyar Date: Wed, 20 Dec 2023 16:18:35 +0000 Subject: [PATCH] protovalidate: add option to ignore certain message types --- interceptors/protovalidate/options.go | 40 +++++++++++++++++++ interceptors/protovalidate/protovalidate.go | 13 +++--- .../protovalidate/protovalidate_test.go | 14 +++++++ 3 files changed, 62 insertions(+), 5 deletions(-) create mode 100644 interceptors/protovalidate/options.go diff --git a/interceptors/protovalidate/options.go b/interceptors/protovalidate/options.go new file mode 100644 index 000000000..645325d54 --- /dev/null +++ b/interceptors/protovalidate/options.go @@ -0,0 +1,40 @@ +// Copyright (c) The go-grpc-middleware Authors. +// Licensed under the Apache License 2.0. + +// Copyright 2017 David Ackroyd. All Rights Reserved. +// See LICENSE for licensing terms. + +package protovalidate + +import ( + "golang.org/x/exp/slices" + "google.golang.org/protobuf/reflect/protoreflect" +) + +type options struct { + ignoreMessages []protoreflect.MessageType +} + +type Option func(*options) + +func evaluateOpts(opts []Option) *options { + optCopy := &options{} + for _, o := range opts { + o(optCopy) + } + return optCopy +} + +// WithIgnoreMessages sets the messages that should be ignored as they are not yet ready +// for validation at the stage this middleware operates +func WithIgnoreMessages(msgs ...protoreflect.MessageType) Option { + return func(o *options) { + o.ignoreMessages = msgs + } +} + +func (o *options) shouldIgnoreMessage(m protoreflect.MessageType) bool { + return slices.ContainsFunc(o.ignoreMessages, func(t protoreflect.MessageType) bool { + return m == t + }) +} diff --git a/interceptors/protovalidate/protovalidate.go b/interceptors/protovalidate/protovalidate.go index 426fb5075..aabc8ec6b 100644 --- a/interceptors/protovalidate/protovalidate.go +++ b/interceptors/protovalidate/protovalidate.go @@ -12,13 +12,12 @@ import ( "google.golang.org/grpc/codes" "google.golang.org/grpc/status" "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/reflect/protoreflect" ) -// Option interface is currently empty and serves as a placeholder for potential future implementations. -// It allows adding new options without breaking existing code. -type Option interface { - unimplemented() -} +var ( + ignoreMessages []protoreflect.MessageType +) // UnaryServerInterceptor returns a new unary server interceptor that validates incoming messages. func UnaryServerInterceptor(validator *protovalidate.Validator, opts ...Option) grpc.UnaryServerInterceptor { @@ -28,8 +27,12 @@ func UnaryServerInterceptor(validator *protovalidate.Validator, opts ...Option) info *grpc.UnaryServerInfo, handler grpc.UnaryHandler, ) (resp interface{}, err error) { + o := evaluateOpts(opts) switch msg := req.(type) { case proto.Message: + if o.shouldIgnoreMessage(msg.ProtoReflect().Type()) { + break + } if err = validator.Validate(msg); err != nil { return nil, status.Error(codes.InvalidArgument, err.Error()) } diff --git a/interceptors/protovalidate/protovalidate_test.go b/interceptors/protovalidate/protovalidate_test.go index f5f0c586f..6fff35db9 100644 --- a/interceptors/protovalidate/protovalidate_test.go +++ b/interceptors/protovalidate/protovalidate_test.go @@ -50,6 +50,20 @@ func TestUnaryServerInterceptor(t *testing.T) { assert.Error(t, err) assert.Equal(t, codes.InvalidArgument, status.Code(err)) }) + + interceptor = protovalidate_middleware.UnaryServerInterceptor(validator, + protovalidate_middleware.WithIgnoreMessages(testvalidate.BadUnaryRequest.ProtoReflect().Type()), + ) + + t.Run("invalid_email_ignored", func(t *testing.T) { + info := &grpc.UnaryServerInfo{ + FullMethod: "FakeMethod", + } + + resp, err := interceptor(context.TODO(), testvalidate.BadUnaryRequest, info, handler) + assert.Nil(t, err) + assert.Equal(t, resp, "good") + }) } type server struct {