mirror of
https://github.com/encounter/JSONAPI.git
synced 2026-03-30 11:18:38 -07:00
Adding more custom string convertible implementations to reduce the verbosity of printing to terminal. expose JSONAPIDocument.Include so that its constructor can be used.
This commit is contained in:
@@ -12,6 +12,7 @@ Please enjoy these examples, but allow me the forced casting and the lack of err
|
||||
let dogFromCode = try! Dog(name: "Buddy", owner: nil)
|
||||
|
||||
typealias SingleDogDocument = JSONAPIDocument<SingleResourceBody<Dog>, NoIncludes, BasicJSONAPIError>
|
||||
|
||||
let singleDogDocument = SingleDogDocument(body: SingleResourceBody(entity: dogFromCode))
|
||||
|
||||
let singleDogData = try! JSONEncoder().encode(singleDogDocument)
|
||||
@@ -21,10 +22,15 @@ let dogResponse = try! JSONDecoder().decode(SingleDogDocument.self, from: single
|
||||
let dogFromData = dogResponse.body.data?.primary.value
|
||||
|
||||
// MARK: - Create a request or response with multiple people and dogs and houses included
|
||||
//let people
|
||||
let personIds = [Person.Identifier(), Person.Identifier()]
|
||||
let dogs = try [Dog(name: "Buddy", owner: personIds[0]), Dog(name: "Joy", owner: personIds[0]), Dog(name: "Travis", owner: personIds[1])]
|
||||
let houses = [House(), House()]
|
||||
let people = try [Person(id: personIds[0], name: ["Gary", "Doe"], favoriteColor: "Orange-Red", friends: [], dogs: [dogs[0], dogs[1]], home: houses[0]), Person(id: personIds[1], name: ["Elise", "Joy"], favoriteColor: "Red", friends: [], dogs: [dogs[2]], home: houses[1])]
|
||||
|
||||
//typealias BatchPeopleDocument = JSONAPIDocument<ManyResourceBody<Person>, Include2<Dog, House>, BasicJSONAPIError>
|
||||
typealias BatchPeopleDocument = JSONAPIDocument<ManyResourceBody<Person>, Include2<Dog, House>, BasicJSONAPIError>
|
||||
|
||||
let includes = dogs.map { BatchPeopleDocument.Include($0) } + houses.map { BatchPeopleDocument.Include($0) }
|
||||
let batchPeopleDocument = BatchPeopleDocument(body: .init(entities: people), includes: .init(values: includes))
|
||||
|
||||
// MARK: - Parse a request or response body with multiple people in it and dogs and houses included
|
||||
|
||||
|
||||
@@ -46,8 +46,8 @@ public enum PersonDescription: EntityDescription {
|
||||
public typealias Person = ExampleEntity<PersonDescription>
|
||||
|
||||
public extension Entity where Description == PersonDescription, Identifier == Id<String, PersonDescription> {
|
||||
public init(name: [String], favoriteColor: String, friends: [Person], dogs: [Dog], home: House) throws {
|
||||
self = try Person(attributes: .init(name: .init(rawValue: name), favoriteColor: .init(rawValue: favoriteColor)), relationships: .init(friends: .init(entities: friends), dogs: .init(entities: dogs), home: .init(entity: home)))
|
||||
public init(id: Person.Identifier? = nil,name: [String], favoriteColor: String, friends: [Person], dogs: [Dog], home: House) throws {
|
||||
self = try Person(id: id ?? Person.Identifier(), attributes: .init(name: .init(rawValue: name), favoriteColor: .init(rawValue: favoriteColor)), relationships: .init(friends: .init(entities: friends), dogs: .init(entities: dogs), home: .init(entity: home)))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,6 +70,10 @@ public extension Entity where Description == DogDescription, Identifier == Id<St
|
||||
public init(name: String, owner: Person?) throws {
|
||||
self = try Dog(attributes: .init(name: .init(rawValue: name)), relationships: DogDescription.Relationships(owner: .init(entity: owner)))
|
||||
}
|
||||
|
||||
public init(name: String, owner: Person.Identifier) throws {
|
||||
self = try Dog(attributes: .init(name: .init(rawValue: name)), relationships: .init(owner: .init(id: owner)))
|
||||
}
|
||||
}
|
||||
|
||||
public enum HouseDescription: EntityDescription {
|
||||
|
||||
@@ -12,7 +12,9 @@
|
||||
/// 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<ResourceBody: JSONAPI.ResourceBody, Include: IncludeDecoder, Error: JSONAPIError>: Equatable {
|
||||
public struct JSONAPIDocument<ResourceBody: JSONAPI.ResourceBody, IncludeType: JSONAPI.Include, Error: JSONAPIError>: Equatable {
|
||||
public typealias Include = IncludeType
|
||||
|
||||
public let body: Body
|
||||
// public let meta: Meta?
|
||||
// public let jsonApi: APIDescription?
|
||||
@@ -42,7 +44,7 @@ public struct JSONAPIDocument<ResourceBody: JSONAPI.ResourceBody, Include: Inclu
|
||||
}
|
||||
}
|
||||
|
||||
extension JSONAPIDocument where Include == NoIncludes {
|
||||
extension JSONAPIDocument where IncludeType == NoIncludes {
|
||||
public init(body: ResourceBody) {
|
||||
self.body = .data(primary: body, included: .none)
|
||||
}
|
||||
@@ -96,3 +98,11 @@ extension JSONAPIDocument: Codable {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - CustomStringConvertible
|
||||
|
||||
extension JSONAPIDocument: CustomStringConvertible {
|
||||
public var description: String {
|
||||
return "JSONAPIDocument(body: \(String(describing: body))"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,14 +7,14 @@
|
||||
|
||||
import Result
|
||||
|
||||
public protocol IncludeDecoder: Codable, Equatable {}
|
||||
public protocol Include: Codable, Equatable {}
|
||||
|
||||
public struct Includes<I: IncludeDecoder>: Codable, Equatable {
|
||||
public struct Includes<I: Include>: Codable, Equatable {
|
||||
public static var none: Includes { return .init(values: []) }
|
||||
|
||||
let values: [I]
|
||||
|
||||
private init(values: [I]) {
|
||||
public init(values: [I]) {
|
||||
self.values = values
|
||||
}
|
||||
|
||||
@@ -52,6 +52,12 @@ public struct Includes<I: IncludeDecoder>: Codable, Equatable {
|
||||
}
|
||||
}
|
||||
|
||||
extension Includes: CustomStringConvertible {
|
||||
public var description: String {
|
||||
return "Includes(\(String(describing: values))"
|
||||
}
|
||||
}
|
||||
|
||||
extension Includes where I == NoIncludes {
|
||||
public init() {
|
||||
values = []
|
||||
@@ -77,7 +83,7 @@ func decode<Entity: JSONAPI.EntityType>(_ type: Entity.Type, from container: Sin
|
||||
|
||||
// MARK: - 0 includes
|
||||
|
||||
public protocol _Include0: IncludeDecoder { }
|
||||
public protocol _Include0: Include { }
|
||||
public struct Include0: _Include0 {
|
||||
|
||||
public init() {}
|
||||
@@ -95,6 +101,8 @@ public typealias NoIncludes = Include0
|
||||
public protocol _Include1: _Include0 {
|
||||
associatedtype A: EntityType
|
||||
var a: A? { get }
|
||||
|
||||
init(_ a: A)
|
||||
}
|
||||
public enum Include1<A: EntityType>: _Include1 {
|
||||
case a(A)
|
||||
@@ -103,6 +111,10 @@ public enum Include1<A: EntityType>: _Include1 {
|
||||
guard case let .a(ret) = self else { return nil }
|
||||
return ret
|
||||
}
|
||||
|
||||
public init(_ a: A) {
|
||||
self = .a(a)
|
||||
}
|
||||
|
||||
public init(from decoder: Decoder) throws {
|
||||
let container = try decoder.singleValueContainer()
|
||||
@@ -130,6 +142,8 @@ extension Includes where I: _Include1 {
|
||||
public protocol _Include2: _Include1 {
|
||||
associatedtype B: EntityType
|
||||
var b: B? { get }
|
||||
|
||||
init(_ b: B)
|
||||
}
|
||||
public enum Include2<A: EntityType, B: EntityType>: _Include2 {
|
||||
case a(A)
|
||||
@@ -139,11 +153,19 @@ public enum Include2<A: EntityType, B: EntityType>: _Include2 {
|
||||
guard case let .a(ret) = self else { return nil }
|
||||
return ret
|
||||
}
|
||||
|
||||
public init(_ a: A) {
|
||||
self = .a(a)
|
||||
}
|
||||
|
||||
public var b: B? {
|
||||
guard case let .b(ret) = self else { return nil }
|
||||
return ret
|
||||
}
|
||||
|
||||
public init(_ b: B) {
|
||||
self = .b(b)
|
||||
}
|
||||
|
||||
public init(from decoder: Decoder) throws {
|
||||
let container = try decoder.singleValueContainer()
|
||||
@@ -185,6 +207,8 @@ extension Includes where I: _Include2 {
|
||||
public protocol _Include3: _Include2 {
|
||||
associatedtype C: EntityType
|
||||
var c: C? { get }
|
||||
|
||||
init(_ c: C)
|
||||
}
|
||||
public enum Include3<A: EntityType, B: EntityType, C: EntityType>: _Include3 {
|
||||
case a(A)
|
||||
@@ -195,16 +219,28 @@ public enum Include3<A: EntityType, B: EntityType, C: EntityType>: _Include3 {
|
||||
guard case let .a(ret) = self else { return nil }
|
||||
return ret
|
||||
}
|
||||
|
||||
public init(_ a: A) {
|
||||
self = .a(a)
|
||||
}
|
||||
|
||||
public var b: B? {
|
||||
guard case let .b(ret) = self else { return nil }
|
||||
return ret
|
||||
}
|
||||
|
||||
public init(_ b: B) {
|
||||
self = .b(b)
|
||||
}
|
||||
|
||||
public var c: C? {
|
||||
guard case let .c(ret) = self else { return nil }
|
||||
return ret
|
||||
}
|
||||
|
||||
public init(_ c: C) {
|
||||
self = .c(c)
|
||||
}
|
||||
|
||||
public init(from decoder: Decoder) throws {
|
||||
let container = try decoder.singleValueContainer()
|
||||
@@ -249,6 +285,8 @@ extension Includes where I: _Include3 {
|
||||
public protocol _Include4: _Include3 {
|
||||
associatedtype D: EntityType
|
||||
var d: D? { get }
|
||||
|
||||
init(_ d: D)
|
||||
}
|
||||
public enum Include4<A: EntityType, B: EntityType, C: EntityType, D: EntityType>: _Include4 {
|
||||
case a(A)
|
||||
@@ -260,21 +298,37 @@ public enum Include4<A: EntityType, B: EntityType, C: EntityType, D: EntityType>
|
||||
guard case let .a(ret) = self else { return nil }
|
||||
return ret
|
||||
}
|
||||
|
||||
public init(_ a: A) {
|
||||
self = .a(a)
|
||||
}
|
||||
|
||||
public var b: B? {
|
||||
guard case let .b(ret) = self else { return nil }
|
||||
return ret
|
||||
}
|
||||
|
||||
public init(_ b: B) {
|
||||
self = .b(b)
|
||||
}
|
||||
|
||||
public var c: C? {
|
||||
guard case let .c(ret) = self else { return nil }
|
||||
return ret
|
||||
}
|
||||
|
||||
public init(_ c: C) {
|
||||
self = .c(c)
|
||||
}
|
||||
|
||||
public var d: D? {
|
||||
guard case let .d(ret) = self else { return nil }
|
||||
return ret
|
||||
}
|
||||
|
||||
public init(_ d: D) {
|
||||
self = .d(d)
|
||||
}
|
||||
|
||||
public init(from decoder: Decoder) throws {
|
||||
let container = try decoder.singleValueContainer()
|
||||
@@ -322,6 +376,8 @@ extension Includes where I: _Include4 {
|
||||
public protocol _Include5: _Include4 {
|
||||
associatedtype E: EntityType
|
||||
var e: E? { get }
|
||||
|
||||
init(_ e: E)
|
||||
}
|
||||
public enum Include5<A: EntityType, B: EntityType, C: EntityType, D: EntityType, E: EntityType>: _Include5 {
|
||||
case a(A)
|
||||
@@ -334,26 +390,46 @@ public enum Include5<A: EntityType, B: EntityType, C: EntityType, D: EntityType,
|
||||
guard case let .a(ret) = self else { return nil }
|
||||
return ret
|
||||
}
|
||||
|
||||
public init(_ a: A) {
|
||||
self = .a(a)
|
||||
}
|
||||
|
||||
public var b: B? {
|
||||
guard case let .b(ret) = self else { return nil }
|
||||
return ret
|
||||
}
|
||||
|
||||
public init(_ b: B) {
|
||||
self = .b(b)
|
||||
}
|
||||
|
||||
public var c: C? {
|
||||
guard case let .c(ret) = self else { return nil }
|
||||
return ret
|
||||
}
|
||||
|
||||
public init(_ c: C) {
|
||||
self = .c(c)
|
||||
}
|
||||
|
||||
public var d: D? {
|
||||
guard case let .d(ret) = self else { return nil }
|
||||
return ret
|
||||
}
|
||||
|
||||
public init(_ d: D) {
|
||||
self = .d(d)
|
||||
}
|
||||
|
||||
public var e: E? {
|
||||
guard case let .e(ret) = self else { return nil }
|
||||
return ret
|
||||
}
|
||||
|
||||
public init(_ e: E) {
|
||||
self = .e(e)
|
||||
}
|
||||
|
||||
public init(from decoder: Decoder) throws {
|
||||
let container = try decoder.singleValueContainer()
|
||||
@@ -404,6 +480,8 @@ extension Includes where I: _Include5 {
|
||||
public protocol _Include6: _Include5 {
|
||||
associatedtype F: EntityType
|
||||
var f: F? { get }
|
||||
|
||||
init(_ f: F)
|
||||
}
|
||||
public enum Include6<A: EntityType, B: EntityType, C: EntityType, D: EntityType, E: EntityType, F: EntityType>: _Include6 {
|
||||
case a(A)
|
||||
@@ -417,31 +495,55 @@ public enum Include6<A: EntityType, B: EntityType, C: EntityType, D: EntityType,
|
||||
guard case let .a(ret) = self else { return nil }
|
||||
return ret
|
||||
}
|
||||
|
||||
public init(_ a: A) {
|
||||
self = .a(a)
|
||||
}
|
||||
|
||||
public var b: B? {
|
||||
guard case let .b(ret) = self else { return nil }
|
||||
return ret
|
||||
}
|
||||
|
||||
public init(_ b: B) {
|
||||
self = .b(b)
|
||||
}
|
||||
|
||||
public var c: C? {
|
||||
guard case let .c(ret) = self else { return nil }
|
||||
return ret
|
||||
}
|
||||
|
||||
public init(_ c: C) {
|
||||
self = .c(c)
|
||||
}
|
||||
|
||||
public var d: D? {
|
||||
guard case let .d(ret) = self else { return nil }
|
||||
return ret
|
||||
}
|
||||
|
||||
public init(_ d: D) {
|
||||
self = .d(d)
|
||||
}
|
||||
|
||||
public var e: E? {
|
||||
guard case let .e(ret) = self else { return nil }
|
||||
return ret
|
||||
}
|
||||
|
||||
public init(_ e: E) {
|
||||
self = .e(e)
|
||||
}
|
||||
|
||||
public var f: F? {
|
||||
guard case let .f(ret) = self else { return nil }
|
||||
return ret
|
||||
}
|
||||
|
||||
public init(_ f: F) {
|
||||
self = .f(f)
|
||||
}
|
||||
|
||||
public init(from decoder: Decoder) throws {
|
||||
let container = try decoder.singleValueContainer()
|
||||
|
||||
@@ -67,3 +67,17 @@ extension ManyResourceBody {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: CustomStringConvertible
|
||||
|
||||
extension SingleResourceBody: CustomStringConvertible {
|
||||
public var description: String {
|
||||
return "ResourceBody(\(String(describing: value)))"
|
||||
}
|
||||
}
|
||||
|
||||
extension ManyResourceBody: CustomStringConvertible {
|
||||
public var description: String {
|
||||
return "ResourceBody(\(String(describing: values)))"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,12 @@ public struct TransformAttribute<RawValue: Codable, Transformer: JSONAPI.Transfo
|
||||
}
|
||||
}
|
||||
|
||||
extension TransformAttribute: CustomStringConvertible {
|
||||
public var description: String {
|
||||
return "Attribute<\(String(describing: Transformer.From.self)) -> \(String(describing: Transformer.To.self))>(\(String(describing: value)))"
|
||||
}
|
||||
}
|
||||
|
||||
extension TransformAttribute: Equatable where Transformer.From: Equatable, Transformer.To: Equatable {}
|
||||
|
||||
public typealias Attribute<T: Codable> = TransformAttribute<T, IdentityTransformer<T>>
|
||||
|
||||
@@ -70,6 +70,12 @@ public struct Entity<Description: JSONAPI.EntityDescription, Identifier: JSONAPI
|
||||
}
|
||||
}
|
||||
|
||||
extension Entity: CustomStringConvertible {
|
||||
public var description: String {
|
||||
return "Entity<\(Entity.type)>(id: \(String(describing: id)), attributes: \(String(describing: attributes)), relationships: \(String(describing: relationships)))"
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Convenience initializers
|
||||
extension Entity where Identifier: CreatableIdType {
|
||||
public init(attributes: Description.Attributes, relationships: Description.Relationships) {
|
||||
|
||||
@@ -16,6 +16,10 @@ public struct ToOneRelationship<Relatable: JSONAPI.OptionalRelatable>: Equatable
|
||||
public var ids: [Relatable.WrappedIdentifier] {
|
||||
return [id]
|
||||
}
|
||||
|
||||
public init(id: Relatable.WrappedIdentifier) {
|
||||
self.id = id
|
||||
}
|
||||
}
|
||||
|
||||
extension ToOneRelationship where Relatable.WrappedIdentifier == Relatable.Identifier {
|
||||
|
||||
Reference in New Issue
Block a user