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

This commit is contained in:
javier committed 2026-09-21 13:52:52 +02:00
1 parent 31d2d98d49
commit 256d22df40
10 files changed
+473 -4

No files matched your search

@@ -0,0 +1,49 @@
import FluentKit
/// Creates the `events`, `event_shops`, and `owned_issues` tables, seeded with the demo cons and the shops trading at them.
struct CreateCommunity: AsyncMigration {
// MARK: Methods
func prepare(on database: Database) async throws {
try await database.schema(EventRecord.schema)
.id()
.field("slug", .string, .required)
.field("name", .string, .required)
.field("venue", .string, .required)
.field("starts_on", .datetime, .required)
.field("ends_on", .datetime, .required)
.unique(on: "slug")
.create()
try await database.schema(EventShopRecord.schema)
.id()
.field("event_id", .uuid, .required, .references(EventRecord.schema, "id", onDelete: .cascade))
.field("shop_id", .uuid, .required, .references(ShopRecord.schema, "id", onDelete: .cascade))
.unique(on: "event_id", "shop_id")
.create()
try await database.schema(OwnedIssueRecord.schema)
.id()
.field("user_id", .uuid, .required, .references(UserRecord.schema, "id", onDelete: .cascade))
.field("series_slug", .string, .required)
.field("issue", .int, .required)
.unique(on: "user_id", "series_slug", "issue")
.create()
for (event, shops) in Seed.events {
let record = EventRecord(event)
try await record.save(on: database)
for shop in try await ShopRecord.query(on: database).filter(\.$slug ~~ shops).all() {
try await record.$shops.attach(shop, on: database)
}
}
}
func revert(on database: Database) async throws {
for schema in [OwnedIssueRecord.schema, EventShopRecord.schema, EventRecord.schema] {
try await database.schema(schema).delete()
}
}
}
@@ -0,0 +1,103 @@
import FluentKit
import Foundation
/// A FluentKit model of a single `events` row: a con where shops trade in person.
final class EventRecord: Model, @unchecked Sendable {
// MARK: Properties
static let schema = "events"
@ID(key: .id)
var id: UUID?
@Field(key: "slug")
var slug: String
@Field(key: "name")
var name: String
@Field(key: "venue")
var venue: String
@Field(key: "starts_on")
var startsOn: Date
@Field(key: "ends_on")
var endsOn: Date
@Siblings(through: EventShopRecord.self, from: \.$event, to: \.$shop)
var shops: [ShopRecord]
// MARK: Initializers
init() {}
init(
_ event: Event
) {
self.slug = event.slug
self.name = event.name
self.venue = event.venue
self.startsOn = event.startsOn
self.endsOn = event.endsOn
}
}
/// A FluentKit model of a single `event_shops` row: a shop trading at an event.
final class EventShopRecord: Model, @unchecked Sendable {
// MARK: Properties
static let schema = "event_shops"
@ID(key: .id)
var id: UUID?
@Parent(key: "event_id")
var event: EventRecord
@Parent(key: "shop_id")
var shop: ShopRecord
// MARK: Initializers
init() {}
}
/// A FluentKit model of a single `owned_issues` row: an issue of a series in an account's collection.
final class OwnedIssueRecord: Model, @unchecked Sendable {
// MARK: Properties
static let schema = "owned_issues"
@ID(key: .id)
var id: UUID?
@Parent(key: "user_id")
var user: UserRecord
@Field(key: "series_slug")
var seriesSlug: String
@Field(key: "issue")
var issue: Int
// MARK: Initializers
init() {}
init(
userID: UUID,
seriesSlug: String,
issue: Int
) {
self.$user.id = userID
self.seriesSlug = seriesSlug
self.issue = issue
}
}
@@ -158,6 +158,31 @@ enum Seed {
),
]
/// The demo cons, soonest first, each with the slugs of the shops trading there.
static let events: [(event: Event, shops: [String])] = [
(.init(slug: "dutch-comic-con", name: "Dutch Comic Con", venue: "Jaarbeurs, Utrecht", startsOn: day(2026, 9, 26), endsOn: day(2026, 9, 27)),
["kestrel-row-comics", "silverfish-comics", "third-panel-supply"]),
(.init(slug: "stripdagen-haarlem", name: "Stripdagen Haarlem", venue: "Grote Markt, Haarlem", startsOn: day(2026, 10, 3), endsOn: day(2026, 10, 4)),
["silverfish-comics"]),
(.init(slug: "comic-con-amsterdam", name: "Comic Con Amsterdam", venue: "RAI, Amsterdam", startsOn: day(2026, 11, 21), endsOn: day(2026, 11, 22)),
["kestrel-row-comics", "inkwell-and-staple"]),
(.init(slug: "comic-con-brussels", name: "Comic Con Brussels", venue: "Tour & Taxis, Brussels", startsOn: day(2026, 12, 5), endsOn: day(2026, 12, 6)),
["third-panel-supply"]),
]
/// Noon on a day in Amsterdam, where the demo cons are dated.
static func day(
_ year: Int,
_ month: Int,
_ day: Int
) -> Date {
var calendar = Calendar(identifier: .gregorian)
calendar.timeZone = TimeZone(identifier: "Europe/Amsterdam") ?? .gmt
return calendar.date(from: .init(year: year, month: month, day: day, hour: 12)) ?? .distantPast
}
/// The runs the demo listings belong to.
static let series: [Series] = [
.init(slug: "nightward", name: "Nightward", publisher: "Meridian Comics", years: "2019–2021", issueCount: 24,