X Tutup
Skip to content

Commit c66eebc

Browse files
committed
api: return structured error for failed API calls
`fmt.Errorf` hides information and makes it hard to test for specific conditions in returned error. Return a structured error instead. Signed-off-by: Pavel Borzenkov <pavel.borzenkov@gmail.com>
1 parent aa8f8e8 commit c66eebc

File tree

2 files changed

+30
-1
lines changed

2 files changed

+30
-1
lines changed

api/client.go

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,17 @@ func (gr GraphQLErrorResponse) Error() string {
157157
return fmt.Sprintf("graphql error: '%s'", strings.Join(errorMessages, ", "))
158158
}
159159

160+
// HTTPError is an error returned by a failed API call
161+
type HTTPError struct {
162+
Code int
163+
URL string
164+
Message string
165+
}
166+
167+
func (e HTTPError) Error() string {
168+
return fmt.Sprintf("http error, '%s' failed (%d): '%s'", e.URL, e.Code, e.Message)
169+
}
170+
160171
// Returns whether or not scopes are present, appID, and error
161172
func (c Client) HasScopes(wantedScopes ...string) (bool, string, error) {
162173
url := "https://api.github.com/user"
@@ -298,7 +309,11 @@ func handleHTTPError(resp *http.Response) error {
298309
message = parsedBody.Message
299310
}
300311

301-
return fmt.Errorf("http error, '%s' failed (%d): '%s'", resp.Request.URL, resp.StatusCode, message)
312+
return HTTPError{
313+
Code: resp.StatusCode,
314+
URL: resp.Request.URL.String(),
315+
Message: message,
316+
}
302317
}
303318

304319
var jsonTypeRE = regexp.MustCompile(`[/+]json($|;)`)

api/client_test.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package api
22

33
import (
44
"bytes"
5+
"errors"
56
"io/ioutil"
67
"reflect"
78
"testing"
@@ -66,3 +67,16 @@ func TestRESTGetDelete(t *testing.T) {
6667
err := client.REST("DELETE", "applications/CLIENTID/grant", r, nil)
6768
eq(t, err, nil)
6869
}
70+
71+
func TestRESTError(t *testing.T) {
72+
http := &httpmock.Registry{}
73+
client := NewClient(ReplaceTripper(http))
74+
75+
http.StubResponse(422, bytes.NewBufferString(`{"message": "OH NO"}`))
76+
77+
var httpErr HTTPError
78+
err := client.REST("DELETE", "/repos/branch", nil, nil)
79+
if err == nil || !errors.As(err, &httpErr) || httpErr.Code != 422 {
80+
t.Fatalf("got %q", err.Error())
81+
}
82+
}

0 commit comments

Comments
 (0)
X Tutup