Added the catalogue models and seed to support phase 2 of the POC prototype in the Persistence package.
This commit is contained in:
1 parent
ea89ea892a
commit
09c7b8a1a7
10 files changed
+238
-21
No files matched your search
@@ -0,0 +1,26 @@
|
||||
import FluentKit
|
||||
|
||||
/// Creates and drops the `users` table backing ``UserRecord``, seeded with the prototype's demo accounts.
|
||||
struct CreateUser: AsyncMigration {
|
||||
|
||||
// MARK: Methods
|
||||
|
||||
func prepare(on database: Database) async throws {
|
||||
try await database.schema(UserRecord.schema)
|
||||
.id()
|
||||
.field("email", .string, .required)
|
||||
.field("display_name", .string, .required)
|
||||
.unique(on: "email")
|
||||
.create()
|
||||
|
||||
for account in Seed.accounts {
|
||||
try await UserRecord(email: account.email, displayName: account.displayName).save(on: database)
|
||||
}
|
||||
}
|
||||
|
||||
func revert(on database: Database) async throws {
|
||||
try await database.schema(UserRecord.schema)
|
||||
.delete()
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
import FluentKit
|
||||
import Foundation
|
||||
|
||||
/// A FluentKit model of a single `users` row.
|
||||
final class UserRecord: Model, @unchecked Sendable {
|
||||
|
||||
// MARK: Properties
|
||||
|
||||
static let schema = "users"
|
||||
|
||||
@ID(key: .id)
|
||||
var id: UUID?
|
||||
|
||||
@Field(key: "email")
|
||||
var email: String
|
||||
|
||||
@Field(key: "display_name")
|
||||
var displayName: String
|
||||
|
||||
// MARK: Initializers
|
||||
|
||||
init() {}
|
||||
|
||||
/// Creates a record with the given values.
|
||||
/// - Parameters:
|
||||
/// - email: the account's normalized email address.
|
||||
/// - displayName: the name other people see.
|
||||
init(
|
||||
email: String,
|
||||
displayName: String
|
||||
) {
|
||||
self.email = email
|
||||
self.displayName = displayName
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// MARK: - Extensions
|
||||
|
||||
extension UserRecord {
|
||||
|
||||
// MARK: Computed
|
||||
|
||||
/// The `Sendable` snapshot of the saved record.
|
||||
var snapshot: User {
|
||||
get throws {
|
||||
.init(
|
||||
id: try requireID(),
|
||||
email: email,
|
||||
displayName: displayName
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -5,6 +5,12 @@ enum Seed {
|
||||
|
||||
// MARK: Constants
|
||||
|
||||
/// The demo accounts: the buyer the stubbed passkey signs in, then a seller.
|
||||
static let accounts: [(email: String, displayName: String)] = [
|
||||
("mara.v@example.com", "Mara Visser"),
|
||||
("joost@kestrelrow.example", "Joost de Wit"),
|
||||
]
|
||||
|
||||
/// The demo listings, newest first.
|
||||
static let listings: [Listing] = [
|
||||
single(
|
||||
|
||||
@@ -25,6 +25,7 @@ public struct PrepareDB: Sendable {
|
||||
CreateShop(),
|
||||
CreateListing(),
|
||||
SeedCatalogue(),
|
||||
CreateUser(),
|
||||
])
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
import Foundation
|
||||
|
||||
/// A `Sendable` snapshot of an account.
|
||||
public struct User: Sendable, Equatable {
|
||||
|
||||
// MARK: Properties
|
||||
|
||||
/// The account's identifier.
|
||||
public let id: UUID
|
||||
/// The account's normalized email address.
|
||||
public let email: String
|
||||
/// The name other people see.
|
||||
public let displayName: String
|
||||
|
||||
// MARK: Initializers
|
||||
|
||||
public init(
|
||||
id: UUID,
|
||||
email: String,
|
||||
displayName: String
|
||||
) {
|
||||
self.id = id
|
||||
self.email = email
|
||||
self.displayName = displayName
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
import FluentKit
|
||||
import HummingbirdFluent
|
||||
|
||||
/// Reads and creates accounts through the default database.
|
||||
public struct AccountRepository: Sendable {
|
||||
|
||||
// MARK: Constants
|
||||
|
||||
/// The email of the demo buyer the prototype's stubbed passkey signs in.
|
||||
public static let demoEmail = Seed.accounts[0].email
|
||||
|
||||
// MARK: Properties
|
||||
|
||||
private let fluent: Fluent
|
||||
|
||||
// MARK: Initializers
|
||||
|
||||
/// Creates a repository backed by the given `Fluent` service.
|
||||
/// - Parameter fluent: the service whose default database the repository operates on.
|
||||
public init(
|
||||
fluent: Fluent
|
||||
) {
|
||||
self.fluent = fluent
|
||||
}
|
||||
|
||||
// MARK: Methods
|
||||
|
||||
/// Fetches the account with the given email, creating it when there is none: one step both signs in and joins.
|
||||
///
|
||||
/// A new account is named after the email's local part until its owner picks a display name.
|
||||
/// - Parameter email: the account's normalized email address.
|
||||
/// - Returns: the account, existing or new.
|
||||
public func account(
|
||||
email: String
|
||||
) async throws -> User {
|
||||
if let record = try await UserRecord.query(on: fluent.db())
|
||||
.filter(\.$email == email)
|
||||
.first()
|
||||
{
|
||||
return try record.snapshot
|
||||
}
|
||||
|
||||
let record = UserRecord(
|
||||
email: email,
|
||||
displayName: String(email.prefix { $0 != "@" })
|
||||
)
|
||||
|
||||
try await record.save(on: fluent.db())
|
||||
|
||||
return try record.snapshot
|
||||
}
|
||||
|
||||
}
|
||||
Reference in new issue
Block a user