diff --git a/examples/validate/api/example.pb.go b/examples/validate/api/example.pb.go new file mode 100644 index 000000000..341a1e049 --- /dev/null +++ b/examples/validate/api/example.pb.go @@ -0,0 +1,387 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.26.0 +// protoc v3.13.0 +// source: example.proto + +package v1 + +import ( + _ "github.com/envoyproxy/protoc-gen-validate/validate" + _ "google.golang.org/genproto/googleapis/api/annotations" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type Reply struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Message string `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"` +} + +func (x *Reply) Reset() { + *x = Reply{} + if protoimpl.UnsafeEnabled { + mi := &file_example_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Reply) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Reply) ProtoMessage() {} + +func (x *Reply) ProtoReflect() protoreflect.Message { + mi := &file_example_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Reply.ProtoReflect.Descriptor instead. +func (*Reply) Descriptor() ([]byte, []int) { + return file_example_proto_rawDescGZIP(), []int{0} +} + +func (x *Reply) GetMessage() string { + if x != nil { + return x.Message + } + return "" +} + +type Request struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id int64 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` + Age int32 `protobuf:"varint,2,opt,name=age,proto3" json:"age,omitempty"` + Code uint32 `protobuf:"varint,3,opt,name=code,proto3" json:"code,omitempty"` + Score float32 `protobuf:"fixed32,4,opt,name=score,proto3" json:"score,omitempty"` + State bool `protobuf:"varint,5,opt,name=state,proto3" json:"state,omitempty"` + Path string `protobuf:"bytes,6,opt,name=path,proto3" json:"path,omitempty"` + Phone string `protobuf:"bytes,7,opt,name=phone,proto3" json:"phone,omitempty"` + Explain string `protobuf:"bytes,8,opt,name=explain,proto3" json:"explain,omitempty"` + Name string `protobuf:"bytes,9,opt,name=name,proto3" json:"name,omitempty"` + Card string `protobuf:"bytes,10,opt,name=card,proto3" json:"card,omitempty"` + Info *Info `protobuf:"bytes,11,opt,name=info,proto3" json:"info,omitempty"` +} + +func (x *Request) Reset() { + *x = Request{} + if protoimpl.UnsafeEnabled { + mi := &file_example_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Request) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Request) ProtoMessage() {} + +func (x *Request) ProtoReflect() protoreflect.Message { + mi := &file_example_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Request.ProtoReflect.Descriptor instead. +func (*Request) Descriptor() ([]byte, []int) { + return file_example_proto_rawDescGZIP(), []int{1} +} + +func (x *Request) GetId() int64 { + if x != nil { + return x.Id + } + return 0 +} + +func (x *Request) GetAge() int32 { + if x != nil { + return x.Age + } + return 0 +} + +func (x *Request) GetCode() uint32 { + if x != nil { + return x.Code + } + return 0 +} + +func (x *Request) GetScore() float32 { + if x != nil { + return x.Score + } + return 0 +} + +func (x *Request) GetState() bool { + if x != nil { + return x.State + } + return false +} + +func (x *Request) GetPath() string { + if x != nil { + return x.Path + } + return "" +} + +func (x *Request) GetPhone() string { + if x != nil { + return x.Phone + } + return "" +} + +func (x *Request) GetExplain() string { + if x != nil { + return x.Explain + } + return "" +} + +func (x *Request) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *Request) GetCard() string { + if x != nil { + return x.Card + } + return "" +} + +func (x *Request) GetInfo() *Info { + if x != nil { + return x.Info + } + return nil +} + +type Info struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Address string `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` +} + +func (x *Info) Reset() { + *x = Info{} + if protoimpl.UnsafeEnabled { + mi := &file_example_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Info) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Info) ProtoMessage() {} + +func (x *Info) ProtoReflect() protoreflect.Message { + mi := &file_example_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Info.ProtoReflect.Descriptor instead. +func (*Info) Descriptor() ([]byte, []int) { + return file_example_proto_rawDescGZIP(), []int{2} +} + +func (x *Info) GetAddress() string { + if x != nil { + return x.Address + } + return "" +} + +var File_example_proto protoreflect.FileDescriptor + +var file_example_proto_rawDesc = []byte{ + 0x0a, 0x0d, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, + 0x0b, 0x61, 0x70, 0x69, 0x2e, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x1a, 0x1c, 0x67, 0x6f, + 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x17, 0x76, 0x61, 0x6c, 0x69, + 0x64, 0x61, 0x74, 0x65, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x22, 0x21, 0x0a, 0x05, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x18, 0x0a, 0x07, + 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, + 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x88, 0x03, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x17, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x42, 0x07, + 0xfa, 0x42, 0x04, 0x22, 0x02, 0x20, 0x00, 0x52, 0x02, 0x69, 0x64, 0x12, 0x1b, 0x0a, 0x03, 0x61, + 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x42, 0x09, 0xfa, 0x42, 0x06, 0x1a, 0x04, 0x10, + 0x78, 0x20, 0x00, 0x52, 0x03, 0x61, 0x67, 0x65, 0x12, 0x1f, 0x0a, 0x04, 0x63, 0x6f, 0x64, 0x65, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x42, 0x0b, 0xfa, 0x42, 0x08, 0x2a, 0x06, 0x30, 0x01, 0x30, + 0x02, 0x30, 0x03, 0x52, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x12, 0x25, 0x0a, 0x05, 0x73, 0x63, 0x6f, + 0x72, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x02, 0x42, 0x0f, 0xfa, 0x42, 0x0c, 0x0a, 0x0a, 0x3d, + 0x00, 0x00, 0x00, 0x00, 0x3d, 0xe1, 0xfa, 0xc7, 0x42, 0x52, 0x05, 0x73, 0x63, 0x6f, 0x72, 0x65, + 0x12, 0x1d, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x42, + 0x07, 0xfa, 0x42, 0x04, 0x6a, 0x02, 0x08, 0x01, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, + 0x21, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x42, 0x0d, 0xfa, + 0x42, 0x0a, 0x72, 0x08, 0x0a, 0x06, 0x2f, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x04, 0x70, 0x61, + 0x74, 0x68, 0x12, 0x1e, 0x0a, 0x05, 0x70, 0x68, 0x6f, 0x6e, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, + 0x09, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x72, 0x03, 0x98, 0x01, 0x0b, 0x52, 0x05, 0x70, 0x68, 0x6f, + 0x6e, 0x65, 0x12, 0x21, 0x0a, 0x07, 0x65, 0x78, 0x70, 0x6c, 0x61, 0x69, 0x6e, 0x18, 0x08, 0x20, + 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, 0x42, 0x04, 0x72, 0x02, 0x10, 0x03, 0x52, 0x07, 0x65, 0x78, + 0x70, 0x6c, 0x61, 0x69, 0x6e, 0x12, 0x1d, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x09, 0x20, + 0x01, 0x28, 0x09, 0x42, 0x09, 0xfa, 0x42, 0x06, 0x72, 0x04, 0x10, 0x01, 0x18, 0x0a, 0x52, 0x04, + 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x2a, 0x0a, 0x04, 0x63, 0x61, 0x72, 0x64, 0x18, 0x0a, 0x20, 0x01, + 0x28, 0x09, 0x42, 0x16, 0xfa, 0x42, 0x13, 0x72, 0x11, 0x32, 0x0f, 0x28, 0x3f, 0x69, 0x29, 0x5e, + 0x5b, 0x30, 0x2d, 0x39, 0x61, 0x2d, 0x66, 0x5d, 0x2b, 0x24, 0x52, 0x04, 0x63, 0x61, 0x72, 0x64, + 0x12, 0x2f, 0x0a, 0x04, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, + 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x49, 0x6e, 0x66, + 0x6f, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, 0x52, 0x04, 0x69, 0x6e, 0x66, + 0x6f, 0x22, 0x20, 0x0a, 0x04, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, + 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, + 0x65, 0x73, 0x73, 0x32, 0x64, 0x0a, 0x0e, 0x45, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x53, 0x65, + 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x52, 0x0a, 0x0c, 0x54, 0x65, 0x73, 0x74, 0x56, 0x61, 0x6c, + 0x69, 0x64, 0x61, 0x74, 0x65, 0x12, 0x14, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x65, 0x78, 0x61, 0x6d, + 0x70, 0x6c, 0x65, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x12, 0x2e, 0x61, 0x70, + 0x69, 0x2e, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, + 0x18, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x12, 0x22, 0x0d, 0x2f, 0x76, 0x31, 0x2f, 0x76, 0x61, 0x6c, + 0x69, 0x64, 0x61, 0x74, 0x65, 0x2f, 0x3a, 0x01, 0x2a, 0x42, 0x4c, 0x0a, 0x0f, 0x76, 0x61, 0x6c, + 0x69, 0x64, 0x61, 0x74, 0x65, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x50, 0x00, 0x5a, 0x37, + 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x6f, 0x2d, 0x6b, 0x72, + 0x61, 0x74, 0x6f, 0x73, 0x2f, 0x6b, 0x72, 0x61, 0x74, 0x6f, 0x73, 0x2f, 0x65, 0x78, 0x61, 0x6d, + 0x70, 0x6c, 0x65, 0x73, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x2f, 0x61, 0x70, + 0x69, 0x2f, 0x76, 0x31, 0x3b, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_example_proto_rawDescOnce sync.Once + file_example_proto_rawDescData = file_example_proto_rawDesc +) + +func file_example_proto_rawDescGZIP() []byte { + file_example_proto_rawDescOnce.Do(func() { + file_example_proto_rawDescData = protoimpl.X.CompressGZIP(file_example_proto_rawDescData) + }) + return file_example_proto_rawDescData +} + +var file_example_proto_msgTypes = make([]protoimpl.MessageInfo, 3) +var file_example_proto_goTypes = []interface{}{ + (*Reply)(nil), // 0: api.example.Reply + (*Request)(nil), // 1: api.example.Request + (*Info)(nil), // 2: api.example.Info +} +var file_example_proto_depIdxs = []int32{ + 2, // 0: api.example.Request.info:type_name -> api.example.Info + 1, // 1: api.example.ExampleService.TestValidate:input_type -> api.example.Request + 0, // 2: api.example.ExampleService.TestValidate:output_type -> api.example.Reply + 2, // [2:3] is the sub-list for method output_type + 1, // [1:2] is the sub-list for method input_type + 1, // [1:1] is the sub-list for extension type_name + 1, // [1:1] is the sub-list for extension extendee + 0, // [0:1] is the sub-list for field type_name +} + +func init() { file_example_proto_init() } +func file_example_proto_init() { + if File_example_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_example_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Reply); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_example_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Request); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_example_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Info); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_example_proto_rawDesc, + NumEnums: 0, + NumMessages: 3, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_example_proto_goTypes, + DependencyIndexes: file_example_proto_depIdxs, + MessageInfos: file_example_proto_msgTypes, + }.Build() + File_example_proto = out.File + file_example_proto_rawDesc = nil + file_example_proto_goTypes = nil + file_example_proto_depIdxs = nil +} diff --git a/examples/validate/api/example.pb.validate.go b/examples/validate/api/example.pb.validate.go new file mode 100644 index 000000000..ac0130ac5 --- /dev/null +++ b/examples/validate/api/example.pb.validate.go @@ -0,0 +1,493 @@ +// Code generated by protoc-gen-validate. DO NOT EDIT. +// source: example.proto + +package v1 + +import ( + "bytes" + "errors" + "fmt" + "net" + "net/mail" + "net/url" + "regexp" + "strings" + "time" + "unicode/utf8" + + "google.golang.org/protobuf/types/known/anypb" +) + +// ensure the imports are used +var ( + _ = bytes.MinRead + _ = errors.New("") + _ = fmt.Print + _ = utf8.UTFMax + _ = (*regexp.Regexp)(nil) + _ = (*strings.Reader)(nil) + _ = net.IPv4len + _ = time.Duration(0) + _ = (*url.URL)(nil) + _ = (*mail.Address)(nil) + _ = anypb.Any{} +) + +// Validate checks the field values on Reply 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 *Reply) Validate() error { + return m.validate(false) +} + +// ValidateAll checks the field values on Reply 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 ReplyMultiError, or nil if none found. +func (m *Reply) ValidateAll() error { + return m.validate(true) +} + +func (m *Reply) validate(all bool) error { + if m == nil { + return nil + } + + var errors []error + + // no validation rules for Message + + if len(errors) > 0 { + return ReplyMultiError(errors) + } + return nil +} + +// ReplyMultiError is an error wrapping multiple validation errors returned by +// Reply.ValidateAll() if the designated constraints aren't met. +type ReplyMultiError []error + +// Error returns a concatenation of all the error messages it wraps. +func (m ReplyMultiError) Error() string { + var msgs []string + for _, err := range m { + msgs = append(msgs, err.Error()) + } + return strings.Join(msgs, "; ") +} + +// AllErrors returns a list of validation violation errors. +func (m ReplyMultiError) AllErrors() []error { return m } + +// ReplyValidationError is the validation error returned by Reply.Validate if +// the designated constraints aren't met. +type ReplyValidationError struct { + field string + reason string + cause error + key bool +} + +// Field function returns field value. +func (e ReplyValidationError) Field() string { return e.field } + +// Reason function returns reason value. +func (e ReplyValidationError) Reason() string { return e.reason } + +// Cause function returns cause value. +func (e ReplyValidationError) Cause() error { return e.cause } + +// Key function returns key value. +func (e ReplyValidationError) Key() bool { return e.key } + +// ErrorName returns error name. +func (e ReplyValidationError) ErrorName() string { return "ReplyValidationError" } + +// Error satisfies the builtin error interface +func (e ReplyValidationError) Error() string { + cause := "" + if e.cause != nil { + cause = fmt.Sprintf(" | caused by: %v", e.cause) + } + + key := "" + if e.key { + key = "key for " + } + + return fmt.Sprintf( + "invalid %sReply.%s: %s%s", + key, + e.field, + e.reason, + cause) +} + +var _ error = ReplyValidationError{} + +var _ interface { + Field() string + Reason() string + Key() bool + Cause() error + ErrorName() string +} = ReplyValidationError{} + +// Validate checks the field values on Request 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 *Request) Validate() error { + return m.validate(false) +} + +// ValidateAll checks the field values on Request 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 RequestMultiError, or nil if none found. +func (m *Request) ValidateAll() error { + return m.validate(true) +} + +func (m *Request) validate(all bool) error { + if m == nil { + return nil + } + + var errors []error + + if m.GetId() <= 0 { + err := RequestValidationError{ + field: "Id", + reason: "value must be greater than 0", + } + if !all { + return err + } + errors = append(errors, err) + } + + if val := m.GetAge(); val <= 0 || val >= 120 { + err := RequestValidationError{ + field: "Age", + reason: "value must be inside range (0, 120)", + } + if !all { + return err + } + errors = append(errors, err) + } + + if _, ok := _Request_Code_InLookup[m.GetCode()]; !ok { + err := RequestValidationError{ + field: "Code", + reason: "value must be in list [1 2 3]", + } + if !all { + return err + } + errors = append(errors, err) + } + + if _, ok := _Request_Score_NotInLookup[m.GetScore()]; ok { + err := RequestValidationError{ + field: "Score", + reason: "value must not be in list [0 99.99]", + } + if !all { + return err + } + errors = append(errors, err) + } + + if m.GetState() != true { + err := RequestValidationError{ + field: "State", + reason: "value must equal true", + } + if !all { + return err + } + errors = append(errors, err) + } + + if m.GetPath() != "/hello" { + err := RequestValidationError{ + field: "Path", + reason: "value must equal /hello", + } + if !all { + return err + } + errors = append(errors, err) + } + + if utf8.RuneCountInString(m.GetPhone()) != 11 { + err := RequestValidationError{ + field: "Phone", + reason: "value length must be 11 runes", + } + if !all { + return err + } + errors = append(errors, err) + + } + + if utf8.RuneCountInString(m.GetExplain()) < 3 { + err := RequestValidationError{ + field: "Explain", + reason: "value length must be at least 3 runes", + } + if !all { + return err + } + errors = append(errors, err) + } + + if l := utf8.RuneCountInString(m.GetName()); l < 1 || l > 10 { + err := RequestValidationError{ + field: "Name", + reason: "value length must be between 1 and 10 runes, inclusive", + } + if !all { + return err + } + errors = append(errors, err) + } + + if !_Request_Card_Pattern.MatchString(m.GetCard()) { + err := RequestValidationError{ + field: "Card", + reason: "value does not match regex pattern \"(?i)^[0-9a-f]+$\"", + } + if !all { + return err + } + errors = append(errors, err) + } + + if m.GetInfo() == nil { + err := RequestValidationError{ + field: "Info", + reason: "value is required", + } + if !all { + return err + } + errors = append(errors, err) + } + + if all { + switch v := interface{}(m.GetInfo()).(type) { + case interface{ ValidateAll() error }: + if err := v.ValidateAll(); err != nil { + errors = append(errors, RequestValidationError{ + field: "Info", + reason: "embedded message failed validation", + cause: err, + }) + } + case interface{ Validate() error }: + if err := v.Validate(); err != nil { + errors = append(errors, RequestValidationError{ + field: "Info", + reason: "embedded message failed validation", + cause: err, + }) + } + } + } else if v, ok := interface{}(m.GetInfo()).(interface{ Validate() error }); ok { + if err := v.Validate(); err != nil { + return RequestValidationError{ + field: "Info", + reason: "embedded message failed validation", + cause: err, + } + } + } + + if len(errors) > 0 { + return RequestMultiError(errors) + } + return nil +} + +// RequestMultiError is an error wrapping multiple validation errors returned +// by Request.ValidateAll() if the designated constraints aren't met. +type RequestMultiError []error + +// Error returns a concatenation of all the error messages it wraps. +func (m RequestMultiError) Error() string { + var msgs []string + for _, err := range m { + msgs = append(msgs, err.Error()) + } + return strings.Join(msgs, "; ") +} + +// AllErrors returns a list of validation violation errors. +func (m RequestMultiError) AllErrors() []error { return m } + +// RequestValidationError is the validation error returned by Request.Validate +// if the designated constraints aren't met. +type RequestValidationError struct { + field string + reason string + cause error + key bool +} + +// Field function returns field value. +func (e RequestValidationError) Field() string { return e.field } + +// Reason function returns reason value. +func (e RequestValidationError) Reason() string { return e.reason } + +// Cause function returns cause value. +func (e RequestValidationError) Cause() error { return e.cause } + +// Key function returns key value. +func (e RequestValidationError) Key() bool { return e.key } + +// ErrorName returns error name. +func (e RequestValidationError) ErrorName() string { return "RequestValidationError" } + +// Error satisfies the builtin error interface +func (e RequestValidationError) Error() string { + cause := "" + if e.cause != nil { + cause = fmt.Sprintf(" | caused by: %v", e.cause) + } + + key := "" + if e.key { + key = "key for " + } + + return fmt.Sprintf( + "invalid %sRequest.%s: %s%s", + key, + e.field, + e.reason, + cause) +} + +var _ error = RequestValidationError{} + +var _ interface { + Field() string + Reason() string + Key() bool + Cause() error + ErrorName() string +} = RequestValidationError{} + +var _Request_Code_InLookup = map[uint32]struct{}{ + 1: {}, + 2: {}, + 3: {}, +} + +var _Request_Score_NotInLookup = map[float32]struct{}{ + 0: {}, + 99.99: {}, +} + +var _Request_Card_Pattern = regexp.MustCompile("(?i)^[0-9a-f]+$") + +// Validate checks the field values on Info 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 *Info) Validate() error { + return m.validate(false) +} + +// ValidateAll checks the field values on Info 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 InfoMultiError, or nil if none found. +func (m *Info) ValidateAll() error { + return m.validate(true) +} + +func (m *Info) validate(all bool) error { + if m == nil { + return nil + } + + var errors []error + + // no validation rules for Address + + if len(errors) > 0 { + return InfoMultiError(errors) + } + return nil +} + +// InfoMultiError is an error wrapping multiple validation errors returned by +// Info.ValidateAll() if the designated constraints aren't met. +type InfoMultiError []error + +// Error returns a concatenation of all the error messages it wraps. +func (m InfoMultiError) Error() string { + var msgs []string + for _, err := range m { + msgs = append(msgs, err.Error()) + } + return strings.Join(msgs, "; ") +} + +// AllErrors returns a list of validation violation errors. +func (m InfoMultiError) AllErrors() []error { return m } + +// InfoValidationError is the validation error returned by Info.Validate if the +// designated constraints aren't met. +type InfoValidationError struct { + field string + reason string + cause error + key bool +} + +// Field function returns field value. +func (e InfoValidationError) Field() string { return e.field } + +// Reason function returns reason value. +func (e InfoValidationError) Reason() string { return e.reason } + +// Cause function returns cause value. +func (e InfoValidationError) Cause() error { return e.cause } + +// Key function returns key value. +func (e InfoValidationError) Key() bool { return e.key } + +// ErrorName returns error name. +func (e InfoValidationError) ErrorName() string { return "InfoValidationError" } + +// Error satisfies the builtin error interface +func (e InfoValidationError) Error() string { + cause := "" + if e.cause != nil { + cause = fmt.Sprintf(" | caused by: %v", e.cause) + } + + key := "" + if e.key { + key = "key for " + } + + return fmt.Sprintf( + "invalid %sInfo.%s: %s%s", + key, + e.field, + e.reason, + cause) +} + +var _ error = InfoValidationError{} + +var _ interface { + Field() string + Reason() string + Key() bool + Cause() error + ErrorName() string +} = InfoValidationError{} diff --git a/examples/validate/api/example.proto b/examples/validate/api/example.proto new file mode 100644 index 000000000..35cc6e771 --- /dev/null +++ b/examples/validate/api/example.proto @@ -0,0 +1,43 @@ +syntax = "proto3"; + +package api.example; + +option go_package = "github.com/go-kratos/kratos/examples/validate/api/v1;v1"; +option java_multiple_files = true; +option java_package = "validate.api.v1"; + +import "google/api/annotations.proto"; +// the validate rules: +// https://github.com/envoyproxy/protoc-gen-validate +import "validate/validate.proto"; + +service ExampleService { + rpc TestValidate (Request) returns (Reply) { + option (google.api.http) = { + post: "/v1/validate" + body: "*" + }; + } +} + +message Reply { + string message = 1; +} + +message Request { + int64 id = 1 [(validate.rules).int64 = {gt: 0}]; + int32 age = 2 [(validate.rules).int32 = {gt:0, lt: 120}]; + uint32 code = 3 [(validate.rules).uint32 = {in: [1,2,3]}]; + float score = 4 [(validate.rules).float = {not_in: [0, 99.99]}]; + bool state = 5 [(validate.rules).bool.const = true]; + string path = 6 [(validate.rules).string.const = "/hello"]; + string phone = 7 [(validate.rules).string.len = 11]; + string explain = 8 [(validate.rules).string.min_len = 3]; + string name = 9 [(validate.rules).string = {min_len: 1, max_len: 10}]; + string card = 10 [(validate.rules).string.pattern = "(?i)^[0-9a-f]+$"]; + Info info = 11 [(validate.rules).message.required = true]; +} + +message Info { + string address = 1; +} \ No newline at end of file diff --git a/examples/validate/api/example_grpc.pb.go b/examples/validate/api/example_grpc.pb.go new file mode 100644 index 000000000..90fe42ce2 --- /dev/null +++ b/examples/validate/api/example_grpc.pb.go @@ -0,0 +1,101 @@ +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. + +package v1 + +import ( + context "context" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +// Requires gRPC-Go v1.32.0 or later. +const _ = grpc.SupportPackageIsVersion7 + +// ExampleServiceClient is the client API for ExampleService service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +type ExampleServiceClient interface { + TestValidate(ctx context.Context, in *Request, opts ...grpc.CallOption) (*Reply, error) +} + +type exampleServiceClient struct { + cc grpc.ClientConnInterface +} + +func NewExampleServiceClient(cc grpc.ClientConnInterface) ExampleServiceClient { + return &exampleServiceClient{cc} +} + +func (c *exampleServiceClient) TestValidate(ctx context.Context, in *Request, opts ...grpc.CallOption) (*Reply, error) { + out := new(Reply) + err := c.cc.Invoke(ctx, "/api.example.ExampleService/TestValidate", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// ExampleServiceServer is the server API for ExampleService service. +// All implementations must embed UnimplementedExampleServiceServer +// for forward compatibility +type ExampleServiceServer interface { + TestValidate(context.Context, *Request) (*Reply, error) + mustEmbedUnimplementedExampleServiceServer() +} + +// UnimplementedExampleServiceServer must be embedded to have forward compatible implementations. +type UnimplementedExampleServiceServer struct { +} + +func (UnimplementedExampleServiceServer) TestValidate(context.Context, *Request) (*Reply, error) { + return nil, status.Errorf(codes.Unimplemented, "method TestValidate not implemented") +} +func (UnimplementedExampleServiceServer) mustEmbedUnimplementedExampleServiceServer() {} + +// UnsafeExampleServiceServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to ExampleServiceServer will +// result in compilation errors. +type UnsafeExampleServiceServer interface { + mustEmbedUnimplementedExampleServiceServer() +} + +func RegisterExampleServiceServer(s grpc.ServiceRegistrar, srv ExampleServiceServer) { + s.RegisterService(&ExampleService_ServiceDesc, srv) +} + +func _ExampleService_TestValidate_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(Request) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ExampleServiceServer).TestValidate(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/api.example.ExampleService/TestValidate", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ExampleServiceServer).TestValidate(ctx, req.(*Request)) + } + return interceptor(ctx, in, info, handler) +} + +// ExampleService_ServiceDesc is the grpc.ServiceDesc for ExampleService service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var ExampleService_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "api.example.ExampleService", + HandlerType: (*ExampleServiceServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "TestValidate", + Handler: _ExampleService_TestValidate_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "example.proto", +} diff --git a/examples/validate/api/example_http.pb.go b/examples/validate/api/example_http.pb.go new file mode 100644 index 000000000..3e3dc4c86 --- /dev/null +++ b/examples/validate/api/example_http.pb.go @@ -0,0 +1,69 @@ +// Code generated by protoc-gen-go-http. DO NOT EDIT. +// versions: +// protoc-gen-go-http v2.0.0-rc3 + +package v1 + +import ( + context "context" + http "github.com/go-kratos/kratos/v2/transport/http" + binding "github.com/go-kratos/kratos/v2/transport/http/binding" +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the kratos package it is being compiled against. +var _ = new(context.Context) +var _ = binding.EncodeURL + +const _ = http.SupportPackageIsVersion1 + +type ExampleServiceHTTPServer interface { + TestValidate(context.Context, *Request) (*Reply, error) +} + +func RegisterExampleServiceHTTPServer(s *http.Server, srv ExampleServiceHTTPServer) { + r := s.Route("/") + r.POST("/v1/validate/", _ExampleService_TestValidate0_HTTP_Handler(srv)) +} + +func _ExampleService_TestValidate0_HTTP_Handler(srv ExampleServiceHTTPServer) func(ctx http.Context) error { + return func(ctx http.Context) error { + var in Request + if err := ctx.Bind(&in); err != nil { + return err + } + http.SetOperation(ctx, "/api.example.ExampleService/TestValidate") + h := ctx.Middleware(func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.TestValidate(ctx, req.(*Request)) + }) + out, err := h(ctx, &in) + if err != nil { + return err + } + reply := out.(*Reply) + return ctx.Result(200, reply) + } +} + +type ExampleServiceHTTPClient interface { + TestValidate(ctx context.Context, req *Request, opts ...http.CallOption) (rsp *Reply, err error) +} + +type ExampleServiceHTTPClientImpl struct { + cc *http.Client +} + +func NewExampleServiceHTTPClient(client *http.Client) ExampleServiceHTTPClient { + return &ExampleServiceHTTPClientImpl{client} +} + +func (c *ExampleServiceHTTPClientImpl) TestValidate(ctx context.Context, in *Request, opts ...http.CallOption) (*Reply, error) { + var out Reply + path := binding.EncodeURL("/v1/validate/", in, false) + opts = append(opts, http.Operation("/api.example.ExampleService/TestValidate")) + err := c.cc.Invoke(ctx, "POST", path, in, &out, opts...) + if err != nil { + return nil, err + } + return &out, err +} diff --git a/examples/validate/main.go b/examples/validate/main.go new file mode 100644 index 000000000..bd42edfbe --- /dev/null +++ b/examples/validate/main.go @@ -0,0 +1,55 @@ +package main + +import ( + "context" + "log" + + "github.com/go-kratos/kratos/examples/validate/api" + "github.com/go-kratos/kratos/v2" + "github.com/go-kratos/kratos/v2/middleware/validate" + "github.com/go-kratos/kratos/v2/transport/grpc" + "github.com/go-kratos/kratos/v2/transport/http" +) + +// go build -ldflags "-X main.Version=x.y.z" +var ( + // Name is the name of the compiled software. + Name = "errors" + // Version is the version of the compiled software. + Version = "v1.0.0" +) + +type server struct { + v1.UnimplementedExampleServiceServer +} + +func (s *server) TestValidate(ctx context.Context, in *v1.Request) (*v1.Reply, error) { + return &v1.Reply{Message: "ok"}, nil +} + +func main() { + s := &server{} + grpcSrv := grpc.NewServer( + grpc.Address(":9000"), + grpc.Middleware( + validate.Validator(), + )) + httpSrv := http.NewServer( + http.Address(":8000"), + http.Middleware( + validate.Validator(), + )) + v1.RegisterExampleServiceServer(grpcSrv, s) + v1.RegisterExampleServiceHTTPServer(httpSrv, s) + app := kratos.New( + kratos.Name(Name), + kratos.Server( + grpcSrv, + httpSrv, + ), + ) + + if err := app.Run(); err != nil { + log.Fatal(err) + } +}