Skip to content

Commit 6c34331

Browse files
API: Allow COMMENT reviews to not specify a body (#16229)
* Allow COMMENT reviews to not specify a body when using web ui there is no need to specify a body. so we don't need to specify a body if adding a COMMENT-review via our api. * Ensure comments or Body is provided and add some integration tests for reviewtype COMMENT. Signed-off-by: Sebastian Sauer <[email protected]>
1 parent fd6b1be commit 6c34331

File tree

2 files changed

+70
-6
lines changed

2 files changed

+70
-6
lines changed

integrations/api_pull_review_test.go

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111

1212
"code.gitea.io/gitea/models"
1313
api "code.gitea.io/gitea/modules/structs"
14+
jsoniter "github.com/json-iterator/go"
1415
"github.com/stretchr/testify/assert"
1516
)
1617

@@ -139,6 +140,59 @@ func TestAPIPullReview(t *testing.T) {
139140
req = NewRequestf(t, http.MethodDelete, "/api/v1/repos/%s/%s/pulls/%d/reviews/%d?token=%s", repo.OwnerName, repo.Name, pullIssue.Index, review.ID, token)
140141
resp = session.MakeRequest(t, req, http.StatusNoContent)
141142

143+
// test CreatePullReview Comment without body but with comments
144+
req = NewRequestWithJSON(t, http.MethodPost, fmt.Sprintf("/api/v1/repos/%s/%s/pulls/%d/reviews?token=%s", repo.OwnerName, repo.Name, pullIssue.Index, token), &api.CreatePullReviewOptions{
145+
// Body: "",
146+
Event: "COMMENT",
147+
Comments: []api.CreatePullReviewComment{{
148+
Path: "README.md",
149+
Body: "first new line",
150+
OldLineNum: 0,
151+
NewLineNum: 1,
152+
}, {
153+
Path: "README.md",
154+
Body: "first old line",
155+
OldLineNum: 1,
156+
NewLineNum: 0,
157+
},
158+
},
159+
})
160+
var commentReview api.PullReview
161+
162+
resp = session.MakeRequest(t, req, http.StatusOK)
163+
DecodeJSON(t, resp, &commentReview)
164+
assert.EqualValues(t, "COMMENT", commentReview.State)
165+
assert.EqualValues(t, 2, commentReview.CodeCommentsCount)
166+
assert.EqualValues(t, "", commentReview.Body)
167+
assert.EqualValues(t, false, commentReview.Dismissed)
168+
169+
// test CreatePullReview Comment with body but without comments
170+
commentBody := "This is a body of the comment."
171+
req = NewRequestWithJSON(t, http.MethodPost, fmt.Sprintf("/api/v1/repos/%s/%s/pulls/%d/reviews?token=%s", repo.OwnerName, repo.Name, pullIssue.Index, token), &api.CreatePullReviewOptions{
172+
Body: commentBody,
173+
Event: "COMMENT",
174+
Comments: []api.CreatePullReviewComment{},
175+
})
176+
177+
resp = session.MakeRequest(t, req, http.StatusOK)
178+
DecodeJSON(t, resp, &commentReview)
179+
assert.EqualValues(t, "COMMENT", commentReview.State)
180+
assert.EqualValues(t, 0, commentReview.CodeCommentsCount)
181+
assert.EqualValues(t, commentBody, commentReview.Body)
182+
assert.EqualValues(t, false, commentReview.Dismissed)
183+
184+
// test CreatePullReview Comment without body and no comments
185+
req = NewRequestWithJSON(t, http.MethodPost, fmt.Sprintf("/api/v1/repos/%s/%s/pulls/%d/reviews?token=%s", repo.OwnerName, repo.Name, pullIssue.Index, token), &api.CreatePullReviewOptions{
186+
Body: "",
187+
Event: "COMMENT",
188+
Comments: []api.CreatePullReviewComment{},
189+
})
190+
resp = session.MakeRequest(t, req, http.StatusUnprocessableEntity)
191+
errMap := make(map[string]interface{})
192+
json := jsoniter.ConfigCompatibleWithStandardLibrary
193+
json.Unmarshal(resp.Body.Bytes(), &errMap)
194+
assert.EqualValues(t, "review event COMMENT requires a body or a comment", errMap["message"].(string))
195+
142196
// test get review requests
143197
// to make it simple, use same api with get review
144198
pullIssue12 := models.AssertExistsAndLoadBean(t, &models.Issue{ID: 12}).(*models.Issue)

routers/api/v1/repo/pull_review.go

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -307,7 +307,7 @@ func CreatePullReview(ctx *context.APIContext) {
307307
}
308308

309309
// determine review type
310-
reviewType, isWrong := preparePullReviewType(ctx, pr, opts.Event, opts.Body)
310+
reviewType, isWrong := preparePullReviewType(ctx, pr, opts.Event, opts.Body, len(opts.Comments) > 0)
311311
if isWrong {
312312
return
313313
}
@@ -429,7 +429,7 @@ func SubmitPullReview(ctx *context.APIContext) {
429429
}
430430

431431
// determine review type
432-
reviewType, isWrong := preparePullReviewType(ctx, pr, opts.Event, opts.Body)
432+
reviewType, isWrong := preparePullReviewType(ctx, pr, opts.Event, opts.Body, len(review.Comments) > 0)
433433
if isWrong {
434434
return
435435
}
@@ -463,12 +463,15 @@ func SubmitPullReview(ctx *context.APIContext) {
463463
}
464464

465465
// preparePullReviewType return ReviewType and false or nil and true if an error happen
466-
func preparePullReviewType(ctx *context.APIContext, pr *models.PullRequest, event api.ReviewStateType, body string) (models.ReviewType, bool) {
466+
func preparePullReviewType(ctx *context.APIContext, pr *models.PullRequest, event api.ReviewStateType, body string, hasComments bool) (models.ReviewType, bool) {
467467
if err := pr.LoadIssue(); err != nil {
468468
ctx.Error(http.StatusInternalServerError, "LoadIssue", err)
469469
return -1, true
470470
}
471471

472+
needsBody := true
473+
hasBody := len(strings.TrimSpace(body)) > 0
474+
472475
var reviewType models.ReviewType
473476
switch event {
474477
case api.ReviewStateApproved:
@@ -478,6 +481,7 @@ func preparePullReviewType(ctx *context.APIContext, pr *models.PullRequest, even
478481
return -1, true
479482
}
480483
reviewType = models.ReviewTypeApprove
484+
needsBody = false
481485

482486
case api.ReviewStateRequestChanges:
483487
// can not reject your own PR
@@ -489,13 +493,19 @@ func preparePullReviewType(ctx *context.APIContext, pr *models.PullRequest, even
489493

490494
case api.ReviewStateComment:
491495
reviewType = models.ReviewTypeComment
496+
needsBody = false
497+
// if there is no body we need to ensure that there are comments
498+
if !hasBody && !hasComments {
499+
ctx.Error(http.StatusUnprocessableEntity, "", fmt.Errorf("review event %s requires a body or a comment", event))
500+
return -1, true
501+
}
492502
default:
493503
reviewType = models.ReviewTypePending
494504
}
495505

496-
// reject reviews with empty body if not approve type
497-
if reviewType != models.ReviewTypeApprove && len(strings.TrimSpace(body)) == 0 {
498-
ctx.Error(http.StatusUnprocessableEntity, "", fmt.Errorf("review event %s need body", event))
506+
// reject reviews with empty body if a body is required for this call
507+
if needsBody && !hasBody {
508+
ctx.Error(http.StatusUnprocessableEntity, "", fmt.Errorf("review event %s requires a body", event))
499509
return -1, true
500510
}
501511

0 commit comments

Comments
 (0)