-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy patherrors.go
More file actions
140 lines (122 loc) · 3.48 KB
/
errors.go
File metadata and controls
140 lines (122 loc) · 3.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
package bbapi
import (
"encoding/json"
"errors"
"fmt"
"net/http"
"strings"
)
// ErrorDetail represents a single error entry from the Banco do Brasil API.
type ErrorDetail struct {
Codigo string `json:"codigo"`
Versao string `json:"versao"`
Mensagem string `json:"mensagem"`
Ocorrencia string `json:"ocorrencia"`
}
// ErrorResponse is the top-level BB API error body.
type ErrorResponse struct {
Erros []ErrorDetail `json:"erros"`
}
// OAuthError represents an error returned by the OAuth2 server.
type OAuthError struct {
StatusCode int `json:"statusCode"`
Error string `json:"error"`
Message string `json:"message"`
Attributes struct {
Error string `json:"error"`
} `json:"attributes"`
}
// APIError represents an error returned by the BB API or OAuth server.
type APIError struct {
StatusCode int
Message string
RawResponse string
Details []ErrorDetail
}
// Error implements the error interface.
func (e *APIError) Error() string {
if len(e.Details) == 0 {
msg := e.RawResponse
if e.Message != "" {
msg = e.Message
}
return fmt.Sprintf("bbapi: HTTP %d: %s", e.StatusCode, msg)
}
parts := make([]string, 0, len(e.Details))
for _, d := range e.Details {
s := d.Mensagem
if d.Codigo != "" {
s = fmt.Sprintf("[%s] %s", d.Codigo, s)
}
if d.Ocorrencia != "" {
s += " (" + d.Ocorrencia + ")"
}
parts = append(parts, s)
}
return fmt.Sprintf("bbapi: HTTP %d: %s", e.StatusCode, strings.Join(parts, "; "))
}
// asAPIError unwraps err into an *APIError, returning it and true if successful.
func asAPIError(err error) (*APIError, bool) {
var e *APIError
return e, errors.As(err, &e)
}
// IsNotFound returns true if err is an *APIError with HTTP 404.
func IsNotFound(err error) bool {
e, ok := asAPIError(err)
return ok && e.StatusCode == http.StatusNotFound
}
// IsUnauthorized returns true if err is an *APIError with HTTP 401.
func IsUnauthorized(err error) bool {
e, ok := asAPIError(err)
return ok && e.StatusCode == http.StatusUnauthorized
}
// IsForbidden returns true if err is an *APIError with HTTP 403.
func IsForbidden(err error) bool {
e, ok := asAPIError(err)
return ok && e.StatusCode == http.StatusForbidden
}
// IsRateLimited returns true if err is an *APIError with HTTP 429.
func IsRateLimited(err error) bool {
e, ok := asAPIError(err)
return ok && e.StatusCode == http.StatusTooManyRequests
}
// IsServerError returns true if err is an *APIError with a 5xx status code.
func IsServerError(err error) bool {
e, ok := asAPIError(err)
return ok && e.StatusCode >= 500
}
// isRetryableStatus reports whether the status code warrants a retry.
func isRetryableStatus(code int) bool {
switch code {
case http.StatusTooManyRequests,
http.StatusInternalServerError,
http.StatusBadGateway,
http.StatusServiceUnavailable,
http.StatusGatewayTimeout:
return true
default:
return false
}
}
// parseAPIError builds an *APIError from a status code and raw body bytes.
func parseAPIError(statusCode int, body []byte) *APIError {
apiErr := &APIError{
StatusCode: statusCode,
RawResponse: string(body),
}
var errResp ErrorResponse
if json.Unmarshal(body, &errResp) == nil && len(errResp.Erros) > 0 {
apiErr.Details = errResp.Erros
apiErr.Message = errResp.Erros[0].Mensagem
return apiErr
}
var oauthErr OAuthError
if json.Unmarshal(body, &oauthErr) == nil && oauthErr.Error != "" {
apiErr.Message = oauthErr.Message
if apiErr.Message == "" {
apiErr.Message = oauthErr.Error
}
return apiErr
}
return apiErr
}