resource_store.go 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660
  1. package postgres
  2. import (
  3. "context"
  4. "encoding/json"
  5. "errors"
  6. "fmt"
  7. "time"
  8. "github.com/jackc/pgx/v5"
  9. )
  10. type ResourceMap struct {
  11. ID string
  12. PublicID string
  13. Code string
  14. Name string
  15. Status string
  16. Description *string
  17. CurrentVersionID *string
  18. CreatedAt time.Time
  19. UpdatedAt time.Time
  20. }
  21. type ResourceMapVersion struct {
  22. ID string
  23. PublicID string
  24. MapID string
  25. VersionCode string
  26. Status string
  27. MapmetaURL string
  28. TilesRootURL string
  29. PublishedAssetRoot *string
  30. BoundsJSON json.RawMessage
  31. MetadataJSON json.RawMessage
  32. CreatedAt time.Time
  33. UpdatedAt time.Time
  34. }
  35. type ResourcePlayfield struct {
  36. ID string
  37. PublicID string
  38. Code string
  39. Name string
  40. Kind string
  41. Status string
  42. Description *string
  43. CurrentVersionID *string
  44. CreatedAt time.Time
  45. UpdatedAt time.Time
  46. }
  47. type ResourcePlayfieldVersion struct {
  48. ID string
  49. PublicID string
  50. PlayfieldID string
  51. VersionCode string
  52. Status string
  53. SourceType string
  54. SourceURL string
  55. PublishedAssetRoot *string
  56. ControlCount *int
  57. BoundsJSON json.RawMessage
  58. MetadataJSON json.RawMessage
  59. CreatedAt time.Time
  60. UpdatedAt time.Time
  61. }
  62. type ResourcePack struct {
  63. ID string
  64. PublicID string
  65. Code string
  66. Name string
  67. Status string
  68. Description *string
  69. CurrentVersionID *string
  70. CreatedAt time.Time
  71. UpdatedAt time.Time
  72. }
  73. type ResourcePackVersion struct {
  74. ID string
  75. PublicID string
  76. ResourcePackID string
  77. VersionCode string
  78. Status string
  79. ContentEntryURL *string
  80. AudioRootURL *string
  81. ThemeProfileCode *string
  82. PublishedAssetRoot *string
  83. MetadataJSON json.RawMessage
  84. CreatedAt time.Time
  85. UpdatedAt time.Time
  86. }
  87. type CreateResourceMapParams struct {
  88. PublicID string
  89. Code string
  90. Name string
  91. Status string
  92. Description *string
  93. }
  94. type CreateResourceMapVersionParams struct {
  95. PublicID string
  96. MapID string
  97. VersionCode string
  98. Status string
  99. MapmetaURL string
  100. TilesRootURL string
  101. PublishedAssetRoot *string
  102. BoundsJSON map[string]any
  103. MetadataJSON map[string]any
  104. }
  105. type CreateResourcePlayfieldParams struct {
  106. PublicID string
  107. Code string
  108. Name string
  109. Kind string
  110. Status string
  111. Description *string
  112. }
  113. type CreateResourcePlayfieldVersionParams struct {
  114. PublicID string
  115. PlayfieldID string
  116. VersionCode string
  117. Status string
  118. SourceType string
  119. SourceURL string
  120. PublishedAssetRoot *string
  121. ControlCount *int
  122. BoundsJSON map[string]any
  123. MetadataJSON map[string]any
  124. }
  125. type CreateResourcePackParams struct {
  126. PublicID string
  127. Code string
  128. Name string
  129. Status string
  130. Description *string
  131. }
  132. type CreateResourcePackVersionParams struct {
  133. PublicID string
  134. ResourcePackID string
  135. VersionCode string
  136. Status string
  137. ContentEntryURL *string
  138. AudioRootURL *string
  139. ThemeProfileCode *string
  140. PublishedAssetRoot *string
  141. MetadataJSON map[string]any
  142. }
  143. func (s *Store) ListResourceMaps(ctx context.Context, limit int) ([]ResourceMap, error) {
  144. if limit <= 0 || limit > 200 {
  145. limit = 50
  146. }
  147. rows, err := s.pool.Query(ctx, `
  148. SELECT id, map_public_id, code, name, status, description, current_version_id, created_at, updated_at
  149. FROM maps
  150. ORDER BY created_at DESC
  151. LIMIT $1
  152. `, limit)
  153. if err != nil {
  154. return nil, fmt.Errorf("list resource maps: %w", err)
  155. }
  156. defer rows.Close()
  157. items := []ResourceMap{}
  158. for rows.Next() {
  159. item, err := scanResourceMapFromRows(rows)
  160. if err != nil {
  161. return nil, err
  162. }
  163. items = append(items, *item)
  164. }
  165. if err := rows.Err(); err != nil {
  166. return nil, fmt.Errorf("iterate resource maps: %w", err)
  167. }
  168. return items, nil
  169. }
  170. func (s *Store) GetResourceMapByPublicID(ctx context.Context, publicID string) (*ResourceMap, error) {
  171. row := s.pool.QueryRow(ctx, `
  172. SELECT id, map_public_id, code, name, status, description, current_version_id, created_at, updated_at
  173. FROM maps
  174. WHERE map_public_id = $1
  175. LIMIT 1
  176. `, publicID)
  177. return scanResourceMap(row)
  178. }
  179. func (s *Store) CreateResourceMap(ctx context.Context, tx Tx, params CreateResourceMapParams) (*ResourceMap, error) {
  180. row := tx.QueryRow(ctx, `
  181. INSERT INTO maps (map_public_id, code, name, status, description)
  182. VALUES ($1, $2, $3, $4, $5)
  183. RETURNING id, map_public_id, code, name, status, description, current_version_id, created_at, updated_at
  184. `, params.PublicID, params.Code, params.Name, params.Status, params.Description)
  185. return scanResourceMap(row)
  186. }
  187. func (s *Store) ListResourceMapVersions(ctx context.Context, mapID string) ([]ResourceMapVersion, error) {
  188. rows, err := s.pool.Query(ctx, `
  189. SELECT id, version_public_id, map_id, version_code, status, mapmeta_url, tiles_root_url, published_asset_root,
  190. bounds_jsonb::text, metadata_jsonb::text, created_at, updated_at
  191. FROM map_versions
  192. WHERE map_id = $1
  193. ORDER BY created_at DESC
  194. `, mapID)
  195. if err != nil {
  196. return nil, fmt.Errorf("list resource map versions: %w", err)
  197. }
  198. defer rows.Close()
  199. items := []ResourceMapVersion{}
  200. for rows.Next() {
  201. item, err := scanResourceMapVersionFromRows(rows)
  202. if err != nil {
  203. return nil, err
  204. }
  205. items = append(items, *item)
  206. }
  207. if err := rows.Err(); err != nil {
  208. return nil, fmt.Errorf("iterate resource map versions: %w", err)
  209. }
  210. return items, nil
  211. }
  212. func (s *Store) GetResourceMapVersionByPublicID(ctx context.Context, mapPublicID, versionPublicID string) (*ResourceMapVersion, error) {
  213. row := s.pool.QueryRow(ctx, `
  214. SELECT mv.id, mv.version_public_id, mv.map_id, mv.version_code, mv.status, mv.mapmeta_url, mv.tiles_root_url, mv.published_asset_root,
  215. mv.bounds_jsonb::text, mv.metadata_jsonb::text, mv.created_at, mv.updated_at
  216. FROM map_versions mv
  217. JOIN maps m ON m.id = mv.map_id
  218. WHERE m.map_public_id = $1
  219. AND mv.version_public_id = $2
  220. LIMIT 1
  221. `, mapPublicID, versionPublicID)
  222. return scanResourceMapVersion(row)
  223. }
  224. func (s *Store) CreateResourceMapVersion(ctx context.Context, tx Tx, params CreateResourceMapVersionParams) (*ResourceMapVersion, error) {
  225. boundsJSON, err := marshalJSONMap(params.BoundsJSON)
  226. if err != nil {
  227. return nil, fmt.Errorf("marshal map bounds: %w", err)
  228. }
  229. metadataJSON, err := marshalJSONMap(params.MetadataJSON)
  230. if err != nil {
  231. return nil, fmt.Errorf("marshal map metadata: %w", err)
  232. }
  233. row := tx.QueryRow(ctx, `
  234. INSERT INTO map_versions (
  235. version_public_id, map_id, version_code, status, mapmeta_url, tiles_root_url,
  236. published_asset_root, bounds_jsonb, metadata_jsonb
  237. )
  238. VALUES ($1, $2, $3, $4, $5, $6, $7, $8::jsonb, $9::jsonb)
  239. RETURNING id, version_public_id, map_id, version_code, status, mapmeta_url, tiles_root_url, published_asset_root,
  240. bounds_jsonb::text, metadata_jsonb::text, created_at, updated_at
  241. `, params.PublicID, params.MapID, params.VersionCode, params.Status, params.MapmetaURL, params.TilesRootURL, params.PublishedAssetRoot, boundsJSON, metadataJSON)
  242. return scanResourceMapVersion(row)
  243. }
  244. func (s *Store) SetResourceMapCurrentVersion(ctx context.Context, tx Tx, mapID, versionID string) error {
  245. _, err := tx.Exec(ctx, `UPDATE maps SET current_version_id = $2 WHERE id = $1`, mapID, versionID)
  246. if err != nil {
  247. return fmt.Errorf("set resource map current version: %w", err)
  248. }
  249. return nil
  250. }
  251. func (s *Store) ListResourcePlayfields(ctx context.Context, limit int) ([]ResourcePlayfield, error) {
  252. if limit <= 0 || limit > 200 {
  253. limit = 50
  254. }
  255. rows, err := s.pool.Query(ctx, `
  256. SELECT id, playfield_public_id, code, name, kind, status, description, current_version_id, created_at, updated_at
  257. FROM playfields
  258. ORDER BY created_at DESC
  259. LIMIT $1
  260. `, limit)
  261. if err != nil {
  262. return nil, fmt.Errorf("list resource playfields: %w", err)
  263. }
  264. defer rows.Close()
  265. items := []ResourcePlayfield{}
  266. for rows.Next() {
  267. item, err := scanResourcePlayfieldFromRows(rows)
  268. if err != nil {
  269. return nil, err
  270. }
  271. items = append(items, *item)
  272. }
  273. if err := rows.Err(); err != nil {
  274. return nil, fmt.Errorf("iterate resource playfields: %w", err)
  275. }
  276. return items, nil
  277. }
  278. func (s *Store) GetResourcePlayfieldByPublicID(ctx context.Context, publicID string) (*ResourcePlayfield, error) {
  279. row := s.pool.QueryRow(ctx, `
  280. SELECT id, playfield_public_id, code, name, kind, status, description, current_version_id, created_at, updated_at
  281. FROM playfields
  282. WHERE playfield_public_id = $1
  283. LIMIT 1
  284. `, publicID)
  285. return scanResourcePlayfield(row)
  286. }
  287. func (s *Store) CreateResourcePlayfield(ctx context.Context, tx Tx, params CreateResourcePlayfieldParams) (*ResourcePlayfield, error) {
  288. row := tx.QueryRow(ctx, `
  289. INSERT INTO playfields (playfield_public_id, code, name, kind, status, description)
  290. VALUES ($1, $2, $3, $4, $5, $6)
  291. RETURNING id, playfield_public_id, code, name, kind, status, description, current_version_id, created_at, updated_at
  292. `, params.PublicID, params.Code, params.Name, params.Kind, params.Status, params.Description)
  293. return scanResourcePlayfield(row)
  294. }
  295. func (s *Store) ListResourcePlayfieldVersions(ctx context.Context, playfieldID string) ([]ResourcePlayfieldVersion, error) {
  296. rows, err := s.pool.Query(ctx, `
  297. SELECT id, version_public_id, playfield_id, version_code, status, source_type, source_url, published_asset_root,
  298. control_count, bounds_jsonb::text, metadata_jsonb::text, created_at, updated_at
  299. FROM playfield_versions
  300. WHERE playfield_id = $1
  301. ORDER BY created_at DESC
  302. `, playfieldID)
  303. if err != nil {
  304. return nil, fmt.Errorf("list resource playfield versions: %w", err)
  305. }
  306. defer rows.Close()
  307. items := []ResourcePlayfieldVersion{}
  308. for rows.Next() {
  309. item, err := scanResourcePlayfieldVersionFromRows(rows)
  310. if err != nil {
  311. return nil, err
  312. }
  313. items = append(items, *item)
  314. }
  315. if err := rows.Err(); err != nil {
  316. return nil, fmt.Errorf("iterate resource playfield versions: %w", err)
  317. }
  318. return items, nil
  319. }
  320. func (s *Store) GetResourcePlayfieldVersionByPublicID(ctx context.Context, playfieldPublicID, versionPublicID string) (*ResourcePlayfieldVersion, error) {
  321. row := s.pool.QueryRow(ctx, `
  322. SELECT pv.id, pv.version_public_id, pv.playfield_id, pv.version_code, pv.status, pv.source_type, pv.source_url, pv.published_asset_root,
  323. pv.control_count, pv.bounds_jsonb::text, pv.metadata_jsonb::text, pv.created_at, pv.updated_at
  324. FROM playfield_versions pv
  325. JOIN playfields p ON p.id = pv.playfield_id
  326. WHERE p.playfield_public_id = $1
  327. AND pv.version_public_id = $2
  328. LIMIT 1
  329. `, playfieldPublicID, versionPublicID)
  330. return scanResourcePlayfieldVersion(row)
  331. }
  332. func (s *Store) CreateResourcePlayfieldVersion(ctx context.Context, tx Tx, params CreateResourcePlayfieldVersionParams) (*ResourcePlayfieldVersion, error) {
  333. boundsJSON, err := marshalJSONMap(params.BoundsJSON)
  334. if err != nil {
  335. return nil, fmt.Errorf("marshal playfield bounds: %w", err)
  336. }
  337. metadataJSON, err := marshalJSONMap(params.MetadataJSON)
  338. if err != nil {
  339. return nil, fmt.Errorf("marshal playfield metadata: %w", err)
  340. }
  341. row := tx.QueryRow(ctx, `
  342. INSERT INTO playfield_versions (
  343. version_public_id, playfield_id, version_code, status, source_type, source_url,
  344. published_asset_root, control_count, bounds_jsonb, metadata_jsonb
  345. )
  346. VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9::jsonb, $10::jsonb)
  347. RETURNING id, version_public_id, playfield_id, version_code, status, source_type, source_url, published_asset_root,
  348. control_count, bounds_jsonb::text, metadata_jsonb::text, created_at, updated_at
  349. `, params.PublicID, params.PlayfieldID, params.VersionCode, params.Status, params.SourceType, params.SourceURL, params.PublishedAssetRoot, params.ControlCount, boundsJSON, metadataJSON)
  350. return scanResourcePlayfieldVersion(row)
  351. }
  352. func (s *Store) SetResourcePlayfieldCurrentVersion(ctx context.Context, tx Tx, playfieldID, versionID string) error {
  353. _, err := tx.Exec(ctx, `UPDATE playfields SET current_version_id = $2 WHERE id = $1`, playfieldID, versionID)
  354. if err != nil {
  355. return fmt.Errorf("set resource playfield current version: %w", err)
  356. }
  357. return nil
  358. }
  359. func (s *Store) ListResourcePacks(ctx context.Context, limit int) ([]ResourcePack, error) {
  360. if limit <= 0 || limit > 200 {
  361. limit = 50
  362. }
  363. rows, err := s.pool.Query(ctx, `
  364. SELECT id, resource_pack_public_id, code, name, status, description, current_version_id, created_at, updated_at
  365. FROM resource_packs
  366. ORDER BY created_at DESC
  367. LIMIT $1
  368. `, limit)
  369. if err != nil {
  370. return nil, fmt.Errorf("list resource packs: %w", err)
  371. }
  372. defer rows.Close()
  373. items := []ResourcePack{}
  374. for rows.Next() {
  375. item, err := scanResourcePackFromRows(rows)
  376. if err != nil {
  377. return nil, err
  378. }
  379. items = append(items, *item)
  380. }
  381. if err := rows.Err(); err != nil {
  382. return nil, fmt.Errorf("iterate resource packs: %w", err)
  383. }
  384. return items, nil
  385. }
  386. func (s *Store) GetResourcePackByPublicID(ctx context.Context, publicID string) (*ResourcePack, error) {
  387. row := s.pool.QueryRow(ctx, `
  388. SELECT id, resource_pack_public_id, code, name, status, description, current_version_id, created_at, updated_at
  389. FROM resource_packs
  390. WHERE resource_pack_public_id = $1
  391. LIMIT 1
  392. `, publicID)
  393. return scanResourcePack(row)
  394. }
  395. func (s *Store) CreateResourcePack(ctx context.Context, tx Tx, params CreateResourcePackParams) (*ResourcePack, error) {
  396. row := tx.QueryRow(ctx, `
  397. INSERT INTO resource_packs (resource_pack_public_id, code, name, status, description)
  398. VALUES ($1, $2, $3, $4, $5)
  399. RETURNING id, resource_pack_public_id, code, name, status, description, current_version_id, created_at, updated_at
  400. `, params.PublicID, params.Code, params.Name, params.Status, params.Description)
  401. return scanResourcePack(row)
  402. }
  403. func (s *Store) ListResourcePackVersions(ctx context.Context, resourcePackID string) ([]ResourcePackVersion, error) {
  404. rows, err := s.pool.Query(ctx, `
  405. SELECT id, version_public_id, resource_pack_id, version_code, status, content_entry_url, audio_root_url,
  406. theme_profile_code, published_asset_root, metadata_jsonb::text, created_at, updated_at
  407. FROM resource_pack_versions
  408. WHERE resource_pack_id = $1
  409. ORDER BY created_at DESC
  410. `, resourcePackID)
  411. if err != nil {
  412. return nil, fmt.Errorf("list resource pack versions: %w", err)
  413. }
  414. defer rows.Close()
  415. items := []ResourcePackVersion{}
  416. for rows.Next() {
  417. item, err := scanResourcePackVersionFromRows(rows)
  418. if err != nil {
  419. return nil, err
  420. }
  421. items = append(items, *item)
  422. }
  423. if err := rows.Err(); err != nil {
  424. return nil, fmt.Errorf("iterate resource pack versions: %w", err)
  425. }
  426. return items, nil
  427. }
  428. func (s *Store) GetResourcePackVersionByPublicID(ctx context.Context, resourcePackPublicID, versionPublicID string) (*ResourcePackVersion, error) {
  429. row := s.pool.QueryRow(ctx, `
  430. SELECT pv.id, pv.version_public_id, pv.resource_pack_id, pv.version_code, pv.status, pv.content_entry_url, pv.audio_root_url,
  431. pv.theme_profile_code, pv.published_asset_root, pv.metadata_jsonb::text, pv.created_at, pv.updated_at
  432. FROM resource_pack_versions pv
  433. JOIN resource_packs rp ON rp.id = pv.resource_pack_id
  434. WHERE rp.resource_pack_public_id = $1
  435. AND pv.version_public_id = $2
  436. LIMIT 1
  437. `, resourcePackPublicID, versionPublicID)
  438. return scanResourcePackVersion(row)
  439. }
  440. func (s *Store) CreateResourcePackVersion(ctx context.Context, tx Tx, params CreateResourcePackVersionParams) (*ResourcePackVersion, error) {
  441. metadataJSON, err := marshalJSONMap(params.MetadataJSON)
  442. if err != nil {
  443. return nil, fmt.Errorf("marshal resource pack metadata: %w", err)
  444. }
  445. row := tx.QueryRow(ctx, `
  446. INSERT INTO resource_pack_versions (
  447. version_public_id, resource_pack_id, version_code, status, content_entry_url,
  448. audio_root_url, theme_profile_code, published_asset_root, metadata_jsonb
  449. )
  450. VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9::jsonb)
  451. RETURNING id, version_public_id, resource_pack_id, version_code, status, content_entry_url, audio_root_url,
  452. theme_profile_code, published_asset_root, metadata_jsonb::text, created_at, updated_at
  453. `, params.PublicID, params.ResourcePackID, params.VersionCode, params.Status, params.ContentEntryURL, params.AudioRootURL, params.ThemeProfileCode, params.PublishedAssetRoot, metadataJSON)
  454. return scanResourcePackVersion(row)
  455. }
  456. func (s *Store) SetResourcePackCurrentVersion(ctx context.Context, tx Tx, resourcePackID, versionID string) error {
  457. _, err := tx.Exec(ctx, `UPDATE resource_packs SET current_version_id = $2 WHERE id = $1`, resourcePackID, versionID)
  458. if err != nil {
  459. return fmt.Errorf("set resource pack current version: %w", err)
  460. }
  461. return nil
  462. }
  463. func scanResourceMap(row pgx.Row) (*ResourceMap, error) {
  464. var item ResourceMap
  465. err := row.Scan(&item.ID, &item.PublicID, &item.Code, &item.Name, &item.Status, &item.Description, &item.CurrentVersionID, &item.CreatedAt, &item.UpdatedAt)
  466. if errors.Is(err, pgx.ErrNoRows) {
  467. return nil, nil
  468. }
  469. if err != nil {
  470. return nil, fmt.Errorf("scan resource map: %w", err)
  471. }
  472. return &item, nil
  473. }
  474. func scanResourceMapFromRows(rows pgx.Rows) (*ResourceMap, error) {
  475. var item ResourceMap
  476. err := rows.Scan(&item.ID, &item.PublicID, &item.Code, &item.Name, &item.Status, &item.Description, &item.CurrentVersionID, &item.CreatedAt, &item.UpdatedAt)
  477. if err != nil {
  478. return nil, fmt.Errorf("scan resource map row: %w", err)
  479. }
  480. return &item, nil
  481. }
  482. func scanResourceMapVersion(row pgx.Row) (*ResourceMapVersion, error) {
  483. var item ResourceMapVersion
  484. var boundsJSON string
  485. var metadataJSON string
  486. err := row.Scan(&item.ID, &item.PublicID, &item.MapID, &item.VersionCode, &item.Status, &item.MapmetaURL, &item.TilesRootURL, &item.PublishedAssetRoot, &boundsJSON, &metadataJSON, &item.CreatedAt, &item.UpdatedAt)
  487. if errors.Is(err, pgx.ErrNoRows) {
  488. return nil, nil
  489. }
  490. if err != nil {
  491. return nil, fmt.Errorf("scan resource map version: %w", err)
  492. }
  493. item.BoundsJSON = json.RawMessage(boundsJSON)
  494. item.MetadataJSON = json.RawMessage(metadataJSON)
  495. return &item, nil
  496. }
  497. func scanResourceMapVersionFromRows(rows pgx.Rows) (*ResourceMapVersion, error) {
  498. var item ResourceMapVersion
  499. var boundsJSON string
  500. var metadataJSON string
  501. err := rows.Scan(&item.ID, &item.PublicID, &item.MapID, &item.VersionCode, &item.Status, &item.MapmetaURL, &item.TilesRootURL, &item.PublishedAssetRoot, &boundsJSON, &metadataJSON, &item.CreatedAt, &item.UpdatedAt)
  502. if err != nil {
  503. return nil, fmt.Errorf("scan resource map version row: %w", err)
  504. }
  505. item.BoundsJSON = json.RawMessage(boundsJSON)
  506. item.MetadataJSON = json.RawMessage(metadataJSON)
  507. return &item, nil
  508. }
  509. func scanResourcePlayfield(row pgx.Row) (*ResourcePlayfield, error) {
  510. var item ResourcePlayfield
  511. err := row.Scan(&item.ID, &item.PublicID, &item.Code, &item.Name, &item.Kind, &item.Status, &item.Description, &item.CurrentVersionID, &item.CreatedAt, &item.UpdatedAt)
  512. if errors.Is(err, pgx.ErrNoRows) {
  513. return nil, nil
  514. }
  515. if err != nil {
  516. return nil, fmt.Errorf("scan resource playfield: %w", err)
  517. }
  518. return &item, nil
  519. }
  520. func scanResourcePlayfieldFromRows(rows pgx.Rows) (*ResourcePlayfield, error) {
  521. var item ResourcePlayfield
  522. err := rows.Scan(&item.ID, &item.PublicID, &item.Code, &item.Name, &item.Kind, &item.Status, &item.Description, &item.CurrentVersionID, &item.CreatedAt, &item.UpdatedAt)
  523. if err != nil {
  524. return nil, fmt.Errorf("scan resource playfield row: %w", err)
  525. }
  526. return &item, nil
  527. }
  528. func scanResourcePlayfieldVersion(row pgx.Row) (*ResourcePlayfieldVersion, error) {
  529. var item ResourcePlayfieldVersion
  530. var boundsJSON string
  531. var metadataJSON string
  532. err := row.Scan(&item.ID, &item.PublicID, &item.PlayfieldID, &item.VersionCode, &item.Status, &item.SourceType, &item.SourceURL, &item.PublishedAssetRoot, &item.ControlCount, &boundsJSON, &metadataJSON, &item.CreatedAt, &item.UpdatedAt)
  533. if errors.Is(err, pgx.ErrNoRows) {
  534. return nil, nil
  535. }
  536. if err != nil {
  537. return nil, fmt.Errorf("scan resource playfield version: %w", err)
  538. }
  539. item.BoundsJSON = json.RawMessage(boundsJSON)
  540. item.MetadataJSON = json.RawMessage(metadataJSON)
  541. return &item, nil
  542. }
  543. func scanResourcePlayfieldVersionFromRows(rows pgx.Rows) (*ResourcePlayfieldVersion, error) {
  544. var item ResourcePlayfieldVersion
  545. var boundsJSON string
  546. var metadataJSON string
  547. err := rows.Scan(&item.ID, &item.PublicID, &item.PlayfieldID, &item.VersionCode, &item.Status, &item.SourceType, &item.SourceURL, &item.PublishedAssetRoot, &item.ControlCount, &boundsJSON, &metadataJSON, &item.CreatedAt, &item.UpdatedAt)
  548. if err != nil {
  549. return nil, fmt.Errorf("scan resource playfield version row: %w", err)
  550. }
  551. item.BoundsJSON = json.RawMessage(boundsJSON)
  552. item.MetadataJSON = json.RawMessage(metadataJSON)
  553. return &item, nil
  554. }
  555. func scanResourcePack(row pgx.Row) (*ResourcePack, error) {
  556. var item ResourcePack
  557. err := row.Scan(&item.ID, &item.PublicID, &item.Code, &item.Name, &item.Status, &item.Description, &item.CurrentVersionID, &item.CreatedAt, &item.UpdatedAt)
  558. if errors.Is(err, pgx.ErrNoRows) {
  559. return nil, nil
  560. }
  561. if err != nil {
  562. return nil, fmt.Errorf("scan resource pack: %w", err)
  563. }
  564. return &item, nil
  565. }
  566. func scanResourcePackFromRows(rows pgx.Rows) (*ResourcePack, error) {
  567. var item ResourcePack
  568. err := rows.Scan(&item.ID, &item.PublicID, &item.Code, &item.Name, &item.Status, &item.Description, &item.CurrentVersionID, &item.CreatedAt, &item.UpdatedAt)
  569. if err != nil {
  570. return nil, fmt.Errorf("scan resource pack row: %w", err)
  571. }
  572. return &item, nil
  573. }
  574. func scanResourcePackVersion(row pgx.Row) (*ResourcePackVersion, error) {
  575. var item ResourcePackVersion
  576. var metadataJSON string
  577. err := row.Scan(&item.ID, &item.PublicID, &item.ResourcePackID, &item.VersionCode, &item.Status, &item.ContentEntryURL, &item.AudioRootURL, &item.ThemeProfileCode, &item.PublishedAssetRoot, &metadataJSON, &item.CreatedAt, &item.UpdatedAt)
  578. if errors.Is(err, pgx.ErrNoRows) {
  579. return nil, nil
  580. }
  581. if err != nil {
  582. return nil, fmt.Errorf("scan resource pack version: %w", err)
  583. }
  584. item.MetadataJSON = json.RawMessage(metadataJSON)
  585. return &item, nil
  586. }
  587. func scanResourcePackVersionFromRows(rows pgx.Rows) (*ResourcePackVersion, error) {
  588. var item ResourcePackVersion
  589. var metadataJSON string
  590. err := rows.Scan(&item.ID, &item.PublicID, &item.ResourcePackID, &item.VersionCode, &item.Status, &item.ContentEntryURL, &item.AudioRootURL, &item.ThemeProfileCode, &item.PublishedAssetRoot, &metadataJSON, &item.CreatedAt, &item.UpdatedAt)
  591. if err != nil {
  592. return nil, fmt.Errorf("scan resource pack version row: %w", err)
  593. }
  594. item.MetadataJSON = json.RawMessage(metadataJSON)
  595. return &item, nil
  596. }
  597. func marshalJSONMap(value map[string]any) (string, error) {
  598. if value == nil {
  599. value = map[string]any{}
  600. }
  601. raw, err := json.Marshal(value)
  602. if err != nil {
  603. return "", err
  604. }
  605. return string(raw), nil
  606. }