Add support for 'const' via OpenAPI 3.0 workaround suggested by others: Use 'enum' with one value. Add Sampleable support for requesting samples representing 'success' and 'failure' if available.

This commit is contained in:
Mathew Polzin
2019-01-21 22:47:03 -08:00
parent 95f9d8084d
commit 5433dddc81
6 changed files with 57 additions and 13 deletions
@@ -45,4 +45,12 @@ extension Document: Sampleable where PrimaryResourceBody: Arbitrary, IncludeType
public static var sample: Document {
return Document.arbitrary.generate
}
public static var successSample: Document? {
return Document.arbitraryData.generate
}
public static var failureSample: Document? {
return Document.arbitraryErrors.generate
}
}
@@ -72,13 +72,14 @@ extension TransformedAttribute: OpenAPINodeType where RawValue: OpenAPINodeType
}
extension RelationshipType {
static func relationshipNode(nullable: Bool) -> JSONNode {
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),
required: true,
allowedValues: [.init(jsonType)]),
.init())
]
@@ -97,7 +98,7 @@ extension ToOneRelationship: OpenAPINodeType {
return .object(.init(format: .generic,
required: true),
.init(properties: [
"data": ToOneRelationship.relationshipNode(nullable: nullable)
"data": ToOneRelationship.relationshipNode(nullable: nullable, jsonType: Identifiable.jsonType)
]))
}
}
@@ -111,14 +112,16 @@ extension ToManyRelationship: OpenAPINodeType {
.init(properties: [
"data": .array(.init(format: .generic,
required: true),
.init(items: ToManyRelationship.relationshipNode(nullable: false)))
.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 {
// TODO: const for json `type`
// 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,
@@ -127,7 +130,8 @@ extension Entity: OpenAPINodeType where Description.Attributes: Sampleable, Desc
let idProperty = ("id", idNode)
let typeNode = JSONNode.string(.init(format: .generic,
required: true),
required: true,
allowedValues: [.init(Entity.jsonType)]),
.init())
let typeProperty = ("type", typeNode)
@@ -13,6 +13,7 @@ extension JSONNode.Context: Encodable {
case allowedValues = "enum"
case nullable
case example
// case constantValue = "const"
}
public func encode(to encoder: Encoder) throws {
@@ -28,6 +29,10 @@ extension JSONNode.Context: Encodable {
try container.encode(allowedValues, forKey: .allowedValues)
}
// if constantValue != nil {
// try container.encode(constantValue, forKey: .constantValue)
// }
try container.encode(nullable, forKey: .nullable)
if example != nil {
+12 -1
View File
@@ -18,7 +18,7 @@ public protocol OpenAPINodeType {
extension OpenAPINodeType where Self: Sampleable, Self: Encodable {
public static func openAPINodeWithExample() throws -> JSONNode {
return try openAPINode().with(example: Self.sample)
return try openAPINode().with(example: Self.successSample ?? Self.sample)
}
}
@@ -255,6 +255,10 @@ public enum JSONNode: Equatable {
public let required: Bool
public let nullable: Bool
// NOTE: "const" is supported by the newest JSON Schema spec but not
// yet by OpenAPI. Instead, will use "enum" with one possible value for now.
// public let constantValue: Format.SwiftType?
/// The OpenAPI spec calls this "enum"
/// If not specified, it is assumed that any
/// value of the given format is allowed.
@@ -276,11 +280,13 @@ public enum JSONNode: Equatable {
public init(format: Format,
required: Bool,
nullable: Bool = false,
// constantValue: Format.SwiftType? = nil,
allowedValues: [AnyCodable]? = nil,
example: AnyCodable? = nil) {
self.format = format
self.required = required
self.nullable = nullable
// self.constantValue = constantValue
self.allowedValues = allowedValues
self.example = example
.flatMap { try? JSONEncoder().encode($0)}
@@ -292,6 +298,7 @@ public enum JSONNode: Equatable {
return .init(format: format,
required: false,
nullable: nullable,
// constantValue: constantValue,
allowedValues: allowedValues)
}
@@ -300,6 +307,7 @@ public enum JSONNode: Equatable {
return .init(format: format,
required: true,
nullable: nullable,
// constantValue: constantValue,
allowedValues: allowedValues)
}
@@ -308,6 +316,7 @@ public enum JSONNode: Equatable {
return .init(format: format,
required: required,
nullable: true,
// constantValue: constantValue,
allowedValues: allowedValues)
}
@@ -316,6 +325,7 @@ public enum JSONNode: Equatable {
return .init(format: format,
required: required,
nullable: nullable,
// constantValue: constantValue,
allowedValues: allowedValues)
}
@@ -324,6 +334,7 @@ public enum JSONNode: Equatable {
return .init(format: format,
required: required,
nullable: nullable,
// constantValue: constantValue,
allowedValues: allowedValues,
example: example)
}
+14
View File
@@ -15,6 +15,20 @@ public protocol Sampleable {
/// 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 {
@@ -34,7 +34,8 @@ class JSONAPIEntityOpenAPITests: XCTestCase {
required: true),
.init()))
XCTAssertEqual(objectContext1.properties["type"], .string(.init(format: .generic,
required: true),
required: true,
allowedValues: [.init(TestType1.jsonType)]),
.init()))
}
@@ -62,7 +63,8 @@ class JSONAPIEntityOpenAPITests: XCTestCase {
required: true),
.init()))
XCTAssertEqual(objectContext1.properties["type"], .string(.init(format: .generic,
required: true),
required: true,
allowedValues: [.init(TestType2.jsonType)]),
.init()))
let attributesNode = objectContext1.properties["attributes"]
@@ -143,7 +145,8 @@ class JSONAPIEntityOpenAPITests: XCTestCase {
required: true),
.init()))
XCTAssertEqual(objectContext1.properties["type"], .string(.init(format: .generic,
required: true),
required: true,
allowedValues: [.init(TestType3.jsonType)]),
.init()))
let relationshipsNode = objectContext1.properties["relationships"]
@@ -170,7 +173,8 @@ class JSONAPIEntityOpenAPITests: XCTestCase {
required: true),
.init()),
"type": .string(.init(format: .generic,
required: true),
required: true,
allowedValues: [.init(TestType1.jsonType)]),
.init())])
let pointerContext = JSONNode.ObjectContext(properties: ["data": .object(.init(format: .generic,
@@ -225,8 +229,6 @@ class JSONAPIEntityOpenAPITests: XCTestCase {
nullable: false,
allowedValues: nil),
manyPointerContext))
let tmpData = try! JSONEncoder().encode(node)
}
func test_AttributesAndRelationshipsEntity() {