// ABOUTME: SQLite storage layer for metrics receiver using GORM. // ABOUTME: Handles database initialization and metric storage/retrieval. package receiver import ( "encoding/json" "fmt" "time" "gorm.io/driver/sqlite" "gorm.io/gorm" "gorm.io/gorm/logger" ) // Metric represents a stored metric record in the database type Metric struct { ID uint `gorm:"primaryKey"` Organization string `gorm:"index:idx_org_repo;not null"` Repository string `gorm:"index:idx_org_repo;not null"` Workflow string `gorm:"not null"` Job string `gorm:"not null"` RunID string `gorm:"index;not null"` ReceivedAt time.Time `gorm:"index;not null"` Payload string `gorm:"type:text;not null"` // JSON-encoded RunSummary } // Store handles SQLite storage for metrics using GORM type Store struct { db *gorm.DB } // NewStore creates a new SQLite store at the given path func NewStore(dbPath string) (*Store, error) { db, err := gorm.Open(sqlite.Open(dbPath), &gorm.Config{ Logger: logger.Default.LogMode(logger.Silent), }) if err != nil { return nil, fmt.Errorf("opening database: %w", err) } if err := db.AutoMigrate(&Metric{}); err != nil { return nil, fmt.Errorf("migrating schema: %w", err) } return &Store{db: db}, nil } // SaveMetric stores a metrics payload in the database func (s *Store) SaveMetric(payload *MetricsPayload) (uint, error) { summaryJSON, err := json.Marshal(payload.Summary) if err != nil { return 0, fmt.Errorf("marshaling summary: %w", err) } metric := Metric{ Organization: payload.Execution.Organization, Repository: payload.Execution.Repository, Workflow: payload.Execution.Workflow, Job: payload.Execution.Job, RunID: payload.Execution.RunID, ReceivedAt: time.Now().UTC(), Payload: string(summaryJSON), } result := s.db.Create(&metric) if result.Error != nil { return 0, fmt.Errorf("inserting metric: %w", result.Error) } return metric.ID, nil } // GetMetricsByRunID retrieves all metrics for a specific run func (s *Store) GetMetricsByRunID(runID string) ([]Metric, error) { var metrics []Metric result := s.db.Where("run_id = ?", runID).Order("received_at DESC").Find(&metrics) return metrics, result.Error } // GetMetricsByRepository retrieves all metrics for a specific repository func (s *Store) GetMetricsByRepository(org, repo string) ([]Metric, error) { var metrics []Metric result := s.db.Where("organization = ? AND repository = ?", org, repo).Order("received_at DESC").Find(&metrics) return metrics, result.Error } // Close closes the database connection func (s *Store) Close() error { sqlDB, err := s.db.DB() if err != nil { return err } return sqlDB.Close() }