summaryrefslogtreecommitdiff
path: root/internal/storage/events.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/storage/events.go')
-rw-r--r--internal/storage/events.go35
1 files changed, 28 insertions, 7 deletions
diff --git a/internal/storage/events.go b/internal/storage/events.go
index 82016ab..2051127 100644
--- a/internal/storage/events.go
+++ b/internal/storage/events.go
@@ -1,6 +1,7 @@
package storage
import (
+ "database/sql"
"encoding/json"
"errors"
"fmt"
@@ -8,12 +9,29 @@ import (
"github.com/google/uuid"
"github.com/thepeterstone/claudomator/internal/event"
+ "github.com/thepeterstone/claudomator/internal/task"
)
// CreateEvent appends an event to a task's stream. Seq is assigned by the
// database as MAX(seq)+1 within a transaction. On return, e.ID, e.Seq, and
// e.Timestamp are populated (timestamps default to time.Now().UTC()).
func (s *DB) CreateEvent(e *event.Event) error {
+ tx, err := s.db.Begin()
+ if err != nil {
+ return err
+ }
+ defer tx.Rollback()
+ if err := createEventTx(tx, e); err != nil {
+ return err
+ }
+ return tx.Commit()
+}
+
+// createEventTx validates, defaults, assigns a per-task monotonic seq, and
+// inserts an event using the supplied transaction. The caller commits. This
+// lets state-changing methods write their state_change event atomically with
+// the row update.
+func createEventTx(tx *sql.Tx, e *event.Event) error {
if e == nil {
return errors.New("event is nil")
}
@@ -38,12 +56,6 @@ func (s *DB) CreateEvent(e *event.Event) error {
e.Timestamp = e.Timestamp.UTC()
}
- tx, err := s.db.Begin()
- if err != nil {
- return err
- }
- defer tx.Rollback()
-
var nextSeq int64
if err := tx.QueryRow(`SELECT COALESCE(MAX(seq), 0) + 1 FROM events WHERE task_id = ?`, e.TaskID).Scan(&nextSeq); err != nil {
return fmt.Errorf("computing next seq: %w", err)
@@ -56,7 +68,16 @@ func (s *DB) CreateEvent(e *event.Event) error {
); err != nil {
return fmt.Errorf("inserting event: %w", err)
}
- return tx.Commit()
+ return nil
+}
+
+// stateChangePayload builds the JSON payload for a state_change event.
+func stateChangePayload(from, to task.State) json.RawMessage {
+ b, _ := json.Marshal(struct {
+ From task.State `json:"from"`
+ To task.State `json:"to"`
+ }{From: from, To: to})
+ return b
}
// ListEvents returns events for a task in seq order. Pass sinceSeq=0 for the