event_store.go 8.3 KB

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