diff --git a/Sources/OpenAPIKit/Parameter/DereferencedParameter.swift b/Sources/OpenAPIKit/Parameter/DereferencedParameter.swift index 4b5002ca3..23c079e82 100644 --- a/Sources/OpenAPIKit/Parameter/DereferencedParameter.swift +++ b/Sources/OpenAPIKit/Parameter/DereferencedParameter.swift @@ -90,26 +90,59 @@ extension OpenAPI.Parameter: ExternallyDereferenceable { // next line: // let (newSchemaOrContent, components) = try await schemaOrContent.externallyDereferenced(with: loader) - let newSchemaOrContent: Either + let newContext: OpenAPI.Parameter.Context let newComponents: OpenAPI.Components let newMessages: [Loader.Message] - switch schemaOrContent { - case .a(let schemaContext): - let (context, components, messages) = try await schemaContext.externallyDereferenced(with: loader) - newSchemaOrContent = .a(context) - newComponents = components - newMessages = messages - case .b(let contentMap): - let (map, components, messages) = try await contentMap.externallyDereferenced(with: loader) - newSchemaOrContent = .b(map) - newComponents = components - newMessages = messages + switch context { + case .query(required: let required, allowEmptyValue: let allowEmptyValue, schemaOrContent: let schemaOrContent): + let newSchemaOrContent: Either + (newSchemaOrContent, newComponents, newMessages) = try await externallyDereference(schemaOrContent: schemaOrContent, with: Loader.self) + + newContext = .query(required: required, allowEmptyValue: allowEmptyValue, schemaOrContent: newSchemaOrContent) + + case .header(required: let required, schemaOrContent: let schemaOrContent): + let newSchemaOrContent: Either + (newSchemaOrContent, newComponents, newMessages) = try await externallyDereference(schemaOrContent: schemaOrContent, with: Loader.self) + + newContext = .header(required: required, schemaOrContent: newSchemaOrContent) + + case .path(schemaOrContent: let schemaOrContent): + let newSchemaOrContent: Either + (newSchemaOrContent, newComponents, newMessages) = try await externallyDereference(schemaOrContent: schemaOrContent, with: Loader.self) + + newContext = .path(schemaOrContent: newSchemaOrContent) + + case .cookie(required: let required, schemaOrContent: let schemaOrContent): + let newSchemaOrContent: Either + (newSchemaOrContent, newComponents, newMessages) = try await externallyDereference(schemaOrContent: schemaOrContent, with: Loader.self) + + newContext = .cookie(required: required, schemaOrContent: newSchemaOrContent) + + case .querystring(required: let required, content: let content): + let newContent: OpenAPI.Content.Map + (newContent, newComponents, newMessages) = try await content.externallyDereferenced(with: Loader.self) + + newContext = .querystring(required: required, content: newContent) } var newParameter = self - newParameter.schemaOrContent = newSchemaOrContent + newParameter.context = newContext return (newParameter, newComponents, newMessages) } } + +fileprivate func externallyDereference( + schemaOrContent: Either, + with loader: Loader.Type +) async throws -> (Either, OpenAPI.Components, [Loader.Message]) { + switch schemaOrContent { + case .a(let schemaContext): + let (context, components, messages) = try await schemaContext.externallyDereferenced(with: loader) + return (.a(context), components, messages) + case .b(let contentMap): + let (map, components, messages) = try await contentMap.externallyDereferenced(with: loader) + return (.b(map), components, messages) + } +} diff --git a/Sources/OpenAPIKit/Parameter/Parameter.swift b/Sources/OpenAPIKit/Parameter/Parameter.swift index 20b3843ad..1d0332a85 100644 --- a/Sources/OpenAPIKit/Parameter/Parameter.swift +++ b/Sources/OpenAPIKit/Parameter/Parameter.swift @@ -25,22 +25,6 @@ extension OpenAPI { /// if unspecified and only gets encoded if true. public var deprecated: Bool // default is false - /// OpenAPI Spec "content" or "schema" properties. - /// - /// You can access the schema context (if it is in use for - /// this parameter) with `schemaOrContent.schemaContextValue`. - /// The schema context contains lots of information detailed in the - /// OpenAPI specification under the **Parameter Object** section. - /// - /// You can directly access the underlying `JSONSchema` with - /// `schemaOrContent.schemaValue`. If the schema is a reference - /// instead of an inline value, `schemaOrContent.schemaReference` - /// will get you the reference. - /// - /// You can access the content map (if it is in use for - /// this parameter) with `schemaOrContent.contentValue`. - public var schemaOrContent: Either - /// Dictionary of vendor extensions. /// /// These should be of the form: @@ -58,88 +42,45 @@ extension OpenAPI { /// parameter. public var location: Context.Location { return context.location } - /// Create a parameter with an `Either`. - public init( - name: String, - context: Context, - schemaOrContent: Either, - description: String? = nil, - deprecated: Bool = false, - vendorExtensions: [String: AnyCodable] = [:] - ) { - self.name = name - self.context = context - self.schemaOrContent = schemaOrContent - self.description = description - self.deprecated = deprecated - self.vendorExtensions = vendorExtensions - } - - /// Create a parameter with a `SchemaContext`. - public init( - name: String, - context: Context, - schema: SchemaContext, - description: String? = nil, - deprecated: Bool = false, - vendorExtensions: [String: AnyCodable] = [:] - ) { - self.name = name - self.context = context - self.schemaOrContent = .init(schema) - self.description = description - self.deprecated = deprecated - self.vendorExtensions = vendorExtensions - } - - /// Create a parameter with a `JSONSchema` and the default - /// `style` for the given `Context`. - public init( - name: String, - context: Context, - schema: JSONSchema, - description: String? = nil, - deprecated: Bool = false, - vendorExtensions: [String: AnyCodable] = [:] - ) { - self.name = name - self.context = context - self.schemaOrContent = .init(SchemaContext(schema, style: .default(for: context))) - self.description = description - self.deprecated = deprecated - self.vendorExtensions = vendorExtensions - } - - /// Create a parameter with a reference to a `JSONSchema` - /// and the default `style` for the given `Context`. - public init( - name: String, - context: Context, - schemaReference: OpenAPI.Reference, - description: String? = nil, - deprecated: Bool = false, - vendorExtensions: [String: AnyCodable] = [:] - ) { - self.name = name - self.context = context - self.schemaOrContent = .init(SchemaContext(schemaReference: schemaReference, style: .default(for: context))) - self.description = description - self.deprecated = deprecated - self.vendorExtensions = vendorExtensions + /// OpenAPI Spec "content" or "schema" properties. + /// + /// You can access the schema context (if it is in use for + /// this parameter) with `schemaOrContent.schemaContextValue`. + /// The schema context contains lots of information detailed in the + /// OpenAPI specification under the **Parameter Object** section. + /// + /// You can directly access the underlying `JSONSchema` with + /// `schemaOrContent.schemaValue`. If the schema is a reference + /// instead of an inline value, `schemaOrContent.schemaReference` + /// will get you the reference. + /// + /// You can access the content map (if it is in use for + /// this parameter) with `schemaOrContent.contentValue`. + public var schemaOrContent: Either { + switch context { + case .query(required: _, allowEmptyValue: _, schemaOrContent: let schemaOrContent): + return schemaOrContent + case .header(required: _, schemaOrContent: let schemaOrContent): + return schemaOrContent + case .path(schemaOrContent: let schemaOrContent): + return schemaOrContent + case .cookie(required: _, schemaOrContent: let schemaOrContent): + return schemaOrContent + case .querystring(required: _, content: let content): + return .content(content) + } } - /// Create a parameter with a `Content.Map`. + /// Create a parameter. public init( name: String, context: Context, - content: OpenAPI.Content.Map, description: String? = nil, deprecated: Bool = false, vendorExtensions: [String: AnyCodable] = [:] ) { self.name = name self.context = context - self.schemaOrContent = .init(content) self.description = description self.deprecated = deprecated self.vendorExtensions = vendorExtensions @@ -157,6 +98,328 @@ extension OpenAPI.Parameter { public typealias Array = [Either, OpenAPI.Parameter>] } +// MARK: Convenience constructors +extension OpenAPI.Parameter { + public static func cookie( + name: String, + required: Bool = false, + schemaOrContent: Either, + description: String? = nil, + deprecated: Bool = false, + vendorExtensions: [String: AnyCodable] = [:] + ) -> Self { + .init( + name: name, + context: .cookie(required: required, schemaOrContent: schemaOrContent), + description: description, + deprecated: deprecated, + vendorExtensions: vendorExtensions + ) + } + + public static func cookie( + name: String, + required: Bool = false, + content: OpenAPI.Content.Map, + description: String? = nil, + deprecated: Bool = false, + vendorExtensions: [String: AnyCodable] = [:] + ) -> Self { + .init( + name: name, + context: .cookie( + required: required, + content: content + ), + description: description, + deprecated: deprecated, + vendorExtensions: vendorExtensions + ) + } + + public static func cookie( + name: String, + required: Bool = false, + schema: JSONSchema, + description: String? = nil, + deprecated: Bool = false, + vendorExtensions: [String: AnyCodable] = [:] + ) -> Self { + .init( + name: name, + context: .cookie( + required: required, + schema: schema + ), + description: description, + deprecated: deprecated, + vendorExtensions: vendorExtensions + ) + } + + public static func cookie( + name: String, + required: Bool = false, + schemaReference: OpenAPI.Reference, + description: String? = nil, + deprecated: Bool = false, + vendorExtensions: [String: AnyCodable] = [:] + ) -> Self { + .init( + name: name, + context: .cookie( + required: required, + schemaOrContent: .schema(.init(schemaReference: schemaReference, style: .default(for: .cookie))) + ), + description: description, + deprecated: deprecated, + vendorExtensions: vendorExtensions + ) + } + + public static func header( + name: String, + required: Bool = false, + schemaOrContent: Either, + description: String? = nil, + deprecated: Bool = false, + vendorExtensions: [String: AnyCodable] = [:] + ) -> Self { + .init( + name: name, + context: .header(required: required, schemaOrContent: schemaOrContent), + description: description, + deprecated: deprecated, + vendorExtensions: vendorExtensions + ) + } + + public static func header( + name: String, + required: Bool = false, + content: OpenAPI.Content.Map, + description: String? = nil, + deprecated: Bool = false, + vendorExtensions: [String: AnyCodable] = [:] + ) -> Self { + .init( + name: name, + context: .header( + required: required, + content: content + ), + description: description, + deprecated: deprecated, + vendorExtensions: vendorExtensions + ) + } + + public static func header( + name: String, + required: Bool = false, + schema: JSONSchema, + description: String? = nil, + deprecated: Bool = false, + vendorExtensions: [String: AnyCodable] = [:] + ) -> Self { + .init( + name: name, + context: .header( + required: required, + schema: schema + ), + description: description, + deprecated: deprecated, + vendorExtensions: vendorExtensions + ) + } + + public static func header( + name: String, + required: Bool = false, + schemaReference: OpenAPI.Reference, + description: String? = nil, + deprecated: Bool = false, + vendorExtensions: [String: AnyCodable] = [:] + ) -> Self { + .init( + name: name, + context: .header( + required: required, + schemaOrContent: .schema(.init(schemaReference: schemaReference, style: .default(for: .header))) + ), + description: description, + deprecated: deprecated, + vendorExtensions: vendorExtensions + ) + } + + public static func path( + name: String, + schemaOrContent: Either, + description: String? = nil, + deprecated: Bool = false, + vendorExtensions: [String: AnyCodable] = [:] + ) -> Self { + .init( + name: name, + context: .path(schemaOrContent: schemaOrContent), + description: description, + deprecated: deprecated, + vendorExtensions: vendorExtensions + ) + } + + public static func path( + name: String, + content: OpenAPI.Content.Map, + description: String? = nil, + deprecated: Bool = false, + vendorExtensions: [String: AnyCodable] = [:] + ) -> Self { + .init( + name: name, + context: .path(content: content), + description: description, + deprecated: deprecated, + vendorExtensions: vendorExtensions + ) + } + + public static func path( + name: String, + schema: JSONSchema, + description: String? = nil, + deprecated: Bool = false, + vendorExtensions: [String: AnyCodable] = [:] + ) -> Self { + .init( + name: name, + context: .path(schema: schema), + description: description, + deprecated: deprecated, + vendorExtensions: vendorExtensions + ) + } + + public static func path( + name: String, + schemaReference: OpenAPI.Reference, + description: String? = nil, + deprecated: Bool = false, + vendorExtensions: [String: AnyCodable] = [:] + ) -> Self { + .init( + name: name, + context: .path(schemaOrContent: .schema(.init(schemaReference: schemaReference, style: .default(for: .path)))), + description: description, + deprecated: deprecated, + vendorExtensions: vendorExtensions + ) + } + + public static func query( + name: String, + required: Bool = false, + allowEmptyValue: Bool = false, + schemaOrContent: Either, + description: String? = nil, + deprecated: Bool = false, + vendorExtensions: [String: AnyCodable] = [:] + ) -> Self { + .init( + name: name, + context: .query(required: required, allowEmptyValue: allowEmptyValue, schemaOrContent: schemaOrContent), + description: description, + deprecated: deprecated, + vendorExtensions: vendorExtensions + ) + } + + public static func query( + name: String, + required: Bool = false, + allowEmptyValue: Bool = false, + content: OpenAPI.Content.Map, + description: String? = nil, + deprecated: Bool = false, + vendorExtensions: [String: AnyCodable] = [:] + ) -> Self { + .init( + name: name, + context: .query( + required: required, + allowEmptyValue: allowEmptyValue, + content: content + ), + description: description, + deprecated: deprecated, + vendorExtensions: vendorExtensions + ) + } + + public static func query( + name: String, + required: Bool = false, + allowEmptyValue: Bool = false, + schema: JSONSchema, + description: String? = nil, + deprecated: Bool = false, + vendorExtensions: [String: AnyCodable] = [:] + ) -> Self { + .init( + name: name, + context: .query( + required: required, + allowEmptyValue: allowEmptyValue, + schema: schema + ), + description: description, + deprecated: deprecated, + vendorExtensions: vendorExtensions + ) + } + + public static func query( + name: String, + required: Bool = false, + allowEmptyValue: Bool = false, + schemaReference: OpenAPI.Reference, + description: String? = nil, + deprecated: Bool = false, + vendorExtensions: [String: AnyCodable] = [:] + ) -> Self { + .init( + name: name, + context: .query( + required: required, + allowEmptyValue: allowEmptyValue, + schemaOrContent: .schema(.init(schemaReference: schemaReference, style: .default(for: .query))) + ), + description: description, + deprecated: deprecated, + vendorExtensions: vendorExtensions + ) + } + + public static func querystring( + name: String, + required: Bool = false, + content: OpenAPI.Content.Map, + description: String? = nil, + deprecated: Bool = false, + vendorExtensions: [String: AnyCodable] = [:] + ) -> Self { + .init( + name: name, + context: .querystring(content: content), + description: description, + deprecated: deprecated, + vendorExtensions: vendorExtensions + ) + } +} + extension OpenAPI.Parameter { /// A parameter identity is just a hashable struct /// containing exactly the things that differentiate @@ -173,32 +436,10 @@ extension OpenAPI.Parameter { // OpenAPI.PathItem.Array.Element => extension Either where A == OpenAPI.Reference, B == OpenAPI.Parameter { - /// Construct a parameter using a `JSONSchema`. - public static func parameter( - name: String, - context: OpenAPI.Parameter.Context, - schema: JSONSchema, - description: String? = nil, - deprecated: Bool = false, - vendorExtensions: [String: AnyCodable] = [:] - ) -> Self { - return .b( - .init( - name: name, - context: context, - schema: schema, - description: description, - deprecated: deprecated, - vendorExtensions: vendorExtensions - ) - ) - } - - /// Construct a parameter using a `Content.Map`. + /// Construct a parameter. public static func parameter( name: String, context: OpenAPI.Parameter.Context, - content: OpenAPI.Content.Map, description: String? = nil, deprecated: Bool = false, vendorExtensions: [String: AnyCodable] = [:] @@ -207,7 +448,6 @@ extension Either where A == OpenAPI.Reference, B == OpenAPI.P .init( name: name, context: context, - content: content, description: description, deprecated: deprecated, vendorExtensions: vendorExtensions @@ -238,22 +478,25 @@ extension OpenAPI.Parameter: Encodable { let required: Bool let location: Context.Location switch context { - case .query(required: let req, allowEmptyValue: let allowEmptyValue): + case .query(required: let req, allowEmptyValue: let allowEmptyValue, schemaOrContent: _): required = req location = .query if allowEmptyValue { try container.encode(allowEmptyValue, forKey: .allowEmptyValue) } - case .header(required: let req): + case .header(required: let req, schemaOrContent: _): required = req location = .header - case .path: + case .path(schemaOrContent: _): required = true location = .path - case .cookie(required: let req): + case .cookie(required: let req, schemaOrContent: _): required = req location = .cookie + case .querystring(required: let req, content: _): + required = req + location = .querystring } try container.encode(location, forKey: .parameterLocation) @@ -263,7 +506,7 @@ extension OpenAPI.Parameter: Encodable { switch schemaOrContent { case .a(let schema): - try schema.encode(to: encoder, for: context) + try schema.encode(to: encoder, for: location) case .b(let contentMap): try container.encode(contentMap, forKey: .content) } @@ -290,34 +533,16 @@ extension OpenAPI.Parameter: Decodable { let required = try container.decodeIfPresent(Bool.self, forKey: .required) ?? false let location = try container.decode(Context.Location.self, forKey: .parameterLocation) - switch location { - case .query: - let allowEmptyValue = try container.decodeIfPresent(Bool.self, forKey: .allowEmptyValue) ?? false - context = .query(required: required, allowEmptyValue: allowEmptyValue) - case .header: - context = .header(required: required) - case .path: - if !required { - throw GenericError( - subjectName: name, - details: "positional path parameters must be explicitly set to required", - codingPath: decoder.codingPath - ) - } - context = .path - case .cookie: - context = .cookie(required: required) - } - let maybeContent = try container.decodeIfPresent(OpenAPI.Content.Map.self, forKey: .content) let maybeSchema: SchemaContext? if container.contains(.schema) { - maybeSchema = try SchemaContext(from: decoder, for: context) + maybeSchema = try SchemaContext(from: decoder, for: location) } else { maybeSchema = nil } + let schemaOrContent: Either switch (maybeContent, maybeSchema) { case (let content?, nil): schemaOrContent = .init(content) @@ -337,6 +562,34 @@ extension OpenAPI.Parameter: Decodable { ) } + switch location { + case .query: + let allowEmptyValue = try container.decodeIfPresent(Bool.self, forKey: .allowEmptyValue) ?? false + context = .query(required: required, allowEmptyValue: allowEmptyValue, schemaOrContent: schemaOrContent) + case .header: + context = .header(required: required, schemaOrContent: schemaOrContent) + case .path: + if !required { + throw GenericError( + subjectName: name, + details: "positional path parameters must be explicitly set to required", + codingPath: decoder.codingPath + ) + } + context = .path(schemaOrContent: schemaOrContent) + case .cookie: + context = .cookie(required: required, schemaOrContent: schemaOrContent) + case .querystring: + guard case .b(let content) = schemaOrContent else { + throw GenericError( + subjectName: name, + details: "`schema` and `style` are disallowed for `querystring` parameters", + codingPath: decoder.codingPath + ) + } + context = .querystring(required: required, content: content) + } + description = try container.decodeIfPresent(String.self, forKey: .description) deprecated = try container.decodeIfPresent(Bool.self, forKey: .deprecated) ?? false diff --git a/Sources/OpenAPIKit/Parameter/ParameterContext.swift b/Sources/OpenAPIKit/Parameter/ParameterContext.swift index d017b7c67..5b8ab5f5e 100644 --- a/Sources/OpenAPIKit/Parameter/ParameterContext.swift +++ b/Sources/OpenAPIKit/Parameter/ParameterContext.swift @@ -17,24 +17,78 @@ extension OpenAPI.Parameter { /// `required: true` to the context construction. /// Path parameters are always required. public enum Context: Equatable, Sendable { - case query(required: Bool, allowEmptyValue: Bool) - case header(required: Bool) - case path - case cookie(required: Bool) + case query(required: Bool, allowEmptyValue: Bool, schemaOrContent: Either) + case header(required: Bool, schemaOrContent: Either) + case path(schemaOrContent: Either) + case cookie(required: Bool, schemaOrContent: Either) + case querystring(required: Bool, content: OpenAPI.Content.Map) - public static func query(required: Bool) -> Context { return .query(required: required, allowEmptyValue: false) } + /// A query parameter that does not allow empty values. + public static func query( + required: Bool = false, + schemaOrContent: Either + ) -> Context { return .query(required: required, allowEmptyValue: false, schemaOrContent: schemaOrContent) } - public static func query(allowEmptyValue: Bool) -> Context { return .query(required: false, allowEmptyValue: allowEmptyValue) } + /// A query parameter that is not required. + public static func query( + allowEmptyValue: Bool, + schemaOrContent: Either + ) -> Context { return .query(required: false, allowEmptyValue: allowEmptyValue, schemaOrContent: schemaOrContent) } - /// An optional query parameter that does not allow - /// empty values. - public static var query: Context { return .query(required: false, allowEmptyValue: false) } + public static func query( + required: Bool = false, + allowEmptyValue: Bool = false, + schema: JSONSchema + ) -> Context { return .query(required: required, allowEmptyValue: allowEmptyValue, schemaOrContent: .schema(.init(schema, style: .default(for: .query)))) } + + public static func query( + required: Bool = false, + allowEmptyValue: Bool = false, + content: OpenAPI.Content.Map + ) -> Context { return .query(required: required, allowEmptyValue: allowEmptyValue, schemaOrContent: .content(content)) } /// An optional header parameter. - public static var header: Context { return .header(required: false) } + public static func header( + schemaOrContent: Either + ) -> Context { return .header(required: false, schemaOrContent: schemaOrContent) } + + public static func header( + required: Bool = false, + schema: JSONSchema + ) -> Context { return .header(required: required, schemaOrContent: .schema(.init(schema, style: .default(for: .header)))) } + + public static func header( + required: Bool = false, + content: OpenAPI.Content.Map + ) -> Context { return .header(required: required, schemaOrContent: .content(content)) } /// An optional cookie parameter. - public static var cookie: Context { return .cookie(required: false) } + public static func cookie( + schemaOrContent: Either + ) -> Context { return .cookie(required: false, schemaOrContent: schemaOrContent) } + + public static func cookie( + required: Bool = false, + schema: JSONSchema + ) -> Context { return .cookie(required: required, schemaOrContent: .schema(.init(schema, style: .default(for: .cookie)))) } + + public static func cookie( + required: Bool = false, + content: OpenAPI.Content.Map + ) -> Context { return .cookie(required: required, schemaOrContent: .content(content)) } + + public static func path( + schema: JSONSchema + ) -> Context { return .path(schemaOrContent: .schema(.init(schema, style: .default(for: .path)))) } + + public static func path( + content: OpenAPI.Content.Map + ) -> Context { return .path(schemaOrContent: .content(content)) } + + /// An optional querystring parameter. + public static func querystring( + content: OpenAPI.Content.Map + ) -> Context { return .querystring(required: false, content: content) } public var inQuery: Bool { guard case .query = self else { @@ -50,7 +104,12 @@ extension OpenAPI.Parameter { return true } - public var inPath: Bool { return self == .path } + public var inPath: Bool { + guard case .path = self else { + return false + } + return true + } public var inCookie: Bool { guard case .cookie = self else { @@ -59,13 +118,21 @@ extension OpenAPI.Parameter { return true } + public var inQuerystring: Bool { + guard case .querystring = self else { + return false + } + return true + } + public var required: Bool { switch self { - case .query(required: let required, allowEmptyValue: _), - .header(required: let required), - .cookie(required: let required): + case .query(required: let required, allowEmptyValue: _, schemaOrContent: _), + .header(required: let required, schemaOrContent: _), + .cookie(required: let required, schemaOrContent: _), + .querystring(required: let required, content: _): return required - case .path: + case .path(schemaOrContent: _): return true } } @@ -83,6 +150,8 @@ extension OpenAPI.Parameter.Context { return .path case .cookie: return .cookie + case .querystring: + return .querystring } } } diff --git a/Sources/OpenAPIKit/Parameter/ParameterContextLocation.swift b/Sources/OpenAPIKit/Parameter/ParameterContextLocation.swift new file mode 100644 index 000000000..7c5099b4d --- /dev/null +++ b/Sources/OpenAPIKit/Parameter/ParameterContextLocation.swift @@ -0,0 +1,20 @@ +// +// ParameterContextLocation.swift +// +// +// Created by Mathew Polzin on 12/24/22. +// + +import OpenAPIKitCore + +extension OpenAPI.Parameter.Context { + public enum Location: String, CaseIterable, Codable { + case query + case header + case path + case cookie + case querystring + } +} + +extension OpenAPI.Parameter.Context.Location: Validatable {} diff --git a/Sources/OpenAPIKit/Parameter/ParameterSchemaContext.swift b/Sources/OpenAPIKit/Parameter/ParameterSchemaContext.swift index f8828da2e..c3ef75243 100644 --- a/Sources/OpenAPIKit/Parameter/ParameterSchemaContext.swift +++ b/Sources/OpenAPIKit/Parameter/ParameterSchemaContext.swift @@ -127,13 +127,52 @@ extension OpenAPI.Parameter { } } +extension OpenAPI.Parameter.SchemaContext { + public static func schema(_ schema: JSONSchema, + style: Style, + explode: Bool, + allowReserved: Bool = false, + examples: OpenAPI.Example.Map? = nil) -> Self { + .init(schema, style: style, explode: explode, allowReserved: allowReserved, examples: examples) + } + + public static func schema(_ schema: JSONSchema, + style: Style, + allowReserved: Bool = false, + examples: OpenAPI.Example.Map? = nil) -> Self { + .init(schema, style: style, allowReserved: allowReserved, examples: examples) + } + + public static func schemaReference(_ reference: OpenAPI.Reference, + style: Style, + explode: Bool, + allowReserved: Bool = false, + examples: OpenAPI.Example.Map? = nil) -> Self { + .init(schemaReference: reference, + style: style, + explode: explode, + allowReserved: allowReserved, + examples: examples) + } + + public static func schemaReference(_ reference: OpenAPI.Reference, + style: Style, + allowReserved: Bool = false, + examples: OpenAPI.Example.Map? = nil) -> Self { + .init(schemaReference: reference, + style: style, + allowReserved: allowReserved, + examples: examples) + } +} + extension OpenAPI.Parameter.SchemaContext.Style { /// Get the default `Style` for the given location /// per the OpenAPI Specification. /// /// See the `style` fixed field under /// [OpenAPI Parameter Object](https://spec.openapis.org/oas/v3.1.1.html#parameter-object). - public static func `default`(for location: OpenAPI.Parameter.Context) -> Self { + public static func `default`(for location: OpenAPI.Parameter.Context.Location) -> Self { switch location { case .query: return .form @@ -143,6 +182,28 @@ extension OpenAPI.Parameter.SchemaContext.Style { return .simple case .header: return .simple + case .querystring: + return .simple + } + } + + /// Get the default `Style` for the given context + /// per the OpenAPI Specification. + /// + /// See the `style` fixed field under + /// [OpenAPI Parameter Object](https://spec.openapis.org/oas/v3.1.1.html#parameter-object). + public static func `default`(for context: OpenAPI.Parameter.Context) -> Self { + switch context { + case .query: + return .form + case .cookie: + return .form + case .path: + return .simple + case .header: + return .simple + case .querystring: + return .simple } } @@ -171,7 +232,7 @@ extension OpenAPI.Parameter.SchemaContext { } extension OpenAPI.Parameter.SchemaContext { - public func encode(to encoder: Encoder, for location: OpenAPI.Parameter.Context) throws { + public func encode(to encoder: Encoder, for location: OpenAPI.Parameter.Context.Location) throws { var container = encoder.container(keyedBy: CodingKeys.self) if style != Style.default(for: location) { @@ -197,7 +258,7 @@ extension OpenAPI.Parameter.SchemaContext { } extension OpenAPI.Parameter.SchemaContext { - public init(from decoder: Decoder, for location: OpenAPI.Parameter.Context) throws { + public init(from decoder: Decoder, for location: OpenAPI.Parameter.Context.Location) throws { let container = try decoder.container(keyedBy: CodingKeys.self) schema = try container.decode(Either, JSONSchema>.self, forKey: .schema) diff --git a/Sources/OpenAPIKit/_CoreReExport.swift b/Sources/OpenAPIKit/_CoreReExport.swift index a249fbc95..997063193 100644 --- a/Sources/OpenAPIKit/_CoreReExport.swift +++ b/Sources/OpenAPIKit/_CoreReExport.swift @@ -31,10 +31,6 @@ public extension OpenAPI.SecurityScheme { typealias Location = OpenAPIKitCore.Shared.SecuritySchemeLocation } -public extension OpenAPI.Parameter.Context { - typealias Location = OpenAPIKitCore.Shared.ParameterContextLocation -} - public extension OpenAPI.Parameter.SchemaContext { typealias Style = OpenAPIKitCore.Shared.ParameterSchemaContextStyle } diff --git a/Sources/OpenAPIKit30/Parameter/ParameterContextLocation.swift b/Sources/OpenAPIKit30/Parameter/ParameterContextLocation.swift new file mode 100644 index 000000000..89d323021 --- /dev/null +++ b/Sources/OpenAPIKit30/Parameter/ParameterContextLocation.swift @@ -0,0 +1,19 @@ +// +// ParameterContextLocation.swift +// +// +// Created by Mathew Polzin on 12/24/22. +// + +import OpenAPIKitCore + +extension OpenAPI.Parameter.Context { + public enum Location: String, CaseIterable, Codable { + case query + case header + case path + case cookie + } +} + +extension OpenAPI.Parameter.Context.Location: Validatable {} diff --git a/Sources/OpenAPIKit30/_CoreReExport.swift b/Sources/OpenAPIKit30/_CoreReExport.swift index a249fbc95..997063193 100644 --- a/Sources/OpenAPIKit30/_CoreReExport.swift +++ b/Sources/OpenAPIKit30/_CoreReExport.swift @@ -31,10 +31,6 @@ public extension OpenAPI.SecurityScheme { typealias Location = OpenAPIKitCore.Shared.SecuritySchemeLocation } -public extension OpenAPI.Parameter.Context { - typealias Location = OpenAPIKitCore.Shared.ParameterContextLocation -} - public extension OpenAPI.Parameter.SchemaContext { typealias Style = OpenAPIKitCore.Shared.ParameterSchemaContextStyle } diff --git a/Sources/OpenAPIKitCompat/Compat30To31.swift b/Sources/OpenAPIKitCompat/Compat30To31.swift index 92928524a..06b210672 100644 --- a/Sources/OpenAPIKitCompat/Compat30To31.swift +++ b/Sources/OpenAPIKitCompat/Compat30To31.swift @@ -100,16 +100,8 @@ extension OpenAPIKit30.OpenAPI.Server: To31 { extension OpenAPIKit30.OpenAPI.Header: To31 { fileprivate func to31() -> OpenAPIKit.OpenAPI.Header { - let newSchemaOrContent: Either - switch schemaOrContent { - case .a(let context): - newSchemaOrContent = .a(context.to31()) - case .b(let contentMap): - newSchemaOrContent = .b(contentMap.mapValues { $0.to31() }) - } - - return OpenAPIKit.OpenAPI.Header( - schemaOrContent: newSchemaOrContent, + OpenAPIKit.OpenAPI.Header( + schemaOrContent: schemaOrContent.to31(), description: description, required: `required`, deprecated: deprecated, @@ -118,17 +110,28 @@ extension OpenAPIKit30.OpenAPI.Header: To31 { } } -extension OpenAPIKit30.OpenAPI.Parameter.Context: To31 { - fileprivate func to31() -> OpenAPIKit.OpenAPI.Parameter.Context { +extension Either where A == OpenAPIKit30.OpenAPI.Parameter.SchemaContext, B == OpenAPIKit30.OpenAPI.Content.Map { + fileprivate func to31() -> Either { + switch self { + case .a(let context): + .a(context.to31()) + case .b(let contentMap): + .b(contentMap.mapValues { $0.to31() }) + } + } +} + +extension OpenAPIKit30.OpenAPI.Parameter.Context { + fileprivate func to31(with schemaOrContent: Either) -> OpenAPIKit.OpenAPI.Parameter.Context { switch self { case .query(required: let required, allowEmptyValue: let allowEmptyValue): - return .query(required: required, allowEmptyValue: allowEmptyValue) + return .query(required: required, allowEmptyValue: allowEmptyValue, schemaOrContent: schemaOrContent.to31()) case .header(required: let required): - return .header(required: required) + return .header(required: required, schemaOrContent: schemaOrContent.to31()) case .path: - return .path + return .path(schemaOrContent: schemaOrContent.to31()) case .cookie(required: let required): - return .cookie(required: required) + return .cookie(required: required, schemaOrContent: schemaOrContent.to31()) } } } @@ -242,18 +245,9 @@ extension OpenAPIKit30.OpenAPI.Content: To31 { extension OpenAPIKit30.OpenAPI.Parameter: To31 { fileprivate func to31() -> OpenAPIKit.OpenAPI.Parameter { - let newSchemaOrContent: Either - switch schemaOrContent { - case .a(let context): - newSchemaOrContent = .a(context.to31()) - case .b(let contentMap): - newSchemaOrContent = .b(contentMap.mapValues { $0.to31() }) - } - return OpenAPIKit.OpenAPI.Parameter( name: name, - context: context.to31(), - schemaOrContent: newSchemaOrContent, + context: context.to31(with: schemaOrContent), description: description, deprecated: deprecated, vendorExtensions: vendorExtensions diff --git a/Sources/OpenAPIKitCore/Shared/ParameterContextLocation.swift b/Sources/OpenAPIKitCore/Shared/ParameterContextLocation.swift deleted file mode 100644 index 1a36cc8e0..000000000 --- a/Sources/OpenAPIKitCore/Shared/ParameterContextLocation.swift +++ /dev/null @@ -1,17 +0,0 @@ -// -// ParameterContextLocation.swift -// -// -// Created by Mathew Polzin on 12/24/22. -// - -extension Shared { - public enum ParameterContextLocation: String, CaseIterable, Codable { - case query - case header - case path - case cookie - } -} - -extension Shared.ParameterContextLocation: Validatable {} diff --git a/Tests/OpenAPIKitCompatTests/DocumentConversionTests.swift b/Tests/OpenAPIKitCompatTests/DocumentConversionTests.swift index 100430d22..4f73d05d2 100644 --- a/Tests/OpenAPIKitCompatTests/DocumentConversionTests.swift +++ b/Tests/OpenAPIKitCompatTests/DocumentConversionTests.swift @@ -996,14 +996,14 @@ fileprivate func assertEqualNewToOld(_ newParam: OpenAPIKit.OpenAPI.Parameter, _ fileprivate func assertEqualNewToOld(_ newParamContext: OpenAPIKit.OpenAPI.Parameter.Context, _ oldParamContext: OpenAPIKit30.OpenAPI.Parameter.Context) { switch (newParamContext, oldParamContext) { - case (.query(required: let req, allowEmptyValue: let empty), .query(required: let req2, allowEmptyValue: let empty2)): + case (.query(required: let req, allowEmptyValue: let empty, schemaOrContent: _), .query(required: let req2, allowEmptyValue: let empty2)): XCTAssertEqual(req, req2) XCTAssertEqual(empty, empty2) - case (.header(required: let req), .header(required: let req2)): + case (.header(required: let req, schemaOrContent: _), .header(required: let req2)): XCTAssertEqual(req, req2) case (.path, .path): break - case (.cookie(required: let req), .cookie(required: let req2)): + case (.cookie(required: let req, schemaOrContent: _), .cookie(required: let req2)): XCTAssertEqual(req, req2) default: XCTFail("Parameter contexts are not equal. \(newParamContext) / \(oldParamContext)") @@ -1137,6 +1137,7 @@ fileprivate func assertEqualNewToOld(_ newSchema: OpenAPIKit.JSONSchema, _ oldSc case .number(let coreContext, let numericContext): let newNumericContext = try XCTUnwrap(newSchema.numberContext) // TODO: compare number contexts + // try assertEqualNewToOld(newNumericContext, numericContext) try assertEqualNewToOld(newCoreContext, coreContext) case .integer(let coreContext, let integerContext): diff --git a/Tests/OpenAPIKitTests/ComponentsTests.swift b/Tests/OpenAPIKitTests/ComponentsTests.swift index 0cbf875b7..8f603962b 100644 --- a/Tests/OpenAPIKitTests/ComponentsTests.swift +++ b/Tests/OpenAPIKitTests/ComponentsTests.swift @@ -99,7 +99,7 @@ final class ComponentsTests: XCTestCase { "two": .init(description: "hello", content: [:]) ], parameters: [ - "three": .init(name: "hello", context: .query, schema: .string) + "three": .init(name: "hello", context: .query(schema: .string)) ], examples: [ "four": .init(value: .init(URL(string: "hello.com/hello")!)) @@ -139,7 +139,7 @@ final class ComponentsTests: XCTestCase { XCTAssertEqual(components[ref1], .string) XCTAssertEqual(components[ref2], .init(description: "hello", content: [:])) - XCTAssertEqual(components[ref3], .init(name: "hello", context: .query, schema: .string)) + XCTAssertEqual(components[ref3], .init(name: "hello", context: .query(schema: .string))) XCTAssertEqual(components[ref4], .init(value: .init(URL(string: "hello.com/hello")!))) XCTAssertEqual(components[ref5], .init(content: [:])) XCTAssertEqual(components[ref6], .init(schema: .string)) @@ -284,7 +284,7 @@ extension ComponentsTests { "two": .init(description: "hello", content: [:]) ], parameters: [ - "three": .init(name: "hi", context: .query, content: [:]) + "three": .init(name: "hi", context: .query(content: [:])) ], examples: [ "four": .init(value: .init(URL(string: "http://address.com")!)) @@ -506,7 +506,7 @@ extension ComponentsTests { "two": .init(description: "hello", content: [:]) ], parameters: [ - "three": .init(name: "hi", context: .query, content: [:]) + "three": .init(name: "hi", context: .query(content: [:])) ], examples: [ "four": .init(value: .init(URL(string: "http://address.com")!)) diff --git a/Tests/OpenAPIKitTests/EaseOfUseTests.swift b/Tests/OpenAPIKitTests/EaseOfUseTests.swift index 45854c598..d8ecabe81 100644 --- a/Tests/OpenAPIKitTests/EaseOfUseTests.swift +++ b/Tests/OpenAPIKitTests/EaseOfUseTests.swift @@ -39,8 +39,7 @@ final class DeclarativeEaseOfUseTests: XCTestCase { parameters: [ .parameter( name: "param", - context: .path, - schema: .string + context: .path(schema: .string) ) ], get: .init( @@ -51,12 +50,14 @@ final class DeclarativeEaseOfUseTests: XCTestCase { .reference(.component( named: "filter")), .parameter( name: "Content-Type", - context: .header(required: false), - schema: .string( - allowedValues: [ - .init(OpenAPI.ContentType.json.rawValue), - .init(OpenAPI.ContentType.txt.rawValue) - ] + context: .header( + required: false, + schema: .string( + allowedValues: [ + .init(OpenAPI.ContentType.json.rawValue), + .init(OpenAPI.ContentType.txt.rawValue) + ] + ) ) ) ], @@ -116,16 +117,18 @@ final class DeclarativeEaseOfUseTests: XCTestCase { parameters: [ "filter": .init( name: "filter", - context: .query(required: false), - schema: .init( - .object( - properties: [ - "size": .integer, - "shape": .string(allowedValues: [ "round", "square" ]) - ] - ), - style: .deepObject, - explode: true + context: .query( + required: false, + schemaOrContent: .schema(.init( + .object( + properties: [ + "size": .integer, + "shape": .string(allowedValues: [ "round", "square" ]) + ] + ), + style: .deepObject, + explode: true + )) ) ) ] @@ -168,12 +171,13 @@ final class DeclarativeEaseOfUseTests: XCTestCase { .reference(.component( named: "filter")), .parameter( name: "Content-Type", - context: .header(required: false), - schema: .string( - allowedValues: [ - .init(OpenAPI.ContentType.json.rawValue), - .init(OpenAPI.ContentType.txt.rawValue) - ] + context: .header(required: false, + schema: .string( + allowedValues: [ + .init(OpenAPI.ContentType.json.rawValue), + .init(OpenAPI.ContentType.txt.rawValue) + ] + ) ) ) ], @@ -232,8 +236,7 @@ final class DeclarativeEaseOfUseTests: XCTestCase { parameters: [ .parameter( name: "param", - context: .path, - schema: .string + context: .path(schema: .string) ) ], get: testSHOW_endpoint, @@ -245,10 +248,10 @@ final class DeclarativeEaseOfUseTests: XCTestCase { "string_schema": .string ], parameters: [ - "filter": .init( + "filter": .query( name: "filter", - context: .query(required: false), - schema: .init( + required: false, + schemaOrContent: .schema(.init( .object( properties: [ "size": .integer, @@ -258,7 +261,7 @@ final class DeclarativeEaseOfUseTests: XCTestCase { style: .deepObject, explode: true ) - ) + )) ] ) @@ -517,8 +520,7 @@ fileprivate let testDocument = OpenAPI.Document( parameters: [ .parameter( name: "id", - context: .path, - schema: .string + context: .path(schema: .string) ) ], get: OpenAPI.Operation( diff --git a/Tests/OpenAPIKitTests/OpenAPIReferenceTests.swift b/Tests/OpenAPIKitTests/OpenAPIReferenceTests.swift index 925885bcc..b192b1bbe 100644 --- a/Tests/OpenAPIKitTests/OpenAPIReferenceTests.swift +++ b/Tests/OpenAPIKitTests/OpenAPIReferenceTests.swift @@ -84,7 +84,7 @@ final class OpenAPIReferenceTests: XCTestCase { "hello": .init(description: "description") ], parameters: [ - "hello": .init(name: "name", context: .path, content: [:], description: "description") + "hello": .path(name: "name", content: [:], description: "description") ], examples: [ "hello": .init(summary: "summary", description: "description", value: .b("")) diff --git a/Tests/OpenAPIKitTests/Operation/DereferencedOperationTests.swift b/Tests/OpenAPIKitTests/Operation/DereferencedOperationTests.swift index 611528269..1409cfa74 100644 --- a/Tests/OpenAPIKitTests/Operation/DereferencedOperationTests.swift +++ b/Tests/OpenAPIKitTests/Operation/DereferencedOperationTests.swift @@ -25,8 +25,7 @@ final class DereferencedOperationTests: XCTestCase { parameters: [ .parameter( name: "test", - context: .header, - schema: .string + context: .header(schema: .string) ) ], requestBody: OpenAPI.Request(content: [.json: .init(schema: .string)]), @@ -44,9 +43,8 @@ final class DereferencedOperationTests: XCTestCase { func test_parameterReference() throws { let components = OpenAPI.Components( parameters: [ - "test": .init( + "test": .header( name: "test", - context: .header, schema: .string ) ] @@ -59,9 +57,8 @@ final class DereferencedOperationTests: XCTestCase { ).dereferenced(in: components) XCTAssertEqual( t1.parameters.first?.underlyingParameter, - .init( + .header( name: "test", - context: .header, schema: .string, vendorExtensions: ["x-component-name": "test"] ) diff --git a/Tests/OpenAPIKitTests/Operation/OperationTests.swift b/Tests/OpenAPIKitTests/Operation/OperationTests.swift index a5bea626f..6cd175652 100644 --- a/Tests/OpenAPIKitTests/Operation/OperationTests.swift +++ b/Tests/OpenAPIKitTests/Operation/OperationTests.swift @@ -23,7 +23,7 @@ final class OperationTests: XCTestCase { description: "description", externalDocs: .init(url: URL(string: "https://google.com")!), operationId: "123", - parameters: [.parameter(name: "hi", context: .query, schema: .string)], + parameters: [.parameter(name: "hi", context: .query(schema: .string))], requestBody: .init(content: [:]), responses: [:], callbacks: [:], diff --git a/Tests/OpenAPIKitTests/Operation/ResolvedEndpointTests.swift b/Tests/OpenAPIKitTests/Operation/ResolvedEndpointTests.swift index 6cd40b9ef..dcac3c9bd 100644 --- a/Tests/OpenAPIKitTests/Operation/ResolvedEndpointTests.swift +++ b/Tests/OpenAPIKitTests/Operation/ResolvedEndpointTests.swift @@ -205,14 +205,14 @@ final class ResolvedEndpointTests: XCTestCase { "/hello/world": .init( summary: "routeSummary", description: "routeDescription", - parameters: [.parameter(name: "one", context: .header, schema: .string)], + parameters: [.parameter(name: "one", context: .header(schema: .string))], get: .init( tags: "a", "b", summary: "endpointSummary", description: "endpointDescription", externalDocs: .init(url: URL(string: "http://website.com")!), operationId: "hi there", - parameters: [.parameter(name: "two", context: .query, schema: .string)], + parameters: [.parameter(name: "two", context: .query(schema: .string))], requestBody: .init(description: "requestBody", content: [:]), responses: [200: .response(description: "hello world")], deprecated: true, @@ -243,14 +243,14 @@ final class ResolvedEndpointTests: XCTestCase { "/hello/world": .init( summary: "routeSummary", description: "routeDescription", - parameters: [.parameter(name: "one", context: .header, schema: .string)], + parameters: [.parameter(name: "one", context: .header(schema: .string))], get: .init( tags: "a", "b", summary: "endpointSummary", description: "endpointDescription", externalDocs: .init(url: URL(string: "http://website.com")!), operationId: "hi there", - parameters: [.parameter(name: "one", context: .header, schema: .integer)], + parameters: [.parameter(name: "one", context: .header(schema: .integer))], requestBody: .init(description: "requestBody", content: [:]), responses: [200: .response(description: "hello world")], deprecated: true, @@ -445,14 +445,14 @@ final class ResolvedEndpointTests: XCTestCase { "/hello/world": .init( summary: "routeSummary", description: "routeDescription", - parameters: [.parameter(name: "one", context: .header(required: true), schema: .string)], + parameters: [.parameter(name: "one", context: .header(required: true, schema: .string))], get: .init( tags: "a", "b", summary: "endpointSummary", description: "endpointDescription", externalDocs: .init(url: URL(string: "http://website.com")!), operationId: "hi there", - parameters: [.parameter(name: "two", context: .query, schema: .string)], + parameters: [.parameter(name: "two", context: .query(schema: .string))], requestBody: .init(description: "requestBody", content: [:]), responses: [200: .response(description: "hello world")], deprecated: true, diff --git a/Tests/OpenAPIKitTests/Parameter/DereferencedParameterTests.swift b/Tests/OpenAPIKitTests/Parameter/DereferencedParameterTests.swift index 126d3acc0..559491a89 100644 --- a/Tests/OpenAPIKitTests/Parameter/DereferencedParameterTests.swift +++ b/Tests/OpenAPIKitTests/Parameter/DereferencedParameterTests.swift @@ -10,14 +10,13 @@ import OpenAPIKit final class DereferencedParameterTests: XCTestCase { func test_inlineSchemaParameter() throws { - let t1 = try OpenAPI.Parameter( + let t1 = try OpenAPI.Parameter.header( name: "test", - context: .header, schema: .string ).dereferenced(in: .noComponents) XCTAssertEqual(t1.name, "test") - XCTAssertEqual(t1.context, .header) + XCTAssertEqual(t1.context, .header(schema: .string)) XCTAssertEqual( t1.schemaOrContent.schemaContextValue, try OpenAPI.Parameter.SchemaContext.header(.string).dereferenced(in: .noComponents) @@ -25,16 +24,17 @@ final class DereferencedParameterTests: XCTestCase { XCTAssertEqual(t1.schemaOrContent.schemaValue?.jsonSchema, .string) XCTAssertNil(t1.schemaOrContent.contentValue) - let t2 = try OpenAPI.Parameter( + let t2 = try OpenAPI.Parameter.path( name: "test2", - context: .path, content: [ .anyText: .init(schema: .string) ] ).dereferenced(in: .noComponents) XCTAssertEqual(t2.name, "test2") - XCTAssertEqual(t2.context, .path) + XCTAssertEqual(t2.context, .path(content: [ + .anyText: .init(schema: .string) + ])) XCTAssertEqual( t2.schemaOrContent.contentValue, [ @@ -46,9 +46,8 @@ final class DereferencedParameterTests: XCTestCase { } func test_inlineContentParameter() throws { - let t1 = try OpenAPI.Parameter( + let t1 = try OpenAPI.Parameter.header( name: "test", - context: .header, content: [ .json: .init(schema: .string) ] @@ -63,9 +62,8 @@ final class DereferencedParameterTests: XCTestCase { "test": .string ] ) - let t1 = try OpenAPI.Parameter( + let t1 = try OpenAPI.Parameter.header( name: "test", - context: .header, schemaReference: .component(named: "test") ).dereferenced(in: components) @@ -81,9 +79,8 @@ final class DereferencedParameterTests: XCTestCase { "test": .string ] ) - let t1 = try OpenAPI.Parameter( + let t1 = try OpenAPI.Parameter.header( name: "test", - context: .header, content: [.json: .init(schemaReference: .component(named: "test"))] ).dereferenced(in: components) diff --git a/Tests/OpenAPIKitTests/Parameter/ParameterContextTests.swift b/Tests/OpenAPIKitTests/Parameter/ParameterContextTests.swift index 1e75cd68f..ad93d2b0d 100644 --- a/Tests/OpenAPIKitTests/Parameter/ParameterContextTests.swift +++ b/Tests/OpenAPIKitTests/Parameter/ParameterContextTests.swift @@ -12,24 +12,24 @@ final class ParameterContextTests: XCTestCase { typealias Context = OpenAPI.Parameter.Context func test_query() { - let t1: Context = .query - XCTAssertEqual(t1, Context.query(required: false, allowEmptyValue: false)) + let t1: Context = .query(schema: .string) + XCTAssertEqual(t1, Context.query(required: false, allowEmptyValue: false, schema: .string)) XCTAssertFalse(t1.required) XCTAssertTrue(t1.inQuery) XCTAssertFalse(t1.inHeader) XCTAssertFalse(t1.inPath) XCTAssertFalse(t1.inCookie) - let t2: Context = .query(allowEmptyValue: true) - XCTAssertEqual(t2, Context.query(required: false, allowEmptyValue: true)) + let t2: Context = .query(allowEmptyValue: true, schema: .string) + XCTAssertEqual(t2, Context.query(required: false, allowEmptyValue: true, schema: .string)) XCTAssertFalse(t2.required) XCTAssertTrue(t2.inQuery) XCTAssertFalse(t2.inHeader) XCTAssertFalse(t2.inPath) XCTAssertFalse(t2.inCookie) - let t3: Context = .query(required: true) - XCTAssertEqual(t3, Context.query(required: true, allowEmptyValue: false)) + let t3: Context = .query(required: true, schema: .string) + XCTAssertEqual(t3, Context.query(required: true, allowEmptyValue: false, schema: .string)) XCTAssertTrue(t3.required) XCTAssertTrue(t3.inQuery) XCTAssertFalse(t3.inHeader) @@ -38,31 +38,31 @@ final class ParameterContextTests: XCTestCase { } func test_header() { - let t1: Context = .header - XCTAssertEqual(t1, Context.header(required: false)) + let t1: Context = .header(schema: .string) + XCTAssertEqual(t1, Context.header(required: false, schema: .string)) XCTAssertFalse(t1.required) XCTAssertTrue(t1.inHeader) XCTAssertFalse(t1.inQuery) XCTAssertFalse(t1.inPath) XCTAssertFalse(t1.inCookie) - XCTAssertTrue(Context.header(required: true).required) + XCTAssertTrue(Context.header(required: true, schema: .string).required) } func test_cookie() { - let t1: Context = .cookie - XCTAssertEqual(t1, Context.cookie(required: false)) + let t1: Context = .cookie(schema: .string) + XCTAssertEqual(t1, Context.cookie(required: false, schema: .string)) XCTAssertFalse(t1.required) XCTAssertTrue(t1.inCookie) XCTAssertFalse(t1.inQuery) XCTAssertFalse(t1.inPath) XCTAssertFalse(t1.inHeader) - XCTAssertTrue(Context.cookie(required: true).required) + XCTAssertTrue(Context.cookie(required: true, schema: .string).required) } func test_path() { - let t1: Context = .path + let t1: Context = .path(schema: .string) XCTAssertTrue(t1.required) XCTAssertTrue(t1.inPath) XCTAssertFalse(t1.inQuery) @@ -71,10 +71,10 @@ final class ParameterContextTests: XCTestCase { } func test_location() { - let t1: Context = .cookie - let t2: Context = .header - let t3: Context = .path - let t4: Context = .query + let t1: Context = .cookie(schema: .string) + let t2: Context = .header(schema: .string) + let t3: Context = .path(schema: .string) + let t4: Context = .query(schema: .string) XCTAssertEqual(t1.location, .cookie) XCTAssertEqual(t2.location, .header) diff --git a/Tests/OpenAPIKitTests/Parameter/ParameterSchemaTests.swift b/Tests/OpenAPIKitTests/Parameter/ParameterSchemaTests.swift index 35ea9da84..5300f8f3b 100644 --- a/Tests/OpenAPIKitTests/Parameter/ParameterSchemaTests.swift +++ b/Tests/OpenAPIKitTests/Parameter/ParameterSchemaTests.swift @@ -531,7 +531,7 @@ fileprivate struct SchemaWrapper: Codable { let location: TestLocation let schema: OpenAPI.Parameter.SchemaContext - init(location: OpenAPI.Parameter.Context, schema: OpenAPI.Parameter.SchemaContext) { + init(location: OpenAPI.Parameter.Context.Location, schema: OpenAPI.Parameter.SchemaContext) { self.location = .init(location) self.schema = schema } @@ -546,22 +546,25 @@ fileprivate struct SchemaWrapper: Codable { case header case path case cookie + case querystring - var paramLoc: OpenAPI.Parameter.Context { + var paramLoc: OpenAPI.Parameter.Context.Location { switch self { - case .query: return .query - case .header: return .header - case .path: return .path - case .cookie: return .cookie + case .query: .query + case .header: .header + case .path: .path + case .cookie: .cookie + case .querystring: .querystring } } - init(_ paramLoc: OpenAPI.Parameter.Context) { + init(_ paramLoc: OpenAPI.Parameter.Context.Location) { switch paramLoc { case .query: self = .query case .header: self = .header case .path: self = .path case .cookie: self = .cookie + case .querystring: self = .querystring } } } diff --git a/Tests/OpenAPIKitTests/Parameter/ParameterTests.swift b/Tests/OpenAPIKitTests/Parameter/ParameterTests.swift index 6e86c40aa..bb8bd8035 100644 --- a/Tests/OpenAPIKitTests/Parameter/ParameterTests.swift +++ b/Tests/OpenAPIKitTests/Parameter/ParameterTests.swift @@ -10,18 +10,18 @@ import OpenAPIKit final class ParameterTests: XCTestCase { func test_initialize() { - let t1 = OpenAPI.Parameter( + let t1 = OpenAPI.Parameter.cookie( name: "hello", - context: .cookie(required: true), + required: true, schemaOrContent: .init([.json: OpenAPI.Content(schema: .string)]), description: "hi", deprecated: true ) XCTAssertTrue(t1.required) - let t2 = OpenAPI.Parameter( + let t2 = OpenAPI.Parameter.cookie( name: "hello", - context: .cookie(required: true), + required: true, schemaOrContent: .content([.json: OpenAPI.Content(schema: .string)]), description: "hi", deprecated: true @@ -29,46 +29,21 @@ final class ParameterTests: XCTestCase { XCTAssertTrue(t2.deprecated) XCTAssertEqual(t1, t2) - let t4 = OpenAPI.Parameter( + let t6 = OpenAPI.Parameter.cookie( name: "hello", - context: .cookie(required: false), - schema: .init(.string, style: .default(for: .cookie)), - description: "hi", - deprecated: false - ) - XCTAssertFalse(t4.required) - - let t5 = OpenAPI.Parameter( - name: "hello", - context: .cookie, - schema: .string - ) - XCTAssertFalse(t5.deprecated) - - let t6 = OpenAPI.Parameter( - name: "hello", - context: .cookie, schemaOrContent: .schema(.init(.string, style: .default(for: .cookie))) ) - XCTAssertEqual(t5, t6) + XCTAssertFalse(t6.required) - let _ = OpenAPI.Parameter( + let _ = OpenAPI.Parameter.cookie( name: "hello", - context: .cookie, schemaReference: .component( named: "hello") ) - - let _ = OpenAPI.Parameter( - name: "hello", - context: .cookie, - content: [.json: OpenAPI.Content(schema: .string)] - ) } func test_schemaAccess() { - let t1 = OpenAPI.Parameter( + let t1 = OpenAPI.Parameter.cookie( name: "hello", - context: .cookie, schemaOrContent: .schema(.init(.string, style: .default(for: .cookie))) ) @@ -79,9 +54,8 @@ final class ParameterTests: XCTestCase { XCTAssertEqual(t1.schemaOrContent.schemaContextValue, .init(.string, style: .default(for: .cookie))) XCTAssertEqual(t1.schemaOrContent.schemaContextValue?.schema.schemaValue, t1.schemaOrContent.schemaValue) - let t2 = OpenAPI.Parameter( + let t2 = OpenAPI.Parameter.cookie( name: "hello", - context: .cookie, schemaReference: .component( named: "hello") ) @@ -91,9 +65,8 @@ final class ParameterTests: XCTestCase { XCTAssertEqual(t2.schemaOrContent.schemaReference, .component( named: "hello")) XCTAssertEqual(t2.schemaOrContent.schemaContextValue?.schema.reference, t2.schemaOrContent.schemaReference) - let t3 = OpenAPI.Parameter( + let t3 = OpenAPI.Parameter.path( name: "hello", - context: .path, content: [:] ) @@ -105,10 +78,10 @@ final class ParameterTests: XCTestCase { func test_parameterArray() { let t1: OpenAPI.Parameter.Array = [ - .parameter(OpenAPI.Parameter(name: "hello", context: .cookie, schema: .string)), - .parameter(name: "hello", context: .cookie, schema: .string), - .parameter(OpenAPI.Parameter(name: "hello", context: .cookie, content: [.json: OpenAPI.Content(schema: .string)])), - .parameter(name: "hello", context: .cookie, content: [.json: OpenAPI.Content(schema: .string)]), + .parameter(OpenAPI.Parameter.cookie(name: "hello", schema: .string)), + .parameter(name: "hello", context: .cookie(schema: .string)), + .parameter(OpenAPI.Parameter.cookie(name: "hello", content: [.json: OpenAPI.Content(schema: .string)])), + .parameter(name: "hello", context: .cookie(content: [.json: OpenAPI.Content(schema: .string)])), .reference(.component( named: "hello")) ] @@ -119,7 +92,7 @@ final class ParameterTests: XCTestCase { XCTAssertNotEqual(t1[4], t1[2]) XCTAssertNotEqual(t1[4], t1[3]) - XCTAssertEqual(t1[0].parameterValue, OpenAPI.Parameter(name: "hello", context: .cookie, schema: .string)) + XCTAssertEqual(t1[0].parameterValue, OpenAPI.Parameter.cookie(name: "hello", schema: .string)) XCTAssertEqual(t1[4].reference, .component( named: "hello")) } } @@ -127,9 +100,8 @@ final class ParameterTests: XCTestCase { // MARK: - Codable Tests extension ParameterTests { func test_minimalContent_encode() throws { - let parameter = OpenAPI.Parameter( + let parameter = OpenAPI.Parameter.path( name: "hello", - context: .path, content: [ .json: .init(schema: .string)] ) @@ -175,9 +147,8 @@ extension ParameterTests { XCTAssertEqual( parameter, - OpenAPI.Parameter( + OpenAPI.Parameter.path( name: "hello", - context: .path, content: [ .json: .init(schema: .string)] ) ) @@ -185,9 +156,8 @@ extension ParameterTests { } func test_minimalSchema_encode() throws { - let parameter = OpenAPI.Parameter( + let parameter = OpenAPI.Parameter.path( name: "hello", - context: .path, schema: .string ) @@ -225,18 +195,16 @@ extension ParameterTests { XCTAssertEqual( parameter, - OpenAPI.Parameter( + OpenAPI.Parameter.path( name: "hello", - context: .path, schema: .string ) ) } func test_queryParam_encode() throws { - let parameter = OpenAPI.Parameter( + let parameter = OpenAPI.Parameter.query( name: "hello", - context: .query, schema: .string ) @@ -273,9 +241,8 @@ extension ParameterTests { XCTAssertEqual(parameter.location, .query) XCTAssertEqual( parameter, - OpenAPI.Parameter( + OpenAPI.Parameter.query( name: "hello", - context: .query, schema: .string ) ) @@ -287,9 +254,9 @@ extension ParameterTests { } func test_queryParamAllowEmpty_encode() throws { - let parameter = OpenAPI.Parameter( + let parameter = OpenAPI.Parameter.query( name: "hello", - context: .query(allowEmptyValue: true), + allowEmptyValue: true, schema: .string ) @@ -327,18 +294,18 @@ extension ParameterTests { XCTAssertEqual( parameter, - OpenAPI.Parameter( + OpenAPI.Parameter.query( name: "hello", - context: .query(allowEmptyValue: true), + allowEmptyValue: true, schema: .string ) ) } func test_requiredQueryParam_encode() throws { - let parameter = OpenAPI.Parameter( + let parameter = OpenAPI.Parameter.query( name: "hello", - context: .query(required: true), + required: true, schema: .string ) @@ -376,18 +343,17 @@ extension ParameterTests { XCTAssertEqual( parameter, - OpenAPI.Parameter( + OpenAPI.Parameter.query( name: "hello", - context: .query(required: true), + required: true, schema: .string ) ) } func test_headerParam_encode() throws { - let parameter = OpenAPI.Parameter( + let parameter = OpenAPI.Parameter.header( name: "hello", - context: .header, schema: .string ) @@ -424,18 +390,17 @@ extension ParameterTests { XCTAssertEqual(parameter.location, .header) XCTAssertEqual( parameter, - OpenAPI.Parameter( + OpenAPI.Parameter.header( name: "hello", - context: .header, schema: .string ) ) } func test_requiredHeaderParam_encode() throws { - let parameter = OpenAPI.Parameter( + let parameter = OpenAPI.Parameter.header( name: "hello", - context: .header(required: true), + required: true, schema: .string ) @@ -473,18 +438,17 @@ extension ParameterTests { XCTAssertEqual( parameter, - OpenAPI.Parameter( + OpenAPI.Parameter.header( name: "hello", - context: .header(required: true), + required: true, schema: .string ) ) } func test_cookieParam_encode() throws { - let parameter = OpenAPI.Parameter( + let parameter = OpenAPI.Parameter.cookie( name: "hello", - context: .cookie, schema: .string ) @@ -521,18 +485,17 @@ extension ParameterTests { XCTAssertEqual(parameter.location, .cookie) XCTAssertEqual( parameter, - OpenAPI.Parameter( + OpenAPI.Parameter.cookie( name: "hello", - context: .cookie, schema: .string ) ) } func test_requiredCookieParam_encode() throws { - let parameter = OpenAPI.Parameter( + let parameter = OpenAPI.Parameter.cookie( name: "hello", - context: .cookie(required: true), + required: true, schema: .string ) @@ -570,18 +533,17 @@ extension ParameterTests { XCTAssertEqual( parameter, - OpenAPI.Parameter( + OpenAPI.Parameter.cookie( name: "hello", - context: .cookie(required: true), + required: true, schema: .string ) ) } func test_deprecated_encode() throws { - let parameter = OpenAPI.Parameter( + let parameter = OpenAPI.Parameter.path( name: "hello", - context: .path, schema: .string, deprecated: true ) @@ -622,9 +584,8 @@ extension ParameterTests { XCTAssertEqual( parameter, - OpenAPI.Parameter( + OpenAPI.Parameter.path( name: "hello", - context: .path, schema: .string, deprecated: true ) @@ -632,9 +593,8 @@ extension ParameterTests { } func test_description_encode() throws { - let parameter = OpenAPI.Parameter( + let parameter = OpenAPI.Parameter.path( name: "hello", - context: .path, schema: .string, description: "world" ) @@ -676,9 +636,8 @@ extension ParameterTests { XCTAssertEqual(parameter.location, .path) XCTAssertEqual( parameter, - OpenAPI.Parameter( + OpenAPI.Parameter.path( name: "hello", - context: .path, schema: .string, description: "world" ) @@ -686,13 +645,15 @@ extension ParameterTests { } func test_example_encode() throws { - let parameter = OpenAPI.Parameter( + let parameter = OpenAPI.Parameter.header( name: "hello", - context: .header(required: true), - schema: .init( - .string, - style: .default(for: .header), - example: "hello string" + required: true, + schemaOrContent: .schema( + .init( + .string, + style: .default(for: .header), + example: "hello string" + ) ) ) @@ -733,29 +694,33 @@ extension ParameterTests { XCTAssertEqual(parameter.location, .header) XCTAssertEqual( parameter, - OpenAPI.Parameter( + OpenAPI.Parameter.header( name: "hello", - context: .header(required: true), - schema: .init( - .string, - style: .default(for: .header), - example: "hello string" + required: true, + schemaOrContent: .schema( + .init( + .string, + style: .default(for: .header), + example: "hello string" + ) ) ) ) } func test_examples_encode() throws { - let parameter = OpenAPI.Parameter( + let parameter = OpenAPI.Parameter.header( name: "hello", - context: .header(required: true), - schema: .init( - .string, - style: .default(for: .header), - allowReserved: true, - examples: [ - "test": .example(value: .init(URL(string: "http://website.com")!)) - ] + required: true, + schemaOrContent: .schema( + .init( + .string, + style: .default(for: .header), + allowReserved: true, + examples: [ + "test": .example(value: .init(URL(string: "http://website.com")!)) + ] + ) ) ) @@ -806,25 +771,26 @@ extension ParameterTests { XCTAssertEqual(parameter.location, .header) XCTAssertEqual( parameter, - OpenAPI.Parameter( + OpenAPI.Parameter.header( name: "hello", - context: .header(required: true), - schema: .init( - .string, - style: .default(for: .header), - allowReserved: true, - examples: [ - "test": .example(value: .init(URL(string: "http://website.com")!)) - ] + required: true, + schemaOrContent: .schema( + .init( + .string, + style: .default(for: .header), + allowReserved: true, + examples: [ + "test": .example(value: .init(URL(string: "http://website.com")!)) + ] + ) ) ) ) } func test_vendorExtension_encode() throws { - let parameter = OpenAPI.Parameter( + let parameter = OpenAPI.Parameter.path( name: "hello", - context: .path, schema: .string, description: "world", vendorExtensions: ["x-specialFeature": .init(["hello", "world"])] @@ -875,9 +841,8 @@ extension ParameterTests { XCTAssertEqual(parameter.location, .path) XCTAssertEqual( parameter, - OpenAPI.Parameter( + OpenAPI.Parameter.path( name: "hello", - context: .path, schema: .string, description: "world", vendorExtensions: ["x-specialFeature": .init(["hello", "world"])] diff --git a/Tests/OpenAPIKitTests/Path Item/DereferencedPathItemTests.swift b/Tests/OpenAPIKitTests/Path Item/DereferencedPathItemTests.swift index 5ca4c9448..3a48d3f33 100644 --- a/Tests/OpenAPIKitTests/Path Item/DereferencedPathItemTests.swift +++ b/Tests/OpenAPIKitTests/Path Item/DereferencedPathItemTests.swift @@ -34,7 +34,7 @@ final class DereferencedPathItemTests: XCTestCase { func test_inlinedOperationsAndParameters() throws { let t1 = try OpenAPI.PathItem( parameters: [ - .parameter(name: "param", context: .header, schema: .string) + .parameter(name: "param", context: .header(schema: .string)) ], get: .init(tags: "get op", responses: [:]), put: .init(tags: "put op", responses: [:]), @@ -67,7 +67,7 @@ final class DereferencedPathItemTests: XCTestCase { func test_referencedParameter() throws { let components = OpenAPI.Components( parameters: [ - "test": .init(name: "param", context: .header, schema: .string) + "test": .init(name: "param", context: .header(schema: .string)) ] ) let t1 = try OpenAPI.PathItem( diff --git a/Tests/OpenAPIKitTests/Path Item/PathItemTests.swift b/Tests/OpenAPIKitTests/Path Item/PathItemTests.swift index 13087fbbd..367aff363 100644 --- a/Tests/OpenAPIKitTests/Path Item/PathItemTests.swift +++ b/Tests/OpenAPIKitTests/Path Item/PathItemTests.swift @@ -46,7 +46,7 @@ final class PathItemTests: XCTestCase { summary: "summary", description: "description", servers: [OpenAPI.Server(url: URL(string: "http://google.com")!)], - parameters: [.parameter(name: "hello", context: .query, schema: .string)], + parameters: [.parameter(name: "hello", context: .query(schema: .string))], get: op, put: op, post: op, @@ -159,7 +159,7 @@ final class PathItemTests: XCTestCase { summary: "summary", description: "description", servers: [OpenAPI.Server(url: URL(string: "http://google.com")!)], - parameters: [.parameter(name: "hello", context: .query, schema: .string)], + parameters: [.parameter(name: "hello", context: .query(schema: .string))], get: op, put: op, post: op, @@ -240,7 +240,7 @@ extension PathItemTests { summary: "summary", description: "description", servers: [OpenAPI.Server(url: URL(string: "http://google.com")!)], - parameters: [.parameter(name: "hello", context: .query, schema: .string)], + parameters: [.parameter(name: "hello", context: .query(schema: .string))], vendorExtensions: ["x-specialFeature": .init(["hello", "world"])] ) @@ -310,7 +310,7 @@ extension PathItemTests { summary: "summary", description: "description", servers: [OpenAPI.Server(url: URL(string: "http://google.com")!)], - parameters: [.parameter(name: "hello", context: .query, schema: .string)], + parameters: [.parameter(name: "hello", context: .query(schema: .string))], vendorExtensions: ["x-specialFeature": .init(["hello", "world"])] ) ) diff --git a/Tests/OpenAPIKitTests/Path Item/ResolvedRouteTests.swift b/Tests/OpenAPIKitTests/Path Item/ResolvedRouteTests.swift index 246fe1cf2..5b611aa9b 100644 --- a/Tests/OpenAPIKitTests/Path Item/ResolvedRouteTests.swift +++ b/Tests/OpenAPIKitTests/Path Item/ResolvedRouteTests.swift @@ -18,7 +18,7 @@ final class ResolvedRouteTests: XCTestCase { summary: "routeSummary", description: "routeDescription", servers: [], - parameters: [.parameter(name: "id", context: .path, schema: .integer)], + parameters: [.parameter(name: "id", context: .path(schema: .integer))], get: .init( summary: "get", responses: [200: .response(description: "hello world")] diff --git a/Tests/OpenAPIKitTests/Validator/BuiltinValidationTests.swift b/Tests/OpenAPIKitTests/Validator/BuiltinValidationTests.swift index a7a07cc51..25a761ae9 100644 --- a/Tests/OpenAPIKitTests/Validator/BuiltinValidationTests.swift +++ b/Tests/OpenAPIKitTests/Validator/BuiltinValidationTests.swift @@ -207,7 +207,7 @@ final class BuiltinValidationTests: XCTestCase { paths: [ "/hello/world/{idx}": .init( parameters: [ - .parameter(name: "idx", context: .path, schema: .string) + .parameter(name: "idx", context: .path(schema: .string)) ], get: .init( responses: [:] @@ -229,7 +229,7 @@ final class BuiltinValidationTests: XCTestCase { "/hello/world/{idx}": .init( get: .init( parameters: [ - .parameter(name: "idx", context: .path, schema: .string) + .parameter(name: "idx", context: .path(schema: .string)) ], responses: [:] ) @@ -496,8 +496,8 @@ final class BuiltinValidationTests: XCTestCase { "/hello": .init( get: .init( parameters: [ - .parameter(name: "hiya", context: .path, schema: .string), - .parameter(name: "hiya", context: .path, schema: .string) + .parameter(name: "hiya", context: .path(schema: .string)), + .parameter(name: "hiya", context: .path(schema: .string)) ], responses: [ 200: .response(description: "hi") @@ -524,9 +524,9 @@ final class BuiltinValidationTests: XCTestCase { "/hello": .init( get: .init( parameters: [ - .parameter(name: "hiya", context: .query, schema: .string), - .parameter(name: "hiya", context: .path, schema: .string), // changes parameter location but not name - .parameter(name: "cool", context: .path, schema: .string) // changes parameter name but not location + .parameter(name: "hiya", context: .query(schema: .string)), + .parameter(name: "hiya", context: .path(schema: .string)), // changes parameter location but not name + .parameter(name: "cool", context: .path(schema: .string)) // changes parameter name but not location ], responses: [ 200: .response(description: "hi") @@ -646,8 +646,8 @@ final class BuiltinValidationTests: XCTestCase { paths: [ "/hello": .init( parameters: [ - .parameter(name: "hiya", context: .query, schema: .string), - .parameter(name: "hiya", context: .query, schema: .string) + .parameter(name: "hiya", context: .query(schema: .string)), + .parameter(name: "hiya", context: .query(schema: .string)) ], get: .init( responses: [ @@ -674,9 +674,9 @@ final class BuiltinValidationTests: XCTestCase { paths: [ "/hello": .init( parameters: [ - .parameter(name: "hiya", context: .query, schema: .string), - .parameter(name: "hiya", context: .path, schema: .string), // changes parameter location but not name - .parameter(name: "cool", context: .path, schema: .string) // changes parameter name but not location + .parameter(name: "hiya", context: .query(schema: .string)), + .parameter(name: "hiya", context: .path(schema: .string)), // changes parameter location but not name + .parameter(name: "cool", context: .path(schema: .string)) // changes parameter name but not location ], get: .init( responses: [ @@ -845,7 +845,7 @@ final class BuiltinValidationTests: XCTestCase { "response1": .init(description: "test") ], parameters: [ - "parameter1": .init(name: "test", context: .header, schema: .string) + "parameter1": .init(name: "test", context: .header(schema: .string)) ], examples: [ "example1": .init(value: .b("hello")) diff --git a/Tests/OpenAPIKitTests/Validator/Validation+ConvenienceTests.swift b/Tests/OpenAPIKitTests/Validator/Validation+ConvenienceTests.swift index 61f028303..61ec37a7a 100644 --- a/Tests/OpenAPIKitTests/Validator/Validation+ConvenienceTests.swift +++ b/Tests/OpenAPIKitTests/Validator/Validation+ConvenienceTests.swift @@ -296,8 +296,8 @@ final class ValidationConvenienceTests: XCTestCase { ], components: .init( parameters: [ - "test1": .init(name: "test", context: .header, content: [:]), - "test2": .init(name: "test2", context: .query, content: [:]) + "test1": .init(name: "test", context: .header(content: [:])), + "test2": .init(name: "test2", context: .query(content: [:])) ] ) ) @@ -338,8 +338,8 @@ final class ValidationConvenienceTests: XCTestCase { ], components: .init( parameters: [ - "test1": .init(name: "test", context: .header, content: [:]), - "test2": .init(name: "test2", context: .query, content: [:]) + "test1": .init(name: "test", context: .header(content: [:])), + "test2": .init(name: "test2", context: .query(content: [:])) ] ) ) diff --git a/documentation/migration_guides/v5_migration_guide.md b/documentation/migration_guides/v5_migration_guide.md index 452cebf4e..cabb0b268 100644 --- a/documentation/migration_guides/v5_migration_guide.md +++ b/documentation/migration_guides/v5_migration_guide.md @@ -66,6 +66,89 @@ builtin methods so the following code _does not need to change_: let httpMethod : OpenAPI.HttpMethod = .post ``` +### Parameters +There are no breaking changes for the `OpenAPIKit30` module (OAS 3.0.x +specification). + +For the `OpenAPIKit` module (OAS 3.1.x and 3.2.x versions) read on. + +An additional parameter location of `querystring` has been added. This is a +breaking change to code that exhaustively switches on `OpenAPI.Parameter.Context` +or `OpenAPI.Parameter.Context.Location`. + +To support the new `querystring` location, `schemaOrContent` has been moved into +the `OpenAPI.Parameter.Context` because it only applies to locations other than +`querystring`. You can still access `schemaOrContent` as a property on the +`Parameter`. Code that pattern matches on cases of `OpenAPI.Parameter.Context` +will need to add the new `schemaOrContent` values associated with each case. + +```swift +// BEFORE +switch parameter.context { +case .query(required: _) +} + +// AFTER +switch parameter.context { +case .query(required: _, schemaOrContent: _) +} +``` + +#### Constructors +The following only applies if you construct parameters in-code (use Swift to +build an OpenAPI Document). + +Unfortunately, the change that made `schemaOrContent` not apply to all possible +locations means that the existing convenience constructors and static functions +that created parameters in-code do not make sense anymore. There were fairly +substantial changes to what is available with an aim to continue to offer +simular convenience as before. + +Following are a few changes you made need to make with examples. + +Code that populates the `parameters` array of the `OpenAPI.Operation` type with the +`.parameter(name:,context:,schema:)` function needs to be updated. The `schema` +has moved into the `context` so you change your code in the following way: +```swift +// BEFORE +.parameter( + name: "name", + context: .header, + schema: .string +) + +// AFTER +.parameter( + name: "name", + context: .header(schema: .string) +) +``` + +Code that initializes `OpenAPI.Parameter` via one of its `init` functions will +most likely need to change. Many of the initializers have been removed but you can +replace `.init(name:,context:,schema:)` or similar initializers with +`.header(name:,schema:)` (same goes for `query`, `path`, and `cookie`). So you change +your code in the following way: +```swift +// BEFORE +.init( + name: "name", + context: .header, + schema: .string +) + +// AFTER +.header( + name: "name", + schema: .string +) +``` + +Because the `ParameterContext` has taken on the `schemaOrContent` of the +`Parameter`, convenience constructors like `ParameterContext.header` (and +similar for the other locations) no longer make sense and have been removed. You +must also specify the schema or content, e.g. `ParameterContext.header(schema: .string)`. + ### Errors Some error messages have been tweaked in small ways. If you match on the string descriptions of any OpenAPIKit errors, you may need to update the