From d258e38d53611bf3c418702e061c14f9858cd0c6 Mon Sep 17 00:00:00 2001 From: jonkofee Date: Wed, 16 Oct 2024 14:39:43 +0300 Subject: [PATCH 1/3] use standard code --- errors/errors.go | 210 ++++--- errors/proto/errors.pb.go | 575 +++--------------- errors/proto/errors.proto | 40 +- examples/clubchat/chats/cmd/api/list_chats.go | 2 +- grpc/errors_interceptor_test.go | 2 +- 5 files changed, 206 insertions(+), 623 deletions(-) diff --git a/errors/errors.go b/errors/errors.go index a2f5b7a..7fafd26 100644 --- a/errors/errors.go +++ b/errors/errors.go @@ -3,11 +3,10 @@ package errors import ( "encoding/json" "fmt" + "github.com/getsentry/sentry-go" "strings" - "github.com/getsentry/sentry-go" "github.com/pkg/errors" - "google.golang.org/genproto/googleapis/rpc/errdetails" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" "google.golang.org/protobuf/proto" @@ -65,27 +64,45 @@ type FoundationError interface { // InternalError describes an internal error type InternalError struct { *BaseError + Code uint32 + Message string } func (e *InternalError) GRPCStatus() *status.Status { - return status.New(codes.Internal, "internal error") + detailErr := &pb.Error{ + Code: e.Code, + Message: e.Message, + } + + st := status.New(codes.Internal, "internal error") + st, err := st.WithDetails(detailErr) + if err != nil { + sentry.CaptureException(err) + } + + return st } // MarshalProto marshals the error to a proto.Message. func (e *InternalError) MarshalProto() proto.Message { - return &pb.InternalError{} + return &pb.Error{ + Code: e.Code, + Message: e.Message, + } } func (e *InternalError) MarshalJSON() ([]byte, error) { - return []byte("{}"), nil + return json.Marshal(e.MarshalProto()) } // NewInternalError creates a generic internal error. -func NewInternalError(err error, msg string) *InternalError { +func NewInternalError(err error, message string) *InternalError { return &InternalError{ BaseError: &BaseError{ - Err: errors.Wrap(err, msg), + Err: errors.Wrap(err, message), }, + Code: uint32(codes.Internal), + Message: message, } } @@ -96,33 +113,27 @@ type ErrorViolations = map[string][]fmt.Stringer type InvalidArgumentError struct { *BaseError - Kind string - ID string + Code uint32 Violations ErrorViolations } func (e *InvalidArgumentError) GRPCStatus() *status.Status { - obj := fmt.Sprintf("%s/%s", e.Kind, e.ID) - msg := "validation error" - - // Create status with error message - st := status.New(codes.InvalidArgument, msg) + detailErr := &pb.Error{ + Code: e.Code, + Message: e.Error(), + Details: make(map[string]string, len(e.Violations)), + } - // Attach error details - badRequest := &errdetails.BadRequest{} for field, description := range e.Violations { for _, d := range description { - badRequest.FieldViolations = append(badRequest.FieldViolations, &errdetails.BadRequest_FieldViolation{ - Field: fmt.Sprintf("%s#%s", obj, field), - Description: d.String(), - }) + detailErr.Details[field] = d.String() } } - st, err := st.WithDetails(badRequest) + st := status.New(codes.InvalidArgument, e.Error()) + st, err := st.WithDetails(detailErr) if err != nil { sentry.CaptureException(err) - return status.New(codes.Internal, "internal error") } return st @@ -130,17 +141,15 @@ func (e *InvalidArgumentError) GRPCStatus() *status.Status { // MarshalProto marshals the error to a proto.Message. func (e *InvalidArgumentError) MarshalProto() proto.Message { - err := &pb.InvalidArgumentError{ - Kind: e.Kind, - Id: e.ID, + err := &pb.Error{ + Code: e.Code, + Message: e.Error(), + Details: make(map[string]string, len(e.Violations)), } for field, description := range e.Violations { for _, d := range description { - err.Violations = append(err.Violations, &pb.InvalidArgumentError_Violation{ - Field: field, - Description: d.String(), - }) + err.Details[field] = d.String() } } @@ -153,14 +162,13 @@ func (e *InvalidArgumentError) MarshalJSON() ([]byte, error) { } // NewInvalidArgumentError creates an invalid argument error with error details. -func NewInvalidArgumentError(kind string, id string, violations map[string][]fmt.Stringer) *InvalidArgumentError { +func NewInvalidArgumentError(kind string, id string, violations map[string][]fmt.Stringer, code uint32) *InvalidArgumentError { return &InvalidArgumentError{ BaseError: &BaseError{ Err: fmt.Errorf("invalid argument: %s/%s", kind, id), }, - Kind: kind, - ID: id, Violations: violations, + Code: code, } } @@ -168,21 +176,32 @@ func NewInvalidArgumentError(kind string, id string, violations map[string][]fmt type NotFoundError struct { *BaseError - Kind string - ID string + Kind string + ID string + Code uint32 + Message string } func (e *NotFoundError) GRPCStatus() *status.Status { - msg := fmt.Sprintf("not found: %s/%s", e.Kind, e.ID) + detailErr := &pb.Error{ + Code: e.Code, + Message: e.Message, + } - return status.New(codes.NotFound, msg) + st := status.New(codes.NotFound, e.Message) + st, err := st.WithDetails(detailErr) + if err != nil { + sentry.CaptureException(err) + } + + return st } // MarshalProto marshals the error to a proto.Message. func (e *NotFoundError) MarshalProto() proto.Message { - return &pb.NotFoundError{ - Kind: e.Kind, - Id: e.ID, + return &pb.Error{ + Message: e.Message, + Code: e.Code, } } @@ -192,13 +211,15 @@ func (e *NotFoundError) MarshalJSON() ([]byte, error) { } // NewNotFoundError creates a not found error. -func NewNotFoundError(err error, kind string, id string) *NotFoundError { +func NewNotFoundError(err error, kind string, id string, code uint32) *NotFoundError { return &NotFoundError{ BaseError: &BaseError{ Err: err, }, - Kind: kind, - ID: id, + Kind: kind, + ID: id, + Code: code, + Message: fmt.Sprintf(`not found: %s/%s`, kind, id), } } @@ -207,20 +228,29 @@ type PermissionDeniedError struct { *BaseError Action string - Kind string - ID string + Code uint32 } func (e *PermissionDeniedError) GRPCStatus() *status.Status { - return status.New(codes.PermissionDenied, e.Err.Error()) + detailErr := &pb.Error{ + Code: e.Code, + Message: e.Error(), + } + + st := status.New(codes.PermissionDenied, e.Error()) + st, err := st.WithDetails(detailErr) + if err != nil { + sentry.CaptureException(err) + } + + return st } // MarshalProto marshals the error to a proto.Message. func (e *PermissionDeniedError) MarshalProto() proto.Message { - return &pb.PermissionDeniedError{ - Action: e.Action, - Kind: e.Kind, - Id: e.ID, + return &pb.Error{ + Message: e.Error(), + Code: e.Code, } } @@ -230,16 +260,12 @@ func (e *PermissionDeniedError) MarshalJSON() ([]byte, error) { } // NewPermissionDeniedError creates a permission denied error. -func NewPermissionDeniedError(action string, kind string, id string) *PermissionDeniedError { - err := fmt.Errorf("permission denied: `%s` on %s/%s", action, kind, id) - +func NewPermissionDeniedError(action string, kind string, id string, code uint32) *PermissionDeniedError { return &PermissionDeniedError{ BaseError: &BaseError{ - Err: err, + Err: fmt.Errorf("permission denied: `%s` on %s/%s", action, kind, id), }, - Action: action, - Kind: kind, - ID: id, + Code: code, } } @@ -266,28 +292,45 @@ func NewInsufficientScopeAnyError(expectedScopes ...string) *PermissionDeniedErr // UnauthenticatedError describes an unauthenticated error. type UnauthenticatedError struct { *BaseError + + Code uint32 } func (e *UnauthenticatedError) GRPCStatus() *status.Status { - return status.New(codes.Unauthenticated, e.Err.Error()) + detailErr := &pb.Error{ + Code: e.Code, + Message: e.Error(), + } + + st := status.New(codes.Unauthenticated, e.Error()) + st, err := st.WithDetails(detailErr) + if err != nil { + sentry.CaptureException(err) + } + + return st } // MarshalProto marshals the error to a proto.Message. func (e *UnauthenticatedError) MarshalProto() proto.Message { - return &pb.UnauthenticatedError{} + return &pb.Error{ + Code: e.Code, + Message: e.Error(), + } } // MarshalJSON marshals the error to JSON. func (e *UnauthenticatedError) MarshalJSON() ([]byte, error) { - return []byte("{}"), nil + return json.Marshal(e.MarshalProto()) } // NewUnauthenticatedError creates an unauthenticated error. -func NewUnauthenticatedError(msg string) *UnauthenticatedError { +func NewUnauthenticatedError(msg string, code uint32) *UnauthenticatedError { return &UnauthenticatedError{ BaseError: &BaseError{ Err: fmt.Errorf("unauthenticated: %s", msg), }, + Code: code, } } @@ -299,25 +342,25 @@ type StaleObjectError struct { ID string ActualVersion int32 ExpectedVersion int32 + Code uint32 } func (e *StaleObjectError) GRPCStatus() *status.Status { - msg := fmt.Sprintf("stale object: %s/%s", e.Kind, e.ID) - - // Create status with error message - st := status.New(codes.FailedPrecondition, msg) - - // Attach error details - st, err := st.WithDetails(&errdetails.PreconditionFailure{ - Violations: []*errdetails.PreconditionFailure_Violation{{ - Type: "stale_object", - Subject: fmt.Sprintf("%s/%s", e.Kind, e.ID), - Description: fmt.Sprintf("actual version: %d, expected version: %d", e.ActualVersion, e.ExpectedVersion), - }}, - }) + detailErr := &pb.Error{ + Code: e.Code, + Message: e.Error(), + Details: map[string]string{ + `kind`: e.Kind, + `id`: e.ID, + `actual_version`: string(e.ActualVersion), + `expected_version`: string(e.ExpectedVersion), + }, + } + + st := status.New(codes.FailedPrecondition, e.Error()) + st, err := st.WithDetails(detailErr) if err != nil { - // TODO: maybe `fatal` here? - return status.New(codes.Internal, "internal error") + sentry.CaptureException(err) } return st @@ -325,11 +368,15 @@ func (e *StaleObjectError) GRPCStatus() *status.Status { // MarshalProto marshals the error to a proto.Message. func (e *StaleObjectError) MarshalProto() proto.Message { - return &pb.StaleObjectError{ - Kind: e.Kind, - Id: e.ID, - ActualVersion: e.ActualVersion, - ExpectedVersion: e.ExpectedVersion, + return &pb.Error{ + Message: e.Error(), + Code: e.Code, + Details: map[string]string{ + `kind`: e.Kind, + `id`: e.ID, + `actual_version`: string(e.ActualVersion), + `expected_version`: string(e.ExpectedVersion), + }, } } @@ -339,7 +386,7 @@ func (e *StaleObjectError) MarshalJSON() ([]byte, error) { } // NewStaleObjectError creates a stale object error. -func NewStaleObjectError(kind string, id string, actualVersion, expectedVersion int32) *StaleObjectError { +func NewStaleObjectError(kind string, id string, actualVersion, expectedVersion int32, code uint32) *StaleObjectError { return &StaleObjectError{ BaseError: &BaseError{ Err: fmt.Errorf("stale object: %s/%s", kind, id), @@ -348,5 +395,6 @@ func NewStaleObjectError(kind string, id string, actualVersion, expectedVersion ID: id, ActualVersion: actualVersion, ExpectedVersion: expectedVersion, + Code: code, } } diff --git a/errors/proto/errors.pb.go b/errors/proto/errors.pb.go index faefcfd..82c05e8 100644 --- a/errors/proto/errors.pb.go +++ b/errors/proto/errors.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.31.0 -// protoc v4.23.4 +// protoc-gen-go v1.35.1 +// protoc v5.27.3 // source: errors/proto/errors.proto package proto @@ -20,180 +20,32 @@ const ( _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) -type InternalError struct { +type Error struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields -} - -func (x *InternalError) Reset() { - *x = InternalError{} - if protoimpl.UnsafeEnabled { - mi := &file_errors_proto_errors_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *InternalError) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*InternalError) ProtoMessage() {} - -func (x *InternalError) ProtoReflect() protoreflect.Message { - mi := &file_errors_proto_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 InternalError.ProtoReflect.Descriptor instead. -func (*InternalError) Descriptor() ([]byte, []int) { - return file_errors_proto_errors_proto_rawDescGZIP(), []int{0} -} - -type UnauthenticatedError struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields -} - -func (x *UnauthenticatedError) Reset() { - *x = UnauthenticatedError{} - if protoimpl.UnsafeEnabled { - mi := &file_errors_proto_errors_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} -func (x *UnauthenticatedError) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*UnauthenticatedError) ProtoMessage() {} - -func (x *UnauthenticatedError) ProtoReflect() protoreflect.Message { - mi := &file_errors_proto_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) + Code uint32 `protobuf:"varint,1,opt,name=code,proto3" json:"code,omitempty"` + Message string `protobuf:"bytes,2,opt,name=message,proto3" json:"message,omitempty"` + Details map[string]string `protobuf:"bytes,3,rep,name=details,proto3" json:"details,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` } -// Deprecated: Use UnauthenticatedError.ProtoReflect.Descriptor instead. -func (*UnauthenticatedError) Descriptor() ([]byte, []int) { - return file_errors_proto_errors_proto_rawDescGZIP(), []int{1} +func (x *Error) Reset() { + *x = Error{} + mi := &file_errors_errors_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } -type StaleObjectError struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Kind string `protobuf:"bytes,1,opt,name=kind,proto3" json:"kind,omitempty"` - Id string `protobuf:"bytes,2,opt,name=id,proto3" json:"id,omitempty"` - ActualVersion int32 `protobuf:"varint,3,opt,name=actual_version,json=actualVersion,proto3" json:"actual_version,omitempty"` - ExpectedVersion int32 `protobuf:"varint,4,opt,name=expected_version,json=expectedVersion,proto3" json:"expected_version,omitempty"` -} - -func (x *StaleObjectError) Reset() { - *x = StaleObjectError{} - if protoimpl.UnsafeEnabled { - mi := &file_errors_proto_errors_proto_msgTypes[2] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *StaleObjectError) String() string { +func (x *Error) String() string { return protoimpl.X.MessageStringOf(x) } -func (*StaleObjectError) ProtoMessage() {} - -func (x *StaleObjectError) ProtoReflect() protoreflect.Message { - mi := &file_errors_proto_errors_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 StaleObjectError.ProtoReflect.Descriptor instead. -func (*StaleObjectError) Descriptor() ([]byte, []int) { - return file_errors_proto_errors_proto_rawDescGZIP(), []int{2} -} - -func (x *StaleObjectError) GetKind() string { - if x != nil { - return x.Kind - } - return "" -} +func (*Error) ProtoMessage() {} -func (x *StaleObjectError) GetId() string { +func (x *Error) ProtoReflect() protoreflect.Message { + mi := &file_errors_errors_proto_msgTypes[0] if x != nil { - return x.Id - } - return "" -} - -func (x *StaleObjectError) GetActualVersion() int32 { - if x != nil { - return x.ActualVersion - } - return 0 -} - -func (x *StaleObjectError) GetExpectedVersion() int32 { - if x != nil { - return x.ExpectedVersion - } - return 0 -} - -type NotFoundError struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Kind string `protobuf:"bytes,1,opt,name=kind,proto3" json:"kind,omitempty"` - Id string `protobuf:"bytes,2,opt,name=id,proto3" json:"id,omitempty"` -} - -func (x *NotFoundError) Reset() { - *x = NotFoundError{} - if protoimpl.UnsafeEnabled { - mi := &file_errors_proto_errors_proto_msgTypes[3] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *NotFoundError) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*NotFoundError) ProtoMessage() {} - -func (x *NotFoundError) ProtoReflect() protoreflect.Message { - mi := &file_errors_proto_errors_proto_msgTypes[3] - if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -203,275 +55,74 @@ func (x *NotFoundError) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use NotFoundError.ProtoReflect.Descriptor instead. -func (*NotFoundError) Descriptor() ([]byte, []int) { - return file_errors_proto_errors_proto_rawDescGZIP(), []int{3} +// Deprecated: Use Error.ProtoReflect.Descriptor instead. +func (*Error) Descriptor() ([]byte, []int) { + return file_errors_errors_proto_rawDescGZIP(), []int{0} } -func (x *NotFoundError) GetKind() string { +func (x *Error) GetCode() uint32 { if x != nil { - return x.Kind + return x.Code } - return "" -} - -func (x *NotFoundError) GetId() string { - if x != nil { - return x.Id - } - return "" -} - -type PermissionDeniedError struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Kind string `protobuf:"bytes,1,opt,name=kind,proto3" json:"kind,omitempty"` - Id string `protobuf:"bytes,2,opt,name=id,proto3" json:"id,omitempty"` - Action string `protobuf:"bytes,3,opt,name=action,proto3" json:"action,omitempty"` -} - -func (x *PermissionDeniedError) Reset() { - *x = PermissionDeniedError{} - if protoimpl.UnsafeEnabled { - mi := &file_errors_proto_errors_proto_msgTypes[4] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *PermissionDeniedError) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*PermissionDeniedError) ProtoMessage() {} - -func (x *PermissionDeniedError) ProtoReflect() protoreflect.Message { - mi := &file_errors_proto_errors_proto_msgTypes[4] - 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 PermissionDeniedError.ProtoReflect.Descriptor instead. -func (*PermissionDeniedError) Descriptor() ([]byte, []int) { - return file_errors_proto_errors_proto_rawDescGZIP(), []int{4} -} - -func (x *PermissionDeniedError) GetKind() string { - if x != nil { - return x.Kind - } - return "" -} - -func (x *PermissionDeniedError) GetId() string { - if x != nil { - return x.Id - } - return "" -} - -func (x *PermissionDeniedError) GetAction() string { - if x != nil { - return x.Action - } - return "" -} - -type InvalidArgumentError struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Kind string `protobuf:"bytes,1,opt,name=kind,proto3" json:"kind,omitempty"` - Id string `protobuf:"bytes,2,opt,name=id,proto3" json:"id,omitempty"` - Violations []*InvalidArgumentError_Violation `protobuf:"bytes,3,rep,name=violations,proto3" json:"violations,omitempty"` -} - -func (x *InvalidArgumentError) Reset() { - *x = InvalidArgumentError{} - if protoimpl.UnsafeEnabled { - mi := &file_errors_proto_errors_proto_msgTypes[5] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *InvalidArgumentError) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*InvalidArgumentError) ProtoMessage() {} - -func (x *InvalidArgumentError) ProtoReflect() protoreflect.Message { - mi := &file_errors_proto_errors_proto_msgTypes[5] - 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 InvalidArgumentError.ProtoReflect.Descriptor instead. -func (*InvalidArgumentError) Descriptor() ([]byte, []int) { - return file_errors_proto_errors_proto_rawDescGZIP(), []int{5} -} - -func (x *InvalidArgumentError) GetKind() string { - if x != nil { - return x.Kind - } - return "" + return 0 } -func (x *InvalidArgumentError) GetId() string { +func (x *Error) GetMessage() string { if x != nil { - return x.Id + return x.Message } return "" } -func (x *InvalidArgumentError) GetViolations() []*InvalidArgumentError_Violation { +func (x *Error) GetDetails() map[string]string { if x != nil { - return x.Violations + return x.Details } return nil } -type InvalidArgumentError_Violation struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Field string `protobuf:"bytes,1,opt,name=field,proto3" json:"field,omitempty"` - Description string `protobuf:"bytes,2,opt,name=description,proto3" json:"description,omitempty"` -} - -func (x *InvalidArgumentError_Violation) Reset() { - *x = InvalidArgumentError_Violation{} - if protoimpl.UnsafeEnabled { - mi := &file_errors_proto_errors_proto_msgTypes[6] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *InvalidArgumentError_Violation) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*InvalidArgumentError_Violation) ProtoMessage() {} - -func (x *InvalidArgumentError_Violation) ProtoReflect() protoreflect.Message { - mi := &file_errors_proto_errors_proto_msgTypes[6] - 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 InvalidArgumentError_Violation.ProtoReflect.Descriptor instead. -func (*InvalidArgumentError_Violation) Descriptor() ([]byte, []int) { - return file_errors_proto_errors_proto_rawDescGZIP(), []int{5, 0} -} - -func (x *InvalidArgumentError_Violation) GetField() string { - if x != nil { - return x.Field - } - return "" -} - -func (x *InvalidArgumentError_Violation) GetDescription() string { - if x != nil { - return x.Description - } - return "" -} - -var File_errors_proto_errors_proto protoreflect.FileDescriptor - -var file_errors_proto_errors_proto_rawDesc = []byte{ - 0x0a, 0x19, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x65, - 0x72, 0x72, 0x6f, 0x72, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x11, 0x66, 0x6f, 0x75, - 0x6e, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x22, 0x0f, - 0x0a, 0x0d, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x22, - 0x16, 0x0a, 0x14, 0x55, 0x6e, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, - 0x65, 0x64, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x22, 0x88, 0x01, 0x0a, 0x10, 0x53, 0x74, 0x61, 0x6c, - 0x65, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x12, 0x0a, 0x04, - 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6b, 0x69, 0x6e, 0x64, - 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, - 0x12, 0x25, 0x0a, 0x0e, 0x61, 0x63, 0x74, 0x75, 0x61, 0x6c, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, - 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0d, 0x61, 0x63, 0x74, 0x75, 0x61, 0x6c, - 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x29, 0x0a, 0x10, 0x65, 0x78, 0x70, 0x65, 0x63, - 0x74, 0x65, 0x64, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, - 0x05, 0x52, 0x0f, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x56, 0x65, 0x72, 0x73, 0x69, - 0x6f, 0x6e, 0x22, 0x33, 0x0a, 0x0d, 0x4e, 0x6f, 0x74, 0x46, 0x6f, 0x75, 0x6e, 0x64, 0x45, 0x72, - 0x72, 0x6f, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x22, 0x53, 0x0a, 0x15, 0x50, 0x65, 0x72, 0x6d, 0x69, - 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x44, 0x65, 0x6e, 0x69, 0x65, 0x64, 0x45, 0x72, 0x72, 0x6f, 0x72, - 0x12, 0x12, 0x0a, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, - 0x6b, 0x69, 0x6e, 0x64, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x02, 0x69, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0xd2, 0x01, 0x0a, - 0x14, 0x49, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x41, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, - 0x45, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x51, 0x0a, 0x0a, 0x76, 0x69, 0x6f, - 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x31, 0x2e, - 0x66, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x65, 0x72, 0x72, 0x6f, 0x72, - 0x73, 0x2e, 0x49, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x41, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, - 0x74, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x2e, 0x56, 0x69, 0x6f, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x52, 0x0a, 0x76, 0x69, 0x6f, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x1a, 0x43, 0x0a, 0x09, - 0x56, 0x69, 0x6f, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x14, 0x0a, 0x05, 0x66, 0x69, 0x65, - 0x6c, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x12, - 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, - 0x6e, 0x42, 0x2b, 0x5a, 0x29, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, - 0x72, 0x69, 0x2d, 0x6e, 0x61, 0x74, 0x2f, 0x66, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x2f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +var File_errors_errors_proto protoreflect.FileDescriptor + +var file_errors_errors_proto_rawDesc = []byte{ + 0x0a, 0x13, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x2f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x11, 0x66, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x2e, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x22, 0xb2, 0x01, 0x0a, 0x05, 0x45, 0x72, 0x72, + 0x6f, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, + 0x52, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, + 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, + 0x12, 0x3f, 0x0a, 0x07, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x25, 0x2e, 0x66, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x65, + 0x72, 0x72, 0x6f, 0x72, 0x73, 0x2e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x2e, 0x44, 0x65, 0x74, 0x61, + 0x69, 0x6c, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x07, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, + 0x73, 0x1a, 0x3a, 0x0a, 0x0c, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x45, 0x6e, 0x74, 0x72, + 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, + 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x42, 0x32, 0x5a, + 0x30, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x6f, 0x75, 0x6e, + 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2d, 0x67, 0x6f, 0x2f, 0x66, 0x6f, 0x75, 0x6e, 0x64, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x2f, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( - file_errors_proto_errors_proto_rawDescOnce sync.Once - file_errors_proto_errors_proto_rawDescData = file_errors_proto_errors_proto_rawDesc + file_errors_errors_proto_rawDescOnce sync.Once + file_errors_errors_proto_rawDescData = file_errors_errors_proto_rawDesc ) -func file_errors_proto_errors_proto_rawDescGZIP() []byte { - file_errors_proto_errors_proto_rawDescOnce.Do(func() { - file_errors_proto_errors_proto_rawDescData = protoimpl.X.CompressGZIP(file_errors_proto_errors_proto_rawDescData) +func file_errors_errors_proto_rawDescGZIP() []byte { + file_errors_errors_proto_rawDescOnce.Do(func() { + file_errors_errors_proto_rawDescData = protoimpl.X.CompressGZIP(file_errors_errors_proto_rawDescData) }) - return file_errors_proto_errors_proto_rawDescData + return file_errors_errors_proto_rawDescData } -var file_errors_proto_errors_proto_msgTypes = make([]protoimpl.MessageInfo, 7) -var file_errors_proto_errors_proto_goTypes = []interface{}{ - (*InternalError)(nil), // 0: foundation.errors.InternalError - (*UnauthenticatedError)(nil), // 1: foundation.errors.UnauthenticatedError - (*StaleObjectError)(nil), // 2: foundation.errors.StaleObjectError - (*NotFoundError)(nil), // 3: foundation.errors.NotFoundError - (*PermissionDeniedError)(nil), // 4: foundation.errors.PermissionDeniedError - (*InvalidArgumentError)(nil), // 5: foundation.errors.InvalidArgumentError - (*InvalidArgumentError_Violation)(nil), // 6: foundation.errors.InvalidArgumentError.Violation +var file_errors_errors_proto_msgTypes = make([]protoimpl.MessageInfo, 2) +var file_errors_errors_proto_goTypes = []any{ + (*Error)(nil), // 0: foundation.errors.Error + nil, // 1: foundation.errors.Error.DetailsEntry } -var file_errors_proto_errors_proto_depIdxs = []int32{ - 6, // 0: foundation.errors.InvalidArgumentError.violations:type_name -> foundation.errors.InvalidArgumentError.Violation +var file_errors_errors_proto_depIdxs = []int32{ + 1, // 0: foundation.errors.Error.details:type_name -> foundation.errors.Error.DetailsEntry 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 @@ -479,113 +130,27 @@ var file_errors_proto_errors_proto_depIdxs = []int32{ 0, // [0:1] is the sub-list for field type_name } -func init() { file_errors_proto_errors_proto_init() } -func file_errors_proto_errors_proto_init() { - if File_errors_proto_errors_proto != nil { +func init() { file_errors_errors_proto_init() } +func file_errors_errors_proto_init() { + if File_errors_errors_proto != nil { return } - if !protoimpl.UnsafeEnabled { - file_errors_proto_errors_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*InternalError); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_errors_proto_errors_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*UnauthenticatedError); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_errors_proto_errors_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*StaleObjectError); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_errors_proto_errors_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*NotFoundError); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_errors_proto_errors_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*PermissionDeniedError); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_errors_proto_errors_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*InvalidArgumentError); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_errors_proto_errors_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*InvalidArgumentError_Violation); 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_errors_proto_rawDesc, + RawDescriptor: file_errors_errors_proto_rawDesc, NumEnums: 0, - NumMessages: 7, + NumMessages: 2, NumExtensions: 0, NumServices: 0, }, - GoTypes: file_errors_proto_errors_proto_goTypes, - DependencyIndexes: file_errors_proto_errors_proto_depIdxs, - MessageInfos: file_errors_proto_errors_proto_msgTypes, + GoTypes: file_errors_errors_proto_goTypes, + DependencyIndexes: file_errors_errors_proto_depIdxs, + MessageInfos: file_errors_errors_proto_msgTypes, }.Build() - File_errors_proto_errors_proto = out.File - file_errors_proto_errors_proto_rawDesc = nil - file_errors_proto_errors_proto_goTypes = nil - file_errors_proto_errors_proto_depIdxs = nil + File_errors_errors_proto = out.File + file_errors_errors_proto_rawDesc = nil + file_errors_errors_proto_goTypes = nil + file_errors_errors_proto_depIdxs = nil } diff --git a/errors/proto/errors.proto b/errors/proto/errors.proto index 0e4c768..a527d57 100644 --- a/errors/proto/errors.proto +++ b/errors/proto/errors.proto @@ -4,38 +4,8 @@ package foundation.errors; option go_package = "github.com/foundation-go/foundation/errors/proto"; -message InternalError { -} - -message UnauthenticatedError { -} - -message StaleObjectError { - string kind = 1; - string id = 2; - int32 actual_version = 3; - int32 expected_version = 4; -} - -message NotFoundError { - string kind = 1; - string id = 2; -} - -message PermissionDeniedError { - string kind = 1; - string id = 2; - string action = 3; -} - -message InvalidArgumentError { - message Violation { - string field = 1; - string description = 2; - } - - string kind = 1; - string id = 2; - - repeated Violation violations = 3; -} +message Error { + uint32 code = 1; + string message = 2; + map details = 3; +} \ No newline at end of file diff --git a/examples/clubchat/chats/cmd/api/list_chats.go b/examples/clubchat/chats/cmd/api/list_chats.go index 7afd06e..7bf39a5 100644 --- a/examples/clubchat/chats/cmd/api/list_chats.go +++ b/examples/clubchat/chats/cmd/api/list_chats.go @@ -13,7 +13,7 @@ func (s *chatsServer) ListChats(ctx context.Context, req *pb.ListChatsRequest) ( if false { return nil, ferr.NewInvalidArgumentError("Chat", "", ferr.ErrorViolations{ "base": {ErrorCodeCustom}, - }) + }, 0) } // Check required scopes diff --git a/grpc/errors_interceptor_test.go b/grpc/errors_interceptor_test.go index a1dfea1..656e92d 100644 --- a/grpc/errors_interceptor_test.go +++ b/grpc/errors_interceptor_test.go @@ -14,7 +14,7 @@ import ( func TestFoundationErrorToStatusInterceptor(t *testing.T) { // Define a mock handler that returns an error mockHandler := func(context.Context, interface{}) (interface{}, error) { - return nil, ferr.NewNotFoundError(nil, "test", "123") + return nil, ferr.NewNotFoundError(nil, "test", "123", 0) } ctx := context.Background() From e7629b1aeaebd856d4e944265325fc2f00bcaf1f Mon Sep 17 00:00:00 2001 From: jonkofee Date: Sun, 20 Oct 2024 19:20:56 +0300 Subject: [PATCH 2/3] refactor --- cable_courier.go | 7 +- errors/errors.go | 171 +++++++++----------------------------- errors/proto/errors.pb.go | 124 +++++++++++++++++---------- errors/proto/errors.proto | 10 ++- 4 files changed, 123 insertions(+), 189 deletions(-) diff --git a/cable_courier.go b/cable_courier.go index 7332b49..46001de 100644 --- a/cable_courier.go +++ b/cable_courier.go @@ -59,12 +59,7 @@ func (opts *CableCourierOptions) EventHandlers(s *Service) map[proto.Message][]E handlers := make(map[proto.Message][]EventHandler) errors := []proto.Message{ - &ferrpb.InternalError{}, - &ferrpb.UnauthenticatedError{}, - &ferrpb.StaleObjectError{}, - &ferrpb.NotFoundError{}, - &ferrpb.PermissionDeniedError{}, - &ferrpb.InvalidArgumentError{}, + &ferrpb.InvalidArgument{}, } // Add default resolvers for errors, if not already defined diff --git a/errors/errors.go b/errors/errors.go index 7fafd26..05bb24e 100644 --- a/errors/errors.go +++ b/errors/errors.go @@ -64,31 +64,17 @@ type FoundationError interface { // InternalError describes an internal error type InternalError struct { *BaseError - Code uint32 - Message string + + Code uint32 } func (e *InternalError) GRPCStatus() *status.Status { - detailErr := &pb.Error{ - Code: e.Code, - Message: e.Message, - } - - st := status.New(codes.Internal, "internal error") - st, err := st.WithDetails(detailErr) - if err != nil { - sentry.CaptureException(err) - } - - return st + return status.New(codes.Code(e.Code), "internal error") } // MarshalProto marshals the error to a proto.Message. func (e *InternalError) MarshalProto() proto.Message { - return &pb.Error{ - Code: e.Code, - Message: e.Message, - } + return nil } func (e *InternalError) MarshalJSON() ([]byte, error) { @@ -101,8 +87,7 @@ func NewInternalError(err error, message string) *InternalError { BaseError: &BaseError{ Err: errors.Wrap(err, message), }, - Code: uint32(codes.Internal), - Message: message, + Code: uint32(codes.Internal), } } @@ -118,19 +103,19 @@ type InvalidArgumentError struct { } func (e *InvalidArgumentError) GRPCStatus() *status.Status { - detailErr := &pb.Error{ - Code: e.Code, - Message: e.Error(), - Details: make(map[string]string, len(e.Violations)), + detailErr := &pb.InvalidArgument{ + Fields: make(map[string]*pb.InvalidArgumentRule, len(e.Violations)), } for field, description := range e.Violations { + fieldRules := make([]string, 0, len(description)) for _, d := range description { - detailErr.Details[field] = d.String() + fieldRules = append(fieldRules, d.String()) } + detailErr.Fields[field] = &pb.InvalidArgumentRule{Rules: fieldRules} } - st := status.New(codes.InvalidArgument, e.Error()) + st := status.New(codes.Code(e.Code), e.Error()) st, err := st.WithDetails(detailErr) if err != nil { sentry.CaptureException(err) @@ -141,16 +126,16 @@ func (e *InvalidArgumentError) GRPCStatus() *status.Status { // MarshalProto marshals the error to a proto.Message. func (e *InvalidArgumentError) MarshalProto() proto.Message { - err := &pb.Error{ - Code: e.Code, - Message: e.Error(), - Details: make(map[string]string, len(e.Violations)), + err := &pb.InvalidArgument{ + Fields: make(map[string]*pb.InvalidArgumentRule, len(e.Violations)), } for field, description := range e.Violations { + fieldRules := make([]string, len(description)) for _, d := range description { - err.Details[field] = d.String() + fieldRules = append(fieldRules, d.String()) } + err.Fields[field] = &pb.InvalidArgumentRule{Rules: fieldRules} } return err @@ -162,13 +147,13 @@ func (e *InvalidArgumentError) MarshalJSON() ([]byte, error) { } // NewInvalidArgumentError creates an invalid argument error with error details. -func NewInvalidArgumentError(kind string, id string, violations map[string][]fmt.Stringer, code uint32) *InvalidArgumentError { +func NewInvalidArgumentError(kind string, id string, violations map[string][]fmt.Stringer) *InvalidArgumentError { return &InvalidArgumentError{ BaseError: &BaseError{ Err: fmt.Errorf("invalid argument: %s/%s", kind, id), }, Violations: violations, - Code: code, + Code: uint32(codes.InvalidArgument), } } @@ -176,33 +161,16 @@ func NewInvalidArgumentError(kind string, id string, violations map[string][]fmt type NotFoundError struct { *BaseError - Kind string - ID string - Code uint32 - Message string + Code uint32 } func (e *NotFoundError) GRPCStatus() *status.Status { - detailErr := &pb.Error{ - Code: e.Code, - Message: e.Message, - } - - st := status.New(codes.NotFound, e.Message) - st, err := st.WithDetails(detailErr) - if err != nil { - sentry.CaptureException(err) - } - - return st + return status.New(codes.Code(e.Code), e.Error()) } // MarshalProto marshals the error to a proto.Message. func (e *NotFoundError) MarshalProto() proto.Message { - return &pb.Error{ - Message: e.Message, - Code: e.Code, - } + return nil } // MarshalJSON marshals the error to JSON. @@ -211,15 +179,12 @@ func (e *NotFoundError) MarshalJSON() ([]byte, error) { } // NewNotFoundError creates a not found error. -func NewNotFoundError(err error, kind string, id string, code uint32) *NotFoundError { +func NewNotFoundError(err error, kind string, id string) *NotFoundError { return &NotFoundError{ BaseError: &BaseError{ - Err: err, + Err: errors.New(fmt.Sprintf(`not found: %s/%s`, kind, id)), }, - Kind: kind, - ID: id, - Code: code, - Message: fmt.Sprintf(`not found: %s/%s`, kind, id), + Code: uint32(codes.NotFound), } } @@ -232,26 +197,12 @@ type PermissionDeniedError struct { } func (e *PermissionDeniedError) GRPCStatus() *status.Status { - detailErr := &pb.Error{ - Code: e.Code, - Message: e.Error(), - } - - st := status.New(codes.PermissionDenied, e.Error()) - st, err := st.WithDetails(detailErr) - if err != nil { - sentry.CaptureException(err) - } - - return st + return status.New(codes.PermissionDenied, e.Error()) } // MarshalProto marshals the error to a proto.Message. func (e *PermissionDeniedError) MarshalProto() proto.Message { - return &pb.Error{ - Message: e.Error(), - Code: e.Code, - } + return nil } // MarshalJSON marshals the error to JSON. @@ -260,12 +211,12 @@ func (e *PermissionDeniedError) MarshalJSON() ([]byte, error) { } // NewPermissionDeniedError creates a permission denied error. -func NewPermissionDeniedError(action string, kind string, id string, code uint32) *PermissionDeniedError { +func NewPermissionDeniedError(action string, kind string, id string) *PermissionDeniedError { return &PermissionDeniedError{ BaseError: &BaseError{ Err: fmt.Errorf("permission denied: `%s` on %s/%s", action, kind, id), }, - Code: code, + Code: uint32(codes.PermissionDenied), } } @@ -297,26 +248,12 @@ type UnauthenticatedError struct { } func (e *UnauthenticatedError) GRPCStatus() *status.Status { - detailErr := &pb.Error{ - Code: e.Code, - Message: e.Error(), - } - - st := status.New(codes.Unauthenticated, e.Error()) - st, err := st.WithDetails(detailErr) - if err != nil { - sentry.CaptureException(err) - } - - return st + return status.New(codes.Code(e.Code), e.Error()) } // MarshalProto marshals the error to a proto.Message. func (e *UnauthenticatedError) MarshalProto() proto.Message { - return &pb.Error{ - Code: e.Code, - Message: e.Error(), - } + return nil } // MarshalJSON marshals the error to JSON. @@ -330,7 +267,7 @@ func NewUnauthenticatedError(msg string, code uint32) *UnauthenticatedError { BaseError: &BaseError{ Err: fmt.Errorf("unauthenticated: %s", msg), }, - Code: code, + Code: uint32(codes.Unauthenticated), } } @@ -338,46 +275,16 @@ func NewUnauthenticatedError(msg string, code uint32) *UnauthenticatedError { type StaleObjectError struct { *BaseError - Kind string - ID string - ActualVersion int32 - ExpectedVersion int32 - Code uint32 + Code uint32 } func (e *StaleObjectError) GRPCStatus() *status.Status { - detailErr := &pb.Error{ - Code: e.Code, - Message: e.Error(), - Details: map[string]string{ - `kind`: e.Kind, - `id`: e.ID, - `actual_version`: string(e.ActualVersion), - `expected_version`: string(e.ExpectedVersion), - }, - } - - st := status.New(codes.FailedPrecondition, e.Error()) - st, err := st.WithDetails(detailErr) - if err != nil { - sentry.CaptureException(err) - } - - return st + return status.New(codes.Code(e.Code), e.Error()) } // MarshalProto marshals the error to a proto.Message. func (e *StaleObjectError) MarshalProto() proto.Message { - return &pb.Error{ - Message: e.Error(), - Code: e.Code, - Details: map[string]string{ - `kind`: e.Kind, - `id`: e.ID, - `actual_version`: string(e.ActualVersion), - `expected_version`: string(e.ExpectedVersion), - }, - } + return nil } // MarshalJSON marshals the error to JSON. @@ -386,15 +293,11 @@ func (e *StaleObjectError) MarshalJSON() ([]byte, error) { } // NewStaleObjectError creates a stale object error. -func NewStaleObjectError(kind string, id string, actualVersion, expectedVersion int32, code uint32) *StaleObjectError { +func NewStaleObjectError(kind string, id string, actualVersion, expectedVersion int32) *StaleObjectError { return &StaleObjectError{ BaseError: &BaseError{ - Err: fmt.Errorf("stale object: %s/%s", kind, id), + Err: fmt.Errorf("stale object: %s/%s actual version: %d, expected version: %d", kind, id, actualVersion, expectedVersion), }, - Kind: kind, - ID: id, - ActualVersion: actualVersion, - ExpectedVersion: expectedVersion, - Code: code, + Code: uint32(codes.FailedPrecondition), } } diff --git a/errors/proto/errors.pb.go b/errors/proto/errors.pb.go index 82c05e8..ed0afca 100644 --- a/errors/proto/errors.pb.go +++ b/errors/proto/errors.pb.go @@ -20,30 +20,28 @@ const ( _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) -type Error struct { +type InvalidArgumentRule struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Code uint32 `protobuf:"varint,1,opt,name=code,proto3" json:"code,omitempty"` - Message string `protobuf:"bytes,2,opt,name=message,proto3" json:"message,omitempty"` - Details map[string]string `protobuf:"bytes,3,rep,name=details,proto3" json:"details,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + Rules []string `protobuf:"bytes,1,rep,name=rules,proto3" json:"rules,omitempty"` } -func (x *Error) Reset() { - *x = Error{} +func (x *InvalidArgumentRule) Reset() { + *x = InvalidArgumentRule{} mi := &file_errors_errors_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } -func (x *Error) String() string { +func (x *InvalidArgumentRule) String() string { return protoimpl.X.MessageStringOf(x) } -func (*Error) ProtoMessage() {} +func (*InvalidArgumentRule) ProtoMessage() {} -func (x *Error) ProtoReflect() protoreflect.Message { +func (x *InvalidArgumentRule) ProtoReflect() protoreflect.Message { mi := &file_errors_errors_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -55,28 +53,59 @@ func (x *Error) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use Error.ProtoReflect.Descriptor instead. -func (*Error) Descriptor() ([]byte, []int) { +// Deprecated: Use InvalidArgumentRule.ProtoReflect.Descriptor instead. +func (*InvalidArgumentRule) Descriptor() ([]byte, []int) { return file_errors_errors_proto_rawDescGZIP(), []int{0} } -func (x *Error) GetCode() uint32 { +func (x *InvalidArgumentRule) GetRules() []string { if x != nil { - return x.Code + return x.Rules } - return 0 + return nil +} + +type InvalidArgument struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Fields map[string]*InvalidArgumentRule `protobuf:"bytes,3,rep,name=fields,proto3" json:"fields,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` +} + +func (x *InvalidArgument) Reset() { + *x = InvalidArgument{} + mi := &file_errors_errors_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } -func (x *Error) GetMessage() string { +func (x *InvalidArgument) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*InvalidArgument) ProtoMessage() {} + +func (x *InvalidArgument) ProtoReflect() protoreflect.Message { + mi := &file_errors_errors_proto_msgTypes[1] if x != nil { - return x.Message + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms } - return "" + return mi.MessageOf(x) +} + +// Deprecated: Use InvalidArgument.ProtoReflect.Descriptor instead. +func (*InvalidArgument) Descriptor() ([]byte, []int) { + return file_errors_errors_proto_rawDescGZIP(), []int{1} } -func (x *Error) GetDetails() map[string]string { +func (x *InvalidArgument) GetFields() map[string]*InvalidArgumentRule { if x != nil { - return x.Details + return x.Fields } return nil } @@ -86,22 +115,25 @@ var File_errors_errors_proto protoreflect.FileDescriptor var file_errors_errors_proto_rawDesc = []byte{ 0x0a, 0x13, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x2f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x11, 0x66, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x2e, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x22, 0xb2, 0x01, 0x0a, 0x05, 0x45, 0x72, 0x72, - 0x6f, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, - 0x52, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, - 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, - 0x12, 0x3f, 0x0a, 0x07, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, - 0x0b, 0x32, 0x25, 0x2e, 0x66, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x65, - 0x72, 0x72, 0x6f, 0x72, 0x73, 0x2e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x2e, 0x44, 0x65, 0x74, 0x61, - 0x69, 0x6c, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x07, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, - 0x73, 0x1a, 0x3a, 0x0a, 0x0c, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x45, 0x6e, 0x74, 0x72, - 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, - 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x42, 0x32, 0x5a, - 0x30, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x6f, 0x75, 0x6e, - 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2d, 0x67, 0x6f, 0x2f, 0x66, 0x6f, 0x75, 0x6e, 0x64, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x2f, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x6e, 0x2e, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x22, 0x2b, 0x0a, 0x13, 0x49, 0x6e, 0x76, 0x61, + 0x6c, 0x69, 0x64, 0x41, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x75, 0x6c, 0x65, 0x12, + 0x14, 0x0a, 0x05, 0x72, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, + 0x72, 0x75, 0x6c, 0x65, 0x73, 0x22, 0xbc, 0x01, 0x0a, 0x0f, 0x49, 0x6e, 0x76, 0x61, 0x6c, 0x69, + 0x64, 0x41, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x46, 0x0a, 0x06, 0x66, 0x69, 0x65, + 0x6c, 0x64, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2e, 0x2e, 0x66, 0x6f, 0x75, 0x6e, + 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x2e, 0x49, 0x6e, + 0x76, 0x61, 0x6c, 0x69, 0x64, 0x41, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x46, 0x69, + 0x65, 0x6c, 0x64, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x06, 0x66, 0x69, 0x65, 0x6c, 0x64, + 0x73, 0x1a, 0x61, 0x0a, 0x0b, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, + 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, + 0x65, 0x79, 0x12, 0x3c, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x26, 0x2e, 0x66, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x65, + 0x72, 0x72, 0x6f, 0x72, 0x73, 0x2e, 0x49, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x41, 0x72, 0x67, + 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x75, 0x6c, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x3a, 0x02, 0x38, 0x01, 0x42, 0x32, 0x5a, 0x30, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, + 0x6f, 0x6d, 0x2f, 0x66, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2d, 0x67, 0x6f, + 0x2f, 0x66, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x65, 0x72, 0x72, 0x6f, + 0x72, 0x73, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -116,18 +148,20 @@ func file_errors_errors_proto_rawDescGZIP() []byte { return file_errors_errors_proto_rawDescData } -var file_errors_errors_proto_msgTypes = make([]protoimpl.MessageInfo, 2) +var file_errors_errors_proto_msgTypes = make([]protoimpl.MessageInfo, 3) var file_errors_errors_proto_goTypes = []any{ - (*Error)(nil), // 0: foundation.errors.Error - nil, // 1: foundation.errors.Error.DetailsEntry + (*InvalidArgumentRule)(nil), // 0: foundation.errors.InvalidArgumentRule + (*InvalidArgument)(nil), // 1: foundation.errors.InvalidArgument + nil, // 2: foundation.errors.InvalidArgument.FieldsEntry } var file_errors_errors_proto_depIdxs = []int32{ - 1, // 0: foundation.errors.Error.details:type_name -> foundation.errors.Error.DetailsEntry - 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 + 2, // 0: foundation.errors.InvalidArgument.fields:type_name -> foundation.errors.InvalidArgument.FieldsEntry + 0, // 1: foundation.errors.InvalidArgument.FieldsEntry.value:type_name -> foundation.errors.InvalidArgumentRule + 2, // [2:2] is the sub-list for method output_type + 2, // [2:2] is the sub-list for method input_type + 2, // [2:2] is the sub-list for extension type_name + 2, // [2:2] is the sub-list for extension extendee + 0, // [0:2] is the sub-list for field type_name } func init() { file_errors_errors_proto_init() } @@ -141,7 +175,7 @@ func file_errors_errors_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_errors_errors_proto_rawDesc, NumEnums: 0, - NumMessages: 2, + NumMessages: 3, NumExtensions: 0, NumServices: 0, }, diff --git a/errors/proto/errors.proto b/errors/proto/errors.proto index a527d57..741be2e 100644 --- a/errors/proto/errors.proto +++ b/errors/proto/errors.proto @@ -4,8 +4,10 @@ package foundation.errors; option go_package = "github.com/foundation-go/foundation/errors/proto"; -message Error { - uint32 code = 1; - string message = 2; - map details = 3; +message InvalidArgumentRule { + repeated string rules = 1; +} + +message InvalidArgument { + map fields = 3; } \ No newline at end of file From b3190a81db404bdf388e77ee5ccf7cf8ec23adf6 Mon Sep 17 00:00:00 2001 From: Daniil <1272421+daniildulin@users.noreply.github.com> Date: Sat, 2 Nov 2024 11:20:39 +0300 Subject: [PATCH 3/3] Revert "refactor errors" --- cable_courier.go | 7 +- errors/errors.go | 141 +++-- errors/proto/errors.pb.go | 581 +++++++++++++++--- errors/proto/errors.proto | 38 +- examples/clubchat/chats/cmd/api/list_chats.go | 2 +- grpc/errors_interceptor_test.go | 2 +- 6 files changed, 627 insertions(+), 144 deletions(-) diff --git a/cable_courier.go b/cable_courier.go index 46001de..7332b49 100644 --- a/cable_courier.go +++ b/cable_courier.go @@ -59,7 +59,12 @@ func (opts *CableCourierOptions) EventHandlers(s *Service) map[proto.Message][]E handlers := make(map[proto.Message][]EventHandler) errors := []proto.Message{ - &ferrpb.InvalidArgument{}, + &ferrpb.InternalError{}, + &ferrpb.UnauthenticatedError{}, + &ferrpb.StaleObjectError{}, + &ferrpb.NotFoundError{}, + &ferrpb.PermissionDeniedError{}, + &ferrpb.InvalidArgumentError{}, } // Add default resolvers for errors, if not already defined diff --git a/errors/errors.go b/errors/errors.go index 05bb24e..a2f5b7a 100644 --- a/errors/errors.go +++ b/errors/errors.go @@ -3,10 +3,11 @@ package errors import ( "encoding/json" "fmt" - "github.com/getsentry/sentry-go" "strings" + "github.com/getsentry/sentry-go" "github.com/pkg/errors" + "google.golang.org/genproto/googleapis/rpc/errdetails" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" "google.golang.org/protobuf/proto" @@ -64,30 +65,27 @@ type FoundationError interface { // InternalError describes an internal error type InternalError struct { *BaseError - - Code uint32 } func (e *InternalError) GRPCStatus() *status.Status { - return status.New(codes.Code(e.Code), "internal error") + return status.New(codes.Internal, "internal error") } // MarshalProto marshals the error to a proto.Message. func (e *InternalError) MarshalProto() proto.Message { - return nil + return &pb.InternalError{} } func (e *InternalError) MarshalJSON() ([]byte, error) { - return json.Marshal(e.MarshalProto()) + return []byte("{}"), nil } // NewInternalError creates a generic internal error. -func NewInternalError(err error, message string) *InternalError { +func NewInternalError(err error, msg string) *InternalError { return &InternalError{ BaseError: &BaseError{ - Err: errors.Wrap(err, message), + Err: errors.Wrap(err, msg), }, - Code: uint32(codes.Internal), } } @@ -98,27 +96,33 @@ type ErrorViolations = map[string][]fmt.Stringer type InvalidArgumentError struct { *BaseError - Code uint32 + Kind string + ID string Violations ErrorViolations } func (e *InvalidArgumentError) GRPCStatus() *status.Status { - detailErr := &pb.InvalidArgument{ - Fields: make(map[string]*pb.InvalidArgumentRule, len(e.Violations)), - } + obj := fmt.Sprintf("%s/%s", e.Kind, e.ID) + msg := "validation error" + // Create status with error message + st := status.New(codes.InvalidArgument, msg) + + // Attach error details + badRequest := &errdetails.BadRequest{} for field, description := range e.Violations { - fieldRules := make([]string, 0, len(description)) for _, d := range description { - fieldRules = append(fieldRules, d.String()) + badRequest.FieldViolations = append(badRequest.FieldViolations, &errdetails.BadRequest_FieldViolation{ + Field: fmt.Sprintf("%s#%s", obj, field), + Description: d.String(), + }) } - detailErr.Fields[field] = &pb.InvalidArgumentRule{Rules: fieldRules} } - st := status.New(codes.Code(e.Code), e.Error()) - st, err := st.WithDetails(detailErr) + st, err := st.WithDetails(badRequest) if err != nil { sentry.CaptureException(err) + return status.New(codes.Internal, "internal error") } return st @@ -126,16 +130,18 @@ func (e *InvalidArgumentError) GRPCStatus() *status.Status { // MarshalProto marshals the error to a proto.Message. func (e *InvalidArgumentError) MarshalProto() proto.Message { - err := &pb.InvalidArgument{ - Fields: make(map[string]*pb.InvalidArgumentRule, len(e.Violations)), + err := &pb.InvalidArgumentError{ + Kind: e.Kind, + Id: e.ID, } for field, description := range e.Violations { - fieldRules := make([]string, len(description)) for _, d := range description { - fieldRules = append(fieldRules, d.String()) + err.Violations = append(err.Violations, &pb.InvalidArgumentError_Violation{ + Field: field, + Description: d.String(), + }) } - err.Fields[field] = &pb.InvalidArgumentRule{Rules: fieldRules} } return err @@ -152,8 +158,9 @@ func NewInvalidArgumentError(kind string, id string, violations map[string][]fmt BaseError: &BaseError{ Err: fmt.Errorf("invalid argument: %s/%s", kind, id), }, + Kind: kind, + ID: id, Violations: violations, - Code: uint32(codes.InvalidArgument), } } @@ -161,16 +168,22 @@ func NewInvalidArgumentError(kind string, id string, violations map[string][]fmt type NotFoundError struct { *BaseError - Code uint32 + Kind string + ID string } func (e *NotFoundError) GRPCStatus() *status.Status { - return status.New(codes.Code(e.Code), e.Error()) + msg := fmt.Sprintf("not found: %s/%s", e.Kind, e.ID) + + return status.New(codes.NotFound, msg) } // MarshalProto marshals the error to a proto.Message. func (e *NotFoundError) MarshalProto() proto.Message { - return nil + return &pb.NotFoundError{ + Kind: e.Kind, + Id: e.ID, + } } // MarshalJSON marshals the error to JSON. @@ -182,9 +195,10 @@ func (e *NotFoundError) MarshalJSON() ([]byte, error) { func NewNotFoundError(err error, kind string, id string) *NotFoundError { return &NotFoundError{ BaseError: &BaseError{ - Err: errors.New(fmt.Sprintf(`not found: %s/%s`, kind, id)), + Err: err, }, - Code: uint32(codes.NotFound), + Kind: kind, + ID: id, } } @@ -193,16 +207,21 @@ type PermissionDeniedError struct { *BaseError Action string - Code uint32 + Kind string + ID string } func (e *PermissionDeniedError) GRPCStatus() *status.Status { - return status.New(codes.PermissionDenied, e.Error()) + return status.New(codes.PermissionDenied, e.Err.Error()) } // MarshalProto marshals the error to a proto.Message. func (e *PermissionDeniedError) MarshalProto() proto.Message { - return nil + return &pb.PermissionDeniedError{ + Action: e.Action, + Kind: e.Kind, + Id: e.ID, + } } // MarshalJSON marshals the error to JSON. @@ -212,11 +231,15 @@ func (e *PermissionDeniedError) MarshalJSON() ([]byte, error) { // NewPermissionDeniedError creates a permission denied error. func NewPermissionDeniedError(action string, kind string, id string) *PermissionDeniedError { + err := fmt.Errorf("permission denied: `%s` on %s/%s", action, kind, id) + return &PermissionDeniedError{ BaseError: &BaseError{ - Err: fmt.Errorf("permission denied: `%s` on %s/%s", action, kind, id), + Err: err, }, - Code: uint32(codes.PermissionDenied), + Action: action, + Kind: kind, + ID: id, } } @@ -243,31 +266,28 @@ func NewInsufficientScopeAnyError(expectedScopes ...string) *PermissionDeniedErr // UnauthenticatedError describes an unauthenticated error. type UnauthenticatedError struct { *BaseError - - Code uint32 } func (e *UnauthenticatedError) GRPCStatus() *status.Status { - return status.New(codes.Code(e.Code), e.Error()) + return status.New(codes.Unauthenticated, e.Err.Error()) } // MarshalProto marshals the error to a proto.Message. func (e *UnauthenticatedError) MarshalProto() proto.Message { - return nil + return &pb.UnauthenticatedError{} } // MarshalJSON marshals the error to JSON. func (e *UnauthenticatedError) MarshalJSON() ([]byte, error) { - return json.Marshal(e.MarshalProto()) + return []byte("{}"), nil } // NewUnauthenticatedError creates an unauthenticated error. -func NewUnauthenticatedError(msg string, code uint32) *UnauthenticatedError { +func NewUnauthenticatedError(msg string) *UnauthenticatedError { return &UnauthenticatedError{ BaseError: &BaseError{ Err: fmt.Errorf("unauthenticated: %s", msg), }, - Code: uint32(codes.Unauthenticated), } } @@ -275,16 +295,42 @@ func NewUnauthenticatedError(msg string, code uint32) *UnauthenticatedError { type StaleObjectError struct { *BaseError - Code uint32 + Kind string + ID string + ActualVersion int32 + ExpectedVersion int32 } func (e *StaleObjectError) GRPCStatus() *status.Status { - return status.New(codes.Code(e.Code), e.Error()) + msg := fmt.Sprintf("stale object: %s/%s", e.Kind, e.ID) + + // Create status with error message + st := status.New(codes.FailedPrecondition, msg) + + // Attach error details + st, err := st.WithDetails(&errdetails.PreconditionFailure{ + Violations: []*errdetails.PreconditionFailure_Violation{{ + Type: "stale_object", + Subject: fmt.Sprintf("%s/%s", e.Kind, e.ID), + Description: fmt.Sprintf("actual version: %d, expected version: %d", e.ActualVersion, e.ExpectedVersion), + }}, + }) + if err != nil { + // TODO: maybe `fatal` here? + return status.New(codes.Internal, "internal error") + } + + return st } // MarshalProto marshals the error to a proto.Message. func (e *StaleObjectError) MarshalProto() proto.Message { - return nil + return &pb.StaleObjectError{ + Kind: e.Kind, + Id: e.ID, + ActualVersion: e.ActualVersion, + ExpectedVersion: e.ExpectedVersion, + } } // MarshalJSON marshals the error to JSON. @@ -296,8 +342,11 @@ func (e *StaleObjectError) MarshalJSON() ([]byte, error) { func NewStaleObjectError(kind string, id string, actualVersion, expectedVersion int32) *StaleObjectError { return &StaleObjectError{ BaseError: &BaseError{ - Err: fmt.Errorf("stale object: %s/%s actual version: %d, expected version: %d", kind, id, actualVersion, expectedVersion), + Err: fmt.Errorf("stale object: %s/%s", kind, id), }, - Code: uint32(codes.FailedPrecondition), + Kind: kind, + ID: id, + ActualVersion: actualVersion, + ExpectedVersion: expectedVersion, } } diff --git a/errors/proto/errors.pb.go b/errors/proto/errors.pb.go index ed0afca..faefcfd 100644 --- a/errors/proto/errors.pb.go +++ b/errors/proto/errors.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.35.1 -// protoc v5.27.3 +// protoc-gen-go v1.31.0 +// protoc v4.23.4 // source: errors/proto/errors.proto package proto @@ -20,30 +20,180 @@ const ( _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) -type InvalidArgumentRule struct { +type InternalError struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *InternalError) Reset() { + *x = InternalError{} + if protoimpl.UnsafeEnabled { + mi := &file_errors_proto_errors_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *InternalError) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*InternalError) ProtoMessage() {} + +func (x *InternalError) ProtoReflect() protoreflect.Message { + mi := &file_errors_proto_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 InternalError.ProtoReflect.Descriptor instead. +func (*InternalError) Descriptor() ([]byte, []int) { + return file_errors_proto_errors_proto_rawDescGZIP(), []int{0} +} + +type UnauthenticatedError struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *UnauthenticatedError) Reset() { + *x = UnauthenticatedError{} + if protoimpl.UnsafeEnabled { + mi := &file_errors_proto_errors_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *UnauthenticatedError) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UnauthenticatedError) ProtoMessage() {} + +func (x *UnauthenticatedError) ProtoReflect() protoreflect.Message { + mi := &file_errors_proto_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 UnauthenticatedError.ProtoReflect.Descriptor instead. +func (*UnauthenticatedError) Descriptor() ([]byte, []int) { + return file_errors_proto_errors_proto_rawDescGZIP(), []int{1} +} + +type StaleObjectError struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Rules []string `protobuf:"bytes,1,rep,name=rules,proto3" json:"rules,omitempty"` + Kind string `protobuf:"bytes,1,opt,name=kind,proto3" json:"kind,omitempty"` + Id string `protobuf:"bytes,2,opt,name=id,proto3" json:"id,omitempty"` + ActualVersion int32 `protobuf:"varint,3,opt,name=actual_version,json=actualVersion,proto3" json:"actual_version,omitempty"` + ExpectedVersion int32 `protobuf:"varint,4,opt,name=expected_version,json=expectedVersion,proto3" json:"expected_version,omitempty"` } -func (x *InvalidArgumentRule) Reset() { - *x = InvalidArgumentRule{} - mi := &file_errors_errors_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) +func (x *StaleObjectError) Reset() { + *x = StaleObjectError{} + if protoimpl.UnsafeEnabled { + mi := &file_errors_proto_errors_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } -func (x *InvalidArgumentRule) String() string { +func (x *StaleObjectError) String() string { return protoimpl.X.MessageStringOf(x) } -func (*InvalidArgumentRule) ProtoMessage() {} +func (*StaleObjectError) ProtoMessage() {} + +func (x *StaleObjectError) ProtoReflect() protoreflect.Message { + mi := &file_errors_proto_errors_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 StaleObjectError.ProtoReflect.Descriptor instead. +func (*StaleObjectError) Descriptor() ([]byte, []int) { + return file_errors_proto_errors_proto_rawDescGZIP(), []int{2} +} + +func (x *StaleObjectError) GetKind() string { + if x != nil { + return x.Kind + } + return "" +} + +func (x *StaleObjectError) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +func (x *StaleObjectError) GetActualVersion() int32 { + if x != nil { + return x.ActualVersion + } + return 0 +} -func (x *InvalidArgumentRule) ProtoReflect() protoreflect.Message { - mi := &file_errors_errors_proto_msgTypes[0] +func (x *StaleObjectError) GetExpectedVersion() int32 { if x != nil { + return x.ExpectedVersion + } + return 0 +} + +type NotFoundError struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Kind string `protobuf:"bytes,1,opt,name=kind,proto3" json:"kind,omitempty"` + Id string `protobuf:"bytes,2,opt,name=id,proto3" json:"id,omitempty"` +} + +func (x *NotFoundError) Reset() { + *x = NotFoundError{} + if protoimpl.UnsafeEnabled { + mi := &file_errors_proto_errors_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *NotFoundError) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*NotFoundError) ProtoMessage() {} + +func (x *NotFoundError) ProtoReflect() protoreflect.Message { + mi := &file_errors_proto_errors_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -53,42 +203,116 @@ func (x *InvalidArgumentRule) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use InvalidArgumentRule.ProtoReflect.Descriptor instead. -func (*InvalidArgumentRule) Descriptor() ([]byte, []int) { - return file_errors_errors_proto_rawDescGZIP(), []int{0} +// Deprecated: Use NotFoundError.ProtoReflect.Descriptor instead. +func (*NotFoundError) Descriptor() ([]byte, []int) { + return file_errors_proto_errors_proto_rawDescGZIP(), []int{3} } -func (x *InvalidArgumentRule) GetRules() []string { +func (x *NotFoundError) GetKind() string { if x != nil { - return x.Rules + return x.Kind } - return nil + return "" } -type InvalidArgument struct { +func (x *NotFoundError) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +type PermissionDeniedError struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Fields map[string]*InvalidArgumentRule `protobuf:"bytes,3,rep,name=fields,proto3" json:"fields,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + Kind string `protobuf:"bytes,1,opt,name=kind,proto3" json:"kind,omitempty"` + Id string `protobuf:"bytes,2,opt,name=id,proto3" json:"id,omitempty"` + Action string `protobuf:"bytes,3,opt,name=action,proto3" json:"action,omitempty"` } -func (x *InvalidArgument) Reset() { - *x = InvalidArgument{} - mi := &file_errors_errors_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) +func (x *PermissionDeniedError) Reset() { + *x = PermissionDeniedError{} + if protoimpl.UnsafeEnabled { + mi := &file_errors_proto_errors_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } -func (x *InvalidArgument) String() string { +func (x *PermissionDeniedError) String() string { return protoimpl.X.MessageStringOf(x) } -func (*InvalidArgument) ProtoMessage() {} +func (*PermissionDeniedError) ProtoMessage() {} + +func (x *PermissionDeniedError) ProtoReflect() protoreflect.Message { + mi := &file_errors_proto_errors_proto_msgTypes[4] + 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 PermissionDeniedError.ProtoReflect.Descriptor instead. +func (*PermissionDeniedError) Descriptor() ([]byte, []int) { + return file_errors_proto_errors_proto_rawDescGZIP(), []int{4} +} + +func (x *PermissionDeniedError) GetKind() string { + if x != nil { + return x.Kind + } + return "" +} + +func (x *PermissionDeniedError) GetId() string { + if x != nil { + return x.Id + } + return "" +} -func (x *InvalidArgument) ProtoReflect() protoreflect.Message { - mi := &file_errors_errors_proto_msgTypes[1] +func (x *PermissionDeniedError) GetAction() string { if x != nil { + return x.Action + } + return "" +} + +type InvalidArgumentError struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Kind string `protobuf:"bytes,1,opt,name=kind,proto3" json:"kind,omitempty"` + Id string `protobuf:"bytes,2,opt,name=id,proto3" json:"id,omitempty"` + Violations []*InvalidArgumentError_Violation `protobuf:"bytes,3,rep,name=violations,proto3" json:"violations,omitempty"` +} + +func (x *InvalidArgumentError) Reset() { + *x = InvalidArgumentError{} + if protoimpl.UnsafeEnabled { + mi := &file_errors_proto_errors_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *InvalidArgumentError) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*InvalidArgumentError) ProtoMessage() {} + +func (x *InvalidArgumentError) ProtoReflect() protoreflect.Message { + mi := &file_errors_proto_errors_proto_msgTypes[5] + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -98,93 +322,270 @@ func (x *InvalidArgument) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use InvalidArgument.ProtoReflect.Descriptor instead. -func (*InvalidArgument) Descriptor() ([]byte, []int) { - return file_errors_errors_proto_rawDescGZIP(), []int{1} +// Deprecated: Use InvalidArgumentError.ProtoReflect.Descriptor instead. +func (*InvalidArgumentError) Descriptor() ([]byte, []int) { + return file_errors_proto_errors_proto_rawDescGZIP(), []int{5} } -func (x *InvalidArgument) GetFields() map[string]*InvalidArgumentRule { +func (x *InvalidArgumentError) GetKind() string { if x != nil { - return x.Fields + return x.Kind + } + return "" +} + +func (x *InvalidArgumentError) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +func (x *InvalidArgumentError) GetViolations() []*InvalidArgumentError_Violation { + if x != nil { + return x.Violations } return nil } -var File_errors_errors_proto protoreflect.FileDescriptor - -var file_errors_errors_proto_rawDesc = []byte{ - 0x0a, 0x13, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x2f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x2e, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x11, 0x66, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x2e, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x22, 0x2b, 0x0a, 0x13, 0x49, 0x6e, 0x76, 0x61, - 0x6c, 0x69, 0x64, 0x41, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x75, 0x6c, 0x65, 0x12, - 0x14, 0x0a, 0x05, 0x72, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, - 0x72, 0x75, 0x6c, 0x65, 0x73, 0x22, 0xbc, 0x01, 0x0a, 0x0f, 0x49, 0x6e, 0x76, 0x61, 0x6c, 0x69, - 0x64, 0x41, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x46, 0x0a, 0x06, 0x66, 0x69, 0x65, - 0x6c, 0x64, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2e, 0x2e, 0x66, 0x6f, 0x75, 0x6e, - 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x2e, 0x49, 0x6e, - 0x76, 0x61, 0x6c, 0x69, 0x64, 0x41, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x46, 0x69, - 0x65, 0x6c, 0x64, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x06, 0x66, 0x69, 0x65, 0x6c, 0x64, - 0x73, 0x1a, 0x61, 0x0a, 0x0b, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, - 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, - 0x65, 0x79, 0x12, 0x3c, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x26, 0x2e, 0x66, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x65, - 0x72, 0x72, 0x6f, 0x72, 0x73, 0x2e, 0x49, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x41, 0x72, 0x67, - 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x75, 0x6c, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, - 0x3a, 0x02, 0x38, 0x01, 0x42, 0x32, 0x5a, 0x30, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, - 0x6f, 0x6d, 0x2f, 0x66, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2d, 0x67, 0x6f, - 0x2f, 0x66, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x65, 0x72, 0x72, 0x6f, - 0x72, 0x73, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +type InvalidArgumentError_Violation struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Field string `protobuf:"bytes,1,opt,name=field,proto3" json:"field,omitempty"` + Description string `protobuf:"bytes,2,opt,name=description,proto3" json:"description,omitempty"` +} + +func (x *InvalidArgumentError_Violation) Reset() { + *x = InvalidArgumentError_Violation{} + if protoimpl.UnsafeEnabled { + mi := &file_errors_proto_errors_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *InvalidArgumentError_Violation) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*InvalidArgumentError_Violation) ProtoMessage() {} + +func (x *InvalidArgumentError_Violation) ProtoReflect() protoreflect.Message { + mi := &file_errors_proto_errors_proto_msgTypes[6] + 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 InvalidArgumentError_Violation.ProtoReflect.Descriptor instead. +func (*InvalidArgumentError_Violation) Descriptor() ([]byte, []int) { + return file_errors_proto_errors_proto_rawDescGZIP(), []int{5, 0} +} + +func (x *InvalidArgumentError_Violation) GetField() string { + if x != nil { + return x.Field + } + return "" +} + +func (x *InvalidArgumentError_Violation) GetDescription() string { + if x != nil { + return x.Description + } + return "" +} + +var File_errors_proto_errors_proto protoreflect.FileDescriptor + +var file_errors_proto_errors_proto_rawDesc = []byte{ + 0x0a, 0x19, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x65, + 0x72, 0x72, 0x6f, 0x72, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x11, 0x66, 0x6f, 0x75, + 0x6e, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x22, 0x0f, + 0x0a, 0x0d, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x22, + 0x16, 0x0a, 0x14, 0x55, 0x6e, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, + 0x65, 0x64, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x22, 0x88, 0x01, 0x0a, 0x10, 0x53, 0x74, 0x61, 0x6c, + 0x65, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x12, 0x0a, 0x04, + 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6b, 0x69, 0x6e, 0x64, + 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, + 0x12, 0x25, 0x0a, 0x0e, 0x61, 0x63, 0x74, 0x75, 0x61, 0x6c, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, + 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0d, 0x61, 0x63, 0x74, 0x75, 0x61, 0x6c, + 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x29, 0x0a, 0x10, 0x65, 0x78, 0x70, 0x65, 0x63, + 0x74, 0x65, 0x64, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x05, 0x52, 0x0f, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x56, 0x65, 0x72, 0x73, 0x69, + 0x6f, 0x6e, 0x22, 0x33, 0x0a, 0x0d, 0x4e, 0x6f, 0x74, 0x46, 0x6f, 0x75, 0x6e, 0x64, 0x45, 0x72, + 0x72, 0x6f, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x22, 0x53, 0x0a, 0x15, 0x50, 0x65, 0x72, 0x6d, 0x69, + 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x44, 0x65, 0x6e, 0x69, 0x65, 0x64, 0x45, 0x72, 0x72, 0x6f, 0x72, + 0x12, 0x12, 0x0a, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, + 0x6b, 0x69, 0x6e, 0x64, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x02, 0x69, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0xd2, 0x01, 0x0a, + 0x14, 0x49, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x41, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, + 0x45, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x51, 0x0a, 0x0a, 0x76, 0x69, 0x6f, + 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x31, 0x2e, + 0x66, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x65, 0x72, 0x72, 0x6f, 0x72, + 0x73, 0x2e, 0x49, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x41, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, + 0x74, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x2e, 0x56, 0x69, 0x6f, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x52, 0x0a, 0x76, 0x69, 0x6f, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x1a, 0x43, 0x0a, 0x09, + 0x56, 0x69, 0x6f, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x14, 0x0a, 0x05, 0x66, 0x69, 0x65, + 0x6c, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x12, + 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, + 0x6e, 0x42, 0x2b, 0x5a, 0x29, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, + 0x72, 0x69, 0x2d, 0x6e, 0x61, 0x74, 0x2f, 0x66, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x2f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( - file_errors_errors_proto_rawDescOnce sync.Once - file_errors_errors_proto_rawDescData = file_errors_errors_proto_rawDesc + file_errors_proto_errors_proto_rawDescOnce sync.Once + file_errors_proto_errors_proto_rawDescData = file_errors_proto_errors_proto_rawDesc ) -func file_errors_errors_proto_rawDescGZIP() []byte { - file_errors_errors_proto_rawDescOnce.Do(func() { - file_errors_errors_proto_rawDescData = protoimpl.X.CompressGZIP(file_errors_errors_proto_rawDescData) +func file_errors_proto_errors_proto_rawDescGZIP() []byte { + file_errors_proto_errors_proto_rawDescOnce.Do(func() { + file_errors_proto_errors_proto_rawDescData = protoimpl.X.CompressGZIP(file_errors_proto_errors_proto_rawDescData) }) - return file_errors_errors_proto_rawDescData + return file_errors_proto_errors_proto_rawDescData } -var file_errors_errors_proto_msgTypes = make([]protoimpl.MessageInfo, 3) -var file_errors_errors_proto_goTypes = []any{ - (*InvalidArgumentRule)(nil), // 0: foundation.errors.InvalidArgumentRule - (*InvalidArgument)(nil), // 1: foundation.errors.InvalidArgument - nil, // 2: foundation.errors.InvalidArgument.FieldsEntry +var file_errors_proto_errors_proto_msgTypes = make([]protoimpl.MessageInfo, 7) +var file_errors_proto_errors_proto_goTypes = []interface{}{ + (*InternalError)(nil), // 0: foundation.errors.InternalError + (*UnauthenticatedError)(nil), // 1: foundation.errors.UnauthenticatedError + (*StaleObjectError)(nil), // 2: foundation.errors.StaleObjectError + (*NotFoundError)(nil), // 3: foundation.errors.NotFoundError + (*PermissionDeniedError)(nil), // 4: foundation.errors.PermissionDeniedError + (*InvalidArgumentError)(nil), // 5: foundation.errors.InvalidArgumentError + (*InvalidArgumentError_Violation)(nil), // 6: foundation.errors.InvalidArgumentError.Violation } -var file_errors_errors_proto_depIdxs = []int32{ - 2, // 0: foundation.errors.InvalidArgument.fields:type_name -> foundation.errors.InvalidArgument.FieldsEntry - 0, // 1: foundation.errors.InvalidArgument.FieldsEntry.value:type_name -> foundation.errors.InvalidArgumentRule - 2, // [2:2] is the sub-list for method output_type - 2, // [2:2] is the sub-list for method input_type - 2, // [2:2] is the sub-list for extension type_name - 2, // [2:2] is the sub-list for extension extendee - 0, // [0:2] is the sub-list for field type_name +var file_errors_proto_errors_proto_depIdxs = []int32{ + 6, // 0: foundation.errors.InvalidArgumentError.violations:type_name -> foundation.errors.InvalidArgumentError.Violation + 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_errors_proto_init() } -func file_errors_errors_proto_init() { - if File_errors_errors_proto != nil { +func init() { file_errors_proto_errors_proto_init() } +func file_errors_proto_errors_proto_init() { + if File_errors_proto_errors_proto != nil { return } + if !protoimpl.UnsafeEnabled { + file_errors_proto_errors_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*InternalError); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_errors_proto_errors_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*UnauthenticatedError); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_errors_proto_errors_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*StaleObjectError); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_errors_proto_errors_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*NotFoundError); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_errors_proto_errors_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PermissionDeniedError); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_errors_proto_errors_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*InvalidArgumentError); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_errors_proto_errors_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*InvalidArgumentError_Violation); 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_errors_proto_rawDesc, + RawDescriptor: file_errors_proto_errors_proto_rawDesc, NumEnums: 0, - NumMessages: 3, + NumMessages: 7, NumExtensions: 0, NumServices: 0, }, - GoTypes: file_errors_errors_proto_goTypes, - DependencyIndexes: file_errors_errors_proto_depIdxs, - MessageInfos: file_errors_errors_proto_msgTypes, + GoTypes: file_errors_proto_errors_proto_goTypes, + DependencyIndexes: file_errors_proto_errors_proto_depIdxs, + MessageInfos: file_errors_proto_errors_proto_msgTypes, }.Build() - File_errors_errors_proto = out.File - file_errors_errors_proto_rawDesc = nil - file_errors_errors_proto_goTypes = nil - file_errors_errors_proto_depIdxs = nil + File_errors_proto_errors_proto = out.File + file_errors_proto_errors_proto_rawDesc = nil + file_errors_proto_errors_proto_goTypes = nil + file_errors_proto_errors_proto_depIdxs = nil } diff --git a/errors/proto/errors.proto b/errors/proto/errors.proto index 741be2e..0e4c768 100644 --- a/errors/proto/errors.proto +++ b/errors/proto/errors.proto @@ -4,10 +4,38 @@ package foundation.errors; option go_package = "github.com/foundation-go/foundation/errors/proto"; -message InvalidArgumentRule { - repeated string rules = 1; +message InternalError { } -message InvalidArgument { - map fields = 3; -} \ No newline at end of file +message UnauthenticatedError { +} + +message StaleObjectError { + string kind = 1; + string id = 2; + int32 actual_version = 3; + int32 expected_version = 4; +} + +message NotFoundError { + string kind = 1; + string id = 2; +} + +message PermissionDeniedError { + string kind = 1; + string id = 2; + string action = 3; +} + +message InvalidArgumentError { + message Violation { + string field = 1; + string description = 2; + } + + string kind = 1; + string id = 2; + + repeated Violation violations = 3; +} diff --git a/examples/clubchat/chats/cmd/api/list_chats.go b/examples/clubchat/chats/cmd/api/list_chats.go index 7bf39a5..7afd06e 100644 --- a/examples/clubchat/chats/cmd/api/list_chats.go +++ b/examples/clubchat/chats/cmd/api/list_chats.go @@ -13,7 +13,7 @@ func (s *chatsServer) ListChats(ctx context.Context, req *pb.ListChatsRequest) ( if false { return nil, ferr.NewInvalidArgumentError("Chat", "", ferr.ErrorViolations{ "base": {ErrorCodeCustom}, - }, 0) + }) } // Check required scopes diff --git a/grpc/errors_interceptor_test.go b/grpc/errors_interceptor_test.go index 656e92d..a1dfea1 100644 --- a/grpc/errors_interceptor_test.go +++ b/grpc/errors_interceptor_test.go @@ -14,7 +14,7 @@ import ( func TestFoundationErrorToStatusInterceptor(t *testing.T) { // Define a mock handler that returns an error mockHandler := func(context.Context, interface{}) (interface{}, error) { - return nil, ferr.NewNotFoundError(nil, "test", "123", 0) + return nil, ferr.NewNotFoundError(nil, "test", "123") } ctx := context.Background()