Skip to content

Commit b04f6a3

Browse files
committed
add test coverage and fill a logic gap around nullable relationships paired with non-optional id metadata.
1 parent 27604d4 commit b04f6a3

3 files changed

Lines changed: 54 additions & 10 deletions

File tree

Sources/JSONAPI/Resource/Relationship.swift

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -133,23 +133,23 @@ public struct ToManyRelationship<Relatable: JSONAPI.Relatable, IdMetaType: JSONA
133133
}
134134
}
135135

136-
public let metaIds: [ID]
136+
public let idsWithMeta: [ID]
137137

138138
public var ids: [Relatable.ID] {
139-
metaIds.map(\.id)
139+
idsWithMeta.map(\.id)
140140
}
141141

142142
public let meta: MetaType
143143
public let links: LinksType
144144

145145
public init(idsWithMetadata ids: [(Relatable.ID, IdMetaType)], meta: MetaType, links: LinksType) {
146-
self.metaIds = ids.map { ID.init($0) }
146+
self.idsWithMeta = ids.map { ID.init($0) }
147147
self.meta = meta
148148
self.links = links
149149
}
150150

151151
public init<T: JSONAPIIdentifiable>(pointers: [ToOneRelationship<T, NoIdMetadata, NoMetadata, NoLinks>], meta: MetaType, links: LinksType) where T.ID == Relatable.ID, IdMetaType == NoIdMetadata {
152-
metaIds = pointers.map { .init(id: $0.id, meta: .none) }
152+
idsWithMeta = pointers.map { .init(id: $0.id, meta: .none) }
153153
self.meta = meta
154154
self.links = links
155155
}
@@ -169,7 +169,7 @@ public struct ToManyRelationship<Relatable: JSONAPI.Relatable, IdMetaType: JSONA
169169

170170
extension ToManyRelationship where IdMetaType == NoIdMetadata {
171171
public init(ids: [Relatable.ID], meta: MetaType, links: LinksType) {
172-
self.metaIds = ids.map { .init(id: $0, meta: .none) }
172+
self.idsWithMeta = ids.map { .init(id: $0, meta: .none) }
173173
self.meta = meta
174174
self.links = links
175175
}
@@ -263,6 +263,9 @@ extension MetaRelationship: Codable {
263263
}
264264
}
265265

266+
fileprivate protocol _Optional {}
267+
extension Optional: _Optional {}
268+
266269
extension ToOneRelationship: Codable where Identifiable.ID: OptionalId {
267270
public init(from decoder: Decoder) throws {
268271
let container = try decoder.container(keyedBy: ResourceLinkageCodingKeys.self)
@@ -295,7 +298,15 @@ extension ToOneRelationship: Codable where Identifiable.ID: OptionalId {
295298
)
296299
)
297300
}
298-
guard let noIdMeta = NoIdMetadata() as? IdMetaType else {
301+
// if we know we aren't getting any Resource Identifer Object at all
302+
// (which we do inside this block) then we better either be expecting
303+
// no Id Metadata or optional Id Metadata or else we will report an
304+
// error.
305+
if let noIdMeta = NoIdMetadata() as? IdMetaType {
306+
idMeta = noIdMeta
307+
} else if let nilMeta = anyNil as? IdMetaType {
308+
idMeta = nilMeta
309+
} else {
299310
throw DecodingError.valueNotFound(
300311
Self.self,
301312
DecodingError.Context(
@@ -304,7 +315,6 @@ extension ToOneRelationship: Codable where Identifiable.ID: OptionalId {
304315
)
305316
)
306317
}
307-
idMeta = noIdMeta
308318
id = val
309319
return
310320
}
@@ -425,7 +435,7 @@ extension ToManyRelationship: Codable {
425435

426436
newIds.append(.init(id: Relatable.ID(rawValue: id), meta: idMeta) )
427437
}
428-
metaIds = newIds
438+
idsWithMeta = newIds
429439
}
430440

431441
public func encode(to encoder: Encoder) throws {
@@ -441,7 +451,7 @@ extension ToManyRelationship: Codable {
441451

442452
var identifiers = container.nestedUnkeyedContainer(forKey: .data)
443453

444-
for id in metaIds {
454+
for id in idsWithMeta {
445455
var identifier = identifiers.nestedContainer(keyedBy: ResourceIdentifierCodingKeys.self)
446456

447457
try identifier.encode(id.id.rawValue, forKey: .id)

Tests/JSONAPITests/Relationships/RelationshipTests.swift

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,26 @@ class RelationshipTests: XCTestCase {
3131
XCTAssertEqual(relationship.ids.count, 4)
3232
XCTAssertEqual(relationship.ids, [entity1, entity2, entity3, entity4].map(\.id))
3333
}
34+
35+
func test_initToOneWithIdMeta() {
36+
let entity1 = TestEntity1(attributes: .none, relationships: .none, meta: .none, links: .none)
37+
let relationship = ToOneWithMetaInIds(
38+
id: (entity1.id, .init(a: "hello"))
39+
)
40+
XCTAssertEqual(relationship.id, entity1.id)
41+
XCTAssertEqual(relationship.idMeta, .init(a: "hello"))
42+
}
43+
44+
func test_initToManyWithIdMeta() {
45+
let entity1 = TestEntity1(attributes: .none, relationships: .none, meta: .none, links: .none)
46+
let relationship = ToManyWithMetaInIds(
47+
idsWithMetadata: [
48+
(entity1.id, .init(a: "hello"))
49+
]
50+
)
51+
XCTAssertEqual(relationship.ids, [entity1.id])
52+
XCTAssertEqual(relationship.idsWithMeta, [.init(id: entity1.id, meta: .init(a: "hello"))])
53+
}
3454
}
3555

3656
// MARK: - Encode/Decode
@@ -181,7 +201,7 @@ extension RelationshipTests {
181201
data: to_many_relationship_with_meta_inside_identifier)
182202

183203
XCTAssertEqual(relationship.ids.map(\.rawValue), ["2DF03B69-4B0A-467F-B52E-B0C9E44FCECF", "90F03B69-4DF1-467F-B52E-B0C9E44FC333", "2DF03B69-4B0A-467F-B52E-B0C9E44FCECF"])
184-
XCTAssertEqual(relationship.metaIds[0].meta.a, "hello")
204+
XCTAssertEqual(relationship.idsWithMeta[0].meta.a, "hello")
185205
}
186206

187207
func test_ToManyRelationshipWithMetaInsideIdentifier_encode() {
@@ -234,6 +254,10 @@ extension RelationshipTests {
234254

235255
XCTAssertEqual(encoded(value: relationship1), encoded(value: relationship2))
236256
}
257+
258+
func test_nullableIdMeta() {
259+
let _ = decoded(type: ToOneRelationship<TestEntity1?, TestMeta?, NoMetadata, NoLinks>.self, data: to_one_relationship_nulled_out)
260+
}
237261
}
238262

239263
// MARK: Failure tests
@@ -245,6 +269,10 @@ extension RelationshipTests {
245269
func test_ToOneTypeMismatch() {
246270
XCTAssertThrowsError(try JSONDecoder().decode(ToOneRelationship<TestEntity1, NoIdMetadata, NoMetadata, NoLinks>.self, from: to_one_relationship_type_mismatch))
247271
}
272+
273+
func test_toOneNulledOutWithExpectedIdMetadata() {
274+
XCTAssertThrowsError(try JSONDecoder().decode(ToOneRelationship<TestEntity1?, TestMeta, NoMetadata, NoLinks>.self, from: to_one_relationship_nulled_out))
275+
}
248276
}
249277

250278
// MARK: - Test types

Tests/JSONAPITests/Relationships/stubs/RelationshipStubs.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,12 @@ let to_one_relationship = """
4141
}
4242
""".data(using: .utf8)!
4343

44+
let to_one_relationship_nulled_out = """
45+
{
46+
"data": null
47+
}
48+
""".data(using: .utf8)!
49+
4450
let to_one_relationship_type_mismatch = """
4551
{
4652
"data": {

0 commit comments

Comments
 (0)