Repository: andygrunwald/go-jira
Pull request #589 (2022-10-19 20:21:04)

Cloud/Group: Update Add/Remove and replace username parameter with accountId
Additionally:
* Cloud/Group: Renamed `Group.Add` to `Group.AddUserByGroupName`
* Cloud/Group: Renamed `Group.Remove` to `Group.RemoveUserByGroupName`

Thanks a lot to @t-junjie for the work in https://github.com/andygrunwald/go-jira/pull/414


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index ede1797..d9e4e3c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -398,6 +398,8 @@ client, err := jira.NewClient("https://...", tp.Client())
 * Cloud/Authentication: Removes `CookieAuthTransport` and `AuthenticationService`, because this type of auth is not supported by the Jira cloud offering
 * Cloud/Component: The type `CreateComponentOptions` was renamed to `ComponentCreateOptions`
 * Cloud/User: Renamed `User.GetSelf` to `User.GetCurrentUser`
+* Cloud/Group: Renamed `Group.Add` to `Group.AddUserByGroupName`
+* Cloud/Group: Renamed `Group.Remove` to `Group.RemoveUserByGroupName`
 
 ### Features
 
diff --git a/cloud/group.go b/cloud/group.go
index 0694b13..38736fa 100644
--- a/cloud/group.go
+++ b/cloud/group.go
@@ -23,19 +23,19 @@ type groupMembersResult struct {
 
 // Group represents a Jira group
 type Group struct {
-	ID                   string          `json:"id"`
-	Title                string          `json:"title"`
-	Type                 string          `json:"type"`
-	Properties           groupProperties `json:"properties"`
-	AdditionalProperties bool            `json:"additionalProperties"`
+	Name   string       `json:"name,omitempty" structs:"name,omitempty"`
+	Self   string       `json:"self,omitempty" structs:"self,omitempty"`
+	Users  GroupMembers `json:"users,omitempty" structs:"users,omitempty"`
+	Expand string       `json:"expand,omitempty" structs:"expand,omitempty"`
 }
 
-type groupProperties struct {
-	Name groupPropertiesName `json:"name"`
-}
-
-type groupPropertiesName struct {
-	Type string `json:"type"`
+// GroupMembers represent members in a Jira group
+type GroupMembers struct {
+	Size       int           `json:"size,omitempty" structs:"size,omitempty"`
+	Items      []GroupMember `json:"items,omitempty" structs:"items,omitempty"`
+	MaxResults int           `json:"max-results,omitempty" structs:"max-results.omitempty"`
+	StartIndex int           `json:"start-index,omitempty" structs:"start-index,omitempty"`
+	EndIndex   int           `json:"end-index,omitempty" structs:"end-index,omitempty"`
 }
 
 // GroupMember reflects a single member of a group
@@ -95,18 +95,18 @@ func (s *GroupService) Get(ctx context.Context, name string, options *GroupSearc
 	return group.Members, resp, nil
 }
 
-// Add adds user to group
+// Add adds a user to a group.
 //
-// Jira API docs: https://docs.atlassian.com/jira/REST/cloud/#api/2/group-addUserToGroup
+// The account ID of the user, which uniquely identifies the user across all Atlassian products.
+// For example, 5b10ac8d82e05b22cc7d4ef5.
 //
-// TODO Double check this method if this works as expected, is using the latest API and the response is complete
-// This double check effort is done for v2 - Remove this two lines if this is completed.
-func (s *GroupService) Add(ctx context.Context, groupname string, username string) (*Group, *Response, error) {
-	apiEndpoint := fmt.Sprintf("/rest/api/2/group/user?groupname=%s", groupname)
+// Jira API docs: https://developer.atlassian.com/cloud/jira/platform/rest/v3/api-group-groups/#api-rest-api-3-group-user-post
+func (s *GroupService) AddUserByGroupName(ctx context.Context, groupName string, accountID string) (*Group, *Response, error) {
+	apiEndpoint := fmt.Sprintf("/rest/api/3/group/user?groupname=%s", groupName)
 	var user struct {
-		Name string `json:"name"`
+		AccountID string `json:"accountId"`
 	}
-	user.Name = username
+	user.AccountID = accountID
 	req, err := s.client.NewRequest(ctx, http.MethodPost, apiEndpoint, &user)
 	if err != nil {
 		return nil, nil, err
@@ -122,15 +122,15 @@ func (s *GroupService) Add(ctx context.Context, groupname string, username strin
 	return responseGroup, resp, nil
 }
 
-// Remove removes user from group
+// Remove removes a user from a group.
 //
-// Jira API docs: https://docs.atlassian.com/jira/REST/cloud/#api/2/group-removeUserFromGroup
-// Caller must close resp.Body
+// The account ID of the user, which uniquely identifies the user across all Atlassian products.
+// For example, 5b10ac8d82e05b22cc7d4ef5.
 //
-// TODO Double check this method if this works as expected, is using the latest API and the response is complete
-// This double check effort is done for v2 - Remove this two lines if this is completed.
-func (s *GroupService) Remove(ctx context.Context, groupname string, username string) (*Response, error) {
-	apiEndpoint := fmt.Sprintf("/rest/api/2/group/user?groupname=%s&username=%s", groupname, username)
+// Jira API docs: https://developer.atlassian.com/cloud/jira/platform/rest/v3/api-group-groups/#api-rest-api-3-group-user-delete
+// Caller must close resp.Body
+func (s *GroupService) RemoveUserByGroupName(ctx context.Context, groupName string, accountID string) (*Response, error) {
+	apiEndpoint := fmt.Sprintf("/rest/api/3/group/user?groupname=%s&accountId=%s", groupName, accountID)
 	req, err := s.client.NewRequest(ctx, http.MethodDelete, apiEndpoint, nil)
 	if err != nil {
 		return nil, err


Test patch:
diff --git a/cloud/group_test.go b/cloud/group_test.go
index 596ac61..e37f79e 100644
--- a/cloud/group_test.go
+++ b/cloud/group_test.go
@@ -65,15 +65,15 @@ func TestGroupService_GetPage(t *testing.T) {
 func TestGroupService_Add(t *testing.T) {
 	setup()
 	defer teardown()
-	testMux.HandleFunc("/rest/api/2/group/user", func(w http.ResponseWriter, r *http.Request) {
+	testMux.HandleFunc("/rest/api/3/group/user", func(w http.ResponseWriter, r *http.Request) {
 		testMethod(t, r, http.MethodPost)
-		testRequestURL(t, r, "/rest/api/2/group/user?groupname=default")
+		testRequestURL(t, r, "/rest/api/3/group/user?groupname=default")
 
 		w.WriteHeader(http.StatusCreated)
 		fmt.Fprint(w, `{"name":"default","self":"http://www.example.com/jira/rest/api/2/group?groupname=default","users":{"size":1,"items":[],"max-results":50,"start-index":0,"end-index":0},"expand":"users"}`)
 	})
 
-	if group, _, err := testClient.Group.Add(context.Background(), "default", "theodore"); err != nil {
+	if group, _, err := testClient.Group.AddUserByGroupName(context.Background(), "default", "5b10ac8d82e05b22cc7d4ef5"); err != nil {
 		t.Errorf("Error given: %s", err)
 	} else if group == nil {
 		t.Error("Expected group. Group is nil")
@@ -83,15 +83,15 @@ func TestGroupService_Add(t *testing.T) {
 func TestGroupService_Remove(t *testing.T) {
 	setup()
 	defer teardown()
-	testMux.HandleFunc("/rest/api/2/group/user", func(w http.ResponseWriter, r *http.Request) {
+	testMux.HandleFunc("/rest/api/3/group/user", func(w http.ResponseWriter, r *http.Request) {
 		testMethod(t, r, http.MethodDelete)
-		testRequestURL(t, r, "/rest/api/2/group/user?groupname=default")
+		testRequestURL(t, r, "/rest/api/3/group/user?groupname=default")
 
 		w.WriteHeader(http.StatusOK)
 		fmt.Fprint(w, `{"name":"default","self":"http://www.example.com/jira/rest/api/2/group?groupname=default","users":{"size":1,"items":[],"max-results":50,"start-index":0,"end-index":0},"expand":"users"}`)
 	})
 
-	if _, err := testClient.Group.Remove(context.Background(), "default", "theodore"); err != nil {
+	if _, err := testClient.Group.RemoveUserByGroupName(context.Background(), "default", "5b10ac8d82e05b22cc7d4ef5"); err != nil {
 		t.Errorf("Error given: %s", err)
 	}
 }