70 lines
1.7 KiB
Go
70 lines
1.7 KiB
Go
package notify
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
)
|
|
|
|
// Sentinel errors for programmatic handling.
|
|
var (
|
|
ErrInvalidConfig = errors.New("invalid configuration")
|
|
ErrUnauthorized = errors.New("unauthorized")
|
|
ErrForbidden = errors.New("forbidden")
|
|
ErrSuppressed = errors.New("recipient suppressed")
|
|
ErrHostNotFound = errors.New("host not found")
|
|
ErrFromNotFound = errors.New("from address not registered")
|
|
ErrValidation = errors.New("validation error")
|
|
ErrServerError = errors.New("server error")
|
|
ErrRateLimit = errors.New("rate limit exceeded")
|
|
)
|
|
|
|
// APIError represents an error returned by the notify API.
|
|
type APIError struct {
|
|
StatusCode int
|
|
Message string
|
|
Code string
|
|
err error
|
|
}
|
|
|
|
func (e *APIError) Error() string {
|
|
if e.Code != "" {
|
|
return fmt.Sprintf("notify api error (status %d): [%s] %s", e.StatusCode, e.Code, e.Message)
|
|
}
|
|
return fmt.Sprintf("notify api error (status %d): %s", e.StatusCode, e.Message)
|
|
}
|
|
|
|
func (e *APIError) Unwrap() error {
|
|
return e.err
|
|
}
|
|
|
|
// classifyError maps an HTTP status and optional error code to a sentinel error.
|
|
func classifyError(statusCode int, code string) error {
|
|
// Check specific error codes first.
|
|
switch code {
|
|
case "suppressed":
|
|
return ErrSuppressed
|
|
case "host_not_found":
|
|
return ErrHostNotFound
|
|
case "from_not_found":
|
|
return ErrFromNotFound
|
|
}
|
|
|
|
// Fall back to HTTP status.
|
|
switch {
|
|
case statusCode == 401:
|
|
return ErrUnauthorized
|
|
case statusCode == 403:
|
|
return ErrForbidden
|
|
case statusCode == 422:
|
|
return ErrValidation
|
|
case statusCode == 429:
|
|
return ErrRateLimit
|
|
case statusCode >= 400 && statusCode < 500:
|
|
return ErrValidation
|
|
case statusCode >= 500:
|
|
return ErrServerError
|
|
default:
|
|
return nil
|
|
}
|
|
}
|