Repository: xanzy/go-gitlab
Pull request #1400 (2022-03-02 21:56:27)

Implement user `ban` and `unban` APIs
See https://docs.gitlab.com/ee/api/users.html#ban-user


Patch:
diff --git a/commits.go b/commits.go
index 7ff5c38..fd26624 100644
--- a/commits.go
+++ b/commits.go
@@ -399,7 +399,6 @@ type CommitStatus struct {
 	FinishedAt   *time.Time `json:"finished_at"`
 	Name         string     `json:"name"`
 	AllowFailure bool       `json:"allow_failure"`
-	Coverage     float64    `json:"coverage"`
 	Author       Author     `json:"author"`
 	Description  string     `json:"description"`
 	TargetURL    string     `json:"target_url"`
diff --git a/groups.go b/groups.go
index 28174cb..2383ca8 100644
--- a/groups.go
+++ b/groups.go
@@ -47,7 +47,6 @@ type Group struct {
 	RequestAccessEnabled    bool                       `json:"request_access_enabled"`
 	FullName                string                     `json:"full_name"`
 	FullPath                string                     `json:"full_path"`
-	FileTemplateProjectID   int                        `json:"file_template_project_id"`
 	ParentID                int                        `json:"parent_id"`
 	Projects                []*Project                 `json:"projects"`
 	Statistics              *StorageStatistics         `json:"statistics"`
@@ -74,7 +73,6 @@ type Group struct {
 	LDAPGroupLinks                 []*LDAPGroupLink `json:"ldap_group_links"`
 	SharedRunnersMinutesLimit      int              `json:"shared_runners_minutes_limit"`
 	ExtraSharedRunnersMinutesLimit int              `json:"extra_shared_runners_minutes_limit"`
-	PreventForkingOutsideGroup     bool             `json:"prevent_forking_outside_group"`
 	MarkedForDeletionOn            *ISOTime         `json:"marked_for_deletion_on"`
 	CreatedAt                      *time.Time       `json:"created_at"`
 }
diff --git a/projects.go b/projects.go
index 81def90..baf3b31 100644
--- a/projects.go
+++ b/projects.go
@@ -1746,12 +1746,11 @@ func (s *ProjectsService) GetProjectApprovalRules(pid interface{}, options ...Re
 // GitLab API docs:
 // https://docs.gitlab.com/ee/api/merge_request_approvals.html#create-project-level-rules
 type CreateProjectLevelRuleOptions struct {
-	Name               *string `url:"name,omitempty" json:"name,omitempty"`
 	ApprovalsRequired  *int    `url:"approvals_required,omitempty" json:"approvals_required,omitempty"`
-	RuleType           *string `url:"rule_type,omitempty" json:"rule_type,omitempty"`
-	UserIDs            *[]int  `url:"user_ids,omitempty" json:"user_ids,omitempty"`
 	GroupIDs           *[]int  `url:"group_ids,omitempty" json:"group_ids,omitempty"`
+	Name               *string `url:"name,omitempty" json:"name,omitempty"`
 	ProtectedBranchIDs *[]int  `url:"protected_branch_ids,omitempty" json:"protected_branch_ids,omitempty"`
+	UserIDs            *[]int  `url:"user_ids,omitempty" json:"user_ids,omitempty"`
 }
 
 // CreateProjectApprovalRule creates a new project-level approval rule.
@@ -1785,11 +1784,11 @@ func (s *ProjectsService) CreateProjectApprovalRule(pid interface{}, opt *Create
 // GitLab API docs:
 // https://docs.gitlab.com/ee/api/merge_request_approvals.html#update-project-level-rules
 type UpdateProjectLevelRuleOptions struct {
-	Name               *string `url:"name,omitempty" json:"name,omitempty"`
 	ApprovalsRequired  *int    `url:"approvals_required,omitempty" json:"approvals_required,omitempty"`
-	UserIDs            *[]int  `url:"user_ids,omitempty" json:"user_ids,omitempty"`
 	GroupIDs           *[]int  `url:"group_ids,omitempty" json:"group_ids,omitempty"`
+	Name               *string `url:"name,omitempty" json:"name,omitempty"`
 	ProtectedBranchIDs *[]int  `url:"protected_branch_ids,omitempty" json:"protected_branch_ids,omitempty"`
+	UserIDs            *[]int  `url:"user_ids,omitempty" json:"user_ids,omitempty"`
 }
 
 // UpdateProjectApprovalRule updates an existing approval rule with new options.
diff --git a/users.go b/users.go
index c16b07e..5c7e2b0 100644
--- a/users.go
+++ b/users.go
@@ -921,6 +921,58 @@ func (s *UsersService) UnblockUser(user int, options ...RequestOptionFunc) error
 	}
 }
 
+// BanUser bans the specified user. Available only for admin.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/users.html#ban-user
+func (s *UsersService) BanUser(user int, options ...RequestOptionFunc) error {
+	u := fmt.Sprintf("users/%d/ban", user)
+
+	req, err := s.client.NewRequest(http.MethodPost, u, nil, options)
+	if err != nil {
+		return err
+	}
+
+	resp, err := s.client.Do(req, nil)
+	if err != nil && resp == nil {
+		return err
+	}
+
+	switch resp.StatusCode {
+	case 201:
+		return nil
+	case 404:
+		return ErrUserNotFound
+	default:
+		return fmt.Errorf("Received unexpected result code: %d", resp.StatusCode)
+	}
+}
+
+// UnbanUser unbans the specified user. Available only for admin.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/users.html#unban-user
+func (s *UsersService) UnbanUser(user int, options ...RequestOptionFunc) error {
+	u := fmt.Sprintf("users/%d/unban", user)
+
+	req, err := s.client.NewRequest(http.MethodPost, u, nil, options)
+	if err != nil {
+		return err
+	}
+
+	resp, err := s.client.Do(req, nil)
+	if err != nil && resp == nil {
+		return err
+	}
+
+	switch resp.StatusCode {
+	case 201:
+		return nil
+	case 404:
+		return ErrUserNotFound
+	default:
+		return fmt.Errorf("Received unexpected result code: %d", resp.StatusCode)
+	}
+}
+
 // DeactivateUser deactivate the specified user. Available only for admin.
 //
 // GitLab API docs: https://docs.gitlab.com/ce/api/users.html#deactivate-user


Test patch:
diff --git a/groups_test.go b/groups_test.go
index ccf0d82..e2e2b87 100644
--- a/groups_test.go
+++ b/groups_test.go
@@ -49,27 +49,6 @@ func TestGetGroup(t *testing.T) {
 	}
 }
 
-func TestGetGroupWithFileTemplateId(t *testing.T) {
-	mux, server, client := setup(t)
-	defer teardown(server)
-
-	mux.HandleFunc("/api/v4/groups/g",
-		func(w http.ResponseWriter, r *http.Request) {
-			testMethod(t, r, http.MethodGet)
-			fmt.Fprint(w, `{"id": 1, "name": "g","file_template_project_id": 12345}`)
-		})
-
-	group, _, err := client.Groups.GetGroup("g", &GetGroupOptions{})
-	if err != nil {
-		t.Errorf("Groups.GetGroup returned error: %v", err)
-	}
-
-	want := &Group{ID: 1, Name: "g", FileTemplateProjectID: 12345}
-	if !reflect.DeepEqual(want, group) {
-		t.Errorf("Groups.GetGroup returned %+v, want %+v", group, want)
-	}
-}
-
 func TestCreateGroup(t *testing.T) {
 	mux, server, client := setup(t)
 	defer teardown(server)
diff --git a/projects_test.go b/projects_test.go
index 63c82ad..90815e7 100644
--- a/projects_test.go
+++ b/projects_test.go
@@ -1130,48 +1130,3 @@ func TestCreateProjectApprovalRule(t *testing.T) {
 		t.Errorf("Projects.CreateProjectApprovalRule returned %+v, want %+v", rule, want)
 	}
 }
-
-func TestCreateProjectApprovalRuleEligibleApprovers(t *testing.T) {
-	mux, server, client := setup(t)
-	defer teardown(server)
-
-	mux.HandleFunc("/api/v4/projects/1/approval_rules", func(w http.ResponseWriter, r *http.Request) {
-		testMethod(t, r, http.MethodPost)
-		fmt.Fprint(w, `{
-			"id": 1,
-			"name": "Any name",
-			"rule_type": "any_approver",
-			"eligible_approvers": [],
-			"approvals_required": 1,
-			"users": [],
-			"groups": [],
-			"contains_hidden_groups": false,
-			"protected_branches": []
-		}`)
-	})
-
-	opt := &CreateProjectLevelRuleOptions{
-		Name:              String("Any name"),
-		ApprovalsRequired: Int(1),
-	}
-
-	rule, _, err := client.Projects.CreateProjectApprovalRule(1, opt)
-	if err != nil {
-		t.Errorf("Projects.CreateProjectApprovalRule returned error: %v", err)
-	}
-
-	want := &ProjectApprovalRule{
-		ID:       1,
-		Name:     "Any name",
-		RuleType: "any_approver",
-		EligibleApprovers: []*BasicUser{},
-		ApprovalsRequired: 1,
-		Users: []*BasicUser{},
-		Groups: []*Group{},
-		ProtectedBranches: []*ProtectedBranch{},
-	}
-
-	if !reflect.DeepEqual(want, rule) {
-		t.Errorf("Projects.CreateProjectApprovalRule returned %+v, want %+v", rule, want)
-	}
-}
diff --git a/users_test.go b/users_test.go
index 6f98e47..ddb9f7e 100644
--- a/users_test.go
+++ b/users_test.go
@@ -250,6 +250,106 @@ func TestUnblockUser_UnknownError(t *testing.T) {
 	}
 }
 
+func TestBanUser(t *testing.T) {
+	mux, server, client := setup(t)
+	defer teardown(server)
+
+	path := fmt.Sprintf("/%susers/1/block", apiVersionPath)
+	mux.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, http.MethodPost)
+		w.WriteHeader(http.StatusCreated)
+	})
+
+	err := client.Users.BlockUser(1)
+	if err != nil {
+		t.Errorf("Users.BlockUser returned error: %v", err)
+	}
+}
+
+func TestBanUser_UserNotFound(t *testing.T) {
+	mux, server, client := setup(t)
+	defer teardown(server)
+
+	path := fmt.Sprintf("/%susers/1/ban", apiVersionPath)
+	mux.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, http.MethodPost)
+		w.WriteHeader(http.StatusNotFound)
+	})
+
+	err := client.Users.BanUser(1)
+	if !errors.Is(err, ErrUserNotFound) {
+		t.Errorf("Users.BanUser error.\nExpected: %+v\nGot: %+v", ErrUserNotFound, err)
+	}
+}
+
+func TestBanUser_UnknownError(t *testing.T) {
+	mux, server, client := setup(t)
+	defer teardown(server)
+
+	path := fmt.Sprintf("/%susers/1/ban", apiVersionPath)
+	mux.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, http.MethodPost)
+		w.WriteHeader(http.StatusTeapot)
+	})
+
+	want := fmt.Sprintf("Received unexpected result code: %d", http.StatusTeapot)
+
+	err := client.Users.BanUser(1)
+	if err.Error() != want {
+		t.Errorf("Users.BanUSer error.\nExpected: %s\nGot: %v", want, err)
+	}
+}
+
+func TestUnbanUser(t *testing.T) {
+	mux, server, client := setup(t)
+	defer teardown(server)
+
+	path := fmt.Sprintf("/%susers/1/unban", apiVersionPath)
+	mux.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, http.MethodPost)
+		w.WriteHeader(http.StatusCreated)
+	})
+
+	err := client.Users.UnbanUser(1)
+	if err != nil {
+		t.Errorf("Users.UnbanUser returned error: %v", err)
+	}
+}
+
+func TestUnbanUser_UserNotFound(t *testing.T) {
+	mux, server, client := setup(t)
+	defer teardown(server)
+
+	path := fmt.Sprintf("/%susers/1/unban", apiVersionPath)
+	mux.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, http.MethodPost)
+		w.WriteHeader(http.StatusNotFound)
+	})
+
+	err := client.Users.UnbanUser(1)
+	if !errors.Is(err, ErrUserNotFound) {
+		t.Errorf("Users.UnbanUser error.\nExpected: %v\nGot: %v", ErrUserNotFound, err)
+	}
+}
+
+func TestUnbanUser_UnknownError(t *testing.T) {
+	mux, server, client := setup(t)
+	defer teardown(server)
+
+	path := fmt.Sprintf("/%susers/1/unban", apiVersionPath)
+	mux.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, http.MethodPost)
+		w.WriteHeader(http.StatusTeapot)
+	})
+
+	want := fmt.Sprintf("Received unexpected result code: %d", http.StatusTeapot)
+
+	err := client.Users.UnbanUser(1)
+	if err.Error() != want {
+		t.Errorf("Users.UnbanUser error.\nExpected: %s\n\tGot: %v", want, err)
+	}
+}
+
 func TestDeactivateUser(t *testing.T) {
 	mux, server, client := setup(t)
 	defer teardown(server)