diff --git a/src/main/kotlin/com/epam/brn/repo/UserAccountRepository.kt b/src/main/kotlin/com/epam/brn/repo/UserAccountRepository.kt index cde382fb8..28a637c22 100644 --- a/src/main/kotlin/com/epam/brn/repo/UserAccountRepository.kt +++ b/src/main/kotlin/com/epam/brn/repo/UserAccountRepository.kt @@ -48,11 +48,22 @@ interface UserAccountRepository : JpaRepository { fun findAllByUserIdIsNullAndIsFirebaseErrorIsFalse(pageable: Pageable): Page @Query( - """select DISTINCT u FROM UserAccount u left JOIN FETCH u.roleSet roles + """select DISTINCT u FROM UserAccount u left JOIN FETCH u.roleSet roles left JOIN FETCH u.headphones where roles.name = :roleName""", ) fun findUsersAccountsByRole(roleName: String): List + @Query( + value = """select DISTINCT u FROM UserAccount u left JOIN FETCH u.roleSet roles + left JOIN FETCH u.headphones where roles.name = :roleName""", + countQuery = """select count(DISTINCT u) FROM UserAccount u left JOIN u.roleSet roles + where roles.name = :roleName""", + ) + fun findUsersAccountsByRole( + roleName: String, + pageable: Pageable, + ): Page + @Transactional @Modifying @Query( diff --git a/src/main/kotlin/com/epam/brn/service/ContributorServiceImpl.kt b/src/main/kotlin/com/epam/brn/service/ContributorServiceImpl.kt index 8ad7faf92..eadd7452c 100644 --- a/src/main/kotlin/com/epam/brn/service/ContributorServiceImpl.kt +++ b/src/main/kotlin/com/epam/brn/service/ContributorServiceImpl.kt @@ -18,9 +18,7 @@ class ContributorServiceImpl( @Transactional(readOnly = true) override fun getAllContributors(): List = contributorRepository .findAll() - .stream() .map { e -> e.toContributorResponse() } - .toList() @Transactional(readOnly = true) override fun getContributors( @@ -28,9 +26,7 @@ class ContributorServiceImpl( type: ContributorType, ): List = contributorRepository .findAllByType(type) - .stream() .map { e -> e.toContributorResponse(locale) } - .toList() @Transactional override fun createContributor(request: ContributorRequest): ContributorResponse = diff --git a/src/main/kotlin/com/epam/brn/service/UserAccountService.kt b/src/main/kotlin/com/epam/brn/service/UserAccountService.kt index b2efc386a..4731303fe 100644 --- a/src/main/kotlin/com/epam/brn/service/UserAccountService.kt +++ b/src/main/kotlin/com/epam/brn/service/UserAccountService.kt @@ -5,6 +5,7 @@ import com.epam.brn.dto.UserAccountDto import com.epam.brn.dto.request.UserAccountChangeRequest import com.epam.brn.model.UserAccount import com.google.firebase.auth.UserRecord +import org.springframework.data.domain.Page import org.springframework.data.domain.Pageable interface UserAccountService { @@ -22,7 +23,7 @@ interface UserAccountService { fun getUsers( pageable: Pageable, role: String, - ): List + ): Page fun updateAvatarForCurrentUser(avatarUrl: String): UserAccountDto fun updateCurrentUser(userChangeRequest: UserAccountChangeRequest): UserAccountDto diff --git a/src/main/kotlin/com/epam/brn/service/UserAnalyticsService.kt b/src/main/kotlin/com/epam/brn/service/UserAnalyticsService.kt index ec07fda1c..5218b929c 100644 --- a/src/main/kotlin/com/epam/brn/service/UserAnalyticsService.kt +++ b/src/main/kotlin/com/epam/brn/service/UserAnalyticsService.kt @@ -2,6 +2,7 @@ package com.epam.brn.service import com.epam.brn.dto.AudioFileMetaData import com.epam.brn.dto.response.UserWithAnalyticsResponse +import org.springframework.data.domain.Page import org.springframework.data.domain.Pageable import java.io.InputStream @@ -9,7 +10,7 @@ interface UserAnalyticsService { fun getUsersWithAnalytics( pageable: Pageable, role: String, - ): List + ): Page fun prepareAudioStreamForUser( exerciseId: Long, audioFileMetaData: AudioFileMetaData, diff --git a/src/main/kotlin/com/epam/brn/service/impl/UserAccountServiceImpl.kt b/src/main/kotlin/com/epam/brn/service/impl/UserAccountServiceImpl.kt index df1c3c2ae..759ec5f28 100644 --- a/src/main/kotlin/com/epam/brn/service/impl/UserAccountServiceImpl.kt +++ b/src/main/kotlin/com/epam/brn/service/impl/UserAccountServiceImpl.kt @@ -15,6 +15,7 @@ import com.epam.brn.service.TimeService import com.epam.brn.service.UserAccountService import com.google.firebase.auth.UserRecord import org.springframework.beans.factory.annotation.Value +import org.springframework.data.domain.Page import org.springframework.data.domain.Pageable import org.springframework.security.core.Authentication import org.springframework.security.core.context.SecurityContextHolder @@ -90,8 +91,8 @@ class UserAccountServiceImpl( override fun getUsers( pageable: Pageable, role: String, - ): List = userAccountRepository - .findUsersAccountsByRole(role) + ): Page = userAccountRepository + .findUsersAccountsByRole(role, pageable) .map { it.toDto() } override fun updateAvatarForCurrentUser(avatarUrl: String): UserAccountDto { diff --git a/src/main/kotlin/com/epam/brn/service/impl/UserAnalyticsServiceImpl.kt b/src/main/kotlin/com/epam/brn/service/impl/UserAnalyticsServiceImpl.kt index 441f60337..fe77fdf5d 100644 --- a/src/main/kotlin/com/epam/brn/service/impl/UserAnalyticsServiceImpl.kt +++ b/src/main/kotlin/com/epam/brn/service/impl/UserAnalyticsServiceImpl.kt @@ -17,6 +17,7 @@ import com.epam.brn.service.UserAccountService import com.epam.brn.service.UserAnalyticsService import com.epam.brn.service.WordsService import com.epam.brn.service.statistics.UserPeriodStatisticsService +import org.springframework.data.domain.Page import org.springframework.data.domain.Pageable import org.springframework.stereotype.Service import java.io.InputStream @@ -44,8 +45,8 @@ class UserAnalyticsServiceImpl( override fun getUsersWithAnalytics( pageable: Pageable, role: String, - ): List { - val users = userAccountRepository.findUsersAccountsByRole(role).map { it.toAnalyticsDto() } + ): Page { + val usersPage = userAccountRepository.findUsersAccountsByRole(role, pageable) val now = timeService.now() val firstWeekDay = WeekFields.of(Locale.getDefault()).dayOfWeek() @@ -54,7 +55,8 @@ class UserAnalyticsServiceImpl( val to = startDay.plusDays(7L).with(LocalTime.MAX) val startOfCurrentMonth = now.withDayOfMonth(1).with(LocalTime.MIN) - users.onEach { user -> + return usersPage.map { userAccount -> + val user = userAccount.toAnalyticsDto() user.lastWeek = userDayStatisticsService.getStatisticsForPeriod(from, to, user.id) user.studyDaysInCurrentMonth = countWorkDaysForMonth( @@ -69,7 +71,6 @@ class UserAnalyticsServiceImpl( this.doneExercises = userStatistic.doneExercises } } - return users } override fun prepareAudioStreamForUser( diff --git a/src/test/kotlin/com/epam/brn/controller/UserDetailControllerTest.kt b/src/test/kotlin/com/epam/brn/controller/UserDetailControllerTest.kt index 4f83556fc..6082bab0a 100644 --- a/src/test/kotlin/com/epam/brn/controller/UserDetailControllerTest.kt +++ b/src/test/kotlin/com/epam/brn/controller/UserDetailControllerTest.kt @@ -30,6 +30,7 @@ import org.junit.jupiter.api.DisplayName import org.junit.jupiter.api.Nested import org.junit.jupiter.api.Test import org.junit.jupiter.api.extension.ExtendWith +import org.springframework.data.domain.PageImpl import org.springframework.data.domain.Pageable import kotlin.test.assertEquals @@ -295,7 +296,8 @@ internal class UserDetailControllerTest { val role = BrnRole.USER val pageable = mockk() val userWithAnalyticsResponse = mockk() - every { userAnalyticsService.getUsersWithAnalytics(pageable, role) } returns listOf(userWithAnalyticsResponse) + val page = PageImpl(listOf(userWithAnalyticsResponse)) + every { userAnalyticsService.getUsersWithAnalytics(pageable, role) } returns page // WHEN val users = userDetailController.getUsers(withAnalytics, role, pageable) @@ -303,7 +305,7 @@ internal class UserDetailControllerTest { // THEN verify(exactly = 1) { userAnalyticsService.getUsersWithAnalytics(pageable, role) } users.statusCodeValue shouldBe HttpStatus.SC_OK - (users.body as BrnResponse<*>).data shouldBe listOf(userWithAnalyticsResponse) + (users.body as BrnResponse<*>).data shouldBe page } @Test @@ -312,7 +314,8 @@ internal class UserDetailControllerTest { val withAnalytics = false val role = BrnRole.USER val pageable = mockk() - every { userAccountService.getUsers(pageable, role) } returns listOf(userAccountDto) + val page = PageImpl(listOf(userAccountDto)) + every { userAccountService.getUsers(pageable, role) } returns page // WHEN val users = userDetailController.getUsers(withAnalytics, role, pageable) @@ -320,7 +323,7 @@ internal class UserDetailControllerTest { // THEN verify(exactly = 1) { userAccountService.getUsers(pageable, role) } users.statusCodeValue shouldBe HttpStatus.SC_OK - (users.body as BrnResponse<*>).data shouldBe listOf(userAccountDto) + (users.body as BrnResponse<*>).data shouldBe page } @Test diff --git a/src/test/kotlin/com/epam/brn/integration/UserDetailsControllerIT.kt b/src/test/kotlin/com/epam/brn/integration/UserDetailsControllerIT.kt index 5176f4480..5c195333d 100644 --- a/src/test/kotlin/com/epam/brn/integration/UserDetailsControllerIT.kt +++ b/src/test/kotlin/com/epam/brn/integration/UserDetailsControllerIT.kt @@ -341,12 +341,60 @@ class UserDetailsControllerIT : BaseIT() { .response .getContentAsString(StandardCharsets.UTF_8) - val data = gson.fromJson(response, BrnResponse::class.java).data + val responseMap: Map = objectMapper.readValue(response, object : TypeReference>() {}) + val dataMap = responseMap["data"] as Map<*, *> + val content = dataMap["content"] val users: List = - objectMapper.readValue(gson.toJson(data), object : TypeReference>() {}) + objectMapper.readValue(gson.toJson(content), object : TypeReference>() {}) // THEN users.size shouldBe 1 + dataMap["totalElements"] shouldBe 1 + } + + @Test + fun `should get paginated users by role`() { + // GIVEN + val roleUser = insertRole(BrnRole.USER) + + for (i in 1..5) { + val user = + UserAccount( + fullName = "testUser$i", + email = "testuser$i@test.test", + gender = BrnGender.MALE.toString(), + bornYear = 2000, + active = true, + ) + user.roleSet = mutableSetOf(roleUser) + userAccountRepository.save(user) + } + + // WHEN - request page 0 with size 2 + val response = + mockMvc + .perform( + MockMvcRequestBuilders + .get(baseUrl) + .param("role", BrnRole.USER) + .param("page", "0") + .param("size", "2"), + ).andExpect(status().isOk) + .andReturn() + .response + .getContentAsString(StandardCharsets.UTF_8) + + val responseMap: Map = objectMapper.readValue(response, object : TypeReference>() {}) + val dataMap = responseMap["data"] as Map<*, *> + val content = dataMap["content"] + val users: List = + objectMapper.readValue(gson.toJson(content), object : TypeReference>() {}) + + // THEN + users.size shouldBe 2 + dataMap["totalElements"] shouldBe 5 + dataMap["totalPages"] shouldBe 3 + dataMap["number"] shouldBe 0 } @Test diff --git a/src/test/kotlin/com/epam/brn/service/UserAccountServiceTest.kt b/src/test/kotlin/com/epam/brn/service/UserAccountServiceTest.kt index 791e3746d..06997684c 100644 --- a/src/test/kotlin/com/epam/brn/service/UserAccountServiceTest.kt +++ b/src/test/kotlin/com/epam/brn/service/UserAccountServiceTest.kt @@ -31,6 +31,7 @@ import org.junit.jupiter.api.DisplayName import org.junit.jupiter.api.Nested import org.junit.jupiter.api.Test import org.junit.jupiter.api.extension.ExtendWith +import org.springframework.data.domain.PageImpl import org.springframework.data.domain.Pageable import org.springframework.security.core.Authentication import org.springframework.security.core.context.SecurityContext @@ -499,11 +500,13 @@ internal class UserAccountServiceTest { fun `should return all users`() { // GIVEN val usersList = listOf(userAccount, userAccount, userAccount) - every { userAccountRepository.findUsersAccountsByRole(BrnRole.USER) } returns usersList + val usersPage = PageImpl(usersList) + every { userAccountRepository.findUsersAccountsByRole(BrnRole.USER, pageable) } returns usersPage // WHEN val userAccountDtos = userAccountService.getUsers(pageable = pageable, BrnRole.USER) // THEN - userAccountDtos.size shouldBe 3 + userAccountDtos.totalElements shouldBe 3 + userAccountDtos.content.size shouldBe 3 } } diff --git a/src/test/kotlin/com/epam/brn/service/UserAnalyticsServiceTest.kt b/src/test/kotlin/com/epam/brn/service/UserAnalyticsServiceTest.kt index 0341bf4d6..537b1dd73 100644 --- a/src/test/kotlin/com/epam/brn/service/UserAnalyticsServiceTest.kt +++ b/src/test/kotlin/com/epam/brn/service/UserAnalyticsServiceTest.kt @@ -24,6 +24,7 @@ import io.mockk.mockk import org.junit.jupiter.api.DisplayName import org.junit.jupiter.api.Test import org.junit.jupiter.api.extension.ExtendWith +import org.springframework.data.domain.PageImpl import org.springframework.data.domain.Pageable import java.io.InputStream import java.time.LocalDateTime @@ -76,40 +77,43 @@ internal class UserAnalyticsServiceTest { @Test fun `should return all users with analytics`() { val usersList = listOf(doctorAccount, doctorAccount) + val usersPage = PageImpl(usersList) val dayStatisticList = listOf(dayStudyStatistics, dayStudyStatistics) every { userStatisticView.firstStudy } returns LocalDateTime.now() every { userStatisticView.lastStudy } returns LocalDateTime.now() every { userStatisticView.spentTime } returns 10000L every { userStatisticView.doneExercises } returns 1 - every { userAccountRepository.findUsersAccountsByRole(BrnRole.ADMIN) } returns usersList + every { userAccountRepository.findUsersAccountsByRole(BrnRole.ADMIN, pageable) } returns usersPage every { userDayStatisticService.getStatisticsForPeriod(any(), any(), any()) } returns dayStatisticList every { timeService.now() } returns LocalDateTime.now() every { studyHistoryRepository.getStatisticsByUserAccountId(any()) } returns userStatisticView val userAnalyticsDtos = userAnalyticsService.getUsersWithAnalytics(pageable, BrnRole.ADMIN) - userAnalyticsDtos.size shouldBe 2 + userAnalyticsDtos.totalElements shouldBe 2 + userAnalyticsDtos.content.size shouldBe 2 } @Test fun `should not return user with analytics`() { val usersList = listOf(doctorAccount) + val usersPage = PageImpl(usersList) val dayStatisticList = emptyList() every { userStatisticView.firstStudy } returns LocalDateTime.now() every { userStatisticView.lastStudy } returns LocalDateTime.now() every { userStatisticView.spentTime } returns 10000L every { userStatisticView.doneExercises } returns 1 - every { userAccountRepository.findUsersAccountsByRole(BrnRole.ADMIN) } returns usersList + every { userAccountRepository.findUsersAccountsByRole(BrnRole.ADMIN, pageable) } returns usersPage every { userDayStatisticService.getStatisticsForPeriod(any(), any(), any()) } returns dayStatisticList every { timeService.now() } returns LocalDateTime.now() every { studyHistoryRepository.getStatisticsByUserAccountId(any()) } returns userStatisticView val userAnalyticsDtos = userAnalyticsService.getUsersWithAnalytics(pageable, BrnRole.ADMIN) - userAnalyticsDtos.size shouldBe 1 - userAnalyticsDtos[0].lastWeek.size shouldBe 0 + userAnalyticsDtos.totalElements shouldBe 1 + userAnalyticsDtos.content[0].lastWeek.size shouldBe 0 } private val currentUserId = 1L