import Dependencies import DependenciesMacros import Fluent import Vapor extension DependencyValues { // An intermediate layer between our api and view controllers that interacts with the // database model. var employees: EmployeeDB { get { self[EmployeeDB.self] } set { self[EmployeeDB.self] = newValue } } } @DependencyClient struct EmployeeDB: Sendable { var create: @Sendable (Employee.Create) async throws -> Employee.DTO var fetchAll: @Sendable (FetchRequest) async throws -> [Employee.DTO] var get: @Sendable (Employee.IDValue) async throws -> Employee.DTO? var update: @Sendable (Employee.IDValue, Employee.Update) async throws -> Employee.DTO var delete: @Sendable (Employee.IDValue) async throws -> Void var toggleActive: @Sendable (Employee.IDValue) async throws -> Employee.DTO enum FetchRequest { case active case `default` } func fetchAll() async throws -> [Employee.DTO] { try await fetchAll(.default) } func get(_ id: String?) async throws -> Employee.DTO? { guard let idString = id, let id = UUID(uuidString: idString) else { throw Abort(.badRequest, reason: "Employee id not valid.") } return try await get(id) } } extension EmployeeDB: TestDependencyKey { static let testValue: EmployeeDB = Self() static func live(database: any Database) -> Self { .init( create: { model in let model = model.toModel() try await model.save(on: database) return model.toDTO() }, fetchAll: { request in var query = Employee.query(on: database) .sort(\.$lastName) if request == .active { query = query.filter(\.$active == true) } return try await query.all().map { $0.toDTO() } }, get: { id in try await Employee.find(id, on: database).map { $0.toDTO() } }, update: { id, updates in guard let employee = try await Employee.find(id, on: database) else { throw Abort(.badRequest, reason: "Employee id not found.") } employee.applyUpdates(updates) try await employee.save(on: database) return employee.toDTO() }, delete: { id in guard let employee = try await Employee.find(id, on: database) else { throw Abort(.badRequest, reason: "Employee id not found.") } try await employee.delete(on: database) }, toggleActive: { id in guard let employee = try await Employee.find(id, on: database) else { throw Abort(.notFound) } employee.active.toggle() try await employee.save(on: database) return employee.toDTO() } ) } }