Add some more documentation, remove unneded Foundation imports.

This commit is contained in:
Mathew Polzin
2018-11-13 01:08:23 -08:00
parent 2179f23dc7
commit ec37902819
20 changed files with 393 additions and 175 deletions
-2
View File
@@ -5,8 +5,6 @@
// Created by Mathew Polzin on 11/5/18.
//
import Foundation
/// A JSON API Document represents the entire body
/// of a JSON API request or the entire body of
/// a JSON API response.
-2
View File
@@ -5,8 +5,6 @@
// Created by Mathew Polzin on 11/10/18.
//
import Foundation
public protocol JSONAPIError: Swift.Error {
static var unknown: Self { get }
}
+13 -14
View File
@@ -5,7 +5,6 @@
// Created by Mathew Polzin on 11/10/18.
//
import Foundation
import Result
public protocol IncludeDecoder: Decodable {}
@@ -37,7 +36,7 @@ public struct Includes<I: IncludeDecoder>: Decodable {
// MARK: - Decoding
func decode<EntityType: JSONAPI.EntityType>(_ type: EntityType.Type, from container: SingleValueDecodingContainer) throws -> Result<Entity<EntityType>, EncodingError> {
func decode<EntityType: JSONAPI.EntityDescription>(_ type: EntityType.Type, from container: SingleValueDecodingContainer) throws -> Result<Entity<EntityType>, EncodingError> {
let ret: Result<Entity<EntityType>, EncodingError>
do {
ret = try .success(container.decode(Entity<EntityType>.self))
@@ -64,10 +63,10 @@ public typealias NoIncludes = Include0
// MARK: - 1 include
public protocol _Include1: _Include0 {
associatedtype A: EntityType
associatedtype A: EntityDescription
var a: Entity<A>? { get }
}
public enum Include1<A: EntityType>: _Include1 {
public enum Include1<A: EntityDescription>: _Include1 {
case a(Entity<A>)
public var a: Entity<A>? {
@@ -94,10 +93,10 @@ extension Includes where I: _Include1 {
// MARK: - 2 includes
public protocol _Include2: _Include1 {
associatedtype B: EntityType
associatedtype B: EntityDescription
var b: Entity<B>? { get }
}
public enum Include2<A: EntityType, B: EntityType>: _Include2 {
public enum Include2<A: EntityDescription, B: EntityDescription>: _Include2 {
case a(Entity<A>)
case b(Entity<B>)
@@ -142,10 +141,10 @@ extension Includes where I: _Include2 {
// MARK: - 3 includes
public protocol _Include3: _Include2 {
associatedtype C: EntityType
associatedtype C: EntityDescription
var c: Entity<C>? { get }
}
public enum Include3<A: EntityType, B: EntityType, C: EntityType>: _Include3 {
public enum Include3<A: EntityDescription, B: EntityDescription, C: EntityDescription>: _Include3 {
case a(Entity<A>)
case b(Entity<B>)
case c(Entity<C>)
@@ -197,10 +196,10 @@ extension Includes where I: _Include3 {
// MARK: - 4 includes
public protocol _Include4: _Include3 {
associatedtype D: EntityType
associatedtype D: EntityDescription
var d: Entity<D>? { get }
}
public enum Include4<A: EntityType, B: EntityType, C: EntityType, D: EntityType>: _Include4 {
public enum Include4<A: EntityDescription, B: EntityDescription, C: EntityDescription, D: EntityDescription>: _Include4 {
case a(Entity<A>)
case b(Entity<B>)
case c(Entity<C>)
@@ -259,10 +258,10 @@ extension Includes where I: _Include4 {
// MARK: - 5 includes
public protocol _Include5: _Include4 {
associatedtype E: EntityType
associatedtype E: EntityDescription
var e: Entity<E>? { get }
}
public enum Include5<A: EntityType, B: EntityType, C: EntityType, D: EntityType, E: EntityType>: _Include5 {
public enum Include5<A: EntityDescription, B: EntityDescription, C: EntityDescription, D: EntityDescription, E: EntityDescription>: _Include5 {
case a(Entity<A>)
case b(Entity<B>)
case c(Entity<C>)
@@ -328,10 +327,10 @@ extension Includes where I: _Include5 {
// MARK: - 6 includes
public protocol _Include6: _Include5 {
associatedtype F: EntityType
associatedtype F: EntityDescription
var f: Entity<F>? { get }
}
public enum Include6<A: EntityType, B: EntityType, C: EntityType, D: EntityType, E: EntityType, F: EntityType>: _Include6 {
public enum Include6<A: EntityDescription, B: EntityDescription, C: EntityDescription, D: EntityDescription, E: EntityDescription, F: EntityDescription>: _Include6 {
case a(Entity<A>)
case b(Entity<B>)
case c(Entity<C>)
+4 -6
View File
@@ -5,18 +5,16 @@
// Created by Mathew Polzin on 11/10/18.
//
import Foundation
public protocol ResourceBody: Decodable {
typealias Single<EntityType: JSONAPI.EntityType> = SingleResourceBody<EntityType>
typealias Many<EntityType: JSONAPI.EntityType> = ManyResourceBody<EntityType>
typealias Single<EntityType: JSONAPI.EntityDescription> = SingleResourceBody<EntityType>
typealias Many<EntityType: JSONAPI.EntityDescription> = ManyResourceBody<EntityType>
}
public struct SingleResourceBody<EntityType: JSONAPI.EntityType>: ResourceBody {
public struct SingleResourceBody<EntityType: JSONAPI.EntityDescription>: ResourceBody {
public let value: Entity<EntityType>?
}
public struct ManyResourceBody<EntityType: JSONAPI.EntityType>: ResourceBody {
public struct ManyResourceBody<EntityType: JSONAPI.EntityDescription>: ResourceBody {
public let values: [Entity<EntityType>]
}
+59 -40
View File
@@ -22,40 +22,42 @@ public struct NoRelatives: Relationships {}
/// have any Attributes.
public struct NoAttributes: Attributes {}
/// An `EntityType` describes a JSON API
/// An `EntityDescription` describes a JSON API
/// Resource Object. The Resource Object
/// itself is encoded and decoded as an
/// `Entity`, which gets specialized on an
/// `EntityType`.
public protocol EntityType {
/// `EntityDescription`.
public protocol EntityDescription {
associatedtype Identifier: JSONAPI.Identifier
associatedtype AttributeType: Attributes
associatedtype RelatedType: Relationships
associatedtype Attributes: JSONAPI.Attributes
associatedtype Relationships: JSONAPI.Relationships
static var type: String { get }
}
/// Shorthand for an `EntityType` that has an Id.
/// The only times you would not want an `EntityType`
/// to not be an `IdentifiedEntityType` are when you
/// 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 IdentifiedEntityType: EntityType where Identifier: IdType {}
public protocol IdentifiedEntityDescription: EntityDescription where Identifier: IdType {}
/// Shorthand for an `EntityType` that does not have an Id.
/// The only times you would not want an `EntityType`
/// to not be an `IdentifiedEntityType` are when you
/// 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 UnidentifiedEntityType: EntityType where Identifier == Unidentified {}
public protocol UnidentifiedEntityDescription: EntityDescription where Identifier == Unidentified {}
/// An `Entity` is a single model type that can be
/// encoded to or decoded from a JSON API
/// "Resource Object."
/// See https://jsonapi.org/format/#document-resource-objects
public struct Entity<EntityType: JSONAPI.EntityType>: Codable, Equatable {
public struct Entity<EntityType: JSONAPI.EntityDescription>: Codable, Equatable {
public typealias Identifier = EntityType.Identifier
/// The JSON API compliant "type" of this `Entity`.
public static var type: String { return EntityType.type }
@@ -63,59 +65,67 @@ public struct Entity<EntityType: JSONAPI.EntityType>: Codable, Equatable {
/// the entity is being created clientside and the
/// server is being asked to create a unique Id. Otherwise,
/// this should be of a type conforming to `IdType`.
public let id: EntityType.Identifier
public let id: Identifier
/// The JSON API compliant attributes of this `Entity`.
public let attributes: EntityType.AttributeType
public let attributes: EntityType.Attributes
/// The JSON API compliant relationships of this `Entity`.
public let relationships: EntityType.RelatedType
public let relationships: EntityType.Relationships
public init(id: EntityType.Identifier, attributes: EntityType.AttributeType, relationships: EntityType.RelatedType) {
public init(id: EntityType.Identifier, attributes: EntityType.Attributes, relationships: EntityType.Relationships) {
self.id = id
self.attributes = attributes
self.relationships = relationships
}
public init(attributes: EntityType.AttributeType, relationships: EntityType.RelatedType) {
self.id = .init()
}
// MARK: Convenience initializers
extension Entity where EntityType.Identifier: CreatableIdType {
public init(attributes: EntityType.Attributes, relationships: EntityType.Relationships) {
self.id = EntityType.Identifier()
self.attributes = attributes
self.relationships = relationships
}
}
extension Entity where EntityType.AttributeType == NoAttributes {
public init(id: EntityType.Identifier, relationships: EntityType.RelatedType) {
extension Entity where EntityType.Attributes == NoAttributes {
public init(id: EntityType.Identifier, relationships: EntityType.Relationships) {
self.init(id: id, attributes: NoAttributes(), relationships: relationships)
}
public init(relationships: EntityType.RelatedType) {
}
extension Entity where EntityType.Attributes == NoAttributes, EntityType.Identifier: CreatableIdType {
public init(relationships: EntityType.Relationships) {
self.init(attributes: NoAttributes(), relationships: relationships)
}
}
extension Entity where EntityType.RelatedType == NoRelatives {
public init(id: EntityType.Identifier, attributes: EntityType.AttributeType) {
extension Entity where EntityType.Relationships == NoRelatives {
public init(id: EntityType.Identifier, attributes: EntityType.Attributes) {
self.init(id: id, attributes: attributes, relationships: NoRelatives())
}
public init(attributes: EntityType.AttributeType) {
}
extension Entity where EntityType.Relationships == NoRelatives, EntityType.Identifier: CreatableIdType {
public init(attributes: EntityType.Attributes) {
self.init(attributes: attributes, relationships: NoRelatives())
}
}
extension Entity where EntityType.AttributeType == NoAttributes, EntityType.RelatedType == NoRelatives {
extension Entity where EntityType.Attributes == NoAttributes, EntityType.Relationships == NoRelatives {
public init(id: EntityType.Identifier) {
self.init(id: id, attributes: NoAttributes(), relationships: NoRelatives())
}
}
extension Entity where EntityType.Attributes == NoAttributes, EntityType.Relationships == NoRelatives, EntityType.Identifier: CreatableIdType {
public init() {
self.init(attributes: NoAttributes(), relationships: NoRelatives())
}
}
//public protocol IdentifiedEntityType: JSONAPI.EntityType where IdentifiedEntityType.Identifier: IdType, Identifier.Entity == Self {}
// MARK: Pointer for Relationships use.
public extension Entity where EntityType.Identifier: IdType {
/// Get a pointer to this entity that can be used as a
/// relationship to another entity.
@@ -126,18 +136,27 @@ public extension Entity where EntityType.Identifier: IdType {
// MARK: Attribute Access
public extension Entity {
subscript<T>(_ path: KeyPath<EntityType.AttributeType, T>) -> T {
/// Access the attribute at the given keypath. This just
/// allows you to write `entity[\.propertyName]` instead
/// of `entity.relationships.propertyName`.
subscript<T>(_ path: KeyPath<EntityType.Attributes, T>) -> T {
return attributes[keyPath: path]
}
}
// MARK: Relationship Access
public extension Entity {
public static func ~><OtherEntityType: JSONAPI.EntityType>(entity: Entity<EntityType>, path: KeyPath<EntityType.RelatedType, ToOneRelationship<OtherEntityType>>) -> OtherEntityType.Identifier {
/// Access to an Id of a `ToOneRelationship`.
/// This allows you to write `entity ~> \.other` instead
/// of `entity.relationships.other.id`.
public static func ~><OtherEntityType: JSONAPI.EntityDescription>(entity: Entity<EntityType>, path: KeyPath<EntityType.Relationships, ToOneRelationship<OtherEntityType>>) -> OtherEntityType.Identifier {
return entity.relationships[keyPath: path].id
}
public static func ~><OtherEntityType: JSONAPI.EntityType>(entity: Entity<EntityType>, path: KeyPath<EntityType.RelatedType, ToManyRelationship<OtherEntityType>>) -> [OtherEntityType.Identifier] {
/// Access to all Ids of a `ToManyRelationship`.
/// This allows you to write `entity ~> \.others` instead
/// of `entity.relationships.others.ids`.
public static func ~><OtherEntityType: JSONAPI.EntityDescription>(entity: Entity<EntityType>, path: KeyPath<EntityType.Relationships, ToManyRelationship<OtherEntityType>>) -> [OtherEntityType.Identifier] {
return entity.relationships[keyPath: path].ids
}
}
@@ -162,11 +181,11 @@ public extension Entity {
try container.encode(id, forKey: .id)
}
if EntityType.AttributeType.self != NoAttributes.self {
if EntityType.Attributes.self != NoAttributes.self {
try container.encode(attributes, forKey: .attributes)
}
if EntityType.RelatedType.self != NoRelatives.self {
if EntityType.Relationships.self != NoRelatives.self {
try container.encode(relationships, forKey: .relationships)
}
}
@@ -183,8 +202,8 @@ public extension Entity {
id = try (Unidentified() as? EntityType.Identifier) ?? container.decode(EntityType.Identifier.self, forKey: .id)
attributes = try (NoAttributes() as? EntityType.AttributeType) ?? container.decode(EntityType.AttributeType.self, forKey: .attributes)
attributes = try (NoAttributes() as? EntityType.Attributes) ?? container.decode(EntityType.Attributes.self, forKey: .attributes)
relationships = try (NoRelatives() as? EntityType.RelatedType) ?? container.decode(EntityType.RelatedType.self, forKey: .relationships)
relationships = try (NoRelatives() as? EntityType.Relationships) ?? container.decode(EntityType.Relationships.self, forKey: .relationships)
}
}
+20 -21
View File
@@ -5,46 +5,43 @@
// Created by Mathew Polzin on 7/24/18.
//
import Foundation
/// Any type that you would like to be encoded to and
/// decoded from JSON API ids should conform to this
/// protocol. Conformance for `String` and `UUID`
/// is given by this library.
public protocol RawIdType: Codable, Equatable {
/// protocol. Conformance for `String` is given.
public protocol RawIdType: Codable, Equatable {}
/// If you would like to be able to create new
/// Entities with Ids backed by a RawIdType then
/// your Id type should conform to
/// `CreatableRawIdType`.
/// Conformances for `String` and `UUID`
/// are given in the README for this library.
public protocol CreatableRawIdType: RawIdType {
static func unique() -> Self
}
public protocol Identifier: Codable, Equatable {
init()
}
extension String: RawIdType {}
public protocol Identifier: Codable, Equatable {}
public struct Unidentified: Identifier {
public init() {}
}
public protocol IdType: Identifier {
associatedtype EntityType: JSONAPI.EntityType
associatedtype EntityType: JSONAPI.EntityDescription
associatedtype RawType: RawIdType
var rawValue: RawType { get }
}
extension UUID: RawIdType {
public static func unique() -> UUID {
return UUID()
}
}
extension String: RawIdType {
public static func unique() -> String {
return UUID().uuidString
}
public protocol CreatableIdType: IdType {
init()
}
/// An Entity ID. These IDs can be encoded to or decoded from
/// JSON API IDs.
public struct Id<RawType: RawIdType, EntityType: JSONAPI.EntityType>: IdType {
public struct Id<RawType: RawIdType, EntityType: JSONAPI.EntityDescription>: IdType {
public let rawValue: RawType
public init(rawValue: RawType) {
@@ -60,7 +57,9 @@ public struct Id<RawType: RawIdType, EntityType: JSONAPI.EntityType>: IdType {
var container = encoder.singleValueContainer()
try container.encode(rawValue)
}
}
extension Id: CreatableIdType where RawType: CreatableRawIdType {
public init() {
rawValue = .unique()
}
+9 -7
View File
@@ -5,23 +5,21 @@
// Created by Mathew Polzin on 8/31/18.
//
import Foundation
/// An Entity relationship that can be encoded to or decoded from
/// a JSON API "Resource Linkage."
/// You should use the `ToOneRelationship` and `ToManyRelationship`
/// concrete types.
/// See https://jsonapi.org/format/#document-resource-object-linkage
public protocol Relationship: Equatable, Encodable {
associatedtype EntityType: JSONAPI.EntityType where EntityType.Identifier: IdType
associatedtype EntityType: JSONAPI.EntityDescription where EntityType.Identifier: IdType
var ids: [EntityType.Identifier] { get }
}
/// An Entity relationship that can be encoded to or decoded from
/// a JSON API "Resource Linkage."
/// See https://jsonapi.org/format/#document-resource-object-linkage
/// A convenient typealias might make your code much more legible: `One<Entity>`
public struct ToOneRelationship<EntityType: JSONAPI.EntityType>: Equatable, Relationship, Decodable where EntityType.Identifier: IdType {
/// A convenient typealias might make your code much more legible: `One<EntityDescription>`
public struct ToOneRelationship<EntityType: JSONAPI.EntityDescription>: Equatable, Relationship, Decodable where EntityType.Identifier: IdType {
public let id: EntityType.Identifier
public init(entity: Entity<EntityType>) {
@@ -36,8 +34,8 @@ public struct ToOneRelationship<EntityType: JSONAPI.EntityType>: Equatable, Rela
/// An Entity relationship that can be encoded to or decoded from
/// a JSON API "Resource Linkage."
/// See https://jsonapi.org/format/#document-resource-object-linkage
/// A convenient typealias might make your code much more legible: `Many<Entity>`
public struct ToManyRelationship<EntityType: JSONAPI.EntityType>: Equatable, Relationship, Decodable where EntityType.Identifier: IdType {
/// A convenient typealias might make your code much more legible: `Many<EntityDescription>`
public struct ToManyRelationship<EntityType: JSONAPI.EntityDescription>: Equatable, Relationship, Decodable where EntityType.Identifier: IdType {
public let ids: [EntityType.Identifier]
public init(entities: [Entity<EntityType>]) {
@@ -47,6 +45,10 @@ public struct ToManyRelationship<EntityType: JSONAPI.EntityType>: Equatable, Rel
public init<T: Relationship>(relationships: [T]) where T.EntityType == EntityType {
ids = relationships.flatMap { $0.ids }
}
public static var none: ToManyRelationship {
return .init(entities: [])
}
}
// MARK: Codable