ikurotime / gitgud

public
main / internal/infra/persistence/sqlite/pr_repo.go
4.0 KB · Go Raw
  1package sqlite
  2
  3import (
  4	"context"
  5	"database/sql"
  6	"errors"
  7
  8	"gitgud/internal/domain"
  9)
 10
 11type PRRepo struct {
 12	db *sql.DB
 13}
 14
 15func NewPRRepo(db *sql.DB) *PRRepo {
 16	return &PRRepo{db: db}
 17}
 18
 19func (r *PRRepo) Create(ctx context.Context, pr *domain.PullRequest) error {
 20	tx, err := r.db.BeginTx(ctx, nil)
 21	if err != nil {
 22		return err
 23	}
 24	defer tx.Rollback()
 25
 26	var next int
 27	err = tx.QueryRowContext(ctx,
 28		`SELECT COALESCE(MAX(number),0)+1 FROM pull_requests WHERE repo_id=?`, pr.RepoID).Scan(&next)
 29	if err != nil {
 30		return err
 31	}
 32	pr.Number = next
 33
 34	res, err := tx.ExecContext(ctx,
 35		`INSERT INTO pull_requests(repo_id,number,author_id,title,body,base_branch,head_branch,state)
 36		 VALUES(?,?,?,?,?,?,?,?)`,
 37		pr.RepoID, pr.Number, pr.AuthorID, pr.Title, pr.Body, pr.BaseBranch, pr.HeadBranch, string(pr.State))
 38	if err != nil {
 39		return err
 40	}
 41	pr.ID, _ = res.LastInsertId()
 42	return tx.Commit()
 43}
 44
 45func (r *PRRepo) ByNumber(ctx context.Context, repoID int64, number int) (*domain.PullRequest, error) {
 46	const q = `SELECT p.id, p.repo_id, p.number, p.author_id, u.username, p.title, p.body, p.base_branch, p.head_branch, p.state, p.created_at
 47FROM pull_requests p
 48JOIN users u ON u.id = p.author_id
 49WHERE p.repo_id = ? AND p.number = ?`
 50
 51	pr, err := scanPR(r.db.QueryRowContext(ctx, q, repoID, number))
 52	if err != nil {
 53		if errors.Is(err, sql.ErrNoRows) {
 54			return nil, domain.ErrNotFound
 55		}
 56		return nil, err
 57	}
 58	return pr, nil
 59}
 60
 61func (r *PRRepo) List(ctx context.Context, repoID int64, state domain.PRState) ([]*domain.PullRequest, error) {
 62	q := `SELECT p.id, p.repo_id, p.number, p.author_id, u.username, p.title, p.body, p.base_branch, p.head_branch, p.state, p.created_at
 63FROM pull_requests p
 64JOIN users u ON u.id = p.author_id
 65WHERE p.repo_id = ?`
 66	args := []any{repoID}
 67	if state != "" {
 68		q += ` AND p.state = ?`
 69		args = append(args, string(state))
 70	}
 71	q += ` ORDER BY p.number DESC`
 72
 73	rows, err := r.db.QueryContext(ctx, q, args...)
 74	if err != nil {
 75		return nil, err
 76	}
 77	defer rows.Close()
 78
 79	var prs []*domain.PullRequest
 80	for rows.Next() {
 81		pr, err := scanPR(rows)
 82		if err != nil {
 83			return nil, err
 84		}
 85		prs = append(prs, pr)
 86	}
 87	return prs, rows.Err()
 88}
 89
 90func (r *PRRepo) SetState(ctx context.Context, id int64, state domain.PRState) error {
 91	_, err := r.db.ExecContext(ctx, `UPDATE pull_requests SET state=? WHERE id=?`, string(state), id)
 92	return err
 93}
 94
 95func (r *PRRepo) AddComment(ctx context.Context, c *domain.PRComment) error {
 96	res, err := r.db.ExecContext(ctx,
 97		`INSERT INTO pr_comments(pr_id,author_id,body) VALUES(?,?,?)`,
 98		c.PRID, c.AuthorID, c.Body)
 99	if err != nil {
100		return err
101	}
102	c.ID, _ = res.LastInsertId()
103	return nil
104}
105
106func (r *PRRepo) Comments(ctx context.Context, prID int64) ([]*domain.PRComment, error) {
107	const q = `SELECT c.id, c.pr_id, c.author_id, u.username, c.body, c.created_at
108FROM pr_comments c
109JOIN users u ON u.id = c.author_id
110WHERE c.pr_id = ?
111ORDER BY c.created_at, c.id`
112
113	rows, err := r.db.QueryContext(ctx, q, prID)
114	if err != nil {
115		return nil, err
116	}
117	defer rows.Close()
118
119	var comments []*domain.PRComment
120	for rows.Next() {
121		var c domain.PRComment
122		if err := rows.Scan(&c.ID, &c.PRID, &c.AuthorID, &c.AuthorName, &c.Body, &c.CreatedAt); err != nil {
123			return nil, err
124		}
125		comments = append(comments, &c)
126	}
127	return comments, rows.Err()
128}
129
130func (r *PRRepo) CountByState(ctx context.Context, repoID int64) (open, merged, closed int, err error) {
131	const q = `SELECT
132  COALESCE(SUM(CASE WHEN state='open' THEN 1 ELSE 0 END),0),
133  COALESCE(SUM(CASE WHEN state='merged' THEN 1 ELSE 0 END),0),
134  COALESCE(SUM(CASE WHEN state='closed' THEN 1 ELSE 0 END),0)
135FROM pull_requests WHERE repo_id=?`
136	err = r.db.QueryRowContext(ctx, q, repoID).Scan(&open, &merged, &closed)
137	return open, merged, closed, err
138}
139
140type rowScanner interface {
141	Scan(dest ...any) error
142}
143
144func scanPR(row rowScanner) (*domain.PullRequest, error) {
145	var pr domain.PullRequest
146	err := row.Scan(&pr.ID, &pr.RepoID, &pr.Number, &pr.AuthorID, &pr.AuthorName,
147		&pr.Title, &pr.Body, &pr.BaseBranch, &pr.HeadBranch, &pr.State, &pr.CreatedAt)
148	if err != nil {
149		return nil, err
150	}
151	return &pr, nil
152}