Added the catalogue models and seed to support phase 1 of the POC prototype in the Persistence package.

This commit is contained in:
javier committed 2026-09-21 11:16:30 +02:00
1 parent 717df5208c
commit 6d7ab4bcba
19 files changed
+1381 -151

No files matched your search

@@ -1,45 +0,0 @@
import FluentKit
import Foundation
/// A FluentKit model of a single `example_records` row.
///
/// This is reference scaffolding: it demonstrates the model → migration → repository pattern the rest of
/// the package is built around, and is what the tests exercise. Replace it with the first real domain model
/// (paired with its own migration and repository) once one is defined.
///
/// FluentKit models are mutable reference types whose property wrappers are not `Sendable`; the model never
/// crosses a concurrency boundary (repositories map it to a `Sendable` snapshot before returning), so the
/// conformance is declared `@unchecked Sendable`.
final class ExampleRecord: Model, @unchecked Sendable {
// MARK: Properties
/// The name of the backing table.
static let schema = "example_records"
/// The row's primary key, assigned on first save.
@ID(key: .id)
var id: UUID?
/// The row's name column.
@Field(key: "name")
var name: String
// MARK: Initializers
/// Creates an empty record, as required by FluentKit to hydrate query results.
init() {}
/// Creates a record with the given values.
/// - Parameters:
/// - id: the primary key, or `nil` to have one assigned on save.
/// - name: the value of the name column.
init(
id: UUID? = nil,
name: String
) {
self.id = id
self.name = name
}
}
@@ -0,0 +1,176 @@
import FluentKit
import Foundation
/// A FluentKit model of a single `listings` row.
///
/// The catalogue fields (series, issue, publisher) are folded into the listing; they move to tables of their own once the series screen needs them.
final class ListingRecord: Model, @unchecked Sendable {
// MARK: Properties
static let schema = "listings"
@ID(key: .id)
var id: UUID?
@Parent(key: "shop_id")
var shop: ShopRecord
@Field(key: "slug")
var slug: String
@Field(key: "title")
var title: String
@Field(key: "series_title")
var seriesTitle: String
@Field(key: "issue_label")
var issueLabel: String
@Field(key: "publisher")
var publisher: String
@Field(key: "year")
var year: Int
@Field(key: "format")
var format: String
@Field(key: "printing")
var printing: String
@Field(key: "printing_detail")
var printingDetail: String
@Field(key: "is_first_print")
var isFirstPrint: Bool
@OptionalField(key: "grader")
var grader: String?
@OptionalField(key: "grade")
var grade: Double?
@Field(key: "condition")
var condition: String
@OptionalField(key: "cert_number")
var certNumber: String?
@OptionalField(key: "page_quality")
var pageQuality: String?
@OptionalField(key: "key_notes")
var keyNotes: String?
@Field(key: "seller_notes")
var sellerNotes: String
@Field(key: "price_cents")
var priceCents: Int
@Field(key: "shipping_cents")
var shippingCents: Int
@Field(key: "accepts_offers")
var acceptsOffers: Bool
@Field(key: "cover_tone")
var coverTone: String
@Field(key: "cover_title")
var coverTitle: String
@Field(key: "cover_imprint")
var coverImprint: String
@Field(key: "cover_number")
var coverNumber: String
@Field(key: "listed_at")
var listedAt: Date
// MARK: Initializers
init() {}
/// Creates a record from a snapshot.
/// - Parameters:
/// - listing: the snapshot the record takes its values from.
/// - shopID: the identifier of the shop the listing belongs to.
init(
_ listing: Listing,
shopID: UUID
) {
self.$shop.id = shopID
self.slug = listing.slug
self.title = listing.title
self.seriesTitle = listing.seriesTitle
self.issueLabel = listing.issueLabel
self.publisher = listing.publisher
self.year = listing.year
self.format = listing.format
self.printing = listing.printing
self.printingDetail = listing.printingDetail
self.isFirstPrint = listing.isFirstPrint
self.grader = listing.grader
self.grade = listing.grade
self.condition = listing.condition
self.certNumber = listing.certNumber
self.pageQuality = listing.pageQuality
self.keyNotes = listing.keyNotes
self.sellerNotes = listing.sellerNotes
self.priceCents = listing.priceCents
self.shippingCents = listing.shippingCents
self.acceptsOffers = listing.acceptsOffers
self.coverTone = listing.cover.tone
self.coverTitle = listing.cover.title
self.coverImprint = listing.cover.imprint
self.coverNumber = listing.cover.number
self.listedAt = listing.listedAt
}
}
// MARK: - Extensions
extension ListingRecord {
// MARK: Computed
/// The `Sendable` snapshot of the record; its shop must be eager-loaded.
var snapshot: Listing {
.init(
slug: slug,
shop: shop.snapshot,
title: title,
seriesTitle: seriesTitle,
issueLabel: issueLabel,
publisher: publisher,
year: year,
format: format,
printing: printing,
printingDetail: printingDetail,
isFirstPrint: isFirstPrint,
grader: grader,
grade: grade,
condition: condition,
certNumber: certNumber,
pageQuality: pageQuality,
keyNotes: keyNotes,
sellerNotes: sellerNotes,
priceCents: priceCents,
shippingCents: shippingCents,
acceptsOffers: acceptsOffers,
cover: .init(
tone: coverTone,
title: coverTitle,
imprint: coverImprint,
number: coverNumber
),
listedAt: listedAt
)
}
}
@@ -0,0 +1,107 @@
import FluentKit
import Foundation
/// A FluentKit model of a single `shops` row.
final class ShopRecord: Model, @unchecked Sendable {
// MARK: Properties
static let schema = "shops"
@ID(key: .id)
var id: UUID?
@Field(key: "slug")
var slug: String
@Field(key: "name")
var name: String
@Field(key: "city")
var city: String
@Field(key: "country")
var country: String
@Field(key: "since_year")
var sinceYear: Int
@Field(key: "is_verified")
var isVerified: Bool
@Field(key: "rating")
var rating: Double
@Field(key: "rated_sales")
var ratedSales: Int
@Field(key: "sold_count")
var soldCount: Int
@Field(key: "handling_days")
var handlingDays: Int
@Field(key: "ships_note")
var shipsNote: String
@Field(key: "returns_note")
var returnsNote: String
@Field(key: "about")
var about: String
@Children(for: \.$shop)
var listings: [ListingRecord]
// MARK: Initializers
init() {}
/// Creates a record from a snapshot.
/// - Parameter shop: the snapshot the record takes its values from.
init(
_ shop: Shop
) {
self.slug = shop.slug
self.name = shop.name
self.city = shop.city
self.country = shop.country
self.sinceYear = shop.sinceYear
self.isVerified = shop.isVerified
self.rating = shop.rating
self.ratedSales = shop.ratedSales
self.soldCount = shop.soldCount
self.handlingDays = shop.handlingDays
self.shipsNote = shop.shipsNote
self.returnsNote = shop.returnsNote
self.about = shop.about
}
}
// MARK: - Extensions
extension ShopRecord {
// MARK: Computed
/// The `Sendable` snapshot of the record.
var snapshot: Shop {
.init(
slug: slug,
name: name,
city: city,
country: country,
sinceYear: sinceYear,
isVerified: isVerified,
rating: rating,
ratedSales: ratedSales,
soldCount: soldCount,
handlingDays: handlingDays,
shipsNote: shipsNote,
returnsNote: returnsNote,
about: about
)
}
}