diff --git a/issue.go b/issue.go index f69373d..2a98747 100644 --- a/issue.go +++ b/issue.go @@ -225,11 +225,20 @@ type Priority struct { ID string `json:"id,omitempty" structs:"id,omitempty"` } -// Watches represents a type of how many user are "observing" a JIRA issue to track the status / updates. +// Watches represents a type of how many and which user are "observing" a JIRA issue to track the status / updates. type Watches struct { - Self string `json:"self,omitempty" structs:"self,omitempty"` - WatchCount int `json:"watchCount,omitempty" structs:"watchCount,omitempty"` - IsWatching bool `json:"isWatching,omitempty" structs:"isWatching,omitempty"` + Self string `json:"self,omitempty" structs:"self,omitempty"` + WatchCount int `json:"watchCount,omitempty" structs:"watchCount,omitempty"` + IsWatching bool `json:"isWatching,omitempty" structs:"isWatching,omitempty"` + Watchers []*Watcher `json:"watchers,omitempty" structs:"watchers,omitempty"` +} + +// Watcher represents a simplified user that "observes" the issue +type Watcher struct { + Self string `json:"self,omitempty" structs:"self,omitempty"` + Name string `json:"name,omitempty" structs:"name,omitempty"` + DisplayName string `json:"displayName,omitempty" structs:"displayName,omitempty"` + Active bool `json:"active,omitempty" structs:"active,omitempty"` } // AvatarUrls represents different dimensions of avatars / images @@ -960,3 +969,71 @@ func (s *IssueService) Delete(issueID string) (*Response, error) { resp, err := s.client.Do(req, nil) return resp, err } + +// GetWatchers wil return all the users watching/observing the given issue +// +// JIRA API docs: https://docs.atlassian.com/software/jira/docs/api/REST/latest/#api/2/issue-getIssueWatchers +func (s *IssueService) GetWatchers(issueID string) (*[]User, *Response, error) { + watchesApiEndPoint := fmt.Sprintf("rest/api/2/issue/%s/watchers", issueID) + + req, err := s.client.NewRequest("GET", watchesApiEndPoint, nil) + if err != nil { + return nil, nil, err + } + + watches := new(Watches) + resp, err := s.client.Do(req, watches) + if err != nil { + return nil, nil, NewJiraError(resp, err) + } + + result := []User{} + user := new(User) + for _, watcher := range watches.Watchers { + user, resp, err = s.client.User.Get(watcher.Name) + if err != nil { + return nil, resp, NewJiraError(resp, err) + } + result = append(result, *user) + } + + return &result, resp, nil +} + +// SetWatcher adds watcher to the given issue +// +// JIRA API docs: https://docs.atlassian.com/software/jira/docs/api/REST/latest/#api/2/issue-addWatcher +func (s *IssueService) AddWatcher(issueID string, userName string) (*Response, error) { + apiEndPoint := fmt.Sprintf("rest/api/2/issue/%s/watchers", issueID) + + req, err := s.client.NewRequest("POST", apiEndPoint, userName) + if err != nil { + return nil, err + } + + resp, err := s.client.Do(req, nil) + if err != nil { + err = NewJiraError(resp, err) + } + + return resp, err +} + +// RemoveWatcher removes given user from given issue +// +// JIRA API docs: https://docs.atlassian.com/software/jira/docs/api/REST/latest/#api/2/issue-removeWatcher +func (s *IssueService) RemoveWatcher(issueID string, userName string) (*Response, error) { + apiEndPoint := fmt.Sprintf("rest/api/2/issue/%s/watchers", issueID) + + req, err := s.client.NewRequest("DELETE", apiEndPoint, userName) + if err != nil { + return nil, err + } + + resp, err := s.client.Do(req, nil) + if err != nil { + err = NewJiraError(resp, err) + } + + return resp, err +} diff --git a/issue_test.go b/issue_test.go index 4470411..8cad8a4 100644 --- a/issue_test.go +++ b/issue_test.go @@ -1214,3 +1214,44 @@ func TestIssueService_GetWorklogs(t *testing.T) { t.Errorf("Error given: %s", err) } } + +func TestIssueService_GetWatchers(t *testing.T) { + setup() + defer teardown() + testMux.HandleFunc("/rest/api/2/issue/10002/watchers", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testRequestURL(t, r, "/rest/api/2/issue/10002/watchers") + + fmt.Fprint(w, `{"self":"http://www.example.com/jira/rest/api/2/issue/EX-1/watchers","isWatching":false,"watchCount":1,"watchers":[{"self":"http://www.example.com/jira/rest/api/2/user?username=fred","name":"fred","displayName":"Fred F. User","active":false}]}`) + }) + + testMux.HandleFunc("/rest/api/2/user", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testRequestURL(t, r, "/rest/api/2/user?username=fred") + + fmt.Fprint(w, `{"self":"http://www.example.com/jira/rest/api/2/user?username=fred","key":"fred", + "name":"fred","emailAddress":"fred@example.com","avatarUrls":{"48x48":"http://www.example.com/jira/secure/useravatar?size=large&ownerId=fred", + "24x24":"http://www.example.com/jira/secure/useravatar?size=small&ownerId=fred","16x16":"http://www.example.com/jira/secure/useravatar?size=xsmall&ownerId=fred", + "32x32":"http://www.example.com/jira/secure/useravatar?size=medium&ownerId=fred"},"displayName":"Fred F. User","active":true,"timeZone":"Australia/Sydney","groups":{"size":3,"items":[ + {"name":"jira-user","self":"http://www.example.com/jira/rest/api/2/group?groupname=jira-user"},{"name":"jira-admin", + "self":"http://www.example.com/jira/rest/api/2/group?groupname=jira-admin"},{"name":"important","self":"http://www.example.com/jira/rest/api/2/group?groupname=important" + }]},"applicationRoles":{"size":1,"items":[]},"expand":"groups,applicationRoles"}`) + }) + + watchers, _, err := testClient.Issue.GetWatchers("10002") + if err != nil { + t.Errorf("Error given: %s", err) + return + } + if watchers == nil { + t.Error("Expected watchers. Watchers is nil") + return + } + if len(*watchers) != 1 { + t.Errorf("Expected 1 watcher, got: %d", len(*watchers)) + return + } + if (*watchers)[0].Name != "fred" { + t.Error("Expected watcher name fred") + } +}