|
1 | 1 | package api |
2 | 2 |
|
3 | 3 | import ( |
| 4 | + "bytes" |
| 5 | + "encoding/json" |
4 | 6 | "fmt" |
5 | 7 | "io" |
6 | 8 | "io/ioutil" |
@@ -109,24 +111,36 @@ func apiRun(opts *ApiOptions) error { |
109 | 111 | if resp.StatusCode == 204 { |
110 | 112 | return nil |
111 | 113 | } |
| 114 | + var responseBody io.Reader = resp.Body |
112 | 115 | defer resp.Body.Close() |
113 | 116 |
|
114 | 117 | isJSON, _ := regexp.MatchString(`[/+]json(;|$)`, resp.Header.Get("Content-Type")) |
115 | 118 |
|
| 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 | + |
116 | 127 | if isJSON && opts.IO.ColorEnabled() { |
117 | | - err = jsoncolor.Write(opts.IO.Out, resp.Body, " ") |
| 128 | + err = jsoncolor.Write(opts.IO.Out, responseBody, " ") |
118 | 129 | if err != nil { |
119 | 130 | return err |
120 | 131 | } |
121 | 132 | } else { |
122 | | - _, err = io.Copy(opts.IO.Out, resp.Body) |
| 133 | + _, err = io.Copy(opts.IO.Out, responseBody) |
123 | 134 | if err != nil { |
124 | 135 | return err |
125 | 136 | } |
126 | 137 | } |
127 | 138 |
|
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) |
130 | 144 | return cmdutil.SilentError |
131 | 145 | } |
132 | 146 |
|
@@ -219,3 +233,34 @@ func readUserFile(fn string, stdin io.ReadCloser) ([]byte, error) { |
219 | 233 | defer r.Close() |
220 | 234 | return ioutil.ReadAll(r) |
221 | 235 | } |
| 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 | +} |
0 commit comments