Files
cmr-mini/backend/internal/store/postgres/event_store.go

346 lines
8.3 KiB
Go

package postgres
import (
"context"
"errors"
"fmt"
"time"
"github.com/jackc/pgx/v5"
)
type Event struct {
ID string
PublicID string
Slug string
DisplayName string
Summary *string
Status string
CurrentReleaseID *string
CurrentReleasePubID *string
ConfigLabel *string
ManifestURL *string
ManifestChecksum *string
RouteCode *string
}
type EventRelease struct {
ID string
PublicID string
EventID string
ReleaseNo int
ConfigLabel string
ManifestURL string
ManifestChecksum *string
RouteCode *string
BuildID *string
Status string
PublishedAt time.Time
}
type CreateGameSessionParams struct {
SessionPublicID string
UserID string
EventID string
EventReleaseID string
DeviceKey string
ClientType string
RouteCode *string
SessionTokenHash string
SessionTokenExpiresAt time.Time
}
type GameSession struct {
ID string
SessionPublicID string
UserID string
EventID string
EventReleaseID string
DeviceKey string
ClientType string
RouteCode *string
Status string
SessionTokenExpiresAt time.Time
}
func (s *Store) GetEventByPublicID(ctx context.Context, eventPublicID string) (*Event, error) {
row := s.pool.QueryRow(ctx, `
SELECT
e.id,
e.event_public_id,
e.slug,
e.display_name,
e.summary,
e.status,
e.current_release_id,
er.release_public_id,
er.config_label,
er.manifest_url,
er.manifest_checksum_sha256,
er.route_code
FROM events e
LEFT JOIN event_releases er ON er.id = e.current_release_id
WHERE e.event_public_id = $1
LIMIT 1
`, eventPublicID)
var event Event
err := row.Scan(
&event.ID,
&event.PublicID,
&event.Slug,
&event.DisplayName,
&event.Summary,
&event.Status,
&event.CurrentReleaseID,
&event.CurrentReleasePubID,
&event.ConfigLabel,
&event.ManifestURL,
&event.ManifestChecksum,
&event.RouteCode,
)
if errors.Is(err, pgx.ErrNoRows) {
return nil, nil
}
if err != nil {
return nil, fmt.Errorf("get event by public id: %w", err)
}
return &event, nil
}
func (s *Store) GetEventByID(ctx context.Context, eventID string) (*Event, error) {
row := s.pool.QueryRow(ctx, `
SELECT
e.id,
e.event_public_id,
e.slug,
e.display_name,
e.summary,
e.status,
e.current_release_id,
er.release_public_id,
er.config_label,
er.manifest_url,
er.manifest_checksum_sha256,
er.route_code
FROM events e
LEFT JOIN event_releases er ON er.id = e.current_release_id
WHERE e.id = $1
LIMIT 1
`, eventID)
var event Event
err := row.Scan(
&event.ID,
&event.PublicID,
&event.Slug,
&event.DisplayName,
&event.Summary,
&event.Status,
&event.CurrentReleaseID,
&event.CurrentReleasePubID,
&event.ConfigLabel,
&event.ManifestURL,
&event.ManifestChecksum,
&event.RouteCode,
)
if errors.Is(err, pgx.ErrNoRows) {
return nil, nil
}
if err != nil {
return nil, fmt.Errorf("get event by id: %w", err)
}
return &event, nil
}
func (s *Store) NextEventReleaseNo(ctx context.Context, eventID string) (int, error) {
var next int
if err := s.pool.QueryRow(ctx, `
SELECT COALESCE(MAX(release_no), 0) + 1
FROM event_releases
WHERE event_id = $1
`, eventID).Scan(&next); err != nil {
return 0, fmt.Errorf("next event release no: %w", err)
}
return next, nil
}
type CreateEventReleaseParams struct {
PublicID string
EventID string
ReleaseNo int
ConfigLabel string
ManifestURL string
ManifestChecksum *string
RouteCode *string
BuildID *string
Status string
PayloadJSON string
}
func (s *Store) CreateEventRelease(ctx context.Context, tx Tx, params CreateEventReleaseParams) (*EventRelease, error) {
row := tx.QueryRow(ctx, `
INSERT INTO event_releases (
release_public_id,
event_id,
release_no,
config_label,
manifest_url,
manifest_checksum_sha256,
route_code,
build_id,
status,
payload_jsonb
)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10::jsonb)
RETURNING id, release_public_id, event_id, release_no, config_label, manifest_url, manifest_checksum_sha256, route_code, build_id, status, published_at
`, params.PublicID, params.EventID, params.ReleaseNo, params.ConfigLabel, params.ManifestURL, params.ManifestChecksum, params.RouteCode, params.BuildID, params.Status, params.PayloadJSON)
var item EventRelease
if err := row.Scan(
&item.ID,
&item.PublicID,
&item.EventID,
&item.ReleaseNo,
&item.ConfigLabel,
&item.ManifestURL,
&item.ManifestChecksum,
&item.RouteCode,
&item.BuildID,
&item.Status,
&item.PublishedAt,
); err != nil {
return nil, fmt.Errorf("create event release: %w", err)
}
return &item, nil
}
func (s *Store) SetCurrentEventRelease(ctx context.Context, tx Tx, eventID, releaseID string) error {
if _, err := tx.Exec(ctx, `
UPDATE events
SET current_release_id = $2
WHERE id = $1
`, eventID, releaseID); err != nil {
return fmt.Errorf("set current event release: %w", err)
}
return nil
}
func (s *Store) CreateGameSession(ctx context.Context, tx Tx, params CreateGameSessionParams) (*GameSession, error) {
row := tx.QueryRow(ctx, `
INSERT INTO game_sessions (
session_public_id,
user_id,
event_id,
event_release_id,
device_key,
client_type,
route_code,
session_token_hash,
session_token_expires_at
)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)
RETURNING id, session_public_id, user_id, event_id, event_release_id, device_key, client_type, route_code, status, session_token_expires_at
`, params.SessionPublicID, params.UserID, params.EventID, params.EventReleaseID, params.DeviceKey, params.ClientType, params.RouteCode, params.SessionTokenHash, params.SessionTokenExpiresAt)
var session GameSession
err := row.Scan(
&session.ID,
&session.SessionPublicID,
&session.UserID,
&session.EventID,
&session.EventReleaseID,
&session.DeviceKey,
&session.ClientType,
&session.RouteCode,
&session.Status,
&session.SessionTokenExpiresAt,
)
if err != nil {
return nil, fmt.Errorf("create game session: %w", err)
}
return &session, nil
}
func (s *Store) ListEventReleasesByEventID(ctx context.Context, eventID string, limit int) ([]EventRelease, error) {
if limit <= 0 || limit > 100 {
limit = 20
}
rows, err := s.pool.Query(ctx, `
SELECT id, release_public_id, event_id, release_no, config_label, manifest_url, manifest_checksum_sha256, route_code, build_id, status, published_at
FROM event_releases
WHERE event_id = $1
ORDER BY release_no DESC
LIMIT $2
`, eventID, limit)
if err != nil {
return nil, fmt.Errorf("list event releases by event id: %w", err)
}
defer rows.Close()
items := []EventRelease{}
for rows.Next() {
item, err := scanEventReleaseFromRows(rows)
if err != nil {
return nil, err
}
items = append(items, *item)
}
if err := rows.Err(); err != nil {
return nil, fmt.Errorf("iterate event releases by event id: %w", err)
}
return items, nil
}
func (s *Store) GetEventReleaseByPublicID(ctx context.Context, releasePublicID string) (*EventRelease, error) {
row := s.pool.QueryRow(ctx, `
SELECT id, release_public_id, event_id, release_no, config_label, manifest_url, manifest_checksum_sha256, route_code, build_id, status, published_at
FROM event_releases
WHERE release_public_id = $1
LIMIT 1
`, releasePublicID)
var item EventRelease
err := row.Scan(
&item.ID,
&item.PublicID,
&item.EventID,
&item.ReleaseNo,
&item.ConfigLabel,
&item.ManifestURL,
&item.ManifestChecksum,
&item.RouteCode,
&item.BuildID,
&item.Status,
&item.PublishedAt,
)
if errors.Is(err, pgx.ErrNoRows) {
return nil, nil
}
if err != nil {
return nil, fmt.Errorf("get event release by public id: %w", err)
}
return &item, nil
}
func scanEventReleaseFromRows(rows pgx.Rows) (*EventRelease, error) {
var item EventRelease
err := rows.Scan(
&item.ID,
&item.PublicID,
&item.EventID,
&item.ReleaseNo,
&item.ConfigLabel,
&item.ManifestURL,
&item.ManifestChecksum,
&item.RouteCode,
&item.BuildID,
&item.Status,
&item.PublishedAt,
)
if err != nil {
return nil, fmt.Errorf("scan event release row: %w", err)
}
return &item, nil
}