Skip to content

Commit bb2c239

Browse files
authored
Updating client interface and adding ApprovalStatus model (#1827)
1 parent afc170c commit bb2c239

21 files changed

+351
-252
lines changed

server/controllers/events/events_controller_e2e_test.go

+3-1
Original file line numberDiff line numberDiff line change
@@ -734,7 +734,9 @@ func TestGitHubWorkflowWithPolicyCheck(t *testing.T) {
734734
// Setup test dependencies.
735735
w := httptest.NewRecorder()
736736
When(vcsClient.PullIsMergeable(AnyRepo(), matchers.AnyModelsPullRequest())).ThenReturn(true, nil)
737-
When(vcsClient.PullIsApproved(AnyRepo(), matchers.AnyModelsPullRequest())).ThenReturn(true, nil)
737+
When(vcsClient.PullIsApproved(AnyRepo(), matchers.AnyModelsPullRequest())).ThenReturn(models.ApprovalStatus{
738+
IsApproved: true,
739+
}, nil)
738740
When(githubGetter.GetPullRequest(AnyRepo(), AnyInt())).ThenReturn(GitHubPullRequestParsed(headSHA), nil)
739741
When(vcsClient.GetModifiedFiles(AnyRepo(), matchers.AnyModelsPullRequest())).ThenReturn(c.ModifiedFiles, nil)
740742

server/core/runtime/mocks/matchers/models_approvalstatus.go

+33
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

server/core/runtime/mocks/mock_pull_approved_checker.go

+9-9
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

server/core/runtime/pull_approved_checker.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,5 @@ import (
77
//go:generate pegomock generate -m --use-experimental-model-gen --package mocks -o mocks/mock_pull_approved_checker.go PullApprovedChecker
88

99
type PullApprovedChecker interface {
10-
PullIsApproved(baseRepo models.Repo, pull models.PullRequest) (bool, error)
10+
PullIsApproved(baseRepo models.Repo, pull models.PullRequest) (models.ApprovalStatus, error)
1111
}

server/events/apply_requirement_handler.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,11 @@ func (a *AggregateApplyRequirements) ValidateProject(repoDir string, ctx models.
2323
for _, req := range ctx.ApplyRequirements {
2424
switch req {
2525
case raw.ApprovedApplyRequirement:
26-
approved, err := a.PullApprovedChecker.PullIsApproved(ctx.Pull.BaseRepo, ctx.Pull) // nolint: vetshadow
26+
approvalStatus, err := a.PullApprovedChecker.PullIsApproved(ctx.Pull.BaseRepo, ctx.Pull) // nolint: vetshadow
2727
if err != nil {
2828
return "", errors.Wrap(err, "checking if pull request was approved")
2929
}
30-
if !approved {
30+
if !approvalStatus.IsApproved {
3131
return "Pull request must be approved by at least one person other than the author before running apply.", nil
3232
}
3333
// this should come before mergeability check since mergeability is a superset of this check.

server/events/command_context.go

+3
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,9 @@ type CommandContext struct {
4646
// required the Atlantis status to be successful prior to merging.
4747
PullMergeable bool
4848

49+
// Current PR state
50+
PullRequestStatus models.PullReqStatus
51+
4952
PullStatus *models.PullStatus
5053

5154
Trigger CommandTrigger

server/events/models/models.go

+12
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,16 @@ func NewRepo(vcsHostType VCSHostType, repoFullName string, cloneURL string, vcsU
142142
}, nil
143143
}
144144

145+
type PullReqStatus struct {
146+
Approved ApprovalStatus
147+
}
148+
149+
type ApprovalStatus struct {
150+
IsApproved bool
151+
ApprovedBy string
152+
Date time.Time
153+
}
154+
145155
// PullRequest is a VCS pull request.
146156
// GitLab calls these Merge Requests.
147157
type PullRequest struct {
@@ -367,6 +377,8 @@ type ProjectCommandContext struct {
367377
// PullMergeable is true if the pull request for this project is able to be merged.
368378
PullMergeable bool
369379
// CurrentProjectPlanStatus is the status of the current project prior to this command.
380+
PullReqStatus PullReqStatus
381+
// CurrentProjectPlanStatus is the status of the current project prior to this command.
370382
ProjectPlanStatus ProjectPlanStatus
371383
// Pull is the pull request we're responding to.
372384
Pull PullRequest

server/events/project_command_runner_test.go

+6-2
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,9 @@ func TestDefaultProjectCommandRunner_ApplyNotApproved(t *testing.T) {
162162
tmp, cleanup := TempDir(t)
163163
defer cleanup()
164164
When(mockWorkingDir.GetWorkingDir(ctx.BaseRepo, ctx.Pull, ctx.Workspace)).ThenReturn(tmp, nil)
165-
When(mockApproved.PullIsApproved(ctx.BaseRepo, ctx.Pull)).ThenReturn(false, nil)
165+
When(mockApproved.PullIsApproved(ctx.BaseRepo, ctx.Pull)).ThenReturn(models.ApprovalStatus{
166+
IsApproved: false,
167+
}, nil)
166168

167169
res := runner.Apply(ctx)
168170
Equals(t, "Pull request must be approved by at least one person other than the author before running apply.", res.Failure)
@@ -346,7 +348,9 @@ func TestDefaultProjectCommandRunner_Apply(t *testing.T) {
346348
When(mockApply.Run(ctx, nil, repoDir, expEnvs)).ThenReturn("apply", nil)
347349
When(mockRun.Run(ctx, "", repoDir, expEnvs)).ThenReturn("run", nil)
348350
When(mockEnv.Run(ctx, "", "value", repoDir, make(map[string]string))).ThenReturn("value", nil)
349-
When(mockApproved.PullIsApproved(ctx.BaseRepo, ctx.Pull)).ThenReturn(true, nil)
351+
When(mockApproved.PullIsApproved(ctx.BaseRepo, ctx.Pull)).ThenReturn(models.ApprovalStatus{
352+
IsApproved: true,
353+
}, nil)
350354

351355
res := runner.Apply(ctx)
352356
Equals(t, c.expOut, res.ApplySuccess)

server/events/vcs/azuredevops_client.go

+6-4
Original file line numberDiff line numberDiff line change
@@ -136,15 +136,15 @@ func (g *AzureDevopsClient) HidePrevCommandComments(repo models.Repo, pullNum in
136136

137137
// PullIsApproved returns true if the merge request was approved by another reviewer.
138138
// https://docs.microsoft.com/en-us/azure/devops/repos/git/branch-policies?view=azure-devops#require-a-minimum-number-of-reviewers
139-
func (g *AzureDevopsClient) PullIsApproved(repo models.Repo, pull models.PullRequest) (bool, error) {
139+
func (g *AzureDevopsClient) PullIsApproved(repo models.Repo, pull models.PullRequest) (approvalStatus models.ApprovalStatus, err error) {
140140
owner, project, repoName := SplitAzureDevopsRepoFullName(repo.FullName)
141141

142142
opts := azuredevops.PullRequestGetOptions{
143143
IncludeWorkItemRefs: true,
144144
}
145145
adPull, _, err := g.Client.PullRequests.GetWithRepo(g.ctx, owner, project, repoName, pull.Num, &opts)
146146
if err != nil {
147-
return false, errors.Wrap(err, "getting pull request")
147+
return approvalStatus, errors.Wrap(err, "getting pull request")
148148
}
149149

150150
for _, review := range adPull.Reviewers {
@@ -157,11 +157,13 @@ func (g *AzureDevopsClient) PullIsApproved(repo models.Repo, pull models.PullReq
157157
}
158158

159159
if review.GetVote() == azuredevops.VoteApproved || review.GetVote() == azuredevops.VoteApprovedWithSuggestions {
160-
return true, nil
160+
return models.ApprovalStatus{
161+
IsApproved: true,
162+
}, nil
161163
}
162164
}
163165

164-
return false, nil
166+
return approvalStatus, nil
165167
}
166168

167169
// PullIsMergeable returns true if the merge request can be merged.

server/events/vcs/azuredevops_client_test.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -495,7 +495,7 @@ func TestAzureDevopsClient_PullIsApproved(t *testing.T) {
495495

496496
defer disableSSLVerification()()
497497

498-
actApproved, err := client.PullIsApproved(models.Repo{
498+
approvalStatus, err := client.PullIsApproved(models.Repo{
499499
FullName: "owner/project/repo",
500500
Owner: "owner",
501501
Name: "repo",
@@ -509,7 +509,7 @@ func TestAzureDevopsClient_PullIsApproved(t *testing.T) {
509509
Num: 1,
510510
})
511511
Ok(t, err)
512-
Equals(t, c.expApproved, actApproved)
512+
Equals(t, c.expApproved, approvalStatus.IsApproved)
513513
})
514514
}
515515
}

server/events/vcs/bitbucketcloud/client.go

+8-6
Original file line numberDiff line numberDiff line change
@@ -105,28 +105,30 @@ func (b *Client) HidePrevCommandComments(repo models.Repo, pullNum int, command
105105
}
106106

107107
// PullIsApproved returns true if the merge request was approved.
108-
func (b *Client) PullIsApproved(repo models.Repo, pull models.PullRequest) (bool, error) {
108+
func (b *Client) PullIsApproved(repo models.Repo, pull models.PullRequest) (approvalStatus models.ApprovalStatus, err error) {
109109
path := fmt.Sprintf("%s/2.0/repositories/%s/pullrequests/%d", b.BaseURL, repo.FullName, pull.Num)
110110
resp, err := b.makeRequest("GET", path, nil)
111111
if err != nil {
112-
return false, err
112+
return approvalStatus, err
113113
}
114114
var pullResp PullRequest
115115
if err := json.Unmarshal(resp, &pullResp); err != nil {
116-
return false, errors.Wrapf(err, "Could not parse response %q", string(resp))
116+
return approvalStatus, errors.Wrapf(err, "Could not parse response %q", string(resp))
117117
}
118118
if err := validator.New().Struct(pullResp); err != nil {
119-
return false, errors.Wrapf(err, "API response %q was missing fields", string(resp))
119+
return approvalStatus, errors.Wrapf(err, "API response %q was missing fields", string(resp))
120120
}
121121
authorUUID := *pullResp.Author.UUID
122122
for _, participant := range pullResp.Participants {
123123
// Bitbucket allows the author to approve their own pull request. This
124124
// defeats the purpose of approvals so we don't count that approval.
125125
if *participant.Approved && *participant.User.UUID != authorUUID {
126-
return true, nil
126+
return models.ApprovalStatus{
127+
IsApproved: true,
128+
}, nil
127129
}
128130
}
129-
return false, nil
131+
return approvalStatus, nil
130132
}
131133

132134
// PullIsMergeable returns true if the merge request has no conflicts and can be merged.

server/events/vcs/bitbucketcloud/client_test.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -202,14 +202,14 @@ func TestClient_PullIsApproved(t *testing.T) {
202202

203203
repo, err := models.NewRepo(models.BitbucketServer, "owner/repo", "https://bitbucket.org/owner/repo.git", "user", "token")
204204
Ok(t, err)
205-
approved, err := client.PullIsApproved(repo, models.PullRequest{
205+
approvalStatus, err := client.PullIsApproved(repo, models.PullRequest{
206206
Num: 1,
207207
HeadBranch: "branch",
208208
Author: "author",
209209
BaseRepo: repo,
210210
})
211211
Ok(t, err)
212-
Equals(t, c.exp, approved)
212+
Equals(t, c.exp, approvalStatus.IsApproved)
213213
})
214214
}
215215
}

server/events/vcs/bitbucketserver/client.go

+9-7
Original file line numberDiff line numberDiff line change
@@ -161,29 +161,31 @@ func (b *Client) postComment(repo models.Repo, pullNum int, comment string) erro
161161
}
162162

163163
// PullIsApproved returns true if the merge request was approved.
164-
func (b *Client) PullIsApproved(repo models.Repo, pull models.PullRequest) (bool, error) {
164+
func (b *Client) PullIsApproved(repo models.Repo, pull models.PullRequest) (approvalStatus models.ApprovalStatus, err error) {
165165
projectKey, err := b.GetProjectKey(repo.Name, repo.SanitizedCloneURL)
166166
if err != nil {
167-
return false, err
167+
return approvalStatus, err
168168
}
169169
path := fmt.Sprintf("%s/rest/api/1.0/projects/%s/repos/%s/pull-requests/%d", b.BaseURL, projectKey, repo.Name, pull.Num)
170170
resp, err := b.makeRequest("GET", path, nil)
171171
if err != nil {
172-
return false, err
172+
return approvalStatus, err
173173
}
174174
var pullResp PullRequest
175175
if err := json.Unmarshal(resp, &pullResp); err != nil {
176-
return false, errors.Wrapf(err, "Could not parse response %q", string(resp))
176+
return approvalStatus, errors.Wrapf(err, "Could not parse response %q", string(resp))
177177
}
178178
if err := validator.New().Struct(pullResp); err != nil {
179-
return false, errors.Wrapf(err, "API response %q was missing fields", string(resp))
179+
return approvalStatus, errors.Wrapf(err, "API response %q was missing fields", string(resp))
180180
}
181181
for _, reviewer := range pullResp.Reviewers {
182182
if *reviewer.Approved {
183-
return true, nil
183+
return models.ApprovalStatus{
184+
IsApproved: true,
185+
}, nil
184186
}
185187
}
186-
return false, nil
188+
return approvalStatus, nil
187189
}
188190

189191
// PullIsMergeable returns true if the merge request has no conflicts and can be merged.

server/events/vcs/client.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ type Client interface {
2626
GetModifiedFiles(repo models.Repo, pull models.PullRequest) ([]string, error)
2727
CreateComment(repo models.Repo, pullNum int, comment string, command string) error
2828
HidePrevCommandComments(repo models.Repo, pullNum int, command string) error
29-
PullIsApproved(repo models.Repo, pull models.PullRequest) (bool, error)
29+
PullIsApproved(repo models.Repo, pull models.PullRequest) (models.ApprovalStatus, error)
3030
PullIsMergeable(repo models.Repo, pull models.PullRequest) (bool, error)
3131
// UpdateStatus updates the commit status to state for pull. src is the
3232
// source of this status. This should be relatively static across runs,

server/events/vcs/github_client.go

+8-4
Original file line numberDiff line numberDiff line change
@@ -232,7 +232,7 @@ func (g *GithubClient) HidePrevCommandComments(repo models.Repo, pullNum int, co
232232
}
233233

234234
// PullIsApproved returns true if the pull request was approved.
235-
func (g *GithubClient) PullIsApproved(repo models.Repo, pull models.PullRequest) (bool, error) {
235+
func (g *GithubClient) PullIsApproved(repo models.Repo, pull models.PullRequest) (approvalStatus models.ApprovalStatus, err error) {
236236
nextPage := 0
237237
for {
238238
opts := github.ListOptions{
@@ -244,19 +244,23 @@ func (g *GithubClient) PullIsApproved(repo models.Repo, pull models.PullRequest)
244244
g.logger.Debug("GET /repos/%v/%v/pulls/%d/reviews", repo.Owner, repo.Name, pull.Num)
245245
pageReviews, resp, err := g.client.PullRequests.ListReviews(g.ctx, repo.Owner, repo.Name, pull.Num, &opts)
246246
if err != nil {
247-
return false, errors.Wrap(err, "getting reviews")
247+
return approvalStatus, errors.Wrap(err, "getting reviews")
248248
}
249249
for _, review := range pageReviews {
250250
if review != nil && review.GetState() == "APPROVED" {
251-
return true, nil
251+
return models.ApprovalStatus{
252+
IsApproved: true,
253+
ApprovedBy: *review.User.Login,
254+
Date: *review.SubmittedAt,
255+
}, nil
252256
}
253257
}
254258
if resp.NextPage == 0 {
255259
break
256260
}
257261
nextPage = resp.NextPage
258262
}
259-
return false, nil
263+
return approvalStatus, nil
260264
}
261265

262266
// PullIsMergeable returns true if the pull request is mergeable.

server/events/vcs/github_client_test.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -455,7 +455,7 @@ func TestGithubClient_PullIsApproved(t *testing.T) {
455455
Ok(t, err)
456456
defer disableSSLVerification()()
457457

458-
approved, err := client.PullIsApproved(models.Repo{
458+
approvalStatus, err := client.PullIsApproved(models.Repo{
459459
FullName: "owner/repo",
460460
Owner: "owner",
461461
Name: "repo",
@@ -469,7 +469,7 @@ func TestGithubClient_PullIsApproved(t *testing.T) {
469469
Num: 1,
470470
})
471471
Ok(t, err)
472-
Equals(t, false, approved)
472+
Equals(t, false, approvalStatus.IsApproved)
473473
}
474474

475475
func TestGithubClient_PullIsMergeable(t *testing.T) {

server/events/vcs/gitlab_client.go

+6-4
Original file line numberDiff line numberDiff line change
@@ -168,15 +168,17 @@ func (g *GitlabClient) HidePrevCommandComments(repo models.Repo, pullNum int, co
168168
}
169169

170170
// PullIsApproved returns true if the merge request was approved.
171-
func (g *GitlabClient) PullIsApproved(repo models.Repo, pull models.PullRequest) (bool, error) {
171+
func (g *GitlabClient) PullIsApproved(repo models.Repo, pull models.PullRequest) (approvalStatus models.ApprovalStatus, err error) {
172172
approvals, _, err := g.Client.MergeRequests.GetMergeRequestApprovals(repo.FullName, pull.Num)
173173
if err != nil {
174-
return false, err
174+
return approvalStatus, err
175175
}
176176
if approvals.ApprovalsLeft > 0 {
177-
return false, nil
177+
return approvalStatus, nil
178178
}
179-
return true, nil
179+
return models.ApprovalStatus{
180+
IsApproved: true,
181+
}, nil
180182
}
181183

182184
// PullIsMergeable returns true if the merge request can be merged.

0 commit comments

Comments
 (0)