diff --git a/merge_request_approvals.go b/merge_request_approvals.go index ff4ef8868..60fd52932 100644 --- a/merge_request_approvals.go +++ b/merge_request_approvals.go @@ -74,6 +74,17 @@ type MergeRequestApprovalRule struct { Users []*BasicUser `json:"users"` Groups []*Group `json:"groups"` ContainsHiddenGroups bool `json:"contains_hidden_groups"` + ApprovedBy []*BasicUser `json:"approved_by"` + Approved bool `json:"approved"` +} + +// MergeRequestApprovalState represents a GitLab merge request approval state. +// +// GitLab API docs: +// https://docs.gitlab.com/ee/api/merge_request_approvals.html#get-the-approval-state-of-merge-requests +type MergeRequestApprovalState struct { + ApprovalRulesOverwritten bool `json:"approval_rules_overwritten"` + Rules []*MergeRequestApprovalRule `json:"rules"` } // String is a stringify for MergeRequestApprovalRule @@ -236,6 +247,31 @@ func (s *MergeRequestApprovalsService) GetApprovalRules(pid interface{}, mergeRe return par, resp, err } +// GetApprovalState requests information about a merge request’s approval state +// +// GitLab API docs: +// https://docs.gitlab.com/ee/api/merge_request_approvals.html#get-the-approval-state-of-merge-requests +func (s *MergeRequestApprovalsService) GetApprovalState(pid interface{}, mergeRequest int, options ...OptionFunc) (*MergeRequestApprovalState, *Response, error) { + project, err := parseID(pid) + if err != nil { + return nil, nil, err + } + u := fmt.Sprintf("projects/%s/merge_requests/%d/approval_state", pathEscape(project), mergeRequest) + + req, err := s.client.NewRequest("GET", u, nil, options) + if err != nil { + return nil, nil, err + } + + var pas *MergeRequestApprovalState + resp, err := s.client.Do(req, &pas) + if err != nil { + return nil, resp, err + } + + return pas, resp, err +} + // CreateMergeRequestApprovalRuleOptions represents the available CreateApprovalRule() // options. // diff --git a/merge_request_approvals_test.go b/merge_request_approvals_test.go index d5f0ce0cf..2f7a415dd 100644 --- a/merge_request_approvals_test.go +++ b/merge_request_approvals_test.go @@ -7,6 +7,160 @@ import ( "testing" ) +func TestGetApprovalState(t *testing.T) { + mux, server, client := setup() + defer teardown(server) + + mux.HandleFunc("/api/v4/projects/1/merge_requests/1/approval_state", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{ + "approval_rules_overwritten": true, + "rules": [ + { + "id": 1, + "name": "security", + "rule_type": "regular", + "eligible_approvers": [ + { + "id": 5, + "name": "John Doe", + "username": "jdoe", + "state": "active", + "avatar_url": "https://www.gravatar.com/avatar/0?s=80&d=identicon", + "web_url": "http://localhost/jdoe" + }, + { + "id": 50, + "name": "Group Member 1", + "username": "group_member_1", + "state": "active", + "avatar_url": "https://www.gravatar.com/avatar/0?s=80&d=identicon", + "web_url": "http://localhost/group_member_1" + } + ], + "approvals_required": 3, + "source_rule": null, + "users": [ + { + "id": 5, + "name": "John Doe", + "username": "jdoe", + "state": "active", + "avatar_url": "https://www.gravatar.com/avatar/0?s=80&d=identicon", + "web_url": "http://localhost/jdoe" + } + ], + "groups": [ + { + "id": 5, + "name": "group1", + "path": "group1", + "description": "", + "visibility": "public", + "lfs_enabled": false, + "avatar_url": null, + "web_url": "http://localhost/groups/group1", + "request_access_enabled": false, + "full_name": "group1", + "full_path": "group1", + "parent_id": null, + "ldap_cn": null, + "ldap_access": null + } + ], + "contains_hidden_groups": false, + "approved_by": [ + { + "id": 5, + "name": "John Doe", + "username": "jdoe", + "state": "active", + "avatar_url": "https://www.gravatar.com/avatar/0?s=80&d=identicon", + "web_url": "http://localhost/jdoe" + } + ], + "approved": false + } + ] + }`) + }) + + approvals, _, err := client.MergeRequestApprovals.GetApprovalState(1, 1) + if err != nil { + t.Errorf("MergeRequestApprovals.GetApprovalState returned error: %v", err) + } + + want := &MergeRequestApprovalState{ + ApprovalRulesOverwritten: true, + Rules: []*MergeRequestApprovalRule{ + &MergeRequestApprovalRule{ + ID: 1, + Name: "security", + RuleType: "regular", + EligibleApprovers: []*BasicUser{ + &BasicUser{ + ID: 5, + Name: "John Doe", + Username: "jdoe", + State: "active", + AvatarURL: "https://www.gravatar.com/avatar/0?s=80&d=identicon", + WebURL: "http://localhost/jdoe", + }, + &BasicUser{ + ID: 50, + Name: "Group Member 1", + Username: "group_member_1", + State: "active", + AvatarURL: "https://www.gravatar.com/avatar/0?s=80&d=identicon", + WebURL: "http://localhost/group_member_1", + }, + }, + ApprovalsRequired: 3, + Users: []*BasicUser{ + &BasicUser{ + ID: 5, + Name: "John Doe", + Username: "jdoe", + State: "active", + AvatarURL: "https://www.gravatar.com/avatar/0?s=80&d=identicon", + WebURL: "http://localhost/jdoe", + }, + }, + Groups: []*Group{ + &Group{ + ID: 5, + Name: "group1", + Path: "group1", + Description: "", + Visibility: PublicVisibility, + LFSEnabled: false, + AvatarURL: "", + WebURL: "http://localhost/groups/group1", + RequestAccessEnabled: false, + FullName: "group1", + FullPath: "group1", + }, + }, + ApprovedBy: []*BasicUser{ + &BasicUser{ + ID: 5, + Name: "John Doe", + Username: "jdoe", + State: "active", + AvatarURL: "https://www.gravatar.com/avatar/0?s=80&d=identicon", + WebURL: "http://localhost/jdoe", + }, + }, + Approved: false, + }, + }, + } + + if !reflect.DeepEqual(want, approvals) { + t.Errorf("MergeRequestApprovals.GetApprovalState returned %+v, want %+v", approvals, want) + } +} + func TestGetApprovalRules(t *testing.T) { mux, server, client := setup() defer teardown(server)