From bce1769e43b69acf44913af10013e39fcfc58461 Mon Sep 17 00:00:00 2001 From: Elena Moshnikova Date: Mon, 5 Jun 2023 22:43:10 +0300 Subject: [PATCH 1/6] 2508: fix and switch on job which clear unused pictures --- .../com/epam/brn/job/UnverifiedPicturesClearJob.kt | 12 ++++++++---- src/main/resources/application.properties | 5 +++-- .../epam/brn/job/UnverifiedPicturesClearJobTest.kt | 4 ++-- 3 files changed, 13 insertions(+), 8 deletions(-) diff --git a/src/main/kotlin/com/epam/brn/job/UnverifiedPicturesClearJob.kt b/src/main/kotlin/com/epam/brn/job/UnverifiedPicturesClearJob.kt index bb0383f47..a5f185f52 100644 --- a/src/main/kotlin/com/epam/brn/job/UnverifiedPicturesClearJob.kt +++ b/src/main/kotlin/com/epam/brn/job/UnverifiedPicturesClearJob.kt @@ -3,6 +3,7 @@ package com.epam.brn.job import com.epam.brn.service.cloud.CloudService import org.apache.logging.log4j.kotlin.logger import org.springframework.beans.factory.annotation.Value +import org.springframework.scheduling.annotation.Scheduled import org.springframework.stereotype.Component @Component @@ -15,11 +16,14 @@ class UnverifiedPicturesClearJob(private val cloudService: CloudService) { private val log = logger() - // todo: fix it as now it removes folder unverified - // @Scheduled(cron = "\${brn.resources.unverified-pictures.clean-job.cron}") + @Scheduled(cron = "\${brn.resources.unverified-pictures.clean-job.cron}") fun clearUnusedPictures() { - val defaultFolderPictures = cloudService.getFileNames(defaultPicturesPath) - val unverifiedFolderPictures = cloudService.getFileNames(unverifiedPicturesPath) + val unverifiedFolderPictures: List = cloudService + .getFileNames(unverifiedPicturesPath) + .filter { it != "/" } + val defaultFolderPictures: List = cloudService + .getFileNames(defaultPicturesPath) + .filter { it != "/" } val fileNamesToDelete = defaultFolderPictures .intersect(unverifiedFolderPictures) diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 08ae7a846..9e00a35b3 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -85,8 +85,9 @@ brn.resources.pictures.ext=png # The max size of upload unverified picture to cloud. Possible values: 1B, 1KB, 1MB, 1GB, 1TB. If not set data unit will use: BYTES brn.resources.unverified-pictures.max-size=512KB brn.resources.contributor-pictures.max-size=10MB -# Every night -brn.resources.unverified-pictures.clean-job.cron = 0 0 0 * * * +# Every week oon monday +#brn.resources.unverified-pictures.clean-job.cron = 0 0 1 * * MON +brn.resources.unverified-pictures.clean-job.cron = 0 0/5 * * * ? # Monthly brn.resources.pictures.update-job.cron = 0 0 0 1 * * diff --git a/src/test/kotlin/com/epam/brn/job/UnverifiedPicturesClearJobTest.kt b/src/test/kotlin/com/epam/brn/job/UnverifiedPicturesClearJobTest.kt index 204703996..b7045df63 100644 --- a/src/test/kotlin/com/epam/brn/job/UnverifiedPicturesClearJobTest.kt +++ b/src/test/kotlin/com/epam/brn/job/UnverifiedPicturesClearJobTest.kt @@ -28,8 +28,8 @@ internal class UnverifiedPicturesClearJobTest { ReflectionTestUtils.setField(unverifiedPicturesClearJob, "unverifiedPicturesPath", "unverifiedPicturesPath") val capturedFileNames = slot>() - every { cloudService.getFileNames("defaultPicturesPath") } returns listOf("/file1.png", "/file2.png") - every { cloudService.getFileNames("unverifiedPicturesPath") } returns listOf("/file2.png", "/file3.png") + every { cloudService.getFileNames("defaultPicturesPath") } returns listOf("/file1.png", "/file2.png", "/") + every { cloudService.getFileNames("unverifiedPicturesPath") } returns listOf("/file2.png", "/file3.png", "/") every { cloudService.deleteFiles(capture(capturedFileNames)) } just Runs // WHEN From 1c3f86775d465362e8efc444eb9dde55e8b1ce6e Mon Sep 17 00:00:00 2001 From: Elena Moshnikova Date: Wed, 7 Jun 2023 14:02:34 +0300 Subject: [PATCH 2/6] 2508: fix and switch on job which clear unused pictures --- src/main/resources/application.properties | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 9e00a35b3..63b49ace1 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -75,7 +75,7 @@ brn.picture.file.default.path=pictures/%s.jpg # The default path in cloud with picture brn.resources.default-pictures.path=pictures/ # The path for upload unverified picture to cloud -brn.resources.unverified-pictures.path=pictures/unverified +brn.resources.unverified-pictures.path=pictures-unverified # The path for upload contributor pictures brn.resources.contributor-pictures.path=pictures/contributors # The extensions allowed for upload unverified picture to cloud @@ -85,11 +85,14 @@ brn.resources.pictures.ext=png # The max size of upload unverified picture to cloud. Possible values: 1B, 1KB, 1MB, 1GB, 1TB. If not set data unit will use: BYTES brn.resources.unverified-pictures.max-size=512KB brn.resources.contributor-pictures.max-size=10MB -# Every week oon monday -#brn.resources.unverified-pictures.clean-job.cron = 0 0 1 * * MON -brn.resources.unverified-pictures.clean-job.cron = 0 0/5 * * * ? +# Every week on monday +brn.resources.unverified-pictures.clean-job.cron = 0 0 1 * * MON +#for testing +#brn.resources.unverified-pictures.clean-job.cron = 0 0/35 * * * ? # Monthly brn.resources.pictures.update-job.cron = 0 0 0 1 * * +#for testing +#brn.resources.pictures.update-job.cron = 0 0/1 * * * ? brn.dataFormatNumLines=5 brn.statistic.progress.day.status.bad.minimal=0 From 97db0f039c460f0781a6bab8dbfa8e139cad8f19 Mon Sep 17 00:00:00 2001 From: Elena Moshnikova Date: Fri, 4 Oct 2024 18:11:19 +0300 Subject: [PATCH 3/6] 2590 fix version update --- src/main/kotlin/com/epam/brn/config/SwaggerConfig.kt | 2 +- src/main/resources/initFiles/series_sentences_ru.csv | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/kotlin/com/epam/brn/config/SwaggerConfig.kt b/src/main/kotlin/com/epam/brn/config/SwaggerConfig.kt index d2f386855..6d4d4cc91 100644 --- a/src/main/kotlin/com/epam/brn/config/SwaggerConfig.kt +++ b/src/main/kotlin/com/epam/brn/config/SwaggerConfig.kt @@ -21,7 +21,7 @@ class SwaggerConfig { .contact( Contact() .name("Elena.Moshnikova") - .url("https://www.epam.com/") + .url("https://t.me/ElenaLovesSpb") .email("brainupproject@yandex.ru") ) diff --git a/src/main/resources/initFiles/series_sentences_ru.csv b/src/main/resources/initFiles/series_sentences_ru.csv index 23d3dd00c..a4e270f56 100644 --- a/src/main/resources/initFiles/series_sentences_ru.csv +++ b/src/main/resources/initFiles/series_sentences_ru.csv @@ -50,7 +50,7 @@ level,code,exerciseName,orderNumber,words 12,sentence_with_3_words,Пойми предложение из 3 слов,2,(();(мудрая великая);(волшебница фея);(прощает поёт);();()) 13,sentence_with_3_words,Пойми предложение из 3 слов,2,(();(великодушный трусливый);(рыцарь кузнец);(спрашивает отвечает);();()) 13,sentence_with_3_words,Пойми предложение из 3 слов,2,(();(смелая храбрая);(муха лягушка);(сражается борется);();()) -14,sentence_with_3_words,Пойми предложение из 3 слов,2,((три семь);();(поросёнка волчонка);(строят дуют);();()) +14,sentence_with_3_words,Пойми предложение из 3 слов,2,((три четыре);();(поросёнка волчонка);(строят дуют);();()) 15,sentence_with_3_words,Пойми предложение из 3 слов,2,((двенадцать семнадцать);();(лебедей червяков);(летят ползут);();()) 16,sentence_with_3_words,Пойми предложение из 3 слов,2,((три два);();(дядюшки зайца);(бегут стоят);();()) 17,sentence_with_3_words,Пойми предложение из 3 слов,2,((три четыре);();(зайчонка лисы);(пищат шумят);();()) From a0e5f7e34be8bb755fcaab74c04e27bd35fed7be Mon Sep 17 00:00:00 2001 From: Elena Moshnikova Date: Sat, 16 Nov 2024 13:20:43 +0300 Subject: [PATCH 4/6] 2369 add caching --- build.gradle.kts | 2 ++ .../com/epam/brn/config/CachingConfig.kt | 8 ++++++ .../com/epam/brn/model/ExerciseGroup.kt | 7 +++++ .../epam/brn/service/ExerciseGroupsService.kt | 28 +++++++------------ .../com/epam/brn/service/SeriesService.kt | 8 ++---- .../com/epam/brn/service/SubGroupService.kt | 24 ++++++++-------- .../com/epam/brn/service/TaskService.kt | 13 +++++---- .../epam/brn/service/UrlConversionService.kt | 8 ++---- .../brn/service/ExerciseGroupServiceTest.kt | 15 ---------- 9 files changed, 50 insertions(+), 63 deletions(-) create mode 100644 src/main/kotlin/com/epam/brn/config/CachingConfig.kt 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 From a872c88fdcad59392c6a3b9d23b4e0b2d9952f97 Mon Sep 17 00:00:00 2001 From: Elena Moshnikova Date: Sun, 17 Nov 2024 15:16:38 +0300 Subject: [PATCH 5/6] 2369 add caching --- .../com/epam/brn/model/ExerciseGroup.kt | 3 +- .../epam/brn/service/ExerciseGroupsService.kt | 2 +- .../kotlin/com/epam/brn/integration/BaseIT.kt | 27 +++--- .../brn/integration/ExercisesControllerIT.kt | 29 ++---- .../epam/brn/integration/GroupControllerIT.kt | 94 ++++++++++++++----- .../brn/integration/SeriesControllerIT.kt | 18 ++-- .../brn/integration/SubGroupControllerIT.kt | 13 ++- .../epam/brn/integration/TaskControllerIT.kt | 15 +-- 8 files changed, 118 insertions(+), 83 deletions(-) diff --git a/src/main/kotlin/com/epam/brn/model/ExerciseGroup.kt b/src/main/kotlin/com/epam/brn/model/ExerciseGroup.kt index 3b409f47c..ff4e8a9ea 100644 --- a/src/main/kotlin/com/epam/brn/model/ExerciseGroup.kt +++ b/src/main/kotlin/com/epam/brn/model/ExerciseGroup.kt @@ -6,6 +6,7 @@ import com.epam.brn.upload.csv.group.GroupRecord import javax.persistence.CascadeType import javax.persistence.Column import javax.persistence.Entity +import javax.persistence.FetchType import javax.persistence.GeneratedValue import javax.persistence.GenerationType import javax.persistence.Id @@ -27,7 +28,7 @@ data class ExerciseGroup( val name: String, @Column val description: String? = "", - @OneToMany(mappedBy = "exerciseGroup", cascade = [(CascadeType.ALL)]) + @OneToMany(mappedBy = "exerciseGroup", fetch = FetchType.LAZY, cascade = [(CascadeType.ALL)]) val series: MutableList = ArrayList() ) { constructor(record: GroupRecord) : this( diff --git a/src/main/kotlin/com/epam/brn/service/ExerciseGroupsService.kt b/src/main/kotlin/com/epam/brn/service/ExerciseGroupsService.kt index 089e9dffe..a46efca07 100644 --- a/src/main/kotlin/com/epam/brn/service/ExerciseGroupsService.kt +++ b/src/main/kotlin/com/epam/brn/service/ExerciseGroupsService.kt @@ -27,7 +27,7 @@ class ExerciseGroupsService( @Cacheable("groupsByLocale") fun findByLocale(locale: String): List { return if (locale.isEmpty()) - exerciseGroupRepository.findAll().map { group -> group.toDto() } + exerciseGroupRepository.findAll().map { group -> group.toDtoWithoutSeries() } else exerciseGroupRepository.findByLocale(locale) .map { group -> group.toDtoWithoutSeries() } } diff --git a/src/test/kotlin/com/epam/brn/integration/BaseIT.kt b/src/test/kotlin/com/epam/brn/integration/BaseIT.kt index d6cfde71c..daca94266 100644 --- a/src/test/kotlin/com/epam/brn/integration/BaseIT.kt +++ b/src/test/kotlin/com/epam/brn/integration/BaseIT.kt @@ -1,21 +1,25 @@ package com.epam.brn.integration -import com.epam.brn.model.Role +import com.epam.brn.enums.BrnGender import com.epam.brn.model.Exercise import com.epam.brn.model.ExerciseGroup -import com.epam.brn.enums.BrnGender +import com.epam.brn.model.Role import com.epam.brn.model.Series import com.epam.brn.model.StudyHistory import com.epam.brn.model.SubGroup import com.epam.brn.model.UserAccount -import com.epam.brn.repo.RoleRepository import com.epam.brn.repo.ExerciseGroupRepository import com.epam.brn.repo.ExerciseRepository +import com.epam.brn.repo.RoleRepository import com.epam.brn.repo.SeriesRepository import com.epam.brn.repo.StudyHistoryRepository import com.epam.brn.repo.SubGroupRepository +import com.epam.brn.repo.TaskRepository import com.epam.brn.repo.UserAccountRepository import com.fasterxml.jackson.databind.ObjectMapper +import java.time.LocalDateTime +import java.time.format.DateTimeFormatter +import java.time.temporal.ChronoUnit import org.amshove.kluent.internal.platformClassName import org.junit.jupiter.api.Tag import org.springframework.beans.factory.annotation.Autowired @@ -24,9 +28,6 @@ import org.springframework.boot.test.context.SpringBootTest import org.springframework.test.context.ActiveProfiles import org.springframework.test.web.servlet.MockMvc import org.testcontainers.junit.jupiter.Testcontainers -import java.time.LocalDateTime -import java.time.format.DateTimeFormatter -import java.time.temporal.ChronoUnit @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) @AutoConfigureMockMvc @@ -43,22 +44,18 @@ abstract class BaseIT { @Autowired private lateinit var userAccountRepository: UserAccountRepository - @Autowired private lateinit var studyHistoryRepository: StudyHistoryRepository - @Autowired - private lateinit var exerciseRepository: ExerciseRepository - + private lateinit var exerciseGroupRepository: ExerciseGroupRepository + @Autowired + private lateinit var seriesRepository: SeriesRepository @Autowired private lateinit var subGroupRepository: SubGroupRepository - @Autowired - private lateinit var exerciseGroupRepository: ExerciseGroupRepository - + private lateinit var exerciseRepository: ExerciseRepository @Autowired - private lateinit var seriesRepository: SeriesRepository - + private lateinit var taskRepository: TaskRepository @Autowired private lateinit var roleRepository: RoleRepository diff --git a/src/test/kotlin/com/epam/brn/integration/ExercisesControllerIT.kt b/src/test/kotlin/com/epam/brn/integration/ExercisesControllerIT.kt index 49c6b9433..45202ae83 100644 --- a/src/test/kotlin/com/epam/brn/integration/ExercisesControllerIT.kt +++ b/src/test/kotlin/com/epam/brn/integration/ExercisesControllerIT.kt @@ -21,7 +21,6 @@ import com.epam.brn.repo.SeriesRepository import com.epam.brn.repo.StudyHistoryRepository import com.epam.brn.repo.SubGroupRepository import com.epam.brn.repo.UserAccountRepository -import com.google.gson.Gson import org.json.JSONObject import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.Assertions @@ -60,9 +59,6 @@ class ExercisesControllerIT : BaseIT() { @Autowired lateinit var exerciseGroupRepository: ExerciseGroupRepository - @Autowired - lateinit var gson: Gson - @AfterEach fun deleteAfterTest() { studyHistoryRepository.deleteAll() @@ -80,7 +76,7 @@ class ExercisesControllerIT : BaseIT() { val exerciseName = "ExerciseNameTest" val existingSeries = insertSeries() val subGroup = insertSubGroup(existingSeries) - val existingExercise = insertExercise(exerciseName, subGroup) + val existingExercise = insertExercise(subGroup, exerciseName) insertStudyHistory(existingUser, existingExercise, LocalDateTime.now().minusHours(1)) insertStudyHistory(existingUser, existingExercise, LocalDateTime.now()) // WHEN @@ -103,7 +99,7 @@ class ExercisesControllerIT : BaseIT() { val exerciseName = "ExerciseNameTest" val existingSeries = insertSeries() val subGroup = insertSubGroup(existingSeries) - val existingExercise = insertExercise(exerciseName, subGroup) + val existingExercise = insertExercise(subGroup, exerciseName) // WHEN val resultAction = mockMvc.perform( MockMvcRequestBuilders @@ -131,7 +127,7 @@ class ExercisesControllerIT : BaseIT() { val exerciseName = "ExerciseNameTest" val existingSeries = insertSeries() val subGroup = insertSubGroup(existingSeries) - val exercise = insertExercise(exerciseName, subGroup) + val exercise = insertExercise(subGroup, exerciseName) val requestJson: String = objectMapper.writeValueAsString(ExerciseRequest(listOf(exercise.id!!))) // WHEN val resultAction = mockMvc.perform( @@ -299,12 +295,11 @@ class ExercisesControllerIT : BaseIT() { ) } - private fun insertSubGroup(series: Series): SubGroup = subGroupRepository.save( - SubGroup(series = series, level = 1, code = "code", name = "subGroup name") - ) + private fun insertSubGroup(series: Series): SubGroup = + subGroupRepository.save(SubGroup(series = series, level = 1, code = "code", name = "subGroup name")) - fun insertExercise(exerciseName: String, subGroup: SubGroup): Exercise { - return exerciseRepository.save( + fun insertExercise(subGroup: SubGroup, exerciseName: String): Exercise = + exerciseRepository.save( Exercise( subGroup = subGroup, level = 0, @@ -313,13 +308,7 @@ class ExercisesControllerIT : BaseIT() { noiseUrl = "/testNoiseUrl" ) ) - } - fun insertExercise(exerciseName: String): Exercise { - return exerciseRepository.save( - Exercise( - name = exerciseName - ) - ) - } + fun insertExercise(exerciseName: String): Exercise = + exerciseRepository.save(Exercise(name = exerciseName)) } diff --git a/src/test/kotlin/com/epam/brn/integration/GroupControllerIT.kt b/src/test/kotlin/com/epam/brn/integration/GroupControllerIT.kt index c05560eaa..439600bf3 100644 --- a/src/test/kotlin/com/epam/brn/integration/GroupControllerIT.kt +++ b/src/test/kotlin/com/epam/brn/integration/GroupControllerIT.kt @@ -2,8 +2,17 @@ package com.epam.brn.integration import com.epam.brn.enums.BrnLocale import com.epam.brn.enums.BrnRole -import com.epam.brn.repo.ExerciseGroupRepository +import com.epam.brn.enums.ExerciseType +import com.epam.brn.model.Exercise import com.epam.brn.model.ExerciseGroup +import com.epam.brn.model.Series +import com.epam.brn.model.SubGroup +import com.epam.brn.model.Task +import com.epam.brn.repo.ExerciseGroupRepository +import com.epam.brn.repo.ExerciseRepository +import com.epam.brn.repo.SeriesRepository +import com.epam.brn.repo.SubGroupRepository +import com.epam.brn.repo.TaskRepository import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.Assertions.assertTrue import org.junit.jupiter.api.Test @@ -21,35 +30,21 @@ class GroupControllerIT : BaseIT() { private val baseUrl = "/groups" @Autowired - lateinit var exerciseGroupRepository: ExerciseGroupRepository + private lateinit var exerciseGroupRepository: ExerciseGroupRepository + @Autowired + private lateinit var seriesRepository: SeriesRepository + @Autowired + private lateinit var subGroupRepository: SubGroupRepository + @Autowired + private lateinit var exerciseRepository: ExerciseRepository + @Autowired + private lateinit var taskRepository: TaskRepository @AfterEach fun deleteAfterTest() { exerciseGroupRepository.deleteAll() } - @Test - fun `test get all groups`() { - // GIVEN - val exerciseGroupName1 = "GroupName1" - val existingExerciseGroup1 = insertExerciseGroup(exerciseGroupName1) - val exerciseGroupName2 = "GroupName2" - val existingExerciseGroup2 = insertExerciseGroup(exerciseGroupName2) - // WHEN - val resultAction = mockMvc.perform( - MockMvcRequestBuilders - .get(baseUrl) - .contentType(MediaType.APPLICATION_JSON) - ) - // THEN - resultAction - .andExpect(status().isOk) - .andExpect(MockMvcResultMatchers.content().contentType(MediaType.APPLICATION_JSON)) - val response = resultAction.andReturn().response.contentAsString - assertTrue(response.contains(existingExerciseGroup1.name)) - assertTrue(response.contains(existingExerciseGroup2.name)) - } - @Test fun `test find ru groups`() { // GIVEN @@ -57,6 +52,12 @@ class GroupControllerIT : BaseIT() { val groupRu = insertExerciseGroup(exerciseGroupName1, BrnLocale.RU.locale) val exerciseGroupName2 = "GroupName2" val groupEn = insertExerciseGroup(exerciseGroupName2, BrnLocale.EN.locale) + val series1 = insertSeries(groupRu, "series1Name") + val series2 = insertSeries(groupRu, "series2Name") + val subGroup1 = insertSubGroup(series1, 1) + val subGroup2 = insertSubGroup(series1, 2) + val exercise = insertExercise(subGroup1, "ex1") + val task = insertTask(exercise) // WHEN val resultAction = mockMvc.perform( MockMvcRequestBuilders @@ -92,8 +93,8 @@ class GroupControllerIT : BaseIT() { assertTrue(response.contains(existingExerciseGroup.name)) } - private fun insertExerciseGroup(exerciseGroupName: String, locale: String = BrnLocale.RU.locale): ExerciseGroup { - return exerciseGroupRepository.save( + fun insertExerciseGroup(exerciseGroupName: String, locale: String = BrnLocale.RU.locale): ExerciseGroup = + exerciseGroupRepository.save( ExerciseGroup( code = "CODE", description = "desc", @@ -101,5 +102,46 @@ class GroupControllerIT : BaseIT() { locale = locale ) ) + + fun insertSeries(group: ExerciseGroup, name: String): Series { + val series = Series( + name = name, + description = "description", + exerciseGroup = group, + level = 1, + type = ExerciseType.SINGLE_SIMPLE_WORDS.name + ) + return seriesRepository.save(series) } + + fun insertSubGroup(series: Series, level: Int): SubGroup = + subGroupRepository.save( + SubGroup( + series = series, + level = level, + code = "code", + name = "${series.name}subGroupName$level" + ) + ) + + fun insertExercise(subGroup: SubGroup, exerciseName: String): Exercise = + exerciseRepository.save( + Exercise( + subGroup = subGroup, + level = 0, + name = exerciseName, + noiseLevel = 50, + noiseUrl = "/testNoiseUrl" + ) + ) + + fun insertTask(exercise: Exercise): Task = + taskRepository.save( + Task( + id = 1, + name = "${exercise.name} Task", + serialNumber = 1, + exercise = exercise + ) + ) } diff --git a/src/test/kotlin/com/epam/brn/integration/SeriesControllerIT.kt b/src/test/kotlin/com/epam/brn/integration/SeriesControllerIT.kt index 42732f58a..4fe5a9244 100644 --- a/src/test/kotlin/com/epam/brn/integration/SeriesControllerIT.kt +++ b/src/test/kotlin/com/epam/brn/integration/SeriesControllerIT.kt @@ -5,8 +5,8 @@ import com.epam.brn.repo.SeriesRepository import com.epam.brn.dto.SeriesDto import com.epam.brn.dto.response.BrnResponse import com.epam.brn.enums.BrnRole -import com.epam.brn.model.ExerciseGroup import com.epam.brn.enums.ExerciseType +import com.epam.brn.model.ExerciseGroup import com.epam.brn.model.Series import com.fasterxml.jackson.core.type.TypeReference import com.google.gson.Gson @@ -47,11 +47,6 @@ class SeriesControllerIT : BaseIT() { return exerciseGroupRepository.save(group) } - private fun insertSeries(group: ExerciseGroup, name: String): Series { - val series = Series(name = name, description = "description", exerciseGroup = group, level = 1, type = ExerciseType.SINGLE_SIMPLE_WORDS.name) - return seriesRepository.save(series) - } - @Test fun `test get series for group`() { // GIVEN @@ -120,4 +115,15 @@ class SeriesControllerIT : BaseIT() { Assertions.assertTrue(response.contains("1,family,Семья,(сын ребёнок мама),0,")) Assertions.assertEquals(expectedResponse, response) } + + fun insertSeries(group: ExerciseGroup, name: String): Series { + val series = Series( + name = name, + description = "description", + exerciseGroup = group, + level = 1, + type = ExerciseType.SINGLE_SIMPLE_WORDS.name + ) + return seriesRepository.save(series) + } } diff --git a/src/test/kotlin/com/epam/brn/integration/SubGroupControllerIT.kt b/src/test/kotlin/com/epam/brn/integration/SubGroupControllerIT.kt index c03346756..af96efd0b 100644 --- a/src/test/kotlin/com/epam/brn/integration/SubGroupControllerIT.kt +++ b/src/test/kotlin/com/epam/brn/integration/SubGroupControllerIT.kt @@ -95,8 +95,7 @@ class SubGroupControllerIT : BaseIT() { .contentType(MediaType.APPLICATION_JSON) ) // THEN - resultAction - .andExpect(status().isOk) + resultAction.andExpect(status().isOk) } @Test @@ -115,7 +114,10 @@ class SubGroupControllerIT : BaseIT() { resultAction .andExpect(status().isBadRequest) .andExpect(MockMvcResultMatchers.content().contentType(MediaType.APPLICATION_JSON)) - .andExpect(MockMvcResultMatchers.content().string(containsString("Can not delete subGroup because there are exercises that refer to the subGroup."))) + .andExpect( + MockMvcResultMatchers.content() + .string(containsString("Can not delete subGroup because there are exercises that refer to the subGroup.")) + ) } @Test @@ -133,7 +135,10 @@ class SubGroupControllerIT : BaseIT() { resultAction .andExpect(status().isBadRequest) .andExpect(MockMvcResultMatchers.content().contentType(MediaType.APPLICATION_JSON)) - .andExpect(MockMvcResultMatchers.content().string(containsString("Can not delete subGroup because subGroup is not found by this id."))) + .andExpect( + MockMvcResultMatchers.content() + .string(containsString("Can not delete subGroup because subGroup is not found by this id.")) + ) } @Test diff --git a/src/test/kotlin/com/epam/brn/integration/TaskControllerIT.kt b/src/test/kotlin/com/epam/brn/integration/TaskControllerIT.kt index c4016c135..21badcbb6 100644 --- a/src/test/kotlin/com/epam/brn/integration/TaskControllerIT.kt +++ b/src/test/kotlin/com/epam/brn/integration/TaskControllerIT.kt @@ -5,13 +5,13 @@ import com.epam.brn.repo.ExerciseGroupRepository import com.epam.brn.repo.ExerciseRepository import com.epam.brn.repo.SeriesRepository import com.epam.brn.repo.SubGroupRepository -import com.epam.brn.repo.TaskRepository import com.epam.brn.model.Exercise import com.epam.brn.model.ExerciseGroup import com.epam.brn.enums.ExerciseType import com.epam.brn.model.Series import com.epam.brn.model.SubGroup import com.epam.brn.model.Task +import com.epam.brn.repo.TaskRepository import org.json.JSONObject import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.Assertions @@ -27,20 +27,16 @@ import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status @WithMockUser(username = "test@test.test", roles = [BrnRole.USER]) class TaskControllerIT : BaseIT() { - @Autowired - lateinit var taskRepository: TaskRepository - @Autowired lateinit var exerciseRepository: ExerciseRepository - @Autowired lateinit var subGroupRepository: SubGroupRepository - @Autowired lateinit var seriesRepository: SeriesRepository - @Autowired lateinit var exerciseGroupRepository: ExerciseGroupRepository + @Autowired + private lateinit var taskRepository: TaskRepository lateinit var exercise: Exercise @@ -145,8 +141,8 @@ class TaskControllerIT : BaseIT() { ) } - private fun insertTask(exercise: Exercise): Task { - return taskRepository.save( + private fun insertTask(exercise: Exercise): Task = + taskRepository.save( Task( id = 1, name = "${exercise.name} Task", @@ -154,5 +150,4 @@ class TaskControllerIT : BaseIT() { exercise = exercise ) ) - } } From 446f4c638242a1ef84c78c0ebad7799881d4d034 Mon Sep 17 00:00:00 2001 From: Elena Moshnikova Date: Sun, 17 Nov 2024 15:38:56 +0300 Subject: [PATCH 6/6] 2369 add caching --- .../epam/brn/service/ExerciseGroupServiceTest.kt | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/test/kotlin/com/epam/brn/service/ExerciseGroupServiceTest.kt b/src/test/kotlin/com/epam/brn/service/ExerciseGroupServiceTest.kt index 3cb263d14..14a11a370 100644 --- a/src/test/kotlin/com/epam/brn/service/ExerciseGroupServiceTest.kt +++ b/src/test/kotlin/com/epam/brn/service/ExerciseGroupServiceTest.kt @@ -14,6 +14,7 @@ import org.junit.jupiter.api.Test import org.junit.jupiter.api.extension.ExtendWith import java.util.Optional import kotlin.test.assertFailsWith +import org.amshove.kluent.`should be` @ExtendWith(MockKExtension::class) internal class ExerciseGroupServiceTest { @@ -50,6 +51,21 @@ internal class ExerciseGroupServiceTest { assertEquals(actualResult, exerciseGroupMock) } + @Test + fun `should get group by locale if it's exists`() { + // GIVEN + val locale = "ru-ru" + val exerciseGroupMock = + ExerciseGroup(id = 1, code = "groupCode", locale = "ru-ru", name = "name", description = "descr") + every { exerciseGroupRepository.findByLocale(locale) } returns listOf(exerciseGroupMock) + // WHEN + val actualResult: List = exerciseGroupsService.findByLocale(locale) + // THEN + actualResult.isNotEmpty() + actualResult.first().id `should be` 1 + actualResult.first().locale `should be` "ru-ru" + } + @Test fun `should throw error when find group by not exist code`() { // GIVEN