80 lines
1.8 KiB
Go
80 lines
1.8 KiB
Go
package apierrors
|
|
|
|
import (
|
|
stdErrors "errors"
|
|
"net/http"
|
|
)
|
|
|
|
// APIError is the public error shape used by handlers and middleware.
|
|
// Cause is retained for logging and unwrapping, but is never serialized.
|
|
type APIError struct {
|
|
Status int
|
|
Code string
|
|
Message string
|
|
Cause error
|
|
BusinessCode any // Optional public code resolved from a project catalog.
|
|
reference bool
|
|
}
|
|
|
|
func (e *APIError) Error() string {
|
|
if e == nil {
|
|
return ""
|
|
}
|
|
if e.Cause != nil {
|
|
return e.Code + ": " + e.Message + ": " + e.Cause.Error()
|
|
}
|
|
return e.Code + ": " + e.Message
|
|
}
|
|
|
|
func (e *APIError) Unwrap() error {
|
|
if e == nil {
|
|
return nil
|
|
}
|
|
return e.Cause
|
|
}
|
|
|
|
// WithCause attaches an internal cause while keeping the public response stable.
|
|
func (e *APIError) WithCause(cause error) *APIError {
|
|
e.Cause = cause
|
|
return e
|
|
}
|
|
|
|
func New(status int, code, message string) *APIError {
|
|
return &APIError{Status: status, Code: code, Message: message}
|
|
}
|
|
|
|
func BadRequest(code, message string) *APIError {
|
|
return New(http.StatusBadRequest, code, message)
|
|
}
|
|
|
|
func Unauthorized(code, message string) *APIError {
|
|
return New(http.StatusUnauthorized, code, message)
|
|
}
|
|
|
|
func Forbidden(code, message string) *APIError {
|
|
return New(http.StatusForbidden, code, message)
|
|
}
|
|
|
|
func NotFound(code, message string) *APIError {
|
|
return New(http.StatusNotFound, code, message)
|
|
}
|
|
|
|
func Internal(cause error) *APIError {
|
|
return New(http.StatusInternalServerError, "INTERNAL_ERROR", "internal server error").WithCause(cause)
|
|
}
|
|
|
|
// From converts any error into the public API error shape.
|
|
func From(err error) *APIError {
|
|
if err == nil {
|
|
return nil
|
|
}
|
|
var apiErr *APIError
|
|
if stdErrors.As(err, &apiErr) && apiErr != nil {
|
|
if apiErr.reference {
|
|
return Internal(err)
|
|
}
|
|
return apiErr
|
|
}
|
|
return Internal(err)
|
|
}
|