300 lines
7.7 KiB
Go
300 lines
7.7 KiB
Go
package models
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"time"
|
|
|
|
cairnapi "github.com/mattnite/cairn/internal/api"
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
type CreateTargetParams struct {
|
|
RepositoryID uint
|
|
Name string
|
|
Type string
|
|
Tags json.RawMessage
|
|
Metadata json.RawMessage
|
|
}
|
|
|
|
func GetOrCreateTarget(ctx context.Context, db *gorm.DB, p CreateTargetParams) (*cairnapi.Target, error) {
|
|
if p.Tags == nil {
|
|
p.Tags = json.RawMessage("{}")
|
|
}
|
|
if p.Metadata == nil {
|
|
p.Metadata = json.RawMessage("{}")
|
|
}
|
|
|
|
target := &Target{}
|
|
err := db.WithContext(ctx).
|
|
Where("repository_id = ? AND name = ?", p.RepositoryID, p.Name).
|
|
First(target).Error
|
|
|
|
if err == gorm.ErrRecordNotFound {
|
|
target = &Target{
|
|
RepositoryID: p.RepositoryID,
|
|
Name: p.Name,
|
|
Type: p.Type,
|
|
Tags: p.Tags,
|
|
Metadata: p.Metadata,
|
|
}
|
|
if err := db.WithContext(ctx).Create(target).Error; err != nil {
|
|
return nil, fmt.Errorf("creating target: %w", err)
|
|
}
|
|
} else if err != nil {
|
|
return nil, fmt.Errorf("querying target: %w", err)
|
|
}
|
|
|
|
return enrichTarget(ctx, db, *target)
|
|
}
|
|
|
|
func GetTarget(ctx context.Context, db *gorm.DB, id uint) (*cairnapi.Target, error) {
|
|
target := &Target{}
|
|
if err := db.WithContext(ctx).First(target, id).Error; err != nil {
|
|
return nil, fmt.Errorf("getting target: %w", err)
|
|
}
|
|
return enrichTarget(ctx, db, *target)
|
|
}
|
|
|
|
func ListTargets(ctx context.Context, db *gorm.DB, repoID *uint, limit, offset int) ([]cairnapi.Target, int64, error) {
|
|
if limit <= 0 {
|
|
limit = 50
|
|
}
|
|
|
|
query := db.WithContext(ctx).Model(&Target{})
|
|
if repoID != nil {
|
|
query = query.Where("repository_id = ?", *repoID)
|
|
}
|
|
|
|
var total int64
|
|
if err := query.Count(&total).Error; err != nil {
|
|
return nil, 0, fmt.Errorf("counting targets: %w", err)
|
|
}
|
|
|
|
var dbTargets []Target
|
|
if err := query.Order("updated_at DESC").Limit(limit).Offset(offset).Find(&dbTargets).Error; err != nil {
|
|
return nil, 0, fmt.Errorf("listing targets: %w", err)
|
|
}
|
|
|
|
targets := make([]cairnapi.Target, 0, len(dbTargets))
|
|
for _, m := range dbTargets {
|
|
t, err := enrichTarget(ctx, db, m)
|
|
if err != nil {
|
|
return nil, 0, err
|
|
}
|
|
targets = append(targets, *t)
|
|
}
|
|
|
|
return targets, total, nil
|
|
}
|
|
|
|
func enrichTarget(ctx context.Context, db *gorm.DB, model Target) (*cairnapi.Target, error) {
|
|
repo := &Repository{}
|
|
if err := db.WithContext(ctx).First(repo, model.RepositoryID).Error; err != nil {
|
|
return nil, fmt.Errorf("loading target repository: %w", err)
|
|
}
|
|
|
|
var runCount int64
|
|
if err := db.WithContext(ctx).Model(&Run{}).Where("target_id = ?", model.ID).Count(&runCount).Error; err != nil {
|
|
return nil, fmt.Errorf("counting target runs: %w", err)
|
|
}
|
|
|
|
var corpusCount int64
|
|
if err := db.WithContext(ctx).Model(&CorpusEntry{}).Where("target_id = ?", model.ID).Count(&corpusCount).Error; err != nil {
|
|
return nil, fmt.Errorf("counting target corpus: %w", err)
|
|
}
|
|
|
|
t := targetFromModel(model)
|
|
t.RepoName = repo.Name
|
|
t.RunCount = runCount
|
|
t.CorpusCount = corpusCount
|
|
return &t, nil
|
|
}
|
|
|
|
func targetFromModel(m Target) cairnapi.Target {
|
|
return cairnapi.Target{
|
|
ID: m.ID,
|
|
RepositoryID: m.RepositoryID,
|
|
Name: m.Name,
|
|
Type: m.Type,
|
|
Tags: m.Tags,
|
|
Metadata: m.Metadata,
|
|
CreatedAt: m.CreatedAt,
|
|
UpdatedAt: m.UpdatedAt,
|
|
}
|
|
}
|
|
|
|
// Run functions
|
|
|
|
func CreateRun(ctx context.Context, db *gorm.DB, targetID, commitID uint) (*cairnapi.Run, error) {
|
|
run := &Run{
|
|
TargetID: targetID,
|
|
CommitID: commitID,
|
|
Status: "running",
|
|
StartedAt: time.Now(),
|
|
Tags: json.RawMessage("{}"),
|
|
Metadata: json.RawMessage("{}"),
|
|
}
|
|
|
|
if err := db.WithContext(ctx).Create(run).Error; err != nil {
|
|
return nil, fmt.Errorf("creating run: %w", err)
|
|
}
|
|
return enrichRun(ctx, db, *run)
|
|
}
|
|
|
|
func FinishRun(ctx context.Context, db *gorm.DB, id uint) error {
|
|
now := time.Now()
|
|
if err := db.WithContext(ctx).Model(&Run{}).Where("id = ?", id).Updates(map[string]any{
|
|
"status": "finished",
|
|
"finished_at": now,
|
|
}).Error; err != nil {
|
|
return fmt.Errorf("finishing run: %w", err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func GetRun(ctx context.Context, db *gorm.DB, id uint) (*cairnapi.Run, error) {
|
|
run := &Run{}
|
|
if err := db.WithContext(ctx).First(run, id).Error; err != nil {
|
|
return nil, fmt.Errorf("getting run: %w", err)
|
|
}
|
|
return enrichRun(ctx, db, *run)
|
|
}
|
|
|
|
func ListRuns(ctx context.Context, db *gorm.DB, targetID *uint, limit, offset int) ([]cairnapi.Run, int64, error) {
|
|
if limit <= 0 {
|
|
limit = 50
|
|
}
|
|
|
|
query := db.WithContext(ctx).Model(&Run{})
|
|
if targetID != nil {
|
|
query = query.Where("target_id = ?", *targetID)
|
|
}
|
|
|
|
var total int64
|
|
if err := query.Count(&total).Error; err != nil {
|
|
return nil, 0, fmt.Errorf("counting runs: %w", err)
|
|
}
|
|
|
|
var dbRuns []Run
|
|
if err := query.Order("created_at DESC").Limit(limit).Offset(offset).Find(&dbRuns).Error; err != nil {
|
|
return nil, 0, fmt.Errorf("listing runs: %w", err)
|
|
}
|
|
|
|
runs := make([]cairnapi.Run, 0, len(dbRuns))
|
|
for _, m := range dbRuns {
|
|
r, err := enrichRun(ctx, db, m)
|
|
if err != nil {
|
|
return nil, 0, err
|
|
}
|
|
runs = append(runs, *r)
|
|
}
|
|
|
|
return runs, total, nil
|
|
}
|
|
|
|
func enrichRun(ctx context.Context, db *gorm.DB, model Run) (*cairnapi.Run, error) {
|
|
target := &Target{}
|
|
if err := db.WithContext(ctx).First(target, model.TargetID).Error; err != nil {
|
|
return nil, fmt.Errorf("loading run target: %w", err)
|
|
}
|
|
|
|
repo := &Repository{}
|
|
if err := db.WithContext(ctx).First(repo, target.RepositoryID).Error; err != nil {
|
|
return nil, fmt.Errorf("loading run repository: %w", err)
|
|
}
|
|
|
|
commit := &Commit{}
|
|
if err := db.WithContext(ctx).First(commit, model.CommitID).Error; err != nil {
|
|
return nil, fmt.Errorf("loading run commit: %w", err)
|
|
}
|
|
|
|
var artifactCount int64
|
|
_ = db.WithContext(ctx).Model(&Artifact{}).Where("run_id = ?", model.ID).Count(&artifactCount).Error
|
|
|
|
r := runFromModel(model)
|
|
r.TargetName = target.Name
|
|
r.RepoName = repo.Name
|
|
r.CommitSHA = commit.SHA
|
|
r.ArtifactCount = artifactCount
|
|
return &r, nil
|
|
}
|
|
|
|
func runFromModel(m Run) cairnapi.Run {
|
|
return cairnapi.Run{
|
|
ID: m.ID,
|
|
TargetID: m.TargetID,
|
|
CommitID: m.CommitID,
|
|
Status: m.Status,
|
|
StartedAt: m.StartedAt,
|
|
FinishedAt: m.FinishedAt,
|
|
Tags: m.Tags,
|
|
Metadata: m.Metadata,
|
|
CreatedAt: m.CreatedAt,
|
|
}
|
|
}
|
|
|
|
// Corpus functions
|
|
|
|
type CreateCorpusEntryParams struct {
|
|
TargetID uint
|
|
RunID *uint
|
|
BlobKey string
|
|
BlobSize int64
|
|
Fingerprint *string
|
|
}
|
|
|
|
func CreateCorpusEntry(ctx context.Context, db *gorm.DB, p CreateCorpusEntryParams) (*cairnapi.CorpusEntry, error) {
|
|
entry := &CorpusEntry{
|
|
TargetID: p.TargetID,
|
|
RunID: p.RunID,
|
|
BlobKey: p.BlobKey,
|
|
BlobSize: p.BlobSize,
|
|
Fingerprint: p.Fingerprint,
|
|
}
|
|
|
|
if err := db.WithContext(ctx).Create(entry).Error; err != nil {
|
|
return nil, fmt.Errorf("creating corpus entry: %w", err)
|
|
}
|
|
|
|
return corpusEntryToAPI(*entry), nil
|
|
}
|
|
|
|
func ListCorpusEntries(ctx context.Context, db *gorm.DB, targetID uint, limit, offset int) ([]cairnapi.CorpusEntry, int64, error) {
|
|
if limit <= 0 {
|
|
limit = 1000
|
|
}
|
|
|
|
query := db.WithContext(ctx).Model(&CorpusEntry{}).Where("target_id = ?", targetID)
|
|
|
|
var total int64
|
|
if err := query.Count(&total).Error; err != nil {
|
|
return nil, 0, fmt.Errorf("counting corpus entries: %w", err)
|
|
}
|
|
|
|
var dbEntries []CorpusEntry
|
|
if err := query.Order("created_at DESC").Limit(limit).Offset(offset).Find(&dbEntries).Error; err != nil {
|
|
return nil, 0, fmt.Errorf("listing corpus entries: %w", err)
|
|
}
|
|
|
|
entries := make([]cairnapi.CorpusEntry, 0, len(dbEntries))
|
|
for _, e := range dbEntries {
|
|
entries = append(entries, *corpusEntryToAPI(e))
|
|
}
|
|
|
|
return entries, total, nil
|
|
}
|
|
|
|
func corpusEntryToAPI(m CorpusEntry) *cairnapi.CorpusEntry {
|
|
return &cairnapi.CorpusEntry{
|
|
ID: m.ID,
|
|
TargetID: m.TargetID,
|
|
RunID: m.RunID,
|
|
BlobKey: m.BlobKey,
|
|
BlobSize: m.BlobSize,
|
|
Fingerprint: m.Fingerprint,
|
|
CreatedAt: m.CreatedAt,
|
|
}
|
|
}
|