Files
go-omni/errors/errors.go

83 lines
1.9 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; default response encoders and
// replay snapshots never serialize it.
type APIError struct {
Status int
Code string
Message string
Cause error
BusinessCode any // Optional public code resolved from a project catalog.
reference bool
params map[string]any
paramErr error
}
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)
}