Repository: xanzy/go-gitlab
Pull request #1702 (2023-04-15 12:23:56)

feat: Add `email` field to `GroupMember`
This is required for https://github.com/cloudquery/cloudquery/issues/10117.
Although the `email` attribute is only visible to group Owners for any [enterprise user](https://docs.gitlab.com/ee/user/enterprise_user/index.html) (see [limitations](https://docs.gitlab.com/ee/api/members.html#limitations)), it'd be nice to have this included.

Might be safely ditched in favor of #1703 (I presume #1703 is a better one)


Patch:
diff --git a/group_members.go b/group_members.go
index 850b023..4a169cd 100644
--- a/group_members.go
+++ b/group_members.go
@@ -54,6 +54,7 @@ type GroupMember struct {
 	CreatedAt         *time.Time               `json:"created_at"`
 	ExpiresAt         *ISOTime                 `json:"expires_at"`
 	AccessLevel       AccessLevelValue         `json:"access_level"`
+	Email             string                   `json:"email,omitempty"`
 	GroupSAMLIdentity *GroupMemberSAMLIdentity `json:"group_saml_identity"`
 }
 
@@ -168,7 +169,7 @@ type BillableGroupMember struct {
 	Email          string     `json:"email"`
 	LastActivityOn *ISOTime   `json:"last_activity_on"`
 	MembershipType string     `json:"membership_type"`
-	Removeable     bool       `json:"removeable"`
+	Removable      bool       `json:"removable"`
 	CreatedAt      *time.Time `json:"created_at"`
 	IsLastOwner    bool       `json:"is_last_owner"`
 	LastLoginAt    *time.Time `json:"last_login_at"`


Test patch:
diff --git a/group_members_test.go b/group_members_test.go
index 519b976..43b9dae 100644
--- a/group_members_test.go
+++ b/group_members_test.go
@@ -43,7 +43,7 @@ func TestListBillableGroupMembers(t *testing.T) {
 						"web_url":"http://192.168.1.8:3000/root",
 						"last_activity_on":"2021-01-27",
 						"membership_type": "group_member",
-						"removeable": true,
+						"removable": true,
 						"created_at": "2017-10-23T11:41:28.793Z",
 						"is_last_owner": false,
 						"last_login_at": "2022-12-12T09:22:51.581Z"
@@ -71,7 +71,7 @@ func TestListBillableGroupMembers(t *testing.T) {
 			WebURL:         "http://192.168.1.8:3000/root",
 			LastActivityOn: &lastActivityOnISOTime,
 			MembershipType: "group_member",
-			Removeable:     true,
+			Removable:      true,
 			CreatedAt:      &createdAt,
 			IsLastOwner:    false,
 			LastLoginAt:    &lastLoginAt,
@@ -80,6 +80,106 @@ func TestListBillableGroupMembers(t *testing.T) {
 	assert.Equal(t, want, billableMembers, "Expected returned Groups.ListBillableGroupMembers to equal")
 }
 
+func TestListGroupMembersWithoutEmail(t *testing.T) {
+	mux, client := setup(t)
+
+	mux.HandleFunc("/api/v4/groups/1/members",
+		func(w http.ResponseWriter, r *http.Request) {
+			testMethod(t, r, http.MethodGet)
+			fmt.Fprint(w,
+				`[
+					{
+						"id": 1,
+						"username": "raymond_smith",
+						"name": "Raymond Smith",
+						"state": "active",
+						"avatar_url": "https://www.gravatar.com/avatar/c2525a7f58ae3776070e44c106c48e15?s=80&d=identicon",
+						"web_url": "http://192.168.1.8:3000/root",
+						"created_at": "2012-10-21T14:13:35Z",
+						"expires_at": "2012-10-22",
+						"access_level": 30,
+						"group_saml_identity": null
+					}
+				]`)
+		})
+
+	members, _, err := client.Groups.ListGroupMembers(1, &ListGroupMembersOptions{})
+	if err != nil {
+		t.Errorf("Groups.ListGroupMembers returned error: %v", err)
+	}
+
+	createdAt, _ := time.Parse(time.RFC3339, "2012-10-21T14:13:35Z")
+	expiresAt, _ := time.Parse(time.RFC3339, "2012-10-22T00:00:00Z")
+	expiresAtISOTime := ISOTime(expiresAt)
+	want := []*GroupMember{
+		{
+			ID:          1,
+			Username:    "raymond_smith",
+			Name:        "Raymond Smith",
+			State:       "active",
+			AvatarURL:   "https://www.gravatar.com/avatar/c2525a7f58ae3776070e44c106c48e15?s=80&d=identicon",
+			WebURL:      "http://192.168.1.8:3000/root",
+			CreatedAt:   &createdAt,
+			ExpiresAt:   &expiresAtISOTime,
+			AccessLevel: 30,
+		},
+	}
+	if !reflect.DeepEqual(want, members) {
+		t.Errorf("Groups.ListBillableGroupMembers returned %+v, want %+v", members[0], want[0])
+	}
+}
+
+func TestListGroupMembersWithEmail(t *testing.T) {
+	mux, client := setup(t)
+
+	mux.HandleFunc("/api/v4/groups/1/members",
+		func(w http.ResponseWriter, r *http.Request) {
+			testMethod(t, r, http.MethodGet)
+			fmt.Fprint(w,
+				`[
+					{
+						"id": 1,
+						"username": "raymond_smith",
+						"name": "Raymond Smith",
+						"state": "active",
+						"avatar_url": "https://www.gravatar.com/avatar/c2525a7f58ae3776070e44c106c48e15?s=80&d=identicon",
+						"web_url": "http://192.168.1.8:3000/root",
+						"created_at": "2012-10-21T14:13:35Z",
+						"expires_at": "2012-10-22",
+						"access_level": 30,
+						"email": "john@example.com",
+						"group_saml_identity": null
+					}
+				]`)
+		})
+
+	members, _, err := client.Groups.ListGroupMembers(1, &ListGroupMembersOptions{})
+	if err != nil {
+		t.Errorf("Groups.ListGroupMembers returned error: %v", err)
+	}
+
+	createdAt, _ := time.Parse(time.RFC3339, "2012-10-21T14:13:35Z")
+	expiresAt, _ := time.Parse(time.RFC3339, "2012-10-22T00:00:00Z")
+	expiresAtISOTime := ISOTime(expiresAt)
+	want := []*GroupMember{
+		{
+			ID:          1,
+			Username:    "raymond_smith",
+			Name:        "Raymond Smith",
+			State:       "active",
+			AvatarURL:   "https://www.gravatar.com/avatar/c2525a7f58ae3776070e44c106c48e15?s=80&d=identicon",
+			WebURL:      "http://192.168.1.8:3000/root",
+			CreatedAt:   &createdAt,
+			ExpiresAt:   &expiresAtISOTime,
+			AccessLevel: 30,
+			Email:       "john@example.com",
+		},
+	}
+	if !reflect.DeepEqual(want, members) {
+		t.Errorf("Groups.ListBillableGroupMembers returned %+v, want %+v", members[0], want[0])
+	}
+}
+
 func TestListGroupMembersWithoutSAML(t *testing.T) {
 	mux, client := setup(t)