forked from octokit/go-octokit
/
error.go
176 lines (143 loc) · 4.62 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
package octokit
import (
"fmt"
"github.com/lostisland/go-sawyer"
"io/ioutil"
"net/http"
"regexp"
"strings"
)
type ResponseErrorType int
const (
ErrorClientError ResponseErrorType = iota // 400-499
ErrorBadRequest ResponseErrorType = iota // 400
ErrorUnauthorized ResponseErrorType = iota // 401
ErrorOneTimePasswordRequired ResponseErrorType = iota // 401
ErrorForbidden ResponseErrorType = iota // 403
ErrorTooManyRequests ResponseErrorType = iota // 403
ErrorTooManyLoginAttempts ResponseErrorType = iota // 403
ErrorNotFound ResponseErrorType = iota // 404
ErrorNotAcceptable ResponseErrorType = iota // 406
ErrorUnsupportedMediaType ResponseErrorType = iota // 414
ErrorUnprocessableEntity ResponseErrorType = iota // 422
ErrorServerError ResponseErrorType = iota // 500-599
ErrorInternalServerError ResponseErrorType = iota // 500
ErrorNotImplemented ResponseErrorType = iota // 501
ErrorBadGateway ResponseErrorType = iota // 502
ErrorServiceUnavailable ResponseErrorType = iota // 503
ErrorMissingContentType ResponseErrorType = iota
ErrorUnknownError ResponseErrorType = iota
)
type ErrorObject struct {
Resource string `json:"resource,omitempty"`
Code string `json:"code,omitempty"`
Field string `json:"field,omitempty"`
Message string `json:"message,omitempty"`
}
func (e *ErrorObject) Error() string {
err := fmt.Sprintf("%v error", e.Code)
if e.Field != "" {
err = fmt.Sprintf("%v caused by %v field", err, e.Field)
}
err = fmt.Sprintf("%v on %v resource", err, e.Resource)
if e.Message != "" {
err = fmt.Sprintf("%v: %v", err, e.Message)
}
return err
}
type ResponseError struct {
Response *http.Response `json:"-"`
Type ResponseErrorType `json:"-"`
Message string `json:"message,omitempty"`
Err string `json:"error,omitempty"`
Errors []ErrorObject `json:"errors,omitempty"`
DocumentationURL string `json:"documentation_url,omitempty"`
}
func (e *ResponseError) Error() string {
return fmt.Sprintf("%v %v: %d - %s",
e.Response.Request.Method, e.Response.Request.URL,
e.Response.StatusCode, e.errorMessage())
}
func (e *ResponseError) errorMessage() string {
messages := []string{}
if e.Message != "" {
messages = append(messages, e.Message)
}
if e.Err != "" {
m := fmt.Sprintf("Error: %s", e.Err)
messages = append(messages, m)
}
if len(e.Errors) > 0 {
m := []string{}
m = append(m, "\nError summary:")
for _, e := range e.Errors {
m = append(m, fmt.Sprintf("\t%s", e.Error()))
}
messages = append(messages, strings.Join(m, "\n"))
}
if e.DocumentationURL != "" {
messages = append(messages, fmt.Sprintf("// See: %s", e.DocumentationURL))
}
return strings.Join(messages, "\n")
}
func NewResponseError(resp *sawyer.Response) (err error) {
var respErr *ResponseError
t := getResponseErrorType(resp.Response)
err = resp.Decode(&respErr)
if err != nil {
return
}
respErr.Response = resp.Response
respErr.Type = t
err = respErr
return
}
func getResponseErrorType(resp *http.Response) ResponseErrorType {
code := resp.StatusCode
switch {
case code == http.StatusBadRequest:
return ErrorBadRequest
case code == http.StatusUnauthorized:
header := resp.Header.Get("X-GitHub-OTP")
r := regexp.MustCompile(`(?i)required; (\\w+)`)
if r.MatchString(header) {
return ErrorOneTimePasswordRequired
}
return ErrorUnauthorized
case code == http.StatusForbidden:
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return ErrorForbidden
}
rr := regexp.MustCompile("(?i)rate limit exceeded")
if rr.MatchString(string(body)) {
return ErrorTooManyRequests
}
lr := regexp.MustCompile("(?i)login attempts exceeded")
if lr.MatchString(string(body)) {
return ErrorTooManyLoginAttempts
}
return ErrorForbidden
case code == http.StatusNotFound:
return ErrorNotFound
case code == http.StatusNotAcceptable:
return ErrorNotAcceptable
case code == http.StatusUnsupportedMediaType:
return ErrorUnsupportedMediaType
case code == 422:
return ErrorUnprocessableEntity
case code >= 400 && code <= 499:
return ErrorClientError
case code == http.StatusInternalServerError:
return ErrorInternalServerError
case code == http.StatusNotImplemented:
return ErrorNotImplemented
case code == http.StatusBadGateway:
return ErrorBadGateway
case code == http.StatusServiceUnavailable:
return ErrorServiceUnavailable
case code >= 500 && code <= 599:
return ErrorServerError
}
return ErrorUnknownError
}