Skip to content

Commit

Permalink
Download non-200 response no longer swallowed
Browse files Browse the repository at this point in the history
Based on exercism#794, we've decide to
error out and display API error messages.

This overlaps with cmd/submit, so this change extracts it for sharing.
  • Loading branch information
jdsutherland committed Feb 21, 2019
1 parent fa6b272 commit c934fc6
Show file tree
Hide file tree
Showing 2 changed files with 22 additions and 20 deletions.
27 changes: 21 additions & 6 deletions cmd/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,24 @@ func validateUserConfig(cfg *viper.Viper) error {
return nil
}

// decodedAPIErrorResponse decodes and returns the error message from the API response.
// If the message is blank, it returns a fallback message with the status code.
func decodedAPIErrorResponse(resp *http.Response) error {
var apiError struct {
Error struct {
Type string `json:"type"`
Message string `json:"message"`
} `json:"error,omitempty"`
}
if err := json.NewDecoder(resp.Body).Decode(&apiError); err != nil {
return fmt.Errorf("failed to parse error response - %s", err)
}
if apiError.Error.Message != "" {
return fmt.Errorf(apiError.Error.Message)
}
return fmt.Errorf("unexpected API response: %d", resp.StatusCode)
}

// download is a download from the Exercism API.
type download struct {
params *downloadParams
Expand Down Expand Up @@ -250,7 +268,7 @@ type solutionFileRequester struct {
}

// request requests a Solution file from the API, returning an HTTP response.
// Non-200 responses and 0 Content-Length responses are swallowed, returning nil.
// 0 Content-Length responses are swallowed, returning nil.
func (s solutionFileRequester) request(filename string) (*http.Response, error) {
parsedURL, err := netURL.ParseRequestURI(
fmt.Sprintf("%s%s", s.download.Solution.FileDownloadBaseURL, filename))
Expand All @@ -269,8 +287,7 @@ func (s solutionFileRequester) request(filename string) (*http.Response, error)
}

if res.StatusCode != http.StatusOK {
// TODO: deal with it
return nil, nil
return nil, decodedAPIErrorResponse(res)
}
// Don't bother with empty files.
if res.Header.Get("Content-Length") == "0" {
Expand Down Expand Up @@ -300,9 +317,6 @@ func (w downloadWriter) writeMetadata() error {
return metadata.Write(w.destination())
}

// writeSolutionFiles attempts to write each file that is part of the downloaded solution.
// An HTTP request is made using each filename and failed responses are swallowed.
// All successful file responses are written except when 0 Content-Length.
func (w downloadWriter) writeSolutionFiles() error {
for _, filename := range w.download.Solution.Files {
if err := w.writeSolutionFile(filename); err != nil {
Expand All @@ -312,6 +326,7 @@ func (w downloadWriter) writeSolutionFiles() error {
return nil
}

// writeSolutionFile attempts to write the file from the downloaded solution.
func (w downloadWriter) writeSolutionFile(filename string) error {
if err := w.download.ensureSolutionFilesWritable(); err != nil {
return err
Expand Down
15 changes: 1 addition & 14 deletions cmd/submit.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package cmd

import (
"bytes"
"encoding/json"
"errors"
"fmt"
"io"
Expand Down Expand Up @@ -252,12 +251,7 @@ func runSubmit(cfg config.Config, flags *pflag.FlagSet, args []string) error {
defer resp.Body.Close()

if resp.StatusCode == http.StatusBadRequest {
var jsonErrBody apiErrorMessage
if err := json.NewDecoder(resp.Body).Decode(&jsonErrBody); err != nil {
return fmt.Errorf("failed to parse error response - %s", err)
}

return fmt.Errorf(jsonErrBody.Error.Message)
return decodedAPIErrorResponse(resp)
}

bb := &bytes.Buffer{}
Expand All @@ -283,10 +277,3 @@ func runSubmit(cfg config.Config, flags *pflag.FlagSet, args []string) error {
func init() {
RootCmd.AddCommand(submitCmd)
}

type apiErrorMessage struct {
Error struct {
Type string `json:"type"`
Message string `json:"message"`
} `json:"error,omitempty"`
}

0 comments on commit c934fc6

Please sign in to comment.