@@ -25,11 +25,11 @@ import OpenAPIKit
25
25
/// - Throws: An error with diagnostic information if any invalid content types are found.
26
26
func validateContentTypes( in doc: ParsedOpenAPIRepresentation , validate: ( String ) -> Bool ) throws {
27
27
for (path, pathValue) in doc. paths {
28
- guard case . b ( let pathItem) = pathValue else { continue }
28
+ guard let pathItem = pathValue. pathItemValue else { continue }
29
29
for endpoint in pathItem. endpoints {
30
30
31
31
if let eitherRequest = endpoint. operation. requestBody {
32
- if case . b ( let actualRequest) = eitherRequest {
32
+ if let actualRequest = eitherRequest. requestValue {
33
33
for contentType in actualRequest. content. keys {
34
34
if !validate( contentType. rawValue) {
35
35
throw Diagnostic . error (
@@ -47,7 +47,7 @@ func validateContentTypes(in doc: ParsedOpenAPIRepresentation, validate: (String
47
47
}
48
48
49
49
for eitherResponse in endpoint. operation. responses. values {
50
- if case . b ( let actualResponse) = eitherResponse {
50
+ if let actualResponse = eitherResponse. responseValue {
51
51
for contentType in actualResponse. content. keys {
52
52
if !validate( contentType. rawValue) {
53
53
throw Diagnostic . error (
@@ -95,13 +95,175 @@ func validateContentTypes(in doc: ParsedOpenAPIRepresentation, validate: (String
95
95
}
96
96
}
97
97
98
+ /// Validates all references from an OpenAPI document represented by a ParsedOpenAPIRepresentation against its components.
99
+ ///
100
+ /// This method traverses the OpenAPI document to ensure that all references
101
+ /// within the document are valid and point to existing components.
102
+ ///
103
+ /// - Parameter doc: The OpenAPI document to validate.
104
+ /// - Throws: `Diagnostic.error` if an external reference is found or a reference is not found in components.
105
+ func validateReferences( in doc: ParsedOpenAPIRepresentation ) throws {
106
+ func validateReference< ReferenceType: ComponentDictionaryLocatable > (
107
+ _ reference: OpenAPI . Reference < ReferenceType > ,
108
+ in components: OpenAPI . Components ,
109
+ location: String
110
+ ) throws {
111
+ if reference. isExternal {
112
+ throw Diagnostic . error (
113
+ message: " External references are not suppported. " ,
114
+ context: [ " reference " : reference. absoluteString, " location " : location]
115
+ )
116
+ }
117
+ if components [ reference] == nil {
118
+ throw Diagnostic . error (
119
+ message: " Reference not found in components. " ,
120
+ context: [ " reference " : reference. absoluteString, " location " : location]
121
+ )
122
+ }
123
+ }
124
+
125
+ func validateReferencesInContentTypes( _ content: OpenAPI . Content . Map , location: String ) throws {
126
+ for (contentKey, contentType) in content {
127
+ if let reference = contentType. schema? . reference {
128
+ try validateReference (
129
+ reference,
130
+ in: doc. components,
131
+ location: location + " /content/ \( contentKey. rawValue) /schema "
132
+ )
133
+ }
134
+ if let eitherExamples = contentType. examples? . values {
135
+ for example in eitherExamples {
136
+ if let reference = example. reference {
137
+ try validateReference (
138
+ reference,
139
+ in: doc. components,
140
+ location: location + " /content/ \( contentKey. rawValue) /examples "
141
+ )
142
+ }
143
+ }
144
+ }
145
+ }
146
+ }
147
+
148
+ for (key, value) in doc. webhooks {
149
+ if let reference = value. reference { try validateReference ( reference, in: doc. components, location: key) }
150
+ }
151
+
152
+ for (path, pathValue) in doc. paths {
153
+ if let reference = pathValue. reference {
154
+ try validateReference ( reference, in: doc. components, location: path. rawValue)
155
+ } else if let pathItem = pathValue. pathItemValue {
156
+
157
+ for endpoint in pathItem. endpoints {
158
+ for (endpointKey, endpointValue) in endpoint. operation. callbacks {
159
+ if let reference = endpointValue. reference {
160
+ try validateReference (
161
+ reference,
162
+ in: doc. components,
163
+ location: " \( path. rawValue) / \( endpoint. method. rawValue) /callbacks/ \( endpointKey) "
164
+ )
165
+ }
166
+ }
167
+
168
+ for eitherParameter in endpoint. operation. parameters {
169
+ if let reference = eitherParameter. reference {
170
+ try validateReference (
171
+ reference,
172
+ in: doc. components,
173
+ location: " \( path. rawValue) / \( endpoint. method. rawValue) /parameters "
174
+ )
175
+ } else if let parameter = eitherParameter. parameterValue {
176
+ if let reference = parameter. schemaOrContent. schemaReference {
177
+ try validateReference (
178
+ reference,
179
+ in: doc. components,
180
+ location: " \( path. rawValue) / \( endpoint. method. rawValue) /parameters/ \( parameter. name) "
181
+ )
182
+ } else if let content = parameter. schemaOrContent. contentValue {
183
+ try validateReferencesInContentTypes (
184
+ content,
185
+ location: " \( path. rawValue) / \( endpoint. method. rawValue) /parameters/ \( parameter. name) "
186
+ )
187
+ }
188
+ }
189
+ }
190
+ if let reference = endpoint. operation. requestBody? . reference {
191
+ try validateReference (
192
+ reference,
193
+ in: doc. components,
194
+ location: " \( path. rawValue) / \( endpoint. method. rawValue) /requestBody "
195
+ )
196
+ } else if let requestBodyValue = endpoint. operation. requestBody? . requestValue {
197
+ try validateReferencesInContentTypes (
198
+ requestBodyValue. content,
199
+ location: " \( path. rawValue) / \( endpoint. method. rawValue) /requestBody "
200
+ )
201
+ }
202
+
203
+ for (statusCode, eitherResponse) in endpoint. operation. responses {
204
+ if let reference = eitherResponse. reference {
205
+ try validateReference (
206
+ reference,
207
+ in: doc. components,
208
+ location: " \( path. rawValue) / \( endpoint. method. rawValue) /responses/ \( statusCode. rawValue) "
209
+ )
210
+ } else if let responseValue = eitherResponse. responseValue {
211
+ try validateReferencesInContentTypes (
212
+ responseValue. content,
213
+ location: " \( path. rawValue) / \( endpoint. method. rawValue) /responses/ \( statusCode. rawValue) "
214
+ )
215
+ }
216
+ if let headers = eitherResponse. responseValue? . headers {
217
+ for (headerKey, eitherHeader) in headers {
218
+ if let reference = eitherHeader. reference {
219
+ try validateReference (
220
+ reference,
221
+ in: doc. components,
222
+ location:
223
+ " \( path. rawValue) / \( endpoint. method. rawValue) /responses/ \( statusCode. rawValue) /headers/ \( headerKey) "
224
+ )
225
+ } else if let headerValue = eitherHeader. headerValue {
226
+ if let schemaReference = headerValue. schemaOrContent. schemaReference {
227
+ try validateReference (
228
+ schemaReference,
229
+ in: doc. components,
230
+ location:
231
+ " \( path. rawValue) / \( endpoint. method. rawValue) /responses/ \( statusCode. rawValue) /headers/ \( headerKey) "
232
+ )
233
+ } else if let contentValue = headerValue. schemaOrContent. contentValue {
234
+ try validateReferencesInContentTypes (
235
+ contentValue,
236
+ location:
237
+ " \( path. rawValue) / \( endpoint. method. rawValue) /responses/ \( statusCode. rawValue) /headers/ \( headerKey) "
238
+ )
239
+ }
240
+ }
241
+ }
242
+ }
243
+ }
244
+ }
245
+
246
+ for eitherParameter in pathItem. parameters {
247
+ if let reference = eitherParameter. reference {
248
+ try validateReference ( reference, in: doc. components, location: " \( path. rawValue) /parameters " )
249
+ }
250
+ }
251
+ }
252
+ }
253
+ }
254
+
98
255
/// Runs validation steps on the incoming OpenAPI document.
99
256
/// - Parameters:
100
257
/// - doc: The OpenAPI document to validate.
101
258
/// - config: The generator config.
102
259
/// - Returns: An array of diagnostic messages representing validation warnings.
103
260
/// - Throws: An error if a fatal issue is found.
104
261
func validateDoc( _ doc: ParsedOpenAPIRepresentation , config: Config ) throws -> [ Diagnostic ] {
262
+ try validateReferences ( in: doc)
263
+ try validateContentTypes ( in: doc) { contentType in
264
+ ( try ? _OpenAPIGeneratorCore. ContentType ( string: contentType) ) != nil
265
+ }
266
+
105
267
// Run OpenAPIKit's built-in validation.
106
268
// Pass `false` to `strict`, however, because we don't
107
269
// want to turn schema loading warnings into errors.
@@ -111,10 +273,6 @@ func validateDoc(_ doc: ParsedOpenAPIRepresentation, config: Config) throws -> [
111
273
// block the generator from running.
112
274
// Validation errors continue to be fatal, such as
113
275
// structural issues, like non-unique operationIds, etc.
114
- try validateContentTypes ( in: doc) { contentType in
115
- ( try ? _OpenAPIGeneratorCore. ContentType ( string: contentType) ) != nil
116
- }
117
-
118
276
let warnings = try doc. validate ( using: Validator ( ) . validating ( . operationsContainResponses) , strict: false )
119
277
let diagnostics : [ Diagnostic ] = warnings. map { warning in
120
278
. warning(
0 commit comments