diff --git a/api/pom.xml b/api/pom.xml index 40e5d0b..3df2ab1 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -160,6 +160,11 @@ hoplite-yaml ${hoplite_version} + + am.ik.yavi + yavi + 0.4.0 + com.github.javafaker javafaker diff --git a/api/src/routing/UserController.kt b/api/src/routing/UserController.kt index bbdbea9..4205f85 100644 --- a/api/src/routing/UserController.kt +++ b/api/src/routing/UserController.kt @@ -4,6 +4,8 @@ import be.vandewalleh.entities.User import be.vandewalleh.extensions.respondStatus import be.vandewalleh.extensions.userId import be.vandewalleh.services.UserService +import be.vandewalleh.validation.receiveValidated +import be.vandewalleh.validation.user.registerValidator import io.ktor.application.* import io.ktor.auth.* import io.ktor.http.* @@ -18,14 +20,9 @@ import java.time.LocalDateTime fun Routing.user(kodein: Kodein) { val userService by kodein.instance() - post("/user/test") { - val user = call.receive() - call.respond(user) - } - route("/user") { post { - val user = call.receive() + val user = call.receiveValidated(registerValidator) if (userService.userExists(user.username, user.email)) return@post call.respondStatus(HttpStatusCode.Conflict) @@ -39,7 +36,7 @@ fun Routing.user(kodein: Kodein) { authenticate { put { - val user = call.receive() + val user = call.receiveValidated(registerValidator) if (userService.userExists(user.username, user.email)) return@put call.respond(HttpStatusCode.Conflict) diff --git a/api/src/validation/ValidationExtensions.kt b/api/src/validation/ValidationExtensions.kt new file mode 100644 index 0000000..1d00401 --- /dev/null +++ b/api/src/validation/ValidationExtensions.kt @@ -0,0 +1,14 @@ +package be.vandewalleh.validation + +import am.ik.yavi.core.Validator +import am.ik.yavi.core.ViolationDetail +import io.ktor.application.* +import io.ktor.request.* + +suspend inline fun ApplicationCall.receiveValidated(validator: Validator): T { + val value: T = receive() + validator.validate(value).throwIfInvalid { ValidationException(it.details()) } + return value +} + +data class ValidationException(val details: List) : RuntimeException() diff --git a/api/src/validation/user/UserValidation.kt b/api/src/validation/user/UserValidation.kt new file mode 100644 index 0000000..6f2063e --- /dev/null +++ b/api/src/validation/user/UserValidation.kt @@ -0,0 +1,18 @@ +package be.vandewalleh.validation.user + +import am.ik.yavi.builder.ValidatorBuilder +import am.ik.yavi.builder.konstraint +import am.ik.yavi.core.Validator +import be.vandewalleh.entities.User + +val registerValidator: Validator = ValidatorBuilder.of() + .konstraint(User::username) { + notNull().lessThanOrEqual(50).greaterThanOrEqual(3) + } + .konstraint(User::email) { + notNull().notEmpty().lessThanOrEqual(255).email() + } + .konstraint(User::password) { + notNull().greaterThanOrEqual(6) + } + .build() diff --git a/api/test/routing/UserControllerKtTest.kt b/api/test/integration/routing/UserControllerKtTest.kt similarity index 95% rename from api/test/routing/UserControllerKtTest.kt rename to api/test/integration/routing/UserControllerKtTest.kt index 8e521fc..04fe9d1 100644 --- a/api/test/routing/UserControllerKtTest.kt +++ b/api/test/integration/routing/UserControllerKtTest.kt @@ -1,4 +1,4 @@ -package routing +package integration.routing import be.vandewalleh.auth.SimpleJWT import be.vandewalleh.entities.User @@ -70,7 +70,7 @@ class UserControllerKtTest { val res = testEngine.post("/user") { json { it["username"] = "new" - it["password"] = "test" + it["password"] = "test123abc" it["email"] = "new@test.com" } } @@ -84,7 +84,7 @@ class UserControllerKtTest { json { it["username"] = "existing" it["email"] = "existing@test.com" - it["password"] = "test" + it["password"] = "test123abc" } } res.status() `should be equal to` HttpStatusCode.Conflict @@ -129,6 +129,7 @@ class UserControllerKtTest { json { it["username"] = "ThisIsMyNewName" it["email"] = "ThisIsMyNewName@mail.com" + it["password"] = "ThisIsMyCurrentPassword" } } diff --git a/api/test/services/UserServiceTest.kt b/api/test/integration/services/UserServiceTest.kt similarity index 98% rename from api/test/services/UserServiceTest.kt rename to api/test/integration/services/UserServiceTest.kt index d9a6c57..4cb9b4c 100644 --- a/api/test/services/UserServiceTest.kt +++ b/api/test/integration/services/UserServiceTest.kt @@ -1,4 +1,4 @@ -package services +package integration.services import be.vandewalleh.mainModule import be.vandewalleh.migrations.Migration diff --git a/api/test/unit/validation/RegisterValidationTest.kt b/api/test/unit/validation/RegisterValidationTest.kt new file mode 100644 index 0000000..b2ea374 --- /dev/null +++ b/api/test/unit/validation/RegisterValidationTest.kt @@ -0,0 +1,57 @@ +package unit.validation + +import be.vandewalleh.entities.User +import be.vandewalleh.validation.user.registerValidator +import org.amshove.kluent.* +import org.junit.jupiter.api.* +import utils.firstInvalid + +@TestInstance(TestInstance.Lifecycle.PER_CLASS) +class RegisterValidationTest { + + @Test + fun `valid register test`() { + val violations = registerValidator.validate(User { + username = "hubert" + password = "definitelyNotMyPassword" + email = "test@mail.com" + }) + + violations.isValid `should be equal to` true + } + + @Test + fun `invalid email test`() { + val violations = registerValidator.validate(User { + username = "hubert" + password = "definitelyNotMyPassword" + email = "teom" + }) + + violations.isValid `should be equal to` false + violations.firstInvalid `should be equal to` "email" + } + + @Test + fun `missing email test`() { + val violations = registerValidator.validate(User { + username = "hubert" + password = "definitelyNotMyPassword" + }) + + violations.isValid `should be equal to` false + violations.firstInvalid `should be equal to` "email" + } + + @Test + fun `username too long test`() { + val violations = registerValidator.validate(User { + username = "6X9iboWmEOWjVjkO328ReTJ1gGPTTmB/ZGgBLhB6EzAJoWkJht8" + password = "definitelyNotMyPassword" + email = "test@mail.com" + }) + + violations.isValid `should be equal to` false + violations.firstInvalid `should be equal to` "username" + } +} diff --git a/api/test/utils/ValidationExtensions.kt b/api/test/utils/ValidationExtensions.kt new file mode 100644 index 0000000..91e461a --- /dev/null +++ b/api/test/utils/ValidationExtensions.kt @@ -0,0 +1,6 @@ +package utils + +import am.ik.yavi.core.ConstraintViolations + +val ConstraintViolations.firstInvalid: Any? + get() = this.violations().firstOrNull()?.name()