feat: Fixes build issues, adds ApiController module.
This commit is contained in:
@@ -6,6 +6,7 @@ let package = Package(
|
||||
name: "swift-manual-d",
|
||||
products: [
|
||||
.executable(name: "App", targets: ["App"]),
|
||||
.library(name: "ApiController", targets: ["ApiController"]),
|
||||
.library(name: "DatabaseClient", targets: ["DatabaseClient"]),
|
||||
.library(name: "ManualDCore", targets: ["ManualDCore"]),
|
||||
.library(name: "ManualDClient", targets: ["ManualDClient"]),
|
||||
@@ -28,6 +29,7 @@ let package = Package(
|
||||
.executableTarget(
|
||||
name: "App",
|
||||
dependencies: [
|
||||
.target(name: "ApiController"),
|
||||
.target(name: "DatabaseClient"),
|
||||
.target(name: "ViewController"),
|
||||
.product(name: "Dependencies", package: "swift-dependencies"),
|
||||
@@ -40,6 +42,16 @@ let package = Package(
|
||||
.product(name: "VaporRouting", package: "vapor-routing"),
|
||||
]
|
||||
),
|
||||
.target(
|
||||
name: "ApiController",
|
||||
dependencies: [
|
||||
.target(name: "DatabaseClient"),
|
||||
.target(name: "ManualDCore"),
|
||||
.product(name: "Dependencies", package: "swift-dependencies"),
|
||||
.product(name: "DependenciesMacros", package: "swift-dependencies"),
|
||||
.product(name: "Vapor", package: "vapor"),
|
||||
]
|
||||
),
|
||||
.target(
|
||||
name: "DatabaseClient",
|
||||
dependencies: [
|
||||
|
||||
38
Sources/ApiController/Interface.swift
Normal file
38
Sources/ApiController/Interface.swift
Normal file
@@ -0,0 +1,38 @@
|
||||
import Dependencies
|
||||
import DependenciesMacros
|
||||
import Logging
|
||||
import ManualDCore
|
||||
|
||||
extension DependencyValues {
|
||||
public var apiController: ApiController {
|
||||
get { self[ApiController.self] }
|
||||
set { self[ApiController.self] = newValue }
|
||||
}
|
||||
}
|
||||
|
||||
@DependencyClient
|
||||
public struct ApiController: Sendable {
|
||||
public var json: @Sendable (Request) async throws -> (any Encodable)?
|
||||
}
|
||||
|
||||
extension ApiController {
|
||||
public struct Request: Sendable {
|
||||
public let route: SiteRoute.Api
|
||||
public let logger: Logger
|
||||
|
||||
public init(route: SiteRoute.Api, logger: Logger) {
|
||||
self.route = route
|
||||
self.logger = logger
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension ApiController: DependencyKey {
|
||||
public static let testValue = Self()
|
||||
|
||||
public static let liveValue = Self(
|
||||
json: { request in
|
||||
try await request.route.respond(logger: request.logger)
|
||||
}
|
||||
)
|
||||
}
|
||||
119
Sources/ApiController/Live.swift
Normal file
119
Sources/ApiController/Live.swift
Normal file
@@ -0,0 +1,119 @@
|
||||
import DatabaseClient
|
||||
import Dependencies
|
||||
import Logging
|
||||
import ManualDCore
|
||||
|
||||
extension SiteRoute.Api {
|
||||
func respond(logger: Logger) async throws -> (any Encodable)? {
|
||||
switch self {
|
||||
case .project(let route):
|
||||
return try await route.respond(logger: logger)
|
||||
case .room(let route):
|
||||
return try await route.respond(logger: logger)
|
||||
case .equipment(let route):
|
||||
return try await route.respond(logger: logger)
|
||||
case .componentLoss(let route):
|
||||
return try await route.respond(logger: logger)
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension SiteRoute.Api.ProjectRoute {
|
||||
|
||||
func respond(logger: Logger) async throws -> (any Encodable)? {
|
||||
@Dependency(\.database) var database
|
||||
|
||||
switch self {
|
||||
case .create(let request):
|
||||
return try await database.projects.create(request)
|
||||
case .delete(let id):
|
||||
try await database.projects.delete(id)
|
||||
return nil
|
||||
case .get(let id):
|
||||
guard let project = try await database.projects.get(id) else {
|
||||
logger.error("Project not found for id: \(id)")
|
||||
throw ApiError("Project not found.")
|
||||
}
|
||||
return project
|
||||
case .index:
|
||||
// FIX: Fix to return projects.
|
||||
return [Project]()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension SiteRoute.Api.RoomRoute {
|
||||
|
||||
func respond(logger: Logger) async throws -> (any Encodable)? {
|
||||
@Dependency(\.database) var database
|
||||
|
||||
switch self {
|
||||
case .create(let request):
|
||||
return try await database.rooms.create(request)
|
||||
case .delete(let id):
|
||||
try await database.rooms.delete(id)
|
||||
return nil
|
||||
case .get(let id):
|
||||
guard let room = try await database.rooms.get(id) else {
|
||||
logger.error("Room not found for id: \(id)")
|
||||
throw ApiError("Room not found.")
|
||||
}
|
||||
return room
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension SiteRoute.Api.EquipmentRoute {
|
||||
|
||||
func respond(logger: Logger) async throws -> (any Encodable)? {
|
||||
@Dependency(\.database) var database
|
||||
|
||||
switch self {
|
||||
case .create(let request):
|
||||
return try await database.equipment.create(request)
|
||||
case .delete(let id):
|
||||
try await database.equipment.delete(id)
|
||||
return nil
|
||||
case .fetch(let projectID):
|
||||
return try await database.equipment.fetch(projectID)
|
||||
case .get(let id):
|
||||
guard let room = try await database.equipment.get(id) else {
|
||||
logger.error("Equipment not found for id: \(id)")
|
||||
throw ApiError("Equipment not found.")
|
||||
}
|
||||
return room
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension SiteRoute.Api.ComponentLossRoute {
|
||||
|
||||
func respond(logger: Logger) async throws -> (any Encodable)? {
|
||||
@Dependency(\.database) var database
|
||||
|
||||
switch self {
|
||||
case .create(let request):
|
||||
return try await database.componentLoss.create(request)
|
||||
case .delete(let id):
|
||||
try await database.componentLoss.delete(id)
|
||||
return nil
|
||||
case .fetch(let projectID):
|
||||
return try await database.componentLoss.fetch(projectID)
|
||||
case .get(let id):
|
||||
guard let room = try await database.componentLoss.get(id) else {
|
||||
logger.error("Component loss not found for id: \(id)")
|
||||
throw ApiError("Component loss not found.")
|
||||
}
|
||||
return room
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public struct ApiError: Error {
|
||||
let message: String
|
||||
|
||||
init(_ message: String) {
|
||||
self.message = message
|
||||
}
|
||||
}
|
||||
37
Sources/App/Extensions/ApiController+respond.swift
Normal file
37
Sources/App/Extensions/ApiController+respond.swift
Normal file
@@ -0,0 +1,37 @@
|
||||
import ApiController
|
||||
import ManualDCore
|
||||
import Vapor
|
||||
|
||||
extension ApiController {
|
||||
|
||||
func respond(_ route: SiteRoute.Api, request: Vapor.Request) async throws
|
||||
-> any AsyncResponseEncodable
|
||||
{
|
||||
guard let encodable = try await json(.init(route: route, logger: request.logger)) else {
|
||||
return HTTPStatus.ok
|
||||
}
|
||||
return AnyJSONResponse(value: encodable)
|
||||
}
|
||||
}
|
||||
|
||||
struct AnyJSONResponse: AsyncResponseEncodable {
|
||||
public var headers: HTTPHeaders = ["Content-Type": "application/json"]
|
||||
let value: any Encodable
|
||||
|
||||
init(additionalHeaders: HTTPHeaders = [:], value: any Encodable) {
|
||||
if additionalHeaders.contains(name: .contentType) {
|
||||
self.headers = additionalHeaders
|
||||
} else {
|
||||
headers.add(contentsOf: additionalHeaders)
|
||||
}
|
||||
self.value = value
|
||||
}
|
||||
|
||||
func encodeResponse(for request: Request) async throws -> Response {
|
||||
try Response(
|
||||
status: .ok,
|
||||
headers: headers,
|
||||
body: .init(data: JSONEncoder().encode(value))
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
// import ApiControllerLive
|
||||
import ApiController
|
||||
import DatabaseClient
|
||||
import Dependencies
|
||||
import Vapor
|
||||
@@ -10,17 +10,17 @@ import ViewController
|
||||
struct DependenciesMiddleware: AsyncMiddleware {
|
||||
|
||||
private let values: DependencyValues.Continuation
|
||||
// private let apiController: ApiController
|
||||
private let apiController: ApiController
|
||||
private let database: DatabaseClient
|
||||
private let viewController: ViewController
|
||||
|
||||
init(
|
||||
database: DatabaseClient,
|
||||
// apiController: ApiController = .liveValue,
|
||||
apiController: ApiController = .liveValue,
|
||||
viewController: ViewController = .testValue
|
||||
) {
|
||||
self.values = withEscapedDependencies { $0 }
|
||||
// self.apiController = apiController
|
||||
self.apiController = apiController
|
||||
self.database = database
|
||||
self.viewController = viewController
|
||||
}
|
||||
@@ -28,7 +28,7 @@ struct DependenciesMiddleware: AsyncMiddleware {
|
||||
func respond(to request: Request, chainingTo next: any AsyncResponder) async throws -> Response {
|
||||
try await values.yield {
|
||||
try await withDependencies {
|
||||
// $0.apiController = apiController
|
||||
$0.apiController = apiController
|
||||
$0.database = database
|
||||
// $0.dateFormatter = .liveValue
|
||||
$0.viewController = viewController
|
||||
|
||||
@@ -117,13 +117,12 @@ private func siteHandler(
|
||||
request: Request,
|
||||
route: SiteRoute
|
||||
) async throws -> any AsyncResponseEncodable {
|
||||
// @Dependency(\.apiController) var apiController
|
||||
@Dependency(\.apiController) var apiController
|
||||
@Dependency(\.viewController) var viewController
|
||||
|
||||
switch route {
|
||||
case .api(let route):
|
||||
return HTTPStatus.ok
|
||||
// return try await apiController.respond(route, request: request)
|
||||
return try await apiController.respond(route, request: request)
|
||||
case .health:
|
||||
return HTTPStatus.ok
|
||||
case .view(let route):
|
||||
|
||||
@@ -82,7 +82,8 @@ extension ComponentPressureLoss {
|
||||
.field("value", .double, .required)
|
||||
.field("createdAt", .datetime)
|
||||
.field("updatedAt", .datetime)
|
||||
.foreignKey("projectID", references: ProjectModel.schema, "id", onDelete: .cascade)
|
||||
.field("projectID", .uuid, .required, .references(ProjectModel.schema, "id"))
|
||||
// .foreignKey("projectID", references: ProjectModel.schema, "id", onDelete: .cascade)
|
||||
.unique(on: "projectID", "name")
|
||||
.create()
|
||||
}
|
||||
|
||||
@@ -91,7 +91,7 @@ extension EquipmentInfo {
|
||||
.field("coolingCFM", .int16, .required)
|
||||
.field("createdAt", .datetime)
|
||||
.field("updatedAt", .datetime)
|
||||
.foreignKey("projectID", references: ProjectModel.schema, "id", onDelete: .cascade)
|
||||
.field("projectID", .uuid, .required, .references(ProjectModel.schema, "id"))
|
||||
.unique(on: "projectID")
|
||||
.create()
|
||||
}
|
||||
|
||||
@@ -54,9 +54,9 @@ extension DatabaseClient.Migrations: DependencyKey {
|
||||
public static let liveValue = Self(
|
||||
run: {
|
||||
[
|
||||
Project.Migrate(),
|
||||
ComponentPressureLoss.Migrate(),
|
||||
EquipmentInfo.Migrate(),
|
||||
Project.Migrate(),
|
||||
Room.Migrate(),
|
||||
]
|
||||
}
|
||||
|
||||
@@ -123,6 +123,9 @@ final class ProjectModel: Model, @unchecked Sendable {
|
||||
@Timestamp(key: "updatedAt", on: .update, format: .iso8601)
|
||||
var updatedAt: Date?
|
||||
|
||||
@Children(for: \.$project)
|
||||
var componentLosses: [ComponentLossModel]
|
||||
|
||||
init() {}
|
||||
|
||||
init(
|
||||
|
||||
@@ -83,7 +83,7 @@ extension Room {
|
||||
.field("coolingTotal", .double, .required)
|
||||
.field("coolingSensible", .double, .required)
|
||||
.field("registerCount", .int8, .required)
|
||||
.foreignKey("projectID", references: ProjectModel.schema, "id", onDelete: .cascade)
|
||||
.field("projectID", .uuid, .required, .references(ProjectModel.schema, "id"))
|
||||
.unique(on: "projectID", "name")
|
||||
.create()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user