Files
smart-search-back/pkg/errors/errors.go
vallyenfail 33b70d1ee4
All checks were successful
Deploy Smart Search Backend Test / deploy (push) Successful in 2m55s
add service
2026-01-19 17:20:21 +03:00

120 lines
2.5 KiB
Go

package errors
import (
"errors"
"fmt"
"go.uber.org/zap"
"google.golang.org/genproto/googleapis/rpc/errdetails"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
type ErrorType int
const (
BusinessError ErrorType = iota
InternalErrorType
)
type AppError struct {
Code string
Message string
Type ErrorType
Err error
}
func (e *AppError) Error() string {
if e.Err != nil {
return fmt.Sprintf("%s: %s (%v)", e.Code, e.Message, e.Err)
}
return fmt.Sprintf("%s: %s", e.Code, e.Message)
}
func (e *AppError) Unwrap() error {
return e.Err
}
func NewBusinessError(code, message string) *AppError {
return &AppError{
Code: code,
Message: message,
Type: BusinessError,
}
}
func NewInternalError(code, message string, err error) *AppError {
return &AppError{
Code: code,
Message: message,
Type: InternalErrorType,
Err: err,
}
}
func IsBusinessError(err error, code string) bool {
var appErr *AppError
if !errors.As(err, &appErr) {
return false
}
return appErr.Type == BusinessError && appErr.Code == code
}
func ToGRPCError(err error, zapLogger *zap.Logger, method string) error {
if err == nil {
return nil
}
var appErr *AppError
ok := errors.As(err, &appErr)
if !ok {
if zapLogger != nil {
zapLogger.Error("gRPC handler error: unknown error type",
zap.String("method", method),
zap.Error(err),
)
}
return status.Error(codes.Internal, "internal server error")
}
if appErr.Type == InternalErrorType {
if zapLogger != nil {
zapLogger.Error("gRPC handler error: internal error",
zap.String("method", method),
zap.String("code", appErr.Code),
zap.String("message", appErr.Message),
zap.Error(appErr.Err),
)
}
return status.Error(codes.Internal, "internal server error")
}
var grpcCode codes.Code
switch appErr.Code {
case AuthInvalidCredentials, AuthMissing, AuthInvalidToken, RefreshInvalid:
grpcCode = codes.Unauthenticated
case PermissionDenied:
grpcCode = codes.PermissionDenied
case InviteLimitReached:
grpcCode = codes.ResourceExhausted
case InsufficientBalance:
grpcCode = codes.FailedPrecondition
case InviteInvalidOrExpired:
grpcCode = codes.NotFound
case EmailAlreadyExists:
grpcCode = codes.AlreadyExists
case UserNotFound, RequestNotFound:
grpcCode = codes.NotFound
default:
grpcCode = codes.Unknown
}
st, err := status.New(grpcCode, appErr.Message).WithDetails(&errdetails.ErrorInfo{
Reason: appErr.Code,
})
if err != nil {
return status.Error(grpcCode, appErr.Message)
}
return st.Err()
}