-
Notifications
You must be signed in to change notification settings - Fork 14
/
factory.go
157 lines (135 loc) · 5.64 KB
/
factory.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
package terrors
import (
"strings"
"github.com/monzo/terrors/stack"
)
var (
// Used when setting Error.IsRetryable
retryable = true
notRetryable = false
// Used when setting Error.IsUnexpected
unexpected = true
notUnexpected = false
)
// Wrap takes any error interface and wraps it into an Error.
// This is useful because an Error contains lots of useful goodies, like the stacktrace of the error.
// NOTE: If `err` is already an `Error`, it will add the params passed in to the params of the Error
// Deprecated: Use Augment instead.
func Wrap(err error, params map[string]string) error {
return WrapWithCode(err, params, ErrInternalService)
}
// WrapWithCode wraps an error with a custom error code. If `err` is already
// an `Error`, it will add the params passed in to the params of the error
// Deprecated: Use Augment instead. If you need to set the code of the error,
// then you should return a new error instead. For example
//
// terrors.WrapWithCode(err, map[string]string{"foo": "bar"}, "bad_request.failed")
//
// would become
//
// terrors.BadRequest("failed", err.Error(), map[string]string{"foo": "bar"})
func WrapWithCode(err error, params map[string]string, code string) error {
if err == nil {
return nil
}
switch err := err.(type) {
case *Error:
return addParams(err, params)
default:
return errorFactory(code, err.Error(), params)
}
}
// InternalService creates a new error to represent an internal service error.
// Only use internal service error if we know very little about the error. Most
// internal service errors will come from `Wrap`ing a vanilla `error` interface.
// Errors returned by this function are considered to be retryable by default.
// Consider using NonRetryableInternalService if retries are not desirable.
func InternalService(code, message string, params map[string]string) *Error {
return errorFactory(errCode(ErrInternalService, code), message, params)
}
// NonRetryableInternalService creates a new error to represent an internal service error.
// Only use internal service error if we know very little about the error. Most
// internal service errors will come from `Wrap`ing a vanilla `error` interface.
// Errors returned by this function are not considered to be retryable by default.
func NonRetryableInternalService(code, message string, params map[string]string) *Error {
err := InternalService(code, message, params)
err.SetIsRetryable(false)
return err
}
// BadRequest creates a new error to represent an error caused by the client sending
// an invalid request. This is non-retryable unless the request is modified.
func BadRequest(code, message string, params map[string]string) *Error {
return errorFactory(errCode(ErrBadRequest, code), message, params)
}
// BadResponse creates a new error representing a failure to response with a valid response
// Examples of this would be a handler returning an invalid message format
func BadResponse(code, message string, params map[string]string) *Error {
return errorFactory(errCode(ErrBadResponse, code), message, params)
}
// Timeout creates a new error representing a timeout from client to server
func Timeout(code, message string, params map[string]string) *Error {
return errorFactory(errCode(ErrTimeout, code), message, params)
}
// NotFound creates a new error representing a resource that cannot be found. In some
// cases this is not an error, and would be better represented by a zero length slice of elements
func NotFound(code, message string, params map[string]string) *Error {
return errorFactory(errCode(ErrNotFound, code), message, params)
}
// Forbidden creates a new error representing a resource that cannot be accessed with
// the current authorisation credentials. The user may need authorising, or if authorised,
// may not be permitted to perform this action
func Forbidden(code, message string, params map[string]string) *Error {
return errorFactory(errCode(ErrForbidden, code), message, params)
}
// Unauthorized creates a new error indicating that authentication is required,
// but has either failed or not been provided.
func Unauthorized(code, message string, params map[string]string) *Error {
return errorFactory(errCode(ErrUnauthorized, code), message, params)
}
// PreconditionFailed creates a new error indicating that one or more conditions
// given in the request evaluated to false when tested on the server.
func PreconditionFailed(code, message string, params map[string]string) *Error {
return errorFactory(errCode(ErrPreconditionFailed, code), message, params)
}
// RateLimited creates a new error indicating that the request has been rate-limited,
// and that the caller should back-off.
func RateLimited(code, message string, params map[string]string) *Error {
return errorFactory(errCode(ErrRateLimited, code), message, params)
}
// errorConstructor returns a `*Error` with the specified code, message and params.
// Builds a stack based on the current call stack
func errorFactory(code string, message string, params map[string]string) *Error {
err := &Error{
Code: ErrUnknown,
Message: message,
Params: map[string]string{},
}
if len(code) > 0 {
err.Code = code
err.IsRetryable = ¬Retryable
for _, c := range retryableCodes {
if PrefixMatches(err, c) {
err.IsRetryable = &retryable
}
}
}
if params != nil {
err.Params = params
}
// TODO pass in context.Context
// Build stack and skip first three lines:
// - stack.go BuildStack()
// - errors.go errorFactory()
// - errors.go public constructor method
err.StackFrames = stack.BuildStack(3)
return err
}
func errCode(prefix, code string) string {
if code == "" {
return prefix
}
if prefix == "" {
return code
}
return strings.Join([]string{prefix, code}, ".")
}