From 575199e775d6afebcbb497edeccbbe2c7933d7cc Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Tue, 23 Aug 2022 22:07:44 -0700 Subject: [PATCH 01/13] Add Include12 and Include13 types. --- .github/workflows/tests.yml | 5 + Package.resolved | 4 +- Package.swift | 2 +- .../JSONAPI/Document/CompoundResource.swift | 2 +- Sources/JSONAPI/Document/Includes.swift | 16 ++ .../Resource/Poly+PrimaryResource.swift | 64 ++++++ .../Comparisons/DocumentDataCompare.swift | 2 +- .../JSONAPITests/Includes/IncludeTests.swift | 69 +++++++ .../Includes/stubs/IncludeStubs.swift | 189 ++++++++++++++++++ 9 files changed, 348 insertions(+), 5 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 2e0ea6f..3386cdf 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -28,6 +28,11 @@ jobs: - swift:5.4-focal - swift:5.4-centos8 - swift:5.4-amazonlinux2 + - swift:5.5-xenial + - swift:5.5-bionic + - swift:5.5-focal + - swift:5.5-centos8 + - swift:5.5-amazonlinux2 container: ${{ matrix.image }} steps: - name: Checkout code diff --git a/Package.resolved b/Package.resolved index 63465c7..8a582ff 100644 --- a/Package.resolved +++ b/Package.resolved @@ -6,8 +6,8 @@ "repositoryURL": "https://github.com/mattpolzin/Poly.git", "state": { "branch": null, - "revision": "36ba3f624bffa34f5f9b9c7648eab3cfdcab4748", - "version": "2.5.0" + "revision": "c108e9e0a2904134719b082f6c18d64406afc6db", + "version": "2.6.0" } } ] diff --git a/Package.swift b/Package.swift index 3762356..e7a0e51 100644 --- a/Package.swift +++ b/Package.swift @@ -17,7 +17,7 @@ let package = Package( targets: ["JSONAPITesting"]) ], dependencies: [ - .package(url: "https://github.com/mattpolzin/Poly.git", .upToNextMajor(from: "2.4.0")), + .package(url: "https://github.com/mattpolzin/Poly.git", .upToNextMajor(from: "2.6.0")), ], targets: [ .target( diff --git a/Sources/JSONAPI/Document/CompoundResource.swift b/Sources/JSONAPI/Document/CompoundResource.swift index 9ad84fe..9baaf2f 100644 --- a/Sources/JSONAPI/Document/CompoundResource.swift +++ b/Sources/JSONAPI/Document/CompoundResource.swift @@ -71,7 +71,7 @@ extension Sequence where Element: CompoundResourceProtocol { } } -extension EncodableJSONAPIDocument where PrimaryResourceBody: EncodableResourceBody, PrimaryResourceBody.PrimaryResource: ResourceObjectType { +extension EncodableJSONAPIDocument where PrimaryResourceBody.PrimaryResource: ResourceObjectType { public typealias CompoundResource = JSONAPI.CompoundResource } diff --git a/Sources/JSONAPI/Document/Includes.swift b/Sources/JSONAPI/Document/Includes.swift index e48a547..30634ae 100644 --- a/Sources/JSONAPI/Document/Includes.swift +++ b/Sources/JSONAPI/Document/Includes.swift @@ -206,6 +206,22 @@ extension Includes where I: _Poly11 { } } +// MARK: - 12 includes +public typealias Include12 = Poly12 +extension Includes where I: _Poly12 { + public subscript(_ lookup: I.L.Type) -> [I.L] { + return values.compactMap(\.l) + } +} + +// MARK: - 13 includes +public typealias Include13 = Poly13 +extension Includes where I: _Poly13 { + public subscript(_ lookup: I.M.Type) -> [I.M] { + return values.compactMap(\.m) + } +} + // MARK: - DecodingError public struct IncludesDecodingError: Swift.Error, Equatable { public let error: Swift.Error diff --git a/Sources/JSONAPI/Resource/Poly+PrimaryResource.swift b/Sources/JSONAPI/Resource/Poly+PrimaryResource.swift index 73c1246..0dab925 100644 --- a/Sources/JSONAPI/Resource/Poly+PrimaryResource.swift +++ b/Sources/JSONAPI/Resource/Poly+PrimaryResource.swift @@ -162,3 +162,67 @@ extension Poly11: CodablePrimaryResource, OptionalCodablePrimaryResource I: CodablePolyWrapped, J: CodablePolyWrapped, K: CodablePolyWrapped {} + +// MARK: - 12 types +extension Poly12: EncodablePrimaryResource, OptionalEncodablePrimaryResource +where +A: EncodablePolyWrapped, +B: EncodablePolyWrapped, +C: EncodablePolyWrapped, +D: EncodablePolyWrapped, +E: EncodablePolyWrapped, +F: EncodablePolyWrapped, +G: EncodablePolyWrapped, +H: EncodablePolyWrapped, +I: EncodablePolyWrapped, +J: EncodablePolyWrapped, +K: EncodablePolyWrapped, +L: EncodablePolyWrapped {} + +extension Poly12: CodablePrimaryResource, OptionalCodablePrimaryResource +where +A: CodablePolyWrapped, +B: CodablePolyWrapped, +C: CodablePolyWrapped, +D: CodablePolyWrapped, +E: CodablePolyWrapped, +F: CodablePolyWrapped, +G: CodablePolyWrapped, +H: CodablePolyWrapped, +I: CodablePolyWrapped, +J: CodablePolyWrapped, +K: CodablePolyWrapped, +L: CodablePolyWrapped {} + +// MARK: - 13 types +extension Poly13: EncodablePrimaryResource, OptionalEncodablePrimaryResource +where +A: EncodablePolyWrapped, +B: EncodablePolyWrapped, +C: EncodablePolyWrapped, +D: EncodablePolyWrapped, +E: EncodablePolyWrapped, +F: EncodablePolyWrapped, +G: EncodablePolyWrapped, +H: EncodablePolyWrapped, +I: EncodablePolyWrapped, +J: EncodablePolyWrapped, +K: EncodablePolyWrapped, +L: EncodablePolyWrapped, +M: EncodablePolyWrapped {} + +extension Poly13: CodablePrimaryResource, OptionalCodablePrimaryResource +where +A: CodablePolyWrapped, +B: CodablePolyWrapped, +C: CodablePolyWrapped, +D: CodablePolyWrapped, +E: CodablePolyWrapped, +F: CodablePolyWrapped, +G: CodablePolyWrapped, +H: CodablePolyWrapped, +I: CodablePolyWrapped, +J: CodablePolyWrapped, +K: CodablePolyWrapped, +L: CodablePolyWrapped, +M: CodablePolyWrapped {} diff --git a/Sources/JSONAPITesting/Comparisons/DocumentDataCompare.swift b/Sources/JSONAPITesting/Comparisons/DocumentDataCompare.swift index da7f27e..be2cad6 100644 --- a/Sources/JSONAPITesting/Comparisons/DocumentDataCompare.swift +++ b/Sources/JSONAPITesting/Comparisons/DocumentDataCompare.swift @@ -86,7 +86,7 @@ public struct ManyResourceObjectComparison: Equatable, PropertyComparison { } } -extension TestableResourceBody where TestablePrimaryResourceType: ResourceObjectType { +extension TestableResourceBody { public func compare(to other: Self) -> PrimaryResourceBodyComparison { guard let one = testableResourceObject, let two = other.testableResourceObject else { diff --git a/Tests/JSONAPITests/Includes/IncludeTests.swift b/Tests/JSONAPITests/Includes/IncludeTests.swift index 6da21e5..e0a44a4 100644 --- a/Tests/JSONAPITests/Includes/IncludeTests.swift +++ b/Tests/JSONAPITests/Includes/IncludeTests.swift @@ -219,6 +219,53 @@ class IncludedTests: XCTestCase { test_DecodeEncodeEquality(type: Includes>.self, data: eleven_different_type_includes) } + + func test_TwelveDifferentIncludes() { + let includes = decoded(type: Includes>.self, + data: twelve_different_type_includes) + + XCTAssertEqual(includes[TestEntity.self].count, 1) + XCTAssertEqual(includes[TestEntity2.self].count, 1) + XCTAssertEqual(includes[TestEntity3.self].count, 1) + XCTAssertEqual(includes[TestEntity4.self].count, 1) + XCTAssertEqual(includes[TestEntity5.self].count, 1) + XCTAssertEqual(includes[TestEntity6.self].count, 1) + XCTAssertEqual(includes[TestEntity7.self].count, 1) + XCTAssertEqual(includes[TestEntity8.self].count, 1) + XCTAssertEqual(includes[TestEntity9.self].count, 1) + XCTAssertEqual(includes[TestEntity10.self].count, 1) + XCTAssertEqual(includes[TestEntity11.self].count, 1) + XCTAssertEqual(includes[TestEntity12.self].count, 1) + } + + func test_TwelveDifferentIncludes_encode() { + test_DecodeEncodeEquality(type: Includes>.self, + data: twelve_different_type_includes) + } + + func test_ThirteenDifferentIncludes() { + let includes = decoded(type: Includes>.self, + data: thirteen_different_type_includes) + + XCTAssertEqual(includes[TestEntity.self].count, 1) + XCTAssertEqual(includes[TestEntity2.self].count, 1) + XCTAssertEqual(includes[TestEntity3.self].count, 1) + XCTAssertEqual(includes[TestEntity4.self].count, 1) + XCTAssertEqual(includes[TestEntity5.self].count, 1) + XCTAssertEqual(includes[TestEntity6.self].count, 1) + XCTAssertEqual(includes[TestEntity7.self].count, 1) + XCTAssertEqual(includes[TestEntity8.self].count, 1) + XCTAssertEqual(includes[TestEntity9.self].count, 1) + XCTAssertEqual(includes[TestEntity10.self].count, 1) + XCTAssertEqual(includes[TestEntity11.self].count, 1) + XCTAssertEqual(includes[TestEntity12.self].count, 1) + XCTAssertEqual(includes[TestEntity13.self].count, 1) + } + + func test_ThirteenDifferentIncludes_encode() { + test_DecodeEncodeEquality(type: Includes>.self, + data: thirteen_different_type_includes) + } } // MARK: - Appending @@ -536,4 +583,26 @@ extension IncludedTests { } typealias TestEntity11 = BasicEntity + + enum TestEntityType12: ResourceObjectDescription { + + typealias Attributes = NoAttributes + + public static var jsonType: String { return "test_entity12" } + + typealias Relationships = NoRelationships + } + + typealias TestEntity12 = BasicEntity + + enum TestEntityType13: ResourceObjectDescription { + + typealias Attributes = NoAttributes + + public static var jsonType: String { return "test_entity13" } + + typealias Relationships = NoRelationships + } + + typealias TestEntity13 = BasicEntity } diff --git a/Tests/JSONAPITests/Includes/stubs/IncludeStubs.swift b/Tests/JSONAPITests/Includes/stubs/IncludeStubs.swift index 708e154..2cfd3fa 100644 --- a/Tests/JSONAPITests/Includes/stubs/IncludeStubs.swift +++ b/Tests/JSONAPITests/Includes/stubs/IncludeStubs.swift @@ -687,6 +687,195 @@ let eleven_different_type_includes = """ ] """.data(using: .utf8)! +let twelve_different_type_includes = """ +[ + { + "type": "test_entity1", + "id": "2DF03B69-4B0A-467F-B52E-B0C9E44FCECF", + "attributes": { + "foo": "Hello", + "bar": 123 + } + }, + { + "type": "test_entity2", + "id": "90F03B69-4DF1-467F-B52E-B0C9E44FC333", + "attributes": { + "foo": "World", + "bar": 456 + }, + "relationships": { + "entity1": { + "data": { + "type": "test_entity1", + "id": "2DF03B69-4B0A-467F-B52E-B0C9E44FCECF" + } + } + } + }, + { + "type": "test_entity3", + "id": "11223B69-4DF1-467F-B52E-B0C9E44FC443", + "relationships": { + "entity1": { + "data": { + "type": "test_entity1", + "id": "2DF03B69-4B0A-467F-B52E-B0C9E44FCECF" + } + }, + "entity2": { + "data": [ + { + "type": "test_entity2", + "id": "90F03B69-4DF1-467F-B52E-B0C9E44FC333" + } + ] + } + } + }, + { + "type": "test_entity6", + "id": "11113B69-4DF1-467F-B52E-B0C9E44FC444", + "relationships": { + "entity4": { + "data": { + "type": "test_entity4", + "id": "364B3B69-4DF1-467F-B52E-B0C9E44F666E" + } + } + } + }, + { + "type": "test_entity5", + "id": "A24B3B69-4DF1-467F-B52E-B0C9E44F436A" + }, + { + "type": "test_entity4", + "id": "364B3B69-4DF1-467F-B52E-B0C9E44F666E" + }, + { + "type": "test_entity7", + "id": "364B3B69-4DF1-222F-B52E-B0C9E44F666E" + }, + { + "type": "test_entity8", + "id": "364B3B69-4DF1-222F-B52E-B0C9E44F266F" + }, + { + "type": "test_entity9", + "id": "364B3B69-4DF1-218F-B52E-B0C9E44F2661" + }, + { + "type": "test_entity10", + "id": "264B3B69-4DF1-212F-B52E-B0C9E44F2660" + }, + { + "type": "test_entity11", + "id": "264B3B69-4DF3-212F-B32E-A0C9E44F26C0B" + }, + { + "type": "test_entity12", + "id": "264B3B69-4DF3-212F-B32E-A0C9E44F26C00" + } +] +""".data(using: .utf8)! + +let thirteen_different_type_includes = """ +[ + { + "type": "test_entity1", + "id": "2DF03B69-4B0A-467F-B52E-B0C9E44FCECF", + "attributes": { + "foo": "Hello", + "bar": 123 + } + }, + { + "type": "test_entity2", + "id": "90F03B69-4DF1-467F-B52E-B0C9E44FC333", + "attributes": { + "foo": "World", + "bar": 456 + }, + "relationships": { + "entity1": { + "data": { + "type": "test_entity1", + "id": "2DF03B69-4B0A-467F-B52E-B0C9E44FCECF" + } + } + } + }, + { + "type": "test_entity3", + "id": "11223B69-4DF1-467F-B52E-B0C9E44FC443", + "relationships": { + "entity1": { + "data": { + "type": "test_entity1", + "id": "2DF03B69-4B0A-467F-B52E-B0C9E44FCECF" + } + }, + "entity2": { + "data": [ + { + "type": "test_entity2", + "id": "90F03B69-4DF1-467F-B52E-B0C9E44FC333" + } + ] + } + } + }, + { + "type": "test_entity6", + "id": "11113B69-4DF1-467F-B52E-B0C9E44FC444", + "relationships": { + "entity4": { + "data": { + "type": "test_entity4", + "id": "364B3B69-4DF1-467F-B52E-B0C9E44F666E" + } + } + } + }, + { + "type": "test_entity5", + "id": "A24B3B69-4DF1-467F-B52E-B0C9E44F436A" + }, + { + "type": "test_entity4", + "id": "364B3B69-4DF1-467F-B52E-B0C9E44F666E" + }, + { + "type": "test_entity7", + "id": "364B3B69-4DF1-222F-B52E-B0C9E44F666E" + }, + { + "type": "test_entity8", + "id": "364B3B69-4DF1-222F-B52E-B0C9E44F266F" + }, + { + "type": "test_entity9", + "id": "364B3B69-4DF1-218F-B52E-B0C9E44F2661" + }, + { + "type": "test_entity10", + "id": "264B3B69-4DF1-212F-B52E-B0C9E44F2660" + }, + { + "type": "test_entity11", + "id": "264B3B69-4DF3-212F-B32E-A0C9E44F26C0B" + }, + { + "type": "test_entity12", + "id": "264B3B69-4DF3-212F-B32E-A0C9E44F26C00" + }, + { + "type": "test_entity13", + "id": "264B3B69-4DF3-212F-B32E-A0C9E44F26C01" + } +] +""".data(using: .utf8)! let three_includes_one_missing_attributes = """ [ From 91639846031a4a3fae32d1b470b5cf404c2833f9 Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Tue, 23 Aug 2022 22:08:56 -0700 Subject: [PATCH 02/13] bump podspec versions --- JSONAPI.podspec | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/JSONAPI.podspec b/JSONAPI.podspec index 19e5c0e..4a22764 100644 --- a/JSONAPI.podspec +++ b/JSONAPI.podspec @@ -16,7 +16,7 @@ Pod::Spec.new do |spec| # spec.name = "MP-JSONAPI" - spec.version = "5.0.2" + spec.version = "5.1.0" spec.summary = "Swift Codable JSON API framework." # This description is used to generate tags and improve search results. @@ -136,6 +136,6 @@ See the JSON API Spec here: https://jsonapi.org/format/ # spec.requires_arc = true # spec.xcconfig = { "HEADER_SEARCH_PATHS" => "$(SDKROOT)/usr/include/libxml2" } - spec.dependency "Poly", "~> 2.4" + spec.dependency "Poly", "~> 2.6" end From f7e5df3d5e21b385587c368d80e90a1f736beb76 Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Tue, 23 Aug 2022 22:09:51 -0700 Subject: [PATCH 03/13] update readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c74fb68..c42c9d7 100644 --- a/README.md +++ b/README.md @@ -66,7 +66,7 @@ If you find something wrong with this library and it isn't already mentioned und ### Swift Package Manager Just include the following in your package's dependencies and add `JSONAPI` to the dependencies for any of your targets. ```swift -.package(url: "https://github.com/mattpolzin/JSONAPI.git", from: "5.0.2") +.package(url: "https://github.com/mattpolzin/JSONAPI.git", from: "5.1.0") ``` ### Xcode project From 022611713f5a99294ab1c46ea6d63bc481e89610 Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Tue, 23 Aug 2022 22:11:21 -0700 Subject: [PATCH 04/13] Add swift 5.6 linux images to tests as well. --- .github/workflows/tests.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 3386cdf..00fc0fe 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -33,6 +33,11 @@ jobs: - swift:5.5-focal - swift:5.5-centos8 - swift:5.5-amazonlinux2 + - swift:5.6-xenial + - swift:5.6-bionic + - swift:5.6-focal + - swift:5.6-centos8 + - swift:5.6-amazonlinux2 container: ${{ matrix.image }} steps: - name: Checkout code From a6ffeab1cb225f87323f218b327b84c83a908e00 Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Tue, 23 Aug 2022 22:24:55 -0700 Subject: [PATCH 05/13] See if this fixes latest linux swift versions. --- .../Resource Object/ResourceObjectDecodingError.swift | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/Sources/JSONAPI/Resource/Resource Object/ResourceObjectDecodingError.swift b/Sources/JSONAPI/Resource/Resource Object/ResourceObjectDecodingError.swift index 7ca4c04..72f0a2a 100644 --- a/Sources/JSONAPI/Resource/Resource Object/ResourceObjectDecodingError.swift +++ b/Sources/JSONAPI/Resource/Resource Object/ResourceObjectDecodingError.swift @@ -117,9 +117,16 @@ public struct ResourceObjectDecodingError: Swift.Error, Equatable { location = .type } + let subjectPath: [CodingKey] + if location == .relationships && path.last?.stringValue == "data" { + subjectPath = path.dropLast() + } else { + subjectPath = path + } + return ( location, - name: path.last?.stringValue ?? "unnamed" + name: subjectPath.last?.stringValue ?? "unnamed" ) } } From fd412a4ccd9e3b6fa36ca7e36931a2c00708689a Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Tue, 23 Aug 2022 22:49:31 -0700 Subject: [PATCH 06/13] slowly fixing latest swift version linux tests --- .../ResourceObjectDecodingErrorTests.swift | 96 ++++++++++++++----- 1 file changed, 72 insertions(+), 24 deletions(-) diff --git a/Tests/JSONAPITests/ResourceObject/ResourceObjectDecodingErrorTests.swift b/Tests/JSONAPITests/ResourceObject/ResourceObjectDecodingErrorTests.swift index 6597293..3003dae 100644 --- a/Tests/JSONAPITests/ResourceObject/ResourceObjectDecodingErrorTests.swift +++ b/Tests/JSONAPITests/ResourceObject/ResourceObjectDecodingErrorTests.swift @@ -103,20 +103,44 @@ final class ResourceObjectDecodingErrorTests: XCTestCase { TestEntity.self, from: entity_nonNullable_relationship_is_null )) { error in - XCTAssertEqual( - error as? ResourceObjectDecodingError, - ResourceObjectDecodingError( - subjectName: "required", - cause: .valueNotFound, - location: .relationships, - jsonAPIType: TestEntity.jsonType + let specialError = error as? ResourceObjectDecodingError + + XCTAssertNotNil(specialError) + + // later Linux versions of Swift will catch that the value is not + // a string rather than calling null "not found." The errors are both + // effective, so we check that one of the two is the result: + if specialError?.cause == .valueNotFound { + XCTAssertEqual( + specialError, + ResourceObjectDecodingError( + subjectName: "required", + cause: .valueNotFound, + location: .relationships, + jsonAPIType: TestEntity.jsonType + ) ) - ) - XCTAssertEqual( - (error as? ResourceObjectDecodingError)?.description, - "'required' relationship is not nullable but null was found." - ) + XCTAssertEqual( + specialError?.description, + "'required' relationship is not nullable but null was found." + ) + } else { + XCTAssertEqual( + specialError, + ResourceObjectDecodingError( + subjectName: "required", + cause: .typeMismatch(expectedTypeName: "Dictionary"), + location: .relationships, + jsonAPIType: TestEntity.jsonType + ) + ) + + XCTAssertEqual( + specialError?.description, + "'required' relationship is not a Dictionary as expected." + ) + } } } @@ -259,20 +283,44 @@ extension ResourceObjectDecodingErrorTests { TestEntity2.self, from: entity_nonNullable_attribute_is_null )) { error in - XCTAssertEqual( - error as? ResourceObjectDecodingError, - ResourceObjectDecodingError( - subjectName: "required", - cause: .valueNotFound, - location: .attributes, - jsonAPIType: TestEntity2.jsonType + let specialError = error as? ResourceObjectDecodingError + + XCTAssertNotNil(specialError) + + // later Linux versions of Swift will catch that the value is not + // a string rather than calling null "not found." The errors are both + // effective, so we check that one of the two is the result: + if specialError?.cause == .valueNotFound { + XCTAssertEqual( + specialError, + ResourceObjectDecodingError( + subjectName: "required", + cause: .valueNotFound, + location: .attributes, + jsonAPIType: TestEntity2.jsonType + ) ) - ) - XCTAssertEqual( - (error as? ResourceObjectDecodingError)?.description, - "'required' attribute is not nullable but null was found." - ) + XCTAssertEqual( + specialError?.description, + "'required' attribute is not nullable but null was found." + ) + } else { + XCTAssertEqual( + specialError, + ResourceObjectDecodingError( + subjectName: "required", + cause: .typeMismatch(expectedTypeName: "String"), + location: .attributes, + jsonAPIType: TestEntity2.jsonType + ) + ) + + XCTAssertEqual( + specialError?.description, + "'required' attribute is not a String as expected." + ) + } } } From 5e125abc5943924035dab28dfb5d0d032fd731ac Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Tue, 23 Aug 2022 22:52:21 -0700 Subject: [PATCH 07/13] fix another test --- .../ResourceObjectDecodingErrorTests.swift | 48 ++++++++++++++----- 1 file changed, 36 insertions(+), 12 deletions(-) diff --git a/Tests/JSONAPITests/ResourceObject/ResourceObjectDecodingErrorTests.swift b/Tests/JSONAPITests/ResourceObject/ResourceObjectDecodingErrorTests.swift index 3003dae..d8fcd0c 100644 --- a/Tests/JSONAPITests/ResourceObject/ResourceObjectDecodingErrorTests.swift +++ b/Tests/JSONAPITests/ResourceObject/ResourceObjectDecodingErrorTests.swift @@ -498,20 +498,44 @@ extension ResourceObjectDecodingErrorTests { TestEntity2.self, from: entity_type_is_null )) { error in - XCTAssertEqual( - error as? ResourceObjectDecodingError, - ResourceObjectDecodingError( - subjectName: "type", - cause: .valueNotFound, - location: .type, - jsonAPIType: TestEntity2.jsonType + let specialError = error as? ResourceObjectDecodingError + + XCTAssertNotNil(specialError) + + // later Linux versions of Swift will catch that the value is not + // a string rather than calling null "not found." The errors are both + // effective, so we check that one of the two is the result: + if specialError?.cause == .valueNotFound { + XCTAssertEqual( + specialError, + ResourceObjectDecodingError( + subjectName: "type", + cause: .valueNotFound, + location: .type, + jsonAPIType: TestEntity2.jsonType + ) ) - ) - XCTAssertEqual( - (error as? ResourceObjectDecodingError)?.description, - #"'type' (a.k.a. JSON:API type name) is not nullable but null was found."# - ) + XCTAssertEqual( + specialError?.description, + "'type' (a.k.a. JSON:API type name) is not nullable but null was found." + ) + } else { + XCTAssertEqual( + specialError, + ResourceObjectDecodingError( + subjectName: "type", + cause: .typeMismatch(expectedTypeName: "String"), + location: .type, + jsonAPIType: TestEntity2.jsonType + ) + ) + + XCTAssertEqual( + specialError?.description, + "'type' (a.k.a. JSON:API type name) is not a String as expected." + ) + } } } } From 9d0c320e3b327c3a2831a9623e7765b60aab9cb7 Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Tue, 23 Aug 2022 22:56:24 -0700 Subject: [PATCH 08/13] update linux swift images used for later versions of swift. --- .github/workflows/tests.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 00fc0fe..944a0a1 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -33,10 +33,9 @@ jobs: - swift:5.5-focal - swift:5.5-centos8 - swift:5.5-amazonlinux2 - - swift:5.6-xenial - swift:5.6-bionic - swift:5.6-focal - - swift:5.6-centos8 + - swift:5.6-centos7 - swift:5.6-amazonlinux2 container: ${{ matrix.image }} steps: From 6ccd8fd89183cf518e6721614204015ed88b2180 Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Tue, 23 Aug 2022 23:14:28 -0700 Subject: [PATCH 09/13] hopefully catch a stupid handling of null in linux decoding of json. --- Sources/JSONAPI/Document/DocumentDecodingError.swift | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Sources/JSONAPI/Document/DocumentDecodingError.swift b/Sources/JSONAPI/Document/DocumentDecodingError.swift index 2bcfa60..e31b816 100644 --- a/Sources/JSONAPI/Document/DocumentDecodingError.swift +++ b/Sources/JSONAPI/Document/DocumentDecodingError.swift @@ -33,6 +33,8 @@ public enum DocumentDecodingError: Swift.Error, Equatable { self = .primaryResourceMissing case .valueNotFound(let type, let context) where Location(context) == .data && type == UnkeyedDecodingContainer.self: self = .primaryResourcesMissing + case .typeMismatch(_, let context) where context.debugDescription.hasSuffix("but found null instead."): + self = .primaryResourcesMissing default: return nil } From d94166074c38b384c3280e54baa44156b9cb615c Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Tue, 23 Aug 2022 23:22:55 -0700 Subject: [PATCH 10/13] tweak test expectation --- .../Resource Object/ResourceObjectDecodingError.swift | 4 ++-- .../ResourceObject/ResourceObjectDecodingErrorTests.swift | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Sources/JSONAPI/Resource/Resource Object/ResourceObjectDecodingError.swift b/Sources/JSONAPI/Resource/Resource Object/ResourceObjectDecodingError.swift index 72f0a2a..004e03c 100644 --- a/Sources/JSONAPI/Resource/Resource Object/ResourceObjectDecodingError.swift +++ b/Sources/JSONAPI/Resource/Resource Object/ResourceObjectDecodingError.swift @@ -137,7 +137,7 @@ extension ResourceObjectDecodingError: CustomStringConvertible { case .keyNotFound where subjectName == ResourceObjectDecodingError.entireObject: return "\(location) object is required and missing." case .keyNotFound where location == .type: - return "'type' (a.k.a. JSON:API type name) is required and missing." + return "'type' (a.k.a. the JSON:API type name) is required and missing." case .keyNotFound where location == .relationshipType: return "'\(subjectName)' relationship does not have a 'type'." case .keyNotFound where location == .relationshipId: @@ -145,7 +145,7 @@ extension ResourceObjectDecodingError: CustomStringConvertible { case .keyNotFound: return "'\(subjectName)' \(location.singular) is required and missing." case .valueNotFound where location == .type: - return "'\(location.singular)' (a.k.a. JSON:API type name) is not nullable but null was found." + return "'\(location.singular)' (a.k.a. the JSON:API type name) is not nullable but null was found." case .valueNotFound: return "'\(subjectName)' \(location.singular) is not nullable but null was found." case .typeMismatch(expectedTypeName: let expected) where location == .type: diff --git a/Tests/JSONAPITests/ResourceObject/ResourceObjectDecodingErrorTests.swift b/Tests/JSONAPITests/ResourceObject/ResourceObjectDecodingErrorTests.swift index d8fcd0c..694acb9 100644 --- a/Tests/JSONAPITests/ResourceObject/ResourceObjectDecodingErrorTests.swift +++ b/Tests/JSONAPITests/ResourceObject/ResourceObjectDecodingErrorTests.swift @@ -488,7 +488,7 @@ extension ResourceObjectDecodingErrorTests { XCTAssertEqual( (error as? ResourceObjectDecodingError)?.description, - #"'type' (a.k.a. JSON:API type name) is required and missing."# + #"'type' (a.k.a. the JSON:API type name) is required and missing."# ) } } @@ -518,7 +518,7 @@ extension ResourceObjectDecodingErrorTests { XCTAssertEqual( specialError?.description, - "'type' (a.k.a. JSON:API type name) is not nullable but null was found." + "'type' (a.k.a. the JSON:API type name) is not nullable but null was found." ) } else { XCTAssertEqual( @@ -533,7 +533,7 @@ extension ResourceObjectDecodingErrorTests { XCTAssertEqual( specialError?.description, - "'type' (a.k.a. JSON:API type name) is not a String as expected." + "'type' (a.k.a. the JSON:API type name) is not a String as expected." ) } } From 86de52ffb87a4d4011d860b46cd3c4341c7a3f8d Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Tue, 23 Aug 2022 23:31:09 -0700 Subject: [PATCH 11/13] ditch centos which had non-test-related error. fix type mismatch heuristic. --- .github/workflows/tests.yml | 1 - Sources/JSONAPI/Document/DocumentDecodingError.swift | 4 +++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 944a0a1..7f5d35b 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -35,7 +35,6 @@ jobs: - swift:5.5-amazonlinux2 - swift:5.6-bionic - swift:5.6-focal - - swift:5.6-centos7 - swift:5.6-amazonlinux2 container: ${{ matrix.image }} steps: diff --git a/Sources/JSONAPI/Document/DocumentDecodingError.swift b/Sources/JSONAPI/Document/DocumentDecodingError.swift index e31b816..f9994cf 100644 --- a/Sources/JSONAPI/Document/DocumentDecodingError.swift +++ b/Sources/JSONAPI/Document/DocumentDecodingError.swift @@ -33,7 +33,9 @@ public enum DocumentDecodingError: Swift.Error, Equatable { self = .primaryResourceMissing case .valueNotFound(let type, let context) where Location(context) == .data && type == UnkeyedDecodingContainer.self: self = .primaryResourcesMissing - case .typeMismatch(_, let context) where context.debugDescription.hasSuffix("but found null instead."): + case .typeMismatch(let type, let context) where Location(context) == .data && type is AbstractResourceObject.Type && context.debugDescription.hasSuffix("but found null instead."): + self = .primaryResourceMissing + case .typeMismatch(let type, let context) where Location(context) == .data && type == UnkeyedDecodingContainer.self && context.debugDescription.hasSuffix("but found null instead."): self = .primaryResourcesMissing default: return nil From 9e8c288a25580da0d3802fa8b2c6cc5c0692fc39 Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Tue, 23 Aug 2022 23:35:16 -0700 Subject: [PATCH 12/13] still trying to fix primary resource decoding error --- Sources/JSONAPI/Document/DocumentDecodingError.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Sources/JSONAPI/Document/DocumentDecodingError.swift b/Sources/JSONAPI/Document/DocumentDecodingError.swift index f9994cf..34ff222 100644 --- a/Sources/JSONAPI/Document/DocumentDecodingError.swift +++ b/Sources/JSONAPI/Document/DocumentDecodingError.swift @@ -33,10 +33,10 @@ public enum DocumentDecodingError: Swift.Error, Equatable { self = .primaryResourceMissing case .valueNotFound(let type, let context) where Location(context) == .data && type == UnkeyedDecodingContainer.self: self = .primaryResourcesMissing - case .typeMismatch(let type, let context) where Location(context) == .data && type is AbstractResourceObject.Type && context.debugDescription.hasSuffix("but found null instead."): - self = .primaryResourceMissing case .typeMismatch(let type, let context) where Location(context) == .data && type == UnkeyedDecodingContainer.self && context.debugDescription.hasSuffix("but found null instead."): self = .primaryResourcesMissing + case .typeMismatch(_, let context) where Location(context) == .data && context.debugDescription.hasSuffix("but found null instead."): + self = .primaryResourceMissing default: return nil } From aec39fc010c6408f6c6e52db0bfaccb0787d07ec Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Tue, 23 Aug 2022 23:48:18 -0700 Subject: [PATCH 13/13] trying to determine when a primary resource error should be plural or not. --- Sources/JSONAPI/Document/DocumentDecodingError.swift | 5 ++++- Tests/JSONAPITests/Document/DocumentDecodingErrorTests.swift | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/Sources/JSONAPI/Document/DocumentDecodingError.swift b/Sources/JSONAPI/Document/DocumentDecodingError.swift index 34ff222..92e9ade 100644 --- a/Sources/JSONAPI/Document/DocumentDecodingError.swift +++ b/Sources/JSONAPI/Document/DocumentDecodingError.swift @@ -33,7 +33,7 @@ public enum DocumentDecodingError: Swift.Error, Equatable { self = .primaryResourceMissing case .valueNotFound(let type, let context) where Location(context) == .data && type == UnkeyedDecodingContainer.self: self = .primaryResourcesMissing - case .typeMismatch(let type, let context) where Location(context) == .data && type == UnkeyedDecodingContainer.self && context.debugDescription.hasSuffix("but found null instead."): + case .typeMismatch(let type, let context) where Location(context) == .data && type is _ArrayType.Type && context.debugDescription.hasSuffix("but found null instead."): self = .primaryResourcesMissing case .typeMismatch(_, let context) where Location(context) == .data && context.debugDescription.hasSuffix("but found null instead."): self = .primaryResourceMissing @@ -75,3 +75,6 @@ extension DocumentDecodingError: CustomStringConvertible { } } } + +private protocol _ArrayType {} +extension Array: _ArrayType {} diff --git a/Tests/JSONAPITests/Document/DocumentDecodingErrorTests.swift b/Tests/JSONAPITests/Document/DocumentDecodingErrorTests.swift index a10d45f..0100a07 100644 --- a/Tests/JSONAPITests/Document/DocumentDecodingErrorTests.swift +++ b/Tests/JSONAPITests/Document/DocumentDecodingErrorTests.swift @@ -53,7 +53,7 @@ final class DocumentDecodingErrorTests: XCTestCase { ) { error in guard let docError = error as? DocumentDecodingError, case .primaryResourcesMissing = docError else { - XCTFail("Expected primary resource missing error. Got \(error)") + XCTFail("Expected primary resources missing error. Got \(error)") return }