mirror of
https://github.com/encounter/JSONAPI.git
synced 2026-03-30 11:18:38 -07:00
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:
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user