diff --git a/build.gradle.kts b/build.gradle.kts index c83a64b4c..8695e5103 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -52,8 +52,10 @@ dependencies { implementation("org.springframework.boot:spring-boot-starter-batch") implementation("org.springframework.boot:spring-boot-starter-security") implementation("org.springframework.boot:spring-boot-starter-validation") + implementation("org.springframework.boot:spring-boot-starter-cache") implementation("org.springframework.security:spring-security-test") implementation("org.springframework.boot:spring-boot-devtools") + implementation("org.postgresql:postgresql") implementation("org.flywaydb:flyway-core:$flywayVersion") diff --git a/src/main/kotlin/com/epam/brn/config/CachingConfig.kt b/src/main/kotlin/com/epam/brn/config/CachingConfig.kt new file mode 100644 index 000000000..876e275b9 --- /dev/null +++ b/src/main/kotlin/com/epam/brn/config/CachingConfig.kt @@ -0,0 +1,8 @@ +package com.epam.brn.config + +import org.springframework.cache.annotation.EnableCaching +import org.springframework.context.annotation.Configuration + +@Configuration +@EnableCaching +class CachingConfig diff --git a/src/main/kotlin/com/epam/brn/model/ExerciseGroup.kt b/src/main/kotlin/com/epam/brn/model/ExerciseGroup.kt index a55387ecb..3b409f47c 100644 --- a/src/main/kotlin/com/epam/brn/model/ExerciseGroup.kt +++ b/src/main/kotlin/com/epam/brn/model/ExerciseGroup.kt @@ -45,6 +45,13 @@ data class ExerciseGroup( series = series.map { series -> series.id }.toMutableList() ) + fun toDtoWithoutSeries() = ExerciseGroupDto( + id = id, + locale = locale, + name = name, + description = description + ) + override fun equals(other: Any?): Boolean { if (this === other) return true if (javaClass != other?.javaClass) return false diff --git a/src/main/kotlin/com/epam/brn/service/ExerciseGroupsService.kt b/src/main/kotlin/com/epam/brn/service/ExerciseGroupsService.kt index fc448c5da..089e9dffe 100644 --- a/src/main/kotlin/com/epam/brn/service/ExerciseGroupsService.kt +++ b/src/main/kotlin/com/epam/brn/service/ExerciseGroupsService.kt @@ -6,6 +6,7 @@ import com.epam.brn.model.ExerciseGroup import com.epam.brn.repo.ExerciseGroupRepository import org.apache.logging.log4j.kotlin.logger import org.springframework.beans.factory.annotation.Autowired +import org.springframework.cache.annotation.Cacheable import org.springframework.stereotype.Service import java.util.Optional @@ -15,34 +16,25 @@ class ExerciseGroupsService( ) { private val log = logger() - fun findAllGroups(): List { - log.debug("Searching all groups") - val groups: List = exerciseGroupRepository.findAll() - return groups.map { group -> group.toDto() } - } - + @Cacheable("groupsById") fun findGroupDtoById(groupId: Long): ExerciseGroupDto { log.debug("Searching group with id=$groupId") val group: Optional = exerciseGroupRepository.findById(groupId) - return group.map { x -> x.toDto() } - .orElseThrow { EntityNotFoundException("no group was found for id=$groupId") } + return group.map { it.toDto() } + .orElseThrow { EntityNotFoundException("No group was found for id=$groupId") } } + @Cacheable("groupsByLocale") fun findByLocale(locale: String): List { - log.debug("Searching groups by locale=$locale") - if (locale.isEmpty()) - return exerciseGroupRepository.findAll().map { group -> group.toDto() } - return exerciseGroupRepository.findByLocale(locale) - .map { group -> group.toDto() } - } - - fun save(exerciseGroup: ExerciseGroup): ExerciseGroup { - return exerciseGroupRepository.save(exerciseGroup) + return if (locale.isEmpty()) + exerciseGroupRepository.findAll().map { group -> group.toDto() } + else exerciseGroupRepository.findByLocale(locale) + .map { group -> group.toDtoWithoutSeries() } } fun findGroupByCode(groupCode: String): ExerciseGroup { log.debug("Searching group with code=$groupCode") return exerciseGroupRepository.findByCode(groupCode) - .orElseThrow { EntityNotFoundException("no group was found for code=$groupCode") } + .orElseThrow { EntityNotFoundException("No group was found for code=$groupCode") } } } diff --git a/src/main/kotlin/com/epam/brn/service/SeriesService.kt b/src/main/kotlin/com/epam/brn/service/SeriesService.kt index a2bb86fff..f1e3fac5a 100644 --- a/src/main/kotlin/com/epam/brn/service/SeriesService.kt +++ b/src/main/kotlin/com/epam/brn/service/SeriesService.kt @@ -2,9 +2,9 @@ package com.epam.brn.service import com.epam.brn.dto.SeriesDto import com.epam.brn.exception.EntityNotFoundException -import com.epam.brn.model.Series import com.epam.brn.repo.SeriesRepository import org.apache.logging.log4j.kotlin.logger +import org.springframework.cache.annotation.Cacheable import org.springframework.stereotype.Service @Service @@ -12,20 +12,18 @@ class SeriesService(private val seriesRepository: SeriesRepository) { private val log = logger() + @Cacheable("series") fun findSeriesForGroup(groupId: Long): List { log.debug("try to find series for groupId=$groupId") val series = seriesRepository.findByExerciseGroupLike(groupId) return series.map { seriesEntry -> seriesEntry.toDto() } } + @Cacheable("seriesDto") fun findSeriesDtoForId(seriesId: Long): SeriesDto { log.debug("try to find series for seriesId=$seriesId") val series = seriesRepository.findById(seriesId) .orElseThrow { EntityNotFoundException("no series was found for id=$seriesId") } return series.toDto() } - - fun save(series: Series): Series { - return seriesRepository.save(series) - } } diff --git a/src/main/kotlin/com/epam/brn/service/SubGroupService.kt b/src/main/kotlin/com/epam/brn/service/SubGroupService.kt index ece38c15a..d55c57214 100644 --- a/src/main/kotlin/com/epam/brn/service/SubGroupService.kt +++ b/src/main/kotlin/com/epam/brn/service/SubGroupService.kt @@ -9,8 +9,10 @@ import com.epam.brn.repo.ExerciseRepository import com.epam.brn.repo.SeriesRepository import com.epam.brn.repo.SubGroupRepository import org.apache.logging.log4j.kotlin.logger +import org.springframework.cache.annotation.CacheEvict +import org.springframework.cache.annotation.CachePut +import org.springframework.cache.annotation.Cacheable import org.springframework.stereotype.Service -import java.util.stream.Collectors @Service class SubGroupService( @@ -21,25 +23,23 @@ class SubGroupService( ) { private val log = logger() + @Cacheable("subgroupsBySeriesId") fun findSubGroupsForSeries(seriesId: Long): List { - log.debug("Try to find subGroups for seriesId=$seriesId") return subGroupRepository .findBySeriesId(seriesId) - .parallelStream() .map { subGroup -> toSubGroupResponse(subGroup) } - .collect(Collectors.toList()) .sortedWith(compareBy({ it.level }, { it.withPictures })) } + @Cacheable("subgroupsBySubGroupId") fun findById(subGroupId: Long): SubGroupResponse { - log.debug("try to find SubGroup by Id=$subGroupId") val subGroup = subGroupRepository.findById(subGroupId) .orElseThrow { EntityNotFoundException("No subGroup was found by id=$subGroupId.") } return toSubGroupResponse(subGroup) } + @CacheEvict("subgroupsBySeriesId", "subgroupsBySubGroupId") fun deleteSubGroupById(subGroupId: Long) { - log.debug("try to delete SubGroup by Id=$subGroupId") if (subGroupRepository.existsById(subGroupId)) { if (exerciseRepository.existsBySubGroupId(subGroupId)) throw IllegalArgumentException("Can not delete subGroup because there are exercises that refer to the subGroup.") @@ -48,8 +48,8 @@ class SubGroupService( throw IllegalArgumentException("Can not delete subGroup because subGroup is not found by this id.") } + @CachePut("subgroupsBySeriesId", "subgroupsBySubGroupId") fun updateSubGroupById(subGroupId: Long, subGroupChangeRequest: SubGroupChangeRequest): SubGroupResponse { - log.debug("try to update SubGroup by Id=$subGroupId") val subGroup = subGroupRepository.findById(subGroupId) .orElseThrow { EntityNotFoundException("Can not update subGroup because subGroup is not found by this id.") } subGroup.withPictures = subGroupChangeRequest.withPictures @@ -59,15 +59,13 @@ class SubGroupService( fun addSubGroupToSeries(subGroupRequest: SubGroupRequest, seriesId: Long): SubGroupResponse { log.debug("try to find subgroup by name=${subGroupRequest.name} and the level=${subGroupRequest.level}") - val existSubGroup = subGroupRepository.findByNameAndLevel(subGroupRequest.name, subGroupRequest.level!!) + val level = subGroupRequest.level ?: throw IllegalArgumentException("Level is required") + val existSubGroup = subGroupRepository.findByNameAndLevel(subGroupRequest.name, level) if (existSubGroup != null) - throw IllegalArgumentException("The subgroup with name=${subGroupRequest.name} and the level=${subGroupRequest.level} already exists!") - log.debug("try to find Series by Id=$seriesId") + throw IllegalArgumentException("The subgroup with name=${subGroupRequest.name} and the level=$level already exists!") val series = seriesRepository.findById(seriesId) .orElseThrow { EntityNotFoundException("No series was found by id=$seriesId.") } - val subGroup = subGroupRequest.toModel(series) - val savedSubGroup = subGroupRepository.save(subGroup) - return toSubGroupResponse(savedSubGroup) + return toSubGroupResponse(subGroupRepository.save(subGroupRequest.toModel(series))) } fun toSubGroupResponse(subGroup: SubGroup): SubGroupResponse { diff --git a/src/main/kotlin/com/epam/brn/service/TaskService.kt b/src/main/kotlin/com/epam/brn/service/TaskService.kt index afdb4a064..61dde3792 100644 --- a/src/main/kotlin/com/epam/brn/service/TaskService.kt +++ b/src/main/kotlin/com/epam/brn/service/TaskService.kt @@ -21,6 +21,8 @@ import com.epam.brn.repo.ResourceRepository import com.epam.brn.repo.TaskRepository import com.epam.brn.service.cloud.CloudService import org.apache.logging.log4j.kotlin.logger +import org.springframework.cache.annotation.CacheEvict +import org.springframework.cache.annotation.Cacheable import org.springframework.stereotype.Service import org.springframework.transaction.annotation.Transactional @@ -33,6 +35,7 @@ class TaskService( ) { private val log = logger() + @Cacheable("tasksByExerciseId") fun getTasksByExerciseId(exerciseId: Long): List { val exercise: Exercise = exerciseRepository.findById(exerciseId) .orElseThrow { EntityNotFoundException("No exercise found for id=$exerciseId") } @@ -51,6 +54,7 @@ class TaskService( } } + @Cacheable("tasksById") fun getTaskById(taskId: Long): Any { log.debug("Searching task with id=$taskId") val task = @@ -75,6 +79,7 @@ class TaskService( } } + @CacheEvict("tasksByExerciseId", "tasksById") @Transactional fun save(task: Task): Task { val resources = mutableSetOf() @@ -85,13 +90,9 @@ class TaskService( } } -val vowels = "а,е,ё,и,о,у,э,ы,ю,я".toCharArray() +private val vowelSet = setOf('а', 'е', 'ё', 'и', 'о', 'у', 'э', 'ы', 'ю', 'я') -fun String.findSyllableCount(): Int { - var syllableCount = 0 - this.toCharArray().forEach { if (vowels.contains(it)) syllableCount++ } - return syllableCount -} +fun String.findSyllableCount(): Int = count { it in vowelSet } fun Task.toDetailWordsTaskDto(exerciseType: ExerciseType) = TaskResponse( id = id!!, diff --git a/src/main/kotlin/com/epam/brn/service/UrlConversionService.kt b/src/main/kotlin/com/epam/brn/service/UrlConversionService.kt index c78a5e8ea..19fd13aac 100644 --- a/src/main/kotlin/com/epam/brn/service/UrlConversionService.kt +++ b/src/main/kotlin/com/epam/brn/service/UrlConversionService.kt @@ -1,7 +1,6 @@ package com.epam.brn.service import com.epam.brn.service.cloud.CloudService -import org.apache.logging.log4j.kotlin.logger import org.springframework.beans.factory.annotation.Value import org.springframework.stereotype.Service @@ -10,12 +9,9 @@ class UrlConversionService(private val cloudService: CloudService) { @Value("\${aws.folderForThemePictures}") private lateinit var folderForThemePictures: String - private val log = logger() - fun makeUrlForNoise(noiseUrl: String?): String { - if (noiseUrl.isNullOrEmpty()) - return "" - return cloudService.baseFileUrl() + noiseUrl + return if (noiseUrl.isNullOrEmpty()) "" + else cloudService.baseFileUrl() + noiseUrl } fun makeUrlForSubGroupPicture(subGroupCode: String): String = diff --git a/src/test/kotlin/com/epam/brn/service/ExerciseGroupServiceTest.kt b/src/test/kotlin/com/epam/brn/service/ExerciseGroupServiceTest.kt index 4358d76b4..3cb263d14 100644 --- a/src/test/kotlin/com/epam/brn/service/ExerciseGroupServiceTest.kt +++ b/src/test/kotlin/com/epam/brn/service/ExerciseGroupServiceTest.kt @@ -10,7 +10,6 @@ import io.mockk.impl.annotations.MockK import io.mockk.junit5.MockKExtension import io.mockk.mockk import org.junit.jupiter.api.Assertions.assertEquals -import org.junit.jupiter.api.Assertions.assertTrue import org.junit.jupiter.api.Test import org.junit.jupiter.api.extension.ExtendWith import java.util.Optional @@ -24,20 +23,6 @@ internal class ExerciseGroupServiceTest { @InjectMockKs lateinit var exerciseGroupsService: ExerciseGroupsService - @Test - fun `should get all groups`() { - // GIVEN - val exerciseGroupMock: ExerciseGroup = mockk(relaxed = true) - val exerciseGroupDtoMock = ExerciseGroupDto(id = 1, locale = "en", name = "name", description = "descr") - - every { exerciseGroupMock.toDto() } returns (exerciseGroupDtoMock) - every { exerciseGroupRepository.findAll() } returns (listOf(exerciseGroupMock)) - // WHEN - val actualResult: List = exerciseGroupsService.findAllGroups() - // THEN - assertTrue(actualResult.contains(exerciseGroupDtoMock)) - } - @Test fun `should get group by id`() { // GIVEN