Compare commits

...

39 Commits

Author SHA1 Message Date
Mathew Polzin a88bb77bd0 update linuxmain 2019-01-30 18:00:51 -08:00
Mathew Polzin 247848e788 Update README 2019-01-30 18:00:27 -08:00
Mathew Polzin 7adbdb4505 Move JSONAPIArbitrary and JSONAPIOpenAPI into their own packages. 2019-01-30 17:54:26 -08:00
Mathew Polzin ea663bb229 Move Sampleable into its own Package. 2019-01-29 00:44:31 -08:00
Mathew Polzin f48c385f99 super minor code doc tweak 2019-01-28 23:51:29 -08:00
Mathew Polzin c96bfadd82 Had not updated linuxmain in a while. pretty sure I'll have problems building on Linux at least with the stuff going on in JSONAPI+OpenAPI. 2019-01-28 23:46:56 -08:00
Mathew Polzin 88be701062 Add OpenAPI support for TransformedAttributes 2019-01-28 23:45:28 -08:00
Mathew Polzin 1e85e1d2a3 fix open API playground. 2019-01-27 22:55:32 -08:00
Mathew Polzin 11a7727ac9 Fix Include OpenAPI support. Fix bug with single include being considered 'OneOf' even though that is an overcomplication of the schema. Add tests for single include type and two include types on document 2019-01-27 22:47:01 -08:00
Mathew Polzin e8bfbc881b Added a couple of test cases for JSONDocuments 2019-01-27 22:24:13 -08:00
Mathew Polzin af757a2fac Add emphasis to README line. 2019-01-27 21:46:22 -08:00
Mathew Polzin 84955872f8 Finish writing Date Attribute test cases and add support for Optional Date OpenAPI Node guesses. 2019-01-27 21:39:18 -08:00
Mathew Polzin cb2800abd4 Add support for RequestBody on an OpenAPI Operation. 2019-01-27 13:57:04 -08:00
Mathew Polzin 85d5fef3c8 Fix bug where some references were correctly encoded as objects and others were just encoded as strings 2019-01-27 13:22:18 -08:00
Mathew Polzin e4ef61fd56 bugfix: OpenAPI path components should begin with a slash. 2019-01-27 12:46:13 -08:00
Mathew Polzin 75ec4f156e Fix a third dictionary-as-array bug 2019-01-26 20:04:11 -08:00
Mathew Polzin b45fc73188 Merge pull request #15 from mattpolzin/feature/OpenAPISchema
Feature/open api schema
2019-01-26 19:52:15 -08:00
Mathew Polzin 3b73dc9989 Fix super shitty bug caused by Apples implementation of Dictionary's conformance to Encodable sometimes encoding the dictionary as an array. 2019-01-26 19:50:20 -08:00
Mathew Polzin b2c81026f4 remove 'id' from Unidentified Entity OpenAPI node. 2019-01-26 18:55:47 -08:00
Mathew Polzin 8d057b4398 Merge pull request #14 from mattpolzin/feature/OpenAPISchema
Just enough OpenAPI Schema stuff to be dangerous
2019-01-25 18:25:19 -08:00
Mathew Polzin c8421cdd58 just throw that json extension on the filename to make everything super real 2019-01-25 18:20:04 -08:00
Mathew Polzin cde10a8491 Finish implementing remaining first wave of encodable conformances for OpenAPI 2019-01-25 18:17:58 -08:00
Mathew Polzin ad05d3908a Add a simple test OpenAPISchema and start to tweak things to get it workable. 2019-01-25 12:49:59 -08:00
Mathew Polzin 23b2b2e04f merge w/ master 2019-01-25 12:01:01 -08:00
Mathew Polzin 2988503d7d Add Sampleable conformance to Unidentified. Rename file slightly. 2019-01-25 11:59:05 -08:00
Mathew Polzin 5ea83b07c1 Hopefully remove some ambiguity. 2019-01-24 19:09:46 -08:00
Mathew Polzin 2b59f54067 Fix bug causing a supplied encoder to be used for generating an example but not for helping determine the correct Date formatting. 2019-01-24 17:47:44 -08:00
Mathew Polzin 58a7c82436 Restructure files a bit. Make Date handling relatively robust compared to my first pass at it. Make the failure to construct a generic open API node type throw an error rather than silently omit the node. 2019-01-24 17:25:34 -08:00
Mathew Polzin dc30cb3b9e Make Attribute Sampleable where its RawValue is Sampleable. Allow Sampleable things to provide a best guess for their node type based on the result of encoding and then deserializing them. 2019-01-24 00:47:24 -08:00
Mathew Polzin 7045373708 update and make sure all Playground pages run 2019-01-23 23:24:53 -08:00
Mathew Polzin 952fe8ba7e Remove encoder requirement to almost all Open API Node constructors. Made a new protocol for the few places where an encoder did need to be passed in. 2019-01-23 23:21:16 -08:00
Mathew Polzin 951c04ad44 Add Sampleable conformances. Make blanket JSONEncoder change I am not happy with; will try to walk back requirement that encoder gets passed to all functions creating OpenAPI Nodes 2019-01-23 22:21:27 -08:00
Mathew Polzin 57df6b147e Add Sampleable conformance to UnknownJSONAPIError. Add Sampleable conformance to all Include types and the Includes type. 2019-01-23 11:52:16 -08:00
Mathew Polzin a88844fe5e Switch to Poly 1.0.0 (same commit as previously used but by version rather than branch) 2019-01-23 00:34:14 -08:00
Mathew Polzin d1cf19f9fe Neck deep in stubbing out OpenAPI types 2019-01-22 22:14:36 -08:00
Mathew Polzin 59835fbe11 Add JSON Reference and OpenAPIComponents (initially just with schemas) 2019-01-22 16:08:47 -08:00
Mathew Polzin 850a713dcb Merge pull request #13 from mattpolzin/feature/OpenAPISchema
Add Include support to OpenAPI schema of JSONAPI Document.
2019-01-22 12:01:40 -08:00
Mathew Polzin e3c637a41e update README and add associated type labels to patterns for clarity 2019-01-22 11:57:18 -08:00
Mathew Polzin 9cbc626410 Add Include support to OpenAPI schema of JSONAPI Document. 2019-01-22 11:53:32 -08:00
31 changed files with 51 additions and 3038 deletions
@@ -1,5 +1,6 @@
import Foundation
import JSONAPI
import Poly
// MARK: - Preamble (setup)
@@ -3,21 +3,44 @@
import Foundation
import JSONAPI
import JSONAPIOpenAPI
import Poly
// print Entity Schema
let encoder = JSONEncoder()
encoder.outputFormatting = .prettyPrinted
let personSchemaData = try? encoder.encode(Person.openAPINode())
let personSchemaData = try? encoder.encode(Person.openAPINode(using: encoder))
print("Person Schema")
print("====")
print(personSchemaData.map { String(data: $0, encoding: .utf8)! } ?? "Schema Construction Failed")
print("====")
let dogDocumentSchemaData = try? encoder.encode(SingleDogDocument.openAPINodeWithExample())
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: JSONNode] = [
"BatchPerson": try! BatchPeopleDocument.openAPINodeWithExample(using: encoder)
]
let components = OpenAPIComponents(schemas: tmp, parameters: [:])
let batchPeopleRef = JSONReference.node(.init(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)!)
@@ -5,13 +5,28 @@ import JSONAPIOpenAPI
import SwiftCheck
import JSONAPIArbitrary
extension PersonDescription.Attributes: Sampleable {
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())
}
}
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")
}
+2 -29
View File
@@ -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
View File
@@ -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]
)
+3 -7
View File
@@ -104,7 +104,7 @@ Note that Playground support for importing non-system Frameworks is still a bit
- `included`
- [x] Encoding/Decoding
- [x] Arbitrary
- [ ] OpenAPI
- [x] OpenAPI
- `errors`
- [x] Encoding/Decoding
- [x] Arbitrary
@@ -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,15 +0,0 @@
//
// 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)
}
}
@@ -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,198 +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, jsonType: String) -> JSONNode {
let propertiesDict: [String: JSONNode] = [
"id": .string(.init(format: .generic,
required: true),
.init()),
"type": .string(.init(format: .generic,
required: true,
allowedValues: [.init(jsonType)]),
.init())
]
return .object(.init(format: .generic,
required: true,
nullable: nullable),
.init(properties: propertiesDict))
}
}
extension ToOneRelationship: OpenAPINodeType {
// 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, jsonType: Identifiable.jsonType)
]))
}
}
extension ToManyRelationship: OpenAPINodeType {
// 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,
required: true),
.init(properties: [
"data": .array(.init(format: .generic,
required: true),
.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 {
// 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,
required: true),
.init())
let idProperty = ("id", idNode)
let typeNode = JSONNode.string(.init(format: .generic,
required: true,
allowedValues: [.init(Entity.jsonType)]),
.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))
}
}
extension SingleResourceBody: OpenAPINodeType where Entity: OpenAPINodeType {
public static func openAPINode() throws -> JSONNode {
return try Entity.openAPINode()
}
}
extension ManyResourceBody: OpenAPINodeType where Entity: OpenAPINodeType {
public static func openAPINode() throws -> JSONNode {
return .array(.init(format: .generic,
required: true),
.init(items: try Entity.openAPINode()))
}
}
extension Document: OpenAPINodeType where PrimaryResourceBody: OpenAPINodeType {
public static func openAPINode() throws -> JSONNode {
// TODO: metadata, links, api description, includes, errors
// TODO: represent data and errors as the two distinct possible outcomes
let primaryDataNode: JSONNode? = try PrimaryResourceBody.openAPINode()
let primaryDataProperty = primaryDataNode.map { ("data", $0) }
let propertiesDict = Dictionary([
primaryDataProperty
].compactMap { $0 }) { _, value in value }
return .object(.init(format: .generic,
required: true),
.init(properties: propertiesDict))
}
}
@@ -1,182 +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
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 {
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
@@ -1,13 +0,0 @@
//
// Optional+ZipWith.swift
// JSONAPIOpenAPI
//
// Created by Mathew Polzin on 1/19/19.
//
/// Zip two optionals together with the given operation performed on
/// the unwrapped contents. If either optional is nil, the zip
/// yields nil.
func zip<X, Y, Z>(_ left: X?, _ right: Y?, with fn: (X, Y) -> Z) -> Z? {
return left.flatMap { lft in right.map { rght in fn(lft, rght) }}
}
-116
View File
@@ -1,116 +0,0 @@
//
// Sampleable.swift
// JSONAPIOpenAPI
//
// Created by Mathew Polzin on 1/15/19.
//
import JSONAPI
import AnyCodable
/// A Sampleable type can provide a sample value.
/// This is useful for reflection.
public protocol Sampleable {
/// Get a sample value of type Self. This can be the
/// same value every time, or it can be an arbitrarily random
/// value each time.
static var sample: Self { get }
/// Get an example of success, if that is meaningful and
/// available. If not, will be nil.
static var successSample: Self? { get }
/// Get an example of failure, if that is meaningful and
/// available. If not, will be nil.
static var failureSample: Self? { get }
}
public extension Sampleable {
public static var successSample: Self? { return nil }
public static var failureSample: Self? { return nil }
}
extension Sampleable {
public static func genericObjectOpenAPINode() throws -> JSONNode {
let mirror = Mirror(reflecting: Self.sample)
let properties: [(String, JSONNode)] = try mirror.children.compactMap { child in
// see if we can enumerate the possible values
let maybeAllCases: [AnyCodable]? = {
switch type(of: child.value) {
case let valType as AnyJSONCaseIterable.Type:
return valType.allCases
case let valType as AnyWrappedJSONCaseIterable.Type:
return valType.allCases
default:
return nil
}
}()
// try to snag an OpenAPI Node
let maybeOpenAPINode: JSONNode? = try {
switch type(of: child.value) {
case let valType as OpenAPINodeType.Type:
return try valType.openAPINode()
case let valType as RawOpenAPINodeType.Type:
return try valType.rawOpenAPINode()
case let valType as WrappedRawOpenAPIType.Type:
return try valType.wrappedOpenAPINode()
case let valType as DoubleWrappedRawOpenAPIType.Type:
return try valType.wrappedOpenAPINode()
default:
return nil
}
}()
// put it all together
let newNode: JSONNode?
if let allCases = maybeAllCases,
let openAPINode = maybeOpenAPINode {
newNode = try openAPINode.with(allowedValues: allCases)
} else {
newNode = maybeOpenAPINode
}
return zip(child.label, newNode) { ($0, $1) }
}
// There should not be any duplication of keys since these are
// property names, but rather than risk runtime exception, we just
// fail to the newer value arbitrarily
let propertiesDict = Dictionary(properties) { _, value2 in value2 }
return .object(.init(format: .generic,
required: true),
.init(properties: propertiesDict))
}
}
extension NoAttributes: Sampleable {
public static var sample: NoAttributes {
return .none
}
}
extension NoRelationships: Sampleable {
public static var sample: NoRelationships {
return .none
}
}
extension NoMetadata: Sampleable {
public static var sample: NoMetadata {
return .none
}
}
extension NoLinks: Sampleable {
public static var sample: NoLinks {
return .none
}
}

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