internal/store/releases.go

b937a769f31481aa5f2e4e70711dea53e22ce6da
gitbay/internal/store/releases.go history · blame · raw

133 lines · 3320 bytes

  1package store
  2
  3import (
  4	"database/sql"
  5	"errors"
  6	"fmt"
  7)
  8
  9type Release struct {
 10	ID        int64
 11	RepoID    int64
 12	Tag       string
 13	Title     string
 14	Notes     string
 15	Author    string
 16	CreatedAt string
 17	Assets    []ReleaseAsset
 18}
 19
 20type ReleaseAsset struct {
 21	ID         int64
 22	Name       string
 23	Size       int64
 24	SHA256     string
 25	UploadedAt string
 26}
 27
 28func (s *Store) CreateRelease(repoID int64, tag, title, notes string, authorID int64) (int64, error) {
 29	res, err := s.DB.Exec(
 30		"INSERT INTO releases (repo_id, tag, title, notes, author_id) VALUES (?, ?, ?, ?, ?)",
 31		repoID, tag, title, notes, authorID)
 32	if err != nil {
 33		if isUniqueErr(err) {
 34			return 0, fmt.Errorf("a release for tag %q already exists", tag)
 35		}
 36		return 0, err
 37	}
 38	return res.LastInsertId()
 39}
 40
 41const releaseSelect = `
 42	SELECT r.id, r.repo_id, r.tag, r.title, r.notes, COALESCE(u.username, ''), r.created_at
 43	FROM releases r LEFT JOIN users u ON u.id = r.author_id`
 44
 45func (s *Store) releaseAssets(rel *Release) error {
 46	rows, err := s.DB.Query(
 47		"SELECT id, name, size, sha256, uploaded_at FROM release_assets WHERE release_id = ? ORDER BY name",
 48		rel.ID)
 49	if err != nil {
 50		return err
 51	}
 52	defer rows.Close()
 53	for rows.Next() {
 54		var a ReleaseAsset
 55		if err := rows.Scan(&a.ID, &a.Name, &a.Size, &a.SHA256, &a.UploadedAt); err != nil {
 56			return err
 57		}
 58		rel.Assets = append(rel.Assets, a)
 59	}
 60	return rows.Err()
 61}
 62
 63func (s *Store) ReleaseByTag(repoID int64, tag string) (Release, error) {
 64	var r Release
 65	err := s.DB.QueryRow(releaseSelect+" WHERE r.repo_id = ? AND r.tag = ?", repoID, tag).
 66		Scan(&r.ID, &r.RepoID, &r.Tag, &r.Title, &r.Notes, &r.Author, &r.CreatedAt)
 67	if errors.Is(err, sql.ErrNoRows) {
 68		return r, ErrNotFound
 69	}
 70	if err != nil {
 71		return r, err
 72	}
 73	return r, s.releaseAssets(&r)
 74}
 75
 76// ListReleases returns releases newest-first, assets included.
 77func (s *Store) ListReleases(repoID int64) ([]Release, error) {
 78	rows, err := s.DB.Query(releaseSelect+" WHERE r.repo_id = ? ORDER BY r.created_at DESC", repoID)
 79	if err != nil {
 80		return nil, err
 81	}
 82	defer rows.Close()
 83	var out []Release
 84	for rows.Next() {
 85		var r Release
 86		if err := rows.Scan(&r.ID, &r.RepoID, &r.Tag, &r.Title, &r.Notes, &r.Author, &r.CreatedAt); err != nil {
 87			return nil, err
 88		}
 89		out = append(out, r)
 90	}
 91	if err := rows.Err(); err != nil {
 92		return nil, err
 93	}
 94	for i := range out {
 95		if err := s.releaseAssets(&out[i]); err != nil {
 96			return nil, err
 97		}
 98	}
 99	return out, nil
100}
101
102func (s *Store) DeleteRelease(id int64) error {
103	res, err := s.DB.Exec("DELETE FROM releases WHERE id = ?", id)
104	if err != nil {
105		return err
106	}
107	if n, _ := res.RowsAffected(); n == 0 {
108		return ErrNotFound
109	}
110	return nil
111}
112
113func (s *Store) AddReleaseAsset(releaseID int64, name string, size int64, sha256 string) error {
114	_, err := s.DB.Exec(
115		"INSERT INTO release_assets (release_id, name, size, sha256) VALUES (?, ?, ?, ?)",
116		releaseID, name, size, sha256)
117	if isUniqueErr(err) {
118		return fmt.Errorf("asset %q already exists on this release", name)
119	}
120	return err
121}
122
123func (s *Store) RemoveReleaseAsset(releaseID int64, name string) error {
124	res, err := s.DB.Exec(
125		"DELETE FROM release_assets WHERE release_id = ? AND name = ?", releaseID, name)
126	if err != nil {
127		return err
128	}
129	if n, _ := res.RowsAffected(); n == 0 {
130		return ErrNotFound
131	}
132	return nil
133}