Skip to content

Commit 119a123

Browse files
committed
really get includes errors down to a concise message when we know that all types are wrong or exactly one type is correct.
1 parent 9521e2c commit 119a123

4 files changed

Lines changed: 34 additions & 63 deletions

File tree

Sources/JSONAPI/Document/Includes.swift

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -243,14 +243,33 @@ extension IncludesDecodingError: CustomStringConvertible {
243243
}
244244
let ordinalDescription = "\(idx + 1)\(ordinalSuffix)"
245245

246-
return "Out of \(totalIncludesCount) includes, the \(ordinalDescription) one failed to parse: \(error)"
246+
return "Out of the \(totalIncludesCount) includes in the document, the \(ordinalDescription) one failed to parse: \(error)"
247247
}
248248
}
249249

250250
public struct IncludeDecodingError: Swift.Error, Equatable, CustomStringConvertible {
251251
public let failures: [ResourceObjectDecodingError]
252252

253253
public var description: String {
254+
// concise error when all failures are mismatched JSON:API types:
255+
if case let .jsonTypeMismatch(foundType: foundType)? = failures.first?.cause,
256+
failures.allSatisfy({ $0.cause.isTypeMismatch }) {
257+
let expectedTypes = failures
258+
.compactMap { "'\($0.resourceObjectJsonAPIType)'" }
259+
.joined(separator: ", ")
260+
261+
return "Found JSON:API type '\(foundType)' but expected one of \(expectedTypes)"
262+
}
263+
264+
// concise error when all but failures but one are type mismatches because
265+
// we can assume the correct type was found but there was some other error:
266+
let nonTypeMismatches = failures.filter({ !$0.cause.isTypeMismatch})
267+
if nonTypeMismatches.count == 1, let nonTypeMismatch = nonTypeMismatches.first {
268+
return String(describing: nonTypeMismatch)
269+
}
270+
271+
// fall back to just describing all of the reasons it could not have been any of the available
272+
// types:
254273
return failures
255274
.enumerated()
256275
.map {

Sources/JSONAPI/Resource/Resource Object/ResourceObjectDecodingError.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,11 @@ public struct ResourceObjectDecodingError: Swift.Error, Equatable {
1919
case typeMismatch(expectedTypeName: String)
2020
case jsonTypeMismatch(foundType: String)
2121
case quantityMismatch(expected: JSONAPICodingError.Quantity)
22+
23+
internal var isTypeMismatch: Bool {
24+
guard case .jsonTypeMismatch = self else { return false}
25+
return true
26+
}
2227
}
2328

2429
public enum Location: String, Equatable {

Tests/JSONAPITests/Document/DocumentDecodingErrorTests.swift

Lines changed: 3 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ final class DocumentDecodingErrorTests: XCTestCase {
9292
return
9393
}
9494

95-
XCTAssertEqual(String(describing: error), #"Out of 3 includes, the 3rd one failed to parse: found JSON:API type "not_an_author" but expected "authors""#)
95+
XCTAssertEqual(String(describing: error), #"Out of the 3 includes in the document, the 3rd one failed to parse: found JSON:API type "not_an_author" but expected "authors""#)
9696
}
9797
}
9898

@@ -112,14 +112,7 @@ final class DocumentDecodingErrorTests: XCTestCase {
112112

113113
XCTAssertEqual(
114114
String(describing: error),
115-
#"""
116-
Out of 3 includes, the 3rd one failed to parse:
117-
Could not have been Include Type `articles` because:
118-
found JSON:API type "not_an_author" but expected "articles"
119-
120-
Could not have been Include Type `authors` because:
121-
found JSON:API type "not_an_author" but expected "authors"
122-
"""#
115+
"Out of the 3 includes in the document, the 3rd one failed to parse: Found JSON:API type 'not_an_author' but expected one of 'articles', 'authors'"
123116
)
124117
}
125118
}
@@ -140,14 +133,7 @@ final class DocumentDecodingErrorTests: XCTestCase {
140133

141134
XCTAssertEqual(
142135
String(describing: error),
143-
#"""
144-
Out of 3 includes, the 2nd one failed to parse:
145-
Could not have been Include Type `articles` because:
146-
found JSON:API type "not_an_author" but expected "articles"
147-
148-
Could not have been Include Type `authors` because:
149-
found JSON:API type "not_an_author" but expected "authors"
150-
"""#
136+
"Out of the 3 includes in the document, the 2nd one failed to parse: Found JSON:API type 'not_an_author' but expected one of 'articles', 'authors'"
151137
)
152138
}
153139
}

Tests/JSONAPITests/Includes/IncludesDecodingErrorTests.swift

Lines changed: 6 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -18,69 +18,39 @@ final class IncludesDecodingErrorTests: XCTestCase {
1818

1919
XCTAssertEqual(
2020
(error as? IncludesDecodingError).map(String.init(describing:)),
21-
"""
22-
Out of 3 includes, the 3rd one failed to parse: \nCould not have been Include Type `test_entity1` because:
23-
found JSON:API type "test_entity4" but expected "test_entity1"
24-
25-
Could not have been Include Type `test_entity2` because:
26-
found JSON:API type "test_entity4" but expected "test_entity2"
27-
"""
21+
"Out of the 3 includes in the document, the 3rd one failed to parse: Found JSON:API type 'test_entity4' but expected one of 'test_entity1', 'test_entity2'"
2822
)
2923
}
3024

3125
// now test that we get the same error with a different total include count from a different test stub
3226
XCTAssertThrowsError(try testDecoder.decode(Includes<Include2<TestEntity, TestEntity2>>.self, from: four_different_type_includes)) { (error2: Error) -> Void in
3327
XCTAssertEqual(
3428
(error2 as? IncludesDecodingError).map(String.init(describing:)),
35-
"""
36-
Out of 4 includes, the 3rd one failed to parse: \nCould not have been Include Type `test_entity1` because:
37-
found JSON:API type "test_entity4" but expected "test_entity1"
38-
39-
Could not have been Include Type `test_entity2` because:
40-
found JSON:API type "test_entity4" but expected "test_entity2"
41-
"""
29+
"Out of the 4 includes in the document, the 3rd one failed to parse: Found JSON:API type 'test_entity4' but expected one of 'test_entity1', 'test_entity2'"
4230
)
4331
}
4432

4533
// and with six total includes
4634
XCTAssertThrowsError(try testDecoder.decode(Includes<Include2<TestEntity, TestEntity2>>.self, from: six_includes_one_bad_type)) { (error2: Error) -> Void in
4735
XCTAssertEqual(
4836
(error2 as? IncludesDecodingError).map(String.init(describing:)),
49-
"""
50-
Out of 6 includes, the 5th one failed to parse: \nCould not have been Include Type `test_entity1` because:
51-
found JSON:API type "test_entity4" but expected "test_entity1"
52-
53-
Could not have been Include Type `test_entity2` because:
54-
found JSON:API type "test_entity4" but expected "test_entity2"
55-
"""
37+
"Out of the 6 includes in the document, the 5th one failed to parse: Found JSON:API type 'test_entity4' but expected one of 'test_entity1', 'test_entity2'"
5638
)
5739
}
5840

5941
// and with a number of total includes between 10 and 19
6042
XCTAssertThrowsError(try testDecoder.decode(Includes<Include2<TestEntity, TestEntity2>>.self, from: eleven_includes_one_bad_type)) { (error2: Error) -> Void in
6143
XCTAssertEqual(
6244
(error2 as? IncludesDecodingError).map(String.init(describing:)),
63-
"""
64-
Out of 11 includes, the 10th one failed to parse: \nCould not have been Include Type `test_entity1` because:
65-
found JSON:API type "test_entity4" but expected "test_entity1"
66-
67-
Could not have been Include Type `test_entity2` because:
68-
found JSON:API type "test_entity4" but expected "test_entity2"
69-
"""
45+
"Out of the 11 includes in the document, the 10th one failed to parse: Found JSON:API type 'test_entity4' but expected one of 'test_entity1', 'test_entity2'"
7046
)
7147
}
7248

7349
// and finally with a larger number of total includes
7450
XCTAssertThrowsError(try testDecoder.decode(Includes<Include2<TestEntity, TestEntity2>>.self, from: twenty_two_includes_one_bad_type)) { (error2: Error) -> Void in
7551
XCTAssertEqual(
7652
(error2 as? IncludesDecodingError).map(String.init(describing:)),
77-
"""
78-
Out of 22 includes, the 21st one failed to parse: \nCould not have been Include Type `test_entity1` because:
79-
found JSON:API type "test_entity4" but expected "test_entity1"
80-
81-
Could not have been Include Type `test_entity2` because:
82-
found JSON:API type "test_entity4" but expected "test_entity2"
83-
"""
53+
"Out of the 22 includes in the document, the 21st one failed to parse: Found JSON:API type 'test_entity4' but expected one of 'test_entity1', 'test_entity2'"
8454
)
8555
}
8656
}
@@ -94,16 +64,7 @@ final class IncludesDecodingErrorTests: XCTestCase {
9464
) { (error: Error) -> Void in
9565
XCTAssertEqual(
9666
(error as? IncludesDecodingError).map(String.init(describing:)),
97-
"""
98-
Out of 3 includes, the 3rd one failed to parse: \nCould not have been Include Type `test_entity1` because:
99-
found JSON:API type "test_entity2" but expected "test_entity1"
100-
101-
Could not have been Include Type `test_entity2` because:
102-
'foo' attribute is required and missing.
103-
104-
Could not have been Include Type `test_entity4` because:
105-
found JSON:API type "test_entity2" but expected "test_entity4"
106-
"""
67+
"Out of the 3 includes in the document, the 3rd one failed to parse: 'foo' attribute is required and missing."
10768
)
10869
}
10970
}

0 commit comments

Comments
 (0)