import DatabaseClient import FluentKit import Foundation import SharedModels public extension DatabaseClient.VendorBranches { static func live(database db: any Database) -> Self { .init { create in let model = try create.toModel() try await model.save(on: db) return try model.toDTO() } delete: { id in guard let model = try await VendorBranchModel.find(id, on: db) else { throw NotFoundError() } try await model.delete(on: db) } fetchAll: { request in let query = VendorBranchModel.query(on: db) switch request { case .all: break case let .for(vendorID: vendorID): let branches = try await VendorModel.query(on: db) .filter(\VendorModel.$id == vendorID) .with(\VendorModel.$branches) .first()? .branches .map { try $0.toDTO() } guard let branches else { throw NotFoundError() } return branches } return try await query.all().map { try $0.toDTO() } } fetchAllWithDetail: { try await VendorBranchModel.query(on: db) .with(\VendorBranchModel.$vendor) .all() .map { try $0.toDetail() } } get: { id in try await VendorBranchModel.find(id, on: db).map { try $0.toDTO() } } update: { id, updates in guard let model = try await VendorBranchModel.find(id, on: db) else { throw NotFoundError() } try model.applyUpdates(updates) try await model.save(on: db) return try model.toDTO() } } } extension VendorBranch { struct Migrate: AsyncMigration { let name = "CreateVendorBranch" fileprivate typealias FieldKey = VendorBranch.Key.Field func prepare(on database: any Database) async throws { try await database.schema(VendorBranchModel.schema) .id() .field(.string(FieldKey.name), .string, .required) .field(.string(FieldKey.vendorID), .uuid, .required) .field(.string(FieldKey.createdAt), .datetime) .field(.string(FieldKey.updatedAt), .datetime) .foreignKey(.string(FieldKey.vendorID), references: VendorModel.schema, "id", onDelete: .cascade) .create() } func revert(on database: any Database) async throws { try await database.schema(VendorBranchModel.schema).delete() } } } extension VendorBranch.Create { func toModel() throws -> VendorBranchModel { try validate() return .init(name: name, vendorId: vendorID) } func validate() throws { guard !name.isEmpty else { throw ValidationError(message: "Vendor branch name should not be empty.") } } } extension VendorBranch.Update { func validate() throws { if let name { guard !name.isEmpty else { throw ValidationError(message: "Vendor branch name should not be empty.") } } } } private extension VendorBranch { enum Key { static let schema = "vendor_branch" enum Field { static let createdAt = "created_at" static let name = "name" static let updatedAt = "updated_at" static let vendorID = "vendor_id" } } } final class VendorBranchModel: Model, @unchecked Sendable { fileprivate typealias FieldKey = VendorBranch.Key.Field static let schema = VendorBranch.Key.schema @ID(key: .id) var id: UUID? @Field(key: .string(FieldKey.name)) var name: String @Timestamp(key: .string(FieldKey.createdAt), on: .create, format: .iso8601) var createdAt: Date? @Timestamp(key: .string(FieldKey.updatedAt), on: .update, format: .iso8601) var updatedAt: Date? @Parent(key: .string(FieldKey.vendorID)) var vendor: VendorModel init() {} init(id: UUID? = nil, name: String, vendorId: Vendor.ID) { self.id = id self.name = name $vendor.id = vendorId } func toDTO() throws -> VendorBranch { try .init( id: requireID(), name: name, vendorID: $vendor.id, createdAt: createdAt, updatedAt: updatedAt ) } func toDetail() throws -> VendorBranch.Detail { try .init( id: requireID(), name: name, vendor: vendor.toDTO(), createdAt: createdAt, updatedAt: updatedAt ) } func applyUpdates(_ updates: VendorBranch.Update) throws { try updates.validate() if let name = updates.name { self.name = name } } }