feat: Removes api routes and controller as they're currently not used.
All checks were successful
CI / Linux Tests (push) Successful in 5m30s
All checks were successful
CI / Linux Tests (push) Successful in 5m30s
This commit is contained in:
@@ -6,7 +6,6 @@ let package = Package(
|
|||||||
name: "swift-manual-d",
|
name: "swift-manual-d",
|
||||||
products: [
|
products: [
|
||||||
.executable(name: "App", targets: ["App"]),
|
.executable(name: "App", targets: ["App"]),
|
||||||
.library(name: "ApiController", targets: ["ApiController"]),
|
|
||||||
.library(name: "AuthClient", targets: ["AuthClient"]),
|
.library(name: "AuthClient", targets: ["AuthClient"]),
|
||||||
.library(name: "DatabaseClient", targets: ["DatabaseClient"]),
|
.library(name: "DatabaseClient", targets: ["DatabaseClient"]),
|
||||||
.library(name: "EnvClient", targets: ["EnvClient"]),
|
.library(name: "EnvClient", targets: ["EnvClient"]),
|
||||||
@@ -38,7 +37,6 @@ let package = Package(
|
|||||||
.executableTarget(
|
.executableTarget(
|
||||||
name: "App",
|
name: "App",
|
||||||
dependencies: [
|
dependencies: [
|
||||||
.target(name: "ApiController"),
|
|
||||||
.target(name: "AuthClient"),
|
.target(name: "AuthClient"),
|
||||||
.target(name: "DatabaseClient"),
|
.target(name: "DatabaseClient"),
|
||||||
.target(name: "ViewController"),
|
.target(name: "ViewController"),
|
||||||
@@ -52,22 +50,6 @@ let package = Package(
|
|||||||
.product(name: "VaporRouting", package: "vapor-routing"),
|
.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"),
|
|
||||||
]
|
|
||||||
),
|
|
||||||
.testTarget(
|
|
||||||
name: "ApiRouteTests",
|
|
||||||
dependencies: [
|
|
||||||
.target(name: "ManualDCore")
|
|
||||||
]
|
|
||||||
),
|
|
||||||
.target(
|
.target(
|
||||||
name: "AuthClient",
|
name: "AuthClient",
|
||||||
dependencies: [
|
dependencies: [
|
||||||
|
|||||||
@@ -1,38 +0,0 @@
|
|||||||
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)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,129 +0,0 @@
|
|||||||
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)
|
|
||||||
// FIX:
|
|
||||||
fatalError()
|
|
||||||
case .delete(let id):
|
|
||||||
try await database.projects.delete(id)
|
|
||||||
return nil
|
|
||||||
case .detail(let id, let route):
|
|
||||||
switch route {
|
|
||||||
case .index:
|
|
||||||
return try await database.projects.detail(id)
|
|
||||||
case .completedSteps:
|
|
||||||
// FIX:
|
|
||||||
fatalError()
|
|
||||||
|
|
||||||
}
|
|
||||||
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.componentLosses.create(request)
|
|
||||||
case .delete(let id):
|
|
||||||
try await database.componentLosses.delete(id)
|
|
||||||
return nil
|
|
||||||
case .fetch(let projectID):
|
|
||||||
return try await database.componentLosses.fetch(projectID)
|
|
||||||
case .get(let id):
|
|
||||||
guard let room = try await database.componentLosses.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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,37 +1,37 @@
|
|||||||
import ApiController
|
// import ApiController
|
||||||
import ManualDCore
|
// import ManualDCore
|
||||||
import Vapor
|
// import Vapor
|
||||||
|
//
|
||||||
extension ApiController {
|
// extension ApiController {
|
||||||
|
//
|
||||||
func respond(_ route: SiteRoute.Api, request: Vapor.Request) async throws
|
// func respond(_ route: SiteRoute.Api, request: Vapor.Request) async throws
|
||||||
-> any AsyncResponseEncodable
|
// -> any AsyncResponseEncodable
|
||||||
{
|
// {
|
||||||
guard let encodable = try await json(.init(route: route, logger: request.logger)) else {
|
// guard let encodable = try await json(.init(route: route, logger: request.logger)) else {
|
||||||
return HTTPStatus.ok
|
// return HTTPStatus.ok
|
||||||
}
|
// }
|
||||||
return AnyJSONResponse(value: encodable)
|
// return AnyJSONResponse(value: encodable)
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
struct AnyJSONResponse: AsyncResponseEncodable {
|
// struct AnyJSONResponse: AsyncResponseEncodable {
|
||||||
public var headers: HTTPHeaders = ["Content-Type": "application/json"]
|
// public var headers: HTTPHeaders = ["Content-Type": "application/json"]
|
||||||
let value: any Encodable
|
// let value: any Encodable
|
||||||
|
//
|
||||||
init(additionalHeaders: HTTPHeaders = [:], value: any Encodable) {
|
// init(additionalHeaders: HTTPHeaders = [:], value: any Encodable) {
|
||||||
if additionalHeaders.contains(name: .contentType) {
|
// if additionalHeaders.contains(name: .contentType) {
|
||||||
self.headers = additionalHeaders
|
// self.headers = additionalHeaders
|
||||||
} else {
|
// } else {
|
||||||
headers.add(contentsOf: additionalHeaders)
|
// headers.add(contentsOf: additionalHeaders)
|
||||||
}
|
// }
|
||||||
self.value = value
|
// self.value = value
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
func encodeResponse(for request: Request) async throws -> Response {
|
// func encodeResponse(for request: Request) async throws -> Response {
|
||||||
try Response(
|
// try Response(
|
||||||
status: .ok,
|
// status: .ok,
|
||||||
headers: headers,
|
// headers: headers,
|
||||||
body: .init(data: JSONEncoder().encode(value))
|
// body: .init(data: JSONEncoder().encode(value))
|
||||||
)
|
// )
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import ApiController
|
// import ApiController
|
||||||
import AuthClient
|
import AuthClient
|
||||||
import DatabaseClient
|
import DatabaseClient
|
||||||
import Dependencies
|
import Dependencies
|
||||||
@@ -12,17 +12,17 @@ import ViewController
|
|||||||
struct DependenciesMiddleware: AsyncMiddleware {
|
struct DependenciesMiddleware: AsyncMiddleware {
|
||||||
|
|
||||||
private let values: DependencyValues.Continuation
|
private let values: DependencyValues.Continuation
|
||||||
private let apiController: ApiController
|
// private let apiController: ApiController
|
||||||
private let database: DatabaseClient
|
private let database: DatabaseClient
|
||||||
private let viewController: ViewController
|
private let viewController: ViewController
|
||||||
|
|
||||||
init(
|
init(
|
||||||
database: DatabaseClient,
|
database: DatabaseClient,
|
||||||
apiController: ApiController = .liveValue,
|
// apiController: ApiController = .liveValue,
|
||||||
viewController: ViewController = .liveValue
|
viewController: ViewController = .liveValue
|
||||||
) {
|
) {
|
||||||
self.values = withEscapedDependencies { $0 }
|
self.values = withEscapedDependencies { $0 }
|
||||||
self.apiController = apiController
|
// self.apiController = apiController
|
||||||
self.database = database
|
self.database = database
|
||||||
self.viewController = viewController
|
self.viewController = viewController
|
||||||
}
|
}
|
||||||
@@ -30,7 +30,7 @@ struct DependenciesMiddleware: AsyncMiddleware {
|
|||||||
func respond(to request: Request, chainingTo next: any AsyncResponder) async throws -> Response {
|
func respond(to request: Request, chainingTo next: any AsyncResponder) async throws -> Response {
|
||||||
try await values.yield {
|
try await values.yield {
|
||||||
try await withDependencies {
|
try await withDependencies {
|
||||||
$0.apiController = apiController
|
// $0.apiController = apiController
|
||||||
$0.auth = .live(on: request)
|
$0.auth = .live(on: request)
|
||||||
$0.database = database
|
$0.database = database
|
||||||
// $0.dateFormatter = .liveValue
|
// $0.dateFormatter = .liveValue
|
||||||
|
|||||||
@@ -100,8 +100,6 @@ extension SiteRoute {
|
|||||||
|
|
||||||
fileprivate func middleware() -> [any Middleware]? {
|
fileprivate func middleware() -> [any Middleware]? {
|
||||||
switch self {
|
switch self {
|
||||||
case .api:
|
|
||||||
return nil
|
|
||||||
case .health:
|
case .health:
|
||||||
return nil
|
return nil
|
||||||
case .view(let route):
|
case .view(let route):
|
||||||
@@ -117,13 +115,10 @@ private func siteHandler(
|
|||||||
request: Request,
|
request: Request,
|
||||||
route: SiteRoute
|
route: SiteRoute
|
||||||
) async throws -> any AsyncResponseEncodable {
|
) async throws -> any AsyncResponseEncodable {
|
||||||
@Dependency(\.apiController) var apiController
|
|
||||||
@Dependency(\.viewController) var viewController
|
@Dependency(\.viewController) var viewController
|
||||||
@Dependency(\.projectClient) var projectClient
|
@Dependency(\.projectClient) var projectClient
|
||||||
|
|
||||||
switch route {
|
switch route {
|
||||||
case .api(let route):
|
|
||||||
return try await apiController.respond(route, request: request)
|
|
||||||
case .health:
|
case .health:
|
||||||
return HTTPStatus.ok
|
return HTTPStatus.ok
|
||||||
// Generating a pdf return's a `Response` instead of `HTML` like other views, so we
|
// Generating a pdf return's a `Response` instead of `HTML` like other views, so we
|
||||||
|
|||||||
@@ -1,270 +0,0 @@
|
|||||||
import CasePathsCore
|
|
||||||
import Foundation
|
|
||||||
@preconcurrency import URLRouting
|
|
||||||
|
|
||||||
extension SiteRoute {
|
|
||||||
/// Represents api routes.
|
|
||||||
///
|
|
||||||
/// The routes return json as opposed to view routes that return html.
|
|
||||||
public enum Api: Sendable, Equatable {
|
|
||||||
|
|
||||||
case project(Self.ProjectRoute)
|
|
||||||
case room(Self.RoomRoute)
|
|
||||||
case equipment(Self.EquipmentRoute)
|
|
||||||
case componentLoss(Self.ComponentLossRoute)
|
|
||||||
|
|
||||||
public static let rootPath = Path {
|
|
||||||
"api"
|
|
||||||
"v1"
|
|
||||||
}
|
|
||||||
|
|
||||||
public static let router = OneOf {
|
|
||||||
Route(.case(Self.project)) {
|
|
||||||
rootPath
|
|
||||||
ProjectRoute.router
|
|
||||||
}
|
|
||||||
Route(.case(Self.room)) {
|
|
||||||
rootPath
|
|
||||||
RoomRoute.router
|
|
||||||
}
|
|
||||||
Route(.case(Self.equipment)) {
|
|
||||||
rootPath
|
|
||||||
EquipmentRoute.router
|
|
||||||
}
|
|
||||||
Route(.case(Self.componentLoss)) {
|
|
||||||
rootPath
|
|
||||||
ComponentLossRoute.router
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
extension SiteRoute.Api {
|
|
||||||
public enum ProjectRoute: Sendable, Equatable {
|
|
||||||
case create(Project.Create)
|
|
||||||
case delete(id: Project.ID)
|
|
||||||
case detail(id: Project.ID, route: DetailRoute)
|
|
||||||
case get(id: Project.ID)
|
|
||||||
case index
|
|
||||||
|
|
||||||
static let rootPath = "projects"
|
|
||||||
|
|
||||||
public static let router = OneOf {
|
|
||||||
Route(.case(Self.create)) {
|
|
||||||
Path { rootPath }
|
|
||||||
Method.post
|
|
||||||
Body(.json(Project.Create.self))
|
|
||||||
}
|
|
||||||
Route(.case(Self.delete(id:))) {
|
|
||||||
Path {
|
|
||||||
rootPath
|
|
||||||
Project.ID.parser()
|
|
||||||
}
|
|
||||||
Method.delete
|
|
||||||
}
|
|
||||||
Route(.case(Self.get(id:))) {
|
|
||||||
Path {
|
|
||||||
rootPath
|
|
||||||
Project.ID.parser()
|
|
||||||
}
|
|
||||||
Method.get
|
|
||||||
}
|
|
||||||
Route(.case(Self.index)) {
|
|
||||||
Path { rootPath }
|
|
||||||
Method.get
|
|
||||||
}
|
|
||||||
Route(.case(Self.detail(id:route:))) {
|
|
||||||
Path {
|
|
||||||
rootPath
|
|
||||||
Project.ID.parser()
|
|
||||||
}
|
|
||||||
DetailRoute.router
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension SiteRoute.Api.ProjectRoute {
|
|
||||||
public enum DetailRoute: Equatable, Sendable {
|
|
||||||
case index
|
|
||||||
case completedSteps
|
|
||||||
|
|
||||||
static let rootPath = "details"
|
|
||||||
|
|
||||||
static let router = OneOf {
|
|
||||||
Route(.case(Self.index)) {
|
|
||||||
Path { rootPath }
|
|
||||||
Method.get
|
|
||||||
}
|
|
||||||
Route(.case(Self.completedSteps)) {
|
|
||||||
Path {
|
|
||||||
rootPath
|
|
||||||
"completed"
|
|
||||||
}
|
|
||||||
Method.get
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension SiteRoute.Api {
|
|
||||||
|
|
||||||
public enum RoomRoute: Sendable, Equatable {
|
|
||||||
case create(Room.Create)
|
|
||||||
case delete(id: Room.ID)
|
|
||||||
case get(id: Room.ID)
|
|
||||||
|
|
||||||
static let rootPath = "rooms"
|
|
||||||
|
|
||||||
public static let router = OneOf {
|
|
||||||
Route(.case(Self.create)) {
|
|
||||||
Path { rootPath }
|
|
||||||
Method.post
|
|
||||||
Body(.json(Room.Create.self))
|
|
||||||
}
|
|
||||||
Route(.case(Self.delete(id:))) {
|
|
||||||
Path {
|
|
||||||
rootPath
|
|
||||||
Room.ID.parser()
|
|
||||||
}
|
|
||||||
Method.delete
|
|
||||||
}
|
|
||||||
Route(.case(Self.get(id:))) {
|
|
||||||
Path {
|
|
||||||
rootPath
|
|
||||||
Room.ID.parser()
|
|
||||||
}
|
|
||||||
Method.get
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension SiteRoute.Api {
|
|
||||||
|
|
||||||
public enum EquipmentRoute: Sendable, Equatable {
|
|
||||||
case create(EquipmentInfo.Create)
|
|
||||||
case delete(id: EquipmentInfo.ID)
|
|
||||||
case fetch(projectID: Project.ID)
|
|
||||||
case get(id: EquipmentInfo.ID)
|
|
||||||
|
|
||||||
static let rootPath = "equipment"
|
|
||||||
|
|
||||||
public static let router = OneOf {
|
|
||||||
Route(.case(Self.create)) {
|
|
||||||
Path { rootPath }
|
|
||||||
Method.post
|
|
||||||
Body(.json(EquipmentInfo.Create.self))
|
|
||||||
}
|
|
||||||
Route(.case(Self.delete(id:))) {
|
|
||||||
Path {
|
|
||||||
rootPath
|
|
||||||
EquipmentInfo.ID.parser()
|
|
||||||
}
|
|
||||||
Method.delete
|
|
||||||
}
|
|
||||||
Route(.case(Self.fetch(projectID:))) {
|
|
||||||
Path { rootPath }
|
|
||||||
Method.get
|
|
||||||
Query {
|
|
||||||
Field("projectID") { Project.ID.parser() }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Route(.case(Self.get(id:))) {
|
|
||||||
Path {
|
|
||||||
rootPath
|
|
||||||
EquipmentInfo.ID.parser()
|
|
||||||
}
|
|
||||||
Method.get
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension SiteRoute.Api {
|
|
||||||
|
|
||||||
public enum ComponentLossRoute: Sendable, Equatable {
|
|
||||||
case create(ComponentPressureLoss.Create)
|
|
||||||
case delete(id: ComponentPressureLoss.ID)
|
|
||||||
case fetch(projectID: Project.ID)
|
|
||||||
case get(id: ComponentPressureLoss.ID)
|
|
||||||
|
|
||||||
static let rootPath = "componentLoss"
|
|
||||||
|
|
||||||
public static let router = OneOf {
|
|
||||||
Route(.case(Self.create)) {
|
|
||||||
Path { rootPath }
|
|
||||||
Method.post
|
|
||||||
Body(.json(ComponentPressureLoss.Create.self))
|
|
||||||
}
|
|
||||||
Route(.case(Self.delete(id:))) {
|
|
||||||
Path {
|
|
||||||
rootPath
|
|
||||||
ComponentPressureLoss.ID.parser()
|
|
||||||
}
|
|
||||||
Method.delete
|
|
||||||
}
|
|
||||||
Route(.case(Self.fetch(projectID:))) {
|
|
||||||
Path { rootPath }
|
|
||||||
Method.get
|
|
||||||
Query {
|
|
||||||
Field("projectID") { Project.ID.parser() }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Route(.case(Self.get(id:))) {
|
|
||||||
Path {
|
|
||||||
rootPath
|
|
||||||
ComponentPressureLoss.ID.parser()
|
|
||||||
}
|
|
||||||
Method.get
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension SiteRoute.Api {
|
|
||||||
public enum EffectiveLengthRoute: Equatable, Sendable {
|
|
||||||
case create(EquivalentLength.Create)
|
|
||||||
case delete(id: EquivalentLength.ID)
|
|
||||||
case fetch(projectID: Project.ID)
|
|
||||||
case get(id: EquivalentLength.ID)
|
|
||||||
|
|
||||||
static let rootPath = "effectiveLength"
|
|
||||||
|
|
||||||
public static let router = OneOf {
|
|
||||||
Route(.case(Self.create)) {
|
|
||||||
Path {
|
|
||||||
rootPath
|
|
||||||
"create"
|
|
||||||
}
|
|
||||||
Method.post
|
|
||||||
Body(.json(EquivalentLength.Create.self))
|
|
||||||
}
|
|
||||||
Route(.case(Self.delete(id:))) {
|
|
||||||
Path {
|
|
||||||
rootPath
|
|
||||||
EquivalentLength.ID.parser()
|
|
||||||
}
|
|
||||||
Method.delete
|
|
||||||
}
|
|
||||||
Route(.case(Self.fetch(projectID:))) {
|
|
||||||
Path {
|
|
||||||
rootPath
|
|
||||||
}
|
|
||||||
Method.get
|
|
||||||
Query {
|
|
||||||
Field("projectID") { Project.ID.parser() }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Route(.case(Self.get(id:))) {
|
|
||||||
Path {
|
|
||||||
rootPath
|
|
||||||
EquivalentLength.ID.parser()
|
|
||||||
}
|
|
||||||
Method.get
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -5,14 +5,10 @@ import Foundation
|
|||||||
|
|
||||||
public enum SiteRoute: Equatable, Sendable {
|
public enum SiteRoute: Equatable, Sendable {
|
||||||
|
|
||||||
case api(Self.Api)
|
|
||||||
case health
|
case health
|
||||||
case view(Self.View)
|
case view(Self.View)
|
||||||
|
|
||||||
public static let router = OneOf {
|
public static let router = OneOf {
|
||||||
Route(.case(Self.api)) {
|
|
||||||
SiteRoute.Api.router
|
|
||||||
}
|
|
||||||
Route(.case(Self.health)) {
|
Route(.case(Self.health)) {
|
||||||
Path { "health" }
|
Path { "health" }
|
||||||
Method.get
|
Method.get
|
||||||
|
|||||||
@@ -27,11 +27,4 @@ extension Badge where Inner == Number {
|
|||||||
self.inner = Number(number, digits: digits)
|
self.inner = Number(number, digits: digits)
|
||||||
}
|
}
|
||||||
|
|
||||||
public init<T>(number: Tagged<T, Int>) {
|
|
||||||
self.inner = Number(number.rawValue)
|
|
||||||
}
|
|
||||||
|
|
||||||
public init<T>(number: Tagged<T, Double>, digits: Int = 2) {
|
|
||||||
self.inner = Number(number.rawValue, digits: digits)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,32 +26,6 @@ public struct SubmitButton: HTML, Sendable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public struct CancelButton: HTML, Sendable {
|
|
||||||
let title: String
|
|
||||||
let type: HTMLAttribute<HTMLTag.button>.ButtonType
|
|
||||||
|
|
||||||
public init(
|
|
||||||
title: String = "Cancel",
|
|
||||||
type: HTMLAttribute<HTMLTag.button>.ButtonType = .button
|
|
||||||
) {
|
|
||||||
self.title = title
|
|
||||||
self.type = type
|
|
||||||
}
|
|
||||||
|
|
||||||
public var body: some HTML<HTMLTag.button> {
|
|
||||||
button(
|
|
||||||
.class(
|
|
||||||
"""
|
|
||||||
text-white font-bold text-xl bg-red-500 hover:bg-red-600 px-4 py-2 rounded-lg shadow-lg
|
|
||||||
"""
|
|
||||||
),
|
|
||||||
.type(type)
|
|
||||||
) {
|
|
||||||
title
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public struct EditButton: HTML, Sendable {
|
public struct EditButton: HTML, Sendable {
|
||||||
let title: String?
|
let title: String?
|
||||||
let type: HTMLAttribute<HTMLTag.button>.ButtonType
|
let type: HTMLAttribute<HTMLTag.button>.ButtonType
|
||||||
|
|||||||
@@ -9,13 +9,6 @@ extension HTMLAttribute where Tag: HTMLTrait.Attributes.href {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension HTMLAttribute where Tag == HTMLTag.form {
|
|
||||||
|
|
||||||
public static func action(route: SiteRoute.View) -> Self {
|
|
||||||
action(SiteRoute.View.router.path(for: route))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension HTMLAttribute where Tag == HTMLTag.input {
|
extension HTMLAttribute where Tag == HTMLTag.input {
|
||||||
|
|
||||||
public static func value(_ string: String?) -> Self {
|
public static func value(_ string: String?) -> Self {
|
||||||
@@ -42,9 +35,6 @@ extension HTMLAttribute where Tag == HTMLTag.button {
|
|||||||
}
|
}
|
||||||
|
|
||||||
extension HTML where Tag: HTMLTrait.Attributes.Global {
|
extension HTML where Tag: HTMLTrait.Attributes.Global {
|
||||||
public func badge() -> _AttributedElement<Self> {
|
|
||||||
attributes(.class("badge badge-lg badge-outline"))
|
|
||||||
}
|
|
||||||
|
|
||||||
public func hidden(when shouldHide: Bool) -> _AttributedElement<Self> {
|
public func hidden(when shouldHide: Bool) -> _AttributedElement<Self> {
|
||||||
attributes(.class("hidden"), when: shouldHide)
|
attributes(.class("hidden"), when: shouldHide)
|
||||||
|
|||||||
@@ -1,45 +0,0 @@
|
|||||||
import Elementary
|
|
||||||
|
|
||||||
// TODO: Remove, using svg's.
|
|
||||||
public struct Icon: HTML, Sendable {
|
|
||||||
|
|
||||||
let icon: String
|
|
||||||
|
|
||||||
public init(icon: String) {
|
|
||||||
self.icon = icon
|
|
||||||
}
|
|
||||||
|
|
||||||
public var body: some HTML {
|
|
||||||
i(.data("lucide", value: icon)) {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension Icon {
|
|
||||||
|
|
||||||
public init(_ icon: Key) {
|
|
||||||
self.init(icon: icon.icon)
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum Key: String {
|
|
||||||
|
|
||||||
case circlePlus
|
|
||||||
case close
|
|
||||||
case doorClosed
|
|
||||||
case mapPin
|
|
||||||
case rulerDimensionLine
|
|
||||||
case squareFunction
|
|
||||||
case wind
|
|
||||||
|
|
||||||
var icon: String {
|
|
||||||
switch self {
|
|
||||||
case .circlePlus: return "circle-plus"
|
|
||||||
case .close: return "x"
|
|
||||||
case .doorClosed: return "door-closed"
|
|
||||||
case .mapPin: return "map-pin"
|
|
||||||
case .rulerDimensionLine: return "ruler-dimension-line"
|
|
||||||
case .squareFunction: return "square-function"
|
|
||||||
case .wind: return rawValue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -21,60 +21,6 @@ public struct LabeledInput: HTML, Sendable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public struct Input: HTML, Sendable {
|
|
||||||
|
|
||||||
let id: String?
|
|
||||||
let name: String?
|
|
||||||
let placeholder: String
|
|
||||||
|
|
||||||
private var _name: String {
|
|
||||||
guard let name else {
|
|
||||||
return id ?? ""
|
|
||||||
}
|
|
||||||
return name
|
|
||||||
}
|
|
||||||
|
|
||||||
init(
|
|
||||||
id: String? = nil,
|
|
||||||
name: String? = nil,
|
|
||||||
placeholder: String
|
|
||||||
) {
|
|
||||||
self.id = id
|
|
||||||
self.name = name
|
|
||||||
self.placeholder = placeholder
|
|
||||||
}
|
|
||||||
|
|
||||||
public init(
|
|
||||||
id: String,
|
|
||||||
name: String? = nil,
|
|
||||||
placeholder: String
|
|
||||||
) {
|
|
||||||
self.id = id
|
|
||||||
self.name = name
|
|
||||||
self.placeholder = placeholder
|
|
||||||
}
|
|
||||||
|
|
||||||
public init(
|
|
||||||
name: String,
|
|
||||||
placeholder: String
|
|
||||||
) {
|
|
||||||
self.init(id: nil, name: name, placeholder: placeholder)
|
|
||||||
}
|
|
||||||
|
|
||||||
public var body: some HTML<HTMLTag.input> {
|
|
||||||
input(
|
|
||||||
.id(id ?? ""), .name(_name), .placeholder(placeholder),
|
|
||||||
.class(
|
|
||||||
"""
|
|
||||||
input w-full rounded-md bg-white px-3 py-1.5 text-slate-900 outline-1
|
|
||||||
-outline-offset-1 outline-slate-300 focus:outline focus:-outline-offset-2
|
|
||||||
focus:outline-indigo-600 invalid:border-red-500 out-of-range:border-red-500
|
|
||||||
"""
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension HTMLAttribute where Tag == HTMLTag.input {
|
extension HTMLAttribute where Tag == HTMLTag.input {
|
||||||
|
|
||||||
public static func max(_ value: String) -> Self {
|
public static func max(_ value: String) -> Self {
|
||||||
|
|||||||
@@ -6,15 +6,6 @@ public struct Number: HTML, Sendable {
|
|||||||
let fractionDigits: Int
|
let fractionDigits: Int
|
||||||
let value: Double
|
let value: Double
|
||||||
|
|
||||||
// private var formatter: NumberFormatter {
|
|
||||||
// let formatter = NumberFormatter()
|
|
||||||
// formatter.maximumFractionDigits = fractionDigits
|
|
||||||
// formatter.numberStyle = .decimal
|
|
||||||
// formatter.groupingSize = 3
|
|
||||||
// formatter.groupingSeparator = ","
|
|
||||||
// return formatter
|
|
||||||
// }
|
|
||||||
|
|
||||||
public init(
|
public init(
|
||||||
_ value: Double,
|
_ value: Double,
|
||||||
digits fractionDigits: Int = 2
|
digits fractionDigits: Int = 2
|
||||||
|
|||||||
@@ -1,104 +0,0 @@
|
|||||||
import Dependencies
|
|
||||||
import Foundation
|
|
||||||
import ManualDCore
|
|
||||||
import Testing
|
|
||||||
import URLRouting
|
|
||||||
|
|
||||||
@Suite("ProjectRouteTests")
|
|
||||||
struct ProjectRouteTests {
|
|
||||||
let router = SiteRoute.Api.router
|
|
||||||
|
|
||||||
@Test
|
|
||||||
func create() throws {
|
|
||||||
let json = """
|
|
||||||
{
|
|
||||||
\"name\": \"Test\",
|
|
||||||
\"streetAddress\": \"1234 Seasme Street\",
|
|
||||||
\"city\": \"Nowhere\",
|
|
||||||
\"state\": \"OH\",
|
|
||||||
\"zipCode\": \"55555\"
|
|
||||||
}
|
|
||||||
"""
|
|
||||||
var request = URLRequestData(
|
|
||||||
method: "POST",
|
|
||||||
path: "/api/v1/projects",
|
|
||||||
body: .init(json.utf8)
|
|
||||||
)
|
|
||||||
let route = try router.parse(&request)
|
|
||||||
#expect(
|
|
||||||
route
|
|
||||||
== .project(
|
|
||||||
.create(
|
|
||||||
.init(
|
|
||||||
name: "Test",
|
|
||||||
streetAddress: "1234 Seasme Street",
|
|
||||||
city: "Nowhere",
|
|
||||||
state: "OH",
|
|
||||||
zipCode: "55555"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
func delete() throws {
|
|
||||||
let id = UUID(0)
|
|
||||||
var request = URLRequestData(
|
|
||||||
method: "DELETE",
|
|
||||||
path: "/api/v1/projects/\(id)"
|
|
||||||
)
|
|
||||||
let route = try router.parse(&request)
|
|
||||||
#expect(route == .project(.delete(id: id)))
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
func get() throws {
|
|
||||||
let id = UUID(0)
|
|
||||||
var request = URLRequestData(
|
|
||||||
method: "GET",
|
|
||||||
path: "/api/v1/projects/\(id)"
|
|
||||||
)
|
|
||||||
let route = try router.parse(&request)
|
|
||||||
#expect(route == .project(.get(id: id)))
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
func index() throws {
|
|
||||||
var request = URLRequestData(
|
|
||||||
method: "GET",
|
|
||||||
path: "/api/v1/projects"
|
|
||||||
)
|
|
||||||
let route = try router.parse(&request)
|
|
||||||
#expect(route == .project(.index))
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
func formData() throws {
|
|
||||||
let p = Body {
|
|
||||||
FormData {
|
|
||||||
Optionally {
|
|
||||||
Field("id", default: nil) { EquivalentLength.ID.parser() }
|
|
||||||
}
|
|
||||||
Field("name", .string)
|
|
||||||
Field("type") { EquivalentLength.EffectiveLengthType.parser() }
|
|
||||||
Many {
|
|
||||||
Field("straightLengths") {
|
|
||||||
Int.parser()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.map(.memberwise(SiteRoute.View.ProjectRoute.EquivalentLengthRoute.StepTwo.init))
|
|
||||||
}
|
|
||||||
|
|
||||||
var request = URLRequestData(
|
|
||||||
body: .init(
|
|
||||||
"name=Test&type=supply&straightLengths=20&straightLengths=10"
|
|
||||||
.utf8
|
|
||||||
)
|
|
||||||
)
|
|
||||||
let value = try p.parse(&request)
|
|
||||||
print(value)
|
|
||||||
#expect(value.straightLengths == [20, 10])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user