diff --git a/README.md b/README.md index 9f38cb3..ec3ade7 100644 --- a/README.md +++ b/README.md @@ -47,8 +47,8 @@ Note that Playground support for importing non-system Frameworks is still a bit - [x] `type` - [x] `attributes` - [x] `relationships` -- [x] `links` (untested) -- [x] `meta` (untested) +- [x] `links` +- [x] `meta` #### Relationship Object - [x] `data` diff --git a/Sources/JSONAPI/Meta/Links.swift b/Sources/JSONAPI/Meta/Links.swift index fb18008..00658cb 100644 --- a/Sources/JSONAPI/Meta/Links.swift +++ b/Sources/JSONAPI/Meta/Links.swift @@ -21,6 +21,17 @@ public protocol JSONAPIURL: Codable, Equatable {} public struct Link: Equatable, Codable { public let url: URL public let meta: Meta + + public init(url: URL, meta: Meta) { + self.url = url + self.meta = meta + } +} + +extension Link where Meta == NoMetadata { + public init(url: URL) { + self.init(url: url, meta: .none) + } } public extension Link { diff --git a/Tests/JSONAPITests/Entity/EntityTests.swift b/Tests/JSONAPITests/Entity/EntityTests.swift index 8ad511a..bd70c9b 100644 --- a/Tests/JSONAPITests/Entity/EntityTests.swift +++ b/Tests/JSONAPITests/Entity/EntityTests.swift @@ -57,6 +57,8 @@ class EntityTests: XCTestCase { let _ = TestEntity10(id: .init(rawValue: "10"), relationships: .init(selfRef: .init(id: e10id1), selfRefs: .init(ids: [e10id2, e10id3]))) XCTAssertNoThrow(try TestEntity11(id: .init(rawValue: "11"), attributes: .init(number: .init(rawValue: 11)))) let _ = UnidentifiedTestEntity(attributes: .init(me: .init(value: "hello"))) + let _ = UnidentifiedTestEntityWithMeta(attributes: .init(me: .init(value: "hello")), meta: .init(x: "world", y: nil)) + let _ = UnidentifiedTestEntityWithLinks(attributes: .init(me: .init(value: "hello")), links: .init(link1: .init(url: "hmmm"))) } } @@ -336,6 +338,109 @@ extension EntityTests { } } +// MARK: With Meta and/or Links + +extension EntityTests { + func test_UnidentifiedEntityWithAttributesAndMeta() { + let entity = decoded(type: UnidentifiedTestEntityWithMeta.self, + data: entity_unidentified_with_attributes_and_meta) + + XCTAssertEqual(entity[\.me], "unknown") + XCTAssertEqual(entity.id, .unidentified) + XCTAssertEqual(entity.meta.x, "world") + XCTAssertEqual(entity.meta.y, 5) + XCTAssertNoThrow(try UnidentifiedTestEntityWithMeta.check(entity)) + } + + func test_UnidentifiedEntityWithAttributesAndMeta_encode() { + test_DecodeEncodeEquality(type: UnidentifiedTestEntityWithMeta.self, + data: entity_unidentified_with_attributes_and_meta) + } + + func test_UnidentifiedEntityWithAttributesAndLinks() { + let entity = decoded(type: UnidentifiedTestEntityWithLinks.self, + data: entity_unidentified_with_attributes_and_links) + + XCTAssertEqual(entity[\.me], "unknown") + XCTAssertEqual(entity.id, .unidentified) + XCTAssertEqual(entity.links.link1, .init(url: "https://image.com/image.png")) + XCTAssertNoThrow(try UnidentifiedTestEntityWithLinks.check(entity)) + } + + func test_UnidentifiedEntityWithAttributesAndLinks_encode() { + test_DecodeEncodeEquality(type: UnidentifiedTestEntityWithLinks.self, + data: entity_unidentified_with_attributes_and_links) + } + + func test_UnidentifiedEntityWithAttributesAndMetaAndLinks() { + let entity = decoded(type: UnidentifiedTestEntityWithMetaAndLinks.self, + data: entity_unidentified_with_attributes_and_meta_and_links) + + XCTAssertEqual(entity[\.me], "unknown") + XCTAssertEqual(entity.id, .unidentified) + XCTAssertEqual(entity.meta.x, "world") + XCTAssertEqual(entity.meta.y, 5) + XCTAssertEqual(entity.links.link1, .init(url: "https://image.com/image.png")) + XCTAssertNoThrow(try UnidentifiedTestEntityWithMetaAndLinks.check(entity)) + } + + func test_UnidentifiedEntityWithAttributesAndMetaAndLinks_encode() { + test_DecodeEncodeEquality(type: UnidentifiedTestEntityWithMetaAndLinks.self, + data: entity_unidentified_with_attributes_and_meta_and_links) + } + + func test_EntitySomeRelationshipsSomeAttributesWithMeta() { + let entity = decoded(type: TestEntity4WithMeta.self, + data: entity_some_relationships_some_attributes_with_meta) + + XCTAssertEqual(entity[\.word], "coolio") + XCTAssertEqual(entity[\.number], 992299) + XCTAssertEqual((entity ~> \.other).rawValue, "2DF03B69-4B0A-467F-B52E-B0C9E44FCECF") + XCTAssertEqual(entity.meta.x, "world") + XCTAssertEqual(entity.meta.y, 5) + XCTAssertNoThrow(try TestEntity4WithMeta.check(entity)) + } + + func test_EntitySomeRelationshipsSomeAttributesWithMeta_encode() { + test_DecodeEncodeEquality(type: TestEntity4WithMeta.self, + data: entity_some_relationships_some_attributes_with_meta) + } + + func test_EntitySomeRelationshipsSomeAttributesWithLinks() { + let entity = decoded(type: TestEntity4WithLinks.self, + data: entity_some_relationships_some_attributes_with_links) + + XCTAssertEqual(entity[\.word], "coolio") + XCTAssertEqual(entity[\.number], 992299) + XCTAssertEqual((entity ~> \.other).rawValue, "2DF03B69-4B0A-467F-B52E-B0C9E44FCECF") + XCTAssertEqual(entity.links.link1, .init(url: "https://image.com/image.png")) + XCTAssertNoThrow(try TestEntity4WithLinks.check(entity)) + } + + func test_EntitySomeRelationshipsSomeAttributesWithLinks_encode() { + test_DecodeEncodeEquality(type: TestEntity4WithLinks.self, + data: entity_some_relationships_some_attributes_with_links) + } + + func test_EntitySomeRelationshipsSomeAttributesWithMetaAndLinks() { + let entity = decoded(type: TestEntity4WithMetaAndLinks.self, + data: entity_some_relationships_some_attributes_with_meta_and_links) + + XCTAssertEqual(entity[\.word], "coolio") + XCTAssertEqual(entity[\.number], 992299) + XCTAssertEqual((entity ~> \.other).rawValue, "2DF03B69-4B0A-467F-B52E-B0C9E44FCECF") + XCTAssertEqual(entity.meta.x, "world") + XCTAssertEqual(entity.meta.y, 5) + XCTAssertEqual(entity.links.link1, .init(url: "https://image.com/image.png")) + XCTAssertNoThrow(try TestEntity4WithMetaAndLinks.check(entity)) + } + + func test_EntitySomeRelationshipsSomeAttributesWithMetaAndLinks_encode() { + test_DecodeEncodeEquality(type: TestEntity4WithMetaAndLinks.self, + data: entity_some_relationships_some_attributes_with_meta_and_links) + } +} + // MARK: - Test Types extension EntityTests { @@ -388,6 +493,12 @@ extension EntityTests { typealias TestEntity4 = BasicEntity + typealias TestEntity4WithMeta = Entity + + typealias TestEntity4WithLinks = Entity + + typealias TestEntity4WithMetaAndLinks = Entity + enum TestEntityType5: EntityDescription { static var type: String { return "fifth_test_entities"} @@ -504,6 +615,12 @@ extension EntityTests { typealias UnidentifiedTestEntity = NewEntity + typealias UnidentifiedTestEntityWithMeta = NewEntity + + typealias UnidentifiedTestEntityWithLinks = NewEntity + + typealias UnidentifiedTestEntityWithMetaAndLinks = NewEntity + enum IntToString: Transformer { public static func transform(_ from: Int) -> String { return String(from) @@ -540,4 +657,13 @@ extension EntityTests { return from } } + + struct TestEntityMeta: JSONAPI.Meta { + let x: String + let y: Int? + } + + struct TestEntityLinks: JSONAPI.Links { + let link1: Link + } } diff --git a/Tests/JSONAPITests/Entity/stubs/EntityStubs.swift b/Tests/JSONAPITests/Entity/stubs/EntityStubs.swift index d4d5f27..f2e14c2 100644 --- a/Tests/JSONAPITests/Entity/stubs/EntityStubs.swift +++ b/Tests/JSONAPITests/Entity/stubs/EntityStubs.swift @@ -57,6 +57,83 @@ let entity_some_relationships_some_attributes = """ } """.data(using: .utf8)! +let entity_some_relationships_some_attributes_with_meta = """ +{ + "id": "90F03B69-4DF1-467F-B52E-B0C9E44FC333", + "type": "fourth_test_entities", + "attributes": { + "word": "coolio", + "number": 992299, + "array": [12.3, 4, 0.1] + }, + "relationships": { + "other": { + "data": { + "type": "second_test_entities", + "id": "2DF03B69-4B0A-467F-B52E-B0C9E44FCECF" + } + } + }, + "meta": { + "x": "world", + "y": 5 + }, + "links": { + "link1": "https://image.com/image.png" + } +} +""".data(using: .utf8)! + +let entity_some_relationships_some_attributes_with_links = """ +{ + "id": "90F03B69-4DF1-467F-B52E-B0C9E44FC333", + "type": "fourth_test_entities", + "attributes": { + "word": "coolio", + "number": 992299, + "array": [12.3, 4, 0.1] + }, + "relationships": { + "other": { + "data": { + "type": "second_test_entities", + "id": "2DF03B69-4B0A-467F-B52E-B0C9E44FCECF" + } + } + }, + "links": { + "link1": "https://image.com/image.png" + } +} +""".data(using: .utf8)! + +let entity_some_relationships_some_attributes_with_meta_and_links = """ +{ + "id": "90F03B69-4DF1-467F-B52E-B0C9E44FC333", + "type": "fourth_test_entities", + "attributes": { + "word": "coolio", + "number": 992299, + "array": [12.3, 4, 0.1] + }, + "relationships": { + "other": { + "data": { + "type": "second_test_entities", + "id": "2DF03B69-4B0A-467F-B52E-B0C9E44FCECF" + } + } + }, + "meta": { + "x": "world", + "y": 5 + }, + "links": { + "link1": "https://image.com/image.png" + } +} +""".data(using: .utf8)! + let entity_one_omitted_attribute = """ { "id": "1", @@ -241,3 +318,44 @@ let entity_unidentified_with_attributes = """ } } """.data(using: .utf8)! + +let entity_unidentified_with_attributes_and_meta = """ +{ + "type": "unidentified_test_entities", + "attributes": { + "me": "unknown" + }, + "meta": { + "x": "world", + "y": 5 + } +} +""".data(using: .utf8)! + +let entity_unidentified_with_attributes_and_links = """ +{ + "type": "unidentified_test_entities", + "attributes": { + "me": "unknown" + }, + "links": { + "link1": "https://image.com/image.png" + } +} +""".data(using: .utf8)! + +let entity_unidentified_with_attributes_and_meta_and_links = """ +{ + "type": "unidentified_test_entities", + "attributes": { + "me": "unknown" + }, + "meta": { + "x": "world", + "y": 5 + }, + "links": { + "link1": "https://image.com/image.png" + } +} +""".data(using: .utf8)!