Files
vapor-po/Sources/DatabaseClientLive/Employees.swift

177 lines
4.2 KiB
Swift

import DatabaseClient
import Fluent
import Foundation
import SharedModels
public extension DatabaseClient.Employees {
static func live(database: any Database) -> DatabaseClient.Employees {
.init { create in
let model = try create.toModel()
try await model.save(on: database)
return try model.toDTO()
} delete: { id in
guard let model = try await EmployeeModel.find(id, on: database) else {
throw NotFoundError()
}
try await model.delete(on: database)
} fetchAll: { request in
var query = EmployeeModel.query(on: database)
.sort(\.$lastName)
switch request {
case .active:
query = query.filter(\.$active == true)
case .inactive:
query = query.filter(\.$active == false)
case .all:
break
}
return try await query.all().map { try $0.toDTO() }
} get: { id in
try await EmployeeModel.find(id, on: database).map { try $0.toDTO() }
} update: { id, updates in
guard let model = try await EmployeeModel.find(id, on: database) else {
throw NotFoundError()
}
try model.applyUpdate(updates)
try await model.save(on: database)
return try model.toDTO()
}
}
}
private extension Employee.Create {
func toModel() throws -> EmployeeModel {
try validate()
return .init(firstName: firstName, lastName: lastName, active: active ?? true)
}
func validate() throws {
guard !firstName.isEmpty else {
throw ValidationError(message: "Employee first name should not be empty.")
}
guard !lastName.isEmpty else {
throw ValidationError(message: "Employee first name should not be empty.")
}
}
}
extension Employee.Update {
func validate() throws {
if let firstName {
guard !firstName.isEmpty else {
throw ValidationError(message: "Employee first name should not be empty.")
}
}
if let lastName {
guard !lastName.isEmpty else {
throw ValidationError(message: "Employee first name should not be empty.")
}
}
}
}
extension Employee {
struct Migrate: AsyncMigration {
let name = "CreateEmployee"
func prepare(on database: any Database) async throws {
try await database.schema(EmployeeModel.schema)
.id()
.field("first_name", .string, .required)
.field("last_name", .string, .required)
.field("is_active", .bool, .required)
.field("created_at", .datetime)
.field("updated_at", .datetime)
.unique(on: "first_name", "last_name")
.create()
}
func revert(on database: any Database) async throws {
try await database.schema(EmployeeModel.schema).delete()
}
}
}
/// The employee database model.
///
/// An employee is someone that PO's can be generated for. They can be either a field
/// employee / technician, an office employee, or an administrator.
///
/// # NOTE: Only `User` types can login and generate po's for employees.
///
final class EmployeeModel: Model, @unchecked Sendable {
static let schema = "employee"
// @ID(key: ")
// var id: UUID?
@ID(key: .id)
var id: UUID?
@Field(key: "first_name")
var firstName: String
@Field(key: "last_name")
var lastName: String
@Field(key: "is_active")
var active: Bool
@Timestamp(key: "created_at", on: .create)
var createdAt: Date?
@Timestamp(key: "updated_at", on: .update)
var updatedAt: Date?
init() {}
init(
id: UUID? = nil,
firstName: String,
lastName: String,
active: Bool,
createdAt: Date? = nil,
updatedAt: Date? = nil
) {
self.id = id
self.firstName = firstName
self.lastName = lastName
self.active = active
self.createdAt = createdAt
self.updatedAt = updatedAt
}
func toDTO() throws -> Employee {
try .init(
id: requireID(),
active: active,
createdAt: createdAt!,
firstName: firstName,
lastName: lastName,
updatedAt: updatedAt!
)
}
func applyUpdate(_ updates: Employee.Update) throws {
try updates.validate()
if let firstName = updates.firstName {
self.firstName = firstName
}
if let lastName = updates.lastName {
self.lastName = lastName
}
if let active = updates.active {
self.active = active
}
}
}