From 59835fbe11b591e4392d26ad77e016f50d67fe2c Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Tue, 22 Jan 2019 16:08:47 -0800 Subject: [PATCH 1/5] Add JSON Reference and OpenAPIComponents (initially just with schemas) --- .../Contents.swift | 16 ++++ .../OpenAPI/OpenAPITypes+Codable.swift | 22 +++++ .../JSONAPIOpenAPI/OpenAPI/OpenAPITypes.swift | 82 +++++++++++++++++-- 3 files changed, 113 insertions(+), 7 deletions(-) diff --git a/JSONAPI.playground/Pages/OpenAPI Documentation.xcplaygroundpage/Contents.swift b/JSONAPI.playground/Pages/OpenAPI Documentation.xcplaygroundpage/Contents.swift index 01cff69..b9a4f1e 100644 --- a/JSONAPI.playground/Pages/OpenAPI Documentation.xcplaygroundpage/Contents.swift +++ b/JSONAPI.playground/Pages/OpenAPI Documentation.xcplaygroundpage/Contents.swift @@ -3,6 +3,7 @@ import Foundation import JSONAPI import JSONAPIOpenAPI +import Poly // print Entity Schema let encoder = JSONEncoder() @@ -28,3 +29,18 @@ print("Batch Person Document Schema") print("====") print(batchPersonSchemaData.map { String(data: $0, encoding: .utf8)! } ?? "Schema Construction Failed") print("====") + +let tmp: [String: OpenAPIComponents.SchemasDict.RefType] = [ + "BatchPerson": try! BatchPeopleDocument.openAPINodeWithExample() +] + +let components = OpenAPIComponents(schemas: tmp) + +let batchPeopleRef = JSONReference(type: \OpenAPIComponents.schemas, selector: "BatchPerson") + +let tmp2 = JSONNode.reference(batchPeopleRef) + +print("====") +print("====") +//print(String(data: try! encoder.encode(components), encoding: .utf8)!) +print(String(data: try! encoder.encode(tmp2), encoding: .utf8)!) diff --git a/Sources/JSONAPIOpenAPI/OpenAPI/OpenAPITypes+Codable.swift b/Sources/JSONAPIOpenAPI/OpenAPI/OpenAPITypes+Codable.swift index 9322035..d6357c7 100644 --- a/Sources/JSONAPIOpenAPI/OpenAPI/OpenAPITypes+Codable.swift +++ b/Sources/JSONAPIOpenAPI/OpenAPI/OpenAPITypes+Codable.swift @@ -155,6 +155,7 @@ extension JSONNode: Encodable { case oneOf case anyOf case not + case reference = "$ref" } public func encode(to encoder: Encoder) throws { @@ -189,6 +190,27 @@ extension JSONNode: Encodable { var container = encoder.container(keyedBy: SubschemaCodingKeys.self) try container.encode(node, forKey: .not) + + case .reference(let reference): + var container = encoder.container(keyedBy: SubschemaCodingKeys.self) + + try container.encode(reference, forKey: .reference) } } } + +extension JSONReference: Encodable { + public func encode(to encoder: Encoder) throws { + var container = encoder.singleValueContainer() + + try container.encode("#/\(Root.refName)/\(refName)/\(selector)") + } +} + +extension RefDict: Encodable { + public func encode(to encoder: Encoder) throws { + var container = encoder.singleValueContainer() + + try container.encode(dict) + } +} diff --git a/Sources/JSONAPIOpenAPI/OpenAPI/OpenAPITypes.swift b/Sources/JSONAPIOpenAPI/OpenAPI/OpenAPITypes.swift index c5ac131..87c67e7 100644 --- a/Sources/JSONAPIOpenAPI/OpenAPI/OpenAPITypes.swift +++ b/Sources/JSONAPIOpenAPI/OpenAPI/OpenAPITypes.swift @@ -7,6 +7,7 @@ import AnyCodable import Foundation +import Poly // MARK: Node (i.e. schema) Protocols @@ -249,6 +250,7 @@ public enum JSONNode: Equatable { indirect case one(of: [JSONNode]) indirect case any(of: [JSONNode]) indirect case not(JSONNode) + case reference(JSONReference) public struct Context: JSONNodeContext, Equatable { public let format: Format @@ -451,7 +453,7 @@ public enum JSONNode: Equatable { return .integer(context.format) case .string(let context, _): return .string(context.format) - case .all, .one, .any, .not: + case .all, .one, .any, .not, .reference: return nil } } @@ -465,7 +467,7 @@ public enum JSONNode: Equatable { .integer(let contextA as JSONNodeContext, _), .string(let contextA as JSONNodeContext, _): return contextA.required - case .all, .one, .any, .not: + case .all, .one, .any, .not, .reference: return true } } @@ -485,7 +487,7 @@ public enum JSONNode: Equatable { return .integer(context.optionalContext(), contextB) case .string(let context, let contextB): return .string(context.optionalContext(), contextB) - case .all, .one, .any, .not: + case .all, .one, .any, .not, .reference: return self } } @@ -505,7 +507,7 @@ public enum JSONNode: Equatable { return .integer(context.requiredContext(), contextB) case .string(let context, let contextB): return .string(context.requiredContext(), contextB) - case .all, .one, .any, .not: + case .all, .one, .any, .not, .reference: return self } } @@ -525,7 +527,7 @@ public enum JSONNode: Equatable { return .integer(context.nullableContext(), contextB) case .string(let context, let contextB): return .string(context.nullableContext(), contextB) - case .all, .one, .any, .not: + case .all, .one, .any, .not, .reference: return self } } @@ -545,7 +547,7 @@ public enum JSONNode: Equatable { return .integer(context.with(allowedValues: allowedValues), contextB) case .string(let context, let contextB): return .string(context.with(allowedValues: allowedValues), contextB) - case .all, .one, .any, .not: + case .all, .one, .any, .not, .reference: return self } } @@ -571,7 +573,7 @@ public enum JSONNode: Equatable { return .integer(context.with(example: example), contextB) case .string(let context, let contextB): return .string(context.with(example: example), contextB) - case .all, .one, .any, .not: + case .all, .one, .any, .not, .reference: return self } } @@ -585,3 +587,69 @@ public enum OpenAPICodableError: Swift.Error, Equatable { public enum OpenAPITypeError: Swift.Error, Equatable { case invalidNode } + +/// Anything conforming to RefName knows what to call itself +/// in the context of JSON References. +public protocol RefName { + static var refName: String { get } +} + +public protocol ReferenceRoot: RefName {} + +public protocol ReferenceDict: RefName {} + +/// A RefDict knows what to call itself (Name) and where to +/// look for itself (Root) and it stores a dictionary of +/// JSONNodes (some of which might be other references). +public struct RefDict: ReferenceDict, Equatable { + public static var refName: String { return Name.refName } + + public typealias RefType = JSONNode + + let dict: [String: RefType] + + public init(_ dict: [String: RefType]) { + self.dict = dict + } + + public subscript(_ key: String) -> RefType? { + return dict[key] + } +} + +/// A Reference is the combination of +/// a path to a reference dictionary +/// and a selector that the dictionary is keyed off of. +public struct JSONReference: Equatable { + public let path: PartialKeyPath + public let selector: String + + public var refName: String { + return (type(of: path).valueType as! ReferenceDict.Type).refName + } + + public init(type: KeyPath, + selector: String) { + self.path = type + self.selector = selector + } +} + +/// What the spec calls the "Components Object". +/// This is a place to put reusable components to +/// be referenced from other parts of the spec. +public struct OpenAPIComponents: Equatable, Encodable, ReferenceRoot { + public static var refName: String { return "components" } + + public let schemas: SchemasDict + + public init(schemas: [String: SchemasDict.RefType]) { + self.schemas = SchemasDict(schemas) + } + + public enum SchemasName: RefName { + public static var refName: String { return "schemas" } + } + + public typealias SchemasDict = RefDict +} From d1cf19f9fe918309b09a0c0650b4b45e528b0ceb Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Tue, 22 Jan 2019 22:14:36 -0800 Subject: [PATCH 2/5] Neck deep in stubbing out OpenAPI types --- .../JSONAPIOpenAPI/OpenAPI/OpenAPITypes.swift | 216 +++++++++++++++++- 1 file changed, 206 insertions(+), 10 deletions(-) diff --git a/Sources/JSONAPIOpenAPI/OpenAPI/OpenAPITypes.swift b/Sources/JSONAPIOpenAPI/OpenAPI/OpenAPITypes.swift index 87c67e7..6778e3b 100644 --- a/Sources/JSONAPIOpenAPI/OpenAPI/OpenAPITypes.swift +++ b/Sources/JSONAPIOpenAPI/OpenAPI/OpenAPITypes.swift @@ -250,7 +250,7 @@ public enum JSONNode: Equatable { indirect case one(of: [JSONNode]) indirect case any(of: [JSONNode]) indirect case not(JSONNode) - case reference(JSONReference) + case reference(JSONReference) public struct Context: JSONNodeContext, Equatable { public let format: Format @@ -596,15 +596,18 @@ public protocol RefName { public protocol ReferenceRoot: RefName {} -public protocol ReferenceDict: RefName {} +public protocol ReferenceDict: RefName { + associatedtype Value +} /// A RefDict knows what to call itself (Name) and where to /// look for itself (Root) and it stores a dictionary of /// JSONNodes (some of which might be other references). -public struct RefDict: ReferenceDict, Equatable { +public struct RefDict: ReferenceDict, Equatable { public static var refName: String { return Name.refName } - public typealias RefType = JSONNode + public typealias Value = RefType + public typealias Key = String let dict: [String: RefType] @@ -620,21 +623,145 @@ public struct RefDict: ReferenceDict, Equata /// A Reference is the combination of /// a path to a reference dictionary /// and a selector that the dictionary is keyed off of. -public struct JSONReference: Equatable { +public struct JSONReference: Equatable { public let path: PartialKeyPath public let selector: String public var refName: String { - return (type(of: path).valueType as! ReferenceDict.Type).refName + // we require RD be a RefName in the initializer + // so it is safe to force cast here. + return (type(of: path).valueType as! RefName.Type).refName } - public init(type: KeyPath, - selector: String) { + public init(type: KeyPath, + selector: String) where RD.Value == RefType { self.path = type self.selector = selector } } +/// An OpenAPI Path Item +/// This type describes the endpoints a server has +/// bound to a particular path. +public enum JSONPathItem: Equatable { + case reference(JSONReference) + case operations(PathProperties) + + public struct PathProperties: Equatable { + public let summary: String? + public let description: String? +// public let servers: + public let parameters: ParameterArray + + public let get: Operation? + public let put: Operation? + public let post: Operation? + public let delete: Operation? + public let options: Operation? + public let head: Operation? + public let patch: Operation? + public let trace: Operation? + + public init(summary: String? = nil, + description: String? = nil, + parameters: ParameterArray, + get: Operation? = nil, + put: Operation? = nil, + post: Operation? = nil, + delete: Operation? = nil, + options: Operation? = nil, + head: Operation? = nil, + patch: Operation? = nil, + trace: Operation? = nil) { + self.summary = summary + self.description = description + self.parameters = parameters + + self.get = get + self.put = put + self.post = post + self.delete = delete + self.options = options + self.head = head + self.patch = patch + self.trace = trace + } + + public typealias ParameterArray = [Either>] + + public struct Parameter: Equatable, Encodable { + private enum CodingKeys: String, CodingKey { + case name +// case parameterLocation = "in" + case description + case deprecated + } + + public let name: String +// public let parameterLocation: Location + public let description: String? + public let deprecated: Bool // default is false + // TODO: serialization rules + /* + Serialization Rules + */ + + public init(name: String, + description: String? = nil, + deprecated: Bool = false) { + self.name = name + self.description = description + self.deprecated = deprecated + } + +// public enum Location: Encodable { +// case query(required: Bool?) +// case header(required: Bool?) +// case path +// case cookie(required: Bool?) +// } + } + + public struct Operation: Equatable { + public let tags: [String]? + public let summary: String? + public let description: String? +// public let externalDocs: + public let operationId: String + public let parameters: ParameterArray +// public let requestBody: + public let responses: ResponseArray +// public let callbacks: + public let deprecated: Bool // default is false +// public let security: +// public let servers: + + public typealias ResponseArray = [OpenAPIResponse.Code: Either>] + } + } +} + +public struct OpenAPIResponse: Equatable { + public let description: String +// public let headers: + public let content: ContentMap +// public let links: + + public typealias ContentMap = [String: Content] + + public enum Code: Equatable, Hashable { + case `default` + case status(code: Int) + } + + public struct Content: Equatable { + public let schema: Either> +// public let example: +// public let examples: +// public let encoding: + } +} + /// What the spec calls the "Components Object". /// This is a place to put reusable components to /// be referenced from other parts of the spec. @@ -642,14 +769,83 @@ public struct OpenAPIComponents: Equatable, Encodable, ReferenceRoot { public static var refName: String { return "components" } public let schemas: SchemasDict +// public let responses: + public let parameters: ParametersDict +// public let examples: +// public let requestBodies: +// public let headers: +// public let headers: +// public let securitySchemas: +// public let links: +// public let callbacks: - public init(schemas: [String: SchemasDict.RefType]) { + public init(schemas: [String: SchemasDict.Value], parameters: [String: ParametersDict.Value]) { self.schemas = SchemasDict(schemas) + self.parameters = ParametersDict(parameters) } public enum SchemasName: RefName { public static var refName: String { return "schemas" } } - public typealias SchemasDict = RefDict + public typealias SchemasDict = RefDict + + public enum ParametersName: RefName { + public static var refName: String { return "parameters" } + } + + public typealias ParametersDict = RefDict +} + +/// The root of an OpenAPI 3.0 document. +public struct OpenAPISchema: Encodable { + private enum CodingKeys: String, CodingKey { + case openAPIVersion = "openapi" + case info + case components + } + + public let openAPIVersion: Version + public let info: Info +// public let servers: + public let paths: [PathComponents: JSONPathItem] + public let components: OpenAPIComponents +// public let security: +// public let tags: +// public let externalDocs: + + public init(openAPIVersion: Version = .v3_0_0, + info: Info, + paths: [PathComponents: JSONPathItem], + components: OpenAPIComponents) { + self.openAPIVersion = openAPIVersion + self.info = info + self.paths = paths + self.components = components + } + + public enum Version: String, Encodable { + case v3_0_0 = "3.0.0" + } + + public struct Info: Encodable { + public let title: String + public let description: String? + public let termsOfService: URL? +// public let contact: +// public let license: + public let version: String + + public init(title: String, + description: String? = nil, + termsOfService: URL? = nil, + version: String) { + self.title = title + self.description = description + self.termsOfService = termsOfService + self.version = version + } + } + + public typealias PathComponents = [String] } From ad05d3908a0a405dfb7bc027205525367bb9b1e8 Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Fri, 25 Jan 2019 12:49:59 -0800 Subject: [PATCH 3/5] Add a simple test OpenAPISchema and start to tweak things to get it workable. --- .../OpenAPI/OpenAPITypes+Codable.swift | 11 ++- .../JSONAPIOpenAPI/OpenAPI/OpenAPITypes.swift | 93 ++++++++++++++----- .../OpenAPI/OpenAPITests.swift | 52 +++++++++++ 3 files changed, 134 insertions(+), 22 deletions(-) create mode 100644 Tests/JSONAPIOpenAPITests/OpenAPI/OpenAPITests.swift diff --git a/Sources/JSONAPIOpenAPI/OpenAPI/OpenAPITypes+Codable.swift b/Sources/JSONAPIOpenAPI/OpenAPI/OpenAPITypes+Codable.swift index d6357c7..53d2261 100644 --- a/Sources/JSONAPIOpenAPI/OpenAPI/OpenAPITypes+Codable.swift +++ b/Sources/JSONAPIOpenAPI/OpenAPI/OpenAPITypes+Codable.swift @@ -203,7 +203,16 @@ extension JSONReference: Encodable { public func encode(to encoder: Encoder) throws { var container = encoder.singleValueContainer() - try container.encode("#/\(Root.refName)/\(refName)/\(selector)") + let referenceString: String = { + switch self { + case .file(let reference): + return reference + case .node(let reference): + return "#/\(Root.refName)/\(reference.refName)/\(reference.selector)" + } + }() + + try container.encode(referenceString) } } diff --git a/Sources/JSONAPIOpenAPI/OpenAPI/OpenAPITypes.swift b/Sources/JSONAPIOpenAPI/OpenAPI/OpenAPITypes.swift index c966a30..cf2327b 100644 --- a/Sources/JSONAPIOpenAPI/OpenAPI/OpenAPITypes.swift +++ b/Sources/JSONAPIOpenAPI/OpenAPI/OpenAPITypes.swift @@ -645,28 +645,36 @@ public struct RefDict: Equatable { - public let path: PartialKeyPath - public let selector: String +public enum JSONReference: Equatable { - public var refName: String { - // we require RD be a RefName in the initializer - // so it is safe to force cast here. - return (type(of: path).valueType as! RefName.Type).refName - } + case node(InternalReference) + case file(FileReference) - public init(type: KeyPath, - selector: String) where RD.Value == RefType { - self.path = type - self.selector = selector + public typealias FileReference = String + + public struct InternalReference: Equatable { + public let path: PartialKeyPath + public let selector: String + + public var refName: String { + // we require RD be a RefName in the initializer + // so it is safe to force cast here. + return (type(of: path).valueType as! RefName.Type).refName + } + + public init(type: KeyPath, + selector: String) where RD.Value == RefType { + self.path = type + self.selector = selector + } } } /// An OpenAPI Path Item /// This type describes the endpoints a server has /// bound to a particular path. -public enum JSONPathItem: Equatable { - case reference(JSONReference) +public enum OpenAPIPathItem: Equatable { + case reference(JSONReference) case operations(PathProperties) public struct PathProperties: Equatable { @@ -752,13 +760,29 @@ public enum JSONPathItem: Equatable { public let operationId: String public let parameters: ParameterArray // public let requestBody: - public let responses: ResponseArray + public let responses: ResponseMap // public let callbacks: public let deprecated: Bool // default is false // public let security: // public let servers: - public typealias ResponseArray = [OpenAPIResponse.Code: Either>] + public init(tags: [String]? = nil, + summary: String? = nil, + description: String? = nil, + operationId: String, + parameters: ParameterArray, + responses: ResponseMap, + deprecated: Bool = false) { + self.tags = tags + self.summary = summary + self.description = description + self.operationId = operationId + self.parameters = parameters + self.responses = responses + self.deprecated = deprecated + } + + public typealias ResponseMap = [OpenAPIResponse.Code: Either>] } } } @@ -769,18 +793,32 @@ public struct OpenAPIResponse: Equatable { public let content: ContentMap // public let links: - public typealias ContentMap = [String: Content] + public init(description: String, + content: ContentMap) { + self.description = description + self.content = content + } + + public typealias ContentMap = [ContentType: Content] public enum Code: Equatable, Hashable { case `default` case status(code: Int) } + public enum ContentType: String, Equatable, Hashable { + case json = "application/json" + } + public struct Content: Equatable { public let schema: Either> // public let example: // public let examples: // public let encoding: + + public init(schema: Either>) { + self.schema = schema + } } } @@ -816,7 +854,7 @@ public struct OpenAPIComponents: Equatable, Encodable, ReferenceRoot { public static var refName: String { return "parameters" } } - public typealias ParametersDict = RefDict + public typealias ParametersDict = RefDict } /// The root of an OpenAPI 3.0 document. @@ -824,13 +862,14 @@ public struct OpenAPISchema: Encodable { private enum CodingKeys: String, CodingKey { case openAPIVersion = "openapi" case info + case paths case components } public let openAPIVersion: Version public let info: Info // public let servers: - public let paths: [PathComponents: JSONPathItem] + public let paths: [PathComponents: OpenAPIPathItem] public let components: OpenAPIComponents // public let security: // public let tags: @@ -838,7 +877,7 @@ public struct OpenAPISchema: Encodable { public init(openAPIVersion: Version = .v3_0_0, info: Info, - paths: [PathComponents: JSONPathItem], + paths: [PathComponents: OpenAPIPathItem], components: OpenAPIComponents) { self.openAPIVersion = openAPIVersion self.info = info @@ -869,5 +908,17 @@ public struct OpenAPISchema: Encodable { } } - public typealias PathComponents = [String] + public struct PathComponents: Encodable, Equatable, Hashable { + public let components: [String] + + public init(_ components: [String]) { + self.components = components + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.singleValueContainer() + + try container.encode(components.joined(separator: "/")) + } + } } diff --git a/Tests/JSONAPIOpenAPITests/OpenAPI/OpenAPITests.swift b/Tests/JSONAPIOpenAPITests/OpenAPI/OpenAPITests.swift new file mode 100644 index 0000000..7410aa1 --- /dev/null +++ b/Tests/JSONAPIOpenAPITests/OpenAPI/OpenAPITests.swift @@ -0,0 +1,52 @@ +// +// OpenAPITests.swift +// JSONAPIOpenAPITests +// +// Created by Mathew Polzin on 1/25/19. +// + +import XCTest +import JSONAPI +import JSONAPIOpenAPI + +class OpenAPITests: XCTestCase { + + func test_placeholder() { + + let schemaInfo = OpenAPISchema.Info(title: "Cool API", version: "0.1.0") + + let personResponse = OpenAPIResponse(description: "Successfully created a Person", + content: [ + .json: .init(schema: .init(JSONReference.node(.init(type: \.schemas, selector: "person")))) + ]) + + let schemaPaths: [OpenAPISchema.PathComponents: OpenAPIPathItem] = [ + .init(["api","people"]): + .operations( + .init(parameters: [], + post: OpenAPIPathItem.PathProperties.Operation( + summary: "", + operationId: "createPerson", + parameters: [], + responses: [ + .status(code: 200): .init(personResponse) + ] + ) + ) + ) + ] + + let schemaComponents = OpenAPIComponents(schemas: ["person": .reference(.file("filename"))], + parameters: [:]) + + let openAPISchema = OpenAPISchema(info: schemaInfo, + paths: schemaPaths, + components: schemaComponents) + + let encoder = JSONEncoder() + encoder.outputFormatting = .prettyPrinted + + print(String(data: try! encoder.encode(openAPISchema), encoding: .utf8)!) + } + +} From cde10a8491418978c0d40dc92bce2cffea1e67f6 Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Fri, 25 Jan 2019 18:17:58 -0800 Subject: [PATCH 4/5] Finish implementing remaining first wave of encodable conformances for OpenAPI --- .../OpenAPI/OpenAPITypes+Codable.swift | 136 ++++++++++++++++++ .../JSONAPIOpenAPI/OpenAPI/OpenAPITypes.swift | 6 +- 2 files changed, 139 insertions(+), 3 deletions(-) diff --git a/Sources/JSONAPIOpenAPI/OpenAPI/OpenAPITypes+Codable.swift b/Sources/JSONAPIOpenAPI/OpenAPI/OpenAPITypes+Codable.swift index 53d2261..91a71cd 100644 --- a/Sources/JSONAPIOpenAPI/OpenAPI/OpenAPITypes+Codable.swift +++ b/Sources/JSONAPIOpenAPI/OpenAPI/OpenAPITypes+Codable.swift @@ -223,3 +223,139 @@ extension RefDict: Encodable { try container.encode(dict) } } + +extension OpenAPIResponse.Code: Encodable { + public func encode(to encoder: Encoder) throws { + var container = encoder.singleValueContainer() + + let string: String + switch self { + case .`default`: + string = "default" + + case .status(code: let code): + string = String(code) + } + + try container.encode(string) + } +} + +extension OpenAPIPathItem.PathProperties.Operation: Encodable { + private enum CodingKeys: String, CodingKey { + case tags + case summary + case description + case externalDocs + case operationId + case parameters + case requestBody + case responses + case callbacks + case deprecated + case security + case servers + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + + if tags != nil { + try container.encode(tags, forKey: .tags) + } + + if summary != nil { + try container.encode(summary, forKey: .summary) + } + + if description != nil { + try container.encode(description, forKey: .description) + } + + try container.encode(operationId, forKey: .operationId) + + try container.encode(parameters, forKey: .parameters) + + try container.encode(responses, forKey: .responses) + + try container.encode(deprecated, forKey: .deprecated) + } +} + +extension OpenAPIPathItem.PathProperties: Encodable { + private enum CodingKeys: String, CodingKey { + case summary + case description + case servers + case parameters + + case get + case put + case post + case delete + case options + case head + case patch + case trace + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + + if summary != nil { + try container.encode(summary, forKey: .summary) + } + + if description != nil { + try container.encode(description, forKey: .description) + } + + try container.encode(parameters, forKey: .parameters) + + if get != nil { + try container.encode(get, forKey: .get) + } + + if put != nil { + try container.encode(put, forKey: .put) + } + + if post != nil { + try container.encode(post, forKey: .post) + } + + if delete != nil { + try container.encode(delete, forKey: .delete) + } + + if options != nil { + try container.encode(options, forKey: .options) + } + + if head != nil { + try container.encode(head, forKey: .head) + } + + if patch != nil { + try container.encode(patch, forKey: .patch) + } + + if trace != nil { + try container.encode(trace, forKey: .trace) + } + } +} + +extension OpenAPIPathItem: Encodable { + public func encode(to encoder: Encoder) throws { + var container = encoder.singleValueContainer() + + switch self { + case .reference(let reference): + try container.encode(reference) + + case .operations(let operations): + try container.encode(operations) + } + } +} diff --git a/Sources/JSONAPIOpenAPI/OpenAPI/OpenAPITypes.swift b/Sources/JSONAPIOpenAPI/OpenAPI/OpenAPITypes.swift index cf2327b..faad090 100644 --- a/Sources/JSONAPIOpenAPI/OpenAPI/OpenAPITypes.swift +++ b/Sources/JSONAPIOpenAPI/OpenAPI/OpenAPITypes.swift @@ -787,7 +787,7 @@ public enum OpenAPIPathItem: Equatable { } } -public struct OpenAPIResponse: Equatable { +public struct OpenAPIResponse: Encodable, Equatable { public let description: String // public let headers: public let content: ContentMap @@ -806,11 +806,11 @@ public struct OpenAPIResponse: Equatable { case status(code: Int) } - public enum ContentType: String, Equatable, Hashable { + public enum ContentType: String, Encodable, Equatable, Hashable { case json = "application/json" } - public struct Content: Equatable { + public struct Content: Encodable, Equatable { public let schema: Either> // public let example: // public let examples: From c8421cdd588e6a48d19afdf3d147347b2a82008c Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Fri, 25 Jan 2019 18:20:04 -0800 Subject: [PATCH 5/5] just throw that json extension on the filename to make everything super real --- Tests/JSONAPIOpenAPITests/OpenAPI/OpenAPITests.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/JSONAPIOpenAPITests/OpenAPI/OpenAPITests.swift b/Tests/JSONAPIOpenAPITests/OpenAPI/OpenAPITests.swift index 7410aa1..62faad8 100644 --- a/Tests/JSONAPIOpenAPITests/OpenAPI/OpenAPITests.swift +++ b/Tests/JSONAPIOpenAPITests/OpenAPI/OpenAPITests.swift @@ -36,7 +36,7 @@ class OpenAPITests: XCTestCase { ) ] - let schemaComponents = OpenAPIComponents(schemas: ["person": .reference(.file("filename"))], + let schemaComponents = OpenAPIComponents(schemas: ["person": .reference(.file("person.json"))], parameters: [:]) let openAPISchema = OpenAPISchema(info: schemaInfo,