Skip to content

Commit 570ce9b

Browse files
committed
Add new optional stricter reference validations that assert the references are internal and can be found in the components object
1 parent 79d3bc2 commit 570ce9b

4 files changed

Lines changed: 346 additions & 75 deletions

File tree

Sources/OpenAPIKit/Validator/ReferenceValidations.swift

Lines changed: 36 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,17 @@ extension Validation {
1313
/// given type that point at the Components Object are found in the
1414
/// document's components dictionary. You can choose whether an
1515
/// internal reference to somewhere other than the components
16-
/// dictionary should pass or fail.
17-
internal static func referencesAreValid<ReferenceType: ComponentDictionaryLocatable>(ofType type: ReferenceType.Type, mustPointToComponents requireComponents: Bool) -> Validation<OpenAPI.Reference<ReferenceType>> {
18-
.init(
19-
description: "OpenAPI \(String(describing: type)) reference can be found in components/\(ReferenceType.openAPIComponentsKey)",
16+
/// dictionary should pass or fail. You can also choose whether
17+
/// external references should fail or pass.
18+
internal static func referencesAreValid<ReferenceType: ComponentDictionaryLocatable>(ofType type: ReferenceType.Type, named name: String, mustBeInternal requireInternal: Bool, mustPointToComponents requireComponents: Bool) -> Validation<OpenAPI.Reference<ReferenceType>> {
19+
let requireInternalAddendum = if requireInternal { " points to this document and" } else { "" }
20+
21+
return .init(
22+
description: "\(name) reference\(requireInternalAddendum) can be found in components/\(ReferenceType.openAPIComponentsKey)",
2023
check: { context in
2124
guard case let .internal(internalReference) = context.subject.jsonReference else {
22-
// don't make assertions about external references
23-
return true
25+
// don't make assertions about external references other than if we are requiring references to be internal
26+
return !requireInternal
2427
}
2528

2629
guard case .component = internalReference else {
@@ -34,17 +37,19 @@ extension Validation {
3437
)
3538
}
3639

37-
internal static func schemaReferencesAreValid(mustPointToComponents requireComponents: Bool) -> Validation<OpenAPI.Reference<JSONSchema>> {
38-
referencesAreValid(ofType: JSONSchema.self, mustPointToComponents: requireComponents)
40+
internal static func schemaReferencesAreValid(mustBeInternal requireInternal: Bool, mustPointToComponents requireComponents: Bool) -> Validation<OpenAPI.Reference<JSONSchema>> {
41+
referencesAreValid(ofType: JSONSchema.self, named: "JSONSchema", mustBeInternal: requireInternal, mustPointToComponents: requireComponents)
3942
}
4043

41-
internal static func jsonSchemaReferencesAreValid(mustPointToComponents requireComponents: Bool) -> Validation<JSONSchema> {
42-
.init(
43-
description: "JSONSchema reference can be found in components/schemas",
44+
internal static func jsonSchemaReferencesAreValid(mustBeInternal requireInternal: Bool, mustPointToComponents requireComponents: Bool) -> Validation<JSONSchema> {
45+
let requireInternalAddendum = if requireInternal { " points to this document and" } else { "" }
46+
47+
return .init(
48+
description: "JSONSchema reference\(requireInternalAddendum) can be found in components/schemas",
4449
check: { context in
45-
guard case let .internal(internalReference) = context.subject.reference else {
50+
guard case let .internal(internalReference) = context.subject.reference else {
4651
// don't make assertions about external references
47-
return true
52+
return !requireInternal
4853
}
4954

5055
guard case .component = internalReference else {
@@ -54,40 +59,41 @@ extension Validation {
5459
return !requireComponents
5560
}
5661
return context.document.components.contains(internalReference)
57-
}
62+
},
63+
when: \.reference != nil
5864
)
5965
}
6066

61-
internal static func responseReferencesAreValid(mustPointToComponents requireComponents: Bool) -> Validation<OpenAPI.Reference<OpenAPI.Response>> {
62-
referencesAreValid(ofType: OpenAPI.Response.self, mustPointToComponents: requireComponents)
67+
internal static func responseReferencesAreValid(mustBeInternal requireInternal: Bool, mustPointToComponents requireComponents: Bool) -> Validation<OpenAPI.Reference<OpenAPI.Response>> {
68+
referencesAreValid(ofType: OpenAPI.Response.self, named: "Response", mustBeInternal: requireInternal, mustPointToComponents: requireComponents)
6369
}
6470

65-
internal static func parameterReferencesAreValid(mustPointToComponents requireComponents: Bool) -> Validation<OpenAPI.Reference<OpenAPI.Parameter>> {
66-
referencesAreValid(ofType: OpenAPI.Parameter.self, mustPointToComponents: requireComponents)
71+
internal static func parameterReferencesAreValid(mustBeInternal requireInternal: Bool, mustPointToComponents requireComponents: Bool) -> Validation<OpenAPI.Reference<OpenAPI.Parameter>> {
72+
referencesAreValid(ofType: OpenAPI.Parameter.self, named: "Parameter", mustBeInternal: requireInternal, mustPointToComponents: requireComponents)
6773
}
6874

69-
internal static func exampleReferencesAreValid(mustPointToComponents requireComponents: Bool) -> Validation<OpenAPI.Reference<OpenAPI.Example>> {
70-
referencesAreValid(ofType: OpenAPI.Example.self, mustPointToComponents: requireComponents)
75+
internal static func exampleReferencesAreValid(mustBeInternal requireInternal: Bool, mustPointToComponents requireComponents: Bool) -> Validation<OpenAPI.Reference<OpenAPI.Example>> {
76+
referencesAreValid(ofType: OpenAPI.Example.self, named: "Example", mustBeInternal: requireInternal, mustPointToComponents: requireComponents)
7177
}
7278

73-
internal static func requestReferencesAreValid(mustPointToComponents requireComponents: Bool) -> Validation<OpenAPI.Reference<OpenAPI.Request>> {
74-
referencesAreValid(ofType: OpenAPI.Request.self, mustPointToComponents: requireComponents)
79+
internal static func requestReferencesAreValid(mustBeInternal requireInternal: Bool, mustPointToComponents requireComponents: Bool) -> Validation<OpenAPI.Reference<OpenAPI.Request>> {
80+
referencesAreValid(ofType: OpenAPI.Request.self, named: "Request", mustBeInternal: requireInternal, mustPointToComponents: requireComponents)
7581
}
7682

77-
internal static func headerReferencesAreValid(mustPointToComponents requireComponents: Bool) -> Validation<OpenAPI.Reference<OpenAPI.Header>> {
78-
referencesAreValid(ofType: OpenAPI.Header.self, mustPointToComponents: requireComponents)
83+
internal static func headerReferencesAreValid(mustBeInternal requireInternal: Bool, mustPointToComponents requireComponents: Bool) -> Validation<OpenAPI.Reference<OpenAPI.Header>> {
84+
referencesAreValid(ofType: OpenAPI.Header.self, named: "Header", mustBeInternal: requireInternal, mustPointToComponents: requireComponents)
7985
}
8086

81-
internal static func linkReferencesAreValid(mustPointToComponents requireComponents: Bool) -> Validation<OpenAPI.Reference<OpenAPI.Link>> {
82-
referencesAreValid(ofType: OpenAPI.Link.self, mustPointToComponents: requireComponents)
87+
internal static func linkReferencesAreValid(mustBeInternal requireInternal: Bool, mustPointToComponents requireComponents: Bool) -> Validation<OpenAPI.Reference<OpenAPI.Link>> {
88+
referencesAreValid(ofType: OpenAPI.Link.self, named: "Link", mustBeInternal: requireInternal, mustPointToComponents: requireComponents)
8389
}
8490

85-
internal static func callbacksReferencesAreValid(mustPointToComponents requireComponents: Bool) -> Validation<OpenAPI.Reference<OpenAPI.Callbacks>> {
86-
referencesAreValid(ofType: OpenAPI.Callbacks.self, mustPointToComponents: requireComponents)
91+
internal static func callbacksReferencesAreValid(mustBeInternal requireInternal: Bool, mustPointToComponents requireComponents: Bool) -> Validation<OpenAPI.Reference<OpenAPI.Callbacks>> {
92+
referencesAreValid(ofType: OpenAPI.Callbacks.self, named: "Callbacks", mustBeInternal: requireInternal, mustPointToComponents: requireComponents)
8793
}
8894

89-
internal static func pathItemReferencesAreValid(mustPointToComponents requireComponents: Bool) -> Validation<OpenAPI.Reference<OpenAPI.PathItem>> {
90-
referencesAreValid(ofType: OpenAPI.PathItem.self, mustPointToComponents: requireComponents)
95+
internal static func pathItemReferencesAreValid(mustBeInternal requireInternal: Bool, mustPointToComponents requireComponents: Bool) -> Validation<OpenAPI.Reference<OpenAPI.PathItem>> {
96+
referencesAreValid(ofType: OpenAPI.PathItem.self, named: "PathItem", mustBeInternal: requireInternal, mustPointToComponents: requireComponents)
9197
}
9298
}
9399
}

Sources/OpenAPIKit/Validator/Validation+Builtins.swift

Lines changed: 130 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,126 @@ extension Validation {
199199
)
200200
}
201201

202+
/// Validate that all OpenAPI JSONSchema references are internal and found
203+
/// in the document's components dictionary.
204+
///
205+
/// - See also: The similar but distinct default-on validation
206+
/// `schemaReferencesAreValid`.
207+
///
208+
/// - Important: This is not an included in validation by default.
209+
///
210+
public static var schemaReferencesFoundInComponents: Validation<OpenAPI.Reference<JSONSchema>> {
211+
References.schemaReferencesAreValid(mustBeInternal: true, mustPointToComponents: true)
212+
}
213+
214+
/// Validate that all JSONSchema references are internal and found in the
215+
/// document's components dictionary.
216+
///
217+
/// - See also: The similar but distinct default-on validation
218+
/// `jsonSchemaReferencesAreValid`.
219+
///
220+
/// - Important: This is not an included in validation by default.
221+
///
222+
public static var jsonSchemaReferencesFoundInComponents: Validation<JSONSchema> {
223+
References.jsonSchemaReferencesAreValid(mustBeInternal: true, mustPointToComponents: true)
224+
}
225+
226+
/// Validate that all Response references are internal and found in the
227+
/// document's components dictionary.
228+
///
229+
/// - See also: The similar but distinct default-on validation
230+
/// `responseReferencesAreValid`.
231+
///
232+
/// - Important: This is not an included in validation by default.
233+
///
234+
public static var responseReferencesFoundInComponents: Validation<OpenAPI.Reference<OpenAPI.Response>> {
235+
References.responseReferencesAreValid(mustBeInternal: true, mustPointToComponents: true)
236+
}
237+
238+
/// Validate that all Parameter references are internal and found in the
239+
/// document's components dictionary.
240+
///
241+
/// - See also: The similar but distinct default-on validation
242+
/// `parameterReferencesAreValid`.
243+
///
244+
/// - Important: This is not an included in validation by default.
245+
///
246+
public static var parameterReferencesFoundInComponents: Validation<OpenAPI.Reference<OpenAPI.Parameter>> {
247+
References.parameterReferencesAreValid(mustBeInternal: true, mustPointToComponents: true)
248+
}
249+
250+
/// Validate that all Example references are internal and found in the
251+
/// document's components dictionary.
252+
///
253+
/// - See also: The similar but distinct default-on validation
254+
/// `exampleReferencesAreValid`.
255+
///
256+
/// - Important: This is not an included in validation by default.
257+
///
258+
public static var exampleReferencesFoundInComponents: Validation<OpenAPI.Reference<OpenAPI.Example>> {
259+
References.exampleReferencesAreValid(mustBeInternal: true, mustPointToComponents: true)
260+
}
261+
262+
/// Validate that all Request references are internal and found in the
263+
/// document's components dictionary.
264+
///
265+
/// - See also: The similar but distinct default-on validation
266+
/// `requestReferencesAreValid`.
267+
///
268+
/// - Important: This is not an included in validation by default.
269+
///
270+
public static var requestReferencesFoundInComponents: Validation<OpenAPI.Reference<OpenAPI.Request>> {
271+
References.requestReferencesAreValid(mustBeInternal: true, mustPointToComponents: true)
272+
}
273+
274+
/// Validate that all Header references are internal and found in the
275+
/// document's components dictionary.
276+
///
277+
/// - See also: The similar but distinct default-on validation
278+
/// `headerReferencesAreValid`.
279+
///
280+
/// - Important: This is not an included in validation by default.
281+
///
282+
public static var headerReferencesFoundInComponents: Validation<OpenAPI.Reference<OpenAPI.Header>> {
283+
References.headerReferencesAreValid(mustBeInternal: true, mustPointToComponents: true)
284+
}
285+
286+
/// Validate that all Link references are internal and found in the document's
287+
/// components dictionary.
288+
///
289+
/// - See also: The similar but distinct default-on validation
290+
/// `linkReferencesAreValid`.
291+
///
292+
/// - Important: This is not an included in validation by default.
293+
///
294+
public static var linkReferencesFoundInComponents: Validation<OpenAPI.Reference<OpenAPI.Link>> {
295+
References.linkReferencesAreValid(mustBeInternal: true, mustPointToComponents: true)
296+
}
297+
298+
/// Validate that all Callbacks references are internal and found in the
299+
/// document's components dictionary.
300+
///
301+
/// - See also: The similar but distinct default-on validation
302+
/// `callbacksReferencesAreValid`.
303+
///
304+
/// - Important: This is not an included in validation by default.
305+
///
306+
public static var callbacksReferencesFoundInComponents: Validation<OpenAPI.Reference<OpenAPI.Callbacks>> {
307+
References.callbacksReferencesAreValid(mustBeInternal: true, mustPointToComponents: true)
308+
}
309+
310+
/// Validate that all PathItem references are internal and found in the
311+
/// document's components dictionary.
312+
///
313+
/// - See also: The similar but distinct default-on validation
314+
/// `pathItemReferencesAreValid`.
315+
///
316+
/// - Important: This is not an included in validation by default.
317+
///
318+
public static var pathItemReferencesFoundInComponents: Validation<OpenAPI.Reference<OpenAPI.PathItem>> {
319+
References.pathItemReferencesAreValid(mustBeInternal: true, mustPointToComponents: true)
320+
}
321+
202322
// MARK: - Included with `Validator()` by default
203323

204324
// You can start with no validations (not even the defaults below)
@@ -284,7 +404,7 @@ extension Validation {
284404
/// - Important: This is included in validation by default.
285405
///
286406
public static var schemaReferencesAreValid: Validation<OpenAPI.Reference<JSONSchema>> {
287-
References.schemaReferencesAreValid(mustPointToComponents: false)
407+
References.schemaReferencesAreValid(mustBeInternal: false, mustPointToComponents: false)
288408
}
289409

290410
/// Validate that all JSONSchema components references are found in the
@@ -293,7 +413,7 @@ extension Validation {
293413
/// - Important: This is included in validation by default.
294414
///
295415
public static var jsonSchemaReferencesAreValid: Validation<JSONSchema> {
296-
References.jsonSchemaReferencesAreValid(mustPointToComponents: false)
416+
References.jsonSchemaReferencesAreValid(mustBeInternal: false, mustPointToComponents: false)
297417
}
298418

299419
/// Validate that all Response components references are found in the
@@ -302,7 +422,7 @@ extension Validation {
302422
/// - Important: This is included in validation by default.
303423
///
304424
public static var responseReferencesAreValid: Validation<OpenAPI.Reference<OpenAPI.Response>> {
305-
References.responseReferencesAreValid(mustPointToComponents: false)
425+
References.responseReferencesAreValid(mustBeInternal: false, mustPointToComponents: false)
306426
}
307427

308428
/// Validate that all Parameter components references are found in the
@@ -311,7 +431,7 @@ extension Validation {
311431
/// - Important: This is included in validation by default.
312432
///
313433
public static var parameterReferencesAreValid: Validation<OpenAPI.Reference<OpenAPI.Parameter>> {
314-
References.parameterReferencesAreValid(mustPointToComponents: false)
434+
References.parameterReferencesAreValid(mustBeInternal: false, mustPointToComponents: false)
315435
}
316436

317437
/// Validate that all Example components references are found in the
@@ -320,7 +440,7 @@ extension Validation {
320440
/// - Important: This is included in validation by default.
321441
///
322442
public static var exampleReferencesAreValid: Validation<OpenAPI.Reference<OpenAPI.Example>> {
323-
References.exampleReferencesAreValid(mustPointToComponents: false)
443+
References.exampleReferencesAreValid(mustBeInternal: false, mustPointToComponents: false)
324444
}
325445

326446
/// Validate that all Request components references are found in the
@@ -329,7 +449,7 @@ extension Validation {
329449
/// - Important: This is included in validation by default.
330450
///
331451
public static var requestReferencesAreValid: Validation<OpenAPI.Reference<OpenAPI.Request>> {
332-
References.requestReferencesAreValid(mustPointToComponents: false)
452+
References.requestReferencesAreValid(mustBeInternal: false, mustPointToComponents: false)
333453
}
334454

335455
/// Validate that all Header components references are found in the
@@ -338,7 +458,7 @@ extension Validation {
338458
/// - Important: This is included in validation by default.
339459
///
340460
public static var headerReferencesAreValid: Validation<OpenAPI.Reference<OpenAPI.Header>> {
341-
References.headerReferencesAreValid(mustPointToComponents: false)
461+
References.headerReferencesAreValid(mustBeInternal: false, mustPointToComponents: false)
342462
}
343463

344464
/// Validate that all Link components references are found in the
@@ -347,7 +467,7 @@ extension Validation {
347467
/// - Important: This is included in validation by default.
348468
///
349469
public static var linkReferencesAreValid: Validation<OpenAPI.Reference<OpenAPI.Link>> {
350-
References.linkReferencesAreValid(mustPointToComponents: false)
470+
References.linkReferencesAreValid(mustBeInternal: false, mustPointToComponents: false)
351471
}
352472

353473
/// Validate that all Callbacks components references are found in the
@@ -356,7 +476,7 @@ extension Validation {
356476
/// - Important: This is included in validation by default.
357477
///
358478
public static var callbacksReferencesAreValid: Validation<OpenAPI.Reference<OpenAPI.Callbacks>> {
359-
References.callbacksReferencesAreValid(mustPointToComponents: false)
479+
References.callbacksReferencesAreValid(mustBeInternal: false, mustPointToComponents: false)
360480
}
361481

362482
/// Validate that all PathItem components references are found in the
@@ -365,7 +485,7 @@ extension Validation {
365485
/// - Important: This is included in validation by default.
366486
///
367487
public static var pathItemReferencesAreValid: Validation<OpenAPI.Reference<OpenAPI.PathItem>> {
368-
References.pathItemReferencesAreValid(mustPointToComponents: false)
488+
References.pathItemReferencesAreValid(mustBeInternal: false, mustPointToComponents: false)
369489
}
370490

371491
/// Validate that `enum` must not be empty in the document's

0 commit comments

Comments
 (0)