errors.go 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107
  1. // Package webhook provides functionality for sending and validating webhooks
  2. package webhook
  3. import (
  4. "errors"
  5. "fmt"
  6. "net/http"
  7. )
  8. // Common webhook error types
  9. var (
  10. // ErrInvalidConfig indicates missing or invalid configuration values
  11. ErrInvalidConfig = errors.New("invalid webhook configuration")
  12. // ErrPayloadMarshall indicates a failure to marshal the webhook payload
  13. ErrPayloadMarshall = errors.New("failed to marshal webhook payload")
  14. // ErrRequestCreation indicates a failure to create the HTTP request
  15. ErrRequestCreation = errors.New("failed to create webhook request")
  16. // ErrRequestFailed indicates a permanent failure in the webhook request
  17. ErrRequestFailed = errors.New("webhook request failed")
  18. // ErrTooManyRequests indicates rate limiting by the webhook endpoint
  19. ErrTooManyRequests = errors.New("rate limit exceeded")
  20. // ErrMaxRetriesReached indicates all retry attempts have been exhausted
  21. ErrMaxRetriesReached = errors.New("maximum retries reached")
  22. // ErrInvalidSignature indicates webhook signature validation failed
  23. ErrInvalidSignature = errors.New("invalid webhook signature")
  24. )
  25. // Error represents a webhook-specific error with detailed context
  26. type Error struct {
  27. // Op is the operation that failed (e.g., "send", "validate")
  28. Op string
  29. // Code is the HTTP status code (if applicable)
  30. Code int
  31. // Err is the underlying error
  32. Err error
  33. // Details contains additional error context
  34. Details string
  35. }
  36. // Error returns a string representation of the error
  37. func (e *Error) Error() string {
  38. var msg string
  39. if e.Code > 0 {
  40. msg = fmt.Sprintf("webhook %s failed with status %d", e.Op, e.Code)
  41. } else {
  42. msg = fmt.Sprintf("webhook %s failed", e.Op)
  43. }
  44. if e.Details != "" {
  45. msg = fmt.Sprintf("%s: %s", msg, e.Details)
  46. }
  47. if e.Err != nil {
  48. msg = fmt.Sprintf("%s: %v", msg, e.Err)
  49. }
  50. return msg
  51. }
  52. // Unwrap returns the underlying error
  53. func (e *Error) Unwrap() error {
  54. return e.Err
  55. }
  56. // Is reports whether target matches this error
  57. func (e *Error) Is(target error) bool {
  58. return errors.Is(e.Err, target)
  59. }
  60. // NewError creates a new webhook error
  61. func NewError(op string, err error, details string) *Error {
  62. return &Error{
  63. Op: op,
  64. Err: err,
  65. Details: details,
  66. }
  67. }
  68. // NewHTTPError creates a new webhook error with HTTP status code
  69. func NewHTTPError(op string, code int, err error, details string) *Error {
  70. return &Error{
  71. Op: op,
  72. Code: code,
  73. Err: err,
  74. Details: details,
  75. }
  76. }
  77. // IsTemporaryError returns true if the error is temporary and the operation can be retried
  78. func IsTemporaryError(err error) bool {
  79. var e *Error
  80. if errors.As(err, &e) {
  81. return e.Code == http.StatusTooManyRequests ||
  82. (e.Code >= 500 && e.Code <= 599) ||
  83. e.Is(ErrTooManyRequests)
  84. }
  85. return false
  86. }