-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy patherrors.go
More file actions
351 lines (305 loc) · 11.8 KB
/
errors.go
File metadata and controls
351 lines (305 loc) · 11.8 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
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
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
package intelligencecloud
import (
"errors"
"fmt"
"time"
)
// maxCauseBodyBytes is the hard cap on UnexpectedError.CauseBody to prevent
// unbounded memory from large error responses.
const maxCauseBodyBytes = 4096
// Sentinel errors allow callers to classify failures with errors.Is without
// inspecting concrete types. Every concrete error Unwraps to the
// corresponding sentinel.
var (
// ErrAuthentication indicates a 401 authentication failure.
ErrAuthentication = errors.New("intelligencecloud: authentication failed")
// ErrAuthorization indicates a 403 authorization failure.
ErrAuthorization = errors.New("intelligencecloud: not authorized")
// ErrValidation indicates a 400/422 validation failure.
ErrValidation = errors.New("intelligencecloud: validation failed")
// ErrNotFound indicates a 404 resource-not-found response.
ErrNotFound = errors.New("intelligencecloud: resource not found")
// ErrConflict indicates a 409 resource conflict.
ErrConflict = errors.New("intelligencecloud: resource conflict")
// ErrRateLimit indicates a 429 rate-limit response.
ErrRateLimit = errors.New("intelligencecloud: rate-limited")
// ErrServer indicates a 5xx server-side failure.
ErrServer = errors.New("intelligencecloud: server error")
)
// APIError is satisfied by every non-nil error the SDK returns from an API
// call. Callers can use errors.As to obtain the concrete type and access
// additional fields such as RequiredScopes, FieldErrors, or RetryAfter.
type APIError interface {
error
// Status returns the HTTP status code of the failed response.
Status() int
// Code returns the backend-provided error code, if any.
Code() string
// RequestID returns the backend-provided request identifier for support
// triage.
RequestID() string
// Operation returns the SDK operation name that triggered the error.
Operation() string
}
// FieldError describes a single field-level validation failure as reported by
// the backend.
type FieldError struct {
// Field is the path of the invalid field (e.g. "name" or "address.zip").
Field string
// Code is the machine-readable validation code (e.g. "required").
Code string
// Message is the human-readable description of the failure.
Message string
}
// ---------------------------------------------------------------------------
// Concrete error types
// ---------------------------------------------------------------------------
// apiErrorBase holds the common fields shared by all concrete API error types.
// It is unexported to keep the public surface area clean while satisfying the
// APIError interface methods.
type apiErrorBase struct {
statusCode int
errorCode string
message string
requestID string
operationID string
}
// Status returns the HTTP status code.
func (b apiErrorBase) Status() int { return b.statusCode }
// Code returns the backend error code.
func (b apiErrorBase) Code() string { return b.errorCode }
// RequestID returns the request identifier.
func (b apiErrorBase) RequestID() string { return b.requestID }
// Operation returns the operation name.
func (b apiErrorBase) Operation() string { return b.operationID }
// AuthenticationError represents a 401 authentication failure.
type AuthenticationError struct {
apiErrorBase
}
// NewAuthenticationError constructs an AuthenticationError.
func NewAuthenticationError(statusCode int, code, message, requestID, operationID string) *AuthenticationError {
return &AuthenticationError{
apiErrorBase: apiErrorBase{
statusCode: statusCode,
errorCode: code,
message: message,
requestID: requestID,
operationID: operationID,
},
}
}
// Error implements the error interface.
func (e *AuthenticationError) Error() string {
return fmt.Sprintf("%s: %s (status=%d, code=%s, request_id=%s, operation=%s)",
ErrAuthentication.Error(), e.message, e.statusCode, e.errorCode, e.requestID, e.operationID)
}
// Unwrap returns the sentinel so that errors.Is works.
func (e *AuthenticationError) Unwrap() error { return ErrAuthentication }
// AuthorizationError represents a 403 authorization failure.
type AuthorizationError struct {
apiErrorBase
// RequiredScopes lists the scopes the caller would need to succeed, when
// the backend provides this information.
RequiredScopes []string
}
// NewAuthorizationError constructs an AuthorizationError.
func NewAuthorizationError(statusCode int, code, message, requestID, operationID string, requiredScopes []string) *AuthorizationError {
return &AuthorizationError{
apiErrorBase: apiErrorBase{
statusCode: statusCode,
errorCode: code,
message: message,
requestID: requestID,
operationID: operationID,
},
RequiredScopes: requiredScopes,
}
}
// Error implements the error interface.
func (e *AuthorizationError) Error() string {
return fmt.Sprintf("%s: %s (status=%d, code=%s, request_id=%s, operation=%s)",
ErrAuthorization.Error(), e.message, e.statusCode, e.errorCode, e.requestID, e.operationID)
}
// Unwrap returns the sentinel so that errors.Is works.
func (e *AuthorizationError) Unwrap() error { return ErrAuthorization }
// ValidationError represents a 400 or 422 validation failure. It carries
// field-level details when the backend provides them.
type ValidationError struct {
apiErrorBase
// FieldErrors lists the individual field-level validation failures.
FieldErrors []FieldError
}
// NewValidationError constructs a ValidationError.
func NewValidationError(statusCode int, code, message, requestID, operationID string, fieldErrors []FieldError) *ValidationError {
return &ValidationError{
apiErrorBase: apiErrorBase{
statusCode: statusCode,
errorCode: code,
message: message,
requestID: requestID,
operationID: operationID,
},
FieldErrors: fieldErrors,
}
}
// Error implements the error interface.
func (e *ValidationError) Error() string {
return fmt.Sprintf("%s: %s (status=%d, code=%s, request_id=%s, operation=%s, fields=%d)",
ErrValidation.Error(), e.message, e.statusCode, e.errorCode, e.requestID, e.operationID, len(e.FieldErrors))
}
// Unwrap returns the sentinel so that errors.Is works.
func (e *ValidationError) Unwrap() error { return ErrValidation }
// NotFoundError represents a 404 resource-not-found response.
type NotFoundError struct {
apiErrorBase
// ResourceType identifies the kind of resource that was not found.
ResourceType string
// ResourceID identifies the specific resource that was not found.
ResourceID string
}
// NewNotFoundError constructs a NotFoundError.
func NewNotFoundError(statusCode int, code, message, requestID, operationID, resourceType, resourceID string) *NotFoundError {
return &NotFoundError{
apiErrorBase: apiErrorBase{
statusCode: statusCode,
errorCode: code,
message: message,
requestID: requestID,
operationID: operationID,
},
ResourceType: resourceType,
ResourceID: resourceID,
}
}
// Error implements the error interface.
func (e *NotFoundError) Error() string {
return fmt.Sprintf("%s: %s (status=%d, code=%s, request_id=%s, operation=%s, type=%s, id=%s)",
ErrNotFound.Error(), e.message, e.statusCode, e.errorCode, e.requestID, e.operationID, e.ResourceType, e.ResourceID)
}
// Unwrap returns the sentinel so that errors.Is works.
func (e *NotFoundError) Unwrap() error { return ErrNotFound }
// ConflictError represents a 409 resource conflict.
type ConflictError struct {
apiErrorBase
}
// NewConflictError constructs a ConflictError.
func NewConflictError(statusCode int, code, message, requestID, operationID string) *ConflictError {
return &ConflictError{
apiErrorBase: apiErrorBase{
statusCode: statusCode,
errorCode: code,
message: message,
requestID: requestID,
operationID: operationID,
},
}
}
// Error implements the error interface.
func (e *ConflictError) Error() string {
return fmt.Sprintf("%s: %s (status=%d, code=%s, request_id=%s, operation=%s)",
ErrConflict.Error(), e.message, e.statusCode, e.errorCode, e.requestID, e.operationID)
}
// Unwrap returns the sentinel so that errors.Is works.
func (e *ConflictError) Unwrap() error { return ErrConflict }
// RateLimitError represents a 429 rate-limit response. RetryAfter carries the
// Retry-After hint from the server, if present.
type RateLimitError struct {
apiErrorBase
// RetryAfter is the server-suggested delay before retrying.
RetryAfter time.Duration
}
// NewRateLimitError constructs a RateLimitError.
func NewRateLimitError(statusCode int, code, message, requestID, operationID string, retryAfter time.Duration) *RateLimitError {
return &RateLimitError{
apiErrorBase: apiErrorBase{
statusCode: statusCode,
errorCode: code,
message: message,
requestID: requestID,
operationID: operationID,
},
RetryAfter: retryAfter,
}
}
// Error implements the error interface.
func (e *RateLimitError) Error() string {
return fmt.Sprintf("%s: %s (status=%d, code=%s, request_id=%s, operation=%s, retry_after=%s)",
ErrRateLimit.Error(), e.message, e.statusCode, e.errorCode, e.requestID, e.operationID, e.RetryAfter)
}
// Unwrap returns the sentinel so that errors.Is works.
func (e *RateLimitError) Unwrap() error { return ErrRateLimit }
// ServerError represents a 5xx server-side failure.
type ServerError struct {
apiErrorBase
}
// NewServerError constructs a ServerError.
func NewServerError(statusCode int, code, message, requestID, operationID string) *ServerError {
return &ServerError{
apiErrorBase: apiErrorBase{
statusCode: statusCode,
errorCode: code,
message: message,
requestID: requestID,
operationID: operationID,
},
}
}
// Error implements the error interface.
func (e *ServerError) Error() string {
return fmt.Sprintf("%s: %s (status=%d, code=%s, request_id=%s, operation=%s)",
ErrServer.Error(), e.message, e.statusCode, e.errorCode, e.requestID, e.operationID)
}
// Unwrap returns the sentinel so that errors.Is works.
func (e *ServerError) Unwrap() error { return ErrServer }
// UnexpectedError represents an error response that does not map to any known
// sentinel. CauseBody is capped at 4 KiB for safety. Use NewUnexpectedError
// to construct instances; it enforces the size cap.
type UnexpectedError struct {
apiErrorBase
// CauseBody is the raw response body, capped at 4 KiB.
CauseBody []byte
}
// NewUnexpectedError constructs an UnexpectedError, capping causeBody at 4 KiB.
func NewUnexpectedError(statusCode int, code, message, requestID, operationID string, causeBody []byte) *UnexpectedError {
var body []byte
if causeBody != nil {
if len(causeBody) > maxCauseBodyBytes {
body = make([]byte, maxCauseBodyBytes)
copy(body, causeBody[:maxCauseBodyBytes])
} else {
body = make([]byte, len(causeBody))
copy(body, causeBody)
}
}
return &UnexpectedError{
apiErrorBase: apiErrorBase{
statusCode: statusCode,
errorCode: code,
message: message,
requestID: requestID,
operationID: operationID,
},
CauseBody: body,
}
}
// Error implements the error interface.
func (e *UnexpectedError) Error() string {
return fmt.Sprintf("intelligencecloud: unexpected error: %s (status=%d, code=%s, request_id=%s, operation=%s)",
e.message, e.statusCode, e.errorCode, e.requestID, e.operationID)
}
// Unwrap is not provided because UnexpectedError does not map to any sentinel.
// Callers should use errors.As to obtain this type directly.
// ---------------------------------------------------------------------------
// ConfigurationError — NOT an APIError
// ---------------------------------------------------------------------------
// ConfigurationError represents a configuration validation failure. It is
// returned only from option validation and client construction, never from API
// calls. It deliberately does NOT implement APIError.
type ConfigurationError struct {
// Message describes the configuration problem.
Message string
}
// Error implements the error interface.
func (e *ConfigurationError) Error() string {
return fmt.Sprintf("intelligencecloud: configuration error: %s", e.Message)
}