Compare commits

..

4 Commits

Author SHA1 Message Date
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 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
10 changed files with 213 additions and 31 deletions
@@ -21,3 +21,10 @@ print("Dog Document Schema")
print("====")
print(dogDocumentSchemaData.map { String(data: $0, encoding: .utf8)! } ?? "Schema Construction Failed")
print("====")
let batchPersonSchemaData = try? encoder.encode(BatchPeopleDocument.openAPINodeWithExample())
print("Batch Person Document Schema")
print("====")
print(batchPersonSchemaData.map { String(data: $0, encoding: .utf8)! } ?? "Schema Construction Failed")
print("====")
@@ -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 -2
View File
@@ -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
View File
@@ -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")
],
+1 -1
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
@@ -0,0 +1,131 @@
//
// JSONAPIInclude+OpenAPI.swift
// JSONAPIOpenAPI
//
// Created by Mathew Polzin on 1/22/19.
//
import JSONAPI
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()
])
}
}
@@ -178,17 +178,30 @@ extension ManyResourceBody: OpenAPINodeType where Entity: OpenAPINodeType {
}
}
extension Document: OpenAPINodeType where PrimaryResourceBody: OpenAPINodeType {
extension Document: OpenAPINodeType where PrimaryResourceBody: OpenAPINodeType, IncludeType: OpenAPINodeType {
public static func openAPINode() throws -> JSONNode {
// TODO: metadata, links, api description, includes, errors
// TODO: metadata, links, api description, 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 includeNode: JSONNode?
do {
includeNode = try Includes<Include>.openAPINode()
} catch let err as OpenAPITypeError {
guard err == .invalidNode else {
throw err
}
includeNode = nil
}
let includeProperty = includeNode.map { ("included", $0) }
let propertiesDict = Dictionary([
primaryDataProperty
primaryDataProperty,
includeProperty
].compactMap { $0 }) { _, value in value }
return .object(.init(format: .generic,
@@ -149,6 +149,14 @@ extension JSONNode.ObjectContext : Encodable {
}
extension JSONNode: Encodable {
private enum SubschemaCodingKeys: String, CodingKey {
case allOf
case oneOf
case anyOf
case not
}
public func encode(to encoder: Encoder) throws {
switch self {
case .boolean(let context):
@@ -162,21 +170,25 @@ extension JSONNode: Encodable {
try contextA.encode(to: encoder)
try contextB.encode(to: encoder)
case .allOf(let nodes):
// TODO
print("TODO")
case .all(of: let nodes):
var container = encoder.container(keyedBy: SubschemaCodingKeys.self)
case .oneOf(let nodes):
// TODO
print("TODO")
try container.encode(nodes, forKey: .allOf)
case .anyOf(let nodes):
// TODO
print("TODO")
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):
// TODO
print("TODO")
var container = encoder.container(keyedBy: SubschemaCodingKeys.self)
try container.encode(node, forKey: .not)
}
}
}
@@ -245,9 +245,9 @@ public enum JSONNode: Equatable {
case number(Context<JSONTypeFormat.NumberFormat>, NumericContext)
case integer(Context<JSONTypeFormat.IntegerFormat>, NumericContext)
case string(Context<JSONTypeFormat.StringFormat>, StringContext)
indirect case allOf([JSONNode])
indirect case oneOf([JSONNode])
indirect case anyOf([JSONNode])
indirect case all(of: [JSONNode])
indirect case one(of: [JSONNode])
indirect case any(of: [JSONNode])
indirect case not(JSONNode)
public struct Context<Format: OpenAPIFormat>: JSONNodeContext, Equatable {
@@ -451,7 +451,7 @@ public enum JSONNode: Equatable {
return .integer(context.format)
case .string(let context, _):
return .string(context.format)
case .allOf, .oneOf, .anyOf, .not:
case .all, .one, .any, .not:
return nil
}
}
@@ -465,7 +465,7 @@ public enum JSONNode: Equatable {
.integer(let contextA as JSONNodeContext, _),
.string(let contextA as JSONNodeContext, _):
return contextA.required
case .allOf, .oneOf, .anyOf, .not:
case .all, .one, .any, .not:
return true
}
}
@@ -485,7 +485,7 @@ public enum JSONNode: Equatable {
return .integer(context.optionalContext(), contextB)
case .string(let context, let contextB):
return .string(context.optionalContext(), contextB)
case .allOf, .oneOf, .anyOf, .not:
case .all, .one, .any, .not:
return self
}
}
@@ -505,7 +505,7 @@ public enum JSONNode: Equatable {
return .integer(context.requiredContext(), contextB)
case .string(let context, let contextB):
return .string(context.requiredContext(), contextB)
case .allOf, .oneOf, .anyOf, .not:
case .all, .one, .any, .not:
return self
}
}
@@ -525,7 +525,7 @@ public enum JSONNode: Equatable {
return .integer(context.nullableContext(), contextB)
case .string(let context, let contextB):
return .string(context.nullableContext(), contextB)
case .allOf, .oneOf, .anyOf, .not:
case .all, .one, .any, .not:
return self
}
}
@@ -545,7 +545,7 @@ public enum JSONNode: Equatable {
return .integer(context.with(allowedValues: allowedValues), contextB)
case .string(let context, let contextB):
return .string(context.with(allowedValues: allowedValues), contextB)
case .allOf, .oneOf, .anyOf, .not:
case .all, .one, .any, .not:
return self
}
}
@@ -571,13 +571,17 @@ public enum JSONNode: Equatable {
return .integer(context.with(example: example), contextB)
case .string(let context, let contextB):
return .string(context.with(example: example), contextB)
case .allOf, .oneOf, .anyOf, .not:
case .all, .one, .any, .not:
return self
}
}
}
public enum OpenAPICodableError: Swift.Error {
public enum OpenAPICodableError: Swift.Error, Equatable {
case allCasesArrayNotCodable
case exampleNotCodable
}
public enum OpenAPITypeError: Swift.Error, Equatable {
case invalidNode
}