package models import ( "context" "encoding/json" "fmt" "github.com/jackc/pgx/v5/pgxpool" ) type CreateArtifactParams struct { RepositoryID string CommitID string BuildID *string Type string BlobKey string BlobSize int64 CrashMessage *string StackTrace *string Tags json.RawMessage Metadata json.RawMessage } func CreateArtifact(ctx context.Context, pool *pgxpool.Pool, p CreateArtifactParams) (*Artifact, error) { if p.Tags == nil { p.Tags = json.RawMessage("{}") } if p.Metadata == nil { p.Metadata = json.RawMessage("{}") } a := &Artifact{} err := pool.QueryRow(ctx, ` INSERT INTO artifacts (repository_id, commit_id, build_id, type, blob_key, blob_size, crash_message, stack_trace, tags, metadata) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10) RETURNING id, repository_id, commit_id, build_id, type, blob_key, blob_size, crash_message, stack_trace, tags, metadata, created_at `, p.RepositoryID, p.CommitID, p.BuildID, p.Type, p.BlobKey, p.BlobSize, p.CrashMessage, p.StackTrace, p.Tags, p.Metadata).Scan( &a.ID, &a.RepositoryID, &a.CommitID, &a.BuildID, &a.Type, &a.BlobKey, &a.BlobSize, &a.CrashMessage, &a.StackTrace, &a.Tags, &a.Metadata, &a.CreatedAt, ) if err != nil { return nil, fmt.Errorf("creating artifact: %w", err) } return a, nil } func GetArtifact(ctx context.Context, pool *pgxpool.Pool, id string) (*Artifact, error) { a := &Artifact{} err := pool.QueryRow(ctx, ` SELECT a.id, a.repository_id, a.commit_id, a.build_id, a.type, a.blob_key, a.blob_size, a.crash_message, a.stack_trace, a.tags, a.metadata, a.created_at, r.name, c.sha FROM artifacts a JOIN repositories r ON r.id = a.repository_id JOIN commits c ON c.id = a.commit_id WHERE a.id = $1 `, id).Scan( &a.ID, &a.RepositoryID, &a.CommitID, &a.BuildID, &a.Type, &a.BlobKey, &a.BlobSize, &a.CrashMessage, &a.StackTrace, &a.Tags, &a.Metadata, &a.CreatedAt, &a.RepoName, &a.CommitSHA, ) if err != nil { return nil, fmt.Errorf("getting artifact: %w", err) } return a, nil } type ListArtifactsParams struct { RepositoryID string CommitSHA string Type string SignatureID string CampaignID string Limit int Offset int } func ListArtifacts(ctx context.Context, pool *pgxpool.Pool, p ListArtifactsParams) ([]Artifact, int, error) { if p.Limit <= 0 { p.Limit = 50 } baseQuery := ` FROM artifacts a JOIN repositories r ON r.id = a.repository_id JOIN commits c ON c.id = a.commit_id WHERE 1=1 ` args := []any{} argN := 1 if p.RepositoryID != "" { baseQuery += fmt.Sprintf(" AND a.repository_id = $%d", argN) args = append(args, p.RepositoryID) argN++ } if p.CommitSHA != "" { baseQuery += fmt.Sprintf(" AND c.sha = $%d", argN) args = append(args, p.CommitSHA) argN++ } if p.Type != "" { baseQuery += fmt.Sprintf(" AND a.type = $%d", argN) args = append(args, p.Type) argN++ } if p.SignatureID != "" { baseQuery += fmt.Sprintf(" AND a.signature_id = $%d", argN) args = append(args, p.SignatureID) argN++ } if p.CampaignID != "" { baseQuery += fmt.Sprintf(" AND a.campaign_id = $%d", argN) args = append(args, p.CampaignID) argN++ } var total int err := pool.QueryRow(ctx, "SELECT COUNT(*) "+baseQuery, args...).Scan(&total) if err != nil { return nil, 0, fmt.Errorf("counting artifacts: %w", err) } selectQuery := fmt.Sprintf(` SELECT a.id, a.repository_id, a.commit_id, a.build_id, a.type, a.blob_key, a.blob_size, a.crash_message, a.stack_trace, a.tags, a.metadata, a.created_at, r.name, c.sha %s ORDER BY a.created_at DESC LIMIT $%d OFFSET $%d `, baseQuery, argN, argN+1) args = append(args, p.Limit, p.Offset) rows, err := pool.Query(ctx, selectQuery, args...) if err != nil { return nil, 0, fmt.Errorf("listing artifacts: %w", err) } defer rows.Close() var artifacts []Artifact for rows.Next() { var a Artifact if err := rows.Scan( &a.ID, &a.RepositoryID, &a.CommitID, &a.BuildID, &a.Type, &a.BlobKey, &a.BlobSize, &a.CrashMessage, &a.StackTrace, &a.Tags, &a.Metadata, &a.CreatedAt, &a.RepoName, &a.CommitSHA, ); err != nil { return nil, 0, fmt.Errorf("scanning artifact: %w", err) } artifacts = append(artifacts, a) } return artifacts, total, nil }