Repository: exercism/cli
Pull request #585 (2018-06-25 03:54:54)

Pass only needed primitives to api.Client
The client doesn't need the entire config objects, it just needs a couple of strings.

Between this PR and #581 the api package doesn't need to know about the config package anymore!

FYI @nywilken I'm going to merge this one when it goes green and then rebase #581 onto nextercism again.


Patch:
diff --git a/api/client.go b/api/client.go
index 1719781..7ad6757 100644
--- a/api/client.go
+++ b/api/client.go
@@ -23,26 +23,17 @@ var (
 // Client is an http client that is configured for Exercism.
 type Client struct {
 	*http.Client
-	APIConfig   *config.APIConfig
-	UserConfig  *config.UserConfig
 	ContentType string
+	Token       string
+	BaseURL     string
 }
 
 // NewClient returns an Exercism API client.
-func NewClient() (*Client, error) {
-	apiCfg, err := config.NewAPIConfig()
-	if err != nil {
-		return nil, err
-	}
-	userCfg, err := config.NewUserConfig()
-	if err != nil {
-		return nil, err
-	}
-
+func NewClient(token, baseURL string) (*Client, error) {
 	return &Client{
-		Client:     DefaultHTTPClient,
-		APIConfig:  apiCfg,
-		UserConfig: userCfg,
+		Client:  DefaultHTTPClient,
+		Token:   token,
+		BaseURL: baseURL,
 	}, nil
 }
 
@@ -63,8 +54,8 @@ func (c *Client) NewRequest(method, url string, body io.Reader) (*http.Request,
 	} else {
 		req.Header.Set("Content-Type", c.ContentType)
 	}
-	if c.UserConfig != nil && c.UserConfig.Token != "" {
-		req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", c.UserConfig.Token))
+	if c.Token != "" {
+		req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", c.Token))
 	}
 
 	return req, nil
@@ -87,7 +78,7 @@ func (c *Client) Do(req *http.Request, v interface{}) (*http.Response, error) {
 		// TODO: if it's json, and it has an error key, print the message.
 		return nil, fmt.Errorf("%s", res.Status)
 	case http.StatusUnauthorized:
-		siteURL := config.InferSiteURL(c.APIConfig.BaseURL)
+		siteURL := config.InferSiteURL(c.BaseURL)
 		return nil, fmt.Errorf("unauthorized request. Please run the configure command. You can find your API token at %s/my/settings", siteURL)
 	default:
 		if v != nil {
@@ -104,7 +95,7 @@ func (c *Client) Do(req *http.Request, v interface{}) (*http.Response, error) {
 
 // ValidateToken calls the API to determine whether the token is valid.
 func (c *Client) ValidateToken() error {
-	url := fmt.Sprintf("%s/validate_token", c.APIConfig.BaseURL)
+	url := fmt.Sprintf("%s/validate_token", c.BaseURL)
 	req, err := c.NewRequest("GET", url, nil)
 	if err != nil {
 		return err
diff --git a/cmd/configure.go b/cmd/configure.go
index a951f3b..ff150e4 100644
--- a/cmd/configure.go
+++ b/cmd/configure.go
@@ -64,7 +64,7 @@ You can also override certain default settings to suit your preferences.
 		if show {
 			defer printCurrentConfig()
 		}
-		client, err := api.NewClient()
+		client, err := api.NewClient(usrCfg.Token, apiCfg.BaseURL)
 		if err != nil {
 			return err
 		}
diff --git a/cmd/download.go b/cmd/download.go
index 8becdae..e8b9824 100644
--- a/cmd/download.go
+++ b/cmd/download.go
@@ -48,6 +48,11 @@ Download other people's solutions by providing the UUID.
 			// TODO: usage
 			return errors.New("need an exercise name or a solution --uuid")
 		}
+		usrCfg, err := config.NewUserConfig()
+		if err != nil {
+			return err
+		}
+
 		apiCfg, err := config.NewAPIConfig()
 		if err != nil {
 			return err
@@ -61,7 +66,7 @@ Download other people's solutions by providing the UUID.
 		}
 		url := fmt.Sprintf("%s/solutions/%s", apiCfg.BaseURL, slug)
 
-		client, err := api.NewClient()
+		client, err := api.NewClient(usrCfg.Token, apiCfg.BaseURL)
 		if err != nil {
 			return err
 		}
@@ -116,9 +121,9 @@ Download other people's solutions by providing the UUID.
 
 		var ws workspace.Workspace
 		if solution.IsRequester {
-			ws = workspace.New(filepath.Join(client.UserConfig.Workspace, solution.Track))
+			ws = workspace.New(filepath.Join(usrCfg.Workspace, solution.Track))
 		} else {
-			ws = workspace.New(filepath.Join(client.UserConfig.Workspace, "users", solution.Handle, solution.Track))
+			ws = workspace.New(filepath.Join(usrCfg.Workspace, "users", solution.Handle, solution.Track))
 		}
 		os.MkdirAll(ws.Dir, os.FileMode(0755))
 
diff --git a/cmd/prepare.go b/cmd/prepare.go
index 9504b66..f240394 100644
--- a/cmd/prepare.go
+++ b/cmd/prepare.go
@@ -43,12 +43,16 @@ To customize the CLI to suit your own preferences, use the configure command.
 }
 
 func prepareTrack(id string) error {
+	usrCfg, err := config.NewUserConfig()
+	if err != nil {
+		return err
+	}
 	apiCfg, err := config.NewAPIConfig()
 	if err != nil {
 		return err
 	}
 
-	client, err := api.NewClient()
+	client, err := api.NewClient(usrCfg.Token, apiCfg.BaseURL)
 	if err != nil {
 		return err
 	}
diff --git a/cmd/submit.go b/cmd/submit.go
index d640e87..cd3994c 100644
--- a/cmd/submit.go
+++ b/cmd/submit.go
@@ -208,7 +208,7 @@ figuring things out if necessary.
 			return err
 		}
 
-		client, err := api.NewClient()
+		client, err := api.NewClient(usrCfg.Token, apiCfg.BaseURL)
 		if err != nil {
 			return err
 		}
@@ -232,8 +232,8 @@ figuring things out if necessary.
 		}
 
 		if solution.AutoApprove == true {
-			fmt.Fprintf(Out, "Your solution has been submitted " +
-				"successfully and has been auto-approved. You can complete " +
+			fmt.Fprintf(Out, "Your solution has been submitted "+
+				"successfully and has been auto-approved. You can complete "+
 				"the exercise and unlock the next core exercise at %s\n",
 				solution.URL)
 		} else {


Test patch:
diff --git a/api/client_test.go b/api/client_test.go
index ba069b7..c730236 100644
--- a/api/client_test.go
+++ b/api/client_test.go
@@ -6,7 +6,6 @@ import (
 	"net/http/httptest"
 	"testing"
 
-	"github.com/exercism/cli/config"
 	"github.com/stretchr/testify/assert"
 )
 
@@ -33,8 +32,8 @@ func TestNewRequestSetsDefaultHeaders(t *testing.T) {
 		{
 			desc: "Override defaults",
 			client: &Client{
-				UserConfig:  &config.UserConfig{Token: "abc123"},
 				ContentType: "bogus",
+				Token:       "abc123",
 			},
 			auth:        "Bearer abc123",
 			contentType: "bogus",