first commit
This commit is contained in:
@@ -0,0 +1,329 @@
|
||||
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())
|
||||
}
|
||||
Reference in New Issue
Block a user