Add some tests around Attribute OpenAPI descriptions

This commit is contained in:
Mathew Polzin
2019-01-20 18:18:35 -08:00
parent 875d938b95
commit 2de6580519
7 changed files with 638 additions and 12 deletions
@@ -24,8 +24,10 @@ extension Attribute: OpenAPINodeType where RawValue: OpenAPINodeType {
}
extension Attribute: RawOpenAPINodeType where RawValue: RawRepresentable, RawValue.RawValue: OpenAPINodeType {
static public func openAPINode() throws -> JSONNode {
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()
}
@@ -33,6 +35,18 @@ extension Attribute: RawOpenAPINodeType where RawValue: RawRepresentable, RawVal
}
}
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))) ?? []
+29 -6
View File
@@ -23,7 +23,30 @@ public protocol OpenAPINodeType {
/// different schema. The "different" conditions have to do
/// with Raw Representability, hence the name of this protocol.
public protocol RawOpenAPINodeType {
static func openAPINode() throws -> JSONNode
static func rawOpenAPINode() throws -> JSONNode
}
/// Anything conforming to `RawOpenAPINodeType` can provide an
/// OpenAPI schema representing itself. This third protocol is
/// necessary so that one type can conditionally provide a
/// schema and then (under different conditions) provide a
/// different schema. The "different" conditions have to do
/// with Optionality, hence the name of this protocol.
public protocol WrappedRawOpenAPIType {
static func wrappedOpenAPINode() throws -> JSONNode
}
/// Anything conforming to `RawOpenAPINodeType` can provide an
/// OpenAPI schema representing itself. This third protocol is
/// necessary so that one type can conditionally provide a
/// schema and then (under different conditions) provide a
/// different schema. The "different" conditions have to do
/// with Optionality, hence the name of this protocol.
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
}
/// Anything conforming to `AnyJSONCaseIterable` can provide a
@@ -209,7 +232,7 @@ public enum JSONTypeFormat: Equatable {
/// A JSON Node is what OpenAPI calls a
/// "Schema Object"
public enum JSONNode {
public enum JSONNode: Equatable {
case boolean(Context<JSONTypeFormat.BooleanFormat>)
indirect case object(Context<JSONTypeFormat.ObjectFormat>, ObjectContext)
indirect case array(Context<JSONTypeFormat.ArrayFormat>, ArrayContext)
@@ -281,7 +304,7 @@ public enum JSONNode {
}
}
public struct NumericContext {
public struct NumericContext: Equatable {
/// A numeric instance is valid only if division by this keyword's value results in an integer. Defaults to nil.
public let multipleOf: Double?
public let maximum: Double?
@@ -302,7 +325,7 @@ public enum JSONNode {
}
}
public struct StringContext {
public struct StringContext: Equatable {
public let maxLength: Int?
public let minLength: Int
@@ -318,7 +341,7 @@ public enum JSONNode {
}
}
public struct ArrayContext {
public struct ArrayContext: Equatable {
/// A JSON Type Node that describes
/// the type of each element in the array.
public let items: JSONNode
@@ -346,7 +369,7 @@ public enum JSONNode {
}
}
public struct ObjectContext {
public struct ObjectContext: Equatable {
public let maxProperties: Int?
public let minProperties: Int
public let properties: [String: JSONNode]
+4 -1
View File
@@ -38,7 +38,10 @@ extension Sampleable {
return try valType.openAPINode()
case let valType as RawOpenAPINodeType.Type:
return try valType.openAPINode()
return try valType.rawOpenAPINode()
case let valType as WrappedRawOpenAPIType.Type:
return try valType.wrappedOpenAPINode()
default:
return nil
@@ -38,11 +38,23 @@ extension Optional: OpenAPINodeType where Wrapped: OpenAPINodeType {
}
extension Optional: RawOpenAPINodeType where Wrapped: RawRepresentable, Wrapped.RawValue: OpenAPINodeType {
static public func openAPINode() throws -> JSONNode {
static public func rawOpenAPINode() throws -> JSONNode {
return try Wrapped.RawValue.openAPINode().optionalNode()
}
}
extension Optional: WrappedRawOpenAPIType where Wrapped: RawOpenAPINodeType {
static public func wrappedOpenAPINode() throws -> JSONNode {
return try Wrapped.rawOpenAPINode().optionalNode()
}
}
extension Optional: DoubleWrappedRawOpenAPIType where Wrapped: WrappedRawOpenAPIType {
static public func wrappedOpenAPINode() throws -> JSONNode {
return try Wrapped.wrappedOpenAPINode().optionalNode()
}
}
extension Optional: AnyJSONCaseIterable where Wrapped: CaseIterable, Wrapped: Codable {
public static var allCases: [AnyCodable] {
return (try? allCases(from: Array(Wrapped.allCases))) ?? []