From 9a60185697584a467964c0c2dfc15a08ac509c6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Dr=C3=B6=C3=9Fler?= Date: Tue, 1 Oct 2024 17:20:25 +0200 Subject: [PATCH 1/2] Support API endpoint: List memberships for a billable member of a group --- group_members.go | 40 ++++++++++++++++++++++++++++++++++++++ group_members_test.go | 45 +++++++++++++++++++++++++++++++++++++++++++ types.go | 6 ++++++ 3 files changed, 91 insertions(+) diff --git a/group_members.go b/group_members.go index cdf225c3d..e9d0684a6 100644 --- a/group_members.go +++ b/group_members.go @@ -239,6 +239,46 @@ func (s *GroupsService) ListBillableGroupMembers(gid interface{}, opt *ListBilla return bgm, resp, nil } +// BillableUserMembership represents a Membership of a billable user of a group +// +// GitLab API docs: +// https://docs.gitlab.com/ee/api/members.html#list-memberships-for-a-billable-member-of-a-group +type BillableUserMembership struct { + ID int `json:"id"` + SourceId int `json:"source_id"` + SourceFullName string `json:"source_full_name"` + SourceMembersUrl string `json:"source_members_url"` + CreatedAt *time.Time `json:"created_at,omitempty"` + ExpiresAt *time.Time `json:"expires_at,omitempty"` + AccessLevel *AccessLevelDetails `json:"access_level,omitempty"` +} + +// ListMembershipsForBillableGroupMember Gets a list of memberships for a billable member of a group. +// Lists all projects and groups a user is a member of. Only projects and groups within the group hierarchy are included. +// +// GitLab API docs: +// https://docs.gitlab.com/ee/api/members.html#list-memberships-for-a-billable-member-of-a-group +func (s *GroupsService) ListMembershipsForBillableGroupMember(gid interface{}, user int, options ...RequestOptionFunc) ([]*BillableUserMembership, *Response, error) { + group, err := parseID(gid) + if err != nil { + return nil, nil, err + } + u := fmt.Sprintf("groups/%s/billable_members/%d/memberships", PathEscape(group), user) + + req, err := s.client.NewRequest(http.MethodGet, u, nil, options) + if err != nil { + return nil, nil, err + } + + var bum []*BillableUserMembership + resp, err := s.client.Do(req, &bum) + if err != nil { + return nil, resp, err + } + + return bum, resp, nil +} + // RemoveBillableGroupMember removes a given group members that count as billable. // // GitLab API docs: diff --git a/group_members_test.go b/group_members_test.go index 652d6345c..987174c9c 100644 --- a/group_members_test.go +++ b/group_members_test.go @@ -81,6 +81,51 @@ func TestListBillableGroupMembers(t *testing.T) { assert.Equal(t, want, billableMembers, "Expected returned Groups.ListBillableGroupMembers to equal") } +func TestListMembershipsForBillableGroupMember(t *testing.T) { + mux, client := setup(t) + mux.HandleFunc("/api/v4/groups/1/billable_members/42/memberships", + func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, http.MethodGet) + fmt.Fprint(w, + `[ + { + "id":21, + "source_id":36, + "source_full_name":"Root Group / Test Group", + "source_members_url":"https://gitlab.example.com/groups/root-group/test-group/-/group_members", + "created_at":"2021-03-31T17:28:44.812Z", + "access_level": { + "string_value": "Developer", + "integer_value": 30 + } + } + ]`) + }) + + memberships, _, err := client.Groups.ListMembershipsForBillableGroupMember(1, 42) + if err != nil { + t.Errorf("Groups.ListMembershipsForBillableGroupMember returned error: %v", err) + } + + createdAt, _ := time.Parse(time.RFC3339, "2021-03-31T17:28:44.812Z") + + want := []*BillableUserMembership{ + { + ID: 21, + SourceId: 36, + SourceFullName: "Root Group / Test Group", + SourceMembersUrl: "https://gitlab.example.com/groups/root-group/test-group/-/group_members", + CreatedAt: &createdAt, + ExpiresAt: nil, + AccessLevel: &AccessLevelDetails{ + ID: 30, + Name: "Developer", + }, + }, + } + assert.Equal(t, want, memberships, "Expected returned Groups.ListMembershipsForBillableGroupMember to equal") +} + func TestListGroupMembersWithoutEmail(t *testing.T) { mux, client := setup(t) diff --git a/types.go b/types.go index 9ce13d735..634e6b75f 100644 --- a/types.go +++ b/types.go @@ -89,6 +89,12 @@ func AccessLevel(v AccessLevelValue) *AccessLevelValue { return Ptr(v) } +type AccessLevelDetails struct { + ID AccessLevelValue `json:"integer_value"` + Name string `json:"string_value"` + CustomRole string `json:"custom_role,omitempty"` +} + // UserIDValue represents a user ID value within GitLab. type UserIDValue string From 38c50a4fb72d95a89c285716e3fdf6aca1ed0dc1 Mon Sep 17 00:00:00 2001 From: Sander van Harmelen Date: Sun, 6 Oct 2024 14:11:50 +0200 Subject: [PATCH 2/2] Make a few small tweaks to get the PR inline with the docs --- group_members.go | 88 +++++++++++++++++++++---------------------- group_members_test.go | 9 ++--- types.go | 5 +-- 3 files changed, 50 insertions(+), 52 deletions(-) diff --git a/group_members.go b/group_members.go index e9d0684a6..c18b180e1 100644 --- a/group_members.go +++ b/group_members.go @@ -30,17 +30,6 @@ type GroupMembersService struct { client *Client } -// GroupMemberSAMLIdentity represents the SAML Identity link for the group member. -// -// GitLab API docs: https://docs.gitlab.com/ee/api/members.html#list-all-members-of-a-group-or-project -// Gitlab MR for API change: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/20357 -// Gitlab MR for API Doc change: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/25652 -type GroupMemberSAMLIdentity struct { - ExternUID string `json:"extern_uid"` - Provider string `json:"provider"` - SAMLProviderID int `json:"saml_provider_id"` -} - // GroupMember represents a GitLab group member. // // GitLab API docs: https://docs.gitlab.com/ee/api/members.html @@ -59,6 +48,50 @@ type GroupMember struct { MemberRole *MemberRole `json:"member_role"` } +// GroupMemberSAMLIdentity represents the SAML Identity link for the group member. +// +// GitLab API docs: +// https://docs.gitlab.com/ee/api/members.html#list-all-members-of-a-group-or-project +type GroupMemberSAMLIdentity struct { + ExternUID string `json:"extern_uid"` + Provider string `json:"provider"` + SAMLProviderID int `json:"saml_provider_id"` +} + +// 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"` + MembershipType string `json:"membership_type"` + Removable bool `json:"removable"` + CreatedAt *time.Time `json:"created_at"` + IsLastOwner bool `json:"is_last_owner"` + LastLoginAt *time.Time `json:"last_login_at"` +} + +// BillableUserMembership represents a Membership of a billable user of a group +// +// GitLab API docs: +// https://docs.gitlab.com/ee/api/members.html#list-memberships-for-a-billable-member-of-a-group +type BillableUserMembership struct { + ID int `json:"id"` + SourceID int `json:"source_id"` + SourceFullName string `json:"source_full_name"` + SourceMembersURL string `json:"source_members_url"` + CreatedAt *time.Time `json:"created_at"` + ExpiresAt *time.Time `json:"expires_at"` + AccessLevel *AccessLevelDetails `json:"access_level"` +} + // ListGroupMembersOptions represents the available ListGroupMembers() and // ListAllGroupMembers() options. // @@ -184,25 +217,6 @@ func (s *GroupMembersService) GetInheritedGroupMember(gid interface{}, user int, 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"` - MembershipType string `json:"membership_type"` - Removable bool `json:"removable"` - CreatedAt *time.Time `json:"created_at"` - IsLastOwner bool `json:"is_last_owner"` - LastLoginAt *time.Time `json:"last_login_at"` -} - // ListBillableGroupMembersOptions represents the available ListBillableGroupMembers() options. // // GitLab API docs: @@ -239,20 +253,6 @@ func (s *GroupsService) ListBillableGroupMembers(gid interface{}, opt *ListBilla return bgm, resp, nil } -// BillableUserMembership represents a Membership of a billable user of a group -// -// GitLab API docs: -// https://docs.gitlab.com/ee/api/members.html#list-memberships-for-a-billable-member-of-a-group -type BillableUserMembership struct { - ID int `json:"id"` - SourceId int `json:"source_id"` - SourceFullName string `json:"source_full_name"` - SourceMembersUrl string `json:"source_members_url"` - CreatedAt *time.Time `json:"created_at,omitempty"` - ExpiresAt *time.Time `json:"expires_at,omitempty"` - AccessLevel *AccessLevelDetails `json:"access_level,omitempty"` -} - // ListMembershipsForBillableGroupMember Gets a list of memberships for a billable member of a group. // Lists all projects and groups a user is a member of. Only projects and groups within the group hierarchy are included. // diff --git a/group_members_test.go b/group_members_test.go index 987174c9c..98fc0d280 100644 --- a/group_members_test.go +++ b/group_members_test.go @@ -112,14 +112,13 @@ func TestListMembershipsForBillableGroupMember(t *testing.T) { want := []*BillableUserMembership{ { ID: 21, - SourceId: 36, + SourceID: 36, SourceFullName: "Root Group / Test Group", - SourceMembersUrl: "https://gitlab.example.com/groups/root-group/test-group/-/group_members", + SourceMembersURL: "https://gitlab.example.com/groups/root-group/test-group/-/group_members", CreatedAt: &createdAt, - ExpiresAt: nil, AccessLevel: &AccessLevelDetails{ - ID: 30, - Name: "Developer", + IntegerValue: 30, + StringValue: "Developer", }, }, } diff --git a/types.go b/types.go index 634e6b75f..5b7f74eb3 100644 --- a/types.go +++ b/types.go @@ -90,9 +90,8 @@ func AccessLevel(v AccessLevelValue) *AccessLevelValue { } type AccessLevelDetails struct { - ID AccessLevelValue `json:"integer_value"` - Name string `json:"string_value"` - CustomRole string `json:"custom_role,omitempty"` + IntegerValue AccessLevelValue `json:"integer_value"` + StringValue string `json:"string_value"` } // UserIDValue represents a user ID value within GitLab.