diff --git a/Sources/App/Controllers/ApiController.swift b/Sources/App/Controllers/ApiController.swift index 8ec658a..9eb3af1 100644 --- a/Sources/App/Controllers/ApiController.swift +++ b/Sources/App/Controllers/ApiController.swift @@ -57,101 +57,116 @@ extension ApiRoute.EmployeeRoute { } } -extension BaseRoute.PurchaseOrderRoute { +extension ApiRoute.PurchaseOrderRoute { func handleApiRequest(request: Request) async throws -> any AsyncResponseEncodable { @Dependency(\.database.purchaseOrders) var purchaseOrders switch self { - case .index: - return try await purchaseOrders.fetchAll() - case let .create(purchaseOrder): - return try await purchaseOrders.create(purchaseOrder) case let .delete(id: id): try await purchaseOrders.delete(id) return HTTPStatus.ok - case let .get(id: id): - guard let output = try await purchaseOrders.get(id) else { - throw Abort(.badRequest, reason: "Purchase order not found.") + case let .base(route): + switch route { + case .index: + return try await purchaseOrders.fetchAll() + case let .create(purchaseOrder): + return try await purchaseOrders.create(purchaseOrder) + case let .get(id: id): + guard let output = try await purchaseOrders.get(id) else { + throw Abort(.badRequest, reason: "Purchase order not found.") + } + return output + case let .page(page: page, limit: limit): + return try await purchaseOrders.fetchPage(.init(page: page, per: limit)) } - return output - case let .page(page: page, limit: limit): - return try await purchaseOrders.fetchPage(.init(page: page, per: limit)) } } } // TODO: Add Login. -extension BaseRoute.UserRoute { +extension ApiRoute.UserRoute { func handleApiRequest(request: Request) async throws -> any AsyncResponseEncodable { @Dependency(\.database.users) var users switch self { - case let .create(user): - return try await users.create(user) case let .delete(id: id): try await users.delete(id) return HTTPStatus.ok - case .index: - return try await users.fetchAll() - case let .get(id: id): - guard let user = try await users.get(id) else { - throw Abort(.badRequest, reason: "Employee not found") + + case let .base(route): + switch route { + case let .create(user): + return try await users.create(user) + case .index: + return try await users.fetchAll() + case let .get(id: id): + guard let user = try await users.get(id) else { + throw Abort(.badRequest, reason: "Employee not found") + } + return user + // case let .login(user): + // return try await users.login(user) + case let .update(id: id, updates: updates): + return try await users.update(id, updates) } - return user - // case let .login(user): - // return try await users.login(user) - case let .update(id: id, updates: updates): - return try await users.update(id, updates) } } } -extension BaseRoute.VendorRoute { +extension ApiRoute.VendorRoute { func handleApiRequest(request: Request) async throws -> any AsyncResponseEncodable { @Dependency(\.database.vendors) var vendors switch self { - case let .create(vendor): - return try await vendors.create(vendor) case let .delete(id: id): try await vendors.delete(id) return HTTPStatus.ok - case let .get(id: id): - guard let vendor = try await vendors.get(id) else { - throw Abort(.badRequest, reason: "Employee not found") + + case let .base(route): + switch route { + case let .create(vendor): + return try await vendors.create(vendor) + case let .get(id: id): + guard let vendor = try await vendors.get(id) else { + throw Abort(.badRequest, reason: "Employee not found") + } + return vendor + case let .update(id: id, updates: updates): + return try await vendors.update(id, with: updates) + case let .index(withBranches: withBranches): + guard withBranches == true else { + return try await vendors.fetchAll() + } + return try await vendors.fetchAll(.withBranches) } - return vendor - case let .update(id: id, updates: updates): - return try await vendors.update(id, with: updates) - case let .index(withBranches: withBranches): - guard withBranches == true else { - return try await vendors.fetchAll() - } - return try await vendors.fetchAll(.withBranches) } } } -extension BaseRoute.VendorBranchRoute { +extension ApiRoute.VendorBranchRoute { func handleApiRequest(request: Request) async throws -> any AsyncResponseEncodable { @Dependency(\.database.vendorBranches) var vendorBranches switch self { - case let .create(branch): - return try await vendorBranches.create(branch) case let .delete(id: id): try await vendorBranches.delete(id) return HTTPStatus.ok - case let .index(for: optionalVendorID): - guard let vendorID = optionalVendorID else { - return try await vendorBranches.fetchAll() + + case let .base(route): + switch route { + case let .create(branch): + return try await vendorBranches.create(branch) + case let .index(for: optionalVendorID): + guard let vendorID = optionalVendorID else { + return try await vendorBranches.fetchAll() + } + return try await vendorBranches.fetchAll(.for(vendorID: vendorID)) + case let .get(id: id): + guard let branch = try await vendorBranches.get(id) else { + throw Abort(.badRequest, reason: "Employee not found") + } + return branch + case let .update(id: id, updates: updates): + return try await vendorBranches.update(id, updates) } - return try await vendorBranches.fetchAll(.for(vendorID: vendorID)) - case let .get(id: id): - guard let branch = try await vendorBranches.get(id) else { - throw Abort(.badRequest, reason: "Employee not found") - } - return branch - case let .update(id: id, updates: updates): - return try await vendorBranches.update(id, updates) } } } diff --git a/Sources/SharedModels/Routes/ApiRoute.swift b/Sources/SharedModels/Routes/ApiRoute.swift index 128c637..99423cb 100644 --- a/Sources/SharedModels/Routes/ApiRoute.swift +++ b/Sources/SharedModels/Routes/ApiRoute.swift @@ -2,15 +2,13 @@ import CasePaths import Foundation @preconcurrency import URLRouting -@CasePathable -@dynamicMemberLookup public enum ApiRoute: Sendable, Equatable { case employee(EmployeeRoute) - case purchaseOrder(BaseRoute.PurchaseOrderRoute) - case user(BaseRoute.UserRoute) - case vendor(BaseRoute.VendorRoute) - case vendorBranch(BaseRoute.VendorBranchRoute) + case purchaseOrder(PurchaseOrderRoute) + case user(UserRoute) + case vendor(VendorRoute) + case vendorBranch(VendorBranchRoute) static let rootPath = Path { "api"; "v1" } @@ -21,24 +19,22 @@ public enum ApiRoute: Sendable, Equatable { } Route(.case(Self.purchaseOrder)) { rootPath - BaseRoute.PurchaseOrderRoute.router + PurchaseOrderRoute.router } Route(.case(Self.user)) { rootPath - BaseRoute.UserRoute.router + UserRoute.router } Route(.case(Self.vendor)) { rootPath - BaseRoute.VendorRoute.router + VendorRoute.router } Route(.case(Self.vendorBranch)) { rootPath - BaseRoute.VendorBranchRoute.router + VendorBranchRoute.router } } - @CasePathable - @dynamicMemberLookup public enum EmployeeRoute: Sendable, Equatable { case base(BaseRoute.EmployeeRoute) case delete(id: Employee.ID) @@ -48,7 +44,67 @@ public enum ApiRoute: Sendable, Equatable { BaseRoute.EmployeeRoute.router } Route(.case(Self.delete(id:))) { - Path { BaseRoute.EmployeeRoute.rootPath; UUID.parser() } + Path { BaseRoute.EmployeeRoute.rootPath; Employee.ID.parser() } + Method.delete + } + } + } + + public enum PurchaseOrderRoute: Sendable, Equatable { + case base(BaseRoute.PurchaseOrderRoute) + case delete(id: PurchaseOrder.ID) + + public static let router = OneOf { + Route(.case(Self.base)) { + BaseRoute.PurchaseOrderRoute.router + } + Route(.case(Self.delete(id:))) { + Path { BaseRoute.PurchaseOrderRoute.rootPath; PurchaseOrder.ID.parser() } + Method.delete + } + } + } + + public enum UserRoute: Sendable, Equatable { + case base(BaseRoute.UserRoute) + case delete(id: User.ID) + + public static let router = OneOf { + Route(.case(Self.base)) { + BaseRoute.UserRoute.router + } + Route(.case(Self.delete(id:))) { + Path { BaseRoute.UserRoute.rootPath; User.ID.parser() } + Method.delete + } + } + } + + public enum VendorRoute: Sendable, Equatable { + case base(BaseRoute.VendorRoute) + case delete(id: Vendor.ID) + + public static let router = OneOf { + Route(.case(Self.base)) { + BaseRoute.VendorRoute.router + } + Route(.case(Self.delete(id:))) { + Path { BaseRoute.VendorRoute.rootPath; Vendor.ID.parser() } + Method.delete + } + } + } + + public enum VendorBranchRoute: Sendable, Equatable { + case base(BaseRoute.VendorBranchRoute) + case delete(id: VendorBranch.ID) + + public static let router = OneOf { + Route(.case(Self.base)) { + BaseRoute.VendorBranchRoute.router + } + Route(.case(Self.delete(id:))) { + Path { "vendors"; "branches"; VendorBranch.ID.parser() } Method.delete } } diff --git a/Sources/SharedModels/Routes/BaseRoutes.swift b/Sources/SharedModels/Routes/BaseRoutes.swift index 517941b..bce91f4 100644 --- a/Sources/SharedModels/Routes/BaseRoutes.swift +++ b/Sources/SharedModels/Routes/BaseRoutes.swift @@ -5,9 +5,6 @@ import Foundation public enum BaseRoute {} public extension BaseRoute { - - @CasePathable - @dynamicMemberLookup enum EmployeeRoute: Sendable, Equatable { case create(Employee.Create) case get(id: Employee.ID) @@ -37,11 +34,11 @@ public extension BaseRoute { Method.get } Route(.case(Self.get(id:))) { - Path { rootPath; UUID.parser() } + Path { rootPath; Employee.ID.parser() } Method.get } Route(.case(Self.update(id:updates:))) { - Path { rootPath; UUID.parser() } + Path { rootPath; Employee.ID.parser() } Method.put OneOf { Body(.json(Employee.Update.self)) @@ -62,7 +59,6 @@ public extension BaseRoute { public extension BaseRoute { enum PurchaseOrderRoute: Sendable, Equatable { case create(PurchaseOrder.Create) - case delete(id: PurchaseOrder.ID) case get(id: PurchaseOrder.ID) case index case page(page: Int, limit: Int) @@ -90,10 +86,6 @@ public extension BaseRoute { } } } - Route(.case(Self.delete(id:))) { - Path { rootPath; Digits() } - Method.delete - } Route(.case(Self.get(id:))) { Path { rootPath; Digits() } Method.get @@ -117,7 +109,6 @@ public extension BaseRoute { public extension BaseRoute { enum UserRoute: Sendable, Equatable { case create(User.Create) - case delete(id: User.ID) case get(id: User.ID) case index case update(id: User.ID, updates: User.Update) @@ -141,10 +132,6 @@ public extension BaseRoute { } } } - Route(.case(Self.delete(id:))) { - Path { rootPath; User.ID.parser() } - Method.delete - } Route(.case(Self.get(id:))) { Path { rootPath; User.ID.parser() } Method.get @@ -178,7 +165,6 @@ public extension BaseRoute { enum VendorRoute: Sendable, Equatable { case index(withBranches: Bool? = nil) case create(Vendor.Create) - case delete(id: Vendor.ID) case get(id: Vendor.ID) case update(id: Vendor.ID, updates: Vendor.Update) @@ -198,10 +184,6 @@ public extension BaseRoute { } } } - Route(.case(Self.delete(id:))) { - Path { rootPath; Vendor.ID.parser() } - Method.delete - } Route(.case(Self.get(id:))) { Path { rootPath; Vendor.ID.parser() } Method.get @@ -237,7 +219,6 @@ public extension BaseRoute { public extension BaseRoute { enum VendorBranchRoute: Sendable, Equatable { case create(VendorBranch.Create) - case delete(id: VendorBranch.ID) case get(id: VendorBranch.ID) case index(for: Vendor.ID? = nil) case update(id: VendorBranch.ID, updates: VendorBranch.Update) @@ -257,11 +238,6 @@ public extension BaseRoute { } } } - Route(.case(Self.delete(id:))) { - Path { "vendors"; "branches"; VendorBranch.ID.parser() } - Method.delete - } - Route(.case(Self.get(id:))) { Path { "vendors"; "branches"; VendorBranch.ID.parser() } Method.get diff --git a/Tests/ApiRouteTests/EmployeeApiRouteTests.swift b/Tests/ApiRouteTests/EmployeeApiRouteTests.swift index 8c26bde..a27b586 100644 --- a/Tests/ApiRouteTests/EmployeeApiRouteTests.swift +++ b/Tests/ApiRouteTests/EmployeeApiRouteTests.swift @@ -63,6 +63,7 @@ struct EmployeeApiRouteTests { let route = try router.parse(&request) #expect( route == .employee(.base(.index)) + // route == .employee(\.index) ) } diff --git a/Tests/ApiRouteTests/PurchaseOrderApiRouteTests.swift b/Tests/ApiRouteTests/PurchaseOrderApiRouteTests.swift index 05d51f2..110cb2f 100644 --- a/Tests/ApiRouteTests/PurchaseOrderApiRouteTests.swift +++ b/Tests/ApiRouteTests/PurchaseOrderApiRouteTests.swift @@ -29,7 +29,7 @@ struct PurchaseOrderApiRouteTests { body: .init(json.utf8) ) let route = try router.parse(&request) - #expect(route == .purchaseOrder(.create(.init( + #expect(route == .purchaseOrder(.base(.create(.init( id: 1, workOrder: 12345, materials: "some", @@ -38,7 +38,7 @@ struct PurchaseOrderApiRouteTests { createdByID: id, createdForID: id, vendorBranchID: id - )))) + ))))) } @Test @@ -60,7 +60,7 @@ struct PurchaseOrderApiRouteTests { path: "/api/v1/purchase-orders/\(id)" ) let route = try router.parse(&request) - #expect(route == .purchaseOrder(.get(id: id))) + #expect(route == .purchaseOrder(.base(.get(id: id)))) } @Test @@ -70,7 +70,7 @@ struct PurchaseOrderApiRouteTests { path: "/api/v1/purchase-orders" ) let route = try router.parse(&request) - #expect(route == .purchaseOrder(.index)) + #expect(route == .purchaseOrder(.base(.index))) } @Test @@ -80,7 +80,7 @@ struct PurchaseOrderApiRouteTests { path: "/api/v1/purchase-orders/next" ) let route = try router.parse(&request) - #expect(route == .purchaseOrder(.page(page: 1, limit: 25))) + #expect(route == .purchaseOrder(.base(.page(page: 1, limit: 25)))) var request2 = URLRequestData( method: "GET", @@ -88,6 +88,6 @@ struct PurchaseOrderApiRouteTests { query: ["page": ["2"], "limit": ["50"]] ) let route2 = try router.parse(&request2) - #expect(route2 == .purchaseOrder(.page(page: 2, limit: 50))) + #expect(route2 == .purchaseOrder(.base(.page(page: 2, limit: 50)))) } } diff --git a/Tests/ApiRouteTests/UserApiRouteTests.swift b/Tests/ApiRouteTests/UserApiRouteTests.swift index 30669a3..a437087 100644 --- a/Tests/ApiRouteTests/UserApiRouteTests.swift +++ b/Tests/ApiRouteTests/UserApiRouteTests.swift @@ -25,12 +25,12 @@ struct UserApiRouteTests { ) let route = try router.parse(&request) #expect( - route == .user(.create(.init( + route == .user(.base(.create(.init( username: "foo", email: "foo@bar.com", password: "super-secret", confirmPassword: "super-secret" - )))) + ))))) } @Test @@ -52,7 +52,7 @@ struct UserApiRouteTests { path: "/api/v1/users/\(id)" ) let route = try router.parse(&request) - #expect(route == .user(.get(id: id))) + #expect(route == .user(.base(.get(id: id)))) } @Test @@ -62,7 +62,7 @@ struct UserApiRouteTests { path: "/api/v1/users" ) let route = try router.parse(&request) - #expect(route == .user(.index)) + #expect(route == .user(.base(.index))) } @Test @@ -80,6 +80,6 @@ struct UserApiRouteTests { body: .init(json.utf8) ) let route = try router.parse(&request) - #expect(route == .user(.update(id: id, updates: .init(username: "bar", email: "bar@foo.com")))) + #expect(route == .user(.base(.update(id: id, updates: .init(username: "bar", email: "bar@foo.com"))))) } } diff --git a/Tests/ApiRouteTests/VendorApiRouteTests.swift b/Tests/ApiRouteTests/VendorApiRouteTests.swift index 61a41c8..db0aecd 100644 --- a/Tests/ApiRouteTests/VendorApiRouteTests.swift +++ b/Tests/ApiRouteTests/VendorApiRouteTests.swift @@ -21,7 +21,7 @@ struct VendorApiRouteTests { body: .init(json.utf8) ) let route = try router.parse(&request) - #expect(route == .vendor(.create(.init(name: "Test")))) + #expect(route == .vendor(.base(.create(.init(name: "Test"))))) } @Test @@ -43,7 +43,7 @@ struct VendorApiRouteTests { path: "/api/v1/vendors/\(id)" ) let route = try router.parse(&request) - #expect(route == .vendor(.get(id: id))) + #expect(route == .vendor(.base(.get(id: id)))) } @Test @@ -53,7 +53,7 @@ struct VendorApiRouteTests { path: "/api/v1/vendors" ) let route = try router.parse(&request) - #expect(route == .vendor(.index())) + #expect(route == .vendor(.base(.index()))) var request2 = URLRequestData( method: "GET", @@ -61,7 +61,7 @@ struct VendorApiRouteTests { query: ["branches": ["true"]] ) let route2 = try router.parse(&request2) - #expect(route2 == .vendor(.index(withBranches: true))) + #expect(route2 == .vendor(.base(.index(withBranches: true)))) } @Test @@ -78,6 +78,6 @@ struct VendorApiRouteTests { body: .init(json.utf8) ) let route = try router.parse(&request) - #expect(route == .vendor(.update(id: id, updates: .init(name: "Test")))) + #expect(route == .vendor(.base(.update(id: id, updates: .init(name: "Test"))))) } } diff --git a/Tests/ApiRouteTests/VendorBranchApiRouteTests.swift b/Tests/ApiRouteTests/VendorBranchApiRouteTests.swift index 91dd20b..ebdefca 100644 --- a/Tests/ApiRouteTests/VendorBranchApiRouteTests.swift +++ b/Tests/ApiRouteTests/VendorBranchApiRouteTests.swift @@ -23,7 +23,7 @@ struct VendorBranchApiRouteTests { body: .init(json.utf8) ) let route = try router.parse(&request) - #expect(route == .vendorBranch(.create(.init(name: "Test", vendorID: id)))) + #expect(route == .vendorBranch(.base(.create(.init(name: "Test", vendorID: id))))) } @Test @@ -45,7 +45,7 @@ struct VendorBranchApiRouteTests { path: "/api/v1/vendors/branches/\(id)" ) let route = try router.parse(&request) - #expect(route == .vendorBranch(.get(id: id))) + #expect(route == .vendorBranch(.base(.get(id: id)))) } @Test @@ -56,7 +56,7 @@ struct VendorBranchApiRouteTests { path: "/api/v1/vendors/branches" ) let route = try router.parse(&request) - #expect(route == .vendorBranch(.index())) + #expect(route == .vendorBranch(.base(.index()))) var request2 = URLRequestData( method: "GET", @@ -64,7 +64,7 @@ struct VendorBranchApiRouteTests { query: ["vendorID": ["\(id)"]] ) let route2 = try router.parse(&request2) - #expect(route2 == .vendorBranch(.index(for: id))) + #expect(route2 == .vendorBranch(.base(.index(for: id)))) } @Test @@ -81,7 +81,7 @@ struct VendorBranchApiRouteTests { body: .init(json.utf8) ) let route = try router.parse(&request) - #expect(route == .vendorBranch(.update(id: id, updates: .init(name: "Test")))) + #expect(route == .vendorBranch(.base(.update(id: id, updates: .init(name: "Test"))))) } }