@@ -14,26 +14,9 @@ var DefaultErrorDetail = "Request failed, something went wrong."
14
14
// DefaultTitle can be customized to provide a more customized ISE Title
15
15
var DefaultErrorTitle = "Internal Server Error"
16
16
17
- // SendableError conforms to a standard error format for logging, but can also
18
- // be sent as a JSON response
19
- type SendableError interface {
20
- Sendable
21
- // Error returns a safe for user error message
22
- Error () string
23
- // Internal returns a fully formatted error including any sensitive debugging
24
- // information contained in the ISE field. Really only useful when logging an
25
- // outbound response
26
- Internal () string
27
- }
28
-
29
- // Error represents a JSON Specification Error. Error.Source.Pointer is used in 422
30
- // status responses to indicate validation errors on a JSON Object attribute.
31
- //
32
- // ISE (internal server error) captures the server error internally to help with
33
- // logging/troubleshooting, but is never returned in a response.
34
- //
35
- // Once a jsh.Error is returned, and you have logged/handled it accordingly, you
36
- // can simply return it using jsh.Send():
17
+ // ErrorObject consists of a number of contextual attributes to make conveying
18
+ // certain error type simpler as per the JSON API specification:
19
+ // http://jsonapi.org/format/#error-objects
37
20
//
38
21
// error := &jsh.Error{
39
22
// Title: "Authentication Failure",
@@ -43,7 +26,7 @@ type SendableError interface {
43
26
//
44
27
// jsh.Send(w, r, error)
45
28
//
46
- type Error struct {
29
+ type ErrorObject struct {
47
30
Title string `json:"title"`
48
31
Detail string `json:"detail"`
49
32
Status int `json:"status"`
@@ -54,7 +37,7 @@ type Error struct {
54
37
}
55
38
56
39
// Error is a safe for public consumption error message
57
- func (e * Error ) Error () string {
40
+ func (e * ErrorObject ) Error () string {
58
41
msg := fmt .Sprintf ("%s: %s" , e .Title , e .Detail )
59
42
if e .Source .Pointer != "" {
60
43
msg += fmt .Sprintf ("Source.Pointer: %s" , e .Source .Pointer )
@@ -65,62 +48,62 @@ func (e *Error) Error() string {
65
48
// Internal is a convenience function that prints out the full error including the
66
49
// ISE which is useful when debugging, NOT to be used for returning errors to user,
67
50
// use e.Error() for that
68
- func (e * Error ) Internal () string {
51
+ func (e * ErrorObject ) Internal () string {
69
52
return fmt .Sprintf ("%s ISE: %s" , e .Error (), e .ISE )
70
53
}
71
54
72
- // Prepare returns a response containing a prepared error list since the JSON
73
- // API specification requires that errors are returned as a list
74
- func ( e * Error ) Prepare ( req * http. Request , response bool ) ( * Response , SendableError ) {
75
- list := & ErrorList { Errors : [] * Error { e }}
76
- return list . Prepare ( req , response )
77
- }
78
-
79
- // ErrorList is just a wrapped error array that implements Sendable
80
- type ErrorList struct {
81
- Errors []* Error
55
+ // Error is a Sendable type consistenting of one or more error messages. Error
56
+ // implements Sendable and as such, when encountered, can simply be sent via
57
+ // jsh:
58
+ //
59
+ // object, err := ParseObject(request )
60
+ // if err != nil {
61
+ // err := jsh.Send(err, w, request)
62
+ // }
63
+ type Error struct {
64
+ Objects []* ErrorObject
82
65
}
83
66
84
67
// Error allows ErrorList to conform to the default Go error interface
85
- func (e * ErrorList ) Error () string {
68
+ func (e * Error ) Error () string {
86
69
err := "Errors: "
87
- for _ , e := range e .Errors {
88
- err = strings .Join ([]string {err , fmt .Sprintf ("%s;" , e .Error ())}, "\n " )
70
+ for _ , m := range e .Objects {
71
+ err = strings .Join ([]string {err , fmt .Sprintf ("%s;" , m .Error ())}, "\n " )
89
72
}
90
73
return err
91
74
}
92
75
93
76
// Internal prints a formatted error list including ISE's, useful for debugging
94
- func (e * ErrorList ) Internal () string {
77
+ func (e * Error ) Internal () string {
95
78
err := "Errors:"
96
- for _ , e := range e .Errors {
97
- err = strings .Join ([]string {err , fmt .Sprintf ("%s;" , e .Internal ())}, "\n " )
79
+ for _ , m := range e .Objects {
80
+ err = strings .Join ([]string {err , fmt .Sprintf ("%s;" , m .Internal ())}, "\n " )
98
81
}
99
82
return err
100
83
}
101
84
102
85
// Add first validates the error, and then appends it to the ErrorList
103
- func (e * ErrorList ) Add (newError * Error ) * Error {
104
- err := validateError (newError )
86
+ func (e * Error ) Add (object * ErrorObject ) * Error {
87
+ err := validateError (object )
105
88
if err != nil {
106
89
return err
107
90
}
108
91
109
- e .Errors = append (e .Errors , newError )
92
+ e .Objects = append (e .Objects , object )
110
93
return nil
111
94
}
112
95
113
96
// Prepare first validates the errors, and then returns an appropriate response
114
- func (e * ErrorList ) Prepare (req * http.Request , response bool ) (* Response , SendableError ) {
115
- if len (e .Errors ) == 0 {
97
+ func (e * Error ) Prepare (req * http.Request , response bool ) (* Response , * Error ) {
98
+ if len (e .Objects ) == 0 {
116
99
return nil , ISE ("No errors provided for attempted error response." )
117
100
}
118
101
119
- return & Response {Errors : e .Errors , HTTPStatus : e .Errors [0 ].Status }, nil
102
+ return & Response {Errors : e .Objects , HTTPStatus : e .Objects [0 ].Status }, nil
120
103
}
121
104
122
105
// validateError ensures that the error is ready for a response in it's current state
123
- func validateError (err * Error ) * Error {
106
+ func validateError (err * ErrorObject ) * Error {
124
107
125
108
if err .Status < 400 || err .Status > 600 {
126
109
return ISE (fmt .Sprintf ("Invalid HTTP Status for error %+v\n " , err ))
@@ -131,38 +114,64 @@ func validateError(err *Error) *Error {
131
114
return nil
132
115
}
133
116
117
+ // NewError is a convenience function that makes creating a Sendable Error from a
118
+ // Error Object simple. Because ErrorObjects are validated agains the JSON API
119
+ // Specification before being added, there is a chance that a ISE error might be
120
+ // returned in your new error's place.
121
+ func NewError (object * ErrorObject ) * Error {
122
+ newError := & Error {}
123
+
124
+ err := newError .Add (object )
125
+ if err != nil {
126
+ return err
127
+ }
128
+
129
+ return newError
130
+ }
131
+
134
132
// ISE is a convenience function for creating a ready-to-go Internal Service Error
135
- // response. As previously mentioned, the Error.ISE field is for logging only, and
136
- // won't be returned to the end user.
137
- func ISE (err string ) * Error {
138
- return & Error {
133
+ // response. The message you pass in is set to the ErrorObject.ISE attribute so you
134
+ // can gracefully log ISE's internally before sending them
135
+ func ISE (internalMessage string ) * Error {
136
+ return NewError ( & ErrorObject {
139
137
Title : DefaultErrorTitle ,
140
138
Detail : DefaultErrorDetail ,
141
139
Status : http .StatusInternalServerError ,
142
- ISE : err ,
143
- }
140
+ ISE : internalMessage ,
141
+ })
144
142
}
145
143
146
144
// InputError creates a properly formatted Status 422 error with an appropriate
147
145
// user facing message, and a Status Pointer to the first attribute that
148
146
func InputError (attribute string , detail string ) * Error {
149
- err := & Error {
147
+ message := & ErrorObject {
150
148
Title : "Invalid Attribute" ,
151
149
Detail : detail ,
152
150
Status : 422 ,
153
151
}
154
152
155
153
// Assign this after the fact, easier to do
156
- err .Source .Pointer = fmt .Sprintf ("/data/attributes/%s" , strings .ToLower (attribute ))
154
+ message .Source .Pointer = fmt .Sprintf ("/data/attributes/%s" , strings .ToLower (attribute ))
157
155
156
+ err := & Error {}
157
+ err .Add (message )
158
158
return err
159
159
}
160
160
161
161
// SpecificationError is used whenever the Client violates the JSON API Spec
162
162
func SpecificationError (detail string ) * Error {
163
- return & Error {
164
- Title : "API Specification Error" ,
163
+ return NewError ( & ErrorObject {
164
+ Title : "JSON API Specification Error" ,
165
165
Detail : detail ,
166
166
Status : http .StatusNotAcceptable ,
167
- }
167
+ })
168
+ }
169
+
170
+ // NotFound returns a 404 formatted error
171
+ func NotFound (resourceType string , id string ) * Error {
172
+ return NewError (& ErrorObject {
173
+ Title : "Not Found" ,
174
+ Detail : fmt .Sprintf ("No resource of type '%s' exists for ID: %s" , resourceType , id ),
175
+ Status : http .StatusNotFound ,
176
+ })
168
177
}
0 commit comments