feat: Fixes release build failures and get's release dockerfile working.
This commit is contained in:
@@ -27,8 +27,8 @@ COPY . .
|
|||||||
# N.B.: The static version of jemalloc is incompatible with the static Swift runtime.
|
# N.B.: The static version of jemalloc is incompatible with the static Swift runtime.
|
||||||
RUN swift build -c release \
|
RUN swift build -c release \
|
||||||
--product App \
|
--product App \
|
||||||
--static-swift-stdlib \
|
--static-swift-stdlib \
|
||||||
-Xlinker -ljemalloc
|
-Xlinker -ljemalloc
|
||||||
|
|
||||||
# Switch to the staging area
|
# Switch to the staging area
|
||||||
WORKDIR /staging
|
WORKDIR /staging
|
||||||
|
|||||||
@@ -164,5 +164,5 @@ var swiftSettings: [SwiftSetting] { [
|
|||||||
.enableExperimentalFeature("StrictConcurrency=complete"),
|
.enableExperimentalFeature("StrictConcurrency=complete"),
|
||||||
.enableUpcomingFeature("ExistentialAny"),
|
.enableUpcomingFeature("ExistentialAny"),
|
||||||
.enableUpcomingFeature("ConciseMagicFile"),
|
.enableUpcomingFeature("ConciseMagicFile"),
|
||||||
.enableUpcomingFeature("ForwardTrailinClosures")
|
.enableUpcomingFeature("ForwardTrailingClosures")
|
||||||
] }
|
] }
|
||||||
|
|||||||
@@ -16,36 +16,13 @@ extension SharedModels.ViewRoute {
|
|||||||
|
|
||||||
var middleware: [any Middleware]? {
|
var middleware: [any Middleware]? {
|
||||||
switch self {
|
switch self {
|
||||||
// case .index: return viewProtectedMiddleware
|
case .employee,
|
||||||
case let .employee(route): return route.middleware
|
.purchaseOrder,
|
||||||
|
.user,
|
||||||
|
.vendor,
|
||||||
|
.vendorBranch:
|
||||||
|
return viewProtectedMiddleware
|
||||||
case .login: return nil
|
case .login: return nil
|
||||||
case let .purchaseOrder(route): return route.middleware
|
|
||||||
case let .user(route): return route.middleware
|
|
||||||
case let .vendor(route): return route.middleware
|
|
||||||
case let .vendorBranch(route): return route.middleware
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension SharedModels.ViewRoute.EmployeeRoute {
|
|
||||||
|
|
||||||
var middleware: [any Middleware]? { viewProtectedMiddleware }
|
|
||||||
}
|
|
||||||
|
|
||||||
extension SharedModels.ViewRoute.PurchaseOrderRoute {
|
|
||||||
var middleware: [any Middleware]? { viewProtectedMiddleware }
|
|
||||||
}
|
|
||||||
|
|
||||||
extension SharedModels.ViewRoute.UserRoute {
|
|
||||||
var middleware: [any Middleware]? {
|
|
||||||
viewProtectedMiddleware
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension SharedModels.ViewRoute.VendorRoute {
|
|
||||||
var middleware: [any Middleware]? { viewProtectedMiddleware }
|
|
||||||
}
|
|
||||||
|
|
||||||
extension SharedModels.ViewRoute.VendorBranchRoute {
|
|
||||||
var middleware: [any Middleware]? { viewProtectedMiddleware }
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -6,8 +6,8 @@ import Vapor
|
|||||||
|
|
||||||
public extension DatabaseClient.Employees {
|
public extension DatabaseClient.Employees {
|
||||||
|
|
||||||
static func live(database: any Database) -> DatabaseClient.Employees {
|
static func live(database: any Database) -> Self {
|
||||||
.init { create in
|
Self { create in
|
||||||
let model = try create.toModel()
|
let model = try create.toModel()
|
||||||
try await model.save(on: database)
|
try await model.save(on: database)
|
||||||
return try model.toDTO()
|
return try model.toDTO()
|
||||||
@@ -18,13 +18,13 @@ public extension DatabaseClient.Employees {
|
|||||||
try await model.delete(on: database)
|
try await model.delete(on: database)
|
||||||
} fetchAll: { request in
|
} fetchAll: { request in
|
||||||
var query = EmployeeModel.query(on: database)
|
var query = EmployeeModel.query(on: database)
|
||||||
.sort(\.$lastName)
|
.sort(\EmployeeModel.$lastName)
|
||||||
|
|
||||||
switch request {
|
switch request {
|
||||||
case .active:
|
case .active:
|
||||||
query = query.filter(\.$active == true)
|
query = query.filter(\EmployeeModel.$active == true)
|
||||||
case .inactive:
|
case .inactive:
|
||||||
query = query.filter(\.$active == false)
|
query = query.filter(\EmployeeModel.$active == false)
|
||||||
case .all:
|
case .all:
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@@ -9,7 +9,8 @@ public extension DatabaseClient.PurchaseOrders {
|
|||||||
.init { create in
|
.init { create in
|
||||||
let model = try create.toModel()
|
let model = try create.toModel()
|
||||||
try await model.save(on: database)
|
try await model.save(on: database)
|
||||||
let fetched = try await PurchaseOrderModel.allQuery(on: database).filter(\.$id == model.id!).first()!
|
let fetched = try await PurchaseOrderModel.allQuery(on: database)
|
||||||
|
.filter(\PurchaseOrderModel.$id == model.id!).first()!
|
||||||
return try fetched.toDTO()
|
return try fetched.toDTO()
|
||||||
} fetchAll: {
|
} fetchAll: {
|
||||||
try await PurchaseOrderModel.allQuery(on: database)
|
try await PurchaseOrderModel.allQuery(on: database)
|
||||||
@@ -21,7 +22,7 @@ public extension DatabaseClient.PurchaseOrders {
|
|||||||
.map { try $0.toDTO() }
|
.map { try $0.toDTO() }
|
||||||
} get: { id in
|
} get: { id in
|
||||||
try await PurchaseOrderModel.allQuery(on: database)
|
try await PurchaseOrderModel.allQuery(on: database)
|
||||||
.filter(\.$id == id)
|
.filter(\PurchaseOrderModel.$id == id)
|
||||||
.first()
|
.first()
|
||||||
.map { try $0.toDTO() }
|
.map { try $0.toDTO() }
|
||||||
} delete: { id in
|
} delete: { id in
|
||||||
@@ -34,17 +35,17 @@ public extension DatabaseClient.PurchaseOrders {
|
|||||||
|
|
||||||
switch search {
|
switch search {
|
||||||
case let .employee(id):
|
case let .employee(id):
|
||||||
return try await query.filter(\.$createdFor.$id == id)
|
return try await query.filter(\PurchaseOrderModel.$createdFor.$id == id)
|
||||||
.paginate(page)
|
.paginate(page)
|
||||||
.map { try $0.toDTO() }
|
.map { try $0.toDTO() }
|
||||||
|
|
||||||
case let .customer(search):
|
case let .customer(search):
|
||||||
return try await query.filter(\.$customer ~~ search)
|
return try await query.filter(\PurchaseOrderModel.$customer ~~ search)
|
||||||
.paginate(page)
|
.paginate(page)
|
||||||
.map { try $0.toDTO() }
|
.map { try $0.toDTO() }
|
||||||
|
|
||||||
case let .vendor(id):
|
case let .vendor(id):
|
||||||
return try await query.filter(\.$vendorBranch.$id == id)
|
return try await query.filter(\PurchaseOrderModel.$vendorBranch.$id == id)
|
||||||
.paginate(page)
|
.paginate(page)
|
||||||
.map { try $0.toDTO() }
|
.map { try $0.toDTO() }
|
||||||
}
|
}
|
||||||
@@ -190,11 +191,11 @@ final class PurchaseOrderModel: Model, Codable, @unchecked Sendable {
|
|||||||
|
|
||||||
static func allQuery(on db: any Database) -> QueryBuilder<PurchaseOrderModel> {
|
static func allQuery(on db: any Database) -> QueryBuilder<PurchaseOrderModel> {
|
||||||
PurchaseOrderModel.query(on: db)
|
PurchaseOrderModel.query(on: db)
|
||||||
.sort(\.$id, .descending)
|
.sort(\PurchaseOrderModel.$id, .descending)
|
||||||
.with(\.$createdBy)
|
.with(\PurchaseOrderModel.$createdBy)
|
||||||
.with(\.$createdFor)
|
.with(\PurchaseOrderModel.$createdFor)
|
||||||
.with(\.$vendorBranch) { branch in
|
.with(\PurchaseOrderModel.$vendorBranch) { branch in
|
||||||
branch.with(\.$vendor)
|
branch.with(\VendorBranchModel.$vendor)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -28,9 +28,9 @@ public extension DatabaseClient.Users {
|
|||||||
var query = UserModel.query(on: database)
|
var query = UserModel.query(on: database)
|
||||||
|
|
||||||
if let username = login.username {
|
if let username = login.username {
|
||||||
query = query.filter(\.$username == username)
|
query = query.filter(\UserModel.$username == username)
|
||||||
} else {
|
} else {
|
||||||
query = query.filter(\.$email == login.email!)
|
query = query.filter(\UserModel.$email == login.email!)
|
||||||
}
|
}
|
||||||
|
|
||||||
guard let user = try await query.first() else {
|
guard let user = try await query.first() else {
|
||||||
@@ -265,7 +265,7 @@ public struct UserPasswordAuthenticator: AsyncBasicAuthenticator {
|
|||||||
|
|
||||||
public func authenticate(basic: BasicAuthorization, for request: Request) async throws {
|
public func authenticate(basic: BasicAuthorization, for request: Request) async throws {
|
||||||
guard let user = try await UserModel.query(on: request.db)
|
guard let user = try await UserModel.query(on: request.db)
|
||||||
.filter(\.$username == basic.username)
|
.filter(\UserModel.$username == basic.username)
|
||||||
.first(),
|
.first(),
|
||||||
try Bcrypt.verify(basic.password, created: user.passwordHash)
|
try Bcrypt.verify(basic.password, created: user.passwordHash)
|
||||||
else {
|
else {
|
||||||
@@ -282,8 +282,8 @@ public struct UserTokenAuthenticator: AsyncBearerAuthenticator {
|
|||||||
|
|
||||||
public func authenticate(bearer: BearerAuthorization, for request: Request) async throws {
|
public func authenticate(bearer: BearerAuthorization, for request: Request) async throws {
|
||||||
guard let token = try await UserTokenModel.query(on: request.db)
|
guard let token = try await UserTokenModel.query(on: request.db)
|
||||||
.filter(\.$value == bearer.token)
|
.filter(\UserTokenModel.$value == bearer.token)
|
||||||
.with(\.$user)
|
.with(\UserTokenModel.$user)
|
||||||
.first()
|
.first()
|
||||||
else {
|
else {
|
||||||
throw Abort(.unauthorized)
|
throw Abort(.unauthorized)
|
||||||
@@ -299,7 +299,7 @@ public struct UserSessionAuthenticator: AsyncSessionAuthenticator {
|
|||||||
|
|
||||||
public func authenticate(sessionID: User.SessionID, for request: Request) async throws {
|
public func authenticate(sessionID: User.SessionID, for request: Request) async throws {
|
||||||
guard let user = try await UserModel.query(on: request.db)
|
guard let user = try await UserModel.query(on: request.db)
|
||||||
.filter(\.$username == sessionID)
|
.filter(\UserModel.$username == sessionID)
|
||||||
.first()
|
.first()
|
||||||
else {
|
else {
|
||||||
throw Abort(.unauthorized)
|
throw Abort(.unauthorized)
|
||||||
@@ -23,8 +23,8 @@ public extension DatabaseClient.VendorBranches {
|
|||||||
break
|
break
|
||||||
case let .for(vendorID: vendorID):
|
case let .for(vendorID: vendorID):
|
||||||
let branches = try await VendorModel.query(on: db)
|
let branches = try await VendorModel.query(on: db)
|
||||||
.filter(\.$id == vendorID)
|
.filter(\VendorModel.$id == vendorID)
|
||||||
.with(\.$branches)
|
.with(\VendorModel.$branches)
|
||||||
.first()?
|
.first()?
|
||||||
.branches
|
.branches
|
||||||
.map { try $0.toDTO() }
|
.map { try $0.toDTO() }
|
||||||
@@ -36,7 +36,7 @@ public extension DatabaseClient.VendorBranches {
|
|||||||
return try await query.all().map { try $0.toDTO() }
|
return try await query.all().map { try $0.toDTO() }
|
||||||
} fetchAllWithDetail: {
|
} fetchAllWithDetail: {
|
||||||
try await VendorBranchModel.query(on: db)
|
try await VendorBranchModel.query(on: db)
|
||||||
.with(\.$vendor)
|
.with(\VendorBranchModel.$vendor)
|
||||||
.all()
|
.all()
|
||||||
.map { try $0.toDetail() }
|
.map { try $0.toDetail() }
|
||||||
} get: { id in
|
} get: { id in
|
||||||
@@ -17,13 +17,13 @@ public extension DatabaseClient.Vendors {
|
|||||||
}
|
}
|
||||||
try await model.delete(on: db)
|
try await model.delete(on: db)
|
||||||
} fetchAll: { request in
|
} fetchAll: { request in
|
||||||
var query = VendorModel.query(on: db).sort(\.$name, .ascending)
|
var query = VendorModel.query(on: db).sort(\VendorModel.$name, .ascending)
|
||||||
|
|
||||||
let withBranches = request == .withBranches
|
let withBranches = request == .withBranches
|
||||||
|
|
||||||
switch request {
|
switch request {
|
||||||
case .withBranches:
|
case .withBranches:
|
||||||
query = query.with(\.$branches)
|
query = query.with(\VendorModel.$branches)
|
||||||
case .all:
|
case .all:
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@@ -34,7 +34,7 @@ public extension DatabaseClient.Vendors {
|
|||||||
var query = VendorModel.query(on: db).filter(\.$id == id)
|
var query = VendorModel.query(on: db).filter(\.$id == id)
|
||||||
let withBranches = request == .withBranches
|
let withBranches = request == .withBranches
|
||||||
if withBranches {
|
if withBranches {
|
||||||
query = query.with(\.$branches)
|
query = query.with(\VendorModel.$branches)
|
||||||
}
|
}
|
||||||
return try await query.first().map { try $0.toDTO(includeBranches: withBranches) }
|
return try await query.first().map { try $0.toDTO(includeBranches: withBranches) }
|
||||||
} update: { id, updates, withBranches in
|
} update: { id, updates, withBranches in
|
||||||
@@ -47,8 +47,8 @@ public extension DatabaseClient.Vendors {
|
|||||||
return try model.toDTO()
|
return try model.toDTO()
|
||||||
}
|
}
|
||||||
return try await VendorModel.query(on: db)
|
return try await VendorModel.query(on: db)
|
||||||
.filter(\.$id == id)
|
.filter(\VendorModel.$id == id)
|
||||||
.with(\.$branches)
|
.with(\VendorModel.$branches)
|
||||||
.first()!
|
.first()!
|
||||||
.toDTO()
|
.toDTO()
|
||||||
}
|
}
|
||||||
@@ -8,6 +8,7 @@ import ViewController
|
|||||||
|
|
||||||
public extension SharedModels.ViewRoute {
|
public extension SharedModels.ViewRoute {
|
||||||
|
|
||||||
|
@Sendable
|
||||||
func view(
|
func view(
|
||||||
isHtmxRequest: Bool,
|
isHtmxRequest: Bool,
|
||||||
logger: Logger,
|
logger: Logger,
|
||||||
@@ -55,9 +56,10 @@ extension SharedModels.ViewRoute.EmployeeRoute {
|
|||||||
|
|
||||||
private func mainPage<C: HTML>(
|
private func mainPage<C: HTML>(
|
||||||
_ html: C
|
_ html: C
|
||||||
) async throws -> some SendableHTMLDocument where C: Sendable {
|
) async throws -> AnySendableHTML where C: Sendable {
|
||||||
@Dependency(\.database) var database
|
@Dependency(\.database) var database
|
||||||
let employees = try await database.employees.fetchAll()
|
let employees = try await database.employees.fetchAll()
|
||||||
|
// return EmployeeMainPage(employees: employees, html: html)
|
||||||
return MainPage(displayNav: true, route: .employees) {
|
return MainPage(displayNav: true, route: .employees) {
|
||||||
div(.class("container")) {
|
div(.class("container")) {
|
||||||
html
|
html
|
||||||
@@ -66,6 +68,7 @@ extension SharedModels.ViewRoute.EmployeeRoute {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Sendable
|
||||||
func view(isHtmxRequest: Bool) async throws -> AnySendableHTML {
|
func view(isHtmxRequest: Bool) async throws -> AnySendableHTML {
|
||||||
@Dependency(\.database.employees) var employees
|
@Dependency(\.database.employees) var employees
|
||||||
|
|
||||||
@@ -98,7 +101,7 @@ extension SharedModels.ViewRoute.EmployeeRoute {
|
|||||||
extension SharedModels.ViewRoute.PurchaseOrderRoute {
|
extension SharedModels.ViewRoute.PurchaseOrderRoute {
|
||||||
private func mainPage<C: HTML>(
|
private func mainPage<C: HTML>(
|
||||||
_ html: C
|
_ html: C
|
||||||
) async throws -> some SendableHTMLDocument where C: Sendable {
|
) async throws -> AnySendableHTML where C: Sendable {
|
||||||
@Dependency(\.database.purchaseOrders) var purchaseOrders
|
@Dependency(\.database.purchaseOrders) var purchaseOrders
|
||||||
let page = try await purchaseOrders.fetchPage(.init(page: 1, per: 25))
|
let page = try await purchaseOrders.fetchPage(.init(page: 1, per: 25))
|
||||||
return MainPage(displayNav: true, route: .purchaseOrders) {
|
return MainPage(displayNav: true, route: .purchaseOrders) {
|
||||||
@@ -109,6 +112,7 @@ extension SharedModels.ViewRoute.PurchaseOrderRoute {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Sendable
|
||||||
func view(isHtmxRequest: Bool) async throws -> AnySendableHTML {
|
func view(isHtmxRequest: Bool) async throws -> AnySendableHTML {
|
||||||
@Dependency(\.database.purchaseOrders) var purchaseOrders
|
@Dependency(\.database.purchaseOrders) var purchaseOrders
|
||||||
|
|
||||||
@@ -146,7 +150,7 @@ extension SharedModels.ViewRoute.PurchaseOrderRoute {
|
|||||||
|
|
||||||
extension SharedModels.ViewRoute.PurchaseOrderRoute.Search {
|
extension SharedModels.ViewRoute.PurchaseOrderRoute.Search {
|
||||||
|
|
||||||
func mainPage(search: PurchaseOrderSearch = .init()) -> some SendableHTMLDocument {
|
func mainPage(search: PurchaseOrderSearch = .init()) -> AnySendableHTML {
|
||||||
MainPage(displayNav: true, route: .purchaseOrders) {
|
MainPage(displayNav: true, route: .purchaseOrders) {
|
||||||
div(.class("container"), .id("purchase-order-content")) {
|
div(.class("container"), .id("purchase-order-content")) {
|
||||||
search
|
search
|
||||||
@@ -158,6 +162,7 @@ extension SharedModels.ViewRoute.PurchaseOrderRoute.Search {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Sendable
|
||||||
func view(isHtmxRequest: Bool) async throws -> AnySendableHTML {
|
func view(isHtmxRequest: Bool) async throws -> AnySendableHTML {
|
||||||
@Dependency(\.database) var database
|
@Dependency(\.database) var database
|
||||||
switch self {
|
switch self {
|
||||||
@@ -177,7 +182,7 @@ extension SharedModels.ViewRoute.PurchaseOrderRoute.Search {
|
|||||||
|
|
||||||
extension SharedModels.ViewRoute.UserRoute {
|
extension SharedModels.ViewRoute.UserRoute {
|
||||||
|
|
||||||
private func mainPage<C: HTML>(_ html: C) async throws -> some SendableHTMLDocument where C: Sendable {
|
private func mainPage<C: HTML>(_ html: C) async throws -> AnySendableHTML where C: Sendable {
|
||||||
@Dependency(\.database) var database
|
@Dependency(\.database) var database
|
||||||
let users = try await database.users.fetchAll()
|
let users = try await database.users.fetchAll()
|
||||||
return MainPage(displayNav: true, route: .users) {
|
return MainPage(displayNav: true, route: .users) {
|
||||||
@@ -188,6 +193,7 @@ extension SharedModels.ViewRoute.UserRoute {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Sendable
|
||||||
func view(isHtmxRequest: Bool) async throws -> AnySendableHTML {
|
func view(isHtmxRequest: Bool) async throws -> AnySendableHTML {
|
||||||
@Dependency(\.database.users) var users
|
@Dependency(\.database.users) var users
|
||||||
|
|
||||||
@@ -215,7 +221,7 @@ extension SharedModels.ViewRoute.UserRoute {
|
|||||||
}
|
}
|
||||||
|
|
||||||
extension SharedModels.ViewRoute.VendorRoute {
|
extension SharedModels.ViewRoute.VendorRoute {
|
||||||
private func mainPage<C: HTML>(_ html: C) async throws -> some SendableHTMLDocument where C: Sendable {
|
private func mainPage<C: HTML>(_ html: C) async throws -> AnySendableHTML where C: Sendable {
|
||||||
@Dependency(\.database) var database
|
@Dependency(\.database) var database
|
||||||
let vendors = try await database.vendors.fetchAll(.withBranches)
|
let vendors = try await database.vendors.fetchAll(.withBranches)
|
||||||
return MainPage(displayNav: true, route: .vendors) {
|
return MainPage(displayNav: true, route: .vendors) {
|
||||||
@@ -226,6 +232,7 @@ extension SharedModels.ViewRoute.VendorRoute {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Sendable
|
||||||
func view(isHtmxRequest: Bool) async throws -> AnySendableHTML {
|
func view(isHtmxRequest: Bool) async throws -> AnySendableHTML {
|
||||||
@Dependency(\.database) var database
|
@Dependency(\.database) var database
|
||||||
|
|
||||||
@@ -262,6 +269,7 @@ extension SharedModels.ViewRoute.VendorRoute {
|
|||||||
|
|
||||||
extension SharedModels.ViewRoute.VendorBranchRoute {
|
extension SharedModels.ViewRoute.VendorBranchRoute {
|
||||||
|
|
||||||
|
@Sendable
|
||||||
func view(isHtmxRequest: Bool) async throws -> AnySendableHTML {
|
func view(isHtmxRequest: Bool) async throws -> AnySendableHTML {
|
||||||
@Dependency(\.database) var database
|
@Dependency(\.database) var database
|
||||||
|
|
||||||
@@ -286,6 +294,7 @@ extension SharedModels.ViewRoute.VendorBranchRoute {
|
|||||||
|
|
||||||
extension SharedModels.ViewRoute.PurchaseOrderRoute.Search.Request {
|
extension SharedModels.ViewRoute.PurchaseOrderRoute.Search.Request {
|
||||||
|
|
||||||
|
@Sendable
|
||||||
func toDatabaseQuery() throws -> PurchaseOrder.SearchContext {
|
func toDatabaseQuery() throws -> PurchaseOrder.SearchContext {
|
||||||
switch context {
|
switch context {
|
||||||
case .employee:
|
case .employee:
|
||||||
@@ -308,6 +317,8 @@ extension SharedModels.ViewRoute.PurchaseOrderRoute.Search.Request {
|
|||||||
}
|
}
|
||||||
|
|
||||||
extension SharedModels.ViewRoute.SelectContext {
|
extension SharedModels.ViewRoute.SelectContext {
|
||||||
|
|
||||||
|
@Sendable
|
||||||
func toHTML(employees: [Employee]) -> EmployeeSelect {
|
func toHTML(employees: [Employee]) -> EmployeeSelect {
|
||||||
switch self {
|
switch self {
|
||||||
case .purchaseOrderForm:
|
case .purchaseOrderForm:
|
||||||
@@ -327,8 +338,9 @@ extension SharedModels.ViewRoute.SelectContext {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Sendable
|
||||||
private func render<C: HTML>(
|
private func render<C: HTML>(
|
||||||
_ mainPage: (C) async throws -> any SendableHTMLDocument,
|
_ mainPage: (C) async throws -> AnySendableHTML,
|
||||||
_ isHtmxRequest: Bool,
|
_ isHtmxRequest: Bool,
|
||||||
@HTMLBuilder html: () -> C
|
@HTMLBuilder html: () -> C
|
||||||
) async rethrows -> AnySendableHTML where C: Sendable {
|
) async rethrows -> AnySendableHTML where C: Sendable {
|
||||||
@@ -338,8 +350,9 @@ private func render<C: HTML>(
|
|||||||
return html()
|
return html()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Sendable
|
||||||
private func render<C: HTML>(
|
private func render<C: HTML>(
|
||||||
_ mainPage: (C) async throws -> any SendableHTMLDocument,
|
_ mainPage: (C) async throws -> AnySendableHTML,
|
||||||
_ isHtmxRequest: Bool,
|
_ isHtmxRequest: Bool,
|
||||||
_ html: @autoclosure @escaping () -> C
|
_ html: @autoclosure @escaping () -> C
|
||||||
) async rethrows -> AnySendableHTML where C: Sendable {
|
) async rethrows -> AnySendableHTML where C: Sendable {
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import Elementary
|
|||||||
import ElementaryHTMX
|
import ElementaryHTMX
|
||||||
import SharedModels
|
import SharedModels
|
||||||
|
|
||||||
struct EmployeeTable: HTML {
|
struct EmployeeTable: HTML, Sendable {
|
||||||
let employees: [Employee]
|
let employees: [Employee]
|
||||||
|
|
||||||
var content: some HTML {
|
var content: some HTML {
|
||||||
@@ -29,7 +29,7 @@ struct EmployeeTable: HTML {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Row: HTML {
|
struct Row: HTML, Sendable {
|
||||||
let employee: Employee
|
let employee: Employee
|
||||||
|
|
||||||
var content: some HTML {
|
var content: some HTML {
|
||||||
|
|||||||
@@ -3,24 +3,29 @@ import ElementaryHTMX
|
|||||||
import SharedModels
|
import SharedModels
|
||||||
|
|
||||||
extension HTMLAttribute.hx {
|
extension HTMLAttribute.hx {
|
||||||
|
@Sendable
|
||||||
static func get(route: SharedModels.ViewRoute) -> HTMLAttribute {
|
static func get(route: SharedModels.ViewRoute) -> HTMLAttribute {
|
||||||
get(SharedModels.ViewRoute.router.path(for: route))
|
get(SharedModels.ViewRoute.router.path(for: route))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Sendable
|
||||||
static func post(route: SharedModels.ViewRoute) -> HTMLAttribute {
|
static func post(route: SharedModels.ViewRoute) -> HTMLAttribute {
|
||||||
post(SharedModels.ViewRoute.router.path(for: route))
|
post(SharedModels.ViewRoute.router.path(for: route))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Sendable
|
||||||
static func put(route: SharedModels.ViewRoute) -> HTMLAttribute {
|
static func put(route: SharedModels.ViewRoute) -> HTMLAttribute {
|
||||||
put(SharedModels.ViewRoute.router.path(for: route))
|
put(SharedModels.ViewRoute.router.path(for: route))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Sendable
|
||||||
static func delete(route: SharedModels.ApiRoute) -> HTMLAttribute {
|
static func delete(route: SharedModels.ApiRoute) -> HTMLAttribute {
|
||||||
delete(SharedModels.ApiRoute.router.path(for: route))
|
delete(SharedModels.ApiRoute.router.path(for: route))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension HTMLAttribute.hx {
|
extension HTMLAttribute.hx {
|
||||||
|
@Sendable
|
||||||
static func target(_ target: HXTarget) -> HTMLAttribute {
|
static func target(_ target: HXTarget) -> HTMLAttribute {
|
||||||
Self.target(target.selector)
|
Self.target(target.selector)
|
||||||
}
|
}
|
||||||
@@ -28,37 +33,43 @@ extension HTMLAttribute.hx {
|
|||||||
|
|
||||||
extension HTMLAttribute.hx where Tag: HTMLTrait.Attributes.Global {
|
extension HTMLAttribute.hx where Tag: HTMLTrait.Attributes.Global {
|
||||||
|
|
||||||
|
@Sendable
|
||||||
static func on(_ event: HXOnEvent, value: String) -> HTMLAttribute {
|
static func on(_ event: HXOnEvent, value: String) -> HTMLAttribute {
|
||||||
HTMLAttribute.custom(name: "hx-on::\(event.rawValue)", value: value)
|
HTMLAttribute.custom(name: "hx-on::\(event.rawValue)", value: value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Sendable
|
||||||
static func on(_ event: HXOnEvent, _ value: HXOnValue) -> HTMLAttribute {
|
static func on(_ event: HXOnEvent, _ value: HXOnValue) -> HTMLAttribute {
|
||||||
on(event, value: value.value)
|
on(event, value: value.value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Sendable
|
||||||
static func on(_ event: HXOnEvent, _ values: HXOnValue...) -> HTMLAttribute {
|
static func on(_ event: HXOnEvent, _ values: HXOnValue...) -> HTMLAttribute {
|
||||||
on(event, value: values.value)
|
on(event, value: values.value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum HXOnEvent: String {
|
enum HXOnEvent: String, Sendable {
|
||||||
case afterRequest = "after-request"
|
case afterRequest = "after-request"
|
||||||
}
|
}
|
||||||
|
|
||||||
indirect enum HXOnValue {
|
indirect enum HXOnValue: Sendable {
|
||||||
case ifSuccessful([Self])
|
case ifSuccessful([Self])
|
||||||
case resetForm
|
case resetForm
|
||||||
case setWindowLocation(String)
|
case setWindowLocation(String)
|
||||||
case toggleContent(id: String)
|
case toggleContent(id: String)
|
||||||
|
|
||||||
|
@Sendable
|
||||||
static func toggleContent(_ toggle: Toggle) -> Self {
|
static func toggleContent(_ toggle: Toggle) -> Self {
|
||||||
toggleContent(id: toggle.rawValue)
|
toggleContent(id: toggle.rawValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Sendable
|
||||||
static func setWindowLocation(to route: ViewRoute) -> Self {
|
static func setWindowLocation(to route: ViewRoute) -> Self {
|
||||||
setWindowLocation(ViewRoute.router.path(for: route))
|
setWindowLocation(ViewRoute.router.path(for: route))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Sendable
|
||||||
static func ifSuccessful(_ values: Self...) -> Self {
|
static func ifSuccessful(_ values: Self...) -> Self {
|
||||||
.ifSuccessful(values)
|
.ifSuccessful(values)
|
||||||
}
|
}
|
||||||
@@ -76,7 +87,7 @@ indirect enum HXOnValue {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum Toggle: String {
|
enum Toggle: String, Sendable {
|
||||||
case float
|
case float
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -89,12 +100,14 @@ extension Array where Element == HXOnValue {
|
|||||||
}
|
}
|
||||||
|
|
||||||
extension HTMLAttribute where Tag: HTMLTrait.Attributes.Global {
|
extension HTMLAttribute where Tag: HTMLTrait.Attributes.Global {
|
||||||
|
|
||||||
|
@Sendable
|
||||||
static func id(_ key: IDKey) -> Self {
|
static func id(_ key: IDKey) -> Self {
|
||||||
id(key.description)
|
id(key.description)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum IDKey: CustomStringConvertible {
|
enum IDKey: CustomStringConvertible, Sendable {
|
||||||
case branch(Branch)
|
case branch(Branch)
|
||||||
case employee(Employee)
|
case employee(Employee)
|
||||||
case float
|
case float
|
||||||
@@ -115,7 +128,7 @@ enum IDKey: CustomStringConvertible {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum Branch: CustomStringConvertible {
|
enum Branch: CustomStringConvertible, Sendable {
|
||||||
case list
|
case list
|
||||||
case form
|
case form
|
||||||
case row(id: VendorBranch.ID)
|
case row(id: VendorBranch.ID)
|
||||||
@@ -129,7 +142,7 @@ enum IDKey: CustomStringConvertible {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum Employee: CustomStringConvertible {
|
enum Employee: CustomStringConvertible, Sendable {
|
||||||
case table
|
case table
|
||||||
case row(id: SharedModels.Employee.ID)
|
case row(id: SharedModels.Employee.ID)
|
||||||
|
|
||||||
@@ -141,7 +154,7 @@ enum IDKey: CustomStringConvertible {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum PurchaseOrder: CustomStringConvertible {
|
enum PurchaseOrder: CustomStringConvertible, Sendable {
|
||||||
case content
|
case content
|
||||||
case row(id: SharedModels.PurchaseOrder.ID)
|
case row(id: SharedModels.PurchaseOrder.ID)
|
||||||
case search
|
case search
|
||||||
@@ -157,7 +170,7 @@ enum IDKey: CustomStringConvertible {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum User: CustomStringConvertible {
|
enum User: CustomStringConvertible, Sendable {
|
||||||
case form
|
case form
|
||||||
case row(id: SharedModels.User.ID)
|
case row(id: SharedModels.User.ID)
|
||||||
case table
|
case table
|
||||||
@@ -171,7 +184,7 @@ enum IDKey: CustomStringConvertible {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum Vendor: CustomStringConvertible {
|
enum Vendor: CustomStringConvertible, Sendable {
|
||||||
case form
|
case form
|
||||||
case row(id: SharedModels.Vendor.ID)
|
case row(id: SharedModels.Vendor.ID)
|
||||||
|
|
||||||
@@ -185,7 +198,7 @@ enum IDKey: CustomStringConvertible {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum HXTarget: CustomStringConvertible {
|
enum HXTarget: CustomStringConvertible, Sendable {
|
||||||
case body
|
case body
|
||||||
case id(IDKey)
|
case id(IDKey)
|
||||||
case this
|
case this
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ extension MainPage where Inner == LoggedIn {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct LoggedIn: HTML {
|
struct LoggedIn: HTML, Sendable {
|
||||||
let next: String?
|
let next: String?
|
||||||
var content: some HTML {
|
var content: some HTML {
|
||||||
div(
|
div(
|
||||||
@@ -72,7 +72,7 @@ struct LoggedIn: HTML {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct RouteHeaderView: HTML {
|
struct RouteHeaderView: HTML, Sendable {
|
||||||
|
|
||||||
let title: String
|
let title: String
|
||||||
let description: String
|
let description: String
|
||||||
@@ -95,7 +95,7 @@ struct RouteHeaderView: HTML {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum ViewRoute: String {
|
enum ViewRoute: String, Sendable {
|
||||||
|
|
||||||
case employees
|
case employees
|
||||||
case login
|
case login
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import Elementary
|
|||||||
import ElementaryHTMX
|
import ElementaryHTMX
|
||||||
import SharedModels
|
import SharedModels
|
||||||
|
|
||||||
struct PurchaseOrderForm: HTML {
|
struct PurchaseOrderForm: HTML, Sendable {
|
||||||
|
|
||||||
@Dependency(\.dateFormatter) var dateFormatter
|
@Dependency(\.dateFormatter) var dateFormatter
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import ElementaryHTMX
|
|||||||
import SharedModels
|
import SharedModels
|
||||||
import Vapor
|
import Vapor
|
||||||
|
|
||||||
struct PurchaseOrderSearch: HTML {
|
struct PurchaseOrderSearch: HTML, Sendable {
|
||||||
|
|
||||||
typealias Context = SharedModels.ViewRoute.PurchaseOrderRoute.Search.Context
|
typealias Context = SharedModels.ViewRoute.PurchaseOrderRoute.Search.Context
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import Fluent
|
|||||||
import SharedModels
|
import SharedModels
|
||||||
import Vapor
|
import Vapor
|
||||||
|
|
||||||
struct PurchaseOrderTable: HTML {
|
struct PurchaseOrderTable: HTML, Sendable {
|
||||||
typealias SearchContext = SharedModels.ViewRoute.PurchaseOrderRoute.Search.Context
|
typealias SearchContext = SharedModels.ViewRoute.PurchaseOrderRoute.Search.Context
|
||||||
|
|
||||||
let page: Page<PurchaseOrder>
|
let page: Page<PurchaseOrder>
|
||||||
@@ -72,7 +72,7 @@ struct PurchaseOrderTable: HTML {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Produces only the rows for the given page
|
// Produces only the rows for the given page
|
||||||
struct Rows: HTML {
|
struct Rows: HTML, Sendable {
|
||||||
let page: Page<PurchaseOrder>
|
let page: Page<PurchaseOrder>
|
||||||
|
|
||||||
var content: some HTML {
|
var content: some HTML {
|
||||||
@@ -98,7 +98,7 @@ struct PurchaseOrderTable: HTML {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// A single row.
|
// A single row.
|
||||||
struct Row: HTML {
|
struct Row: HTML, Sendable {
|
||||||
let purchaseOrder: PurchaseOrder
|
let purchaseOrder: PurchaseOrder
|
||||||
|
|
||||||
var content: some HTML<HTMLTag.tr> {
|
var content: some HTML<HTMLTag.tr> {
|
||||||
@@ -124,7 +124,7 @@ struct PurchaseOrderTable: HTML {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum Context: String {
|
enum Context: String, Sendable {
|
||||||
case `default`
|
case `default`
|
||||||
case search
|
case search
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ struct UserForm: HTML, Sendable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum Context: Equatable {
|
enum Context: Equatable, Sendable {
|
||||||
case create
|
case create
|
||||||
case login(next: String?)
|
case login(next: String?)
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import Elementary
|
|||||||
import ElementaryHTMX
|
import ElementaryHTMX
|
||||||
import SharedModels
|
import SharedModels
|
||||||
|
|
||||||
struct UserTable: HTML {
|
struct UserTable: HTML, Sendable {
|
||||||
|
|
||||||
let users: [User]
|
let users: [User]
|
||||||
|
|
||||||
@@ -32,7 +32,7 @@ struct UserTable: HTML {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Row: HTML {
|
struct Row: HTML, Sendable {
|
||||||
let user: User
|
let user: User
|
||||||
|
|
||||||
init(user: User) {
|
init(user: User) {
|
||||||
|
|||||||
@@ -2,7 +2,8 @@ import Elementary
|
|||||||
import SharedModels
|
import SharedModels
|
||||||
import URLRouting
|
import URLRouting
|
||||||
|
|
||||||
struct ToggleFormButton: HTML {
|
// TODO: Remove.
|
||||||
|
struct ToggleFormButton: HTML, Sendable {
|
||||||
var content: some HTML<HTMLTag.a> {
|
var content: some HTML<HTMLTag.a> {
|
||||||
a(.href("javascript:void(0)"), .on(.click, "toggleContent('form')"), .class("btn-add")) {
|
a(.href("javascript:void(0)"), .on(.click, "toggleContent('form')"), .class("btn-add")) {
|
||||||
"+"
|
"+"
|
||||||
@@ -12,20 +13,24 @@ struct ToggleFormButton: HTML {
|
|||||||
|
|
||||||
enum Button {
|
enum Button {
|
||||||
|
|
||||||
|
@Sendable
|
||||||
static func add() -> some HTML<HTMLTag.button> {
|
static func add() -> some HTML<HTMLTag.button> {
|
||||||
button(.class("btn btn-add")) { "+" }
|
button(.class("btn btn-add")) { "+" }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Sendable
|
||||||
static func danger<C: HTML>(@HTMLBuilder body: () -> C) -> some HTML<HTMLTag.button> {
|
static func danger<C: HTML>(@HTMLBuilder body: () -> C) -> some HTML<HTMLTag.button> {
|
||||||
button(.class("danger")) { body() }
|
button(.class("danger")) { body() }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Sendable
|
||||||
static func close(id: String, resetURL: String? = nil) -> some HTML<HTMLTag.button> {
|
static func close(id: String, resetURL: String? = nil) -> some HTML<HTMLTag.button> {
|
||||||
button(.class("btn-close"), .on(.click, makeOnClick(id, resetURL))) {
|
button(.class("btn-close"), .on(.click, makeOnClick(id, resetURL))) {
|
||||||
"x"
|
"x"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Sendable
|
||||||
static func close(id: IDKey, resetURL route: ViewRoute? = nil) -> some HTML<HTMLTag.button> {
|
static func close(id: IDKey, resetURL route: ViewRoute? = nil) -> some HTML<HTMLTag.button> {
|
||||||
close(
|
close(
|
||||||
id: id.description,
|
id: id.description,
|
||||||
@@ -33,16 +38,19 @@ enum Button {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Sendable
|
||||||
static func update() -> some HTML<HTMLTag.button> {
|
static func update() -> some HTML<HTMLTag.button> {
|
||||||
button(.class("btn-update")) { "Update" }
|
button(.class("btn-update")) { "Update" }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Sendable
|
||||||
static func detail() -> some HTML<HTMLTag.button> {
|
static func detail() -> some HTML<HTMLTag.button> {
|
||||||
button(.class("btn-detail")) {
|
button(.class("btn-detail")) {
|
||||||
"〉"
|
"〉"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Sendable
|
||||||
private static func makeOnClick(_ id: String, _ resetURL: String?) -> String {
|
private static func makeOnClick(_ id: String, _ resetURL: String?) -> String {
|
||||||
let output = "toggleContent('\(id)');"
|
let output = "toggleContent('\(id)');"
|
||||||
if let resetURL {
|
if let resetURL {
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ struct Float<C: HTML, B: HTML>: HTML {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct DefaultCloseButton: HTML {
|
struct DefaultCloseButton: HTML, Sendable {
|
||||||
let id: String
|
let id: String
|
||||||
let resetURL: String?
|
let resetURL: String?
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import ElementaryHTMX
|
|||||||
import SharedModels
|
import SharedModels
|
||||||
import Vapor
|
import Vapor
|
||||||
|
|
||||||
struct EmployeeSelect: HTML {
|
struct EmployeeSelect: HTML, Sendable {
|
||||||
|
|
||||||
let employees: [Employee]?
|
let employees: [Employee]?
|
||||||
let context: ViewRoute.SelectContext
|
let context: ViewRoute.SelectContext
|
||||||
@@ -40,7 +40,7 @@ struct EmployeeSelect: HTML {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct VendorBranchSelect: HTML {
|
struct VendorBranchSelect: HTML, Sendable {
|
||||||
let branches: [VendorBranch.Detail]?
|
let branches: [VendorBranch.Detail]?
|
||||||
let context: ViewRoute.SelectContext
|
let context: ViewRoute.SelectContext
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import Elementary
|
|||||||
import ElementaryHTMX
|
import ElementaryHTMX
|
||||||
import SharedModels
|
import SharedModels
|
||||||
|
|
||||||
struct VendorBranchForm: HTML {
|
struct VendorBranchForm: HTML, Sendable {
|
||||||
let vendorID: Vendor.ID
|
let vendorID: Vendor.ID
|
||||||
|
|
||||||
var content: some HTML {
|
var content: some HTML {
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import Elementary
|
|||||||
import ElementaryHTMX
|
import ElementaryHTMX
|
||||||
import SharedModels
|
import SharedModels
|
||||||
|
|
||||||
struct VendorBranchList: HTML {
|
struct VendorBranchList: HTML, Sendable {
|
||||||
let vendorID: Vendor.ID
|
let vendorID: Vendor.ID
|
||||||
let branches: [VendorBranch]?
|
let branches: [VendorBranch]?
|
||||||
|
|
||||||
@@ -25,7 +25,7 @@ struct VendorBranchList: HTML {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Row: HTML {
|
struct Row: HTML, Sendable {
|
||||||
let branch: VendorBranch
|
let branch: VendorBranch
|
||||||
|
|
||||||
var content: some HTML<HTMLTag.li> {
|
var content: some HTML<HTMLTag.li> {
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import Elementary
|
|||||||
import ElementaryHTMX
|
import ElementaryHTMX
|
||||||
import SharedModels
|
import SharedModels
|
||||||
|
|
||||||
struct VendorDetail: HTML {
|
struct VendorDetail: HTML, Sendable {
|
||||||
|
|
||||||
let vendor: Vendor
|
let vendor: Vendor
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import Elementary
|
|||||||
import ElementaryHTMX
|
import ElementaryHTMX
|
||||||
import SharedModels
|
import SharedModels
|
||||||
|
|
||||||
struct VendorForm: HTML {
|
struct VendorForm: HTML, Sendable {
|
||||||
|
|
||||||
let context: Context
|
let context: Context
|
||||||
var vendor: Vendor? { context.vendor }
|
var vendor: Vendor? { context.vendor }
|
||||||
@@ -15,7 +15,7 @@ struct VendorForm: HTML {
|
|||||||
|
|
||||||
init() { self.init(.float(nil)) }
|
init() { self.init(.float(nil)) }
|
||||||
|
|
||||||
enum Context {
|
enum Context: Sendable {
|
||||||
case float(Vendor? = nil, shouldShow: Bool = false)
|
case float(Vendor? = nil, shouldShow: Bool = false)
|
||||||
case formOnly(Vendor)
|
case formOnly(Vendor)
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import Elementary
|
|||||||
import ElementaryHTMX
|
import ElementaryHTMX
|
||||||
import SharedModels
|
import SharedModels
|
||||||
|
|
||||||
struct VendorTable: HTML {
|
struct VendorTable: HTML, Sendable {
|
||||||
let vendors: [Vendor]
|
let vendors: [Vendor]
|
||||||
|
|
||||||
var content: some HTML {
|
var content: some HTML {
|
||||||
@@ -30,7 +30,7 @@ struct VendorTable: HTML {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Row: HTML {
|
struct Row: HTML, Sendable {
|
||||||
let vendor: Vendor
|
let vendor: Vendor
|
||||||
|
|
||||||
var content: some HTML<HTMLTag.tr> {
|
var content: some HTML<HTMLTag.tr> {
|
||||||
|
|||||||
@@ -101,80 +101,86 @@ struct ViewControllerTests {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
func userViews() async throws {
|
func userViews() async throws {
|
||||||
try await withDependencies {
|
try await withSnapshotTesting(record: record) {
|
||||||
$0.dateFormatter = .liveValue
|
try await withDependencies {
|
||||||
$0.database.users = .mock
|
$0.dateFormatter = .liveValue
|
||||||
$0.viewController = .liveValue
|
$0.database.users = .mock
|
||||||
} operation: {
|
$0.viewController = .liveValue
|
||||||
@Dependency(\.database) var database
|
} operation: {
|
||||||
@Dependency(\.viewController) var viewController
|
@Dependency(\.database) var database
|
||||||
|
@Dependency(\.viewController) var viewController
|
||||||
|
|
||||||
var htmlString = try await viewController.render(.user(.index))
|
var htmlString = try await viewController.render(.user(.index))
|
||||||
assertSnapshot(of: htmlString, as: .html)
|
assertSnapshot(of: htmlString, as: .html)
|
||||||
|
|
||||||
htmlString = try await viewController.render(.user(.form))
|
htmlString = try await viewController.render(.user(.form))
|
||||||
assertSnapshot(of: htmlString, as: .html)
|
assertSnapshot(of: htmlString, as: .html)
|
||||||
|
|
||||||
htmlString = try await viewController.render(.user(.create(.mock)))
|
htmlString = try await viewController.render(.user(.create(.mock)))
|
||||||
assertSnapshot(of: htmlString, as: .html)
|
assertSnapshot(of: htmlString, as: .html)
|
||||||
|
|
||||||
htmlString = try await viewController.render(.user(.get(id: UUID(0))))
|
htmlString = try await viewController.render(.user(.get(id: UUID(0))))
|
||||||
assertSnapshot(of: htmlString, as: .html)
|
assertSnapshot(of: htmlString, as: .html)
|
||||||
|
|
||||||
htmlString = try await viewController.render(.user(.update(id: UUID(0), updates: .mock)))
|
htmlString = try await viewController.render(.user(.update(id: UUID(0), updates: .mock)))
|
||||||
assertSnapshot(of: htmlString, as: .html)
|
assertSnapshot(of: htmlString, as: .html)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
func vendorViews() async throws {
|
func vendorViews() async throws {
|
||||||
try await withDependencies {
|
try await withSnapshotTesting(record: record) {
|
||||||
$0.dateFormatter = .liveValue
|
try await withDependencies {
|
||||||
$0.database.vendors = .mock
|
$0.dateFormatter = .liveValue
|
||||||
$0.viewController = .liveValue
|
$0.database.vendors = .mock
|
||||||
} operation: {
|
$0.viewController = .liveValue
|
||||||
@Dependency(\.database) var database
|
} operation: {
|
||||||
@Dependency(\.viewController) var viewController
|
@Dependency(\.database) var database
|
||||||
|
@Dependency(\.viewController) var viewController
|
||||||
|
|
||||||
var htmlString = try await viewController.render(.vendor(.index))
|
var htmlString = try await viewController.render(.vendor(.index))
|
||||||
assertSnapshot(of: htmlString, as: .html)
|
assertSnapshot(of: htmlString, as: .html)
|
||||||
|
|
||||||
htmlString = try await viewController.render(.vendor(.form))
|
htmlString = try await viewController.render(.vendor(.form))
|
||||||
assertSnapshot(of: htmlString, as: .html)
|
assertSnapshot(of: htmlString, as: .html)
|
||||||
|
|
||||||
htmlString = try await viewController.render(.vendor(.create(.mock)))
|
htmlString = try await viewController.render(.vendor(.create(.mock)))
|
||||||
assertSnapshot(of: htmlString, as: .html)
|
assertSnapshot(of: htmlString, as: .html)
|
||||||
|
|
||||||
htmlString = try await viewController.render(.vendor(.get(id: UUID(0))))
|
htmlString = try await viewController.render(.vendor(.get(id: UUID(0))))
|
||||||
assertSnapshot(of: htmlString, as: .html)
|
assertSnapshot(of: htmlString, as: .html)
|
||||||
|
|
||||||
htmlString = try await viewController.render(.vendor(.update(id: UUID(0), updates: .mock)))
|
htmlString = try await viewController.render(.vendor(.update(id: UUID(0), updates: .mock)))
|
||||||
assertSnapshot(of: htmlString, as: .html)
|
assertSnapshot(of: htmlString, as: .html)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
func vendorBranchViews() async throws {
|
func vendorBranchViews() async throws {
|
||||||
try await withDependencies {
|
try await withSnapshotTesting(record: record) {
|
||||||
$0.dateFormatter = .liveValue
|
try await withDependencies {
|
||||||
$0.database.vendors = .mock
|
$0.dateFormatter = .liveValue
|
||||||
$0.database.vendorBranches = .mock
|
$0.database.vendors = .mock
|
||||||
$0.viewController = .liveValue
|
$0.database.vendorBranches = .mock
|
||||||
} operation: {
|
$0.viewController = .liveValue
|
||||||
@Dependency(\.database) var database
|
} operation: {
|
||||||
@Dependency(\.viewController) var viewController
|
@Dependency(\.database) var database
|
||||||
|
@Dependency(\.viewController) var viewController
|
||||||
|
|
||||||
var htmlString = try await viewController.render(.vendorBranch(.index(for: UUID(0))))
|
var htmlString = try await viewController.render(.vendorBranch(.index(for: UUID(0))))
|
||||||
assertSnapshot(of: htmlString, as: .html)
|
assertSnapshot(of: htmlString, as: .html)
|
||||||
|
|
||||||
htmlString = try await viewController.render(.vendorBranch(.select(context: .purchaseOrderSearch)))
|
htmlString = try await viewController.render(.vendorBranch(.select(context: .purchaseOrderSearch)))
|
||||||
assertSnapshot(of: htmlString, as: .html)
|
assertSnapshot(of: htmlString, as: .html)
|
||||||
|
|
||||||
htmlString = try await viewController.render(.vendorBranch(.select(context: .purchaseOrderForm)))
|
htmlString = try await viewController.render(.vendorBranch(.select(context: .purchaseOrderForm)))
|
||||||
assertSnapshot(of: htmlString, as: .html)
|
assertSnapshot(of: htmlString, as: .html)
|
||||||
|
|
||||||
htmlString = try await viewController.render(.vendorBranch(.create(.mock)))
|
htmlString = try await viewController.render(.vendorBranch(.create(.mock)))
|
||||||
assertSnapshot(of: htmlString, as: .html)
|
assertSnapshot(of: htmlString, as: .html)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -182,14 +188,12 @@ struct ViewControllerTests {
|
|||||||
extension ViewController {
|
extension ViewController {
|
||||||
|
|
||||||
func render(_ route: ViewRoute) async throws -> String {
|
func render(_ route: ViewRoute) async throws -> String {
|
||||||
guard let html = try await view(
|
let html = try await view(
|
||||||
for: route,
|
for: route,
|
||||||
isHtmxRequest: true,
|
isHtmxRequest: true,
|
||||||
logger: .init(label: "tests"),
|
logger: .init(label: "tests"),
|
||||||
authenticate: { _ in }
|
authenticate: { _ in }
|
||||||
) else {
|
)
|
||||||
throw TestError()
|
|
||||||
}
|
|
||||||
return html.renderFormatted()
|
return html.renderFormatted()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="btn-row">
|
<div class="btn-row">
|
||||||
<button type="submit" class="btn-primary">Update</button>
|
<button type="submit" class="btn-primary">Update</button>
|
||||||
<button class="danger" hx-confirm="Are you sure you want to delete this employee?" hx-delete="/employees/00000000-0000-0000-0000-000000000000" hx-target="#employee-00000000-0000-0000-0000-000000000000" hx-swap="outerHTML transition:true swap:1s">Delete</button>
|
<button class="danger" hx-confirm="Are you sure you want to delete this employee?" hx-delete="/api/v1/employees/00000000-0000-0000-0000-000000000000" hx-target="#employee-00000000-0000-0000-0000-000000000000" hx-swap="outerHTML transition:true swap:1s">Delete</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
@@ -12,7 +12,7 @@
|
|||||||
<div class="row"><span class="label col-2">Created:</span><span class="date col-4"></span><span class="label col-2">Updated:</span><span class="date col-4"></span></div>
|
<div class="row"><span class="label col-2">Created:</span><span class="date col-4"></span><span class="label col-2">Updated:</span><span class="date col-4"></span></div>
|
||||||
<div class="btn-row user-buttons">
|
<div class="btn-row user-buttons">
|
||||||
<button type="submit" class="btn-secondary">Update</button>
|
<button type="submit" class="btn-secondary">Update</button>
|
||||||
<button class="danger" hx-delete="/users/00000000-0000-0000-0000-000000000000" hx-trigger="click" hx-swap="outerHTML" hx-target="#user-00000000-0000-0000-0000-000000000000" hx-confirm="Are you sure you want to delete this user?" hx-on::after-request="toggleContent('float'); window.location.href='/users';">Delete</button>
|
<button class="danger" hx-delete="/api/v1/users/00000000-0000-0000-0000-000000000000" hx-trigger="click" hx-swap="outerHTML" hx-target="#user-00000000-0000-0000-0000-000000000000" hx-confirm="Are you sure you want to delete this user?" hx-on::after-request="toggleContent('float'); window.location.href='/users';">Delete</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
<ul id="branch-list">
|
<ul id="branch-list">
|
||||||
<li id="branch-00000000-0000-0000-0000-000000000001" class="branch-row">
|
<li id="branch-00000000-0000-0000-0000-000000000001" class="branch-row">
|
||||||
<span class="label">Mock</span>
|
<span class="label">Mock</span>
|
||||||
<button class="btn" hx-delete="/vendors/branches/00000000-0000-0000-0000-000000000001" hx-target="#branch-00000000-0000-0000-0000-000000000001" hx-swap="outerHTML transition:true swap:0.5s">
|
<button class="btn" hx-delete="/api/v1/vendors/branches/00000000-0000-0000-0000-000000000001" hx-target="#branch-00000000-0000-0000-0000-000000000001" hx-swap="outerHTML transition:true swap:0.5s">
|
||||||
<img src="/images/trash-can.svg" width="30" height="30" style="margin-top: 5px;">
|
<img src="/images/trash-can.svg" width="30" height="30" style="margin-top: 5px;">
|
||||||
</button>
|
</button>
|
||||||
</li>
|
</li>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<li id="branch-00000000-0000-0000-0000-000000000001" class="branch-row">
|
<li id="branch-00000000-0000-0000-0000-000000000001" class="branch-row">
|
||||||
<span class="label">Mock</span>
|
<span class="label">Mock</span>
|
||||||
<button class="btn" hx-delete="/vendors/branches/00000000-0000-0000-0000-000000000001" hx-target="#branch-00000000-0000-0000-0000-000000000001" hx-swap="outerHTML transition:true swap:0.5s">
|
<button class="btn" hx-delete="/api/v1/vendors/branches/00000000-0000-0000-0000-000000000001" hx-target="#branch-00000000-0000-0000-0000-000000000001" hx-swap="outerHTML transition:true swap:0.5s">
|
||||||
<img src="/images/trash-can.svg" width="30" height="30" style="margin-top: 5px;">
|
<img src="/images/trash-can.svg" width="30" height="30" style="margin-top: 5px;">
|
||||||
</button>
|
</button>
|
||||||
</li>
|
</li>
|
||||||
@@ -6,7 +6,7 @@
|
|||||||
<form id="vendor-form" hx-put="/vendors/00000000-0000-0000-0000-000000000000" hx-target="#content" hx-swap="outerHTML">
|
<form id="vendor-form" hx-put="/vendors/00000000-0000-0000-0000-000000000000" hx-target="#content" hx-swap="outerHTML">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<input type="text" class="col-9" id="vendor-name" name="name" value="Test" placeholder="Vendor Name" hx-put="/vendors/00000000-0000-0000-0000-000000000000" hx-trigger="keyup changed delay:500ms" required>
|
<input type="text" class="col-9" id="vendor-name" name="name" value="Test" placeholder="Vendor Name" hx-put="/vendors/00000000-0000-0000-0000-000000000000" hx-trigger="keyup changed delay:500ms" required>
|
||||||
<button class="danger" style="font-size: 1.25em; padding: 10px 20px; border-radius: 10px;" hx-delete="/vendors/00000000-0000-0000-0000-000000000000" hx-confirm="Are you sure you want to delete this vendor?" hx-target="#vendor-00000000-0000-0000-0000-000000000000" hx-swap="outerHTML transition:true swap:1s" hx-on::after-request="if(event.detail.successful) toggleContent('float'); window.location.href='/vendors';">Delete</button>
|
<button class="danger" style="font-size: 1.25em; padding: 10px 20px; border-radius: 10px;" hx-delete="/api/v1/vendors/00000000-0000-0000-0000-000000000000" hx-confirm="Are you sure you want to delete this vendor?" hx-target="#vendor-00000000-0000-0000-0000-000000000000" hx-swap="outerHTML transition:true swap:1s" hx-on::after-request="if(event.detail.successful) toggleContent('float'); window.location.href='/vendors';">Delete</button>
|
||||||
<button type="submit" class="btn-primary" style="float: right">Update</button>
|
<button type="submit" class="btn-primary" style="float: right">Update</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
<form id="vendor-form" hx-put="/vendors/00000000-0000-0000-0000-000000000000" hx-target="#content" hx-swap="outerHTML">
|
<form id="vendor-form" hx-put="/vendors/00000000-0000-0000-0000-000000000000" hx-target="#content" hx-swap="outerHTML">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<input type="text" class="col-9" id="vendor-name" name="name" value="Test" placeholder="Vendor Name" hx-put="/vendors/00000000-0000-0000-0000-000000000000" hx-trigger="keyup changed delay:500ms" required>
|
<input type="text" class="col-9" id="vendor-name" name="name" value="Test" placeholder="Vendor Name" hx-put="/vendors/00000000-0000-0000-0000-000000000000" hx-trigger="keyup changed delay:500ms" required>
|
||||||
<button class="danger" style="font-size: 1.25em; padding: 10px 20px; border-radius: 10px;" hx-delete="/vendors/00000000-0000-0000-0000-000000000000" hx-confirm="Are you sure you want to delete this vendor?" hx-target="#vendor-00000000-0000-0000-0000-000000000000" hx-swap="outerHTML transition:true swap:1s" hx-on::after-request="if(event.detail.successful) toggleContent('float'); window.location.href='/vendors';">Delete</button>
|
<button class="danger" style="font-size: 1.25em; padding: 10px 20px; border-radius: 10px;" hx-delete="/api/v1/vendors/00000000-0000-0000-0000-000000000000" hx-confirm="Are you sure you want to delete this vendor?" hx-target="#vendor-00000000-0000-0000-0000-000000000000" hx-swap="outerHTML transition:true swap:1s" hx-on::after-request="if(event.detail.successful) toggleContent('float'); window.location.href='/vendors';">Delete</button>
|
||||||
<button type="submit" class="btn-primary" style="float: right">Update</button>
|
<button type="submit" class="btn-primary" style="float: right">Update</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
<form id="vendor-form" hx-put="/vendors/00000000-0000-0000-0000-000000000000" hx-target="#content" hx-swap="outerHTML">
|
<form id="vendor-form" hx-put="/vendors/00000000-0000-0000-0000-000000000000" hx-target="#content" hx-swap="outerHTML">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<input type="text" class="col-9" id="vendor-name" name="name" value="Test" placeholder="Vendor Name" hx-put="/vendors/00000000-0000-0000-0000-000000000000" hx-trigger="keyup changed delay:500ms" required>
|
<input type="text" class="col-9" id="vendor-name" name="name" value="Test" placeholder="Vendor Name" hx-put="/vendors/00000000-0000-0000-0000-000000000000" hx-trigger="keyup changed delay:500ms" required>
|
||||||
<button class="danger" style="font-size: 1.25em; padding: 10px 20px; border-radius: 10px;" hx-delete="/vendors/00000000-0000-0000-0000-000000000000" hx-confirm="Are you sure you want to delete this vendor?" hx-target="#vendor-00000000-0000-0000-0000-000000000000" hx-swap="outerHTML transition:true swap:1s" hx-on::after-request="if(event.detail.successful) toggleContent('float'); window.location.href='/vendors';">Delete</button>
|
<button class="danger" style="font-size: 1.25em; padding: 10px 20px; border-radius: 10px;" hx-delete="/api/v1/vendors/00000000-0000-0000-0000-000000000000" hx-confirm="Are you sure you want to delete this vendor?" hx-target="#vendor-00000000-0000-0000-0000-000000000000" hx-swap="outerHTML transition:true swap:1s" hx-on::after-request="if(event.detail.successful) toggleContent('float'); window.location.href='/vendors';">Delete</button>
|
||||||
<button type="submit" class="btn-primary" style="float: right">Update</button>
|
<button type="submit" class="btn-primary" style="float: right">Update</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
@@ -21,18 +21,18 @@ struct EmployeeViewRouteTests {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
// @Test
|
||||||
func employeeDelete() throws {
|
// func employeeDelete() throws {
|
||||||
let id = UUID(0)
|
// let id = UUID(0)
|
||||||
var request = URLRequestData(
|
// var request = URLRequestData(
|
||||||
method: "DELETE",
|
// method: "DELETE",
|
||||||
path: "/employees/\(id)"
|
// path: "/employees/\(id)"
|
||||||
)
|
// )
|
||||||
let route = try router.parse(&request)
|
// let route = try router.parse(&request)
|
||||||
#expect(
|
// #expect(
|
||||||
route == .employee(.delete(id: id))
|
// route == .employee(.delete(id: id))
|
||||||
)
|
// )
|
||||||
}
|
// }
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
func employeeForm() throws {
|
func employeeForm() throws {
|
||||||
|
|||||||
@@ -29,16 +29,16 @@ struct PurchaseOrderViewRouteTests {
|
|||||||
))))
|
))))
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
// @Test
|
||||||
func delete() throws {
|
// func delete() throws {
|
||||||
let id = 1
|
// let id = 1
|
||||||
var request = URLRequestData(
|
// var request = URLRequestData(
|
||||||
method: "DELETE",
|
// method: "DELETE",
|
||||||
path: "/purchase-orders/\(id)"
|
// path: "/purchase-orders/\(id)"
|
||||||
)
|
// )
|
||||||
let route = try router.parse(&request)
|
// let route = try router.parse(&request)
|
||||||
#expect(route == .purchaseOrder(.delete(id: id)))
|
// #expect(route == .purchaseOrder(.delete(id: id)))
|
||||||
}
|
// }
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
func form() throws {
|
func form() throws {
|
||||||
|
|||||||
@@ -25,16 +25,16 @@ struct UserViewRouteTests {
|
|||||||
))))
|
))))
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
// @Test
|
||||||
func delete() throws {
|
// func delete() throws {
|
||||||
let id = UUID(0)
|
// let id = UUID(0)
|
||||||
var request = URLRequestData(
|
// var request = URLRequestData(
|
||||||
method: "DELETE",
|
// method: "DELETE",
|
||||||
path: "/users/\(id)"
|
// path: "/users/\(id)"
|
||||||
)
|
// )
|
||||||
let route = try router.parse(&request)
|
// let route = try router.parse(&request)
|
||||||
#expect(route == .user(.delete(id: id)))
|
// #expect(route == .user(.delete(id: id)))
|
||||||
}
|
// }
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
func form() throws {
|
func form() throws {
|
||||||
|
|||||||
@@ -20,16 +20,16 @@ struct VendorBranchViewRouteTests {
|
|||||||
#expect(route == .vendorBranch(.create(.init(name: "Test", vendorID: id))))
|
#expect(route == .vendorBranch(.create(.init(name: "Test", vendorID: id))))
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
// @Test
|
||||||
func delete() throws {
|
// func delete() throws {
|
||||||
let id = UUID(0)
|
// let id = UUID(0)
|
||||||
var request = URLRequestData(
|
// var request = URLRequestData(
|
||||||
method: "DELETE",
|
// method: "DELETE",
|
||||||
path: "/vendors/branches/\(id)"
|
// path: "/vendors/branches/\(id)"
|
||||||
)
|
// )
|
||||||
let route = try router.parse(&request)
|
// let route = try router.parse(&request)
|
||||||
#expect(route == .vendorBranch(.delete(id: id)))
|
// #expect(route == .vendorBranch(.delete(id: id)))
|
||||||
}
|
// }
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
func index() throws {
|
func index() throws {
|
||||||
|
|||||||
@@ -19,16 +19,16 @@ struct VendorViewRouteTests {
|
|||||||
#expect(route == .vendor(.create(.init(name: "Test"))))
|
#expect(route == .vendor(.create(.init(name: "Test"))))
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
// @Test
|
||||||
func delete() throws {
|
// func delete() throws {
|
||||||
let id = UUID(0)
|
// let id = UUID(0)
|
||||||
var request = URLRequestData(
|
// var request = URLRequestData(
|
||||||
method: "DELETE",
|
// method: "DELETE",
|
||||||
path: "/vendors/\(id)"
|
// path: "/vendors/\(id)"
|
||||||
)
|
// )
|
||||||
let route = try router.parse(&request)
|
// let route = try router.parse(&request)
|
||||||
#expect(route == .vendor(.delete(id: id)))
|
// #expect(route == .vendor(.delete(id: id)))
|
||||||
}
|
// }
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
func get() throws {
|
func get() throws {
|
||||||
|
|||||||
Reference in New Issue
Block a user