604 lines
16 KiB
Go
604 lines
16 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
|
|
ReleasePayloadJSON *string
|
|
RuntimeBindingID *string
|
|
PlacePublicID *string
|
|
PlaceName *string
|
|
MapAssetPublicID *string
|
|
MapAssetName *string
|
|
TileReleasePublicID *string
|
|
CourseSetPublicID *string
|
|
CourseVariantID *string
|
|
CourseVariantName *string
|
|
RuntimeRouteCode *string
|
|
PresentationID *string
|
|
PresentationName *string
|
|
PresentationType *string
|
|
ContentBundleID *string
|
|
ContentBundleName *string
|
|
ContentEntryURL *string
|
|
ContentAssetRootURL *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
|
|
RuntimeBindingID *string
|
|
PlacePublicID *string
|
|
PlaceName *string
|
|
MapAssetPublicID *string
|
|
MapAssetName *string
|
|
TileReleaseID *string
|
|
CourseSetID *string
|
|
CourseVariantID *string
|
|
CourseVariantName *string
|
|
RuntimeRouteCode *string
|
|
PresentationID *string
|
|
PresentationName *string
|
|
PresentationType *string
|
|
ContentBundleID *string
|
|
ContentBundleName *string
|
|
ContentEntryURL *string
|
|
ContentAssetURL *string
|
|
}
|
|
|
|
type CreateGameSessionParams struct {
|
|
SessionPublicID string
|
|
UserID string
|
|
EventID string
|
|
EventReleaseID string
|
|
DeviceKey string
|
|
ClientType string
|
|
AssignmentMode *string
|
|
VariantID *string
|
|
VariantName *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
|
|
AssignmentMode *string
|
|
VariantID *string
|
|
VariantName *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,
|
|
er.payload_jsonb::text,
|
|
mrb.runtime_binding_public_id,
|
|
p.place_public_id,
|
|
p.name,
|
|
ma.map_asset_public_id,
|
|
ma.name,
|
|
tr.tile_release_public_id,
|
|
cset.course_set_public_id,
|
|
cv.course_variant_public_id,
|
|
cv.name,
|
|
cv.route_code,
|
|
ep.presentation_public_id,
|
|
ep.name,
|
|
ep.presentation_type,
|
|
cb.content_bundle_public_id,
|
|
cb.name,
|
|
cb.entry_url,
|
|
cb.asset_root_url
|
|
FROM events e
|
|
LEFT JOIN event_releases er ON er.id = e.current_release_id
|
|
LEFT JOIN map_runtime_bindings mrb ON mrb.id = er.runtime_binding_id
|
|
LEFT JOIN places p ON p.id = mrb.place_id
|
|
LEFT JOIN map_assets ma ON ma.id = mrb.map_asset_id
|
|
LEFT JOIN tile_releases tr ON tr.id = mrb.tile_release_id
|
|
LEFT JOIN course_sets cset ON cset.id = mrb.course_set_id
|
|
LEFT JOIN course_variants cv ON cv.id = mrb.course_variant_id
|
|
LEFT JOIN event_presentations ep ON ep.id = er.presentation_id
|
|
LEFT JOIN content_bundles cb ON cb.id = er.content_bundle_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,
|
|
&event.ReleasePayloadJSON,
|
|
&event.RuntimeBindingID,
|
|
&event.PlacePublicID,
|
|
&event.PlaceName,
|
|
&event.MapAssetPublicID,
|
|
&event.MapAssetName,
|
|
&event.TileReleasePublicID,
|
|
&event.CourseSetPublicID,
|
|
&event.CourseVariantID,
|
|
&event.CourseVariantName,
|
|
&event.RuntimeRouteCode,
|
|
&event.PresentationID,
|
|
&event.PresentationName,
|
|
&event.PresentationType,
|
|
&event.ContentBundleID,
|
|
&event.ContentBundleName,
|
|
&event.ContentEntryURL,
|
|
&event.ContentAssetRootURL,
|
|
)
|
|
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,
|
|
er.payload_jsonb::text,
|
|
mrb.runtime_binding_public_id,
|
|
p.place_public_id,
|
|
p.name,
|
|
ma.map_asset_public_id,
|
|
ma.name,
|
|
tr.tile_release_public_id,
|
|
cset.course_set_public_id,
|
|
cv.course_variant_public_id,
|
|
cv.name,
|
|
cv.route_code,
|
|
ep.presentation_public_id,
|
|
ep.name,
|
|
ep.presentation_type,
|
|
cb.content_bundle_public_id,
|
|
cb.name,
|
|
cb.entry_url,
|
|
cb.asset_root_url
|
|
FROM events e
|
|
LEFT JOIN event_releases er ON er.id = e.current_release_id
|
|
LEFT JOIN map_runtime_bindings mrb ON mrb.id = er.runtime_binding_id
|
|
LEFT JOIN places p ON p.id = mrb.place_id
|
|
LEFT JOIN map_assets ma ON ma.id = mrb.map_asset_id
|
|
LEFT JOIN tile_releases tr ON tr.id = mrb.tile_release_id
|
|
LEFT JOIN course_sets cset ON cset.id = mrb.course_set_id
|
|
LEFT JOIN course_variants cv ON cv.id = mrb.course_variant_id
|
|
LEFT JOIN event_presentations ep ON ep.id = er.presentation_id
|
|
LEFT JOIN content_bundles cb ON cb.id = er.content_bundle_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,
|
|
&event.ReleasePayloadJSON,
|
|
&event.RuntimeBindingID,
|
|
&event.PlacePublicID,
|
|
&event.PlaceName,
|
|
&event.MapAssetPublicID,
|
|
&event.MapAssetName,
|
|
&event.TileReleasePublicID,
|
|
&event.CourseSetPublicID,
|
|
&event.CourseVariantID,
|
|
&event.CourseVariantName,
|
|
&event.RuntimeRouteCode,
|
|
&event.PresentationID,
|
|
&event.PresentationName,
|
|
&event.PresentationType,
|
|
&event.ContentBundleID,
|
|
&event.ContentBundleName,
|
|
&event.ContentEntryURL,
|
|
&event.ContentAssetRootURL,
|
|
)
|
|
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 er
|
|
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
|
|
RuntimeBindingID *string
|
|
PresentationID *string
|
|
ContentBundleID *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,
|
|
runtime_binding_id,
|
|
presentation_id,
|
|
content_bundle_id,
|
|
status,
|
|
payload_jsonb
|
|
)
|
|
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13::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.RuntimeBindingID, params.PresentationID, params.ContentBundleID, 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,
|
|
assignment_mode,
|
|
variant_id,
|
|
variant_name,
|
|
route_code,
|
|
session_token_hash,
|
|
session_token_expires_at
|
|
)
|
|
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)
|
|
RETURNING id, session_public_id, user_id, event_id, event_release_id, device_key, client_type, assignment_mode, variant_id, variant_name, route_code, status, session_token_expires_at
|
|
`, params.SessionPublicID, params.UserID, params.EventID, params.EventReleaseID, params.DeviceKey, params.ClientType, params.AssignmentMode, params.VariantID, params.VariantName, 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.AssignmentMode,
|
|
&session.VariantID,
|
|
&session.VariantName,
|
|
&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
|
|
er.id,
|
|
er.release_public_id,
|
|
er.event_id,
|
|
er.release_no,
|
|
er.config_label,
|
|
er.manifest_url,
|
|
er.manifest_checksum_sha256,
|
|
er.route_code,
|
|
er.build_id,
|
|
er.status,
|
|
er.published_at,
|
|
mrb.runtime_binding_public_id,
|
|
p.place_public_id,
|
|
p.name,
|
|
ma.map_asset_public_id,
|
|
ma.name,
|
|
tr.tile_release_public_id,
|
|
cset.course_set_public_id,
|
|
cv.course_variant_public_id,
|
|
cv.name,
|
|
cv.route_code,
|
|
ep.presentation_public_id,
|
|
ep.name,
|
|
ep.presentation_type,
|
|
cb.content_bundle_public_id,
|
|
cb.name,
|
|
cb.entry_url,
|
|
cb.asset_root_url
|
|
FROM event_releases er
|
|
LEFT JOIN map_runtime_bindings mrb ON mrb.id = er.runtime_binding_id
|
|
LEFT JOIN places p ON p.id = mrb.place_id
|
|
LEFT JOIN map_assets ma ON ma.id = mrb.map_asset_id
|
|
LEFT JOIN tile_releases tr ON tr.id = mrb.tile_release_id
|
|
LEFT JOIN course_sets cset ON cset.id = mrb.course_set_id
|
|
LEFT JOIN course_variants cv ON cv.id = mrb.course_variant_id
|
|
LEFT JOIN event_presentations ep ON ep.id = er.presentation_id
|
|
LEFT JOIN content_bundles cb ON cb.id = er.content_bundle_id
|
|
WHERE er.event_id = $1
|
|
ORDER BY er.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
|
|
er.id,
|
|
er.release_public_id,
|
|
er.event_id,
|
|
er.release_no,
|
|
er.config_label,
|
|
er.manifest_url,
|
|
er.manifest_checksum_sha256,
|
|
er.route_code,
|
|
er.build_id,
|
|
er.status,
|
|
er.published_at,
|
|
mrb.runtime_binding_public_id,
|
|
p.place_public_id,
|
|
p.name,
|
|
ma.map_asset_public_id,
|
|
ma.name,
|
|
tr.tile_release_public_id,
|
|
cset.course_set_public_id,
|
|
cv.course_variant_public_id,
|
|
cv.name,
|
|
cv.route_code,
|
|
ep.presentation_public_id,
|
|
ep.name,
|
|
ep.presentation_type,
|
|
cb.content_bundle_public_id,
|
|
cb.name,
|
|
cb.entry_url,
|
|
cb.asset_root_url
|
|
FROM event_releases er
|
|
LEFT JOIN map_runtime_bindings mrb ON mrb.id = er.runtime_binding_id
|
|
LEFT JOIN places p ON p.id = mrb.place_id
|
|
LEFT JOIN map_assets ma ON ma.id = mrb.map_asset_id
|
|
LEFT JOIN tile_releases tr ON tr.id = mrb.tile_release_id
|
|
LEFT JOIN course_sets cset ON cset.id = mrb.course_set_id
|
|
LEFT JOIN course_variants cv ON cv.id = mrb.course_variant_id
|
|
LEFT JOIN event_presentations ep ON ep.id = er.presentation_id
|
|
LEFT JOIN content_bundles cb ON cb.id = er.content_bundle_id
|
|
WHERE er.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,
|
|
&item.RuntimeBindingID,
|
|
&item.PlacePublicID,
|
|
&item.PlaceName,
|
|
&item.MapAssetPublicID,
|
|
&item.MapAssetName,
|
|
&item.TileReleaseID,
|
|
&item.CourseSetID,
|
|
&item.CourseVariantID,
|
|
&item.CourseVariantName,
|
|
&item.RuntimeRouteCode,
|
|
&item.PresentationID,
|
|
&item.PresentationName,
|
|
&item.PresentationType,
|
|
&item.ContentBundleID,
|
|
&item.ContentBundleName,
|
|
&item.ContentEntryURL,
|
|
&item.ContentAssetURL,
|
|
)
|
|
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,
|
|
&item.RuntimeBindingID,
|
|
&item.PlacePublicID,
|
|
&item.PlaceName,
|
|
&item.MapAssetPublicID,
|
|
&item.MapAssetName,
|
|
&item.TileReleaseID,
|
|
&item.CourseSetID,
|
|
&item.CourseVariantID,
|
|
&item.CourseVariantName,
|
|
&item.RuntimeRouteCode,
|
|
&item.PresentationID,
|
|
&item.PresentationName,
|
|
&item.PresentationType,
|
|
&item.ContentBundleID,
|
|
&item.ContentBundleName,
|
|
&item.ContentEntryURL,
|
|
&item.ContentAssetURL,
|
|
)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("scan event release row: %w", err)
|
|
}
|
|
return &item, nil
|
|
}
|
|
|
|
func (s *Store) SetEventReleaseRuntimeBinding(ctx context.Context, tx Tx, releaseID string, runtimeBindingID *string) error {
|
|
if _, err := tx.Exec(ctx, `
|
|
UPDATE event_releases
|
|
SET runtime_binding_id = $2
|
|
WHERE id = $1
|
|
`, releaseID, runtimeBindingID); err != nil {
|
|
return fmt.Errorf("set event release runtime binding: %w", err)
|
|
}
|
|
return nil
|
|
}
|