forked from sourcegraph/sourcegraph-public-snapshot
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcode.go
More file actions
194 lines (161 loc) · 5.58 KB
/
Copy pathcode.go
File metadata and controls
194 lines (161 loc) · 5.58 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
// Package errcode maps Go errors to HTTP status codes as well as other useful
// functions for inspecting errors.
package errcode
import (
"context"
"fmt"
"net/http"
"os"
"strings"
"github.com/gorilla/schema"
"github.com/sourcegraph/sourcegraph/internal/gitserver/gitdomain"
"github.com/sourcegraph/sourcegraph/lib/errors"
)
// HTTP returns the most appropriate HTTP status code that describes
// err. It contains a hard-coded list of error types and error values
// (such as mapping store.RepoNotFoundError to NotFound) and
// heuristics (such as mapping os.IsNotExist-satisfying errors to
// NotFound). All other errors are mapped to HTTP 500 Internal Server
// Error.
func HTTP(err error) int {
if err == nil {
return http.StatusOK
}
if errors.Is(err, context.DeadlineExceeded) {
return http.StatusRequestTimeout
}
if gitdomain.IsCloneInProgress(err) || strings.Contains(err.Error(), (&gitdomain.RepoNotExistError{CloneInProgress: true}).Error()) {
return http.StatusAccepted
} else if gitdomain.IsRepoNotExist(err) || strings.Contains(err.Error(), (&gitdomain.RepoNotExistError{}).Error()) {
return http.StatusNotFound
}
var e interface{ HTTPStatusCode() int }
if errors.As(err, &e) {
return e.HTTPStatusCode()
}
if errors.HasType(err, schema.ConversionError{}) {
return http.StatusBadRequest
}
if errors.HasType(err, schema.MultiError{}) {
return http.StatusBadRequest
}
if errors.Is(err, os.ErrNotExist) {
return http.StatusNotFound
} else if errors.Is(err, os.ErrPermission) {
return http.StatusForbidden
} else if IsNotFound(err) {
return http.StatusNotFound
} else if IsBadRequest(err) {
return http.StatusBadRequest
}
return http.StatusInternalServerError
}
type HTTPErr struct {
Status int // HTTP status code.
Err error // Optional reason for the HTTP error.
}
func (err *HTTPErr) Error() string {
if err.Err != nil {
return fmt.Sprintf("status %d, reason %s", err.Status, err.Err)
}
return fmt.Sprintf("Status %d", err.Status)
}
func (err *HTTPErr) HTTPStatusCode() int { return err.Status }
func IsHTTPErrorCode(err error, statusCode int) bool {
return HTTP(err) == statusCode
}
// Mock is a convenience error which makes it easy to satisfy the optional
// interfaces errors implement.
type Mock struct {
// Message is the return value for Error() string
Message string
// IsNotFound is the return value for NotFound
IsNotFound bool
}
func (e *Mock) Error() string {
return e.Message
}
func (e *Mock) NotFound() bool {
return e.IsNotFound
}
// IsNotFound will check if err or one of its causes is a not found
// error. Note: This will not check os.IsNotExist, but rather is returned by
// methods like Repo.Get when the repo is not found. It will also *not* map
// HTTPStatusCode into not found.
func IsNotFound(err error) bool {
var e interface{ NotFound() bool }
return errors.As(err, &e) && e.NotFound()
}
// IsUnauthorized will check if err or one of its causes is an unauthorized
// error.
func IsUnauthorized(err error) bool {
var e interface{ Unauthorized() bool }
return errors.As(err, &e) && e.Unauthorized()
}
// IsForbidden will check if err or one of its causes is a forbidden error.
func IsForbidden(err error) bool {
var e interface{ Forbidden() bool }
return errors.As(err, &e) && e.Forbidden()
}
// IsAccountSuspended will check if err or one of its causes was due to the
// account being suspended
func IsAccountSuspended(err error) bool {
var e interface{ AccountSuspended() bool }
return errors.As(err, &e) && e.AccountSuspended()
}
// IsUnavailableForLegalReasons will check if err or one of its causes was due to
// legal reasons.
func IsUnavailableForLegalReasons(err error) bool {
var e interface{ UnavailableForLegalReasons() bool }
return errors.As(err, &e) && e.UnavailableForLegalReasons()
}
// IsBadRequest will check if err or one of its causes is a bad request.
func IsBadRequest(err error) bool {
var e interface{ BadRequest() bool }
return errors.As(err, &e) && e.BadRequest()
}
// IsTemporary will check if err or one of its causes is temporary. A
// temporary error can be retried. Many errors in the go stdlib implement the
// temporary interface.
func IsTemporary(err error) bool {
var e interface{ Temporary() bool }
return errors.As(err, &e) && e.Temporary()
}
// IsArchived will check if err or one of its causes is an archived error.
// (This is generally going to be in the context of repositories being
// archived.)
func IsArchived(err error) bool {
var e interface{ Archived() bool }
return errors.As(err, &e) && e.Archived()
}
// IsBlocked will check if err or one of its causes is a blocked error.
func IsBlocked(err error) bool {
var e interface{ Blocked() bool }
return errors.As(err, &e) && e.Blocked()
}
// IsTimeout will check if err or one of its causes is a timeout. Many errors
// in the go stdlib implement the timeout interface.
func IsTimeout(err error) bool {
var e interface{ Timeout() bool }
return errors.As(err, &e) && e.Timeout()
}
// IsNonRetryable will check if err or one of its causes is a error that cannot be retried.
func IsNonRetryable(err error) bool {
var e interface{ NonRetryable() bool }
return errors.As(err, &e) && e.NonRetryable()
}
// MakeNonRetryable makes any error non-retryable.
func MakeNonRetryable(err error) error {
return nonRetryableError{err}
}
type nonRetryableError struct{ error }
func (nonRetryableError) NonRetryable() bool { return true }
func MaybeMakeNonRetryable(statusCode int, err error) error {
if statusCode > 0 && statusCode < 200 ||
statusCode >= 300 && statusCode < 500 ||
statusCode == 501 ||
statusCode >= 600 {
return MakeNonRetryable(err)
}
return err
}