Skip to content

Commit 1ee7cb9

Browse files
committed
Validation, & lifecycle methods
1 parent 5bd4baf commit 1ee7cb9

24 files changed

+341
-16
lines changed

Chapter 13/myProject/Sources/App/Framework/Controllers/CreateController.swift

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,22 @@
88
import Vapor
99

1010
protocol CreateController: ModelController {
11+
func beforeCreate(_ req: Request, _ model: DatabaseModel) async throws
12+
func afterCreate(_ req: Request, _ model: DatabaseModel) async throws
1113

14+
func create(_ req: Request, _ model: DatabaseModel) async throws
1215
}
1316

1417
extension CreateController {
1518

19+
func beforeCreate(_ req: Request, _ model: DatabaseModel) async throws {}
20+
21+
func afterCreate(_ req: Request, _ model: DatabaseModel) async throws {}
22+
23+
func create(_ req: Request, _ model: DatabaseModel) async throws {
24+
try await beforeCreate(req, model)
25+
try await model.create(on: req.db)
26+
try await afterCreate(req, model)
27+
}
1628
}
1729

Chapter 13/myProject/Sources/App/Framework/Controllers/DeleteController.swift

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,19 @@
88
import Vapor
99

1010
protocol DeleteController: ModelController {
11-
11+
func beforeDelete(_ req: Request, _ model: DatabaseModel) async throws
12+
func afterDelete(_ req: Request, _ model: DatabaseModel) async throws
13+
func delete(_ req: Request, _ model: DatabaseModel) async throws
1214
}
1315

1416
extension DeleteController {
1517

18+
func beforeDelete(_ req: Request, _ model: DatabaseModel) async throws {}
19+
func afterDelete(_ req: Request, _ model: DatabaseModel) async throws {}
20+
21+
func delete(_ req: Request, _ model: DatabaseModel) async throws {
22+
try await beforeDelete(req, model)
23+
try await model.delete(on: req.db)
24+
try await afterDelete(req, model)
25+
}
1626
}

Chapter 13/myProject/Sources/App/Framework/Controllers/DetailController.swift

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,32 @@
66
//
77

88
import Vapor
9+
import Fluent
910

1011
protocol DetailController: ModelController {
11-
12+
13+
func beforeDetail(_ req: Request, _ queryBuilder: QueryBuilder<DatabaseModel>) async throws -> QueryBuilder<DatabaseModel>
14+
func afterDetail(_ req: Request, _ model: DatabaseModel) async throws -> DatabaseModel
15+
func detail(_ req: Request) async throws -> DatabaseModel
1216
}
1317

1418
extension DetailController {
1519

20+
func beforeDetail(_ req: Request, _ queryBuilder: QueryBuilder<DatabaseModel>) async throws -> QueryBuilder<DatabaseModel> {
21+
queryBuilder
22+
}
23+
24+
func afterDetail(_ req: Request, _ model: DatabaseModel) async throws -> DatabaseModel {
25+
model
26+
}
27+
28+
func detail(_ req: Request) async throws -> DatabaseModel {
29+
let queryBuilder = DatabaseModel.query(on: req.db)
30+
let model = try await beforeDetail(req, queryBuilder).filter(\._$id == identifier(req)).first()
31+
guard let model = model else {
32+
throw Abort(.notFound)
33+
}
34+
return try await afterDetail(req, model)
35+
}
36+
1637
}

Chapter 13/myProject/Sources/App/Framework/Controllers/ListController.swift

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,28 @@
66
//
77

88
import Vapor
9+
import Fluent
910

1011
protocol ListController: ModelController {
1112

1213
func list(_ req: Request) async throws -> [DatabaseModel]
14+
func beforeList(_ req: Request, _ queryBuilder: QueryBuilder<DatabaseModel>) async throws -> QueryBuilder<DatabaseModel>
15+
func afterList(_ req: Request, _ models: [DatabaseModel]) async throws -> [DatabaseModel]
1316
}
1417

1518
extension ListController {
19+
20+
func beforeList(_ req: Request, _ queryBuilder: QueryBuilder<DatabaseModel>) async throws -> QueryBuilder<DatabaseModel> {
21+
queryBuilder
22+
}
23+
24+
func afterList(_ req: Request, _ models: [DatabaseModel]) async throws -> [DatabaseModel] {
25+
models
26+
}
1627

1728
func list(_ req: Request) async throws -> [DatabaseModel] {
18-
try await DatabaseModel.query(on: req.db).all()
29+
let queryBuilder = DatabaseModel.query(on: req.db)
30+
let list = try await beforeList(req, queryBuilder).all()
31+
return try await afterList(req, list)
1932
}
2033
}

Chapter 13/myProject/Sources/App/Framework/Controllers/PatchController.swift

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,18 @@
88
import Vapor
99

1010
protocol PatchController: ModelController {
11-
11+
func beforePatch(_ req: Request, _ model: DatabaseModel) async throws
12+
func afterPatch(_ req: Request, _ model: DatabaseModel) async throws
13+
func patch(_ req: Request, _ model: DatabaseModel) async throws
1214
}
1315

1416
extension PatchController {
15-
17+
func beforePatch(_ req: Request, _ model: DatabaseModel) async throws {}
18+
func afterPatch(_ req: Request, _ model: DatabaseModel) async throws {}
19+
20+
func patch(_ req: Request, _ model: DatabaseModel) async throws {
21+
try await beforePatch(req, model)
22+
try await model.update(on: req.db)
23+
try await afterPatch(req, model)
24+
}
1625
}

Chapter 13/myProject/Sources/App/Framework/Controllers/UpdateController.swift

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,20 @@
88
import Vapor
99

1010
protocol UpdateController: ModelController {
11-
11+
func beforeUpdate(_ req: Request, _ model: DatabaseModel) async throws
12+
func afterUpdate(_ req: Request, _ model: DatabaseModel) async throws
13+
func update(_ req: Request, _ model: DatabaseModel) async throws
1214
}
1315

1416
extension UpdateController {
1517

18+
func beforeUpdate(_ req: Request, _ model: DatabaseModel) async throws {}
19+
20+
func afterUpdate(_ req: Request, _ model: DatabaseModel) async throws {}
21+
22+
func update(_ req: Request, _ model: DatabaseModel) async throws {
23+
try await beforeUpdate(req, model)
24+
try await model.update(on: req.db)
25+
try await afterUpdate(req, model)
26+
}
1627
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
//
2+
// File.swift
3+
//
4+
//
5+
// Created by Tibor Bodecs on 2022. 01. 07..
6+
//
7+
8+
import Vapor
9+
10+
public extension KeyedContentValidator where T == String {
11+
12+
static func required(_ key: String, _ message: String? = nil, optional: Bool = false) -> KeyedContentValidator<T> {
13+
.init(key, message ?? "\(key.capitalized) is required", optional: optional) { value, _ in !value.isEmpty }
14+
}
15+
16+
static func min(_ key: String, _ length: Int, _ message: String? = nil, optional: Bool = false) -> KeyedContentValidator<T> {
17+
.init(key, message ?? "\(key.capitalized) is too short (min: \(length) characters)", optional: optional) { value, _ in value.count >= length }
18+
}
19+
20+
static func max(_ key: String, _ length: Int, _ message: String? = nil, optional: Bool = false) -> KeyedContentValidator<T> {
21+
.init(key, message ?? "\(key.capitalized) is too long (max: \(length) characters)", optional: optional) { value, _ in value.count <= length }
22+
}
23+
24+
static func alphanumeric(_ key: String, _ message: String? = nil, _ optional: Bool = false) -> KeyedContentValidator<T> {
25+
.init(key, message ?? "\(key.capitalized) should be only alphanumeric characters", optional: optional) { value, _ in
26+
!Validator.characterSet(.alphanumerics).validate(value).isFailure
27+
}
28+
}
29+
30+
static func email(_ key: String, _ message: String? = nil, _ optional: Bool = false) -> KeyedContentValidator<T> {
31+
.init(key, message ?? "\(key.capitalized) should be a valid email address", optional: optional) { value, _ in
32+
!Validator.email.validate(value).isFailure
33+
}
34+
}
35+
}
36+
37+
public extension KeyedContentValidator where T == Int {
38+
39+
static func min(_ key: String, _ length: Int, _ message: String? = nil, optional: Bool = false) -> KeyedContentValidator<T> {
40+
.init(key, message ?? "\(key.capitalized) is too short (min: \(length) characters)", optional: optional) { value, _ in value >= length }
41+
}
42+
43+
static func max(_ key: String, _ length: Int, _ message: String? = nil, optional: Bool = false) -> KeyedContentValidator<T> {
44+
.init(key, message ?? "\(key.capitalized) is too long (max: \(length) characters)", optional: optional) { value, _ in value <= length }
45+
}
46+
47+
static func contains(_ key: String, _ values: [Int], _ message: String? = nil, optional: Bool = false) -> KeyedContentValidator<T> {
48+
.init(key, message ?? "\(key.capitalized) is an invalid value", optional: optional) { value, _ in values.contains(value) }
49+
}
50+
}
51+
52+
public extension KeyedContentValidator where T == UUID {
53+
54+
static func required(_ key: String, _ message: String? = nil, optional: Bool = false) -> KeyedContentValidator<T> {
55+
.init(key, message ?? "\(key.capitalized) is required", optional: optional) { _, _ in true }
56+
}
57+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
//
2+
// File.swift
3+
//
4+
//
5+
// Created by Tibor Bodecs on 2022. 01. 07..
6+
//
7+
8+
import Vapor
9+
10+
public struct KeyedContentValidator<T: Codable>: AsyncValidator {
11+
12+
public let key: String
13+
public let message: String
14+
public let optional: Bool
15+
public let validation: (T, Request) async throws -> Bool
16+
17+
public init(_ key: String,
18+
_ message: String,
19+
optional: Bool = false,
20+
_ validation: @escaping (T, Request) async throws -> Bool) {
21+
self.key = key
22+
self.message = message
23+
self.optional = optional
24+
self.validation = validation
25+
}
26+
27+
public func validate(_ req: Request) async throws -> ValidationErrorDetail? {
28+
let optionalValue = try? req.content.get(T.self, at: key)
29+
if let value = optionalValue {
30+
return try await validation(value, req) ? nil : error
31+
}
32+
return optional ? nil : error
33+
}
34+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
//
2+
// File.swift
3+
//
4+
//
5+
// Created by Tibor Bodecs on 2022. 01. 07..
6+
//
7+
8+
import Vapor
9+
10+
struct ValidationError: Codable {
11+
12+
let message: String?
13+
let details: [ValidationErrorDetail]
14+
15+
init(message: String?, details: [ValidationErrorDetail]) {
16+
self.message = message
17+
self.details = details
18+
}
19+
}
20+
21+
extension ValidationError: Content {}

Chapter 13/myProject/Sources/App/Modules/Admin/Controllers/AdminCreateController.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ extension AdminCreateController {
4242
return render(req, editor: editor)
4343
}
4444
try await editor.write(req: req)
45-
try await editor.model.create(on: req.db)
45+
try await create(req, editor.model as! DatabaseModel)
4646
try await editor.save(req: req)
4747
var components = req.url.path.pathComponents.dropLast()
4848
components += editor.model.id!.uuidString.pathComponents

0 commit comments

Comments
 (0)