@@ -45,7 +45,7 @@ extension OpenAPI {
4545 /// "my_param": .reference(.component(named: "my_direct_param"))
4646 /// ]
4747 /// )
48- public struct Components : Equatable , CodableVendorExtendable , Sendable {
48+ public struct Components : HasConditionalWarnings , CodableVendorExtendable , Sendable {
4949
5050 public var schemas : ComponentDictionary < JSONSchema >
5151 public var responses : ComponentReferenceDictionary < Response >
@@ -56,6 +56,8 @@ extension OpenAPI {
5656 public var securitySchemes : ComponentReferenceDictionary < SecurityScheme >
5757 public var links : ComponentReferenceDictionary < Link >
5858 public var callbacks : ComponentReferenceDictionary < Callbacks >
59+ /// Media Type Objects (aka `OpenAPI.Content`)
60+ public var mediaTypes : ComponentReferenceDictionary < Content >
5961
6062 public var pathItems : ComponentDictionary < PathItem >
6163
@@ -66,6 +68,8 @@ extension OpenAPI {
6668 /// where the values are anything codable.
6769 public var vendorExtensions : [ String : AnyCodable ]
6870
71+ public let conditionalWarnings : [ ( any Condition , OpenAPI . Warning ) ]
72+
6973 public init (
7074 schemas: ComponentDictionary < JSONSchema > = [ : ] ,
7175 responses: ComponentReferenceDictionary < Response > = [ : ] ,
@@ -76,6 +80,7 @@ extension OpenAPI {
7680 securitySchemes: ComponentReferenceDictionary < SecurityScheme > = [ : ] ,
7781 links: ComponentReferenceDictionary < Link > = [ : ] ,
7882 callbacks: ComponentReferenceDictionary < Callbacks > = [ : ] ,
83+ mediaTypes: ComponentReferenceDictionary < Content > = [ : ] ,
7984 pathItems: ComponentDictionary < PathItem > = [ : ] ,
8085 vendorExtensions: [ String : AnyCodable ] = [ : ]
8186 ) {
@@ -88,8 +93,13 @@ extension OpenAPI {
8893 self . securitySchemes = securitySchemes
8994 self . links = links
9095 self . callbacks = callbacks
96+ self . mediaTypes = mediaTypes
9197 self . pathItems = pathItems
9298 self . vendorExtensions = vendorExtensions
99+
100+ self . conditionalWarnings = [
101+ nonEmptyVersionWarning ( fieldName: " mediaTypes " , value: mediaTypes, minimumVersion: . v3_2_0)
102+ ] . compactMap { $0 }
93103 }
94104
95105 /// Construct components as "direct" entries (no references). When
@@ -105,6 +115,7 @@ extension OpenAPI {
105115 securitySchemes: ComponentDictionary < SecurityScheme > = [ : ] ,
106116 links: ComponentDictionary < Link > = [ : ] ,
107117 callbacks: ComponentDictionary < Callbacks > = [ : ] ,
118+ mediaTypes: ComponentDictionary < Content > = [ : ] ,
108119 pathItems: ComponentDictionary < PathItem > = [ : ] ,
109120 vendorExtensions: [ String : AnyCodable ] = [ : ]
110121 ) -> Self {
@@ -118,6 +129,7 @@ extension OpenAPI {
118129 securitySchemes: securitySchemes. mapValues { . b( $0) } ,
119130 links: links. mapValues { . b( $0) } ,
120131 callbacks: callbacks. mapValues { . b( $0) } ,
132+ mediaTypes: mediaTypes. mapValues { . b( $0) } ,
121133 pathItems: pathItems,
122134 vendorExtensions: vendorExtensions
123135 )
@@ -132,6 +144,32 @@ extension OpenAPI {
132144 }
133145}
134146
147+ extension OpenAPI . Components : Equatable {
148+ public static func == ( lhs: Self , rhs: Self ) -> Bool {
149+ lhs. schemas == rhs. schemas
150+ && lhs. responses == rhs. responses
151+ && lhs. parameters == rhs. parameters
152+ && lhs. examples == rhs. examples
153+ && lhs. requestBodies == rhs. requestBodies
154+ && lhs. headers == rhs. headers
155+ && lhs. securitySchemes == rhs. securitySchemes
156+ && lhs. links == rhs. links
157+ && lhs. callbacks == rhs. callbacks
158+ && lhs. mediaTypes == rhs. mediaTypes
159+ && lhs. pathItems == rhs. pathItems
160+ && lhs. vendorExtensions == rhs. vendorExtensions
161+ }
162+ }
163+
164+ fileprivate func nonEmptyVersionWarning( fieldName: String , value: any Collection , minimumVersion: OpenAPI . Document . Version ) -> ( any Condition , OpenAPI . Warning ) ? {
165+ if value. isEmpty { return nil }
166+
167+ return OpenAPI . Document. ConditionalWarnings. version (
168+ lessThan: minimumVersion,
169+ doesNotSupport: " The Components \( fieldName) map "
170+ )
171+ }
172+
135173extension OpenAPI {
136174
137175 public typealias ComponentDictionary < T> = OrderedDictionary < ComponentKey , T >
@@ -170,6 +208,7 @@ extension OpenAPI.Components {
170208 try securitySchemes. merge ( other. securitySchemes, uniquingKeysWith: detectCollision ( type: " securitySchemes " ) )
171209 try links. merge ( other. links, uniquingKeysWith: detectCollision ( type: " links " ) )
172210 try callbacks. merge ( other. callbacks, uniquingKeysWith: detectCollision ( type: " callbacks " ) )
211+ try mediaTypes. merge ( other. mediaTypes, uniquingKeysWith: detectCollision ( type: " mediaTypes " ) )
173212 try pathItems. merge ( other. pathItems, uniquingKeysWith: detectCollision ( type: " pathItems " ) )
174213 try vendorExtensions. merge ( other. vendorExtensions, uniquingKeysWith: detectCollision ( type: " vendorExtensions " ) )
175214 }
@@ -185,6 +224,7 @@ extension OpenAPI.Components {
185224 securitySchemes. sortKeys ( )
186225 links. sortKeys ( )
187226 callbacks. sortKeys ( )
227+ mediaTypes. sortKeys ( )
188228 pathItems. sortKeys ( )
189229 }
190230}
@@ -237,6 +277,10 @@ extension OpenAPI.Components: Encodable {
237277 if !callbacks. isEmpty {
238278 try container. encode ( callbacks, forKey: . callbacks)
239279 }
280+
281+ if !mediaTypes. isEmpty {
282+ try container. encode ( mediaTypes, forKey: . mediaTypes)
283+ }
240284
241285 if !pathItems. isEmpty {
242286 try container. encode ( pathItems, forKey: . pathItems)
@@ -276,10 +320,16 @@ extension OpenAPI.Components: Decodable {
276320 links = try container. decodeIfPresent ( OpenAPI . ComponentReferenceDictionary< OpenAPI . Link> . self , forKey: . links) ?? [ : ]
277321
278322 callbacks = try container. decodeIfPresent ( OpenAPI . ComponentReferenceDictionary< OpenAPI . Callbacks> . self , forKey: . callbacks) ?? [ : ]
323+
324+ mediaTypes = try container. decodeIfPresent ( OpenAPI . ComponentReferenceDictionary< OpenAPI . Content> . self , forKey: . mediaTypes) ?? [ : ]
279325
280326 pathItems = try container. decodeIfPresent ( OpenAPI . ComponentDictionary< OpenAPI . PathItem> . self , forKey: . pathItems) ?? [ : ]
281327
282328 vendorExtensions = try Self . extensions ( from: decoder)
329+
330+ conditionalWarnings = [
331+ nonEmptyVersionWarning ( fieldName: " mediaTypes " , value: mediaTypes, minimumVersion: . v3_2_0)
332+ ] . compactMap { $0 }
283333 } catch let error as EitherDecodeNoTypesMatchedError {
284334 if let underlyingError = OpenAPI . Error. Decoding. Document. eitherBranchToDigInto ( error) {
285335 throw ( underlyingError. underlyingError ?? underlyingError)
@@ -310,6 +360,7 @@ extension OpenAPI.Components {
310360 case securitySchemes
311361 case links
312362 case callbacks
363+ case mediaTypes
313364 case pathItems
314365
315366 case extended( String )
@@ -325,6 +376,7 @@ extension OpenAPI.Components {
325376 . securitySchemes,
326377 . links,
327378 . callbacks,
379+ . mediaTypes,
328380 . pathItems
329381 ]
330382 }
@@ -353,6 +405,8 @@ extension OpenAPI.Components {
353405 self = . links
354406 case " callbacks " :
355407 self = . callbacks
408+ case " mediaTypes " :
409+ self = . mediaTypes
356410 case " pathItems " :
357411 self = . pathItems
358412 default :
@@ -380,6 +434,8 @@ extension OpenAPI.Components {
380434 return " links "
381435 case . callbacks:
382436 return " callbacks "
437+ case . mediaTypes:
438+ return " mediaTypes "
383439 case . pathItems:
384440 return " pathItems "
385441 case . extended( let key) :
@@ -409,6 +465,7 @@ extension OpenAPI.Components {
409465 let oldSecuritySchemes = securitySchemes
410466 let oldLinks = links
411467 let oldCallbacks = callbacks
468+ let oldMediaTypes = mediaTypes
412469 let oldPathItems = pathItems
413470
414471 async let ( newSchemas, c1, m1) = oldSchemas. externallyDereferenced ( with: loader)
@@ -420,7 +477,8 @@ extension OpenAPI.Components {
420477 async let ( newSecuritySchemes, c7, m7) = oldSecuritySchemes. externallyDereferenced ( with: loader)
421478// async let (newLinks, c8, m8) = oldLinks.externallyDereferenced(with: loader)
422479// async let (newCallbacks, c9, m9) = oldCallbacks.externallyDereferenced(with: loader)
423- async let ( newPathItems, c10, m10) = oldPathItems. externallyDereferenced ( with: loader)
480+ async let ( newMediaTypes, c10, m10) = oldMediaTypes. externallyDereferenced ( with: loader)
481+ async let ( newPathItems, c11, m11) = oldPathItems. externallyDereferenced ( with: loader)
424482
425483 schemas = try await newSchemas
426484 responses = try await newResponses
@@ -431,6 +489,7 @@ extension OpenAPI.Components {
431489 securitySchemes = try await newSecuritySchemes
432490// links = try await newLinks
433491// callbacks = try await newCallbacks
492+ mediaTypes = try await newMediaTypes
434493 pathItems = try await newPathItems
435494
436495 let c1Resolved = try await c1
@@ -443,6 +502,7 @@ extension OpenAPI.Components {
443502// let c8Resolved = try await c8
444503// let c9Resolved = try await c9
445504 let c10Resolved = try await c10
505+ let c11Resolved = try await c11
446506
447507 // For Swift 5.10+ we can delete the following links and callbacks code and uncomment the
448508 // preferred code above.
@@ -464,8 +524,9 @@ extension OpenAPI.Components {
464524 && c8Resolved. isEmpty
465525 && c9Resolved. isEmpty
466526 && c10Resolved. isEmpty
527+ && c11Resolved. isEmpty
467528
468- let newMessages = try await context + m1 + m2 + m3 + m4 + m5 + m6 + m7 + m8 + m9 + m10
529+ let newMessages = try await context + m1 + m2 + m3 + m4 + m5 + m6 + m7 + m8 + m9 + m10 + m11
469530
470531 if noNewComponents { return newMessages }
471532
@@ -479,6 +540,7 @@ extension OpenAPI.Components {
479540 try merge ( c8Resolved)
480541 try merge ( c9Resolved)
481542 try merge ( c10Resolved)
543+ try merge ( c11Resolved)
482544
483545 switch depth {
484546 case . iterations( let number) :
0 commit comments