cairn/internal/models/crash_group.go

149 lines
4.3 KiB
Go

package models
import (
"context"
"fmt"
"time"
cairnapi "github.com/mattnite/cairn/internal/api"
"gorm.io/gorm"
)
func GetOrCreateSignature(ctx context.Context, db *gorm.DB, repoID uint, fingerprint string, sampleTrace *string) (*cairnapi.CrashSignature, bool, error) {
sig := &CrashSignature{}
err := db.WithContext(ctx).Where("repository_id = ? AND fingerprint = ?", repoID, fingerprint).First(sig).Error
if err == nil {
sig.LastSeenAt = time.Now()
sig.OccurrenceCount++
if sig.SampleTrace == nil && sampleTrace != nil {
sig.SampleTrace = sampleTrace
}
if saveErr := db.WithContext(ctx).Save(sig).Error; saveErr != nil {
return nil, false, fmt.Errorf("updating signature: %w", saveErr)
}
out := crashSignatureFromModel(*sig)
return &out, false, nil
}
if err != gorm.ErrRecordNotFound {
return nil, false, fmt.Errorf("querying signature: %w", err)
}
now := time.Now()
sig = &CrashSignature{
RepositoryID: repoID,
Fingerprint: fingerprint,
SampleTrace: sampleTrace,
FirstSeenAt: now,
LastSeenAt: now,
OccurrenceCount: 1,
}
if err := db.WithContext(ctx).Create(sig).Error; err != nil {
return nil, false, fmt.Errorf("creating signature: %w", err)
}
out := crashSignatureFromModel(*sig)
return &out, true, nil
}
func CreateCrashGroup(ctx context.Context, db *gorm.DB, sigID, repoID uint, title string) (*cairnapi.CrashGroup, error) {
now := time.Now()
group := &CrashGroup{
CrashSignatureID: sigID,
RepositoryID: repoID,
Title: title,
Status: "open",
FirstSeenAt: now,
LastSeenAt: now,
}
if err := db.WithContext(ctx).Create(group).Error; err != nil {
return nil, fmt.Errorf("creating crash group: %w", err)
}
return enrichCrashGroup(ctx, db, *group)
}
func ListCrashGroups(ctx context.Context, db *gorm.DB, repoID *uint, status string, limit, offset int) ([]cairnapi.CrashGroup, int64, error) {
if limit <= 0 {
limit = 50
}
query := db.WithContext(ctx).Model(&CrashGroup{})
if repoID != nil {
query = query.Where("repository_id = ?", *repoID)
}
if status != "" {
query = query.Where("status = ?", status)
}
var total int64
if err := query.Count(&total).Error; err != nil {
return nil, 0, fmt.Errorf("counting crash groups: %w", err)
}
var dbGroups []CrashGroup
if err := query.Order("last_seen_at DESC").Limit(limit).Offset(offset).Find(&dbGroups).Error; err != nil {
return nil, 0, fmt.Errorf("listing crash groups: %w", err)
}
groups := make([]cairnapi.CrashGroup, 0, len(dbGroups))
for _, m := range dbGroups {
g, err := enrichCrashGroup(ctx, db, m)
if err != nil {
return nil, 0, err
}
groups = append(groups, *g)
}
return groups, total, nil
}
func GetCrashGroup(ctx context.Context, db *gorm.DB, id uint) (*cairnapi.CrashGroup, error) {
group := &CrashGroup{}
if err := db.WithContext(ctx).First(group, id).Error; err != nil {
return nil, fmt.Errorf("getting crash group: %w", err)
}
return enrichCrashGroup(ctx, db, *group)
}
func enrichCrashGroup(ctx context.Context, db *gorm.DB, model CrashGroup) (*cairnapi.CrashGroup, error) {
repo := &Repository{}
if err := db.WithContext(ctx).First(repo, model.RepositoryID).Error; err != nil {
return nil, fmt.Errorf("loading crash group repository: %w", err)
}
sig := &CrashSignature{}
if err := db.WithContext(ctx).First(sig, model.CrashSignatureID).Error; err != nil {
return nil, fmt.Errorf("loading crash signature: %w", err)
}
group := crashGroupFromModel(model)
group.RepoName = repo.Name
group.Fingerprint = sig.Fingerprint
group.OccurrenceCount = sig.OccurrenceCount
return &group, nil
}
func crashSignatureFromModel(m CrashSignature) cairnapi.CrashSignature {
return cairnapi.CrashSignature{
ID: m.ID,
RepositoryID: m.RepositoryID,
Fingerprint: m.Fingerprint,
SampleTrace: m.SampleTrace,
FirstSeenAt: m.FirstSeenAt,
LastSeenAt: m.LastSeenAt,
OccurrenceCount: m.OccurrenceCount,
}
}
func crashGroupFromModel(m CrashGroup) cairnapi.CrashGroup {
return cairnapi.CrashGroup{
ID: m.ID,
RepositoryID: m.RepositoryID,
CrashSignatureID: m.CrashSignatureID,
Title: m.Title,
Status: m.Status,
ForgejoIssueID: m.ForgejoIssueID,
FirstSeenAt: m.FirstSeenAt,
LastSeenAt: m.LastSeenAt,
CreatedAt: m.CreatedAt,
UpdatedAt: m.UpdatedAt,
}
}