Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update error handling as per Go 1.13 guidelines #1854

Merged
merged 3 commits into from
Apr 14, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

go-github is a Go client library for accessing the [GitHub API v3][].

Currently, **go-github requires Go version 1.9 or greater**. go-github tracks
Currently, **go-github requires Go version 1.13 or greater**. go-github tracks
[Go's version support policy][support-policy]. We do our best not to break
older versions of Go if we don't have to, but due to tooling constraints, we
don't always test older versions.
Expand Down
95 changes: 94 additions & 1 deletion github/github.go
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,8 @@ const (
mediaTypeContentAttachmentsPreview = "application/vnd.github.corsair-preview+json"
)

var errNonNilContext = errors.New("context must be non-nil")

// A Client manages communication with the GitHub API.
type Client struct {
clientMu sync.Mutex // clientMu protects the client during calls that modify the CheckRedirect func.
Expand Down Expand Up @@ -530,7 +532,7 @@ func parseRate(r *http.Response) Rate {
// canceled or times out, ctx.Err() will be returned.
func (c *Client) BareDo(ctx context.Context, req *http.Request) (*Response, error) {
if ctx == nil {
return nil, errors.New("context must be non-nil")
return nil, errNonNilContext
}
req = withContext(ctx, req)

Expand Down Expand Up @@ -653,6 +655,20 @@ func (c *Client) checkRateLimitBeforeDo(req *http.Request, rateLimitCategory rat
return nil
}

// compareHttpResponse returns whether two http.Response objects are equal or not.
// Currently, only StatusCode is checked. This function is used when implementing the
// Is(error) bool interface for the custom error types in this package.
func compareHttpResponse(r1, r2 *http.Response) bool {
if r1 == nil && r2 == nil {
return true
}

if r1 != nil && r2 != nil {
return r1.StatusCode == r2.StatusCode
}
return false
}

/*
An ErrorResponse reports one or more errors caused by an API request.

Expand Down Expand Up @@ -681,6 +697,50 @@ func (r *ErrorResponse) Error() string {
r.Response.StatusCode, r.Message, r.Errors)
}

// Is returns whether the provided error equals this error.
func (r *ErrorResponse) Is(target error) bool {
mbalayil marked this conversation as resolved.
Show resolved Hide resolved
v, ok := target.(*ErrorResponse)
if !ok {
return false
mbalayil marked this conversation as resolved.
Show resolved Hide resolved
}

if r.Message != v.Message || (r.DocumentationURL != v.DocumentationURL) ||
!compareHttpResponse(r.Response, v.Response) {
return false
mbalayil marked this conversation as resolved.
Show resolved Hide resolved
}

// Compare Errors.
if len(r.Errors) != len(v.Errors) {
return false
mbalayil marked this conversation as resolved.
Show resolved Hide resolved
}
for idx := range r.Errors {
if r.Errors[idx] != v.Errors[idx] {
return false
mbalayil marked this conversation as resolved.
Show resolved Hide resolved
}
}

// Compare Block.
if (r.Block != nil && v.Block == nil) || (r.Block == nil && v.Block != nil) {
return false
}
if r.Block != nil && v.Block != nil {
if r.Block.Reason != v.Block.Reason {
return false
}
if (r.Block.CreatedAt != nil && v.Block.CreatedAt == nil) || (r.Block.CreatedAt ==
nil && v.Block.CreatedAt != nil) {
return false
mbalayil marked this conversation as resolved.
Show resolved Hide resolved
}
if r.Block.CreatedAt != nil && v.Block.CreatedAt != nil {
if *(r.Block.CreatedAt) != *(v.Block.CreatedAt) {
return false
}
}
}

return true
}

// TwoFactorAuthError occurs when using HTTP Basic Authentication for a user
// that has two-factor authentication enabled. The request can be reattempted
// by providing a one-time password in the request.
Expand All @@ -702,6 +762,18 @@ func (r *RateLimitError) Error() string {
r.Response.StatusCode, r.Message, formatRateReset(time.Until(r.Rate.Reset.Time)))
}

// Is returns whether the provided error equals this error.
func (r *RateLimitError) Is(target error) bool {
mbalayil marked this conversation as resolved.
Show resolved Hide resolved
v, ok := target.(*RateLimitError)
if !ok {
return false
mbalayil marked this conversation as resolved.
Show resolved Hide resolved
}

return r.Rate == v.Rate &&
r.Message == v.Message &&
compareHttpResponse(r.Response, v.Response)
}

// AcceptedError occurs when GitHub returns 202 Accepted response with an
// empty body, which means a job was scheduled on the GitHub side to process
// the information needed and cache it.
Expand All @@ -717,6 +789,15 @@ func (*AcceptedError) Error() string {
return "job scheduled on GitHub side; try again later"
}

// Is returns whether the provided error equals this error.
func (ae *AcceptedError) Is(target error) bool {
mbalayil marked this conversation as resolved.
Show resolved Hide resolved
v, ok := target.(*AcceptedError)
if !ok {
return false
mbalayil marked this conversation as resolved.
Show resolved Hide resolved
}
return bytes.Compare(ae.Raw, v.Raw) == 0
mbalayil marked this conversation as resolved.
Show resolved Hide resolved
}

// AbuseRateLimitError occurs when GitHub returns 403 Forbidden response with the
// "documentation_url" field value equal to "https://docs.github.com/en/free-pro-team@latest/rest/reference/#abuse-rate-limits".
type AbuseRateLimitError struct {
Expand All @@ -735,6 +816,18 @@ func (r *AbuseRateLimitError) Error() string {
r.Response.StatusCode, r.Message)
}

// Is returns whether the provided error equals this error.
func (r *AbuseRateLimitError) Is(target error) bool {
mbalayil marked this conversation as resolved.
Show resolved Hide resolved
v, ok := target.(*AbuseRateLimitError)
if !ok {
return false
mbalayil marked this conversation as resolved.
Show resolved Hide resolved
}

return r.Message == v.Message &&
r.RetryAfter == v.RetryAfter &&
compareHttpResponse(r.Response, v.Response)
}

// sanitizeURL redacts the client_secret parameter from the URL which may be
// exposed to the user.
func sanitizeURL(uri *url.URL) *url.URL {
Expand Down
Loading