data layer
This commit is contained in:
75
data-layer/api/internal/apperr/apperr.go
Normal file
75
data-layer/api/internal/apperr/apperr.go
Normal file
@@ -0,0 +1,75 @@
|
||||
// Package apperr defines AppError, the single error type returned by every
|
||||
// service/repo function. Handlers translate AppError into HTTP responses.
|
||||
package apperr
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type AppError struct {
|
||||
Code int // HTTP status to return
|
||||
Message string // user-facing message (safe to expose)
|
||||
Field string // optional: which field caused the error
|
||||
RetryAfter int // seconds, for 429
|
||||
Err error // original error for logging (never exposed)
|
||||
}
|
||||
|
||||
func (e *AppError) Error() string {
|
||||
if e.Err != nil {
|
||||
return fmt.Sprintf("%s: %v", e.Message, e.Err)
|
||||
}
|
||||
return e.Message
|
||||
}
|
||||
|
||||
func (e *AppError) Unwrap() error { return e.Err }
|
||||
|
||||
// As reports whether err is or wraps an *AppError.
|
||||
func As(err error) (*AppError, bool) {
|
||||
var ae *AppError
|
||||
if errors.As(err, &ae) {
|
||||
return ae, true
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Constructors
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
func BadRequest(msg, field string, err error) *AppError {
|
||||
return &AppError{Code: http.StatusBadRequest, Message: msg, Field: field, Err: err}
|
||||
}
|
||||
|
||||
func Unauthorized(msg string) *AppError {
|
||||
return &AppError{Code: http.StatusUnauthorized, Message: msg}
|
||||
}
|
||||
|
||||
func Forbidden(msg string) *AppError {
|
||||
return &AppError{Code: http.StatusForbidden, Message: msg}
|
||||
}
|
||||
|
||||
func NotFound(msg string) *AppError {
|
||||
return &AppError{Code: http.StatusNotFound, Message: msg}
|
||||
}
|
||||
|
||||
func Conflict(msg string, err error) *AppError {
|
||||
return &AppError{Code: http.StatusConflict, Message: msg, Err: err}
|
||||
}
|
||||
|
||||
func UnprocessableEntity(msg string) *AppError {
|
||||
return &AppError{Code: http.StatusUnprocessableEntity, Message: msg}
|
||||
}
|
||||
|
||||
func TooManyRequests(retryAfterSeconds int) *AppError {
|
||||
return &AppError{
|
||||
Code: http.StatusTooManyRequests,
|
||||
Message: "rate limit exceeded",
|
||||
RetryAfter: retryAfterSeconds,
|
||||
}
|
||||
}
|
||||
|
||||
func Internal(err error) *AppError {
|
||||
return &AppError{Code: http.StatusInternalServerError, Message: "internal server error", Err: err}
|
||||
}
|
||||
Reference in New Issue
Block a user