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

This commit is contained in:
Mathew Polzin
2019-01-23 22:21:27 -08:00
parent 57df6b147e
commit 951c04ad44
6 changed files with 211 additions and 132 deletions
@@ -6,10 +6,11 @@
//
import JSONAPI
import Foundation
extension Includes: OpenAPINodeType where I: OpenAPINodeType {
public static func openAPINode() throws -> JSONNode {
let includeNode = try I.openAPINode()
public static func openAPINode(using encoder: JSONEncoder) throws -> JSONNode {
let includeNode = try I.openAPINode(using: encoder)
return .array(.init(format: .generic,
required: true),
@@ -19,113 +20,113 @@ extension Includes: OpenAPINodeType where I: OpenAPINodeType {
}
extension Include0: OpenAPINodeType {
public static func openAPINode() throws -> JSONNode {
public static func openAPINode(using encoder: JSONEncoder) throws -> JSONNode {
throw OpenAPITypeError.invalidNode
}
}
extension Include1: OpenAPINodeType where A: OpenAPINodeType {
public static func openAPINode() throws -> JSONNode {
return try .one(of: [A.openAPINode()])
public static func openAPINode(using encoder: JSONEncoder) throws -> JSONNode {
return try .one(of: [A.openAPINode(using: encoder)])
}
}
extension Include2: OpenAPINodeType where A: OpenAPINodeType, B: OpenAPINodeType {
public static func openAPINode() throws -> JSONNode {
public static func openAPINode(using encoder: JSONEncoder) throws -> JSONNode {
return try .one(of: [
A.openAPINode(),
B.openAPINode()
A.openAPINode(using: encoder),
B.openAPINode(using: encoder)
])
}
}
extension Include3: OpenAPINodeType where A: OpenAPINodeType, B: OpenAPINodeType, C: OpenAPINodeType {
public static func openAPINode() throws -> JSONNode {
public static func openAPINode(using encoder: JSONEncoder) throws -> JSONNode {
return try .one(of: [
A.openAPINode(),
B.openAPINode(),
C.openAPINode()
A.openAPINode(using: encoder),
B.openAPINode(using: encoder),
C.openAPINode(using: encoder)
])
}
}
extension Include4: OpenAPINodeType where A: OpenAPINodeType, B: OpenAPINodeType, C: OpenAPINodeType, D: OpenAPINodeType {
public static func openAPINode() throws -> JSONNode {
public static func openAPINode(using encoder: JSONEncoder) throws -> JSONNode {
return try .one(of: [
A.openAPINode(),
B.openAPINode(),
C.openAPINode(),
D.openAPINode()
A.openAPINode(using: encoder),
B.openAPINode(using: encoder),
C.openAPINode(using: encoder),
D.openAPINode(using: encoder)
])
}
}
extension Include5: OpenAPINodeType where A: OpenAPINodeType, B: OpenAPINodeType, C: OpenAPINodeType, D: OpenAPINodeType, E: OpenAPINodeType {
public static func openAPINode() throws -> JSONNode {
public static func openAPINode(using encoder: JSONEncoder) throws -> JSONNode {
return try .one(of: [
A.openAPINode(),
B.openAPINode(),
C.openAPINode(),
D.openAPINode(),
E.openAPINode()
A.openAPINode(using: encoder),
B.openAPINode(using: encoder),
C.openAPINode(using: encoder),
D.openAPINode(using: encoder),
E.openAPINode(using: encoder)
])
}
}
extension Include6: OpenAPINodeType where A: OpenAPINodeType, B: OpenAPINodeType, C: OpenAPINodeType, D: OpenAPINodeType, E: OpenAPINodeType, F: OpenAPINodeType {
public static func openAPINode() throws -> JSONNode {
public static func openAPINode(using encoder: JSONEncoder) throws -> JSONNode {
return try .one(of: [
A.openAPINode(),
B.openAPINode(),
C.openAPINode(),
D.openAPINode(),
E.openAPINode(),
F.openAPINode()
A.openAPINode(using: encoder),
B.openAPINode(using: encoder),
C.openAPINode(using: encoder),
D.openAPINode(using: encoder),
E.openAPINode(using: encoder),
F.openAPINode(using: encoder)
])
}
}
extension Include7: OpenAPINodeType where A: OpenAPINodeType, B: OpenAPINodeType, C: OpenAPINodeType, D: OpenAPINodeType, E: OpenAPINodeType, F: OpenAPINodeType, G: OpenAPINodeType {
public static func openAPINode() throws -> JSONNode {
public static func openAPINode(using encoder: JSONEncoder) throws -> JSONNode {
return try .one(of: [
A.openAPINode(),
B.openAPINode(),
C.openAPINode(),
D.openAPINode(),
E.openAPINode(),
F.openAPINode(),
G.openAPINode()
A.openAPINode(using: encoder),
B.openAPINode(using: encoder),
C.openAPINode(using: encoder),
D.openAPINode(using: encoder),
E.openAPINode(using: encoder),
F.openAPINode(using: encoder),
G.openAPINode(using: encoder)
])
}
}
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 {
public static func openAPINode(using encoder: JSONEncoder) throws -> JSONNode {
return try .one(of: [
A.openAPINode(),
B.openAPINode(),
C.openAPINode(),
D.openAPINode(),
E.openAPINode(),
F.openAPINode(),
G.openAPINode(),
H.openAPINode()
A.openAPINode(using: encoder),
B.openAPINode(using: encoder),
C.openAPINode(using: encoder),
D.openAPINode(using: encoder),
E.openAPINode(using: encoder),
F.openAPINode(using: encoder),
G.openAPINode(using: encoder),
H.openAPINode(using: encoder)
])
}
}
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 {
public static func openAPINode(using encoder: JSONEncoder) throws -> JSONNode {
return try .one(of: [
A.openAPINode(),
B.openAPINode(),
C.openAPINode(),
D.openAPINode(),
E.openAPINode(),
F.openAPINode(),
G.openAPINode(),
H.openAPINode(),
I.openAPINode()
A.openAPINode(using: encoder),
B.openAPINode(using: encoder),
C.openAPINode(using: encoder),
D.openAPINode(using: encoder),
E.openAPINode(using: encoder),
F.openAPINode(using: encoder),
G.openAPINode(using: encoder),
H.openAPINode(using: encoder),
I.openAPINode(using: encoder)
])
}
}
@@ -6,68 +6,69 @@
//
import JSONAPI
import Foundation
import AnyCodable
private protocol _Optional {}
extension Optional: _Optional {}
extension Attribute: OpenAPINodeType where RawValue: OpenAPINodeType {
static public func openAPINode() throws -> JSONNode {
static public func openAPINode(using encoder: JSONEncoder) 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()
if try !RawValue.openAPINode(using: encoder).required {
return try RawValue.openAPINode(using: encoder).requiredNode().nullableNode()
}
return try RawValue.openAPINode()
return try RawValue.openAPINode(using: encoder)
}
}
extension Attribute: RawOpenAPINodeType where RawValue: RawRepresentable, RawValue.RawValue: OpenAPINodeType {
static public func rawOpenAPINode() throws -> JSONNode {
static public func rawOpenAPINode(using encoder: JSONEncoder) 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()
if try !RawValue.RawValue.openAPINode(using: encoder).required {
return try RawValue.RawValue.openAPINode(using: encoder).requiredNode().nullableNode()
}
return try RawValue.RawValue.openAPINode()
return try RawValue.RawValue.openAPINode(using: encoder)
}
}
extension Attribute: WrappedRawOpenAPIType where RawValue: RawOpenAPINodeType {
public static func wrappedOpenAPINode() throws -> JSONNode {
public static func wrappedOpenAPINode(using encoder: JSONEncoder) 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()
if try !RawValue.rawOpenAPINode(using: encoder).required {
return try RawValue.rawOpenAPINode(using: encoder).requiredNode().nullableNode()
}
return try RawValue.rawOpenAPINode()
return try RawValue.rawOpenAPINode(using: encoder)
}
}
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 TransformedAttribute: OpenAPINodeType where RawValue: OpenAPINodeType {
static public func openAPINode() throws -> JSONNode {
static public func openAPINode(using encoder: JSONEncoder) 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()
if try !RawValue.openAPINode(using: encoder).required {
return try RawValue.openAPINode(using: encoder).requiredNode().nullableNode()
}
return try RawValue.openAPINode()
return try RawValue.openAPINode(using: encoder)
}
}
@@ -95,7 +96,7 @@ extension ToOneRelationship: OpenAPINodeType {
// Will use "enum" with one possible value for now.
// TODO: metadata & links
static public func openAPINode() throws -> JSONNode {
static public func openAPINode(using encoder: JSONEncoder) throws -> JSONNode {
let nullable = Identifiable.self is _Optional.Type
return .object(.init(format: .generic,
required: true),
@@ -110,7 +111,7 @@ extension ToManyRelationship: OpenAPINodeType {
// Will use "enum" with one possible value for now.
// TODO: metadata & links
static public func openAPINode() throws -> JSONNode {
static public func openAPINode(using encoder: JSONEncoder) throws -> JSONNode {
return .object(.init(format: .generic,
required: true),
.init(properties: [
@@ -122,7 +123,7 @@ extension ToManyRelationship: OpenAPINodeType {
}
extension Entity: OpenAPINodeType where Description.Attributes: Sampleable, Description.Relationships: Sampleable {
public static func openAPINode() throws -> JSONNode {
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.
@@ -141,13 +142,13 @@ extension Entity: OpenAPINodeType where Description.Attributes: Sampleable, Desc
let attributesNode: JSONNode? = Description.Attributes.self == NoAttributes.self
? nil
: try Description.Attributes.genericObjectOpenAPINode()
: try Description.Attributes.genericObjectOpenAPINode(using: encoder)
let attributesProperty = attributesNode.map { ("attributes", $0) }
let relationshipsNode: JSONNode? = Description.Relationships.self == NoRelationships.self
? nil
: try Description.Relationships.genericObjectOpenAPINode()
: try Description.Relationships.genericObjectOpenAPINode(using: encoder)
let relationshipsProperty = relationshipsNode.map { ("relationships", $0) }
@@ -165,31 +166,31 @@ extension Entity: OpenAPINodeType where Description.Attributes: Sampleable, Desc
}
extension SingleResourceBody: OpenAPINodeType where Entity: OpenAPINodeType {
public static func openAPINode() throws -> JSONNode {
return try Entity.openAPINode()
public static func openAPINode(using encoder: JSONEncoder) throws -> JSONNode {
return try Entity.openAPINode(using: encoder)
}
}
extension ManyResourceBody: OpenAPINodeType where Entity: OpenAPINodeType {
public static func openAPINode() throws -> JSONNode {
public static func openAPINode(using encoder: JSONEncoder) throws -> JSONNode {
return .array(.init(format: .generic,
required: true),
.init(items: try Entity.openAPINode()))
.init(items: try Entity.openAPINode(using: encoder)))
}
}
extension Document: OpenAPINodeType where PrimaryResourceBody: OpenAPINodeType, IncludeType: OpenAPINodeType {
public static func openAPINode() throws -> JSONNode {
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()
let primaryDataNode: JSONNode? = try PrimaryResourceBody.openAPINode(using: encoder)
let primaryDataProperty = primaryDataNode.map { ("data", $0) }
let includeNode: JSONNode?
do {
includeNode = try Includes<Include>.openAPINode()
includeNode = try Includes<Include>.openAPINode(using: encoder)
} catch let err as OpenAPITypeError {
guard err == .invalidNode else {
throw err
@@ -13,12 +13,12 @@ import Foundation
/// Anything conforming to `OpenAPINodeType` can provide an
/// OpenAPI schema representing itself.
public protocol OpenAPINodeType {
static func openAPINode() throws -> JSONNode
static func openAPINode(using encoder: JSONEncoder) throws -> JSONNode
}
extension OpenAPINodeType where Self: Sampleable, Self: Encodable {
public static func openAPINodeWithExample() throws -> JSONNode {
return try openAPINode().with(example: Self.successSample ?? Self.sample)
public static func openAPINodeWithExample(using encoder: JSONEncoder = JSONEncoder()) throws -> JSONNode {
return try openAPINode(using: encoder).with(example: Self.successSample ?? Self.sample, using: encoder)
}
}
@@ -29,7 +29,7 @@ extension OpenAPINodeType where Self: Sampleable, Self: Encodable {
/// different schema. The "different" conditions have to do
/// with Raw Representability, hence the name of this protocol.
public protocol RawOpenAPINodeType {
static func rawOpenAPINode() throws -> JSONNode
static func rawOpenAPINode(using encoder: JSONEncoder) throws -> JSONNode
}
/// Anything conforming to `RawOpenAPINodeType` can provide an
@@ -39,7 +39,7 @@ public protocol RawOpenAPINodeType {
/// different schema. The "different" conditions have to do
/// with Optionality, hence the name of this protocol.
public protocol WrappedRawOpenAPIType {
static func wrappedOpenAPINode() throws -> JSONNode
static func wrappedOpenAPINode(using encoder: JSONEncoder) throws -> JSONNode
}
/// Anything conforming to `RawOpenAPINodeType` can provide an
@@ -52,18 +52,18 @@ public protocol DoubleWrappedRawOpenAPIType {
// NOTE: This is definitely a rabbit hole... hopefully I
// will realize I've been missing something obvious
// and dig my way back out at some point...
static func wrappedOpenAPINode() throws -> JSONNode
static func wrappedOpenAPINode(using encoder: JSONEncoder) throws -> JSONNode
}
/// Anything conforming to `AnyJSONCaseIterable` can provide a
/// list of its possible values.
public protocol AnyJSONCaseIterable {
static var allCases: [AnyCodable] { get }
static func allCases(using encoder: JSONEncoder) -> [AnyCodable]
}
extension AnyJSONCaseIterable {
/// Given an array of Codable values, retrieve an array of AnyCodables.
static func allCases<T: Codable>(from input: [T]) throws -> [AnyCodable] {
static func allCases<T: Codable>(from input: [T], using encoder: JSONEncoder) throws -> [AnyCodable] {
if let alreadyGoodToGo = input as? [AnyCodable] {
return alreadyGoodToGo
}
@@ -76,7 +76,7 @@ extension AnyJSONCaseIterable {
// by AnyCodable, but AnyCodable wants it to actually BE a String
// upon initialization.
guard let arrayOfCodables = try JSONSerialization.jsonObject(with: JSONEncoder().encode(input), options: []) as? [Any] else {
guard let arrayOfCodables = try JSONSerialization.jsonObject(with: encoder.encode(input), options: []) as? [Any] else {
throw OpenAPICodableError.allCasesArrayNotCodable
}
return arrayOfCodables.map(AnyCodable.init)
@@ -91,7 +91,7 @@ extension AnyJSONCaseIterable {
/// The "different" conditions have to do
/// with Optionality, hence the name of this protocol.
public protocol AnyWrappedJSONCaseIterable {
static var allCases: [AnyCodable] { get }
static func allCases(using encoder: JSONEncoder) -> [AnyCodable]
}
public protocol SwiftTyped {
@@ -282,14 +282,15 @@ public enum JSONNode: Equatable {
nullable: Bool = false,
// constantValue: Format.SwiftType? = nil,
allowedValues: [AnyCodable]? = nil,
example: AnyCodable? = nil) {
example: AnyCodable? = nil,
using encoder: JSONEncoder = JSONEncoder()) {
self.format = format
self.required = required
self.nullable = nullable
// self.constantValue = constantValue
self.allowedValues = allowedValues
self.example = example
.flatMap { try? JSONEncoder().encode($0)}
.flatMap { try? encoder.encode($0)}
.flatMap { String(data: $0, encoding: .utf8) }
}
@@ -550,12 +551,13 @@ public enum JSONNode: Equatable {
}
}
public func with<T: Encodable>(example codableExample: T) throws -> JSONNode {
public func with<T: Encodable>(example codableExample: T,
using encoder: JSONEncoder) throws -> JSONNode {
let example: AnyCodable
if let goodToGo = codableExample as? AnyCodable {
example = goodToGo
} else {
example = AnyCodable(try JSONSerialization.jsonObject(with: JSONEncoder().encode(codableExample), options: []))
example = AnyCodable(try JSONSerialization.jsonObject(with: encoder.encode(codableExample), options: []))
}
switch self {
@@ -6,6 +6,7 @@
//
import AnyCodable
import Foundation
/**
@@ -32,37 +33,37 @@ Any object:
**/
extension Optional: OpenAPINodeType where Wrapped: OpenAPINodeType {
static public func openAPINode() throws -> JSONNode {
return try Wrapped.openAPINode().optionalNode()
static public func openAPINode(using encoder: JSONEncoder) throws -> JSONNode {
return try Wrapped.openAPINode(using: encoder).optionalNode()
}
}
extension Optional: RawOpenAPINodeType where Wrapped: RawRepresentable, Wrapped.RawValue: OpenAPINodeType {
static public func rawOpenAPINode() throws -> JSONNode {
return try Wrapped.RawValue.openAPINode().optionalNode()
static public func rawOpenAPINode(using encoder: JSONEncoder) throws -> JSONNode {
return try Wrapped.RawValue.openAPINode(using: encoder).optionalNode()
}
}
extension Optional: WrappedRawOpenAPIType where Wrapped: RawOpenAPINodeType {
static public func wrappedOpenAPINode() throws -> JSONNode {
return try Wrapped.rawOpenAPINode().optionalNode()
static public func wrappedOpenAPINode(using encoder: JSONEncoder) throws -> JSONNode {
return try Wrapped.rawOpenAPINode(using: encoder).optionalNode()
}
}
extension Optional: DoubleWrappedRawOpenAPIType where Wrapped: WrappedRawOpenAPIType {
static public func wrappedOpenAPINode() throws -> JSONNode {
return try Wrapped.wrappedOpenAPINode().optionalNode()
static public func wrappedOpenAPINode(using encoder: JSONEncoder) throws -> JSONNode {
return try Wrapped.wrappedOpenAPINode(using: encoder).optionalNode()
}
}
extension Optional: AnyJSONCaseIterable where Wrapped: CaseIterable, Wrapped: Codable {
public static var allCases: [AnyCodable] {
return (try? allCases(from: Array(Wrapped.allCases))) ?? []
public static func allCases(using encoder: JSONEncoder) -> [AnyCodable] {
return (try? allCases(from: Array(Wrapped.allCases), using: encoder)) ?? []
}
}
extension String: OpenAPINodeType {
static public func openAPINode() throws -> JSONNode {
static public func openAPINode(using encoder: JSONEncoder) throws -> JSONNode {
return .string(.init(format: .generic,
required: true),
.init())
@@ -70,22 +71,22 @@ extension String: OpenAPINodeType {
}
extension Bool: OpenAPINodeType {
static public func openAPINode() throws -> JSONNode {
static public func openAPINode(using encoder: JSONEncoder) throws -> JSONNode {
return .boolean(.init(format: .generic,
required: true))
}
}
extension Array: OpenAPINodeType where Element: OpenAPINodeType {
static public func openAPINode() throws -> JSONNode {
static public func openAPINode(using encoder: JSONEncoder) throws -> JSONNode {
return .array(.init(format: .generic,
required: true),
.init(items: try Element.openAPINode()))
.init(items: try Element.openAPINode(using: encoder)))
}
}
extension Double: OpenAPINodeType {
static public func openAPINode() throws -> JSONNode {
static public func openAPINode(using encoder: JSONEncoder) throws -> JSONNode {
return .number(.init(format: .double,
required: true),
.init())
@@ -93,7 +94,7 @@ extension Double: OpenAPINodeType {
}
extension Float: OpenAPINodeType {
static public func openAPINode() throws -> JSONNode {
static public func openAPINode(using encoder: JSONEncoder) throws -> JSONNode {
return .number(.init(format: .float,
required: true),
.init())
@@ -101,7 +102,7 @@ extension Float: OpenAPINodeType {
}
extension Int: OpenAPINodeType {
static public func openAPINode() throws -> JSONNode {
static public func openAPINode(using encoder: JSONEncoder) throws -> JSONNode {
return .integer(.init(format: .generic,
required: true),
.init())
@@ -109,7 +110,7 @@ extension Int: OpenAPINodeType {
}
extension Int32: OpenAPINodeType {
static public func openAPINode() throws -> JSONNode {
static public func openAPINode(using encoder: JSONEncoder) throws -> JSONNode {
return .integer(.init(format: .int32,
required: true),
.init())
@@ -117,7 +118,7 @@ extension Int32: OpenAPINodeType {
}
extension Int64: OpenAPINodeType {
static public func openAPINode() throws -> JSONNode {
static public func openAPINode(using encoder: JSONEncoder) throws -> JSONNode {
return .integer(.init(format: .int64,
required: true),
.init())
@@ -6,6 +6,7 @@
//
import JSONAPI
import Foundation
import AnyCodable
/// A Sampleable type can provide a sample value.
@@ -48,7 +49,7 @@ public extension Sampleable {
}
extension Sampleable {
public static func genericObjectOpenAPINode() throws -> JSONNode {
public static func genericObjectOpenAPINode(using encoder: JSONEncoder) throws -> JSONNode {
let mirror = Mirror(reflecting: Self.sample)
let properties: [(String, JSONNode)] = try mirror.children.compactMap { child in
@@ -56,9 +57,9 @@ extension Sampleable {
let maybeAllCases: [AnyCodable]? = {
switch type(of: child.value) {
case let valType as AnyJSONCaseIterable.Type:
return valType.allCases
return valType.allCases(using: encoder)
case let valType as AnyWrappedJSONCaseIterable.Type:
return valType.allCases
return valType.allCases(using: encoder)
default:
return nil
}
@@ -68,16 +69,16 @@ extension Sampleable {
let maybeOpenAPINode: JSONNode? = try {
switch type(of: child.value) {
case let valType as OpenAPINodeType.Type:
return try valType.openAPINode()
return try valType.openAPINode(using: encoder)
case let valType as RawOpenAPINodeType.Type:
return try valType.rawOpenAPINode()
return try valType.rawOpenAPINode(using: encoder)
case let valType as WrappedRawOpenAPIType.Type:
return try valType.wrappedOpenAPINode()
return try valType.wrappedOpenAPINode(using: encoder)
case let valType as DoubleWrappedRawOpenAPIType.Type:
return try valType.wrappedOpenAPINode()
return try valType.wrappedOpenAPINode(using: encoder)
default:
return nil
@@ -107,6 +108,61 @@ extension Sampleable {
}
}
extension Sampleable {
public static func samples<S1: Sampleable>(using s1: S1.Type, with constructor: (S1) -> Self) -> [Self] {
return S1.samples.map(constructor)
}
public static func samples<S1: Sampleable, S2: Sampleable>(using s1: S1.Type, _ s2: S2.Type, with constructor: (S1, S2) -> Self) -> [Self] {
return zip(S1.samples, S2.samples).map(constructor)
}
public static func samples<S1: Sampleable, S2: Sampleable, S3: Sampleable>(using s1: S1.Type, _ s2: S2.Type, _ s3: S3.Type, with constructor: (S1, S2, S3) -> Self) -> [Self] {
return zip3(S1.samples, S2.samples, S3.samples).map(constructor)
}
public static func samples<S1: Sampleable, S2: Sampleable, S3: Sampleable, S4: Sampleable>(using s1: S1.Type, _ s2: S2.Type, _ s3: S3.Type, _ s4: S4.Type, with constructor: (S1, S2, S3, S4) -> Self) -> [Self] {
return zip4(S1.samples, S2.samples, S3.samples, S4.samples).map(constructor)
}
public static func samples<S1: Sampleable, S2: Sampleable, S3: Sampleable, S4: Sampleable, S5: Sampleable>(using s1: S1.Type, _ s2: S2.Type, _ s3: S3.Type, _ s4: S4.Type, _ s5: S5.Type, with constructor: (S1, S2, S3, S4, S5) -> Self) -> [Self] {
return zip5(S1.samples, S2.samples, S3.samples, S4.samples, S5.samples).map(constructor)
}
public static func samples<S1: Sampleable, S2: Sampleable, S3: Sampleable, S4: Sampleable, S5: Sampleable, S6: Sampleable>(using s1: S1.Type, _ s2: S2.Type, _ s3: S3.Type, _ s4: S4.Type, _ s5: S5.Type, _ s6: S6.Type, with constructor: (S1, S2, S3, S4, S5, S6) -> Self) -> [Self] {
// the compiler craps out at zip6. breaking it down makes the difference.
let firstZip = zip3(S1.samples, S2.samples, S3.samples)
let secondZip = zip3(S4.samples, S5.samples, S6.samples)
return zip(firstZip, secondZip).map { arg in (arg.0.0, arg.0.1, arg.0.2, arg.1.0, arg.1.1, arg.1.2) }.map(constructor)
}
public static func samples<S1: Sampleable, S2: Sampleable, S3: Sampleable, S4: Sampleable, S5: Sampleable, S6: Sampleable, S7: Sampleable>(using s1: S1.Type, _ s2: S2.Type, _ s3: S3.Type, _ s4: S4.Type, _ s5: S5.Type, _ s6: S6.Type, _ s7: S7.Type, with constructor: (S1, S2, S3, S4, S5, S6, S7) -> Self) -> [Self] {
// the compiler craps out at zip6. breaking it down makes the difference.
let firstZip = zip3(S1.samples, S2.samples, S3.samples)
let secondZip = zip4(S4.samples, S5.samples, S6.samples, S7.samples)
return zip(firstZip, secondZip).map { arg in (arg.0.0, arg.0.1, arg.0.2, arg.1.0, arg.1.1, arg.1.2, arg.1.3) }.map(constructor)
}
public static func samples<S1: Sampleable, S2: Sampleable, S3: Sampleable, S4: Sampleable, S5: Sampleable, S6: Sampleable, S7: Sampleable, S8: Sampleable>(using s1: S1.Type, _ s2: S2.Type, _ s3: S3.Type, _ s4: S4.Type, _ s5: S5.Type, _ s6: S6.Type, _ s7: S7.Type, _ s8: S8.Type, with constructor: (S1, S2, S3, S4, S5, S6, S7, S8) -> Self) -> [Self] {
// the compiler craps out at zip6. breaking it down makes the difference.
let firstZip = zip4(S1.samples, S2.samples, S3.samples, S4.samples)
let secondZip = zip4(S5.samples, S6.samples, S7.samples, S8.samples)
return zip(firstZip, secondZip).map { arg in (arg.0.0, arg.0.1, arg.0.2, arg.0.3, arg.1.0, arg.1.1, arg.1.2, arg.1.3) }.map(constructor)
}
@inlinable static func zip3<A: Sequence, B: Sequence, C: Sequence>(_ a: A, _ b: B, _ c: C) -> [(A.Element, B.Element, C.Element)] {
return zip(a, zip(b, c)).map { arg in (arg.0, arg.1.0, arg.1.1) }
}
@inlinable static func zip4<A: Sequence, B: Sequence, C: Sequence, D: Sequence>(_ a: A, _ b: B, _ c: C, _ d: D) -> [(A.Element, B.Element, C.Element, D.Element)] {
return zip(a, zip(b, zip(c, d))).map { arg in (arg.0, arg.1.0, arg.1.1.0, arg.1.1.1) }
}
@inlinable static func zip5<A: Sequence, B: Sequence, C: Sequence, D: Sequence, E: Sequence>(_ a: A, _ b: B, _ c: C, _ d: D, _ e: E) -> [(A.Element, B.Element, C.Element, D.Element, E.Element)] {
return zip(a, zip(b, zip(c, zip(d, e)))).map { arg in (arg.0, arg.1.0, arg.1.1.0, arg.1.1.1.0, arg.1.1.1.1) }
}
}
extension NoAttributes: Sampleable {
public static var sample: NoAttributes {
return .none
@@ -131,8 +187,26 @@ extension NoLinks: Sampleable {
}
}
extension NoAPIDescription: Sampleable {
public static var sample: NoAPIDescription {
return .none
}
}
extension UnknownJSONAPIError: Sampleable {
public static var sample: UnknownJSONAPIError {
return .unknownError
}
}
extension SingleResourceBody: Sampleable where Entity: Sampleable {
public static var sample: SingleResourceBody<Entity> {
return .init(entity: Entity.sample)
}
}
extension ManyResourceBody: Sampleable where Entity: Sampleable {
public static var sample: ManyResourceBody<Entity> {
return .init(entities: Entity.samples)
}
}
@@ -12,7 +12,7 @@ import AnyCodable
class JSONAPIEntityOpenAPITests: XCTestCase {
func test_EmptyEntity() {
let node = try! TestType1.openAPINode()
let node = try! TestType1.openAPINode(using: JSONEncoder())
XCTAssertTrue(node.required)
XCTAssertEqual(node.jsonTypeFormat, .object(.generic))
@@ -40,7 +40,7 @@ class JSONAPIEntityOpenAPITests: XCTestCase {
}
func test_AttributesEntity() {
let node = try! TestType2.openAPINode()
let node = try! TestType2.openAPINode(using: JSONEncoder())
XCTAssertTrue(node.required)
XCTAssertEqual(node.jsonTypeFormat, .object(.generic))
@@ -122,7 +122,7 @@ class JSONAPIEntityOpenAPITests: XCTestCase {
}
func test_RelationshipsEntity() {
let node = try! TestType3.openAPINode()
let node = try! TestType3.openAPINode(using: JSONEncoder())
XCTAssertTrue(node.required)
XCTAssertEqual(node.jsonTypeFormat, .object(.generic))