errors.go 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  1. package errors
  2. import (
  3. "fmt"
  4. "net/http"
  5. )
  6. // ErrorType represents the type of error
  7. type ErrorType string
  8. const (
  9. ErrorTypeValidation ErrorType = "VALIDATION_ERROR"
  10. ErrorTypeAuthorization ErrorType = "AUTHORIZATION_ERROR"
  11. ErrorTypeAuthentication ErrorType = "AUTHENTICATION_ERROR"
  12. ErrorTypeNotFound ErrorType = "NOT_FOUND"
  13. ErrorTypeConflict ErrorType = "CONFLICT"
  14. ErrorTypeInternal ErrorType = "INTERNAL_ERROR"
  15. ErrorTypeBadRequest ErrorType = "BAD_REQUEST"
  16. )
  17. // AppError represents an application error
  18. type AppError struct {
  19. Type ErrorType `json:"type"`
  20. Message string `json:"message"`
  21. Detail string `json:"detail,omitempty"`
  22. Field string `json:"field,omitempty"`
  23. Code int `json:"-"`
  24. Err error `json:"-"`
  25. }
  26. func (e *AppError) Error() string {
  27. if e.Detail != "" {
  28. return fmt.Sprintf("%s: %s (%s)", e.Type, e.Message, e.Detail)
  29. }
  30. return fmt.Sprintf("%s: %s", e.Type, e.Message)
  31. }
  32. func (e *AppError) Unwrap() error {
  33. return e.Err
  34. }
  35. // Error constructors
  36. func NewValidationError(message, field string) *AppError {
  37. return &AppError{
  38. Type: ErrorTypeValidation,
  39. Message: message,
  40. Field: field,
  41. Code: http.StatusBadRequest,
  42. }
  43. }
  44. func NewAuthorizationError(message string) *AppError {
  45. return &AppError{
  46. Type: ErrorTypeAuthorization,
  47. Message: message,
  48. Code: http.StatusForbidden,
  49. }
  50. }
  51. func NewAuthenticationError(message string) *AppError {
  52. return &AppError{
  53. Type: ErrorTypeAuthentication,
  54. Message: message,
  55. Code: http.StatusUnauthorized,
  56. }
  57. }
  58. func NewNotFoundError(resource, id string) *AppError {
  59. return &AppError{
  60. Type: ErrorTypeNotFound,
  61. Message: fmt.Sprintf("%s not found", resource),
  62. Detail: fmt.Sprintf("ID: %s", id),
  63. Code: http.StatusNotFound,
  64. }
  65. }
  66. func NewConflictError(message, detail string) *AppError {
  67. return &AppError{
  68. Type: ErrorTypeConflict,
  69. Message: message,
  70. Detail: detail,
  71. Code: http.StatusConflict,
  72. }
  73. }
  74. func NewInternalError(message string, err error) *AppError {
  75. return &AppError{
  76. Type: ErrorTypeInternal,
  77. Message: message,
  78. Detail: err.Error(),
  79. Code: http.StatusInternalServerError,
  80. Err: err,
  81. }
  82. }
  83. // Error type checks
  84. func IsNotFound(err error) bool {
  85. if err == nil {
  86. return false
  87. }
  88. if e, ok := err.(*AppError); ok {
  89. return e.Type == ErrorTypeNotFound
  90. }
  91. return false
  92. }
  93. func IsValidation(err error) bool {
  94. if err == nil {
  95. return false
  96. }
  97. if e, ok := err.(*AppError); ok {
  98. return e.Type == ErrorTypeValidation
  99. }
  100. return false
  101. }
  102. func IsAuthorization(err error) bool {
  103. if err == nil {
  104. return false
  105. }
  106. if e, ok := err.(*AppError); ok {
  107. return e.Type == ErrorTypeAuthorization || e.Type == ErrorTypeAuthentication
  108. }
  109. return false
  110. }
  111. // WithDetail adds detail to an existing error
  112. func WithDetail(err *AppError, detail string) *AppError {
  113. if err == nil {
  114. return nil
  115. }
  116. err.Detail = detail
  117. return err
  118. }
  119. // WithField adds a field name to an existing error
  120. func WithField(err *AppError, field string) *AppError {
  121. if err == nil {
  122. return nil
  123. }
  124. err.Field = field
  125. return err
  126. }