Add users validation

This commit is contained in:
Hubert Van De Walle 2020-06-15 00:27:55 +02:00
parent f09219b032
commit e360489257
8 changed files with 109 additions and 11 deletions

View File

@ -160,6 +160,11 @@
<artifactId>hoplite-yaml</artifactId> <artifactId>hoplite-yaml</artifactId>
<version>${hoplite_version}</version> <version>${hoplite_version}</version>
</dependency> </dependency>
<dependency>
<groupId>am.ik.yavi</groupId>
<artifactId>yavi</artifactId>
<version>0.4.0</version>
</dependency>
<dependency> <dependency>
<groupId>com.github.javafaker</groupId> <groupId>com.github.javafaker</groupId>
<artifactId>javafaker</artifactId> <artifactId>javafaker</artifactId>

View File

@ -4,6 +4,8 @@ import be.vandewalleh.entities.User
import be.vandewalleh.extensions.respondStatus import be.vandewalleh.extensions.respondStatus
import be.vandewalleh.extensions.userId import be.vandewalleh.extensions.userId
import be.vandewalleh.services.UserService import be.vandewalleh.services.UserService
import be.vandewalleh.validation.receiveValidated
import be.vandewalleh.validation.user.registerValidator
import io.ktor.application.* import io.ktor.application.*
import io.ktor.auth.* import io.ktor.auth.*
import io.ktor.http.* import io.ktor.http.*
@ -18,14 +20,9 @@ import java.time.LocalDateTime
fun Routing.user(kodein: Kodein) { fun Routing.user(kodein: Kodein) {
val userService by kodein.instance<UserService>() val userService by kodein.instance<UserService>()
post("/user/test") {
val user = call.receive<User>()
call.respond(user)
}
route("/user") { route("/user") {
post { post {
val user = call.receive<User>() val user = call.receiveValidated(registerValidator)
if (userService.userExists(user.username, user.email)) if (userService.userExists(user.username, user.email))
return@post call.respondStatus(HttpStatusCode.Conflict) return@post call.respondStatus(HttpStatusCode.Conflict)
@ -39,7 +36,7 @@ fun Routing.user(kodein: Kodein) {
authenticate { authenticate {
put { put {
val user = call.receive<User>() val user = call.receiveValidated(registerValidator)
if (userService.userExists(user.username, user.email)) if (userService.userExists(user.username, user.email))
return@put call.respond(HttpStatusCode.Conflict) return@put call.respond(HttpStatusCode.Conflict)

View File

@ -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 <reified T : Any> ApplicationCall.receiveValidated(validator: Validator<T>): T {
val value: T = receive()
validator.validate(value).throwIfInvalid { ValidationException(it.details()) }
return value
}
data class ValidationException(val details: List<ViolationDetail>) : RuntimeException()

View File

@ -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<User> = ValidatorBuilder.of<User>()
.konstraint(User::username) {
notNull().lessThanOrEqual(50).greaterThanOrEqual(3)
}
.konstraint(User::email) {
notNull().notEmpty().lessThanOrEqual(255).email()
}
.konstraint(User::password) {
notNull().greaterThanOrEqual(6)
}
.build()

View File

@ -1,4 +1,4 @@
package routing package integration.routing
import be.vandewalleh.auth.SimpleJWT import be.vandewalleh.auth.SimpleJWT
import be.vandewalleh.entities.User import be.vandewalleh.entities.User
@ -70,7 +70,7 @@ class UserControllerKtTest {
val res = testEngine.post("/user") { val res = testEngine.post("/user") {
json { json {
it["username"] = "new" it["username"] = "new"
it["password"] = "test" it["password"] = "test123abc"
it["email"] = "new@test.com" it["email"] = "new@test.com"
} }
} }
@ -84,7 +84,7 @@ class UserControllerKtTest {
json { json {
it["username"] = "existing" it["username"] = "existing"
it["email"] = "existing@test.com" it["email"] = "existing@test.com"
it["password"] = "test" it["password"] = "test123abc"
} }
} }
res.status() `should be equal to` HttpStatusCode.Conflict res.status() `should be equal to` HttpStatusCode.Conflict
@ -129,6 +129,7 @@ class UserControllerKtTest {
json { json {
it["username"] = "ThisIsMyNewName" it["username"] = "ThisIsMyNewName"
it["email"] = "ThisIsMyNewName@mail.com" it["email"] = "ThisIsMyNewName@mail.com"
it["password"] = "ThisIsMyCurrentPassword"
} }
} }

View File

@ -1,4 +1,4 @@
package services package integration.services
import be.vandewalleh.mainModule import be.vandewalleh.mainModule
import be.vandewalleh.migrations.Migration import be.vandewalleh.migrations.Migration

View File

@ -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"
}
}

View File

@ -0,0 +1,6 @@
package utils
import am.ik.yavi.core.ConstraintViolations
val ConstraintViolations.firstInvalid: Any?
get() = this.violations().firstOrNull()?.name()