All checks were successful
Deploy Smart Search Backend Test / deploy (push) Successful in 1m24s
106 lines
2.3 KiB
Go
106 lines
2.3 KiB
Go
package errors
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
|
|
"go.uber.org/zap"
|
|
"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")
|
|
}
|
|
|
|
switch appErr.Code {
|
|
case AuthInvalidCredentials, AuthMissing, AuthInvalidToken, RefreshInvalid:
|
|
return status.Error(codes.Unauthenticated, appErr.Message)
|
|
case InviteLimitReached:
|
|
return status.Error(codes.ResourceExhausted, appErr.Message)
|
|
case InsufficientBalance, InviteInvalidOrExpired:
|
|
return status.Error(codes.FailedPrecondition, appErr.Message)
|
|
case EmailAlreadyExists:
|
|
return status.Error(codes.AlreadyExists, appErr.Message)
|
|
case UserNotFound, RequestNotFound:
|
|
return status.Error(codes.NotFound, appErr.Message)
|
|
default:
|
|
return status.Error(codes.Unknown, appErr.Message)
|
|
}
|
|
}
|