diff --git a/server/src/at_record_server/browse.gleam b/server/src/at_record_server/browse.gleam index acaf6ca..d65f9f7 100644 --- a/server/src/at_record_server/browse.gleam +++ b/server/src/at_record_server/browse.gleam @@ -7,14 +7,13 @@ import at_record/gen/catalog/release as catalog_release import at_record/gen/client as generated_client import at_record/gen/shelf/entry.{type ShelfEntry} import at_record/storage.{type StoredItem} -import at_record_server/catalog/source.{type VariantRow} +import at_record_server/catalog/row.{type BrowseRow, BrowseRow} import at_record_server/catalog/stats.{type VariantStats} import at_record_server/catalog/strategy.{type Strategy} import at_record_server/catalog/variants import at_record_server/crate import at_record_server/external_id import at_record_server/known_users.{type KnownUser} -import atproto/blob.{type Blob} import atproto/xrpc.{type Client} import gleam/dict import gleam/dynamic/decode @@ -28,30 +27,6 @@ import gleam/string // The per-user cap on the fan-out listRecords; the merged set is capped at 100. const per_user_limit = 50 -/// A decoded browse candidate: a foreign release plus the publisher it came -/// from, before dedup/ownership/encoding. -pub type BrowseRow { - BrowseRow( - uri: String, - cid: String, - title: String, - artist_display: Option(String), - genres: List(String), - styles: List(String), - released: Option(String), - country: Option(String), - cover: Option(Blob), - thumb_url: Option(String), - discogs_id: Option(String), - created_at: String, - publisher_did: String, - publisher_handle: String, - publisher_pds: String, - supersedes: Option(String), - based_on: Option(String), - ) -} - /// The viewer's crate reduced to what browse matching needs: which release uris /// and Discogs ids they own vs want. pub type Ownership { @@ -70,7 +45,7 @@ pub fn empty_ownership() -> Ownership { /// One variant set's resolved winner, tagged with its own public stats and /// how many variants it was picked from. pub type ResolvedRelease { - ResolvedRelease(row: VariantRow, stats: VariantStats, variant_count: Int) + ResolvedRelease(row: BrowseRow, stats: VariantStats, variant_count: Int) } /// Group candidate rows into variant sets (shared Discogs id or a @@ -78,7 +53,7 @@ pub type ResolvedRelease { /// `chosen`. A set only drops out if resolution somehow returns no winner, /// which `variants.group` never hands it (every set is non-empty). pub fn resolve_variants( - rows: List(VariantRow), + rows: List(BrowseRow), adoption_count: fn(String) -> Int, chosen: Strategy, ) -> List(ResolvedRelease) { diff --git a/server/src/at_record_server/catalog/row.gleam b/server/src/at_record_server/catalog/row.gleam new file mode 100644 index 0000000..c78228c --- /dev/null +++ b/server/src/at_record_server/catalog/row.gleam @@ -0,0 +1,33 @@ +//// The catalog release row: a decoded catalog.release record plus who +//// published it. The shared shape every layer that reads or writes the +//// catalog index passes around -- `browse`'s live fan-out, `catalog_index`'s +//// storage, and the resolution layer's `variants`/`stats`/`strategy`. A leaf +//// module (only `atproto/blob` and `gleam/option` below it) so every one of +//// those can depend on it without risking a cycle. + +import atproto/blob.{type Blob} +import gleam/option.{type Option} + +/// A release row: the release fields plus the publisher it came from and its +/// chain edges (`supersedes`/`based_on`, at-uris of the target release). +pub type BrowseRow { + BrowseRow( + uri: String, + cid: String, + title: String, + artist_display: Option(String), + genres: List(String), + styles: List(String), + released: Option(String), + country: Option(String), + cover: Option(Blob), + thumb_url: Option(String), + discogs_id: Option(String), + created_at: String, + publisher_did: String, + publisher_handle: String, + publisher_pds: String, + supersedes: Option(String), + based_on: Option(String), + ) +} diff --git a/server/src/at_record_server/catalog/source.gleam b/server/src/at_record_server/catalog/source.gleam index b946c06..878818b 100644 --- a/server/src/at_record_server/catalog/source.gleam +++ b/server/src/at_record_server/catalog/source.gleam @@ -1,46 +1,15 @@ //// The resolution layer's port onto the outside world: everything //// `variants`/`stats`/`strategy` need, and nothing more, so those three stay -//// pure and testable with fakes. `wiring` supplies the real implementation -//// (today: `catalog_index.list` plus a per-row `fetch_release` call for -//// chain edges, and a Constellation backlink tally for adoption; see the -//// swap-point note on `wiring.variant_source`). -//// -//// `VariantRow` deliberately doesn't wrap `browse.BrowseRow`: `browse.gleam` -//// depends on this package for its variant-resolution glue, so a dependency -//// back onto `browse` would be a cycle. `wiring` maps a `BrowseRow` onto a -//// `VariantRow` field-by-field instead. +//// pure and testable with fakes. `wiring` supplies the real implementation: +//// `catalog_index`'s stored rows and adoption count directly, both already +//// public (see `wiring.variant_source`). -import atproto/blob.{type Blob} -import gleam/option.{type Option} - -/// A candidate release row plus the chain edges its own release record -/// carries. `supersedes`/`based_on` are at-uris of the target release. -pub type VariantRow { - VariantRow( - uri: String, - cid: String, - title: String, - artist_display: Option(String), - genres: List(String), - styles: List(String), - released: Option(String), - country: Option(String), - cover: Option(Blob), - thumb_url: Option(String), - discogs_id: Option(String), - created_at: String, - publisher_did: String, - publisher_handle: String, - publisher_pds: String, - supersedes: Option(String), - based_on: Option(String), - ) -} +import at_record_server/catalog/row.{type BrowseRow} pub type Source { Source( /// Every candidate release row, chain edges included. - releases: fn() -> List(VariantRow), + releases: fn() -> List(BrowseRow), /// Public adoption count for a release uri: how many shelf.entry rows /// across every repo point at it. Re-tallyable by anyone from /// Constellation's public backlink index. diff --git a/server/src/at_record_server/catalog/stats.gleam b/server/src/at_record_server/catalog/stats.gleam index 7d6ba6d..e0813ad 100644 --- a/server/src/at_record_server/catalog/stats.gleam +++ b/server/src/at_record_server/catalog/stats.gleam @@ -5,7 +5,7 @@ //// fact that it's re-tallyable by anyone from public backlinks) lives in //// `catalog/source`. -import at_record_server/catalog/source.{type VariantRow} +import at_record_server/catalog/row.{type BrowseRow} import gleam/dict.{type Dict} import gleam/int import gleam/list @@ -20,7 +20,7 @@ pub type VariantStats { /// country, cover art, thumbnail, and the Discogs natural key. Chain edges /// (`supersedes`/`based_on`) aren't counted: they describe lineage, not how /// filled-in the release itself is. -pub fn completeness(row: VariantRow) -> Float { +pub fn completeness(row: BrowseRow) -> Float { let fields = [ option.is_some(row.artist_display), option.is_some(row.released), @@ -34,7 +34,7 @@ pub fn completeness(row: VariantRow) -> Float { } pub fn compute( - row: VariantRow, + row: BrowseRow, adoption_count: fn(String) -> Int, ) -> VariantStats { VariantStats( @@ -46,7 +46,7 @@ pub fn compute( /// Stats for every row in a variant set, keyed by uri. pub fn compute_all( - variant_set: List(VariantRow), + variant_set: List(BrowseRow), adoption_count: fn(String) -> Int, ) -> Dict(String, VariantStats) { variant_set diff --git a/server/src/at_record_server/catalog/strategy.gleam b/server/src/at_record_server/catalog/strategy.gleam index 88b55ed..cbe8627 100644 --- a/server/src/at_record_server/catalog/strategy.gleam +++ b/server/src/at_record_server/catalog/strategy.gleam @@ -3,7 +3,7 @@ //// network; anyone can re-run `resolve` over the same public stats and get //// the same winner. -import at_record_server/catalog/source.{type VariantRow} +import at_record_server/catalog/row.{type BrowseRow} import at_record_server/catalog/stats.{type VariantStats} import gleam/dict.{type Dict} import gleam/float @@ -36,10 +36,10 @@ pub fn default_strategy() -> Strategy { /// strategy names first, then created_at ascending, then uri ascending, so /// the result is deterministic even across an exact tie. pub fn resolve( - variant_set: List(VariantRow), + variant_set: List(BrowseRow), stats: Dict(String, VariantStats), strategy: Strategy, -) -> Option(VariantRow) { +) -> Option(BrowseRow) { variant_set |> list.sort(fn(a, b) { compare(a, b, stats, strategy) }) |> list.first @@ -47,8 +47,8 @@ pub fn resolve( } fn compare( - a: VariantRow, - b: VariantRow, + a: BrowseRow, + b: BrowseRow, stats: Dict(String, VariantStats), strategy: Strategy, ) -> Order { @@ -83,7 +83,7 @@ fn compare_desc_float(a: Float, b: Float) -> Order { float.compare(a, b) |> order.negate } -fn lookup(stats: Dict(String, VariantStats), row: VariantRow) -> VariantStats { +fn lookup(stats: Dict(String, VariantStats), row: BrowseRow) -> VariantStats { case dict.get(stats, row.uri) { Ok(s) -> s Error(Nil) -> diff --git a/server/src/at_record_server/catalog/variants.gleam b/server/src/at_record_server/catalog/variants.gleam index 3e0fb39..8803a8c 100644 --- a/server/src/at_record_server/catalog/variants.gleam +++ b/server/src/at_record_server/catalog/variants.gleam @@ -1,11 +1,11 @@ -//// Pure grouping of `VariantRow`s into variant sets: everything reachable +//// Pure grouping of `BrowseRow`s into variant sets: everything reachable //// from one another via a shared Discogs natural key or a supersedes/basedOn //// chain edge. Grouping is connected components over an undirected graph //// (chain edges point one way, but "the same release" isn't directional), //// walked with an explicit visited set so a supersedes/basedOn cycle simply //// stops rather than looping. -import at_record_server/catalog/source.{type VariantRow} +import at_record_server/catalog/row.{type BrowseRow} import gleam/dict.{type Dict} import gleam/list import gleam/option.{None, Some} @@ -13,11 +13,11 @@ import gleam/result import gleam/set.{type Set} pub type VariantSet = - List(VariantRow) + List(BrowseRow) /// Every input row appears in exactly one output set; sets are returned in /// the order their first-seen row appeared in `rows`. -pub fn group(rows: List(VariantRow)) -> List(VariantSet) { +pub fn group(rows: List(BrowseRow)) -> List(VariantSet) { let by_uri = index_by_uri(rows) let adjacency = build_adjacency(rows) let #(_visited, sets) = @@ -36,13 +36,13 @@ pub fn group(rows: List(VariantRow)) -> List(VariantSet) { list.reverse(sets) } -fn index_by_uri(rows: List(VariantRow)) -> Dict(String, VariantRow) { +fn index_by_uri(rows: List(BrowseRow)) -> Dict(String, BrowseRow) { rows |> list.map(fn(r) { #(r.uri, r) }) |> dict.from_list } /// uri -> neighboring uris: same Discogs id, or a supersedes/basedOn edge /// followed in either direction. -fn build_adjacency(rows: List(VariantRow)) -> Dict(String, List(String)) { +fn build_adjacency(rows: List(BrowseRow)) -> Dict(String, List(String)) { list.append(natural_key_edges(rows), chain_edges(rows)) |> list.fold(dict.new(), fn(acc, edge) { let #(a, b) = edge @@ -50,7 +50,7 @@ fn build_adjacency(rows: List(VariantRow)) -> Dict(String, List(String)) { }) } -fn natural_key_edges(rows: List(VariantRow)) -> List(#(String, String)) { +fn natural_key_edges(rows: List(BrowseRow)) -> List(#(String, String)) { let uris_by_discogs_id = rows |> list.fold(dict.new(), fn(acc, row) { @@ -72,7 +72,7 @@ fn natural_key_edges(rows: List(VariantRow)) -> List(#(String, String)) { }) } -fn chain_edges(rows: List(VariantRow)) -> List(#(String, String)) { +fn chain_edges(rows: List(BrowseRow)) -> List(#(String, String)) { rows |> list.flat_map(fn(row) { let a = row.uri @@ -85,10 +85,10 @@ fn chain_edges(rows: List(VariantRow)) -> List(#(String, String)) { fn walk( frontier: List(String), adjacency: Dict(String, List(String)), - by_uri: Dict(String, VariantRow), + by_uri: Dict(String, BrowseRow), visited: Set(String), - acc: List(VariantRow), -) -> List(VariantRow) { + acc: List(BrowseRow), +) -> List(BrowseRow) { case frontier { [] -> acc [uri, ..rest] -> diff --git a/server/src/at_record_server/catalog_index.gleam b/server/src/at_record_server/catalog_index.gleam index 1421a86..e0de4c1 100644 --- a/server/src/at_record_server/catalog_index.gleam +++ b/server/src/at_record_server/catalog_index.gleam @@ -6,7 +6,7 @@ //// additive: `upsert`/`delete`/`list` signatures never change so existing //// callers compile untouched. -import at_record_server/browse.{type BrowseRow} +import at_record_server/catalog/row.{type BrowseRow} import at_record_server/parallel import gleam/dict.{type Dict} import gleam/erlang/process.{type Subject} diff --git a/server/src/at_record_server/catalog_index_postgres.gleam b/server/src/at_record_server/catalog_index_postgres.gleam index 7fd7901..ceca66a 100644 --- a/server/src/at_record_server/catalog_index_postgres.gleam +++ b/server/src/at_record_server/catalog_index_postgres.gleam @@ -5,7 +5,7 @@ //// tracking Jetstream replay progress). This is derived, rebuildable data, //// so writes swallow-and-log rather than fail the caller. -import at_record_server/browse.{type BrowseRow, BrowseRow} +import at_record_server/catalog/row.{type BrowseRow, BrowseRow} import at_record_server/catalog_index.{ type Adoption, type Edit, type Store, Edit, Store, } diff --git a/server/src/at_record_server/handlers/browse.gleam b/server/src/at_record_server/handlers/browse.gleam index 7f7d6d1..8248534 100644 --- a/server/src/at_record_server/handlers/browse.gleam +++ b/server/src/at_record_server/handlers/browse.gleam @@ -8,7 +8,7 @@ import at_record/gen/defs import at_record/gen/repo/strong_ref.{RepoStrongRef} import at_record/gen/shelf/entry.{ShelfEntry} import at_record_server/browse as browse_domain -import at_record_server/catalog/source.{type VariantRow} +import at_record_server/catalog/row.{type BrowseRow} import at_record_server/catalog/strategy as resolution_strategy import at_record_server/context.{ type Context, error_json, require_session, with_pds_client, @@ -56,16 +56,16 @@ pub fn browse(req: Request, ctx: Context) -> Response { } fn filter_query( - rows: List(VariantRow), + rows: List(BrowseRow), q: Result(String, Nil), -) -> List(VariantRow) { +) -> List(BrowseRow) { case q { Ok(q) if q != "" -> list.filter(rows, matches_query(_, q)) _ -> rows } } -fn matches_query(row: VariantRow, q: String) -> Bool { +fn matches_query(row: BrowseRow, q: String) -> Bool { let needle = string.lowercase(q) string.contains(string.lowercase(row.title), needle) || case row.artist_display { @@ -75,16 +75,16 @@ fn matches_query(row: VariantRow, q: String) -> Bool { } fn filter_genre( - rows: List(VariantRow), + rows: List(BrowseRow), genre: Result(String, Nil), -) -> List(VariantRow) { +) -> List(BrowseRow) { case genre { Ok(genre) if genre != "" -> list.filter(rows, matches_genre(_, genre)) _ -> rows } } -fn matches_genre(row: VariantRow, genre: String) -> Bool { +fn matches_genre(row: BrowseRow, genre: String) -> Bool { let needle = string.lowercase(genre) list.any(row.genres, fn(g) { string.contains(string.lowercase(g), needle) }) } diff --git a/server/src/at_record_server/jetstream_consumer.gleam b/server/src/at_record_server/jetstream_consumer.gleam index 0ffe3bc..dac2922 100644 --- a/server/src/at_record_server/jetstream_consumer.gleam +++ b/server/src/at_record_server/jetstream_consumer.gleam @@ -12,6 +12,7 @@ import at_record/gen/catalog/release as catalog_release import at_record/gen/client as generated_client import at_record/gen/shelf/entry as shelf_entry import at_record_server/browse +import at_record_server/catalog/row import at_record_server/catalog_index import at_record_server/known_users import atproto/xrpc.{type Client} @@ -288,8 +289,8 @@ fn to_browse_row( value: catalog_release.CatalogRelease, handle: String, pds: String, -) -> browse.BrowseRow { - browse.BrowseRow( +) -> row.BrowseRow { + row.BrowseRow( uri: at_uri(frame), cid:, title: value.title, diff --git a/server/src/at_record_server/wiring.gleam b/server/src/at_record_server/wiring.gleam index ef42cb0..d4dab8a 100644 --- a/server/src/at_record_server/wiring.gleam +++ b/server/src/at_record_server/wiring.gleam @@ -5,8 +5,6 @@ import at_record/gen/catalog/edit as catalog_edit import at_record/gen/catalog/release as catalog_release import at_record/gen/client as generated_client -import at_record/gen/defs -import at_record_server/browse.{type BrowseRow} import at_record_server/catalog/source as catalog_source import at_record_server/catalog_deps import at_record_server/catalog_index @@ -16,8 +14,7 @@ import at_record_server/promotion import atproto/constellation import atproto/xrpc.{type Client} import gleam/dynamic/decode -import gleam/list -import gleam/option.{None, Some} +import gleam/option.{None} import gleam/result /// Backlink source for adoption: shelf.entry rows whose `release` field @@ -53,72 +50,20 @@ pub fn context( ) } -/// The resolution layer's read port, built over what's already wired: the -/// live `catalog_index` for candidate rows, plus a per-row `fetch_release` -/// call to decode the chain edges `BrowseRow` doesn't carry yet, and a -/// Constellation backlink tally for adoption. -/// -/// Swap point: once `feat/catalog-indexer` lands (BrowseRow gaining -/// `supersedes`/`based_on` and a maintained adoption count), `releases` -/// becomes a direct map over `catalog_index.list()` with no per-row -/// `fetch_release` round-trip, and `adoption_count` can read the indexed -/// count instead of calling Constellation live. +/// The resolution layer's read port: candidate rows read straight off the +/// maintained `catalog_index` (its rows already carry the chain edges, so no +/// per-row fetch is needed), with adoption still tallied live from +/// Constellation. fn variant_source( catalog: catalog_deps.Deps, catalog_index: catalog_index.Store, ) -> catalog_source.Source { - catalog_source.Source( - releases: fn() { catalog_index.list() |> list_variant_rows(catalog, _) }, - adoption_count: fn(uri) { - case catalog.backlinks(uri, shelf_entry_release_source) { - Ok(page) -> page.total - Error(_) -> 0 - } - }, - ) -} - -fn list_variant_rows( - catalog: catalog_deps.Deps, - rows: List(BrowseRow), -) -> List(catalog_source.VariantRow) { - rows |> list.map(to_variant_row(catalog, _)) -} - -fn to_variant_row( - catalog: catalog_deps.Deps, - row: BrowseRow, -) -> catalog_source.VariantRow { - let #(supersedes, based_on) = case catalog.fetch_release(row.uri) { - Some(#(_cid, value)) -> #( - value.supersedes |> option.map(ref_uri), - value.based_on |> option.map(ref_uri), - ) - None -> #(None, None) - } - catalog_source.VariantRow( - uri: row.uri, - cid: row.cid, - title: row.title, - artist_display: row.artist_display, - genres: row.genres, - styles: row.styles, - released: row.released, - country: row.country, - cover: row.cover, - thumb_url: row.thumb_url, - discogs_id: row.discogs_id, - created_at: row.created_at, - publisher_did: row.publisher_did, - publisher_handle: row.publisher_handle, - publisher_pds: row.publisher_pds, - supersedes:, - based_on:, - ) -} - -fn ref_uri(ref: defs.CatalogRef) -> String { - ref.uri + catalog_source.Source(releases: catalog_index.list, adoption_count: fn(uri) { + case catalog.backlinks(uri, shelf_entry_release_source) { + Ok(page) -> page.total + Error(_) -> 0 + } + }) } fn text_sender(client: Client) { diff --git a/server/test/browse_handler_test.gleam b/server/test/browse_handler_test.gleam index d25b72f..0220053 100644 --- a/server/test/browse_handler_test.gleam +++ b/server/test/browse_handler_test.gleam @@ -4,7 +4,8 @@ //// `catalog_stats_test`, and `catalog_strategy_test`; this only exercises the //// HTTP wiring (query param parsing, `variant_source` plumbing, JSON output). -import at_record_server/catalog/source.{type Source, Source, VariantRow} +import at_record_server/catalog/row.{BrowseRow} +import at_record_server/catalog/source.{type Source, Source} import at_record_server/context.{type Context} import at_record_server/handlers/browse.{browse} import at_record_server/oauth/config @@ -33,7 +34,7 @@ fn a_session() -> sessions.OauthSession { /// the other more recently created, so the two strategies disagree. fn variant_source() -> Source { let more_adopted = - VariantRow( + BrowseRow( uri: "at://a/1", cid: "bafya", title: "Spiderland", @@ -53,7 +54,7 @@ fn variant_source() -> Source { based_on: None, ) let more_recent = - VariantRow( + BrowseRow( ..more_adopted, uri: "at://b/1", cid: "bafyb", diff --git a/server/test/browse_test.gleam b/server/test/browse_test.gleam index 33976e6..b6cc5c5 100644 --- a/server/test/browse_test.gleam +++ b/server/test/browse_test.gleam @@ -1,8 +1,8 @@ import at_record/gen/defs.{CatalogRef, ExternalId} import at_record/gen/shelf/entry.{type ShelfEntry, ShelfEntry} import at_record/storage.{type StoredItem, StoredItem} -import at_record_server/browse.{type BrowseRow, BrowseRow, NetworkMatch} -import at_record_server/catalog/source.{type VariantRow, VariantRow} +import at_record_server/browse.{NetworkMatch} +import at_record_server/catalog/row.{type BrowseRow, BrowseRow} import at_record_server/catalog/strategy.{MostAdopted, MostRecent} import at_record_server/known_users.{KnownUser} import atproto/xrpc @@ -17,25 +17,7 @@ fn row( discogs_id discogs_id: Option(String), created_at created_at: String, ) -> BrowseRow { - BrowseRow( - uri:, - cid: "bafyrow", - title: "Title", - artist_display: None, - genres: [], - styles: [], - released: None, - country: None, - cover: None, - thumb_url: None, - discogs_id:, - created_at:, - publisher_did: "did:plc:pub", - publisher_handle: "pub.test", - publisher_pds: "https://pds.test", - supersedes: None, - based_on: None, - ) + variant_row(uri:, discogs_id:, created_at:, supersedes: None, based_on: None) } fn variant_row( @@ -44,8 +26,8 @@ fn variant_row( created_at created_at: String, supersedes supersedes: Option(String), based_on based_on: Option(String), -) -> VariantRow { - VariantRow( +) -> BrowseRow { + BrowseRow( uri:, cid: "bafyrow", title: "Title", diff --git a/server/test/catalog_index_postgres_test.gleam b/server/test/catalog_index_postgres_test.gleam index f0dff09..a1752b5 100644 --- a/server/test/catalog_index_postgres_test.gleam +++ b/server/test/catalog_index_postgres_test.gleam @@ -4,7 +4,7 @@ //// `catalog_index_postgres` (the `genres`/`styles` array encode/decode) //// when run against a live Postgres, e.g. `DATABASE_URL=... gleam test`. -import at_record_server/browse.{BrowseRow} +import at_record_server/catalog/row.{type BrowseRow, BrowseRow} import at_record_server/catalog_index.{Adoption, Edit} import at_record_server/catalog_index_postgres import at_record_server/oauth/sessions_postgres @@ -23,7 +23,7 @@ fn with_store(run: fn(catalog_index.Store) -> Nil) -> Nil { } } -fn row(uri: String) -> browse.BrowseRow { +fn row(uri: String) -> BrowseRow { BrowseRow( uri:, cid: "bafyrow", diff --git a/server/test/catalog_index_test.gleam b/server/test/catalog_index_test.gleam index b2e37cc..705e4cf 100644 --- a/server/test/catalog_index_test.gleam +++ b/server/test/catalog_index_test.gleam @@ -1,10 +1,10 @@ -import at_record_server/browse.{BrowseRow} +import at_record_server/catalog/row.{type BrowseRow, BrowseRow} import at_record_server/catalog_index.{Adoption, Edit} import gleam/dict import gleam/list import gleam/option.{None, Some} -fn row(uri: String) -> browse.BrowseRow { +fn row(uri: String) -> BrowseRow { BrowseRow( uri:, cid: "bafyrow", diff --git a/server/test/catalog_variants_test.gleam b/server/test/catalog_variants_test.gleam index a44ee1e..57916e4 100644 --- a/server/test/catalog_variants_test.gleam +++ b/server/test/catalog_variants_test.gleam @@ -1,4 +1,4 @@ -import at_record_server/catalog/source.{type VariantRow, VariantRow} +import at_record_server/catalog/row.{type BrowseRow, BrowseRow} import at_record_server/catalog/variants import gleam/list import gleam/option.{type Option, None, Some} @@ -9,8 +9,8 @@ fn variant_row( discogs_id discogs_id: Option(String), supersedes supersedes: Option(String), based_on based_on: Option(String), -) -> VariantRow { - VariantRow( +) -> BrowseRow { + BrowseRow( uri:, cid: "bafyrow", title: "Title",