From 31c6b5137189bd364e45b19f364336d4dc385955 Mon Sep 17 00:00:00 2001 From: Michael Housh Date: Tue, 14 Jan 2025 13:10:24 -0500 Subject: [PATCH] feat: Updates api controllers to use database client. --- .../Api/EmployeeApiController.swift | 134 +++++++++--------- .../Api/PurchaseOrderApiController.swift | 120 ++++++++-------- .../Controllers/Api/UserApiController.swift | 18 +-- .../Controllers/Api/VendorApiController.swift | 108 +++++++------- .../Api/VendorBranchApiController.swift | 132 +++++++++-------- Sources/App/Controllers/ApiController.swift | 8 +- .../Extensions/RouteBuilder+protected.swift | 3 - Sources/App/configure.swift | 23 ++- Sources/DatabaseClient/Employees.swift | 7 +- Sources/DatabaseClient/PurchaseOrders.swift | 4 + Sources/DatabaseClient/Users.swift | 9 +- Sources/DatabaseClient/VendorBranches.swift | 6 + Sources/DatabaseClient/Vendors.swift | 7 + Sources/DatabaseClientLive/Employees.swift | 1 + Sources/DatabaseClientLive/Users.swift | 24 +++- Sources/DatabaseClientLive/Vendors.swift | 1 + Sources/SharedModels/Vendor.swift | 11 -- 17 files changed, 313 insertions(+), 303 deletions(-) diff --git a/Sources/App/Controllers/Api/EmployeeApiController.swift b/Sources/App/Controllers/Api/EmployeeApiController.swift index 66b27b3..4c479f7 100644 --- a/Sources/App/Controllers/Api/EmployeeApiController.swift +++ b/Sources/App/Controllers/Api/EmployeeApiController.swift @@ -1,64 +1,70 @@ -// import Dependencies -// import Fluent -// import Vapor -// -// struct EmployeeApiController: RouteCollection { -// -// @Dependency(\.employees) var employees -// -// func boot(routes: any RoutesBuilder) throws { -// let protected = routes.apiProtected(route: "employees") -// protected.get(use: index(req:)) -// protected.post(use: create(req:)) -// protected.group(":employeeID") { -// $0.get(use: get(req:)) -// $0.put(use: update(req:)) -// $0.delete(use: delete(req:)) -// } -// } -// -// @Sendable -// func index(req: Request) async throws -> [Employee.DTO] { -// let params = try req.query.decode(EmployeesIndexQuery.self) -// return try await employees.fetchAll(params.active == true ? .active : .default) -// } -// -// @Sendable -// func create(req: Request) async throws -> Employee.DTO { -// try await employees.create( -// req.ensureValidContent(Employee.Create.self) -// ) -// } -// -// @Sendable -// func get(req: Request) async throws -> Employee.DTO { -// guard let id = req.parameters.get("employeeID", as: Employee.IDValue.self), -// let employee = try await employees.get(id) -// else { -// throw Abort(.notFound) -// } -// return employee -// } -// -// @Sendable -// func update(req: Request) async throws -> Employee.DTO { -// guard let employeeID = req.parameters.get("employeeID", as: Employee.IDValue.self) else { -// throw Abort(.badRequest, reason: "Employee id value not provided") -// } -// let updates = try req.ensureValidContent(Employee.Update.self) -// return try await employees.update(employeeID, updates) -// } -// -// @Sendable -// func delete(req: Request) async throws -> HTTPStatus { -// guard let employeeID = req.parameters.get("employeeID", as: Employee.IDValue.self) else { -// throw Abort(.badRequest, reason: "Employee id value not provided") -// } -// try await employees.delete(employeeID) -// return .ok -// } -// } -// -// struct EmployeesIndexQuery: Content { -// let active: Bool? -// } +import DatabaseClient +import Dependencies +import SharedModels +import Vapor + +struct EmployeeApiController: RouteCollection { + + @Dependency(\.database.employees) var employees + + func boot(routes: any RoutesBuilder) throws { + let protected = routes.apiProtected(route: "employees") + protected.get(use: index(req:)) + protected.post(use: create(req:)) + protected.group(":id") { + $0.get(use: get(req:)) + $0.put(use: update(req:)) + $0.delete(use: delete(req:)) + } + } + + @Sendable + func index(req: Request) async throws -> [Employee] { + let params = try req.query.decode(EmployeesIndexQuery.self) + return try await employees.fetchAll(params.request) + } + + @Sendable + func create(req: Request) async throws -> Employee { + try await employees.create( + req.content.decode(Employee.Create.self) + ) + } + + @Sendable + func get(req: Request) async throws -> Employee { + guard let employee = try await employees.get(req.ensureIDPathComponent()) else { + throw Abort(.notFound) + } + return employee + } + + @Sendable + func update(req: Request) async throws -> Employee { + return try await employees.update( + req.ensureIDPathComponent(), + req.content.decode(Employee.Update.self) + ) + } + + @Sendable + func delete(req: Request) async throws -> HTTPStatus { + try await employees.delete(req.ensureIDPathComponent()) + return .ok + } +} + +struct EmployeesIndexQuery: Content { + let active: Bool? + + var request: DatabaseClient.Employees.FetchRequest { + switch active { + case .none: + return .all + case .some(true): + return .active + case .some(false): + return .inactive + } + } +} diff --git a/Sources/App/Controllers/Api/PurchaseOrderApiController.swift b/Sources/App/Controllers/Api/PurchaseOrderApiController.swift index b9b9b18..ce84f34 100644 --- a/Sources/App/Controllers/Api/PurchaseOrderApiController.swift +++ b/Sources/App/Controllers/Api/PurchaseOrderApiController.swift @@ -1,61 +1,59 @@ -// import Dependencies -// import Fluent -// import Vapor -// -// // TODO: Add update route. -// -// struct PurchaseOrderApiController: RouteCollection { -// -// @Dependency(\.purchaseOrders) var purchaseOrders -// -// func boot(routes: any RoutesBuilder) throws { -// let protected = routes.apiProtected(route: "purchase-orders") -// protected.get(use: index(req:)) -// protected.post(use: create(req:)) -// protected.group(":id") { -// $0.get(use: get(req:)) -// $0.delete(use: delete(req:)) -// } -// } -// -// @Sendable -// func index(req: Request) async throws -> [PurchaseOrder.DTO] { -// try await purchaseOrders.fetchAll() -// } -// -// @Sendable -// func create(req: Request) async throws -> PurchaseOrder.DTO { -// try await purchaseOrders.create( -// req.ensureValidContent(PurchaseOrder.Create.self), -// req.auth.require(User.self).requireID() -// ) -// } -// -// @Sendable -// func get(req: Request) async throws -> PurchaseOrder.DTO { -// guard let id = req.parameters.get("id", as: PurchaseOrder.IDValue.self), -// let purchaseOrder = try await purchaseOrders.get(id) -// else { -// throw Abort(.notFound) -// } -// return purchaseOrder -// } -// -// @Sendable -// func delete(req: Request) async throws -> HTTPStatus { -// guard let id = req.parameters.get("id", as: PurchaseOrder.IDValue.self) else { -// throw Abort(.badRequest, reason: "Purchase order id not provided.") -// } -// try await purchaseOrders.delete(id) -// return .ok -// } -// -// // @Sendable -// // func update(req: Request) async throws -> PurchaseOrder.DTO { -// // guard let id = req.parameters.get("id", as: PurchaseOrder.IDValue.self) else { -// // throw Abort(.badRequest, reason: "Purchase order id not provided.") -// // } -// // try await purchaseOrders.delete(id: id, on: req.db) -// // return .ok -// // } -// } +import DatabaseClient +import Dependencies +import Fluent +import SharedModels +import Vapor + +// TODO: Add update route. + +struct PurchaseOrderApiController: RouteCollection { + + @Dependency(\.database.purchaseOrders) var purchaseOrders + + func boot(routes: any RoutesBuilder) throws { + let protected = routes.apiProtected(route: "purchase-orders") + protected.get(use: index(req:)) + protected.post(use: create(req:)) + protected.group(":id") { + $0.get(use: get(req:)) + $0.delete(use: delete(req:)) + } + } + + @Sendable + func index(req: Request) async throws -> [PurchaseOrder] { + try await purchaseOrders.fetchAll() + } + + @Sendable + func create(req: Request) async throws -> PurchaseOrder { + try await purchaseOrders.create( + req.content.decode(PurchaseOrder.Create.self), + req.auth.require(User.self).id + ) + } + + @Sendable + func get(req: Request) async throws -> PurchaseOrder { + guard let purchaseOrder = try await purchaseOrders.get(req.ensureIDPathComponent(as: Int.self)) + else { + throw Abort(.notFound) + } + return purchaseOrder + } + + @Sendable + func delete(req: Request) async throws -> HTTPStatus { + try await purchaseOrders.delete(req.ensureIDPathComponent(as: Int.self)) + return .ok + } + + // @Sendable + // func update(req: Request) async throws -> PurchaseOrder.DTO { + // guard let id = req.parameters.get("id", as: PurchaseOrder.ID.self) else { + // throw Abort(.badRequest, reason: "Purchase order id not provided.") + // } + // try await purchaseOrders.delete(id: id, on: req.db) + // return .ok + // } +} diff --git a/Sources/App/Controllers/Api/UserApiController.swift b/Sources/App/Controllers/Api/UserApiController.swift index 63cc6d7..8cb1f6c 100644 --- a/Sources/App/Controllers/Api/UserApiController.swift +++ b/Sources/App/Controllers/Api/UserApiController.swift @@ -29,7 +29,6 @@ struct UserApiController: RouteCollection { @Sendable func create(req: Request) async throws -> User { // Allow the first user to be created without authentication. - // let count = try await User.query(on: req.db).count() let count = try await users.count() if count > 0 { guard req.auth.get(User.self) != nil else { @@ -40,25 +39,14 @@ struct UserApiController: RouteCollection { } @Sendable - func login(req: Request) async throws -> User { + func login(req: Request) async throws -> User.Token { let user = try req.auth.require(User.self) - return user - // return try await users.login(user) + return try await users.token(user.id) } - // @Sendable - // func get(req: Request) async throws -> User.DTO { - // guard let id = req.parameters.get("id", as: User.IDValue.self), - // let user = users. - // } - @Sendable func delete(req: Request) async throws -> HTTPStatus { - // guard let id = req.parameters.get("id", as: User.IDValue.self) else { - // throw Abort(.badRequest, reason: "User id not provided") - // } - let id = try req.ensureIDPathComponent() - try await users.delete(id) + try await users.delete(req.ensureIDPathComponent()) return .ok } } diff --git a/Sources/App/Controllers/Api/VendorApiController.swift b/Sources/App/Controllers/Api/VendorApiController.swift index d52c865..5395290 100644 --- a/Sources/App/Controllers/Api/VendorApiController.swift +++ b/Sources/App/Controllers/Api/VendorApiController.swift @@ -1,57 +1,51 @@ -// import Dependencies -// import Fluent -// import Vapor -// -// struct VendorApiController: RouteCollection { -// -// @Dependency(\.vendors) var vendors -// -// func boot(routes: any RoutesBuilder) throws { -// let protected = routes.apiProtected(route: "vendors") -// protected.get(use: index(req:)) -// protected.post(use: create(req:)) -// protected.group(":id") { -// $0.put(use: update(req:)) -// $0.delete(use: delete(req:)) -// } -// } -// -// @Sendable -// func index(req: Request) async throws -> [Vendor.DTO] { -// let params = try req.query.decode(VendorsIndexQuery.self) -// return try await vendors.fetchAll(params.fetchRequest) -// } -// -// @Sendable -// func create(req: Request) async throws -> Vendor.DTO { -// try await vendors.create(req.ensureValidContent(Vendor.Create.self)) -// } -// -// @Sendable -// func update(req: Request) async throws -> Vendor.DTO { -// guard let id = req.parameters.get("id", as: Vendor.IDValue.self) else { -// throw Abort(.badRequest, reason: "Vendor id not provided.") -// } -// try Vendor.Update.validate(content: req) -// let updates = try req.content.decode(Vendor.Update.self) -// return try await vendors.update(id, updates) -// } -// -// @Sendable -// func delete(req: Request) async throws -> HTTPStatus { -// guard let id = req.parameters.get("id", as: Vendor.IDValue.self) else { -// throw Abort(.badRequest, reason: "Vendor id not provided.") -// } -// try await vendors.delete(id) -// return .ok -// } -// } -// -// struct VendorsIndexQuery: Content { -// let branches: Bool? -// -// var fetchRequest: VendorDB.FetchRequest { -// if branches == true { return .withBranches } -// return .default -// } -// } +import DatabaseClient +import Dependencies +import Fluent +import SharedModels +import Vapor + +struct VendorApiController: RouteCollection { + + @Dependency(\.database.vendors) var vendors + + func boot(routes: any RoutesBuilder) throws { + let protected = routes.apiProtected(route: "vendors") + protected.get(use: index(req:)) + protected.post(use: create(req:)) + protected.group(":id") { + $0.put(use: update(req:)) + $0.delete(use: delete(req:)) + } + } + + @Sendable + func index(req: Request) async throws -> [Vendor] { + let params = try req.query.decode(VendorsIndexQuery.self) + return try await vendors.fetchAll(params.request) + } + + @Sendable + func create(req: Request) async throws -> Vendor { + try await vendors.create(req.content.decode(Vendor.Create.self)) + } + + @Sendable + func update(req: Request) async throws -> Vendor { + return try await vendors.update(req.ensureIDPathComponent(), req.content.decode(Vendor.Update.self)) + } + + @Sendable + func delete(req: Request) async throws -> HTTPStatus { + try await vendors.delete(req.ensureIDPathComponent()) + return .ok + } +} + +struct VendorsIndexQuery: Content { + let branches: Bool? + + var request: DatabaseClient.Vendors.FetchRequest { + if branches == true { return .withBranches } + return .all + } +} diff --git a/Sources/App/Controllers/Api/VendorBranchApiController.swift b/Sources/App/Controllers/Api/VendorBranchApiController.swift index 251fc60..bde2a62 100644 --- a/Sources/App/Controllers/Api/VendorBranchApiController.swift +++ b/Sources/App/Controllers/Api/VendorBranchApiController.swift @@ -1,67 +1,65 @@ -// import Dependencies -// import Fluent -// import Vapor -// -// struct VendorBranchApiController: RouteCollection { -// -// @Dependency(\.vendorBranches) var vendorBranches -// -// func boot(routes: any RoutesBuilder) throws { -// let prefix = routes.apiProtected(route: "vendors") -// let root = prefix.grouped("branches") -// root.get(use: index(req:)) -// root.group(":id") { -// $0.put(use: update(req:)) -// $0.delete(use: delete(req:)) -// } -// -// prefix.group(":vendorID", "branches") { -// $0.get(use: indexForVendor(req:)) -// $0.post(use: create(req:)) -// } -// } -// -// @Sendable -// func index(req: Request) async throws -> [VendorBranch.DTO] { -// try await vendorBranches.fetchAll() -// } -// -// @Sendable -// func indexForVendor(req: Request) async throws -> [VendorBranch.DTO] { -// guard let id = req.parameters.get("vendorID", as: Vendor.IDValue.self) else { -// throw Abort(.badRequest, reason: "Vendor id not provided.") -// } -// return try await vendorBranches.fetchAll(.for(vendorID: id)) -// } -// -// @Sendable -// func create(req: Request) async throws -> VendorBranch.DTO { -// guard let id = req.parameters.get("vendorID", as: Vendor.IDValue.self) else { -// throw Abort(.badRequest, reason: "Vendor id not provided.") -// } -// return try await vendorBranches.create( -// req.ensureValidContent(VendorBranch.Create.self), -// id -// ) -// } -// -// @Sendable -// func update(req: Request) async throws -> VendorBranch.DTO { -// guard let id = req.parameters.get("id", as: VendorBranch.IDValue.self) else { -// throw Abort(.badRequest, reason: "Vendor branch id not provided.") -// } -// try VendorBranch.Update.validate(content: req) -// let updates = try req.content.decode(VendorBranch.Update.self) -// return try await vendorBranches.update(id, updates) -// } -// -// @Sendable -// func delete(req: Request) async throws -> HTTPStatus { -// guard let id = req.parameters.get("id", as: VendorBranch.IDValue.self) else { -// throw Abort(.badRequest, reason: "Vendor branch id not provided.") -// } -// try await vendorBranches.delete(id) -// return .ok -// } -// -// } +import DatabaseClient +import Dependencies +import Fluent +import SharedModels +import Vapor + +struct VendorBranchApiController: RouteCollection { + + @Dependency(\.database.vendorBranches) var vendorBranches + + func boot(routes: any RoutesBuilder) throws { + let prefix = routes.apiProtected(route: "vendors") + let root = prefix.grouped("branches") + root.get(use: index(req:)) + root.group(":id") { + $0.put(use: update(req:)) + $0.delete(use: delete(req:)) + } + prefix.group(":vendorID", "branches") { + $0.get(use: indexForVendor(req:)) + $0.post(use: create(req:)) + } + } + + @Sendable + func index(req: Request) async throws -> [VendorBranch] { + try await vendorBranches.fetchAll() + } + + @Sendable + func indexForVendor(req: Request) async throws -> [VendorBranch] { + guard let id = req.parameters.get("vendorID", as: Vendor.ID.self) else { + throw Abort(.badRequest, reason: "Vendor id not provided.") + } + return try await vendorBranches.fetchAll(.for(vendorID: id)) + } + + @Sendable + func create(req: Request) async throws -> VendorBranch { + let id = try req.ensureIDPathComponent(key: "vendorID") + let content = try req.content.decode(BranchCreateRequest.self) + return try await vendorBranches.create( + .init(name: content.name, vendorID: id) + ) + } + + @Sendable + func update(req: Request) async throws -> VendorBranch { + return try await vendorBranches.update( + req.ensureIDPathComponent(), + req.content.decode(VendorBranch.Update.self) + ) + } + + @Sendable + func delete(req: Request) async throws -> HTTPStatus { + try await vendorBranches.delete(req.ensureIDPathComponent()) + return .ok + } + +} + +private struct BranchCreateRequest: Content { + let name: String +} diff --git a/Sources/App/Controllers/ApiController.swift b/Sources/App/Controllers/ApiController.swift index 2e34aa8..bd49309 100644 --- a/Sources/App/Controllers/ApiController.swift +++ b/Sources/App/Controllers/ApiController.swift @@ -3,10 +3,10 @@ import Vapor struct ApiController: RouteCollection { func boot(routes: any RoutesBuilder) throws { - // try routes.register(collection: EmployeeApiController()) - // try routes.register(collection: PurchaseOrderApiController()) + try routes.register(collection: EmployeeApiController()) + try routes.register(collection: PurchaseOrderApiController()) try routes.register(collection: UserApiController()) - // try routes.register(collection: VendorApiController()) - // try routes.register(collection: VendorBranchApiController()) + try routes.register(collection: VendorApiController()) + try routes.register(collection: VendorBranchApiController()) } } diff --git a/Sources/App/Extensions/RouteBuilder+protected.swift b/Sources/App/Extensions/RouteBuilder+protected.swift index d608e3e..8ed0a0b 100644 --- a/Sources/App/Extensions/RouteBuilder+protected.swift +++ b/Sources/App/Extensions/RouteBuilder+protected.swift @@ -11,7 +11,6 @@ extension RoutesBuilder { // return self // #else return grouped( - // User.credentialsAuthenticator(), UserPasswordAuthenticator(), UserTokenAuthenticator(), UserSessionAuthenticator(), @@ -36,8 +35,6 @@ extension RoutesBuilder { return prefixed.grouped( UserPasswordAuthenticator(), UserTokenAuthenticator(), - // User.authenticator(), - // UserToken.authenticator(), User.guardMiddleware() ) // #endif diff --git a/Sources/App/configure.swift b/Sources/App/configure.swift index 7215301..562106f 100644 --- a/Sources/App/configure.swift +++ b/Sources/App/configure.swift @@ -9,10 +9,19 @@ import Vapor // configures your application public func configure(_ app: Application) async throws { + // cors middleware should come before default error middleware using `at: .beginning` + let corsConfiguration = CORSMiddleware.Configuration( + allowedOrigin: .all, + allowedMethods: [.GET, .POST, .PUT, .OPTIONS, .DELETE, .PATCH], + allowedHeaders: [.accept, .authorization, .contentType, .origin, + .xRequestedWith, .userAgent, .accessControlAllowOrigin] + ) + let cors = CORSMiddleware(configuration: corsConfiguration) + app.middleware.use(cors, at: .beginning) + // uncomment to serve files from /Public folder app.middleware.use(FileMiddleware(publicDirectory: app.directory.publicDirectory)) app.middleware.use(app.sessions.middleware) - // app.middleware.use(User.sessionAuthenticator()) #if DEBUG app.lifecycle.use(BrowserSyncHandler()) @@ -28,22 +37,10 @@ public func configure(_ app: Application) async throws { let databaseClient = DatabaseClient.live(database: app.db) try await app.migrations.add(databaseClient.migrations()) - // app.migrations.add(Vendor.Migrate()) - // app.migrations.add(VendorBranch.Migrate()) - // app.migrations.add(Employee.Migrate()) - // app.migrations.add(User.Migrate()) - // app.migrations.add(UserToken.Migrate()) - // app.migrations.add(PurchaseOrder.Migrate()) - app.views.use(.leaf) try withDependencies { $0.database = databaseClient - // $0.employees = .live(database: app.db(.sqlite)) - // $0.purchaseOrders = .live(database: app.db(.sqlite)) - // $0.users = .live(database: app.db(.sqlite)) - // $0.vendorBranches = .live(database: app.db(.sqlite)) - // $0.vendors = .live(database: app.db(.sqlite)) } operation: { // register routes try routes(app) diff --git a/Sources/DatabaseClient/Employees.swift b/Sources/DatabaseClient/Employees.swift index 14744ba..e295eb1 100644 --- a/Sources/DatabaseClient/Employees.swift +++ b/Sources/DatabaseClient/Employees.swift @@ -1,6 +1,7 @@ import Dependencies import DependenciesMacros import SharedModels +import Vapor public extension DatabaseClient { @@ -16,7 +17,7 @@ public extension DatabaseClient { try await fetchAll(.all) } - public enum FetchRequest { + public enum FetchRequest: String, Content { case active case all case inactive @@ -24,6 +25,10 @@ public extension DatabaseClient { } } +extension Employee: Content {} +extension Employee.Create: Content {} +extension Employee.Update: Content {} + extension DatabaseClient.Employees: TestDependencyKey { public static let testValue = Self() } diff --git a/Sources/DatabaseClient/PurchaseOrders.swift b/Sources/DatabaseClient/PurchaseOrders.swift index c31e65a..9381372 100644 --- a/Sources/DatabaseClient/PurchaseOrders.swift +++ b/Sources/DatabaseClient/PurchaseOrders.swift @@ -2,6 +2,7 @@ import Dependencies import DependenciesMacros import Fluent import SharedModels +import Vapor public extension DatabaseClient { @DependencyClient @@ -15,6 +16,9 @@ public extension DatabaseClient { } } +extension PurchaseOrder: Content {} +extension PurchaseOrder.Create: Content {} + extension DatabaseClient.PurchaseOrders: TestDependencyKey { public static let testValue: DatabaseClient.PurchaseOrders = Self() } diff --git a/Sources/DatabaseClient/Users.swift b/Sources/DatabaseClient/Users.swift index f62951f..fa9e3b1 100644 --- a/Sources/DatabaseClient/Users.swift +++ b/Sources/DatabaseClient/Users.swift @@ -15,14 +15,13 @@ public extension DatabaseClient { public var get: @Sendable (User.ID) async throws -> User? public var login: @Sendable (User.Login) async throws -> User.Token public var logout: @Sendable (User.Token.ID) async throws -> Void + public var token: @Sendable (User.ID) async throws -> User.Token } } -public extension DatabaseClient.Users { - enum AuthRequest { - case basic(BasicAuthorization) - } -} +extension User: Content {} +extension User.Create: Content {} +extension User.Token: Content {} extension DatabaseClient.Users: TestDependencyKey { public static let testValue: DatabaseClient.Users = Self() diff --git a/Sources/DatabaseClient/VendorBranches.swift b/Sources/DatabaseClient/VendorBranches.swift index 150a85f..f39be7a 100644 --- a/Sources/DatabaseClient/VendorBranches.swift +++ b/Sources/DatabaseClient/VendorBranches.swift @@ -1,6 +1,7 @@ import Dependencies import DependenciesMacros import SharedModels +import Vapor public extension DatabaseClient { @DependencyClient @@ -23,6 +24,11 @@ public extension DatabaseClient { } } +extension VendorBranch: Content {} +extension VendorBranch.Create: Content {} +extension VendorBranch.Update: Content {} +extension DatabaseClient.VendorBranches.FetchRequest: Content {} + extension DatabaseClient.VendorBranches: TestDependencyKey { public static let testValue: DatabaseClient.VendorBranches = Self() } diff --git a/Sources/DatabaseClient/Vendors.swift b/Sources/DatabaseClient/Vendors.swift index a354e45..0ff9d4f 100644 --- a/Sources/DatabaseClient/Vendors.swift +++ b/Sources/DatabaseClient/Vendors.swift @@ -1,6 +1,7 @@ import Dependencies import DependenciesMacros import SharedModels +import Vapor public extension DatabaseClient { @DependencyClient @@ -31,6 +32,12 @@ public extension DatabaseClient { } } +extension Vendor: Content {} +extension Vendor.Create: Content {} +extension Vendor.Update: Content {} +extension DatabaseClient.Vendors.FetchRequest: Content {} +extension DatabaseClient.Vendors.GetRequest: Content {} + extension DatabaseClient.Vendors: TestDependencyKey { public static let testValue: DatabaseClient.Vendors = Self() } diff --git a/Sources/DatabaseClientLive/Employees.swift b/Sources/DatabaseClientLive/Employees.swift index 39fe72e..3112d04 100644 --- a/Sources/DatabaseClientLive/Employees.swift +++ b/Sources/DatabaseClientLive/Employees.swift @@ -2,6 +2,7 @@ import DatabaseClient import Fluent import Foundation import SharedModels +import Vapor public extension DatabaseClient.Employees { diff --git a/Sources/DatabaseClientLive/Users.swift b/Sources/DatabaseClientLive/Users.swift index d7063ca..40a923d 100644 --- a/Sources/DatabaseClientLive/Users.swift +++ b/Sources/DatabaseClientLive/Users.swift @@ -51,6 +51,21 @@ public extension DatabaseClient.Users { guard let token = try await UserTokenModel.find(id, on: database) else { return } try await token.delete(on: database) + } token: { _ in + guard let user = try await UserModel.query(on: database) + .with(\.$token) + .first() + else { + throw Abort(.notFound) + } + + guard let token = user.token else { + let token = try user.generateToken() + try await token.save(on: database) + return try token.toDTO() + } + + return try token.toDTO() } } } @@ -156,6 +171,9 @@ final class UserModel: Model, @unchecked Sendable { @Timestamp(key: "updated_at", on: .update) var updatedAt: Date? + @OptionalChild(for: \.$user) + var token: UserTokenModel? + init() {} init( @@ -210,6 +228,10 @@ final class UserTokenModel: Model, Codable, @unchecked Sendable { $user.id = userID } + func toDTO() throws -> User.Token { + try .init(id: requireID(), userID: $user.id, value: value) + } + } // MARK: - Authentication @@ -219,8 +241,6 @@ extension User: SessionAuthenticatable { public var sessionID: String { username } } -extension User: Content {} - public struct UserPasswordAuthenticator: AsyncBasicAuthenticator { public typealias User = SharedModels.User diff --git a/Sources/DatabaseClientLive/Vendors.swift b/Sources/DatabaseClientLive/Vendors.swift index 3f381d2..6334827 100644 --- a/Sources/DatabaseClientLive/Vendors.swift +++ b/Sources/DatabaseClientLive/Vendors.swift @@ -2,6 +2,7 @@ import DatabaseClient import FluentKit import Foundation import SharedModels +import Vapor public extension DatabaseClient.Vendors { diff --git a/Sources/SharedModels/Vendor.swift b/Sources/SharedModels/Vendor.swift index a57f511..bc5cb91 100644 --- a/Sources/SharedModels/Vendor.swift +++ b/Sources/SharedModels/Vendor.swift @@ -41,14 +41,3 @@ public extension Vendor { } } } - -// public extension Vendor { -// -// static var mocks: [Self] { -// [ -// .init(name: "Corken"), -// .init(name: "Johnstone"), -// .init(name: "Winstel Controls") -// ] -// } -// }