Skip to content
This repository was archived by the owner on Nov 16, 2020. It is now read-only.

Make error validation details public, and option to customise the error message #33

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
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
18 changes: 13 additions & 5 deletions Sources/Validation/ValidationError.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import Debugging
public protocol ValidationError: Debuggable {
/// Key path to the invalid data.
var path: [String] { get set }
var customMessage: String? { get set }
}

extension ValidationError {
Expand All @@ -22,20 +23,27 @@ extension ValidationError {
public struct BasicValidationError: ValidationError {
/// See `Debuggable`
public var reason: String {
let path: String
if self.path.count > 0 {
path = "'" + self.path.joined(separator: ".") + "'"
if let customMessage = customMessage {
return customMessage
} else {
path = "data"
let path: String
if self.path.count > 0 {
path = "'" + self.path.joined(separator: ".") + "'"
} else {
path = "data"
}
return "\(path) \(message)"
}
return "\(path) \(message)"
}

/// The validation failure
public var message: String

/// Key path the validation error happened at
public var path: [String]

/// See ValidationError.customMessage
public var customMessage: String?

/// Create a new JWT error
public init(_ message: String) {
Expand Down
30 changes: 19 additions & 11 deletions Sources/Validation/Validations.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,9 @@ public struct Validations<M>: CustomStringConvertible where M: Validatable {
/// - keyPath: `KeyPath` to validatable property.
/// - path: Readable path. Will be displayed when showing errors.
/// - validation: `Validation` to run on this property.
public mutating func add<T>(_ keyPath: KeyPath<M, T>, at path: [String], _ validator: Validator<T>) {
add(keyPath, at: path, "is " + validator.readable, { value in
/// - customMessage: Custom error message.
public mutating func add<T>(_ keyPath: KeyPath<M, T>, at path: [String], _ validator: Validator<T>, customMessage: String? = nil) {
add(keyPath, at: path, "is " + validator.readable, customMessage: customMessage, { value in
try validator.validate(value)
})
}
Expand All @@ -37,13 +38,15 @@ public struct Validations<M>: CustomStringConvertible where M: Validatable {
/// - keyPath: `KeyPath` to validatable property.
/// - path: Readable path. Will be displayed when showing errors.
/// - readable: Readable string describing this validation.
/// - customMessage: Custom error message.
/// - custom: Closure accepting the `KeyPath`'s value. Throw a `ValidationError` here if the data is invalid.
public mutating func add<T>(_ keyPath: KeyPath<M, T>, at path: [String], _ readable: String, _ custom: @escaping (T) throws -> Void) {
public mutating func add<T>(_ keyPath: KeyPath<M, T>, at path: [String], _ readable: String, customMessage: String? = nil, _ custom: @escaping (T) throws -> Void) {
add("\(path.joined(separator: ".")): \(readable)") { model in
do {
try custom(model[keyPath: keyPath])
} catch var error as ValidationError {
error.path += path
error.customMessage = customMessage
throw error
}
}
Expand Down Expand Up @@ -91,8 +94,9 @@ extension Validations where M: Reflectable {
/// - parameters:
/// - keyPath: `KeyPath` to validatable property.
/// - validation: `Validation` to run on this property.
public mutating func add<T>(_ keyPath: KeyPath<M, T>, _ validator: Validator<T>) throws {
try add(keyPath, at: M.reflectProperty(forKey: keyPath)?.path ?? [], validator)
/// - customMessage: Custom error message.
public mutating func add<T>(_ keyPath: KeyPath<M, T>, _ validator: Validator<T>, customMessage: String? = nil) throws {
try add(keyPath, at: M.reflectProperty(forKey: keyPath)?.path ?? [], validator, customMessage: customMessage)
}


Expand All @@ -105,24 +109,28 @@ extension Validations where M: Reflectable {
/// - parameters:
/// - keyPath: `KeyPath` to validatable property.
/// - readable: Readable string describing this validation.
/// - customMessage: Custom error message.
/// - custom: Closure accepting the `KeyPath`'s value. Throw a `ValidationError` here if the data is invalid.
public mutating func add<T>(_ keyPath: KeyPath<M, T>, _ readable: String, _ custom: @escaping (T) throws -> Void) throws {
try add(keyPath, at: M.reflectProperty(forKey: keyPath)?.path ?? [], readable, custom)
public mutating func add<T>(_ keyPath: KeyPath<M, T>, _ readable: String, customMessage: String? = nil, _ custom: @escaping (T) throws -> Void) throws {
try add(keyPath, at: M.reflectProperty(forKey: keyPath)?.path ?? [], readable, customMessage: customMessage, custom)
}
}

// MARK: Private

/// A collection of errors thrown by validatable models validations
fileprivate struct ValidateErrors: ValidationError {
public struct ValidateErrors: ValidationError {
/// the errors thrown
var errors: [ValidationError]
public var errors: [ValidationError]

/// See ValidationError.keyPath
var path: [String]
public var path: [String]

/// See ValidationError.customMessage
public var customMessage: String?

/// See ValidationError.reason
var reason: String {
public var reason: String {
return errors.map { error in
var mutableError = error
mutableError.path = path + error.path
Expand Down
13 changes: 8 additions & 5 deletions Sources/Validation/Validators/AndValidator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -48,15 +48,15 @@ fileprivate struct AndValidator<T>: ValidatorType {
}

/// Error thrown if and validation fails
fileprivate struct AndValidatorError: ValidationError {
public struct AndValidatorError: ValidationError {
/// error thrown by left validator
let left: ValidationError?
public let left: ValidationError?

/// error thrown by right validator
let right: ValidationError?
public let right: ValidationError?

/// See ValidationError.reason
var reason: String {
public var reason: String {
if let left = left, let right = right {
var mutableLeft = left, mutableRight = right
mutableLeft.path = path + left.path
Expand All @@ -76,7 +76,10 @@ fileprivate struct AndValidatorError: ValidationError {
}

/// See ValidationError.keyPath
var path: [String]
public var path: [String]

/// See ValidationError.customMessage
public var customMessage: String?

/// Creates a new or validator error
init(_ left: ValidationError?, _ right: ValidationError?) {
Expand Down
13 changes: 8 additions & 5 deletions Sources/Validation/Validators/OrValidator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -43,15 +43,15 @@ fileprivate struct OrValidator<T>: ValidatorType {
}

/// Error thrown if or validation fails
fileprivate struct OrValidatorError: ValidationError {
public struct OrValidatorError: ValidationError {
/// error thrown by left validator
let left: ValidationError
public let left: ValidationError

/// error thrown by right validator
let right: ValidationError
public let right: ValidationError

/// See ValidationError.reason
var reason: String {
public var reason: String {
var left = self.left
left.path = self.path + self.left.path
var right = self.right
Expand All @@ -60,7 +60,10 @@ fileprivate struct OrValidatorError: ValidationError {
}

/// See ValidationError.keyPath
var path: [String]
public var path: [String]

/// See ValidationError.customMessage
public var customMessage: String?

/// Creates a new or validator error
init(_ left: ValidationError, _ right: ValidationError) {
Expand Down