From b34622fc9c1d19a7956e8390459f943614e8d929 Mon Sep 17 00:00:00 2001 From: Ivan Dvylyuk <98835689+ubah-lpnu@users.noreply.github.com> Date: Mon, 9 Oct 2023 11:21:08 +0300 Subject: [PATCH] added statistics search implementation v.2 --- .../config/SearchStatisticsProperties.java | 14 ++++ .../teachua/controller/ClubController.java | 7 +- .../controller/PdfGenerationController.java | 5 +- .../teachua/controller/SearchController.java | 5 +- .../teachua/service/ClubService.java | 5 +- .../teachua/service/impl/ClubServiceImpl.java | 20 ++++-- .../impl/SearchStatisticsServiceImpl.java | 15 +--- .../validations/QueryStringValidator.java | 29 ++++++++ .../SearchStatisticsPropertiesTest.java | 64 +++++++++++++++++ .../impl/SearchStatisticsServiceImplTest.java | 11 +-- .../validations/QueryStringValidatorTest.java | 72 +++++++++++++++++++ 11 files changed, 207 insertions(+), 40 deletions(-) create mode 100644 src/main/java/com/softserve/teachua/utils/validations/QueryStringValidator.java create mode 100644 src/test/java/com/softserve/teachua/config/SearchStatisticsPropertiesTest.java create mode 100644 src/test/java/com/softserve/teachua/utils/validations/QueryStringValidatorTest.java diff --git a/src/main/java/com/softserve/teachua/config/SearchStatisticsProperties.java b/src/main/java/com/softserve/teachua/config/SearchStatisticsProperties.java index aaf70803a..89b869633 100644 --- a/src/main/java/com/softserve/teachua/config/SearchStatisticsProperties.java +++ b/src/main/java/com/softserve/teachua/config/SearchStatisticsProperties.java @@ -1,5 +1,9 @@ package com.softserve.teachua.config; +import jakarta.annotation.PostConstruct; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; import lombok.Getter; import lombok.Setter; import org.springframework.boot.context.properties.ConfigurationProperties; @@ -13,4 +17,14 @@ public class SearchStatisticsProperties { private boolean toCache; private String maxTime; private int maxNumber; + + @PostConstruct + public Date validateAndParseMaxTime() { + SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + try { + return formatter.parse(this.maxTime); + } catch (ParseException | IllegalArgumentException e) { + throw new IllegalArgumentException("Invalid date format", e); + } + } } diff --git a/src/main/java/com/softserve/teachua/controller/ClubController.java b/src/main/java/com/softserve/teachua/controller/ClubController.java index 2471d47ad..b9b02e643 100644 --- a/src/main/java/com/softserve/teachua/controller/ClubController.java +++ b/src/main/java/com/softserve/teachua/controller/ClubController.java @@ -31,6 +31,7 @@ import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; /** @@ -155,11 +156,13 @@ public List getListClubsByUserId(@PathVariable Long id) { * * @return {@code Page}. */ + @GetMapping("/clubs/search") public Page getClubsListOfClubs(SearchClubProfile searchClubProfile, @PageableDefault(value = CLUBS_PER_PAGE, sort = "id") - Pageable pageable) { - return clubService.getClubsBySearchParameters(searchClubProfile, pageable); + Pageable pageable, + @RequestParam(required = false) Long userId) { + return clubService.getClubsBySearchParameters(searchClubProfile, pageable, userId); } /** diff --git a/src/main/java/com/softserve/teachua/controller/PdfGenerationController.java b/src/main/java/com/softserve/teachua/controller/PdfGenerationController.java index 737704468..410c2efa6 100644 --- a/src/main/java/com/softserve/teachua/controller/PdfGenerationController.java +++ b/src/main/java/com/softserve/teachua/controller/PdfGenerationController.java @@ -13,6 +13,7 @@ import org.springframework.data.web.PageableDefault; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RestController @@ -47,8 +48,8 @@ public byte[] generateCenterPdfReport(@PathVariable Long centerId) { @GetMapping(value = { "/pdf/resultsearch" }, produces = { "application/pdf" }) public byte[] generateResultSearchPdfReport(SearchClubProfile searchClubProfile, - @PageableDefault(sort = "id", value = 50) Pageable pageable) { + @PageableDefault(sort = "id", value = 50) Pageable pageable, @RequestParam(required = false) Long userId) { return resultSearchReportGenerationService - .getPdfOutput(clubService.getClubsBySearchParameters(searchClubProfile, pageable)); + .getPdfOutput(clubService.getClubsBySearchParameters(searchClubProfile, pageable, userId)); } } diff --git a/src/main/java/com/softserve/teachua/controller/SearchController.java b/src/main/java/com/softserve/teachua/controller/SearchController.java index e43d2476a..0c9711e6a 100644 --- a/src/main/java/com/softserve/teachua/controller/SearchController.java +++ b/src/main/java/com/softserve/teachua/controller/SearchController.java @@ -43,10 +43,9 @@ public SearchController(CategoryService categoryService, ClubService clubService @GetMapping("/search") public CombinedPossibleResponse possibleResponses( @RequestParam @Length(max = 50) String text, - @RequestParam String cityName, - @RequestParam(required = false) Long userId + @RequestParam String cityName ) { return CombinedPossibleResponse.builder().categories(categoryService.getPossibleCategoryByName(text)) - .clubs(clubService.getPossibleClubByName(text, cityName, userId)).build(); + .clubs(clubService.getPossibleClubByName(text, cityName)).build(); } } diff --git a/src/main/java/com/softserve/teachua/service/ClubService.java b/src/main/java/com/softserve/teachua/service/ClubService.java index a7c64030e..5a36cfeca 100644 --- a/src/main/java/com/softserve/teachua/service/ClubService.java +++ b/src/main/java/com/softserve/teachua/service/ClubService.java @@ -146,7 +146,7 @@ public interface ClubService { * @param searchClubProfile - put text of search (based on clubName, cityName & categoryName) * @return {@code Page} */ - Page getClubsBySearchParameters(SearchClubProfile searchClubProfile, Pageable pageable); + Page getClubsBySearchParameters(SearchClubProfile searchClubProfile, Pageable pageable, Long userId); /** * The method which return possible results of search by entered text. @@ -167,10 +167,9 @@ public interface ClubService { * The method which return possible results of search by entered text. * * @param text - put text of search (based on clubName & cityName) - * @param userId - put user id for collecting search statistics * @return {@code List} */ - List getPossibleClubByName(String text, String cityName, Long userId); + List getPossibleClubByName(String text, String cityName); /** * The method which return possible results of search by category and city. diff --git a/src/main/java/com/softserve/teachua/service/impl/ClubServiceImpl.java b/src/main/java/com/softserve/teachua/service/impl/ClubServiceImpl.java index a8ee8faea..29ee9ff80 100644 --- a/src/main/java/com/softserve/teachua/service/impl/ClubServiceImpl.java +++ b/src/main/java/com/softserve/teachua/service/impl/ClubServiceImpl.java @@ -56,6 +56,7 @@ import com.softserve.teachua.service.StationService; import com.softserve.teachua.service.UserService; import com.softserve.teachua.utils.CategoryUtil; +import com.softserve.teachua.utils.validations.QueryStringValidator; import jakarta.validation.ValidationException; import java.util.HashSet; import java.util.List; @@ -106,6 +107,7 @@ public class ClubServiceImpl implements ClubService, ArchiveMark { private final ObjectMapper objectMapper; private final ContactsStringConverter contactsStringConverter; private FeedbackService feedbackService; + private final QueryStringValidator queryStringValidator; @Autowired public ClubServiceImpl(ClubRepository clubRepository, CenterRepository centerRepository, @@ -117,7 +119,7 @@ public ClubServiceImpl(ClubRepository clubRepository, CenterRepository centerRep CenterService centerService, SearchStatisticsService searchStatisticsService, FeedbackRepository feedbackRepository, ObjectMapper objectMapper, ContactsStringConverter contactsStringConverter, - ComplaintRepository complaintRepository) { + ComplaintRepository complaintRepository, QueryStringValidator queryStringValidator) { this.clubRepository = clubRepository; this.locationRepository = locationRepository; this.dtoConverter = dtoConverter; @@ -138,6 +140,7 @@ public ClubServiceImpl(ClubRepository clubRepository, CenterRepository centerRep this.objectMapper = objectMapper; this.contactsStringConverter = contactsStringConverter; this.complaintRepository = complaintRepository; + this.queryStringValidator = queryStringValidator; } @Autowired @@ -396,14 +399,22 @@ public Page getClubsWithoutCategories(Pageable pageable) { } @Override - public Page getClubsBySearchParameters(SearchClubProfile searchClubProfile, Pageable pageable) { + public Page getClubsBySearchParameters(SearchClubProfile searchClubProfile, Pageable pageable, + Long userId) { log.debug("getClubsBySearchParameters ===> "); log.debug(searchClubProfile.toString()); + Page clubResponses = clubRepository.findAllByParameters(searchClubProfile.getClubName(), searchClubProfile.getCityName(), searchClubProfile.getCategoryName(), searchClubProfile.getIsOnline(), pageable); + if (queryStringValidator.isValid( + searchClubProfile.getClubName(), + clubResponses.getTotalElements())) { + searchStatisticsService.addToStatistics(searchClubProfile.getClubName(), userId); + } + log.debug("===find clubs : " + clubResponses.getNumberOfElements()); return new PageImpl<>( @@ -413,10 +424,7 @@ public Page getClubsBySearchParameters(SearchClubProfile searchClu } @Override - public List getPossibleClubByName(String text, String cityName, Long userId) { - if (text.length() >= 4) { - searchStatisticsService.addToStatistics(text, userId); - } + public List getPossibleClubByName(String text, String cityName) { return clubRepository.findTop3ByName(text, cityName, PageRequest.of(0, 3)).stream() .map(category -> (SearchPossibleResponse) dtoConverter.convertToDto(category, SearchPossibleResponse.class)) diff --git a/src/main/java/com/softserve/teachua/service/impl/SearchStatisticsServiceImpl.java b/src/main/java/com/softserve/teachua/service/impl/SearchStatisticsServiceImpl.java index 56d58cf85..2a94dabe0 100644 --- a/src/main/java/com/softserve/teachua/service/impl/SearchStatisticsServiceImpl.java +++ b/src/main/java/com/softserve/teachua/service/impl/SearchStatisticsServiceImpl.java @@ -6,8 +6,6 @@ import com.softserve.teachua.repository.SearchStatisticsRepository; import com.softserve.teachua.service.SearchStatisticsService; import jakarta.transaction.Transactional; -import java.text.ParseException; -import java.text.SimpleDateFormat; import java.util.Date; import java.util.List; import java.util.Optional; @@ -76,11 +74,10 @@ public void deleteOldStatisticsByCount() { } } - @Async @Scheduled(cron = "@weekly") public void deleteOldStatisticsByTime() { try { - Date date = validateDate(); + Date date = searchStatisticsProperties.validateAndParseMaxTime(); List oldRecords = searchStatisticsRepository.findAllByTimestampBefore(date); if (oldRecords.size() > 0) { searchStatisticsRepository.deleteAll(oldRecords); @@ -90,16 +87,6 @@ public void deleteOldStatisticsByTime() { log.error("Error while deleting old statistics: {}", e.getMessage()); } } - - public Date validateDate() { - SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); - try { - return formatter.parse(searchStatisticsProperties.getMaxTime()); - } catch (ParseException | IllegalArgumentException e) { - log.error("Error while parsing date: {}", e.getMessage()); - throw new IllegalArgumentException("Invalid date format"); - } - } } diff --git a/src/main/java/com/softserve/teachua/utils/validations/QueryStringValidator.java b/src/main/java/com/softserve/teachua/utils/validations/QueryStringValidator.java new file mode 100644 index 000000000..490ef2401 --- /dev/null +++ b/src/main/java/com/softserve/teachua/utils/validations/QueryStringValidator.java @@ -0,0 +1,29 @@ +package com.softserve.teachua.utils.validations; + +import org.springframework.stereotype.Component; + +@Component +public class QueryStringValidator { + private static final int MIN_QUERY_LENGTH = 4; + private static final int MAX_QUERY_LENGTH = 50; + + public boolean isValid(String queryString, long elemCount) { + if (queryString == null || queryString.trim().isEmpty()) { + return false; + } + + if (elemCount < 0) { + return false; + } + + if (queryString.length() < MIN_QUERY_LENGTH || queryString.length() > MAX_QUERY_LENGTH) { + return false; + } + + if (!queryString.matches("^[a-zA-Z0-9\\s]+$")) { + return false; + } + + return true; + } +} diff --git a/src/test/java/com/softserve/teachua/config/SearchStatisticsPropertiesTest.java b/src/test/java/com/softserve/teachua/config/SearchStatisticsPropertiesTest.java new file mode 100644 index 000000000..37b7a50aa --- /dev/null +++ b/src/test/java/com/softserve/teachua/config/SearchStatisticsPropertiesTest.java @@ -0,0 +1,64 @@ +package com.softserve.teachua.config; + +import java.util.Date; +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.BeanCreationException; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.boot.test.context.runner.ApplicationContextRunner; + +@EnableConfigurationProperties(SearchStatisticsProperties.class) +class SearchStatisticsPropertiesTest { + + private SearchStatisticsProperties properties; + + @BeforeEach + public void setUp() { + properties = new SearchStatisticsProperties(); + } + + @Test + void testToCache() { + properties.setToCache(true); + assertTrue(properties.isToCache()); + + properties.setToCache(false); + assertFalse(properties.isToCache()); + } + + @Test + void testMaxTime() { + properties.setMaxTime("2023-10-09 12:00:00"); + assertEquals("2023-10-09 12:00:00", properties.getMaxTime()); + } + + @Test + void testMaxNumber() { + properties.setMaxNumber(42); + assertEquals(42, properties.getMaxNumber()); + } + + @Test + void testValidateAndParseMaxTimeValid() { + properties.setMaxTime("2023-10-09 12:00:00"); + Date result = properties.validateAndParseMaxTime(); + assertNotNull(result); + } + + @Test + void testValidateAndParseMaxTimeInvalid() { + properties.setMaxTime("invalid-date-format"); + assertThrows(IllegalArgumentException.class, () -> { + properties.validateAndParseMaxTime(); + }); + } + + @Test + void testValidateAndParseMaxTimeNull() { + properties.setMaxTime("23324:323-122"); + assertThrows(IllegalArgumentException.class, () -> { + properties.validateAndParseMaxTime(); + }); + } +} diff --git a/src/test/java/com/softserve/teachua/service/impl/SearchStatisticsServiceImplTest.java b/src/test/java/com/softserve/teachua/service/impl/SearchStatisticsServiceImplTest.java index 94bd9f023..39182752d 100644 --- a/src/test/java/com/softserve/teachua/service/impl/SearchStatisticsServiceImplTest.java +++ b/src/test/java/com/softserve/teachua/service/impl/SearchStatisticsServiceImplTest.java @@ -81,7 +81,7 @@ public void testDeleteOldStatisticsByTime() { oldRecords.add(new SearchStatistics()); when(searchStatisticsProperties.getMaxTime()).thenReturn(SEARCH_DATE); - Date date = searchStatisticsService.validateDate(); + Date date = searchStatisticsProperties.validateAndParseMaxTime(); when(searchStatisticsRepository.findAllByTimestampBefore(date)).thenReturn(oldRecords); @@ -91,13 +91,4 @@ public void testDeleteOldStatisticsByTime() { verify(searchStatisticsRepository, times(1)).deleteAll(oldRecords); } - @Test - void testValidateDateWithInvalidFormat() { - String invalidDate = "Invalid Date String"; - when(searchStatisticsProperties.getMaxTime()).thenReturn(invalidDate); - - assertThrows(IllegalArgumentException.class, () -> { - searchStatisticsService.validateDate(); - }); - } } diff --git a/src/test/java/com/softserve/teachua/utils/validations/QueryStringValidatorTest.java b/src/test/java/com/softserve/teachua/utils/validations/QueryStringValidatorTest.java new file mode 100644 index 000000000..c53b04ad1 --- /dev/null +++ b/src/test/java/com/softserve/teachua/utils/validations/QueryStringValidatorTest.java @@ -0,0 +1,72 @@ +package com.softserve.teachua.utils.validations; + +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + + +class QueryStringValidatorTest { + + private QueryStringValidator queryStringValidator; + + @BeforeEach + public void setUp() { + queryStringValidator = new QueryStringValidator(); + } + + @Test + public void testValidQueryString() { + String validQueryString = "Valid Query String"; + long elemCount = 10; + + assertTrue(queryStringValidator.isValid(validQueryString, elemCount)); + } + + @Test + public void testInvalidEmptyQueryString() { + String invalidQueryString = ""; + long elemCount = 10; + + assertFalse(queryStringValidator.isValid(invalidQueryString, elemCount)); + } + + @Test + public void testInvalidNullQueryString() { + String invalidQueryString = null; + long elemCount = 10; + + assertFalse(queryStringValidator.isValid(invalidQueryString, elemCount)); + } + + @Test + public void testInvalidShortQueryString() { + String invalidQueryString = "ABC"; + long elemCount = 10; + + assertFalse(queryStringValidator.isValid(invalidQueryString, elemCount)); + } + + @Test + public void testInvalidLongQueryString() { + String invalidQueryString = "This is a very long query string exceeding the maximum allowed length of characters"; // More than MAX_QUERY_LENGTH + long elemCount = 10; + + assertFalse(queryStringValidator.isValid(invalidQueryString, elemCount)); + } + + @Test + public void testInvalidSpecialCharactersQueryString() { + String invalidQueryString = "Invalid@Query*String"; + long elemCount = 10; + + assertFalse(queryStringValidator.isValid(invalidQueryString, elemCount)); + } + + @Test + public void testInvalidNegativeElemCount() { + String validQueryString = "Valid Query String"; + long invalidElemCount = -5; + + assertFalse(queryStringValidator.isValid(validQueryString, invalidElemCount)); + } +}