Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions models/issues/comment.go
Original file line number Diff line number Diff line change
Expand Up @@ -1034,6 +1034,20 @@ func GetCommentByID(ctx context.Context, id int64) (*Comment, error) {
return c, nil
}

func GetCommentWithRepoID(ctx context.Context, repoID, commentID int64) (*Comment, error) {
c, err := GetCommentByID(ctx, commentID)
if err != nil {
return nil, err
}
if err := c.LoadIssue(ctx); err != nil {
return nil, err
}
if c.Issue.RepoID != repoID {
return nil, ErrCommentNotExist{commentID, 0}
}
return c, nil
}

// FindCommentsOptions describes the conditions to Find comments
type FindCommentsOptions struct {
db.ListOptions
Expand Down
1 change: 1 addition & 0 deletions models/issues/comment_code.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ func findCodeComments(ctx context.Context, opts FindCommentsOptions, issue *Issu
continue
}
comment.Review = re
comment.Issue = issue
}
comments[n] = comment
n++
Expand Down
2 changes: 2 additions & 0 deletions routers/api/v1/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -1353,6 +1353,8 @@ func Routes() *web.Router {
m.Combo("").Get(repo.ListPullRequests).
Post(reqToken(), mustNotBeArchived, bind(api.CreatePullRequestOption{}), repo.CreatePullRequest)
m.Get("/pinned", repo.ListPinnedPullRequests)
m.Post("/comments/{id}/resolve", reqToken(), mustNotBeArchived, repo.ResolvePullReviewComment)
m.Post("/comments/{id}/unresolve", reqToken(), mustNotBeArchived, repo.UnresolvePullReviewComment)
m.Group("/{index}", func() {
m.Combo("").Get(repo.GetPullRequest).
Patch(reqToken(), bind(api.EditPullRequestOption{}), repo.EditPullRequest)
Expand Down
35 changes: 3 additions & 32 deletions routers/api/v1/repo/issue_comment.go
Original file line number Diff line number Diff line change
Expand Up @@ -445,7 +445,7 @@ func GetIssueComment(ctx *context.APIContext) {
// "404":
// "$ref": "#/responses/notFound"

comment, err := issues_model.GetCommentByID(ctx, ctx.PathParamInt64("id"))
comment, err := issues_model.GetCommentWithRepoID(ctx, ctx.Repo.Repository.ID, ctx.PathParamInt64("id"))
if err != nil {
if issues_model.IsErrCommentNotExist(err) {
ctx.APIErrorNotFound(err)
Expand All @@ -455,15 +455,6 @@ func GetIssueComment(ctx *context.APIContext) {
return
}

if err = comment.LoadIssue(ctx); err != nil {
ctx.APIErrorInternal(err)
return
}
if comment.Issue.RepoID != ctx.Repo.Repository.ID {
ctx.Status(http.StatusNotFound)
return
}

if !ctx.Repo.CanReadIssuesOrPulls(comment.Issue.IsPull) {
ctx.APIErrorNotFound()
return
Expand Down Expand Up @@ -579,7 +570,7 @@ func EditIssueCommentDeprecated(ctx *context.APIContext) {
}

func editIssueComment(ctx *context.APIContext, form api.EditIssueCommentOption) {
comment, err := issues_model.GetCommentByID(ctx, ctx.PathParamInt64("id"))
comment, err := issues_model.GetCommentWithRepoID(ctx, ctx.Repo.Repository.ID, ctx.PathParamInt64("id"))
if err != nil {
if issues_model.IsErrCommentNotExist(err) {
ctx.APIErrorNotFound(err)
Expand All @@ -589,16 +580,6 @@ func editIssueComment(ctx *context.APIContext, form api.EditIssueCommentOption)
return
}

if err := comment.LoadIssue(ctx); err != nil {
ctx.APIErrorInternal(err)
return
}

if comment.Issue.RepoID != ctx.Repo.Repository.ID {
ctx.Status(http.StatusNotFound)
return
}

if !ctx.IsSigned || (ctx.Doer.ID != comment.PosterID && !ctx.Repo.CanWriteIssuesOrPulls(comment.Issue.IsPull)) {
ctx.Status(http.StatusForbidden)
return
Expand Down Expand Up @@ -698,7 +679,7 @@ func DeleteIssueCommentDeprecated(ctx *context.APIContext) {
}

func deleteIssueComment(ctx *context.APIContext) {
comment, err := issues_model.GetCommentByID(ctx, ctx.PathParamInt64("id"))
comment, err := issues_model.GetCommentWithRepoID(ctx, ctx.Repo.Repository.ID, ctx.PathParamInt64("id"))
if err != nil {
if issues_model.IsErrCommentNotExist(err) {
ctx.APIErrorNotFound(err)
Expand All @@ -708,16 +689,6 @@ func deleteIssueComment(ctx *context.APIContext) {
return
}

if err := comment.LoadIssue(ctx); err != nil {
ctx.APIErrorInternal(err)
return
}

if comment.Issue.RepoID != ctx.Repo.Repository.ID {
ctx.Status(http.StatusNotFound)
return
}

if !ctx.IsSigned || (ctx.Doer.ID != comment.PosterID && !ctx.Repo.CanWriteIssuesOrPulls(comment.Issue.IsPull)) {
ctx.Status(http.StatusForbidden)
return
Expand Down
120 changes: 120 additions & 0 deletions routers/api/v1/repo/pull_review.go
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,126 @@ func GetPullReviewComments(ctx *context.APIContext) {
ctx.JSON(http.StatusOK, apiComments)
}

// ResolvePullReviewComment resolves a review comment in a pull request
func ResolvePullReviewComment(ctx *context.APIContext) {
// swagger:operation POST /repos/{owner}/{repo}/pulls/comments/{id}/resolve repository repoResolvePullReviewComment
// ---
// summary: Resolve a pull review comment
// 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: id
// in: path
// description: id of the review comment
// type: integer
// format: int64
// required: true
// responses:
// "204":
// "$ref": "#/responses/empty"
// "400":
// "$ref": "#/responses/validationError"
// "403":
// "$ref": "#/responses/forbidden"
// "404":
// "$ref": "#/responses/notFound"
updatePullReviewCommentResolve(ctx, true)
}

// UnresolvePullReviewComment unresolves a review comment in a pull request
func UnresolvePullReviewComment(ctx *context.APIContext) {
// swagger:operation POST /repos/{owner}/{repo}/pulls/comments/{id}/unresolve repository repoUnresolvePullReviewComment
// ---
// summary: Unresolve a pull review comment
// 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: id
// in: path
// description: id of the review comment
// type: integer
// format: int64
// required: true
// responses:
// "204":
// "$ref": "#/responses/empty"
// "400":
// "$ref": "#/responses/validationError"
// "403":
// "$ref": "#/responses/forbidden"
// "404":
// "$ref": "#/responses/notFound"
updatePullReviewCommentResolve(ctx, false)
}

func updatePullReviewCommentResolve(ctx *context.APIContext, isResolve bool) {
comment := getPullReviewCommentToResolve(ctx)
if comment == nil {
return
}

canMarkConv, err := issues_model.CanMarkConversation(ctx, comment.Issue, ctx.Doer)
if err != nil {
ctx.APIErrorInternal(err)
return
}
if !canMarkConv {
ctx.APIError(http.StatusForbidden, "user should have permission to resolve comment")
return
}

if err = issues_model.MarkConversation(ctx, comment, ctx.Doer, isResolve); err != nil {
ctx.APIErrorInternal(err)
return
}

ctx.Status(http.StatusNoContent)
}

func getPullReviewCommentToResolve(ctx *context.APIContext) *issues_model.Comment {
comment, err := issues_model.GetCommentWithRepoID(ctx, ctx.Repo.Repository.ID, ctx.PathParamInt64("id"))
if err != nil {
if issues_model.IsErrCommentNotExist(err) {
ctx.APIErrorNotFound("GetCommentByID", err)
} else {
ctx.APIErrorInternal(err)
}
return nil
}

if !comment.Issue.IsPull {
ctx.APIError(http.StatusBadRequest, "comment does not belong to a pull request")
return nil
}

if comment.Type != issues_model.CommentTypeCode {
ctx.APIError(http.StatusBadRequest, "comment is not a review comment")
return nil
}

return comment
}

// 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
Expand Down
50 changes: 28 additions & 22 deletions services/convert/pull_review.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,34 +92,40 @@ func ToPullReviewCommentList(ctx context.Context, review *issues_model.Review, d
for _, lines := range review.CodeComments {
for _, comments := range lines {
for _, comment := range comments {
apiComment := &api.PullReviewComment{
ID: comment.ID,
Body: comment.Content,
Poster: ToUser(ctx, comment.Poster, doer),
Resolver: ToUser(ctx, comment.ResolveDoer, doer),
ReviewID: review.ID,
Created: comment.CreatedUnix.AsTime(),
Updated: comment.UpdatedUnix.AsTime(),
Path: comment.TreePath,
CommitID: comment.CommitSHA,
OrigCommitID: comment.OldRef,
DiffHunk: patch2diff(comment.Patch),
HTMLURL: comment.HTMLURL(ctx),
HTMLPullURL: review.Issue.HTMLURL(ctx),
}

if comment.Line < 0 {
apiComment.OldLineNum = comment.UnsignedLine()
} else {
apiComment.LineNum = comment.UnsignedLine()
}
apiComments = append(apiComments, apiComment)
apiComments = append(apiComments, ToPullReviewComment(ctx, comment, doer))
}
}
}
return apiComments, nil
}

// ToPullReviewComment convert a single code review comment to api format
func ToPullReviewComment(ctx context.Context, comment *issues_model.Comment, doer *user_model.User) *api.PullReviewComment {
apiComment := &api.PullReviewComment{
ID: comment.ID,
Body: comment.Content,
Poster: ToUser(ctx, comment.Poster, doer),
Resolver: ToUser(ctx, comment.ResolveDoer, doer),
ReviewID: comment.ReviewID,
Created: comment.CreatedUnix.AsTime(),
Updated: comment.UpdatedUnix.AsTime(),
Path: comment.TreePath,
CommitID: comment.CommitSHA,
OrigCommitID: comment.OldRef,
DiffHunk: patch2diff(comment.Patch),
HTMLURL: comment.HTMLURL(ctx),
HTMLPullURL: comment.Issue.HTMLURL(ctx),
}

if comment.Line < 0 {
apiComment.OldLineNum = comment.UnsignedLine()
} else {
apiComment.LineNum = comment.UnsignedLine()
}

return apiComment
}

func patch2diff(patch string) string {
split := strings.Split(patch, "\n@@")
if len(split) == 2 {
Expand Down
Loading