mirror of
https://github.com/encounter/JSONAPI.git
synced 2026-03-30 11:18:38 -07:00
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.
This commit is contained in:
+2
-11
@@ -7,10 +7,7 @@
|
||||
|
||||
import JSONAPI
|
||||
|
||||
typealias StringId<E: EntityDescription> = Id<String, E>
|
||||
|
||||
enum PersonDescription: IdentifiedEntityDescription {
|
||||
typealias Identifier = Id<String, PersonDescription>
|
||||
enum PersonDescription: EntityDescription {
|
||||
|
||||
static var type: String { return "people" }
|
||||
|
||||
@@ -24,10 +21,4 @@ enum PersonDescription: IdentifiedEntityDescription {
|
||||
}
|
||||
}
|
||||
|
||||
typealias Person = Entity<PersonDescription>
|
||||
|
||||
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<PersonDescription, Id<String, PersonDescription>>
|
||||
|
||||
@@ -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<String, PersonDescription>
|
||||
|
||||
struct Attributes: JSONAPI.Attributes {
|
||||
let name: Attribute<[String]>
|
||||
let favoriteColor: Attribute<String>
|
||||
@@ -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<PersonDescription>`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<Description: JSONAPI.EntityDescription> = JSONAPI.Entity<Description, Id<String, Description>>
|
||||
|
||||
public typealias NewEntity<Description: JSONAPI.EntityDescription> = JSONAPI.Entity<Description, Unidentified>
|
||||
```
|
||||
|
||||
It can also be nice to create a `typealias` for each type of entity you want to work with:
|
||||
```
|
||||
typealias Person = Entity<PersonDescription>
|
||||
|
||||
typealias NewPerson = NewEntity<PersonDescription>
|
||||
```
|
||||
|
||||
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`.
|
||||
|
||||
@@ -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<Description: JSONAPI.EntityDescription>: EntityType {
|
||||
public typealias Identifier = Description.Identifier
|
||||
public struct Entity<Description: JSONAPI.EntityDescription, Identifier: JSONAPI.Identifier>: EntityType {
|
||||
|
||||
/// The JSON API compliant "type" of this `Entity`.
|
||||
public static var type: String { return Description.type }
|
||||
@@ -81,7 +63,7 @@ public struct Entity<Description: JSONAPI.EntityDescription>: 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<Description: JSONAPI.EntityDescription>: 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<Entity> {
|
||||
@@ -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 ~><OtherEntity: OptionalRelatable>(entity: Entity, path: KeyPath<Description.Relationships, ToOneRelationship<OtherEntity>>) -> OtherEntity.Identifier {
|
||||
public static func ~><OtherEntity: OptionalRelatable>(entity: Entity, path: KeyPath<Description.Relationships, ToOneRelationship<OtherEntity>>) -> 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)
|
||||
|
||||
|
||||
@@ -11,15 +11,21 @@
|
||||
/// A convenient typealias might make your code much more legible: `One<EntityDescription>`
|
||||
public struct ToOneRelationship<Relatable: JSONAPI.OptionalRelatable>: 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<Relatable.Description>) {
|
||||
extension ToOneRelationship where Relatable.WrappedIdentifier == Relatable.Identifier {
|
||||
public init(entity: Entity<Relatable.Description, Relatable.Identifier>) {
|
||||
id = entity.id
|
||||
}
|
||||
}
|
||||
|
||||
extension ToOneRelationship where Relatable.WrappedIdentifier == Optional<Relatable.Identifier> {
|
||||
public init(entity: Entity<Relatable.Description, Relatable.Identifier>) {
|
||||
id = entity.id
|
||||
}
|
||||
}
|
||||
@@ -32,7 +38,7 @@ public struct ToManyRelationship<Relatable: JSONAPI.Relatable>: Equatable, Codab
|
||||
|
||||
public let ids: [Relatable.Identifier]
|
||||
|
||||
public init<T: JSONAPI.Relatable>(relationships: [ToOneRelationship<T>]) where T.Identifier == Relatable.Identifier {
|
||||
public init<T: JSONAPI.Relatable>(relationships: [ToOneRelationship<T>]) where T.WrappedIdentifier == Relatable.Identifier {
|
||||
ids = relationships.map { $0.id }
|
||||
}
|
||||
|
||||
@@ -45,28 +51,33 @@ public struct ToManyRelationship<Relatable: JSONAPI.Relatable>: Equatable, Codab
|
||||
}
|
||||
}
|
||||
|
||||
extension ToManyRelationship where Relatable.Description.Identifier == Relatable.Identifier {
|
||||
public init(entities: [Entity<Relatable.Description>]) {
|
||||
extension ToManyRelationship {
|
||||
public init(entities: [Entity<Relatable.Description, Relatable.Identifier>]) {
|
||||
ids = entities.map { $0.id }
|
||||
}
|
||||
}
|
||||
|
||||
/// The OptionalRelatable protocol ONLY describes
|
||||
/// Optional<T: Relatable> types.
|
||||
public protocol OptionalRelatable: Codable, Equatable where Description.Identifier: IdType {
|
||||
/// The WrappedRelatable (a.k.a OptionalRelatable) protocol
|
||||
/// describes Optional<T: Relatable> 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 {
|
||||
|
||||
@@ -76,8 +76,7 @@ class DocumentTests: XCTestCase {
|
||||
|
||||
enum AuthorType: EntityDescription {
|
||||
static var type: String { return "authors" }
|
||||
|
||||
typealias Identifier = Id<String, AuthorType>
|
||||
|
||||
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<String, ArticleType>
|
||||
typealias Attributes = NoAttributes
|
||||
|
||||
struct Relationships: JSONAPI.Relationships {
|
||||
|
||||
@@ -232,8 +232,7 @@ extension EntityTests {
|
||||
|
||||
enum TestEntityType1: EntityDescription {
|
||||
static var type: String { return "test_entities"}
|
||||
|
||||
typealias Identifier = Id<String, TestEntityType1>
|
||||
|
||||
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<String, TestEntityType2>
|
||||
|
||||
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<String, TestEntityType3>
|
||||
|
||||
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<String, TestEntityType4>
|
||||
|
||||
struct Relationships: JSONAPI.Relationships {
|
||||
let other: ToOneRelationship<TestEntity2>
|
||||
}
|
||||
@@ -287,7 +282,6 @@ extension EntityTests {
|
||||
enum TestEntityType5: EntityDescription {
|
||||
static var type: String { return "fifth_test_entities"}
|
||||
|
||||
typealias Identifier = Id<String, TestEntityType5>
|
||||
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<String, TestEntityType6>
|
||||
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<String, TestEntityType7>
|
||||
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<String, TestEntityType8>
|
||||
|
||||
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<String, TestEntityType9>
|
||||
|
||||
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<String, TestEntityType10>
|
||||
|
||||
typealias Attributes = NoAttributes
|
||||
|
||||
public struct Relationships: JSONAPI.Relationships {
|
||||
@@ -384,14 +371,14 @@ extension EntityTests {
|
||||
|
||||
typealias TestEntity10 = Entity<TestEntityType10>
|
||||
|
||||
enum UnidentifiedTestEntityType: UnidentifiedEntityDescription {
|
||||
enum UnidentifiedTestEntityType: EntityDescription {
|
||||
public static var type: String { return "unidentified_test_entities" }
|
||||
|
||||
typealias Attributes = NoAttributes
|
||||
typealias Relationships = NoRelatives
|
||||
}
|
||||
|
||||
typealias UnidentifiedTestEntity = Entity<UnidentifiedTestEntityType>
|
||||
typealias UnidentifiedTestEntity = NewEntity<UnidentifiedTestEntityType>
|
||||
|
||||
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<EntityTests.TestEntity1>) {
|
||||
self.init(relationships: .init(other: other))
|
||||
}
|
||||
}
|
||||
|
||||
extension Entity where Description == EntityTests.TestEntityType3 {
|
||||
extension Entity where Description == EntityTests.TestEntityType3, Identifier: CreatableIdType {
|
||||
init(others: ToManyRelationship<EntityTests.TestEntity1>) {
|
||||
self.init(relationships: .init(others: others))
|
||||
}
|
||||
|
||||
@@ -120,40 +120,37 @@ class IncludedTests: XCTestCase {
|
||||
|
||||
extension IncludedTests {
|
||||
enum TestEntityType: EntityDescription {
|
||||
typealias Identifier = Id<String, TestEntityType>
|
||||
|
||||
|
||||
typealias Relationships = NoRelatives
|
||||
|
||||
|
||||
public static var type: String { return "test_entity1" }
|
||||
|
||||
|
||||
public struct Attributes: JSONAPI.Attributes {
|
||||
let foo: Attribute<String>
|
||||
let bar: Attribute<Int>
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
typealias TestEntity = Entity<TestEntityType>
|
||||
|
||||
|
||||
enum TestEntityType2: EntityDescription {
|
||||
typealias Identifier = Id<String, TestEntityType2>
|
||||
|
||||
|
||||
public static var type: String { return "test_entity2" }
|
||||
|
||||
|
||||
public struct Relationships: JSONAPI.Relationships {
|
||||
let entity1: ToOneRelationship<TestEntity>
|
||||
}
|
||||
|
||||
|
||||
public struct Attributes: JSONAPI.Attributes {
|
||||
let foo: Attribute<String>
|
||||
let bar: Attribute<Int>
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
typealias TestEntity2 = Entity<TestEntityType2>
|
||||
|
||||
|
||||
enum TestEntityType3: EntityDescription {
|
||||
typealias Identifier = Id<String, TestEntityType3>
|
||||
|
||||
|
||||
typealias Attributes = NoAttributes
|
||||
|
||||
public static var type: String { return "test_entity3" }
|
||||
@@ -163,44 +160,41 @@ extension IncludedTests {
|
||||
let entity2: ToManyRelationship<TestEntity2>
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
typealias TestEntity3 = Entity<TestEntityType3>
|
||||
|
||||
|
||||
enum TestEntityType4: EntityDescription {
|
||||
typealias Identifier = Id<String, TestEntityType4>
|
||||
|
||||
|
||||
typealias Attributes = NoAttributes
|
||||
|
||||
|
||||
typealias Relationships = NoRelatives
|
||||
|
||||
|
||||
public static var type: String { return "test_entity4" }
|
||||
}
|
||||
|
||||
|
||||
typealias TestEntity4 = Entity<TestEntityType4>
|
||||
|
||||
|
||||
enum TestEntityType5: EntityDescription {
|
||||
typealias Identifier = Id<String, TestEntityType5>
|
||||
|
||||
|
||||
typealias Attributes = NoAttributes
|
||||
|
||||
|
||||
typealias Relationships = NoRelatives
|
||||
|
||||
|
||||
public static var type: String { return "test_entity5" }
|
||||
}
|
||||
|
||||
|
||||
typealias TestEntity5 = Entity<TestEntityType5>
|
||||
|
||||
|
||||
enum TestEntityType6: EntityDescription {
|
||||
typealias Identifier = Id<String, TestEntityType6>
|
||||
|
||||
|
||||
typealias Attributes = NoAttributes
|
||||
|
||||
|
||||
public static var type: String { return "test_entity6" }
|
||||
|
||||
|
||||
struct Relationships: JSONAPI.Relationships {
|
||||
let entity4: ToOneRelationship<TestEntity4>
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
typealias TestEntity6 = Entity<TestEntityType6>
|
||||
}
|
||||
|
||||
@@ -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<TestEntity1>(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<TestEntity1>(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<TestEntity1>.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<TestEntity1>.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<String, TestEntityType1>
|
||||
|
||||
typealias Attributes = NoAttributes
|
||||
|
||||
|
||||
typealias Relationships = NoRelatives
|
||||
|
||||
|
||||
public static var type: String { return "test_entity1" }
|
||||
}
|
||||
|
||||
|
||||
typealias TestEntity1 = Entity<TestEntityType1>
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ class ResourceBodyTests: XCTestCase {
|
||||
|
||||
func test_singleResourceBody() {
|
||||
let body = try? JSONDecoder().decode(SingleResourceBody<Article>.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<String, ArticleType>(rawValue: "1"),
|
||||
attributes: ArticleType.Attributes(title: try! .init(rawValue: "JSON:API paints my bikeshed!"))))
|
||||
}
|
||||
|
||||
|
||||
func test_manyResourceBody() {
|
||||
let body = try? JSONDecoder().decode(ManyResourceBody<Article>.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<String, ArticleType>
|
||||
|
||||
typealias Relationships = NoRelatives
|
||||
|
||||
|
||||
struct Attributes: JSONAPI.Attributes {
|
||||
let title: Attribute<String>
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
typealias Article = Entity<ArticleType>
|
||||
}
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
//
|
||||
// Entity+Id.swift
|
||||
// JSONAPITests
|
||||
//
|
||||
// Created by Mathew Polzin on 11/15/18.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import JSONAPI
|
||||
|
||||
public typealias Entity<Description: JSONAPI.EntityDescription> = JSONAPI.Entity<Description, Id<String, Description>>
|
||||
|
||||
public typealias NewEntity<Description: JSONAPI.EntityDescription> = JSONAPI.Entity<Description, Unidentified>
|
||||
Reference in New Issue
Block a user