Skip to content

Commit 4f90506

Browse files
committed
feat(api): add resolve/unresolve review comment endpoints
1 parent 0f78b99 commit 4f90506

File tree

5 files changed

+420
-21
lines changed

5 files changed

+420
-21
lines changed

routers/api/v1/api.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1360,6 +1360,8 @@ func Routes() *web.Router {
13601360
m.Post("/update", reqToken(), repo.UpdatePullRequest)
13611361
m.Get("/commits", repo.GetPullRequestCommits)
13621362
m.Get("/files", repo.GetPullRequestFiles)
1363+
m.Post("/comments/{id}/resolve", reqToken(), mustNotBeArchived, repo.ResolvePullReviewComment)
1364+
m.Post("/comments/{id}/unresolve", reqToken(), mustNotBeArchived, repo.UnresolvePullReviewComment)
13631365
m.Combo("/merge").Get(repo.IsPullRequestMerged).
13641366
Post(reqToken(), mustNotBeArchived, bind(forms.MergePullRequestForm{}), repo.MergePullRequest).
13651367
Delete(reqToken(), mustNotBeArchived, repo.CancelScheduledAutoMerge)

routers/api/v1/repo/pull_review.go

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,168 @@ func GetPullReviewComments(ctx *context.APIContext) {
208208
ctx.JSON(http.StatusOK, apiComments)
209209
}
210210

211+
// ResolvePullReviewComment resolves a review comment in a pull request
212+
func ResolvePullReviewComment(ctx *context.APIContext) {
213+
// swagger:operation POST /repos/{owner}/{repo}/pulls/{index}/comments/{id}/resolve repository repoResolvePullReviewComment
214+
// ---
215+
// summary: Resolve a pull review comment
216+
// produces:
217+
// - application/json
218+
// parameters:
219+
// - name: owner
220+
// in: path
221+
// description: owner of the repo
222+
// type: string
223+
// required: true
224+
// - name: repo
225+
// in: path
226+
// description: name of the repo
227+
// type: string
228+
// required: true
229+
// - name: index
230+
// in: path
231+
// description: index of the pull request
232+
// type: integer
233+
// format: int64
234+
// required: true
235+
// - name: id
236+
// in: path
237+
// description: id of the review comment
238+
// type: integer
239+
// format: int64
240+
// required: true
241+
// responses:
242+
// "200":
243+
// "$ref": "#/responses/PullReviewComment"
244+
// "400":
245+
// "$ref": "#/responses/validationError"
246+
// "403":
247+
// "$ref": "#/responses/forbidden"
248+
// "404":
249+
// "$ref": "#/responses/notFound"
250+
updatePullReviewCommentResolve(ctx, true)
251+
}
252+
253+
// UnresolvePullReviewComment unresolves a review comment in a pull request
254+
func UnresolvePullReviewComment(ctx *context.APIContext) {
255+
// swagger:operation POST /repos/{owner}/{repo}/pulls/{index}/comments/{id}/unresolve repository repoUnresolvePullReviewComment
256+
// ---
257+
// summary: Unresolve a pull review comment
258+
// produces:
259+
// - application/json
260+
// parameters:
261+
// - name: owner
262+
// in: path
263+
// description: owner of the repo
264+
// type: string
265+
// required: true
266+
// - name: repo
267+
// in: path
268+
// description: name of the repo
269+
// type: string
270+
// required: true
271+
// - name: index
272+
// in: path
273+
// description: index of the pull request
274+
// type: integer
275+
// format: int64
276+
// required: true
277+
// - name: id
278+
// in: path
279+
// description: id of the review comment
280+
// type: integer
281+
// format: int64
282+
// required: true
283+
// responses:
284+
// "200":
285+
// "$ref": "#/responses/PullReviewComment"
286+
// "400":
287+
// "$ref": "#/responses/validationError"
288+
// "403":
289+
// "$ref": "#/responses/forbidden"
290+
// "404":
291+
// "$ref": "#/responses/notFound"
292+
updatePullReviewCommentResolve(ctx, false)
293+
}
294+
295+
func updatePullReviewCommentResolve(ctx *context.APIContext, isResolve bool) {
296+
commentID := ctx.PathParamInt64("id")
297+
comment, err := issues_model.GetCommentByID(ctx, commentID)
298+
if err != nil {
299+
if issues_model.IsErrCommentNotExist(err) {
300+
ctx.APIErrorNotFound("GetCommentByID", err)
301+
} else {
302+
ctx.APIErrorInternal(err)
303+
}
304+
return
305+
}
306+
307+
if err = comment.LoadIssue(ctx); err != nil {
308+
ctx.APIErrorInternal(err)
309+
return
310+
}
311+
312+
if comment.Issue.RepoID != ctx.Repo.Repository.ID {
313+
ctx.APIErrorNotFound("CommentNotInRepo")
314+
return
315+
}
316+
317+
if !comment.Issue.IsPull {
318+
ctx.APIError(http.StatusBadRequest, "comment does not belong to a pull request")
319+
return
320+
}
321+
322+
if comment.Issue.Index != ctx.PathParamInt64("index") {
323+
ctx.APIErrorNotFound("CommentNotInPullRequest")
324+
return
325+
}
326+
327+
if comment.Type != issues_model.CommentTypeCode {
328+
ctx.APIError(http.StatusBadRequest, "comment is not a review comment")
329+
return
330+
}
331+
332+
permResult, err := issues_model.CanMarkConversation(ctx, comment.Issue, ctx.Doer)
333+
if err != nil {
334+
ctx.APIErrorInternal(err)
335+
return
336+
}
337+
if !permResult {
338+
ctx.APIError(http.StatusForbidden, "user should have permission to resolve comment")
339+
return
340+
}
341+
342+
wasResolved := comment.ResolveDoerID != 0
343+
if err = issues_model.MarkConversation(ctx, comment, ctx.Doer, isResolve); err != nil {
344+
ctx.APIErrorInternal(err)
345+
return
346+
}
347+
348+
if isResolve {
349+
if !wasResolved {
350+
comment.ResolveDoerID = ctx.Doer.ID
351+
}
352+
} else {
353+
if wasResolved {
354+
comment.ResolveDoerID = 0
355+
}
356+
comment.ResolveDoer = nil
357+
}
358+
359+
if err = comment.LoadPoster(ctx); err != nil {
360+
ctx.APIErrorInternal(err)
361+
return
362+
}
363+
364+
if err = comment.LoadResolveDoer(ctx); err != nil {
365+
ctx.APIErrorInternal(err)
366+
return
367+
}
368+
369+
comment.Issue.Repo = ctx.Repo.Repository
370+
ctx.JSON(http.StatusOK, convert.ToPullReviewComment(ctx, comment, comment.Issue, ctx.Doer))
371+
}
372+
211373
// DeletePullReview delete a specific review from a pull request
212374
func DeletePullReview(ctx *context.APIContext) {
213375
// swagger:operation DELETE /repos/{owner}/{repo}/pulls/{index}/reviews/{id} repository repoDeletePullReview

services/convert/pull_review.go

Lines changed: 41 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -92,34 +92,54 @@ func ToPullReviewCommentList(ctx context.Context, review *issues_model.Review, d
9292
for _, lines := range review.CodeComments {
9393
for _, comments := range lines {
9494
for _, comment := range comments {
95-
apiComment := &api.PullReviewComment{
96-
ID: comment.ID,
97-
Body: comment.Content,
98-
Poster: ToUser(ctx, comment.Poster, doer),
99-
Resolver: ToUser(ctx, comment.ResolveDoer, doer),
100-
ReviewID: review.ID,
101-
Created: comment.CreatedUnix.AsTime(),
102-
Updated: comment.UpdatedUnix.AsTime(),
103-
Path: comment.TreePath,
104-
CommitID: comment.CommitSHA,
105-
OrigCommitID: comment.OldRef,
106-
DiffHunk: patch2diff(comment.Patch),
107-
HTMLURL: comment.HTMLURL(ctx),
108-
HTMLPullURL: review.Issue.HTMLURL(ctx),
95+
apiComment := ToPullReviewComment(ctx, comment, review.Issue, doer)
96+
if apiComment != nil {
97+
apiComments = append(apiComments, apiComment)
10998
}
110-
111-
if comment.Line < 0 {
112-
apiComment.OldLineNum = comment.UnsignedLine()
113-
} else {
114-
apiComment.LineNum = comment.UnsignedLine()
115-
}
116-
apiComments = append(apiComments, apiComment)
11799
}
118100
}
119101
}
120102
return apiComments, nil
121103
}
122104

105+
// ToPullReviewComment convert a single code review comment to api format
106+
func ToPullReviewComment(ctx context.Context, comment *issues_model.Comment, issue *issues_model.Issue, doer *user_model.User) *api.PullReviewComment {
107+
if comment == nil {
108+
return nil
109+
}
110+
111+
if issue == nil {
112+
issue = comment.Issue
113+
}
114+
115+
apiComment := &api.PullReviewComment{
116+
ID: comment.ID,
117+
Body: comment.Content,
118+
Poster: ToUser(ctx, comment.Poster, doer),
119+
Resolver: ToUser(ctx, comment.ResolveDoer, doer),
120+
ReviewID: comment.ReviewID,
121+
Created: comment.CreatedUnix.AsTime(),
122+
Updated: comment.UpdatedUnix.AsTime(),
123+
Path: comment.TreePath,
124+
CommitID: comment.CommitSHA,
125+
OrigCommitID: comment.OldRef,
126+
DiffHunk: patch2diff(comment.Patch),
127+
HTMLURL: comment.HTMLURL(ctx),
128+
}
129+
130+
if issue != nil {
131+
apiComment.HTMLPullURL = issue.HTMLURL(ctx)
132+
}
133+
134+
if comment.Line < 0 {
135+
apiComment.OldLineNum = comment.UnsignedLine()
136+
} else {
137+
apiComment.LineNum = comment.UnsignedLine()
138+
}
139+
140+
return apiComment
141+
}
142+
123143
func patch2diff(patch string) string {
124144
split := strings.Split(patch, "\n@@")
125145
if len(split) == 2 {

templates/swagger/v1_json.tmpl

Lines changed: 116 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)