Repository: xanzy/go-gitlab
Pull request #1055 (2021-02-05 15:51:45)

Added list billable member func
Under the same API as the regular group member functions, GitLab has a list "billable" group members function that was released fairly recently. See: https://docs.gitlab.com/ee/api/members.html#list-all-billable-members-of-a-group

The structure of a billable group member is fairly close to that of a regular group member, but the main difference is the removal of the `expires_at`, `access_level`, and `group_saml_identity` fields, and the addition of a `last_activity_on` field. 


Patch:
diff --git a/group_members.go b/group_members.go
index 104a761..9b6fb7d 100644
--- a/group_members.go
+++ b/group_members.go
@@ -18,6 +18,7 @@ package gitlab
 
 import (
 	"fmt"
+	"net/http"
 )
 
 // GroupMembersService handles communication with the group members
@@ -151,6 +152,56 @@ func (s *GroupMembersService) GetGroupMember(gid interface{}, user int, options
 	return gm, resp, err
 }
 
+// BillableGroupMember represents a GitLab billable group member.
+//
+// GitLab API docs: https://docs.gitlab.com/ee/api/members.html#list-all-billable-members-of-a-group
+type BillableGroupMember struct {
+	ID             int     `json:"id"`
+	Username       string  `json:"username"`
+	Name           string  `json:"name"`
+	State          string  `json:"state"`
+	AvatarURL      string  `json:"avatar_url"`
+	WebURL         string  `json:"web_url"`
+	Email          string  `json:"email"`
+	LastActivityOn ISOTime `json:"last_activity_on"`
+}
+
+// ListBillableGroupMembersOptions represents the available ListBillableGroupMembers() options.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ee/api/members.html#list-all-billable-members-of-a-group
+type ListBillableGroupMembersOptions struct {
+	ListOptions
+	Search *string `url:"search,omitempty" json:"search,omitempty"`
+	Sort   *string `url:"sort,omitempty" json:"sort,omitempty"`
+}
+
+// ListBillableGroupMembers Gets a list of group members that count as billable.
+// The list includes members in the subgroup or subproject.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ee/api/members.html#list-all-billable-members-of-a-group
+func (s *GroupsService) ListBillableGroupMembers(gid interface{}, opt *ListBillableGroupMembersOptions, options ...RequestOptionFunc) ([]*BillableGroupMember, *Response, error) {
+	group, err := parseID(gid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("groups/%s/billable_members", pathEscape(group))
+
+	req, err := s.client.NewRequest(http.MethodGet, u, opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	var bgm []*BillableGroupMember
+	resp, err := s.client.Do(req, &bgm)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return bgm, resp, err
+}
+
 // AddGroupMember adds a user to the list of group members.
 //
 // GitLab API docs:


Test patch:
diff --git a/group_members_test.go b/group_members_test.go
new file mode 100644
index 0000000..890597f
--- /dev/null
+++ b/group_members_test.go
@@ -0,0 +1,50 @@
+//
+// Copyright 2021, Sander van Harmelen
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+package gitlab
+
+import (
+	"fmt"
+	"net/http"
+	"reflect"
+	"testing"
+)
+
+func TestListBillableGroupMembers(t *testing.T) {
+	mux, server, client := setup(t)
+	defer teardown(server)
+
+	mux.HandleFunc("/api/v4/groups/1/billable_members",
+		func(w http.ResponseWriter, r *http.Request) {
+			testMethod(t, r, http.MethodGet)
+			fmt.Fprint(w, `[{"id":1,"username":"ray","name":"Raymond","state":"active","avatar_url":"https://foo.bar/mypic","web_url":"http://192.168.1.8:3000/root","last_activity_on":"2021-01-27"}]`)
+		})
+
+	billableMembers, _, err := client.Groups.ListBillableGroupMembers(1, &ListBillableGroupMembersOptions{})
+	if err != nil {
+		t.Errorf("Groups.ListBillableGroupMembers returned error: %v", err)
+	}
+
+	testTime := ISOTime{}
+	err = testTime.UnmarshalJSON([]byte(`"2021-01-27"`))
+	if err != nil {
+		t.Errorf("Could not unmarshal date string to ISOTime: %v", err)
+	}
+	want := []*BillableGroupMember{{ID: 1, Username: "ray", Name: "Raymond", State: "active", AvatarURL: "https://foo.bar/mypic", WebURL: "http://192.168.1.8:3000/root", LastActivityOn: testTime}}
+	if !reflect.DeepEqual(want, billableMembers) {
+		t.Errorf("Groups.ListBillableGroupMembers returned %+v, want %+v", billableMembers, want)
+	}
+}