Skip to content

Commit dad67ca

Browse files
authored
Refactor pull request review (#8954)
* refactor submit review * remove unnecessary code * remove unused comment * fix lint * remove duplicated actions * remove duplicated actions * fix typo * fix comment content
1 parent 16a4315 commit dad67ca

File tree

8 files changed

+289
-249
lines changed

8 files changed

+289
-249
lines changed

Diff for: models/issue_comment.go

+5-2
Original file line numberDiff line numberDiff line change
@@ -539,8 +539,10 @@ func createComment(e *xorm.Session, opts *CreateCommentOptions) (_ *Comment, err
539539
return nil, err
540540
}
541541

542-
if err = sendCreateCommentAction(e, opts, comment); err != nil {
543-
return nil, err
542+
if !opts.NoAction {
543+
if err = sendCreateCommentAction(e, opts, comment); err != nil {
544+
return nil, err
545+
}
544546
}
545547

546548
if err = comment.addCrossReferences(e, opts.Doer); err != nil {
@@ -816,6 +818,7 @@ type CreateCommentOptions struct {
816818
RefCommentID int64
817819
RefAction references.XRefAction
818820
RefIsPull bool
821+
NoAction bool
819822
}
820823

821824
// CreateComment creates comment of issue or commit.

Diff for: models/repo_watch.go

+15
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,21 @@ func NotifyWatchers(act *Action) error {
216216
return notifyWatchers(x, act)
217217
}
218218

219+
// NotifyWatchersActions creates batch of actions for every watcher.
220+
func NotifyWatchersActions(acts []*Action) error {
221+
sess := x.NewSession()
222+
defer sess.Close()
223+
if err := sess.Begin(); err != nil {
224+
return err
225+
}
226+
for _, act := range acts {
227+
if err := notifyWatchers(sess, act); err != nil {
228+
return err
229+
}
230+
}
231+
return sess.Commit()
232+
}
233+
219234
func watchIfAuto(e Engine, userID, repoID int64, isWrite bool) error {
220235
if !isWrite || !setting.Service.AutoWatchOnChanges {
221236
return nil

Diff for: models/review.go

+78-56
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,12 @@
55
package models
66

77
import (
8-
"fmt"
8+
"strings"
99

10-
"code.gitea.io/gitea/modules/log"
1110
"code.gitea.io/gitea/modules/timeutil"
1211

1312
"xorm.io/builder"
1413
"xorm.io/core"
15-
"xorm.io/xorm"
1614
)
1715

1816
// ReviewType defines the sort of feedback a review gives
@@ -86,6 +84,11 @@ func (r *Review) loadReviewer(e Engine) (err error) {
8684
return
8785
}
8886

87+
// LoadReviewer loads reviewer
88+
func (r *Review) LoadReviewer() error {
89+
return r.loadReviewer(x)
90+
}
91+
8992
func (r *Review) loadAttributes(e Engine) (err error) {
9093
if err = r.loadReviewer(e); err != nil {
9194
return
@@ -101,54 +104,6 @@ func (r *Review) LoadAttributes() error {
101104
return r.loadAttributes(x)
102105
}
103106

104-
// Publish will send notifications / actions to participants for all code comments; parts are concurrent
105-
func (r *Review) Publish() error {
106-
return r.publish(x)
107-
}
108-
109-
func (r *Review) publish(e *xorm.Engine) error {
110-
if r.Type == ReviewTypePending || r.Type == ReviewTypeUnknown {
111-
return fmt.Errorf("review cannot be published if type is pending or unknown")
112-
}
113-
if r.Issue == nil {
114-
if err := r.loadIssue(e); err != nil {
115-
return err
116-
}
117-
}
118-
if err := r.Issue.loadRepo(e); err != nil {
119-
return err
120-
}
121-
if len(r.CodeComments) == 0 {
122-
if err := r.loadCodeComments(e); err != nil {
123-
return err
124-
}
125-
}
126-
for _, lines := range r.CodeComments {
127-
for _, comments := range lines {
128-
for _, comment := range comments {
129-
go func(en *xorm.Engine, review *Review, comm *Comment) {
130-
sess := en.NewSession()
131-
defer sess.Close()
132-
opts := &CreateCommentOptions{
133-
Doer: comm.Poster,
134-
Issue: review.Issue,
135-
Repo: review.Issue.Repo,
136-
Type: comm.Type,
137-
Content: comm.Content,
138-
}
139-
if err := updateCommentInfos(sess, opts, comm); err != nil {
140-
log.Warn("updateCommentInfos: %v", err)
141-
}
142-
if err := sendCreateCommentAction(sess, opts, comm); err != nil {
143-
log.Warn("sendCreateCommentAction: %v", err)
144-
}
145-
}(e, r, comment)
146-
}
147-
}
148-
}
149-
return nil
150-
}
151-
152107
func getReviewByID(e Engine, id int64) (*Review, error) {
153108
review := new(Review)
154109
if has, err := e.ID(id).Get(review); err != nil {
@@ -271,12 +226,79 @@ func GetCurrentReview(reviewer *User, issue *Issue) (*Review, error) {
271226
return getCurrentReview(x, reviewer, issue)
272227
}
273228

274-
// UpdateReview will update all cols of the given review in db
275-
func UpdateReview(r *Review) error {
276-
if _, err := x.ID(r.ID).AllCols().Update(r); err != nil {
277-
return err
229+
// ContentEmptyErr represents an content empty error
230+
type ContentEmptyErr struct {
231+
}
232+
233+
func (ContentEmptyErr) Error() string {
234+
return "Review content is empty"
235+
}
236+
237+
// IsContentEmptyErr returns true if err is a ContentEmptyErr
238+
func IsContentEmptyErr(err error) bool {
239+
_, ok := err.(ContentEmptyErr)
240+
return ok
241+
}
242+
243+
// SubmitReview creates a review out of the existing pending review or creates a new one if no pending review exist
244+
func SubmitReview(doer *User, issue *Issue, reviewType ReviewType, content string) (*Review, *Comment, error) {
245+
sess := x.NewSession()
246+
defer sess.Close()
247+
if err := sess.Begin(); err != nil {
248+
return nil, nil, err
249+
}
250+
251+
review, err := getCurrentReview(sess, doer, issue)
252+
if err != nil {
253+
if !IsErrReviewNotExist(err) {
254+
return nil, nil, err
255+
}
256+
257+
if len(strings.TrimSpace(content)) == 0 {
258+
return nil, nil, ContentEmptyErr{}
259+
}
260+
261+
// No current review. Create a new one!
262+
review, err = createReview(sess, CreateReviewOptions{
263+
Type: reviewType,
264+
Issue: issue,
265+
Reviewer: doer,
266+
Content: content,
267+
})
268+
if err != nil {
269+
return nil, nil, err
270+
}
271+
} else {
272+
if err := review.loadCodeComments(sess); err != nil {
273+
return nil, nil, err
274+
}
275+
if len(review.CodeComments) == 0 && len(strings.TrimSpace(content)) == 0 {
276+
return nil, nil, ContentEmptyErr{}
277+
}
278+
279+
review.Issue = issue
280+
review.Content = content
281+
review.Type = reviewType
282+
if _, err := sess.ID(review.ID).Cols("content, type").Update(review); err != nil {
283+
return nil, nil, err
284+
}
278285
}
279-
return nil
286+
287+
comm, err := createComment(sess, &CreateCommentOptions{
288+
Type: CommentTypeReview,
289+
Doer: doer,
290+
Content: review.Content,
291+
Issue: issue,
292+
Repo: issue.Repo,
293+
ReviewID: review.ID,
294+
NoAction: true,
295+
})
296+
if err != nil || comm == nil {
297+
return nil, nil, err
298+
}
299+
300+
comm.Review = review
301+
return review, comm, sess.Commit()
280302
}
281303

282304
// PullReviewersWithType represents the type used to display a review overview

Diff for: models/review_test.go

-8
Original file line numberDiff line numberDiff line change
@@ -98,14 +98,6 @@ func TestCreateReview(t *testing.T) {
9898
AssertExistsAndLoadBean(t, &Review{Content: "New Review"})
9999
}
100100

101-
func TestUpdateReview(t *testing.T) {
102-
assert.NoError(t, PrepareTestDatabase())
103-
review := AssertExistsAndLoadBean(t, &Review{ID: 1}).(*Review)
104-
review.Content = "Updated Review"
105-
assert.NoError(t, UpdateReview(review))
106-
AssertExistsAndLoadBean(t, &Review{ID: 1, Content: "Updated Review"})
107-
}
108-
109101
func TestGetReviewersByPullID(t *testing.T) {
110102
assert.NoError(t, PrepareTestDatabase())
111103

Diff for: modules/notification/action/action.go

+49
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ package action
66

77
import (
88
"fmt"
9+
"strings"
910

1011
"code.gitea.io/gitea/models"
1112
"code.gitea.io/gitea/modules/log"
@@ -117,3 +118,51 @@ func (a *actionNotifier) NotifyForkRepository(doer *models.User, oldRepo, repo *
117118
log.Error("notify watchers '%d/%d': %v", doer.ID, repo.ID, err)
118119
}
119120
}
121+
122+
func (a *actionNotifier) NotifyPullRequestReview(pr *models.PullRequest, review *models.Review, comment *models.Comment) {
123+
if err := review.LoadReviewer(); err != nil {
124+
log.Error("LoadReviewer '%d/%d': %v", review.ID, review.ReviewerID, err)
125+
return
126+
}
127+
if err := review.LoadCodeComments(); err != nil {
128+
log.Error("LoadCodeComments '%d/%d': %v", review.Reviewer.ID, review.ID, err)
129+
return
130+
}
131+
132+
var actions = make([]*models.Action, 0, 10)
133+
for _, lines := range review.CodeComments {
134+
for _, comments := range lines {
135+
for _, comm := range comments {
136+
actions = append(actions, &models.Action{
137+
ActUserID: review.Reviewer.ID,
138+
ActUser: review.Reviewer,
139+
Content: fmt.Sprintf("%d|%s", review.Issue.Index, strings.Split(comm.Content, "\n")[0]),
140+
OpType: models.ActionCommentIssue,
141+
RepoID: review.Issue.RepoID,
142+
Repo: review.Issue.Repo,
143+
IsPrivate: review.Issue.Repo.IsPrivate,
144+
Comment: comm,
145+
CommentID: comm.ID,
146+
})
147+
}
148+
}
149+
}
150+
151+
if strings.TrimSpace(comment.Content) != "" {
152+
actions = append(actions, &models.Action{
153+
ActUserID: review.Reviewer.ID,
154+
ActUser: review.Reviewer,
155+
Content: fmt.Sprintf("%d|%s", review.Issue.Index, strings.Split(comment.Content, "\n")[0]),
156+
OpType: models.ActionCommentIssue,
157+
RepoID: review.Issue.RepoID,
158+
Repo: review.Issue.Repo,
159+
IsPrivate: review.Issue.Repo.IsPrivate,
160+
Comment: comment,
161+
CommentID: comment.ID,
162+
})
163+
}
164+
165+
if err := models.NotifyWatchersActions(actions); err != nil {
166+
log.Error("notify watchers '%d/%d': %v", review.Reviewer.ID, review.Issue.RepoID, err)
167+
}
168+
}

0 commit comments

Comments
 (0)