cairn/internal/models/campaign.go

147 lines
4.1 KiB
Go

package models
import (
"context"
"encoding/json"
"fmt"
"time"
"github.com/jackc/pgx/v5/pgxpool"
)
type Campaign struct {
ID string `json:"id"`
RepositoryID string `json:"repository_id"`
Name string `json:"name"`
Type string `json:"type"`
Status string `json:"status"`
StartedAt time.Time `json:"started_at"`
FinishedAt *time.Time `json:"finished_at,omitempty"`
Tags json.RawMessage `json:"tags,omitempty"`
Metadata json.RawMessage `json:"metadata,omitempty"`
CreatedAt time.Time `json:"created_at"`
// Joined fields.
RepoName string `json:"repo_name,omitempty"`
ArtifactCount int `json:"artifact_count,omitempty"`
}
type CreateCampaignParams struct {
RepositoryID string
Name string
Type string
Tags json.RawMessage
Metadata json.RawMessage
}
func CreateCampaign(ctx context.Context, pool *pgxpool.Pool, p CreateCampaignParams) (*Campaign, error) {
if p.Tags == nil {
p.Tags = json.RawMessage("{}")
}
if p.Metadata == nil {
p.Metadata = json.RawMessage("{}")
}
c := &Campaign{}
err := pool.QueryRow(ctx, `
INSERT INTO campaigns (repository_id, name, type, tags, metadata)
VALUES ($1, $2, $3, $4, $5)
RETURNING id, repository_id, name, type, status, started_at, finished_at, tags, metadata, created_at
`, p.RepositoryID, p.Name, p.Type, p.Tags, p.Metadata).Scan(
&c.ID, &c.RepositoryID, &c.Name, &c.Type, &c.Status,
&c.StartedAt, &c.FinishedAt, &c.Tags, &c.Metadata, &c.CreatedAt,
)
if err != nil {
return nil, fmt.Errorf("creating campaign: %w", err)
}
return c, nil
}
func FinishCampaign(ctx context.Context, pool *pgxpool.Pool, id string) error {
_, err := pool.Exec(ctx, `
UPDATE campaigns SET status = 'finished', finished_at = NOW() WHERE id = $1
`, id)
if err != nil {
return fmt.Errorf("finishing campaign: %w", err)
}
return nil
}
func GetCampaign(ctx context.Context, pool *pgxpool.Pool, id string) (*Campaign, error) {
c := &Campaign{}
err := pool.QueryRow(ctx, `
SELECT c.id, c.repository_id, c.name, c.type, c.status, c.started_at, c.finished_at,
c.tags, c.metadata, c.created_at,
r.name,
(SELECT COUNT(*) FROM artifacts a WHERE a.campaign_id = c.id)
FROM campaigns c
JOIN repositories r ON r.id = c.repository_id
WHERE c.id = $1
`, id).Scan(
&c.ID, &c.RepositoryID, &c.Name, &c.Type, &c.Status,
&c.StartedAt, &c.FinishedAt, &c.Tags, &c.Metadata, &c.CreatedAt,
&c.RepoName, &c.ArtifactCount,
)
if err != nil {
return nil, fmt.Errorf("getting campaign: %w", err)
}
return c, nil
}
func ListCampaigns(ctx context.Context, pool *pgxpool.Pool, repoID string, limit, offset int) ([]Campaign, int, error) {
if limit <= 0 {
limit = 50
}
baseQuery := `
FROM campaigns c
JOIN repositories r ON r.id = c.repository_id
WHERE 1=1
`
args := []any{}
argN := 1
if repoID != "" {
baseQuery += fmt.Sprintf(" AND c.repository_id = $%d", argN)
args = append(args, repoID)
argN++
}
var total int
err := pool.QueryRow(ctx, "SELECT COUNT(*) "+baseQuery, args...).Scan(&total)
if err != nil {
return nil, 0, fmt.Errorf("counting campaigns: %w", err)
}
selectQuery := fmt.Sprintf(`
SELECT c.id, c.repository_id, c.name, c.type, c.status, c.started_at, c.finished_at,
c.tags, c.metadata, c.created_at,
r.name,
(SELECT COUNT(*) FROM artifacts a WHERE a.campaign_id = c.id)
%s
ORDER BY c.created_at DESC
LIMIT $%d OFFSET $%d
`, baseQuery, argN, argN+1)
args = append(args, limit, offset)
rows, err := pool.Query(ctx, selectQuery, args...)
if err != nil {
return nil, 0, fmt.Errorf("listing campaigns: %w", err)
}
defer rows.Close()
var campaigns []Campaign
for rows.Next() {
var c Campaign
if err := rows.Scan(
&c.ID, &c.RepositoryID, &c.Name, &c.Type, &c.Status,
&c.StartedAt, &c.FinishedAt, &c.Tags, &c.Metadata, &c.CreatedAt,
&c.RepoName, &c.ArtifactCount,
); err != nil {
return nil, 0, fmt.Errorf("scanning campaign: %w", err)
}
campaigns = append(campaigns, c)
}
return campaigns, total, nil
}