Initial commit of files taken from a package started as part of another project.

This commit is contained in:
Mathew Polzin
2018-11-12 20:36:03 -08:00
commit 34c82b135f
29 changed files with 2324 additions and 0 deletions
+4
View File
@@ -0,0 +1,4 @@
.DS_Store
/.build
/Packages
/*.xcodeproj
+28
View File
@@ -0,0 +1,28 @@
// swift-tools-version:4.2
// The swift-tools-version declares the minimum version of Swift required to build this package.
import PackageDescription
let package = Package(
name: "JSONAPI",
products: [
// Products define the executables and libraries produced by a package, and make them visible to other packages.
.library(
name: "JSONAPI",
targets: ["JSONAPI"]),
],
dependencies: [
// Dependencies declare other packages that this package depends on.
// .package(url: /* package url */, from: "1.0.0"),
],
targets: [
// Targets are the basic building blocks of a package. A target can define a module or a test suite.
// Targets can depend on other targets in this package, and on products in packages which this package depends on.
.target(
name: "JSONAPI",
dependencies: []),
.testTarget(
name: "JSONAPITests",
dependencies: ["JSONAPI"]),
]
)
+3
View File
@@ -0,0 +1,3 @@
# JSONAPI
A description of this package.
+126
View File
@@ -0,0 +1,126 @@
//
// AnyEntity.swift
// ElevatedCore
//
// Created by Mathew Polzin on 11/5/18.
//
import Foundation
//private class AnyRelationshipBase<Entity: ElevatedCore.Entity>: Relationship {
//
// init() {
// guard type(of: self) != AnyRelationshipBase.self else {
// fatalError("Must use subclasses")
// }
// }
//
// var ids: [Id<Entity>] {
// fatalError("Implemented by subclasses")
// }
//
// static func == (lhs: AnyRelationshipBase<Entity>, rhs: AnyRelationshipBase<Entity>) -> Bool {
// fatalError("Implemented by subclasses")
// }
//}
//
//private final class AnyRelationshipBox<Concrete: Relationship>: AnyRelationshipBase<Concrete.Entity> {
// let concrete: Concrete
//
// init(_ concrete: Concrete) {
// self.concrete = concrete
// super.init()
// }
//
// override func encode(to encoder: Encoder) throws {
// try concrete.encode(to: encoder)
// }
//
// override var ids: [Id<Concrete.Entity>] {
// return concrete.ids
// }
//
// static func == (lhs: AnyRelationshipBox<Concrete>, rhs: AnyRelationshipBox<Concrete>) -> Bool {
// return lhs.concrete == rhs.concrete
// }
//}
//
//public final class AnyRelationship<Entity: ElevatedCore.Entity>: Relationship {
// private let box: AnyRelationshipBase<Entity>
//
// public init<Concrete: Relationship>(_ concrete: Concrete) where Concrete.Entity == Entity {
// box = AnyRelationshipBox(concrete)
// }
//
// public func encode(to encoder: Encoder) throws {
// try box.encode(to: encoder)
// }
//
// public var ids: [Id<Entity>] {
// return box.ids
// }
//
// public static func == (lhs: AnyRelationship<Entity>, rhs: AnyRelationship<Entity>) -> Bool {
// return lhs.box == rhs.box
// }
//}
//
//private class AnyEntityBase<Attributes: Equatable & Encodable, Relationships: Equatable & Encodable, ID: IdType>: Entity {
//
// init() {
// guard Swift.type(of: self) != AnyEntityBase.self else {
// fatalError("Must use subclasses")
// }
// }
//
// class var type: String { fatalError("Implemented by subclasses") }
// var id: Id<AnyEntity> { fatalError("Implemented by subclasses") }
//
// var attributes: Attributes { fatalError("Implemented by subclasses") }
// var relationships: Relationships { fatalError("Implemented by subclasses") }
//
// static func == (lhs: AnyEntityBase<Attributes, Relationships, ID>, rhs: AnyEntityBase<Attributes, Relationships, ID>) -> Bool {
// fatalError("Implemented by subclasses")
// }
//}
//
//private final class AnyEntityBox<Concrete: Entity>: AnyEntityBase<Concrete.Attributes, Concrete.Relationships, Concrete.ID> {
//
// let concrete: Concrete
//
// init(_ concrete: Concrete) {
// self.concrete = concrete
// super.init()
// }
//
// override class var type: String {
// return Concrete.type
// }
//
// override var id: Id<AnyEntity> {
// return concrete.id
// }
//
// override var attributes: Concrete.Attributes {
// return concrete.attributes
// }
//
// override var relationships: Concrete.Relationships {
// return concrete.relationships
// }
//
// static func == (lhs: AnyEntityBox<Concrete>, rhs: AnyEntityBox<Concrete>) -> Bool {
// return lhs.concrete == rhs.concrete
// }
//}
//
//public final class AnyEntity<Attributes: Equatable & Encodable, Relationships: Equatable & Encodable, ID: IdType>: Entity {
// private let box: AnyEntityBase<Attributes, Relationships, ID>
//
// init<Concrete: Entity>(_ concrete: Concrete) where Concrete.Attributes == Attributes, Concrete.Relationships == Relationships, Concrete.ID == ID {
// box = AnyEntityBox(concrete)
// }
//
// public class var type: String { return Swift.type(of: box).type }
//}
@@ -0,0 +1,73 @@
//
// JSONAPIDocument.swift
// ElevatedCore
//
// 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.
/// Note that this type uses Camel case. If your
/// API uses snake case, you will want to use
/// a conversion such as the one offerred by the
/// Foundation JSONEncoder/Decoder: `KeyDecodingStrategy`
public struct JSONAPIDocument<Body: ResourceBody, Include: IncludeDecoder, Error: JSONAPIError & Decodable> {
public let body: Data
// public let meta: Meta?
// public let jsonApi: APIDescription?
// public let links: Links?
public enum Data {
case errors([Error])
case data(Body, included: Includes<Include>)
public var isError: Bool {
guard case .errors = self else { return false }
return true
}
public var data: (Body, included: Includes<Include>)? {
guard case let .data(body, included: includes) = self else { return nil }
return (body, included: includes)
}
}
}
extension JSONAPIDocument: Decodable {
private enum RootCodingKeys: String, CodingKey {
case data
case errors
case included
case meta
case links
case jsonapi
}
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: RootCodingKeys.self)
let maybeData = try container.decodeIfPresent(Body.self, forKey: .data)
let maybeIncludes = try container.decodeIfPresent(Includes<Include>.self, forKey: .included)
let errors = try container.decodeIfPresent([Error].self, forKey: .errors)
assert(!(maybeData != nil && errors != nil), "JSON API Spec dictates data and errors will not both be present.")
assert((maybeIncludes == nil || maybeData != nil), "JSON API Spec dictates that includes will not be present if data is not present.")
// TODO come back to this and make robust
if let errors = errors {
body = .errors(errors)
return
}
guard let data = maybeData else {
body = .errors([.unknown]) // TODO: this should be more descriptive
return
}
body = .data(data, included: maybeIncludes ?? Includes<Include>.none)
}
}
@@ -0,0 +1,25 @@
//
// JSONAPI_Error.swift
// ElevatedCore
//
// Created by Mathew Polzin on 11/10/18.
//
import Foundation
public protocol JSONAPIError: Swift.Error {
static var unknown: Self { get }
}
// TODO: remove temp error stuff below
public enum TmpError: JSONAPIError & Decodable {
case unknownError
public init(from decoder: Decoder) throws {
self = .unknown
}
public static var unknown: TmpError {
return .unknownError
}
}
@@ -0,0 +1,403 @@
//
// JSONAPI_Includes.swift
// JSONAPI
//
// Created by Mathew Polzin on 11/10/18.
//
import Foundation
import Result
public protocol IncludeDecoder: Decodable {}
public struct Includes<I: IncludeDecoder>: Decodable {
public static var none: Includes { return .init(values: []) }
let values: [I]
private init(values: [I]) {
self.values = values
}
public init(from decoder: Decoder) throws {
var container = try decoder.unkeyedContainer()
var valueAggregator = [I]()
while !container.isAtEnd {
valueAggregator.append(try container.decode(I.self))
}
values = valueAggregator
}
public var count: Int {
return values.count
}
}
// MARK: - Decoding
func decode<EntityType: JSONAPI.EntityType>(_ 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))
} catch (let err as EncodingError) {
ret = .failure(err)
} catch (let err) {
ret = .failure(EncodingError.invalidValue(EntityType.self,
.init(codingPath: container.codingPath,
debugDescription: err.localizedDescription,
underlyingError: err)))
}
return ret
}
// MARK: - 0 includes
public protocol _Include0: IncludeDecoder { }
public struct Include0: _Include0 {
public init(from decoder: Decoder) throws {
}
}
public typealias NoIncludes = Include0
// MARK: - 1 include
public protocol _Include1: _Include0 {
associatedtype A: EntityType
var a: Entity<A>? { get }
}
public enum Include1<A: EntityType>: _Include1 {
case a(Entity<A>)
public var a: Entity<A>? {
guard case let .a(ret) = self else { return nil }
return ret
}
public init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
self = .a(try container.decode(Entity<A>.self))
}
}
extension Includes where I: _Include1 {
public subscript(_ lookup: I.A.Type) -> [Entity<I.A>] {
return values.compactMap { $0.a }
}
public subscript(_ lookup: Entity<I.A>.Type) -> [Entity<I.A>] {
return values.compactMap { $0.a}
}
}
// MARK: - 2 includes
public protocol _Include2: _Include1 {
associatedtype B: EntityType
var b: Entity<B>? { get }
}
public enum Include2<A: EntityType, B: EntityType>: _Include2 {
case a(Entity<A>)
case b(Entity<B>)
public var a: Entity<A>? {
guard case let .a(ret) = self else { return nil }
return ret
}
public var b: Entity<B>? {
guard case let .b(ret) = self else { return nil }
return ret
}
public init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
let attempts = [
try decode(A.self, from: container).map { Include2.a($0) },
try decode(B.self, from: container).map { Include2.b($0) } ]
let maybeVal: Include2<A, B>? = attempts
.compactMap { $0.value }
.first
guard let val = maybeVal else {
throw EncodingError.invalidValue(Include2<A, B>.self, .init(codingPath: decoder.codingPath, debugDescription: "Failed to find an include of the expected type. Attempts: \(attempts.map { $0.error }.compactMap { $0 })"))
}
self = val
}
}
extension Includes where I: _Include2 {
public subscript(_ lookup: I.B.Type) -> [Entity<I.B>] {
return values.compactMap { $0.b }
}
public subscript(_ lookup: Entity<I.B>.Type) -> [Entity<I.B>] {
return values.compactMap { $0.b}
}
}
// MARK: - 3 includes
public protocol _Include3: _Include2 {
associatedtype C: EntityType
var c: Entity<C>? { get }
}
public enum Include3<A: EntityType, B: EntityType, C: EntityType>: _Include3 {
case a(Entity<A>)
case b(Entity<B>)
case c(Entity<C>)
public var a: Entity<A>? {
guard case let .a(ret) = self else { return nil }
return ret
}
public var b: Entity<B>? {
guard case let .b(ret) = self else { return nil }
return ret
}
public var c: Entity<C>? {
guard case let .c(ret) = self else { return nil }
return ret
}
public init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
let attempts = [
try decode(A.self, from: container).map { Include3.a($0) },
try decode(B.self, from: container).map { Include3.b($0) },
try decode(C.self, from: container).map { Include3.c($0) }]
let maybeVal: Include3<A, B, C>? = attempts
.compactMap { $0.value }
.first
guard let val = maybeVal else {
throw EncodingError.invalidValue(Include3<A, B, C>.self, .init(codingPath: decoder.codingPath, debugDescription: "Failed to find an include of the expected type. Attempts: \(attempts.map { $0.error }.compactMap { $0 })"))
}
self = val
}
}
extension Includes where I: _Include3 {
public subscript(_ lookup: I.C.Type) -> [Entity<I.C>] {
return values.compactMap { $0.c }
}
public subscript(_ lookup: Entity<I.C>.Type) -> [Entity<I.C>] {
return values.compactMap { $0.c}
}
}
// MARK: - 4 includes
public protocol _Include4: _Include3 {
associatedtype D: EntityType
var d: Entity<D>? { get }
}
public enum Include4<A: EntityType, B: EntityType, C: EntityType, D: EntityType>: _Include4 {
case a(Entity<A>)
case b(Entity<B>)
case c(Entity<C>)
case d(Entity<D>)
public var a: Entity<A>? {
guard case let .a(ret) = self else { return nil }
return ret
}
public var b: Entity<B>? {
guard case let .b(ret) = self else { return nil }
return ret
}
public var c: Entity<C>? {
guard case let .c(ret) = self else { return nil }
return ret
}
public var d: Entity<D>? {
guard case let .d(ret) = self else { return nil }
return ret
}
public init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
let attempts = [
try decode(A.self, from: container).map { Include4.a($0) },
try decode(B.self, from: container).map { Include4.b($0) },
try decode(C.self, from: container).map { Include4.c($0) },
try decode(D.self, from: container).map { Include4.d($0) }]
let maybeVal: Include4<A, B, C, D>? = attempts
.compactMap { $0.value }
.first
guard let val = maybeVal else {
throw EncodingError.invalidValue(Include4<A, B, C, D>.self, .init(codingPath: decoder.codingPath, debugDescription: "Failed to find an include of the expected type. Attempts: \(attempts.map { $0.error }.compactMap { $0 })"))
}
self = val
}
}
extension Includes where I: _Include4 {
public subscript(_ lookup: I.D.Type) -> [Entity<I.D>] {
return values.compactMap { $0.d }
}
public subscript(_ lookup: Entity<I.D>.Type) -> [Entity<I.D>] {
return values.compactMap { $0.d}
}
}
// MARK: - 5 includes
public protocol _Include5: _Include4 {
associatedtype E: EntityType
var e: Entity<E>? { get }
}
public enum Include5<A: EntityType, B: EntityType, C: EntityType, D: EntityType, E: EntityType>: _Include5 {
case a(Entity<A>)
case b(Entity<B>)
case c(Entity<C>)
case d(Entity<D>)
case e(Entity<E>)
public var a: Entity<A>? {
guard case let .a(ret) = self else { return nil }
return ret
}
public var b: Entity<B>? {
guard case let .b(ret) = self else { return nil }
return ret
}
public var c: Entity<C>? {
guard case let .c(ret) = self else { return nil }
return ret
}
public var d: Entity<D>? {
guard case let .d(ret) = self else { return nil }
return ret
}
public var e: Entity<E>? {
guard case let .e(ret) = self else { return nil }
return ret
}
public init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
let attempts = [
try decode(A.self, from: container).map { Include5.a($0) },
try decode(B.self, from: container).map { Include5.b($0) },
try decode(C.self, from: container).map { Include5.c($0) },
try decode(D.self, from: container).map { Include5.d($0) },
try decode(E.self, from: container).map { Include5.e($0) }]
let maybeVal: Include5<A, B, C, D, E>? = attempts
.compactMap { $0.value }
.first
guard let val = maybeVal else {
throw EncodingError.invalidValue(Include5<A, B, C, D, E>.self, .init(codingPath: decoder.codingPath, debugDescription: "Failed to find an include of the expected type. Attempts: \(attempts.map { $0.error }.compactMap { $0 })"))
}
self = val
}
}
extension Includes where I: _Include5 {
public subscript(_ lookup: I.E.Type) -> [Entity<I.E>] {
return values.compactMap { $0.e }
}
public subscript(_ lookup: Entity<I.E>.Type) -> [Entity<I.E>] {
return values.compactMap { $0.e}
}
}
// MARK: - 6 includes
public protocol _Include6: _Include5 {
associatedtype F: EntityType
var f: Entity<F>? { get }
}
public enum Include6<A: EntityType, B: EntityType, C: EntityType, D: EntityType, E: EntityType, F: EntityType>: _Include6 {
case a(Entity<A>)
case b(Entity<B>)
case c(Entity<C>)
case d(Entity<D>)
case e(Entity<E>)
case f(Entity<F>)
public var a: Entity<A>? {
guard case let .a(ret) = self else { return nil }
return ret
}
public var b: Entity<B>? {
guard case let .b(ret) = self else { return nil }
return ret
}
public var c: Entity<C>? {
guard case let .c(ret) = self else { return nil }
return ret
}
public var d: Entity<D>? {
guard case let .d(ret) = self else { return nil }
return ret
}
public var e: Entity<E>? {
guard case let .e(ret) = self else { return nil }
return ret
}
public var f: Entity<F>? {
guard case let .f(ret) = self else { return nil }
return ret
}
public init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
let attempts = [
try decode(A.self, from: container).map { Include6.a($0) },
try decode(B.self, from: container).map { Include6.b($0) },
try decode(C.self, from: container).map { Include6.c($0) },
try decode(D.self, from: container).map { Include6.d($0) },
try decode(E.self, from: container).map { Include6.e($0) },
try decode(F.self, from: container).map { Include6.f($0) }]
let maybeVal: Include6<A, B, C, D, E, F>? = attempts
.compactMap { $0.value }
.first
guard let val = maybeVal else {
throw EncodingError.invalidValue(Include6<A, B, C, D, E, F>.self, .init(codingPath: decoder.codingPath, debugDescription: "Failed to find an include of the expected type. Attempts: \(attempts.map { $0.error }.compactMap { $0 })"))
}
self = val
}
}
extension Includes where I: _Include6 {
public subscript(_ lookup: I.F.Type) -> [Entity<I.F>] {
return values.compactMap { $0.f }
}
public subscript(_ lookup: Entity<I.F>.Type) -> [Entity<I.F>] {
return values.compactMap { $0.f}
}
}
@@ -0,0 +1,40 @@
//
// JSONAPI_ResourceBody.swift
// ElevatedCore
//
// 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>
}
public struct SingleResourceBody<EntityType: JSONAPI.EntityType>: ResourceBody {
public let value: Entity<EntityType>?
}
public struct ManyResourceBody<EntityType: JSONAPI.EntityType>: ResourceBody {
public let values: [Entity<EntityType>]
}
// MARK: Decodable
extension SingleResourceBody {
public init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
value = try container.decode(Entity<EntityType>.self)
}
}
extension ManyResourceBody {
public init(from decoder: Decoder) throws {
var container = try decoder.unkeyedContainer()
var valueAggregator = [Entity<EntityType>]()
while !container.isAtEnd {
valueAggregator.append(try container.decode(Entity<EntityType>.self))
}
values = valueAggregator
}
}
+3
View File
@@ -0,0 +1,3 @@
struct JSONAPI {
var text = "Hello, World!"
}
@@ -0,0 +1,159 @@
//
// Entity.swift
// ElevatedCore
//
// Created by Mathew Polzin on 7/24/18.
//
public typealias Relatives = Codable & Equatable
public typealias Attributes = Codable & Equatable
/// Can be used as Relationships Type for Entities that do not
/// have any Relationships.
public struct NoRelatives: Relatives {}
/// Can be used as Attributes Type for Entities that do not
/// have any Attributes.
public struct NoAttributes: Attributes {}
public protocol EntityType {
associatedtype Identifier: JSONAPI.Identifier
associatedtype AttributeType: Attributes
associatedtype RelatedType: Relatives
static var type: String { get }
}
public protocol IdentifiedEntityType: EntityType where Identifier: IdType {}
/// 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
/// Easiest to use with `protocol MyEntity: Entity, Identified, Related, Attributed where ID = UUID`.
public struct Entity<EntityType: JSONAPI.EntityType>: Codable, Equatable {
public static var type: String { return EntityType.type }
public let id: EntityType.Identifier
public let attributes: EntityType.AttributeType
public let relationships: EntityType.RelatedType
public init(id: EntityType.Identifier, attributes: EntityType.AttributeType, relationships: EntityType.RelatedType) {
self.id = id
self.attributes = attributes
self.relationships = relationships
}
public init(attributes: EntityType.AttributeType, relationships: EntityType.RelatedType) {
self.id = .init()
self.attributes = attributes
self.relationships = relationships
}
}
extension Entity where EntityType.AttributeType == NoAttributes {
public init(id: EntityType.Identifier, relationships: EntityType.RelatedType) {
self.init(id: id, attributes: NoAttributes(), relationships: relationships)
}
public init(relationships: EntityType.RelatedType) {
self.init(attributes: NoAttributes(), relationships: relationships)
}
}
extension Entity where EntityType.RelatedType == NoRelatives {
public init(id: EntityType.Identifier, attributes: EntityType.AttributeType) {
self.init(id: id, attributes: attributes, relationships: NoRelatives())
}
public init(attributes: EntityType.AttributeType) {
self.init(attributes: attributes, relationships: NoRelatives())
}
}
extension Entity where EntityType.AttributeType == NoAttributes, EntityType.RelatedType == NoRelatives {
public init(id: EntityType.Identifier) {
self.init(id: id, attributes: NoAttributes(), relationships: NoRelatives())
}
public init() {
self.init(attributes: NoAttributes(), relationships: NoRelatives())
}
}
//public protocol IdentifiedEntityType: JSONAPI.EntityType where IdentifiedEntityType.Identifier: IdType, Identifier.Entity == Self {}
public extension Entity where EntityType.Identifier: IdType {
/// Get a pointer to this entity that can be used as a
/// relationship to another entity.
public var pointer: ToOneRelationship<EntityType> {
return ToOneRelationship(entity: self)
}
}
// MARK: Attribute Access
public extension Entity {
subscript<T>(_ path: KeyPath<EntityType.AttributeType, 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 {
return entity.relationships[keyPath: path].id
}
public static func ~><OtherEntityType: JSONAPI.EntityType>(entity: Entity<EntityType>, path: KeyPath<EntityType.RelatedType, ToManyRelationship<OtherEntityType>>) -> [OtherEntityType.Identifier] {
return entity.relationships[keyPath: path].ids
}
}
infix operator ~>
// MARK: - Codable
private enum ResourceObjectCodingKeys: String, CodingKey {
case type = "type"
case id = "id"
case attributes = "attributes"
case relationships = "relationships"
}
public extension Entity {
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: ResourceObjectCodingKeys.self)
try container.encode(Entity.type, forKey: .type)
if EntityType.Identifier.self != Unidentified.self {
try container.encode(id, forKey: .id)
}
if EntityType.AttributeType.self != NoAttributes.self {
try container.encode(attributes, forKey: .attributes)
}
if EntityType.RelatedType.self != NoRelatives.self {
try container.encode(relationships, forKey: .relationships)
}
}
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: ResourceObjectCodingKeys.self)
let type = try container.decode(String.self, forKey: .type)
guard Entity.type == type else {
throw JSONAPIEncodingError.typeMismatch(expected: EntityType.type, found: type)
}
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)
relationships = try (NoRelatives() as? EntityType.RelatedType) ?? container.decode(EntityType.RelatedType.self, forKey: .relationships)
}
}
+67
View File
@@ -0,0 +1,67 @@
//
// Id.swift
// ElevatedCore
//
// 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 {
static func unique() -> Self
}
public protocol Identifier: Codable, Equatable {
init()
}
public struct Unidentified: Identifier {
public init() {}
}
public protocol IdType: Identifier {
associatedtype EntityType: JSONAPI.EntityType
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
}
}
/// 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 let rawValue: RawType
public init(rawValue: RawType) {
self.rawValue = rawValue
}
public init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
rawValue = try container.decode(RawType.self)
}
public func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
try container.encode(rawValue)
}
public init() {
rawValue = .unique()
}
}
@@ -0,0 +1,120 @@
//
// Relationship.swift
// ElevatedCore
//
// 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
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 {
public let id: EntityType.Identifier
public init(entity: Entity<EntityType>) {
id = entity.id
}
public var ids: [EntityType.Identifier] {
return [id]
}
}
/// 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 {
public let ids: [EntityType.Identifier]
public init(entities: [Entity<EntityType>]) {
ids = entities.map { $0.id }
}
public init<T: Relationship>(relationships: [T]) where T.EntityType == EntityType {
ids = relationships.flatMap { $0.ids }
}
}
// MARK: Codable
private enum ResourceLinkageCodingKeys: String, CodingKey {
case data = "data"
}
private enum ResourceIdentifierCodingKeys: String, CodingKey {
case id = "id"
case entityType = "type"
}
public enum JSONAPIEncodingError: Swift.Error {
case typeMismatch(expected: String, found: String)
}
extension ToOneRelationship {
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: ResourceLinkageCodingKeys.self)
let identifier = try container.nestedContainer(keyedBy: ResourceIdentifierCodingKeys.self, forKey: .data)
let type = try identifier.decode(String.self, forKey: .entityType)
guard type == EntityType.type else {
throw JSONAPIEncodingError.typeMismatch(expected: EntityType.type, found: type)
}
id = try identifier.decode(EntityType.Identifier.self, forKey: .id)
}
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: ResourceLinkageCodingKeys.self)
var identifier = container.nestedContainer(keyedBy: ResourceIdentifierCodingKeys.self, forKey: .data)
try identifier.encode(id, forKey: .id)
try identifier.encode(EntityType.type, forKey: .entityType)
}
}
extension ToManyRelationship {
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: ResourceLinkageCodingKeys.self)
var identifiers = try container.nestedUnkeyedContainer(forKey: .data)
var newIds = [EntityType.Identifier]()
while !identifiers.isAtEnd {
let identifier = try identifiers.nestedContainer(keyedBy: ResourceIdentifierCodingKeys.self)
let type = try identifier.decode(String.self, forKey: .entityType)
guard type == EntityType.type else {
throw JSONAPIEncodingError.typeMismatch(expected: EntityType.type, found: type)
}
newIds.append(try identifier.decode(EntityType.Identifier.self, forKey: .id))
}
ids = newIds
}
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: ResourceLinkageCodingKeys.self)
var identifiers = container.nestedUnkeyedContainer(forKey: .data)
for id in ids {
var identifier = identifiers.nestedContainer(keyedBy: ResourceIdentifierCodingKeys.self)
try identifier.encode(id, forKey: .id)
try identifier.encode(EntityType.type, forKey: .entityType)
}
}
}
@@ -0,0 +1,100 @@
//
// DocumentTests.swift
// JSONAPITests
//
// Created by Mathew Polzin on 11/12/18.
//
import XCTest
import JSONAPI
class DocumentTests: XCTestCase {
func test_singleDocumentNoIncludes() {
let document = try? JSONDecoder().decode(JSONAPIDocument<SingleResourceBody<ArticleType>, Include0, TmpError>.self, from: single_document_no_includes)
XCTAssertNotNil(document)
guard let d = document else { return }
XCTAssertFalse(d.body.isError)
XCTAssertNotNil(d.body.data)
XCTAssertEqual(d.body.data?.0.value?.id.rawValue, "1")
XCTAssertEqual(d.body.data?.included.count, 0)
}
func test_singleDocumentSomeIncludes() {
let document = try? JSONDecoder().decode(JSONAPIDocument<SingleResourceBody<ArticleType>, Include1<AuthorType>, TmpError>.self, from: single_document_some_includes)
XCTAssertNotNil(document)
guard let d = document else { return }
XCTAssertFalse(d.body.isError)
XCTAssertNotNil(d.body.data)
XCTAssertEqual(d.body.data?.0.value?.id.rawValue, "1")
XCTAssertEqual(d.body.data?.included.count, 1)
XCTAssertEqual(d.body.data?.included[Author.self].count, 1)
XCTAssertEqual(d.body.data?.included[Author.self][0].id.rawValue, "33")
}
func test_manyDocumentNoIncludes() {
let document = try? JSONDecoder().decode(JSONAPIDocument<ManyResourceBody<ArticleType>, Include0, TmpError>.self, from: many_document_no_includes)
XCTAssertNotNil(document)
guard let d = document else { return }
XCTAssertFalse(d.body.isError)
XCTAssertNotNil(d.body.data)
XCTAssertEqual(d.body.data?.0.values.count, 3)
XCTAssertEqual(d.body.data?.0.values[0].id.rawValue, "1")
XCTAssertEqual(d.body.data?.0.values[1].id.rawValue, "2")
XCTAssertEqual(d.body.data?.0.values[2].id.rawValue, "3")
XCTAssertEqual(d.body.data?.included.count, 0)
}
func test_manyDocumentSomeIncludes() {
let document = try? JSONDecoder().decode(JSONAPIDocument<ManyResourceBody<ArticleType>, Include1<AuthorType>, TmpError>.self, from: many_document_some_includes)
XCTAssertNotNil(document)
guard let d = document else { return }
XCTAssertFalse(d.body.isError)
XCTAssertNotNil(d.body.data)
XCTAssertEqual(d.body.data?.0.values.count, 3)
XCTAssertEqual(d.body.data?.0.values[0].id.rawValue, "1")
XCTAssertEqual(d.body.data?.0.values[1].id.rawValue, "2")
XCTAssertEqual(d.body.data?.0.values[2].id.rawValue, "3")
XCTAssertEqual(d.body.data?.included.count, 3)
XCTAssertEqual(d.body.data?.included[Author.self].count, 3)
XCTAssertEqual(d.body.data?.included[Author.self][0].id.rawValue, "33")
XCTAssertEqual(d.body.data?.included[Author.self][1].id.rawValue, "22")
XCTAssertEqual(d.body.data?.included[Author.self][2].id.rawValue, "11")
}
enum AuthorType: EntityType {
static var type: String { return "authors" }
typealias Identifier = Id<String, AuthorType>
typealias AttributeType = NoAttributes
typealias RelatedType = NoRelatives
}
typealias Author = Entity<AuthorType>
enum ArticleType: EntityType {
static var type: String { return "articles" }
typealias Identifier = Id<String, ArticleType>
typealias AttributeType = NoAttributes
typealias RelatedType = Relationships
struct Relationships: Relatives {
let author: ToOneRelationship<AuthorType>
}
}
typealias Article = Entity<ArticleType>
}
@@ -0,0 +1,148 @@
//
// DocumentStubs.swift
// JSONAPITests
//
// Created by Mathew Polzin on 11/12/18.
//
import Foundation
let single_document_no_includes = """
{
"data": {
"id": "1",
"type": "articles",
"relationships": {
"author": {
"data": {
"type": "authors",
"id": "33"
}
}
}
}
}
""".data(using: .utf8)!
let single_document_some_includes = """
{
"data": {
"id": "1",
"type": "articles",
"relationships": {
"author": {
"data": {
"type": "authors",
"id": "33"
}
}
}
},
"included": [
{
"id": "33",
"type": "authors"
}
]
}
""".data(using: .utf8)!
let many_document_no_includes = """
{
"data": [
{
"id": "1",
"type": "articles",
"relationships": {
"author": {
"data": {
"type": "authors",
"id": "33"
}
}
}
},
{
"id": "2",
"type": "articles",
"relationships": {
"author": {
"data": {
"type": "authors",
"id": "22"
}
}
}
},
{
"id": "3",
"type": "articles",
"relationships": {
"author": {
"data": {
"type": "authors",
"id": "11"
}
}
}
}
]
}
""".data(using: .utf8)!
let many_document_some_includes = """
{
"data": [
{
"id": "1",
"type": "articles",
"relationships": {
"author": {
"data": {
"type": "authors",
"id": "33"
}
}
}
},
{
"id": "2",
"type": "articles",
"relationships": {
"author": {
"data": {
"type": "authors",
"id": "22"
}
}
}
},
{
"id": "3",
"type": "articles",
"relationships": {
"author": {
"data": {
"type": "authors",
"id": "11"
}
}
}
}
],
"included": [
{
"id": "33",
"type": "authors"
},
{
"id": "22",
"type": "authors"
},
{
"id": "11",
"type": "authors"
}
]
}
""".data(using: .utf8)!
+168
View File
@@ -0,0 +1,168 @@
//
// EntityTests.swift
// ElevatedCoreTests
//
// Created by Mathew Polzin on 7/25/18.
//
import XCTest
import Foundation
import JSONAPI
class EntityTests: XCTestCase {
func test_relationship_access() {
let entity1 = TestEntity1()
let entity2 = TestEntity2(other: entity1.pointer)
XCTAssertEqual(entity2.relationships.other, entity1.pointer)
}
func test_relationship_operator_access() {
let entity1 = TestEntity1()
let entity2 = TestEntity2(other: entity1.pointer)
XCTAssertEqual(entity2 ~> \.other, entity1.id)
}
func test_toMany_relationship_operator_access() {
let entity1 = TestEntity1()
let entity2 = TestEntity1()
let entity4 = TestEntity1()
let entity3 = TestEntity3(others: .init(relationships: [entity1.pointer, entity2.pointer, entity4.pointer]))
XCTAssertEqual(entity3 ~> \.others, [entity1.id, entity2.id, entity4.id])
}
func test_relationshipIds() {
let entity1 = TestEntity1()
let entity2 = TestEntity2(other: entity1.pointer)
XCTAssertEqual(entity2.relationships.other.ids, [entity1.id])
}
func test_EntityNoRelationshipsNoAttributes() {
let entity = try? JSONDecoder().decode(TestEntity1.self, from: entity_no_relationships_no_attributes)
XCTAssertNotNil(entity)
XCTAssert(type(of: entity?.relationships) == NoRelatives?.self)
XCTAssert(type(of: entity?.attributes) == NoAttributes?.self)
}
func test_EntityNoRelationshipsSomeAttributes() {
let entity = try? JSONDecoder().decode(TestEntity5.self, from: entity_no_relationships_some_attributes)
XCTAssertNotNil(entity)
XCTAssert(type(of: entity?.relationships) == NoRelatives?.self)
guard let e = entity else { return }
XCTAssertEqual(e[\.floater], 123.321)
}
func test_EntitySomeRelationshipsNoAttributes() {
let entity = try? JSONDecoder().decode(TestEntity3.self, from: entity_some_relationships_no_attributes)
XCTAssertNotNil(entity)
XCTAssert(type(of: entity?.attributes) == NoAttributes?.self)
guard let e = entity else { return }
XCTAssertEqual((e ~> \.others).map { $0.rawValue.uuidString }, ["364B3B69-4DF1-467F-B52E-B0C9E44F666E"])
}
func test_EntitySomeRelationshipsSomeAttributes() {
let entity = try? JSONDecoder().decode(TestEntity4.self, from: entity_some_relationships_some_attributes)
XCTAssertNotNil(entity)
guard let e = entity else { return }
XCTAssertEqual(e[\.word], "coolio")
XCTAssertEqual(e[\.number], 992299)
XCTAssertEqual((e ~> \.other).rawValue.uuidString, "2DF03B69-4B0A-467F-B52E-B0C9E44FCECF")
}
enum TestEntityType1: EntityType {
static var type: String { return "test_entities"}
typealias Identifier = Id<UUID, TestEntityType1>
typealias AttributeType = NoAttributes
typealias RelatedType = NoRelatives
}
typealias TestEntity1 = Entity<TestEntityType1>
enum TestEntityType2: EntityType {
static var type: String { return "second_test_entities"}
typealias Identifier = Id<UUID, TestEntityType2>
typealias RelatedType = Relationships
typealias AttributeType = NoAttributes
struct Relationships: Relatives {
let other: ToOneRelationship<TestEntityType1>
}
}
typealias TestEntity2 = Entity<TestEntityType2>
enum TestEntityType3: EntityType {
static var type: String { return "third_test_entities"}
typealias Identifier = Id<UUID, TestEntityType3>
typealias RelatedType = Relationships
typealias AttributeType = NoAttributes
struct Relationships: Relatives {
let others: ToManyRelationship<TestEntityType1>
}
}
typealias TestEntity3 = Entity<TestEntityType3>
enum TestEntityType4: EntityType {
static var type: String { return "fourth_test_entities"}
typealias Identifier = Id<UUID, TestEntityType4>
typealias RelatedType = Relationships
typealias AttributeType = Atts
struct Relationships: Relatives {
let other: ToOneRelationship<TestEntityType2>
}
struct Atts: Attributes {
let word: String
let number: Int
}
}
typealias TestEntity4 = Entity<TestEntityType4>
enum TestEntityType5: EntityType {
static var type: String { return "fifth_test_entities"}
typealias Identifier = Id<UUID, TestEntityType5>
typealias RelatedType = NoRelatives
typealias AttributeType = Atts
struct Atts: Attributes {
let floater: Double
}
}
typealias TestEntity5 = Entity<TestEntityType5>
}
extension Entity where EntityType == EntityTests.TestEntityType2 {
init(other: ToOneRelationship<EntityTests.TestEntityType1>) {
self.init(relationships: .init(other: other))
}
}
extension Entity where EntityType == EntityTests.TestEntityType3 {
init(others: ToManyRelationship<EntityTests.TestEntityType1>) {
self.init(relationships: .init(others: others))
}
}
@@ -0,0 +1,59 @@
//
// EntityStubs.swift
// JSONAPITests
//
// Created by Mathew Polzin on 11/12/18.
//
import Foundation
let entity_no_relationships_no_attributes = """
{
"id": "A24B3B69-4DF1-467F-B52E-B0C9E44F436A",
"type": "test_entities"
}
""".data(using: .utf8)!
let entity_no_relationships_some_attributes = """
{
"id": "2DF03B69-4B0A-467F-B52E-B0C9E44FCECF",
"type": "fifth_test_entities",
"attributes": {
"floater": 123.321
}
}
""".data(using: .utf8)!
let entity_some_relationships_no_attributes = """
{
"id": "11113B69-4DF1-467F-B52E-B0C9E44FC444",
"type": "third_test_entities",
"relationships": {
"others": {
"data": [{
"type": "test_entities",
"id": "364B3B69-4DF1-467F-B52E-B0C9E44F666E"
}]
}
}
}
""".data(using: .utf8)!
let entity_some_relationships_some_attributes = """
{
"id": "90F03B69-4DF1-467F-B52E-B0C9E44FC333",
"type": "fourth_test_entities",
"attributes": {
"word": "coolio",
"number": 992299
},
"relationships": {
"other": {
"data": {
"type": "second_test_entities",
"id": "2DF03B69-4B0A-467F-B52E-B0C9E44FCECF"
}
}
}
}
""".data(using: .utf8)!
@@ -0,0 +1,204 @@
import XCTest
import JSONAPI
class IncludedTests: XCTestCase {
let decoder = JSONDecoder()
func test_OneInclude() {
let maybeIncludes = try? decoder.decode(Includes<Include1<TestEntityType>>.self, from: one_include)
XCTAssertNotNil(maybeIncludes)
guard let includes = maybeIncludes else {
return
}
XCTAssertEqual(includes[TestEntity.self].count, 1)
}
func test_TwoSameIncludes() {
let maybeIncludes = try? decoder.decode(Includes<Include1<TestEntityType>>.self, from: two_same_type_includes)
XCTAssertNotNil(maybeIncludes)
guard let includes = maybeIncludes else {
return
}
XCTAssertEqual(includes[TestEntity.self].count, 2)
}
func test_TwoDifferentIncludes() {
let maybeIncludes = try? decoder.decode(Includes<Include2<TestEntityType, TestEntityType2>>.self, from: two_different_type_includes)
XCTAssertNotNil(maybeIncludes)
guard let includes = maybeIncludes else {
return
}
XCTAssertEqual(includes[TestEntity.self].count, 1)
XCTAssertEqual(includes[TestEntity2.self].count, 1)
}
func test_ThreeDifferentIncludes() {
let maybeIncludes = try? decoder.decode(Includes<Include3<TestEntityType, TestEntityType2, TestEntityType4>>.self, from: three_different_type_includes)
XCTAssertNotNil(maybeIncludes)
guard let includes = maybeIncludes else {
return
}
XCTAssertEqual(includes[TestEntity.self].count, 1)
XCTAssertEqual(includes[TestEntity2.self].count, 1)
XCTAssertEqual(includes[TestEntity4.self].count, 1)
}
func test_FourDifferentIncludes() {
let maybeIncludes = try? decoder.decode(Includes<Include4<TestEntityType, TestEntityType2, TestEntityType4, TestEntityType6>>.self, from: four_different_type_includes)
XCTAssertNotNil(maybeIncludes)
guard let includes = maybeIncludes else {
return
}
XCTAssertEqual(includes[TestEntity.self].count, 1)
XCTAssertEqual(includes[TestEntity2.self].count, 1)
XCTAssertEqual(includes[TestEntity4.self].count, 1)
XCTAssertEqual(includes[TestEntity6.self].count, 1)
}
func test_FiveDifferentIncludes() {
let maybeIncludes = try? decoder.decode(Includes<Include5<TestEntityType, TestEntityType2, TestEntityType3, TestEntityType4, TestEntityType6>>.self, from: five_different_type_includes)
XCTAssertNotNil(maybeIncludes)
guard let includes = maybeIncludes else {
return
}
XCTAssertEqual(includes[TestEntity.self].count, 1)
XCTAssertEqual(includes[TestEntity2.self].count, 1)
XCTAssertEqual(includes[TestEntity3.self].count, 1)
XCTAssertEqual(includes[TestEntity4.self].count, 1)
XCTAssertEqual(includes[TestEntity6.self].count, 1)
}
func test_SixDifferentIncludes() {
let maybeIncludes = try? decoder.decode(Includes<Include6<TestEntityType, TestEntityType2, TestEntityType3, TestEntityType4, TestEntityType5, TestEntityType6>>.self, from: six_different_type_includes)
XCTAssertNotNil(maybeIncludes)
guard let includes = maybeIncludes else {
return
}
XCTAssertEqual(includes[TestEntity.self].count, 1)
XCTAssertEqual(includes[TestEntity2.self].count, 1)
XCTAssertEqual(includes[TestEntity3.self].count, 1)
XCTAssertEqual(includes[TestEntity4.self].count, 1)
XCTAssertEqual(includes[TestEntity5.self].count, 1)
XCTAssertEqual(includes[TestEntity6.self].count, 1)
}
}
extension IncludedTests {
enum TestEntityType: EntityType {
typealias Identifier = Id<UUID, TestEntityType>
typealias AttributeType = Atts
typealias RelatedType = NoRelatives
public static var type: String { return "test_entity1" }
public struct Atts: Attributes {
let foo: String
let bar: Int
}
}
typealias TestEntity = Entity<TestEntityType>
enum TestEntityType2: EntityType {
typealias Identifier = Id<UUID, TestEntityType2>
typealias AttributeType = Atts
typealias RelatedType = Relationships
public static var type: String { return "test_entity2" }
public struct Relationships: Relatives {
let entity1: ToOneRelationship<TestEntityType>
}
public struct Atts: Attributes {
let foo: String
let bar: Int
}
}
typealias TestEntity2 = Entity<TestEntityType2>
enum TestEntityType3: EntityType {
typealias Identifier = Id<UUID, TestEntityType3>
typealias AttributeType = NoAttributes
typealias RelatedType = Relationships
public static var type: String { return "test_entity3" }
public struct Relationships: Relatives {
let entity1: ToOneRelationship<TestEntityType>
let entity2: ToManyRelationship<TestEntityType2>
}
}
typealias TestEntity3 = Entity<TestEntityType3>
enum TestEntityType4: EntityType {
typealias Identifier = Id<UUID, TestEntityType4>
typealias AttributeType = NoAttributes
typealias RelatedType = NoRelatives
public static var type: String { return "test_entity4" }
}
typealias TestEntity4 = Entity<TestEntityType4>
enum TestEntityType5: EntityType {
typealias Identifier = Id<UUID, TestEntityType5>
typealias AttributeType = NoAttributes
typealias RelatedType = NoRelatives
public static var type: String { return "test_entity5" }
}
typealias TestEntity5 = Entity<TestEntityType5>
enum TestEntityType6: EntityType {
typealias Identifier = Id<UUID, TestEntityType6>
typealias AttributeType = NoAttributes
typealias RelatedType = Relationships
public static var type: String { return "test_entity6" }
struct Relationships: Relatives {
let entity4: ToOneRelationship<TestEntityType4>
}
}
typealias TestEntity6 = Entity<TestEntityType6>
}
@@ -0,0 +1,285 @@
//
// IncludeStubs.swift
// ElevatedCore
//
// Created by Mathew Polzin on 11/10/18.
//
import Foundation
let one_include = """
[
{
"type": "test_entity1",
"id": "2DF03B69-4B0A-467F-B52E-B0C9E44FCECF",
"attributes": {
"foo": "Hello",
"bar": 123
}
}
]
""".data(using: .utf8)!
let two_same_type_includes = """
[
{
"type": "test_entity1",
"id": "2DF03B69-4B0A-467F-B52E-B0C9E44FCECF",
"attributes": {
"foo": "Hello",
"bar": 123
}
},
{
"type": "test_entity1",
"id": "90F03B69-4DF1-467F-B52E-B0C9E44FC333",
"attributes": {
"foo": "World",
"bar": 456
}
}
]
""".data(using: .utf8)!
let two_different_type_includes = """
[
{
"type": "test_entity1",
"id": "2DF03B69-4B0A-467F-B52E-B0C9E44FCECF",
"attributes": {
"foo": "Hello",
"bar": 123
}
},
{
"type": "test_entity2",
"id": "90F03B69-4DF1-467F-B52E-B0C9E44FC333",
"attributes": {
"foo": "World",
"bar": 456
},
"relationships": {
"entity1": {
"data": {
"type": "test_entity1",
"id": "2DF03B69-4B0A-467F-B52E-B0C9E44FCECF"
}
}
}
}
]
""".data(using: .utf8)!
let three_different_type_includes = """
[
{
"type": "test_entity1",
"id": "2DF03B69-4B0A-467F-B52E-B0C9E44FCECF",
"attributes": {
"foo": "Hello",
"bar": 123
}
},
{
"type": "test_entity2",
"id": "90F03B69-4DF1-467F-B52E-B0C9E44FC333",
"attributes": {
"foo": "World",
"bar": 456
},
"relationships": {
"entity1": {
"data": {
"type": "test_entity1",
"id": "2DF03B69-4B0A-467F-B52E-B0C9E44FCECF"
}
}
}
},
{
"type": "test_entity4",
"id": "364B3B69-4DF1-467F-B52E-B0C9E44F666E"
}
]
""".data(using: .utf8)!
let four_different_type_includes = """
[
{
"type": "test_entity1",
"id": "2DF03B69-4B0A-467F-B52E-B0C9E44FCECF",
"attributes": {
"foo": "Hello",
"bar": 123
}
},
{
"type": "test_entity2",
"id": "90F03B69-4DF1-467F-B52E-B0C9E44FC333",
"attributes": {
"foo": "World",
"bar": 456
},
"relationships": {
"entity1": {
"data": {
"type": "test_entity1",
"id": "2DF03B69-4B0A-467F-B52E-B0C9E44FCECF"
}
}
}
},
{
"type": "test_entity6",
"id": "11113B69-4DF1-467F-B52E-B0C9E44FC444",
"relationships": {
"entity4": {
"data": {
"type": "test_entity4",
"id": "364B3B69-4DF1-467F-B52E-B0C9E44F666E"
}
}
}
},
{
"type": "test_entity4",
"id": "364B3B69-4DF1-467F-B52E-B0C9E44F666E"
}
]
""".data(using: .utf8)!
let five_different_type_includes = """
[
{
"type": "test_entity1",
"id": "2DF03B69-4B0A-467F-B52E-B0C9E44FCECF",
"attributes": {
"foo": "Hello",
"bar": 123
}
},
{
"type": "test_entity2",
"id": "90F03B69-4DF1-467F-B52E-B0C9E44FC333",
"attributes": {
"foo": "World",
"bar": 456
},
"relationships": {
"entity1": {
"data": {
"type": "test_entity1",
"id": "2DF03B69-4B0A-467F-B52E-B0C9E44FCECF"
}
}
}
},
{
"type": "test_entity3",
"id": "11223B69-4DF1-467F-B52E-B0C9E44FC443",
"relationships": {
"entity1": {
"data": {
"type": "test_entity1",
"id": "2DF03B69-4B0A-467F-B52E-B0C9E44FCECF"
}
},
"entity2": {
"data": [
{
"type": "test_entity2",
"id": "90F03B69-4DF1-467F-B52E-B0C9E44FC333"
}
]
}
}
},
{
"type": "test_entity6",
"id": "11113B69-4DF1-467F-B52E-B0C9E44FC444",
"relationships": {
"entity4": {
"data": {
"type": "test_entity4",
"id": "364B3B69-4DF1-467F-B52E-B0C9E44F666E"
}
}
}
},
{
"type": "test_entity4",
"id": "364B3B69-4DF1-467F-B52E-B0C9E44F666E"
}
]
""".data(using: .utf8)!
let six_different_type_includes = """
[
{
"type": "test_entity1",
"id": "2DF03B69-4B0A-467F-B52E-B0C9E44FCECF",
"attributes": {
"foo": "Hello",
"bar": 123
}
},
{
"type": "test_entity2",
"id": "90F03B69-4DF1-467F-B52E-B0C9E44FC333",
"attributes": {
"foo": "World",
"bar": 456
},
"relationships": {
"entity1": {
"data": {
"type": "test_entity1",
"id": "2DF03B69-4B0A-467F-B52E-B0C9E44FCECF"
}
}
}
},
{
"type": "test_entity3",
"id": "11223B69-4DF1-467F-B52E-B0C9E44FC443",
"relationships": {
"entity1": {
"data": {
"type": "test_entity1",
"id": "2DF03B69-4B0A-467F-B52E-B0C9E44FCECF"
}
},
"entity2": {
"data": [
{
"type": "test_entity2",
"id": "90F03B69-4DF1-467F-B52E-B0C9E44FC333"
}
]
}
}
},
{
"type": "test_entity6",
"id": "11113B69-4DF1-467F-B52E-B0C9E44FC444",
"relationships": {
"entity4": {
"data": {
"type": "test_entity4",
"id": "364B3B69-4DF1-467F-B52E-B0C9E44F666E"
}
}
}
},
{
"type": "test_entity5",
"id": "A24B3B69-4DF1-467F-B52E-B0C9E44F436A"
},
{
"type": "test_entity4",
"id": "364B3B69-4DF1-467F-B52E-B0C9E44F666E"
}
]
""".data(using: .utf8)!
@@ -0,0 +1,10 @@
[
{
"type": "test_entity1",
"id": "2DF03B69-4B0A-467F-B52E-B0C9E44FCECF",
"attributes": {
"foo": "Hello",
"bar": 123
}
}
]
@@ -0,0 +1,30 @@
[
{
"type": "test_entity1",
"id": "2DF03B69-4B0A-467F-B52E-B0C9E44FCECF",
"attributes": {
"foo": "Hello",
"bar": 123
}
},
{
"type": "test_entity2",
"id": "90F03B69-4DF1-467F-B52E-B0C9E44FC333",
"attributes": {
"foo": "World",
"bar": 456
},
"relationships": {
"entity1": {
"data": {
"type": "test_entity1",
"id": "2DF03B69-4B0A-467F-B52E-B0C9E44FCECF"
}
}
}
},
{
"type": "test_entity4",
"id": "364B3B69-4DF1-467F-B52E-B0C9E44F666E"
}
]

Some files were not shown because too many files have changed in this diff Show More