Compare commits

...

23 Commits

Author SHA1 Message Date
Mathew Polzin fcc1796731 Expose Entity properties on EntityType protocol. 2018-11-27 18:58:48 -08:00
Mathew Polzin 921bcef05d Ids are now Hashable 2018-11-27 18:39:57 -08:00
Mathew Polzin 661ff6eca5 A little renaming and easier access to important types under the JSONAPIDocument protocol 2018-11-27 18:20:01 -08:00
Mathew Polzin e36180c9b9 Make properties of Body.Data public 2018-11-27 18:05:02 -08:00
Mathew Polzin abee0c4d0e Add body variable to JSONAPIDocument protocol 2018-11-27 17:55:56 -08:00
Mathew Polzin 9c8b2fbebb Renamed JSONAPIDocument to Document and created a protocol describing a JSONAPI Document. 2018-11-27 17:35:13 -08:00
Mathew Polzin a9ef71f383 indentation change 2018-11-27 17:15:15 -08:00
Mathew Polzin 232554ec51 Wrap JSONAPIDocument body up a bit nicer for use when the JSONAPIDocument does not represent errors. 2018-11-27 16:12:36 -08:00
Mathew Polzin 8f36bb40e3 Add note about the library being a bit 'opinionated' 2018-11-27 14:04:53 -08:00
Mathew Polzin 55f2f52676 minor README change 2018-11-27 13:59:01 -08:00
Mathew Polzin dd5f3b1737 Added check for nullable array attributes 2018-11-27 13:57:55 -08:00
Mathew Polzin 7d7b3d7f19 Update linuxmain 2018-11-27 13:34:22 -08:00
Mathew Polzin aedd5dc29b Add Entity validation via a function in JSONAPITestLib. 2018-11-27 13:33:55 -08:00
Mathew Polzin 3964202ea2 Mention JSONAPITestLib in the README 2018-11-27 11:53:21 -08:00
Mathew Polzin dcabafd583 Add a playground page to show off JSONAPITestLib and add nil literal expressibility to ToOneRelationship 2018-11-27 11:45:15 -08:00
Mathew Polzin 9df9efc2dc rename NoRelatives to NoRelationships to better pair with NoAttributes and NoLinks 2018-11-27 11:08:53 -08:00
Mathew Polzin a55a4cbed0 Added test library, attribute literal expressibility, and tests 2018-11-27 10:54:51 -08:00
Mathew Polzin 6f6ed87b4c Update included playground 2018-11-27 09:14:47 -08:00
Mathew Polzin 59ff145aa8 Rename the included Error type. 2018-11-27 09:11:13 -08:00
Mathew Polzin 20a685e67b Add goal of making it easier to construct Attributes values 2018-11-26 22:29:31 -08:00
Mathew Polzin 3d8d10584b Add typealiases to make accessing Entity Relationships and Attributes types more convenient 2018-11-26 22:27:10 -08:00
Mathew Polzin b93580c900 Allow ManyResourceBody to use any PrimaryResource type 2018-11-26 18:57:34 -08:00
Mathew Polzin 8927938d56 Rename URL to JSONAPIURL to avoid conflict with Foundation.URL 2018-11-26 18:29:24 -08:00
31 changed files with 859 additions and 172 deletions
@@ -0,0 +1,24 @@
//: [Previous](@previous)
import Foundation
import JSONAPI
import JSONAPITestLib
/*******
Please enjoy these examples, but allow me the forced casting and the lack of error checking for the sake of brevity.
********/
// MARK: - Literal Expressibility
// The JSONAPITestLib provides literal expressibility for key types to
// make creating tests easier
let dog = Dog(id: "1234", attributes: Dog.Attributes(name: "Buddy"), relationships: Dog.Relationships(owner: nil))
// MARK: - JSON API structure checking
// The JSONAPITestLib provides a `check` function for each Entity type
// that uses reflection to catch mistakes that are not forbidden by
// Swift's type system but will result in unexpected results when
// encoding/decoding. It is a good idea to add a `check` to each of
// your unit tests that create Entities.
try Dog.check(dog)
@@ -11,7 +11,7 @@ Please enjoy these examples, but allow me the forced casting and the lack of err
// MARK: - Create a request or response body with one Dog in it // MARK: - Create a request or response body with one Dog in it
let dogFromCode = try! Dog(name: "Buddy", owner: nil) let dogFromCode = try! Dog(name: "Buddy", owner: nil)
typealias SingleDogDocument = JSONAPIDocument<SingleResourceBody<Dog>, NoIncludes, BasicJSONAPIError> typealias SingleDogDocument = JSONAPI.Document<SingleResourceBody<Dog>, NoMetadata, NoLinks, NoIncludes, UnknownJSONAPIError>
let singleDogDocument = SingleDogDocument(body: SingleResourceBody(entity: dogFromCode)) let singleDogDocument = SingleDogDocument(body: SingleResourceBody(entity: dogFromCode))
@@ -19,7 +19,7 @@ let singleDogData = try! JSONEncoder().encode(singleDogDocument)
// MARK: - Parse a request or response body with one Dog in it // MARK: - Parse a request or response body with one Dog in it
let dogResponse = try! JSONDecoder().decode(SingleDogDocument.self, from: singleDogData) let dogResponse = try! JSONDecoder().decode(SingleDogDocument.self, from: singleDogData)
let dogFromData = dogResponse.body.data?.primary.value let dogFromData = dogResponse.body.primaryData?.value
// MARK: - Create a request or response with multiple people and dogs and houses included // MARK: - Create a request or response with multiple people and dogs and houses included
let personIds = [Person.Identifier(), Person.Identifier()] let personIds = [Person.Identifier(), Person.Identifier()]
@@ -27,7 +27,7 @@ let dogs = try! [Dog(name: "Buddy", owner: personIds[0]), Dog(name: "Joy", owner
let houses = [House(), House()] 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])] 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 = JSONAPI.Document<ManyResourceBody<Person>, NoMetadata, NoLinks, Include2<Dog, House>, UnknownJSONAPIError>
let includes = dogs.map { BatchPeopleDocument.Include($0) } + houses.map { BatchPeopleDocument.Include($0) } let includes = dogs.map { BatchPeopleDocument.Include($0) } + houses.map { BatchPeopleDocument.Include($0) }
let batchPeopleDocument = BatchPeopleDocument(body: .init(entities: people), includes: .init(values: includes)) let batchPeopleDocument = BatchPeopleDocument(body: .init(entities: people), includes: .init(values: includes))
@@ -36,6 +36,24 @@ let batchPeopleData = try! JSONEncoder().encode(batchPeopleDocument)
// MARK: - Parse a request or response body with multiple people in it and dogs and houses included // MARK: - Parse a request or response body with multiple people in it and dogs and houses included
let peopleResponse = try! JSONDecoder().decode(BatchPeopleDocument.self, from: batchPeopleData) let peopleResponse = try! JSONDecoder().decode(BatchPeopleDocument.self, from: batchPeopleData)
let peopleFromData = peopleResponse.body.data?.primary.values let peopleFromData = peopleResponse.body.primaryData?.values
let dogsFromData = peopleResponse.body.data?.included[Dog.self] let dogsFromData = peopleResponse.body.includes?[Dog.self]
let housesFromData = peopleResponse.body.data?.included[House.self] let housesFromData = peopleResponse.body.includes?[House.self]
// MARK: - Pass successfully parsed body to other parts of the code
if case let .data(bodyData) = peopleResponse.body {
print(bodyData)
} else {
print("no body data")
}
// MARK: - Work in the abstract
func process<T: JSONAPIDocument>(document: T) {
guard case let .data(body) = document.body else {
return
}
let x: T.BodyData = body
}
process(document: peopleResponse)
+20 -1
View File
@@ -34,12 +34,23 @@ public enum PersonDescription: EntityDescription {
public struct Attributes: JSONAPI.Attributes { public struct Attributes: JSONAPI.Attributes {
public let name: Attribute<[String]> public let name: Attribute<[String]>
public let favoriteColor: Attribute<String> public let favoriteColor: Attribute<String>
public init(name: Attribute<[String]>, favoriteColor: Attribute<String>) {
self.name = name
self.favoriteColor = favoriteColor
}
} }
public struct Relationships: JSONAPI.Relationships { public struct Relationships: JSONAPI.Relationships {
public let friends: ToManyRelationship<Person> public let friends: ToManyRelationship<Person>
public let dogs: ToManyRelationship<Dog> public let dogs: ToManyRelationship<Dog>
public let home: ToOneRelationship<House> public let home: ToOneRelationship<House>
public init(friends: ToManyRelationship<Person>, dogs: ToManyRelationship<Dog>, home: ToOneRelationship<House>) {
self.friends = friends
self.dogs = dogs
self.home = home
}
} }
} }
@@ -57,10 +68,18 @@ public enum DogDescription: EntityDescription {
public struct Attributes: JSONAPI.Attributes { public struct Attributes: JSONAPI.Attributes {
public let name: Attribute<String> public let name: Attribute<String>
public init(name: Attribute<String>) {
self.name = name
}
} }
public struct Relationships: JSONAPI.Relationships { public struct Relationships: JSONAPI.Relationships {
public let owner: ToOneRelationship<Person?> public let owner: ToOneRelationship<Person?>
public init(owner: ToOneRelationship<Person?>) {
self.owner = owner
}
} }
} }
@@ -81,7 +100,7 @@ public enum HouseDescription: EntityDescription {
public static var type: String { return "houses" } public static var type: String { return "houses" }
public typealias Attributes = NoAttributes public typealias Attributes = NoAttributes
public typealias Relationships = NoRelatives public typealias Relationships = NoRelationships
} }
public typealias House = ExampleEntity<HouseDescription> public typealias House = ExampleEntity<HouseDescription>
+5 -2
View File
@@ -1,4 +1,7 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<playground version='5.0' target-platform='macos' executeOnSourceChanges='false'> <playground version='6.0' target-platform='macos' executeOnSourceChanges='false'>
<timeline fileName='timeline.xctimeline'/> <pages>
<page name='Test Library'/>
<page name='Usage'/>
</pages>
</playground> </playground>
+8 -4
View File
@@ -6,23 +6,27 @@ import PackageDescription
let package = Package( let package = Package(
name: "JSONAPI", name: "JSONAPI",
products: [ products: [
// Products define the executables and libraries produced by a package, and make them visible to other packages.
.library( .library(
name: "JSONAPI", name: "JSONAPI",
targets: ["JSONAPI"]), targets: ["JSONAPI"]),
.library(
name: "JSONAPITestLib",
targets: ["JSONAPITestLib"])
], ],
dependencies: [ dependencies: [
// antitypical/Result without the Foundation requirement:
.package(url: "https://github.com/mattpolzin/Result", .branch("master")) .package(url: "https://github.com/mattpolzin/Result", .branch("master"))
], ],
targets: [ 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( .target(
name: "JSONAPI", name: "JSONAPI",
dependencies: ["Result"]), dependencies: ["Result"]),
.target(
name: "JSONAPITestLib",
dependencies: ["JSONAPI"]),
.testTarget( .testTarget(
name: "JSONAPITests", name: "JSONAPITests",
dependencies: ["JSONAPI"]), dependencies: ["JSONAPITestLib"])
], ],
swiftLanguageVersions: [.v4_2] swiftLanguageVersions: [.v4_2]
) )
+27 -12
View File
@@ -13,6 +13,19 @@ The primary goals of this framework are:
3. Do not sacrifice type safety. 3. Do not sacrifice type safety.
4. Be platform agnostic so that Swift code can be written once and used by both the client and the server. 4. Be platform agnostic so that Swift code can be written once and used by both the client and the server.
### Caveat
The big caveat is that, although the aim is to support the JSON API spec, this framework ends up being _naturally_ opinionated about certain things that the API Spec does not specify. These caveats are largely a side effect of attempting to write the library in a "Swifty" way.
If you find that something in the JSON API v1.0 Spec is not explicitly missing from the **Project Status** checklist but this library does not support it, please let me know! I want to keep working towards a library implementation that is useful in any application.
## Dev Environment
### Prerequisites
1. Swift 4.2+ and Swift Package Manager
### Xcode project
To create an Xcode project for JSONAPI, run
`swift package generate-xcodeproj`
## Project Status ## Project Status
### Decoding ### Decoding
@@ -67,10 +80,10 @@ The primary goals of this framework are:
- [x] `href` - [x] `href`
- [x] `meta` - [x] `meta`
### EntityDescription Validator (using reflection) ### Entity Validator (using reflection)
- [ ] Disallow optional array in `Attribute` and `Relationship` (should be empty array, not `null`). - [x] Disallow optional array in `Attribute` (should be empty array, not `null`).
- [ ] Only allow `Attribute` and `TransformAttribute` within `Attributes` struct. - [x] Only allow `TransformedAttribute` and its derivatives within `Attributes` struct.
- [ ] Only allow `ToManyRelationship` and `ToOneRelationship` within `Relationships` struct. - [x] Only allow `ToManyRelationship` and `ToOneRelationship` within `Relationships` struct.
### Strict Decoding/Encoding Settings ### Strict Decoding/Encoding Settings
- [ ] Error (potentially while still encoding/decoding successfully) if an included entity is not related to a primary entity (Turned off by default). - [ ] Error (potentially while still encoding/decoding successfully) if an included entity is not related to a primary entity (Turned off by default).
@@ -86,10 +99,9 @@ The primary goals of this framework are:
- [ ] Property-based testing (using `SwiftCheck`) - [ ] Property-based testing (using `SwiftCheck`)
- [x] Roll my own `Result` or find an alternative that doesn't use `Foundation`. - [x] Roll my own `Result` or find an alternative that doesn't use `Foundation`.
- [ ] Create more descriptive errors that are easier to use for troubleshooting. - [ ] Create more descriptive errors that are easier to use for troubleshooting.
- [x] Make it easier to construct `Attributes` and `Relationships` values in test cases (literal expressibility).
## Usage ## Usage
### Prerequisites
1. Swift 4.2+ and Swift Package Manager
### `EntityDescription` ### `EntityDescription`
@@ -113,7 +125,7 @@ enum PersonDescription: IdentifiedEntityDescription {
The requirements of an `EntityDescription` are: The requirements of an `EntityDescription` are:
1. A static `var` "type" that matches the JSON type; The JSON spec requires every *Resource Object* to have a "type". 1. A static `var` "type" that matches the JSON type; The JSON spec requires every *Resource Object* to have a "type".
2. A `struct` of `Attributes` **- OR -** `typealias Attributes = NoAttributes` 2. A `struct` of `Attributes` **- OR -** `typealias Attributes = NoAttributes`
3. A `struct` of `Relationships` **- OR -** `typealias Relationships = NoRelatives` 3. A `struct` of `Relationships` **- OR -** `typealias Relationships = NoRelationships`
Note that an `enum` type is used here for the `EntityDescription`; it could have been a `struct`, but `EntityDescription`s do not ever need to be created so an `enum` with no `case`s is a nice fit for the job. Note that an `enum` type is used here for the `EntityDescription`; it could have been a `struct`, but `EntityDescription`s do not ever need to be created so an `enum` with no `case`s is a nice fit for the job.
@@ -188,7 +200,7 @@ let nullableRelative: ToOneRelationship<Person?>
An entity that does not have relationships can be described by adding the following to an `EntityDescription`: An entity that does not have relationships can be described by adding the following to an `EntityDescription`:
``` ```
typealias Relationships = NoRelatives typealias Relationships = NoRelationships
``` ```
`Relationship`s boil down to Ids of other entities. To access the Id of a related entity, you can use the shorthand `~>` operator with the `KeyPath` of the `Relationship` from which you want the Id. The friends of the above `Person` entity could be accessed as follows (type annotations for clarity): `Relationship`s boil down to Ids of other entities. To access the Id of a related entity, you can use the shorthand `~>` operator with the `KeyPath` of the `Relationship` from which you want the Id. The friends of the above `Person` entity could be accessed as follows (type annotations for clarity):
@@ -250,7 +262,7 @@ The entirety of a JSON API request or response is encoded or decoded from- or to
``` ```
let decoder = JSONDecoder() let decoder = JSONDecoder()
let responseStructure = JSONAPIDocument<SingleResourceBody<Person>, NoMetadata, NoLinks, NoIncludes, BasicJSONAPIError>.self let responseStructure = JSONAPIDocument<SingleResourceBody<Person>, NoMetadata, NoLinks, NoIncludes, UnknownJSONAPIError>.self
let document = try decoder.decode(responseStructure, from: data) let document = try decoder.decode(responseStructure, from: data)
``` ```
@@ -279,9 +291,9 @@ The second generic type of a `JSONAPIDocument` is a `Meta`. This structure is en
You would then create the following `Meta` type: You would then create the following `Meta` type:
``` ```
struct PageMetadata: JSONAPI.Meta { struct PageMetadata: JSONAPI.Meta {
let total: Int let total: Int
let limit: Int let limit: Int
let offset: Int let offset: Int
} }
``` ```
@@ -321,3 +333,6 @@ extension String: CreatableRawIdType {
} }
} }
``` ```
# JSONAPITestLib
JSONAPI comes with a test library to help you test your JSON API integration. The test library is called `JSONAPITestLib`. It provides literal expressibility for `Attribute`, `ToOneRelationship`, and `Id` in many situations so that you can easily write test `Entity` values into your unit tests. It also provides a `check()` function for each `Entity` type that can be used to catch problems with your JSONAPI structures that are not caught by Swift's type system. You can see the JSONAPITestLib in action in the Playground included with the JSONAPI repository.
+68 -46
View File
@@ -5,6 +5,19 @@
// Created by Mathew Polzin on 11/5/18. // Created by Mathew Polzin on 11/5/18.
// //
public protocol JSONAPIDocument: Codable, Equatable {
associatedtype PrimaryResourceBody: JSONAPI.ResourceBody
associatedtype MetaType: JSONAPI.Meta
associatedtype LinksType: JSONAPI.Links
associatedtype IncludeType: JSONAPI.Include
associatedtype Error: JSONAPIError
typealias Body = Document<PrimaryResourceBody, MetaType, LinksType, IncludeType, Error>.Body
typealias BodyData = Body.Data
var body: Body { get }
}
/// A JSON API Document represents the entire body /// A JSON API Document represents the entire body
/// of a JSON API request or the entire body of /// of a JSON API request or the entire body of
/// a JSON API response. /// a JSON API response.
@@ -12,7 +25,7 @@
/// API uses snake case, you will want to use /// API uses snake case, you will want to use
/// a conversion such as the one offerred by the /// a conversion such as the one offerred by the
/// Foundation JSONEncoder/Decoder: `KeyDecodingStrategy` /// Foundation JSONEncoder/Decoder: `KeyDecodingStrategy`
public struct JSONAPIDocument<ResourceBody: JSONAPI.ResourceBody, MetaType: JSONAPI.Meta, LinksType: JSONAPI.Links, IncludeType: JSONAPI.Include, Error: JSONAPIError>: Equatable { public struct Document<PrimaryResourceBody: JSONAPI.ResourceBody, MetaType: JSONAPI.Meta, LinksType: JSONAPI.Links, IncludeType: JSONAPI.Include, Error: JSONAPIError>: JSONAPIDocument {
public typealias Include = IncludeType public typealias Include = IncludeType
public let body: Body public let body: Body
@@ -20,7 +33,14 @@ public struct JSONAPIDocument<ResourceBody: JSONAPI.ResourceBody, MetaType: JSON
public enum Body: Equatable { public enum Body: Equatable {
case errors([Error], meta: MetaType?, links: LinksType?) case errors([Error], meta: MetaType?, links: LinksType?)
case data(primary: ResourceBody, included: Includes<Include>, meta: MetaType, links: LinksType) case data(Data)
public struct Data: Equatable {
public let primary: PrimaryResourceBody
public let includes: Includes<Include>
public let meta: MetaType
public let links: LinksType
}
public var isError: Bool { public var isError: Bool {
guard case .errors = self else { return false } guard case .errors = self else { return false }
@@ -32,20 +52,21 @@ public struct JSONAPIDocument<ResourceBody: JSONAPI.ResourceBody, MetaType: JSON
return errors return errors
} }
public var primaryData: ResourceBody? { public var primaryData: PrimaryResourceBody? {
guard case let .data(primary: body, included: _, meta: _, links: _) = self else { return nil } guard case let .data(data) = self else { return nil }
return body return data.primary
} }
public var includes: Includes<Include>? { public var includes: Includes<Include>? {
guard case let .data(primary: _, included: includes, meta: _, links: _) = self else { return nil } guard case let .data(data) = self else { return nil }
return includes return data.includes
} }
public var meta: MetaType? { public var meta: MetaType? {
switch self { switch self {
case .data(primary: _, included: _, meta: let metadata, links: _), case .data(let data):
.errors(_, meta: let metadata?, links: _): return data.meta
case .errors(_, meta: let metadata?, links: _):
return metadata return metadata
default: default:
return nil return nil
@@ -54,8 +75,9 @@ public struct JSONAPIDocument<ResourceBody: JSONAPI.ResourceBody, MetaType: JSON
public var links: LinksType? { public var links: LinksType? {
switch self { switch self {
case .data(primary: _, included: _, meta: _, links: let links), case .data(let data):
.errors(_, meta: _, links: let links?): return data.links
case .errors(_, meta: _, links: let links?):
return links return links
default: default:
return nil return nil
@@ -67,54 +89,54 @@ public struct JSONAPIDocument<ResourceBody: JSONAPI.ResourceBody, MetaType: JSON
body = .errors(errors, meta: meta, links: links) body = .errors(errors, meta: meta, links: links)
} }
public init(body: ResourceBody, includes: Includes<Include>, meta: MetaType, links: LinksType) { public init(body: PrimaryResourceBody, includes: Includes<Include>, meta: MetaType, links: LinksType) {
self.body = .data(primary: body, included: includes, meta: meta, links: links) self.body = .data(.init(primary: body, includes: includes, meta: meta, links: links))
} }
} }
extension JSONAPIDocument where IncludeType == NoIncludes { extension Document where IncludeType == NoIncludes {
public init(body: ResourceBody, meta: MetaType, links: LinksType) { public init(body: PrimaryResourceBody, meta: MetaType, links: LinksType) {
self.body = .data(primary: body, included: .none, meta: meta, links: links) self.body = .data(.init(primary: body, includes: .none, meta: meta, links: links))
} }
} }
extension JSONAPIDocument where MetaType == NoMetadata { extension Document where MetaType == NoMetadata {
public init(body: ResourceBody, includes: Includes<Include>, links: LinksType) { public init(body: PrimaryResourceBody, includes: Includes<Include>, links: LinksType) {
self.body = .data(primary: body, included: includes, meta: .none, links: links) self.body = .data(.init(primary: body, includes: includes, meta: .none, links: links))
} }
} }
extension JSONAPIDocument where LinksType == NoLinks { extension Document where LinksType == NoLinks {
public init(body: ResourceBody, includes: Includes<Include>, meta: MetaType) { public init(body: PrimaryResourceBody, includes: Includes<Include>, meta: MetaType) {
self.body = .data(primary: body, included: includes, meta: meta, links: .none) self.body = .data(.init(primary: body, includes: includes, meta: meta, links: .none))
} }
} }
extension JSONAPIDocument where IncludeType == NoIncludes, LinksType == NoLinks { extension Document where IncludeType == NoIncludes, LinksType == NoLinks {
public init(body: ResourceBody, meta: MetaType) { public init(body: PrimaryResourceBody, meta: MetaType) {
self.body = .data(primary: body, included: .none, meta: meta, links: .none) self.body = .data(.init(primary: body, includes: .none, meta: meta, links: .none))
} }
} }
extension JSONAPIDocument where IncludeType == NoIncludes, MetaType == NoMetadata { extension Document where IncludeType == NoIncludes, MetaType == NoMetadata {
public init(body: ResourceBody, links: LinksType) { public init(body: PrimaryResourceBody, links: LinksType) {
self.body = .data(primary: body, included: .none, meta: .none, links: links) self.body = .data(.init(primary: body, includes: .none, meta: .none, links: links))
} }
} }
extension JSONAPIDocument where MetaType == NoMetadata, LinksType == NoLinks { extension Document where MetaType == NoMetadata, LinksType == NoLinks {
public init(body: ResourceBody, includes: Includes<Include>) { public init(body: PrimaryResourceBody, includes: Includes<Include>) {
self.body = .data(primary: body, included: includes, meta: .none, links: .none) self.body = .data(.init(primary: body, includes: includes, meta: .none, links: .none))
} }
} }
extension JSONAPIDocument where IncludeType == NoIncludes, MetaType == NoMetadata, LinksType == NoLinks { extension Document where IncludeType == NoIncludes, MetaType == NoMetadata, LinksType == NoLinks {
public init(body: ResourceBody) { public init(body: PrimaryResourceBody) {
self.body = .data(primary: body, included: .none, meta: .none, links: .none) self.body = .data(.init(primary: body, includes: .none, meta: .none, links: .none))
} }
} }
extension JSONAPIDocument: Codable { extension Document {
private enum RootCodingKeys: String, CodingKey { private enum RootCodingKeys: String, CodingKey {
case data case data
case errors case errors
@@ -157,11 +179,11 @@ extension JSONAPIDocument: Codable {
return return
} }
let data: ResourceBody let data: PrimaryResourceBody
if let noData = NoResourceBody() as? ResourceBody { if let noData = NoResourceBody() as? PrimaryResourceBody {
data = noData data = noData
} else { } else {
data = try container.decode(ResourceBody.self, forKey: .data) data = try container.decode(PrimaryResourceBody.self, forKey: .data)
} }
let maybeIncludes = try container.decodeIfPresent(Includes<Include>.self, forKey: .included) let maybeIncludes = try container.decodeIfPresent(Includes<Include>.self, forKey: .included)
@@ -175,7 +197,7 @@ extension JSONAPIDocument: Codable {
throw JSONAPIEncodingError.missingOrMalformedLinks throw JSONAPIEncodingError.missingOrMalformedLinks
} }
body = .data(primary: data, included: maybeIncludes ?? Includes<Include>.none, meta: metaVal, links: linksVal) body = .data(.init(primary: data, includes: maybeIncludes ?? Includes<Include>.none, meta: metaVal, links: linksVal))
} }
public func encode(to encoder: Encoder) throws { public func encode(to encoder: Encoder) throws {
@@ -199,19 +221,19 @@ extension JSONAPIDocument: Codable {
try container.encode(linksVal, forKey: .links) try container.encode(linksVal, forKey: .links)
} }
case .data(primary: let resourceBody, included: let includes, let meta, links: let links): case .data(let data):
try container.encode(resourceBody, forKey: .data) try container.encode(data.primary, forKey: .data)
if Include.self != NoIncludes.self { if Include.self != NoIncludes.self {
try container.encode(includes, forKey: .included) try container.encode(data.includes, forKey: .included)
} }
if MetaType.self != NoMetadata.self { if MetaType.self != NoMetadata.self {
try container.encode(meta, forKey: .meta) try container.encode(data.meta, forKey: .meta)
} }
if LinksType.self != NoLinks.self { if LinksType.self != NoLinks.self {
try container.encode(links, forKey: .links) try container.encode(data.links, forKey: .links)
} }
} }
} }
@@ -219,8 +241,8 @@ extension JSONAPIDocument: Codable {
// MARK: - CustomStringConvertible // MARK: - CustomStringConvertible
extension JSONAPIDocument: CustomStringConvertible { extension Document: CustomStringConvertible {
public var description: String { public var description: String {
return "JSONAPIDocument(body: \(String(describing: body))" return "Document(body: \(String(describing: body))"
} }
} }
+2 -2
View File
@@ -9,7 +9,7 @@ public protocol JSONAPIError: Swift.Error, Equatable, Codable {
static var unknown: Self { get } static var unknown: Self { get }
} }
public enum BasicJSONAPIError: JSONAPIError { public enum UnknownJSONAPIError: JSONAPIError {
case unknownError case unknownError
public init(from decoder: Decoder) throws { public init(from decoder: Decoder) throws {
@@ -21,7 +21,7 @@ public enum BasicJSONAPIError: JSONAPIError {
try container.encode("unknown") try container.encode("unknown")
} }
public static var unknown: BasicJSONAPIError { public static var unknown: UnknownJSONAPIError {
return .unknownError return .unknownError
} }
} }
+4 -4
View File
@@ -1,5 +1,5 @@
// //
// ResourceBody.swift // PrimaryResourceBody.swift
// JSONAPI // JSONAPI
// //
// Created by Mathew Polzin on 11/10/18. // Created by Mathew Polzin on 11/10/18.
@@ -18,7 +18,7 @@ public struct SingleResourceBody<Entity: JSONAPI.PrimaryResource>: ResourceBody
} }
} }
public struct ManyResourceBody<Entity: JSONAPI.EntityType>: ResourceBody { public struct ManyResourceBody<Entity: JSONAPI.PrimaryResource>: ResourceBody {
public let values: [Entity] public let values: [Entity]
public init(entities: [Entity]) { public init(entities: [Entity]) {
@@ -80,12 +80,12 @@ extension ManyResourceBody {
extension SingleResourceBody: CustomStringConvertible { extension SingleResourceBody: CustomStringConvertible {
public var description: String { public var description: String {
return "ResourceBody(\(String(describing: value)))" return "PrimaryResourceBody(\(String(describing: value)))"
} }
} }
extension ManyResourceBody: CustomStringConvertible { extension ManyResourceBody: CustomStringConvertible {
public var description: String { public var description: String {
return "ResourceBody(\(String(describing: values)))" return "PrimaryResourceBody(\(String(describing: values)))"
} }
} }
+2 -2
View File
@@ -14,9 +14,9 @@ public struct NoLinks: Links {
public init() {} public init() {}
} }
public protocol URL: Codable, Equatable {} public protocol JSONAPIURL: Codable, Equatable {}
public struct Link<URL: JSONAPI.URL, Meta: JSONAPI.Meta>: Equatable, Codable { public struct Link<URL: JSONAPI.JSONAPIURL, Meta: JSONAPI.Meta>: Equatable, Codable {
public let url: URL public let url: URL
public let meta: Meta public let meta: Meta
} }
+13 -1
View File
@@ -5,7 +5,10 @@
// Created by Mathew Polzin on 11/13/18. // Created by Mathew Polzin on 11/13/18.
// //
public struct TransformedAttribute<RawValue: Codable, Transformer: JSONAPI.Transformer>: Codable where Transformer.From == RawValue { public protocol AttributeType: Codable {
}
public struct TransformedAttribute<RawValue: Codable, Transformer: JSONAPI.Transformer>: AttributeType where Transformer.From == RawValue {
private let rawValue: RawValue private let rawValue: RawValue
public let value: Transformer.To public let value: Transformer.To
@@ -24,6 +27,15 @@ extension TransformedAttribute: CustomStringConvertible {
extension TransformedAttribute: Equatable where Transformer.From: Equatable, Transformer.To: Equatable {} extension TransformedAttribute: Equatable where Transformer.From: Equatable, Transformer.To: Equatable {}
extension TransformedAttribute where Transformer == IdentityTransformer<RawValue> {
// If we are using the identity transform, we can skip the transform and guarantee no
// error is thrown.
public init(value: RawValue) {
rawValue = value
self.value = value
}
}
public typealias ValidatedAttribute<RawValue: Codable, Validator: JSONAPI.Validator> = TransformedAttribute<RawValue, Validator> where RawValue == Validator.From public typealias ValidatedAttribute<RawValue: Codable, Validator: JSONAPI.Validator> = TransformedAttribute<RawValue, Validator> where RawValue == Validator.From
public typealias Attribute<T: Codable> = TransformedAttribute<T, IdentityTransformer<T>> public typealias Attribute<T: Codable> = TransformedAttribute<T, IdentityTransformer<T>>
+28 -13
View File
@@ -16,7 +16,7 @@ public typealias Attributes = Codable & Equatable
/// Can be used as `Relationships` Type for Entities that do not /// Can be used as `Relationships` Type for Entities that do not
/// have any Relationships. /// have any Relationships.
public struct NoRelatives: Relationships {} public struct NoRelationships: Relationships {}
/// Can be used as `Attributes` Type for Entities that do not /// Can be used as `Attributes` Type for Entities that do not
/// have any Attributes. /// have any Attributes.
@@ -40,6 +40,21 @@ public protocol EntityDescription {
public protocol EntityType: PrimaryResource { public protocol EntityType: PrimaryResource {
associatedtype Description: EntityDescription associatedtype Description: EntityDescription
associatedtype Identifier: Equatable & Codable associatedtype Identifier: Equatable & Codable
typealias Attributes = Description.Attributes
typealias Relationships = Description.Relationships
/// The `Entity`'s Id. This can be of type `Unidentified` if
/// 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`.
var id: Identifier { get }
/// The JSON API compliant attributes of this `Entity`.
var attributes: Attributes { get }
/// The JSON API compliant relationships of this `Entity`.
var relationships: Relationships { get }
} }
/// An `Entity` is a single model type that can be /// An `Entity` is a single model type that can be
@@ -97,27 +112,27 @@ extension Entity where Description.Attributes == NoAttributes, Identifier: Creat
} }
} }
extension Entity where Description.Relationships == NoRelatives { extension Entity where Description.Relationships == NoRelationships {
public init(id: Identifier, attributes: Description.Attributes) { public init(id: Identifier, attributes: Description.Attributes) {
self.init(id: id, attributes: attributes, relationships: NoRelatives()) self.init(id: id, attributes: attributes, relationships: NoRelationships())
} }
} }
extension Entity where Description.Relationships == NoRelatives, Identifier: CreatableIdType { extension Entity where Description.Relationships == NoRelationships, Identifier: CreatableIdType {
public init(attributes: Description.Attributes) { public init(attributes: Description.Attributes) {
self.init(attributes: attributes, relationships: NoRelatives()) self.init(attributes: attributes, relationships: NoRelationships())
} }
} }
extension Entity where Description.Attributes == NoAttributes, Description.Relationships == NoRelatives { extension Entity where Description.Attributes == NoAttributes, Description.Relationships == NoRelationships {
public init(id: Identifier) { public init(id: Identifier) {
self.init(id: id, attributes: NoAttributes(), relationships: NoRelatives()) self.init(id: id, attributes: NoAttributes(), relationships: NoRelationships())
} }
} }
extension Entity where Description.Attributes == NoAttributes, Description.Relationships == NoRelatives, Identifier: CreatableIdType { extension Entity where Description.Attributes == NoAttributes, Description.Relationships == NoRelationships, Identifier: CreatableIdType {
public init() { public init() {
self.init(attributes: NoAttributes(), relationships: NoRelatives()) self.init(attributes: NoAttributes(), relationships: NoRelationships())
} }
} }
@@ -187,7 +202,7 @@ public extension Entity {
try container.encode(Entity.type, forKey: .type) try container.encode(Entity.type, forKey: .type)
if Identifier.self != Unidentified.self { if Identifier.self != Unidentified<Description>.self {
try container.encode(id, forKey: .id) try container.encode(id, forKey: .id)
} }
@@ -195,7 +210,7 @@ public extension Entity {
try container.encode(attributes, forKey: .attributes) try container.encode(attributes, forKey: .attributes)
} }
if Description.Relationships.self != NoRelatives.self { if Description.Relationships.self != NoRelationships.self {
try container.encode(relationships, forKey: .relationships) try container.encode(relationships, forKey: .relationships)
} }
} }
@@ -210,10 +225,10 @@ public extension Entity {
throw JSONAPIEncodingError.typeMismatch(expected: Description.type, found: type) throw JSONAPIEncodingError.typeMismatch(expected: Description.type, found: type)
} }
id = try (Unidentified() as? Identifier) ?? container.decode(Identifier.self, forKey: .id) id = try (Unidentified<Description>() as? Identifier) ?? container.decode(Identifier.self, forKey: .id)
attributes = try (NoAttributes() as? Description.Attributes) ?? container.decode(Description.Attributes.self, forKey: .attributes) attributes = try (NoAttributes() as? Description.Attributes) ?? container.decode(Description.Attributes.self, forKey: .attributes)
relationships = try (NoRelatives() as? Description.Relationships) ?? container.decode(Description.Relationships.self, forKey: .relationships) relationships = try (NoRelationships() as? Description.Relationships) ?? container.decode(Description.Relationships.self, forKey: .relationships)
} }
} }
+6 -5
View File
@@ -8,7 +8,7 @@
/// Any type that you would like to be encoded to and /// Any type that you would like to be encoded to and
/// decoded from JSON API ids should conform to this /// decoded from JSON API ids should conform to this
/// protocol. Conformance for `String` is given. /// protocol. Conformance for `String` is given.
public protocol RawIdType: Codable, Equatable {} public protocol RawIdType: Codable, Hashable {}
/// If you would like to be able to create new /// If you would like to be able to create new
/// Entities with Ids backed by a RawIdType then /// Entities with Ids backed by a RawIdType then
@@ -22,16 +22,17 @@ public protocol CreatableRawIdType: RawIdType {
extension String: RawIdType {} extension String: RawIdType {}
public protocol Identifier: Codable, Equatable {} public protocol Identifier: Codable, Equatable {
associatedtype EntityDescription: JSONAPI.EntityDescription
}
public struct Unidentified: Identifier, CustomStringConvertible { public struct Unidentified<EntityDescription: JSONAPI.EntityDescription>: Identifier, CustomStringConvertible {
public init() {} public init() {}
public var description: String { return "Id(Unidentified)" } public var description: String { return "Id(Unidentified)" }
} }
public protocol IdType: Identifier, CustomStringConvertible { public protocol IdType: Identifier, Hashable, CustomStringConvertible {
associatedtype EntityDescription: JSONAPI.EntityDescription
associatedtype RawType: RawIdType associatedtype RawType: RawIdType
var rawValue: RawType { get } var rawValue: RawType { get }
+4 -6
View File
@@ -5,18 +5,16 @@
// Created by Mathew Polzin on 8/31/18. // Created by Mathew Polzin on 8/31/18.
// //
public protocol RelationshipType: Codable {}
/// An Entity relationship that can be encoded to or decoded from /// An Entity relationship that can be encoded to or decoded from
/// a JSON API "Resource Linkage." /// a JSON API "Resource Linkage."
/// See https://jsonapi.org/format/#document-resource-object-linkage /// See https://jsonapi.org/format/#document-resource-object-linkage
/// A convenient typealias might make your code much more legible: `One<EntityDescription>` /// A convenient typealias might make your code much more legible: `One<EntityDescription>`
public struct ToOneRelationship<Relatable: JSONAPI.OptionalRelatable>: Equatable, Codable { public struct ToOneRelationship<Relatable: JSONAPI.OptionalRelatable>: RelationshipType, Equatable {
public let id: Relatable.WrappedIdentifier public let id: Relatable.WrappedIdentifier
public var ids: [Relatable.WrappedIdentifier] {
return [id]
}
public init(id: Relatable.WrappedIdentifier) { public init(id: Relatable.WrappedIdentifier) {
self.id = id self.id = id
} }
@@ -38,7 +36,7 @@ extension ToOneRelationship where Relatable.WrappedIdentifier == Optional<Relata
/// a JSON API "Resource Linkage." /// a JSON API "Resource Linkage."
/// See https://jsonapi.org/format/#document-resource-object-linkage /// See https://jsonapi.org/format/#document-resource-object-linkage
/// A convenient typealias might make your code much more legible: `Many<EntityDescription>` /// A convenient typealias might make your code much more legible: `Many<EntityDescription>`
public struct ToManyRelationship<Relatable: JSONAPI.Relatable>: Equatable, Codable { public struct ToManyRelationship<Relatable: JSONAPI.Relatable>: RelationshipType, Equatable {
public let ids: [Relatable.Identifier] public let ids: [Relatable.Identifier]
@@ -0,0 +1,96 @@
import JSONAPI
extension TransformedAttribute: ExpressibleByUnicodeScalarLiteral where RawValue: ExpressibleByUnicodeScalarLiteral, Transformer == IdentityTransformer<RawValue> {
public typealias UnicodeScalarLiteralType = RawValue.UnicodeScalarLiteralType
public init(unicodeScalarLiteral value: RawValue.UnicodeScalarLiteralType) {
self.init(value: RawValue(unicodeScalarLiteral: value))
}
}
extension TransformedAttribute: ExpressibleByExtendedGraphemeClusterLiteral where RawValue: ExpressibleByExtendedGraphemeClusterLiteral, Transformer == IdentityTransformer<RawValue> {
public typealias ExtendedGraphemeClusterLiteralType = RawValue.ExtendedGraphemeClusterLiteralType
public init(extendedGraphemeClusterLiteral value: RawValue.ExtendedGraphemeClusterLiteralType) {
self.init(value: RawValue(extendedGraphemeClusterLiteral: value))
}
}
extension TransformedAttribute: ExpressibleByStringLiteral where RawValue: ExpressibleByStringLiteral, Transformer == IdentityTransformer<RawValue> {
public typealias StringLiteralType = RawValue.StringLiteralType
public init(stringLiteral value: RawValue.StringLiteralType) {
self.init(value: RawValue(stringLiteral: value))
}
}
extension TransformedAttribute: ExpressibleByNilLiteral where RawValue: ExpressibleByNilLiteral, Transformer == IdentityTransformer<RawValue> {
public init(nilLiteral: ()) {
self.init(value: RawValue(nilLiteral: ()))
}
}
extension TransformedAttribute: ExpressibleByFloatLiteral where RawValue: ExpressibleByFloatLiteral, Transformer == IdentityTransformer<RawValue> {
public typealias FloatLiteralType = RawValue.FloatLiteralType
public init(floatLiteral value: RawValue.FloatLiteralType) {
self.init(value: RawValue(floatLiteral: value))
}
}
extension TransformedAttribute: ExpressibleByBooleanLiteral where RawValue: ExpressibleByBooleanLiteral, Transformer == IdentityTransformer<RawValue> {
public typealias BooleanLiteralType = RawValue.BooleanLiteralType
public init(booleanLiteral value: BooleanLiteralType) {
self.init(value: RawValue(booleanLiteral: value))
}
}
extension TransformedAttribute: ExpressibleByIntegerLiteral where RawValue: ExpressibleByIntegerLiteral, Transformer == IdentityTransformer<RawValue> {
public typealias IntegerLiteralType = RawValue.IntegerLiteralType
public init(integerLiteral value: IntegerLiteralType) {
self.init(value: RawValue(integerLiteral: value))
}
}
// regretably, array and dictionary literals are not so easy because Dictionaries and Arrays
// cannot be turned back into variadic arguments to pass onto the RawValue type's constructor.
// we can still provide a case for the Array and Dictionary types, though.
public protocol DictionaryType {
associatedtype Key: Hashable
associatedtype Value
init<S>(_ keysAndValues: S, uniquingKeysWith combine: (Dictionary<Key, Value>.Value, Dictionary<Key, Value>.Value) throws -> Dictionary<Key, Value>.Value) rethrows where S : Sequence, S.Element == (Key, Value)
}
extension Dictionary: DictionaryType {}
extension TransformedAttribute: ExpressibleByDictionaryLiteral where RawValue: DictionaryType, Transformer == IdentityTransformer<RawValue> {
public typealias Key = RawValue.Key
public typealias Value = RawValue.Value
public init(dictionaryLiteral elements: (RawValue.Key, RawValue.Value)...) {
// we arbitrarily keep the first value if two values are assigned to the same key
self.init(value: RawValue(elements, uniquingKeysWith: { val, _ in val }))
}
}
public protocol ArrayType {
associatedtype Element
init<S>(_ s: S) where Element == S.Element, S : Sequence
}
extension Array: ArrayType {}
extension ArraySlice: ArrayType {}
extension TransformedAttribute: ExpressibleByArrayLiteral where RawValue: ArrayType, Transformer == IdentityTransformer<RawValue> {
public typealias ArrayLiteralElement = RawValue.Element
public init(arrayLiteral elements: ArrayLiteralElement...) {
self.init(value: RawValue(elements))
}
}
+75
View File
@@ -0,0 +1,75 @@
//
// EntityCheck.swift
// JSONAPITestLib
//
// Created by Mathew Polzin on 11/27/18.
//
import JSONAPI
public enum EntityCheckError: Swift.Error {
case attributesNotStruct
case relationshipsNotStruct
case nonAttribute(named: String)
case nonRelationship(named: String)
case nullArray(named: String)
case badId
}
public struct EntityCheckErrors: Swift.Error {
let problems: [EntityCheckError]
}
private protocol OptionalAttributeType {}
extension Optional: OptionalAttributeType where Wrapped: AttributeType {}
private protocol OptionalArray {}
extension Optional: OptionalArray where Wrapped: ArrayType {}
private protocol AttributeTypeWithOptionalArray {}
extension TransformedAttribute: AttributeTypeWithOptionalArray where RawValue: OptionalArray {}
private protocol OptionalRelationshipType {}
extension Optional: OptionalRelationshipType where Wrapped: RelationshipType {}
public extension Entity {
public static func check(_ entity: Entity) throws {
var problems = [EntityCheckError]()
if Swift.type(of: entity.id).EntityDescription.self != Description.self {
problems.append(.badId)
}
let attributesMirror = Mirror(reflecting: entity.attributes)
if attributesMirror.displayStyle != .`struct` {
problems.append(.attributesNotStruct)
}
for attribute in attributesMirror.children {
if attribute.value as? AttributeType == nil,
attribute.value as? OptionalAttributeType == nil {
problems.append(.nonAttribute(named: attribute.label ?? "unnamed"))
}
if attribute.value as? AttributeTypeWithOptionalArray != nil {
problems.append(.nullArray(named: attribute.label ?? "unnamed"))
}
}
let relationshipsMirror = Mirror(reflecting: entity.relationships)
if relationshipsMirror.displayStyle != .`struct` {
problems.append(.relationshipsNotStruct)
}
for relationship in relationshipsMirror.children {
if relationship.value as? RelationshipType == nil {
problems.append(.nonRelationship(named: relationship.label ?? "unnamed"))
}
}
guard problems.count == 0 else {
throw EntityCheckErrors(problems: problems)
}
}
}
+40
View File
@@ -0,0 +1,40 @@
//
// Id+Literal.swift
// JSONAPI
//
// Created by Mathew Polzin on 11/27/18.
//
import JSONAPI
extension Id: ExpressibleByUnicodeScalarLiteral where RawType: ExpressibleByUnicodeScalarLiteral {
public typealias UnicodeScalarLiteralType = RawType.UnicodeScalarLiteralType
public init(unicodeScalarLiteral value: RawType.UnicodeScalarLiteralType) {
self.init(rawValue: RawType(unicodeScalarLiteral: value))
}
}
extension Id: ExpressibleByExtendedGraphemeClusterLiteral where RawType: ExpressibleByExtendedGraphemeClusterLiteral {
public typealias ExtendedGraphemeClusterLiteralType = RawType.ExtendedGraphemeClusterLiteralType
public init(extendedGraphemeClusterLiteral value: RawType.ExtendedGraphemeClusterLiteralType) {
self.init(rawValue: RawType(extendedGraphemeClusterLiteral: value))
}
}
extension Id: ExpressibleByStringLiteral where RawType: ExpressibleByStringLiteral {
public typealias StringLiteralType = RawType.StringLiteralType
public init(stringLiteral value: RawType.StringLiteralType) {
self.init(rawValue: RawType(stringLiteral: value))
}
}
extension Id: ExpressibleByIntegerLiteral where RawType: ExpressibleByIntegerLiteral {
public typealias IntegerLiteralType = RawType.IntegerLiteralType
public init(integerLiteral value: IntegerLiteralType) {
self.init(rawValue: RawType(integerLiteral: value))
}
}
@@ -0,0 +1,14 @@
//
// Relationship+Literal.swift
// JSONAPITestLib
//
// Created by Mathew Polzin on 11/27/18.
//
import JSONAPI
extension ToOneRelationship: ExpressibleByNilLiteral where Relatable.WrappedIdentifier: ExpressibleByNilLiteral {
public init(nilLiteral: ()) {
self.init(id: Relatable.WrappedIdentifier(nilLiteral: ()))
}
}
@@ -0,0 +1,21 @@
//
// AttributeTests.swift
// JSONAPITests
//
// Created by Mathew Polzin on 11/27/18.
//
import XCTest
import JSONAPI
class AttributeTests: XCTestCase {
func test_AttributeIsTransformedAttribute() {
XCTAssertEqual(try TransformedAttribute<String, IdentityTransformer<String>>(rawValue: "hello"), try Attribute<String>(rawValue: "hello"))
}
func test_AttributeNonThrowingConstructor() {
XCTAssertEqual(try Attribute<String>(rawValue: "hello"), Attribute<String>(value: "hello"))
}
}
+46 -46
View File
@@ -11,7 +11,7 @@ import JSONAPI
class DocumentTests: XCTestCase { class DocumentTests: XCTestCase {
func test_singleDocumentNull() { func test_singleDocumentNull() {
let document = decoded(type: JSONAPIDocument<SingleResourceBody<Article>, NoMetadata, NoLinks, NoIncludes, BasicJSONAPIError>.self, let document = decoded(type: Document<SingleResourceBody<Article>, NoMetadata, NoLinks, NoIncludes, UnknownJSONAPIError>.self,
data: single_document_null) data: single_document_null)
XCTAssertFalse(document.body.isError) XCTAssertFalse(document.body.isError)
@@ -23,12 +23,12 @@ class DocumentTests: XCTestCase {
} }
func test_singleDocumentNull_encode() { func test_singleDocumentNull_encode() {
test_DecodeEncodeEquality(type: JSONAPIDocument<SingleResourceBody<Article>, NoMetadata, NoLinks, NoIncludes, BasicJSONAPIError>.self, test_DecodeEncodeEquality(type: Document<SingleResourceBody<Article>, NoMetadata, NoLinks, NoIncludes, UnknownJSONAPIError>.self,
data: single_document_null) data: single_document_null)
} }
func test_unknownErrorDocumentNoMeta() { func test_unknownErrorDocumentNoMeta() {
let document = decoded(type: JSONAPIDocument<NoResourceBody, NoMetadata, NoLinks, NoIncludes, BasicJSONAPIError>.self, let document = decoded(type: Document<NoResourceBody, NoMetadata, NoLinks, NoIncludes, UnknownJSONAPIError>.self,
data: error_document_no_metadata) data: error_document_no_metadata)
XCTAssertTrue(document.body.isError) XCTAssertTrue(document.body.isError)
@@ -48,12 +48,12 @@ class DocumentTests: XCTestCase {
} }
func test_unknownErrorDocumentNoMeta_encode() { func test_unknownErrorDocumentNoMeta_encode() {
test_DecodeEncodeEquality(type: JSONAPIDocument<NoResourceBody, NoMetadata, NoLinks, NoIncludes, BasicJSONAPIError>.self, test_DecodeEncodeEquality(type: Document<NoResourceBody, NoMetadata, NoLinks, NoIncludes, UnknownJSONAPIError>.self,
data: error_document_no_metadata) data: error_document_no_metadata)
} }
func test_unknownErrorDocumentMissingMeta() { func test_unknownErrorDocumentMissingMeta() {
let document = decoded(type: JSONAPIDocument<NoResourceBody, TestPageMetadata, NoLinks, NoIncludes, BasicJSONAPIError>.self, data: error_document_no_metadata) let document = decoded(type: Document<NoResourceBody, TestPageMetadata, NoLinks, NoIncludes, UnknownJSONAPIError>.self, data: error_document_no_metadata)
XCTAssertTrue(document.body.isError) XCTAssertTrue(document.body.isError)
XCTAssertNil(document.body.meta) XCTAssertNil(document.body.meta)
@@ -72,11 +72,11 @@ class DocumentTests: XCTestCase {
} }
func test_unknownErrorDocumentMissingMeta_encode() { func test_unknownErrorDocumentMissingMeta_encode() {
test_DecodeEncodeEquality(type: JSONAPIDocument<NoResourceBody, TestPageMetadata, NoLinks, NoIncludes, BasicJSONAPIError>.self, data: error_document_no_metadata) test_DecodeEncodeEquality(type: Document<NoResourceBody, TestPageMetadata, NoLinks, NoIncludes, UnknownJSONAPIError>.self, data: error_document_no_metadata)
} }
func test_errorDocumentNoMeta() { func test_errorDocumentNoMeta() {
let document = decoded(type: JSONAPIDocument<NoResourceBody, NoMetadata, NoLinks, NoIncludes, TestError>.self, let document = decoded(type: Document<NoResourceBody, NoMetadata, NoLinks, NoIncludes, TestError>.self,
data: error_document_no_metadata) data: error_document_no_metadata)
XCTAssertTrue(document.body.isError) XCTAssertTrue(document.body.isError)
@@ -96,12 +96,12 @@ class DocumentTests: XCTestCase {
} }
func test_errorDocumentNoMeta_encode() { func test_errorDocumentNoMeta_encode() {
test_DecodeEncodeEquality(type: JSONAPIDocument<NoResourceBody, NoMetadata, NoLinks, NoIncludes, TestError>.self, test_DecodeEncodeEquality(type: Document<NoResourceBody, NoMetadata, NoLinks, NoIncludes, TestError>.self,
data: error_document_no_metadata) data: error_document_no_metadata)
} }
func test_unknownErrorDocumentWithMeta() { func test_unknownErrorDocumentWithMeta() {
let document = decoded(type: JSONAPIDocument<NoResourceBody, TestPageMetadata, NoLinks, NoIncludes, BasicJSONAPIError>.self, let document = decoded(type: Document<NoResourceBody, TestPageMetadata, NoLinks, NoIncludes, UnknownJSONAPIError>.self,
data: error_document_with_metadata) data: error_document_with_metadata)
XCTAssertTrue(document.body.isError) XCTAssertTrue(document.body.isError)
@@ -120,12 +120,12 @@ class DocumentTests: XCTestCase {
} }
func test_unknownErrorDocumentWithMeta_encode() { func test_unknownErrorDocumentWithMeta_encode() {
test_DecodeEncodeEquality(type: JSONAPIDocument<NoResourceBody, TestPageMetadata, NoLinks, NoIncludes, BasicJSONAPIError>.self, test_DecodeEncodeEquality(type: Document<NoResourceBody, TestPageMetadata, NoLinks, NoIncludes, UnknownJSONAPIError>.self,
data: error_document_with_metadata) data: error_document_with_metadata)
} }
func test_unknownErrorDocumentWithMetaWithLinks() { func test_unknownErrorDocumentWithMetaWithLinks() {
let document = decoded(type: JSONAPIDocument<NoResourceBody, TestPageMetadata, TestLinks, NoIncludes, BasicJSONAPIError>.self, let document = decoded(type: Document<NoResourceBody, TestPageMetadata, TestLinks, NoIncludes, UnknownJSONAPIError>.self,
data: error_document_with_metadata_with_links) data: error_document_with_metadata_with_links)
XCTAssertTrue(document.body.isError) XCTAssertTrue(document.body.isError)
@@ -148,12 +148,12 @@ class DocumentTests: XCTestCase {
} }
func test_unknownErrorDocumentWithMetaWithLinks_encode() { func test_unknownErrorDocumentWithMetaWithLinks_encode() {
test_DecodeEncodeEquality(type: JSONAPIDocument<NoResourceBody, TestPageMetadata, TestLinks, NoIncludes, BasicJSONAPIError>.self, test_DecodeEncodeEquality(type: Document<NoResourceBody, TestPageMetadata, TestLinks, NoIncludes, UnknownJSONAPIError>.self,
data: error_document_with_metadata_with_links) data: error_document_with_metadata_with_links)
} }
func test_unknownErrorDocumentWithLinks() { func test_unknownErrorDocumentWithLinks() {
let document = decoded(type: JSONAPIDocument<NoResourceBody, TestPageMetadata, TestLinks, NoIncludes, BasicJSONAPIError>.self, let document = decoded(type: Document<NoResourceBody, TestPageMetadata, TestLinks, NoIncludes, UnknownJSONAPIError>.self,
data: error_document_with_links) data: error_document_with_links)
XCTAssertTrue(document.body.isError) XCTAssertTrue(document.body.isError)
@@ -174,12 +174,12 @@ class DocumentTests: XCTestCase {
} }
func test_unknownErrorDocumentWithLinks_encode() { func test_unknownErrorDocumentWithLinks_encode() {
test_DecodeEncodeEquality(type: JSONAPIDocument<NoResourceBody, TestPageMetadata, TestLinks, NoIncludes, BasicJSONAPIError>.self, test_DecodeEncodeEquality(type: Document<NoResourceBody, TestPageMetadata, TestLinks, NoIncludes, UnknownJSONAPIError>.self,
data: error_document_with_links) data: error_document_with_links)
} }
func test_unknownErrorDocumentMissingLinks() { func test_unknownErrorDocumentMissingLinks() {
let document = decoded(type: JSONAPIDocument<NoResourceBody, TestPageMetadata, TestLinks, NoIncludes, BasicJSONAPIError>.self, let document = decoded(type: Document<NoResourceBody, TestPageMetadata, TestLinks, NoIncludes, UnknownJSONAPIError>.self,
data: error_document_no_metadata) data: error_document_no_metadata)
XCTAssertTrue(document.body.isError) XCTAssertTrue(document.body.isError)
@@ -197,12 +197,12 @@ class DocumentTests: XCTestCase {
} }
func test_unknownErrorDocumentMissingLinks_encode() { func test_unknownErrorDocumentMissingLinks_encode() {
test_DecodeEncodeEquality(type: JSONAPIDocument<NoResourceBody, TestPageMetadata, TestLinks, NoIncludes, BasicJSONAPIError>.self, test_DecodeEncodeEquality(type: Document<NoResourceBody, TestPageMetadata, TestLinks, NoIncludes, UnknownJSONAPIError>.self,
data: error_document_no_metadata) data: error_document_no_metadata)
} }
func test_metaDataDocument() { func test_metaDataDocument() {
let document = decoded(type: JSONAPIDocument<NoResourceBody, TestPageMetadata, NoLinks, NoIncludes, BasicJSONAPIError>.self, let document = decoded(type: Document<NoResourceBody, TestPageMetadata, NoLinks, NoIncludes, UnknownJSONAPIError>.self,
data: metadata_document) data: metadata_document)
XCTAssertFalse(document.body.isError) XCTAssertFalse(document.body.isError)
@@ -213,12 +213,12 @@ class DocumentTests: XCTestCase {
} }
func test_metaDataDocument_encode() { func test_metaDataDocument_encode() {
test_DecodeEncodeEquality(type: JSONAPIDocument<NoResourceBody, TestPageMetadata, NoLinks, NoIncludes, BasicJSONAPIError>.self, test_DecodeEncodeEquality(type: Document<NoResourceBody, TestPageMetadata, NoLinks, NoIncludes, UnknownJSONAPIError>.self,
data: metadata_document) data: metadata_document)
} }
func test_metaDataDocumentWithLinks() { func test_metaDataDocumentWithLinks() {
let document = decoded(type: JSONAPIDocument<NoResourceBody, TestPageMetadata, TestLinks, NoIncludes, BasicJSONAPIError>.self, let document = decoded(type: Document<NoResourceBody, TestPageMetadata, TestLinks, NoIncludes, UnknownJSONAPIError>.self,
data: metadata_document_with_links) data: metadata_document_with_links)
XCTAssertFalse(document.body.isError) XCTAssertFalse(document.body.isError)
@@ -233,18 +233,18 @@ class DocumentTests: XCTestCase {
} }
func test_metaDataDocumentWithLinks_encode() { func test_metaDataDocumentWithLinks_encode() {
test_DecodeEncodeEquality(type: JSONAPIDocument<NoResourceBody, TestPageMetadata, TestLinks, NoIncludes, BasicJSONAPIError>.self, test_DecodeEncodeEquality(type: Document<NoResourceBody, TestPageMetadata, TestLinks, NoIncludes, UnknownJSONAPIError>.self,
data: metadata_document_with_links) data: metadata_document_with_links)
} }
func test_metaDocumentMissingMeta() { func test_metaDocumentMissingMeta() {
XCTAssertThrowsError(try JSONDecoder().decode(JSONAPIDocument<NoResourceBody, TestPageMetadata, NoLinks, NoIncludes, BasicJSONAPIError>.self, from: metadata_document_missing_metadata)) XCTAssertThrowsError(try JSONDecoder().decode(Document<NoResourceBody, TestPageMetadata, NoLinks, NoIncludes, UnknownJSONAPIError>.self, from: metadata_document_missing_metadata))
XCTAssertThrowsError(try JSONDecoder().decode(JSONAPIDocument<NoResourceBody, TestPageMetadata, NoLinks, NoIncludes, BasicJSONAPIError>.self, from: metadata_document_missing_metadata2)) XCTAssertThrowsError(try JSONDecoder().decode(Document<NoResourceBody, TestPageMetadata, NoLinks, NoIncludes, UnknownJSONAPIError>.self, from: metadata_document_missing_metadata2))
} }
func test_singleDocumentNoIncludes() { func test_singleDocumentNoIncludes() {
let document = decoded(type: JSONAPIDocument<SingleResourceBody<Article>, NoMetadata, NoLinks, NoIncludes, BasicJSONAPIError>.self, let document = decoded(type: Document<SingleResourceBody<Article>, NoMetadata, NoLinks, NoIncludes, UnknownJSONAPIError>.self,
data: single_document_no_includes) data: single_document_no_includes)
XCTAssertFalse(document.body.isError) XCTAssertFalse(document.body.isError)
@@ -256,12 +256,12 @@ class DocumentTests: XCTestCase {
} }
func test_singleDocumentNoIncludes_encode() { func test_singleDocumentNoIncludes_encode() {
test_DecodeEncodeEquality(type: JSONAPIDocument<SingleResourceBody<Article>, NoMetadata, NoLinks, NoIncludes, BasicJSONAPIError>.self, test_DecodeEncodeEquality(type: Document<SingleResourceBody<Article>, NoMetadata, NoLinks, NoIncludes, UnknownJSONAPIError>.self,
data: single_document_no_includes) data: single_document_no_includes)
} }
func test_singleDocumentNoIncludesWithMetadata() { func test_singleDocumentNoIncludesWithMetadata() {
let document = decoded(type: JSONAPIDocument<SingleResourceBody<Article>, TestPageMetadata, NoLinks, NoIncludes, BasicJSONAPIError>.self, let document = decoded(type: Document<SingleResourceBody<Article>, TestPageMetadata, NoLinks, NoIncludes, UnknownJSONAPIError>.self,
data: single_document_no_includes_with_metadata) data: single_document_no_includes_with_metadata)
XCTAssertFalse(document.body.isError) XCTAssertFalse(document.body.isError)
@@ -273,12 +273,12 @@ class DocumentTests: XCTestCase {
} }
func test_singleDocumentNoIncludesWithMetadata_encode() { func test_singleDocumentNoIncludesWithMetadata_encode() {
test_DecodeEncodeEquality(type: JSONAPIDocument<SingleResourceBody<Article>, TestPageMetadata, NoLinks, NoIncludes, BasicJSONAPIError>.self, test_DecodeEncodeEquality(type: Document<SingleResourceBody<Article>, TestPageMetadata, NoLinks, NoIncludes, UnknownJSONAPIError>.self,
data: single_document_no_includes_with_metadata) data: single_document_no_includes_with_metadata)
} }
func test_singleDocumentNoIncludesWithLinks() { func test_singleDocumentNoIncludesWithLinks() {
let document = decoded(type: JSONAPIDocument<SingleResourceBody<Article>, NoMetadata, TestLinks, NoIncludes, BasicJSONAPIError>.self, let document = decoded(type: Document<SingleResourceBody<Article>, NoMetadata, TestLinks, NoIncludes, UnknownJSONAPIError>.self,
data: single_document_no_includes_with_links) data: single_document_no_includes_with_links)
XCTAssertFalse(document.body.isError) XCTAssertFalse(document.body.isError)
@@ -295,12 +295,12 @@ class DocumentTests: XCTestCase {
} }
func test_singleDocumentNoIncludesWithLinks_encode() { func test_singleDocumentNoIncludesWithLinks_encode() {
test_DecodeEncodeEquality(type: JSONAPIDocument<SingleResourceBody<Article>, NoMetadata, TestLinks, NoIncludes, BasicJSONAPIError>.self, test_DecodeEncodeEquality(type: Document<SingleResourceBody<Article>, NoMetadata, TestLinks, NoIncludes, UnknownJSONAPIError>.self,
data: single_document_no_includes_with_links) data: single_document_no_includes_with_links)
} }
func test_singleDocumentNoIncludesWithMetadataWithLinks() { func test_singleDocumentNoIncludesWithMetadataWithLinks() {
let document = decoded(type: JSONAPIDocument<SingleResourceBody<Article>, TestPageMetadata, TestLinks, NoIncludes, BasicJSONAPIError>.self, let document = decoded(type: Document<SingleResourceBody<Article>, TestPageMetadata, TestLinks, NoIncludes, UnknownJSONAPIError>.self,
data: single_document_no_includes_with_metadata_with_links) data: single_document_no_includes_with_metadata_with_links)
XCTAssertFalse(document.body.isError) XCTAssertFalse(document.body.isError)
@@ -317,20 +317,20 @@ class DocumentTests: XCTestCase {
} }
func test_singleDocumentNoIncludesWithMetadataWithLinks_encode() { func test_singleDocumentNoIncludesWithMetadataWithLinks_encode() {
test_DecodeEncodeEquality(type: JSONAPIDocument<SingleResourceBody<Article>, TestPageMetadata, TestLinks, NoIncludes, BasicJSONAPIError>.self, test_DecodeEncodeEquality(type: Document<SingleResourceBody<Article>, TestPageMetadata, TestLinks, NoIncludes, UnknownJSONAPIError>.self,
data: single_document_no_includes_with_metadata_with_links) data: single_document_no_includes_with_metadata_with_links)
} }
func test_singleDocumentNoIncludesWithMetadataMissingLinks() { func test_singleDocumentNoIncludesWithMetadataMissingLinks() {
XCTAssertThrowsError(try JSONDecoder().decode(JSONAPIDocument<SingleResourceBody<Article>, TestPageMetadata, TestLinks, NoIncludes, BasicJSONAPIError>.self, from: single_document_no_includes_with_metadata)) XCTAssertThrowsError(try JSONDecoder().decode(Document<SingleResourceBody<Article>, TestPageMetadata, TestLinks, NoIncludes, UnknownJSONAPIError>.self, from: single_document_no_includes_with_metadata))
} }
func test_singleDocumentNoIncludesMissingMetadata() { func test_singleDocumentNoIncludesMissingMetadata() {
XCTAssertThrowsError(try JSONDecoder().decode(JSONAPIDocument<SingleResourceBody<Article>, TestPageMetadata, NoLinks, NoIncludes, BasicJSONAPIError>.self, from: single_document_no_includes)) XCTAssertThrowsError(try JSONDecoder().decode(Document<SingleResourceBody<Article>, TestPageMetadata, NoLinks, NoIncludes, UnknownJSONAPIError>.self, from: single_document_no_includes))
} }
func test_singleDocumentSomeIncludes() { func test_singleDocumentSomeIncludes() {
let document = decoded(type: JSONAPIDocument<SingleResourceBody<Article>, NoMetadata, NoLinks, Include1<Author>, BasicJSONAPIError>.self, let document = decoded(type: Document<SingleResourceBody<Article>, NoMetadata, NoLinks, Include1<Author>, UnknownJSONAPIError>.self,
data: single_document_some_includes) data: single_document_some_includes)
XCTAssertFalse(document.body.isError) XCTAssertFalse(document.body.isError)
@@ -343,12 +343,12 @@ class DocumentTests: XCTestCase {
} }
func test_singleDocumentSomeIncludes_encode() { func test_singleDocumentSomeIncludes_encode() {
test_DecodeEncodeEquality(type: JSONAPIDocument<SingleResourceBody<Article>, NoMetadata, NoLinks, Include1<Author>, BasicJSONAPIError>.self, test_DecodeEncodeEquality(type: Document<SingleResourceBody<Article>, NoMetadata, NoLinks, Include1<Author>, UnknownJSONAPIError>.self,
data: single_document_some_includes) data: single_document_some_includes)
} }
func test_singleDocumentSomeIncludesWithMetadata() { func test_singleDocumentSomeIncludesWithMetadata() {
let document = decoded(type: JSONAPIDocument<SingleResourceBody<Article>, TestPageMetadata, NoLinks, Include1<Author>, BasicJSONAPIError>.self, let document = decoded(type: Document<SingleResourceBody<Article>, TestPageMetadata, NoLinks, Include1<Author>, UnknownJSONAPIError>.self,
data: single_document_some_includes_with_metadata) data: single_document_some_includes_with_metadata)
XCTAssertFalse(document.body.isError) XCTAssertFalse(document.body.isError)
@@ -362,12 +362,12 @@ class DocumentTests: XCTestCase {
} }
func test_singleDocumentSomeIncludesWithMetadata_encode() { func test_singleDocumentSomeIncludesWithMetadata_encode() {
test_DecodeEncodeEquality(type: JSONAPIDocument<SingleResourceBody<Article>, TestPageMetadata, NoLinks, Include1<Author>, BasicJSONAPIError>.self, test_DecodeEncodeEquality(type: Document<SingleResourceBody<Article>, TestPageMetadata, NoLinks, Include1<Author>, UnknownJSONAPIError>.self,
data: single_document_some_includes_with_metadata) data: single_document_some_includes_with_metadata)
} }
func test_singleDocumentNoIncludesWithSomeIncludesWithMetadataWithLinks() { func test_singleDocumentNoIncludesWithSomeIncludesWithMetadataWithLinks() {
let document = decoded(type: JSONAPIDocument<SingleResourceBody<Article>, TestPageMetadata, TestLinks, Include1<Author>, BasicJSONAPIError>.self, let document = decoded(type: Document<SingleResourceBody<Article>, TestPageMetadata, TestLinks, Include1<Author>, UnknownJSONAPIError>.self,
data: single_document_some_includes_with_metadata_with_links) data: single_document_some_includes_with_metadata_with_links)
XCTAssertFalse(document.body.isError) XCTAssertFalse(document.body.isError)
@@ -385,24 +385,24 @@ class DocumentTests: XCTestCase {
} }
func test_singleDocumentNoIncludesWithSomeIncludesMetadataWithLinks_encode() { func test_singleDocumentNoIncludesWithSomeIncludesMetadataWithLinks_encode() {
test_DecodeEncodeEquality(type: JSONAPIDocument<SingleResourceBody<Article>, TestPageMetadata, TestLinks, Include1<Author>, BasicJSONAPIError>.self, test_DecodeEncodeEquality(type: Document<SingleResourceBody<Article>, TestPageMetadata, TestLinks, Include1<Author>, UnknownJSONAPIError>.self,
data: single_document_some_includes_with_metadata_with_links) data: single_document_some_includes_with_metadata_with_links)
} }
func test_singleDocument_PolyPrimaryResource() { func test_singleDocument_PolyPrimaryResource() {
let article = Article(id: Id(rawValue: "1"), relationships: .init(author: ToOneRelationship(id: Id(rawValue: "33")))) let article = Article(id: Id(rawValue: "1"), relationships: .init(author: ToOneRelationship(id: Id(rawValue: "33"))))
let document = decoded(type: JSONAPIDocument<SingleResourceBody<Poly2<Article, Author>>, NoMetadata, NoLinks, NoIncludes, BasicJSONAPIError>.self, data: single_document_no_includes) let document = decoded(type: Document<SingleResourceBody<Poly2<Article, Author>>, NoMetadata, NoLinks, NoIncludes, UnknownJSONAPIError>.self, data: single_document_no_includes)
XCTAssertEqual(document.body.primaryData?.value?[Article.self], article) XCTAssertEqual(document.body.primaryData?.value?[Article.self], article)
XCTAssertNil(document.body.primaryData?.value?[Author.self]) XCTAssertNil(document.body.primaryData?.value?[Author.self])
} }
func test_singleDocument_PolyPrimaryResource_encode() { func test_singleDocument_PolyPrimaryResource_encode() {
test_DecodeEncodeEquality(type: JSONAPIDocument<SingleResourceBody<Poly2<Article, Author>>, NoMetadata, NoLinks, NoIncludes, BasicJSONAPIError>.self, data: single_document_no_includes) test_DecodeEncodeEquality(type: Document<SingleResourceBody<Poly2<Article, Author>>, NoMetadata, NoLinks, NoIncludes, UnknownJSONAPIError>.self, data: single_document_no_includes)
} }
func test_manyDocumentNoIncludes() { func test_manyDocumentNoIncludes() {
let document = decoded(type: JSONAPIDocument<ManyResourceBody<Article>, NoMetadata, NoLinks, NoIncludes, BasicJSONAPIError>.self, let document = decoded(type: Document<ManyResourceBody<Article>, NoMetadata, NoLinks, NoIncludes, UnknownJSONAPIError>.self,
data: many_document_no_includes) data: many_document_no_includes)
XCTAssertFalse(document.body.isError) XCTAssertFalse(document.body.isError)
@@ -416,12 +416,12 @@ class DocumentTests: XCTestCase {
} }
func test_manyDocumentNoIncludes_encode() { func test_manyDocumentNoIncludes_encode() {
test_DecodeEncodeEquality(type: JSONAPIDocument<ManyResourceBody<Article>, NoMetadata, NoLinks, NoIncludes, BasicJSONAPIError>.self, test_DecodeEncodeEquality(type: Document<ManyResourceBody<Article>, NoMetadata, NoLinks, NoIncludes, UnknownJSONAPIError>.self,
data: many_document_no_includes) data: many_document_no_includes)
} }
func test_manyDocumentSomeIncludes() { func test_manyDocumentSomeIncludes() {
let document = decoded(type: JSONAPIDocument<ManyResourceBody<Article>, NoMetadata, NoLinks, Include1<Author>, BasicJSONAPIError>.self, let document = decoded(type: Document<ManyResourceBody<Article>, NoMetadata, NoLinks, Include1<Author>, UnknownJSONAPIError>.self,
data: many_document_some_includes) data: many_document_some_includes)
XCTAssertFalse(document.body.isError) XCTAssertFalse(document.body.isError)
@@ -439,7 +439,7 @@ class DocumentTests: XCTestCase {
} }
func test_manyDocumentSomeIncludes_encode() { func test_manyDocumentSomeIncludes_encode() {
test_DecodeEncodeEquality(type: JSONAPIDocument<ManyResourceBody<Article>, NoMetadata, NoLinks, Include1<Author>, BasicJSONAPIError>.self, test_DecodeEncodeEquality(type: Document<ManyResourceBody<Article>, NoMetadata, NoLinks, Include1<Author>, UnknownJSONAPIError>.self,
data: many_document_some_includes) data: many_document_some_includes)
} }
} }
@@ -450,7 +450,7 @@ extension DocumentTests {
static var type: String { return "authors" } static var type: String { return "authors" }
typealias Attributes = NoAttributes typealias Attributes = NoAttributes
typealias Relationships = NoRelatives typealias Relationships = NoRelationships
} }
typealias Author = Entity<AuthorType> typealias Author = Entity<AuthorType>
@@ -514,4 +514,4 @@ extension DocumentTests {
} }
} }
extension String: JSONAPI.URL {} extension String: JSONAPI.JSONAPIURL {}

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