diff --git a/client/cards/src/main/java/org/opfab/cards/model/FilterMatchTypeEnum.java b/client/cards/src/main/java/org/opfab/cards/model/FilterMatchTypeEnum.java index 364c5b99fc..2c3036351a 100644 --- a/client/cards/src/main/java/org/opfab/cards/model/FilterMatchTypeEnum.java +++ b/client/cards/src/main/java/org/opfab/cards/model/FilterMatchTypeEnum.java @@ -1,4 +1,4 @@ -/* Copyright (c) 2022, RTE (http://www.rte-france.com) +/* Copyright (c) 2022-2024, RTE (http://www.rte-france.com) * See AUTHORS.txt * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this @@ -25,5 +25,7 @@ public enum FilterMatchTypeEnum { NOTCONTAINS, BLANK, NOTBLANK, - IN + IN, + LESSTHAN, + GREATERTHAN } diff --git a/services/cards-consultation/src/main/java/org/opfab/cards/consultation/model/FilterMatchTypeEnum.java b/services/cards-consultation/src/main/java/org/opfab/cards/consultation/model/FilterMatchTypeEnum.java index 4908817eb1..a503c34caa 100644 --- a/services/cards-consultation/src/main/java/org/opfab/cards/consultation/model/FilterMatchTypeEnum.java +++ b/services/cards-consultation/src/main/java/org/opfab/cards/consultation/model/FilterMatchTypeEnum.java @@ -21,5 +21,7 @@ public enum FilterMatchTypeEnum { NOTCONTAINS, BLANK, NOTBLANK, - IN + IN, + LESSTHAN, + GREATERTHAN } diff --git a/services/cards-consultation/src/main/java/org/opfab/cards/consultation/repositories/UserUtilitiesCommonToCardRepository.java b/services/cards-consultation/src/main/java/org/opfab/cards/consultation/repositories/UserUtilitiesCommonToCardRepository.java index f0bacb133d..b1ae29b022 100644 --- a/services/cards-consultation/src/main/java/org/opfab/cards/consultation/repositories/UserUtilitiesCommonToCardRepository.java +++ b/services/cards-consultation/src/main/java/org/opfab/cards/consultation/repositories/UserUtilitiesCommonToCardRepository.java @@ -34,8 +34,6 @@ import org.opfab.cards.consultation.model.CardsFilter; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; -import org.springframework.data.mongodb.core.aggregation.AggregationOperation; - import org.springframework.data.mongodb.core.aggregation.*; import static org.springframework.data.mongodb.core.aggregation.Aggregation.*; @@ -286,13 +284,17 @@ private List getFilterCriteria(CardsFilter filter) { filter.filters().forEach(columnFilter -> { if (!SPECIAL_PARAMETERS.contains(columnFilter.columnName()) && columnFilter.operation() == null) { // Multiple conditions operations are not supported yet - criteria.add(getMatchingCriteria(columnFilter)); + try { + criteria.add(getMatchingCriteria(columnFilter)); + } catch (NumberFormatException | NoSuchFieldException | SecurityException e) { + // Ignore criteria on wrong columns + } } }); return criteria; } - private Criteria getMatchingCriteria(FilterModel columnFilter) { + private Criteria getMatchingCriteria(FilterModel columnFilter) throws NumberFormatException, NoSuchFieldException, SecurityException { Criteria criteria = null; switch(columnFilter.matchType()) { case EQUALS: @@ -322,6 +324,16 @@ private Criteria getMatchingCriteria(FilterModel columnFilter) { case IN: criteria = Criteria.where(columnFilter.columnName()).in(columnFilter.filter()); break; + case LESSTHAN: + if (org.opfab.cards.consultation.model.ArchivedCard.class.getDeclaredField(columnFilter.columnName()).getType().getCanonicalName().equals("java.time.Instant")) { + criteria = Criteria.where(columnFilter.columnName()).lt(Instant.ofEpochMilli(Long.parseLong(columnFilter.filter().get(0)))); + } else criteria = Criteria.where(columnFilter.columnName()).lt(columnFilter.filter().get(0)); + break; + case GREATERTHAN: + if (org.opfab.cards.consultation.model.ArchivedCard.class.getDeclaredField(columnFilter.columnName()).getType().getCanonicalName().equals("java.time.Instant")) { + criteria = Criteria.where(columnFilter.columnName()).gt(Instant.ofEpochMilli(Long.parseLong(columnFilter.filter().get(0)))); + } else criteria = Criteria.where(columnFilter.columnName()).gt(columnFilter.filter().get(0)); + break; } return criteria; } diff --git a/services/cards-consultation/src/test/java/org/opfab/cards/consultation/repositories/ArchivedCardRepositoryShould.java b/services/cards-consultation/src/test/java/org/opfab/cards/consultation/repositories/ArchivedCardRepositoryShould.java index 32c0dccf5d..b64cb5cf20 100644 --- a/services/cards-consultation/src/test/java/org/opfab/cards/consultation/repositories/ArchivedCardRepositoryShould.java +++ b/services/cards-consultation/src/test/java/org/opfab/cards/consultation/repositories/ArchivedCardRepositoryShould.java @@ -597,6 +597,122 @@ void fetchArchivedCardsActiveTo() { .verify(); } + @Test + void fetchArchivedCardsPublishDateGreaterThan() { + + FilterModel filter1 = FilterModel.builder() + .columnName("publishDate") + .matchType(FilterMatchTypeEnum.GREATERTHAN) + .filter(List.of(Long.toString(now.toEpochMilli()))) + .build(); + + CardsFilter filters = CardsFilter.builder() + .filters(List.of(filter1)).build(); + + Tuple2 filterParams = of(currentUser1, filters); + + + StepVerifier.create(repository.findWithUserAndFilter(filterParams)) + .assertNext(page -> { + assertThat(page.getTotalElements()).isEqualTo(3); + assertThat(page.getTotalPages()).isEqualTo(1); + // Check criteria are matched + assertTrue(checkIfCardsFromPageMeetCriteria(page, + card -> card.getPublishDate().isAfter(now)) + ); + //Check sort order + assertTrue(checkIfPageIsSorted(page)); + }) + .expectComplete() + .verify(); + } + + @Test + void fetchArchivedCardsPublishDateLessThan() { + + FilterModel filter1 = FilterModel.builder() + .columnName("publishDate") + .matchType(FilterMatchTypeEnum.LESSTHAN) + .filter(List.of(Long.toString(now.toEpochMilli()))) + .build(); + + CardsFilter filters = CardsFilter.builder() + .filters(List.of(filter1)).build(); + + Tuple2 filterParams = of(currentUser1, filters); + + + StepVerifier.create(repository.findWithUserAndFilter(filterParams)) + .assertNext(page -> { + assertThat(page.getTotalElements()).isEqualTo(9); + assertThat(page.getTotalPages()).isEqualTo(1); + // Check criteria are matched + assertTrue(checkIfCardsFromPageMeetCriteria(page, + card -> card.getPublishDate().isBefore(now)) + ); + //Check sort order + assertTrue(checkIfPageIsSorted(page)); + }) + .expectComplete() + .verify(); + } + + @Test + void fetchArchivedCardsProcessInstanceIdLessThan() { + + FilterModel filter1 = FilterModel.builder() + .columnName("processInstanceId") + .matchType(FilterMatchTypeEnum.LESSTHAN) + .filter(List.of("PROCESS3")) + .build(); + + CardsFilter filters = CardsFilter.builder() + .filters(List.of(filter1)).build(); + + Tuple2 filterParams = of(currentUser1, filters); + + StepVerifier.create(repository.findWithUserAndFilter(filterParams)) + .assertNext(page -> { + assertThat(page.getTotalElements()).isEqualTo(6); + assertThat(page.getTotalPages()).isEqualTo(1); + // Check criteria are matched + assertTrue(checkIfCardsFromPageMeetCriteria(page, + card -> card.getProcessInstanceId().compareTo("PROCESS3") < 0)); + // Check sort order + assertTrue(checkIfPageIsSorted(page)); + }) + .expectComplete() + .verify(); + } + + @Test + void fetchArchivedCardsProcessInstanceIdGreaterThan() { + + FilterModel filter1 = FilterModel.builder() + .columnName("processInstanceId") + .matchType(FilterMatchTypeEnum.GREATERTHAN) + .filter(List.of("PROCESS2")) + .build(); + + CardsFilter filters = CardsFilter.builder() + .filters(List.of(filter1)).build(); + + Tuple2 filterParams = of(currentUser1, filters); + + StepVerifier.create(repository.findWithUserAndFilter(filterParams)) + .assertNext(page -> { + assertThat(page.getTotalElements()).isEqualTo(7); + assertThat(page.getTotalPages()).isEqualTo(1); + // Check criteria are matched + assertTrue(checkIfCardsFromPageMeetCriteria(page, + card -> card.getProcessInstanceId().compareTo("PROCESS2") > 0)); + // Check sort order + assertTrue(checkIfPageIsSorted(page)); + }) + .expectComplete() + .verify(); + } + @Test void fetchArchivedCardsMixParams() { diff --git a/src/test/api/karate/cards/fetchArchivedCardsWithFilter.feature b/src/test/api/karate/cards/fetchArchivedCardsWithFilter.feature index c183103ade..00fc58e538 100644 --- a/src/test/api/karate/cards/fetchArchivedCardsWithFilter.feature +++ b/src/test/api/karate/cards/fetchArchivedCardsWithFilter.feature @@ -1105,6 +1105,98 @@ Scenario: fetch the first page Then status 200 And assert response.numberOfElements == 0 + + + Scenario: filter using LESSTHAN and GREATERTHAN + + * def filter = + """ + { + "page" : 0, + "size" : 10, + "filters" : [ + { + "columnName": "startDate", + "filter" : ["1583333122000"], + "matchType" : "GREATERTHAN" + } + ] + } + """ + + Given url opfabUrl + 'cards/archives' + And header Authorization = 'Bearer ' + authTokenAsTSO + And request filter + Then method post + Then status 200 + And assert response.numberOfElements == 0 + + * def filter = + """ + { + "page" : 0, + "size" : 10, + "filters" : [ + { + "columnName": "startDate", + "filter" : ["1583333111000"], + "matchType" : "GREATERTHAN" + } + ] + } + """ + + Given url opfabUrl + 'cards/archives' + And header Authorization = 'Bearer ' + authTokenAsTSO + And request filter + Then method post + Then status 200 + And assert response.numberOfElements == 10 + + * def filter = + """ + { + "page" : 0, + "size" : 10, + "filters" : [ + { + "columnName": "startDate", + "filter" : ["1583333122000"], + "matchType" : "LESSTHAN" + } + ] + } + """ + + Given url opfabUrl + 'cards/archives' + And header Authorization = 'Bearer ' + authTokenAsTSO + And request filter + Then method post + Then status 200 + And assert response.numberOfElements == 0 + + * def filter = + """ + { + "page" : 0, + "size" : 10, + "filters" : [ + { + "columnName": "startDate", + "filter" : ["1583333133000"], + "matchType" : "LESSTHAN" + } + ] + } + """ + + Given url opfabUrl + 'cards/archives' + And header Authorization = 'Bearer ' + authTokenAsTSO + And request filter + Then method post + Then status 200 + And assert response.numberOfElements == 10 + Scenario: delete perimeter #delete perimeter created previously * callonce read('../common/deletePerimeter.feature') {perimeterId: '#(perimeter.id)', token: '#(authTokenAdmin)'}