event_store.go 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362
  1. package postgres
  2. import (
  3. "context"
  4. "errors"
  5. "fmt"
  6. "time"
  7. "github.com/jackc/pgx/v5"
  8. )
  9. type Event struct {
  10. ID string
  11. PublicID string
  12. Slug string
  13. DisplayName string
  14. Summary *string
  15. Status string
  16. CurrentReleaseID *string
  17. CurrentReleasePubID *string
  18. ConfigLabel *string
  19. ManifestURL *string
  20. ManifestChecksum *string
  21. RouteCode *string
  22. ReleasePayloadJSON *string
  23. }
  24. type EventRelease struct {
  25. ID string
  26. PublicID string
  27. EventID string
  28. ReleaseNo int
  29. ConfigLabel string
  30. ManifestURL string
  31. ManifestChecksum *string
  32. RouteCode *string
  33. BuildID *string
  34. Status string
  35. PublishedAt time.Time
  36. }
  37. type CreateGameSessionParams struct {
  38. SessionPublicID string
  39. UserID string
  40. EventID string
  41. EventReleaseID string
  42. DeviceKey string
  43. ClientType string
  44. AssignmentMode *string
  45. VariantID *string
  46. VariantName *string
  47. RouteCode *string
  48. SessionTokenHash string
  49. SessionTokenExpiresAt time.Time
  50. }
  51. type GameSession struct {
  52. ID string
  53. SessionPublicID string
  54. UserID string
  55. EventID string
  56. EventReleaseID string
  57. DeviceKey string
  58. ClientType string
  59. AssignmentMode *string
  60. VariantID *string
  61. VariantName *string
  62. RouteCode *string
  63. Status string
  64. SessionTokenExpiresAt time.Time
  65. }
  66. func (s *Store) GetEventByPublicID(ctx context.Context, eventPublicID string) (*Event, error) {
  67. row := s.pool.QueryRow(ctx, `
  68. SELECT
  69. e.id,
  70. e.event_public_id,
  71. e.slug,
  72. e.display_name,
  73. e.summary,
  74. e.status,
  75. e.current_release_id,
  76. er.release_public_id,
  77. er.config_label,
  78. er.manifest_url,
  79. er.manifest_checksum_sha256,
  80. er.route_code,
  81. er.payload_jsonb::text
  82. FROM events e
  83. LEFT JOIN event_releases er ON er.id = e.current_release_id
  84. WHERE e.event_public_id = $1
  85. LIMIT 1
  86. `, eventPublicID)
  87. var event Event
  88. err := row.Scan(
  89. &event.ID,
  90. &event.PublicID,
  91. &event.Slug,
  92. &event.DisplayName,
  93. &event.Summary,
  94. &event.Status,
  95. &event.CurrentReleaseID,
  96. &event.CurrentReleasePubID,
  97. &event.ConfigLabel,
  98. &event.ManifestURL,
  99. &event.ManifestChecksum,
  100. &event.RouteCode,
  101. &event.ReleasePayloadJSON,
  102. )
  103. if errors.Is(err, pgx.ErrNoRows) {
  104. return nil, nil
  105. }
  106. if err != nil {
  107. return nil, fmt.Errorf("get event by public id: %w", err)
  108. }
  109. return &event, nil
  110. }
  111. func (s *Store) GetEventByID(ctx context.Context, eventID string) (*Event, error) {
  112. row := s.pool.QueryRow(ctx, `
  113. SELECT
  114. e.id,
  115. e.event_public_id,
  116. e.slug,
  117. e.display_name,
  118. e.summary,
  119. e.status,
  120. e.current_release_id,
  121. er.release_public_id,
  122. er.config_label,
  123. er.manifest_url,
  124. er.manifest_checksum_sha256,
  125. er.route_code,
  126. er.payload_jsonb::text
  127. FROM events e
  128. LEFT JOIN event_releases er ON er.id = e.current_release_id
  129. WHERE e.id = $1
  130. LIMIT 1
  131. `, eventID)
  132. var event Event
  133. err := row.Scan(
  134. &event.ID,
  135. &event.PublicID,
  136. &event.Slug,
  137. &event.DisplayName,
  138. &event.Summary,
  139. &event.Status,
  140. &event.CurrentReleaseID,
  141. &event.CurrentReleasePubID,
  142. &event.ConfigLabel,
  143. &event.ManifestURL,
  144. &event.ManifestChecksum,
  145. &event.RouteCode,
  146. &event.ReleasePayloadJSON,
  147. )
  148. if errors.Is(err, pgx.ErrNoRows) {
  149. return nil, nil
  150. }
  151. if err != nil {
  152. return nil, fmt.Errorf("get event by id: %w", err)
  153. }
  154. return &event, nil
  155. }
  156. func (s *Store) NextEventReleaseNo(ctx context.Context, eventID string) (int, error) {
  157. var next int
  158. if err := s.pool.QueryRow(ctx, `
  159. SELECT COALESCE(MAX(release_no), 0) + 1
  160. FROM event_releases
  161. WHERE event_id = $1
  162. `, eventID).Scan(&next); err != nil {
  163. return 0, fmt.Errorf("next event release no: %w", err)
  164. }
  165. return next, nil
  166. }
  167. type CreateEventReleaseParams struct {
  168. PublicID string
  169. EventID string
  170. ReleaseNo int
  171. ConfigLabel string
  172. ManifestURL string
  173. ManifestChecksum *string
  174. RouteCode *string
  175. BuildID *string
  176. Status string
  177. PayloadJSON string
  178. }
  179. func (s *Store) CreateEventRelease(ctx context.Context, tx Tx, params CreateEventReleaseParams) (*EventRelease, error) {
  180. row := tx.QueryRow(ctx, `
  181. INSERT INTO event_releases (
  182. release_public_id,
  183. event_id,
  184. release_no,
  185. config_label,
  186. manifest_url,
  187. manifest_checksum_sha256,
  188. route_code,
  189. build_id,
  190. status,
  191. payload_jsonb
  192. )
  193. VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10::jsonb)
  194. RETURNING id, release_public_id, event_id, release_no, config_label, manifest_url, manifest_checksum_sha256, route_code, build_id, status, published_at
  195. `, params.PublicID, params.EventID, params.ReleaseNo, params.ConfigLabel, params.ManifestURL, params.ManifestChecksum, params.RouteCode, params.BuildID, params.Status, params.PayloadJSON)
  196. var item EventRelease
  197. if err := row.Scan(
  198. &item.ID,
  199. &item.PublicID,
  200. &item.EventID,
  201. &item.ReleaseNo,
  202. &item.ConfigLabel,
  203. &item.ManifestURL,
  204. &item.ManifestChecksum,
  205. &item.RouteCode,
  206. &item.BuildID,
  207. &item.Status,
  208. &item.PublishedAt,
  209. ); err != nil {
  210. return nil, fmt.Errorf("create event release: %w", err)
  211. }
  212. return &item, nil
  213. }
  214. func (s *Store) SetCurrentEventRelease(ctx context.Context, tx Tx, eventID, releaseID string) error {
  215. if _, err := tx.Exec(ctx, `
  216. UPDATE events
  217. SET current_release_id = $2
  218. WHERE id = $1
  219. `, eventID, releaseID); err != nil {
  220. return fmt.Errorf("set current event release: %w", err)
  221. }
  222. return nil
  223. }
  224. func (s *Store) CreateGameSession(ctx context.Context, tx Tx, params CreateGameSessionParams) (*GameSession, error) {
  225. row := tx.QueryRow(ctx, `
  226. INSERT INTO game_sessions (
  227. session_public_id,
  228. user_id,
  229. event_id,
  230. event_release_id,
  231. device_key,
  232. client_type,
  233. assignment_mode,
  234. variant_id,
  235. variant_name,
  236. route_code,
  237. session_token_hash,
  238. session_token_expires_at
  239. )
  240. VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)
  241. 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
  242. `, params.SessionPublicID, params.UserID, params.EventID, params.EventReleaseID, params.DeviceKey, params.ClientType, params.AssignmentMode, params.VariantID, params.VariantName, params.RouteCode, params.SessionTokenHash, params.SessionTokenExpiresAt)
  243. var session GameSession
  244. err := row.Scan(
  245. &session.ID,
  246. &session.SessionPublicID,
  247. &session.UserID,
  248. &session.EventID,
  249. &session.EventReleaseID,
  250. &session.DeviceKey,
  251. &session.ClientType,
  252. &session.AssignmentMode,
  253. &session.VariantID,
  254. &session.VariantName,
  255. &session.RouteCode,
  256. &session.Status,
  257. &session.SessionTokenExpiresAt,
  258. )
  259. if err != nil {
  260. return nil, fmt.Errorf("create game session: %w", err)
  261. }
  262. return &session, nil
  263. }
  264. func (s *Store) ListEventReleasesByEventID(ctx context.Context, eventID string, limit int) ([]EventRelease, error) {
  265. if limit <= 0 || limit > 100 {
  266. limit = 20
  267. }
  268. rows, err := s.pool.Query(ctx, `
  269. SELECT id, release_public_id, event_id, release_no, config_label, manifest_url, manifest_checksum_sha256, route_code, build_id, status, published_at
  270. FROM event_releases
  271. WHERE event_id = $1
  272. ORDER BY release_no DESC
  273. LIMIT $2
  274. `, eventID, limit)
  275. if err != nil {
  276. return nil, fmt.Errorf("list event releases by event id: %w", err)
  277. }
  278. defer rows.Close()
  279. items := []EventRelease{}
  280. for rows.Next() {
  281. item, err := scanEventReleaseFromRows(rows)
  282. if err != nil {
  283. return nil, err
  284. }
  285. items = append(items, *item)
  286. }
  287. if err := rows.Err(); err != nil {
  288. return nil, fmt.Errorf("iterate event releases by event id: %w", err)
  289. }
  290. return items, nil
  291. }
  292. func (s *Store) GetEventReleaseByPublicID(ctx context.Context, releasePublicID string) (*EventRelease, error) {
  293. row := s.pool.QueryRow(ctx, `
  294. SELECT id, release_public_id, event_id, release_no, config_label, manifest_url, manifest_checksum_sha256, route_code, build_id, status, published_at
  295. FROM event_releases
  296. WHERE release_public_id = $1
  297. LIMIT 1
  298. `, releasePublicID)
  299. var item EventRelease
  300. err := row.Scan(
  301. &item.ID,
  302. &item.PublicID,
  303. &item.EventID,
  304. &item.ReleaseNo,
  305. &item.ConfigLabel,
  306. &item.ManifestURL,
  307. &item.ManifestChecksum,
  308. &item.RouteCode,
  309. &item.BuildID,
  310. &item.Status,
  311. &item.PublishedAt,
  312. )
  313. if errors.Is(err, pgx.ErrNoRows) {
  314. return nil, nil
  315. }
  316. if err != nil {
  317. return nil, fmt.Errorf("get event release by public id: %w", err)
  318. }
  319. return &item, nil
  320. }
  321. func scanEventReleaseFromRows(rows pgx.Rows) (*EventRelease, error) {
  322. var item EventRelease
  323. err := rows.Scan(
  324. &item.ID,
  325. &item.PublicID,
  326. &item.EventID,
  327. &item.ReleaseNo,
  328. &item.ConfigLabel,
  329. &item.ManifestURL,
  330. &item.ManifestChecksum,
  331. &item.RouteCode,
  332. &item.BuildID,
  333. &item.Status,
  334. &item.PublishedAt,
  335. )
  336. if err != nil {
  337. return nil, fmt.Errorf("scan event release row: %w", err)
  338. }
  339. return &item, nil
  340. }