Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions Sources/OpenAPIKit/Validator/Validation+Builtins.swift
Original file line number Diff line number Diff line change
Expand Up @@ -408,6 +408,28 @@ extension Validation {
)
}

/// Validate that all non-external Callbacks references are found in the document's
/// components dictionary.
///
/// - Important: This is included in validation by default.
///
public static var callbacksReferencesAreValid: Validation<OpenAPI.Reference<OpenAPI.Callbacks>> {
.init(
description: "Callbacks reference can be found in components/callbacks",
check: { context in
guard case let .internal(internalReference) = context.subject.jsonReference,
case .component = internalReference else {
// don't make assertions about external references
// TODO: could make a stronger assertion including
// internal references outside of components given
// some way to resolve those references.
return true
}
return context.document.components.contains(internalReference)
}
)
}

/// Validate that all non-external PathItem references are found in the document's
/// components dictionary.
///
Expand Down
1 change: 1 addition & 0 deletions Sources/OpenAPIKit/Validator/Validator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,7 @@ public final class Validator {
.init(.requestReferencesAreValid),
.init(.headerReferencesAreValid),
.init(.linkReferencesAreValid),
.init(.callbacksReferencesAreValid),
.init(.pathItemReferencesAreValid),
.init(.serverVariableEnumIsValid),
.init(.serverVariableDefaultExistsInEnum)
Expand Down
22 changes: 22 additions & 0 deletions Sources/OpenAPIKit30/Validator/Validation+Builtins.swift
Original file line number Diff line number Diff line change
Expand Up @@ -397,6 +397,28 @@ extension Validation {
)
}

/// Validate that all non-external Callbacks references are found in the document's
/// components dictionary.
///
/// - Important: This is included in validation by default.
///
public static var callbacksReferencesAreValid: Validation<JSONReference<OpenAPI.Callbacks>> {
.init(
description: "Callbacks reference can be found in components/callbacks",
check: { context in
guard case let .internal(internalReference) = context.subject,
case .component = internalReference else {
// don't make assertions about external references
// TODO: could make a stronger assertion including
// internal references outside of components given
// some way to resolve those references.
return true
}
return context.document.components.contains(internalReference)
}
)
}

/// Validate the OpenAPI Document's `Links` with operationIds refer to
/// Operations that exist in the document.
///
Expand Down
3 changes: 2 additions & 1 deletion Sources/OpenAPIKit30/Validator/Validator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,8 @@ public final class Validator {
.init(.exampleReferencesAreValid),
.init(.requestReferencesAreValid),
.init(.headerReferencesAreValid),
.init(.linkReferencesAreValid)
.init(.linkReferencesAreValid),
.init(.callbacksReferencesAreValid)
])
}

Expand Down
13 changes: 12 additions & 1 deletion Tests/OpenAPIKit30Tests/Validator/BuiltinValidationTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -638,6 +638,9 @@ final class BuiltinValidationTests: XCTestCase {
],
links: ["linky": .reference(.component(named: "link1"))]
)
],
callbacks: [
"callbacks1": .reference(.component(named: "callbacks1"))
]
)
)
Expand All @@ -654,7 +657,7 @@ final class BuiltinValidationTests: XCTestCase {
// NOTE this is part of default validation
XCTAssertThrowsError(try document.validate()) { error in
let error = error as? ValidationErrorCollection
XCTAssertEqual(error?.values.count, 7)
XCTAssertEqual(error?.values.count, 8)
XCTAssertEqual(error?.values[0].reason, "Failed to satisfy: Parameter reference can be found in components/parameters")
XCTAssertEqual(error?.values[0].codingPathString, ".paths['/hello'].get.parameters[0]")
XCTAssertEqual(error?.values[1].reason, "Failed to satisfy: Request reference can be found in components/requestBodies")
Expand All @@ -669,6 +672,8 @@ final class BuiltinValidationTests: XCTestCase {
XCTAssertEqual(error?.values[5].codingPathString, ".paths['/hello'].get.responses.404.content['application/xml'].schema")
XCTAssertEqual(error?.values[6].reason, "Failed to satisfy: Link reference can be found in components/links")
XCTAssertEqual(error?.values[6].codingPathString, ".paths['/hello'].get.responses.404.links.linky")
XCTAssertEqual(error?.values[7].reason, "Failed to satisfy: Callbacks reference can be found in components/callbacks")
XCTAssertEqual(error?.values[7].codingPathString, ".paths['/hello'].get.callbacks.callbacks1")
}
}

Expand Down Expand Up @@ -711,6 +716,9 @@ final class BuiltinValidationTests: XCTestCase {
"linky2": .reference(.external(URL(string: "https://linky.com")!))
]
)
],
callbacks: [
"callbacks1": .reference(.component(named: "callbacks1"))
]
)
)
Expand Down Expand Up @@ -742,6 +750,9 @@ final class BuiltinValidationTests: XCTestCase {
],
links: [
"link1": .init(operationId: "op 1")
],
callbacks: [
"callbacks1": .init()
]
)
)
Expand Down
3 changes: 3 additions & 0 deletions Tests/OpenAPIKitCompatTests/DocumentConversionTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,9 @@ final class DocumentConversionTests: XCTestCase {
components: .init(
parameters: [
"test": .init(name: "referencedParam", context: .query, schema: .string)
],
callbacks: [
"other_callback": callbacks
]
)
)
Expand Down
17 changes: 14 additions & 3 deletions Tests/OpenAPIKitTests/Validator/BuiltinValidationTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -741,6 +741,9 @@ final class BuiltinValidationTests: XCTestCase {
],
links: ["linky": .reference(.component(named: "link1"))]
)
],
callbacks: [
"callbacks1": .reference(.component(named: "callbacks1"))
]
)
)
Expand All @@ -758,7 +761,7 @@ final class BuiltinValidationTests: XCTestCase {
// NOTE this is part of default validation
XCTAssertThrowsError(try document.validate()) { error in
let error = error as? ValidationErrorCollection
XCTAssertEqual(error?.values.count, 8)
XCTAssertEqual(error?.values.count, 9)
XCTAssertEqual(error?.values[0].reason, "Failed to satisfy: Parameter reference can be found in components/parameters")
XCTAssertEqual(error?.values[0].codingPathString, ".paths['/hello'].get.parameters[0]")
XCTAssertEqual(error?.values[1].reason, "Failed to satisfy: Request reference can be found in components/requestBodies")
Expand All @@ -773,8 +776,10 @@ final class BuiltinValidationTests: XCTestCase {
XCTAssertEqual(error?.values[5].codingPathString, ".paths['/hello'].get.responses.404.content['application/xml'].schema")
XCTAssertEqual(error?.values[6].reason, "Failed to satisfy: Link reference can be found in components/links")
XCTAssertEqual(error?.values[6].codingPathString, ".paths['/hello'].get.responses.404.links.linky")
XCTAssertEqual(error?.values[7].reason, "Failed to satisfy: PathItem reference can be found in components/pathItems")
XCTAssertEqual(error?.values[7].codingPathString, ".paths['/world']")
XCTAssertEqual(error?.values[7].reason, "Failed to satisfy: Callbacks reference can be found in components/callbacks")
XCTAssertEqual(error?.values[7].codingPathString, ".paths['/hello'].get.callbacks.callbacks1")
XCTAssertEqual(error?.values[8].reason, "Failed to satisfy: PathItem reference can be found in components/pathItems")
XCTAssertEqual(error?.values[8].codingPathString, ".paths['/world']")
}
}

Expand Down Expand Up @@ -817,6 +822,9 @@ final class BuiltinValidationTests: XCTestCase {
"linky2": .reference(.external(URL(string: "https://linky.com")!))
]
)
],
callbacks: [
"callbacks1": .reference(.component(named: "callbacks1"))
]
)
)
Expand Down Expand Up @@ -851,6 +859,9 @@ final class BuiltinValidationTests: XCTestCase {
links: [
"link1": .init(operationId: "op 1")
],
callbacks: [
"callbacks1": .init()
],
pathItems: [
"path1": .init()
]
Expand Down