diff --git a/Sources/Sanitizable+Request.swift b/Sources/Sanitizable+Request.swift index 4e73c81..dbf07e5 100644 --- a/Sources/Sanitizable+Request.swift +++ b/Sources/Sanitizable+Request.swift @@ -12,12 +12,31 @@ extension Request { /// /// - Returns: The extracted, sanitized `Model`. public func extractModel() throws -> M where M: Sanitizable { + return try extractModel(injecting: .null) + } + + + /// Extracts a `Model` from the Request's JSON, first by adding/overriding + /// the given values and next stripping sensitive fields. + /// + /// - Parameter values: Values to set before sanitizing. + /// - Returns: The extracted, sanitized `Model`. + /// - Throws: + /// - badRequest: Thrown when the request doesn't have a JSON body. + /// - updateErrorThrown: `Sanitizable` models have the ability to override + /// the error thrown when a model fails to instantiate. + public func extractModel(injecting values: Node) throws -> M where M: Sanitizable { guard let json = self.json else { throw Abort.badRequest } - - let sanitized = json.permit(M.permitted) - + + var node = json.makeNode() + values.nodeObject?.forEach { key, value in + node[key] = value + } + + let sanitized = try JSON(node: node).permit(M.permitted) + try M.preValidate(data: sanitized) let model: M diff --git a/Tests/SanitizedTests/SanitizedTests.swift b/Tests/SanitizedTests/SanitizedTests.swift index 79e5619..cf8aacb 100644 --- a/Tests/SanitizedTests/SanitizedTests.swift +++ b/Tests/SanitizedTests/SanitizedTests.swift @@ -13,7 +13,10 @@ class SanitizedTests: XCTestCase { ("testPermitted", testPermitted), ("testEmptyPermitted", testEmptyPermitted), ] - + + + // MARK: - Extraction. + func testBasic() { let request = buildRequest(body: [ "id": 1, @@ -28,14 +31,71 @@ class SanitizedTests: XCTestCase { XCTAssertEqual(model.email, "test@tested.com") } } - + + func testBasicFailed() { let request = buildInvalidRequest() expect(toThrow: Abort.badRequest) { let _: TestModel = try request.extractModel() } } - + + + // MARK: - Injection. + + func testInjectingNewKeys() { + let request = buildRequest(body: [ + "id": 1, + "name": "Brett" + ]) + + expectNoThrow() { + let model: TestModel = try request.extractModel( + injecting: ["email": "test@tested.com"] + ) + XCTAssertNil(model.id) + XCTAssertEqual(model.name, "Brett") + XCTAssertEqual(model.email, "test@tested.com") + } + } + + func testOverridingKeys() { + let request = buildRequest(body: [ + "id": 1, + "name": "Brett", + "email": "test@tested.com" + ]) + + expectNoThrow() { + let model: TestModel = try request.extractModel( + injecting: ["email": "test@doubletested.com"] + ) + XCTAssertNil(model.id) + XCTAssertEqual(model.name, "Brett") + XCTAssertEqual(model.email, "test@doubletested.com") + } + } + + func testInjectingSanitizedKeys() { + let request = buildRequest(body: [ + "id": 1, + "name": "Brett", + "email": "test@tested.com" + ]) + + expectNoThrow() { + let model: TestModel = try request.extractModel( + injecting: ["id": 1337] + ) + XCTAssertNil(model.id) + XCTAssertEqual(model.name, "Brett") + XCTAssertEqual(model.email, "test@tested.com") + } + } + + + // MARK: - Validation. + func testPreValidateError() { let request = buildRequest(body: [ "email": "test@tested.com" @@ -62,7 +122,10 @@ class SanitizedTests: XCTestCase { let _: TestModel = try request.extractModel() } } - + + + // MARK: - Permitted fields. + func testPermitted() { let json = JSON([ "id": 1, @@ -88,7 +151,10 @@ class SanitizedTests: XCTestCase { XCTAssertNil(result["name"]) XCTAssertNil(result["email"]) } - + + + // MARK: - Patching. + func testPatchBasic() { let model = try! TestModel(node: [ "id": 15,