diff --git a/gitlab.go b/gitlab.go index 0fb72e68a..0ed07f2df 100644 --- a/gitlab.go +++ b/gitlab.go @@ -164,6 +164,7 @@ type Client struct { LicenseTemplates *LicenseTemplatesService ManagedLicenses *ManagedLicensesService Markdown *MarkdownService + MemberRolesService *MemberRolesService MergeRequestApprovals *MergeRequestApprovalsService MergeRequests *MergeRequestsService MergeTrains *MergeTrainsService @@ -395,6 +396,7 @@ func newClient(options ...ClientOptionFunc) (*Client, error) { c.LicenseTemplates = &LicenseTemplatesService{client: c} c.ManagedLicenses = &ManagedLicensesService{client: c} c.Markdown = &MarkdownService{client: c} + c.MemberRolesService = &MemberRolesService{client: c} c.MergeRequestApprovals = &MergeRequestApprovalsService{client: c} c.MergeRequests = &MergeRequestsService{client: c, timeStats: timeStats} c.MergeTrains = &MergeTrainsService{client: c} diff --git a/member_roles.go b/member_roles.go new file mode 100644 index 000000000..e3f1285f5 --- /dev/null +++ b/member_roles.go @@ -0,0 +1,115 @@ +package gitlab + +import ( + "fmt" + "net/http" +) + +// MemberRolesService handles communication with the member roles related +// methods of the GitLab API. +// +// GitLab API docs: https://docs.gitlab.com/ee/api/member_roles.html +type MemberRolesService struct { + client *Client +} + +// MemberRole represents a GitLab member role. +// +// GitLab API docs: https://docs.gitlab.com/ee/api/member_roles.html +type MemberRole struct { + ID int `json:"id"` + Name string `json:"name"` + Description string `json:"description,omitempty"` + GroupId int `json:"group_id"` + BaseAccessLevel AccessLevelValue `json:"base_access_level"` + AdminMergeRequests bool `json:"admin_merge_requests,omitempty"` + AdminVulnerability bool `json:"admin_vulnerability,omitempty"` + ReadCode bool `json:"read_code,omitempty"` + ReadDependency bool `json:"read_dependency,omitempty"` + ReadVulnerability bool `json:"read_vulnerability,omitempty"` + ManageProjectAccessToken bool `json:"manage_project_access_token,omitempty"` +} + +// ListMemberRoles gets a list of member roles for a specified group. +// +// Gitlab API docs: +// https://docs.gitlab.com/ee/api/member_roles.html#list-all-member-roles-of-a-group +func (s *MemberRolesService) ListMemberRoles(gid interface{}, options ...RequestOptionFunc) ([]*MemberRole, *Response, error) { + group, err := parseID(gid) + if err != nil { + return nil, nil, err + } + u := fmt.Sprintf("groups/%s/member_roles", PathEscape(group)) + + req, err := s.client.NewRequest(http.MethodGet, u, nil, options) + if err != nil { + return nil, nil, err + } + + var mrs []*MemberRole + resp, err := s.client.Do(req, &mrs) + if err != nil { + return nil, resp, err + } + + return mrs, resp, nil +} + +// CreateMemberRoleOptions represents the available CreateMemberRole() options. +// +// GitLab API docs: +// https://docs.gitlab.com/ee/api/member_roles.html#add-a-member-role-to-a-group +type CreateMemberRoleOptions struct { + Name *string `url:"name,omitempty" json:"name,omitempty"` + BaseAccessLevel *AccessLevelValue `url:"base_access_level,omitempty" json:"base_access_level,omitempty"` + Description *string `url:"description,omitempty" json:"description,omitempty"` + AdminMergeRequest *bool `url:"admin_merge_request,omitempty" json:"admin_merge_request,omitempty"` + AdminVulnerability *bool `url:"admin_vulnerability,omitempty" json:"admin_vulnerability,omitempty"` + ReadCode *bool `url:"read_code,omitempty" json:"read_code,omitempty"` + ReadDependency *bool `url:"read_dependency,omitempty" json:"read_dependency,omitempty"` + ReadVulnerability *bool `url:"read_vulnerability,omitempty" json:"read_vulnerability,omitempty"` +} + +// CreateMemberRole creates a new member role for a specified group. +// +// Gitlab API docs: +// https://docs.gitlab.com/ee/api/member_roles.html#add-a-member-role-to-a-group +func (s *MemberRolesService) CreateMemberRole(gid interface{}, opt *CreateMemberRoleOptions, options ...RequestOptionFunc) (*MemberRole, *Response, error) { + group, err := parseID(gid) + if err != nil { + return nil, nil, err + } + u := fmt.Sprintf("groups/%s/member_roles", PathEscape(group)) + + req, err := s.client.NewRequest(http.MethodPost, u, opt, options) + if err != nil { + return nil, nil, err + } + + mr := new(MemberRole) + resp, err := s.client.Do(req, mr) + if err != nil { + return nil, resp, err + } + + return mr, resp, nil +} + +// DeleteMemberRole deletes a member role from a specified group. +// +// Gitlab API docs: +// https://docs.gitlab.com/ee/api/member_roles.html#remove-member-role-of-a-group +func (s *MemberRolesService) DeleteMemberRole(gid interface{}, memberRole int, options ...RequestOptionFunc) (*Response, error) { + group, err := parseID(gid) + if err != nil { + return nil, err + } + u := fmt.Sprintf("groups/%s/member_roles/%d", PathEscape(group), memberRole) + + req, err := s.client.NewRequest(http.MethodDelete, u, nil, options) + if err != nil { + return nil, err + } + + return s.client.Do(req, nil) +} diff --git a/member_roles_test.go b/member_roles_test.go new file mode 100644 index 000000000..93f0b7743 --- /dev/null +++ b/member_roles_test.go @@ -0,0 +1,105 @@ +package gitlab + +import ( + "net/http" + "testing" + + "github.com/stretchr/testify/require" +) + +func TestListMemberRoles(t *testing.T) { + mux, client := setup(t) + + path := "/api/v4/groups/1/member_roles" + + mux.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, http.MethodGet) + mustWriteHTTPResponse(t, w, "testdata/list_member_roles.json") + }) + + memberRoles, _, err := client.MemberRolesService.ListMemberRoles(1) + require.NoError(t, err) + + want := []*MemberRole{ + { + ID: 1, + Name: "GuestCodeReader", + Description: "A Guest user that can read code", + GroupId: 1, + BaseAccessLevel: 10, // Guest Base Level + AdminMergeRequests: false, + AdminVulnerability: false, + ReadCode: true, + ReadDependency: false, + ReadVulnerability: false, + ManageProjectAccessToken: false, + }, + { + ID: 2, + Name: "GuestVulnerabilityReader", + Description: "A Guest user that can read vulnerabilities", + GroupId: 1, + BaseAccessLevel: 10, // Guest Base Level + AdminMergeRequests: false, + AdminVulnerability: false, + ReadCode: false, + ReadDependency: false, + ReadVulnerability: true, + ManageProjectAccessToken: false, + }, + } + + require.Equal(t, want, memberRoles) +} + +func TestCreateMemberRole(t *testing.T) { + mux, client := setup(t) + + path := "/api/v4/groups/84/member_roles" + + mux.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, http.MethodPost) + mustWriteHTTPResponse(t, w, "testdata/create_member_role.json") + }) + + memberRole, _, err := client.MemberRolesService.CreateMemberRole(84, &CreateMemberRoleOptions{ + Name: Ptr("Custom guest"), + BaseAccessLevel: Ptr(GuestPermissions), + Description: Ptr("a sample custom role"), + AdminMergeRequest: Ptr(false), + AdminVulnerability: Ptr(false), + ReadCode: Ptr(true), + ReadDependency: Ptr(false), + ReadVulnerability: Ptr(false), + }) + require.NoError(t, err) + + want := &MemberRole{ + ID: 3, + Name: "Custom guest", + Description: "a sample custom role", + BaseAccessLevel: GuestPermissions, + GroupId: 84, + AdminMergeRequests: false, + AdminVulnerability: false, + ReadCode: true, + ReadDependency: false, + ReadVulnerability: false, + ManageProjectAccessToken: false, + } + + require.Equal(t, want, memberRole) +} + +func TestDeleteMemberRole(t *testing.T) { + mux, client := setup(t) + + path := "/api/v4/groups/1/member_roles/2" + + mux.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, http.MethodDelete) + }) + + _, err := client.MemberRolesService.DeleteMemberRole(1, 2) + require.NoError(t, err) +} diff --git a/testdata/create_member_role.json b/testdata/create_member_role.json new file mode 100644 index 000000000..0eb7bcd78 --- /dev/null +++ b/testdata/create_member_role.json @@ -0,0 +1,12 @@ +{ + "id": 3, + "name": "Custom guest", + "description": "a sample custom role", + "group_id": 84, + "base_access_level": 10, + "admin_merge_requests": false, + "admin_vulnerability": false, + "read_code": true, + "read_dependency": false, + "read_vulnerability": false +} diff --git a/testdata/list_member_roles.json b/testdata/list_member_roles.json new file mode 100644 index 000000000..b0843b798 --- /dev/null +++ b/testdata/list_member_roles.json @@ -0,0 +1,28 @@ +[ + { + "id": 1, + "name": "GuestCodeReader", + "description": "A Guest user that can read code", + "group_id": 1, + "base_access_level": 10, + "admin_merge_request": false, + "admin_vulnerability": false, + "read_code": true, + "read_dependency": false, + "read_vulnerability": false, + "manage_project_access_token": false + }, + { + "id": 2, + "name": "GuestVulnerabilityReader", + "description": "A Guest user that can read vulnerabilities", + "group_id": 1, + "base_access_level": 10, + "admin_merge_request": false, + "admin_vulnerability": false, + "read_code": false, + "read_dependency": false, + "read_vulnerability": true, + "manage_project_access_token": false + } +]