feat: Removes old tests, fixes authentication middleware not working, view routes updated to not have delete routes and uses api routes for delete methods.

This commit is contained in:
2025-01-24 10:55:59 -05:00
parent aa60f69758
commit 90c6058d56
37 changed files with 146 additions and 564 deletions

View File

@@ -7,13 +7,14 @@ import Vapor
private let apiMiddleware: [any Middleware] = [
UserPasswordAuthenticator(),
UserTokenAuthenticator(),
UserSessionAuthenticator(),
User.guardMiddleware()
]
extension ApiRoute {
var middleware: [any Middleware]? { apiMiddleware }
func handle(request: Request) async throws -> any AsyncResponseEncodable {
func respond(request: Request) async throws -> any AsyncResponseEncodable {
switch self {
case let .employee(route):
return try await route.handleApiRequest(request: request)

View File

@@ -0,0 +1,53 @@
import Elementary
import SharedModels
import Vapor
import VaporElementary
import ViewController
extension ViewController {
func respond(route: ViewRoute, request: Vapor.Request) async throws -> any AsyncResponseEncodable {
let html = try await view(
for: route,
isHtmxRequest: request.isHtmxRequest,
logger: request.logger,
authenticate: { request.session.authenticate($0) }
)
return AnyHTMLResponse(value: html)
}
}
// Re-adapted from `HTMLResponse` in the VaporElementary package to work with any html types
// returned from the view controller.
struct AnyHTMLResponse: AsyncResponseEncodable {
public var chunkSize: Int
public var headers: HTTPHeaders = ["Content-Type": "text/html; charset=utf-8"]
var value: _SendableAnyHTMLBox
init(chunkSize: Int = 1024, additionalHeaders: HTTPHeaders = [:], value: AnySendableHTML) {
self.chunkSize = chunkSize
if additionalHeaders.contains(name: .contentType) {
self.headers = additionalHeaders
} else {
headers.add(contentsOf: additionalHeaders)
}
self.value = .init(value)
}
func encodeResponse(for request: Request) async throws -> Response {
Response(
status: .ok,
headers: headers,
body: .init(asyncStream: { [value, chunkSize] writer in
guard let html = value.tryTake() else {
assertionFailure("Non-sendable HTML value consumed more than once")
request.logger.error("Non-sendable HTML value consumed more than once")
throw Abort(.internalServerError)
}
try await writer.writeHTML(html, chunkSize: chunkSize)
try await writer.write(.end)
})
)
}
}

View File

@@ -1,20 +1,23 @@
import DatabaseClientLive
import Dependencies
import Vapor
import ViewControllerLive
// Taken from discussions page on `swift-dependencies`.
// TODO: Pass dependencies to set into this middleware.
struct DependenciesMiddleware: AsyncMiddleware {
private let values: DependencyValues.Continuation
private let database: DatabaseClient
private let viewController: ViewController
init(
database: DatabaseClient
database: DatabaseClient,
viewController: ViewController = .liveValue
) {
self.values = withEscapedDependencies { $0 }
self.database = database
self.viewController = viewController
}
func respond(to request: Request, chainingTo next: any AsyncResponder) async throws -> Response {
@@ -22,6 +25,7 @@ struct DependenciesMiddleware: AsyncMiddleware {
try await withDependencies {
$0.database = database
$0.dateFormatter = .liveValue
$0.viewController = viewController
} operation: {
try await next.respond(to: request)
}

View File

@@ -16,7 +16,7 @@ extension SharedModels.ViewRoute {
var middleware: [any Middleware]? {
switch self {
case .index: return viewProtectedMiddleware
// case .index: return viewProtectedMiddleware
case let .employee(route): return route.middleware
case .login: return nil
case let .purchaseOrder(route): return route.middleware

View File

@@ -48,6 +48,11 @@ public func configure(
app.middleware.use(DependenciesMiddleware(database: databaseClient))
// Redirect the index path to purchase order route.
app.get { req in
req.redirect(to: ViewRoute.router.path(for: .purchaseOrder(.index)))
}
app.mount(
SiteRoute.router,
middleware: {
@@ -79,7 +84,6 @@ extension SiteRoute {
case .health:
return nil
case let .view(route):
// return nil
return route.middleware
}
}
@@ -90,62 +94,13 @@ func siteHandler(
request: Request,
route: SiteRoute
) async throws -> any AsyncResponseEncodable {
@Dependency(\.viewController) var viewController
switch route {
case let .api(route):
return try await route.handle(request: request)
return try await route.respond(request: request)
case .health:
return HTTPStatus.ok
case let .view(route):
return try await route.respond(request: request)
// return try await route.handle(request: request)
}
}
extension ViewRoute {
func respond(request: Request) async throws -> any AsyncResponseEncodable {
if self == .index {
return request.redirect(to: ViewRoute.router.path(for: .purchaseOrder(.index)))
} else {
let html = try await view(isHtmxRequest: request.isHtmxRequest, authenticate: { request.auth.login($0) })
// Delete routes return nil, but are valid routes.
guard let html else {
return HTTPStatus.ok
}
return AnyHTMLResponse(value: html)
}
}
}
struct AnyHTMLResponse: AsyncResponseEncodable {
public var chunkSize: Int
public var headers: HTTPHeaders = ["Content-Type": "text/html; charset=utf-8"]
var value: _SendableAnyHTMLBox
init(chunkSize: Int = 1024, additionalHeaders: HTTPHeaders = [:], value: any HTML & Sendable) {
self.chunkSize = chunkSize
if additionalHeaders.contains(name: .contentType) {
self.headers = additionalHeaders
} else {
headers.add(contentsOf: additionalHeaders)
}
self.value = .init(value)
}
func encodeResponse(for request: Request) async throws -> Response {
Response(
status: .ok,
headers: headers,
body: .init(asyncStream: { [value, chunkSize] writer in
guard let html = value.tryTake() else {
assertionFailure("Non-sendable HTML value consumed more than once")
request.logger.error("Non-sendable HTML value consumed more than once")
throw Abort(.internalServerError)
}
try await writer.writeHTML(html, chunkSize: chunkSize)
try await writer.write(.end)
})
)
return try await viewController.respond(route: route, request: request)
}
}

View File

@@ -1,4 +0,0 @@
import Dependencies
import Elementary
import SharedModels
@_exported import ViewController