Skip to content

Commit 096bf6b

Browse files
author
Nate Smith
authored
Merge pull request cli#1115 from cli/api-errors
Print HTTP errors on stderr in `api` command
2 parents 771cd59 + 5d5bd04 commit 096bf6b

File tree

2 files changed

+75
-5
lines changed

2 files changed

+75
-5
lines changed

pkg/cmd/api/api.go

Lines changed: 49 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package api
22

33
import (
4+
"bytes"
5+
"encoding/json"
46
"fmt"
57
"io"
68
"io/ioutil"
@@ -109,24 +111,36 @@ func apiRun(opts *ApiOptions) error {
109111
if resp.StatusCode == 204 {
110112
return nil
111113
}
114+
var responseBody io.Reader = resp.Body
112115
defer resp.Body.Close()
113116

114117
isJSON, _ := regexp.MatchString(`[/+]json(;|$)`, resp.Header.Get("Content-Type"))
115118

119+
var serverError string
120+
if isJSON && (opts.RequestPath == "graphql" || resp.StatusCode >= 400) {
121+
responseBody, serverError, err = parseErrorResponse(responseBody, resp.StatusCode)
122+
if err != nil {
123+
return err
124+
}
125+
}
126+
116127
if isJSON && opts.IO.ColorEnabled() {
117-
err = jsoncolor.Write(opts.IO.Out, resp.Body, " ")
128+
err = jsoncolor.Write(opts.IO.Out, responseBody, " ")
118129
if err != nil {
119130
return err
120131
}
121132
} else {
122-
_, err = io.Copy(opts.IO.Out, resp.Body)
133+
_, err = io.Copy(opts.IO.Out, responseBody)
123134
if err != nil {
124135
return err
125136
}
126137
}
127138

128-
// TODO: detect GraphQL errors
129-
if resp.StatusCode > 299 {
139+
if serverError != "" {
140+
fmt.Fprintf(opts.IO.ErrOut, "gh: %s\n", serverError)
141+
return cmdutil.SilentError
142+
} else if resp.StatusCode > 299 {
143+
fmt.Fprintf(opts.IO.ErrOut, "gh: HTTP %d\n", resp.StatusCode)
130144
return cmdutil.SilentError
131145
}
132146

@@ -219,3 +233,34 @@ func readUserFile(fn string, stdin io.ReadCloser) ([]byte, error) {
219233
defer r.Close()
220234
return ioutil.ReadAll(r)
221235
}
236+
237+
func parseErrorResponse(r io.Reader, statusCode int) (io.Reader, string, error) {
238+
bodyCopy := &bytes.Buffer{}
239+
b, err := ioutil.ReadAll(io.TeeReader(r, bodyCopy))
240+
if err != nil {
241+
return r, "", err
242+
}
243+
244+
var parsedBody struct {
245+
Message string
246+
Errors []struct {
247+
Message string
248+
}
249+
}
250+
err = json.Unmarshal(b, &parsedBody)
251+
if err != nil {
252+
return r, "", err
253+
}
254+
255+
if parsedBody.Message != "" {
256+
return bodyCopy, fmt.Sprintf("%s (HTTP %d)", parsedBody.Message, statusCode), nil
257+
} else if len(parsedBody.Errors) > 0 {
258+
msgs := make([]string, len(parsedBody.Errors))
259+
for i, e := range parsedBody.Errors {
260+
msgs[i] = e.Message
261+
}
262+
return bodyCopy, strings.Join(msgs, "\n"), nil
263+
}
264+
265+
return bodyCopy, "", nil
266+
}

pkg/cmd/api/api_test.go

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,31 @@ func Test_apiRun(t *testing.T) {
160160
stdout: ``,
161161
stderr: ``,
162162
},
163+
{
164+
name: "REST error",
165+
httpResponse: &http.Response{
166+
StatusCode: 400,
167+
Body: ioutil.NopCloser(bytes.NewBufferString(`{"message": "THIS IS FINE"}`)),
168+
Header: http.Header{"Content-Type": []string{"application/json; charset=utf-8"}},
169+
},
170+
err: cmdutil.SilentError,
171+
stdout: `{"message": "THIS IS FINE"}`,
172+
stderr: "gh: THIS IS FINE (HTTP 400)\n",
173+
},
174+
{
175+
name: "GraphQL error",
176+
options: ApiOptions{
177+
RequestPath: "graphql",
178+
},
179+
httpResponse: &http.Response{
180+
StatusCode: 200,
181+
Body: ioutil.NopCloser(bytes.NewBufferString(`{"errors": [{"message":"AGAIN"}, {"message":"FINE"}]}`)),
182+
Header: http.Header{"Content-Type": []string{"application/json; charset=utf-8"}},
183+
},
184+
err: cmdutil.SilentError,
185+
stdout: `{"errors": [{"message":"AGAIN"}, {"message":"FINE"}]}`,
186+
stderr: "gh: AGAIN\nFINE\n",
187+
},
163188
{
164189
name: "failure",
165190
httpResponse: &http.Response{
@@ -168,7 +193,7 @@ func Test_apiRun(t *testing.T) {
168193
},
169194
err: cmdutil.SilentError,
170195
stdout: `gateway timeout`,
171-
stderr: ``,
196+
stderr: "gh: HTTP 502\n",
172197
},
173198
}
174199

0 commit comments

Comments
 (0)