diff --git a/Sources/JSONAPIOpenAPI/JSONAPI/JSONAPIInclude+OpenAPI.swift b/Sources/JSONAPIOpenAPI/JSONAPI/JSONAPIInclude+OpenAPI.swift index 948cd0e..6f7f966 100644 --- a/Sources/JSONAPIOpenAPI/JSONAPI/JSONAPIInclude+OpenAPI.swift +++ b/Sources/JSONAPIOpenAPI/JSONAPI/JSONAPIInclude+OpenAPI.swift @@ -9,8 +9,8 @@ import JSONAPI import Foundation extension Includes: OpenAPINodeType where I: OpenAPINodeType { - public static func openAPINode(using encoder: JSONEncoder) throws -> JSONNode { - let includeNode = try I.openAPINode(using: encoder) + public static func openAPINode() throws -> JSONNode { + let includeNode = try I.openAPINode() return .array(.init(format: .generic, required: true), @@ -20,113 +20,113 @@ extension Includes: OpenAPINodeType where I: OpenAPINodeType { } extension Include0: OpenAPINodeType { - public static func openAPINode(using encoder: JSONEncoder) throws -> JSONNode { + public static func openAPINode() throws -> JSONNode { throw OpenAPITypeError.invalidNode } } extension Include1: OpenAPINodeType where A: OpenAPINodeType { - public static func openAPINode(using encoder: JSONEncoder) throws -> JSONNode { - return try .one(of: [A.openAPINode(using: encoder)]) + public static func openAPINode() throws -> JSONNode { + return try .one(of: [A.openAPINode()]) } } extension Include2: OpenAPINodeType where A: OpenAPINodeType, B: OpenAPINodeType { - public static func openAPINode(using encoder: JSONEncoder) throws -> JSONNode { + public static func openAPINode() throws -> JSONNode { return try .one(of: [ - A.openAPINode(using: encoder), - B.openAPINode(using: encoder) + A.openAPINode(), + B.openAPINode() ]) } } extension Include3: OpenAPINodeType where A: OpenAPINodeType, B: OpenAPINodeType, C: OpenAPINodeType { - public static func openAPINode(using encoder: JSONEncoder) throws -> JSONNode { + public static func openAPINode() throws -> JSONNode { return try .one(of: [ - A.openAPINode(using: encoder), - B.openAPINode(using: encoder), - C.openAPINode(using: encoder) + A.openAPINode(), + B.openAPINode(), + C.openAPINode() ]) } } extension Include4: OpenAPINodeType where A: OpenAPINodeType, B: OpenAPINodeType, C: OpenAPINodeType, D: OpenAPINodeType { - public static func openAPINode(using encoder: JSONEncoder) throws -> JSONNode { + public static func openAPINode() throws -> JSONNode { return try .one(of: [ - A.openAPINode(using: encoder), - B.openAPINode(using: encoder), - C.openAPINode(using: encoder), - D.openAPINode(using: encoder) + 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(using encoder: JSONEncoder) throws -> JSONNode { + public static func openAPINode() throws -> JSONNode { return try .one(of: [ - A.openAPINode(using: encoder), - B.openAPINode(using: encoder), - C.openAPINode(using: encoder), - D.openAPINode(using: encoder), - E.openAPINode(using: encoder) + 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(using encoder: JSONEncoder) throws -> JSONNode { + public static func openAPINode() throws -> JSONNode { return try .one(of: [ - A.openAPINode(using: encoder), - B.openAPINode(using: encoder), - C.openAPINode(using: encoder), - D.openAPINode(using: encoder), - E.openAPINode(using: encoder), - F.openAPINode(using: encoder) + 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(using encoder: JSONEncoder) throws -> JSONNode { + public static func openAPINode() throws -> JSONNode { return try .one(of: [ - 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) + 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(using encoder: JSONEncoder) throws -> JSONNode { + public static func openAPINode() throws -> JSONNode { return try .one(of: [ - 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) + 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(using encoder: JSONEncoder) throws -> JSONNode { + public static func openAPINode() throws -> JSONNode { return try .one(of: [ - 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) + A.openAPINode(), + B.openAPINode(), + C.openAPINode(), + D.openAPINode(), + E.openAPINode(), + F.openAPINode(), + G.openAPINode(), + H.openAPINode(), + I.openAPINode() ]) } } diff --git a/Sources/JSONAPIOpenAPI/JSONAPI/JSONAPITypes+OpenAPI.swift b/Sources/JSONAPIOpenAPI/JSONAPI/JSONAPITypes+OpenAPI.swift index 5e0baac..91c6068 100644 --- a/Sources/JSONAPIOpenAPI/JSONAPI/JSONAPITypes+OpenAPI.swift +++ b/Sources/JSONAPIOpenAPI/JSONAPI/JSONAPITypes+OpenAPI.swift @@ -13,38 +13,38 @@ private protocol _Optional {} extension Optional: _Optional {} extension Attribute: OpenAPINodeType where RawValue: OpenAPINodeType { - static public func openAPINode(using encoder: JSONEncoder) throws -> JSONNode { + static public func openAPINode() 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(using: encoder).required { - return try RawValue.openAPINode(using: encoder).requiredNode().nullableNode() + if try !RawValue.openAPINode().required { + return try RawValue.openAPINode().requiredNode().nullableNode() } - return try RawValue.openAPINode(using: encoder) + return try RawValue.openAPINode() } } extension Attribute: RawOpenAPINodeType where RawValue: RawRepresentable, RawValue.RawValue: OpenAPINodeType { - static public func rawOpenAPINode(using encoder: JSONEncoder) 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(using: encoder).required { - return try RawValue.RawValue.openAPINode(using: encoder).requiredNode().nullableNode() + if try !RawValue.RawValue.openAPINode().required { + return try RawValue.RawValue.openAPINode().requiredNode().nullableNode() } - return try RawValue.RawValue.openAPINode(using: encoder) + return try RawValue.RawValue.openAPINode() } } extension Attribute: WrappedRawOpenAPIType where RawValue: RawOpenAPINodeType { - public static func wrappedOpenAPINode(using encoder: JSONEncoder) throws -> JSONNode { + 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(using: encoder).required { - return try RawValue.rawOpenAPINode(using: encoder).requiredNode().nullableNode() + if try !RawValue.rawOpenAPINode().required { + return try RawValue.rawOpenAPINode().requiredNode().nullableNode() } - return try RawValue.rawOpenAPINode(using: encoder) + return try RawValue.rawOpenAPINode() } } @@ -61,14 +61,14 @@ extension Attribute: AnyWrappedJSONCaseIterable where RawValue: AnyJSONCaseItera } extension TransformedAttribute: OpenAPINodeType where RawValue: OpenAPINodeType { - static public func openAPINode(using encoder: JSONEncoder) throws -> JSONNode { + static public func openAPINode() 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(using: encoder).required { - return try RawValue.openAPINode(using: encoder).requiredNode().nullableNode() + if try !RawValue.openAPINode().required { + return try RawValue.openAPINode().requiredNode().nullableNode() } - return try RawValue.openAPINode(using: encoder) + return try RawValue.openAPINode() } } @@ -96,7 +96,7 @@ extension ToOneRelationship: OpenAPINodeType { // Will use "enum" with one possible value for now. // TODO: metadata & links - static public func openAPINode(using encoder: JSONEncoder) throws -> JSONNode { + static public func openAPINode() throws -> JSONNode { let nullable = Identifiable.self is _Optional.Type return .object(.init(format: .generic, required: true), @@ -111,7 +111,7 @@ extension ToManyRelationship: OpenAPINodeType { // Will use "enum" with one possible value for now. // TODO: metadata & links - static public func openAPINode(using encoder: JSONEncoder) throws -> JSONNode { + static public func openAPINode() throws -> JSONNode { return .object(.init(format: .generic, required: true), .init(properties: [ @@ -122,7 +122,7 @@ extension ToManyRelationship: OpenAPINodeType { } } -extension Entity: OpenAPINodeType where Description.Attributes: Sampleable, Description.Relationships: Sampleable { +extension Entity: OpenAPIEncodedNodeType, OpenAPINodeType where Description.Attributes: Sampleable, Description.Relationships: Sampleable { 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. @@ -165,13 +165,13 @@ extension Entity: OpenAPINodeType where Description.Attributes: Sampleable, Desc } } -extension SingleResourceBody: OpenAPINodeType where Entity: OpenAPINodeType { +extension SingleResourceBody: OpenAPIEncodedNodeType, OpenAPINodeType where Entity: OpenAPIEncodedNodeType { public static func openAPINode(using encoder: JSONEncoder) throws -> JSONNode { return try Entity.openAPINode(using: encoder) } } -extension ManyResourceBody: OpenAPINodeType where Entity: OpenAPINodeType { +extension ManyResourceBody: OpenAPIEncodedNodeType, OpenAPINodeType where Entity: OpenAPIEncodedNodeType { public static func openAPINode(using encoder: JSONEncoder) throws -> JSONNode { return .array(.init(format: .generic, required: true), @@ -179,7 +179,7 @@ extension ManyResourceBody: OpenAPINodeType where Entity: OpenAPINodeType { } } -extension Document: OpenAPINodeType where PrimaryResourceBody: OpenAPINodeType, IncludeType: OpenAPINodeType { +extension Document: OpenAPIEncodedNodeType, OpenAPINodeType where PrimaryResourceBody: OpenAPIEncodedNodeType, IncludeType: OpenAPINodeType { 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 @@ -190,7 +190,7 @@ extension Document: OpenAPINodeType where PrimaryResourceBody: OpenAPINodeType, let includeNode: JSONNode? do { - includeNode = try Includes.openAPINode(using: encoder) + includeNode = try Includes.openAPINode() } catch let err as OpenAPITypeError { guard err == .invalidNode else { throw err diff --git a/Sources/JSONAPIOpenAPI/OpenAPI/OpenAPITypes.swift b/Sources/JSONAPIOpenAPI/OpenAPI/OpenAPITypes.swift index d477ae6..a7723ec 100644 --- a/Sources/JSONAPIOpenAPI/OpenAPI/OpenAPITypes.swift +++ b/Sources/JSONAPIOpenAPI/OpenAPI/OpenAPITypes.swift @@ -13,12 +13,25 @@ import Foundation /// Anything conforming to `OpenAPINodeType` can provide an /// OpenAPI schema representing itself. public protocol OpenAPINodeType { - static func openAPINode(using encoder: JSONEncoder) throws -> JSONNode + static func openAPINode() throws -> JSONNode } extension OpenAPINodeType where Self: Sampleable, Self: Encodable { public static func openAPINodeWithExample(using encoder: JSONEncoder = JSONEncoder()) throws -> JSONNode { - return try openAPINode(using: encoder).with(example: Self.successSample ?? Self.sample, using: encoder) + return try openAPINode().with(example: Self.successSample ?? Self.sample, using: encoder) + } +} + +/// Anything conforming to `OpenAPIEncodedNodeType` can provide an +/// OpenAPI schema representing itself but it may need an Encoder +/// to do its job. +public protocol OpenAPIEncodedNodeType: OpenAPINodeType { + static func openAPINode(using encoder: JSONEncoder) throws -> JSONNode +} + +extension OpenAPIEncodedNodeType { + public static func openAPINode() throws -> JSONNode { + return try openAPINode(using: JSONEncoder()) } } @@ -29,7 +42,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(using encoder: JSONEncoder) throws -> JSONNode + static func rawOpenAPINode() throws -> JSONNode } /// Anything conforming to `RawOpenAPINodeType` can provide an @@ -39,7 +52,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(using encoder: JSONEncoder) throws -> JSONNode + static func wrappedOpenAPINode() throws -> JSONNode } /// Anything conforming to `RawOpenAPINodeType` can provide an @@ -52,7 +65,7 @@ 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(using encoder: JSONEncoder) throws -> JSONNode + static func wrappedOpenAPINode() throws -> JSONNode } /// Anything conforming to `AnyJSONCaseIterable` can provide a @@ -282,15 +295,14 @@ public enum JSONNode: Equatable { nullable: Bool = false, // constantValue: Format.SwiftType? = nil, allowedValues: [AnyCodable]? = nil, - example: AnyCodable? = nil, - using encoder: JSONEncoder = JSONEncoder()) { + example: (codable: AnyCodable, encoder: JSONEncoder)? = nil) { self.format = format self.required = required self.nullable = nullable // self.constantValue = constantValue self.allowedValues = allowedValues self.example = example - .flatMap { try? encoder.encode($0)} + .flatMap { try? $0.encoder.encode($0.codable)} .flatMap { String(data: $0, encoding: .utf8) } } @@ -331,13 +343,13 @@ public enum JSONNode: Equatable { } /// Return this context with the given example - public func with(example: AnyCodable) -> Context { + public func with(example: AnyCodable, using encoder: JSONEncoder) -> Context { return .init(format: format, required: required, nullable: nullable, // constantValue: constantValue, allowedValues: allowedValues, - example: example) + example: (codable: example, encoder: encoder)) } } @@ -562,17 +574,17 @@ public enum JSONNode: Equatable { switch self { case .boolean(let context): - return .boolean(context.with(example: example)) + return .boolean(context.with(example: example, using: encoder)) case .object(let contextA, let contextB): - return .object(contextA.with(example: example), contextB) + return .object(contextA.with(example: example, using: encoder), contextB) case .array(let contextA, let contextB): - return .array(contextA.with(example: example), contextB) + return .array(contextA.with(example: example, using: encoder), contextB) case .number(let context, let contextB): - return .number(context.with(example: example), contextB) + return .number(context.with(example: example, using: encoder), contextB) case .integer(let context, let contextB): - return .integer(context.with(example: example), contextB) + return .integer(context.with(example: example, using: encoder), contextB) case .string(let context, let contextB): - return .string(context.with(example: example), contextB) + return .string(context.with(example: example, using: encoder), contextB) case .all, .one, .any, .not: return self } diff --git a/Sources/JSONAPIOpenAPI/OpenAPI/SwiftPrimitiveTypes+OpenAPI.swift b/Sources/JSONAPIOpenAPI/OpenAPI/SwiftPrimitiveTypes+OpenAPI.swift index 90a6be9..19ee4da 100644 --- a/Sources/JSONAPIOpenAPI/OpenAPI/SwiftPrimitiveTypes+OpenAPI.swift +++ b/Sources/JSONAPIOpenAPI/OpenAPI/SwiftPrimitiveTypes+OpenAPI.swift @@ -33,26 +33,26 @@ Any object: **/ extension Optional: OpenAPINodeType where Wrapped: OpenAPINodeType { - static public func openAPINode(using encoder: JSONEncoder) throws -> JSONNode { - return try Wrapped.openAPINode(using: encoder).optionalNode() + static public func openAPINode() throws -> JSONNode { + return try Wrapped.openAPINode().optionalNode() } } extension Optional: RawOpenAPINodeType where Wrapped: RawRepresentable, Wrapped.RawValue: OpenAPINodeType { - static public func rawOpenAPINode(using encoder: JSONEncoder) throws -> JSONNode { - return try Wrapped.RawValue.openAPINode(using: encoder).optionalNode() + static public func rawOpenAPINode() throws -> JSONNode { + return try Wrapped.RawValue.openAPINode().optionalNode() } } extension Optional: WrappedRawOpenAPIType where Wrapped: RawOpenAPINodeType { - static public func wrappedOpenAPINode(using encoder: JSONEncoder) throws -> JSONNode { - return try Wrapped.rawOpenAPINode(using: encoder).optionalNode() + static public func wrappedOpenAPINode() throws -> JSONNode { + return try Wrapped.rawOpenAPINode().optionalNode() } } extension Optional: DoubleWrappedRawOpenAPIType where Wrapped: WrappedRawOpenAPIType { - static public func wrappedOpenAPINode(using encoder: JSONEncoder) throws -> JSONNode { - return try Wrapped.wrappedOpenAPINode(using: encoder).optionalNode() + static public func wrappedOpenAPINode() throws -> JSONNode { + return try Wrapped.wrappedOpenAPINode().optionalNode() } } @@ -63,7 +63,7 @@ extension Optional: AnyJSONCaseIterable where Wrapped: CaseIterable, Wrapped: Co } extension String: OpenAPINodeType { - static public func openAPINode(using encoder: JSONEncoder) throws -> JSONNode { + static public func openAPINode() throws -> JSONNode { return .string(.init(format: .generic, required: true), .init()) @@ -71,22 +71,22 @@ extension String: OpenAPINodeType { } extension Bool: OpenAPINodeType { - static public func openAPINode(using encoder: JSONEncoder) throws -> JSONNode { + static public func openAPINode() throws -> JSONNode { return .boolean(.init(format: .generic, required: true)) } } extension Array: OpenAPINodeType where Element: OpenAPINodeType { - static public func openAPINode(using encoder: JSONEncoder) throws -> JSONNode { + static public func openAPINode() throws -> JSONNode { return .array(.init(format: .generic, required: true), - .init(items: try Element.openAPINode(using: encoder))) + .init(items: try Element.openAPINode())) } } extension Double: OpenAPINodeType { - static public func openAPINode(using encoder: JSONEncoder) throws -> JSONNode { + static public func openAPINode() throws -> JSONNode { return .number(.init(format: .double, required: true), .init()) @@ -94,7 +94,7 @@ extension Double: OpenAPINodeType { } extension Float: OpenAPINodeType { - static public func openAPINode(using encoder: JSONEncoder) throws -> JSONNode { + static public func openAPINode() throws -> JSONNode { return .number(.init(format: .float, required: true), .init()) @@ -102,7 +102,7 @@ extension Float: OpenAPINodeType { } extension Int: OpenAPINodeType { - static public func openAPINode(using encoder: JSONEncoder) throws -> JSONNode { + static public func openAPINode() throws -> JSONNode { return .integer(.init(format: .generic, required: true), .init()) @@ -110,7 +110,7 @@ extension Int: OpenAPINodeType { } extension Int32: OpenAPINodeType { - static public func openAPINode(using encoder: JSONEncoder) throws -> JSONNode { + static public func openAPINode() throws -> JSONNode { return .integer(.init(format: .int32, required: true), .init()) @@ -118,7 +118,7 @@ extension Int32: OpenAPINodeType { } extension Int64: OpenAPINodeType { - static public func openAPINode(using encoder: JSONEncoder) throws -> JSONNode { + static public func openAPINode() throws -> JSONNode { return .integer(.init(format: .int64, required: true), .init()) diff --git a/Sources/JSONAPIOpenAPI/Sampleable/Sampleable.swift b/Sources/JSONAPIOpenAPI/Sampleable/Sampleable.swift index bbf778b..e186222 100644 --- a/Sources/JSONAPIOpenAPI/Sampleable/Sampleable.swift +++ b/Sources/JSONAPIOpenAPI/Sampleable/Sampleable.swift @@ -69,16 +69,16 @@ extension Sampleable { let maybeOpenAPINode: JSONNode? = try { switch type(of: child.value) { case let valType as OpenAPINodeType.Type: - return try valType.openAPINode(using: encoder) + return try valType.openAPINode() case let valType as RawOpenAPINodeType.Type: - return try valType.rawOpenAPINode(using: encoder) + return try valType.rawOpenAPINode() case let valType as WrappedRawOpenAPIType.Type: - return try valType.wrappedOpenAPINode(using: encoder) + return try valType.wrappedOpenAPINode() case let valType as DoubleWrappedRawOpenAPIType.Type: - return try valType.wrappedOpenAPINode(using: encoder) + return try valType.wrappedOpenAPINode() default: return nil diff --git a/Tests/JSONAPIOpenAPITests/JSONAPIDocumentOpenAPITests.swift b/Tests/JSONAPIOpenAPITests/JSONAPIDocumentOpenAPITests.swift index f709f17..e82ca34 100644 --- a/Tests/JSONAPIOpenAPITests/JSONAPIDocumentOpenAPITests.swift +++ b/Tests/JSONAPIOpenAPITests/JSONAPIDocumentOpenAPITests.swift @@ -6,15 +6,23 @@ // import XCTest +import SwiftCheck import JSONAPI import JSONAPIOpenAPI class JSONAPIDocumentOpenAPITests: XCTestCase { func test_SingleResourceDocument() { - let node = try! SingleEntityDocument.openAPINode() + + let dateFormatter = DateFormatter() + dateFormatter.dateStyle = .medium + dateFormatter.timeStyle = .none + dateFormatter.locale = Locale(identifier: "en_US") let encoder = JSONEncoder() encoder.outputFormatting = .prettyPrinted + encoder.dateEncodingStrategy = .formatted(dateFormatter) + + let node = try! SingleEntityDocument.openAPINodeWithExample(using: encoder) print(String(data: try! encoder.encode(node), encoding: .utf8)!) } @@ -27,9 +35,11 @@ extension JSONAPIDocumentOpenAPITests { struct Attributes: JSONAPI.Attributes, Sampleable { let name: Attribute + let date: Attribute static var sample: Attributes { - return .init(name: "hello world") + return .init(name: "hello world", + date: .init(value: Date())) } } @@ -40,3 +50,29 @@ extension JSONAPIDocumentOpenAPITests { typealias SingleEntityDocument = Document, NoMetadata, NoLinks, NoIncludes, NoAPIDescription, UnknownJSONAPIError> } + +extension Id: Sampleable where RawType == String { + public static var sample: Id { + return .init(rawValue: String.arbitrary.generate) + } +} + +extension JSONAPI.Entity: Sampleable where Description.Attributes: Sampleable, Description.Relationships: Sampleable, MetaType: Sampleable, LinksType: Sampleable, EntityRawIdType == String { + public static var sample: JSONAPI.Entity { + return JSONAPI.Entity(id: .sample, + attributes: .sample, + relationships: .sample, + meta: .sample, + links: .sample) + } +} + +extension Document: Sampleable where PrimaryResourceBody: Sampleable, MetaType: Sampleable, LinksType: Sampleable, IncludeType: Sampleable, APIDescription: Sampleable, Error: Sampleable { + public static var sample: Document { + return Document(apiDescription: .sample, + body: .sample, + includes: .sample, + meta: .sample, + links: .sample) + } +}