diff --git a/client/search.go b/client/search.go index bdfe727..71eb070 100644 --- a/client/search.go +++ b/client/search.go @@ -7,6 +7,7 @@ import "strings" import "cloud.google.com/go/civil" type SearchQuery struct { + AssigneeAny []*User SectionsAny []*Section Completed *bool Due *bool @@ -26,11 +27,20 @@ func (wc *WorkspaceClient) Search(q *SearchQuery) ([]*Task, error) { path := fmt.Sprintf("workspaces/%s/tasks/search", wc.workspace.GID) values := &url.Values{ + "is_subtask": []string{"false"}, "sort_by": []string{"created_at"}, "sort_ascending": []string{"true"}, } - values.Add("opt_fields", "created_at,due_on,html_notes,name") + values.Add("opt_fields", "assignee_section,created_at,due_on,html_notes,name") + + if len(q.AssigneeAny) > 0 { + gids := []string{} + for _, u := range q.AssigneeAny { + gids = append(gids, u.GID) + } + values.Add("assignee.any", strings.Join(gids, ",")) + } if len(q.SectionsAny) > 0 { gids := []string{} diff --git a/client/task.go b/client/task.go index 808ae0b..93c0302 100644 --- a/client/task.go +++ b/client/task.go @@ -7,13 +7,18 @@ import "cloud.google.com/go/civil" import "golang.org/x/net/html" type Task struct { - GID string `json:"gid,omitempty"` - Name string `json:"name,omitempty"` - CreatedAt string `json:"created_at,omitempty"` - DueOn string `json:"due_on,omitempty"` - ParsedDueOn *civil.Date `json:"-"` - HTMLNotes string `json:"html_notes,omitempty"` - ParsedHTMLNotes *html.Node `json:"-"` + GID string `json:"gid,omitempty"` + Name string `json:"name,omitempty"` + CreatedAt string `json:"created_at,omitempty"` + DueOn string `json:"due_on,omitempty"` + ParsedDueOn *civil.Date `json:"-"` + HTMLNotes string `json:"html_notes,omitempty"` + ParsedHTMLNotes *html.Node `json:"-"` + AssigneeSection *AssigneeSection `json:"assignee_section"` +} + +type AssigneeSection struct { + GID string `json:"gid,omitempty"` } type taskResponse struct { diff --git a/headers/headers.go b/headers/headers.go index 8967a5c..4733805 100644 --- a/headers/headers.go +++ b/headers/headers.go @@ -3,8 +3,8 @@ package headers import "net/http" type Headers struct { - header http.Header - rt http.RoundTripper + header http.Header + rt http.RoundTripper } func NewHeaders(c *http.Client) Headers { @@ -12,25 +12,25 @@ func NewHeaders(c *http.Client) Headers { c.Transport = http.DefaultTransport } - ret := Headers{ - header: http.Header{}, - rt: c.Transport, - } + ret := Headers{ + header: http.Header{}, + rt: c.Transport, + } - c.Transport = ret + c.Transport = ret - return ret + return ret } func (h *Headers) Add(key, value string) { - h.header.Add(key, value) + h.header.Add(key, value) } func (h Headers) RoundTrip(req *http.Request) (*http.Response, error) { for key, vals := range h.header { - for _, val := range vals { - req.Header.Add(key, val) - } + for _, val := range vals { + req.Header.Add(key, val) + } } return h.rt.RoundTrip(req) diff --git a/rules/rules.go b/rules/rules.go index 8b096a3..37c716d 100644 --- a/rules/rules.go +++ b/rules/rules.go @@ -12,7 +12,7 @@ import "golang.org/x/net/html/atom" type queryMutator func(*client.WorkspaceClient, *client.SearchQuery) error type taskActor func(*client.WorkspaceClient, *client.Task) error -type taskFilter func(*client.WorkspaceClient, *client.Task) (bool, error) +type taskFilter func(*client.WorkspaceClient, *client.SearchQuery, *client.Task) (bool, error) type workspaceClientGetter func(*client.Client) (*client.WorkspaceClient, error) type periodic struct { @@ -54,6 +54,13 @@ func InWorkspace(name string) *periodic { // Query mutators func (p *periodic) InMyTasksSections(names ...string) *periodic { p.queryMutators = append(p.queryMutators, func(wc *client.WorkspaceClient, q *client.SearchQuery) error { + u, err := wc.GetMe() + if err != nil { + return err + } + + q.AssigneeAny = append(q.AssigneeAny, u) + utl, err := wc.GetMyUserTaskList() if err != nil { return err @@ -76,6 +83,18 @@ func (p *periodic) InMyTasksSections(names ...string) *periodic { return nil }) + // Backup filter if the API misbehaves + // Asana issue #600801 + p.taskFilters = append(p.taskFilters, func(wc *client.WorkspaceClient, q *client.SearchQuery, t *client.Task) (bool, error) { + for _, sec := range q.SectionsAny { + if sec.GID == t.AssigneeSection.GID { + return true, nil + } + } + + return false, nil + }) + return p } @@ -204,7 +223,7 @@ func (p *periodic) WithoutTagsAnyOf(names ...string) *periodic { // Task filters func (p *periodic) WithUnlinkedURL() *periodic { - p.taskFilters = append(p.taskFilters, func(wc *client.WorkspaceClient, t *client.Task) (bool, error) { + p.taskFilters = append(p.taskFilters, func(wc *client.WorkspaceClient, _ *client.SearchQuery, t *client.Task) (bool, error) { return hasUnlinkedURL(t.ParsedHTMLNotes), nil }) @@ -213,7 +232,7 @@ func (p *periodic) WithUnlinkedURL() *periodic { func (p *periodic) WithoutDue() *periodic { // We can't mutate the query because due_on=null is buggy in the Asana API - p.taskFilters = append(p.taskFilters, func(wc *client.WorkspaceClient, t *client.Task) (bool, error) { + p.taskFilters = append(p.taskFilters, func(wc *client.WorkspaceClient, _ *client.SearchQuery, t *client.Task) (bool, error) { return t.ParsedDueOn == nil, nil }) @@ -327,7 +346,7 @@ func (p *periodic) exec(c *client.Client) error { included := true for _, filter := range p.taskFilters { - include, err := filter(wc, task) + include, err := filter(wc, q, task) if err != nil { return err }