2020-05-02 03:20:51 +03:00
// Copyright 2020 The Gitea Authors. All rights reserved.
2022-11-27 21:20:29 +03:00
// SPDX-License-Identifier: MIT
2020-05-02 03:20:51 +03:00
package repo
import (
"fmt"
"net/http"
"strings"
2022-06-13 12:37:59 +03:00
issues_model "code.gitea.io/gitea/models/issues"
2022-03-29 09:29:02 +03:00
"code.gitea.io/gitea/models/organization"
2022-05-11 13:09:36 +03:00
access_model "code.gitea.io/gitea/models/perm/access"
2021-11-24 12:49:20 +03:00
user_model "code.gitea.io/gitea/models/user"
2020-05-02 03:20:51 +03:00
"code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/convert"
2020-05-08 21:50:23 +03:00
"code.gitea.io/gitea/modules/git"
2020-05-02 03:20:51 +03:00
api "code.gitea.io/gitea/modules/structs"
2021-01-26 18:36:53 +03:00
"code.gitea.io/gitea/modules/web"
2020-05-02 03:20:51 +03:00
"code.gitea.io/gitea/routers/api/v1/utils"
2020-10-20 21:18:25 +03:00
issue_service "code.gitea.io/gitea/services/issue"
2020-05-02 03:20:51 +03:00
pull_service "code.gitea.io/gitea/services/pull"
)
// ListPullReviews lists all reviews of a pull request
func ListPullReviews ( ctx * context . APIContext ) {
// swagger:operation GET /repos/{owner}/{repo}/pulls/{index}/reviews repository repoListPullReviews
// ---
// summary: List all reviews for a pull request
// produces:
// - application/json
// parameters:
// - name: owner
// in: path
// description: owner of the repo
// type: string
// required: true
// - name: repo
// in: path
// description: name of the repo
// type: string
// required: true
// - name: index
// in: path
// description: index of the pull request
// type: integer
// format: int64
// required: true
// - name: page
// in: query
// description: page number of results to return (1-based)
// type: integer
// - name: limit
// in: query
2020-06-09 07:57:38 +03:00
// description: page size of results
2020-05-02 03:20:51 +03:00
// type: integer
// responses:
// "200":
// "$ref": "#/responses/PullReviewList"
// "404":
// "$ref": "#/responses/notFound"
2022-06-13 12:37:59 +03:00
pr , err := issues_model . GetPullRequestByIndex ( ctx , ctx . Repo . Repository . ID , ctx . ParamsInt64 ( ":index" ) )
2020-05-02 03:20:51 +03:00
if err != nil {
2022-06-13 12:37:59 +03:00
if issues_model . IsErrPullRequestNotExist ( err ) {
2020-05-02 03:20:51 +03:00
ctx . NotFound ( "GetPullRequestByIndex" , err )
} else {
ctx . Error ( http . StatusInternalServerError , "GetPullRequestByIndex" , err )
}
return
}
2022-11-19 11:12:33 +03:00
if err = pr . LoadIssue ( ctx ) ; err != nil {
2020-05-02 03:20:51 +03:00
ctx . Error ( http . StatusInternalServerError , "LoadIssue" , err )
return
}
2022-04-08 12:11:15 +03:00
if err = pr . Issue . LoadRepo ( ctx ) ; err != nil {
2020-05-02 03:20:51 +03:00
ctx . Error ( http . StatusInternalServerError , "LoadRepo" , err )
return
}
2022-06-13 12:37:59 +03:00
opts := issues_model . FindReviewOptions {
2020-05-02 03:20:51 +03:00
ListOptions : utils . GetListOptions ( ctx ) ,
2022-06-13 12:37:59 +03:00
Type : issues_model . ReviewTypeUnknown ,
2020-05-02 03:20:51 +03:00
IssueID : pr . IssueID ,
2021-08-12 15:43:08 +03:00
}
2022-06-13 12:37:59 +03:00
allReviews , err := issues_model . FindReviews ( ctx , opts )
2021-08-12 15:43:08 +03:00
if err != nil {
ctx . InternalServerError ( err )
return
}
2020-05-02 03:20:51 +03:00
2022-06-13 12:37:59 +03:00
count , err := issues_model . CountReviews ( opts )
2020-05-02 03:20:51 +03:00
if err != nil {
2021-08-12 15:43:08 +03:00
ctx . InternalServerError ( err )
2020-05-02 03:20:51 +03:00
return
}
2022-03-22 10:03:22 +03:00
apiReviews , err := convert . ToPullReviewList ( ctx , allReviews , ctx . Doer )
2020-05-02 03:20:51 +03:00
if err != nil {
ctx . Error ( http . StatusInternalServerError , "convertToPullReviewList" , err )
return
}
2021-08-12 15:43:08 +03:00
ctx . SetTotalCountHeader ( count )
2020-05-02 03:20:51 +03:00
ctx . JSON ( http . StatusOK , & apiReviews )
}
// GetPullReview gets a specific review of a pull request
func GetPullReview ( ctx * context . APIContext ) {
// swagger:operation GET /repos/{owner}/{repo}/pulls/{index}/reviews/{id} repository repoGetPullReview
// ---
// summary: Get a specific review for a pull request
// produces:
// - application/json
// parameters:
// - name: owner
// in: path
// description: owner of the repo
// type: string
// required: true
// - name: repo
// in: path
// description: name of the repo
// type: string
// required: true
// - name: index
// in: path
// description: index of the pull request
// type: integer
// format: int64
// required: true
// - name: id
// in: path
// description: id of the review
// type: integer
// format: int64
// required: true
// responses:
// "200":
// "$ref": "#/responses/PullReview"
// "404":
// "$ref": "#/responses/notFound"
review , _ , statusSet := prepareSingleReview ( ctx )
if statusSet {
return
}
2022-03-22 10:03:22 +03:00
apiReview , err := convert . ToPullReview ( ctx , review , ctx . Doer )
2020-05-02 03:20:51 +03:00
if err != nil {
ctx . Error ( http . StatusInternalServerError , "convertToPullReview" , err )
return
}
ctx . JSON ( http . StatusOK , apiReview )
}
// GetPullReviewComments lists all comments of a pull request review
func GetPullReviewComments ( ctx * context . APIContext ) {
// swagger:operation GET /repos/{owner}/{repo}/pulls/{index}/reviews/{id}/comments repository repoGetPullReviewComments
// ---
// summary: Get a specific review for a pull request
// produces:
// - application/json
// parameters:
// - name: owner
// in: path
// description: owner of the repo
// type: string
// required: true
// - name: repo
// in: path
// description: name of the repo
// type: string
// required: true
// - name: index
// in: path
// description: index of the pull request
// type: integer
// format: int64
// required: true
// - name: id
// in: path
// description: id of the review
// type: integer
// format: int64
// required: true
// responses:
// "200":
// "$ref": "#/responses/PullReviewCommentList"
// "404":
// "$ref": "#/responses/notFound"
review , _ , statusSet := prepareSingleReview ( ctx )
if statusSet {
return
}
2022-03-22 10:03:22 +03:00
apiComments , err := convert . ToPullReviewCommentList ( ctx , review , ctx . Doer )
2020-05-02 03:20:51 +03:00
if err != nil {
ctx . Error ( http . StatusInternalServerError , "convertToPullReviewCommentList" , err )
return
}
ctx . JSON ( http . StatusOK , apiComments )
}
// DeletePullReview delete a specific review from a pull request
func DeletePullReview ( ctx * context . APIContext ) {
// swagger:operation DELETE /repos/{owner}/{repo}/pulls/{index}/reviews/{id} repository repoDeletePullReview
// ---
// summary: Delete a specific review from a pull request
// produces:
// - application/json
// parameters:
// - name: owner
// in: path
// description: owner of the repo
// type: string
// required: true
// - name: repo
// in: path
// description: name of the repo
// type: string
// required: true
// - name: index
// in: path
// description: index of the pull request
// type: integer
// format: int64
// required: true
// - name: id
// in: path
// description: id of the review
// type: integer
// format: int64
// required: true
// responses:
// "204":
// "$ref": "#/responses/empty"
// "403":
// "$ref": "#/responses/forbidden"
// "404":
// "$ref": "#/responses/notFound"
review , _ , statusSet := prepareSingleReview ( ctx )
if statusSet {
return
}
2022-03-22 10:03:22 +03:00
if ctx . Doer == nil {
2020-05-02 03:20:51 +03:00
ctx . NotFound ( )
return
}
2022-03-22 10:03:22 +03:00
if ! ctx . Doer . IsAdmin && ctx . Doer . ID != review . ReviewerID {
2020-05-02 03:20:51 +03:00
ctx . Error ( http . StatusForbidden , "only admin and user itself can delete a review" , nil )
return
}
2022-06-13 12:37:59 +03:00
if err := issues_model . DeleteReview ( review ) ; err != nil {
2020-05-02 03:20:51 +03:00
ctx . Error ( http . StatusInternalServerError , "DeleteReview" , fmt . Errorf ( "can not delete ReviewID: %d" , review . ID ) )
return
}
ctx . Status ( http . StatusNoContent )
}
// CreatePullReview create a review to an pull request
2021-01-26 18:36:53 +03:00
func CreatePullReview ( ctx * context . APIContext ) {
2020-05-02 03:20:51 +03:00
// swagger:operation POST /repos/{owner}/{repo}/pulls/{index}/reviews repository repoCreatePullReview
// ---
// summary: Create a review to an pull request
// produces:
// - application/json
// parameters:
// - name: owner
// in: path
// description: owner of the repo
// type: string
// required: true
// - name: repo
// in: path
// description: name of the repo
// type: string
// required: true
// - name: index
// in: path
// description: index of the pull request
// type: integer
// format: int64
// required: true
// - name: body
// in: body
// required: true
// schema:
// "$ref": "#/definitions/CreatePullReviewOptions"
// responses:
// "200":
// "$ref": "#/responses/PullReview"
// "404":
// "$ref": "#/responses/notFound"
// "422":
// "$ref": "#/responses/validationError"
2021-01-26 18:36:53 +03:00
opts := web . GetForm ( ctx ) . ( * api . CreatePullReviewOptions )
2022-06-13 12:37:59 +03:00
pr , err := issues_model . GetPullRequestByIndex ( ctx , ctx . Repo . Repository . ID , ctx . ParamsInt64 ( ":index" ) )
2020-05-02 03:20:51 +03:00
if err != nil {
2022-06-13 12:37:59 +03:00
if issues_model . IsErrPullRequestNotExist ( err ) {
2020-05-02 03:20:51 +03:00
ctx . NotFound ( "GetPullRequestByIndex" , err )
} else {
ctx . Error ( http . StatusInternalServerError , "GetPullRequestByIndex" , err )
}
return
}
// determine review type
2021-06-25 01:05:51 +03:00
reviewType , isWrong := preparePullReviewType ( ctx , pr , opts . Event , opts . Body , len ( opts . Comments ) > 0 )
2020-05-02 03:20:51 +03:00
if isWrong {
return
}
2022-04-08 12:11:15 +03:00
if err := pr . Issue . LoadRepo ( ctx ) ; err != nil {
2020-05-02 03:20:51 +03:00
ctx . Error ( http . StatusInternalServerError , "pr.Issue.LoadRepo" , err )
return
}
2020-05-08 21:50:23 +03:00
// if CommitID is empty, set it as lastCommitID
if opts . CommitID == "" {
2022-01-20 02:26:57 +03:00
gitRepo , closer , err := git . RepositoryFromContextOrOpen ( ctx , pr . Issue . Repo . RepoPath ( ) )
2020-05-08 21:50:23 +03:00
if err != nil {
2020-09-20 23:20:14 +03:00
ctx . Error ( http . StatusInternalServerError , "git.OpenRepository" , err )
2020-05-08 21:50:23 +03:00
return
}
2022-01-20 02:26:57 +03:00
defer closer . Close ( )
2020-05-08 21:50:23 +03:00
headCommitID , err := gitRepo . GetRefCommitID ( pr . GetGitRefName ( ) )
if err != nil {
2020-09-20 23:20:14 +03:00
ctx . Error ( http . StatusInternalServerError , "GetRefCommitID" , err )
2020-05-08 21:50:23 +03:00
return
}
opts . CommitID = headCommitID
}
2020-05-02 03:20:51 +03:00
// create review comments
for _ , c := range opts . Comments {
line := c . NewLineNum
if c . OldLineNum > 0 {
line = c . OldLineNum * - 1
}
2022-01-20 02:26:57 +03:00
if _ , err := pull_service . CreateCodeComment ( ctx ,
2022-03-22 10:03:22 +03:00
ctx . Doer ,
2020-05-02 03:20:51 +03:00
ctx . Repo . GitRepo ,
pr . Issue ,
line ,
c . Body ,
c . Path ,
true , // is review
0 , // no reply
opts . CommitID ,
) ; err != nil {
2020-09-20 23:20:14 +03:00
ctx . Error ( http . StatusInternalServerError , "CreateCodeComment" , err )
2020-05-02 03:20:51 +03:00
return
}
}
// create review and associate all pending review comments
2022-03-22 10:03:22 +03:00
review , _ , err := pull_service . SubmitReview ( ctx , ctx . Doer , ctx . Repo . GitRepo , pr . Issue , reviewType , opts . Body , opts . CommitID , nil )
2020-05-02 03:20:51 +03:00
if err != nil {
ctx . Error ( http . StatusInternalServerError , "SubmitReview" , err )
return
}
// convert response
2022-03-22 10:03:22 +03:00
apiReview , err := convert . ToPullReview ( ctx , review , ctx . Doer )
2020-05-02 03:20:51 +03:00
if err != nil {
ctx . Error ( http . StatusInternalServerError , "convertToPullReview" , err )
return
}
ctx . JSON ( http . StatusOK , apiReview )
}
// SubmitPullReview submit a pending review to an pull request
2021-01-26 18:36:53 +03:00
func SubmitPullReview ( ctx * context . APIContext ) {
2020-05-02 03:20:51 +03:00
// swagger:operation POST /repos/{owner}/{repo}/pulls/{index}/reviews/{id} repository repoSubmitPullReview
// ---
// summary: Submit a pending review to an pull request
// produces:
// - application/json
// parameters:
// - name: owner
// in: path
// description: owner of the repo
// type: string
// required: true
// - name: repo
// in: path
// description: name of the repo
// type: string
// required: true
// - name: index
// in: path
// description: index of the pull request
// type: integer
// format: int64
// required: true
// - name: id
// in: path
// description: id of the review
// type: integer
// format: int64
// required: true
// - name: body
// in: body
// required: true
// schema:
// "$ref": "#/definitions/SubmitPullReviewOptions"
// responses:
// "200":
// "$ref": "#/responses/PullReview"
// "404":
// "$ref": "#/responses/notFound"
// "422":
// "$ref": "#/responses/validationError"
2021-01-26 18:36:53 +03:00
opts := web . GetForm ( ctx ) . ( * api . SubmitPullReviewOptions )
2020-05-02 03:20:51 +03:00
review , pr , isWrong := prepareSingleReview ( ctx )
if isWrong {
return
}
2022-06-13 12:37:59 +03:00
if review . Type != issues_model . ReviewTypePending {
2020-05-02 03:20:51 +03:00
ctx . Error ( http . StatusUnprocessableEntity , "" , fmt . Errorf ( "only a pending review can be submitted" ) )
return
}
// determine review type
2021-06-25 01:05:51 +03:00
reviewType , isWrong := preparePullReviewType ( ctx , pr , opts . Event , opts . Body , len ( review . Comments ) > 0 )
2020-05-02 03:20:51 +03:00
if isWrong {
return
}
// if review stay pending return
2022-06-13 12:37:59 +03:00
if reviewType == issues_model . ReviewTypePending {
2020-05-02 03:20:51 +03:00
ctx . Error ( http . StatusUnprocessableEntity , "" , fmt . Errorf ( "review stay pending" ) )
return
}
headCommitID , err := ctx . Repo . GitRepo . GetRefCommitID ( pr . GetGitRefName ( ) )
if err != nil {
ctx . Error ( http . StatusInternalServerError , "GitRepo: GetRefCommitID" , err )
return
}
// create review and associate all pending review comments
2022-03-22 10:03:22 +03:00
review , _ , err = pull_service . SubmitReview ( ctx , ctx . Doer , ctx . Repo . GitRepo , pr . Issue , reviewType , opts . Body , headCommitID , nil )
2020-05-02 03:20:51 +03:00
if err != nil {
ctx . Error ( http . StatusInternalServerError , "SubmitReview" , err )
return
}
// convert response
2022-03-22 10:03:22 +03:00
apiReview , err := convert . ToPullReview ( ctx , review , ctx . Doer )
2020-05-02 03:20:51 +03:00
if err != nil {
ctx . Error ( http . StatusInternalServerError , "convertToPullReview" , err )
return
}
ctx . JSON ( http . StatusOK , apiReview )
}
// preparePullReviewType return ReviewType and false or nil and true if an error happen
2022-06-13 12:37:59 +03:00
func preparePullReviewType ( ctx * context . APIContext , pr * issues_model . PullRequest , event api . ReviewStateType , body string , hasComments bool ) ( issues_model . ReviewType , bool ) {
2022-11-19 11:12:33 +03:00
if err := pr . LoadIssue ( ctx ) ; err != nil {
2020-05-02 03:20:51 +03:00
ctx . Error ( http . StatusInternalServerError , "LoadIssue" , err )
return - 1 , true
}
2021-06-25 01:05:51 +03:00
needsBody := true
hasBody := len ( strings . TrimSpace ( body ) ) > 0
2022-06-13 12:37:59 +03:00
var reviewType issues_model . ReviewType
2020-05-02 03:20:51 +03:00
switch event {
case api . ReviewStateApproved :
// can not approve your own PR
2022-03-22 10:03:22 +03:00
if pr . Issue . IsPoster ( ctx . Doer . ID ) {
2020-05-02 03:20:51 +03:00
ctx . Error ( http . StatusUnprocessableEntity , "" , fmt . Errorf ( "approve your own pull is not allowed" ) )
return - 1 , true
}
2022-06-13 12:37:59 +03:00
reviewType = issues_model . ReviewTypeApprove
2021-06-25 01:05:51 +03:00
needsBody = false
2020-05-02 03:20:51 +03:00
case api . ReviewStateRequestChanges :
// can not reject your own PR
2022-03-22 10:03:22 +03:00
if pr . Issue . IsPoster ( ctx . Doer . ID ) {
2020-05-02 03:20:51 +03:00
ctx . Error ( http . StatusUnprocessableEntity , "" , fmt . Errorf ( "reject your own pull is not allowed" ) )
return - 1 , true
}
2022-06-13 12:37:59 +03:00
reviewType = issues_model . ReviewTypeReject
2020-05-02 03:20:51 +03:00
case api . ReviewStateComment :
2022-06-13 12:37:59 +03:00
reviewType = issues_model . ReviewTypeComment
2021-06-25 01:05:51 +03:00
needsBody = false
// if there is no body we need to ensure that there are comments
if ! hasBody && ! hasComments {
ctx . Error ( http . StatusUnprocessableEntity , "" , fmt . Errorf ( "review event %s requires a body or a comment" , event ) )
return - 1 , true
}
2020-05-02 03:20:51 +03:00
default :
2022-06-13 12:37:59 +03:00
reviewType = issues_model . ReviewTypePending
2020-05-02 03:20:51 +03:00
}
2021-06-25 01:05:51 +03:00
// reject reviews with empty body if a body is required for this call
if needsBody && ! hasBody {
ctx . Error ( http . StatusUnprocessableEntity , "" , fmt . Errorf ( "review event %s requires a body" , event ) )
2020-05-02 03:20:51 +03:00
return - 1 , true
}
return reviewType , false
}
// prepareSingleReview return review, related pull and false or nil, nil and true if an error happen
2022-06-13 12:37:59 +03:00
func prepareSingleReview ( ctx * context . APIContext ) ( * issues_model . Review , * issues_model . PullRequest , bool ) {
pr , err := issues_model . GetPullRequestByIndex ( ctx , ctx . Repo . Repository . ID , ctx . ParamsInt64 ( ":index" ) )
2020-05-02 03:20:51 +03:00
if err != nil {
2022-06-13 12:37:59 +03:00
if issues_model . IsErrPullRequestNotExist ( err ) {
2020-05-02 03:20:51 +03:00
ctx . NotFound ( "GetPullRequestByIndex" , err )
} else {
ctx . Error ( http . StatusInternalServerError , "GetPullRequestByIndex" , err )
}
return nil , nil , true
}
2022-06-13 12:37:59 +03:00
review , err := issues_model . GetReviewByID ( ctx , ctx . ParamsInt64 ( ":id" ) )
2020-05-02 03:20:51 +03:00
if err != nil {
2022-06-13 12:37:59 +03:00
if issues_model . IsErrReviewNotExist ( err ) {
2020-05-02 03:20:51 +03:00
ctx . NotFound ( "GetReviewByID" , err )
} else {
ctx . Error ( http . StatusInternalServerError , "GetReviewByID" , err )
}
return nil , nil , true
}
// validate the the review is for the given PR
if review . IssueID != pr . IssueID {
ctx . NotFound ( "ReviewNotInPR" )
return nil , nil , true
}
// make sure that the user has access to this review if it is pending
2022-06-13 12:37:59 +03:00
if review . Type == issues_model . ReviewTypePending && review . ReviewerID != ctx . Doer . ID && ! ctx . Doer . IsAdmin {
2020-05-02 03:20:51 +03:00
ctx . NotFound ( "GetReviewByID" )
return nil , nil , true
}
2022-01-20 02:26:57 +03:00
if err := review . LoadAttributes ( ctx ) ; err != nil && ! user_model . IsErrUserNotExist ( err ) {
2020-05-02 03:20:51 +03:00
ctx . Error ( http . StatusInternalServerError , "ReviewLoadAttributes" , err )
return nil , nil , true
}
return review , pr , false
}
2020-10-20 21:18:25 +03:00
// CreateReviewRequests create review requests to an pull request
2021-01-26 18:36:53 +03:00
func CreateReviewRequests ( ctx * context . APIContext ) {
2020-10-20 21:18:25 +03:00
// swagger:operation POST /repos/{owner}/{repo}/pulls/{index}/requested_reviewers repository repoCreatePullReviewRequests
// ---
// summary: create review requests for a pull request
// produces:
// - application/json
// parameters:
// - name: owner
// in: path
// description: owner of the repo
// type: string
// required: true
// - name: repo
// in: path
// description: name of the repo
// type: string
// required: true
// - name: index
// in: path
// description: index of the pull request
// type: integer
// format: int64
// required: true
// - name: body
// in: body
// required: true
// schema:
// "$ref": "#/definitions/PullReviewRequestOptions"
// responses:
// "201":
// "$ref": "#/responses/PullReviewList"
// "422":
// "$ref": "#/responses/validationError"
// "404":
// "$ref": "#/responses/notFound"
2021-01-26 18:36:53 +03:00
opts := web . GetForm ( ctx ) . ( * api . PullReviewRequestOptions )
apiReviewRequest ( ctx , * opts , true )
2020-10-20 21:18:25 +03:00
}
// DeleteReviewRequests delete review requests to an pull request
2021-01-26 18:36:53 +03:00
func DeleteReviewRequests ( ctx * context . APIContext ) {
2020-10-20 21:18:25 +03:00
// swagger:operation DELETE /repos/{owner}/{repo}/pulls/{index}/requested_reviewers repository repoDeletePullReviewRequests
// ---
// summary: cancel review requests for a pull request
// produces:
// - application/json
// parameters:
// - name: owner
// in: path
// description: owner of the repo
// type: string
// required: true
// - name: repo
// in: path
// description: name of the repo
// type: string
// required: true
// - name: index
// in: path
// description: index of the pull request
// type: integer
// format: int64
// required: true
// - name: body
// in: body
// required: true
// schema:
// "$ref": "#/definitions/PullReviewRequestOptions"
// responses:
// "204":
// "$ref": "#/responses/empty"
// "422":
// "$ref": "#/responses/validationError"
// "404":
// "$ref": "#/responses/notFound"
2021-01-26 18:36:53 +03:00
opts := web . GetForm ( ctx ) . ( * api . PullReviewRequestOptions )
apiReviewRequest ( ctx , * opts , false )
2020-10-20 21:18:25 +03:00
}
func apiReviewRequest ( ctx * context . APIContext , opts api . PullReviewRequestOptions , isAdd bool ) {
2022-06-13 12:37:59 +03:00
pr , err := issues_model . GetPullRequestByIndex ( ctx , ctx . Repo . Repository . ID , ctx . ParamsInt64 ( ":index" ) )
2020-10-20 21:18:25 +03:00
if err != nil {
2022-06-13 12:37:59 +03:00
if issues_model . IsErrPullRequestNotExist ( err ) {
2020-10-20 21:18:25 +03:00
ctx . NotFound ( "GetPullRequestByIndex" , err )
} else {
ctx . Error ( http . StatusInternalServerError , "GetPullRequestByIndex" , err )
}
return
}
2022-04-08 12:11:15 +03:00
if err := pr . Issue . LoadRepo ( ctx ) ; err != nil {
2020-10-20 21:18:25 +03:00
ctx . Error ( http . StatusInternalServerError , "pr.Issue.LoadRepo" , err )
return
}
2021-11-24 12:49:20 +03:00
reviewers := make ( [ ] * user_model . User , 0 , len ( opts . Reviewers ) )
2020-10-20 21:18:25 +03:00
2022-05-11 13:09:36 +03:00
permDoer , err := access_model . GetUserRepoPermission ( ctx , pr . Issue . Repo , ctx . Doer )
2020-10-20 21:18:25 +03:00
if err != nil {
ctx . Error ( http . StatusInternalServerError , "GetUserRepoPermission" , err )
return
}
for _ , r := range opts . Reviewers {
2021-11-24 12:49:20 +03:00
var reviewer * user_model . User
2020-10-20 21:18:25 +03:00
if strings . Contains ( r , "@" ) {
2021-11-24 12:49:20 +03:00
reviewer , err = user_model . GetUserByEmail ( r )
2020-10-20 21:18:25 +03:00
} else {
2022-05-20 17:08:52 +03:00
reviewer , err = user_model . GetUserByName ( ctx , r )
2020-10-20 21:18:25 +03:00
}
if err != nil {
2021-11-24 12:49:20 +03:00
if user_model . IsErrUserNotExist ( err ) {
2020-10-20 21:18:25 +03:00
ctx . NotFound ( "UserNotExist" , fmt . Sprintf ( "User '%s' not exist" , r ) )
return
}
ctx . Error ( http . StatusInternalServerError , "GetUser" , err )
return
}
2022-04-28 14:48:48 +03:00
err = issue_service . IsValidReviewRequest ( ctx , reviewer , ctx . Doer , isAdd , pr . Issue , & permDoer )
2020-10-20 21:18:25 +03:00
if err != nil {
2022-06-13 12:37:59 +03:00
if issues_model . IsErrNotValidReviewRequest ( err ) {
2020-10-20 21:18:25 +03:00
ctx . Error ( http . StatusUnprocessableEntity , "NotValidReviewRequest" , err )
return
}
ctx . Error ( http . StatusInternalServerError , "IsValidReviewRequest" , err )
return
}
reviewers = append ( reviewers , reviewer )
}
2022-06-13 12:37:59 +03:00
var reviews [ ] * issues_model . Review
2020-10-20 21:18:25 +03:00
if isAdd {
2022-06-13 12:37:59 +03:00
reviews = make ( [ ] * issues_model . Review , 0 , len ( reviewers ) )
2020-10-20 21:18:25 +03:00
}
for _ , reviewer := range reviewers {
2022-03-22 10:03:22 +03:00
comment , err := issue_service . ReviewRequest ( pr . Issue , ctx . Doer , reviewer , isAdd )
2020-10-20 21:18:25 +03:00
if err != nil {
ctx . Error ( http . StatusInternalServerError , "ReviewRequest" , err )
return
}
if comment != nil && isAdd {
if err = comment . LoadReview ( ) ; err != nil {
ctx . ServerError ( "ReviewRequest" , err )
return
}
reviews = append ( reviews , comment . Review )
}
}
if ctx . Repo . Repository . Owner . IsOrganization ( ) && len ( opts . TeamReviewers ) > 0 {
2022-03-29 09:29:02 +03:00
teamReviewers := make ( [ ] * organization . Team , 0 , len ( opts . TeamReviewers ) )
2020-10-20 21:18:25 +03:00
for _ , t := range opts . TeamReviewers {
2022-03-29 09:29:02 +03:00
var teamReviewer * organization . Team
2022-05-20 17:08:52 +03:00
teamReviewer , err = organization . GetTeam ( ctx , ctx . Repo . Owner . ID , t )
2020-10-20 21:18:25 +03:00
if err != nil {
2022-03-29 09:29:02 +03:00
if organization . IsErrTeamNotExist ( err ) {
2020-10-20 21:18:25 +03:00
ctx . NotFound ( "TeamNotExist" , fmt . Sprintf ( "Team '%s' not exist" , t ) )
return
}
ctx . Error ( http . StatusInternalServerError , "ReviewRequest" , err )
return
}
2022-04-28 14:48:48 +03:00
err = issue_service . IsValidTeamReviewRequest ( ctx , teamReviewer , ctx . Doer , isAdd , pr . Issue )
2020-10-20 21:18:25 +03:00
if err != nil {
2022-06-13 12:37:59 +03:00
if issues_model . IsErrNotValidReviewRequest ( err ) {
2020-10-20 21:18:25 +03:00
ctx . Error ( http . StatusUnprocessableEntity , "NotValidReviewRequest" , err )
return
}
ctx . Error ( http . StatusInternalServerError , "IsValidTeamReviewRequest" , err )
return
}
teamReviewers = append ( teamReviewers , teamReviewer )
}
for _ , teamReviewer := range teamReviewers {
2022-03-22 10:03:22 +03:00
comment , err := issue_service . TeamReviewRequest ( pr . Issue , ctx . Doer , teamReviewer , isAdd )
2020-10-20 21:18:25 +03:00
if err != nil {
ctx . ServerError ( "TeamReviewRequest" , err )
return
}
if comment != nil && isAdd {
if err = comment . LoadReview ( ) ; err != nil {
ctx . ServerError ( "ReviewRequest" , err )
return
}
reviews = append ( reviews , comment . Review )
}
}
}
if isAdd {
2022-03-22 10:03:22 +03:00
apiReviews , err := convert . ToPullReviewList ( ctx , reviews , ctx . Doer )
2020-10-20 21:18:25 +03:00
if err != nil {
ctx . Error ( http . StatusInternalServerError , "convertToPullReviewList" , err )
return
}
ctx . JSON ( http . StatusCreated , apiReviews )
} else {
ctx . Status ( http . StatusNoContent )
return
}
}
2021-02-11 20:32:25 +03:00
// DismissPullReview dismiss a review for a pull request
func DismissPullReview ( ctx * context . APIContext ) {
// swagger:operation POST /repos/{owner}/{repo}/pulls/{index}/reviews/{id}/dismissals repository repoDismissPullReview
// ---
// summary: Dismiss a review for a pull request
// produces:
// - application/json
// parameters:
// - name: owner
// in: path
// description: owner of the repo
// type: string
// required: true
// - name: repo
// in: path
// description: name of the repo
// type: string
// required: true
// - name: index
// in: path
// description: index of the pull request
// type: integer
// format: int64
// required: true
// - name: id
// in: path
// description: id of the review
// type: integer
// format: int64
// required: true
// - name: body
// in: body
// required: true
// schema:
// "$ref": "#/definitions/DismissPullReviewOptions"
// responses:
// "200":
// "$ref": "#/responses/PullReview"
// "403":
// "$ref": "#/responses/forbidden"
// "422":
// "$ref": "#/responses/validationError"
opts := web . GetForm ( ctx ) . ( * api . DismissPullReviewOptions )
2022-07-19 16:20:28 +03:00
dismissReview ( ctx , opts . Message , true , opts . Priors )
2021-02-11 20:32:25 +03:00
}
// UnDismissPullReview cancel to dismiss a review for a pull request
func UnDismissPullReview ( ctx * context . APIContext ) {
// swagger:operation POST /repos/{owner}/{repo}/pulls/{index}/reviews/{id}/undismissals repository repoUnDismissPullReview
// ---
// summary: Cancel to dismiss a review for a pull request
// produces:
// - application/json
// parameters:
// - name: owner
// in: path
// description: owner of the repo
// type: string
// required: true
// - name: repo
// in: path
// description: name of the repo
// type: string
// required: true
// - name: index
// in: path
// description: index of the pull request
// type: integer
// format: int64
// required: true
// - name: id
// in: path
// description: id of the review
// type: integer
// format: int64
// required: true
// responses:
// "200":
// "$ref": "#/responses/PullReview"
// "403":
// "$ref": "#/responses/forbidden"
// "422":
// "$ref": "#/responses/validationError"
2022-07-19 16:20:28 +03:00
dismissReview ( ctx , "" , false , false )
2021-02-11 20:32:25 +03:00
}
2022-07-19 16:20:28 +03:00
func dismissReview ( ctx * context . APIContext , msg string , isDismiss , dismissPriors bool ) {
2021-02-11 20:32:25 +03:00
if ! ctx . Repo . IsAdmin ( ) {
ctx . Error ( http . StatusForbidden , "" , "Must be repo admin" )
return
}
review , pr , isWrong := prepareSingleReview ( ctx )
if isWrong {
return
}
2022-06-13 12:37:59 +03:00
if review . Type != issues_model . ReviewTypeApprove && review . Type != issues_model . ReviewTypeReject {
2021-02-11 20:32:25 +03:00
ctx . Error ( http . StatusForbidden , "" , "not need to dismiss this review because it's type is not Approve or change request" )
return
}
if pr . Issue . IsClosed {
ctx . Error ( http . StatusForbidden , "" , "not need to dismiss this review because this pr is closed" )
return
}
2022-07-19 16:20:28 +03:00
_ , err := pull_service . DismissReview ( ctx , review . ID , ctx . Repo . Repository . ID , msg , ctx . Doer , isDismiss , dismissPriors )
2021-02-11 20:32:25 +03:00
if err != nil {
ctx . Error ( http . StatusInternalServerError , "pull_service.DismissReview" , err )
return
}
2022-06-13 12:37:59 +03:00
if review , err = issues_model . GetReviewByID ( ctx , review . ID ) ; err != nil {
2021-02-11 20:32:25 +03:00
ctx . Error ( http . StatusInternalServerError , "GetReviewByID" , err )
return
}
// convert response
2022-03-22 10:03:22 +03:00
apiReview , err := convert . ToPullReview ( ctx , review , ctx . Doer )
2021-02-11 20:32:25 +03:00
if err != nil {
ctx . Error ( http . StatusInternalServerError , "convertToPullReview" , err )
return
}
ctx . JSON ( http . StatusOK , apiReview )
}