internal/store/releases.go

72920bdf0504bcee3d15305abc0724ae216d088c
gitbay/internal/store/releases.go history · blame · raw

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