diff --git a/springboot/api/src/main/kotlin/webapp/users/UserDao.kt b/springboot/api/src/main/kotlin/webapp/users/UserDao.kt index 7cee05a..db3e1dd 100644 --- a/springboot/api/src/main/kotlin/webapp/users/UserDao.kt +++ b/springboot/api/src/main/kotlin/webapp/users/UserDao.kt @@ -12,9 +12,7 @@ import arrow.core.right import org.springframework.beans.factory.getBean import org.springframework.context.ApplicationContext import org.springframework.data.r2dbc.core.R2dbcEntityTemplate -import org.springframework.r2dbc.core.DatabaseClient -import org.springframework.r2dbc.core.awaitOne -import org.springframework.r2dbc.core.awaitRowsUpdated +import org.springframework.r2dbc.core.* import webapp.core.model.EntityModel import webapp.core.utils.AppUtils.cleanField import webapp.users.UserDao.Attributes.EMAIL_ATTR @@ -108,6 +106,17 @@ object UserDao { object Dao { + suspend fun ApplicationContext.countUsers(): Int = + "SELECT COUNT(*) FROM `user`" + .let(getBean()::sql) + .fetch() + .awaitSingle() + .values + .first() + .toString() + .toInt() + + //TODO : change the return type in Either suspend fun Pair.save(): Either = try { second.getBean() @@ -125,6 +134,11 @@ object UserDao { e.left() } + suspend fun ApplicationContext.deleteAllUsersOnly(): Unit = + "DELETE FROM `user`" + .let(getBean()::sql) + .await() + suspend inline fun > ApplicationContext.findOneByEmail(email: String): Either = when (T::class) { diff --git a/springboot/api/src/main/kotlin/webapp/users/security/Role.kt b/springboot/api/src/main/kotlin/webapp/users/security/Role.kt index c35acc3..d2efb89 100755 --- a/springboot/api/src/main/kotlin/webapp/users/security/Role.kt +++ b/springboot/api/src/main/kotlin/webapp/users/security/Role.kt @@ -4,6 +4,10 @@ package webapp.users.security import jakarta.validation.constraints.NotNull import jakarta.validation.constraints.Size +import org.springframework.beans.factory.getBean +import org.springframework.context.ApplicationContext +import org.springframework.r2dbc.core.DatabaseClient +import org.springframework.r2dbc.core.awaitSingle import webapp.core.property.ROLE_ADMIN import webapp.core.property.ROLE_ANONYMOUS import webapp.core.property.ROLE_USER @@ -33,5 +37,17 @@ data class Role( ('$ROLE_ANONYMOUS'); """ } + + object Dao { + suspend fun ApplicationContext.countRoles(): Int = + "select count(*) from `authority`" + .let(getBean()::sql) + .fetch() + .awaitSingle() + .values + .first() + .toString() + .toInt() + } } } \ No newline at end of file diff --git a/springboot/api/src/main/kotlin/webapp/users/security/UserRole.kt b/springboot/api/src/main/kotlin/webapp/users/security/UserRole.kt index 545d75a..e4585a5 100755 --- a/springboot/api/src/main/kotlin/webapp/users/security/UserRole.kt +++ b/springboot/api/src/main/kotlin/webapp/users/security/UserRole.kt @@ -3,6 +3,11 @@ package webapp.users.security import jakarta.validation.constraints.NotNull +import org.springframework.beans.factory.getBean +import org.springframework.context.ApplicationContext +import org.springframework.r2dbc.core.DatabaseClient +import org.springframework.r2dbc.core.await +import org.springframework.r2dbc.core.awaitSingle import webapp.users.UserDao import webapp.users.security.Role.RoleDao import webapp.users.security.UserRole.UserRoleDao.Fields.ID_FIELD @@ -57,7 +62,58 @@ data class UserRole( // """ } -// object Dao { + object Dao { + suspend fun ApplicationContext.countUserAuthority(): Int = + "select count(*) from `user_authority`" + .let(getBean()::sql) + .fetch() + .awaitSingle() + .values + .first() + .toString() + .toInt() + + + suspend fun ApplicationContext.deleteAllUserAuthorities(): Unit = + "DELETE FROM `user_authority`" + .let(getBean()::sql) + .await() + + suspend fun ApplicationContext.deleteAllUserAuthorityByUserId( + id: UUID + ) = "delete from user_authority where user_id = :userId" + .let(getBean()::sql) + .bind("userId", id) + .await() + + suspend fun ApplicationContext.deleteAuthorityByRole( + role: String + ): Unit = "delete from `authority` a where lower(a.role) = lower(:role)" + .let(getBean()::sql) + .bind("role", role) + .await() + + suspend fun ApplicationContext.deleteUserByIdWithAuthorities_(id: UUID) = getBean().run { + "delete from user_authority where user_id = :userId" + .let(::sql) + .bind("userId", id) + .await() + "delete from `user` where user_id = :userId" + .let(::sql) + .await() + } + + val ApplicationContext.queryDeleteAllUserAuthorityByUserLogin + get() = + "delete from user_authority where user_id = (select u.id from `user` u where u.login=:login)" + + suspend fun ApplicationContext.deleteAllUserAuthorityByUserLogin( + login: String + ): Unit = getBean() + .sql(queryDeleteAllUserAuthorityByUserLogin) + .bind("login", login) + .await() + // val Pair.toJson: String // get() = second.getBean().writeValueAsString(first) // @@ -88,6 +144,6 @@ data class UserRole( // } catch (e: Throwable) { // e.left() // } -// } + } } } diff --git a/springboot/api/src/test/kotlin/tdd/TestUtils.kt b/springboot/api/src/test/kotlin/tdd/TestUtils.kt index d94a4d0..3a8efb8 100755 --- a/springboot/api/src/test/kotlin/tdd/TestUtils.kt +++ b/springboot/api/src/test/kotlin/tdd/TestUtils.kt @@ -1,21 +1,16 @@ @file:Suppress( "IdentifierGrammar", "MemberVisibilityCanBePrivate", - "unused" + "unused", "UNUSED_VARIABLE" ) package tdd -import org.springframework.beans.factory.getBean import org.springframework.context.ApplicationContext -import org.springframework.r2dbc.core.DatabaseClient -import org.springframework.r2dbc.core.await -import org.springframework.r2dbc.core.awaitSingle import webapp.core.property.* import webapp.users.User -import java.time.Instant import java.time.Instant.now -import java.util.* +import java.util.UUID.randomUUID import java.util.regex.Pattern import kotlin.test.assertEquals @@ -33,8 +28,8 @@ object TestUtils { "password": "$USER"}""" fun userFactory(login: String): User { - val now: Instant = now() - val uuid = UUID.randomUUID() + val now = now() + val uuid = randomUUID() return User( password = login, login = login, @@ -52,87 +47,9 @@ object TestUtils { val ApplicationContext.languages get() = setOf("en", "fr", "de", "it", "es") - val ApplicationContext.queryDeleteAllUserAuthorityByUserLogin - get() = - "delete from user_authority where user_id = (select u.id from `user` u where u.login=:login)" - val ApplicationContext.defaultRoles get() = setOf(ROLE_ADMIN, ROLE_USER, ROLE_ANONYMOUS) - - - - suspend fun ApplicationContext.countUsers(): Int = - "select count(*) from `user`" - .let(getBean()::sql) - .fetch() - .awaitSingle() - .values - .first() - .toString() - .toInt() - - suspend fun ApplicationContext.countRoles(): Int = - "select count(*) from `authority`" - .let(getBean()::sql) - .fetch() - .awaitSingle() - .values - .first() - .toString() - .toInt() - - suspend fun ApplicationContext.countUserAuthority(): Int = - "select count(*) from `user_authority`" - .let(getBean()::sql) - .fetch() - .awaitSingle() - .values - .first() - .toString() - .toInt() - - suspend fun ApplicationContext.deleteAllUsersOnly(): Unit = - "delete from `user`" - .let(getBean()::sql) - .await() - - suspend fun ApplicationContext.deleteAllUserAuthorities(): Unit = - "delete from user_authority" - .let(getBean()::sql) - .await() - - suspend fun ApplicationContext.deleteAllUserAuthorityByUserId( - id: UUID - ) = "delete from user_authority where user_id = :userId" - .let(getBean()::sql) - .bind("userId", id) - .await() - - suspend fun ApplicationContext.deleteAuthorityByRole( - role: String - ): Unit = "delete from `authority` a where lower(a.role) = lower(:role)" - .let(getBean()::sql) - .bind("role", role) - .await() - - suspend fun ApplicationContext.deleteUserByIdWithAuthorities_(id: UUID) = getBean().run { - "delete from user_authority where user_id = :userId" - .let(::sql) - .bind("userId", id) - .await() - "delete from `user` where user_id = :userId" - .let(::sql) - .await() - } - - suspend fun ApplicationContext.deleteAllUserAuthorityByUserLogin( - login: String - ): Unit = getBean() - .sql(queryDeleteAllUserAuthorityByUserLogin) - .bind("login", login) - .await() - fun ApplicationContext.checkProperty( property: String, value: String, diff --git a/springboot/api/src/test/kotlin/webapp/users/UserDaoTests.kt b/springboot/api/src/test/kotlin/webapp/users/UserDaoTests.kt index 44ac62d..40ed89f 100755 --- a/springboot/api/src/test/kotlin/webapp/users/UserDaoTests.kt +++ b/springboot/api/src/test/kotlin/webapp/users/UserDaoTests.kt @@ -13,16 +13,16 @@ import org.springframework.context.ApplicationContext import org.springframework.dao.EmptyResultDataAccessException import org.springframework.test.context.ActiveProfiles import tdd.TestUtils.Data.user -import tdd.TestUtils.countRoles -import tdd.TestUtils.countUsers import tdd.TestUtils.defaultRoles -import tdd.TestUtils.deleteAllUsersOnly import webapp.core.model.EntityModel.Members.withId import webapp.core.utils.AppUtils.cleanField import webapp.core.utils.AppUtils.toJson import webapp.core.utils.i +import webapp.users.UserDao.Dao.countUsers +import webapp.users.UserDao.Dao.deleteAllUsersOnly import webapp.users.UserDao.Dao.findOneByEmail import webapp.users.UserDao.Dao.save +import webapp.users.security.Role.RoleDao.Dao.countRoles import kotlin.test.* @@ -39,22 +39,15 @@ class UserDaoTests { fun cleanUp() = runBlocking { context.deleteAllUsersOnly() } @Test - fun `save default user should work in this context `() = runBlocking { - val count = context.countUsers() - (user to context).save() - assertEquals(expected = count + 1, context.countUsers()) + fun `count users, expected 0`() = runBlocking { + assertEquals( + 0, + context.countUsers(), + "because init sql script does not inserts default users." + ) } - @Test - fun `count users, expected 0`() = - runBlocking { - assertEquals( - 0, - context.countUsers(), - "because init sql script does not inserts default users." - ) - } - + //TODO: move this test RoleDaoTests @Test fun `count roles, expected 3`() = runBlocking { context.run { @@ -67,27 +60,19 @@ class UserDaoTests { } @Test - fun `display user formatted in JSON`() = assertDoesNotThrow { - (user to context).toJson.let(::i) - } - - @Test - fun `check toJson build a valid json format`(): Unit = assertDoesNotThrow { - (user to context).toJson.let(mapper::readTree) + fun `save default user should work in this context `() = runBlocking { + val count = context.countUsers() + (user to context).save() + assertEquals(expected = count + 1, context.countUsers()) } - - @Test - fun `test cleanField extension function`() = assertEquals( - "login", - "`login`".cleanField(), - "Backtick should be removed" - ) - - @Test fun `check findOneByEmail with non-existent email`(): Unit = runBlocking { - assertEquals(0, context.countUsers(), "context should not have a user recorded in database") + assertEquals( + 0, + context.countUsers(), + "context should not have a user recorded in database" + ) context.findOneByEmail("user@dummy.com").apply { assertFalse(isRight()) assertTrue(isLeft()) @@ -97,9 +82,17 @@ class UserDaoTests { @Test fun `check findOneByEmail with existant email`(): Unit = runBlocking { - assertEquals(0, context.countUsers(), "context should not have a user recorded in database") + assertEquals( + 0, + context.countUsers(), + "context should not have a user recorded in database" + ) (user to context).save() - assertEquals(1, context.countUsers(), "context should have only one user recorded in database") + assertEquals( + 1, + context.countUsers(), + "context should have only one user recorded in database" + ) context.findOneByEmail(user.email).apply { assertTrue(isRight()) assertFalse(isLeft()) diff --git a/springboot/api/src/test/kotlin/webapp/users/UserTests.kt b/springboot/api/src/test/kotlin/webapp/users/UserTests.kt index 2788fac..a6709b0 100755 --- a/springboot/api/src/test/kotlin/webapp/users/UserTests.kt +++ b/springboot/api/src/test/kotlin/webapp/users/UserTests.kt @@ -5,21 +5,20 @@ package webapp.users import com.fasterxml.jackson.databind.ObjectMapper import jakarta.validation.Validator import kotlinx.coroutines.runBlocking +import org.junit.jupiter.api.assertDoesNotThrow import org.springframework.beans.factory.annotation.Autowired import org.springframework.beans.factory.getBean import org.springframework.boot.test.context.SpringBootTest import org.springframework.context.ApplicationContext -import org.springframework.dao.EmptyResultDataAccessException import org.springframework.test.context.ActiveProfiles import tdd.TestUtils.Data.user -import tdd.TestUtils.countRoles -import tdd.TestUtils.countUsers -import tdd.TestUtils.defaultRoles -import tdd.TestUtils.deleteAllUsersOnly -import webapp.core.model.EntityModel.Members.withId -import webapp.users.UserDao.Dao.findOneByEmail -import webapp.users.UserDao.Dao.save -import kotlin.test.* +import webapp.core.utils.AppUtils.cleanField +import webapp.core.utils.AppUtils.toJson +import webapp.core.utils.i +import webapp.users.UserDao.Dao.deleteAllUsersOnly +import kotlin.test.AfterTest +import kotlin.test.Test +import kotlin.test.assertEquals @SpringBootTest(properties = ["spring.main.web-application-type=reactive"]) @@ -35,51 +34,20 @@ class UserTests { fun cleanUp() = runBlocking { context.deleteAllUsersOnly() } @Test - fun `save default user should work in this context `() = runBlocking { - val count = context.countUsers() - (user to context).save() - assertEquals(expected = count + 1, context.countUsers()) + fun `display user formatted in JSON`() = assertDoesNotThrow { + (user to context).toJson.let(::i) } @Test - fun `count users, expected 0`() = - runBlocking { - assertEquals( - 0, - context.countUsers(), - "because init sql script does not inserts default users." - ) - } - - @Test - fun `count roles, expected 3`() = runBlocking { - context.run { - assertEquals( - defaultRoles.size, - countRoles(), - "Because init sql script does insert default roles." - ) - } + fun `check toJson build a valid json format`(): Unit = assertDoesNotThrow { + (user to context).toJson.let(mapper::readTree) } @Test - fun `check findOneByEmail with non-existent email`(): Unit = runBlocking { - assertEquals(0, context.countUsers(), "context should not have a user recorded in database") - context.findOneByEmail("user@dummy.com").apply { - assertFalse(isRight()) - assertTrue(isLeft()) - }.mapLeft { assertTrue(it is EmptyResultDataAccessException) } - } - - - @Test - fun `check findOneByEmail with existant email`(): Unit = runBlocking { - assertEquals(0, context.countUsers(), "context should not have a user recorded in database") - (user to context).save() - assertEquals(1, context.countUsers(), "context should have only one user recorded in database") - context.findOneByEmail(user.email).apply { - assertTrue(isRight()) - assertFalse(isLeft()) - }.map { assertEquals(it, user.withId(it.id!!)) } - } + fun `test cleanField extension function`() = assertEquals( + "login", + "`login`".cleanField(), + "Backtick should be removed" + ) + //TODO : Write and/or Test toMap, toUser } \ No newline at end of file diff --git a/springboot/api/src/test/kotlin/webapp/users/signup/SignupIntegrationTests.kt b/springboot/api/src/test/kotlin/webapp/users/signup/SignupIntegrationTests.kt index 83686ec..d4efc00 100644 --- a/springboot/api/src/test/kotlin/webapp/users/signup/SignupIntegrationTests.kt +++ b/springboot/api/src/test/kotlin/webapp/users/signup/SignupIntegrationTests.kt @@ -23,14 +23,14 @@ import tdd.TestTools.requestToString import tdd.TestUtils import tdd.TestUtils.Data.DEFAULT_USER_JSON import tdd.TestUtils.Data.user -import tdd.TestUtils.countUserAuthority -import tdd.TestUtils.countUsers -import tdd.TestUtils.deleteAllUsersOnly import webapp.core.utils.i import webapp.users.User +import webapp.users.UserDao +import webapp.users.UserDao.Dao.countUsers +import webapp.users.UserDao.Dao.deleteAllUsersOnly import webapp.users.UserDao.Dao.findOneByEmail import webapp.users.UserRestApiRoutes.API_SIGNUP_PATH -import webapp.users.UserDao +import webapp.users.security.UserRole.UserRoleDao.Dao.countUserAuthority import kotlin.test.* @SpringBootTest(properties = ["spring.main.web-application-type=reactive"])