mirror of
https://github.com/encounter/JSONAPI.git
synced 2026-07-10 12:18:40 -07:00
Compare commits
51 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c8fbc09582 | |||
| a88bb77bd0 | |||
| 247848e788 | |||
| 7adbdb4505 | |||
| ea663bb229 | |||
| f48c385f99 | |||
| c96bfadd82 | |||
| 88be701062 | |||
| 1e85e1d2a3 | |||
| 11a7727ac9 | |||
| e8bfbc881b | |||
| af757a2fac | |||
| 84955872f8 | |||
| cb2800abd4 | |||
| 85d5fef3c8 | |||
| e4ef61fd56 | |||
| 75ec4f156e | |||
| b45fc73188 | |||
| 3b73dc9989 | |||
| b2c81026f4 | |||
| 8d057b4398 | |||
| c8421cdd58 | |||
| cde10a8491 | |||
| ad05d3908a | |||
| 23b2b2e04f | |||
| 2988503d7d | |||
| 5ea83b07c1 | |||
| 2b59f54067 | |||
| 58a7c82436 | |||
| dc30cb3b9e | |||
| 7045373708 | |||
| 952fe8ba7e | |||
| 951c04ad44 | |||
| 57df6b147e | |||
| a88844fe5e | |||
| d1cf19f9fe | |||
| 59835fbe11 | |||
| 850a713dcb | |||
| e3c637a41e | |||
| 9cbc626410 | |||
| 6dd14daf53 | |||
| 744e08acf4 | |||
| 5da4a963d4 | |||
| 5433dddc81 | |||
| 95f9d8084d | |||
| d6911f170c | |||
| 9972d13a4e | |||
| 4dc63167d6 | |||
| 845d085455 | |||
| 45eb7d06bd | |||
| 4afe4cfb31 |
@@ -1,5 +1,6 @@
|
||||
import Foundation
|
||||
import JSONAPI
|
||||
import Poly
|
||||
|
||||
// MARK: - Preamble (setup)
|
||||
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
//: [Previous](@previous)
|
||||
|
||||
import Foundation
|
||||
import JSONAPI
|
||||
import JSONAPIOpenAPI
|
||||
|
||||
// print Entity Schema
|
||||
let encoder = JSONEncoder()
|
||||
encoder.outputFormatting = .prettyPrinted
|
||||
|
||||
let personSchemaData = try? encoder.encode(Person.openAPINode())
|
||||
|
||||
print(personSchemaData.map { String(data: $0, encoding: .utf8)! } ?? "Schema Construction Failed")
|
||||
@@ -11,8 +11,6 @@ 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 = JSONAPI.Document<SingleResourceBody<Dog>, NoMetadata, NoLinks, NoIncludes, NoAPIDescription, UnknownJSONAPIError>
|
||||
|
||||
let singleDogDocument = SingleDogDocument(apiDescription: .none, body: .init(entity: dogFromCode), includes: .none, meta: .none, links: .none)
|
||||
|
||||
let singleDogData = try! JSONEncoder().encode(singleDogDocument)
|
||||
@@ -34,8 +32,6 @@ let dogs = try! [Dog(name: "Buddy", owner: personIds[0]), Dog(name: "Joy", owner
|
||||
let houses = [House(attributes: .none, relationships: .none, meta: .none, links: .none), House(attributes: .none, relationships: .none, meta: .none, links: .none)]
|
||||
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 = JSONAPI.Document<ManyResourceBody<Person>, NoMetadata, NoLinks, Include2<Dog, House>, NoAPIDescription, UnknownJSONAPIError>
|
||||
|
||||
let includes = dogs.map { BatchPeopleDocument.Include($0) } + houses.map { BatchPeopleDocument.Include($0) }
|
||||
let batchPeopleDocument = BatchPeopleDocument(apiDescription: .none, body: .init(entities: people), includes: .init(values: includes), meta: .none, links: .none)
|
||||
let batchPeopleData = try! JSONEncoder().encode(batchPeopleDocument)
|
||||
|
||||
@@ -139,4 +139,6 @@ public enum HouseDescription: EntityDescription {
|
||||
|
||||
public typealias House = ExampleEntity<HouseDescription>
|
||||
|
||||
public typealias SingleDogDocument = JSONAPI.Document<SingleResourceBody<Dog>, NoMetadata, NoLinks, NoIncludes, NoAPIDescription, UnknownJSONAPIError>
|
||||
|
||||
public typealias BatchPeopleDocument = JSONAPI.Document<ManyResourceBody<Person>, NoMetadata, NoLinks, Include2<Dog, House>, NoAPIDescription, UnknownJSONAPIError>
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
import Foundation
|
||||
import JSONAPI
|
||||
import JSONAPITesting // for the convenience of literal initialization
|
||||
import JSONAPIOpenAPI
|
||||
|
||||
extension PersonDescription.Attributes: Sampleable {
|
||||
public static var sample: PersonDescription.Attributes {
|
||||
return .init(name: ["Abbie", "Eibba"], favoriteColor: "Blue")
|
||||
}
|
||||
}
|
||||
|
||||
extension PersonDescription.Relationships: Sampleable {
|
||||
public static var sample: PersonDescription.Relationships {
|
||||
return .init(friends: ["1", "2"], dogs: ["2"], home: "1")
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,5 @@
|
||||
<page name='Usage'/>
|
||||
<page name='Full Client & Server Example'/>
|
||||
<page name='Full Document Verbose Generation'/>
|
||||
<page name='OpenAPI Documentation'/>
|
||||
</pages>
|
||||
</playground>
|
||||
+2
-29
@@ -1,40 +1,13 @@
|
||||
{
|
||||
"object": {
|
||||
"pins": [
|
||||
{
|
||||
"package": "AnyCodable",
|
||||
"repositoryURL": "https://github.com/Flight-School/AnyCodable.git",
|
||||
"state": {
|
||||
"branch": null,
|
||||
"revision": "396ccc3dba5bdee04c1e742e7fab40582861401e",
|
||||
"version": "0.1.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"package": "FileCheck",
|
||||
"repositoryURL": "https://github.com/llvm-swift/FileCheck.git",
|
||||
"state": {
|
||||
"branch": null,
|
||||
"revision": "89b8480055f9adf8ce2f9ad5e2fac7ac1076242e",
|
||||
"version": "0.0.8"
|
||||
}
|
||||
},
|
||||
{
|
||||
"package": "Poly",
|
||||
"repositoryURL": "https://github.com/mattpolzin/Poly.git",
|
||||
"state": {
|
||||
"branch": "master",
|
||||
"revision": "77f45b8963a51c02d71fc4075eba5cff47ff0d07",
|
||||
"version": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"package": "SwiftCheck",
|
||||
"repositoryURL": "https://github.com/typelift/SwiftCheck.git",
|
||||
"state": {
|
||||
"branch": null,
|
||||
"revision": "cf9958085b2ee1643e541e407c3233d1b76c18ff",
|
||||
"version": "0.11.0"
|
||||
"revision": "77f45b8963a51c02d71fc4075eba5cff47ff0d07",
|
||||
"version": "1.0.0"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
+3
-23
@@ -11,18 +11,10 @@ let package = Package(
|
||||
targets: ["JSONAPI"]),
|
||||
.library(
|
||||
name: "JSONAPITesting",
|
||||
targets: ["JSONAPITesting"]),
|
||||
.library(
|
||||
name: "JSONAPIArbitrary",
|
||||
targets: ["JSONAPIArbitrary"]),
|
||||
.library(
|
||||
name: "JSONAPIOpenAPI",
|
||||
targets: ["JSONAPIOpenAPI"])
|
||||
targets: ["JSONAPITesting"])
|
||||
],
|
||||
dependencies: [
|
||||
.package(url: "https://github.com/mattpolzin/Poly.git", .branch("master")),
|
||||
.package(url: "https://github.com/Flight-School/AnyCodable.git", from: "0.1.0"),
|
||||
.package(url: "https://github.com/typelift/SwiftCheck.git", from: "0.11.0")
|
||||
.package(url: "https://github.com/mattpolzin/Poly.git", from: "1.0.0"),
|
||||
],
|
||||
targets: [
|
||||
.target(
|
||||
@@ -31,24 +23,12 @@ let package = Package(
|
||||
.target(
|
||||
name: "JSONAPITesting",
|
||||
dependencies: ["JSONAPI"]),
|
||||
.target(
|
||||
name: "JSONAPIArbitrary",
|
||||
dependencies: ["JSONAPI", "SwiftCheck"]),
|
||||
.target(
|
||||
name: "JSONAPIOpenAPI",
|
||||
dependencies: ["JSONAPI", "AnyCodable", "JSONAPIArbitrary"]),
|
||||
.testTarget(
|
||||
name: "JSONAPITests",
|
||||
dependencies: ["JSONAPI", "JSONAPITesting"]),
|
||||
.testTarget(
|
||||
name: "JSONAPITestingTests",
|
||||
dependencies: ["JSONAPI", "JSONAPITesting"]),
|
||||
.testTarget(
|
||||
name: "JSONAPIArbitraryTests",
|
||||
dependencies: ["JSONAPI", "SwiftCheck", "JSONAPIArbitrary"]),
|
||||
.testTarget(
|
||||
name: "JSONAPIOpenAPITests",
|
||||
dependencies: ["JSONAPI", "JSONAPIOpenAPI"])
|
||||
dependencies: ["JSONAPI", "JSONAPITesting"])
|
||||
],
|
||||
swiftLanguageVersions: [.v4_2]
|
||||
)
|
||||
|
||||
@@ -25,7 +25,7 @@ See the JSON API Spec here: https://jsonapi.org/format/
|
||||
- [Relationship Object](#relationship-object)
|
||||
- [Links Object](#links-object)
|
||||
- [Misc](#misc)
|
||||
- [JSONAPI+Testing](#jsonapitesting)
|
||||
- [Testing](#testing)
|
||||
- [Entity Validator](#entity-validator)
|
||||
- [Potential Improvements](#potential-improvements)
|
||||
- [Usage](#usage)
|
||||
@@ -100,11 +100,11 @@ Note that Playground support for importing non-system Frameworks is still a bit
|
||||
- `data`
|
||||
- [x] Encoding/Decoding
|
||||
- [x] Arbitrary
|
||||
- [ ] OpenAPI
|
||||
- [x] OpenAPI
|
||||
- `included`
|
||||
- [x] Encoding/Decoding
|
||||
- [x] Arbitrary
|
||||
- [ ] OpenAPI
|
||||
- [x] OpenAPI
|
||||
- `errors`
|
||||
- [x] Encoding/Decoding
|
||||
- [x] Arbitrary
|
||||
@@ -175,7 +175,7 @@ Note that Playground support for importing non-system Frameworks is still a bit
|
||||
- [ ] Support sparse fieldsets. At the moment, not sure what this support will look like. A client can likely just define a new model to represent a sparse population of another model in a very specific use case. On the server side, it becomes much more appealing to be able to support arbitrary combinations of omitted fields.
|
||||
- [ ] Create more descriptive errors that are easier to use for troubleshooting.
|
||||
|
||||
### JSONAPI+Testing
|
||||
### Testing
|
||||
#### Entity Validator
|
||||
- [x] Disallow optional array in `Attribute` (should be empty array, not `null`).
|
||||
- [x] Only allow `TransformedAttribute` and its derivatives as stored properties within `Attributes` struct. Computed properties can still be any type because they do not get encoded or decoded.
|
||||
@@ -836,11 +836,7 @@ print(response.author)
|
||||
The `JSONAPI` framework is packaged with a test library to help you test your `JSONAPI` integration. The test library is called `JSONAPITesting`. 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 `JSONAPITesting` in action in the Playground included with the `JSONAPI` repository.
|
||||
|
||||
# JSONAPI+Arbitrary
|
||||
The `JSONAPIArbitrary` framework adds `Arbitrary` support via `SwiftCheck`. With a little extra work on your part, this framework will allow you to create "arbitrary" (i.e. randomly generated) instances of your JSONAPI entities, includes, documents, etc.
|
||||
|
||||
This Framework is currently undocumented, but familiarity with `SwiftCheck` will likely afford the user enough information to use this library to aid in the generation of arbitrary JSONAPI Documents for testing purposes.
|
||||
This library has moved into its own Package. See https://github.com/mattpolzin/JSONAPI-Arbitrary
|
||||
|
||||
# JSONAPI+OpenAPI
|
||||
The `JSONAPIOpenAPI` framework adds the ability to generate OpenAPI compliant JSON documentation of a JSONAPI Document.
|
||||
|
||||
This library is in its infancy. The documentation will grow as the framework becomes more complete.
|
||||
This library has moved into its own Package. See https://github.com/mattpolzin/JSONAPI-OpenAPI
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
//
|
||||
// APIDescription+Arbitrary.swift
|
||||
// JSONAPIArbitrary
|
||||
//
|
||||
// Created by Mathew Polzin on 1/21/19.
|
||||
//
|
||||
|
||||
import SwiftCheck
|
||||
import JSONAPI
|
||||
|
||||
extension APIDescription: Arbitrary where Meta: Arbitrary {
|
||||
public static var arbitrary: Gen<APIDescription<Meta>> {
|
||||
return Gen.compose { c in
|
||||
APIDescription(version: c.generate(),
|
||||
meta: c.generate())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension NoAPIDescription: Arbitrary {
|
||||
public static var arbitrary: Gen<NoAPIDescription> {
|
||||
return Gen.pure(.none)
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
//
|
||||
// Attribute+Arbitrary.swift
|
||||
// JSONAPIArbitrary
|
||||
//
|
||||
// Created by Mathew Polzin on 1/15/19.
|
||||
//
|
||||
|
||||
import SwiftCheck
|
||||
import JSONAPI
|
||||
|
||||
extension Attribute: Arbitrary where RawValue: Arbitrary {
|
||||
public static var arbitrary: Gen<Attribute<RawValue>> {
|
||||
return RawValue.arbitrary.map { .init(value: $0) }
|
||||
}
|
||||
}
|
||||
|
||||
// Cannot conform TransformedAttribute to Arbitrary here
|
||||
// because there is no way to guarantee that an arbitrary
|
||||
// RawValue will successfully transform or that an
|
||||
// arbitrary Value will successfully reverse-transform.
|
||||
@@ -1,110 +0,0 @@
|
||||
//
|
||||
// Document+Arbitrary.swift
|
||||
// JSONAPIArbitrary
|
||||
//
|
||||
// Created by Mathew Polzin on 1/21/19.
|
||||
//
|
||||
|
||||
import SwiftCheck
|
||||
import JSONAPI
|
||||
|
||||
extension Document.Body.Data: Arbitrary where PrimaryResourceBody: Arbitrary, IncludeType: Arbitrary, MetaType: Arbitrary, LinksType: Arbitrary {
|
||||
public static var arbitrary: Gen<Document<PrimaryResourceBody, MetaType, LinksType, IncludeType, APIDescription, Error>.Body.Data> {
|
||||
return Gen.compose { c in
|
||||
Document.Body.Data(primary: c.generate(),
|
||||
includes: c.generate(),
|
||||
meta: c.generate(),
|
||||
links: c.generate())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension Document.Body: Arbitrary where PrimaryResourceBody: Arbitrary, IncludeType: Arbitrary, MetaType: Arbitrary, LinksType: Arbitrary, Error: Arbitrary {
|
||||
public static var arbitrary: Gen<Document<PrimaryResourceBody, MetaType, LinksType, IncludeType, APIDescription, Error>.Body> {
|
||||
return Gen.one(of: [
|
||||
arbitraryData,
|
||||
arbitraryErrors
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
extension Document.Body where PrimaryResourceBody: Arbitrary, IncludeType: Arbitrary, MetaType: Arbitrary, LinksType: Arbitrary {
|
||||
/// Arbitrary Document.Body with data (guaranteed to not
|
||||
/// be an error body).
|
||||
public static var arbitraryData: Gen<Document<PrimaryResourceBody, MetaType, LinksType, IncludeType, APIDescription, Error>.Body> {
|
||||
return Document.Body.Data.arbitrary.map(Document.Body.data)
|
||||
}
|
||||
}
|
||||
|
||||
extension Document.Body where MetaType: Arbitrary, LinksType: Arbitrary, Error: Arbitrary {
|
||||
/// Arbitrary Document.Body with errors (guaranteed to not
|
||||
/// be a data body).
|
||||
public static var arbitraryErrors: Gen<Document<PrimaryResourceBody, MetaType, LinksType, IncludeType, APIDescription, Error>.Body> {
|
||||
return Gen.compose { c in
|
||||
Document.Body.errors(c.generate(),
|
||||
meta: c.generate(),
|
||||
links: c.generate())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension Document.Body where Error: Arbitrary {
|
||||
/// Arbitrary Document.Body with errors but no
|
||||
/// metadata or links (also guaranteed to not
|
||||
/// be a data body).
|
||||
public static var arbitraryErrorsWithoutMetaOrLinks: Gen<Document<PrimaryResourceBody, MetaType, LinksType, IncludeType, APIDescription, Error>.Body> {
|
||||
return Gen.compose { c in
|
||||
Document.Body.errors(c.generate(),
|
||||
meta: nil,
|
||||
links: nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension Document: Arbitrary where PrimaryResourceBody: Arbitrary, IncludeType: Arbitrary, MetaType: Arbitrary, LinksType: Arbitrary, Error: Arbitrary, APIDescription: Arbitrary {
|
||||
public static var arbitrary: Gen<Document<PrimaryResourceBody, MetaType, LinksType, IncludeType, APIDescription, Error>> {
|
||||
return Gen.one(of: [
|
||||
arbitraryData,
|
||||
arbitraryErrors
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
extension Document where PrimaryResourceBody: Arbitrary, IncludeType: Arbitrary, MetaType: Arbitrary, LinksType: Arbitrary, APIDescription: Arbitrary {
|
||||
/// Arbitrary Document with data (guaranteed to not
|
||||
/// be an error body).
|
||||
public static var arbitraryData: Gen<Document<PrimaryResourceBody, MetaType, LinksType, IncludeType, APIDescription, Error>> {
|
||||
return Gen.compose { c in
|
||||
Document(apiDescription: c.generate(),
|
||||
body: c.generate(),
|
||||
includes: c.generate(),
|
||||
meta: c.generate(),
|
||||
links: c.generate())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension Document where MetaType: Arbitrary, LinksType: Arbitrary, Error: Arbitrary, APIDescription: Arbitrary {
|
||||
/// Arbitrary Document with errors (guaranteed to not
|
||||
/// be a data body).
|
||||
public static var arbitraryErrors: Gen<Document<PrimaryResourceBody, MetaType, LinksType, IncludeType, APIDescription, Error>> {
|
||||
return Gen.compose { c in
|
||||
Document(apiDescription: c.generate(),
|
||||
errors: c.generate(),
|
||||
meta: c.generate(),
|
||||
links: c.generate())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension Document where Error: Arbitrary, APIDescription: Arbitrary {
|
||||
/// Arbitrary Document with errors but no
|
||||
/// metadata or links (also guaranteed to not
|
||||
/// be a data body).
|
||||
public static var arbitraryErrors: Gen<Document<PrimaryResourceBody, MetaType, LinksType, IncludeType, APIDescription, Error>> {
|
||||
return Gen.compose { c in
|
||||
Document(apiDescription: c.generate(),
|
||||
errors: c.generate())
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
//
|
||||
// Entity+Arbitrary.swift
|
||||
// JSONAPIArbitrary
|
||||
//
|
||||
// Created by Mathew Polzin on 1/14/19.
|
||||
//
|
||||
|
||||
import SwiftCheck
|
||||
import JSONAPI
|
||||
|
||||
extension NoMetadata: Arbitrary {
|
||||
public static var arbitrary: Gen<NoMetadata> {
|
||||
return Gen.pure(.none)
|
||||
}
|
||||
}
|
||||
|
||||
extension NoLinks: Arbitrary {
|
||||
public static var arbitrary: Gen<NoLinks> {
|
||||
return Gen.pure(.none)
|
||||
}
|
||||
}
|
||||
|
||||
extension NoAttributes: Arbitrary {
|
||||
public static var arbitrary: Gen<NoAttributes> {
|
||||
return Gen.pure(.none)
|
||||
}
|
||||
}
|
||||
|
||||
extension NoRelationships: Arbitrary {
|
||||
public static var arbitrary: Gen<NoRelationships> {
|
||||
return Gen.pure(.none)
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: Arbitrary conformance for MetaType, LinksType, Description.Attributes,
|
||||
// and Description.Relationships must all be provided BY YOU for Entity to
|
||||
// gain Arbitrary conformance (with the exception of NoMetadata, NoLinks,
|
||||
// NoAttributes, and NoRelationships which all have Arbitrary conformance
|
||||
// out of the box).
|
||||
extension Entity: Arbitrary where MetaType: Arbitrary, LinksType: Arbitrary, Description.Attributes: Arbitrary, Description.Relationships: Arbitrary, EntityRawIdType: Arbitrary {
|
||||
public static var arbitrary: Gen<Entity<Description, MetaType, LinksType, EntityRawIdType>> {
|
||||
return Gen.compose { c in
|
||||
Entity(id: c.generate(),
|
||||
attributes: c.generate(),
|
||||
relationships: c.generate(),
|
||||
meta: c.generate(),
|
||||
links: c.generate())
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
//
|
||||
// Id+Arbitrary.swift
|
||||
// JSONAPIArbitrary
|
||||
//
|
||||
// Created by Mathew Polzin on 1/14/19.
|
||||
//
|
||||
|
||||
import SwiftCheck
|
||||
import JSONAPI
|
||||
|
||||
extension Unidentified: Arbitrary {
|
||||
public static var arbitrary: Gen<Unidentified> {
|
||||
return Gen.pure(.init())
|
||||
}
|
||||
}
|
||||
|
||||
extension Id: Arbitrary where RawType: Arbitrary {
|
||||
public static var arbitrary: Gen<Id<RawType, IdentifiableType>> {
|
||||
return RawType.arbitrary.map { Id(rawValue: $0) }
|
||||
}
|
||||
}
|
||||
@@ -1,166 +0,0 @@
|
||||
//
|
||||
// Includes+Arbitrary.swift
|
||||
// JSONAPIArbitrary
|
||||
//
|
||||
// Created by Mathew Polzin on 1/21/19.
|
||||
//
|
||||
|
||||
import SwiftCheck
|
||||
import JSONAPI
|
||||
|
||||
extension Includes: Arbitrary where I: Arbitrary {
|
||||
public static var arbitrary: Gen<Includes<I>> {
|
||||
return I
|
||||
.arbitrary
|
||||
.proliferate
|
||||
.map(Includes.init(values:))
|
||||
}
|
||||
}
|
||||
|
||||
extension NoIncludes: Arbitrary {
|
||||
public static var arbitrary: Gen<NoIncludes> {
|
||||
return Gen.pure(NoIncludes())
|
||||
}
|
||||
}
|
||||
|
||||
extension Include1: Arbitrary where A: Arbitrary {
|
||||
public static var arbitrary: Gen<Include1<A>> {
|
||||
return Gen.one(of: [
|
||||
A.arbitrary.map(Include1.init)
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
extension Include2: Arbitrary where A: Arbitrary, B: Arbitrary {
|
||||
public static var arbitrary: Gen<Include2<A, B>> {
|
||||
return Gen.one(of: [
|
||||
A.arbitrary.map(Include2.init),
|
||||
B.arbitrary.map(Include2.init)
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
extension Include3: Arbitrary where A: Arbitrary, B: Arbitrary, C: Arbitrary {
|
||||
public static var arbitrary: Gen<Include3<A, B, C>> {
|
||||
return Gen.one(of: [
|
||||
A.arbitrary.map(Include3.init),
|
||||
B.arbitrary.map(Include3.init),
|
||||
C.arbitrary.map(Include3.init)
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
extension Include4: Arbitrary where A: Arbitrary, B: Arbitrary, C: Arbitrary, D: Arbitrary {
|
||||
public static var arbitrary: Gen<Include4<A, B, C, D>> {
|
||||
return Gen.one(of: [
|
||||
A.arbitrary.map(Include4.init),
|
||||
B.arbitrary.map(Include4.init),
|
||||
C.arbitrary.map(Include4.init),
|
||||
D.arbitrary.map(Include4.init)
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
extension Include5: Arbitrary where A: Arbitrary, B: Arbitrary, C: Arbitrary, D: Arbitrary, E: Arbitrary {
|
||||
public static var arbitrary: Gen<Include5<A, B, C, D, E>> {
|
||||
return Gen.one(of: [
|
||||
A.arbitrary.map(Include5.init),
|
||||
B.arbitrary.map(Include5.init),
|
||||
C.arbitrary.map(Include5.init),
|
||||
D.arbitrary.map(Include5.init),
|
||||
E.arbitrary.map(Include5.init)
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
extension Include6: Arbitrary where A: Arbitrary, B: Arbitrary, C: Arbitrary, D: Arbitrary, E: Arbitrary, F: Arbitrary {
|
||||
public static var arbitrary: Gen<Include6<A, B, C, D, E, F>> {
|
||||
// Note broken up because compiler cannot typecheck entire array
|
||||
// before it times out
|
||||
let set1: [Gen<Include6<A, B, C, D, E, F>>] = [
|
||||
A.arbitrary.map(Include6.init),
|
||||
B.arbitrary.map(Include6.init),
|
||||
C.arbitrary.map(Include6.init)
|
||||
]
|
||||
|
||||
let set2: [Gen<Include6<A, B, C, D, E, F>>] = [
|
||||
D.arbitrary.map(Include6.init),
|
||||
E.arbitrary.map(Include6.init),
|
||||
F.arbitrary.map(Include6.init)
|
||||
]
|
||||
|
||||
return Gen.one(of: set1 + set2)
|
||||
}
|
||||
}
|
||||
|
||||
extension Include7: Arbitrary where A: Arbitrary, B: Arbitrary, C: Arbitrary, D: Arbitrary, E: Arbitrary, F: Arbitrary, G: Arbitrary {
|
||||
public static var arbitrary: Gen<Include7<A, B, C, D, E, F, G>> {
|
||||
// Note broken up because compiler cannot typecheck entire array
|
||||
// before it times out
|
||||
let set1: [Gen<Include7<A, B, C, D, E, F, G>>] = [
|
||||
A.arbitrary.map(Include7.init),
|
||||
B.arbitrary.map(Include7.init),
|
||||
C.arbitrary.map(Include7.init)
|
||||
]
|
||||
|
||||
let set2: [Gen<Include7<A, B, C, D, E, F, G>>] = [
|
||||
D.arbitrary.map(Include7.init),
|
||||
E.arbitrary.map(Include7.init),
|
||||
F.arbitrary.map(Include7.init),
|
||||
G.arbitrary.map(Include7.init)
|
||||
]
|
||||
|
||||
return Gen.one(of: set1 + set2)
|
||||
}
|
||||
}
|
||||
|
||||
extension Include8: Arbitrary where A: Arbitrary, B: Arbitrary, C: Arbitrary, D: Arbitrary, E: Arbitrary, F: Arbitrary, G: Arbitrary, H: Arbitrary {
|
||||
public static var arbitrary: Gen<Include8<A, B, C, D, E, F, G, H>> {
|
||||
// Note broken up because compiler cannot typecheck entire array
|
||||
// before it times out
|
||||
let set1: [Gen<Include8<A, B, C, D, E, F, G, H>>] = [
|
||||
A.arbitrary.map(Include8.init),
|
||||
B.arbitrary.map(Include8.init),
|
||||
C.arbitrary.map(Include8.init)
|
||||
]
|
||||
|
||||
let set2: [Gen<Include8<A, B, C, D, E, F, G, H>>] = [
|
||||
D.arbitrary.map(Include8.init),
|
||||
E.arbitrary.map(Include8.init),
|
||||
F.arbitrary.map(Include8.init)
|
||||
]
|
||||
|
||||
let set3: [Gen<Include8<A, B, C, D, E, F, G, H>>] = [
|
||||
G.arbitrary.map(Include8.init),
|
||||
H.arbitrary.map(Include8.init)
|
||||
]
|
||||
|
||||
return Gen.one(of: set1 + set2 + set3)
|
||||
}
|
||||
}
|
||||
|
||||
extension Include9: Arbitrary where A: Arbitrary, B: Arbitrary, C: Arbitrary, D: Arbitrary, E: Arbitrary, F: Arbitrary, G: Arbitrary, H: Arbitrary, I: Arbitrary {
|
||||
public static var arbitrary: Gen<Include9<A, B, C, D, E, F, G, H, I>> {
|
||||
// Note broken up because compiler cannot typecheck entire array
|
||||
// before it times out
|
||||
let set1: [Gen<Include9<A, B, C, D, E, F, G, H, I>>] = [
|
||||
A.arbitrary.map(Include9.init),
|
||||
B.arbitrary.map(Include9.init),
|
||||
C.arbitrary.map(Include9.init)
|
||||
]
|
||||
|
||||
let set2: [Gen<Include9<A, B, C, D, E, F, G, H, I>>] = [
|
||||
D.arbitrary.map(Include9.init),
|
||||
E.arbitrary.map(Include9.init),
|
||||
F.arbitrary.map(Include9.init)
|
||||
]
|
||||
|
||||
let set3: [Gen<Include9<A, B, C, D, E, F, G, H, I>>] = [
|
||||
G.arbitrary.map(Include9.init),
|
||||
H.arbitrary.map(Include9.init),
|
||||
I.arbitrary.map(Include9.init)
|
||||
]
|
||||
|
||||
return Gen.one(of: set1 + set2 + set3)
|
||||
}
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
//
|
||||
// Relationship+Arbitrary.swift
|
||||
// JSONAPIArbitrary
|
||||
//
|
||||
// Created by Mathew Polzin on 1/15/19.
|
||||
//
|
||||
|
||||
import SwiftCheck
|
||||
import JSONAPI
|
||||
|
||||
extension ToOneRelationship: Arbitrary where Identifiable.Identifier: Arbitrary, MetaType: Arbitrary, LinksType: Arbitrary {
|
||||
public static var arbitrary: Gen<ToOneRelationship<Identifiable, MetaType, LinksType>> {
|
||||
return Gen.compose { c in
|
||||
return .init(id: c.generate(),
|
||||
meta: c.generate(),
|
||||
links: c.generate())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension ToOneRelationship where MetaType: Arbitrary, LinksType: Arbitrary {
|
||||
/// Create a generator of arbitrary ToOneRelationships that will all
|
||||
/// point to one of the given entities. This allows you to create
|
||||
/// arbitrary relationships that make sense in a broader context where
|
||||
/// the relationship must actually point to another entity.
|
||||
public static func arbitrary<E: EntityType>(givenEntities: [E]) -> Gen<ToOneRelationship<Identifiable, MetaType, LinksType>> where E.Id == Identifiable.Identifier {
|
||||
|
||||
return Gen.compose { c in
|
||||
let idGen = Gen.fromElements(of: givenEntities).map { $0.id }
|
||||
return .init(id: c.generate(using: idGen),
|
||||
meta: c.generate(),
|
||||
links: c.generate())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension ToManyRelationship: Arbitrary where Relatable.Identifier: Arbitrary, MetaType: Arbitrary, LinksType: Arbitrary {
|
||||
public static var arbitrary: Gen<ToManyRelationship<Relatable, MetaType, LinksType>> {
|
||||
return Gen.compose { c in
|
||||
return .init(ids: c.generate(),
|
||||
meta: c.generate(),
|
||||
links: c.generate())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension ToManyRelationship where MetaType: Arbitrary, LinksType: Arbitrary {
|
||||
/// Create a generator of arbitrary ToManyRelationships that will all
|
||||
/// point to some number of the given entities. This allows you to create
|
||||
/// arbitrary relationships that make sense in a broader context where
|
||||
/// the relationship must actually point to other existing entities.
|
||||
public static func arbitrary<E: EntityType>(givenEntities: [E]) -> Gen<ToManyRelationship<Relatable, MetaType, LinksType>> where E.Id == Relatable.Identifier {
|
||||
return Gen.compose { c in
|
||||
let idsGen = Gen.fromElements(of: givenEntities).map { $0.id }.proliferate
|
||||
return .init(ids: c.generate(using: idsGen),
|
||||
meta: c.generate(),
|
||||
links: c.generate())
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
//
|
||||
// ResourceBody+Arbitrary.swift
|
||||
// JSONAPIArbitrary
|
||||
//
|
||||
// Created by Mathew Polzin on 1/21/19.
|
||||
//
|
||||
|
||||
import SwiftCheck
|
||||
import JSONAPI
|
||||
|
||||
extension SingleResourceBody: Arbitrary where Entity: Arbitrary {
|
||||
public static var arbitrary: Gen<SingleResourceBody<Entity>> {
|
||||
return Entity.arbitrary.map(SingleResourceBody.init(entity:))
|
||||
}
|
||||
}
|
||||
|
||||
extension ManyResourceBody: Arbitrary where Entity: Arbitrary {
|
||||
public static var arbitrary: Gen<ManyResourceBody<Entity>> {
|
||||
return Entity.arbitrary.proliferate.map(ManyResourceBody.init(entities:))
|
||||
}
|
||||
}
|
||||
|
||||
extension NoResourceBody: Arbitrary {
|
||||
public static var arbitrary: Gen<NoResourceBody> {
|
||||
return Gen.pure(.none)
|
||||
}
|
||||
}
|
||||
@@ -1,156 +0,0 @@
|
||||
//
|
||||
// JSONAPIOpenAPITypes.swift
|
||||
// JSONAPIOpenAPI
|
||||
//
|
||||
// Created by Mathew Polzin on 1/13/19.
|
||||
//
|
||||
|
||||
import JSONAPI
|
||||
import AnyCodable
|
||||
|
||||
private protocol _Optional {}
|
||||
extension Optional: _Optional {}
|
||||
|
||||
extension Attribute: OpenAPINodeType where RawValue: OpenAPINodeType {
|
||||
static public func openAPINode() throws -> JSONNode {
|
||||
// If the RawValue is not required, we actually consider it
|
||||
// nullable. To be not required is for the Attribute itself
|
||||
// to be optional.
|
||||
if try !RawValue.openAPINode().required {
|
||||
return try RawValue.openAPINode().requiredNode().nullableNode()
|
||||
}
|
||||
return try RawValue.openAPINode()
|
||||
}
|
||||
}
|
||||
|
||||
extension Attribute: RawOpenAPINodeType where RawValue: RawRepresentable, RawValue.RawValue: OpenAPINodeType {
|
||||
static public func rawOpenAPINode() throws -> JSONNode {
|
||||
// If the RawValue is not required, we actually consider it
|
||||
// nullable. To be not required is for the Attribute itself
|
||||
// to be optional.
|
||||
if try !RawValue.RawValue.openAPINode().required {
|
||||
return try RawValue.RawValue.openAPINode().requiredNode().nullableNode()
|
||||
}
|
||||
return try RawValue.RawValue.openAPINode()
|
||||
}
|
||||
}
|
||||
|
||||
extension Attribute: WrappedRawOpenAPIType where RawValue: RawOpenAPINodeType {
|
||||
public static func wrappedOpenAPINode() throws -> JSONNode {
|
||||
// If the RawValue is not required, we actually consider it
|
||||
// nullable. To be not required is for the Attribute itself
|
||||
// to be optional.
|
||||
if try !RawValue.rawOpenAPINode().required {
|
||||
return try RawValue.rawOpenAPINode().requiredNode().nullableNode()
|
||||
}
|
||||
return try RawValue.rawOpenAPINode()
|
||||
}
|
||||
}
|
||||
|
||||
extension Attribute: AnyJSONCaseIterable where RawValue: CaseIterable, RawValue: Codable {
|
||||
public static var allCases: [AnyCodable] {
|
||||
return (try? allCases(from: Array(RawValue.allCases))) ?? []
|
||||
}
|
||||
}
|
||||
|
||||
extension Attribute: AnyWrappedJSONCaseIterable where RawValue: AnyJSONCaseIterable {
|
||||
public static var allCases: [AnyCodable] {
|
||||
return RawValue.allCases
|
||||
}
|
||||
}
|
||||
|
||||
extension TransformedAttribute: OpenAPINodeType where RawValue: OpenAPINodeType {
|
||||
static public func openAPINode() throws -> JSONNode {
|
||||
// If the RawValue is not required, we actually consider it
|
||||
// nullable. To be not required is for the Attribute itself
|
||||
// to be optional.
|
||||
if try !RawValue.openAPINode().required {
|
||||
return try RawValue.openAPINode().requiredNode().nullableNode()
|
||||
}
|
||||
return try RawValue.openAPINode()
|
||||
}
|
||||
}
|
||||
|
||||
extension RelationshipType {
|
||||
static func relationshipNode(nullable: Bool) -> JSONNode {
|
||||
let propertiesDict: [String: JSONNode] = [
|
||||
"id": .string(.init(format: .generic,
|
||||
required: true),
|
||||
.init()),
|
||||
"type": .string(.init(format: .generic,
|
||||
required: true),
|
||||
.init())
|
||||
]
|
||||
|
||||
return .object(.init(format: .generic,
|
||||
required: true,
|
||||
nullable: nullable),
|
||||
.init(properties: propertiesDict))
|
||||
}
|
||||
}
|
||||
|
||||
extension ToOneRelationship: OpenAPINodeType {
|
||||
// TODO: const for json `type`
|
||||
// TODO: metadata & links
|
||||
static public func openAPINode() throws -> JSONNode {
|
||||
let nullable = Identifiable.self is _Optional.Type
|
||||
return .object(.init(format: .generic,
|
||||
required: true),
|
||||
.init(properties: [
|
||||
"data": ToOneRelationship.relationshipNode(nullable: nullable)
|
||||
]))
|
||||
}
|
||||
}
|
||||
|
||||
extension ToManyRelationship: OpenAPINodeType {
|
||||
// TODO: const for json `type`
|
||||
// TODO: metadata & links
|
||||
static public func openAPINode() throws -> JSONNode {
|
||||
return .object(.init(format: .generic,
|
||||
required: true),
|
||||
.init(properties: [
|
||||
"data": .array(.init(format: .generic,
|
||||
required: true),
|
||||
.init(items: ToManyRelationship.relationshipNode(nullable: false)))
|
||||
]))
|
||||
}
|
||||
}
|
||||
|
||||
extension Entity: OpenAPINodeType where Description.Attributes: Sampleable, Description.Relationships: Sampleable {
|
||||
public static func openAPINode() throws -> JSONNode {
|
||||
// TODO: metadata, links
|
||||
|
||||
let idNode = JSONNode.string(.init(format: .generic,
|
||||
required: true),
|
||||
.init())
|
||||
let idProperty = ("id", idNode)
|
||||
|
||||
let typeNode = JSONNode.string(.init(format: .generic,
|
||||
required: true),
|
||||
.init())
|
||||
let typeProperty = ("type", typeNode)
|
||||
|
||||
let attributesNode: JSONNode? = Description.Attributes.self == NoAttributes.self
|
||||
? nil
|
||||
: try Description.Attributes.genericObjectOpenAPINode()
|
||||
|
||||
let attributesProperty = attributesNode.map { ("attributes", $0) }
|
||||
|
||||
let relationshipsNode: JSONNode? = Description.Relationships.self == NoRelationships.self
|
||||
? nil
|
||||
: try Description.Relationships.genericObjectOpenAPINode()
|
||||
|
||||
let relationshipsProperty = relationshipsNode.map { ("relationships", $0) }
|
||||
|
||||
let propertiesDict = Dictionary([
|
||||
idProperty,
|
||||
typeProperty,
|
||||
attributesProperty,
|
||||
relationshipsProperty
|
||||
].compactMap { $0 }) { _, value in value }
|
||||
|
||||
return .object(.init(format: .generic,
|
||||
required: true),
|
||||
.init(properties: propertiesDict))
|
||||
}
|
||||
}
|
||||
@@ -1,176 +0,0 @@
|
||||
//
|
||||
// OpenAPITypes+Codable.swift
|
||||
// JSONAPIOpenAPI
|
||||
//
|
||||
// Created by Mathew Polzin on 1/14/19.
|
||||
//
|
||||
|
||||
extension JSONNode.Context: Encodable {
|
||||
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case type
|
||||
case format
|
||||
case allowedValues = "enum"
|
||||
case nullable
|
||||
}
|
||||
|
||||
public func encode(to encoder: Encoder) throws {
|
||||
var container = encoder.container(keyedBy: CodingKeys.self)
|
||||
|
||||
try container.encode(format.jsonType, forKey: .type)
|
||||
|
||||
if format != Format.unspecified {
|
||||
try container.encode(format, forKey: .format)
|
||||
}
|
||||
|
||||
if allowedValues != nil {
|
||||
try container.encode(allowedValues, forKey: .allowedValues)
|
||||
}
|
||||
|
||||
try container.encode(nullable, forKey: .nullable)
|
||||
}
|
||||
}
|
||||
|
||||
extension JSONNode.NumericContext: Encodable {
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case multipleOf
|
||||
case maximum
|
||||
case exclusiveMaximum
|
||||
case minimum
|
||||
case exclusiveMinimum
|
||||
}
|
||||
|
||||
public func encode(to encoder: Encoder) throws {
|
||||
var container = encoder.container(keyedBy: CodingKeys.self)
|
||||
|
||||
if multipleOf != nil {
|
||||
try container.encode(multipleOf, forKey: .multipleOf)
|
||||
}
|
||||
|
||||
if maximum != nil {
|
||||
try container.encode(maximum, forKey: .maximum)
|
||||
}
|
||||
|
||||
if exclusiveMaximum != nil {
|
||||
try container.encode(exclusiveMaximum, forKey: .exclusiveMaximum)
|
||||
}
|
||||
|
||||
if minimum != nil {
|
||||
try container.encode(minimum, forKey: .minimum)
|
||||
}
|
||||
|
||||
if exclusiveMinimum != nil {
|
||||
try container.encode(exclusiveMinimum, forKey: .exclusiveMinimum)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension JSONNode.StringContext: Encodable {
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case maxLength
|
||||
case minLength
|
||||
case pattern
|
||||
}
|
||||
|
||||
public func encode(to encoder: Encoder) throws {
|
||||
var container = encoder.container(keyedBy: CodingKeys.self)
|
||||
|
||||
if maxLength != nil {
|
||||
try container.encode(maxLength, forKey: .maxLength)
|
||||
}
|
||||
|
||||
try container.encode(minLength, forKey: .minLength)
|
||||
|
||||
if pattern != nil {
|
||||
try container.encode(pattern, forKey: .pattern)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension JSONNode.ArrayContext: Encodable {
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case items
|
||||
case maxItems
|
||||
case minItems
|
||||
case uniqueItems
|
||||
}
|
||||
|
||||
public func encode(to encoder: Encoder) throws {
|
||||
var container = encoder.container(keyedBy: CodingKeys.self)
|
||||
|
||||
try container.encode(items, forKey: .items)
|
||||
|
||||
if maxItems != nil {
|
||||
try container.encode(maxItems, forKey: .maxItems)
|
||||
}
|
||||
|
||||
try container.encode(minItems, forKey: .minItems)
|
||||
|
||||
try container.encode(uniqueItems, forKey: .uniqueItems)
|
||||
}
|
||||
}
|
||||
|
||||
extension JSONNode.ObjectContext : Encodable{
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case maxProperties
|
||||
case minProperties
|
||||
case properties
|
||||
case additionalProperties
|
||||
case required
|
||||
}
|
||||
|
||||
public func encode(to encoder: Encoder) throws {
|
||||
var container = encoder.container(keyedBy: CodingKeys.self)
|
||||
|
||||
if maxProperties != nil {
|
||||
try container.encode(maxProperties, forKey: .maxProperties)
|
||||
}
|
||||
|
||||
try container.encode(properties, forKey: .properties)
|
||||
|
||||
if additionalProperties != nil {
|
||||
try container.encode(additionalProperties, forKey: .additionalProperties)
|
||||
}
|
||||
|
||||
let required = properties.filter { (name, node) in
|
||||
node.required
|
||||
}.keys
|
||||
|
||||
try container.encode(Array(required), forKey: .required)
|
||||
|
||||
try container.encode(max(minProperties, required.count), forKey: .minProperties)
|
||||
}
|
||||
}
|
||||
|
||||
extension JSONNode: Encodable {
|
||||
public func encode(to encoder: Encoder) throws {
|
||||
switch self {
|
||||
case .boolean(let context):
|
||||
try context.encode(to: encoder)
|
||||
|
||||
case .object(let contextA as Encodable, let contextB as Encodable),
|
||||
.array(let contextA as Encodable, let contextB as Encodable),
|
||||
.number(let contextA as Encodable, let contextB as Encodable),
|
||||
.integer(let contextA as Encodable, let contextB as Encodable),
|
||||
.string(let contextA as Encodable, let contextB as Encodable):
|
||||
try contextA.encode(to: encoder)
|
||||
try contextB.encode(to: encoder)
|
||||
|
||||
case .allOf(let nodes):
|
||||
// TODO
|
||||
print("TODO")
|
||||
|
||||
case .oneOf(let nodes):
|
||||
// TODO
|
||||
print("TODO")
|
||||
|
||||
case .anyOf(let nodes):
|
||||
// TODO
|
||||
print("TODO")
|
||||
|
||||
case .not(let node):
|
||||
// TODO
|
||||
print("TODO")
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user