Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 41 additions & 1 deletion Sources/OpenAPIKit/Parameter/Parameter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ extension OpenAPI {
/// OpenAPI Spec "Parameter Object"
///
/// See [OpenAPI Parameter Object](https://spec.openapis.org/oas/v3.1.1.html#parameter-object).
public struct Parameter: Equatable, CodableVendorExtendable, Sendable {
public struct Parameter: HasConditionalWarnings, CodableVendorExtendable, Sendable {
public var name: String

/// OpenAPI Spec "in" property determines the `Context`.
Expand All @@ -32,6 +32,8 @@ extension OpenAPI {
/// where the values are anything codable.
public var vendorExtensions: [String: AnyCodable]

public let conditionalWarnings: [(any Condition, OpenAPI.Warning)]

/// Whether or not this parameter is required. See the context
/// which determines whether the parameter is required or not.
public var required: Bool { context.required }
Expand Down Expand Up @@ -71,6 +73,14 @@ extension OpenAPI {
}
}

/// The parameter's schema `style`, if defined. Note that this is
/// guaranteed to be nil if the parameter has `content` defined. Use
/// the `schemaOrContent` property if you want to switch over the two
/// possibilities.
public var schemaStyle : SchemaContext.Style? {
schemaOrContent.schemaContextValue?.style
}

/// Create a parameter.
public init(
name: String,
Expand All @@ -84,10 +94,38 @@ extension OpenAPI {
self.description = description
self.deprecated = deprecated
self.vendorExtensions = vendorExtensions

self.conditionalWarnings = context.location.conditionalWarnings
}
}
}

extension OpenAPI.Parameter: Equatable {
public static func == (_ lhs: Self, _ rhs: Self) -> Bool {
lhs.name == rhs.name
&& lhs.context == rhs.context
&& lhs.description == rhs.description
&& lhs.deprecated == rhs.deprecated
&& lhs.vendorExtensions == rhs.vendorExtensions
}
}

extension OpenAPI.Parameter.Context.Location {
fileprivate var conditionalWarnings: [(any Condition, OpenAPI.Warning)] {
let querystringWarning: (any Condition, OpenAPI.Warning)?
if self != .querystring {
querystringWarning = nil
} else {
querystringWarning = OpenAPI.Document.ConditionalWarnings.version(lessThan: .v3_2_0, doesNotSupport: "The querystring parameter location")
}


return [
querystringWarning
].compactMap { $0 }
}
}

extension OpenAPI.Parameter {
/// An array of parameters that are `Either` `Parameters` or references to parameters.
///
Expand Down Expand Up @@ -595,6 +633,8 @@ extension OpenAPI.Parameter: Decodable {
deprecated = try container.decodeIfPresent(Bool.self, forKey: .deprecated) ?? false

vendorExtensions = try Self.extensions(from: decoder)

conditionalWarnings = context.location.conditionalWarnings
}
}

Expand Down
49 changes: 48 additions & 1 deletion Sources/OpenAPIKit/Parameter/ParameterSchemaContext.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ extension OpenAPI.Parameter {
///
/// See [OpenAPI Parameter Object](https://spec.openapis.org/oas/v3.1.1.html#parameter-object)
/// and [OpenAPI Style Values](https://spec.openapis.org/oas/v3.1.1.html#style-values).
public struct SchemaContext: Equatable, Sendable {
public struct SchemaContext: HasConditionalWarnings, Sendable {
public var style: Style
public var explode: Bool
public var allowReserved: Bool //defaults to false
Expand All @@ -21,6 +21,8 @@ extension OpenAPI.Parameter {
public var example: AnyCodable?
public var examples: OpenAPI.Example.Map?

public let conditionalWarnings: [(any Condition, OpenAPI.Warning)]

public init(_ schema: JSONSchema,
style: Style,
explode: Bool,
Expand All @@ -32,6 +34,8 @@ extension OpenAPI.Parameter {
self.schema = .init(schema)
self.example = example
self.examples = nil

self.conditionalWarnings = style.conditionalWarnings
}

public init(_ schema: JSONSchema,
Expand All @@ -45,6 +49,8 @@ extension OpenAPI.Parameter {
self.examples = nil

self.explode = style.defaultExplode

self.conditionalWarnings = style.conditionalWarnings
}

public init(schemaReference: OpenAPI.Reference<JSONSchema>,
Expand All @@ -58,6 +64,8 @@ extension OpenAPI.Parameter {
self.schema = .init(schemaReference)
self.example = example
self.examples = nil

self.conditionalWarnings = style.conditionalWarnings
}

public init(schemaReference: OpenAPI.Reference<JSONSchema>,
Expand All @@ -71,6 +79,8 @@ extension OpenAPI.Parameter {
self.examples = nil

self.explode = style.defaultExplode

self.conditionalWarnings = style.conditionalWarnings
}

public init(_ schema: JSONSchema,
Expand All @@ -84,6 +94,8 @@ extension OpenAPI.Parameter {
self.schema = .init(schema)
self.examples = examples
self.example = examples.flatMap(OpenAPI.Content.firstExample(from:))

self.conditionalWarnings = style.conditionalWarnings
}

public init(_ schema: JSONSchema,
Expand All @@ -97,6 +109,8 @@ extension OpenAPI.Parameter {
self.example = examples.flatMap(OpenAPI.Content.firstExample(from:))

self.explode = style.defaultExplode

self.conditionalWarnings = style.conditionalWarnings
}

public init(schemaReference: OpenAPI.Reference<JSONSchema>,
Expand All @@ -110,6 +124,8 @@ extension OpenAPI.Parameter {
self.schema = .init(schemaReference)
self.examples = examples
self.example = examples.flatMap(OpenAPI.Content.firstExample(from:))

self.conditionalWarnings = style.conditionalWarnings
}

public init(schemaReference: OpenAPI.Reference<JSONSchema>,
Expand All @@ -123,10 +139,39 @@ extension OpenAPI.Parameter {
self.example = examples.flatMap(OpenAPI.Content.firstExample(from:))

self.explode = style.defaultExplode

self.conditionalWarnings = style.conditionalWarnings
}
}
}

extension OpenAPI.Parameter.SchemaContext.Style {
fileprivate var conditionalWarnings: [(any Condition, OpenAPI.Warning)] {
let cookieStyleWarning: (any Condition, OpenAPI.Warning)?
if self != .cookie {
cookieStyleWarning = nil
} else {
cookieStyleWarning = OpenAPI.Document.ConditionalWarnings.version(lessThan: .v3_2_0, doesNotSupport: "The cookie style")
}


return [
cookieStyleWarning
].compactMap { $0 }
}
}

extension OpenAPI.Parameter.SchemaContext: Equatable {
public static func == (_ lhs: Self, _ rhs: Self) -> Bool {
lhs.style == rhs.style
&& lhs.allowReserved == rhs.allowReserved
&& lhs.explode == rhs.explode
&& lhs.schema == rhs.schema
&& lhs.examples == rhs.examples
&& lhs.example == rhs.example
}
}

extension OpenAPI.Parameter.SchemaContext {
public static func schema(_ schema: JSONSchema,
style: Style,
Expand Down Expand Up @@ -278,6 +323,8 @@ extension OpenAPI.Parameter.SchemaContext {
examples = examplesMap
example = examplesMap.flatMap(OpenAPI.Content.firstExample(from:))
}

self.conditionalWarnings = style.conditionalWarnings
}
}

Expand Down
19 changes: 19 additions & 0 deletions Sources/OpenAPIKit/Parameter/ParameterSchemaContextStyle.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
//
// ParameterSchemaContextStyle.swift
//
//
// Created by Mathew Polzin on 12/18/22.
//

extension OpenAPI.Parameter.SchemaContext {
public enum Style: String, CaseIterable, Codable, Sendable {
case form
case simple
case matrix
case label
case spaceDelimited
case pipeDelimited
case deepObject
case cookie
}
}
52 changes: 52 additions & 0 deletions Sources/OpenAPIKit/Validator/Validation+Builtins.swift
Original file line number Diff line number Diff line change
Expand Up @@ -505,6 +505,58 @@ extension Validation {
}
)
}

/// Validate the OpenAPI Document's `Parameter`s all have styles that are
/// compatible with their locations per the table found at
/// https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.2.0.md#style-values
///
/// - Important: This is included in validation by default.
public static var parameterStyleAndLocationAreCompatible: Validation<OpenAPI.Parameter> {
.init(
check: all(
Validation<OpenAPI.Parameter>(
description: "the matrix style can only be used for the path location",
check: \.context.location == .path,
when: \.schemaStyle == .matrix
),
Validation<OpenAPI.Parameter>(
description: "the label style can only be used for the path location",
check: \.context.location == .path,
when: \.schemaStyle == .label
),
Validation<OpenAPI.Parameter>(
description: "the simple style can only be used for the path and header locations",
check: \.context.location == .path || \.context.location == .header,
when: \.schemaStyle == .simple
),
Validation<OpenAPI.Parameter>(
description: "the form style can only be used for the query and cookie locations",
check: \.context.location == .query || \.context.location == .cookie,
when: \.schemaStyle == .form
),
Validation<OpenAPI.Parameter>(
description: "the spaceDelimited style can only be used for the query location",
check: \.context.location == .query,
when: \.schemaStyle == .spaceDelimited
),
Validation<OpenAPI.Parameter>(
description: "the pipeDelimited style can only be used for the query location",
check: \.context.location == .query,
when: \.schemaStyle == .pipeDelimited
),
Validation<OpenAPI.Parameter>(
description: "the deepObject style can only be used for the query location",
check: \.context.location == .query,
when: \.schemaStyle == .deepObject
),
Validation<OpenAPI.Parameter>(
description: "the cookie style can only be used for the cookie location",
check: \.context.location == .cookie,
when: \.schemaStyle == .cookie
)
)
)
}
}

/// Used by both the Path Item parameter check and the
Expand Down
15 changes: 8 additions & 7 deletions Sources/OpenAPIKit/Validator/Validator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -170,12 +170,12 @@ public final class Validator {
/// - Parameters are unique within each Path Item.
/// - Parameters are unique within each Operation.
/// - Operation Ids are unique across the whole Document.
/// - All OpenAPI.References that refer to components in this
/// document can be found in the components dictionary.
/// - `Enum` must not be empty in the document's
/// Server Variable.
/// - `Default` must exist in the enum values in the document's
/// Server Variable.
/// - All OpenAPI.References that refer to components in this document can
/// be found in the components dictionary.
/// - `Enum` must not be empty in the document's Server Variable.
/// - `Default` must exist in the enum values in the document's Server
/// Variable.
/// - `Parameter` styles and locations are compatible with each other.
///
public convenience init() {
self.init(validations: [
Expand All @@ -193,7 +193,8 @@ public final class Validator {
.init(.callbacksReferencesAreValid),
.init(.pathItemReferencesAreValid),
.init(.serverVariableEnumIsValid),
.init(.serverVariableDefaultExistsInEnum)
.init(.serverVariableDefaultExistsInEnum),
.init(.parameterStyleAndLocationAreCompatible)
])
}

Expand Down
4 changes: 0 additions & 4 deletions Sources/OpenAPIKit/_CoreReExport.swift
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,6 @@ public extension OpenAPI.SecurityScheme {
typealias Location = OpenAPIKitCore.Shared.SecuritySchemeLocation
}

public extension OpenAPI.Parameter.SchemaContext {
typealias Style = OpenAPIKitCore.Shared.ParameterSchemaContextStyle
}

public extension OpenAPI.Response {
typealias StatusCode = OpenAPIKitCore.Shared.ResponseStatusCode
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
// Created by Mathew Polzin on 12/18/22.
//

extension Shared {
public enum ParameterSchemaContextStyle: String, CaseIterable, Codable, Sendable {
extension OpenAPI.Parameter.SchemaContext {
public enum Style: String, CaseIterable, Codable, Sendable {
case form
case simple
case matrix
Expand Down
4 changes: 0 additions & 4 deletions Sources/OpenAPIKit30/_CoreReExport.swift
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,6 @@ public extension OpenAPI.SecurityScheme {
typealias Location = OpenAPIKitCore.Shared.SecuritySchemeLocation
}

public extension OpenAPI.Parameter.SchemaContext {
typealias Style = OpenAPIKitCore.Shared.ParameterSchemaContextStyle
}

public extension OpenAPI.Response {
typealias StatusCode = OpenAPIKitCore.Shared.ResponseStatusCode
}
Expand Down
Loading