Skip to content
This repository has been archived by the owner on Apr 20, 2024. It is now read-only.

Commit

Permalink
Merge pull request #35 from valen90/feature/validators
Browse files Browse the repository at this point in the history
Feature/validators
  • Loading branch information
steffendsommer authored Jun 7, 2017
2 parents 7841962 + 387d805 commit 0a1fd86
Show file tree
Hide file tree
Showing 12 changed files with 242 additions and 98 deletions.
4 changes: 4 additions & 0 deletions Sources/Sugar/Validators/DateValidatable.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import Vapor
import Validation

extension Date: Validatable {}
19 changes: 19 additions & 0 deletions Sources/Sugar/Validators/Different.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import Vapor
import Validation

/// Validates that matches a given input
public struct Different<T>: Validator where T: Validatable, T: Equatable {
/// The value expected
public let expectation: T

/// Initialize a validator with the expected value
public init(_ expectation: T) {
self.expectation = expectation
}

public func validate(_ input: T) throws {
guard input != expectation else {
throw error("\(input) does not equal expectation \(expectation)")
}
}
}
30 changes: 0 additions & 30 deletions Sources/Sugar/Validators/FieldExist.swift

This file was deleted.

31 changes: 31 additions & 0 deletions Sources/Sugar/Validators/IPAddress.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import Vapor
import Validation

private let ipv4Pattern = "(([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\.){3}([01]?\\d\\d?|2[0-4]\\d|25[0-5])"

private let ipv6Pattern =
"(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))"

public enum IPAddress: Validator {
case ipv4
case ipv6
case ip


public func validate(_ input: String) throws {
switch self {
case .ipv4 where input.range(of: ipv4Pattern, options: .regularExpression) != nil :
break
case .ipv6 where input.range(of: ipv6Pattern, options: .regularExpression) != nil :
break
case .ip where input.range(of: ipv6Pattern, options: .regularExpression) != nil || input.range(of: ipv4Pattern, options: .regularExpression) != nil :
break
default:
throw Abort(
.badRequest,
metadata: nil,
reason: "Not valid IP address"
)
}
}
}
17 changes: 17 additions & 0 deletions Sources/Sugar/Validators/JSON.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import Vapor
import Validation

public struct JSONValidator: Validator {

public func validate(_ input: String) throws {
do {
_ = try JSON(bytes: input.makeBytes())
} catch {
throw Abort(
.badRequest,
metadata: nil,
reason: "Not valid JSON"
)
}
}
}
15 changes: 15 additions & 0 deletions Sources/Sugar/Validators/Numeric.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import Vapor
import Validation

public struct Numeric: Validator {

public func validate(_ input: String) throws {
guard Double(input) != nil else {
throw Abort(
.badRequest,
metadata: nil,
reason: "Not numeric"
)
}
}
}
38 changes: 38 additions & 0 deletions Sources/Sugar/Validators/Required.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import Vapor
import Validation

public struct Required: Validator {
public typealias Input = Node?

public func validate(_ input: Node?) throws {
guard let input = input else {
throw Abort(
.badRequest,
metadata: nil,
reason: "The value is null."
)
}

if let string = input.string {
guard !string.isEmpty else {
throw Abort(
.badRequest,
metadata: nil,
reason: "The value is an empty string."
)
}
}

if let array = input.array {
guard array.count > 0 else {
throw Abort(
.badRequest,
metadata: nil,
reason: "The value is an empty array."
)
}
}
}
}

extension Optional: Validatable {}
18 changes: 18 additions & 0 deletions Sources/Sugar/Validators/URL.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import Vapor
import Validation

private let regex = "^(?:(?:https?|ftp):\\/\\/)(?:\\S+(?::\\S*)?@)?(?:(?!(?:10|127)(?:\\.\\d{1,3}){3})(?!(?:169\\.254|192\\.168)(?:\\.\\d{1,3}){2})(?!172\\.(?:1[6-9]|2\\d|3[0-1])(?:\\.\\d{1,3}){2})(?:[1-9]\\d?|1\\d\\d|2[01]\\d|22[0-3])(?:\\.(?:1?\\d{1,2}|2[0-4]\\d|25[0-5])){2}(?:\\.(?:[1-9]\\d?|1\\d\\d|2[0-4]\\d|25[0-4]))|(?:(?:[a-z\\u00a1-\\uffff0-9]-*)*[a-z\\u00a1-\\uffff0-9]+)(?:\\.(?:[a-z\\u00a1-\\uffff0-9]-*)*[a-z\\u00a1-\\uffff0-9]+)*(?:\\.(?:[a-z\\u00a1-\\uffff]{2,}))\\.?)(?::\\d{2,5})?(?:[/?#]\\S*)?$"

public struct URL: Validator {

public func validate(_ input: String) throws {
guard input.range(of: regex, options: .regularExpression) != nil else {
throw Abort(
.badRequest,
metadata: nil,
reason: "Not valid URL"
)
}

}
}
55 changes: 0 additions & 55 deletions Sources/Sugar/Validators/UniqueField.swift

This file was deleted.

3 changes: 2 additions & 1 deletion Tests/LinuxMain.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,6 @@ import XCTest
XCTMain([
testCase(DatabaseMySQLTests.allTests),
testCase(DateSugarTests.allTests),
testCase(StringSugarTests.allTests)
testCase(StringSugarTests.allTests),
testCase(ValidatorsTests.allTests)
])
14 changes: 2 additions & 12 deletions Tests/SugarTests/StringSugarTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,10 @@ import Foundation

class StringSugarTests: XCTestCase {

static let allTest = [
static var allTests = [
("testThatUrlEncodingWithDanishCharactersWorks", testThatUrlEncodingWithDanishCharactersWorks),
("testThatUrlEncodingWithSpacesWorks", testThatUrlEncodingWithSpacesWorks),
("testThatStrongPasswordValidatorWorks", testThatStrongPasswordValidatorWorks)
]
]

func testThatUrlEncodingWithDanishCharactersWorks() {
let url = "æøå"
Expand All @@ -21,15 +20,6 @@ class StringSugarTests: XCTestCase {
let encoded = try! url.urlEncoded()
XCTAssertEqual(encoded, "%C3%A6%20%C3%B8%20%C3%A5")
}

func testThatStrongPasswordValidatorWorks() {
XCTAssertThrowsError(try StrongPassword().validate("password")) // Only one category
XCTAssertThrowsError(try StrongPassword().validate("p4ssw0rd")) // Only two categories
XCTAssertThrowsError(try StrongPassword().validate("Aa1!")) // Three categories, but too short
XCTAssertNoThrow(try StrongPassword().validate("p@ssw0rd")) // Three categories, and long enough

}
}



96 changes: 96 additions & 0 deletions Tests/SugarTests/ValidatorsTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import XCTest
import Validation
@testable import Sugar

class ValidatorsTests: XCTestCase {

static let allTests = [
("testThatStrongPasswordValidatorWorks", testThatStrongPasswordValidatorWorks),
("testThatURLValidatorWorks", testThatURLValidatorWorks),
("testThatNumericValidatorWorks", testThatNumericValidatorWorks),
("testThatIPv4AddressValidatorWorks", testThatIPv4AddressValidatorWorks),
("testThatIPv6AddressValidatorWorks", testThatIPv6AddressValidatorWorks),
("testThatGenericIPAddressValidatorWorks", testThatGenericIPAddressValidatorWorks),
("testThatRequiredValidatorWorks", testThatRequiredValidatorWorks),
("testThatJSONValidatorWorks", testThatJSONValidatorWorks),
("testThatDifferentValidatorWorks", testThatDifferentValidatorWorks)
]

func testThatStrongPasswordValidatorWorks() {
XCTAssertThrowsError(try StrongPassword().validate("password")) // Only one category
XCTAssertThrowsError(try StrongPassword().validate("p4ssw0rd")) // Only two categories
XCTAssertThrowsError(try StrongPassword().validate("Aa1!")) // Three categories, but too short
XCTAssertNoThrow(try StrongPassword().validate("p@ssw0rd")) // Three categories, and long enough

}

func testThatURLValidatorWorks() {
XCTAssertNoThrow(try URL().validate("ftp://www.website.com"))
XCTAssertNoThrow(try URL().validate("https://www.website.com"))
XCTAssertNoThrow(try URL().validate("http://www.url-with-querystring.com/?url=has-querystring"))
XCTAssertThrowsError(try URL().validate("notvalidurl"))
XCTAssertThrowsError(try URL().validate("http://www.web site.com"))
}

func testThatNumericValidatorWorks() {
XCTAssertThrowsError(try Numeric().validate("notNumber"))
XCTAssertNoThrow(try Numeric().validate("1337"))
XCTAssertNoThrow(try Numeric().validate("1337.1337"))
}

func testThatIPv4AddressValidatorWorks() {
XCTAssertNoThrow(try IPAddress.ipv4.validate("0.0.0.0"))
XCTAssertNoThrow(try IPAddress.ipv4.validate("255.255.255.255"))
XCTAssertThrowsError(try IPAddress.ipv4.validate("300.300.300.300")) // Values out of range
XCTAssertThrowsError(try IPAddress.ipv4.validate("255.255.255")) // Missing values
}

func testThatIPv6AddressValidatorWorks() {
XCTAssertNoThrow(try IPAddress.ipv6.validate("::"))
XCTAssertNoThrow(try IPAddress.ipv6.validate("2001::25de::cade"))
XCTAssertNoThrow(try IPAddress.ipv6.validate("2001:0DB8::1428:57ab"))
XCTAssertNoThrow(try IPAddress.ipv6.validate("2001:0DB8:0000:0000:0000:0000:1428:57ab"))
XCTAssertThrowsError(try IPAddress.ipv6.validate("255.255.255.255"))
XCTAssertThrowsError(try IPAddress.ipv6.validate("xxxx:"))
}

func testThatGenericIPAddressValidatorWorks() {
XCTAssertNoThrow(try IPAddress.ip.validate("0.0.0.0"))
XCTAssertNoThrow(try IPAddress.ip.validate("255.255.255.255"))
XCTAssertNoThrow(try IPAddress.ip.validate("2001:0DB8::1428:57ab"))
XCTAssertNoThrow(try IPAddress.ip.validate("::"))
}

func testThatRequiredValidatorWorks() {
XCTAssertThrowsError(try Required().validate(""))
XCTAssertThrowsError(try Required().validate([]))
XCTAssertThrowsError(try Required().validate(nil))
XCTAssertNoThrow(try Required().validate("notEmpty"))
XCTAssertNoThrow(try Required().validate(["notEmpty"]))
XCTAssertNoThrow(try Required().validate(1))
}

func testThatJSONValidatorWorks() {
XCTAssertNoThrow(try JSONValidator().validate(
"{\n" +
"\"boolean\": true,\n" +
"\"object\": {\n" +
"\"a\": \"b\",\n" +
"\"c\": \"d\",\n" +
"\"e\": \"f\"\n" +
"},\n" +
"\"array\": [1 , 2],\n" +
"\"string\": \"Hello World\"\n" +
"}"
))
XCTAssertThrowsError(try JSONValidator().validate("{notAValidJSON: notValid}"))
}

func testThatDifferentValidatorWorks() {
let collection = 1
XCTAssertNoThrow(try collection.tested(by: Different(2)))
XCTAssertThrowsError(try collection.tested(by: Different(1)))
}

}

0 comments on commit 0a1fd86

Please sign in to comment.