Repository: xanzy/go-gitlab
Pull request #1612 (2022-12-25 12:35:55)

Update the protected branches API
I marked the two functions `BranchesService.ProtectBranch()` & `BranchesService.UnprotectBranch()` as deprecated, since they are replaced by the protected branches API.

In the `ProtectedBranchesService` I added some missing options, and renamed the `RequireCodeOwnerApprovals()` function to `UpdateProtectedBranch()` which has some additional options. (I kept the `RequireCodeOwnerApprovals()` function and marked it as deprecated.)


Patch:
diff --git a/branches.go b/branches.go
index c993003..57ccdc0 100644
--- a/branches.go
+++ b/branches.go
@@ -122,6 +122,9 @@ type ProtectBranchOptions struct {
 // idempotent function, protecting an already protected repository branch
 // still returns a 200 OK status code.
 //
+// Deprecated: This endpoint has been replaced by
+// ProtectedBranchesService.ProtectRepositoryBranches()
+//
 // GitLab API docs:
 // https://docs.gitlab.com/ee/api/branches.html#protect-repository-branch
 func (s *BranchesService) ProtectBranch(pid interface{}, branch string, opts *ProtectBranchOptions, options ...RequestOptionFunc) (*Branch, *Response, error) {
@@ -149,6 +152,9 @@ func (s *BranchesService) ProtectBranch(pid interface{}, branch string, opts *Pr
 // idempotent function, unprotecting an already unprotected repository branch
 // still returns a 200 OK status code.
 //
+// Deprecated: This endpoint has been replaced by
+// ProtectedBranchesService.UnprotectRepositoryBranches()
+//
 // GitLab API docs:
 // https://docs.gitlab.com/ee/api/branches.html#unprotect-repository-branch
 func (s *BranchesService) UnprotectBranch(pid interface{}, branch string, options ...RequestOptionFunc) (*Branch, *Response, error) {
diff --git a/protected_branches.go b/protected_branches.go
index a32e537..9dc919f 100644
--- a/protected_branches.go
+++ b/protected_branches.go
@@ -51,6 +51,7 @@ type ProtectedBranch struct {
 // GitLab API docs:
 // https://docs.gitlab.com/ee/api/protected_branches.html#list-protected-branches
 type BranchAccessDescription struct {
+	ID                     int              `json:"id"`
 	AccessLevel            AccessLevelValue `json:"access_level"`
 	AccessLevelDescription string           `json:"access_level_description"`
 	UserID                 int              `json:"user_id"`
@@ -62,7 +63,10 @@ type BranchAccessDescription struct {
 //
 // GitLab API docs:
 // https://docs.gitlab.com/ee/api/protected_branches.html#list-protected-branches
-type ListProtectedBranchesOptions ListOptions
+type ListProtectedBranchesOptions struct {
+	ListOptions
+	Search *string `url:"search,omitempty" json:"search,omitempty"`
+}
 
 // ListProtectedBranches gets a list of protected branches from a project.
 //
@@ -188,30 +192,64 @@ func (s *ProtectedBranchesService) UnprotectRepositoryBranches(pid interface{},
 	return s.client.Do(req, nil)
 }
 
-// RequireCodeOwnerApprovalsOptions represents the available
-// RequireCodeOwnerApprovals() options.
+// UpdateProtectedBranchOptions represents the available
+// UpdateProtectedBranch() options.
 //
 // GitLab API docs:
 // https://docs.gitlab.com/ee/api/protected_branches.html#update-a-protected-branch
-type RequireCodeOwnerApprovalsOptions struct {
-	CodeOwnerApprovalRequired *bool `url:"code_owner_approval_required,omitempty" json:"code_owner_approval_required,omitempty"`
+type UpdateProtectedBranchOptions 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             *[]*BranchPermissionOptions `url:"allowed_to_push,omitempty" json:"allowed_to_push,omitempty"`
+	AllowedToMerge            *[]*BranchPermissionOptions `url:"allowed_to_merge,omitempty" json:"allowed_to_merge,omitempty"`
+	AllowedToUnprotect        *[]*BranchPermissionOptions `url:"allowed_to_unprotect,omitempty" json:"allowed_to_unprotect,omitempty"`
 }
 
-// RequireCodeOwnerApprovals updates the code owner approval option.
+// UpdateProtectedBranch updates a protected branch.
 //
 // Gitlab API docs:
 // https://docs.gitlab.com/ee/api/protected_branches.html#update-a-protected-branch
-func (s *ProtectedBranchesService) RequireCodeOwnerApprovals(pid interface{}, branch string, opt *RequireCodeOwnerApprovalsOptions, options ...RequestOptionFunc) (*Response, error) {
+func (s *ProtectedBranchesService) UpdateProtectedBranch(pid interface{}, branch string, opt *UpdateProtectedBranchOptions, options ...RequestOptionFunc) (*ProtectedBranch, *Response, error) {
 	project, err := parseID(pid)
 	if err != nil {
-		return nil, err
+		return nil, nil, err
 	}
 	u := fmt.Sprintf("projects/%s/protected_branches/%s", PathEscape(project), url.PathEscape(branch))
 
 	req, err := s.client.NewRequest(http.MethodPatch, u, opt, options)
 	if err != nil {
-		return nil, err
+		return nil, nil, err
 	}
 
-	return s.client.Do(req, nil)
+	p := new(ProtectedBranch)
+	resp, err := s.client.Do(req, p)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return p, resp, err
+}
+
+// RequireCodeOwnerApprovalsOptions represents the available
+// RequireCodeOwnerApprovals() options.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ee/api/protected_branches.html#update-a-protected-branch
+type RequireCodeOwnerApprovalsOptions struct {
+	CodeOwnerApprovalRequired *bool `url:"code_owner_approval_required,omitempty" json:"code_owner_approval_required,omitempty"`
+}
+
+// RequireCodeOwnerApprovals updates the code owner approval option.
+//
+// Deprecated: Use UpdateProtectedBranch() instead.
+//
+// Gitlab API docs:
+// https://docs.gitlab.com/ee/api/protected_branches.html#update-a-protected-branch
+func (s *ProtectedBranchesService) RequireCodeOwnerApprovals(pid interface{}, branch string, opt *RequireCodeOwnerApprovalsOptions, options ...RequestOptionFunc) (*Response, error) {
+	updateOptions := &UpdateProtectedBranchOptions{
+		CodeOwnerApprovalRequired: opt.CodeOwnerApprovalRequired,
+	}
+	_, req, err := s.UpdateProtectedBranch(pid, branch, updateOptions, options...)
+	return req, err
 }


Test patch:
diff --git a/protected_branches_test.go b/protected_branches_test.go
index 25928a4..ce0d5a3 100644
--- a/protected_branches_test.go
+++ b/protected_branches_test.go
@@ -33,10 +33,12 @@ func TestListProtectedBranches(t *testing.T) {
 		"id":1,
 		"name":"master",
 		"push_access_levels":[{
+			"id":1,
 			"access_level":40,
 			"access_level_description":"Maintainers"
 		}],
 		"merge_access_levels":[{
+			"id":1,
 			"access_level":40,
 			"access_level_description":"Maintainers"
 		}],
@@ -55,12 +57,14 @@ func TestListProtectedBranches(t *testing.T) {
 			Name: "master",
 			PushAccessLevels: []*BranchAccessDescription{
 				{
+					ID:                     1,
 					AccessLevel:            40,
 					AccessLevelDescription: "Maintainers",
 				},
 			},
 			MergeAccessLevels: []*BranchAccessDescription{
 				{
+					ID:                     1,
 					AccessLevel:            40,
 					AccessLevelDescription: "Maintainers",
 				},
@@ -188,12 +192,25 @@ func TestUpdateRepositoryBranches(t *testing.T) {
 		if codeApprovalQueryParam != "true" {
 			t.Errorf("query param code_owner_approval_required should be true but was %s", codeApprovalQueryParam)
 		}
+		fmt.Fprintf(w, `{
+			"name": "master",
+			"code_owner_approval_required": true
+		}`)
 	})
-	opt := &RequireCodeOwnerApprovalsOptions{
+	opt := &UpdateProtectedBranchOptions{
 		CodeOwnerApprovalRequired: Bool(true),
 	}
-	_, err := client.ProtectedBranches.RequireCodeOwnerApprovals("1", "master", opt)
+	protectedBranch, _, err := client.ProtectedBranches.UpdateProtectedBranch("1", "master", opt)
 	if err != nil {
-		t.Errorf("ProtectedBranches.UpdateRepositoryBranchesOptions returned error: %v", err)
+		t.Errorf("ProtectedBranches.UpdateProtectedBranch returned error: %v", err)
+	}
+
+	want := &ProtectedBranch{
+		Name:                      "master",
+		CodeOwnerApprovalRequired: true,
+	}
+
+	if !reflect.DeepEqual(want, protectedBranch) {
+		t.Errorf("ProtectedBranches.UpdateProtectedBranch returned %+v, want %+v", protectedBranch, want)
 	}
 }