From a99a1e9356517381f0428b5c74f7f7e1a7dc698c Mon Sep 17 00:00:00 2001 From: Qalifah Date: Wed, 6 Oct 2021 17:55:14 +0100 Subject: [PATCH] add MultiError type (#2297) --- errors/errors.go | 19 +++ errors/errors.pb.go | 269 ++++++++++++++++++++++++++++---------- errors/errors.pb.micro.go | 10 +- errors/errors.proto | 4 + errors/errors_test.go | 78 +++++++++++ go.mod | 1 + go.sum | 1 + 7 files changed, 305 insertions(+), 77 deletions(-) diff --git a/errors/errors.go b/errors/errors.go index 68a75fac..ac6df6c9 100644 --- a/errors/errors.go +++ b/errors/errors.go @@ -160,3 +160,22 @@ func As(err error) (*Error, bool) { } return nil, false } + +func NewMultiError() *MultiError { + return &MultiError{ + Errors: make([]*Error, 0), + } +} + +func (e *MultiError) Append(err *Error) { + e.Errors = append(e.Errors, err) +} + +func (e *MultiError) HasErrors() bool { + return len(e.Errors) > 0 +} + +func (e *MultiError) Error() string { + b, _ := json.Marshal(e) + return string(b) +} \ No newline at end of file diff --git a/errors/errors.pb.go b/errors/errors.pb.go index 90fda81a..172c3c95 100644 --- a/errors/errors.pb.go +++ b/errors/errors.pb.go @@ -1,102 +1,233 @@ // Code generated by protoc-gen-go. DO NOT EDIT. -// source: errors/errors.proto +// versions: +// protoc-gen-go v1.27.1 +// protoc v3.13.0 +// source: errors.proto package errors import ( - fmt "fmt" - proto "github.com/golang/protobuf/proto" - math "math" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" ) -// Reference imports to suppress errors if they are not otherwise used. -var _ = proto.Marshal -var _ = fmt.Errorf -var _ = math.Inf - -// This is a compile-time assertion to ensure that this generated file -// is compatible with the proto package it is being compiled against. -// A compilation error at this line likely means your copy of the -// proto package needs to be updated. -const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package +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 Error struct { - Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` - Code int32 `protobuf:"varint,2,opt,name=code,proto3" json:"code,omitempty"` - Detail string `protobuf:"bytes,3,opt,name=detail,proto3" json:"detail,omitempty"` - Status string `protobuf:"bytes,4,opt,name=status,proto3" json:"status,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + Code int32 `protobuf:"varint,2,opt,name=code,proto3" json:"code,omitempty"` + Detail string `protobuf:"bytes,3,opt,name=detail,proto3" json:"detail,omitempty"` + Status string `protobuf:"bytes,4,opt,name=status,proto3" json:"status,omitempty"` } -func (m *Error) Reset() { *m = Error{} } -func (m *Error) String() string { return proto.CompactTextString(m) } -func (*Error) ProtoMessage() {} +func (x *Error) Reset() { + *x = Error{} + if protoimpl.UnsafeEnabled { + mi := &file_errors_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Error) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Error) ProtoMessage() {} + +func (x *Error) ProtoReflect() protoreflect.Message { + mi := &file_errors_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 Error.ProtoReflect.Descriptor instead. func (*Error) Descriptor() ([]byte, []int) { - return fileDescriptor_85c4eef3398a32b2, []int{0} + return file_errors_proto_rawDescGZIP(), []int{0} } -func (m *Error) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_Error.Unmarshal(m, b) -} -func (m *Error) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_Error.Marshal(b, m, deterministic) -} -func (m *Error) XXX_Merge(src proto.Message) { - xxx_messageInfo_Error.Merge(m, src) -} -func (m *Error) XXX_Size() int { - return xxx_messageInfo_Error.Size(m) -} -func (m *Error) XXX_DiscardUnknown() { - xxx_messageInfo_Error.DiscardUnknown(m) -} - -var xxx_messageInfo_Error proto.InternalMessageInfo - -func (m *Error) GetId() string { - if m != nil { - return m.Id +func (x *Error) GetId() string { + if x != nil { + return x.Id } return "" } -func (m *Error) GetCode() int32 { - if m != nil { - return m.Code +func (x *Error) GetCode() int32 { + if x != nil { + return x.Code } return 0 } -func (m *Error) GetDetail() string { - if m != nil { - return m.Detail +func (x *Error) GetDetail() string { + if x != nil { + return x.Detail } return "" } -func (m *Error) GetStatus() string { - if m != nil { - return m.Status +func (x *Error) GetStatus() string { + if x != nil { + return x.Status } return "" } -func init() { - proto.RegisterType((*Error)(nil), "errors.Error") +type MultiError struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Errors []*Error `protobuf:"bytes,1,rep,name=errors,proto3" json:"errors,omitempty"` } -func init() { proto.RegisterFile("errors/errors.proto", fileDescriptor_85c4eef3398a32b2) } - -var fileDescriptor_85c4eef3398a32b2 = []byte{ - // 118 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x12, 0x4e, 0x2d, 0x2a, 0xca, - 0x2f, 0x2a, 0xd6, 0x87, 0x50, 0x7a, 0x05, 0x45, 0xf9, 0x25, 0xf9, 0x42, 0x6c, 0x10, 0x9e, 0x52, - 0x34, 0x17, 0xab, 0x2b, 0x88, 0x25, 0xc4, 0xc7, 0xc5, 0x94, 0x99, 0x22, 0xc1, 0xa8, 0xc0, 0xa8, - 0xc1, 0x19, 0xc4, 0x94, 0x99, 0x22, 0x24, 0xc4, 0xc5, 0x92, 0x9c, 0x9f, 0x92, 0x2a, 0xc1, 0xa4, - 0xc0, 0xa8, 0xc1, 0x1a, 0x04, 0x66, 0x0b, 0x89, 0x71, 0xb1, 0xa5, 0xa4, 0x96, 0x24, 0x66, 0xe6, - 0x48, 0x30, 0x83, 0xd5, 0x41, 0x79, 0x20, 0xf1, 0xe2, 0x92, 0xc4, 0x92, 0xd2, 0x62, 0x09, 0x16, - 0x88, 0x38, 0x84, 0x97, 0xc4, 0x06, 0xb6, 0xcb, 0x18, 0x10, 0x00, 0x00, 0xff, 0xff, 0xca, 0xc3, - 0xcb, 0x29, 0x82, 0x00, 0x00, 0x00, +func (x *MultiError) Reset() { + *x = MultiError{} + if protoimpl.UnsafeEnabled { + mi := &file_errors_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *MultiError) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*MultiError) ProtoMessage() {} + +func (x *MultiError) ProtoReflect() protoreflect.Message { + mi := &file_errors_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 MultiError.ProtoReflect.Descriptor instead. +func (*MultiError) Descriptor() ([]byte, []int) { + return file_errors_proto_rawDescGZIP(), []int{1} +} + +func (x *MultiError) GetErrors() []*Error { + if x != nil { + return x.Errors + } + return nil +} + +var File_errors_proto protoreflect.FileDescriptor + +var file_errors_proto_rawDesc = []byte{ + 0x0a, 0x0c, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x06, + 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x22, 0x5b, 0x0a, 0x05, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x12, + 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, + 0x12, 0x0a, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x04, 0x63, + 0x6f, 0x64, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x06, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x12, 0x16, 0x0a, 0x06, 0x73, + 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, + 0x74, 0x75, 0x73, 0x22, 0x33, 0x0a, 0x0a, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x45, 0x72, 0x72, 0x6f, + 0x72, 0x12, 0x25, 0x0a, 0x06, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x0d, 0x2e, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x2e, 0x45, 0x72, 0x72, 0x6f, 0x72, + 0x52, 0x06, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x42, 0x03, 0x5a, 0x01, 0x2e, 0x62, 0x06, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_errors_proto_rawDescOnce sync.Once + file_errors_proto_rawDescData = file_errors_proto_rawDesc +) + +func file_errors_proto_rawDescGZIP() []byte { + file_errors_proto_rawDescOnce.Do(func() { + file_errors_proto_rawDescData = protoimpl.X.CompressGZIP(file_errors_proto_rawDescData) + }) + return file_errors_proto_rawDescData +} + +var file_errors_proto_msgTypes = make([]protoimpl.MessageInfo, 2) +var file_errors_proto_goTypes = []interface{}{ + (*Error)(nil), // 0: errors.Error + (*MultiError)(nil), // 1: errors.MultiError +} +var file_errors_proto_depIdxs = []int32{ + 0, // 0: errors.MultiError.errors:type_name -> errors.Error + 1, // [1:1] is the sub-list for method output_type + 1, // [1:1] 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_errors_proto_init() } +func file_errors_proto_init() { + if File_errors_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_errors_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Error); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_errors_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*MultiError); 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_errors_proto_rawDesc, + NumEnums: 0, + NumMessages: 2, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_errors_proto_goTypes, + DependencyIndexes: file_errors_proto_depIdxs, + MessageInfos: file_errors_proto_msgTypes, + }.Build() + File_errors_proto = out.File + file_errors_proto_rawDesc = nil + file_errors_proto_goTypes = nil + file_errors_proto_depIdxs = nil } diff --git a/errors/errors.pb.micro.go b/errors/errors.pb.micro.go index 7a26efe7..3c1233d6 100644 --- a/errors/errors.pb.micro.go +++ b/errors/errors.pb.micro.go @@ -1,11 +1,11 @@ // Code generated by protoc-gen-micro. DO NOT EDIT. -// source: errors/errors.proto +// source: errors.proto package errors import ( fmt "fmt" - proto "github.com/golang/protobuf/proto" + proto "google.golang.org/protobuf/proto" math "math" ) @@ -13,9 +13,3 @@ import ( var _ = proto.Marshal var _ = fmt.Errorf var _ = math.Inf - -// This is a compile-time assertion to ensure that this generated file -// is compatible with the proto package it is being compiled against. -// A compilation error at this line likely means your copy of the -// proto package needs to be updated. -const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package diff --git a/errors/errors.proto b/errors/errors.proto index e9d51ff3..b90b134b 100644 --- a/errors/errors.proto +++ b/errors/errors.proto @@ -8,3 +8,7 @@ message Error { string detail = 3; string status = 4; }; + +message MultiError { + repeated Error errors = 1; +} \ No newline at end of file diff --git a/errors/errors_test.go b/errors/errors_test.go index f4051abb..3a533df5 100644 --- a/errors/errors_test.go +++ b/errors/errors_test.go @@ -98,3 +98,81 @@ func TestAs(t *testing.T) { t.Fatalf("nil should not convert to *Error") } } + +func TestAppend(t *testing.T) { + mError := NewMultiError() + testData := []*Error{ + { + Id: "test1", + Code: 500, + Detail: "Internal server error", + Status: http.StatusText(500), + }, + { + Id: "test2", + Code: 400, + Detail: "Bad Request", + Status: http.StatusText(400), + }, + { + Id: "test3", + Code: 404, + Detail: "Not Found", + Status: http.StatusText(404), + }, + } + + for _, e := range testData { + mError.Append(&Error{ + Id: e.Id, + Code: e.Code, + Detail: e.Detail, + Status: e.Status, + }) + } + + if len(mError.Errors) != 3 { + t.Fatalf("Expected 3 got %v", len(mError.Errors)) + } +} + +func TestHasErrors(t *testing.T) { + mError := NewMultiError() + testData := []*Error{ + { + Id: "test1", + Code: 500, + Detail: "Internal server error", + Status: http.StatusText(500), + }, + { + Id: "test2", + Code: 400, + Detail: "Bad Request", + Status: http.StatusText(400), + }, + { + Id: "test3", + Code: 404, + Detail: "Not Found", + Status: http.StatusText(404), + }, + } + + if mError.HasErrors() { + t.Fatal("Expected no error") + } + + for _, e := range testData { + mError.Errors = append(mError.Errors, &Error{ + Id: e.Id, + Code: e.Code, + Detail: e.Detail, + Status: e.Status, + }) + } + + if !mError.HasErrors() { + t.Fatal("Expected errors") + } +} \ No newline at end of file diff --git a/go.mod b/go.mod index 21869e31..8b8a83ca 100644 --- a/go.mod +++ b/go.mod @@ -28,4 +28,5 @@ require ( golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a golang.org/x/net v0.0.0-20210510120150-4163338589ed golang.org/x/sync v0.0.0-20210220032951-036812b2e83c + google.golang.org/protobuf v1.26.0 ) diff --git a/go.sum b/go.sum index 1cb63fdb..799fc58a 100644 --- a/go.sum +++ b/go.sum @@ -151,6 +151,7 @@ github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5Kwzbycv github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= github.com/felixge/httpsnoop v1.0.1 h1:lvB5Jl89CsZtGIWuTcDM1E/vkVs49/Ml7JJe07l8SPQ= github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= +github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 h1:BHsljHzVlRcyQhjrss6TZTdY2VfCqZPbv5k3iBFa2ZQ= github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=