Skip to content

Commit

Permalink
move codingPath into a parent protocol and use it more widely between…
Browse files Browse the repository at this point in the history
… validation and other OpenAPI errors. see if duplicate validation errors are due to an extra pass at the document that is no longer necessary for some reason (fix in Codable, perhaps).
  • Loading branch information
mattpolzin committed Sep 5, 2023
1 parent 352a9a0 commit bb29fb9
Show file tree
Hide file tree
Showing 6 changed files with 31 additions and 14 deletions.
3 changes: 2 additions & 1 deletion Sources/OpenAPIKit/Validator/Validation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ public struct Validation<Subject: Validatable> {

/// Validation errors are just a textual reason for validation failure and
/// a coding path where the validation error occurred.
public struct ValidationError: Swift.Error, CustomStringConvertible {
public struct ValidationError: Swift.Error, CustomStringConvertible, PathContextError {
/// The reason for the validation failure.
public let reason: String
/// The location where the failure occurred.
Expand Down Expand Up @@ -149,6 +149,7 @@ public struct ValidationErrorCollection: Swift.Error, CustomStringConvertible, E
public var description: String { localizedDescription }

public var swiftErrors: [Swift.Error] { values }
public var pathContextErrors: [PathContextError] { values }
}

/// Erases the type on which a `Validator` is specialized and combines
Expand Down
2 changes: 1 addition & 1 deletion Sources/OpenAPIKit/Validator/Validator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ extension OpenAPI.Document {
// and then encoding with the single value container.
// After this, validations are only applied by keyed/unkeyed containers and
// by the leaf node methods of the single value container.
validator.applyValidations(to: self)
// validator.applyValidations(to: self)
try container.encode(self)

let errors: [ValidationError]
Expand Down
3 changes: 2 additions & 1 deletion Sources/OpenAPIKit30/Validator/Validation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ public struct Validation<Subject: Validatable> {

/// Validation errors are just a textual reason for validation failure and
/// a coding path where the validation error occurred.
public struct ValidationError: Swift.Error, CustomStringConvertible {
public struct ValidationError: Swift.Error, CustomStringConvertible, PathContextError {
/// The reason for the validation failure.
public let reason: String
/// The location where the failure occurred.
Expand Down Expand Up @@ -149,6 +149,7 @@ public struct ValidationErrorCollection: Swift.Error, CustomStringConvertible, E
public var description: String { localizedDescription }

public var swiftErrors: [Swift.Error] { values }
public var pathContextErrors: [PathContextError] { values }
}

/// Erases the type on which a `Validator` is specialized and combines
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,16 @@ public enum ErrorCategory {
}
}

public protocol OpenAPIError: Swift.Error, CustomStringConvertible {
public protocol PathContextError {
/// The complete coding path for where the error occurred.
///
/// This will often overlap with the `contextString` but there is not
/// a 1-1 relationship between the two. This is the same concept of
/// "codingPath" as is used elsewhere for the Swift `Codable` featureset.
var codingPath: [CodingKey] { get }
}

public protocol OpenAPIError: Swift.Error, CustomStringConvertible, PathContextError {
/// The subject of the error (i.e. the thing being worked with
/// when the error occurred).
///
Expand All @@ -52,12 +61,6 @@ public protocol OpenAPIError: Swift.Error, CustomStringConvertible {
/// key in Document.info but it is missing." the **category** is
/// `.missing(.key)`.
var errorCategory: ErrorCategory { get }
/// The complete coding path for where the error occurred.
///
/// This will often overlap with the `contextString` but there is not
/// a 1-1 relationship between the two. This is the same concept of
/// "codingPath" as is used elsewhere for the Swift `Codable` featureset.
var codingPath: [CodingKey] { get }
}

public extension OpenAPIError {
Expand Down Expand Up @@ -121,6 +124,7 @@ public extension OpenAPIError {

public protocol ErrorCollection {
var swiftErrors: [Swift.Error] { get }
var pathContextErrors: [PathContextError] { get }

var localizedDescription: String { get }
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,12 @@ public struct Error: Swift.Error, CustomStringConvertible {

} else if let errorCollection = underlyingError as? ErrorCollection {
localizedDescription = errorCollection.localizedDescription
codingPath = []
if errorCollection.pathContextErrors.count == 1,
let error = errorCollection.pathContextErrors.first {
codingPath = error.codingPath
} else {
codingPath = []
}

} else {
localizedDescription = underlyingError.localizedDescription
Expand Down
12 changes: 9 additions & 3 deletions Tests/OpenAPIKitTests/Validator/BuiltinValidationTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,14 @@ final class BuiltinValidationTests: XCTestCase {
let validator = Validator.blank.validating(.documentContainsPaths)

XCTAssertThrowsError(try document.validate(using: validator)) { error in
let error = error as? ValidationErrorCollection
XCTAssertEqual(error?.values.first?.reason, "Failed to satisfy: Document contains at least one path")
XCTAssertEqual(error?.values.first?.codingPath.map { $0.stringValue }, [])
let errorCollection = error as? ValidationErrorCollection
XCTAssertEqual(errorCollection?.values.first?.reason, "Failed to satisfy: Document contains at least one path")
XCTAssertEqual(errorCollection?.values.first?.codingPath.map { $0.stringValue }, [])
XCTAssertEqual(errorCollection?.values.count, 1)

let openAPIError = OpenAPI.Error(from: error)
XCTAssertEqual(openAPIError.localizedDescription, "Failed to satisfy: Document contains at least one path at root of document")
XCTAssertEqual(openAPIError.codingPath.map { $0.stringValue }, [])
}
}

Expand Down Expand Up @@ -58,6 +63,7 @@ final class BuiltinValidationTests: XCTestCase {
let error = error as? ValidationErrorCollection
XCTAssertEqual(error?.values.first?.reason, "Failed to satisfy: Paths contain at least one operation")
XCTAssertEqual(error?.values.first?.codingPath.map { $0.stringValue }, ["paths", "/hello/world"])
XCTAssertEqual(error?.values.count, 1)
}
}

Expand Down

0 comments on commit bb29fb9

Please sign in to comment.