Add PullReview functions #338
184 gitea/pull_review.go Normal file
184
gitea/pull_review.go Normal file @@ -0,0 +1,184 @@ | ||||
// Copyright 2020 The Gitea Authors. All rights reserved. | ||||
// Use of this source code is governed by a MIT-style | ||||
// license that can be found in the LICENSE file. | ||||
| ||||
package gitea | ||||
| ||||
import ( | ||||
"bytes" | ||||
"encoding/json" | ||||
"fmt" | ||||
"net/url" | ||||
"time" | ||||
) | ||||
| ||||
// ReviewStateType review state type | ||||
type ReviewStateType string | ||||
| ||||
const ( | ||||
// ReviewStateApproved pr is approved | ||||
ReviewStateApproved ReviewStateType = "APPROVED" | ||||
// ReviewStatePending pr state is pending | ||||
ReviewStatePending ReviewStateType = "PENDING" | ||||
// ReviewStateComment is a comment review | ||||
ReviewStateComment ReviewStateType = "COMMENT" | ||||
// ReviewStateRequestChanges changes for pr are requested | ||||
ReviewStateRequestChanges ReviewStateType = "REQUEST_CHANGES" | ||||
// ReviewStateRequestReview review is requested from user | ||||
ReviewStateRequestReview ReviewStateType = "REQUEST_REVIEW" | ||||
// ReviewStateUnknown state of pr is unknown | ||||
ReviewStateUnknown ReviewStateType = "" | ||||
) | ||||
| ||||
// PullReview represents a pull request review | ||||
type PullReview struct { | ||||
ID int64 `json:"id"` | ||||
Reviewer *User `json:"user"` | ||||
State ReviewStateType `json:"state"` | ||||
Body string `json:"body"` | ||||
CommitID string `json:"commit_id"` | ||||
Stale bool `json:"stale"` | ||||
Official bool `json:"official"` | ||||
CodeCommentsCount int `json:"comments_count"` | ||||
// swagger:strfmt date-time | ||||
Submitted time.Time `json:"submitted_at"` | ||||
| ||||
HTMLURL string `json:"html_url"` | ||||
HTMLPullURL string `json:"pull_request_url"` | ||||
} | ||||
| ||||
// PullReviewComment represents a comment on a pull request review | ||||
type PullReviewComment struct { | ||||
ID int64 `json:"id"` | ||||
Body string `json:"body"` | ||||
Reviewer *User `json:"user"` | ||||
ReviewID int64 `json:"pull_request_review_id"` | ||||
| ||||
// swagger:strfmt date-time | ||||
Created time.Time `json:"created_at"` | ||||
// swagger:strfmt date-time | ||||
Updated time.Time `json:"updated_at"` | ||||
| ||||
Path string `json:"path"` | ||||
CommitID string `json:"commit_id"` | ||||
OrigCommitID string `json:"original_commit_id"` | ||||
DiffHunk string `json:"diff_hunk"` | ||||
LineNum uint64 `json:"position"` | ||||
OldLineNum uint64 `json:"original_position"` | ||||
| ||||
HTMLURL string `json:"html_url"` | ||||
HTMLPullURL string `json:"pull_request_url"` | ||||
} | ||||
| ||||
// CreatePullReviewOptions are options to create a pull review | ||||
type CreatePullReviewOptions struct { | ||||
State ReviewStateType `json:"event"` | ||||
Body string `json:"body"` | ||||
CommitID string `json:"commit_id"` | ||||
Comments []CreatePullReviewComment `json:"comments"` | ||||
} | ||||
| ||||
// CreatePullReviewComment represent a review comment for creation api | ||||
type CreatePullReviewComment struct { | ||||
// the tree path | ||||
Path string `json:"path"` | ||||
Body string `json:"body"` | ||||
// if comment to old file line or 0 | ||||
OldLineNum int64 `json:"old_position"` | ||||
// if comment to new file line or 0 | ||||
NewLineNum int64 `json:"new_position"` | ||||
} | ||||
| ||||
// SubmitPullReviewOptions are options to submit a pending pull review | ||||
type SubmitPullReviewOptions struct { | ||||
State ReviewStateType `json:"event"` | ||||
Body string `json:"body"` | ||||
} | ||||
| ||||
// ListPullReviewsOptions options for listing PullReviews | ||||
type ListPullReviewsOptions struct { | ||||
ListOptions | ||||
} | ||||
| ||||
// ListPullReviews lists all reviews of a pull request | ||||
func (c *Client) ListPullReviews(owner, repo string, index int64, opt ListPullReviewsOptions) ([]*PullReview, error) { | ||||
if err := c.CheckServerVersionConstraint(">=1.12.0"); err != nil { | ||||
return nil, err | ||||
} | ||||
opt.setDefaults() | ||||
rs := make([]*PullReview, 0, opt.PageSize) | ||||
| ||||
link, _ := url.Parse(fmt.Sprintf("/repos/%s/%s/pulls/%d/reviews", owner, repo, index)) | ||||
link.RawQuery = opt.ListOptions.getURLQuery().Encode() | ||||
| ||||
return rs, c.getParsedResponse("GET", link.String(), jsonHeader, nil, &rs) | ||||
} | ||||
| ||||
// GetPullReview gets a specific review of a pull request | ||||
func (c *Client) GetPullReview(owner, repo string, index, id int64) (*PullReview, error) { | ||||
if err := c.CheckServerVersionConstraint(">=1.12.0"); err != nil { | ||||
return nil, err | ||||
} | ||||
| ||||
r := new(PullReview) | ||||
return r, c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/pulls/%d/reviews/%d", owner, repo, index, id), jsonHeader, nil, &r) | ||||
} | ||||
| ||||
// ListPullReviewsCommentsOptions options for listing PullReviewsComments | ||||
type ListPullReviewsCommentsOptions struct { | ||||
ListOptions | ||||
} | ||||
| ||||
// ListPullReviewComments lists all comments of a pull request review | ||||
func (c *Client) ListPullReviewComments(owner, repo string, index, id int64, opt ListPullReviewsCommentsOptions) ([]*PullReviewComment, error) { | ||||
if err := c.CheckServerVersionConstraint(">=1.12.0"); err != nil { | ||||
return nil, err | ||||
} | ||||
opt.setDefaults() | ||||
rcl := make([]*PullReviewComment, 0, opt.PageSize) | ||||
| ||||
link, _ := url.Parse(fmt.Sprintf("/repos/%s/%s/pulls/%d/reviews/%d/comments", owner, repo, index, id)) | ||||
link.RawQuery = opt.ListOptions.getURLQuery().Encode() | ||||
| ||||
return rcl, c.getParsedResponse("GET", link.String(), jsonHeader, nil, &rcl) | ||||
} | ||||
| ||||
// DeletePullReview delete a specific review from a pull request | ||||
func (c *Client) DeletePullReview(owner, repo string, index, id int64) error { | ||||
if err := c.CheckServerVersionConstraint(">=1.12.0"); err != nil { | ||||
return err | ||||
} | ||||
| ||||
_, err := c.getResponse("DELETE", fmt.Sprintf("/repos/%s/%s/pulls/%d/reviews/%d", owner, repo, index, id), jsonHeader, nil) | ||||
return err | ||||
} | ||||
| ||||
// CreatePullReview create a review to an pull request | ||||
func (c *Client) CreatePullReview(owner, repo string, index int64, opt CreatePullReviewOptions) (*PullReview, error) { | ||||
if err := c.CheckServerVersionConstraint(">=1.12.0"); err != nil { | ||||
return nil, err | ||||
} | ||||
body, err := json.Marshal(&opt) | ||||
if err != nil { | ||||
return nil, err | ||||
} | ||||
| ||||
r := new(PullReview) | ||||
return r, c.getParsedResponse("POST", fmt.Sprintf("/repos/%s/%s/pulls/%d/reviews", owner, repo, index), | ||||
jsonHeader, bytes.NewReader(body), r) | ||||
} | ||||
| ||||
// SubmitPullReview submit a pending review to an pull request | ||||
func (c *Client) SubmitPullReview(owner, repo string, index, id int64, opt SubmitPullReviewOptions) (*PullReview, error) { | ||||
if err := c.CheckServerVersionConstraint(">=1.12.0"); err != nil { | ||||
return nil, err | ||||
} | ||||
body, err := json.Marshal(&opt) | ||||
if err != nil { | ||||
return nil, err | ||||
} | ||||
| ||||
r := new(PullReview) | ||||
return r, c.getParsedResponse("POST", fmt.Sprintf("/repos/%s/%s/pulls/%d/reviews/%d", owner, repo, index, id), | ||||
jsonHeader, bytes.NewReader(body), r) | ||||
} |
180 gitea/pull_review_test.go Normal file
180
gitea/pull_review_test.go Normal file @@ -0,0 +1,180 @@ | ||||
// Copyright 2020 The Gitea Authors. All rights reserved. | ||||
// Use of this source code is governed by a MIT-style | ||||
// license that can be found in the LICENSE file. | ||||
| ||||
package gitea | ||||
| ||||
import ( | ||||
"log" | ||||
"testing" | ||||
| ||||
"github.com/stretchr/testify/assert" | ||||
) | ||||
| ||||
func TestPullReview(t *testing.T) { | ||||
log.Println("== TestPullReview ==") | ||||
c := newTestClient() | ||||
| ||||
var repoName = "Reviews" | ||||
repo, pull, submitter, reviewer, success := preparePullReviewTest(t, c, repoName) | ||||
if !success { | ||||
return | ||||
} | ||||
defer c.AdminDeleteUser(reviewer.UserName) | ||||
defer c.AdminDeleteUser(submitter.UserName) | ||||
| ||||
// CreatePullReview | ||||
r1, err := c.CreatePullReview(repo.Owner.UserName, repo.Name, pull.Index, CreatePullReviewOptions{ | ||||
State: ReviewStateComment, | ||||
Body: "I'll have a look at it later", | ||||
}) | ||||
assert.NoError(t, err) | ||||
if assert.NotNil(t, r1) { | ||||
assert.EqualValues(t, ReviewStateComment, r1.State) | ||||
assert.EqualValues(t, 1, r1.Reviewer.ID) | ||||
} | ||||
| ||||
c.SetSudo(submitter.UserName) | ||||
r2, err := c.CreatePullReview(repo.Owner.UserName, repo.Name, pull.Index, CreatePullReviewOptions{ | ||||
State: ReviewStateApproved, | ||||
Body: "lgtm it myself", | ||||
}) | ||||
assert.Error(t, err) | ||||
r2, err = c.CreatePullReview(repo.Owner.UserName, repo.Name, pull.Index, CreatePullReviewOptions{ | ||||
State: ReviewStateComment, | ||||
Body: "no seriously please have a look at it", | ||||
}) | ||||
assert.NoError(t, err) | ||||
assert.NotNil(t, r2) | ||||
| ||||
c.SetSudo(reviewer.UserName) | ||||
r3, err := c.CreatePullReview(repo.Owner.UserName, repo.Name, pull.Index, CreatePullReviewOptions{ | ||||
State: ReviewStateApproved, | ||||
Body: "lgtm", | ||||
Comments: []CreatePullReviewComment{{ | ||||
Path: "WOW-file", | ||||
Body: "no better name - really?", | ||||
NewLineNum: 1, | ||||
}, | ||||
}, | ||||
}) | ||||
| ||||
// ListPullReviews | ||||
c.SetSudo("") | ||||
rl, err := c.ListPullReviews(repo.Owner.UserName, repo.Name, pull.Index, ListPullReviewsOptions{}) | ||||
if !assert.NoError(t, err) { | ||||
return | ||||
} | ||||
assert.Len(t, rl, 3) | ||||
for i := range rl { | ||||
assert.EqualValues(t, pull.HTMLURL, rl[i].HTMLPullURL) | ||||
if rl[i].CodeCommentsCount == 1 { | ||||
assert.EqualValues(t, reviewer.ID, rl[i].Reviewer.ID) | ||||
} | ||||
} | ||||
| ||||
// GetPullReview | ||||
rNew, err := c.GetPullReview(repo.Owner.UserName, repo.Name, pull.Index, r3.ID) | ||||
assert.NoError(t, err) | ||||
assert.EqualValues(t, r3, rNew) | ||||
| ||||
// DeletePullReview | ||||
c.SetSudo(submitter.UserName) | ||||
err = c.DeletePullReview(repo.Owner.UserName, repo.Name, pull.Index, r2.ID) | ||||
assert.NoError(t, err) | ||||
err = c.DeletePullReview(repo.Owner.UserName, repo.Name, pull.Index, r3.ID) | ||||
assert.Error(t, err) | ||||
| ||||
// SubmitPullReview | ||||
c.SetSudo("") | ||||
r4, err := c.CreatePullReview(repo.Owner.UserName, repo.Name, pull.Index, CreatePullReviewOptions{ | ||||
Body: "...", | ||||
Comments: []CreatePullReviewComment{{ | ||||
Path: "WOW-file", | ||||
Body: "its ok", | ||||
NewLineNum: 1, | ||||
}, | ||||
}, | ||||
}) | ||||
assert.NoError(t, err) | ||||
r5, err := c.CreatePullReview(repo.Owner.UserName, repo.Name, pull.Index, CreatePullReviewOptions{ | ||||
Body: "...", | ||||
Comments: []CreatePullReviewComment{{ | ||||
Path: "WOW-file", | ||||
Body: "hehe and here it is", | ||||
NewLineNum: 3, | ||||
}, | ||||
}, | ||||
}) | ||||
assert.NoError(t, err) | ||||
assert.EqualValues(t, r4.ID, r5.ID) | ||||
| ||||
r, err := c.SubmitPullReview(repo.Owner.UserName, repo.Name, pull.Index, r4.ID, SubmitPullReviewOptions{ | ||||
State: ReviewStateRequestChanges, | ||||
Body: "one nit", | ||||
}) | ||||
assert.NoError(t, err) | ||||
assert.EqualValues(t, r4.ID, r.ID) | ||||
assert.EqualValues(t, ReviewStateRequestChanges, r.State) | ||||
| ||||
// ListPullReviewComments | ||||
rcl, err := c.ListPullReviewComments(repo.Owner.UserName, repo.Name, pull.Index, r.ID, ListPullReviewsCommentsOptions{}) | ||||
assert.NoError(t, err) | ||||
assert.EqualValues(t, r.CodeCommentsCount, len(rcl)) | ||||
for _, rc := range rcl { | ||||
//assert.EqualValues(t, pull.HTMLURL, rc.HTMLPullURL) https://github.com/go-gitea/gitea/issues/11499 | ||||
if rc.LineNum == 3 { | ||||
assert.EqualValues(t, "hehe and here it is", rc.Body) | ||||
} else { | ||||
assert.EqualValues(t, 1, rc.LineNum) | ||||
assert.EqualValues(t, "its ok", rc.Body) | ||||
} | ||||
} | ||||
| ||||
} | ||||
| ||||
func preparePullReviewTest(t *testing.T, c *Client, repoName string) (*Repository, *PullRequest, *User, *User, bool) { | ||||
repo, err := createTestRepo(t, repoName, c) | ||||
if !assert.NoError(t, err) { | ||||
return nil, nil, nil, nil, false | ||||
} | ||||
| ||||
pullSubmitter := createTestUser(t, "pull_submitter", c) | ||||
write := "write" | ||||
c.AddCollaborator(repo.Owner.UserName, repo.Name, pullSubmitter.UserName, AddCollaboratorOption{ | ||||
Permission: &write, | ||||
}) | ||||
| ||||
c.SetSudo("pull_submitter") | ||||
| ||||
newFile, err := c.CreateFile(repo.Owner.UserName, repo.Name, "WOW-file", CreateFileOptions{ | ||||
Content: "QSBuZXcgRmlsZQoKYW5kIHNvbWUgbGluZXMK", | ||||
FileOptions: FileOptions{ | ||||
Message: "creat a new file", | ||||
BranchName: "master", | ||||
NewBranchName: "new_file", | ||||
}, | ||||
}) | ||||
| ||||
if !assert.NoError(t, err) || !assert.NotNil(t, newFile) { | ||||
return nil, nil, nil, nil, false | ||||
} | ||||
| ||||
pull, err := c.CreatePullRequest(c.username, repoName, CreatePullRequestOption{ | ||||
Base: "master", | ||||
Head: "new_file", | ||||
Title: "Creat a NewFile", | ||||
}) | ||||
assert.NoError(t, err) | ||||
assert.NotNil(t, pull) | ||||
| ||||
c.SetSudo("") | ||||
| ||||
reviewer := createTestUser(t, "pull_reviewer", c) | ||||
admin := "admin" | ||||
c.AddCollaborator(repo.Owner.UserName, repo.Name, pullSubmitter.UserName, AddCollaboratorOption{ | ||||
Permission: &admin, | ||||
}) | ||||
| ||||
return repo, pull, pullSubmitter, reviewer, pull.Poster.ID == pullSubmitter.ID | ||||
} |
Reference in New Issue
Block a user