Skip to content

Commit 5ddc382

Browse files
committed
refactor: make validations throwable
This is to allow us to throw errors in future from these code paths
1 parent 7479605 commit 5ddc382

File tree

7 files changed

+64
-49
lines changed

7 files changed

+64
-49
lines changed

CHANGELOG.md

+5
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,11 @@
22

33
## Master
44

5+
### Breaking Changes
6+
7+
- The validation API may now throw errors if the provided JSON Schema is
8+
invalid.
9+
510
### Enhancements
611

712
- The failing required validation error message is now emitted for each

README.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ pod 'JSONSchema'
1818
```swift
1919
import JSONSchema
2020

21-
JSONSchema.validate(["name": "Eggs", "price": 34.99], schema: [
21+
try JSONSchema.validate(["name": "Eggs", "price": 34.99], schema: [
2222
"type": "object",
2323
"properties": [
2424
"name": ["type": "string"],
@@ -34,7 +34,7 @@ Validate returns an enumeration `ValidationResult` which contains all
3434
validation errors.
3535

3636
```python
37-
print(validate(["price": 34.99], schema: ["required": ["name"]]).errors)
37+
print(try validate(["price": 34.99], schema: ["required": ["name"]]).errors)
3838
>>> "Required property 'name' is missing."
3939
```
4040

Sources/Core/ref.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ func ref(context: Context, reference: Any, instance: Any, schema: [String: Any])
55

66
guard let document = context.resolve(ref: reference) else {
77
return AnySequence([
8-
ValidationError("Reference not found '\(reference)'"),
8+
ValidationError("Reference not found '\(reference)'", instanceLocation: nil),
99
])
1010
}
1111

Sources/JSONSchema.swift

+8-8
Original file line numberDiff line numberDiff line change
@@ -51,14 +51,14 @@ public struct Schema {
5151
self.schema = schema
5252
}
5353

54-
public func validate(_ data: Any) -> ValidationResult {
54+
public func validate(_ data: Any) throws -> ValidationResult {
5555
let validator = JSONSchema.validator(for: schema)
56-
return validator.validate(instance: data)
56+
return try validator.validate(instance: data)
5757
}
5858

59-
public func validate(_ data: Any) -> AnySequence<ValidationError> {
59+
public func validate(_ data: Any) throws -> AnySequence<ValidationError> {
6060
let validator = JSONSchema.validator(for: schema)
61-
return validator.validate(instance: data)
61+
return try validator.validate(instance: data)
6262
}
6363
}
6464

@@ -84,12 +84,12 @@ func validator(for schema: [String: Any]) -> Validator {
8484
}
8585

8686

87-
public func validate(_ value: Any, schema: [String: Any]) -> ValidationResult {
88-
return validator(for: schema).validate(instance: value)
87+
public func validate(_ value: Any, schema: [String: Any]) throws -> ValidationResult {
88+
return try validator(for: schema).validate(instance: value)
8989
}
9090

9191

92-
public func validate(_ value: Any, schema: Bool) -> ValidationResult {
92+
public func validate(_ value: Any, schema: Bool) throws -> ValidationResult {
9393
let validator = Draft4Validator(schema: schema)
94-
return validator.validate(instance: value)
94+
return try validator.validate(instance: value)
9595
}

Sources/Validator.swift

+2-2
Original file line numberDiff line numberDiff line change
@@ -64,12 +64,12 @@ protocol Validator {
6464
}
6565

6666
extension Validator {
67-
public func validate(instance: Any) -> ValidationResult {
67+
public func validate(instance: Any) throws -> ValidationResult {
6868
let context = Context(resolver: resolver, validator: self)
6969
return context.validate(instance: instance, schema: schema).validationResult()
7070
}
7171

72-
public func validate(instance: Any) -> AnySequence<ValidationError> {
72+
public func validate(instance: Any) throws -> AnySequence<ValidationError> {
7373
let context = Context(resolver: resolver, validator: self)
7474
return context.validate(instance: instance, schema: schema)
7575
}

Tests/JSONSchemaTests/JSONSchemaCases.swift

+27-17
Original file line numberDiff line numberDiff line change
@@ -11,52 +11,52 @@ func JSONFixture(_ path: Path) throws -> [[String: Any]] {
1111
}
1212

1313

14-
func draft4Validator(schema: Any, instance: Any) -> ValidationResult {
14+
func draft4Validator(schema: Any, instance: Any) throws -> ValidationResult {
1515
if let schema = schema as? Bool {
16-
return Draft4Validator(schema: schema).validate(instance: instance)
16+
return try Draft4Validator(schema: schema).validate(instance: instance)
1717
}
1818

1919
if let schema = schema as? [String: Any] {
20-
return validate(instance, schema: schema)
20+
return try validate(instance, schema: schema)
2121
}
2222

2323
fatalError()
2424
}
2525

2626

27-
func draft6Validator(schema: Any, instance: Any) -> ValidationResult {
27+
func draft6Validator(schema: Any, instance: Any) throws -> ValidationResult {
2828
if let schema = schema as? Bool {
29-
return Draft6Validator(schema: schema).validate(instance: instance)
29+
return try Draft6Validator(schema: schema).validate(instance: instance)
3030
}
3131

3232
if let schema = schema as? [String: Any] {
33-
return Draft6Validator(schema: schema).validate(instance: instance)
33+
return try Draft6Validator(schema: schema).validate(instance: instance)
3434
}
3535

3636
fatalError()
3737
}
3838

3939

40-
func draft7Validator(schema: Any, instance: Any) -> ValidationResult {
40+
func draft7Validator(schema: Any, instance: Any) throws -> ValidationResult {
4141
if let schema = schema as? Bool {
42-
return Draft7Validator(schema: schema).validate(instance: instance)
42+
return try Draft7Validator(schema: schema).validate(instance: instance)
4343
}
4444

4545
if let schema = schema as? [String: Any] {
46-
return Draft7Validator(schema: schema).validate(instance: instance)
46+
return try Draft7Validator(schema: schema).validate(instance: instance)
4747
}
4848

4949
fatalError()
5050
}
5151

5252

53-
func draft201909Validator(schema: Any, instance: Any) -> ValidationResult {
53+
func draft201909Validator(schema: Any, instance: Any) throws -> ValidationResult {
5454
if let schema = schema as? Bool {
55-
return Draft201909Validator(schema: schema).validate(instance: instance)
55+
return try Draft201909Validator(schema: schema).validate(instance: instance)
5656
}
5757

5858
if let schema = schema as? [String: Any] {
59-
return Draft201909Validator(schema: schema).validate(instance: instance)
59+
return try Draft201909Validator(schema: schema).validate(instance: instance)
6060
}
6161

6262
fatalError()
@@ -174,7 +174,7 @@ class JSONSchemaCases: XCTestCase {
174174
] + additionalExclusions)
175175
}
176176

177-
func test(name: String, validator: @escaping ((_ schema: Any, _ instance: Any) -> (ValidationResult)), excluding: [String]) throws {
177+
func test(name: String, validator: @escaping ((_ schema: Any, _ instance: Any) throws -> (ValidationResult)), excluding: [String]) throws {
178178
let filePath = #file
179179
let path = Path(filePath) + ".." + ".." + "Cases" + "tests" + name
180180

@@ -203,7 +203,7 @@ class JSONSchemaCases: XCTestCase {
203203

204204
let flatCases = cases.reduce([Case](), +)
205205
for c in flatCases {
206-
for (name, assertion) in makeAssertions(c, validator) {
206+
for (_, assertion) in makeAssertions(c, validator) {
207207
// TODO: Improve testing
208208
assertion()
209209
}
@@ -244,16 +244,26 @@ func makeCase(_ filename: String) -> (_ object: [String: Any]) -> Case {
244244
typealias Assertion = (String, () -> ())
245245

246246

247-
func makeAssertions(_ c: Case, _ validator: @escaping ((_ schema: Any, _ instance: Any) -> (ValidationResult))) -> ([Assertion]) {
247+
func makeAssertions(_ c: Case, _ validator: @escaping ((_ schema: Any, _ instance: Any) throws -> (ValidationResult))) -> ([Assertion]) {
248248
return c.tests.map { test -> Assertion in
249249
let label = "\(c.description) \(test.description)"
250250
return (label, {
251251
let result: ValidationResult
252252

253253
if let schema = c.schema as? [String: Any] {
254-
result = validator(schema, test.data)
254+
do {
255+
result = try validator(schema, test.data)
256+
} catch {
257+
XCTFail(error.localizedDescription)
258+
return
259+
}
255260
} else if let schema = c.schema as? Bool {
256-
result = validator(schema, test.data)
261+
do {
262+
result = try validator(schema, test.data)
263+
} catch {
264+
XCTFail(error.localizedDescription)
265+
return
266+
}
257267
} else {
258268
fatalError()
259269
}

Tests/JSONSchemaTests/JSONSchemaTests.swift

+19-19
Original file line numberDiff line numberDiff line change
@@ -29,14 +29,14 @@ class JSONSchemaTests: XCTestCase {
2929
}
3030

3131
func testSuccessfulValidation() {
32-
XCTAssertTrue(schema.validate([String: Any]()).valid)
32+
try XCTAssertTrue(schema.validate([String: Any]()).valid)
3333
}
3434

3535
func testUnsuccessfulValidation() {
36-
XCTAssertFalse(schema.validate([String]()).valid)
36+
try XCTAssertFalse(schema.validate([String]()).valid)
3737
}
3838

39-
func testReadme() {
39+
func testReadme() throws {
4040
let schema = Schema([
4141
"type": "object",
4242
"properties": [
@@ -46,11 +46,11 @@ class JSONSchemaTests: XCTestCase {
4646
"required": ["name"],
4747
])
4848

49-
XCTAssertTrue(schema.validate(["name": "Eggs", "price": 34.99]).valid)
50-
XCTAssertFalse(schema.validate(["price": 34.99]).valid)
49+
try XCTAssertTrue(schema.validate(["name": "Eggs", "price": 34.99]).valid)
50+
try XCTAssertFalse(schema.validate(["price": 34.99]).valid)
5151
}
5252

53-
func testIterableInterface() {
53+
func testIterableInterface() throws {
5454
let schema = Schema([
5555
"type": "object",
5656
"properties": [
@@ -61,14 +61,14 @@ class JSONSchemaTests: XCTestCase {
6161
])
6262

6363
var counter = 0
64-
for error in schema.validate(["price": 34.99]) {
64+
for error in try schema.validate(["price": 34.99]) {
6565
XCTAssertEqual(error.description, "Required property 'name' is missing")
6666
counter += 1
6767
}
6868

6969
XCTAssertEqual(counter, 1)
7070

71-
let result = Array(schema.validate(["price": 34.99]))
71+
let result = try Array(schema.validate(["price": 34.99]))
7272

7373
XCTAssertEqual(result.map(\.description), [
7474
"Required property 'name' is missing"
@@ -89,8 +89,8 @@ class ValidateTests: XCTestCase {
8989
"required": ["name"],
9090
]
9191

92-
XCTAssertTrue(validate(["name": "Eggs", "price": 34.99], schema: schema).valid)
93-
XCTAssertFalse(validate(["price": 34.99], schema: schema).valid)
92+
try XCTAssertTrue(validate(["name": "Eggs", "price": 34.99], schema: schema).valid)
93+
try XCTAssertFalse(validate(["price": 34.99], schema: schema).valid)
9494
}
9595

9696
func testValidateDraft6() {
@@ -104,16 +104,16 @@ class ValidateTests: XCTestCase {
104104
"required": ["name"],
105105
]
106106

107-
XCTAssertTrue(validate(["name": "Eggs", "price": 34.99], schema: schema).valid)
108-
XCTAssertFalse(validate(["price": 34.99], schema: schema).valid)
107+
try XCTAssertTrue(validate(["name": "Eggs", "price": 34.99], schema: schema).valid)
108+
try XCTAssertFalse(validate(["price": 34.99], schema: schema).valid)
109109
}
110110

111111
func testDraft6ValidatorIsAvailable() {
112112
let result = validator(for: ["$schema": "http://json-schema.org/draft-06/schema#"])
113113
XCTAssertTrue(result is Draft6Validator, "Unexpected type of validator \(result)")
114114
}
115115

116-
func testValidateDraft7() {
116+
func testValidateDraft7() throws {
117117
let schema: [String: Any] = [
118118
"$schema": "http://json-schema.org/draft-07/schema#",
119119
"type": "object",
@@ -124,17 +124,17 @@ class ValidateTests: XCTestCase {
124124
"required": ["name"],
125125
]
126126

127-
XCTAssertTrue(validate(["name": "Eggs", "price": 34.99], schema: schema).valid)
128-
XCTAssertFalse(validate(["price": 34.99], schema: schema).valid)
127+
try XCTAssertTrue(validate(["name": "Eggs", "price": 34.99], schema: schema).valid)
128+
try XCTAssertFalse(validate(["price": 34.99], schema: schema).valid)
129129
}
130130

131-
func testValidatesRequired() {
131+
func testValidatesRequired() throws {
132132
let schema: [String: Any] = [
133133
"$schema": "http://json-schema.org/draft-07/schema#",
134134
"required": ["one", "two", "three"],
135135
]
136136

137-
let result = validate(["one": true, "three": true], schema: schema)
137+
let result = try validate(["one": true, "three": true], schema: schema)
138138

139139
switch result {
140140
case .valid:
@@ -146,15 +146,15 @@ class ValidateTests: XCTestCase {
146146
}
147147
}
148148

149-
func testRequiredValidationLocation() {
149+
func testRequiredValidationLocation() throws {
150150
let schema: [String: Any] = [
151151
"$schema": "http://json-schema.org/draft-07/schema#",
152152
"items": [
153153
"required": ["test"],
154154
]
155155
]
156156

157-
let result = validate([[:]], schema: schema)
157+
let result = try validate([[:]], schema: schema)
158158

159159
switch result {
160160
case .valid:

0 commit comments

Comments
 (0)