diff --git a/asana.go b/asana.go index e42cd07..b5f321f 100644 --- a/asana.go +++ b/asana.go @@ -1,5 +1,5 @@ // Package asana provides a client for the Asana API -package asana // import "bitbucket.org/mikehouston/asana-go" +package asana import ( "bytes" diff --git a/attachments.go b/attachments.go index fc2c17e..4b687b2 100644 --- a/attachments.go +++ b/attachments.go @@ -2,15 +2,18 @@ package asana import ( "fmt" - "github.com/pkg/errors" "io" "time" + + "github.com/pkg/errors" ) // Attachment represents any file attached to a task in Asana, // whether it’s an uploaded file or one associated via a third-party service // such as Dropbox or Google Drive. type Attachment struct { + client *Client + // Read-only. Globally unique ID of the object ID string `json:"gid,omitempty"` @@ -45,13 +48,16 @@ type Attachment struct { } // Attachments lists all attachments attached to a task -func (t *Task) Attachments(client *Client, opts ...*Options) ([]*Attachment, *NextPage, error) { - client.trace("Listing attachments for %q", t.Name) +func (t *Task) Attachments(opts ...*Options) ([]*Attachment, *NextPage, error) { + t.client.trace("Listing attachments for %q", t.Name) var result []*Attachment // Make the request - nextPage, err := client.get(fmt.Sprintf("/tasks/%s/attachments", t.ID), nil, &result, opts...) + nextPage, err := t.client.get(fmt.Sprintf("/tasks/%s/attachments", t.ID), nil, &result, opts...) + for _, r := range result { + r.client = t.client + } return result, nextPage, err } @@ -61,13 +67,14 @@ type NewAttachment struct { ContentType string } -func (t *Task) CreateAttachment(client *Client, request *NewAttachment) (*Attachment, error) { - client.trace("Uploading attachment for %q", t.Name) +func (t *Task) CreateAttachment(request *NewAttachment) (*Attachment, error) { + t.client.trace("Uploading attachment for %q", t.Name) result := &Attachment{} - err := client.postMultipart(fmt.Sprintf("/tasks/%s/attachments", t.ID), result, "file", request.Reader, request.FileName, request.ContentType) + err := t.client.postMultipart(fmt.Sprintf("/tasks/%s/attachments", t.ID), result, "file", request.Reader, request.FileName, request.ContentType) if err != nil { return nil, errors.Wrap(err, "Upload attachment") } + result.client = t.client return result, nil } diff --git a/cmd/asana/list.go b/cmd/asana/list.go index 29958fc..8e046c6 100644 --- a/cmd/asana/list.go +++ b/cmd/asana/list.go @@ -24,9 +24,9 @@ func ListWorkspaces(c *asana.Client) error { return nil } -func ListProjects(client *asana.Client, w *asana.Workspace) error { +func ListProjects(w *asana.Workspace) error { // List projects - projects, err := w.AllProjects(client, &asana.Options{ + projects, err := w.AllProjects(&asana.Options{ Fields: []string{"name", "section_migration_status", "layout"}, }) if err != nil { @@ -39,9 +39,9 @@ func ListProjects(client *asana.Client, w *asana.Workspace) error { return nil } -func ListTasks(client *asana.Client, p *asana.Project) error { +func ListTasks(p *asana.Project) error { // List projects - tasks, nextPage, err := p.Tasks(client, asana.Fields(asana.Task{})) + tasks, nextPage, err := p.Tasks(asana.Fields(asana.Task{})) if err != nil { return err } @@ -53,9 +53,9 @@ func ListTasks(client *asana.Client, p *asana.Project) error { return nil } -func ListSections(client *asana.Client, p *asana.Project) error { +func ListSections(p *asana.Project) error { // List sections - sections, nextPage, err := p.Sections(client) + sections, nextPage, err := p.Sections() if err != nil { return err } diff --git a/cmd/asana/main.go b/cmd/asana/main.go index 541b42f..4608151 100644 --- a/cmd/asana/main.go +++ b/cmd/asana/main.go @@ -2,7 +2,6 @@ package main import ( "fmt" - "github.com/jessevdk/go-flags" "log" "mime" "net/http" @@ -11,6 +10,7 @@ import ( "path/filepath" "bitbucket.org/mikehouston/asana-go" + "github.com/jessevdk/go-flags" ) var options struct { @@ -70,7 +70,7 @@ func main() { for _, w := range options.Workspace { workspace := &asana.Workspace{ID: w} - check(ListProjects(client, workspace)) + check(ListProjects(workspace)) } return } @@ -83,12 +83,12 @@ func main() { Name: options.AddSection, } - _, err := project.CreateSection(client, request) + _, err := project.CreateSection(request) check(err) return } - fmtProject(client, project) + fmtProject(project) } return } @@ -99,22 +99,22 @@ func main() { fmt.Printf("Task %s: %q\n", task.ID, task.Name) if options.Attach != "" { - addAttachment(task, client) + addAttachment(task) return } - fmtTask(task, client) + fmtTask(task) } } -func fmtProject(client *asana.Client, project *asana.Project) { +func fmtProject(project *asana.Project) { fmt.Println("\nSections:") - check(ListSections(client, project)) + check(ListSections(project)) fmt.Println("\nTasks:") - check(ListTasks(client, project)) + check(ListTasks(project)) } -func fmtTask(task *asana.Task, client *asana.Client) { +func fmtTask(task *asana.Task) { fmt.Printf(" Completed: %v\n", task.Completed) if task.Completed != nil && !*task.Completed { fmt.Printf(" Due: %s\n", task.DueAt) @@ -123,7 +123,7 @@ func fmtTask(task *asana.Task, client *asana.Client) { fmt.Printf(" Notes: %q\n", task.Notes) } // Get subtasks - subtasks, nextPage, err := task.Subtasks(client) + subtasks, nextPage, err := task.Subtasks() check(err) _ = nextPage for _, subtask := range subtasks { @@ -131,11 +131,11 @@ func fmtTask(task *asana.Task, client *asana.Client) { } } -func addAttachment(task *asana.Task, client *asana.Client) { +func addAttachment(task *asana.Task) { f, err := os.Open(options.Attach) check(err) defer f.Close() - a, err := task.CreateAttachment(client, &asana.NewAttachment{ + a, err := task.CreateAttachment(&asana.NewAttachment{ Reader: f, FileName: f.Name(), ContentType: mime.TypeByExtension(filepath.Ext(f.Name())), diff --git a/customfields.go b/customfields.go index ce1189c..e2a611c 100644 --- a/customfields.go +++ b/customfields.go @@ -100,6 +100,7 @@ type CustomFieldBase struct { // Users in Asana can lock custom fields, which will make them read-only when accessed by other users. // Attempting to edit a locked custom field will return HTTP error code 403 Forbidden. type CustomField struct { + client *Client // Read-only. Globally unique ID of the object ID string `json:"gid,omitempty"` @@ -128,8 +129,8 @@ type AddCustomFieldSettingRequest struct { InsertAfter string `json:"insert_after,omitempty"` } -func (p *Project) AddCustomFieldSetting(client *Client, request *AddCustomFieldSettingRequest) (*CustomFieldSetting, error) { - client.trace("Attach custom field %q to project %q", request.CustomField, p.ID) +func (p *Project) AddCustomFieldSetting(request *AddCustomFieldSettingRequest) (*CustomFieldSetting, error) { + p.client.trace("Attach custom field %q to project %q", request.CustomField, p.ID) // Custom request encoding m := map[string]interface{}{} @@ -149,19 +150,20 @@ func (p *Project) AddCustomFieldSetting(client *Client, request *AddCustomFieldS } result := &CustomFieldSetting{} - err := client.post(fmt.Sprintf("/projects/%s/addCustomFieldSetting", p.ID), m, result) + err := p.client.post(fmt.Sprintf("/projects/%s/addCustomFieldSetting", p.ID), m, result) + result.CustomField.client = p.client return result, err } -func (p *Project) RemoveCustomFieldSetting(client *Client, customFieldID string) error { - client.trace("Remove custom field %q from project %q", customFieldID, p.ID) +func (p *Project) RemoveCustomFieldSetting(customFieldID string) error { + p.client.trace("Remove custom field %q from project %q", customFieldID, p.ID) // Custom request encoding m := map[string]interface{}{ "custom_field": customFieldID, } - err := client.post(fmt.Sprintf("/projects/%s/removeCustomFieldSetting", p.ID), m, &json.RawMessage{}) + err := p.client.post(fmt.Sprintf("/projects/%s/removeCustomFieldSetting", p.ID), m, &json.RawMessage{}) return err } @@ -181,6 +183,7 @@ func (c *Client) CreateCustomField(request *CreateCustomFieldRequest) (*CustomFi result := &CustomField{} err := c.post("/custom_fields", request, result) + result.client = c return result, err } @@ -208,25 +211,28 @@ type CustomFieldValue struct { } // Fetch loads the full details for this CustomField -func (f *CustomField) Fetch(client *Client, options ...*Options) error { - client.trace("Loading details for custom field %q", f.ID) +func (f *CustomField) Fetch(options ...*Options) error { + f.client.trace("Loading details for custom field %q", f.ID) - _, err := client.get(fmt.Sprintf("/custom_fields/%s", f.ID), nil, f, options...) + _, err := f.client.get(fmt.Sprintf("/custom_fields/%s", f.ID), nil, f, options...) return err } // CustomFields returns the compact records for all custom fields in the workspace -func (w *Workspace) CustomFields(client *Client, options ...*Options) ([]*CustomField, *NextPage, error) { - client.trace("Listing custom fields in workspace %s...\n", w.ID) +func (w *Workspace) CustomFields(options ...*Options) ([]*CustomField, *NextPage, error) { + w.client.trace("Listing custom fields in workspace %s...\n", w.ID) var result []*CustomField // Make the request - nextPage, err := client.get(fmt.Sprintf("/workspaces/%s/custom_fields", w.ID), nil, &result, options...) + nextPage, err := w.client.get(fmt.Sprintf("/workspaces/%s/custom_fields", w.ID), nil, &result, options...) + for _, r := range result { + r.client = w.client + } return result, nextPage, err } // AllCustomFields repeatedly pages through all available custom fields in a workspace -func (w *Workspace) AllCustomFields(client *Client, options ...*Options) ([]*CustomField, error) { +func (w *Workspace) AllCustomFields(options ...*Options) ([]*CustomField, error) { var allCustomFields []*CustomField nextPage := &NextPage{} @@ -240,7 +246,7 @@ func (w *Workspace) AllCustomFields(client *Client, options ...*Options) ([]*Cus } allOptions := append([]*Options{page}, options...) - customFields, nextPage, err = w.CustomFields(client, allOptions...) + customFields, nextPage, err = w.CustomFields(allOptions...) if err != nil { return nil, err } diff --git a/portfolios.go b/portfolios.go index 014575f..465a55d 100644 --- a/portfolios.go +++ b/portfolios.go @@ -1,15 +1,14 @@ package asana -import "fmt" - type Portfolio struct { + client *Client // Read-only. Globally unique ID of the object ID string `json:"gid,omitempty"` } // Projects returns a list of projects in this workspace -func (w *Workspace) Portfolios(client *Client, options ...*Options) ([]*Portfolio, *NextPage, error) { - client.trace("Listing portfolios in %q", w.Name) +func (w *Workspace) Portfolios(options ...*Options) ([]*Portfolio, *NextPage, error) { + w.client.trace("Listing portfolios in %q", w.Name) var result []*Portfolio @@ -19,6 +18,9 @@ func (w *Workspace) Portfolios(client *Client, options ...*Options) ([]*Portfoli } // Make the request - nextPage, err := client.get(fmt.Sprintf("/portfolios"), nil, &result, append(options, o)...) + nextPage, err := w.client.get("/portfolios", nil, &result, append(options, o)...) + for _, r := range result { + r.client = w.client + } return result, nextPage, err } diff --git a/projects.go b/projects.go index 90754f2..1307f26 100644 --- a/projects.go +++ b/projects.go @@ -115,6 +115,8 @@ const ( // project will add them as members if they are not already, removing // followers from a project will not affect membership. type Project struct { + client *Client + // Read-only. Globally unique ID of the object ID string `json:"gid,omitempty"` @@ -165,10 +167,10 @@ type Project struct { } // Fetch loads the full details for this Project -func (p *Project) Fetch(client *Client, opts ...*Options) error { - client.trace("Loading project details for %q", p.Name) +func (p *Project) Fetch(opts ...*Options) error { + p.client.trace("Loading project details for %q", p.Name) - _, err := client.get(fmt.Sprintf("/projects/%s", p.ID), nil, p, opts...) + _, err := p.client.get(fmt.Sprintf("/projects/%s", p.ID), nil, p, opts...) return err } @@ -178,21 +180,24 @@ func (p *Project) Fetch(client *Client, opts ...*Options) error { // or else you may overwrite changes made by another user since you last retrieved the task. // // Updates the referenced project object -func (p *Project) Update(client *Client, request *UpdateProjectRequest, opts ...*Options) error { - client.trace("Update project %q", p.Name) +func (p *Project) Update(request *UpdateProjectRequest, opts ...*Options) error { + p.client.trace("Update project %q", p.Name) - err := client.put(fmt.Sprintf("/projects/%s", p.ID), request, p, opts...) + err := p.client.put(fmt.Sprintf("/projects/%s", p.ID), request, p, opts...) return err } // Projects returns a list of projects in this workspace -func (w *Workspace) Projects(client *Client, options ...*Options) ([]*Project, *NextPage, error) { - client.trace("Listing projects in %q", w.Name) +func (w *Workspace) Projects(options ...*Options) ([]*Project, *NextPage, error) { + w.client.trace("Listing projects in %q", w.Name) var result []*Project // Make the request - nextPage, err := client.get(fmt.Sprintf("/workspaces/%s/projects", w.ID), nil, &result, options...) + nextPage, err := w.client.get(fmt.Sprintf("/workspaces/%s/projects", w.ID), nil, &result, options...) + for _, r := range result { + r.client = w.client + } return result, nextPage, err } @@ -202,8 +207,8 @@ type favoritesRequestParams struct { } // FavoriteProjects returns a list of the current user's favorite projects in this workspace -func (w *Workspace) FavoriteProjects(client *Client, options ...*Options) ([]*Project, *NextPage, error) { - client.trace("Listing favorite projects in %q", w.Name) +func (w *Workspace) FavoriteProjects(options ...*Options) ([]*Project, *NextPage, error) { + w.client.trace("Listing favorite projects in %q", w.Name) var result []*Project @@ -212,16 +217,19 @@ func (w *Workspace) FavoriteProjects(client *Client, options ...*Options) ([]*Pr ResourceType: "project", Workspace: w.ID, } - user, err := client.CurrentUser() + user, err := w.client.CurrentUser() if err != nil { return nil, nil, err } - nextPage, err := client.get(fmt.Sprintf("/users/%s/favorites", user.ID), query, &result, options...) + nextPage, err := w.client.get(fmt.Sprintf("/users/%s/favorites", user.ID), query, &result, options...) + for _, r := range result { + r.client = w.client + } return result, nextPage, err } // AllProjects repeatedly pages through all available projects in a workspace -func (w *Workspace) AllProjects(client *Client, options ...*Options) ([]*Project, error) { +func (w *Workspace) AllProjects(options ...*Options) ([]*Project, error) { var allProjects []*Project nextPage := &NextPage{} @@ -235,7 +243,7 @@ func (w *Workspace) AllProjects(client *Client, options ...*Options) ([]*Project } allOptions := append([]*Options{page}, options...) - projects, nextPage, err = w.Projects(client, allOptions...) + projects, nextPage, err = w.Projects(allOptions...) if err != nil { return nil, err } @@ -246,7 +254,7 @@ func (w *Workspace) AllProjects(client *Client, options ...*Options) ([]*Project } // AllProjects repeatedly pages through all available projects in a workspace -func (w *Workspace) AllFavoriteProjects(client *Client, options ...*Options) ([]*Project, error) { +func (w *Workspace) AllFavoriteProjects(options ...*Options) ([]*Project, error) { var allProjects []*Project nextPage := &NextPage{} @@ -260,7 +268,7 @@ func (w *Workspace) AllFavoriteProjects(client *Client, options ...*Options) ([] } allOptions := append([]*Options{page}, options...) - projects, nextPage, err = w.FavoriteProjects(client, allOptions...) + projects, nextPage, err = w.FavoriteProjects(allOptions...) if err != nil { return nil, err } @@ -271,18 +279,21 @@ func (w *Workspace) AllFavoriteProjects(client *Client, options ...*Options) ([] } // Projects returns a list of projects in this team -func (t *Team) Projects(client *Client, options ...*Options) ([]*Project, *NextPage, error) { - client.trace("Listing projects in %q", t.Name) +func (t *Team) Projects(options ...*Options) ([]*Project, *NextPage, error) { + t.client.trace("Listing projects in %q", t.Name) var result []*Project // Make the request - nextPage, err := client.get(fmt.Sprintf("/teams/%s/projects", t.ID), nil, &result, options...) + nextPage, err := t.client.get(fmt.Sprintf("/teams/%s/projects", t.ID), nil, &result, options...) + for _, r := range result { + r.client = t.client + } return result, nextPage, err } // AllProjects repeatedly pages through all available projects in a team -func (t *Team) AllProjects(client *Client, options ...*Options) ([]*Project, error) { +func (t *Team) AllProjects(options ...*Options) ([]*Project, error) { var allProjects []*Project nextPage := &NextPage{} @@ -296,7 +307,7 @@ func (t *Team) AllProjects(client *Client, options ...*Options) ([]*Project, err } allOptions := append([]*Options{page}, options...) - projects, nextPage, err = t.Projects(client, allOptions...) + projects, nextPage, err = t.Projects(allOptions...) if err != nil { return nil, err } @@ -323,5 +334,6 @@ func (t *Team) CreateProject(c *Client, project *CreateProjectRequest) (*Project result := &Project{} err := c.post(fmt.Sprintf("/teams/%s/projects", t.ID), project, result) + result.client = c return result, err } diff --git a/sections.go b/sections.go index df816de..300d525 100644 --- a/sections.go +++ b/sections.go @@ -19,6 +19,8 @@ type SectionBase struct { // The ‘memberships’ property when getting a task will return the information for the // section or the column under ‘section’ in the response. type Section struct { + client *Client + // Read-only. Globally unique ID of the object ID string `json:"gid,omitempty"` @@ -47,22 +49,26 @@ func (s *Section) Delete(client *Client) error { } // Sections returns a list of sections in this project -func (p *Project) Sections(client *Client, opts ...*Options) ([]*Section, *NextPage, error) { - client.trace("Listing sections in %q", p.Name) +func (p *Project) Sections(opts ...*Options) ([]*Section, *NextPage, error) { + p.client.trace("Listing sections in %q", p.Name) var result []*Section // Make the request - nextPage, err := client.get(fmt.Sprintf("/projects/%s/sections", p.ID), nil, &result, opts...) + nextPage, err := p.client.get(fmt.Sprintf("/projects/%s/sections", p.ID), nil, &result, opts...) + for _, r := range result { + r.client = p.client + } return result, nextPage, err } // CreateSection creates a new section in the given project -func (p *Project) CreateSection(client *Client, section *SectionBase) (*Section, error) { - client.info("Creating section %q", section.Name) +func (p *Project) CreateSection(section *SectionBase) (*Section, error) { + p.client.info("Creating section %q", section.Name) result := &Section{} - err := client.post(fmt.Sprintf("/projects/%s/sections", p.ID), section, result) + err := p.client.post(fmt.Sprintf("/projects/%s/sections", p.ID), section, result) + result.client = p.client return result, err } @@ -79,9 +85,9 @@ type SectionInsertRequest struct { // Sections cannot be moved between projects. // // At this point in time, moving sections is not supported in list views, only board views. -func (p *Project) InsertSection(client *Client, request *SectionInsertRequest) error { - client.info("Moving section %s", request.Section) +func (p *Project) InsertSection(request *SectionInsertRequest) error { + p.client.info("Moving section %s", request.Section) - err := client.post(fmt.Sprintf("projects/%s/sections/insert", p.ID), request, nil) + err := p.client.post(fmt.Sprintf("projects/%s/sections/insert", p.ID), request, nil) return err } diff --git a/stories.go b/stories.go index a65cd15..65e5715 100644 --- a/stories.go +++ b/stories.go @@ -109,6 +109,8 @@ type StorySubtypeFields struct { // Stories are a form of history in the system, and as such they are read- // only. Once generated, it is not possible to modify a story. type Story struct { + client *Client + // Read-only. Globally unique ID of the object ID string `json:"gid,omitempty"` @@ -150,34 +152,39 @@ type Story struct { } // Stories lists all stories attached to a task -func (t *Task) Stories(client *Client, opts ...*Options) ([]*Story, *NextPage, error) { - client.trace("Listing stories for %q", t.Name) +func (t *Task) Stories(opts ...*Options) ([]*Story, *NextPage, error) { + t.client.trace("Listing stories for %q", t.Name) var result []*Story // Make the request - nextPage, err := client.get(fmt.Sprintf("/tasks/%s/stories", t.ID), nil, &result, opts...) + nextPage, err := t.client.get(fmt.Sprintf("/tasks/%s/stories", t.ID), nil, &result, opts...) + for _, r := range result { + r.client = t.client + } return result, nextPage, err } // CreateComment adds a comment story to a task -func (t *Task) CreateComment(client *Client, story *StoryBase) (*Story, error) { - client.info("Creating comment for task %q", t.Name) +func (t *Task) CreateComment(story *StoryBase) (*Story, error) { + t.client.info("Creating comment for task %q", t.Name) result := &Story{} - err := client.post(fmt.Sprintf("/tasks/%s/stories", t.ID), story, result) + err := t.client.post(fmt.Sprintf("/tasks/%s/stories", t.ID), story, result) + result.client = t.client return result, err } // UpdateStory updates the story and returns the full record for the updated story. // Only comment stories can have their text updated, and only comment stories and attachment stories can be pinned. // Only one of text and html_text can be specified. -func (s *Story) UpdateStory(client *Client, story *StoryBase) (*Story, error) { - client.info("Updating story %s", s.ID) +func (s *Story) UpdateStory(story *StoryBase) (*Story, error) { + s.client.info("Updating story %s", s.ID) result := &Story{} - err := client.put(fmt.Sprintf("/stories/%s", s.ID), nil, result) + err := s.client.put(fmt.Sprintf("/stories/%s", s.ID), nil, result) + result.client = s.client return result, err } diff --git a/tags.go b/tags.go index dc5ec8c..c0325d1 100644 --- a/tags.go +++ b/tags.go @@ -29,6 +29,8 @@ type TagBase struct { // heavily on it. Unlike projects, tags do not provide any ordering on the // tasks they are associated with. type Tag struct { + client *Client + // Read-only. Globally unique ID of the object ID string `json:"gid,omitempty"` @@ -50,26 +52,29 @@ type Tag struct { } // Fetch loads the full details for this Tag -func (t *Tag) Fetch(client *Client, options ...*Options) error { - client.trace("Loading details for tag %q", t.Name) +func (t *Tag) Fetch(options ...*Options) error { + t.client.trace("Loading details for tag %q", t.Name) - _, err := client.get(fmt.Sprintf("/tags/%s", t.ID), nil, t, options...) + _, err := t.client.get(fmt.Sprintf("/tags/%s", t.ID), nil, t, options...) return err } // Tags returns a list of tags in this workspace -func (w *Workspace) Tags(client *Client, options ...*Options) ([]*Tag, *NextPage, error) { - client.trace("Listing tags in %q", w.Name) +func (w *Workspace) Tags(options ...*Options) ([]*Tag, *NextPage, error) { + w.client.trace("Listing tags in %q", w.Name) var result []*Tag // Make the request - nextPage, err := client.get(fmt.Sprintf("/workspaces/%s/tags", w.ID), nil, &result, options...) + nextPage, err := w.client.get(fmt.Sprintf("/workspaces/%s/tags", w.ID), nil, &result, options...) + for _, r := range result { + r.client = w.client + } return result, nextPage, err } // AllTags repeatedly pages through all available tags in a workspace -func (w *Workspace) AllTags(client *Client, options ...*Options) ([]*Tag, error) { +func (w *Workspace) AllTags(options ...*Options) ([]*Tag, error) { allTags := []*Tag{} nextPage := &NextPage{} @@ -83,7 +88,7 @@ func (w *Workspace) AllTags(client *Client, options ...*Options) ([]*Tag, error) } allOptions := append([]*Options{page}, options...) - tags, nextPage, err = w.Tags(client, allOptions...) + tags, nextPage, err = w.Tags(allOptions...) if err != nil { return nil, err } @@ -94,15 +99,16 @@ func (w *Workspace) AllTags(client *Client, options ...*Options) ([]*Tag, error) } // CreateTag adds a new tag to a workspace -func (w *Workspace) CreateTag(client *Client, tag *TagBase) (*Tag, error) { - client.info("Creating tag %q in %q\n", tag.Name, w.Name) +func (w *Workspace) CreateTag(tag *TagBase) (*Tag, error) { + w.client.info("Creating tag %q in %q\n", tag.Name, w.Name) result := &Tag{} - err := client.post(fmt.Sprintf("/workspaces/%s/tags", w.ID), tag, result) + err := w.client.post(fmt.Sprintf("/workspaces/%s/tags", w.ID), tag, result) if err != nil { return nil, err } + result.client = w.client return result, nil } diff --git a/tasks.go b/tasks.go index cc6da52..a5127bc 100644 --- a/tasks.go +++ b/tasks.go @@ -195,6 +195,8 @@ type UpdateTaskRequest struct { // the fields? Use field selectors to manipulate what data is included in a // response. type Task struct { + client *Client + // Read-only. Globally unique ID of the object ID string `json:"gid,omitempty"` @@ -294,10 +296,10 @@ func (t *Task) Fetch(client *Client) error { } // Update applies new values to a Task record -func (t *Task) Update(client *Client, update *UpdateTaskRequest) error { - client.trace("Updating task %q", t.Name) +func (t *Task) Update(update *UpdateTaskRequest) error { + t.client.trace("Updating task %q", t.Name) - err := client.put(fmt.Sprintf("/tasks/%s", t.ID), update, t) + err := t.client.put(fmt.Sprintf("/tasks/%s", t.ID), update, t) return err } @@ -316,8 +318,8 @@ type AddProjectRequest struct { } // AddProject adds this task to an existing project at the provided location -func (t *Task) AddProject(client *Client, request *AddProjectRequest) error { - client.trace("Adding task %q to project %q", t.ID, request.Project) +func (t *Task) AddProject(request *AddProjectRequest) error { + t.client.trace("Adding task %q to project %q", t.ID, request.Project) // Custom encoding of Insert fields needed m := map[string]interface{}{ @@ -340,19 +342,19 @@ func (t *Task) AddProject(client *Client, request *AddProjectRequest) error { m["section"] = request.Section } - err := client.post(fmt.Sprintf("/tasks/%s/addProject", t.ID), m, &json.RawMessage{}) + err := t.client.post(fmt.Sprintf("/tasks/%s/addProject", t.ID), m, &json.RawMessage{}) return err } -func (t *Task) RemoveProject(client *Client, projectID string) error { - client.trace("Removing task %q from project %q", t.ID, projectID) +func (t *Task) RemoveProject(projectID string) error { + t.client.trace("Removing task %q from project %q", t.ID, projectID) // Custom encoding of Insert fields needed m := map[string]interface{}{ "project": projectID, } - err := client.post(fmt.Sprintf("/tasks/%s/removeProject", t.ID), m, &json.RawMessage{}) + err := t.client.post(fmt.Sprintf("/tasks/%s/removeProject", t.ID), m, &json.RawMessage{}) return err } @@ -365,8 +367,8 @@ type SetParentRequest struct { } // SetParent changes the parent of a task -func (t *Task) SetParent(client *Client, request *SetParentRequest) error { - client.trace("Setting the parent of task %q to %q", t.ID, request.Parent) +func (t *Task) SetParent(request *SetParentRequest) error { + t.client.trace("Setting the parent of task %q to %q", t.ID, request.Parent) // Custom encoding of Insert fields needed m := map[string]interface{}{ @@ -383,7 +385,7 @@ func (t *Task) SetParent(client *Client, request *SetParentRequest) error { m["insert_before"] = request.InsertBefore } - err := client.post(fmt.Sprintf("/tasks/%s/setParent", t.ID), m, &json.RawMessage{}) + err := t.client.post(fmt.Sprintf("/tasks/%s/setParent", t.ID), m, &json.RawMessage{}) return err } @@ -395,10 +397,10 @@ type AddDependenciesRequest struct { // AddDependencies marks a set of tasks as dependencies of this task, if they // are not already dependencies. A task can have at most 15 dependencies. -func (t *Task) AddDependencies(client *Client, request *AddDependenciesRequest) error { - client.trace("Adding dependencies to task %q", t.ID) +func (t *Task) AddDependencies(request *AddDependenciesRequest) error { + t.client.trace("Adding dependencies to task %q", t.ID) - err := client.post(fmt.Sprintf("/tasks/%s/addDependencies", t.ID), request, &json.RawMessage{}) + err := t.client.post(fmt.Sprintf("/tasks/%s/addDependencies", t.ID), request, &json.RawMessage{}) return err } @@ -410,41 +412,50 @@ type AddDependentsRequest struct { // AddDependents marks a set of tasks as dependents of this task, if they // are not already dependents. A task can have at most 30 dependents. -func (t *Task) AddDependents(client *Client, request *AddDependentsRequest) error { - client.trace("Adding dependents to task %q", t.ID) +func (t *Task) AddDependents(request *AddDependentsRequest) error { + t.client.trace("Adding dependents to task %q", t.ID) - err := client.post(fmt.Sprintf("/tasks/%s/addDependents", t.ID), request, &json.RawMessage{}) + err := t.client.post(fmt.Sprintf("/tasks/%s/addDependents", t.ID), request, &json.RawMessage{}) return err } // Tasks returns a list of tasks in this project -func (p *Project) Tasks(client *Client, opts ...*Options) ([]*Task, *NextPage, error) { - client.trace("Listing tasks in %q", p.Name) +func (p *Project) Tasks(opts ...*Options) ([]*Task, *NextPage, error) { + p.client.trace("Listing tasks in %q", p.Name) var result []*Task // Make the request - nextPage, err := client.get(fmt.Sprintf("/projects/%s/tasks", p.ID), nil, &result, opts...) + nextPage, err := p.client.get(fmt.Sprintf("/projects/%s/tasks", p.ID), nil, &result, opts...) + for _, r := range result { + r.client = p.client + } return result, nextPage, err } // Tasks returns a list of tasks in this section. Board view only. -func (s *Section) Tasks(client *Client, opts ...*Options) ([]*Task, *NextPage, error) { - client.trace("Listing tasks in %q", s.Name) +func (s *Section) Tasks(opts ...*Options) ([]*Task, *NextPage, error) { + s.client.trace("Listing tasks in %q", s.Name) var result []*Task // Make the request - nextPage, err := client.get(fmt.Sprintf("/sections/%s/tasks", s.ID), nil, &result, opts...) + nextPage, err := s.client.get(fmt.Sprintf("/sections/%s/tasks", s.ID), nil, &result, opts...) + for _, r := range result { + r.client = s.client + } return result, nextPage, err } // Subtasks returns a list of tasks in this project -func (t *Task) Subtasks(client *Client, opts ...*Options) ([]*Task, *NextPage, error) { - client.trace("Listing subtasks for %q", t.Name) +func (t *Task) Subtasks(opts ...*Options) ([]*Task, *NextPage, error) { + t.client.trace("Listing subtasks for %q", t.Name) var result []*Task // Make the request - nextPage, err := client.get(fmt.Sprintf("/tasks/%s/subtasks", t.ID), nil, &result, opts...) + nextPage, err := t.client.get(fmt.Sprintf("/tasks/%s/subtasks", t.ID), nil, &result, opts...) + for _, r := range result { + r.client = t.client + } return result, nextPage, err } @@ -455,16 +466,18 @@ func (c *Client) CreateTask(task *CreateTaskRequest) (*Task, error) { result := &Task{} err := c.post("/tasks", task, result) + result.client = c return result, err } // CreateSubtask creates a new task as a subtask of this task -func (t *Task) CreateSubtask(client *Client, task *Task) (*Task, error) { - client.info("Creating subtask %q", task.Name) +func (t *Task) CreateSubtask(task *Task) (*Task, error) { + t.client.info("Creating subtask %q", task.Name) result := &Task{} - err := client.post(fmt.Sprintf("/tasks/%s/subtasks", t.ID), task, result) + err := t.client.post(fmt.Sprintf("/tasks/%s/subtasks", t.ID), task, result) + result.client = t.client return result, err } @@ -475,5 +488,8 @@ func (c *Client) QueryTasks(query *TaskQuery, opts ...*Options) ([]*Task, *NextP var result []*Task nextPage, err := c.get("/tasks", query, &result, opts...) + for _, r := range result { + r.client = c + } return result, nextPage, err } diff --git a/teams.go b/teams.go index 92a2ba8..47d94f3 100644 --- a/teams.go +++ b/teams.go @@ -7,6 +7,7 @@ import ( // Team is used to group related projects and people together within an // organization. Each project in an organization is associated with a team. type Team struct { + client *Client // Read-only. Globally unique ID of the object ID string `json:"gid,omitempty"` @@ -33,17 +34,20 @@ func (t *Team) Fetch(client *Client) error { } // Teams returns the compact records for all teams in the organization visible to the authorized user -func (w *Workspace) Teams(client *Client, options ...*Options) ([]*Team, *NextPage, error) { - client.trace("Listing teams in workspace %s...\n", w.ID) +func (w *Workspace) Teams(options ...*Options) ([]*Team, *NextPage, error) { + w.client.trace("Listing teams in workspace %s...\n", w.ID) var result []*Team // Make the request - nextPage, err := client.get(fmt.Sprintf("/organizations/%s/teams", w.ID), nil, &result, options...) + nextPage, err := w.client.get(fmt.Sprintf("/organizations/%s/teams", w.ID), nil, &result, options...) + for _, r := range result { + r.client = w.client + } return result, nextPage, err } // AllTeams repeatedly pages through all available teams in a workspace -func (w *Workspace) AllTeams(client *Client, options ...*Options) ([]*Team, error) { +func (w *Workspace) AllTeams(options ...*Options) ([]*Team, error) { var allTeams []*Team nextPage := &NextPage{} @@ -57,7 +61,7 @@ func (w *Workspace) AllTeams(client *Client, options ...*Options) ([]*Team, erro } allOptions := append([]*Options{page}, options...) - teams, nextPage, err = w.Teams(client, allOptions...) + teams, nextPage, err = w.Teams(allOptions...) if err != nil { return nil, err } diff --git a/users.go b/users.go index 91c8fa6..7524f4c 100644 --- a/users.go +++ b/users.go @@ -1,6 +1,8 @@ package asana -import "fmt" +import ( + "fmt" +) // User represents an account in Asana that can be given access to various // workspaces, projects, and tasks. @@ -9,6 +11,7 @@ import "fmt" // However, the special string identifier me can be used anywhere a user ID is // accepted, to refer to the current authenticated user. type User struct { + client *Client // Read-only. Globally unique ID of the object ID string `json:"gid,omitempty"` @@ -41,26 +44,29 @@ func (c *Client) CurrentUser() (*User, error) { } // Fetch loads the full details for this User -func (u *User) Fetch(client *Client, options ...*Options) error { - client.trace("Loading details for user %q", u.ID) +func (u *User) Fetch(options ...*Options) error { + u.client.trace("Loading details for user %q", u.ID) - _, err := client.get(fmt.Sprintf("/users/%s", u.ID), nil, u, options...) + _, err := u.client.get(fmt.Sprintf("/users/%s", u.ID), nil, u, options...) return err } // Users returns the compact records for all users in the organization visible to the authorized user -func (w *Workspace) Users(client *Client, options ...*Options) ([]*User, *NextPage, error) { - client.trace("Listing users in workspace %s...\n", w.ID) +func (w *Workspace) Users(options ...*Options) ([]*User, *NextPage, error) { + w.client.trace("Listing users in workspace %s...\n", w.ID) var result []*User // Make the request - queryOptions := append([]*Options{&Options{Workspace: w.ID}}, options...) - nextPage, err := client.get("/users", nil, &result, queryOptions...) + queryOptions := append([]*Options{{Workspace: w.ID}}, options...) + nextPage, err := w.client.get("/users", nil, &result, queryOptions...) + for _, r := range result { + r.client = w.client + } return result, nextPage, err } // AllUsers repeatedly pages through all available users in a workspace -func (w *Workspace) AllUsers(client *Client, options ...*Options) ([]*User, error) { +func (w *Workspace) AllUsers(options ...*Options) ([]*User, error) { var allUsers []*User nextPage := &NextPage{} @@ -74,7 +80,7 @@ func (w *Workspace) AllUsers(client *Client, options ...*Options) ([]*User, erro } allOptions := append([]*Options{page}, options...) - users, nextPage, err = w.Users(client, allOptions...) + users, nextPage, err = w.Users(allOptions...) if err != nil { return nil, err } diff --git a/workspaces.go b/workspaces.go index d80769f..d5c1249 100644 --- a/workspaces.go +++ b/workspaces.go @@ -19,6 +19,7 @@ import ( // announcements, you can still reference organizations in any workspace // parameter. type Workspace struct { + client *Client // Read-only. Globally unique ID of the object ID string `json:"gid,omitempty"` @@ -49,12 +50,15 @@ func (c *Client) Workspaces(options ...*Options) ([]*Workspace, *NextPage, error // Make the request nextPage, err := c.get("/workspaces", nil, &result, options...) + for _, r := range result { + r.client = c + } return result, nextPage, err } // AllWorkspaces repeatedly pages through all available workspaces for a client func (c *Client) AllWorkspaces(options ...*Options) ([]*Workspace, error) { - allWorkspaces := []*Workspace{} + var allWorkspaces []*Workspace nextPage := &NextPage{} var workspaces []*Workspace