ikurotime / gitgud

public
main / internal/infra/persistence/sqlite/issue_repo.go
3.7 KB · Go Raw
  1package sqlite
  2
  3import (
  4	"context"
  5	"database/sql"
  6	"errors"
  7
  8	"gitgud/internal/domain"
  9)
 10
 11type IssueRepo struct {
 12	db *sql.DB
 13}
 14
 15func NewIssueRepo(db *sql.DB) *IssueRepo {
 16	return &IssueRepo{db: db}
 17}
 18
 19func (r *IssueRepo) Create(ctx context.Context, i *domain.Issue) 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 issues WHERE repo_id=?`, i.RepoID).Scan(&next)
 29	if err != nil {
 30		return err
 31	}
 32	i.Number = next
 33
 34	res, err := tx.ExecContext(ctx,
 35		`INSERT INTO issues(repo_id,number,author_id,title,body,state) VALUES(?,?,?,?,?,?)`,
 36		i.RepoID, i.Number, i.AuthorID, i.Title, i.Body, string(i.State))
 37	if err != nil {
 38		return err
 39	}
 40	i.ID, _ = res.LastInsertId()
 41	return tx.Commit()
 42}
 43
 44func (r *IssueRepo) ByNumber(ctx context.Context, repoID int64, number int) (*domain.Issue, error) {
 45	const q = `SELECT i.id, i.repo_id, i.number, i.author_id, u.username, i.title, i.body, i.state, i.created_at
 46FROM issues i
 47JOIN users u ON u.id = i.author_id
 48WHERE i.repo_id = ? AND i.number = ?`
 49
 50	var i domain.Issue
 51	err := r.db.QueryRowContext(ctx, q, repoID, number).Scan(
 52		&i.ID, &i.RepoID, &i.Number, &i.AuthorID, &i.AuthorName, &i.Title, &i.Body, &i.State, &i.CreatedAt)
 53	if err != nil {
 54		if errors.Is(err, sql.ErrNoRows) {
 55			return nil, domain.ErrNotFound
 56		}
 57		return nil, err
 58	}
 59	return &i, nil
 60}
 61
 62func (r *IssueRepo) List(ctx context.Context, repoID int64, state domain.IssueState) ([]*domain.Issue, error) {
 63	q := `SELECT i.id, i.repo_id, i.number, i.author_id, u.username, i.title, i.body, i.state, i.created_at
 64FROM issues i
 65JOIN users u ON u.id = i.author_id
 66WHERE i.repo_id = ?`
 67	args := []any{repoID}
 68	if state != "" {
 69		q += ` AND i.state = ?`
 70		args = append(args, string(state))
 71	}
 72	q += ` ORDER BY i.number DESC`
 73
 74	rows, err := r.db.QueryContext(ctx, q, args...)
 75	if err != nil {
 76		return nil, err
 77	}
 78	defer rows.Close()
 79
 80	var issues []*domain.Issue
 81	for rows.Next() {
 82		var i domain.Issue
 83		if err := rows.Scan(&i.ID, &i.RepoID, &i.Number, &i.AuthorID, &i.AuthorName,
 84			&i.Title, &i.Body, &i.State, &i.CreatedAt); err != nil {
 85			return nil, err
 86		}
 87		issues = append(issues, &i)
 88	}
 89	return issues, rows.Err()
 90}
 91
 92func (r *IssueRepo) SetState(ctx context.Context, id int64, state domain.IssueState) error {
 93	_, err := r.db.ExecContext(ctx, `UPDATE issues SET state=? WHERE id=?`, string(state), id)
 94	return err
 95}
 96
 97func (r *IssueRepo) AddComment(ctx context.Context, c *domain.IssueComment) error {
 98	res, err := r.db.ExecContext(ctx,
 99		`INSERT INTO issue_comments(issue_id,author_id,body) VALUES(?,?,?)`,
100		c.IssueID, c.AuthorID, c.Body)
101	if err != nil {
102		return err
103	}
104	c.ID, _ = res.LastInsertId()
105	return nil
106}
107
108func (r *IssueRepo) Comments(ctx context.Context, issueID int64) ([]*domain.IssueComment, error) {
109	const q = `SELECT c.id, c.issue_id, c.author_id, u.username, c.body, c.created_at
110FROM issue_comments c
111JOIN users u ON u.id = c.author_id
112WHERE c.issue_id = ?
113ORDER BY c.created_at, c.id`
114
115	rows, err := r.db.QueryContext(ctx, q, issueID)
116	if err != nil {
117		return nil, err
118	}
119	defer rows.Close()
120
121	var comments []*domain.IssueComment
122	for rows.Next() {
123		var c domain.IssueComment
124		if err := rows.Scan(&c.ID, &c.IssueID, &c.AuthorID, &c.AuthorName, &c.Body, &c.CreatedAt); err != nil {
125			return nil, err
126		}
127		comments = append(comments, &c)
128	}
129	return comments, rows.Err()
130}
131
132func (r *IssueRepo) CountByState(ctx context.Context, repoID int64) (open, closed int, err error) {
133	const q = `SELECT
134  COALESCE(SUM(CASE WHEN state='open' THEN 1 ELSE 0 END),0),
135  COALESCE(SUM(CASE WHEN state='closed' THEN 1 ELSE 0 END),0)
136FROM issues WHERE repo_id=?`
137	err = r.db.QueryRowContext(ctx, q, repoID).Scan(&open, &closed)
138	return open, closed, err
139}