This repository has been archived by the owner on Apr 20, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #2 from nodes-vapor/validation
Validation
- Loading branch information
Showing
8 changed files
with
270 additions
and
95 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
os: | ||
- linux | ||
language: generic | ||
sudo: required | ||
dist: trusty | ||
script: | ||
- eval "$(curl -sL https://swift.vapor.sh/ci)" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,31 @@ | ||
import Vapor | ||
|
||
/// A request-extractable `Model`. | ||
public protocol Sanitizable { | ||
/// Fields that are permitted to be deserialized from a Request's JSON. | ||
static var permitted: [String] { get } | ||
|
||
/// Override the error thrown when a `Model` fails to initialize. | ||
static func updateThrownError(_ error: Error) -> AbortError | ||
|
||
/// Validate the Request's JSON before constructing a Model. | ||
/// Useful for checking if fields exist. | ||
static func preValidate(data: JSON) throws | ||
|
||
/// Validate all deserialized fields. | ||
func postValidate() throws | ||
} | ||
|
||
extension Sanitizable { | ||
public static func updateThrownError(_ error: Error) -> AbortError { | ||
return Abort.badRequest | ||
} | ||
|
||
public static func preValidate(data: JSON) throws { | ||
|
||
} | ||
|
||
public func postValidate() throws { | ||
|
||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,7 @@ | ||
import XCTest | ||
@testable import sanitizedTests | ||
|
||
@testable import SanitizedTests | ||
|
||
XCTMain([ | ||
testCase(sanitizedTests.allTests), | ||
testCase(SanitizedTests.allTests), | ||
]) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,166 @@ | ||
import XCTest | ||
import HTTP | ||
import Vapor | ||
|
||
@testable import Sanitized | ||
|
||
class SanitizedTests: XCTestCase { | ||
static var allTests = [ | ||
("testBasic", testBasic), | ||
("testBasicFailed", testBasicFailed), | ||
("testPreValidateError", testPreValidateError), | ||
("testPostValidateError", testPostValidateError), | ||
("testPermitted", testPermitted), | ||
("testEmptyPermitted", testEmptyPermitted), | ||
] | ||
|
||
func testBasic() { | ||
let request = buildRequest(body: [ | ||
"id": 1, | ||
"name": "Brett", | ||
"email": "[email protected]" | ||
]) | ||
|
||
expectNoThrow() { | ||
let model: TestModel = try request.extractModel() | ||
XCTAssertNil(model.id) | ||
XCTAssertEqual(model.name, "Brett") | ||
XCTAssertEqual(model.email, "[email protected]") | ||
} | ||
} | ||
|
||
func testBasicFailed() { | ||
let request = buildInvalidRequest() | ||
expect(toThrow: Abort.badRequest) { | ||
let _: TestModel = try request.extractModel() | ||
} | ||
} | ||
|
||
func testPreValidateError() { | ||
let request = buildRequest(body: [ | ||
"email": "[email protected]" | ||
]) | ||
|
||
expect(toThrow: Abort.custom(status: .badRequest, message: "No name provided.")) { | ||
let _: TestModel = try request.extractModel() | ||
} | ||
} | ||
|
||
func testPostValidateError() { | ||
let request = buildRequest(body: [ | ||
"id": 1, | ||
"name": "Brett", | ||
"email": "[email protected]" | ||
]) | ||
|
||
let expectedError = Abort.custom( | ||
status: .badRequest, | ||
message: "Email must be longer than 8 characters." | ||
) | ||
|
||
expect(toThrow: expectedError) { | ||
let _: TestModel = try request.extractModel() | ||
} | ||
} | ||
|
||
func testPermitted() { | ||
let json = JSON([ | ||
"id": 1, | ||
"name": "Brett", | ||
"email": "[email protected]" | ||
]) | ||
|
||
let result = json.permit(["name"]) | ||
XCTAssertNil(result["id"]) | ||
XCTAssertEqual(result["name"]?.string, "Brett") | ||
XCTAssertNil(result["email"]) | ||
} | ||
|
||
func testEmptyPermitted() { | ||
let json = JSON([ | ||
"id": 1, | ||
"name": "Brett", | ||
"email": "[email protected]" | ||
]) | ||
|
||
let result = json.permit([]) | ||
XCTAssertNil(result["id"]) | ||
XCTAssertNil(result["name"]) | ||
XCTAssertNil(result["email"]) | ||
} | ||
} | ||
|
||
extension SanitizedTests { | ||
func buildRequest(body: Node) -> Request { | ||
let body = try! JSON(node: body).makeBytes() | ||
|
||
return try! Request( | ||
method: .post, | ||
uri: "/test", | ||
headers: [ | ||
"Content-Type": "application/json" | ||
], | ||
body: .data(body) | ||
) | ||
} | ||
|
||
func buildInvalidRequest() -> Request { | ||
return try! Request( | ||
method: .post, | ||
uri: "/test" | ||
) | ||
} | ||
} | ||
|
||
struct TestModel: Model, Sanitizable { | ||
var id: Node? | ||
|
||
var name: String | ||
var email: String | ||
|
||
static var permitted = ["name", "email"] | ||
|
||
init(node: Node, in context: Context) throws { | ||
id = node["id"] | ||
name = try node.extract("name") | ||
email = try node.extract("email") | ||
} | ||
|
||
func makeNode(context: Context) throws -> Node { | ||
return .null | ||
} | ||
|
||
static func prepare(_ database: Database) throws {} | ||
static func revert(_ database: Database) throws {} | ||
} | ||
|
||
extension TestModel { | ||
static func updateThrownError(_ error: Error) -> AbortError { | ||
return Abort.custom(status: .badRequest, message: "Username not provided.") | ||
} | ||
|
||
static func preValidate(data: JSON) throws { | ||
guard data["name"]?.string != nil else { | ||
throw Abort.custom(status: .badRequest, message: "No name provided.") | ||
} | ||
|
||
guard data["email"]?.string != nil else { | ||
throw Abort.custom(status: .badRequest, message: "No email provided.") | ||
} | ||
} | ||
|
||
func postValidate() throws { | ||
guard email.count > 8 else { | ||
throw Abort.custom( | ||
status: .badRequest, | ||
message: "Email must be longer than 8 characters." | ||
) | ||
} | ||
} | ||
} | ||
|
||
extension Abort: Equatable { | ||
static public func ==(lhs: Abort, rhs: Abort) -> Bool { | ||
return lhs.code == rhs.code && lhs.message == rhs.message | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.