Skip to content

Commit 8fbc91b

Browse files
authored
Merge pull request #452 from mattpolzin/feature/443/cookie-style
Add OAS 3.2.0 cookie style
2 parents 0c9df16 + 72cc847 commit 8fbc91b

File tree

14 files changed

+486
-27
lines changed

14 files changed

+486
-27
lines changed

Sources/OpenAPIKit/Parameter/Parameter.swift

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ extension OpenAPI {
1111
/// OpenAPI Spec "Parameter Object"
1212
///
1313
/// See [OpenAPI Parameter Object](https://spec.openapis.org/oas/v3.1.1.html#parameter-object).
14-
public struct Parameter: Equatable, CodableVendorExtendable, Sendable {
14+
public struct Parameter: HasConditionalWarnings, CodableVendorExtendable, Sendable {
1515
public var name: String
1616

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

35+
public let conditionalWarnings: [(any Condition, OpenAPI.Warning)]
36+
3537
/// Whether or not this parameter is required. See the context
3638
/// which determines whether the parameter is required or not.
3739
public var required: Bool { context.required }
@@ -71,6 +73,14 @@ extension OpenAPI {
7173
}
7274
}
7375

76+
/// The parameter's schema `style`, if defined. Note that this is
77+
/// guaranteed to be nil if the parameter has `content` defined. Use
78+
/// the `schemaOrContent` property if you want to switch over the two
79+
/// possibilities.
80+
public var schemaStyle : SchemaContext.Style? {
81+
schemaOrContent.schemaContextValue?.style
82+
}
83+
7484
/// Create a parameter.
7585
public init(
7686
name: String,
@@ -84,10 +94,38 @@ extension OpenAPI {
8494
self.description = description
8595
self.deprecated = deprecated
8696
self.vendorExtensions = vendorExtensions
97+
98+
self.conditionalWarnings = context.location.conditionalWarnings
8799
}
88100
}
89101
}
90102

103+
extension OpenAPI.Parameter: Equatable {
104+
public static func == (_ lhs: Self, _ rhs: Self) -> Bool {
105+
lhs.name == rhs.name
106+
&& lhs.context == rhs.context
107+
&& lhs.description == rhs.description
108+
&& lhs.deprecated == rhs.deprecated
109+
&& lhs.vendorExtensions == rhs.vendorExtensions
110+
}
111+
}
112+
113+
extension OpenAPI.Parameter.Context.Location {
114+
fileprivate var conditionalWarnings: [(any Condition, OpenAPI.Warning)] {
115+
let querystringWarning: (any Condition, OpenAPI.Warning)?
116+
if self != .querystring {
117+
querystringWarning = nil
118+
} else {
119+
querystringWarning = OpenAPI.Document.ConditionalWarnings.version(lessThan: .v3_2_0, doesNotSupport: "The querystring parameter location")
120+
}
121+
122+
123+
return [
124+
querystringWarning
125+
].compactMap { $0 }
126+
}
127+
}
128+
91129
extension OpenAPI.Parameter {
92130
/// An array of parameters that are `Either` `Parameters` or references to parameters.
93131
///
@@ -595,6 +633,8 @@ extension OpenAPI.Parameter: Decodable {
595633
deprecated = try container.decodeIfPresent(Bool.self, forKey: .deprecated) ?? false
596634

597635
vendorExtensions = try Self.extensions(from: decoder)
636+
637+
conditionalWarnings = context.location.conditionalWarnings
598638
}
599639
}
600640

Sources/OpenAPIKit/Parameter/ParameterSchemaContext.swift

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ extension OpenAPI.Parameter {
1212
///
1313
/// See [OpenAPI Parameter Object](https://spec.openapis.org/oas/v3.1.1.html#parameter-object)
1414
/// and [OpenAPI Style Values](https://spec.openapis.org/oas/v3.1.1.html#style-values).
15-
public struct SchemaContext: Equatable, Sendable {
15+
public struct SchemaContext: HasConditionalWarnings, Sendable {
1616
public var style: Style
1717
public var explode: Bool
1818
public var allowReserved: Bool //defaults to false
@@ -21,6 +21,8 @@ extension OpenAPI.Parameter {
2121
public var example: AnyCodable?
2222
public var examples: OpenAPI.Example.Map?
2323

24+
public let conditionalWarnings: [(any Condition, OpenAPI.Warning)]
25+
2426
public init(_ schema: JSONSchema,
2527
style: Style,
2628
explode: Bool,
@@ -32,6 +34,8 @@ extension OpenAPI.Parameter {
3234
self.schema = .init(schema)
3335
self.example = example
3436
self.examples = nil
37+
38+
self.conditionalWarnings = style.conditionalWarnings
3539
}
3640

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

4751
self.explode = style.defaultExplode
52+
53+
self.conditionalWarnings = style.conditionalWarnings
4854
}
4955

5056
public init(schemaReference: OpenAPI.Reference<JSONSchema>,
@@ -58,6 +64,8 @@ extension OpenAPI.Parameter {
5864
self.schema = .init(schemaReference)
5965
self.example = example
6066
self.examples = nil
67+
68+
self.conditionalWarnings = style.conditionalWarnings
6169
}
6270

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

7381
self.explode = style.defaultExplode
82+
83+
self.conditionalWarnings = style.conditionalWarnings
7484
}
7585

7686
public init(_ schema: JSONSchema,
@@ -84,6 +94,8 @@ extension OpenAPI.Parameter {
8494
self.schema = .init(schema)
8595
self.examples = examples
8696
self.example = examples.flatMap(OpenAPI.Content.firstExample(from:))
97+
98+
self.conditionalWarnings = style.conditionalWarnings
8799
}
88100

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

99111
self.explode = style.defaultExplode
112+
113+
self.conditionalWarnings = style.conditionalWarnings
100114
}
101115

102116
public init(schemaReference: OpenAPI.Reference<JSONSchema>,
@@ -110,6 +124,8 @@ extension OpenAPI.Parameter {
110124
self.schema = .init(schemaReference)
111125
self.examples = examples
112126
self.example = examples.flatMap(OpenAPI.Content.firstExample(from:))
127+
128+
self.conditionalWarnings = style.conditionalWarnings
113129
}
114130

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

125141
self.explode = style.defaultExplode
142+
143+
self.conditionalWarnings = style.conditionalWarnings
126144
}
127145
}
128146
}
129147

148+
extension OpenAPI.Parameter.SchemaContext.Style {
149+
fileprivate var conditionalWarnings: [(any Condition, OpenAPI.Warning)] {
150+
let cookieStyleWarning: (any Condition, OpenAPI.Warning)?
151+
if self != .cookie {
152+
cookieStyleWarning = nil
153+
} else {
154+
cookieStyleWarning = OpenAPI.Document.ConditionalWarnings.version(lessThan: .v3_2_0, doesNotSupport: "The cookie style")
155+
}
156+
157+
158+
return [
159+
cookieStyleWarning
160+
].compactMap { $0 }
161+
}
162+
}
163+
164+
extension OpenAPI.Parameter.SchemaContext: Equatable {
165+
public static func == (_ lhs: Self, _ rhs: Self) -> Bool {
166+
lhs.style == rhs.style
167+
&& lhs.allowReserved == rhs.allowReserved
168+
&& lhs.explode == rhs.explode
169+
&& lhs.schema == rhs.schema
170+
&& lhs.examples == rhs.examples
171+
&& lhs.example == rhs.example
172+
}
173+
}
174+
130175
extension OpenAPI.Parameter.SchemaContext {
131176
public static func schema(_ schema: JSONSchema,
132177
style: Style,
@@ -278,6 +323,8 @@ extension OpenAPI.Parameter.SchemaContext {
278323
examples = examplesMap
279324
example = examplesMap.flatMap(OpenAPI.Content.firstExample(from:))
280325
}
326+
327+
self.conditionalWarnings = style.conditionalWarnings
281328
}
282329
}
283330

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
//
2+
// ParameterSchemaContextStyle.swift
3+
//
4+
//
5+
// Created by Mathew Polzin on 12/18/22.
6+
//
7+
8+
extension OpenAPI.Parameter.SchemaContext {
9+
public enum Style: String, CaseIterable, Codable, Sendable {
10+
case form
11+
case simple
12+
case matrix
13+
case label
14+
case spaceDelimited
15+
case pipeDelimited
16+
case deepObject
17+
case cookie
18+
}
19+
}

Sources/OpenAPIKit/Validator/Validation+Builtins.swift

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -505,6 +505,58 @@ extension Validation {
505505
}
506506
)
507507
}
508+
509+
/// Validate the OpenAPI Document's `Parameter`s all have styles that are
510+
/// compatible with their locations per the table found at
511+
/// https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.2.0.md#style-values
512+
///
513+
/// - Important: This is included in validation by default.
514+
public static var parameterStyleAndLocationAreCompatible: Validation<OpenAPI.Parameter> {
515+
.init(
516+
check: all(
517+
Validation<OpenAPI.Parameter>(
518+
description: "the matrix style can only be used for the path location",
519+
check: \.context.location == .path,
520+
when: \.schemaStyle == .matrix
521+
),
522+
Validation<OpenAPI.Parameter>(
523+
description: "the label style can only be used for the path location",
524+
check: \.context.location == .path,
525+
when: \.schemaStyle == .label
526+
),
527+
Validation<OpenAPI.Parameter>(
528+
description: "the simple style can only be used for the path and header locations",
529+
check: \.context.location == .path || \.context.location == .header,
530+
when: \.schemaStyle == .simple
531+
),
532+
Validation<OpenAPI.Parameter>(
533+
description: "the form style can only be used for the query and cookie locations",
534+
check: \.context.location == .query || \.context.location == .cookie,
535+
when: \.schemaStyle == .form
536+
),
537+
Validation<OpenAPI.Parameter>(
538+
description: "the spaceDelimited style can only be used for the query location",
539+
check: \.context.location == .query,
540+
when: \.schemaStyle == .spaceDelimited
541+
),
542+
Validation<OpenAPI.Parameter>(
543+
description: "the pipeDelimited style can only be used for the query location",
544+
check: \.context.location == .query,
545+
when: \.schemaStyle == .pipeDelimited
546+
),
547+
Validation<OpenAPI.Parameter>(
548+
description: "the deepObject style can only be used for the query location",
549+
check: \.context.location == .query,
550+
when: \.schemaStyle == .deepObject
551+
),
552+
Validation<OpenAPI.Parameter>(
553+
description: "the cookie style can only be used for the cookie location",
554+
check: \.context.location == .cookie,
555+
when: \.schemaStyle == .cookie
556+
)
557+
)
558+
)
559+
}
508560
}
509561

510562
/// Used by both the Path Item parameter check and the

Sources/OpenAPIKit/Validator/Validator.swift

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -170,12 +170,12 @@ public final class Validator {
170170
/// - Parameters are unique within each Path Item.
171171
/// - Parameters are unique within each Operation.
172172
/// - Operation Ids are unique across the whole Document.
173-
/// - All OpenAPI.References that refer to components in this
174-
/// document can be found in the components dictionary.
175-
/// - `Enum` must not be empty in the document's
176-
/// Server Variable.
177-
/// - `Default` must exist in the enum values in the document's
178-
/// Server Variable.
173+
/// - All OpenAPI.References that refer to components in this document can
174+
/// be found in the components dictionary.
175+
/// - `Enum` must not be empty in the document's Server Variable.
176+
/// - `Default` must exist in the enum values in the document's Server
177+
/// Variable.
178+
/// - `Parameter` styles and locations are compatible with each other.
179179
///
180180
public convenience init() {
181181
self.init(validations: [
@@ -193,7 +193,8 @@ public final class Validator {
193193
.init(.callbacksReferencesAreValid),
194194
.init(.pathItemReferencesAreValid),
195195
.init(.serverVariableEnumIsValid),
196-
.init(.serverVariableDefaultExistsInEnum)
196+
.init(.serverVariableDefaultExistsInEnum),
197+
.init(.parameterStyleAndLocationAreCompatible)
197198
])
198199
}
199200

Sources/OpenAPIKit/_CoreReExport.swift

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,6 @@ public extension OpenAPI.SecurityScheme {
3131
typealias Location = OpenAPIKitCore.Shared.SecuritySchemeLocation
3232
}
3333

34-
public extension OpenAPI.Parameter.SchemaContext {
35-
typealias Style = OpenAPIKitCore.Shared.ParameterSchemaContextStyle
36-
}
37-
3834
public extension OpenAPI.Response {
3935
typealias StatusCode = OpenAPIKitCore.Shared.ResponseStatusCode
4036
}

Sources/OpenAPIKitCore/Shared/ParameterSchemaContextStyle.swift renamed to Sources/OpenAPIKit30/Parameter/ParameterSchemaContextStyle.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@
55
// Created by Mathew Polzin on 12/18/22.
66
//
77

8-
extension Shared {
9-
public enum ParameterSchemaContextStyle: String, CaseIterable, Codable, Sendable {
8+
extension OpenAPI.Parameter.SchemaContext {
9+
public enum Style: String, CaseIterable, Codable, Sendable {
1010
case form
1111
case simple
1212
case matrix

Sources/OpenAPIKit30/_CoreReExport.swift

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,6 @@ public extension OpenAPI.SecurityScheme {
3131
typealias Location = OpenAPIKitCore.Shared.SecuritySchemeLocation
3232
}
3333

34-
public extension OpenAPI.Parameter.SchemaContext {
35-
typealias Style = OpenAPIKitCore.Shared.ParameterSchemaContextStyle
36-
}
37-
3834
public extension OpenAPI.Response {
3935
typealias StatusCode = OpenAPIKitCore.Shared.ResponseStatusCode
4036
}

0 commit comments

Comments
 (0)