From 6b4902a65fe9260ebc6d916ea511dfa0d7844e0d Mon Sep 17 00:00:00 2001 From: Prylutskyi Anatolii Date: Tue, 20 Sep 2016 19:52:53 +0300 Subject: [PATCH 01/13] Add Doer interface, DoerFunc type and replace http.Client with Doer --- asana/asana.go | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/asana/asana.go b/asana/asana.go index 090b310..d6b7324 100644 --- a/asana/asana.go +++ b/asana/asana.go @@ -28,8 +28,18 @@ var defaultOptFields = map[string][]string{ } type ( + //Doer interface used for doing http calls. + //Use it as point of setting Auth header or custom status code error handling. + Doer interface { + Do(req *http.Request) (*http.Response, error) + } + + //DoerFunc implements Doer interface. + //Allow to transform any appropriate function "f" to Doer instance: DoerFunc(f). + DoerFunc func(req *http.Request) (resp *http.Response, err error) + Client struct { - client *http.Client + doer Doer BaseURL *url.URL UserAgent string } @@ -123,16 +133,22 @@ type ( } ) +func (f DoerFunc) Do(req *http.Request) (resp *http.Response, err error) { + return f(req) +} + func (e Error) Error() string { return fmt.Sprintf("%v - %v", e.Message, e.Phrase) } -func NewClient(httpClient *http.Client) *Client { - if httpClient == nil { - httpClient = http.DefaultClient +//NewClient created new asana client with doer. +//If doer is nil then http.DefaultClient used intead. +func NewClient(doer Doer) *Client { + if doer == nil { + doer = http.DefaultClient } baseURL, _ := url.Parse(defaultBaseURL) - client := &Client{client: httpClient, BaseURL: baseURL, UserAgent: userAgent} + client := &Client{doer: doer, BaseURL: baseURL, UserAgent: userAgent} return client } @@ -247,7 +263,7 @@ func (c *Client) request(method string, path string, data interface{}, opt *Filt req.Header.Set("Content-Type", "application/json") } req.Header.Set("User-Agent", c.UserAgent) - resp, err := c.client.Do(req) + resp, err := c.doer.Do(req) if err != nil { return err } From 9a6f5cb646a53c7bb360d65fc4640e44c0ef54f2 Mon Sep 17 00:00:00 2001 From: Prylutskyi Anatolii Date: Tue, 20 Sep 2016 19:56:08 +0300 Subject: [PATCH 02/13] Add Errors type and made it error instance to return all errors from asana --- asana/asana.go | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/asana/asana.go b/asana/asana.go index d6b7324..b98b975 100644 --- a/asana/asana.go +++ b/asana/asana.go @@ -8,6 +8,7 @@ import ( "io" "net/http" "net/url" + "strings" "time" "github.com/google/go-querystring/query" @@ -124,13 +125,15 @@ type ( Response struct { Data interface{} `json:"data,omitempty"` - Errors []Error `json:"errors,omitempty"` + Errors Errors `json:"errors,omitempty"` } Error struct { Phrase string `json:"phrase,omitempty"` Message string `json:"message,omitempty"` } + + Errors []Error ) func (f DoerFunc) Do(req *http.Request) (resp *http.Response, err error) { @@ -141,6 +144,14 @@ func (e Error) Error() string { return fmt.Sprintf("%v - %v", e.Message, e.Phrase) } +func (e Errors) Error() string { + var sErrs []string + for _, err := range e { + sErrs = append(sErrs, err.Error()) + } + return strings.Join(sErrs, ", ") +} + //NewClient created new asana client with doer. //If doer is nil then http.DefaultClient used intead. func NewClient(doer Doer) *Client { @@ -272,7 +283,7 @@ func (c *Client) request(method string, path string, data interface{}, opt *Filt res := &Response{Data: v} err = json.NewDecoder(resp.Body).Decode(res) if len(res.Errors) > 0 { - return res.Errors[0] + return res.Errors } return err } From d3f78fd3bd7224308373f504f48fba005945b020 Mon Sep 17 00:00:00 2001 From: Prylutskyi Anatolii Date: Tue, 20 Sep 2016 20:01:46 +0300 Subject: [PATCH 03/13] Add ErrUnauthorized on 401 response status code and test it --- asana/asana.go | 9 +++++++++ asana/asana_test.go | 14 ++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/asana/asana.go b/asana/asana.go index b98b975..f39fba5 100644 --- a/asana/asana.go +++ b/asana/asana.go @@ -4,6 +4,7 @@ package asana import ( "bytes" "encoding/json" + "errors" "fmt" "io" "net/http" @@ -28,6 +29,11 @@ var defaultOptFields = map[string][]string{ "tasks": {"name", "assignee", "assignee_status", "completed", "parent"}, } +var ( + //ErrUnauthorized can be returned on any call on response status code 401 + ErrUnauthorized = errors.New("asana: unauthorized") +) + type ( //Doer interface used for doing http calls. //Use it as point of setting Auth header or custom status code error handling. @@ -279,6 +285,9 @@ func (c *Client) request(method string, path string, data interface{}, opt *Filt return err } defer resp.Body.Close() + if resp.StatusCode == http.StatusUnauthorized { + return ErrUnauthorized + } res := &Response{Data: v} err = json.NewDecoder(resp.Body).Decode(res) diff --git a/asana/asana_test.go b/asana/asana_test.go index abf8494..835dc74 100644 --- a/asana/asana_test.go +++ b/asana/asana_test.go @@ -215,6 +215,20 @@ func TestListTags(t *testing.T) { } } +func TestUnauthorized(t *testing.T) { + setup() + defer teardown() + + mux.HandleFunc("/tags", func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusUnauthorized) + }) + + _, err := client.ListTags(nil) + if err != ErrUnauthorized { + t.Errorf("Unexpected err %v", err) + } +} + func testMethod(t *testing.T, r *http.Request, want string) { if got := r.Method; got != want { t.Errorf("Request method: %v, want %v", got, want) From 6db185103ae3a44f56cd9d919b2adf8327fe94d0 Mon Sep 17 00:00:00 2001 From: Prylutskyi Anatolii Date: Tue, 20 Sep 2016 20:06:52 +0300 Subject: [PATCH 04/13] Add context.Context support for all operations and fix tests --- asana/asana.go | 53 +++++++++++++++++++++++---------------------- asana/asana_test.go | 16 ++++++++------ 2 files changed, 36 insertions(+), 33 deletions(-) diff --git a/asana/asana.go b/asana/asana.go index f39fba5..65f125e 100644 --- a/asana/asana.go +++ b/asana/asana.go @@ -3,6 +3,7 @@ package asana import ( "bytes" + "context" "encoding/json" "errors" "fmt" @@ -169,82 +170,82 @@ func NewClient(doer Doer) *Client { return client } -func (c *Client) ListWorkspaces() ([]Workspace, error) { +func (c *Client) ListWorkspaces(ctx context.Context) ([]Workspace, error) { workspaces := new([]Workspace) - err := c.Request("workspaces", nil, workspaces) + err := c.Request(ctx, "workspaces", nil, workspaces) return *workspaces, err } -func (c *Client) ListUsers(opt *Filter) ([]User, error) { +func (c *Client) ListUsers(ctx context.Context, opt *Filter) ([]User, error) { users := new([]User) - err := c.Request("users", opt, users) + err := c.Request(ctx, "users", opt, users) return *users, err } -func (c *Client) ListProjects(opt *Filter) ([]Project, error) { +func (c *Client) ListProjects(ctx context.Context, opt *Filter) ([]Project, error) { projects := new([]Project) - err := c.Request("projects", opt, projects) + err := c.Request(ctx, "projects", opt, projects) return *projects, err } -func (c *Client) ListTasks(opt *Filter) ([]Task, error) { +func (c *Client) ListTasks(ctx context.Context, opt *Filter) ([]Task, error) { tasks := new([]Task) - err := c.Request("tasks", opt, tasks) + err := c.Request(ctx, "tasks", opt, tasks) return *tasks, err } -func (c *Client) GetTask(id int64, opt *Filter) (Task, error) { +func (c *Client) GetTask(ctx context.Context, id int64, opt *Filter) (Task, error) { task := new(Task) - err := c.Request(fmt.Sprintf("tasks/%d", id), opt, task) + err := c.Request(ctx, fmt.Sprintf("tasks/%d", id), opt, task) return *task, err } // UpdateTask updates a task. // // https://asana.com/developers/api-reference/tasks#update -func (c *Client) UpdateTask(id int64, tu TaskUpdate, opt *Filter) (Task, error) { +func (c *Client) UpdateTask(ctx context.Context, id int64, tu TaskUpdate, opt *Filter) (Task, error) { task := new(Task) - err := c.request("PUT", fmt.Sprintf("tasks/%d", id), tu, opt, task) + err := c.request(ctx, "PUT", fmt.Sprintf("tasks/%d", id), tu, opt, task) return *task, err } -func (c *Client) ListProjectTasks(projectID int64, opt *Filter) ([]Task, error) { +func (c *Client) ListProjectTasks(ctx context.Context, projectID int64, opt *Filter) ([]Task, error) { tasks := new([]Task) - err := c.Request(fmt.Sprintf("projects/%d/tasks", projectID), opt, tasks) + err := c.Request(ctx, fmt.Sprintf("projects/%d/tasks", projectID), opt, tasks) return *tasks, err } -func (c *Client) ListTaskStories(taskID int64, opt *Filter) ([]Story, error) { +func (c *Client) ListTaskStories(ctx context.Context, taskID int64, opt *Filter) ([]Story, error) { stories := new([]Story) - err := c.Request(fmt.Sprintf("tasks/%d/stories", taskID), opt, stories) + err := c.Request(ctx, fmt.Sprintf("tasks/%d/stories", taskID), opt, stories) return *stories, err } -func (c *Client) ListTags(opt *Filter) ([]Tag, error) { +func (c *Client) ListTags(ctx context.Context, opt *Filter) ([]Tag, error) { tags := new([]Tag) - err := c.Request("tags", opt, tags) + err := c.Request(ctx, "tags", opt, tags) return *tags, err } -func (c *Client) GetAuthenticatedUser(opt *Filter) (User, error) { +func (c *Client) GetAuthenticatedUser(ctx context.Context, opt *Filter) (User, error) { user := new(User) - err := c.Request("users/me", opt, user) + err := c.Request(ctx, "users/me", opt, user) return *user, err } -func (c *Client) GetUserByID(id int64, opt *Filter) (User, error) { +func (c *Client) GetUserByID(ctx context.Context, id int64, opt *Filter) (User, error) { user := new(User) - err := c.Request(fmt.Sprintf("users/%d", id), opt, user) + err := c.Request(ctx, fmt.Sprintf("users/%d", id), opt, user) return *user, err } -func (c *Client) Request(path string, opt *Filter, v interface{}) error { - return c.request("GET", path, nil, opt, v) +func (c *Client) Request(ctx context.Context, path string, opt *Filter, v interface{}) error { + return c.request(ctx, "GET", path, nil, opt, v) } // request makes a request to Asana API, using method, at path, sending data with opt filter. // The response is populated into v, and any error is returned. -func (c *Client) request(method string, path string, data interface{}, opt *Filter, v interface{}) error { +func (c *Client) request(ctx context.Context, method string, path string, data interface{}, opt *Filter, v interface{}) error { if opt == nil { opt = &Filter{} } @@ -280,7 +281,7 @@ func (c *Client) request(method string, path string, data interface{}, opt *Filt req.Header.Set("Content-Type", "application/json") } req.Header.Set("User-Agent", c.UserAgent) - resp, err := c.doer.Do(req) + resp, err := c.doer.Do(req.WithContext(ctx)) if err != nil { return err } diff --git a/asana/asana_test.go b/asana/asana_test.go index 835dc74..744956b 100644 --- a/asana/asana_test.go +++ b/asana/asana_test.go @@ -1,6 +1,7 @@ package asana import ( + "context" "fmt" "io/ioutil" "net/http" @@ -14,6 +15,7 @@ var ( client *Client mux *http.ServeMux server *httptest.Server + ctx = context.Background() ) func setup() { @@ -57,7 +59,7 @@ func TestListWorkspaces(t *testing.T) { ]}`) }) - workspaces, err := client.ListWorkspaces() + workspaces, err := client.ListWorkspaces(ctx) if err != nil { t.Errorf("ListWorkspaces returned error: %v", err) } @@ -83,7 +85,7 @@ func TestListUsers(t *testing.T) { ]}`) }) - users, err := client.ListUsers(nil) + users, err := client.ListUsers(ctx, nil) if err != nil { t.Errorf("ListUsers returned error: %v", err) } @@ -109,7 +111,7 @@ func TestListProjects(t *testing.T) { ]}`) }) - projects, err := client.ListProjects(nil) + projects, err := client.ListProjects(ctx, nil) if err != nil { t.Errorf("ListProjects returned error: %v", err) } @@ -135,7 +137,7 @@ func TestListTasks(t *testing.T) { ]}`) }) - tasks, err := client.ListTasks(nil) + tasks, err := client.ListTasks(ctx, nil) if err != nil { t.Errorf("ListTasks returned error: %v", err) } @@ -178,7 +180,7 @@ func TestUpdateTask(t *testing.T) { // to store v and returns a pointer to it. String := func(v string) *string { return &v } - task, err := client.UpdateTask(1, TaskUpdate{Notes: String("updated notes")}, nil) + task, err := client.UpdateTask(ctx, 1, TaskUpdate{Notes: String("updated notes")}, nil) if err != nil { t.Errorf("UpdateTask returned error: %v", err) } @@ -200,7 +202,7 @@ func TestListTags(t *testing.T) { ]}`) }) - tags, err := client.ListTags(nil) + tags, err := client.ListTags(ctx, nil) if err != nil { t.Errorf("ListTags returned error: %v", err) } @@ -223,7 +225,7 @@ func TestUnauthorized(t *testing.T) { w.WriteHeader(http.StatusUnauthorized) }) - _, err := client.ListTags(nil) + _, err := client.ListTags(ctx, nil) if err != ErrUnauthorized { t.Errorf("Unexpected err %v", err) } From e21049480c6c4ef4c6ebae6e4254e8d83f6411fe Mon Sep 17 00:00:00 2001 From: Prylutskyi Anatolii Date: Tue, 20 Sep 2016 21:53:47 +0300 Subject: [PATCH 05/13] Extend request function to allow do x-www-form-urlencoded requests --- asana/asana.go | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/asana/asana.go b/asana/asana.go index 65f125e..1756ca5 100644 --- a/asana/asana.go +++ b/asana/asana.go @@ -205,7 +205,7 @@ func (c *Client) GetTask(ctx context.Context, id int64, opt *Filter) (Task, erro // https://asana.com/developers/api-reference/tasks#update func (c *Client) UpdateTask(ctx context.Context, id int64, tu TaskUpdate, opt *Filter) (Task, error) { task := new(Task) - err := c.request(ctx, "PUT", fmt.Sprintf("tasks/%d", id), tu, opt, task) + err := c.request(ctx, "PUT", fmt.Sprintf("tasks/%d", id), tu, nil, opt, task) return *task, err } @@ -240,12 +240,12 @@ func (c *Client) GetUserByID(ctx context.Context, id int64, opt *Filter) (User, } func (c *Client) Request(ctx context.Context, path string, opt *Filter, v interface{}) error { - return c.request(ctx, "GET", path, nil, opt, v) + return c.request(ctx, "GET", path, nil, nil, opt, v) } // request makes a request to Asana API, using method, at path, sending data with opt filter. // The response is populated into v, and any error is returned. -func (c *Client) request(ctx context.Context, method string, path string, data interface{}, opt *Filter, v interface{}) error { +func (c *Client) request(ctx context.Context, method string, path string, data interface{}, form url.Values, opt *Filter, v interface{}) error { if opt == nil { opt = &Filter{} } @@ -271,6 +271,8 @@ func (c *Client) request(ctx context.Context, method string, path string, data i return err } body = bytes.NewReader(b) + } else if form != nil { + body = strings.NewReader(form.Encode()) } req, err := http.NewRequest(method, u.String(), body) if err != nil { @@ -279,7 +281,10 @@ func (c *Client) request(ctx context.Context, method string, path string, data i if data != nil { req.Header.Set("Content-Type", "application/json") + } else if form != nil { + req.Header.Set("Content-Type", "application/x-www-form-urlencoded") } + req.Header.Set("User-Agent", c.UserAgent) resp, err := c.doer.Do(req.WithContext(ctx)) if err != nil { From 549df7964e460f22d2da692f2d94f8f2a91d1396 Mon Sep 17 00:00:00 2001 From: Prylutskyi Anatolii Date: Tue, 20 Sep 2016 21:54:59 +0300 Subject: [PATCH 06/13] Add CreateTask function and test it --- asana/asana.go | 17 +++++++++++++++++ asana/asana_test.go | 44 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+) diff --git a/asana/asana.go b/asana/asana.go index 1756ca5..5675402 100644 --- a/asana/asana.go +++ b/asana/asana.go @@ -209,6 +209,15 @@ func (c *Client) UpdateTask(ctx context.Context, id int64, tu TaskUpdate, opt *F return *task, err } +// CreateTask creates a task. +// +// https://asana.com/developers/api-reference/tasks#create +func (c *Client) CreateTask(ctx context.Context, fields map[string]string, opts *Filter) (Task, error) { + task := new(Task) + err := c.request(ctx, "POST", "tasks", nil, toURLValues(fields), opts, task) + return *task, err +} + func (c *Client) ListProjectTasks(ctx context.Context, projectID int64, opt *Filter) ([]Task, error) { tasks := new([]Task) err := c.Request(ctx, fmt.Sprintf("projects/%d/tasks", projectID), opt, tasks) @@ -315,3 +324,11 @@ func addOptions(s string, opt interface{}) (string, error) { u.RawQuery = qs.Encode() return u.String(), nil } + +func toURLValues(m map[string]string) url.Values { + values := make(url.Values) + for k, v := range m { + values[k] = []string{v} + } + return values +} diff --git a/asana/asana_test.go b/asana/asana_test.go index 744956b..1e3d71b 100644 --- a/asana/asana_test.go +++ b/asana/asana_test.go @@ -231,6 +231,50 @@ func TestUnauthorized(t *testing.T) { } } +func TestCreateTask(t *testing.T) { + setup() + defer teardown() + + var called int + defer func() { testCalled(t, called, 1) }() + + mux.HandleFunc("/tasks", func(w http.ResponseWriter, r *http.Request) { + called++ + testMethod(t, r, "POST") + testHeader(t, r, "Content-Type", "application/x-www-form-urlencoded") + b, err := ioutil.ReadAll(r.Body) + if err != nil { + t.Fatalf("error reading request body: %v", err) + } + values, err := url.ParseQuery(string(b)) + if err != nil { + t.Fatalf("error parsing body: %v", err) + } + want := url.Values{ + "key1": []string{"value1"}, + "key2": []string{"value2"}, + } + if !reflect.DeepEqual(values, want) { + t.Errorf("invalid body received %v", values) + } + fmt.Fprint(w, `{"data":{"id":1,"notes":"updated notes"}}`) + }) + + task, err := client.CreateTask(ctx, map[string]string{ + "key1": "value1", + "key2": "value2", + }, nil) + + if err != nil { + t.Errorf("CreateTask returned error: %v", err) + } + + want := Task{ID: 1, Notes: "updated notes"} + if !reflect.DeepEqual(task, want) { + t.Errorf("CreateTask returned %+v, want %+v", task, want) + } +} + func testMethod(t *testing.T, r *http.Request, want string) { if got := r.Method; got != want { t.Errorf("Request method: %v, want %v", got, want) From 98380c1edd82d06d207b5fce03ae002b0f40c40b Mon Sep 17 00:00:00 2001 From: Prylutskyi Anatolii Date: Tue, 20 Sep 2016 21:56:27 +0300 Subject: [PATCH 07/13] Change go version to 1.7 in travis.yml --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 883af88..946e08f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,7 @@ sudo: false language: go go: - - 1.6.2 + - 1.7.1 - tip matrix: allow_failures: From 12d49ffaf608bfbd6aebe979f52d7d105649e69b Mon Sep 17 00:00:00 2001 From: Prylutskyi Anatolii Date: Tue, 20 Sep 2016 22:15:37 +0300 Subject: [PATCH 08/13] Add DueOn and DueAt fields to Task struct --- asana/asana.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/asana/asana.go b/asana/asana.go index 5675402..13c4291 100644 --- a/asana/asana.go +++ b/asana/asana.go @@ -86,6 +86,8 @@ type ( Notes string `json:"notes,omitempty"` ParentTask *Task `json:"parent,omitempty"` Projects []Project `json:"projects,omitempty"` + DueOn string `json:"due_on,omitempty"` + DueAt string `json:"due_at,omitempty"` } // TaskUpdate is used to update a task. TaskUpdate struct { From 4ad4491c4ffbf12f1e3069295ed28fa61239e809 Mon Sep 17 00:00:00 2001 From: Prylutskyi Anatolii Date: Sun, 25 Sep 2016 20:58:56 +0300 Subject: [PATCH 09/13] Add space after two slashes for all godocs --- asana/asana.go | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/asana/asana.go b/asana/asana.go index 13c4291..86c5c91 100644 --- a/asana/asana.go +++ b/asana/asana.go @@ -31,19 +31,19 @@ var defaultOptFields = map[string][]string{ } var ( - //ErrUnauthorized can be returned on any call on response status code 401 + // ErrUnauthorized can be returned on any call on response status code 401 ErrUnauthorized = errors.New("asana: unauthorized") ) type ( - //Doer interface used for doing http calls. - //Use it as point of setting Auth header or custom status code error handling. + // Doer interface used for doing http calls. + // Use it as point of setting Auth header or custom status code error handling. Doer interface { Do(req *http.Request) (*http.Response, error) } - //DoerFunc implements Doer interface. - //Allow to transform any appropriate function "f" to Doer instance: DoerFunc(f). + // DoerFunc implements Doer interface. + // Allow to transform any appropriate function "f" to Doer instance: DoerFunc(f). DoerFunc func(req *http.Request) (resp *http.Response, err error) Client struct { @@ -161,8 +161,8 @@ func (e Errors) Error() string { return strings.Join(sErrs, ", ") } -//NewClient created new asana client with doer. -//If doer is nil then http.DefaultClient used intead. +// NewClient created new asana client with doer. +// If doer is nil then http.DefaultClient used intead. func NewClient(doer Doer) *Client { if doer == nil { doer = http.DefaultClient From 5a949765b8a9304b3ca40188f8cba13031f8df69 Mon Sep 17 00:00:00 2001 From: Prylutskyi Anatolii Date: Sun, 25 Sep 2016 20:59:27 +0300 Subject: [PATCH 10/13] Add godoc for Errors type --- asana/asana.go | 1 + 1 file changed, 1 insertion(+) diff --git a/asana/asana.go b/asana/asana.go index 86c5c91..0dfbb03 100644 --- a/asana/asana.go +++ b/asana/asana.go @@ -142,6 +142,7 @@ type ( Message string `json:"message,omitempty"` } + // Errors always has at least 1 element when returned Errors []Error ) From 67b650e8c7ad08d5500b6e03c11e21b0fe183d98 Mon Sep 17 00:00:00 2001 From: Prylutskyi Anatolii Date: Sun, 25 Sep 2016 21:00:43 +0300 Subject: [PATCH 11/13] Improve godoc for request function, add info about form argument --- asana/asana.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/asana/asana.go b/asana/asana.go index 0dfbb03..d6fa48c 100644 --- a/asana/asana.go +++ b/asana/asana.go @@ -255,7 +255,9 @@ func (c *Client) Request(ctx context.Context, path string, opt *Filter, v interf return c.request(ctx, "GET", path, nil, nil, opt, v) } -// request makes a request to Asana API, using method, at path, sending data with opt filter. +// request makes a request to Asana API, using method, at path, sending data or form with opt filter. +// Only data or form could be sent at the same time. If both provided form will be omitted. +// Also it's possible to do request with nil data and form. // The response is populated into v, and any error is returned. func (c *Client) request(ctx context.Context, method string, path string, data interface{}, form url.Values, opt *Filter, v interface{}) error { if opt == nil { From 84ac3b00d15f340a21245456e0511f0e24e17af5 Mon Sep 17 00:00:00 2001 From: Prylutskyi Anatolii Date: Sun, 25 Sep 2016 21:12:35 +0300 Subject: [PATCH 12/13] Remove global context in tests --- asana/asana_test.go | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/asana/asana_test.go b/asana/asana_test.go index 1e3d71b..404d53f 100644 --- a/asana/asana_test.go +++ b/asana/asana_test.go @@ -15,7 +15,6 @@ var ( client *Client mux *http.ServeMux server *httptest.Server - ctx = context.Background() ) func setup() { @@ -59,7 +58,7 @@ func TestListWorkspaces(t *testing.T) { ]}`) }) - workspaces, err := client.ListWorkspaces(ctx) + workspaces, err := client.ListWorkspaces(context.Background()) if err != nil { t.Errorf("ListWorkspaces returned error: %v", err) } @@ -85,7 +84,7 @@ func TestListUsers(t *testing.T) { ]}`) }) - users, err := client.ListUsers(ctx, nil) + users, err := client.ListUsers(context.Background(), nil) if err != nil { t.Errorf("ListUsers returned error: %v", err) } @@ -111,7 +110,7 @@ func TestListProjects(t *testing.T) { ]}`) }) - projects, err := client.ListProjects(ctx, nil) + projects, err := client.ListProjects(context.Background(), nil) if err != nil { t.Errorf("ListProjects returned error: %v", err) } @@ -137,7 +136,7 @@ func TestListTasks(t *testing.T) { ]}`) }) - tasks, err := client.ListTasks(ctx, nil) + tasks, err := client.ListTasks(context.Background(), nil) if err != nil { t.Errorf("ListTasks returned error: %v", err) } @@ -180,7 +179,7 @@ func TestUpdateTask(t *testing.T) { // to store v and returns a pointer to it. String := func(v string) *string { return &v } - task, err := client.UpdateTask(ctx, 1, TaskUpdate{Notes: String("updated notes")}, nil) + task, err := client.UpdateTask(context.Background(), 1, TaskUpdate{Notes: String("updated notes")}, nil) if err != nil { t.Errorf("UpdateTask returned error: %v", err) } @@ -202,7 +201,7 @@ func TestListTags(t *testing.T) { ]}`) }) - tags, err := client.ListTags(ctx, nil) + tags, err := client.ListTags(context.Background(), nil) if err != nil { t.Errorf("ListTags returned error: %v", err) } @@ -225,7 +224,7 @@ func TestUnauthorized(t *testing.T) { w.WriteHeader(http.StatusUnauthorized) }) - _, err := client.ListTags(ctx, nil) + _, err := client.ListTags(context.Background(), nil) if err != ErrUnauthorized { t.Errorf("Unexpected err %v", err) } @@ -260,7 +259,7 @@ func TestCreateTask(t *testing.T) { fmt.Fprint(w, `{"data":{"id":1,"notes":"updated notes"}}`) }) - task, err := client.CreateTask(ctx, map[string]string{ + task, err := client.CreateTask(context.Background(), map[string]string{ "key1": "value1", "key2": "value2", }, nil) From 84152d587bf460dcc7b634b6d6295b471e9aa4e4 Mon Sep 17 00:00:00 2001 From: Dmitri Shuralyov Date: Fri, 30 Sep 2016 01:46:25 -0700 Subject: [PATCH 13/13] Add missing periods to end of sentences. This is more consistent with the rest of the code. --- asana/asana.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/asana/asana.go b/asana/asana.go index d6fa48c..2afe1f0 100644 --- a/asana/asana.go +++ b/asana/asana.go @@ -31,7 +31,7 @@ var defaultOptFields = map[string][]string{ } var ( - // ErrUnauthorized can be returned on any call on response status code 401 + // ErrUnauthorized can be returned on any call on response status code 401. ErrUnauthorized = errors.New("asana: unauthorized") ) @@ -142,7 +142,7 @@ type ( Message string `json:"message,omitempty"` } - // Errors always has at least 1 element when returned + // Errors always has at least 1 element when returned. Errors []Error )