mirror of
https://github.com/encounter/JSONAPI.git
synced 2026-07-10 12:18:40 -07:00
Compare commits
33 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 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 | |||
| 6ce962cec9 | |||
| 477dae0a73 | |||
| 4afe4cfb31 |
@@ -1,5 +1,6 @@
|
||||
import Foundation
|
||||
import JSONAPI
|
||||
import Poly
|
||||
|
||||
// MARK: - Preamble (setup)
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
import Foundation
|
||||
import JSONAPI
|
||||
import JSONAPIOpenAPI
|
||||
import Poly
|
||||
|
||||
// print Entity Schema
|
||||
let encoder = JSONEncoder()
|
||||
@@ -10,4 +11,36 @@ encoder.outputFormatting = .prettyPrinted
|
||||
|
||||
let personSchemaData = try? encoder.encode(Person.openAPINode())
|
||||
|
||||
print("Person Schema")
|
||||
print("====")
|
||||
print(personSchemaData.map { String(data: $0, encoding: .utf8)! } ?? "Schema Construction Failed")
|
||||
print("====")
|
||||
|
||||
let dogDocumentSchemaData = try? encoder.encode(SingleDogDocument.openAPINodeWithExample(using: encoder))
|
||||
|
||||
print("Dog Document Schema")
|
||||
print("====")
|
||||
print(dogDocumentSchemaData.map { String(data: $0, encoding: .utf8)! } ?? "Schema Construction Failed")
|
||||
print("====")
|
||||
|
||||
let batchPersonSchemaData = try? encoder.encode(BatchPeopleDocument.openAPINodeWithExample(using: encoder))
|
||||
|
||||
print("Batch Person Document Schema")
|
||||
print("====")
|
||||
print(batchPersonSchemaData.map { String(data: $0, encoding: .utf8)! } ?? "Schema Construction Failed")
|
||||
print("====")
|
||||
|
||||
let tmp: [String: OpenAPIComponents.SchemasDict.RefType] = [
|
||||
"BatchPerson": try! BatchPeopleDocument.openAPINodeWithExample()
|
||||
]
|
||||
|
||||
let components = OpenAPIComponents(schemas: tmp)
|
||||
|
||||
let batchPeopleRef = JSONReference(type: \OpenAPIComponents.schemas, selector: "BatchPerson")
|
||||
|
||||
let tmp2 = JSONNode.reference(batchPeopleRef)
|
||||
|
||||
print("====")
|
||||
print("====")
|
||||
//print(String(data: try! encoder.encode(components), encoding: .utf8)!)
|
||||
print(String(data: try! encoder.encode(tmp2), encoding: .utf8)!)
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -2,15 +2,70 @@ import Foundation
|
||||
import JSONAPI
|
||||
import JSONAPITesting // for the convenience of literal initialization
|
||||
import JSONAPIOpenAPI
|
||||
import SwiftCheck
|
||||
import JSONAPIArbitrary
|
||||
|
||||
extension PersonDescription.Attributes: Arbitrary, Sampleable {
|
||||
public static var arbitrary: Gen<PersonDescription.Attributes> {
|
||||
return Gen.compose { c in
|
||||
return PersonDescription.Attributes(name: c.generate(),
|
||||
favoriteColor: c.generate())
|
||||
}
|
||||
}
|
||||
|
||||
extension PersonDescription.Attributes: Sampleable {
|
||||
public static var sample: PersonDescription.Attributes {
|
||||
return .init(name: ["Abbie", "Eibba"], favoriteColor: "Blue")
|
||||
}
|
||||
}
|
||||
|
||||
extension PersonDescription.Relationships: Sampleable {
|
||||
extension PersonDescription.Relationships: Arbitrary, Sampleable {
|
||||
public static var arbitrary: Gen<PersonDescription.Relationships> {
|
||||
return Gen.compose { c in
|
||||
return PersonDescription.Relationships(friends: c.generate(),
|
||||
dogs: c.generate(),
|
||||
home: c.generate())
|
||||
}
|
||||
}
|
||||
|
||||
public static var sample: PersonDescription.Relationships {
|
||||
return .init(friends: ["1", "2"], dogs: ["2"], home: "1")
|
||||
}
|
||||
}
|
||||
|
||||
extension DogDescription.Attributes: Arbitrary, Sampleable {
|
||||
public static var arbitrary: Gen<DogDescription.Attributes> {
|
||||
return Gen.compose { c in
|
||||
return DogDescription.Attributes(name: c.generate())
|
||||
}
|
||||
}
|
||||
|
||||
public static var sample: DogDescription.Attributes {
|
||||
return DogDescription.Attributes.arbitrary.generate
|
||||
}
|
||||
}
|
||||
|
||||
extension DogDescription.Relationships: Arbitrary, Sampleable {
|
||||
public static var arbitrary: Gen<DogDescription.Relationships> {
|
||||
return Gen.compose { c in
|
||||
return DogDescription.Relationships(owner: c.generate())
|
||||
}
|
||||
}
|
||||
|
||||
public static var sample: DogDescription.Relationships {
|
||||
return DogDescription.Relationships.arbitrary.generate
|
||||
}
|
||||
}
|
||||
|
||||
extension Document: Sampleable where PrimaryResourceBody: Arbitrary, IncludeType: Arbitrary, MetaType: Arbitrary, LinksType: Arbitrary, Error: Arbitrary, APIDescription: Arbitrary {
|
||||
public static var sample: Document {
|
||||
return Document.arbitrary.generate
|
||||
}
|
||||
|
||||
public static var successSample: Document? {
|
||||
return Document.arbitraryData.generate
|
||||
}
|
||||
|
||||
public static var failureSample: Document? {
|
||||
return Document.arbitraryErrors.generate
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
-2
@@ -23,9 +23,9 @@
|
||||
"package": "Poly",
|
||||
"repositoryURL": "https://github.com/mattpolzin/Poly.git",
|
||||
"state": {
|
||||
"branch": "master",
|
||||
"branch": null,
|
||||
"revision": "77f45b8963a51c02d71fc4075eba5cff47ff0d07",
|
||||
"version": null
|
||||
"version": "1.0.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
||||
+1
-1
@@ -20,7 +20,7 @@ let package = Package(
|
||||
targets: ["JSONAPIOpenAPI"])
|
||||
],
|
||||
dependencies: [
|
||||
.package(url: "https://github.com/mattpolzin/Poly.git", .branch("master")),
|
||||
.package(url: "https://github.com/mattpolzin/Poly.git", from: "1.0.0"),
|
||||
.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")
|
||||
],
|
||||
|
||||
@@ -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)
|
||||
@@ -99,27 +99,27 @@ Note that Playground support for importing non-system Frameworks is still a bit
|
||||
#### Document
|
||||
- `data`
|
||||
- [x] Encoding/Decoding
|
||||
- [ ] Arbitrary
|
||||
- [ ] OpenAPI
|
||||
- [x] Arbitrary
|
||||
- [x] OpenAPI
|
||||
- `included`
|
||||
- [x] Encoding/Decoding
|
||||
- [ ] Arbitrary
|
||||
- [ ] OpenAPI
|
||||
- [x] Arbitrary
|
||||
- [x] OpenAPI
|
||||
- `errors`
|
||||
- [x] Encoding/Decoding
|
||||
- [ ] Arbitrary
|
||||
- [x] Arbitrary
|
||||
- [ ] OpenAPI
|
||||
- `meta`
|
||||
- [x] Encoding/Decoding
|
||||
- [ ] Arbitrary
|
||||
- [x] Arbitrary
|
||||
- [ ] OpenAPI
|
||||
- `jsonapi` (i.e. API Information)
|
||||
- [x] Encoding/Decoding
|
||||
- [ ] Arbitrary
|
||||
- [x] Arbitrary
|
||||
- [ ] OpenAPI
|
||||
- `links`
|
||||
- [x] Encoding/Decoding
|
||||
- [ ] Arbitrary
|
||||
- [x] Arbitrary
|
||||
- [ ] OpenAPI
|
||||
|
||||
#### Resource Object
|
||||
@@ -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.
|
||||
@@ -838,7 +838,7 @@ The `JSONAPI` framework is packaged with a test library to help you test your `J
|
||||
# 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 library does not offer full support of all `JSONAPI` types yet. The documentation will grow as the framework becomes more complete.
|
||||
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.
|
||||
|
||||
# JSONAPI+OpenAPI
|
||||
The `JSONAPIOpenAPI` framework adds the ability to generate OpenAPI compliant JSON documentation of a JSONAPI Document.
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
//
|
||||
// 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)
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,7 @@ extension Attribute: Arbitrary where RawValue: Arbitrary {
|
||||
}
|
||||
}
|
||||
|
||||
// Cannot extend TransformedAttribute here
|
||||
// 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.
|
||||
|
||||
@@ -0,0 +1,110 @@
|
||||
//
|
||||
// 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())
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
//
|
||||
// Error+Arbitrary.swift
|
||||
// JSONAPIArbitrary
|
||||
//
|
||||
// Created by Mathew Polzin on 1/21/19.
|
||||
//
|
||||
|
||||
import SwiftCheck
|
||||
import JSONAPI
|
||||
|
||||
extension UnknownJSONAPIError: Arbitrary {
|
||||
public static var arbitrary: Gen<UnknownJSONAPIError> {
|
||||
return Gen.pure(.unknownError)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,166 @@
|
||||
//
|
||||
// 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)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
//
|
||||
// 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)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
//
|
||||
// JSONAPIInclude+OpenAPI.swift
|
||||
// JSONAPIOpenAPI
|
||||
//
|
||||
// Created by Mathew Polzin on 1/22/19.
|
||||
//
|
||||
|
||||
import JSONAPI
|
||||
import Foundation
|
||||
|
||||
extension Includes: OpenAPINodeType where I: OpenAPINodeType {
|
||||
public static func openAPINode() throws -> JSONNode {
|
||||
let includeNode = try I.openAPINode()
|
||||
|
||||
return .array(.init(format: .generic,
|
||||
required: true),
|
||||
.init(items: includeNode,
|
||||
uniqueItems: true))
|
||||
}
|
||||
}
|
||||
|
||||
extension Include0: OpenAPINodeType {
|
||||
public static func openAPINode() throws -> JSONNode {
|
||||
throw OpenAPITypeError.invalidNode
|
||||
}
|
||||
}
|
||||
|
||||
extension Include1: OpenAPINodeType where A: OpenAPINodeType {
|
||||
public static func openAPINode() throws -> JSONNode {
|
||||
return try .one(of: [A.openAPINode()])
|
||||
}
|
||||
}
|
||||
|
||||
extension Include2: OpenAPINodeType where A: OpenAPINodeType, B: OpenAPINodeType {
|
||||
public static func openAPINode() throws -> JSONNode {
|
||||
return try .one(of: [
|
||||
A.openAPINode(),
|
||||
B.openAPINode()
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
extension Include3: OpenAPINodeType where A: OpenAPINodeType, B: OpenAPINodeType, C: OpenAPINodeType {
|
||||
public static func openAPINode() throws -> JSONNode {
|
||||
return try .one(of: [
|
||||
A.openAPINode(),
|
||||
B.openAPINode(),
|
||||
C.openAPINode()
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
extension Include4: OpenAPINodeType where A: OpenAPINodeType, B: OpenAPINodeType, C: OpenAPINodeType, D: OpenAPINodeType {
|
||||
public static func openAPINode() throws -> JSONNode {
|
||||
return try .one(of: [
|
||||
A.openAPINode(),
|
||||
B.openAPINode(),
|
||||
C.openAPINode(),
|
||||
D.openAPINode()
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
extension Include5: OpenAPINodeType where A: OpenAPINodeType, B: OpenAPINodeType, C: OpenAPINodeType, D: OpenAPINodeType, E: OpenAPINodeType {
|
||||
public static func openAPINode() throws -> JSONNode {
|
||||
return try .one(of: [
|
||||
A.openAPINode(),
|
||||
B.openAPINode(),
|
||||
C.openAPINode(),
|
||||
D.openAPINode(),
|
||||
E.openAPINode()
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
extension Include6: OpenAPINodeType where A: OpenAPINodeType, B: OpenAPINodeType, C: OpenAPINodeType, D: OpenAPINodeType, E: OpenAPINodeType, F: OpenAPINodeType {
|
||||
public static func openAPINode() throws -> JSONNode {
|
||||
return try .one(of: [
|
||||
A.openAPINode(),
|
||||
B.openAPINode(),
|
||||
C.openAPINode(),
|
||||
D.openAPINode(),
|
||||
E.openAPINode(),
|
||||
F.openAPINode()
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
extension Include7: OpenAPINodeType where A: OpenAPINodeType, B: OpenAPINodeType, C: OpenAPINodeType, D: OpenAPINodeType, E: OpenAPINodeType, F: OpenAPINodeType, G: OpenAPINodeType {
|
||||
public static func openAPINode() throws -> JSONNode {
|
||||
return try .one(of: [
|
||||
A.openAPINode(),
|
||||
B.openAPINode(),
|
||||
C.openAPINode(),
|
||||
D.openAPINode(),
|
||||
E.openAPINode(),
|
||||
F.openAPINode(),
|
||||
G.openAPINode()
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
extension Include8: OpenAPINodeType where A: OpenAPINodeType, B: OpenAPINodeType, C: OpenAPINodeType, D: OpenAPINodeType, E: OpenAPINodeType, F: OpenAPINodeType, G: OpenAPINodeType, H: OpenAPINodeType {
|
||||
public static func openAPINode() throws -> JSONNode {
|
||||
return try .one(of: [
|
||||
A.openAPINode(),
|
||||
B.openAPINode(),
|
||||
C.openAPINode(),
|
||||
D.openAPINode(),
|
||||
E.openAPINode(),
|
||||
F.openAPINode(),
|
||||
G.openAPINode(),
|
||||
H.openAPINode()
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
extension Include9: OpenAPINodeType where A: OpenAPINodeType, B: OpenAPINodeType, C: OpenAPINodeType, D: OpenAPINodeType, E: OpenAPINodeType, F: OpenAPINodeType, G: OpenAPINodeType, H: OpenAPINodeType, I: OpenAPINodeType {
|
||||
public static func openAPINode() throws -> JSONNode {
|
||||
return try .one(of: [
|
||||
A.openAPINode(),
|
||||
B.openAPINode(),
|
||||
C.openAPINode(),
|
||||
D.openAPINode(),
|
||||
E.openAPINode(),
|
||||
F.openAPINode(),
|
||||
G.openAPINode(),
|
||||
H.openAPINode(),
|
||||
I.openAPINode()
|
||||
])
|
||||
}
|
||||
}
|
||||
+91
-16
@@ -6,11 +6,17 @@
|
||||
//
|
||||
|
||||
import JSONAPI
|
||||
import Foundation
|
||||
import AnyCodable
|
||||
|
||||
private protocol _Optional {}
|
||||
extension Optional: _Optional {}
|
||||
|
||||
private protocol Wrapper {
|
||||
associatedtype Wrapped
|
||||
}
|
||||
extension Optional: Wrapper {}
|
||||
|
||||
extension Attribute: OpenAPINodeType where RawValue: OpenAPINodeType {
|
||||
static public func openAPINode() throws -> JSONNode {
|
||||
// If the RawValue is not required, we actually consider it
|
||||
@@ -48,14 +54,26 @@ extension Attribute: WrappedRawOpenAPIType where RawValue: RawOpenAPINodeType {
|
||||
}
|
||||
|
||||
extension Attribute: AnyJSONCaseIterable where RawValue: CaseIterable, RawValue: Codable {
|
||||
public static var allCases: [AnyCodable] {
|
||||
return (try? allCases(from: Array(RawValue.allCases))) ?? []
|
||||
public static func allCases(using encoder: JSONEncoder) -> [AnyCodable] {
|
||||
return (try? allCases(from: Array(RawValue.allCases), using: encoder)) ?? []
|
||||
}
|
||||
}
|
||||
|
||||
extension Attribute: AnyWrappedJSONCaseIterable where RawValue: AnyJSONCaseIterable {
|
||||
public static var allCases: [AnyCodable] {
|
||||
return RawValue.allCases
|
||||
public static func allCases(using encoder: JSONEncoder) -> [AnyCodable] {
|
||||
return RawValue.allCases(using: encoder)
|
||||
}
|
||||
}
|
||||
|
||||
extension Attribute: GenericOpenAPINodeType where RawValue: GenericOpenAPINodeType {
|
||||
public static func genericOpenAPINode(using encoder: JSONEncoder) throws -> JSONNode {
|
||||
return try RawValue.genericOpenAPINode(using: encoder)
|
||||
}
|
||||
}
|
||||
|
||||
extension Attribute: DateOpenAPINodeType where RawValue: DateOpenAPINodeType {
|
||||
public static func dateOpenAPINodeGuess(using encoder: JSONEncoder) -> JSONNode? {
|
||||
return RawValue.dateOpenAPINodeGuess(using: encoder)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,14 +89,17 @@ extension TransformedAttribute: OpenAPINodeType where RawValue: OpenAPINodeType
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: conform TransformedAttribute to all of the above protocols that Attribute conforms to.
|
||||
|
||||
extension RelationshipType {
|
||||
static func relationshipNode(nullable: Bool) -> JSONNode {
|
||||
static func relationshipNode(nullable: Bool, jsonType: String) -> JSONNode {
|
||||
let propertiesDict: [String: JSONNode] = [
|
||||
"id": .string(.init(format: .generic,
|
||||
required: true),
|
||||
.init()),
|
||||
"type": .string(.init(format: .generic,
|
||||
required: true),
|
||||
required: true,
|
||||
allowedValues: [.init(jsonType)]),
|
||||
.init())
|
||||
]
|
||||
|
||||
@@ -90,20 +111,24 @@ extension RelationshipType {
|
||||
}
|
||||
|
||||
extension ToOneRelationship: OpenAPINodeType {
|
||||
// TODO: const for json `type`
|
||||
// NOTE: const for json `type` not supported by OpenAPI 3.0
|
||||
// Will use "enum" with one possible value for now.
|
||||
|
||||
// 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)
|
||||
"data": ToOneRelationship.relationshipNode(nullable: nullable, jsonType: Identifiable.jsonType)
|
||||
]))
|
||||
}
|
||||
}
|
||||
|
||||
extension ToManyRelationship: OpenAPINodeType {
|
||||
// TODO: const for json `type`
|
||||
// NOTE: const for json `type` not supported by OpenAPI 3.0
|
||||
// Will use "enum" with one possible value for now.
|
||||
|
||||
// TODO: metadata & links
|
||||
static public func openAPINode() throws -> JSONNode {
|
||||
return .object(.init(format: .generic,
|
||||
@@ -111,13 +136,16 @@ extension ToManyRelationship: OpenAPINodeType {
|
||||
.init(properties: [
|
||||
"data": .array(.init(format: .generic,
|
||||
required: true),
|
||||
.init(items: ToManyRelationship.relationshipNode(nullable: false)))
|
||||
.init(items: ToManyRelationship.relationshipNode(nullable: false, jsonType: Relatable.jsonType)))
|
||||
]))
|
||||
}
|
||||
}
|
||||
|
||||
extension Entity: OpenAPINodeType where Description.Attributes: Sampleable, Description.Relationships: Sampleable {
|
||||
public static func openAPINode() throws -> JSONNode {
|
||||
extension Entity: OpenAPIEncodedNodeType where Description.Attributes: Sampleable, Description.Relationships: Sampleable {
|
||||
public static func openAPINode(using encoder: JSONEncoder) throws -> JSONNode {
|
||||
// NOTE: const for json `type` not supported by OpenAPI 3.0
|
||||
// Will use "enum" with one possible value for now.
|
||||
|
||||
// TODO: metadata, links
|
||||
|
||||
let idNode = JSONNode.string(.init(format: .generic,
|
||||
@@ -126,19 +154,20 @@ extension Entity: OpenAPINodeType where Description.Attributes: Sampleable, Desc
|
||||
let idProperty = ("id", idNode)
|
||||
|
||||
let typeNode = JSONNode.string(.init(format: .generic,
|
||||
required: true),
|
||||
required: true,
|
||||
allowedValues: [.init(Entity.jsonType)]),
|
||||
.init())
|
||||
let typeProperty = ("type", typeNode)
|
||||
|
||||
let attributesNode: JSONNode? = Description.Attributes.self == NoAttributes.self
|
||||
? nil
|
||||
: try Description.Attributes.genericObjectOpenAPINode()
|
||||
: try Description.Attributes.genericOpenAPINode(using: encoder)
|
||||
|
||||
let attributesProperty = attributesNode.map { ("attributes", $0) }
|
||||
|
||||
let relationshipsNode: JSONNode? = Description.Relationships.self == NoRelationships.self
|
||||
? nil
|
||||
: try Description.Relationships.genericObjectOpenAPINode()
|
||||
: try Description.Relationships.genericOpenAPINode(using: encoder)
|
||||
|
||||
let relationshipsProperty = relationshipsNode.map { ("relationships", $0) }
|
||||
|
||||
@@ -147,7 +176,53 @@ extension Entity: OpenAPINodeType where Description.Attributes: Sampleable, Desc
|
||||
typeProperty,
|
||||
attributesProperty,
|
||||
relationshipsProperty
|
||||
].compactMap { $0 }) { _, value in value }
|
||||
].compactMap { $0 }) { _, value in value }
|
||||
|
||||
return .object(.init(format: .generic,
|
||||
required: true),
|
||||
.init(properties: propertiesDict))
|
||||
}
|
||||
}
|
||||
|
||||
extension SingleResourceBody: OpenAPIEncodedNodeType where Entity: OpenAPIEncodedNodeType {
|
||||
public static func openAPINode(using encoder: JSONEncoder) throws -> JSONNode {
|
||||
return try Entity.openAPINode(using: encoder)
|
||||
}
|
||||
}
|
||||
|
||||
extension ManyResourceBody: OpenAPIEncodedNodeType where Entity: OpenAPIEncodedNodeType {
|
||||
public static func openAPINode(using encoder: JSONEncoder) throws -> JSONNode {
|
||||
return .array(.init(format: .generic,
|
||||
required: true),
|
||||
.init(items: try Entity.openAPINode(using: encoder)))
|
||||
}
|
||||
}
|
||||
|
||||
extension Document: OpenAPIEncodedNodeType where PrimaryResourceBody: OpenAPIEncodedNodeType, IncludeType: OpenAPINodeType {
|
||||
public static func openAPINode(using encoder: JSONEncoder) throws -> JSONNode {
|
||||
// TODO: metadata, links, api description, errors
|
||||
// TODO: represent data and errors as the two distinct possible outcomes
|
||||
|
||||
let primaryDataNode: JSONNode? = try PrimaryResourceBody.openAPINode(using: encoder)
|
||||
|
||||
let primaryDataProperty = primaryDataNode.map { ("data", $0) }
|
||||
|
||||
let includeNode: JSONNode?
|
||||
do {
|
||||
includeNode = try Includes<Include>.openAPINode()
|
||||
} catch let err as OpenAPITypeError {
|
||||
guard case .invalidNode = err else {
|
||||
throw err
|
||||
}
|
||||
includeNode = nil
|
||||
}
|
||||
|
||||
let includeProperty = includeNode.map { ("included", $0) }
|
||||
|
||||
let propertiesDict = Dictionary([
|
||||
primaryDataProperty,
|
||||
includeProperty
|
||||
].compactMap { $0 }) { _, value in value }
|
||||
|
||||
return .object(.init(format: .generic,
|
||||
required: true),
|
||||
@@ -0,0 +1,40 @@
|
||||
//
|
||||
// Date+OpenAPI.swift
|
||||
// JSONAPIOpenAPI
|
||||
//
|
||||
// Created by Mathew Polzin on 1/24/19.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
extension Date: DateOpenAPINodeType {
|
||||
public static func dateOpenAPINodeGuess(using encoder: JSONEncoder) -> JSONNode? {
|
||||
|
||||
switch encoder.dateEncodingStrategy {
|
||||
case .deferredToDate, .custom:
|
||||
// I don't know if we can say anything about this case without
|
||||
// encoding the Date and looking at it, which is what `primitiveGuess()`
|
||||
// does.
|
||||
return nil
|
||||
|
||||
case .secondsSince1970,
|
||||
.millisecondsSince1970:
|
||||
return .number(.init(format: .double,
|
||||
required: true),
|
||||
.init())
|
||||
|
||||
case .iso8601:
|
||||
return .string(.init(format: .dateTime,
|
||||
required: true),
|
||||
.init())
|
||||
|
||||
case .formatted(let formatter):
|
||||
let hasTime = formatter.timeStyle != .none
|
||||
let format: JSONTypeFormat.StringFormat = hasTime ? .dateTime : .date
|
||||
|
||||
return .string(.init(format: format,
|
||||
required: true),
|
||||
.init())
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,361 @@
|
||||
//
|
||||
// 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
|
||||
case example
|
||||
// case constantValue = "const"
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
// if constantValue != nil {
|
||||
// try container.encode(constantValue, forKey: .constantValue)
|
||||
// }
|
||||
|
||||
try container.encode(nullable, forKey: .nullable)
|
||||
|
||||
if example != nil {
|
||||
try container.encode(example, forKey: .example)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
try container.encode(requiredProperties, forKey: .required)
|
||||
|
||||
try container.encode(minProperties, forKey: .minProperties)
|
||||
}
|
||||
}
|
||||
|
||||
extension JSONNode: Encodable {
|
||||
|
||||
private enum SubschemaCodingKeys: String, CodingKey {
|
||||
case allOf
|
||||
case oneOf
|
||||
case anyOf
|
||||
case not
|
||||
case reference = "$ref"
|
||||
}
|
||||
|
||||
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 .all(of: let nodes):
|
||||
var container = encoder.container(keyedBy: SubschemaCodingKeys.self)
|
||||
|
||||
try container.encode(nodes, forKey: .allOf)
|
||||
|
||||
case .one(of: let nodes):
|
||||
var container = encoder.container(keyedBy: SubschemaCodingKeys.self)
|
||||
|
||||
try container.encode(nodes, forKey: .oneOf)
|
||||
|
||||
case .any(of: let nodes):
|
||||
var container = encoder.container(keyedBy: SubschemaCodingKeys.self)
|
||||
|
||||
try container.encode(nodes, forKey: .anyOf)
|
||||
|
||||
case .not(let node):
|
||||
var container = encoder.container(keyedBy: SubschemaCodingKeys.self)
|
||||
|
||||
try container.encode(node, forKey: .not)
|
||||
|
||||
case .reference(let reference):
|
||||
var container = encoder.container(keyedBy: SubschemaCodingKeys.self)
|
||||
|
||||
try container.encode(reference, forKey: .reference)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension JSONReference: Encodable {
|
||||
public func encode(to encoder: Encoder) throws {
|
||||
var container = encoder.singleValueContainer()
|
||||
|
||||
let referenceString: String = {
|
||||
switch self {
|
||||
case .file(let reference):
|
||||
return reference
|
||||
case .node(let reference):
|
||||
return "#/\(Root.refName)/\(reference.refName)/\(reference.selector)"
|
||||
}
|
||||
}()
|
||||
|
||||
try container.encode(referenceString)
|
||||
}
|
||||
}
|
||||
|
||||
extension RefDict: Encodable {
|
||||
public func encode(to encoder: Encoder) throws {
|
||||
var container = encoder.singleValueContainer()
|
||||
|
||||
try container.encode(dict)
|
||||
}
|
||||
}
|
||||
|
||||
extension OpenAPIResponse.Code: Encodable {
|
||||
public func encode(to encoder: Encoder) throws {
|
||||
var container = encoder.singleValueContainer()
|
||||
|
||||
let string: String
|
||||
switch self {
|
||||
case .`default`:
|
||||
string = "default"
|
||||
|
||||
case .status(code: let code):
|
||||
string = String(code)
|
||||
}
|
||||
|
||||
try container.encode(string)
|
||||
}
|
||||
}
|
||||
|
||||
extension OpenAPIPathItem.PathProperties.Operation: Encodable {
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case tags
|
||||
case summary
|
||||
case description
|
||||
case externalDocs
|
||||
case operationId
|
||||
case parameters
|
||||
case requestBody
|
||||
case responses
|
||||
case callbacks
|
||||
case deprecated
|
||||
case security
|
||||
case servers
|
||||
}
|
||||
|
||||
public func encode(to encoder: Encoder) throws {
|
||||
var container = encoder.container(keyedBy: CodingKeys.self)
|
||||
|
||||
if tags != nil {
|
||||
try container.encode(tags, forKey: .tags)
|
||||
}
|
||||
|
||||
if summary != nil {
|
||||
try container.encode(summary, forKey: .summary)
|
||||
}
|
||||
|
||||
if description != nil {
|
||||
try container.encode(description, forKey: .description)
|
||||
}
|
||||
|
||||
try container.encode(operationId, forKey: .operationId)
|
||||
|
||||
try container.encode(parameters, forKey: .parameters)
|
||||
|
||||
try container.encode(responses, forKey: .responses)
|
||||
|
||||
try container.encode(deprecated, forKey: .deprecated)
|
||||
}
|
||||
}
|
||||
|
||||
extension OpenAPIPathItem.PathProperties: Encodable {
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case summary
|
||||
case description
|
||||
case servers
|
||||
case parameters
|
||||
|
||||
case get
|
||||
case put
|
||||
case post
|
||||
case delete
|
||||
case options
|
||||
case head
|
||||
case patch
|
||||
case trace
|
||||
}
|
||||
|
||||
public func encode(to encoder: Encoder) throws {
|
||||
var container = encoder.container(keyedBy: CodingKeys.self)
|
||||
|
||||
if summary != nil {
|
||||
try container.encode(summary, forKey: .summary)
|
||||
}
|
||||
|
||||
if description != nil {
|
||||
try container.encode(description, forKey: .description)
|
||||
}
|
||||
|
||||
try container.encode(parameters, forKey: .parameters)
|
||||
|
||||
if get != nil {
|
||||
try container.encode(get, forKey: .get)
|
||||
}
|
||||
|
||||
if put != nil {
|
||||
try container.encode(put, forKey: .put)
|
||||
}
|
||||
|
||||
if post != nil {
|
||||
try container.encode(post, forKey: .post)
|
||||
}
|
||||
|
||||
if delete != nil {
|
||||
try container.encode(delete, forKey: .delete)
|
||||
}
|
||||
|
||||
if options != nil {
|
||||
try container.encode(options, forKey: .options)
|
||||
}
|
||||
|
||||
if head != nil {
|
||||
try container.encode(head, forKey: .head)
|
||||
}
|
||||
|
||||
if patch != nil {
|
||||
try container.encode(patch, forKey: .patch)
|
||||
}
|
||||
|
||||
if trace != nil {
|
||||
try container.encode(trace, forKey: .trace)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension OpenAPIPathItem: Encodable {
|
||||
public func encode(to encoder: Encoder) throws {
|
||||
var container = encoder.singleValueContainer()
|
||||
|
||||
switch self {
|
||||
case .reference(let reference):
|
||||
try container.encode(reference)
|
||||
|
||||
case .operations(let operations):
|
||||
try container.encode(operations)
|
||||
}
|
||||
}
|
||||
}
|
||||
+431
-19
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