summaryrefslogtreecommitdiff
path: root/internal/storage/epic.go
blob: 9f3863cd8e3b94ded0cb6a82f9e9577a0b26bcda (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
package storage

import (
	"database/sql"
	"time"

	"github.com/thepeterstone/claudomator/internal/story"
)

// CreateEpic inserts a new epic. Defaults Status to "OPEN" if unset.
func (s *DB) CreateEpic(e *story.Epic) error {
	if e.Status == "" {
		e.Status = "OPEN"
	}
	now := time.Now().UTC()
	e.CreatedAt = now
	e.UpdatedAt = now
	_, err := s.db.Exec(
		`INSERT INTO epics (id, name, description, status, discovery_source, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?)`,
		e.ID, e.Name, e.Description, e.Status, e.DiscoverySource, now, now,
	)
	return err
}

// GetEpic retrieves an epic by ID.
func (s *DB) GetEpic(id string) (*story.Epic, error) {
	row := s.db.QueryRow(`SELECT id, name, description, status, discovery_source, created_at, updated_at FROM epics WHERE id = ?`, id)
	return scanEpic(row)
}

// ListEpics returns epics, optionally filtered by status. Pass an empty
// string to list all epics.
func (s *DB) ListEpics(status string) ([]*story.Epic, error) {
	query := `SELECT id, name, description, status, discovery_source, created_at, updated_at FROM epics WHERE 1=1`
	var args []interface{}
	if status != "" {
		query += " AND status = ?"
		args = append(args, status)
	}
	query += " ORDER BY created_at DESC"

	rows, err := s.db.Query(query, args...)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	var epics []*story.Epic
	for rows.Next() {
		e, err := scanEpicRows(rows)
		if err != nil {
			return nil, err
		}
		epics = append(epics, e)
	}
	return epics, rows.Err()
}

// UpdateEpic updates an existing epic's name/description/status/discovery_source.
// Mirrors UpdateProject's shape: no existence check, no status-machine validation.
func (s *DB) UpdateEpic(e *story.Epic) error {
	now := time.Now().UTC()
	_, err := s.db.Exec(
		`UPDATE epics SET name = ?, description = ?, status = ?, discovery_source = ?, updated_at = ? WHERE id = ?`,
		e.Name, e.Description, e.Status, e.DiscoverySource, now, e.ID,
	)
	if err != nil {
		return err
	}
	e.UpdatedAt = now
	return nil
}

func scanEpic(row scanner) (*story.Epic, error) {
	var e story.Epic
	var description sql.NullString
	var discoverySource sql.NullString
	if err := row.Scan(&e.ID, &e.Name, &description, &e.Status, &discoverySource, &e.CreatedAt, &e.UpdatedAt); err != nil {
		return nil, err
	}
	e.Description = description.String
	e.DiscoverySource = discoverySource.String
	return &e, nil
}

func scanEpicRows(rows *sql.Rows) (*story.Epic, error) {
	return scanEpic(rows)
}