package db import ( "context" "github.com/dtoro/oikos/internal/adapters/postgres/sqlcgen" "github.com/dtoro/oikos/internal/core/domain" "github.com/dtoro/oikos/internal/core/ports" "github.com/google/uuid" ) // MetricsRepo implements ports.MetricsRepository. type MetricsRepo struct { pool *Pool } var _ ports.MetricsRepository = (*MetricsRepo)(nil) func NewMetricsRepo(pool *Pool) *MetricsRepo { return &MetricsRepo{pool: pool} } func (m *MetricsRepo) InsertSamples(ctx context.Context, entityID domain.UUID, samples []ports.MetricSample) error { for _, s := range samples { if err := sqlcgen.New(m.pool).InsertMetricSample(ctx, sqlcgen.InsertMetricSampleParams{ EntityID: mustUUID(entityID), Metric: s.Metric, Value: s.Value, }); err != nil { return err } } return nil } // SignalRepo implements ports.SignalRepository with inline SQL. type SignalRepo struct { pool *Pool } var _ ports.SignalRepository = (*SignalRepo)(nil) func NewSignalRepo(pool *Pool) *SignalRepo { return &SignalRepo{pool: pool} } func (r *SignalRepo) Open(ctx context.Context) ([]domain.Signal, error) { rows, err := r.pool.Query(ctx, `SELECT entity_id, kind, severity, state FROM signals WHERE state NOT IN ('resolved','failed') ORDER BY severity DESC`) if err != nil { return nil, err } defer rows.Close() var items []domain.Signal for rows.Next() { var s domain.Signal var id uuid.UUID if err := rows.Scan(&id, &s.Kind, &s.Severity, &s.State); err != nil { return nil, err } s.EntityID = domain.UUID(id.String()) items = append(items, s) } return items, rows.Err() } func (r *SignalRepo) History(ctx context.Context, entityID domain.UUID, limit int) ([]domain.Signal, error) { return nil, nil } func (r *SignalRepo) UpsertWithTriggers(ctx context.Context, input ports.SignalUpsertInput) error { eid := mustUUID(input.Signal.EntityID) _, err := r.pool.Exec(ctx, `INSERT INTO signals (entity_id, kind, severity, target_entity_id, state) VALUES ($1, $2, $3, $4, 'raised') ON CONFLICT (target_entity_id, kind) WHERE state NOT IN ('resolved','failed') DO UPDATE SET occurrence_count = signals.occurrence_count + 1, last_seen_at = now(), updated_at = now()`, eid, input.Signal.Kind, input.Signal.Severity, eid) return err } func (r *SignalRepo) Transition(ctx context.Context, input ports.SignalTransitionInput) (domain.Signal, error) { tag, err := r.pool.Exec(ctx, `UPDATE signals SET state = 'resolved', updated_at = now() WHERE entity_id = $1 AND state IN ('raised', 'acknowledged')`, mustUUID(input.SignalID)) if err != nil { return domain.Signal{}, err } if tag.RowsAffected() == 0 { return domain.Signal{}, domain.ErrNotFound } return domain.Signal{}, nil }