User service is now non blocking
This commit is contained in:
parent
ebd897093c
commit
6688b35a9b
@ -6,3 +6,8 @@ import kotlinx.coroutines.*
|
|||||||
fun <T> ioAsync(block: suspend CoroutineScope.() -> T): Deferred<T> {
|
fun <T> ioAsync(block: suspend CoroutineScope.() -> T): Deferred<T> {
|
||||||
return CoroutineScope(Dispatchers.IO).async(block = block)
|
return CoroutineScope(Dispatchers.IO).async(block = block)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
suspend inline fun <T> launchIo(crossinline block: () -> T): T =
|
||||||
|
withContext(Dispatchers.IO) {
|
||||||
|
block()
|
||||||
|
}
|
||||||
|
|||||||
@ -1,7 +1,10 @@
|
|||||||
package be.vandewalleh.services
|
package be.vandewalleh.services
|
||||||
|
|
||||||
import be.vandewalleh.entities.User
|
import be.vandewalleh.entities.User
|
||||||
|
import be.vandewalleh.extensions.ioAsync
|
||||||
|
import be.vandewalleh.extensions.launchIo
|
||||||
import be.vandewalleh.tables.Users
|
import be.vandewalleh.tables.Users
|
||||||
|
import kotlinx.coroutines.Deferred
|
||||||
import me.liuwj.ktorm.database.*
|
import me.liuwj.ktorm.database.*
|
||||||
import me.liuwj.ktorm.dsl.*
|
import me.liuwj.ktorm.dsl.*
|
||||||
import me.liuwj.ktorm.entity.*
|
import me.liuwj.ktorm.entity.*
|
||||||
@ -20,8 +23,8 @@ class UserService(override val kodein: Kodein) : KodeinAware {
|
|||||||
/**
|
/**
|
||||||
* returns a user ID if present or null
|
* returns a user ID if present or null
|
||||||
*/
|
*/
|
||||||
fun getUserId(userEmail: String): Int? {
|
suspend fun getUserId(userEmail: String): Int? = launchIo {
|
||||||
return db.from(Users)
|
db.from(Users)
|
||||||
.select(Users.id)
|
.select(Users.id)
|
||||||
.where { Users.email eq userEmail }
|
.where { Users.email eq userEmail }
|
||||||
.map { it[Users.id] }
|
.map { it[Users.id] }
|
||||||
@ -31,8 +34,8 @@ class UserService(override val kodein: Kodein) : KodeinAware {
|
|||||||
/**
|
/**
|
||||||
* returns a user email and password from it's username if found or null
|
* returns a user email and password from it's username if found or null
|
||||||
*/
|
*/
|
||||||
fun getFromUsername(username: String): User? {
|
suspend fun getFromUsername(username: String): User? = launchIo {
|
||||||
return db.from(Users)
|
db.from(Users)
|
||||||
.select(Users.email, Users.password, Users.id)
|
.select(Users.email, Users.password, Users.id)
|
||||||
.where { Users.username eq username }
|
.where { Users.username eq username }
|
||||||
.map { row ->
|
.map { row ->
|
||||||
@ -41,22 +44,22 @@ class UserService(override val kodein: Kodein) : KodeinAware {
|
|||||||
.firstOrNull()
|
.firstOrNull()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun userExists(username: String, email: String): Boolean {
|
suspend fun userExists(username: String, email: String): Boolean = launchIo {
|
||||||
return db.from(Users)
|
db.from(Users)
|
||||||
.select(Users.id)
|
.select(Users.id)
|
||||||
.where { (Users.username eq username) or (Users.email eq email) }
|
.where { (Users.username eq username) or (Users.email eq email) }
|
||||||
.firstOrNull() != null
|
.firstOrNull() != null
|
||||||
}
|
}
|
||||||
|
|
||||||
fun userExists(userId: Int): Boolean {
|
suspend fun userExists(userId: Int): Boolean = launchIo {
|
||||||
return db.from(Users)
|
db.from(Users)
|
||||||
.select(Users.id)
|
.select(Users.id)
|
||||||
.where { Users.id eq userId }
|
.where { Users.id eq userId }
|
||||||
.firstOrNull() != null
|
.firstOrNull() != null
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getUserInfo(id: Int): User? {
|
suspend fun getUserInfo(id: Int): User? = launchIo {
|
||||||
return db.from(Users)
|
db.from(Users)
|
||||||
.select(Users.email, Users.username)
|
.select(Users.email, Users.username)
|
||||||
.where { Users.id eq id }
|
.where { Users.id eq id }
|
||||||
.map { Users.createEntity(it) }
|
.map { Users.createEntity(it) }
|
||||||
@ -67,25 +70,27 @@ class UserService(override val kodein: Kodein) : KodeinAware {
|
|||||||
* create a new user
|
* create a new user
|
||||||
* password should already be hashed
|
* password should already be hashed
|
||||||
*/
|
*/
|
||||||
fun createUser(username: String, email: String, hashedPassword: String): User? {
|
suspend fun createUser(username: String, email: String, hashedPassword: String): User? {
|
||||||
try {
|
|
||||||
db.useTransaction {
|
|
||||||
val newUser = User {
|
val newUser = User {
|
||||||
this.username = username
|
this.username = username
|
||||||
this.email = email
|
this.email = email
|
||||||
this.password = hashedPassword
|
password = hashedPassword
|
||||||
this.createdAt = LocalDateTime.now()
|
createdAt = LocalDateTime.now()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return try {
|
||||||
|
launchIo {
|
||||||
|
db.useTransaction {
|
||||||
db.sequenceOf(Users).add(newUser)
|
db.sequenceOf(Users).add(newUser)
|
||||||
return newUser
|
newUser
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (e: SQLIntegrityConstraintViolationException) {
|
} catch (e: SQLIntegrityConstraintViolationException) {
|
||||||
return null
|
null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun updateUser(userId: Int, username: String, email: String, hashedPassword: String) {
|
suspend fun updateUser(userId: Int, username: String, email: String, hashedPassword: String): Unit = launchIo {
|
||||||
db.useTransaction {
|
db.useTransaction {
|
||||||
db.update(Users) {
|
db.update(Users) {
|
||||||
it.username to username
|
it.username to username
|
||||||
@ -98,14 +103,17 @@ class UserService(override val kodein: Kodein) : KodeinAware {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun deleteUser(userId: Int): Boolean {
|
suspend fun deleteUser(userId: Int): Boolean = launchIo {
|
||||||
db.useTransaction {
|
db.useTransaction {
|
||||||
return when (db.delete(Users) { it.id eq userId }) {
|
db.delete(Users) { it.id eq userId }
|
||||||
|
}
|
||||||
|
}.let {
|
||||||
|
when (it) {
|
||||||
1 -> true
|
1 -> true
|
||||||
0 -> false
|
0 -> false
|
||||||
else -> error("??")
|
else -> error("??")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -10,7 +10,8 @@ import com.auth0.jwt.JWT
|
|||||||
import com.auth0.jwt.algorithms.Algorithm
|
import com.auth0.jwt.algorithms.Algorithm
|
||||||
import io.ktor.http.*
|
import io.ktor.http.*
|
||||||
import io.ktor.server.testing.*
|
import io.ktor.server.testing.*
|
||||||
import io.mockk.every
|
import io.mockk.coEvery
|
||||||
|
import io.mockk.coVerify
|
||||||
import io.mockk.mockk
|
import io.mockk.mockk
|
||||||
import io.mockk.verify
|
import io.mockk.verify
|
||||||
import org.amshove.kluent.*
|
import org.amshove.kluent.*
|
||||||
@ -36,9 +37,9 @@ class AuthControllerKtTest {
|
|||||||
}
|
}
|
||||||
user["id"] = 1
|
user["id"] = 1
|
||||||
|
|
||||||
every { userService.getFromUsername("existing") } returns user
|
coEvery { userService.getFromUsername("existing") } returns user
|
||||||
every { userService.userExists(1) } returns true
|
coEvery { userService.userExists(1) } returns true
|
||||||
every { userService.getUserInfo(1) } returns User {
|
coEvery { userService.getUserInfo(1) } returns User {
|
||||||
username = "existing"
|
username = "existing"
|
||||||
email = "existing@mail.com"
|
email = "existing@mail.com"
|
||||||
}
|
}
|
||||||
@ -48,12 +49,12 @@ class AuthControllerKtTest {
|
|||||||
username = "wrong"
|
username = "wrong"
|
||||||
}
|
}
|
||||||
user["id"] = 2
|
user["id"] = 2
|
||||||
every { userService.getFromUsername("wrong") } returns user2
|
coEvery { userService.getFromUsername("wrong") } returns user2
|
||||||
|
|
||||||
every { userService.getFromUsername("notExisting") } returns null
|
coEvery { userService.getFromUsername("notExisting") } returns null
|
||||||
|
|
||||||
every { userService.userExists(3) } returns false
|
coEvery { userService.userExists(3) } returns false
|
||||||
every { userService.getUserInfo(3) } returns null
|
coEvery { userService.getUserInfo(3) } returns null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -78,7 +79,7 @@ class AuthControllerKtTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
verify { userService.getFromUsername("existing") }
|
coVerify { userService.getFromUsername("existing") }
|
||||||
|
|
||||||
res.status() `should be equal to` HttpStatusCode.OK
|
res.status() `should be equal to` HttpStatusCode.OK
|
||||||
val jsonObject = JSONObject(res.content)
|
val jsonObject = JSONObject(res.content)
|
||||||
@ -106,7 +107,7 @@ class AuthControllerKtTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
verify { userService.getFromUsername("wrong") }
|
coVerify { userService.getFromUsername("wrong") }
|
||||||
|
|
||||||
res.status() `should be equal to` HttpStatusCode.Unauthorized
|
res.status() `should be equal to` HttpStatusCode.Unauthorized
|
||||||
res.content `should strictly be equal to json` """{msg: "Unauthorized"}"""
|
res.content `should strictly be equal to json` """{msg: "Unauthorized"}"""
|
||||||
@ -121,7 +122,7 @@ class AuthControllerKtTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
verify { userService.getFromUsername("notExisting") }
|
coVerify { userService.getFromUsername("notExisting") }
|
||||||
|
|
||||||
res.status() `should be equal to` HttpStatusCode.Unauthorized
|
res.status() `should be equal to` HttpStatusCode.Unauthorized
|
||||||
res.content `should strictly be equal to json` """{msg: "Unauthorized"}"""
|
res.content `should strictly be equal to json` """{msg: "Unauthorized"}"""
|
||||||
@ -154,7 +155,7 @@ class AuthControllerKtTest {
|
|||||||
val jsonObject = JSONObject(res.content)
|
val jsonObject = JSONObject(res.content)
|
||||||
jsonObject.keyList() `should be equal to` listOf("token", "refreshToken")
|
jsonObject.keyList() `should be equal to` listOf("token", "refreshToken")
|
||||||
|
|
||||||
verify { userService.userExists(1) }
|
coVerify { userService.userExists(1) }
|
||||||
res.status() `should be equal to` HttpStatusCode.OK
|
res.status() `should be equal to` HttpStatusCode.OK
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -169,7 +170,7 @@ class AuthControllerKtTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
verify { userService.userExists(3) }
|
coVerify { userService.userExists(3) }
|
||||||
res.status() `should be equal to` HttpStatusCode.Unauthorized
|
res.status() `should be equal to` HttpStatusCode.Unauthorized
|
||||||
res.content `should strictly be equal to json` """{msg: "Unauthorized"}"""
|
res.content `should strictly be equal to json` """{msg: "Unauthorized"}"""
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,7 +7,7 @@ import be.vandewalleh.module
|
|||||||
import be.vandewalleh.services.UserService
|
import be.vandewalleh.services.UserService
|
||||||
import io.ktor.http.*
|
import io.ktor.http.*
|
||||||
import io.ktor.server.testing.*
|
import io.ktor.server.testing.*
|
||||||
import io.mockk.every
|
import io.mockk.coEvery
|
||||||
import io.mockk.mockk
|
import io.mockk.mockk
|
||||||
import org.amshove.kluent.*
|
import org.amshove.kluent.*
|
||||||
import org.junit.jupiter.api.*
|
import org.junit.jupiter.api.*
|
||||||
@ -24,31 +24,31 @@ class UserControllerKtTest {
|
|||||||
|
|
||||||
init {
|
init {
|
||||||
// new user
|
// new user
|
||||||
every { userService.userExists("new", "new@test.com") } returns false
|
coEvery { userService.userExists("new", "new@test.com") } returns false
|
||||||
every { userService.createUser("new", "new@test.com", any()) } returns User {
|
coEvery { userService.createUser("new", "new@test.com", any()) } returns User {
|
||||||
this.createdAt = LocalDateTime.now()
|
this.createdAt = LocalDateTime.now()
|
||||||
this.username = "new"
|
this.username = "new"
|
||||||
this.email = "new@test.com"
|
this.email = "new@test.com"
|
||||||
}
|
}
|
||||||
|
|
||||||
// existing user
|
// existing user
|
||||||
every { userService.userExists("existing", "existing@test.com") } returns true
|
coEvery { userService.userExists("existing", "existing@test.com") } returns true
|
||||||
every { userService.createUser("existing", "existing@test.com", any()) } returns null
|
coEvery { userService.createUser("existing", "existing@test.com", any()) } returns null
|
||||||
every { userService.getUserId("existing@test.com") } returns 1
|
coEvery { userService.getUserId("existing@test.com") } returns 1
|
||||||
every { userService.deleteUser(1) } returns true andThen false
|
coEvery { userService.deleteUser(1) } returns true andThen false
|
||||||
|
|
||||||
// modified user
|
// modified user
|
||||||
every { userService.userExists("modified", "modified@test.com") } returns true
|
coEvery { userService.userExists("modified", "modified@test.com") } returns true
|
||||||
every {
|
coEvery {
|
||||||
userService.userExists(
|
userService.userExists(
|
||||||
and(not("modified"), not("existing")),
|
and(not("modified"), not("existing")),
|
||||||
and(not("modified@test.com"), not("existing@test.com"))
|
and(not("modified@test.com"), not("existing@test.com"))
|
||||||
)
|
)
|
||||||
} returns false
|
} returns false
|
||||||
every { userService.userExists(1) } returns true
|
coEvery { userService.userExists(1) } returns true
|
||||||
every { userService.createUser("modified", "modified@test.com", any()) } returns null
|
coEvery { userService.createUser("modified", "modified@test.com", any()) } returns null
|
||||||
every { userService.getUserId("modified@test.com") } returns 1
|
coEvery { userService.getUserId("modified@test.com") } returns 1
|
||||||
every { userService.updateUser(1, "ThisIsMyNewName", "ThisIsMyNewName@mail.com", any()) } returns Unit
|
coEvery { userService.updateUser(1, "ThisIsMyNewName", "ThisIsMyNewName@mail.com", any()) } returns Unit
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -3,6 +3,7 @@ package integration.services
|
|||||||
import be.vandewalleh.mainModule
|
import be.vandewalleh.mainModule
|
||||||
import be.vandewalleh.migrations.Migration
|
import be.vandewalleh.migrations.Migration
|
||||||
import be.vandewalleh.services.UserService
|
import be.vandewalleh.services.UserService
|
||||||
|
import kotlinx.coroutines.runBlocking
|
||||||
import org.amshove.kluent.*
|
import org.amshove.kluent.*
|
||||||
import org.junit.jupiter.api.*
|
import org.junit.jupiter.api.*
|
||||||
import org.kodein.di.Kodein
|
import org.kodein.di.Kodein
|
||||||
@ -36,11 +37,12 @@ class UserServiceTest {
|
|||||||
@Test
|
@Test
|
||||||
@Order(1)
|
@Order(1)
|
||||||
fun `test create user`() {
|
fun `test create user`() {
|
||||||
|
runBlocking {
|
||||||
val username = "hubert"
|
val username = "hubert"
|
||||||
val email = "a@a"
|
val email = "a@a"
|
||||||
val password = "password"
|
val password = "password"
|
||||||
println(userService.createUser(username, email, password))
|
|
||||||
|
|
||||||
|
userService.createUser(username, email, password)
|
||||||
val id = userService.getUserId(email)
|
val id = userService.getUserId(email)
|
||||||
id `should not be` null
|
id `should not be` null
|
||||||
|
|
||||||
@ -49,16 +51,20 @@ class UserServiceTest {
|
|||||||
it.email `should be equal to` email
|
it.email `should be equal to` email
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Order(2)
|
@Order(2)
|
||||||
fun `test create same user`() {
|
fun `test create same user`() {
|
||||||
|
runBlocking {
|
||||||
userService.createUser(username = "hubert", hashedPassword = "password", email = "a@a") `should be` null
|
userService.createUser(username = "hubert", hashedPassword = "password", email = "a@a") `should be` null
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Order(3)
|
@Order(3)
|
||||||
fun `test delete user`() {
|
fun `test delete user`() {
|
||||||
|
runBlocking {
|
||||||
val email = "a@a"
|
val email = "a@a"
|
||||||
val id = userService.getUserId(email)!!
|
val id = userService.getUserId(email)!!
|
||||||
userService.deleteUser(id)
|
userService.deleteUser(id)
|
||||||
@ -66,4 +72,6 @@ class UserServiceTest {
|
|||||||
userService.getUserId(email) `should be` null
|
userService.getUserId(email) `should be` null
|
||||||
userService.getUserInfo(id) `should be` null
|
userService.getUserInfo(id) `should be` null
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user