rin
Публичный Open-source lightweight git hosting.
reviews.go · 117 строк · 3.6 KB
Raw
1 package repo
2
3 import (
4 "context"
5 "errors"
6 "fmt"
7 "strings"
8 "time"
9 )
10
11 const (
12 ReviewApproved = "approved"
13 ReviewChangesRequested = "changes_requested"
14 )
15
16 var ErrInvalidReviewState = errors.New("invalid review state")
17
18 type Review struct {
19 ID int64
20 PullID int64
21 ReviewerID int64
22 ReviewerUsername string
23 ReviewerAvatar string
24 State string
25 Body string
26 CreatedAt time.Time
27 UpdatedAt time.Time
28 }
29
30 // UpsertReview records or updates the reviewer's verdict on a pull request.
31 func (s *Service) UpsertReview(ctx context.Context, pullID, reviewerID int64, state, body string) error {
32 state = strings.TrimSpace(state)
33 if state != ReviewApproved && state != ReviewChangesRequested {
34 return ErrInvalidReviewState
35 }
36 body = strings.TrimSpace(body)
37 if len(body) > 10000 {
38 body = body[:10000]
39 }
40
41 cctx, cancel := context.WithTimeout(ctx, 5*time.Second)
42 defer cancel()
43
44 now := time.Now().UTC()
45 _, err := s.db.ExecContext(cctx, `
46 INSERT INTO pull_request_reviews (pull_id, reviewer_id, state, body, created_at, updated_at)
47 VALUES (?, ?, ?, ?, ?, ?)
48 ON CONFLICT(pull_id, reviewer_id) DO UPDATE SET
49 state = excluded.state,
50 body = excluded.body,
51 updated_at = excluded.updated_at
52 `, pullID, reviewerID, state, body, now, now)
53 if err != nil {
54 return fmt.Errorf("upsert review: %w", err)
55 }
56 return nil
57 }
58
59 // ListReviews returns all reviews left on a pull request, oldest first.
60 func (s *Service) ListReviews(ctx context.Context, pullID int64) ([]Review, error) {
61 cctx, cancel := context.WithTimeout(ctx, 5*time.Second)
62 defer cancel()
63
64 rows, err := s.db.QueryContext(cctx, `
65 SELECT rv.id, rv.pull_id, rv.reviewer_id, u.username, u.avatar_url, rv.state, rv.body, rv.created_at, rv.updated_at
66 FROM pull_request_reviews rv
67 JOIN users u ON u.id = rv.reviewer_id
68 WHERE rv.pull_id = ?
69 ORDER BY rv.updated_at ASC
70 `, pullID)
71 if err != nil {
72 return nil, fmt.Errorf("list reviews: %w", err)
73 }
74 defer rows.Close()
75
76 var list []Review
77 for rows.Next() {
78 var rv Review
79 if err := rows.Scan(&rv.ID, &rv.PullID, &rv.ReviewerID, &rv.ReviewerUsername, &rv.ReviewerAvatar,
80 &rv.State, &rv.Body, &rv.CreatedAt, &rv.UpdatedAt); err != nil {
81 return nil, fmt.Errorf("scan review: %w", err)
82 }
83 list = append(list, rv)
84 }
85 return list, rows.Err()
86 }
87
88 // GetReview returns the review left by a specific user, if any.
89 func (s *Service) GetReview(ctx context.Context, pullID, reviewerID int64) (*Review, error) {
90 cctx, cancel := context.WithTimeout(ctx, 3*time.Second)
91 defer cancel()
92
93 var rv Review
94 err := s.db.QueryRowContext(cctx, `
95 SELECT rv.id, rv.pull_id, rv.reviewer_id, u.username, u.avatar_url, rv.state, rv.body, rv.created_at, rv.updated_at
96 FROM pull_request_reviews rv
97 JOIN users u ON u.id = rv.reviewer_id
98 WHERE rv.pull_id = ? AND rv.reviewer_id = ?
99 `, pullID, reviewerID).Scan(&rv.ID, &rv.PullID, &rv.ReviewerID, &rv.ReviewerUsername, &rv.ReviewerAvatar,
100 &rv.State, &rv.Body, &rv.CreatedAt, &rv.UpdatedAt)
101 if err != nil {
102 return nil, err
103 }
104 return &rv, nil
105 }
106
107 // ReviewCounts returns how many reviewers approved and how many requested changes.
108 func (s *Service) ReviewCounts(ctx context.Context, pullID int64) (approved int, changesRequested int) {
109 cctx, cancel := context.WithTimeout(ctx, 3*time.Second)
110 defer cancel()
111
112 _ = s.db.QueryRowContext(cctx, `SELECT COUNT(*) FROM pull_request_reviews WHERE pull_id = ? AND state = ?`,
113 pullID, ReviewApproved).Scan(&approved)
114 _ = s.db.QueryRowContext(cctx, `SELECT COUNT(*) FROM pull_request_reviews WHERE pull_id = ? AND state = ?`,
115 pullID, ReviewChangesRequested).Scan(&changesRequested)
116 return approved, changesRequested
117 }
118