forked from go-fuego/fuego
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy patherrors.go
More file actions
143 lines (104 loc) · 4.48 KB
/
errors.go
File metadata and controls
143 lines (104 loc) · 4.48 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
package fuego
import (
"errors"
"fmt"
"log/slog"
"net/http"
)
// ErrorWithStatus is an interface that can be implemented by an error to provide
// additional information about the error.
type ErrorWithStatus interface {
error
StatusCode() int
}
// HTTPError is the error response used by the serialization part of the framework.
type HTTPError struct {
// Developer readable error message. Not shown to the user to avoid security leaks.
Err error `json:"-" xml:"-"`
// Short title of the error
Title string `json:"title,omitempty" xml:"title,omitempty" description:"Short title of the error" example:"failed to fetch details"`
// HTTP status code. If using a different type than [HTTPError], for example [BadRequestError], this will be automatically overridden after Fuego error handling.
Status int `json:"status,omitempty" xml:"status,omitempty" description:"HTTP status code" example:"403"`
// Human readable error message
Detail string `json:"detail,omitempty" xml:"detail,omitempty" description:"Human readable error message" example:"details cannot be loaded"`
Instance string `json:"instance,omitempty" xml:"instance,omitempty"`
Errors []ErrorItem `json:"errors,omitempty" xml:"errors,omitempty"`
}
func (HTTPError) OpenApiName() string {
return "error response"
}
type ErrorItem struct {
Name string `json:"name" xml:"name" description:"For example, name of the parameter that caused the error"`
Reason string `json:"reason" xml:"reason" description:"Human readable error message"`
More map[string]any `json:"more,omitempty" xml:"more,omitempty" description:"Additional information about the error"`
}
func (e HTTPError) Error() string {
title := e.Title
if title == "" {
title = http.StatusText(e.Status)
if title == "" {
title = "HTTP Error"
}
}
return fmt.Sprintf("%s (%d): %s", title, e.Status, e.Detail)
}
func (e HTTPError) StatusCode() int {
if e.Status == 0 {
return http.StatusInternalServerError
}
return e.Status
}
func (e HTTPError) Unwrap() error { return e.Err }
// BadRequestError is an error used to return a 400 status code.
type BadRequestError HTTPError
var _ ErrorWithStatus = BadRequestError{}
func (e BadRequestError) Error() string { return e.Err.Error() }
func (e BadRequestError) StatusCode() int { return http.StatusBadRequest }
func (e BadRequestError) Unwrap() error { return HTTPError(e) }
// NotFoundError is an error used to return a 404 status code.
type NotFoundError HTTPError
var _ ErrorWithStatus = NotFoundError{}
func (e NotFoundError) Error() string { return e.Err.Error() }
func (e NotFoundError) StatusCode() int { return http.StatusNotFound }
func (e NotFoundError) Unwrap() error { return HTTPError(e) }
// UnauthorizedError is an error used to return a 401 status code.
type UnauthorizedError HTTPError
var _ ErrorWithStatus = UnauthorizedError{}
func (e UnauthorizedError) Error() string { return e.Err.Error() }
func (e UnauthorizedError) StatusCode() int { return http.StatusUnauthorized }
func (e UnauthorizedError) Unwrap() error { return HTTPError(e) }
// ForbiddenError is an error used to return a 403 status code.
type ForbiddenError HTTPError
var _ ErrorWithStatus = ForbiddenError{}
func (e ForbiddenError) Error() string { return e.Err.Error() }
func (e ForbiddenError) StatusCode() int { return http.StatusForbidden }
func (e ForbiddenError) Unwrap() error { return HTTPError(e) }
// ConflictError is an error used to return a 409 status code.
type ConflictError HTTPError
var _ ErrorWithStatus = ConflictError{}
func (e ConflictError) Error() string { return e.Err.Error() }
func (e ConflictError) StatusCode() int { return http.StatusConflict }
func (e ConflictError) Unwrap() error { return HTTPError(e) }
// ErrorHandler is the default error handler used by the framework.
// It transforms any error into the unified error type [HTTPError],
// Using the [ErrorWithStatus] and [ErrorWithInfo] interfaces.
func ErrorHandler(err error) error {
errResponse := HTTPError{
Err: err,
}
var errorInfo HTTPError
if errors.As(err, &errorInfo) {
errResponse = errorInfo
}
// Check status code
errResponse.Status = http.StatusInternalServerError
var errorStatus ErrorWithStatus
if errors.As(err, &errorStatus) {
errResponse.Status = errorStatus.StatusCode()
}
if errResponse.Title == "" {
errResponse.Title = http.StatusText(errResponse.Status)
}
slog.Error("Error "+errResponse.Title, "status", errResponse.StatusCode(), "detail", errResponse.Detail, "error", errResponse.Err)
return errResponse
}