-
-
Notifications
You must be signed in to change notification settings - Fork 564
/
Copy patherror.go
209 lines (186 loc) Β· 7.22 KB
/
error.go
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
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
package goa
import (
"crypto/rand"
"encoding/base64"
"fmt"
"io"
"strings"
)
type (
// ServiceError is the default error type used by the goa package to
// encode and decode error responses.
ServiceError struct {
// Name is a name for that class of errors.
Name string
// ID is a unique value for each occurrence of the error.
ID string
// Message contains the specific error details.
Message string
// Is the error a timeout?
Timeout bool
// Is the error temporary?
Temporary bool
// Is the error a server-side fault?
Fault bool
}
)
// Fault creates an error given a format and values a la fmt.Printf. The error
// has the Fault field set to true.
func Fault(format string, v ...interface{}) *ServiceError {
return newError("fault", false, false, true, format, v...)
}
// PermanentError creates an error given a name and a format and values a la
// fmt.Printf.
func PermanentError(name, format string, v ...interface{}) *ServiceError {
return newError(name, false, false, false, format, v...)
}
// TemporaryError is an error class that indicates that the error is temporary
// and that retrying the request may be successful. TemporaryError creates an
// error given a name and a format and values a la fmt.Printf. The error has the
// Temporary field set to true.
func TemporaryError(name, format string, v ...interface{}) *ServiceError {
return newError(name, false, true, false, format, v...)
}
// PermanentTimeoutError creates an error given a name and a format and values a
// la fmt.Printf. The error has the Timeout field set to true.
func PermanentTimeoutError(name, format string, v ...interface{}) *ServiceError {
return newError(name, true, false, false, format, v...)
}
// TemporaryTimeoutError creates an error given a name and a format and values a
// la fmt.Printf. The error has both the Timeout and Temporary fields set to
// true.
func TemporaryTimeoutError(name, format string, v ...interface{}) *ServiceError {
return newError(name, true, true, false, format, v...)
}
// MissingPayloadError is the error produced by the generated code when a
// request is missing a required payload.
func MissingPayloadError() error {
return PermanentError("missing_payload", "missing required payload")
}
// DecodePayloadError is the error produced by the generated code when a request
// body cannot be decoded successfully.
func DecodePayloadError(msg string) error {
return PermanentError("decode_payload", msg)
}
// InvalidFieldTypeError is the error produced by the generated code when the
// type of a payload field does not match the type defined in the design.
func InvalidFieldTypeError(name string, val interface{}, expected string) error {
return PermanentError("invalid_field_type", "invalid value %#v for %q, must be a %s", val, name, expected)
}
// MissingFieldError is the error produced by the generated code when a payload
// is missing a required field.
func MissingFieldError(name, context string) error {
return PermanentError("missing_field", "%q is missing from %s", name, context)
}
// InvalidEnumValueError is the error produced by the generated code when the
// value of a payload field does not match one the values defined in the design
// Enum validation.
func InvalidEnumValueError(name string, val interface{}, allowed []interface{}) error {
elems := make([]string, len(allowed))
for i, a := range allowed {
elems[i] = fmt.Sprintf("%#v", a)
}
return PermanentError("invalid_enum_value", "value of %s must be one of %s but got value %#v", name, strings.Join(elems, ", "), val)
}
// InvalidFormatError is the error produced by the generated code when the value
// of a payload field does not match the format validation defined in the
// design.
func InvalidFormatError(name, target string, format Format, formatError error) error {
return PermanentError("invalid_format", "%s must be formatted as a %s but got value %q, %s", name, format, target, formatError.Error())
}
// InvalidPatternError is the error produced by the generated code when the
// value of a payload field does not match the pattern validation defined in the
// design.
func InvalidPatternError(name, target string, pattern string) error {
return PermanentError("invalid_pattern", "%s must match the regexp %q but got value %q", name, pattern, target)
}
// InvalidRangeError is the error produced by the generated code when the value
// of a payload field does not match the range validation defined in the design.
// value may be an int or a float64.
func InvalidRangeError(name string, target interface{}, value interface{}, min bool) error {
comp := "greater or equal"
if !min {
comp = "lesser or equal"
}
return PermanentError("invalid_range", "%s must be %s than %d but got value %#v", name, comp, value, target)
}
// InvalidLengthError is the error produced by the generated code when the value
// of a payload field does not match the length validation defined in the
// design.
func InvalidLengthError(name string, target interface{}, ln, value int, min bool) error {
comp := "greater or equal"
if !min {
comp = "lesser or equal"
}
return PermanentError("invalid_length", "length of %s must be %s than %d but got value %#v (len=%d)", name, comp, value, target, ln)
}
// NewErrorID creates a unique 8 character ID that is well suited to use as an
// error identifier.
func NewErrorID() string {
// for the curious - simplifying a bit - the probability of 2 values
// being equal for n 6-bytes values is n^2 / 2^49. For n = 1 million
// this gives around 1 chance in 500. 6 bytes seems to be a good
// trade-off between probability of clashes and length of ID (6 * 4/3 =
// 8 chars) since clashes are not catastrophic.
b := make([]byte, 6)
io.ReadFull(rand.Reader, b)
return base64.RawURLEncoding.EncodeToString(b)
}
// MergeErrors updates an error by merging another into it. It first converts
// other into a ServiceError if not already one. The merge algorithm then:
//
// * uses the name of err if a ServiceError, the name of other otherwise.
//
// * appends both error messages.
//
// * computes Timeout and Temporary by "and"ing the fields of both errors.
//
// Merge returns the updated error. This makes it possible to return other when
// err is nil.
func MergeErrors(err, other error) error {
if err == nil {
if other == nil {
return nil
}
return other
}
if other == nil {
return err
}
e := asError(err)
o := asError(other)
if e.Name == "error" {
e.Name = o.Name
}
e.Message = e.Message + "; " + o.Message
e.Timeout = e.Timeout && o.Timeout
e.Temporary = e.Temporary && o.Temporary
e.Fault = e.Fault && o.Fault
return e
}
// Error returns the error message.
func (s *ServiceError) Error() string { return s.Message }
// ErrorName returns the error name.
func (s *ServiceError) ErrorName() string { return s.Name }
func newError(name string, timeout, temporary, fault bool, format string, v ...interface{}) *ServiceError {
return &ServiceError{
Name: name,
ID: NewErrorID(),
Message: fmt.Sprintf(format, v...),
Timeout: timeout,
Temporary: temporary,
Fault: fault,
}
}
func asError(err error) *ServiceError {
e, ok := err.(*ServiceError)
if !ok {
return &ServiceError{
Name: "error",
ID: NewErrorID(),
Message: err.Error(),
Fault: true, // Default to fault for unexpected errors
}
}
return e
}