diff --git a/plugin/validator/validator.go b/plugin/validator/validator.go index d45f76f753..6b5daa2876 100644 --- a/plugin/validator/validator.go +++ b/plugin/validator/validator.go @@ -233,7 +233,10 @@ func (p *plugin) generateProto3Message(file *generator.FileDescriptor, message * fmt.Fprintf(os.Stderr, "WARNING: field %v.%v is a nullable=false, validator.msg_exists has no effect\n", ccTypeName, fieldName) } } - if !nullable { + if nullable { + p.P(`if `, variableName, ` != nil {`) + p.In() + } else { // non-nullable fields in proto3 store actual structs, we need pointers to operate on interaces variableName = "&(" + variableName + ")" } @@ -242,6 +245,10 @@ func (p *plugin) generateProto3Message(file *generator.FileDescriptor, message * p.P(`return err`) p.Out() p.P(`}`) + if nullable { + p.Out() + p.P(`}`) + } } if repeated { // end the repeated loop diff --git a/test/validator/validator_proto2.pb.go b/test/validator/validator_proto2.pb.go index c9097183ea..4ff42c7042 100644 --- a/test/validator/validator_proto2.pb.go +++ b/test/validator/validator_proto2.pb.go @@ -3,7 +3,7 @@ // DO NOT EDIT! /* -Package validator_test is a generated protocol buffer package. +Package validatortest is a generated protocol buffer package. It is generated from these files: validator_proto2.proto @@ -11,7 +11,7 @@ It is generated from these files: It has these top-level messages: ValidatorMessage */ -package validator_test +package validatortest import proto "github.com/gogo/protobuf/proto" import math "math" @@ -153,39 +153,39 @@ func (m *ValidatorMessage_Embedded) GetSomeValue() int64 { return 0 } -var _regex_ValidatorMessage_StringReq = regexp.MustCompile(".{2,5}") -var _regex_ValidatorMessage_StringReqNonNull = regexp.MustCompile(".{2,5}") -var _regex_ValidatorMessage_StringOpt = regexp.MustCompile(".{2,5}") -var _regex_ValidatorMessage_StringOptNonNull = regexp.MustCompile(".{2,5}") +var _regex_ValidatorMessage_StringReq = regexp.MustCompile("^.{2,5}$") +var _regex_ValidatorMessage_StringReqNonNull = regexp.MustCompile("^.{2,5}$") +var _regex_ValidatorMessage_StringOpt = regexp.MustCompile("^.{2,5}$") +var _regex_ValidatorMessage_StringOptNonNull = regexp.MustCompile("^.{2,5}$") func (this *ValidatorMessage) Validate() error { if this.StringReq != nil { if !_regex_ValidatorMessage_StringReq.MatchString(*(this.StringReq)) { - return fmt.Errorf("validation error: ValidatorMessage.StringReq must conform to regex '.{2,5}'") + return fmt.Errorf("validation error: ValidatorMessage.StringReq must conform to regex '^.{2,5}$'") } } if !_regex_ValidatorMessage_StringReqNonNull.MatchString(this.StringReqNonNull) { - return fmt.Errorf("validation error: ValidatorMessage.StringReqNonNull must conform to regex '.{2,5}'") + return fmt.Errorf("validation error: ValidatorMessage.StringReqNonNull must conform to regex '^.{2,5}$'") } if this.StringOpt != nil { if !_regex_ValidatorMessage_StringOpt.MatchString(*(this.StringOpt)) { - return fmt.Errorf("validation error: ValidatorMessage.StringOpt must conform to regex '.{2,5}'") + return fmt.Errorf("validation error: ValidatorMessage.StringOpt must conform to regex '^.{2,5}$'") } } if !_regex_ValidatorMessage_StringOptNonNull.MatchString(this.StringOptNonNull) { - return fmt.Errorf("validation error: ValidatorMessage.StringOptNonNull must conform to regex '.{2,5}'") + return fmt.Errorf("validation error: ValidatorMessage.StringOptNonNull must conform to regex '^.{2,5}$'") } if this.IntReq != nil { - if !(*(this.IntReq) > 0) { - return fmt.Errorf("validation error: ValidatorMessage.IntReq must be greater than '0'") + if !(*(this.IntReq) > 10) { + return fmt.Errorf("validation error: ValidatorMessage.IntReq must be greater than '10'") } } if !(this.IntReqNonNull > 0) { return fmt.Errorf("validation error: ValidatorMessage.IntReqNonNull must be greater than '0'") } for _, item := range this.IntRep { - if !(item > 0) { - return fmt.Errorf("validation error: ValidatorMessage.IntRep must be greater than '0'") + if !(item > 10) { + return fmt.Errorf("validation error: ValidatorMessage.IntRep must be greater than '10'") } } for _, item := range this.IntRepNonNull { @@ -214,12 +214,12 @@ func (this *ValidatorMessage) Validate() error { return nil } -var _regex_ValidatorMessage_Embedded_Identifier = regexp.MustCompile("[a-z]{2,5}") +var _regex_ValidatorMessage_Embedded_Identifier = regexp.MustCompile("^[a-z]{2,5}$") func (this *ValidatorMessage_Embedded) Validate() error { if this.Identifier != nil { if !_regex_ValidatorMessage_Embedded_Identifier.MatchString(*(this.Identifier)) { - return fmt.Errorf("validation error: ValidatorMessage_Embedded.Identifier must conform to regex '[a-z]{2,5}'") + return fmt.Errorf("validation error: ValidatorMessage_Embedded.Identifier must conform to regex '^[a-z]{2,5}$'") } } if this.SomeValue != nil { diff --git a/test/validator/validator_proto2.proto b/test/validator/validator_proto2.proto index c7250ebd18..efde71cbf9 100644 --- a/test/validator/validator_proto2.proto +++ b/test/validator/validator_proto2.proto @@ -25,24 +25,24 @@ // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. syntax = "proto2"; -package validator_test; +package validatortest; import "github.com/gogo/protobuf/gogoproto/gogo.proto"; message ValidatorMessage { message Embedded { - optional string Identifier = 1 [(gogoproto.validator) = {regex: "[a-z]{2,5}"}]; + optional string Identifier = 1 [(gogoproto.validator) = {regex: "^[a-z]{2,5}$"}]; required int64 SomeValue = 2 [(gogoproto.validator) = {int_gt: 0, int_lt: 100}]; } - required string StringReq = 1 [(gogoproto.validator) = {regex: ".{2,5}"}]; - required string StringReqNonNull = 2 [(gogoproto.validator) = {regex: ".{2,5}"}, (gogoproto.nullable) = false]; - optional string StringOpt = 3 [(gogoproto.validator) = {regex: ".{2,5}"}]; - optional string StringOptNonNull = 4 [(gogoproto.validator) = {regex: ".{2,5}"}, (gogoproto.nullable) = false]; + required string StringReq = 1 [(gogoproto.validator) = {regex: "^.{2,5}$"}]; + required string StringReqNonNull = 2 [(gogoproto.validator) = {regex: "^.{2,5}$"}, (gogoproto.nullable) = false]; + optional string StringOpt = 3 [(gogoproto.validator) = {regex: "^.{2,5}$"}]; + optional string StringOptNonNull = 4 [(gogoproto.validator) = {regex: "^.{2,5}$"}, (gogoproto.nullable) = false]; - required uint32 IntReq = 6 [(gogoproto.validator) = {int_gt: 0}]; + required uint32 IntReq = 6 [(gogoproto.validator) = {int_gt: 10}]; required uint32 IntReqNonNull = 7 [(gogoproto.validator) = {int_gt: 0}, (gogoproto.nullable) = false]; - repeated uint32 IntRep = 8 [(gogoproto.validator) = {int_gt: 0}]; + repeated uint32 IntRep = 8 [(gogoproto.validator) = {int_gt: 10}]; repeated uint32 IntRepNonNull = 9 [(gogoproto.validator) = {int_gt: 0}, (gogoproto.nullable) = false]; diff --git a/test/validator/validator_proto3.pb.go b/test/validator/validator_proto3.pb.go index dfb80787cd..5418e5c7ee 100644 --- a/test/validator/validator_proto3.pb.go +++ b/test/validator/validator_proto3.pb.go @@ -3,7 +3,7 @@ // DO NOT EDIT! /* -Package validator_test is a generated protocol buffer package. +Package validatortest is a generated protocol buffer package. It is generated from these files: validator_proto3.proto @@ -11,7 +11,7 @@ It is generated from these files: It has these top-level messages: ValidatorMessage3 */ -package validator_test +package validatortest import proto "github.com/gogo/protobuf/proto" @@ -92,33 +92,35 @@ func (m *ValidatorMessage3_Embedded) Reset() { *m = ValidatorMessage3_Em func (m *ValidatorMessage3_Embedded) String() string { return proto.CompactTextString(m) } func (*ValidatorMessage3_Embedded) ProtoMessage() {} -var _regex_ValidatorMessage3_SomeString = regexp.MustCompile(".{2,5}") -var _regex_ValidatorMessage3_SomeStringRep = regexp.MustCompile(".{2,5}") +var _regex_ValidatorMessage3_SomeString = regexp.MustCompile("^.{2,5}$") +var _regex_ValidatorMessage3_SomeStringRep = regexp.MustCompile("^.{2,5}$") func (this *ValidatorMessage3) Validate() error { if !_regex_ValidatorMessage3_SomeString.MatchString(this.SomeString) { - return fmt.Errorf("validation error: ValidatorMessage3.SomeString must conform to regex '.{2,5}'") + return fmt.Errorf("validation error: ValidatorMessage3.SomeString must conform to regex '^.{2,5}$'") } for _, item := range this.SomeStringRep { if !_regex_ValidatorMessage3_SomeStringRep.MatchString(item) { - return fmt.Errorf("validation error: ValidatorMessage3.SomeStringRep must conform to regex '.{2,5}'") + return fmt.Errorf("validation error: ValidatorMessage3.SomeStringRep must conform to regex '^.{2,5}$'") } } - if !(this.SomeInt > 0) { - return fmt.Errorf("validation error: ValidatorMessage3.SomeInt must be greater than '0'") + if !(this.SomeInt > 10) { + return fmt.Errorf("validation error: ValidatorMessage3.SomeInt must be greater than '10'") } for _, item := range this.SomeIntRep { - if !(item > 0) { - return fmt.Errorf("validation error: ValidatorMessage3.SomeIntRep must be greater than '0'") + if !(item > 10) { + return fmt.Errorf("validation error: ValidatorMessage3.SomeIntRep must be greater than '10'") } } for _, item := range this.SomeIntRepNonNull { - if !(item > 0) { - return fmt.Errorf("validation error: ValidatorMessage3.SomeIntRepNonNull must be greater than '0'") + if !(item > 10) { + return fmt.Errorf("validation error: ValidatorMessage3.SomeIntRepNonNull must be greater than '10'") } } - if err := proto.CallValidatorIfExists(this.SomeEmbedded); err != nil { - return err + if this.SomeEmbedded != nil { + if err := proto.CallValidatorIfExists(this.SomeEmbedded); err != nil { + return err + } } if err := proto.CallValidatorIfExists(&(this.SomeEmbeddedNonNullable)); err != nil { return err @@ -126,15 +128,19 @@ func (this *ValidatorMessage3) Validate() error { if nil == this.SomeEmbeddedExists { return fmt.Errorf("validation error: ValidatorMessage3.SomeEmbeddedExists message must exist") } - if err := proto.CallValidatorIfExists(this.SomeEmbeddedExists); err != nil { - return err + if this.SomeEmbeddedExists != nil { + if err := proto.CallValidatorIfExists(this.SomeEmbeddedExists); err != nil { + return err + } } if err := proto.CallValidatorIfExists(&(this.SomeEmbeddedExistsNonNullable)); err != nil { return err } for _, item := range this.SomeEmbeddedRep { - if err := proto.CallValidatorIfExists(item); err != nil { - return err + if item != nil { + if err := proto.CallValidatorIfExists(item); err != nil { + return err + } } } for _, item := range this.SomeEmbeddedRepNonNullable { @@ -145,11 +151,11 @@ func (this *ValidatorMessage3) Validate() error { return nil } -var _regex_ValidatorMessage3_Embedded_Identifier = regexp.MustCompile("[a-z]{2,5}") +var _regex_ValidatorMessage3_Embedded_Identifier = regexp.MustCompile("^[a-z]{2,5}$") func (this *ValidatorMessage3_Embedded) Validate() error { if !_regex_ValidatorMessage3_Embedded_Identifier.MatchString(this.Identifier) { - return fmt.Errorf("validation error: ValidatorMessage3_Embedded.Identifier must conform to regex '[a-z]{2,5}'") + return fmt.Errorf("validation error: ValidatorMessage3_Embedded.Identifier must conform to regex '^[a-z]{2,5}$'") } if !(this.SomeValue > 0) { return fmt.Errorf("validation error: ValidatorMessage3_Embedded.SomeValue must be greater than '0'") diff --git a/test/validator/validator_proto3.proto b/test/validator/validator_proto3.proto index 4b4a83fbff..701bd551b7 100644 --- a/test/validator/validator_proto3.proto +++ b/test/validator/validator_proto3.proto @@ -25,21 +25,21 @@ // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. syntax = "proto3"; -package validator_test; +package validatortest; import "github.com/gogo/protobuf/gogoproto/gogo.proto"; message ValidatorMessage3 { message Embedded { - string Identifier = 1 [(gogoproto.validator) = {regex: "[a-z]{2,5}"}]; + string Identifier = 1 [(gogoproto.validator) = {regex: "^[a-z]{2,5}$"}]; int64 SomeValue = 2 [(gogoproto.validator) = {int_gt: 0, int_lt: 100}]; } - string SomeString = 1 [(gogoproto.validator) = {regex: ".{2,5}"}]; - repeated string SomeStringRep = 2 [(gogoproto.validator) = {regex: ".{2,5}"}]; + string SomeString = 1 [(gogoproto.validator) = {regex: "^.{2,5}$"}]; + repeated string SomeStringRep = 2 [(gogoproto.validator) = {regex: "^.{2,5}$"}]; - uint32 SomeInt = 6 [(gogoproto.validator) = {int_gt: 0}]; - repeated uint32 SomeIntRep = 7 [(gogoproto.validator) = {int_gt: 0}]; - repeated uint32 SomeIntRepNonNull = 8 [(gogoproto.validator) = {int_gt: 0}, (gogoproto.nullable) = false]; + uint32 SomeInt = 6 [(gogoproto.validator) = {int_gt: 10}]; + repeated uint32 SomeIntRep = 7 [(gogoproto.validator) = {int_gt: 10}]; + repeated uint32 SomeIntRepNonNull = 8 [(gogoproto.validator) = {int_gt: 10}, (gogoproto.nullable) = false]; Embedded someEmbedded = 10; Embedded someEmbeddedNonNullable = 11 [(gogoproto.nullable) = false]; diff --git a/test/validator/validator_test.go b/test/validator/validator_test.go index 812e3e3b03..c806ec5a3e 100644 --- a/test/validator/validator_test.go +++ b/test/validator/validator_test.go @@ -1,7 +1,130 @@ -package validator_test_test +package validatortest + import "testing" +func buildProto3(someString string, someInt uint32, identifier string, someValue int64) *ValidatorMessage3 { + goodEmbeddedProto3 := &ValidatorMessage3_Embedded{ + Identifier: identifier, + SomeValue: someValue, + } + + goodProto3 := &ValidatorMessage3{ + SomeString: someString, + SomeStringRep: []string{someString, "xyz34"}, + SomeInt: someInt, + SomeIntRep: []uint32{someInt, 12, 13, 14, 15, 16}, + SomeIntRepNonNull: []uint32{someInt, 102}, + SomeEmbedded: nil, + SomeEmbeddedNonNullable: *goodEmbeddedProto3, + SomeEmbeddedExists: goodEmbeddedProto3, + SomeEmbeddedExistsNonNullable: *goodEmbeddedProto3, + SomeEmbeddedRep: []*ValidatorMessage3_Embedded{goodEmbeddedProto3}, + SomeEmbeddedRepNonNullable: []ValidatorMessage3_Embedded{*goodEmbeddedProto3}, + } + return goodProto3 +} + +func buildProto2(someString string, someInt uint32, identifier string, someValue int64) *ValidatorMessage { + goodEmbeddedProto2 := &ValidatorMessage_Embedded{ + Identifier: &identifier, + SomeValue: &someValue, + } + + goodProto2 := &ValidatorMessage{ + StringReq: &someString, + StringReqNonNull: someString, + + StringOpt: nil, + StringOptNonNull: someString, + + IntReq: &someInt, + IntReqNonNull: someInt, + IntRep: []uint32{someInt, 12, 13, 14, 15, 16}, + IntRepNonNull: []uint32{someInt, 12, 13, 14, 15, 16}, + + EmbeddedReq: goodEmbeddedProto2, + EmbeddedNonNull: *goodEmbeddedProto2, + EmbeddedRep: []*ValidatorMessage_Embedded{goodEmbeddedProto2}, + EmbeddedRepNonNullable: []ValidatorMessage_Embedded{*goodEmbeddedProto2}, + } + return goodProto2 +} + +func TestGoodProto3(t *testing.T) { + var err error + goodProto3 := buildProto3("-%ab", 11, "abba", 99) + err = goodProto3.Validate() + if err != nil { + t.Fatalf("unexpected fail in validator: %v", err) + } +} -func TestSomeFoo(t *testing.T) { +func TestGoodProto2(t *testing.T) { + var err error + goodProto2 := buildProto2("-%ab", 11, "abba", 99) + err = goodProto2.Validate() + if err != nil { + t.Fatalf("unexpected fail in validator: %v", err) + } +} + +func TestStringRegex(t *testing.T) { + tooLong1Proto3 := buildProto3("toolong", 11, "abba", 99) + if tooLong1Proto3.Validate() == nil { + t.Fatalf("expected fail in validator, but it didn't happen") + } + tooLong2Proto3 := buildProto3("-%ab", 11, "bad#", 99) + if tooLong2Proto3.Validate() == nil { + t.Fatalf("expected fail in validator, but it didn't happen") + } + tooLong1Proto2 := buildProto2("toolong", 11, "abba", 99) + if tooLong1Proto2.Validate() == nil { + t.Fatalf("expected fail in validator, but it didn't happen") + } + tooLong2Proto2 := buildProto2("-%ab", 11, "bad#", 99) + if tooLong2Proto2.Validate() == nil { + t.Fatalf("expected fail in validator, but it didn't happen") + } +} + +func TestIntLowerBounds(t *testing.T) { + lowerThan10Proto3 := buildProto3("-%ab", 9, "abba", 99) + if lowerThan10Proto3.Validate() == nil { + t.Fatalf("expected fail in validator, but it didn't happen") + } + lowerThan10Proto2 := buildProto2("-%ab", 9, "abba", 99) + if lowerThan10Proto2.Validate() == nil { + t.Fatalf("expected fail in validator, but it didn't happen") + } + lowerThan0Proto3 := buildProto3("-%ab", 11, "abba", -1) + if lowerThan0Proto3.Validate() == nil { + t.Fatalf("expected fail in validator, but it didn't happen") + } + lowerThan0Proto2 := buildProto2("-%ab", 11, "abba", -1) + if lowerThan0Proto2.Validate() == nil { + t.Fatalf("expected fail in validator, but it didn't happen") + } +} + +func TestIntUpperBounds(t *testing.T) { + higherThan100Proto3 := buildProto3("-%ab", 11, "abba", 101) + if higherThan100Proto3.Validate() == nil { + t.Fatalf("expected fail in validator, but it didn't happen") + } + higherThan100Proto2 := buildProto2("-%ab", 11, "abba", 101) + if higherThan100Proto2.Validate() == nil { + t.Fatalf("expected fail in validator, but it didn't happen") + } +} +func TestMsgExist(t *testing.T) { + someProto3 := buildProto3("-%ab", 11, "abba", 99) + someProto3.SomeEmbedded = nil + if err := someProto3.Validate(); err != nil { + t.Fatalf("valiate shoudlnt fail on missing SomeEmbedded, not annotated") + } + someProto3.SomeEmbeddedExists = nil + if err := someProto3.Validate(); err == nil { + t.Fatalf("expected fail due to lacking SomeEmbeddedExists") + } }