// Do performs an http.Request and optionally parses the response body into the given interface. func (c *Client) Do(req *http.Request, v interface{}) (*http.Response, error) { debug.Println("Request", req.Method, req.URL) res, err := c.client.Do(req) if err != nil { return nil, err } debug.Printf("Response StatusCode=%d\n", res.StatusCode) switch res.StatusCode { case http.StatusNoContent: return res, nil case http.StatusInternalServerError: url := urlTrackerAPI if strings.Contains(req.URL.Host, "x.exercism.io") { url = urlTrackerXAPI } return nil, fmt.Errorf("an internal server error was received.\nPlease file a bug report with the contents of 'exercism debug' at: %s ", url) default: if v != nil { defer res.Body.Close() var bodyCopy bytes.Buffer body := io.TeeReader(res.Body, &bodyCopy) err := json.NewDecoder(body).Decode(v) debug.Printf("Response Body\n%s\n\n", bodyCopy.String()) if err != nil { return nil, fmt.Errorf("error parsing API response - %s", err) } } } return res, nil }
func main() { api.UserAgent = fmt.Sprintf("github.com/exercism/cli v%s (%s/%s)", Version, runtime.GOOS, runtime.GOARCH) app := cli.NewApp() app.Name = "exercism" app.Usage = "A command line tool to interact with http://exercism.io" app.Version = Version app.Before = func(ctx *cli.Context) error { debug.Verbose = ctx.GlobalBool("verbose") debug.Println("verbose logging enabled") return nil } app.Flags = []cli.Flag{ cli.StringFlag{ Name: "config, c", Usage: "path to config file", EnvVar: "EXERCISM_CONFIG_FILE,XDG_CONFIG_HOME", }, cli.BoolFlag{ Name: "verbose", Usage: "turn on verbose logging", }, } app.Commands = []cli.Command{ { Name: "debug", Usage: descDebug, Action: cmd.Debug, }, { Name: "configure", Usage: descConfigure, Flags: []cli.Flag{ cli.StringFlag{ Name: "dir, d", Usage: "path to exercises directory", }, cli.StringFlag{ Name: "host, u", Usage: "exercism api host", }, cli.StringFlag{ Name: "key, k", Usage: "exercism.io API key (see http://exercism.io/account/key)", }, cli.StringFlag{ Name: "api, a", Usage: "exercism xapi host", }, }, Action: cmd.Configure, }, { Name: "fetch", ShortName: "f", Usage: descFetch, Action: cmd.Fetch, }, { Name: "restore", ShortName: "r", Usage: descRestore, Description: descLongRestore, Action: cmd.Restore, }, { Name: "skip", Usage: descSkip, Action: cmd.Skip, }, { Name: "submit", ShortName: "s", Usage: descSubmit, Action: cmd.Submit, Flags: []cli.Flag{ cli.BoolFlag{ Name: "test", Usage: "allow submission of test files", }, cli.StringFlag{ Name: "comment", Usage: "includes a comment with the submission", }, }, }, { Name: "unsubmit", ShortName: "u", Usage: "REMOVED", Action: func(*cli.Context) { fmt.Println("For security reasons, this command is no longer in use.\nYou can delete iterations in the web interface.") }, }, { Name: "upgrade", Usage: descUpgrade, Action: cmd.Upgrade, }, { Name: "tracks", ShortName: "t", Usage: descTracks, Action: cmd.Tracks, }, { Name: "open", ShortName: "op", Usage: descOpen, Action: cmd.Open, }, { Name: "download", ShortName: "dl", Usage: descDownload, Description: descLongDownload, Action: cmd.Download, }, { Name: "list", ShortName: "li", Usage: descList, Action: cmd.List, }, { Name: "status", ShortName: "st", Usage: descStatus, Action: cmd.Status, }, } if err := app.Run(os.Args); err != nil { log.Fatal(err) } }