mirror of
https://github.com/encounter/JSONAPI.git
synced 2026-07-10 12:18:40 -07:00
Compare commits
23 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| fcc1796731 | |||
| 921bcef05d | |||
| 661ff6eca5 | |||
| e36180c9b9 | |||
| abee0c4d0e | |||
| 9c8b2fbebb | |||
| a9ef71f383 | |||
| 232554ec51 | |||
| 8f36bb40e3 | |||
| 55f2f52676 | |||
| dd5f3b1737 | |||
| 7d7b3d7f19 | |||
| aedd5dc29b | |||
| 3964202ea2 | |||
| dcabafd583 | |||
| 9df9efc2dc | |||
| a55a4cbed0 | |||
| 6f6ed87b4c | |||
| 59ff145aa8 | |||
| 20a685e67b | |||
| 3d8d10584b | |||
| b93580c900 | |||
| 8927938d56 |
@@ -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)
|
||||
+24
-6
@@ -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
|
||||
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))
|
||||
|
||||
@@ -19,7 +19,7 @@ let singleDogData = try! JSONEncoder().encode(singleDogDocument)
|
||||
|
||||
// MARK: - Parse a request or response body with one Dog in it
|
||||
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
|
||||
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 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 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
|
||||
|
||||
let peopleResponse = try! JSONDecoder().decode(BatchPeopleDocument.self, from: batchPeopleData)
|
||||
let peopleFromData = peopleResponse.body.data?.primary.values
|
||||
let dogsFromData = peopleResponse.body.data?.included[Dog.self]
|
||||
let housesFromData = peopleResponse.body.data?.included[House.self]
|
||||
let peopleFromData = peopleResponse.body.primaryData?.values
|
||||
let dogsFromData = peopleResponse.body.includes?[Dog.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)
|
||||
@@ -34,12 +34,23 @@ public enum PersonDescription: EntityDescription {
|
||||
public struct Attributes: JSONAPI.Attributes {
|
||||
public let name: 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 let friends: ToManyRelationship<Person>
|
||||
public let dogs: ToManyRelationship<Dog>
|
||||
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 let name: Attribute<String>
|
||||
|
||||
public init(name: Attribute<String>) {
|
||||
self.name = name
|
||||
}
|
||||
}
|
||||
|
||||
public struct Relationships: JSONAPI.Relationships {
|
||||
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 typealias Attributes = NoAttributes
|
||||
public typealias Relationships = NoRelatives
|
||||
public typealias Relationships = NoRelationships
|
||||
}
|
||||
|
||||
public typealias House = ExampleEntity<HouseDescription>
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<playground version='5.0' target-platform='macos' executeOnSourceChanges='false'>
|
||||
<timeline fileName='timeline.xctimeline'/>
|
||||
<playground version='6.0' target-platform='macos' executeOnSourceChanges='false'>
|
||||
<pages>
|
||||
<page name='Test Library'/>
|
||||
<page name='Usage'/>
|
||||
</pages>
|
||||
</playground>
|
||||
+8
-4
@@ -6,23 +6,27 @@ import PackageDescription
|
||||
let package = Package(
|
||||
name: "JSONAPI",
|
||||
products: [
|
||||
// Products define the executables and libraries produced by a package, and make them visible to other packages.
|
||||
.library(
|
||||
name: "JSONAPI",
|
||||
targets: ["JSONAPI"]),
|
||||
.library(
|
||||
name: "JSONAPITestLib",
|
||||
targets: ["JSONAPITestLib"])
|
||||
],
|
||||
dependencies: [
|
||||
// antitypical/Result without the Foundation requirement:
|
||||
.package(url: "https://github.com/mattpolzin/Result", .branch("master"))
|
||||
],
|
||||
targets: [
|
||||
// Targets are the basic building blocks of a package. A target can define a module or a test suite.
|
||||
// Targets can depend on other targets in this package, and on products in packages which this package depends on.
|
||||
.target(
|
||||
name: "JSONAPI",
|
||||
dependencies: ["Result"]),
|
||||
.target(
|
||||
name: "JSONAPITestLib",
|
||||
dependencies: ["JSONAPI"]),
|
||||
.testTarget(
|
||||
name: "JSONAPITests",
|
||||
dependencies: ["JSONAPI"]),
|
||||
dependencies: ["JSONAPITestLib"])
|
||||
],
|
||||
swiftLanguageVersions: [.v4_2]
|
||||
)
|
||||
|
||||
@@ -13,6 +13,19 @@ The primary goals of this framework are:
|
||||
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.
|
||||
|
||||
### 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
|
||||
|
||||
### Decoding
|
||||
@@ -67,10 +80,10 @@ The primary goals of this framework are:
|
||||
- [x] `href`
|
||||
- [x] `meta`
|
||||
|
||||
### EntityDescription Validator (using reflection)
|
||||
- [ ] Disallow optional array in `Attribute` and `Relationship` (should be empty array, not `null`).
|
||||
- [ ] Only allow `Attribute` and `TransformAttribute` within `Attributes` struct.
|
||||
- [ ] Only allow `ToManyRelationship` and `ToOneRelationship` within `Relationships` struct.
|
||||
### Entity Validator (using reflection)
|
||||
- [x] Disallow optional array in `Attribute` (should be empty array, not `null`).
|
||||
- [x] Only allow `TransformedAttribute` and its derivatives within `Attributes` struct.
|
||||
- [x] Only allow `ToManyRelationship` and `ToOneRelationship` within `Relationships` struct.
|
||||
|
||||
### 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).
|
||||
@@ -86,10 +99,9 @@ The primary goals of this framework are:
|
||||
- [ ] Property-based testing (using `SwiftCheck`)
|
||||
- [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.
|
||||
- [x] Make it easier to construct `Attributes` and `Relationships` values in test cases (literal expressibility).
|
||||
|
||||
## Usage
|
||||
### Prerequisites
|
||||
1. Swift 4.2+ and Swift Package Manager
|
||||
|
||||
### `EntityDescription`
|
||||
|
||||
@@ -113,7 +125,7 @@ enum PersonDescription: IdentifiedEntityDescription {
|
||||
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".
|
||||
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.
|
||||
|
||||
@@ -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`:
|
||||
```
|
||||
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):
|
||||
@@ -250,7 +262,7 @@ The entirety of a JSON API request or response is encoded or decoded from- or to
|
||||
```
|
||||
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)
|
||||
```
|
||||
@@ -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:
|
||||
```
|
||||
struct PageMetadata: JSONAPI.Meta {
|
||||
let total: Int
|
||||
let limit: Int
|
||||
let offset: Int
|
||||
let total: Int
|
||||
let limit: 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.
|
||||
|
||||
@@ -5,6 +5,19 @@
|
||||
// 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
|
||||
/// of a JSON API request or the entire body of
|
||||
/// a JSON API response.
|
||||
@@ -12,7 +25,7 @@
|
||||
/// 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, 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 let body: Body
|
||||
@@ -20,7 +33,14 @@ public struct JSONAPIDocument<ResourceBody: JSONAPI.ResourceBody, MetaType: JSON
|
||||
|
||||
public enum Body: Equatable {
|
||||
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 {
|
||||
guard case .errors = self else { return false }
|
||||
@@ -32,20 +52,21 @@ public struct JSONAPIDocument<ResourceBody: JSONAPI.ResourceBody, MetaType: JSON
|
||||
return errors
|
||||
}
|
||||
|
||||
public var primaryData: ResourceBody? {
|
||||
guard case let .data(primary: body, included: _, meta: _, links: _) = self else { return nil }
|
||||
return body
|
||||
public var primaryData: PrimaryResourceBody? {
|
||||
guard case let .data(data) = self else { return nil }
|
||||
return data.primary
|
||||
}
|
||||
|
||||
public var includes: Includes<Include>? {
|
||||
guard case let .data(primary: _, included: includes, meta: _, links: _) = self else { return nil }
|
||||
return includes
|
||||
guard case let .data(data) = self else { return nil }
|
||||
return data.includes
|
||||
}
|
||||
|
||||
public var meta: MetaType? {
|
||||
switch self {
|
||||
case .data(primary: _, included: _, meta: let metadata, links: _),
|
||||
.errors(_, meta: let metadata?, links: _):
|
||||
case .data(let data):
|
||||
return data.meta
|
||||
case .errors(_, meta: let metadata?, links: _):
|
||||
return metadata
|
||||
default:
|
||||
return nil
|
||||
@@ -54,8 +75,9 @@ public struct JSONAPIDocument<ResourceBody: JSONAPI.ResourceBody, MetaType: JSON
|
||||
|
||||
public var links: LinksType? {
|
||||
switch self {
|
||||
case .data(primary: _, included: _, meta: _, links: let links),
|
||||
.errors(_, meta: _, links: let links?):
|
||||
case .data(let data):
|
||||
return data.links
|
||||
case .errors(_, meta: _, links: let links?):
|
||||
return links
|
||||
default:
|
||||
return nil
|
||||
@@ -67,54 +89,54 @@ public struct JSONAPIDocument<ResourceBody: JSONAPI.ResourceBody, MetaType: JSON
|
||||
body = .errors(errors, meta: meta, links: links)
|
||||
}
|
||||
|
||||
public init(body: ResourceBody, includes: Includes<Include>, meta: MetaType, links: LinksType) {
|
||||
self.body = .data(primary: body, included: includes, meta: meta, links: links)
|
||||
public init(body: PrimaryResourceBody, includes: Includes<Include>, meta: MetaType, links: LinksType) {
|
||||
self.body = .data(.init(primary: body, includes: includes, meta: meta, links: links))
|
||||
}
|
||||
}
|
||||
|
||||
extension JSONAPIDocument where IncludeType == NoIncludes {
|
||||
public init(body: ResourceBody, meta: MetaType, links: LinksType) {
|
||||
self.body = .data(primary: body, included: .none, meta: meta, links: links)
|
||||
extension Document where IncludeType == NoIncludes {
|
||||
public init(body: PrimaryResourceBody, meta: MetaType, links: LinksType) {
|
||||
self.body = .data(.init(primary: body, includes: .none, meta: meta, links: links))
|
||||
}
|
||||
}
|
||||
|
||||
extension JSONAPIDocument where MetaType == NoMetadata {
|
||||
public init(body: ResourceBody, includes: Includes<Include>, links: LinksType) {
|
||||
self.body = .data(primary: body, included: includes, meta: .none, links: links)
|
||||
extension Document where MetaType == NoMetadata {
|
||||
public init(body: PrimaryResourceBody, includes: Includes<Include>, links: LinksType) {
|
||||
self.body = .data(.init(primary: body, includes: includes, meta: .none, links: links))
|
||||
}
|
||||
}
|
||||
|
||||
extension JSONAPIDocument where LinksType == NoLinks {
|
||||
public init(body: ResourceBody, includes: Includes<Include>, meta: MetaType) {
|
||||
self.body = .data(primary: body, included: includes, meta: meta, links: .none)
|
||||
extension Document where LinksType == NoLinks {
|
||||
public init(body: PrimaryResourceBody, includes: Includes<Include>, meta: MetaType) {
|
||||
self.body = .data(.init(primary: body, includes: includes, meta: meta, links: .none))
|
||||
}
|
||||
}
|
||||
|
||||
extension JSONAPIDocument where IncludeType == NoIncludes, LinksType == NoLinks {
|
||||
public init(body: ResourceBody, meta: MetaType) {
|
||||
self.body = .data(primary: body, included: .none, meta: meta, links: .none)
|
||||
extension Document where IncludeType == NoIncludes, LinksType == NoLinks {
|
||||
public init(body: PrimaryResourceBody, meta: MetaType) {
|
||||
self.body = .data(.init(primary: body, includes: .none, meta: meta, links: .none))
|
||||
}
|
||||
}
|
||||
|
||||
extension JSONAPIDocument where IncludeType == NoIncludes, MetaType == NoMetadata {
|
||||
public init(body: ResourceBody, links: LinksType) {
|
||||
self.body = .data(primary: body, included: .none, meta: .none, links: links)
|
||||
extension Document where IncludeType == NoIncludes, MetaType == NoMetadata {
|
||||
public init(body: PrimaryResourceBody, links: LinksType) {
|
||||
self.body = .data(.init(primary: body, includes: .none, meta: .none, links: links))
|
||||
}
|
||||
}
|
||||
|
||||
extension JSONAPIDocument where MetaType == NoMetadata, LinksType == NoLinks {
|
||||
public init(body: ResourceBody, includes: Includes<Include>) {
|
||||
self.body = .data(primary: body, included: includes, meta: .none, links: .none)
|
||||
extension Document where MetaType == NoMetadata, LinksType == NoLinks {
|
||||
public init(body: PrimaryResourceBody, includes: Includes<Include>) {
|
||||
self.body = .data(.init(primary: body, includes: includes, meta: .none, links: .none))
|
||||
}
|
||||
}
|
||||
|
||||
extension JSONAPIDocument where IncludeType == NoIncludes, MetaType == NoMetadata, LinksType == NoLinks {
|
||||
public init(body: ResourceBody) {
|
||||
self.body = .data(primary: body, included: .none, meta: .none, links: .none)
|
||||
extension Document where IncludeType == NoIncludes, MetaType == NoMetadata, LinksType == NoLinks {
|
||||
public init(body: PrimaryResourceBody) {
|
||||
self.body = .data(.init(primary: body, includes: .none, meta: .none, links: .none))
|
||||
}
|
||||
}
|
||||
|
||||
extension JSONAPIDocument: Codable {
|
||||
extension Document {
|
||||
private enum RootCodingKeys: String, CodingKey {
|
||||
case data
|
||||
case errors
|
||||
@@ -157,11 +179,11 @@ extension JSONAPIDocument: Codable {
|
||||
return
|
||||
}
|
||||
|
||||
let data: ResourceBody
|
||||
if let noData = NoResourceBody() as? ResourceBody {
|
||||
let data: PrimaryResourceBody
|
||||
if let noData = NoResourceBody() as? PrimaryResourceBody {
|
||||
data = noData
|
||||
} 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)
|
||||
@@ -175,7 +197,7 @@ extension JSONAPIDocument: Codable {
|
||||
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 {
|
||||
@@ -199,19 +221,19 @@ extension JSONAPIDocument: Codable {
|
||||
try container.encode(linksVal, forKey: .links)
|
||||
}
|
||||
|
||||
case .data(primary: let resourceBody, included: let includes, let meta, links: let links):
|
||||
try container.encode(resourceBody, forKey: .data)
|
||||
case .data(let data):
|
||||
try container.encode(data.primary, forKey: .data)
|
||||
|
||||
if Include.self != NoIncludes.self {
|
||||
try container.encode(includes, forKey: .included)
|
||||
try container.encode(data.includes, forKey: .included)
|
||||
}
|
||||
|
||||
if MetaType.self != NoMetadata.self {
|
||||
try container.encode(meta, forKey: .meta)
|
||||
try container.encode(data.meta, forKey: .meta)
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
extension JSONAPIDocument: CustomStringConvertible {
|
||||
extension Document: CustomStringConvertible {
|
||||
public var description: String {
|
||||
return "JSONAPIDocument(body: \(String(describing: body))"
|
||||
return "Document(body: \(String(describing: body))"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ public protocol JSONAPIError: Swift.Error, Equatable, Codable {
|
||||
static var unknown: Self { get }
|
||||
}
|
||||
|
||||
public enum BasicJSONAPIError: JSONAPIError {
|
||||
public enum UnknownJSONAPIError: JSONAPIError {
|
||||
case unknownError
|
||||
|
||||
public init(from decoder: Decoder) throws {
|
||||
@@ -21,7 +21,7 @@ public enum BasicJSONAPIError: JSONAPIError {
|
||||
try container.encode("unknown")
|
||||
}
|
||||
|
||||
public static var unknown: BasicJSONAPIError {
|
||||
public static var unknown: UnknownJSONAPIError {
|
||||
return .unknownError
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
//
|
||||
// ResourceBody.swift
|
||||
// PrimaryResourceBody.swift
|
||||
// JSONAPI
|
||||
//
|
||||
// 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 init(entities: [Entity]) {
|
||||
@@ -80,12 +80,12 @@ extension ManyResourceBody {
|
||||
|
||||
extension SingleResourceBody: CustomStringConvertible {
|
||||
public var description: String {
|
||||
return "ResourceBody(\(String(describing: value)))"
|
||||
return "PrimaryResourceBody(\(String(describing: value)))"
|
||||
}
|
||||
}
|
||||
|
||||
extension ManyResourceBody: CustomStringConvertible {
|
||||
public var description: String {
|
||||
return "ResourceBody(\(String(describing: values)))"
|
||||
return "PrimaryResourceBody(\(String(describing: values)))"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,9 +14,9 @@ public struct NoLinks: Links {
|
||||
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 meta: Meta
|
||||
}
|
||||
|
||||
@@ -5,7 +5,10 @@
|
||||
// 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
|
||||
|
||||
public let value: Transformer.To
|
||||
@@ -24,6 +27,15 @@ extension TransformedAttribute: CustomStringConvertible {
|
||||
|
||||
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 Attribute<T: Codable> = TransformedAttribute<T, IdentityTransformer<T>>
|
||||
|
||||
@@ -16,7 +16,7 @@ public typealias Attributes = Codable & Equatable
|
||||
|
||||
/// Can be used as `Relationships` Type for Entities that do not
|
||||
/// have any Relationships.
|
||||
public struct NoRelatives: Relationships {}
|
||||
public struct NoRelationships: Relationships {}
|
||||
|
||||
/// Can be used as `Attributes` Type for Entities that do not
|
||||
/// have any Attributes.
|
||||
@@ -40,6 +40,21 @@ public protocol EntityDescription {
|
||||
public protocol EntityType: PrimaryResource {
|
||||
associatedtype Description: EntityDescription
|
||||
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
|
||||
@@ -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) {
|
||||
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) {
|
||||
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) {
|
||||
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() {
|
||||
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)
|
||||
|
||||
if Identifier.self != Unidentified.self {
|
||||
if Identifier.self != Unidentified<Description>.self {
|
||||
try container.encode(id, forKey: .id)
|
||||
}
|
||||
|
||||
@@ -195,7 +210,7 @@ public extension Entity {
|
||||
try container.encode(attributes, forKey: .attributes)
|
||||
}
|
||||
|
||||
if Description.Relationships.self != NoRelatives.self {
|
||||
if Description.Relationships.self != NoRelationships.self {
|
||||
try container.encode(relationships, forKey: .relationships)
|
||||
}
|
||||
}
|
||||
@@ -210,10 +225,10 @@ public extension Entity {
|
||||
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)
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
/// Any type that you would like to be encoded to and
|
||||
/// decoded from JSON API ids should conform to this
|
||||
/// 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
|
||||
/// Entities with Ids backed by a RawIdType then
|
||||
@@ -22,16 +22,17 @@ public protocol CreatableRawIdType: 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 var description: String { return "Id(Unidentified)" }
|
||||
}
|
||||
|
||||
public protocol IdType: Identifier, CustomStringConvertible {
|
||||
associatedtype EntityDescription: JSONAPI.EntityDescription
|
||||
public protocol IdType: Identifier, Hashable, CustomStringConvertible {
|
||||
associatedtype RawType: RawIdType
|
||||
|
||||
var rawValue: RawType { get }
|
||||
|
||||
@@ -5,17 +5,15 @@
|
||||
// Created by Mathew Polzin on 8/31/18.
|
||||
//
|
||||
|
||||
public protocol RelationshipType: Codable {}
|
||||
|
||||
/// An Entity relationship that can be encoded to or decoded from
|
||||
/// a JSON API "Resource Linkage."
|
||||
/// See https://jsonapi.org/format/#document-resource-object-linkage
|
||||
/// A convenient typealias might make your code much more legible: `One<EntityDescription>`
|
||||
public struct ToOneRelationship<Relatable: JSONAPI.OptionalRelatable>: Equatable, Codable {
|
||||
public struct ToOneRelationship<Relatable: JSONAPI.OptionalRelatable>: RelationshipType, Equatable {
|
||||
|
||||
public let id: Relatable.WrappedIdentifier
|
||||
|
||||
public var ids: [Relatable.WrappedIdentifier] {
|
||||
return [id]
|
||||
}
|
||||
|
||||
public init(id: Relatable.WrappedIdentifier) {
|
||||
self.id = id
|
||||
@@ -38,7 +36,7 @@ extension ToOneRelationship where Relatable.WrappedIdentifier == Optional<Relata
|
||||
/// a JSON API "Resource Linkage."
|
||||
/// See https://jsonapi.org/format/#document-resource-object-linkage
|
||||
/// A convenient typealias might make your code much more legible: `Many<EntityDescription>`
|
||||
public struct ToManyRelationship<Relatable: JSONAPI.Relatable>: Equatable, Codable {
|
||||
public struct ToManyRelationship<Relatable: JSONAPI.Relatable>: RelationshipType, Equatable {
|
||||
|
||||
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))
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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"))
|
||||
}
|
||||
|
||||
}
|
||||
@@ -11,7 +11,7 @@ import JSONAPI
|
||||
class DocumentTests: XCTestCase {
|
||||
|
||||
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)
|
||||
|
||||
XCTAssertFalse(document.body.isError)
|
||||
@@ -23,12 +23,12 @@ class DocumentTests: XCTestCase {
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
XCTAssertTrue(document.body.isError)
|
||||
@@ -48,12 +48,12 @@ class DocumentTests: XCTestCase {
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
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)
|
||||
XCTAssertNil(document.body.meta)
|
||||
@@ -72,11 +72,11 @@ class DocumentTests: XCTestCase {
|
||||
}
|
||||
|
||||
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() {
|
||||
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)
|
||||
|
||||
XCTAssertTrue(document.body.isError)
|
||||
@@ -96,12 +96,12 @@ class DocumentTests: XCTestCase {
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
XCTAssertTrue(document.body.isError)
|
||||
@@ -120,12 +120,12 @@ class DocumentTests: XCTestCase {
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
XCTAssertTrue(document.body.isError)
|
||||
@@ -148,12 +148,12 @@ class DocumentTests: XCTestCase {
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
XCTAssertTrue(document.body.isError)
|
||||
@@ -174,12 +174,12 @@ class DocumentTests: XCTestCase {
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
XCTAssertTrue(document.body.isError)
|
||||
@@ -197,12 +197,12 @@ class DocumentTests: XCTestCase {
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
XCTAssertFalse(document.body.isError)
|
||||
@@ -213,12 +213,12 @@ class DocumentTests: XCTestCase {
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
XCTAssertFalse(document.body.isError)
|
||||
@@ -233,18 +233,18 @@ class DocumentTests: XCTestCase {
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
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() {
|
||||
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)
|
||||
|
||||
XCTAssertFalse(document.body.isError)
|
||||
@@ -256,12 +256,12 @@ class DocumentTests: XCTestCase {
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
XCTAssertFalse(document.body.isError)
|
||||
@@ -273,12 +273,12 @@ class DocumentTests: XCTestCase {
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
XCTAssertFalse(document.body.isError)
|
||||
@@ -295,12 +295,12 @@ class DocumentTests: XCTestCase {
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
XCTAssertFalse(document.body.isError)
|
||||
@@ -317,20 +317,20 @@ class DocumentTests: XCTestCase {
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
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() {
|
||||
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() {
|
||||
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)
|
||||
|
||||
XCTAssertFalse(document.body.isError)
|
||||
@@ -343,12 +343,12 @@ class DocumentTests: XCTestCase {
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
XCTAssertFalse(document.body.isError)
|
||||
@@ -362,12 +362,12 @@ class DocumentTests: XCTestCase {
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
XCTAssertFalse(document.body.isError)
|
||||
@@ -385,24 +385,24 @@ class DocumentTests: XCTestCase {
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
func test_singleDocument_PolyPrimaryResource() {
|
||||
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)
|
||||
XCTAssertNil(document.body.primaryData?.value?[Author.self])
|
||||
}
|
||||
|
||||
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() {
|
||||
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)
|
||||
|
||||
XCTAssertFalse(document.body.isError)
|
||||
@@ -416,12 +416,12 @@ class DocumentTests: XCTestCase {
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
XCTAssertFalse(document.body.isError)
|
||||
@@ -439,7 +439,7 @@ class DocumentTests: XCTestCase {
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
@@ -450,7 +450,7 @@ extension DocumentTests {
|
||||
static var type: String { return "authors" }
|
||||
|
||||
typealias Attributes = NoAttributes
|
||||
typealias Relationships = NoRelatives
|
||||
typealias Relationships = NoRelationships
|
||||
}
|
||||
|
||||
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
Reference in New Issue
Block a user