|
|
@@ -0,0 +1,660 @@
|
|
|
+package postgres
|
|
|
+
|
|
|
+import (
|
|
|
+ "context"
|
|
|
+ "encoding/json"
|
|
|
+ "errors"
|
|
|
+ "fmt"
|
|
|
+ "time"
|
|
|
+
|
|
|
+ "github.com/jackc/pgx/v5"
|
|
|
+)
|
|
|
+
|
|
|
+type ResourceMap struct {
|
|
|
+ ID string
|
|
|
+ PublicID string
|
|
|
+ Code string
|
|
|
+ Name string
|
|
|
+ Status string
|
|
|
+ Description *string
|
|
|
+ CurrentVersionID *string
|
|
|
+ CreatedAt time.Time
|
|
|
+ UpdatedAt time.Time
|
|
|
+}
|
|
|
+
|
|
|
+type ResourceMapVersion struct {
|
|
|
+ ID string
|
|
|
+ PublicID string
|
|
|
+ MapID string
|
|
|
+ VersionCode string
|
|
|
+ Status string
|
|
|
+ MapmetaURL string
|
|
|
+ TilesRootURL string
|
|
|
+ PublishedAssetRoot *string
|
|
|
+ BoundsJSON json.RawMessage
|
|
|
+ MetadataJSON json.RawMessage
|
|
|
+ CreatedAt time.Time
|
|
|
+ UpdatedAt time.Time
|
|
|
+}
|
|
|
+
|
|
|
+type ResourcePlayfield struct {
|
|
|
+ ID string
|
|
|
+ PublicID string
|
|
|
+ Code string
|
|
|
+ Name string
|
|
|
+ Kind string
|
|
|
+ Status string
|
|
|
+ Description *string
|
|
|
+ CurrentVersionID *string
|
|
|
+ CreatedAt time.Time
|
|
|
+ UpdatedAt time.Time
|
|
|
+}
|
|
|
+
|
|
|
+type ResourcePlayfieldVersion struct {
|
|
|
+ ID string
|
|
|
+ PublicID string
|
|
|
+ PlayfieldID string
|
|
|
+ VersionCode string
|
|
|
+ Status string
|
|
|
+ SourceType string
|
|
|
+ SourceURL string
|
|
|
+ PublishedAssetRoot *string
|
|
|
+ ControlCount *int
|
|
|
+ BoundsJSON json.RawMessage
|
|
|
+ MetadataJSON json.RawMessage
|
|
|
+ CreatedAt time.Time
|
|
|
+ UpdatedAt time.Time
|
|
|
+}
|
|
|
+
|
|
|
+type ResourcePack struct {
|
|
|
+ ID string
|
|
|
+ PublicID string
|
|
|
+ Code string
|
|
|
+ Name string
|
|
|
+ Status string
|
|
|
+ Description *string
|
|
|
+ CurrentVersionID *string
|
|
|
+ CreatedAt time.Time
|
|
|
+ UpdatedAt time.Time
|
|
|
+}
|
|
|
+
|
|
|
+type ResourcePackVersion struct {
|
|
|
+ ID string
|
|
|
+ PublicID string
|
|
|
+ ResourcePackID string
|
|
|
+ VersionCode string
|
|
|
+ Status string
|
|
|
+ ContentEntryURL *string
|
|
|
+ AudioRootURL *string
|
|
|
+ ThemeProfileCode *string
|
|
|
+ PublishedAssetRoot *string
|
|
|
+ MetadataJSON json.RawMessage
|
|
|
+ CreatedAt time.Time
|
|
|
+ UpdatedAt time.Time
|
|
|
+}
|
|
|
+
|
|
|
+type CreateResourceMapParams struct {
|
|
|
+ PublicID string
|
|
|
+ Code string
|
|
|
+ Name string
|
|
|
+ Status string
|
|
|
+ Description *string
|
|
|
+}
|
|
|
+
|
|
|
+type CreateResourceMapVersionParams struct {
|
|
|
+ PublicID string
|
|
|
+ MapID string
|
|
|
+ VersionCode string
|
|
|
+ Status string
|
|
|
+ MapmetaURL string
|
|
|
+ TilesRootURL string
|
|
|
+ PublishedAssetRoot *string
|
|
|
+ BoundsJSON map[string]any
|
|
|
+ MetadataJSON map[string]any
|
|
|
+}
|
|
|
+
|
|
|
+type CreateResourcePlayfieldParams struct {
|
|
|
+ PublicID string
|
|
|
+ Code string
|
|
|
+ Name string
|
|
|
+ Kind string
|
|
|
+ Status string
|
|
|
+ Description *string
|
|
|
+}
|
|
|
+
|
|
|
+type CreateResourcePlayfieldVersionParams struct {
|
|
|
+ PublicID string
|
|
|
+ PlayfieldID string
|
|
|
+ VersionCode string
|
|
|
+ Status string
|
|
|
+ SourceType string
|
|
|
+ SourceURL string
|
|
|
+ PublishedAssetRoot *string
|
|
|
+ ControlCount *int
|
|
|
+ BoundsJSON map[string]any
|
|
|
+ MetadataJSON map[string]any
|
|
|
+}
|
|
|
+
|
|
|
+type CreateResourcePackParams struct {
|
|
|
+ PublicID string
|
|
|
+ Code string
|
|
|
+ Name string
|
|
|
+ Status string
|
|
|
+ Description *string
|
|
|
+}
|
|
|
+
|
|
|
+type CreateResourcePackVersionParams struct {
|
|
|
+ PublicID string
|
|
|
+ ResourcePackID string
|
|
|
+ VersionCode string
|
|
|
+ Status string
|
|
|
+ ContentEntryURL *string
|
|
|
+ AudioRootURL *string
|
|
|
+ ThemeProfileCode *string
|
|
|
+ PublishedAssetRoot *string
|
|
|
+ MetadataJSON map[string]any
|
|
|
+}
|
|
|
+
|
|
|
+func (s *Store) ListResourceMaps(ctx context.Context, limit int) ([]ResourceMap, error) {
|
|
|
+ if limit <= 0 || limit > 200 {
|
|
|
+ limit = 50
|
|
|
+ }
|
|
|
+ rows, err := s.pool.Query(ctx, `
|
|
|
+ SELECT id, map_public_id, code, name, status, description, current_version_id, created_at, updated_at
|
|
|
+ FROM maps
|
|
|
+ ORDER BY created_at DESC
|
|
|
+ LIMIT $1
|
|
|
+ `, limit)
|
|
|
+ if err != nil {
|
|
|
+ return nil, fmt.Errorf("list resource maps: %w", err)
|
|
|
+ }
|
|
|
+ defer rows.Close()
|
|
|
+
|
|
|
+ items := []ResourceMap{}
|
|
|
+ for rows.Next() {
|
|
|
+ item, err := scanResourceMapFromRows(rows)
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ items = append(items, *item)
|
|
|
+ }
|
|
|
+ if err := rows.Err(); err != nil {
|
|
|
+ return nil, fmt.Errorf("iterate resource maps: %w", err)
|
|
|
+ }
|
|
|
+ return items, nil
|
|
|
+}
|
|
|
+
|
|
|
+func (s *Store) GetResourceMapByPublicID(ctx context.Context, publicID string) (*ResourceMap, error) {
|
|
|
+ row := s.pool.QueryRow(ctx, `
|
|
|
+ SELECT id, map_public_id, code, name, status, description, current_version_id, created_at, updated_at
|
|
|
+ FROM maps
|
|
|
+ WHERE map_public_id = $1
|
|
|
+ LIMIT 1
|
|
|
+ `, publicID)
|
|
|
+ return scanResourceMap(row)
|
|
|
+}
|
|
|
+
|
|
|
+func (s *Store) CreateResourceMap(ctx context.Context, tx Tx, params CreateResourceMapParams) (*ResourceMap, error) {
|
|
|
+ row := tx.QueryRow(ctx, `
|
|
|
+ INSERT INTO maps (map_public_id, code, name, status, description)
|
|
|
+ VALUES ($1, $2, $3, $4, $5)
|
|
|
+ RETURNING id, map_public_id, code, name, status, description, current_version_id, created_at, updated_at
|
|
|
+ `, params.PublicID, params.Code, params.Name, params.Status, params.Description)
|
|
|
+ return scanResourceMap(row)
|
|
|
+}
|
|
|
+
|
|
|
+func (s *Store) ListResourceMapVersions(ctx context.Context, mapID string) ([]ResourceMapVersion, error) {
|
|
|
+ rows, err := s.pool.Query(ctx, `
|
|
|
+ SELECT id, version_public_id, map_id, version_code, status, mapmeta_url, tiles_root_url, published_asset_root,
|
|
|
+ bounds_jsonb::text, metadata_jsonb::text, created_at, updated_at
|
|
|
+ FROM map_versions
|
|
|
+ WHERE map_id = $1
|
|
|
+ ORDER BY created_at DESC
|
|
|
+ `, mapID)
|
|
|
+ if err != nil {
|
|
|
+ return nil, fmt.Errorf("list resource map versions: %w", err)
|
|
|
+ }
|
|
|
+ defer rows.Close()
|
|
|
+
|
|
|
+ items := []ResourceMapVersion{}
|
|
|
+ for rows.Next() {
|
|
|
+ item, err := scanResourceMapVersionFromRows(rows)
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ items = append(items, *item)
|
|
|
+ }
|
|
|
+ if err := rows.Err(); err != nil {
|
|
|
+ return nil, fmt.Errorf("iterate resource map versions: %w", err)
|
|
|
+ }
|
|
|
+ return items, nil
|
|
|
+}
|
|
|
+
|
|
|
+func (s *Store) GetResourceMapVersionByPublicID(ctx context.Context, mapPublicID, versionPublicID string) (*ResourceMapVersion, error) {
|
|
|
+ row := s.pool.QueryRow(ctx, `
|
|
|
+ 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,
|
|
|
+ mv.bounds_jsonb::text, mv.metadata_jsonb::text, mv.created_at, mv.updated_at
|
|
|
+ FROM map_versions mv
|
|
|
+ JOIN maps m ON m.id = mv.map_id
|
|
|
+ WHERE m.map_public_id = $1
|
|
|
+ AND mv.version_public_id = $2
|
|
|
+ LIMIT 1
|
|
|
+ `, mapPublicID, versionPublicID)
|
|
|
+ return scanResourceMapVersion(row)
|
|
|
+}
|
|
|
+
|
|
|
+func (s *Store) CreateResourceMapVersion(ctx context.Context, tx Tx, params CreateResourceMapVersionParams) (*ResourceMapVersion, error) {
|
|
|
+ boundsJSON, err := marshalJSONMap(params.BoundsJSON)
|
|
|
+ if err != nil {
|
|
|
+ return nil, fmt.Errorf("marshal map bounds: %w", err)
|
|
|
+ }
|
|
|
+ metadataJSON, err := marshalJSONMap(params.MetadataJSON)
|
|
|
+ if err != nil {
|
|
|
+ return nil, fmt.Errorf("marshal map metadata: %w", err)
|
|
|
+ }
|
|
|
+ row := tx.QueryRow(ctx, `
|
|
|
+ INSERT INTO map_versions (
|
|
|
+ version_public_id, map_id, version_code, status, mapmeta_url, tiles_root_url,
|
|
|
+ published_asset_root, bounds_jsonb, metadata_jsonb
|
|
|
+ )
|
|
|
+ VALUES ($1, $2, $3, $4, $5, $6, $7, $8::jsonb, $9::jsonb)
|
|
|
+ RETURNING id, version_public_id, map_id, version_code, status, mapmeta_url, tiles_root_url, published_asset_root,
|
|
|
+ bounds_jsonb::text, metadata_jsonb::text, created_at, updated_at
|
|
|
+ `, params.PublicID, params.MapID, params.VersionCode, params.Status, params.MapmetaURL, params.TilesRootURL, params.PublishedAssetRoot, boundsJSON, metadataJSON)
|
|
|
+ return scanResourceMapVersion(row)
|
|
|
+}
|
|
|
+
|
|
|
+func (s *Store) SetResourceMapCurrentVersion(ctx context.Context, tx Tx, mapID, versionID string) error {
|
|
|
+ _, err := tx.Exec(ctx, `UPDATE maps SET current_version_id = $2 WHERE id = $1`, mapID, versionID)
|
|
|
+ if err != nil {
|
|
|
+ return fmt.Errorf("set resource map current version: %w", err)
|
|
|
+ }
|
|
|
+ return nil
|
|
|
+}
|
|
|
+
|
|
|
+func (s *Store) ListResourcePlayfields(ctx context.Context, limit int) ([]ResourcePlayfield, error) {
|
|
|
+ if limit <= 0 || limit > 200 {
|
|
|
+ limit = 50
|
|
|
+ }
|
|
|
+ rows, err := s.pool.Query(ctx, `
|
|
|
+ SELECT id, playfield_public_id, code, name, kind, status, description, current_version_id, created_at, updated_at
|
|
|
+ FROM playfields
|
|
|
+ ORDER BY created_at DESC
|
|
|
+ LIMIT $1
|
|
|
+ `, limit)
|
|
|
+ if err != nil {
|
|
|
+ return nil, fmt.Errorf("list resource playfields: %w", err)
|
|
|
+ }
|
|
|
+ defer rows.Close()
|
|
|
+
|
|
|
+ items := []ResourcePlayfield{}
|
|
|
+ for rows.Next() {
|
|
|
+ item, err := scanResourcePlayfieldFromRows(rows)
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ items = append(items, *item)
|
|
|
+ }
|
|
|
+ if err := rows.Err(); err != nil {
|
|
|
+ return nil, fmt.Errorf("iterate resource playfields: %w", err)
|
|
|
+ }
|
|
|
+ return items, nil
|
|
|
+}
|
|
|
+
|
|
|
+func (s *Store) GetResourcePlayfieldByPublicID(ctx context.Context, publicID string) (*ResourcePlayfield, error) {
|
|
|
+ row := s.pool.QueryRow(ctx, `
|
|
|
+ SELECT id, playfield_public_id, code, name, kind, status, description, current_version_id, created_at, updated_at
|
|
|
+ FROM playfields
|
|
|
+ WHERE playfield_public_id = $1
|
|
|
+ LIMIT 1
|
|
|
+ `, publicID)
|
|
|
+ return scanResourcePlayfield(row)
|
|
|
+}
|
|
|
+
|
|
|
+func (s *Store) CreateResourcePlayfield(ctx context.Context, tx Tx, params CreateResourcePlayfieldParams) (*ResourcePlayfield, error) {
|
|
|
+ row := tx.QueryRow(ctx, `
|
|
|
+ INSERT INTO playfields (playfield_public_id, code, name, kind, status, description)
|
|
|
+ VALUES ($1, $2, $3, $4, $5, $6)
|
|
|
+ RETURNING id, playfield_public_id, code, name, kind, status, description, current_version_id, created_at, updated_at
|
|
|
+ `, params.PublicID, params.Code, params.Name, params.Kind, params.Status, params.Description)
|
|
|
+ return scanResourcePlayfield(row)
|
|
|
+}
|
|
|
+
|
|
|
+func (s *Store) ListResourcePlayfieldVersions(ctx context.Context, playfieldID string) ([]ResourcePlayfieldVersion, error) {
|
|
|
+ rows, err := s.pool.Query(ctx, `
|
|
|
+ SELECT id, version_public_id, playfield_id, version_code, status, source_type, source_url, published_asset_root,
|
|
|
+ control_count, bounds_jsonb::text, metadata_jsonb::text, created_at, updated_at
|
|
|
+ FROM playfield_versions
|
|
|
+ WHERE playfield_id = $1
|
|
|
+ ORDER BY created_at DESC
|
|
|
+ `, playfieldID)
|
|
|
+ if err != nil {
|
|
|
+ return nil, fmt.Errorf("list resource playfield versions: %w", err)
|
|
|
+ }
|
|
|
+ defer rows.Close()
|
|
|
+
|
|
|
+ items := []ResourcePlayfieldVersion{}
|
|
|
+ for rows.Next() {
|
|
|
+ item, err := scanResourcePlayfieldVersionFromRows(rows)
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ items = append(items, *item)
|
|
|
+ }
|
|
|
+ if err := rows.Err(); err != nil {
|
|
|
+ return nil, fmt.Errorf("iterate resource playfield versions: %w", err)
|
|
|
+ }
|
|
|
+ return items, nil
|
|
|
+}
|
|
|
+
|
|
|
+func (s *Store) GetResourcePlayfieldVersionByPublicID(ctx context.Context, playfieldPublicID, versionPublicID string) (*ResourcePlayfieldVersion, error) {
|
|
|
+ row := s.pool.QueryRow(ctx, `
|
|
|
+ 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,
|
|
|
+ pv.control_count, pv.bounds_jsonb::text, pv.metadata_jsonb::text, pv.created_at, pv.updated_at
|
|
|
+ FROM playfield_versions pv
|
|
|
+ JOIN playfields p ON p.id = pv.playfield_id
|
|
|
+ WHERE p.playfield_public_id = $1
|
|
|
+ AND pv.version_public_id = $2
|
|
|
+ LIMIT 1
|
|
|
+ `, playfieldPublicID, versionPublicID)
|
|
|
+ return scanResourcePlayfieldVersion(row)
|
|
|
+}
|
|
|
+
|
|
|
+func (s *Store) CreateResourcePlayfieldVersion(ctx context.Context, tx Tx, params CreateResourcePlayfieldVersionParams) (*ResourcePlayfieldVersion, error) {
|
|
|
+ boundsJSON, err := marshalJSONMap(params.BoundsJSON)
|
|
|
+ if err != nil {
|
|
|
+ return nil, fmt.Errorf("marshal playfield bounds: %w", err)
|
|
|
+ }
|
|
|
+ metadataJSON, err := marshalJSONMap(params.MetadataJSON)
|
|
|
+ if err != nil {
|
|
|
+ return nil, fmt.Errorf("marshal playfield metadata: %w", err)
|
|
|
+ }
|
|
|
+ row := tx.QueryRow(ctx, `
|
|
|
+ INSERT INTO playfield_versions (
|
|
|
+ version_public_id, playfield_id, version_code, status, source_type, source_url,
|
|
|
+ published_asset_root, control_count, bounds_jsonb, metadata_jsonb
|
|
|
+ )
|
|
|
+ VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9::jsonb, $10::jsonb)
|
|
|
+ RETURNING id, version_public_id, playfield_id, version_code, status, source_type, source_url, published_asset_root,
|
|
|
+ control_count, bounds_jsonb::text, metadata_jsonb::text, created_at, updated_at
|
|
|
+ `, params.PublicID, params.PlayfieldID, params.VersionCode, params.Status, params.SourceType, params.SourceURL, params.PublishedAssetRoot, params.ControlCount, boundsJSON, metadataJSON)
|
|
|
+ return scanResourcePlayfieldVersion(row)
|
|
|
+}
|
|
|
+
|
|
|
+func (s *Store) SetResourcePlayfieldCurrentVersion(ctx context.Context, tx Tx, playfieldID, versionID string) error {
|
|
|
+ _, err := tx.Exec(ctx, `UPDATE playfields SET current_version_id = $2 WHERE id = $1`, playfieldID, versionID)
|
|
|
+ if err != nil {
|
|
|
+ return fmt.Errorf("set resource playfield current version: %w", err)
|
|
|
+ }
|
|
|
+ return nil
|
|
|
+}
|
|
|
+
|
|
|
+func (s *Store) ListResourcePacks(ctx context.Context, limit int) ([]ResourcePack, error) {
|
|
|
+ if limit <= 0 || limit > 200 {
|
|
|
+ limit = 50
|
|
|
+ }
|
|
|
+ rows, err := s.pool.Query(ctx, `
|
|
|
+ SELECT id, resource_pack_public_id, code, name, status, description, current_version_id, created_at, updated_at
|
|
|
+ FROM resource_packs
|
|
|
+ ORDER BY created_at DESC
|
|
|
+ LIMIT $1
|
|
|
+ `, limit)
|
|
|
+ if err != nil {
|
|
|
+ return nil, fmt.Errorf("list resource packs: %w", err)
|
|
|
+ }
|
|
|
+ defer rows.Close()
|
|
|
+
|
|
|
+ items := []ResourcePack{}
|
|
|
+ for rows.Next() {
|
|
|
+ item, err := scanResourcePackFromRows(rows)
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ items = append(items, *item)
|
|
|
+ }
|
|
|
+ if err := rows.Err(); err != nil {
|
|
|
+ return nil, fmt.Errorf("iterate resource packs: %w", err)
|
|
|
+ }
|
|
|
+ return items, nil
|
|
|
+}
|
|
|
+
|
|
|
+func (s *Store) GetResourcePackByPublicID(ctx context.Context, publicID string) (*ResourcePack, error) {
|
|
|
+ row := s.pool.QueryRow(ctx, `
|
|
|
+ SELECT id, resource_pack_public_id, code, name, status, description, current_version_id, created_at, updated_at
|
|
|
+ FROM resource_packs
|
|
|
+ WHERE resource_pack_public_id = $1
|
|
|
+ LIMIT 1
|
|
|
+ `, publicID)
|
|
|
+ return scanResourcePack(row)
|
|
|
+}
|
|
|
+
|
|
|
+func (s *Store) CreateResourcePack(ctx context.Context, tx Tx, params CreateResourcePackParams) (*ResourcePack, error) {
|
|
|
+ row := tx.QueryRow(ctx, `
|
|
|
+ INSERT INTO resource_packs (resource_pack_public_id, code, name, status, description)
|
|
|
+ VALUES ($1, $2, $3, $4, $5)
|
|
|
+ RETURNING id, resource_pack_public_id, code, name, status, description, current_version_id, created_at, updated_at
|
|
|
+ `, params.PublicID, params.Code, params.Name, params.Status, params.Description)
|
|
|
+ return scanResourcePack(row)
|
|
|
+}
|
|
|
+
|
|
|
+func (s *Store) ListResourcePackVersions(ctx context.Context, resourcePackID string) ([]ResourcePackVersion, error) {
|
|
|
+ rows, err := s.pool.Query(ctx, `
|
|
|
+ SELECT id, version_public_id, resource_pack_id, version_code, status, content_entry_url, audio_root_url,
|
|
|
+ theme_profile_code, published_asset_root, metadata_jsonb::text, created_at, updated_at
|
|
|
+ FROM resource_pack_versions
|
|
|
+ WHERE resource_pack_id = $1
|
|
|
+ ORDER BY created_at DESC
|
|
|
+ `, resourcePackID)
|
|
|
+ if err != nil {
|
|
|
+ return nil, fmt.Errorf("list resource pack versions: %w", err)
|
|
|
+ }
|
|
|
+ defer rows.Close()
|
|
|
+
|
|
|
+ items := []ResourcePackVersion{}
|
|
|
+ for rows.Next() {
|
|
|
+ item, err := scanResourcePackVersionFromRows(rows)
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ items = append(items, *item)
|
|
|
+ }
|
|
|
+ if err := rows.Err(); err != nil {
|
|
|
+ return nil, fmt.Errorf("iterate resource pack versions: %w", err)
|
|
|
+ }
|
|
|
+ return items, nil
|
|
|
+}
|
|
|
+
|
|
|
+func (s *Store) GetResourcePackVersionByPublicID(ctx context.Context, resourcePackPublicID, versionPublicID string) (*ResourcePackVersion, error) {
|
|
|
+ row := s.pool.QueryRow(ctx, `
|
|
|
+ SELECT pv.id, pv.version_public_id, pv.resource_pack_id, pv.version_code, pv.status, pv.content_entry_url, pv.audio_root_url,
|
|
|
+ pv.theme_profile_code, pv.published_asset_root, pv.metadata_jsonb::text, pv.created_at, pv.updated_at
|
|
|
+ FROM resource_pack_versions pv
|
|
|
+ JOIN resource_packs rp ON rp.id = pv.resource_pack_id
|
|
|
+ WHERE rp.resource_pack_public_id = $1
|
|
|
+ AND pv.version_public_id = $2
|
|
|
+ LIMIT 1
|
|
|
+ `, resourcePackPublicID, versionPublicID)
|
|
|
+ return scanResourcePackVersion(row)
|
|
|
+}
|
|
|
+
|
|
|
+func (s *Store) CreateResourcePackVersion(ctx context.Context, tx Tx, params CreateResourcePackVersionParams) (*ResourcePackVersion, error) {
|
|
|
+ metadataJSON, err := marshalJSONMap(params.MetadataJSON)
|
|
|
+ if err != nil {
|
|
|
+ return nil, fmt.Errorf("marshal resource pack metadata: %w", err)
|
|
|
+ }
|
|
|
+ row := tx.QueryRow(ctx, `
|
|
|
+ INSERT INTO resource_pack_versions (
|
|
|
+ version_public_id, resource_pack_id, version_code, status, content_entry_url,
|
|
|
+ audio_root_url, theme_profile_code, published_asset_root, metadata_jsonb
|
|
|
+ )
|
|
|
+ VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9::jsonb)
|
|
|
+ RETURNING id, version_public_id, resource_pack_id, version_code, status, content_entry_url, audio_root_url,
|
|
|
+ theme_profile_code, published_asset_root, metadata_jsonb::text, created_at, updated_at
|
|
|
+ `, params.PublicID, params.ResourcePackID, params.VersionCode, params.Status, params.ContentEntryURL, params.AudioRootURL, params.ThemeProfileCode, params.PublishedAssetRoot, metadataJSON)
|
|
|
+ return scanResourcePackVersion(row)
|
|
|
+}
|
|
|
+
|
|
|
+func (s *Store) SetResourcePackCurrentVersion(ctx context.Context, tx Tx, resourcePackID, versionID string) error {
|
|
|
+ _, err := tx.Exec(ctx, `UPDATE resource_packs SET current_version_id = $2 WHERE id = $1`, resourcePackID, versionID)
|
|
|
+ if err != nil {
|
|
|
+ return fmt.Errorf("set resource pack current version: %w", err)
|
|
|
+ }
|
|
|
+ return nil
|
|
|
+}
|
|
|
+
|
|
|
+func scanResourceMap(row pgx.Row) (*ResourceMap, error) {
|
|
|
+ var item ResourceMap
|
|
|
+ err := row.Scan(&item.ID, &item.PublicID, &item.Code, &item.Name, &item.Status, &item.Description, &item.CurrentVersionID, &item.CreatedAt, &item.UpdatedAt)
|
|
|
+ if errors.Is(err, pgx.ErrNoRows) {
|
|
|
+ return nil, nil
|
|
|
+ }
|
|
|
+ if err != nil {
|
|
|
+ return nil, fmt.Errorf("scan resource map: %w", err)
|
|
|
+ }
|
|
|
+ return &item, nil
|
|
|
+}
|
|
|
+
|
|
|
+func scanResourceMapFromRows(rows pgx.Rows) (*ResourceMap, error) {
|
|
|
+ var item ResourceMap
|
|
|
+ err := rows.Scan(&item.ID, &item.PublicID, &item.Code, &item.Name, &item.Status, &item.Description, &item.CurrentVersionID, &item.CreatedAt, &item.UpdatedAt)
|
|
|
+ if err != nil {
|
|
|
+ return nil, fmt.Errorf("scan resource map row: %w", err)
|
|
|
+ }
|
|
|
+ return &item, nil
|
|
|
+}
|
|
|
+
|
|
|
+func scanResourceMapVersion(row pgx.Row) (*ResourceMapVersion, error) {
|
|
|
+ var item ResourceMapVersion
|
|
|
+ var boundsJSON string
|
|
|
+ var metadataJSON string
|
|
|
+ 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)
|
|
|
+ if errors.Is(err, pgx.ErrNoRows) {
|
|
|
+ return nil, nil
|
|
|
+ }
|
|
|
+ if err != nil {
|
|
|
+ return nil, fmt.Errorf("scan resource map version: %w", err)
|
|
|
+ }
|
|
|
+ item.BoundsJSON = json.RawMessage(boundsJSON)
|
|
|
+ item.MetadataJSON = json.RawMessage(metadataJSON)
|
|
|
+ return &item, nil
|
|
|
+}
|
|
|
+
|
|
|
+func scanResourceMapVersionFromRows(rows pgx.Rows) (*ResourceMapVersion, error) {
|
|
|
+ var item ResourceMapVersion
|
|
|
+ var boundsJSON string
|
|
|
+ var metadataJSON string
|
|
|
+ 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)
|
|
|
+ if err != nil {
|
|
|
+ return nil, fmt.Errorf("scan resource map version row: %w", err)
|
|
|
+ }
|
|
|
+ item.BoundsJSON = json.RawMessage(boundsJSON)
|
|
|
+ item.MetadataJSON = json.RawMessage(metadataJSON)
|
|
|
+ return &item, nil
|
|
|
+}
|
|
|
+
|
|
|
+func scanResourcePlayfield(row pgx.Row) (*ResourcePlayfield, error) {
|
|
|
+ var item ResourcePlayfield
|
|
|
+ err := row.Scan(&item.ID, &item.PublicID, &item.Code, &item.Name, &item.Kind, &item.Status, &item.Description, &item.CurrentVersionID, &item.CreatedAt, &item.UpdatedAt)
|
|
|
+ if errors.Is(err, pgx.ErrNoRows) {
|
|
|
+ return nil, nil
|
|
|
+ }
|
|
|
+ if err != nil {
|
|
|
+ return nil, fmt.Errorf("scan resource playfield: %w", err)
|
|
|
+ }
|
|
|
+ return &item, nil
|
|
|
+}
|
|
|
+
|
|
|
+func scanResourcePlayfieldFromRows(rows pgx.Rows) (*ResourcePlayfield, error) {
|
|
|
+ var item ResourcePlayfield
|
|
|
+ err := rows.Scan(&item.ID, &item.PublicID, &item.Code, &item.Name, &item.Kind, &item.Status, &item.Description, &item.CurrentVersionID, &item.CreatedAt, &item.UpdatedAt)
|
|
|
+ if err != nil {
|
|
|
+ return nil, fmt.Errorf("scan resource playfield row: %w", err)
|
|
|
+ }
|
|
|
+ return &item, nil
|
|
|
+}
|
|
|
+
|
|
|
+func scanResourcePlayfieldVersion(row pgx.Row) (*ResourcePlayfieldVersion, error) {
|
|
|
+ var item ResourcePlayfieldVersion
|
|
|
+ var boundsJSON string
|
|
|
+ var metadataJSON string
|
|
|
+ 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)
|
|
|
+ if errors.Is(err, pgx.ErrNoRows) {
|
|
|
+ return nil, nil
|
|
|
+ }
|
|
|
+ if err != nil {
|
|
|
+ return nil, fmt.Errorf("scan resource playfield version: %w", err)
|
|
|
+ }
|
|
|
+ item.BoundsJSON = json.RawMessage(boundsJSON)
|
|
|
+ item.MetadataJSON = json.RawMessage(metadataJSON)
|
|
|
+ return &item, nil
|
|
|
+}
|
|
|
+
|
|
|
+func scanResourcePlayfieldVersionFromRows(rows pgx.Rows) (*ResourcePlayfieldVersion, error) {
|
|
|
+ var item ResourcePlayfieldVersion
|
|
|
+ var boundsJSON string
|
|
|
+ var metadataJSON string
|
|
|
+ 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)
|
|
|
+ if err != nil {
|
|
|
+ return nil, fmt.Errorf("scan resource playfield version row: %w", err)
|
|
|
+ }
|
|
|
+ item.BoundsJSON = json.RawMessage(boundsJSON)
|
|
|
+ item.MetadataJSON = json.RawMessage(metadataJSON)
|
|
|
+ return &item, nil
|
|
|
+}
|
|
|
+
|
|
|
+func scanResourcePack(row pgx.Row) (*ResourcePack, error) {
|
|
|
+ var item ResourcePack
|
|
|
+ err := row.Scan(&item.ID, &item.PublicID, &item.Code, &item.Name, &item.Status, &item.Description, &item.CurrentVersionID, &item.CreatedAt, &item.UpdatedAt)
|
|
|
+ if errors.Is(err, pgx.ErrNoRows) {
|
|
|
+ return nil, nil
|
|
|
+ }
|
|
|
+ if err != nil {
|
|
|
+ return nil, fmt.Errorf("scan resource pack: %w", err)
|
|
|
+ }
|
|
|
+ return &item, nil
|
|
|
+}
|
|
|
+
|
|
|
+func scanResourcePackFromRows(rows pgx.Rows) (*ResourcePack, error) {
|
|
|
+ var item ResourcePack
|
|
|
+ err := rows.Scan(&item.ID, &item.PublicID, &item.Code, &item.Name, &item.Status, &item.Description, &item.CurrentVersionID, &item.CreatedAt, &item.UpdatedAt)
|
|
|
+ if err != nil {
|
|
|
+ return nil, fmt.Errorf("scan resource pack row: %w", err)
|
|
|
+ }
|
|
|
+ return &item, nil
|
|
|
+}
|
|
|
+
|
|
|
+func scanResourcePackVersion(row pgx.Row) (*ResourcePackVersion, error) {
|
|
|
+ var item ResourcePackVersion
|
|
|
+ var metadataJSON string
|
|
|
+ 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)
|
|
|
+ if errors.Is(err, pgx.ErrNoRows) {
|
|
|
+ return nil, nil
|
|
|
+ }
|
|
|
+ if err != nil {
|
|
|
+ return nil, fmt.Errorf("scan resource pack version: %w", err)
|
|
|
+ }
|
|
|
+ item.MetadataJSON = json.RawMessage(metadataJSON)
|
|
|
+ return &item, nil
|
|
|
+}
|
|
|
+
|
|
|
+func scanResourcePackVersionFromRows(rows pgx.Rows) (*ResourcePackVersion, error) {
|
|
|
+ var item ResourcePackVersion
|
|
|
+ var metadataJSON string
|
|
|
+ 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)
|
|
|
+ if err != nil {
|
|
|
+ return nil, fmt.Errorf("scan resource pack version row: %w", err)
|
|
|
+ }
|
|
|
+ item.MetadataJSON = json.RawMessage(metadataJSON)
|
|
|
+ return &item, nil
|
|
|
+}
|
|
|
+
|
|
|
+func marshalJSONMap(value map[string]any) (string, error) {
|
|
|
+ if value == nil {
|
|
|
+ value = map[string]any{}
|
|
|
+ }
|
|
|
+ raw, err := json.Marshal(value)
|
|
|
+ if err != nil {
|
|
|
+ return "", err
|
|
|
+ }
|
|
|
+ return string(raw), nil
|
|
|
+}
|