@@ -57,6 +57,11 @@ extension OpenAPI {
5757 /// The `QUERY` endpoint at this path, if one exists.
5858 public var query : Operation ?
5959
60+ /// Additional operations, keyed by all-caps HTTP method names. This
61+ /// map MUST NOT contain any entries that can be represented by the
62+ /// fixed fields on this type (e.g. `post`, `get`, etc.).
63+ public var additionalOperations : OrderedDictionary < OpenAPI . HttpMethod , Operation >
64+
6065 /// Dictionary of vendor extensions.
6166 ///
6267 /// These should be of the form:
@@ -84,6 +89,7 @@ extension OpenAPI {
8489 patch: Operation ? = nil ,
8590 trace: Operation ? = nil ,
8691 query: Operation ? = nil ,
92+ additionalOperations: OrderedDictionary < OpenAPI . HttpMethod , Operation > = [ : ] ,
8793 vendorExtensions: [ String : AnyCodable ] = [ : ]
8894 ) {
8995 self . summary = summary
@@ -100,11 +106,14 @@ extension OpenAPI {
100106 self . patch = patch
101107 self . trace = trace
102108 self . query = query
109+ self . additionalOperations = additionalOperations
103110 self . vendorExtensions = vendorExtensions
104111
105112 self . conditionalWarnings = [
106113 // If query is non-nil, the document must be OAS version 3.2.0 or greater
107- nonNilVersionWarning ( fieldName: " query " , value: query, minimumVersion: . v3_2_0)
114+ nonNilVersionWarning ( fieldName: " query " , value: query, minimumVersion: . v3_2_0) ,
115+ // If there are additionalOperations defiend, the document must be OAS version 3.2.0 or greater
116+ nonEmptyVersionWarning ( fieldName: " additionalOperations " , value: additionalOperations, minimumVersion: . v3_2_0)
108117 ] . compactMap { $0 }
109118 }
110119
@@ -170,6 +179,7 @@ extension OpenAPI.PathItem: Equatable {
170179 && lhs. patch == rhs. patch
171180 && lhs. trace == rhs. trace
172181 && lhs. query == rhs. query
182+ && lhs. additionalOperations == rhs. additionalOperations
173183 && lhs. vendorExtensions == rhs. vendorExtensions
174184 }
175185}
@@ -183,6 +193,15 @@ fileprivate func nonNilVersionWarning<Subject>(fieldName: String, value: Subject
183193 }
184194}
185195
196+ fileprivate func nonEmptyVersionWarning< Key, Value> ( fieldName: String , value: OrderedDictionary < Key , Value > , minimumVersion: OpenAPI . Document . Version ) -> ( any Condition , OpenAPI . Warning ) ? {
197+ if value. isEmpty { return nil }
198+
199+ return OpenAPI . Document. ConditionalWarnings. version (
200+ lessThan: minimumVersion,
201+ doesNotSupport: " The PathItem \( fieldName) map "
202+ )
203+ }
204+
186205extension OpenAPI . PathItem {
187206 public typealias Map = OrderedDictionary < OpenAPI . Path , Either < OpenAPI . Reference < OpenAPI . PathItem > , OpenAPI . PathItem > >
188207}
@@ -198,48 +217,49 @@ extension OpenAPI.PathItem {
198217 /// Retrieve the operation for the given verb, if one is set for this path.
199218 public func `for`( _ verb: OpenAPI . HttpMethod ) -> OpenAPI . Operation ? {
200219 switch verb {
201- case . delete:
202- return self . delete
203- case . get:
204- return self . get
205- case . head:
206- return self . head
207- case . options:
208- return self . options
209- case . patch:
210- return self . patch
211- case . post:
212- return self . post
213- case . put:
214- return self . put
215- case . trace:
216- return self . trace
217- case . query:
218- return self . query
220+ case . builtin( let builtin) :
221+ switch builtin {
222+ case . delete: self . delete
223+ case . get: self . get
224+ case . head: self . head
225+ case . options: self . options
226+ case . patch: self . patch
227+ case . post: self . post
228+ case . put: self . put
229+ case . trace: self . trace
230+ case . query: self . query
231+ }
232+ case . other( let other) :
233+ additionalOperations [ . other( other) ]
219234 }
220235 }
221236
222237 /// Set the operation for the given verb, overwriting any already set operation for the same verb.
223238 public mutating func set( operation: OpenAPI . Operation ? , for verb: OpenAPI . HttpMethod ) {
224239 switch verb {
225- case . delete:
226- self . delete ( operation)
227- case . get:
228- self . get ( operation)
229- case . head:
230- self . head ( operation)
231- case . options:
232- self . options ( operation)
233- case . patch:
234- self . patch ( operation)
235- case . post:
236- self . post ( operation)
237- case . put:
238- self . put ( operation)
239- case . trace:
240- self . trace ( operation)
241- case . query:
242- self . query ( operation)
240+ case . builtin( let builtin) :
241+ switch builtin {
242+ case . delete:
243+ self . delete ( operation)
244+ case . get:
245+ self . get ( operation)
246+ case . head:
247+ self . head ( operation)
248+ case . options:
249+ self . options ( operation)
250+ case . patch:
251+ self . patch ( operation)
252+ case . post:
253+ self . post ( operation)
254+ case . put:
255+ self . put ( operation)
256+ case . trace:
257+ self . trace ( operation)
258+ case . query:
259+ self . query ( operation)
260+ }
261+ case . other( let other) :
262+ self . additionalOperations [ . other( other) ] = operation
243263 }
244264 }
245265
@@ -264,9 +284,11 @@ extension OpenAPI.PathItem {
264284 /// - Returns: An array of `Endpoints` with the method (i.e. `.get`) and the operation for
265285 /// the method.
266286 public var endpoints : [ Endpoint ] {
267- return OpenAPI . HttpMethod . allCases. compactMap { method in
268- self . for ( method) . map { . init( method: method, operation: $0) }
287+ let builtins = OpenAPI . BuiltinHttpMethod . allCases. compactMap { method -> Endpoint ? in
288+ self . for ( . builtin ( method) ) . map { . init( method: . builtin ( method) , operation: $0) }
269289 }
290+
291+ return builtins + additionalOperations. map { key, value in . init( method: key, operation: value) }
270292 }
271293}
272294
@@ -312,6 +334,10 @@ extension OpenAPI.PathItem: Encodable {
312334 try container. encodeIfPresent ( trace, forKey: . trace)
313335 try container. encodeIfPresent ( query, forKey: . query)
314336
337+ if !additionalOperations. isEmpty {
338+ try container. encode ( additionalOperations, forKey: . additionalOperations)
339+ }
340+
315341 if VendorExtensionsConfiguration . isEnabled ( for: encoder) {
316342 try encodeExtensions ( to: & container)
317343 }
@@ -338,11 +364,15 @@ extension OpenAPI.PathItem: Decodable {
338364 trace = try container. decodeIfPresent ( OpenAPI . Operation. self, forKey: . trace)
339365 query = try container. decodeIfPresent ( OpenAPI . Operation. self, forKey: . query)
340366
367+ additionalOperations = try container. decodeIfPresent ( OrderedDictionary < OpenAPI . HttpMethod , OpenAPI . Operation > . self, forKey: . additionalOperations) ?? [ : ]
368+
341369 vendorExtensions = try Self . extensions ( from: decoder)
342370
343371 self . conditionalWarnings = [
344372 // If query is non-nil, the document must be OAS version 3.2.0 or greater
345- nonNilVersionWarning ( fieldName: " query " , value: query, minimumVersion: . v3_2_0)
373+ nonNilVersionWarning ( fieldName: " query " , value: query, minimumVersion: . v3_2_0) ,
374+ // If there are additionalOperations defiend, the document must be OAS version 3.2.0 or greater
375+ nonEmptyVersionWarning ( fieldName: " additionalOperations " , value: additionalOperations, minimumVersion: . v3_2_0)
346376 ] . compactMap { $0 }
347377 } catch let error as DecodingError {
348378
@@ -377,6 +407,8 @@ extension OpenAPI.PathItem {
377407 case trace
378408 case query
379409
410+ case additionalOperations
411+
380412 case extended( String )
381413
382414 static var allBuiltinKeys : [ CodingKeys ] {
@@ -394,7 +426,9 @@ extension OpenAPI.PathItem {
394426 . head,
395427 . patch,
396428 . trace,
397- . query
429+ . query,
430+
431+ . additionalOperations
398432 ]
399433 }
400434
@@ -430,6 +464,8 @@ extension OpenAPI.PathItem {
430464 self = . trace
431465 case " query " :
432466 self = . query
467+ case " additionalOperations " :
468+ self = . additionalOperations
433469 default :
434470 self = . extendedKey( for: stringValue)
435471 }
@@ -463,6 +499,8 @@ extension OpenAPI.PathItem {
463499 return " trace "
464500 case . query:
465501 return " query "
502+ case . additionalOperations:
503+ return " additionalOperations "
466504 case . extended( let key) :
467505 return key
468506 }
0 commit comments