From ac701637b42d2d6bb5fe9b258f3f54959b6a505e Mon Sep 17 00:00:00 2001 From: a1012112796 <1012112796@qq.com> Date: Fri, 12 Feb 2021 01:32:25 +0800 Subject: Add dismiss review feature (#12674) * Add dismiss review feature refs: https://github.blog/2016-10-12-dismissing-reviews-on-pull-requests/ https://developer.github.com/v3/pulls/reviews/#dismiss-a-review-for-a-pull-request * change modal ui and error message * Add unDismissReview api Signed-off-by: a1012112796 <1012112796@qq.com> Co-authored-by: zeripath Co-authored-by: 6543 <6543@obermui.de> --- routers/api/v1/api.go | 2 + routers/api/v1/repo/pull_review.go | 126 +++++++++++++++++++++++++++++++++++++ routers/api/v1/swagger/options.go | 3 + routers/repo/issue.go | 2 +- routers/repo/pull_review.go | 12 ++++ routers/routes/web.go | 1 + 6 files changed, 145 insertions(+), 1 deletion(-) (limited to 'routers') diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go index 9c21107a28..85c4e4d5bf 100644 --- a/routers/api/v1/api.go +++ b/routers/api/v1/api.go @@ -891,6 +891,8 @@ func Routes() *web.Route { Post(reqToken(), bind(api.SubmitPullReviewOptions{}), repo.SubmitPullReview) m.Combo("/comments"). Get(repo.GetPullReviewComments) + m.Post("/dismissals", reqToken(), bind(api.DismissPullReviewOptions{}), repo.DismissPullReview) + m.Post("/undismissals", reqToken(), repo.UnDismissPullReview) }) }) m.Combo("/requested_reviewers"). diff --git a/routers/api/v1/repo/pull_review.go b/routers/api/v1/repo/pull_review.go index d39db4c660..63179aa990 100644 --- a/routers/api/v1/repo/pull_review.go +++ b/routers/api/v1/repo/pull_review.go @@ -757,3 +757,129 @@ func apiReviewRequest(ctx *context.APIContext, opts api.PullReviewRequestOptions return } } + +// 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) + dismissReview(ctx, opts.Message, true) +} + +// 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" + dismissReview(ctx, "", false) +} + +func dismissReview(ctx *context.APIContext, msg string, isDismiss bool) { + if !ctx.Repo.IsAdmin() { + ctx.Error(http.StatusForbidden, "", "Must be repo admin") + return + } + review, pr, isWrong := prepareSingleReview(ctx) + if isWrong { + return + } + + if review.Type != models.ReviewTypeApprove && review.Type != models.ReviewTypeReject { + 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 + } + + _, err := pull_service.DismissReview(review.ID, msg, ctx.User, isDismiss) + if err != nil { + ctx.Error(http.StatusInternalServerError, "pull_service.DismissReview", err) + return + } + + if review, err = models.GetReviewByID(review.ID); err != nil { + ctx.Error(http.StatusInternalServerError, "GetReviewByID", err) + return + } + + // convert response + apiReview, err := convert.ToPullReview(review, ctx.User) + if err != nil { + ctx.Error(http.StatusInternalServerError, "convertToPullReview", err) + return + } + ctx.JSON(http.StatusOK, apiReview) +} diff --git a/routers/api/v1/swagger/options.go b/routers/api/v1/swagger/options.go index 8919a969ec..a2dc2193a8 100644 --- a/routers/api/v1/swagger/options.go +++ b/routers/api/v1/swagger/options.go @@ -150,6 +150,9 @@ type swaggerParameterBodies struct { // in:body SubmitPullReviewOptions api.SubmitPullReviewOptions + // in:body + DismissPullReviewOptions api.DismissPullReviewOptions + // in:body MigrateRepoOptions api.MigrateRepoOptions diff --git a/routers/repo/issue.go b/routers/repo/issue.go index 71c8f1efbb..fa1ee99771 100644 --- a/routers/repo/issue.go +++ b/routers/repo/issue.go @@ -1364,7 +1364,7 @@ func ViewIssue(ctx *context.Context) { return } } - } else if comment.Type == models.CommentTypeCode || comment.Type == models.CommentTypeReview { + } else if comment.Type == models.CommentTypeCode || comment.Type == models.CommentTypeReview || comment.Type == models.CommentTypeDismissReview { comment.RenderedContent = string(markdown.Render([]byte(comment.Content), ctx.Repo.RepoLink, ctx.Repo.Repository.ComposeMetas())) if err = comment.LoadReview(); err != nil && !models.IsErrReviewNotExist(err) { diff --git a/routers/repo/pull_review.go b/routers/repo/pull_review.go index df49b6cfe1..89e87ccc44 100644 --- a/routers/repo/pull_review.go +++ b/routers/repo/pull_review.go @@ -223,3 +223,15 @@ func SubmitReview(ctx *context.Context) { ctx.Redirect(fmt.Sprintf("%s/pulls/%d#%s", ctx.Repo.RepoLink, issue.Index, comm.HashTag())) } + +// DismissReview dismissing stale review by repo admin +func DismissReview(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.DismissReviewForm) + comm, err := pull_service.DismissReview(form.ReviewID, form.Message, ctx.User, true) + if err != nil { + ctx.ServerError("pull_service.DismissReview", err) + return + } + + ctx.Redirect(fmt.Sprintf("%s/pulls/%d#%s", ctx.Repo.RepoLink, comm.Issue.Index, comm.HashTag())) +} diff --git a/routers/routes/web.go b/routers/routes/web.go index 9e3e690fb9..2f28e567f9 100644 --- a/routers/routes/web.go +++ b/routers/routes/web.go @@ -734,6 +734,7 @@ func RegisterRoutes(m *web.Route) { m.Post("/projects", reqRepoIssuesOrPullsWriter, repo.UpdateIssueProject) m.Post("/assignee", reqRepoIssuesOrPullsWriter, repo.UpdateIssueAssignee) m.Post("/request_review", reqRepoIssuesOrPullsReader, repo.UpdatePullReviewRequest) + m.Post("/dismiss_review", reqRepoAdmin, bindIgnErr(auth.DismissReviewForm{}), repo.DismissReview) m.Post("/status", reqRepoIssuesOrPullsWriter, repo.UpdateIssueStatus) m.Post("/resolve_conversation", reqRepoIssuesOrPullsReader, repo.UpdateResolveConversation) m.Post("/attachments", repo.UploadIssueAttachment) -- cgit v1.2.3