Skip to content

Commit eae7323

Browse files
authored
Merge pull request #420 from mattpolzin/feature/add-callbacks-reference-validation
2 parents 1b6ab9d + ba56325 commit eae7323

7 files changed

Lines changed: 76 additions & 5 deletions

File tree

Sources/OpenAPIKit/Validator/Validation+Builtins.swift

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -408,6 +408,28 @@ extension Validation {
408408
)
409409
}
410410

411+
/// Validate that all non-external Callbacks references are found in the document's
412+
/// components dictionary.
413+
///
414+
/// - Important: This is included in validation by default.
415+
///
416+
public static var callbacksReferencesAreValid: Validation<OpenAPI.Reference<OpenAPI.Callbacks>> {
417+
.init(
418+
description: "Callbacks reference can be found in components/callbacks",
419+
check: { context in
420+
guard case let .internal(internalReference) = context.subject.jsonReference,
421+
case .component = internalReference else {
422+
// don't make assertions about external references
423+
// TODO: could make a stronger assertion including
424+
// internal references outside of components given
425+
// some way to resolve those references.
426+
return true
427+
}
428+
return context.document.components.contains(internalReference)
429+
}
430+
)
431+
}
432+
411433
/// Validate that all non-external PathItem references are found in the document's
412434
/// components dictionary.
413435
///

Sources/OpenAPIKit/Validator/Validator.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,7 @@ public final class Validator {
190190
.init(.requestReferencesAreValid),
191191
.init(.headerReferencesAreValid),
192192
.init(.linkReferencesAreValid),
193+
.init(.callbacksReferencesAreValid),
193194
.init(.pathItemReferencesAreValid),
194195
.init(.serverVariableEnumIsValid),
195196
.init(.serverVariableDefaultExistsInEnum)

Sources/OpenAPIKit30/Validator/Validation+Builtins.swift

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -397,6 +397,28 @@ extension Validation {
397397
)
398398
}
399399

400+
/// Validate that all non-external Callbacks references are found in the document's
401+
/// components dictionary.
402+
///
403+
/// - Important: This is included in validation by default.
404+
///
405+
public static var callbacksReferencesAreValid: Validation<JSONReference<OpenAPI.Callbacks>> {
406+
.init(
407+
description: "Callbacks reference can be found in components/callbacks",
408+
check: { context in
409+
guard case let .internal(internalReference) = context.subject,
410+
case .component = internalReference else {
411+
// don't make assertions about external references
412+
// TODO: could make a stronger assertion including
413+
// internal references outside of components given
414+
// some way to resolve those references.
415+
return true
416+
}
417+
return context.document.components.contains(internalReference)
418+
}
419+
)
420+
}
421+
400422
/// Validate the OpenAPI Document's `Links` with operationIds refer to
401423
/// Operations that exist in the document.
402424
///

Sources/OpenAPIKit30/Validator/Validator.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,8 @@ public final class Validator {
186186
.init(.exampleReferencesAreValid),
187187
.init(.requestReferencesAreValid),
188188
.init(.headerReferencesAreValid),
189-
.init(.linkReferencesAreValid)
189+
.init(.linkReferencesAreValid),
190+
.init(.callbacksReferencesAreValid)
190191
])
191192
}
192193

Tests/OpenAPIKit30Tests/Validator/BuiltinValidationTests.swift

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -638,6 +638,9 @@ final class BuiltinValidationTests: XCTestCase {
638638
],
639639
links: ["linky": .reference(.component(named: "link1"))]
640640
)
641+
],
642+
callbacks: [
643+
"callbacks1": .reference(.component(named: "callbacks1"))
641644
]
642645
)
643646
)
@@ -654,7 +657,7 @@ final class BuiltinValidationTests: XCTestCase {
654657
// NOTE this is part of default validation
655658
XCTAssertThrowsError(try document.validate()) { error in
656659
let error = error as? ValidationErrorCollection
657-
XCTAssertEqual(error?.values.count, 7)
660+
XCTAssertEqual(error?.values.count, 8)
658661
XCTAssertEqual(error?.values[0].reason, "Failed to satisfy: Parameter reference can be found in components/parameters")
659662
XCTAssertEqual(error?.values[0].codingPathString, ".paths['/hello'].get.parameters[0]")
660663
XCTAssertEqual(error?.values[1].reason, "Failed to satisfy: Request reference can be found in components/requestBodies")
@@ -669,6 +672,8 @@ final class BuiltinValidationTests: XCTestCase {
669672
XCTAssertEqual(error?.values[5].codingPathString, ".paths['/hello'].get.responses.404.content['application/xml'].schema")
670673
XCTAssertEqual(error?.values[6].reason, "Failed to satisfy: Link reference can be found in components/links")
671674
XCTAssertEqual(error?.values[6].codingPathString, ".paths['/hello'].get.responses.404.links.linky")
675+
XCTAssertEqual(error?.values[7].reason, "Failed to satisfy: Callbacks reference can be found in components/callbacks")
676+
XCTAssertEqual(error?.values[7].codingPathString, ".paths['/hello'].get.callbacks.callbacks1")
672677
}
673678
}
674679

@@ -711,6 +716,9 @@ final class BuiltinValidationTests: XCTestCase {
711716
"linky2": .reference(.external(URL(string: "https://linky.com")!))
712717
]
713718
)
719+
],
720+
callbacks: [
721+
"callbacks1": .reference(.component(named: "callbacks1"))
714722
]
715723
)
716724
)
@@ -742,6 +750,9 @@ final class BuiltinValidationTests: XCTestCase {
742750
],
743751
links: [
744752
"link1": .init(operationId: "op 1")
753+
],
754+
callbacks: [
755+
"callbacks1": .init()
745756
]
746757
)
747758
)

Tests/OpenAPIKitCompatTests/DocumentConversionTests.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,9 @@ final class DocumentConversionTests: XCTestCase {
202202
components: .init(
203203
parameters: [
204204
"test": .init(name: "referencedParam", context: .query, schema: .string)
205+
],
206+
callbacks: [
207+
"other_callback": callbacks
205208
]
206209
)
207210
)

Tests/OpenAPIKitTests/Validator/BuiltinValidationTests.swift

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -741,6 +741,9 @@ final class BuiltinValidationTests: XCTestCase {
741741
],
742742
links: ["linky": .reference(.component(named: "link1"))]
743743
)
744+
],
745+
callbacks: [
746+
"callbacks1": .reference(.component(named: "callbacks1"))
744747
]
745748
)
746749
)
@@ -758,7 +761,7 @@ final class BuiltinValidationTests: XCTestCase {
758761
// NOTE these are part of default validation
759762
XCTAssertThrowsError(try document.validate()) { error in
760763
let error = error as? ValidationErrorCollection
761-
XCTAssertEqual(error?.values.count, 8)
764+
XCTAssertEqual(error?.values.count, 9)
762765
XCTAssertEqual(error?.values[0].reason, "Failed to satisfy: Parameter reference can be found in components/parameters")
763766
XCTAssertEqual(error?.values[0].codingPathString, ".paths['/hello'].get.parameters[0]")
764767
XCTAssertEqual(error?.values[1].reason, "Failed to satisfy: Request reference can be found in components/requestBodies")
@@ -773,8 +776,10 @@ final class BuiltinValidationTests: XCTestCase {
773776
XCTAssertEqual(error?.values[5].codingPathString, ".paths['/hello'].get.responses.404.content['application/xml'].schema")
774777
XCTAssertEqual(error?.values[6].reason, "Failed to satisfy: Link reference can be found in components/links")
775778
XCTAssertEqual(error?.values[6].codingPathString, ".paths['/hello'].get.responses.404.links.linky")
776-
XCTAssertEqual(error?.values[7].reason, "Failed to satisfy: PathItem reference can be found in components/pathItems")
777-
XCTAssertEqual(error?.values[7].codingPathString, ".paths['/world']")
779+
XCTAssertEqual(error?.values[7].reason, "Failed to satisfy: Callbacks reference can be found in components/callbacks")
780+
XCTAssertEqual(error?.values[7].codingPathString, ".paths['/hello'].get.callbacks.callbacks1")
781+
XCTAssertEqual(error?.values[8].reason, "Failed to satisfy: PathItem reference can be found in components/pathItems")
782+
XCTAssertEqual(error?.values[8].codingPathString, ".paths['/world']")
778783
}
779784
}
780785

@@ -817,6 +822,9 @@ final class BuiltinValidationTests: XCTestCase {
817822
"linky2": .reference(.external(URL(string: "https://linky.com")!))
818823
]
819824
)
825+
],
826+
callbacks: [
827+
"callbacks1": .reference(.component(named: "callbacks1"))
820828
]
821829
)
822830
)
@@ -851,6 +859,9 @@ final class BuiltinValidationTests: XCTestCase {
851859
links: [
852860
"link1": .init(operationId: "op 1")
853861
],
862+
callbacks: [
863+
"callbacks1": .init()
864+
],
854865
pathItems: [
855866
"path1": .init()
856867
]

0 commit comments

Comments
 (0)