Loading gitlab.go +2 −0 Original line number Diff line number Diff line Loading @@ -198,6 +198,7 @@ type Client struct { GroupMembers GroupMembersServiceInterface GroupMilestones GroupMilestonesServiceInterface GroupProtectedEnvironments GroupProtectedEnvironmentsServiceInterface GroupProtectedBranches GroupProtectedBranchesServiceInterface GroupRelationsExport GroupRelationsExportServiceInterface GroupReleases GroupReleasesServiceInterface GroupRepositoryStorageMove GroupRepositoryStorageMoveServiceInterface Loading Loading @@ -517,6 +518,7 @@ func NewAuthSourceClient(as AuthSource, options ...ClientOptionFunc) (*Client, e c.GroupMembers = &GroupMembersService{client: c} c.GroupMilestones = &GroupMilestonesService{client: c} c.GroupProtectedEnvironments = &GroupProtectedEnvironmentsService{client: c} c.GroupProtectedBranches = &GroupProtectedBranchesService{client: c} c.GroupRelationsExport = &GroupRelationsExportService{client: c} c.GroupReleases = &GroupReleasesService{client: c} c.GroupRepositoryStorageMove = &GroupRepositoryStorageMoveService{client: c} Loading gitlab_service_map_generated_test.go +1 −0 Original line number Diff line number Diff line Loading @@ -61,6 +61,7 @@ var serviceMap = map[any]any{ &GroupMarkdownUploadsService{}: (*GroupMarkdownUploadsServiceInterface)(nil), &GroupMembersService{}: (*GroupMembersServiceInterface)(nil), &GroupMilestonesService{}: (*GroupMilestonesServiceInterface)(nil), &GroupProtectedBranchesService{}: (*GroupProtectedBranchesServiceInterface)(nil), &GroupProtectedEnvironmentsService{}: (*GroupProtectedEnvironmentsServiceInterface)(nil), &GroupRelationsExportService{}: (*GroupRelationsExportServiceInterface)(nil), &GroupReleasesService{}: (*GroupReleasesServiceInterface)(nil), Loading gitlab_test/groups_integration_test.go +59 −0 Original line number Diff line number Diff line Loading @@ -107,3 +107,62 @@ func Test_GroupsMaxArtifactsSize_Integration(t *testing.T) { // THEN MaxArtifactsSize should persist assert.Equal(t, int64(100), retrievedGroup.MaxArtifactsSize) } func Test_GroupProtectedBranches_Integration(t *testing.T) { // GIVEN a GitLab client and a test group client := SetupIntegrationClient(t) group := CreateTestGroup(t, client) // Define branch name branchName := "main" // WHEN protecting a branch protectedBranch, _, err := client.GroupProtectedBranches.ProtectRepositoryBranches(group.ID, &gitlab.ProtectGroupRepositoryBranchesOptions{ Name: gitlab.Ptr(branchName), PushAccessLevel: gitlab.Ptr(gitlab.MaintainerPermissions), MergeAccessLevel: gitlab.Ptr(gitlab.MaintainerPermissions), }) require.NoError(t, err, "Failed to protect branch") // THEN the branch should be protected assert.Equal(t, branchName, protectedBranch.Name) // WHEN listing protected branches branches, _, err := client.GroupProtectedBranches.ListProtectedBranches(group.ID, nil) require.NoError(t, err, "Failed to list protected branches") // THEN the protected branch should be in the list found := false for _, b := range branches { if b.Name == branchName { found = true break } } assert.True(t, found, "Protected branch not found in list") // WHEN getting the protected branch gotBranch, _, err := client.GroupProtectedBranches.GetProtectedBranch(group.ID, branchName) require.NoError(t, err, "Failed to get protected branch") // THEN it should match assert.Equal(t, branchName, gotBranch.Name) // WHEN updating the protected branch updatedBranch, _, err := client.GroupProtectedBranches.UpdateProtectedBranch(group.ID, branchName, &gitlab.UpdateGroupProtectedBranchOptions{ AllowForcePush: gitlab.Ptr(true), }) require.NoError(t, err, "Failed to update protected branch") // THEN the update should be reflected assert.True(t, updatedBranch.AllowForcePush) // WHEN unprotecting the branch _, err = client.GroupProtectedBranches.UnprotectRepositoryBranches(group.ID, branchName) require.NoError(t, err, "Failed to unprotect branch") // THEN getting the branch should fail (404) _, resp, err := client.GroupProtectedBranches.GetProtectedBranch(group.ID, branchName) assert.Error(t, err) assert.Equal(t, 404, resp.StatusCode) } group_protected_branches.go 0 → 100644 +177 −0 Original line number Diff line number Diff line package gitlab import ( "net/http" "net/url" ) type ( GroupProtectedBranchesServiceInterface interface { // ListProtectedBranches returns a list of protected branches from a group. // // GitLab API docs: // https://docs.gitlab.com/api/group_protected_branches/#list-protected-branches ListProtectedBranches(gid any, opt *ListGroupProtectedBranchesOptions, options ...RequestOptionFunc) ([]*GroupProtectedBranch, *Response, error) // GetProtectedBranch returns a single group-level protected branch. // // GitLab API docs: // https://docs.gitlab.com/api/group_protected_branches/#get-a-single-protected-branch-or-wildcard-protected-branch GetProtectedBranch(gid any, branch string, options ...RequestOptionFunc) (*GroupProtectedBranch, *Response, error) // ProtectRepositoryBranches protects a single group-level branch. // // GitLab API docs: // https://docs.gitlab.com/api/group_protected_branches/#protect-repository-branches ProtectRepositoryBranches(gid any, opt *ProtectGroupRepositoryBranchesOptions, options ...RequestOptionFunc) (*GroupProtectedBranch, *Response, error) // UpdateProtectedBranch updates a single group-level protected branch. // // GitLab API docs: // https://docs.gitlab.com/api/group_protected_branches/#update-a-protected-branch UpdateProtectedBranch(gid any, branch string, opt *UpdateGroupProtectedBranchOptions, options ...RequestOptionFunc) (*GroupProtectedBranch, *Response, error) // UnprotectRepositoryBranches unprotects the given protected group-level branch. // // GitLab API docs: // https://docs.gitlab.com/api/group_protected_branches/#unprotect-repository-branches UnprotectRepositoryBranches(gid any, branch string, options ...RequestOptionFunc) (*Response, error) } // GroupProtectedBranchesService handles communication with the group-level // protected branch methods of the GitLab API. // // GitLab API docs: // https://docs.gitlab.com/api/group_protected_branches/ GroupProtectedBranchesService struct { client *Client } ) var _ GroupProtectedBranchesServiceInterface = (*GroupProtectedBranchesService)(nil) // GroupProtectedBranch represents a group protected branch. // // GitLab API docs: // https://docs.gitlab.com/api/group_protected_branches/#list-protected-branches type GroupProtectedBranch struct { ID int64 `json:"id"` Name string `json:"name"` PushAccessLevels []*GroupBranchAccessDescription `json:"push_access_levels"` MergeAccessLevels []*GroupBranchAccessDescription `json:"merge_access_levels"` UnprotectAccessLevels []*GroupBranchAccessDescription `json:"unprotect_access_levels"` AllowForcePush bool `json:"allow_force_push"` CodeOwnerApprovalRequired bool `json:"code_owner_approval_required"` } // GroupBranchAccessDescription represents the access description for a group protected // branch. // // GitLab API docs: // https://docs.gitlab.com/api/group_protected_branches/#list-protected-branches type GroupBranchAccessDescription struct { ID int64 `json:"id"` AccessLevel AccessLevelValue `json:"access_level"` AccessLevelDescription string `json:"access_level_description"` DeployKeyID int64 `json:"deploy_key_id"` UserID int64 `json:"user_id"` GroupID int64 `json:"group_id"` } // ListGroupProtectedBranchesOptions represents the available ListProtectedBranches() // options. // // GitLab API docs: // https://docs.gitlab.com/api/group_protected_branches/#list-protected-branches type ListGroupProtectedBranchesOptions struct { ListOptions Search *string `url:"search,omitempty" json:"search,omitempty"` } func (s *GroupProtectedBranchesService) ListProtectedBranches(gid any, opt *ListGroupProtectedBranchesOptions, options ...RequestOptionFunc) ([]*GroupProtectedBranch, *Response, error) { return do[[]*GroupProtectedBranch](s.client, withMethod(http.MethodGet), withPath("groups/%s/protected_branches", GroupID{gid}), withAPIOpts(opt), withRequestOpts(options...), ) } func (s *GroupProtectedBranchesService) GetProtectedBranch(gid any, branch string, options ...RequestOptionFunc) (*GroupProtectedBranch, *Response, error) { return do[*GroupProtectedBranch](s.client, withMethod(http.MethodGet), withPath("groups/%s/protected_branches/%s", GroupID{gid}, url.PathEscape(branch)), withRequestOpts(options...), ) } // ProtectGroupRepositoryBranchesOptions represents the available // ProtectRepositoryBranches() options. // // GitLab API docs: // https://docs.gitlab.com/api/group_protected_branches/#protect-repository-branches type ProtectGroupRepositoryBranchesOptions struct { Name *string `url:"name,omitempty" json:"name,omitempty"` PushAccessLevel *AccessLevelValue `url:"push_access_level,omitempty" json:"push_access_level,omitempty"` MergeAccessLevel *AccessLevelValue `url:"merge_access_level,omitempty" json:"merge_access_level,omitempty"` UnprotectAccessLevel *AccessLevelValue `url:"unprotect_access_level,omitempty" json:"unprotect_access_level,omitempty"` AllowForcePush *bool `url:"allow_force_push,omitempty" json:"allow_force_push,omitempty"` AllowedToPush *[]*GroupBranchPermissionOptions `url:"allowed_to_push,omitempty" json:"allowed_to_push,omitempty"` AllowedToMerge *[]*GroupBranchPermissionOptions `url:"allowed_to_merge,omitempty" json:"allowed_to_merge,omitempty"` AllowedToUnprotect *[]*GroupBranchPermissionOptions `url:"allowed_to_unprotect,omitempty" json:"allowed_to_unprotect,omitempty"` CodeOwnerApprovalRequired *bool `url:"code_owner_approval_required,omitempty" json:"code_owner_approval_required,omitempty"` } // GroupBranchPermissionOptions represents a branch permission option. // // GitLab API docs: // https://docs.gitlab.com/api/group_protected_branches/#protect-repository-branches type GroupBranchPermissionOptions struct { ID *int64 `url:"id,omitempty" json:"id,omitempty"` UserID *int64 `url:"user_id,omitempty" json:"user_id,omitempty"` GroupID *int64 `url:"group_id,omitempty" json:"group_id,omitempty"` DeployKeyID *int64 `url:"deploy_key_id,omitempty" json:"deploy_key_id,omitempty"` AccessLevel *AccessLevelValue `url:"access_level,omitempty" json:"access_level,omitempty"` Destroy *bool `url:"_destroy,omitempty" json:"_destroy,omitempty"` } func (s *GroupProtectedBranchesService) ProtectRepositoryBranches(gid any, opt *ProtectGroupRepositoryBranchesOptions, options ...RequestOptionFunc) (*GroupProtectedBranch, *Response, error) { return do[*GroupProtectedBranch](s.client, withMethod(http.MethodPost), withPath("groups/%s/protected_branches", GroupID{gid}), withAPIOpts(opt), withRequestOpts(options...), ) } // UpdateGroupProtectedBranchOptions represents the available // UpdateProtectedBranch() options. // // GitLab API docs: // https://docs.gitlab.com/api/group_protected_branches/#update-a-protected-branch type UpdateGroupProtectedBranchOptions struct { Name *string `url:"name,omitempty" json:"name,omitempty"` AllowForcePush *bool `url:"allow_force_push,omitempty" json:"allow_force_push,omitempty"` CodeOwnerApprovalRequired *bool `url:"code_owner_approval_required,omitempty" json:"code_owner_approval_required,omitempty"` AllowedToPush *[]*GroupBranchPermissionOptions `url:"allowed_to_push,omitempty" json:"allowed_to_push,omitempty"` AllowedToMerge *[]*GroupBranchPermissionOptions `url:"allowed_to_merge,omitempty" json:"allowed_to_merge,omitempty"` AllowedToUnprotect *[]*GroupBranchPermissionOptions `url:"allowed_to_unprotect,omitempty" json:"allowed_to_unprotect,omitempty"` } func (s *GroupProtectedBranchesService) UpdateProtectedBranch(gid any, branch string, opt *UpdateGroupProtectedBranchOptions, options ...RequestOptionFunc) (*GroupProtectedBranch, *Response, error) { return do[*GroupProtectedBranch](s.client, withMethod(http.MethodPatch), withPath("groups/%s/protected_branches/%s", GroupID{gid}, url.PathEscape(branch)), withAPIOpts(opt), withRequestOpts(options...), ) } func (s *GroupProtectedBranchesService) UnprotectRepositoryBranches(gid any, branch string, options ...RequestOptionFunc) (*Response, error) { _, resp, err := do[none](s.client, withMethod(http.MethodDelete), withPath("groups/%s/protected_branches/%s", GroupID{gid}, url.PathEscape(branch)), withRequestOpts(options...), ) return resp, err } group_protected_branches_test.go 0 → 100644 +223 −0 Original line number Diff line number Diff line package gitlab import ( "fmt" "net/http" "testing" "github.com/stretchr/testify/assert" ) func TestGroupListProtectedBranches(t *testing.T) { t.Parallel() mux, client := setup(t) mux.HandleFunc("/api/v4/groups/1/protected_branches", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprint(w, `[ { "id":1, "name":"master", "push_access_levels":[{ "id":1, "access_level":40, "access_level_description":"Maintainers", "user_id":null, "group_id":null },{ "id":2, "access_level":30, "access_level_description":"User name", "user_id":123, "group_id":null }], "merge_access_levels":[{ "id":1, "access_level":40, "access_level_description":"Maintainers", "user_id":null, "group_id":null }], "code_owner_approval_required":false } ]`) }) opt := &ListGroupProtectedBranchesOptions{} protectedBranches, resp, err := client.GroupProtectedBranches.ListProtectedBranches("1", opt) assert.NoError(t, err) assert.NotNil(t, resp) want := []*GroupProtectedBranch{ { ID: 1, Name: "master", PushAccessLevels: []*GroupBranchAccessDescription{ { ID: 1, AccessLevel: 40, AccessLevelDescription: "Maintainers", }, { ID: 2, AccessLevel: 30, AccessLevelDescription: "User name", UserID: 123, }, }, MergeAccessLevels: []*GroupBranchAccessDescription{ { ID: 1, AccessLevel: 40, AccessLevelDescription: "Maintainers", }, }, AllowForcePush: false, CodeOwnerApprovalRequired: false, }, } assert.Equal(t, want, protectedBranches) } func TestGroupGetProtectedBranch(t *testing.T) { t.Parallel() mux, client := setup(t) mux.HandleFunc("/api/v4/groups/1/protected_branches/main", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprint(w, `{ "id":1, "name":"main", "push_access_levels":[{ "id":1, "access_level":40, "access_level_description":"Maintainers", "user_id":null, "group_id":null }], "merge_access_levels":[{ "id":1, "access_level":40, "access_level_description":"Maintainers", "user_id":null, "group_id":null }], "code_owner_approval_required":false }`) }) protectedBranch, resp, err := client.GroupProtectedBranches.GetProtectedBranch(1, "main") assert.NoError(t, err) assert.NotNil(t, resp) want := &GroupProtectedBranch{ ID: 1, Name: "main", PushAccessLevels: []*GroupBranchAccessDescription{ { ID: 1, AccessLevel: 40, AccessLevelDescription: "Maintainers", }, }, MergeAccessLevels: []*GroupBranchAccessDescription{ { ID: 1, AccessLevel: 40, AccessLevelDescription: "Maintainers", }, }, AllowForcePush: false, CodeOwnerApprovalRequired: false, } assert.Equal(t, want, protectedBranch) } func TestGroupProtectRepositoryBranches(t *testing.T) { t.Parallel() mux, client := setup(t) mux.HandleFunc("/api/v4/groups/1/protected_branches", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPost) fmt.Fprint(w, ` { "id":1, "name":"master", "push_access_levels":[{ "access_level":40, "access_level_description":"Maintainers" }], "merge_access_levels":[{ "access_level":40, "access_level_description":"Maintainers" }], "allow_force_push":true, "code_owner_approval_required":true }`) }) opt := &ProtectGroupRepositoryBranchesOptions{ Name: Ptr("master"), PushAccessLevel: Ptr(MaintainerPermissions), MergeAccessLevel: Ptr(MaintainerPermissions), AllowForcePush: Ptr(true), CodeOwnerApprovalRequired: Ptr(true), } protectedBranches, resp, err := client.GroupProtectedBranches.ProtectRepositoryBranches("1", opt) assert.NoError(t, err) assert.NotNil(t, resp) want := &GroupProtectedBranch{ ID: 1, Name: "master", PushAccessLevels: []*GroupBranchAccessDescription{ { AccessLevel: 40, AccessLevelDescription: "Maintainers", }, }, MergeAccessLevels: []*GroupBranchAccessDescription{ { AccessLevel: 40, AccessLevelDescription: "Maintainers", }, }, AllowForcePush: true, CodeOwnerApprovalRequired: true, } assert.Equal(t, want, protectedBranches) } func TestGroupUnprotectRepositoryBranches(t *testing.T) { t.Parallel() mux, client := setup(t) mux.HandleFunc("/api/v4/groups/1/protected_branches/main", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodDelete) }) resp, err := client.GroupProtectedBranches.UnprotectRepositoryBranches("1", "main") assert.NoError(t, err) assert.NotNil(t, resp) } func TestGroupUpdateProtectedBranch(t *testing.T) { t.Parallel() mux, client := setup(t) mux.HandleFunc("/api/v4/groups/1/protected_branches/master", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPatch) testBodyJSON(t, r, map[string]bool{ "code_owner_approval_required": true, }) fmt.Fprintf(w, `{ "name": "master", "code_owner_approval_required": true }`) }) opt := &UpdateGroupProtectedBranchOptions{ CodeOwnerApprovalRequired: Ptr(true), } protectedBranch, resp, err := client.GroupProtectedBranches.UpdateProtectedBranch("1", "master", opt) assert.NoError(t, err) assert.NotNil(t, resp) want := &GroupProtectedBranch{ Name: "master", CodeOwnerApprovalRequired: true, } assert.Equal(t, want, protectedBranch) } Loading
gitlab.go +2 −0 Original line number Diff line number Diff line Loading @@ -198,6 +198,7 @@ type Client struct { GroupMembers GroupMembersServiceInterface GroupMilestones GroupMilestonesServiceInterface GroupProtectedEnvironments GroupProtectedEnvironmentsServiceInterface GroupProtectedBranches GroupProtectedBranchesServiceInterface GroupRelationsExport GroupRelationsExportServiceInterface GroupReleases GroupReleasesServiceInterface GroupRepositoryStorageMove GroupRepositoryStorageMoveServiceInterface Loading Loading @@ -517,6 +518,7 @@ func NewAuthSourceClient(as AuthSource, options ...ClientOptionFunc) (*Client, e c.GroupMembers = &GroupMembersService{client: c} c.GroupMilestones = &GroupMilestonesService{client: c} c.GroupProtectedEnvironments = &GroupProtectedEnvironmentsService{client: c} c.GroupProtectedBranches = &GroupProtectedBranchesService{client: c} c.GroupRelationsExport = &GroupRelationsExportService{client: c} c.GroupReleases = &GroupReleasesService{client: c} c.GroupRepositoryStorageMove = &GroupRepositoryStorageMoveService{client: c} Loading
gitlab_service_map_generated_test.go +1 −0 Original line number Diff line number Diff line Loading @@ -61,6 +61,7 @@ var serviceMap = map[any]any{ &GroupMarkdownUploadsService{}: (*GroupMarkdownUploadsServiceInterface)(nil), &GroupMembersService{}: (*GroupMembersServiceInterface)(nil), &GroupMilestonesService{}: (*GroupMilestonesServiceInterface)(nil), &GroupProtectedBranchesService{}: (*GroupProtectedBranchesServiceInterface)(nil), &GroupProtectedEnvironmentsService{}: (*GroupProtectedEnvironmentsServiceInterface)(nil), &GroupRelationsExportService{}: (*GroupRelationsExportServiceInterface)(nil), &GroupReleasesService{}: (*GroupReleasesServiceInterface)(nil), Loading
gitlab_test/groups_integration_test.go +59 −0 Original line number Diff line number Diff line Loading @@ -107,3 +107,62 @@ func Test_GroupsMaxArtifactsSize_Integration(t *testing.T) { // THEN MaxArtifactsSize should persist assert.Equal(t, int64(100), retrievedGroup.MaxArtifactsSize) } func Test_GroupProtectedBranches_Integration(t *testing.T) { // GIVEN a GitLab client and a test group client := SetupIntegrationClient(t) group := CreateTestGroup(t, client) // Define branch name branchName := "main" // WHEN protecting a branch protectedBranch, _, err := client.GroupProtectedBranches.ProtectRepositoryBranches(group.ID, &gitlab.ProtectGroupRepositoryBranchesOptions{ Name: gitlab.Ptr(branchName), PushAccessLevel: gitlab.Ptr(gitlab.MaintainerPermissions), MergeAccessLevel: gitlab.Ptr(gitlab.MaintainerPermissions), }) require.NoError(t, err, "Failed to protect branch") // THEN the branch should be protected assert.Equal(t, branchName, protectedBranch.Name) // WHEN listing protected branches branches, _, err := client.GroupProtectedBranches.ListProtectedBranches(group.ID, nil) require.NoError(t, err, "Failed to list protected branches") // THEN the protected branch should be in the list found := false for _, b := range branches { if b.Name == branchName { found = true break } } assert.True(t, found, "Protected branch not found in list") // WHEN getting the protected branch gotBranch, _, err := client.GroupProtectedBranches.GetProtectedBranch(group.ID, branchName) require.NoError(t, err, "Failed to get protected branch") // THEN it should match assert.Equal(t, branchName, gotBranch.Name) // WHEN updating the protected branch updatedBranch, _, err := client.GroupProtectedBranches.UpdateProtectedBranch(group.ID, branchName, &gitlab.UpdateGroupProtectedBranchOptions{ AllowForcePush: gitlab.Ptr(true), }) require.NoError(t, err, "Failed to update protected branch") // THEN the update should be reflected assert.True(t, updatedBranch.AllowForcePush) // WHEN unprotecting the branch _, err = client.GroupProtectedBranches.UnprotectRepositoryBranches(group.ID, branchName) require.NoError(t, err, "Failed to unprotect branch") // THEN getting the branch should fail (404) _, resp, err := client.GroupProtectedBranches.GetProtectedBranch(group.ID, branchName) assert.Error(t, err) assert.Equal(t, 404, resp.StatusCode) }
group_protected_branches.go 0 → 100644 +177 −0 Original line number Diff line number Diff line package gitlab import ( "net/http" "net/url" ) type ( GroupProtectedBranchesServiceInterface interface { // ListProtectedBranches returns a list of protected branches from a group. // // GitLab API docs: // https://docs.gitlab.com/api/group_protected_branches/#list-protected-branches ListProtectedBranches(gid any, opt *ListGroupProtectedBranchesOptions, options ...RequestOptionFunc) ([]*GroupProtectedBranch, *Response, error) // GetProtectedBranch returns a single group-level protected branch. // // GitLab API docs: // https://docs.gitlab.com/api/group_protected_branches/#get-a-single-protected-branch-or-wildcard-protected-branch GetProtectedBranch(gid any, branch string, options ...RequestOptionFunc) (*GroupProtectedBranch, *Response, error) // ProtectRepositoryBranches protects a single group-level branch. // // GitLab API docs: // https://docs.gitlab.com/api/group_protected_branches/#protect-repository-branches ProtectRepositoryBranches(gid any, opt *ProtectGroupRepositoryBranchesOptions, options ...RequestOptionFunc) (*GroupProtectedBranch, *Response, error) // UpdateProtectedBranch updates a single group-level protected branch. // // GitLab API docs: // https://docs.gitlab.com/api/group_protected_branches/#update-a-protected-branch UpdateProtectedBranch(gid any, branch string, opt *UpdateGroupProtectedBranchOptions, options ...RequestOptionFunc) (*GroupProtectedBranch, *Response, error) // UnprotectRepositoryBranches unprotects the given protected group-level branch. // // GitLab API docs: // https://docs.gitlab.com/api/group_protected_branches/#unprotect-repository-branches UnprotectRepositoryBranches(gid any, branch string, options ...RequestOptionFunc) (*Response, error) } // GroupProtectedBranchesService handles communication with the group-level // protected branch methods of the GitLab API. // // GitLab API docs: // https://docs.gitlab.com/api/group_protected_branches/ GroupProtectedBranchesService struct { client *Client } ) var _ GroupProtectedBranchesServiceInterface = (*GroupProtectedBranchesService)(nil) // GroupProtectedBranch represents a group protected branch. // // GitLab API docs: // https://docs.gitlab.com/api/group_protected_branches/#list-protected-branches type GroupProtectedBranch struct { ID int64 `json:"id"` Name string `json:"name"` PushAccessLevels []*GroupBranchAccessDescription `json:"push_access_levels"` MergeAccessLevels []*GroupBranchAccessDescription `json:"merge_access_levels"` UnprotectAccessLevels []*GroupBranchAccessDescription `json:"unprotect_access_levels"` AllowForcePush bool `json:"allow_force_push"` CodeOwnerApprovalRequired bool `json:"code_owner_approval_required"` } // GroupBranchAccessDescription represents the access description for a group protected // branch. // // GitLab API docs: // https://docs.gitlab.com/api/group_protected_branches/#list-protected-branches type GroupBranchAccessDescription struct { ID int64 `json:"id"` AccessLevel AccessLevelValue `json:"access_level"` AccessLevelDescription string `json:"access_level_description"` DeployKeyID int64 `json:"deploy_key_id"` UserID int64 `json:"user_id"` GroupID int64 `json:"group_id"` } // ListGroupProtectedBranchesOptions represents the available ListProtectedBranches() // options. // // GitLab API docs: // https://docs.gitlab.com/api/group_protected_branches/#list-protected-branches type ListGroupProtectedBranchesOptions struct { ListOptions Search *string `url:"search,omitempty" json:"search,omitempty"` } func (s *GroupProtectedBranchesService) ListProtectedBranches(gid any, opt *ListGroupProtectedBranchesOptions, options ...RequestOptionFunc) ([]*GroupProtectedBranch, *Response, error) { return do[[]*GroupProtectedBranch](s.client, withMethod(http.MethodGet), withPath("groups/%s/protected_branches", GroupID{gid}), withAPIOpts(opt), withRequestOpts(options...), ) } func (s *GroupProtectedBranchesService) GetProtectedBranch(gid any, branch string, options ...RequestOptionFunc) (*GroupProtectedBranch, *Response, error) { return do[*GroupProtectedBranch](s.client, withMethod(http.MethodGet), withPath("groups/%s/protected_branches/%s", GroupID{gid}, url.PathEscape(branch)), withRequestOpts(options...), ) } // ProtectGroupRepositoryBranchesOptions represents the available // ProtectRepositoryBranches() options. // // GitLab API docs: // https://docs.gitlab.com/api/group_protected_branches/#protect-repository-branches type ProtectGroupRepositoryBranchesOptions struct { Name *string `url:"name,omitempty" json:"name,omitempty"` PushAccessLevel *AccessLevelValue `url:"push_access_level,omitempty" json:"push_access_level,omitempty"` MergeAccessLevel *AccessLevelValue `url:"merge_access_level,omitempty" json:"merge_access_level,omitempty"` UnprotectAccessLevel *AccessLevelValue `url:"unprotect_access_level,omitempty" json:"unprotect_access_level,omitempty"` AllowForcePush *bool `url:"allow_force_push,omitempty" json:"allow_force_push,omitempty"` AllowedToPush *[]*GroupBranchPermissionOptions `url:"allowed_to_push,omitempty" json:"allowed_to_push,omitempty"` AllowedToMerge *[]*GroupBranchPermissionOptions `url:"allowed_to_merge,omitempty" json:"allowed_to_merge,omitempty"` AllowedToUnprotect *[]*GroupBranchPermissionOptions `url:"allowed_to_unprotect,omitempty" json:"allowed_to_unprotect,omitempty"` CodeOwnerApprovalRequired *bool `url:"code_owner_approval_required,omitempty" json:"code_owner_approval_required,omitempty"` } // GroupBranchPermissionOptions represents a branch permission option. // // GitLab API docs: // https://docs.gitlab.com/api/group_protected_branches/#protect-repository-branches type GroupBranchPermissionOptions struct { ID *int64 `url:"id,omitempty" json:"id,omitempty"` UserID *int64 `url:"user_id,omitempty" json:"user_id,omitempty"` GroupID *int64 `url:"group_id,omitempty" json:"group_id,omitempty"` DeployKeyID *int64 `url:"deploy_key_id,omitempty" json:"deploy_key_id,omitempty"` AccessLevel *AccessLevelValue `url:"access_level,omitempty" json:"access_level,omitempty"` Destroy *bool `url:"_destroy,omitempty" json:"_destroy,omitempty"` } func (s *GroupProtectedBranchesService) ProtectRepositoryBranches(gid any, opt *ProtectGroupRepositoryBranchesOptions, options ...RequestOptionFunc) (*GroupProtectedBranch, *Response, error) { return do[*GroupProtectedBranch](s.client, withMethod(http.MethodPost), withPath("groups/%s/protected_branches", GroupID{gid}), withAPIOpts(opt), withRequestOpts(options...), ) } // UpdateGroupProtectedBranchOptions represents the available // UpdateProtectedBranch() options. // // GitLab API docs: // https://docs.gitlab.com/api/group_protected_branches/#update-a-protected-branch type UpdateGroupProtectedBranchOptions struct { Name *string `url:"name,omitempty" json:"name,omitempty"` AllowForcePush *bool `url:"allow_force_push,omitempty" json:"allow_force_push,omitempty"` CodeOwnerApprovalRequired *bool `url:"code_owner_approval_required,omitempty" json:"code_owner_approval_required,omitempty"` AllowedToPush *[]*GroupBranchPermissionOptions `url:"allowed_to_push,omitempty" json:"allowed_to_push,omitempty"` AllowedToMerge *[]*GroupBranchPermissionOptions `url:"allowed_to_merge,omitempty" json:"allowed_to_merge,omitempty"` AllowedToUnprotect *[]*GroupBranchPermissionOptions `url:"allowed_to_unprotect,omitempty" json:"allowed_to_unprotect,omitempty"` } func (s *GroupProtectedBranchesService) UpdateProtectedBranch(gid any, branch string, opt *UpdateGroupProtectedBranchOptions, options ...RequestOptionFunc) (*GroupProtectedBranch, *Response, error) { return do[*GroupProtectedBranch](s.client, withMethod(http.MethodPatch), withPath("groups/%s/protected_branches/%s", GroupID{gid}, url.PathEscape(branch)), withAPIOpts(opt), withRequestOpts(options...), ) } func (s *GroupProtectedBranchesService) UnprotectRepositoryBranches(gid any, branch string, options ...RequestOptionFunc) (*Response, error) { _, resp, err := do[none](s.client, withMethod(http.MethodDelete), withPath("groups/%s/protected_branches/%s", GroupID{gid}, url.PathEscape(branch)), withRequestOpts(options...), ) return resp, err }
group_protected_branches_test.go 0 → 100644 +223 −0 Original line number Diff line number Diff line package gitlab import ( "fmt" "net/http" "testing" "github.com/stretchr/testify/assert" ) func TestGroupListProtectedBranches(t *testing.T) { t.Parallel() mux, client := setup(t) mux.HandleFunc("/api/v4/groups/1/protected_branches", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprint(w, `[ { "id":1, "name":"master", "push_access_levels":[{ "id":1, "access_level":40, "access_level_description":"Maintainers", "user_id":null, "group_id":null },{ "id":2, "access_level":30, "access_level_description":"User name", "user_id":123, "group_id":null }], "merge_access_levels":[{ "id":1, "access_level":40, "access_level_description":"Maintainers", "user_id":null, "group_id":null }], "code_owner_approval_required":false } ]`) }) opt := &ListGroupProtectedBranchesOptions{} protectedBranches, resp, err := client.GroupProtectedBranches.ListProtectedBranches("1", opt) assert.NoError(t, err) assert.NotNil(t, resp) want := []*GroupProtectedBranch{ { ID: 1, Name: "master", PushAccessLevels: []*GroupBranchAccessDescription{ { ID: 1, AccessLevel: 40, AccessLevelDescription: "Maintainers", }, { ID: 2, AccessLevel: 30, AccessLevelDescription: "User name", UserID: 123, }, }, MergeAccessLevels: []*GroupBranchAccessDescription{ { ID: 1, AccessLevel: 40, AccessLevelDescription: "Maintainers", }, }, AllowForcePush: false, CodeOwnerApprovalRequired: false, }, } assert.Equal(t, want, protectedBranches) } func TestGroupGetProtectedBranch(t *testing.T) { t.Parallel() mux, client := setup(t) mux.HandleFunc("/api/v4/groups/1/protected_branches/main", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprint(w, `{ "id":1, "name":"main", "push_access_levels":[{ "id":1, "access_level":40, "access_level_description":"Maintainers", "user_id":null, "group_id":null }], "merge_access_levels":[{ "id":1, "access_level":40, "access_level_description":"Maintainers", "user_id":null, "group_id":null }], "code_owner_approval_required":false }`) }) protectedBranch, resp, err := client.GroupProtectedBranches.GetProtectedBranch(1, "main") assert.NoError(t, err) assert.NotNil(t, resp) want := &GroupProtectedBranch{ ID: 1, Name: "main", PushAccessLevels: []*GroupBranchAccessDescription{ { ID: 1, AccessLevel: 40, AccessLevelDescription: "Maintainers", }, }, MergeAccessLevels: []*GroupBranchAccessDescription{ { ID: 1, AccessLevel: 40, AccessLevelDescription: "Maintainers", }, }, AllowForcePush: false, CodeOwnerApprovalRequired: false, } assert.Equal(t, want, protectedBranch) } func TestGroupProtectRepositoryBranches(t *testing.T) { t.Parallel() mux, client := setup(t) mux.HandleFunc("/api/v4/groups/1/protected_branches", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPost) fmt.Fprint(w, ` { "id":1, "name":"master", "push_access_levels":[{ "access_level":40, "access_level_description":"Maintainers" }], "merge_access_levels":[{ "access_level":40, "access_level_description":"Maintainers" }], "allow_force_push":true, "code_owner_approval_required":true }`) }) opt := &ProtectGroupRepositoryBranchesOptions{ Name: Ptr("master"), PushAccessLevel: Ptr(MaintainerPermissions), MergeAccessLevel: Ptr(MaintainerPermissions), AllowForcePush: Ptr(true), CodeOwnerApprovalRequired: Ptr(true), } protectedBranches, resp, err := client.GroupProtectedBranches.ProtectRepositoryBranches("1", opt) assert.NoError(t, err) assert.NotNil(t, resp) want := &GroupProtectedBranch{ ID: 1, Name: "master", PushAccessLevels: []*GroupBranchAccessDescription{ { AccessLevel: 40, AccessLevelDescription: "Maintainers", }, }, MergeAccessLevels: []*GroupBranchAccessDescription{ { AccessLevel: 40, AccessLevelDescription: "Maintainers", }, }, AllowForcePush: true, CodeOwnerApprovalRequired: true, } assert.Equal(t, want, protectedBranches) } func TestGroupUnprotectRepositoryBranches(t *testing.T) { t.Parallel() mux, client := setup(t) mux.HandleFunc("/api/v4/groups/1/protected_branches/main", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodDelete) }) resp, err := client.GroupProtectedBranches.UnprotectRepositoryBranches("1", "main") assert.NoError(t, err) assert.NotNil(t, resp) } func TestGroupUpdateProtectedBranch(t *testing.T) { t.Parallel() mux, client := setup(t) mux.HandleFunc("/api/v4/groups/1/protected_branches/master", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPatch) testBodyJSON(t, r, map[string]bool{ "code_owner_approval_required": true, }) fmt.Fprintf(w, `{ "name": "master", "code_owner_approval_required": true }`) }) opt := &UpdateGroupProtectedBranchOptions{ CodeOwnerApprovalRequired: Ptr(true), } protectedBranch, resp, err := client.GroupProtectedBranches.UpdateProtectedBranch("1", "master", opt) assert.NoError(t, err) assert.NotNil(t, resp) want := &GroupProtectedBranch{ Name: "master", CodeOwnerApprovalRequired: true, } assert.Equal(t, want, protectedBranch) }