330 lines
7.3 KiB
Go
330 lines
7.3 KiB
Go
package errors
|
|
|
|
import (
|
|
"net/http"
|
|
"time"
|
|
|
|
"google.golang.org/grpc/codes"
|
|
)
|
|
|
|
// Builder provides fluent API for building errors
|
|
type Builder struct {
|
|
code string
|
|
message string
|
|
category string
|
|
httpStatus int
|
|
grpcCode codes.Code
|
|
metadata Metadata
|
|
cause error
|
|
}
|
|
|
|
// NewBuilder creates a new error builder
|
|
func NewBuilder() *Builder {
|
|
return &Builder{
|
|
metadata: make(Metadata),
|
|
}
|
|
}
|
|
|
|
// Code sets error code
|
|
func (b *Builder) Code(code string) *Builder {
|
|
b.code = code
|
|
info := GetErrorInfo(code)
|
|
b.category = info.Category
|
|
b.httpStatus = info.HTTPStatus
|
|
b.grpcCode = info.GRPCCode
|
|
return b
|
|
}
|
|
|
|
// Message sets error message
|
|
func (b *Builder) Message(message string) *Builder {
|
|
b.message = message
|
|
return b
|
|
}
|
|
|
|
// Category sets error category
|
|
func (b *Builder) Category(category string) *Builder {
|
|
b.category = category
|
|
return b
|
|
}
|
|
|
|
// HTTPStatus sets HTTP status code
|
|
func (b *Builder) HTTPStatus(status int) *Builder {
|
|
b.httpStatus = status
|
|
return b
|
|
}
|
|
|
|
// GRPCCode sets gRPC code
|
|
func (b *Builder) GRPCCode(code codes.Code) *Builder {
|
|
b.grpcCode = code
|
|
return b
|
|
}
|
|
|
|
// Metadata adds metadata
|
|
func (b *Builder) Metadata(key string, value interface{}) *Builder {
|
|
if b.metadata == nil {
|
|
b.metadata = make(Metadata)
|
|
}
|
|
b.metadata[key] = value
|
|
return b
|
|
}
|
|
|
|
// Metadatas adds multiple metadata
|
|
func (b *Builder) Metadatas(metadata Metadata) *Builder {
|
|
if b.metadata == nil {
|
|
b.metadata = make(Metadata)
|
|
}
|
|
for k, v := range metadata {
|
|
b.metadata[k] = v
|
|
}
|
|
return b
|
|
}
|
|
|
|
// Cause sets underlying error
|
|
func (b *Builder) Cause(err error) *Builder {
|
|
b.cause = err
|
|
return b
|
|
}
|
|
|
|
// Timestamp adds timestamp
|
|
func (b *Builder) Timestamp(t time.Time) *Builder {
|
|
b.Metadata("timestamp", t.Format(time.RFC3339))
|
|
return b
|
|
}
|
|
|
|
// RequestID adds request ID
|
|
func (b *Builder) RequestID(id string) *Builder {
|
|
b.Metadata("request_id", id)
|
|
return b
|
|
}
|
|
|
|
// UserID adds user ID
|
|
func (b *Builder) UserID(id string) *Builder {
|
|
b.Metadata("user_id", id)
|
|
return b
|
|
}
|
|
|
|
// Service adds service name
|
|
func (b *Builder) Service(name string) *Builder {
|
|
b.Metadata("service", name)
|
|
return b
|
|
}
|
|
|
|
// Operation adds operation name
|
|
func (b *Builder) Operation(name string) *Builder {
|
|
b.Metadata("operation", name)
|
|
return b
|
|
}
|
|
|
|
// Retryable sets retryable flag
|
|
func (b *Builder) Retryable(retryable bool) *Builder {
|
|
b.Metadata("retryable", retryable)
|
|
return b
|
|
}
|
|
|
|
// Severity sets error severity
|
|
func (b *Builder) Severity(severity string) *Builder {
|
|
b.Metadata("severity", severity)
|
|
return b
|
|
}
|
|
|
|
// Component adds component name
|
|
func (b *Builder) Component(name string) *Builder {
|
|
b.Metadata("component", name)
|
|
return b
|
|
}
|
|
|
|
// Build creates the final error
|
|
func (b *Builder) Build() Error {
|
|
// Use defaults if not set
|
|
if b.code == "" {
|
|
b.code = ErrCodeInternalError
|
|
}
|
|
if b.message == "" {
|
|
b.message = "An error occurred"
|
|
}
|
|
if b.category == "" {
|
|
b.category = CategoryInternal
|
|
}
|
|
if b.httpStatus == 0 {
|
|
b.httpStatus = http.StatusInternalServerError
|
|
}
|
|
if b.grpcCode == codes.OK {
|
|
b.grpcCode = codes.Internal
|
|
}
|
|
if b.metadata == nil {
|
|
b.metadata = make(Metadata)
|
|
}
|
|
if _, exists := b.metadata["timestamp"]; !exists {
|
|
b.Metadata("timestamp", time.Now().Format(time.RFC3339))
|
|
}
|
|
|
|
return &AppError{
|
|
code: b.code,
|
|
message: b.message,
|
|
category: b.category,
|
|
httpStatus: b.httpStatus,
|
|
grpcCode: b.grpcCode,
|
|
metadata: b.metadata,
|
|
cause: b.cause,
|
|
timestamp: time.Now(),
|
|
stackTrace: captureStackTrace(),
|
|
}
|
|
}
|
|
|
|
// NewValidationError creates validation error builder
|
|
func NewValidationError() *Builder {
|
|
return NewBuilder().
|
|
Code(ErrCodeValidationFailed).
|
|
Category(CategoryValidation).
|
|
HTTPStatus(http.StatusBadRequest).
|
|
GRPCCode(codes.InvalidArgument).
|
|
Severity("warning")
|
|
}
|
|
|
|
// NotFoundError creates not found error builder
|
|
func NotFoundError() *Builder {
|
|
return NewBuilder().
|
|
Code(ErrCodeNotFound).
|
|
Category(CategoryNotFound).
|
|
HTTPStatus(http.StatusNotFound).
|
|
GRPCCode(codes.NotFound).
|
|
Severity("info")
|
|
}
|
|
|
|
// UnauthorizedError creates unauthorized error builder
|
|
func UnauthorizedError() *Builder {
|
|
return NewBuilder().
|
|
Code(ErrCodeUnauthorized).
|
|
Category(CategoryUnauthorized).
|
|
HTTPStatus(http.StatusUnauthorized).
|
|
GRPCCode(codes.Unauthenticated).
|
|
Severity("warning")
|
|
}
|
|
|
|
// ForbiddenError creates forbidden error builder
|
|
func ForbiddenError() *Builder {
|
|
return NewBuilder().
|
|
Code(ErrCodeForbidden).
|
|
Category(CategoryForbidden).
|
|
HTTPStatus(http.StatusForbidden).
|
|
GRPCCode(codes.PermissionDenied).
|
|
Severity("warning")
|
|
}
|
|
|
|
// AlreadyExistsError creates already exists error builder
|
|
func AlreadyExistsError() *Builder {
|
|
return NewBuilder().
|
|
Code(ErrCodeConflict).
|
|
Category(CategoryConflict).
|
|
HTTPStatus(http.StatusConflict).
|
|
GRPCCode(codes.AlreadyExists).
|
|
Severity("warning")
|
|
}
|
|
|
|
// DuplicateError creates duplicate entry error builder
|
|
func DuplicateError() *Builder {
|
|
return NewBuilder().
|
|
Code(ErrCodeDuplicateEntry).
|
|
Category(CategoryConflict).
|
|
HTTPStatus(http.StatusConflict).
|
|
GRPCCode(codes.AlreadyExists).
|
|
Severity("warning")
|
|
}
|
|
|
|
// ConflictError creates conflict error builder
|
|
func ConflictError() *Builder {
|
|
return NewBuilder().
|
|
Code(ErrCodeConflict).
|
|
Category(CategoryConflict).
|
|
HTTPStatus(http.StatusConflict).
|
|
GRPCCode(codes.AlreadyExists).
|
|
Severity("warning")
|
|
}
|
|
|
|
// InternalError creates internal error builder
|
|
func InternalError() *Builder {
|
|
return NewBuilder().
|
|
Code(ErrCodeInternalError).
|
|
Category(CategoryInternal).
|
|
HTTPStatus(http.StatusInternalServerError).
|
|
GRPCCode(codes.Internal).
|
|
Severity("error")
|
|
}
|
|
|
|
// ExternalError creates external error builder
|
|
func ExternalError() *Builder {
|
|
return NewBuilder().
|
|
Code(ErrCodeExternalError).
|
|
Category(CategoryExternal).
|
|
HTTPStatus(http.StatusBadGateway).
|
|
GRPCCode(codes.Unavailable).
|
|
Severity("error").
|
|
Retryable(true)
|
|
}
|
|
|
|
// TimeoutError creates timeout error builder
|
|
func TimeoutError() *Builder {
|
|
return NewBuilder().
|
|
Code(ErrCodeTimeout).
|
|
Category(CategoryTimeout).
|
|
HTTPStatus(http.StatusRequestTimeout).
|
|
GRPCCode(codes.DeadlineExceeded).
|
|
Severity("warning").
|
|
Retryable(true)
|
|
}
|
|
|
|
// BusinessError creates business error builder
|
|
func BusinessError() *Builder {
|
|
return NewBuilder().
|
|
Code(ErrCodeBusinessRule).
|
|
Category(CategoryBusiness).
|
|
HTTPStatus(http.StatusBadRequest).
|
|
GRPCCode(codes.FailedPrecondition).
|
|
Severity("warning")
|
|
}
|
|
|
|
// DatabaseError creates database error builder
|
|
func DatabaseError() *Builder {
|
|
return NewBuilder().
|
|
Code(ErrCodeDatabaseError).
|
|
Category(CategoryDatabase).
|
|
HTTPStatus(http.StatusInternalServerError).
|
|
GRPCCode(codes.Internal).
|
|
Severity("error")
|
|
}
|
|
|
|
// NetworkError creates network error builder
|
|
func NetworkError() *Builder {
|
|
return NewBuilder().
|
|
Code(ErrCodeNetworkError).
|
|
Category(CategoryNetwork).
|
|
HTTPStatus(http.StatusBadGateway).
|
|
GRPCCode(codes.Unavailable).
|
|
Severity("error").
|
|
Retryable(true)
|
|
}
|
|
|
|
// CustomError creates custom error builder
|
|
func CustomError(code, message string) *Builder {
|
|
return NewBuilder().
|
|
Code(code).
|
|
Message(message)
|
|
}
|
|
|
|
// FromError creates builder from existing error
|
|
func FromErrorBuilder(err error) *Builder {
|
|
appErr := FromError(err)
|
|
if appErr == nil {
|
|
return NewBuilder()
|
|
}
|
|
|
|
return NewBuilder().
|
|
Code(appErr.Code()).
|
|
Message(appErr.Error()).
|
|
Category(appErr.Category()).
|
|
HTTPStatus(appErr.HTTPStatus()).
|
|
GRPCCode(appErr.GRPCCode()).
|
|
Metadatas(appErr.Metadata()).
|
|
Cause(appErr.Cause())
|
|
}
|