Skip to content

Commit

Permalink
merge and fix conflicts
Browse files Browse the repository at this point in the history
  • Loading branch information
mattpolzin committed Nov 6, 2019
2 parents e6f82c6 + 83233a7 commit 3e96adf
Show file tree
Hide file tree
Showing 32 changed files with 1,855 additions and 342 deletions.
4 changes: 2 additions & 2 deletions Package.resolved
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
"repositoryURL": "https://github.com/mattpolzin/Poly.git",
"state": {
"branch": null,
"revision": "b24fd3b41bf3126d4c6dede3708135182172af60",
"version": "2.2.0"
"revision": "18cd995be5c28c4dfdc1464e54ee0efb03e215bf",
"version": "2.3.0"
}
}
]
Expand Down
2 changes: 1 addition & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ let package = Package(
targets: ["JSONAPITesting"])
],
dependencies: [
.package(url: "https://github.com/mattpolzin/Poly.git", .upToNextMajor(from: "2.2.0")),
.package(url: "https://github.com/mattpolzin/Poly.git", .upToNextMajor(from: "2.3.0")),
],
targets: [
.target(
Expand Down
4 changes: 1 addition & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -332,13 +332,11 @@ As of Swift 5.1, `Attributes` can be accessed via dynamic member keypath lookup
let favoriteColor: String = person.favoriteColor
```

🗒 `Attributes` can also be accessed via the older `subscript` operator as follows:
:warning: `Attributes` can also be accessed via the older `subscript` operator, but this is a deprecated feature that will be removed in the next major version:
```swift
let favoriteColor: String = person[\.favoriteColor]
```

In both cases you retain type-safety. It is best practice to pick an attribute access syntax and stick with it. At some point in the future the syntax deemed less desirable may be deprecated.

#### `Transformer`

Sometimes you need to use a type that does not encode or decode itself in the way you need to represent it as a serialized JSON object. For example, the Swift `Foundation` type `Date` can encode/decode itself to `Double` out of the box, but you might want to represent dates as ISO 8601 compliant `String`s instead. The Foundation library `JSONDecoder` has a setting to make this adjustment, but for the sake of an example, you could create a `Transformer`.
Expand Down
27 changes: 16 additions & 11 deletions Sources/JSONAPI/Document/Document.swift
Original file line number Diff line number Diff line change
Expand Up @@ -91,16 +91,26 @@ public protocol EncodableJSONAPIDocument: Equatable, Encodable, DocumentBodyCont
Body.Error == Error,
Body.BodyData == BodyData

/// The Body of the Document. This body is either one or more errors
/// with links and metadata attempted to parse but not guaranteed or
/// it is a successful data struct containing all the primary and
/// included resources, the metadata, and the links that this
/// document type specifies.
var body: Body { get }

/// The JSON API Spec calls this the JSON:API Object. It contains version
/// and metadata information about the API itself.
var apiDescription: APIDescription { get }
}

/// A `CodableJSONAPIDocument` supports encoding and decoding of a JSON:API
/// compliant Document.
public protocol CodableJSONAPIDocument: EncodableJSONAPIDocument, Decodable where PrimaryResourceBody: JSONAPI.ResourceBody, IncludeType: Decodable {}
public protocol CodableJSONAPIDocument: EncodableJSONAPIDocument, Decodable where PrimaryResourceBody: JSONAPI.CodableResourceBody, IncludeType: Decodable {}

/// 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
Expand All @@ -109,15 +119,10 @@ public struct Document<PrimaryResourceBody: JSONAPI.EncodableResourceBody, MetaT
public typealias Include = IncludeType
public typealias BodyData = Body.Data

/// The JSON API Spec calls this the JSON:API Object. It contains version
/// and metadata information about the API itself.
// See `EncodableJSONAPIDocument` for documentation.
public let apiDescription: APIDescription

/// The Body of the Document. This body is either one or more errors
/// with links and metadata attempted to parse but not guaranteed or
/// it is a successful data struct containing all the primary and
/// included resources, the metadata, and the links that this
/// document type specifies.
// See `EncodableJSONAPIDocument` for documentation.
public let body: Body

public init(apiDescription: APIDescription,
Expand Down Expand Up @@ -340,7 +345,7 @@ extension Document {
}
}

extension Document: Decodable, CodableJSONAPIDocument where PrimaryResourceBody: ResourceBody, IncludeType: Decodable {
extension Document: Decodable, CodableJSONAPIDocument where PrimaryResourceBody: CodableResourceBody, IncludeType: Decodable {
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: RootCodingKeys.self)

Expand Down Expand Up @@ -557,7 +562,7 @@ extension Document {
}

extension Document.ErrorDocument: Decodable, CodableJSONAPIDocument
where PrimaryResourceBody: ResourceBody, IncludeType: Decodable {
where PrimaryResourceBody: CodableResourceBody, IncludeType: Decodable {
public init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()

Expand All @@ -570,7 +575,7 @@ extension Document.ErrorDocument: Decodable, CodableJSONAPIDocument
}

extension Document.SuccessDocument: Decodable, CodableJSONAPIDocument
where PrimaryResourceBody: ResourceBody, IncludeType: Decodable {
where PrimaryResourceBody: CodableResourceBody, IncludeType: Decodable {
public init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()

Expand Down
6 changes: 3 additions & 3 deletions Sources/JSONAPI/Document/Includes.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,15 @@ public typealias Include = EncodableJSONPoly
///
/// If you have
///
/// `let includes: Includes<Include2<Thing1, Thing2>> = ...`
/// let includes: Includes<Include2<Thing1, Thing2>> = ...
///
/// then you can access all `Thing1` included resources with
///
/// `let includedThings = includes[Thing1.self]`
/// let includedThings = includes[Thing1.self]
public struct Includes<I: Include>: Encodable, Equatable {
public static var none: Includes { return .init(values: []) }

let values: [I]
public let values: [I]

public init(values: [I]) {
self.values = values
Expand Down
22 changes: 10 additions & 12 deletions Sources/JSONAPI/Document/ResourceBody.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,33 +10,31 @@
/// array should be used for no results).
public protocol OptionalEncodablePrimaryResource: Equatable, Encodable {}

/// An `EncodablePrimaryResource` is a `PrimaryResource` that only supports encoding.
/// This is actually more restrictave than `PrimaryResource`, which supports both encoding and
/// decoding.
/// An `EncodablePrimaryResource` is a `CodablePrimaryResource` that only supports encoding.
public protocol EncodablePrimaryResource: OptionalEncodablePrimaryResource {}

/// This protocol allows for `SingleResourceBody` to contain a `null`
/// data object where `ManyResourceBody` cannot (because an empty
/// array should be used for no results).
public protocol OptionalPrimaryResource: OptionalEncodablePrimaryResource, Decodable {}
public protocol OptionalCodablePrimaryResource: OptionalEncodablePrimaryResource, Decodable {}

/// A `PrimaryResource` is a type that can be used in the body of a JSON API
/// A `CodablePrimaryResource` is a type that can be used in the body of a JSON API
/// document as the primary resource.
public protocol PrimaryResource: EncodablePrimaryResource, OptionalPrimaryResource {}
public protocol CodablePrimaryResource: EncodablePrimaryResource, OptionalCodablePrimaryResource {}

extension Optional: OptionalEncodablePrimaryResource where Wrapped: EncodablePrimaryResource {}

extension Optional: OptionalPrimaryResource where Wrapped: PrimaryResource {}
extension Optional: OptionalCodablePrimaryResource where Wrapped: CodablePrimaryResource {}

/// An `EncodableResourceBody` is a `ResourceBody` that only supports being
/// encoded. It is actually weaker than `ResourceBody`, which supports both encoding
/// and decoding.
public protocol EncodableResourceBody: Equatable, Encodable {}

/// A ResourceBody is a representation of the body of the JSON API Document.
/// A `CodableResourceBody` is a representation of the body of the JSON:API Document.
/// It can either be one resource (which can be specified as optional or not)
/// or it can contain many resources (and array with zero or more entries).
public protocol ResourceBody: Decodable, EncodableResourceBody {}
public protocol CodableResourceBody: Decodable, EncodableResourceBody {}

/// A `ResourceBody` that has the ability to take on more primary
/// resources by appending another similarly typed `ResourceBody`.
Expand Down Expand Up @@ -74,7 +72,7 @@ public struct ManyResourceBody<Entity: JSONAPI.EncodablePrimaryResource>: Encoda

/// Use NoResourceBody to indicate you expect a JSON API document to not
/// contain a "data" top-level key.
public struct NoResourceBody: ResourceBody {
public struct NoResourceBody: CodableResourceBody {
public static var none: NoResourceBody { return NoResourceBody() }
}

Expand All @@ -94,7 +92,7 @@ extension SingleResourceBody {
}
}

extension SingleResourceBody: Decodable, ResourceBody where Entity: OptionalPrimaryResource {
extension SingleResourceBody: Decodable, CodableResourceBody where Entity: OptionalCodablePrimaryResource {
public init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()

Expand All @@ -119,7 +117,7 @@ extension ManyResourceBody {
}
}

extension ManyResourceBody: Decodable, ResourceBody where Entity: PrimaryResource {
extension ManyResourceBody: Decodable, CodableResourceBody where Entity: CodablePrimaryResource {
public init(from decoder: Decoder) throws {
var container = try decoder.unkeyedContainer()
var valueAggregator = [Entity]()
Expand Down
6 changes: 5 additions & 1 deletion Sources/JSONAPI/Error/BasicJSONAPIError.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
//

/// Most of the JSON:API Spec defined Error fields.
public struct BasicJSONAPIErrorPayload<IdType: Codable & Equatable>: Codable, Equatable, ErrorDictType {
public struct BasicJSONAPIErrorPayload<IdType: Codable & Equatable>: Codable, Equatable, ErrorDictType, CustomStringConvertible {
/// a unique identifier for this particular occurrence of the problem
public let id: IdType?

Expand Down Expand Up @@ -64,6 +64,10 @@ public struct BasicJSONAPIErrorPayload<IdType: Codable & Equatable>: Codable, Eq
].compactMap { $0 }
return Dictionary(uniqueKeysWithValues: keysAndValues)
}

public var description: String {
return definedFields.map { "\($0.key): \($0.value)" }.sorted().joined(separator: ", ")
}
}

/// `BasicJSONAPIError` optionally decodes many possible fields
Expand Down
11 changes: 10 additions & 1 deletion Sources/JSONAPI/Error/GenericJSONAPIError.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
/// `GenericJSONAPIError` can be used to specify whatever error
/// payload you expect to need to parse in responses and handle any
/// other payload structure as `.unknownError`.
public enum GenericJSONAPIError<ErrorPayload: Codable & Equatable>: JSONAPIError {
public enum GenericJSONAPIError<ErrorPayload: Codable & Equatable>: JSONAPIError, CustomStringConvertible {
case unknownError
case error(ErrorPayload)

Expand All @@ -35,6 +35,15 @@ public enum GenericJSONAPIError<ErrorPayload: Codable & Equatable>: JSONAPIError
public static var unknown: Self {
return .unknownError
}

public var description: String {
switch self {
case .unknownError:
return "unknown error"
case .error(let payload):
return String(describing: payload)
}
}
}

public extension GenericJSONAPIError {
Expand Down
8 changes: 4 additions & 4 deletions Sources/JSONAPI/Meta/Meta.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,17 @@
//

/// Conform a type to this protocol to indicate it can be encoded to or decoded from
/// the meta data attached to a component of a JSON API document. Different meta data
/// the meta data attached to a component of a JSON:API document. Different meta data
/// can be stored all over the place: On the root document, on a resource object, on
/// link objects, etc.
///
/// JSON API Metadata is totally open ended. It can take whatever JSON-compliant structure
/// JSON:API Metadata is totally open ended. It can take whatever JSON-compliant structure
/// the server and client agree upon.
public protocol Meta: Codable, Equatable {
}

// We make Optional a Meta if it wraps a Meta so that Metadata can be specified as
// nullable.
// We make Optional a Meta if it wraps a Meta so that
// Metadata can be specified as nullable.
extension Optional: Meta where Wrapped: Meta {}

/// Use this type when you want to specify not to encode or decode any metadata
Expand Down
24 changes: 12 additions & 12 deletions Sources/JSONAPI/Resource/Poly+PrimaryResource.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public typealias EncodableJSONPoly = Poly & EncodablePrimaryResource
public typealias EncodablePolyWrapped = Encodable & Equatable
public typealias PolyWrapped = EncodablePolyWrapped & Decodable

extension Poly0: PrimaryResource {
extension Poly0: CodablePrimaryResource {
public init(from decoder: Decoder) throws {
throw JSONAPIEncodingError.illegalDecoding("Attempted to decode Poly0, which should represent a thing that is not expected to be found in a document.")
}
Expand All @@ -34,42 +34,42 @@ extension Poly0: PrimaryResource {
extension Poly1: EncodablePrimaryResource, OptionalEncodablePrimaryResource
where A: EncodablePolyWrapped {}

extension Poly1: PrimaryResource, OptionalPrimaryResource
extension Poly1: CodablePrimaryResource, OptionalCodablePrimaryResource
where A: PolyWrapped {}

// MARK: - 2 types
extension Poly2: EncodablePrimaryResource, OptionalEncodablePrimaryResource
where A: EncodablePolyWrapped, B: EncodablePolyWrapped {}

extension Poly2: PrimaryResource, OptionalPrimaryResource
extension Poly2: CodablePrimaryResource, OptionalCodablePrimaryResource
where A: PolyWrapped, B: PolyWrapped {}

// MARK: - 3 types
extension Poly3: EncodablePrimaryResource, OptionalEncodablePrimaryResource
where A: EncodablePolyWrapped, B: EncodablePolyWrapped, C: EncodablePolyWrapped {}

extension Poly3: PrimaryResource, OptionalPrimaryResource
extension Poly3: CodablePrimaryResource, OptionalCodablePrimaryResource
where A: PolyWrapped, B: PolyWrapped, C: PolyWrapped {}

// MARK: - 4 types
extension Poly4: EncodablePrimaryResource, OptionalEncodablePrimaryResource
where A: EncodablePolyWrapped, B: EncodablePolyWrapped, C: EncodablePolyWrapped, D: EncodablePolyWrapped {}

extension Poly4: PrimaryResource, OptionalPrimaryResource
extension Poly4: CodablePrimaryResource, OptionalCodablePrimaryResource
where A: PolyWrapped, B: PolyWrapped, C: PolyWrapped, D: PolyWrapped {}

// MARK: - 5 types
extension Poly5: EncodablePrimaryResource, OptionalEncodablePrimaryResource
where A: EncodablePolyWrapped, B: EncodablePolyWrapped, C: EncodablePolyWrapped, D: EncodablePolyWrapped, E: EncodablePolyWrapped {}

extension Poly5: PrimaryResource, OptionalPrimaryResource
extension Poly5: CodablePrimaryResource, OptionalCodablePrimaryResource
where A: PolyWrapped, B: PolyWrapped, C: PolyWrapped, D: PolyWrapped, E: PolyWrapped {}

// MARK: - 6 types
extension Poly6: EncodablePrimaryResource, OptionalEncodablePrimaryResource
where A: EncodablePolyWrapped, B: EncodablePolyWrapped, C: EncodablePolyWrapped, D: EncodablePolyWrapped, E: EncodablePolyWrapped, F: EncodablePolyWrapped {}

extension Poly6: PrimaryResource, OptionalPrimaryResource
extension Poly6: CodablePrimaryResource, OptionalCodablePrimaryResource
where A: PolyWrapped, B: PolyWrapped, C: PolyWrapped, D: PolyWrapped, E: PolyWrapped, F: PolyWrapped {}

// MARK: - 7 types
Expand All @@ -83,7 +83,7 @@ extension Poly7: EncodablePrimaryResource, OptionalEncodablePrimaryResource
F: EncodablePolyWrapped,
G: EncodablePolyWrapped {}

extension Poly7: PrimaryResource, OptionalPrimaryResource
extension Poly7: CodablePrimaryResource, OptionalCodablePrimaryResource
where A: PolyWrapped, B: PolyWrapped, C: PolyWrapped, D: PolyWrapped, E: PolyWrapped, F: PolyWrapped, G: PolyWrapped {}

// MARK: - 8 types
Expand All @@ -98,7 +98,7 @@ extension Poly8: EncodablePrimaryResource, OptionalEncodablePrimaryResource
G: EncodablePolyWrapped,
H: EncodablePolyWrapped {}

extension Poly8: PrimaryResource, OptionalPrimaryResource
extension Poly8: CodablePrimaryResource, OptionalCodablePrimaryResource
where A: PolyWrapped, B: PolyWrapped, C: PolyWrapped, D: PolyWrapped, E: PolyWrapped, F: PolyWrapped, G: PolyWrapped, H: PolyWrapped {}

// MARK: - 9 types
Expand All @@ -114,7 +114,7 @@ extension Poly9: EncodablePrimaryResource, OptionalEncodablePrimaryResource
H: EncodablePolyWrapped,
I: EncodablePolyWrapped {}

extension Poly9: PrimaryResource, OptionalPrimaryResource
extension Poly9: CodablePrimaryResource, OptionalCodablePrimaryResource
where A: PolyWrapped, B: PolyWrapped, C: PolyWrapped, D: PolyWrapped, E: PolyWrapped, F: PolyWrapped, G: PolyWrapped, H: PolyWrapped, I: PolyWrapped {}

// MARK: - 10 types
Expand All @@ -131,7 +131,7 @@ extension Poly10: EncodablePrimaryResource, OptionalEncodablePrimaryResource
I: EncodablePolyWrapped,
J: EncodablePolyWrapped {}

extension Poly10: PrimaryResource, OptionalPrimaryResource
extension Poly10: CodablePrimaryResource, OptionalCodablePrimaryResource
where A: PolyWrapped, B: PolyWrapped, C: PolyWrapped, D: PolyWrapped, E: PolyWrapped, F: PolyWrapped, G: PolyWrapped, H: PolyWrapped, I: PolyWrapped, J: PolyWrapped {}

// MARK: - 11 types
Expand All @@ -149,7 +149,7 @@ extension Poly11: EncodablePrimaryResource, OptionalEncodablePrimaryResource
J: EncodablePolyWrapped,
K: EncodablePolyWrapped {}

extension Poly11: PrimaryResource, OptionalPrimaryResource
extension Poly11: CodablePrimaryResource, OptionalCodablePrimaryResource
where
A: PolyWrapped,
B: PolyWrapped,
Expand Down
Loading

0 comments on commit 3e96adf

Please sign in to comment.