From 7dce7b2299c40e3efd57ad6408b6efaae6716282 Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Thu, 15 Nov 2018 23:02:58 -0800 Subject: [PATCH] Remove Id from EntityDescription, reducing the amount of repetetive and clunky boilerplate and allowing one simple typealias to remove Ids from consideration entirely when creating new Entity types. --- Examples.swift | 13 +--- README.md | 33 ++++++++-- Sources/JSONAPI/Resource/Entity.swift | 46 +++++--------- Sources/JSONAPI/Resource/Relationship.swift | 45 ++++++++------ .../JSONAPITests/Document/DocumentTests.swift | 4 +- Tests/JSONAPITests/Entity/EntityTests.swift | 29 +++------ .../JSONAPITests/Includes/IncludeTests.swift | 60 +++++++++---------- .../Relationships/RelationshipTests.swift | 30 +++++----- .../ResourceBody/ResourceBodyTests.swift | 17 +++--- .../JSONAPITests/Test Helpers/Entity+Id.swift | 13 ++++ 10 files changed, 143 insertions(+), 147 deletions(-) create mode 100644 Tests/JSONAPITests/Test Helpers/Entity+Id.swift diff --git a/Examples.swift b/Examples.swift index f36bc7e..957a989 100644 --- a/Examples.swift +++ b/Examples.swift @@ -7,10 +7,7 @@ import JSONAPI -typealias StringId = Id - -enum PersonDescription: IdentifiedEntityDescription { - typealias Identifier = Id +enum PersonDescription: EntityDescription { static var type: String { return "people" } @@ -24,10 +21,4 @@ enum PersonDescription: IdentifiedEntityDescription { } } -typealias Person = Entity - -func tmp() { - let person = Person(id: .init(rawValue: "33"), attributes: PersonDescription.Attributes(name: [], favoriteColor: "Green"), relationships: PersonDescription.Relationships(friends: .none)) - - print(person.pointer) -} +typealias Person = Entity> diff --git a/README.md b/README.md index d43dda5..20c6bbf 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,7 @@ The primary goals of this framework are: 1. Allow creation of Swift types that are easy to use in code but also can be encoded to- or decoded from *JSON API* compliant payloads without lots of boilerplate code. 2. Leverage `Codable` to avoid additional outside dependencies and get operability with non-JSON encoders/decoders for free. 3. Do not sacrifice type safety. +4. Be platform agnostic so that Swift code can be written once and used by both the client and the server. ## Project Status @@ -90,8 +91,6 @@ An `EntityDescription` is the `JSONAPI` framework's specification for what the J enum PersonDescription: IdentifiedEntityDescription { static var type: String { return "people" } - typealias Identifier = Id - struct Attributes: JSONAPI.Attributes { let name: Attribute<[String]> let favoriteColor: Attribute @@ -103,7 +102,12 @@ enum PersonDescription: IdentifiedEntityDescription { } ``` -Note that an `enum` type is used here; it could have been a `struct`, but `EntityDescription`s do not ever need to be created so an `enum` with no `case`s is a nice fit for the job. +To enumerate them, the requirements of an `EntityDescription` are +1. A static `var` "type" that matches the JSON type; The JSON spec requires every *Resource Object* to have a "type". +2. A `struct` of `Attributes` **- OR -** `typealias Attributes = NoAttributes` +3. A `struct` of `Relationships` **- OR -** `typealias Relationships = NoRelatives` + +Note that an `enum` type is used here for the `EntityDescription`; it could have been a `struct`, but `EntityDescription`s do not ever need to be created so an `enum` with no `case`s is a nice fit for the job. This readme doesn't go into detail on the JSON API Spec, but the following JSON API *Resource Object* would be described by the above `PersonDescription`: @@ -137,15 +141,34 @@ This readme doesn't go into detail on the JSON API Spec, but the following JSON ### `Entity` -Once you have an `EntityDescription`, you _create_, _encode_, and _decode_ `Entity`s that "fit the description". If you have a `CreatableRawIdType` (see the section on `RawIdType`s below) then you can create new `Entity`s, but even without a `CreatableRawIdType` you can encode, decode and work with entities. +Once you have an `EntityDescription`, you _create_, _encode_, and _decode_ `Entity`s that "fit the description". If you have a `CreatableRawIdType` (see the section on `RawIdType`s below) then you can create new `Entity`s that will automatically be given unique Ids, but even without a `CreatableRawIdType` you can encode, decode and work with entities. The `Entity` and `EntityDescription` together embody the rules and properties of a JSON API *Resource Object*. -It can be nice to create a `typealias` for each type of entity you want to work with: +An `Entity` needs to be specialized on two generic types. The first is the `EntityDescription` described above. The second is the type of Id to use for the `Entity`. + +#### `IdType` + +An `IdType` packages up two pieces of information: A unique identifier of a given `RawIdType` and the `EntityDescription` of the type of entity the Id identifies. Having the `EntityDescription` type associated with the Id makes it easy to store all of your entities in a local hash broken out by `EntityDescription`; You can pass Ids around and always know where to look for the `Entity` to which the Id refers. `RawIdType`s are documented below. + +#### Convenient `typealiases` + +Often you can use one `RawIdType` for many if not all of your `Entities`. That means you can save yourself some boilerplate by using a `typealias`es like the following: +``` +public typealias Entity = JSONAPI.Entity> + +public typealias NewEntity = JSONAPI.Entity +``` + +It can also be nice to create a `typealias` for each type of entity you want to work with: ``` typealias Person = Entity + +typealias NewPerson = NewEntity ``` +Note that I am assuming an unidentified person is a "new" person. I suspect that is generally an acceptable conflation because the only time JSON API spec allows a *Resource Object* to be encoded without an Id is when a client is requesting the given *Resource Object* be created by the server and the client wants the server to create the Id for that object. + ### `Relationships` There are two types of `Relationship`s: `ToOneRelationship` and `ToManyRelationship`. An `EntityDescription`'s `Relationships` type can contain any number of `Relationship`s of either of these types. Do not store anything other than `Relationship`s in the `Relationships` struct of an `EntityDescription`. diff --git a/Sources/JSONAPI/Resource/Entity.swift b/Sources/JSONAPI/Resource/Entity.swift index 26cecd2..fee00dd 100644 --- a/Sources/JSONAPI/Resource/Entity.swift +++ b/Sources/JSONAPI/Resource/Entity.swift @@ -28,29 +28,12 @@ public struct NoAttributes: Attributes {} /// `Entity`, which gets specialized on an /// `EntityDescription`. public protocol EntityDescription { - associatedtype Identifier: JSONAPI.Identifier associatedtype Attributes: JSONAPI.Attributes associatedtype Relationships: JSONAPI.Relationships static var type: String { get } } -/// Shorthand for an `EntityDescription` that has an Id. -/// The only times you would not want an `EntityDescription` -/// to not be an `IdentifiedEntityDescription` are when you -/// are a client making a request to create a new `Entity` -/// or you are a server receiving a request to create a -/// new `Entity`. -public protocol IdentifiedEntityDescription: EntityDescription where Identifier: IdType {} - -/// Shorthand for an `EntityDescription` that does not have an Id. -/// The only times you would not want an `EntityDescription` -/// to not be an `IdentifiedEntityDescription` are when you -/// are a client making a request to create a new `Entity` -/// or you are a server receiving a request to create a -/// new `Entity`. -public protocol UnidentifiedEntityDescription: EntityDescription where Identifier == Unidentified {} - /// EntityType is the protocol that Entity conforms to. This /// protocol lets other types accept any Entity as a generic /// specialization. @@ -63,8 +46,7 @@ public protocol EntityType: Codable, Equatable { /// encoded to or decoded from a JSON API /// "Resource Object." /// See https://jsonapi.org/format/#document-resource-objects -public struct Entity: EntityType { - public typealias Identifier = Description.Identifier +public struct Entity: EntityType { /// The JSON API compliant "type" of this `Entity`. public static var type: String { return Description.type } @@ -81,7 +63,7 @@ public struct Entity: EntityType { /// The JSON API compliant relationships of this `Entity`. public let relationships: Description.Relationships - public init(id: Description.Identifier, attributes: Description.Attributes, relationships: Description.Relationships) { + public init(id: Identifier, attributes: Description.Attributes, relationships: Description.Relationships) { self.id = id self.attributes = attributes self.relationships = relationships @@ -89,52 +71,52 @@ public struct Entity: EntityType { } // MARK: Convenience initializers -extension Entity where Description.Identifier: CreatableIdType { +extension Entity where Identifier: CreatableIdType { public init(attributes: Description.Attributes, relationships: Description.Relationships) { - self.id = Description.Identifier() + self.id = Identifier() self.attributes = attributes self.relationships = relationships } } extension Entity where Description.Attributes == NoAttributes { - public init(id: Description.Identifier, relationships: Description.Relationships) { + public init(id: Identifier, relationships: Description.Relationships) { self.init(id: id, attributes: NoAttributes(), relationships: relationships) } } -extension Entity where Description.Attributes == NoAttributes, Description.Identifier: CreatableIdType { +extension Entity where Description.Attributes == NoAttributes, Identifier: CreatableIdType { public init(relationships: Description.Relationships) { self.init(attributes: NoAttributes(), relationships: relationships) } } extension Entity where Description.Relationships == NoRelatives { - public init(id: Description.Identifier, attributes: Description.Attributes) { + public init(id: Identifier, attributes: Description.Attributes) { self.init(id: id, attributes: attributes, relationships: NoRelatives()) } } -extension Entity where Description.Relationships == NoRelatives, Description.Identifier: CreatableIdType { +extension Entity where Description.Relationships == NoRelatives, Identifier: CreatableIdType { public init(attributes: Description.Attributes) { self.init(attributes: attributes, relationships: NoRelatives()) } } extension Entity where Description.Attributes == NoAttributes, Description.Relationships == NoRelatives { - public init(id: Description.Identifier) { + public init(id: Identifier) { self.init(id: id, attributes: NoAttributes(), relationships: NoRelatives()) } } -extension Entity where Description.Attributes == NoAttributes, Description.Relationships == NoRelatives, Description.Identifier: CreatableIdType { +extension Entity where Description.Attributes == NoAttributes, Description.Relationships == NoRelatives, Identifier: CreatableIdType { public init() { self.init(attributes: NoAttributes(), relationships: NoRelatives()) } } // MARK: Pointer for Relationships use. -public extension Entity where Description.Identifier: IdType { +public extension Entity where Identifier: IdType { /// Get a pointer to this entity that can be used as a /// relationship to another entity. public var pointer: ToOneRelationship { @@ -171,7 +153,7 @@ public extension Entity { /// Access to an Id of a `ToOneRelationship`. /// This allows you to write `entity ~> \.other` instead /// of `entity.relationships.other.id`. - public static func ~>(entity: Entity, path: KeyPath>) -> OtherEntity.Identifier { + public static func ~>(entity: Entity, path: KeyPath>) -> OtherEntity.WrappedIdentifier { return entity.relationships[keyPath: path].id } @@ -199,7 +181,7 @@ public extension Entity { try container.encode(Entity.type, forKey: .type) - if Description.Identifier.self != Unidentified.self { + if Identifier.self != Unidentified.self { try container.encode(id, forKey: .id) } @@ -222,7 +204,7 @@ public extension Entity { throw JSONAPIEncodingError.typeMismatch(expected: Description.type, found: type) } - id = try (Unidentified() as? Description.Identifier) ?? container.decode(Description.Identifier.self, forKey: .id) + id = try (Unidentified() as? Identifier) ?? container.decode(Identifier.self, forKey: .id) attributes = try (NoAttributes() as? Description.Attributes) ?? container.decode(Description.Attributes.self, forKey: .attributes) diff --git a/Sources/JSONAPI/Resource/Relationship.swift b/Sources/JSONAPI/Resource/Relationship.swift index abaabf4..cf5ef7b 100644 --- a/Sources/JSONAPI/Resource/Relationship.swift +++ b/Sources/JSONAPI/Resource/Relationship.swift @@ -11,15 +11,21 @@ /// A convenient typealias might make your code much more legible: `One` public struct ToOneRelationship: Equatable, Codable { - public let id: Relatable.Identifier + public let id: Relatable.WrappedIdentifier - public var ids: [Relatable.Identifier] { + public var ids: [Relatable.WrappedIdentifier] { return [id] } } -extension ToOneRelationship where Relatable.Description.Identifier == Relatable.Identifier { - public init(entity: Entity) { +extension ToOneRelationship where Relatable.WrappedIdentifier == Relatable.Identifier { + public init(entity: Entity) { + id = entity.id + } +} + +extension ToOneRelationship where Relatable.WrappedIdentifier == Optional { + public init(entity: Entity) { id = entity.id } } @@ -32,7 +38,7 @@ public struct ToManyRelationship: Equatable, Codab public let ids: [Relatable.Identifier] - public init(relationships: [ToOneRelationship]) where T.Identifier == Relatable.Identifier { + public init(relationships: [ToOneRelationship]) where T.WrappedIdentifier == Relatable.Identifier { ids = relationships.map { $0.id } } @@ -45,28 +51,33 @@ public struct ToManyRelationship: Equatable, Codab } } -extension ToManyRelationship where Relatable.Description.Identifier == Relatable.Identifier { - public init(entities: [Entity]) { +extension ToManyRelationship { + public init(entities: [Entity]) { ids = entities.map { $0.id } } } -/// The OptionalRelatable protocol ONLY describes -/// Optional types. -public protocol OptionalRelatable: Codable, Equatable where Description.Identifier: IdType { +/// The WrappedRelatable (a.k.a OptionalRelatable) protocol +/// describes Optional and Relatable types. +public protocol WrappedRelatable: Codable, Equatable { associatedtype Description: EntityDescription - associatedtype Identifier: Equatable & Codable + associatedtype Identifier: JSONAPI.IdType + associatedtype WrappedIdentifier: Codable, Equatable } +public typealias OptionalRelatable = WrappedRelatable /// The Relatable protocol describes anything that -/// has an EntityDescription -public protocol Relatable: OptionalRelatable {} +/// has an IdType Identifier +public protocol Relatable: WrappedRelatable {} -extension Entity: Relatable, OptionalRelatable where Description.Identifier: IdType {} +extension Entity: Relatable, WrappedRelatable where Identifier: JSONAPI.IdType { + public typealias WrappedIdentifier = Identifier +} extension Optional: OptionalRelatable where Wrapped: Relatable { public typealias Description = Wrapped.Description - public typealias Identifier = Wrapped.Description.Identifier? + public typealias Identifier = Wrapped.Identifier + public typealias WrappedIdentifier = Identifier? } // MARK: Codable @@ -93,7 +104,7 @@ extension ToOneRelationship { // type at which point we can store nil in `id`. let anyNil: Any? = nil if try container.decodeNil(forKey: .data), - let val = anyNil as? Relatable.Identifier { + let val = anyNil as? Relatable.WrappedIdentifier { id = val return } @@ -106,7 +117,7 @@ extension ToOneRelationship { throw JSONAPIEncodingError.typeMismatch(expected: Relatable.Description.type, found: type) } - id = try identifier.decode(Relatable.Identifier.self, forKey: .id) + id = try identifier.decode(Relatable.WrappedIdentifier.self, forKey: .id) } public func encode(to encoder: Encoder) throws { diff --git a/Tests/JSONAPITests/Document/DocumentTests.swift b/Tests/JSONAPITests/Document/DocumentTests.swift index f3c3650..a4bfd67 100644 --- a/Tests/JSONAPITests/Document/DocumentTests.swift +++ b/Tests/JSONAPITests/Document/DocumentTests.swift @@ -76,8 +76,7 @@ class DocumentTests: XCTestCase { enum AuthorType: EntityDescription { static var type: String { return "authors" } - - typealias Identifier = Id + typealias Attributes = NoAttributes typealias Relationships = NoRelatives } @@ -87,7 +86,6 @@ class DocumentTests: XCTestCase { enum ArticleType: EntityDescription { static var type: String { return "articles" } - typealias Identifier = Id typealias Attributes = NoAttributes struct Relationships: JSONAPI.Relationships { diff --git a/Tests/JSONAPITests/Entity/EntityTests.swift b/Tests/JSONAPITests/Entity/EntityTests.swift index c81e817..04fc193 100644 --- a/Tests/JSONAPITests/Entity/EntityTests.swift +++ b/Tests/JSONAPITests/Entity/EntityTests.swift @@ -232,8 +232,7 @@ extension EntityTests { enum TestEntityType1: EntityDescription { static var type: String { return "test_entities"} - - typealias Identifier = Id + typealias Attributes = NoAttributes typealias Relationships = NoRelatives } @@ -242,8 +241,7 @@ extension EntityTests { enum TestEntityType2: EntityDescription { static var type: String { return "second_test_entities"} - - typealias Identifier = Id + typealias Attributes = NoAttributes struct Relationships: JSONAPI.Relationships { @@ -255,8 +253,7 @@ extension EntityTests { enum TestEntityType3: EntityDescription { static var type: String { return "third_test_entities"} - - typealias Identifier = Id + typealias Attributes = NoAttributes struct Relationships: JSONAPI.Relationships { @@ -269,8 +266,6 @@ extension EntityTests { enum TestEntityType4: EntityDescription { static var type: String { return "fourth_test_entities"} - typealias Identifier = Id - struct Relationships: JSONAPI.Relationships { let other: ToOneRelationship } @@ -287,7 +282,6 @@ extension EntityTests { enum TestEntityType5: EntityDescription { static var type: String { return "fifth_test_entities"} - typealias Identifier = Id typealias Relationships = NoRelatives struct Attributes: JSONAPI.Attributes { @@ -300,7 +294,6 @@ extension EntityTests { enum TestEntityType6: EntityDescription { static var type: String { return "sixth_test_entities" } - typealias Identifier = Id typealias Relationships = NoRelatives struct Attributes: JSONAPI.Attributes { @@ -315,7 +308,6 @@ extension EntityTests { enum TestEntityType7: EntityDescription { static var type: String { return "seventh_test_entities" } - typealias Identifier = Id typealias Relationships = NoRelatives struct Attributes: JSONAPI.Attributes { @@ -328,8 +320,7 @@ extension EntityTests { enum TestEntityType8: EntityDescription { static var type: String { return "eighth_test_entities" } - - typealias Identifier = Id + typealias Relationships = NoRelatives struct Attributes: JSONAPI.Attributes { @@ -348,8 +339,6 @@ extension EntityTests { enum TestEntityType9: EntityDescription { public static var type: String { return "ninth_test_entities" } - typealias Identifier = Id - typealias Attributes = NoAttributes public struct Relationships: JSONAPI.Relationships { @@ -372,8 +361,6 @@ extension EntityTests { enum TestEntityType10: EntityDescription { public static var type: String { return "tenth_test_entities" } - typealias Identifier = Id - typealias Attributes = NoAttributes public struct Relationships: JSONAPI.Relationships { @@ -384,14 +371,14 @@ extension EntityTests { typealias TestEntity10 = Entity - enum UnidentifiedTestEntityType: UnidentifiedEntityDescription { + enum UnidentifiedTestEntityType: EntityDescription { public static var type: String { return "unidentified_test_entities" } typealias Attributes = NoAttributes typealias Relationships = NoRelatives } - typealias UnidentifiedTestEntity = Entity + typealias UnidentifiedTestEntity = NewEntity enum IntToString: Transformer { public static func transform(_ from: Int) -> String { @@ -418,13 +405,13 @@ extension EntityTests { } } -extension Entity where Description == EntityTests.TestEntityType2 { +extension Entity where Description == EntityTests.TestEntityType2, Identifier: CreatableIdType { init(other: ToOneRelationship) { self.init(relationships: .init(other: other)) } } -extension Entity where Description == EntityTests.TestEntityType3 { +extension Entity where Description == EntityTests.TestEntityType3, Identifier: CreatableIdType { init(others: ToManyRelationship) { self.init(relationships: .init(others: others)) } diff --git a/Tests/JSONAPITests/Includes/IncludeTests.swift b/Tests/JSONAPITests/Includes/IncludeTests.swift index fe9295f..83fdc20 100644 --- a/Tests/JSONAPITests/Includes/IncludeTests.swift +++ b/Tests/JSONAPITests/Includes/IncludeTests.swift @@ -120,40 +120,37 @@ class IncludedTests: XCTestCase { extension IncludedTests { enum TestEntityType: EntityDescription { - typealias Identifier = Id - + typealias Relationships = NoRelatives - + public static var type: String { return "test_entity1" } - + public struct Attributes: JSONAPI.Attributes { let foo: Attribute let bar: Attribute } } - + typealias TestEntity = Entity - + enum TestEntityType2: EntityDescription { - typealias Identifier = Id - + public static var type: String { return "test_entity2" } - + public struct Relationships: JSONAPI.Relationships { let entity1: ToOneRelationship } - + public struct Attributes: JSONAPI.Attributes { let foo: Attribute let bar: Attribute } } - + typealias TestEntity2 = Entity - + enum TestEntityType3: EntityDescription { - typealias Identifier = Id - + typealias Attributes = NoAttributes public static var type: String { return "test_entity3" } @@ -163,44 +160,41 @@ extension IncludedTests { let entity2: ToManyRelationship } } - + typealias TestEntity3 = Entity - + enum TestEntityType4: EntityDescription { - typealias Identifier = Id - + typealias Attributes = NoAttributes - + typealias Relationships = NoRelatives - + public static var type: String { return "test_entity4" } } - + typealias TestEntity4 = Entity - + enum TestEntityType5: EntityDescription { - typealias Identifier = Id - + typealias Attributes = NoAttributes - + typealias Relationships = NoRelatives - + public static var type: String { return "test_entity5" } } - + typealias TestEntity5 = Entity - + enum TestEntityType6: EntityDescription { - typealias Identifier = Id - + typealias Attributes = NoAttributes - + public static var type: String { return "test_entity6" } - + struct Relationships: JSONAPI.Relationships { let entity4: ToOneRelationship } } - + typealias TestEntity6 = Entity } diff --git a/Tests/JSONAPITests/Relationships/RelationshipTests.swift b/Tests/JSONAPITests/Relationships/RelationshipTests.swift index e72d830..4b142f6 100644 --- a/Tests/JSONAPITests/Relationships/RelationshipTests.swift +++ b/Tests/JSONAPITests/Relationships/RelationshipTests.swift @@ -9,55 +9,53 @@ import XCTest import JSONAPI class RelationshipTests: XCTestCase { - + func test_initToManyWithEntities() { let entity1 = TestEntity1() let entity2 = TestEntity1() let entity3 = TestEntity1() let entity4 = TestEntity1() let relationship = ToManyRelationship(entities: [entity1, entity2, entity3, entity4]) - + XCTAssertEqual(relationship.ids.count, 4) XCTAssertEqual(relationship.ids, [entity1, entity2, entity3, entity4].map { $0.id }) } - + func test_initToManyWithRelationships() { let entity1 = TestEntity1() let entity2 = TestEntity1() let entity3 = TestEntity1() let entity4 = TestEntity1() let relationship = ToManyRelationship(relationships: [entity1.pointer, entity2.pointer, entity3.pointer, entity4.pointer]) - + XCTAssertEqual(relationship.ids.count, 4) XCTAssertEqual(relationship.ids, [entity1, entity2, entity3, entity4].map { $0.id }) } - + func test_ToOneRelationship() { let relationship = try? JSONDecoder().decode(ToOneRelationship.self, from: to_one_relationship) - + XCTAssertNotNil(relationship) - + XCTAssertEqual(relationship?.id.rawValue, "2DF03B69-4B0A-467F-B52E-B0C9E44FCECF") XCTAssertEqual(relationship?.ids.count, 1) } - + func test_ToManyRelationship() { let relationship = try? JSONDecoder().decode(ToManyRelationship.self, from: to_many_relationship) - + XCTAssertNotNil(relationship) - + XCTAssertEqual(relationship?.ids.map { $0.rawValue }, ["2DF03B69-4B0A-467F-B52E-B0C9E44FCECF", "90F03B69-4DF1-467F-B52E-B0C9E44FC333", "2DF03B69-4B0A-467F-B52E-B0C9E44FCECF"]) } - + enum TestEntityType1: EntityDescription { - typealias Identifier = Id - typealias Attributes = NoAttributes - + typealias Relationships = NoRelatives - + public static var type: String { return "test_entity1" } } - + typealias TestEntity1 = Entity } diff --git a/Tests/JSONAPITests/ResourceBody/ResourceBodyTests.swift b/Tests/JSONAPITests/ResourceBody/ResourceBodyTests.swift index dceae88..a121bfe 100644 --- a/Tests/JSONAPITests/ResourceBody/ResourceBodyTests.swift +++ b/Tests/JSONAPITests/ResourceBody/ResourceBodyTests.swift @@ -12,7 +12,7 @@ class ResourceBodyTests: XCTestCase { func test_singleResourceBody() { let body = try? JSONDecoder().decode(SingleResourceBody
.self, from: single_resource_body) - + XCTAssertNotNil(body) guard let b = body else { return } @@ -20,14 +20,14 @@ class ResourceBodyTests: XCTestCase { XCTAssertEqual(b.value, Article(id: Id(rawValue: "1"), attributes: ArticleType.Attributes(title: try! .init(rawValue: "JSON:API paints my bikeshed!")))) } - + func test_manyResourceBody() { let body = try? JSONDecoder().decode(ManyResourceBody
.self, from: many_resource_body) - + XCTAssertNotNil(body) - + guard let b = body else { return } - + XCTAssertEqual(b.values, [ Article(id: .init(rawValue: "1"), attributes: try! .init(title: .init(rawValue: "JSON:API paints my bikeshed!"))), Article(id: .init(rawValue: "2"), attributes: try! .init(title: .init(rawValue: "Sick"))), @@ -37,14 +37,13 @@ class ResourceBodyTests: XCTestCase { enum ArticleType: EntityDescription { public static var type: String { return "articles" } - - typealias Identifier = Id + typealias Relationships = NoRelatives - + struct Attributes: JSONAPI.Attributes { let title: Attribute } } - + typealias Article = Entity } diff --git a/Tests/JSONAPITests/Test Helpers/Entity+Id.swift b/Tests/JSONAPITests/Test Helpers/Entity+Id.swift new file mode 100644 index 0000000..a0422ca --- /dev/null +++ b/Tests/JSONAPITests/Test Helpers/Entity+Id.swift @@ -0,0 +1,13 @@ +// +// Entity+Id.swift +// JSONAPITests +// +// Created by Mathew Polzin on 11/15/18. +// + +import Foundation +import JSONAPI + +public typealias Entity = JSONAPI.Entity> + +public typealias NewEntity = JSONAPI.Entity