diff --git a/pluto-server/src/main/java/de/itvsh/ozg/pluto/common/search/IndexedVorgang.java b/pluto-server/src/main/java/de/itvsh/ozg/pluto/common/search/IndexedVorgang.java index 61c521a388eb812f229ac78e535e29907a31e4c8..89ed240061c82500ca2fa32df984ebb79b6c3a55 100644 --- a/pluto-server/src/main/java/de/itvsh/ozg/pluto/common/search/IndexedVorgang.java +++ b/pluto-server/src/main/java/de/itvsh/ozg/pluto/common/search/IndexedVorgang.java @@ -26,6 +26,8 @@ class IndexedVorgang { static final String FIELD_AKTENZEICHEN = "aktenzeichen"; static final String FIELD_ORGANISATIONSEINHEITEN_ID = "organisationseinheitenId"; static final String FIELD_CREATED_AT = "createdAt"; + static final String FIELD_STATUS = "status"; + static final String FIELD_ASSIGNED_TO = "assignedTo"; @Id private String vorgangId; diff --git a/pluto-server/src/main/java/de/itvsh/ozg/pluto/common/search/SearchRequest.java b/pluto-server/src/main/java/de/itvsh/ozg/pluto/common/search/SearchRequest.java index 41f0cb2372870c1980107151c9c2744b29e97a1d..1c6584d8129c36d3498c77b001c018f51f02e083 100644 --- a/pluto-server/src/main/java/de/itvsh/ozg/pluto/common/search/SearchRequest.java +++ b/pluto-server/src/main/java/de/itvsh/ozg/pluto/common/search/SearchRequest.java @@ -14,4 +14,7 @@ class SearchRequest { private int limit; private String query; private List<String> organisationseinheitIds; + private List<String> status; + private boolean filterByOrganisationseinheitIds; + private String assignedTo; } diff --git a/pluto-server/src/main/java/de/itvsh/ozg/pluto/common/search/SearchService.java b/pluto-server/src/main/java/de/itvsh/ozg/pluto/common/search/SearchService.java index d00df30f0be71f49520d1815c814c1a7bbf60905..e73ac501af849a36c85e741d859531982cdc6370 100644 --- a/pluto-server/src/main/java/de/itvsh/ozg/pluto/common/search/SearchService.java +++ b/pluto-server/src/main/java/de/itvsh/ozg/pluto/common/search/SearchService.java @@ -1,55 +1,27 @@ package de.itvsh.ozg.pluto.common.search; -import static de.itvsh.ozg.pluto.common.search.IndexedVorgang.FIELD_AKTENZEICHEN; -import static de.itvsh.ozg.pluto.common.search.IndexedVorgang.FIELD_ANTRAGSTELLER_NAME; -import static de.itvsh.ozg.pluto.common.search.IndexedVorgang.FIELD_ANTRAGSTELLTER_VORNAME; -import static de.itvsh.ozg.pluto.common.search.IndexedVorgang.FIELD_NAME; -import static de.itvsh.ozg.pluto.common.search.IndexedVorgang.FIELD_ORGANISATIONSEINHEITEN_ID; -import static de.itvsh.ozg.pluto.common.search.IndexedVorgang.FIELD_VORGANG_NUMMER; -import static org.elasticsearch.index.query.AbstractQueryBuilder.DEFAULT_BOOST; -import static org.elasticsearch.index.query.QueryBuilders.boolQuery; -import static org.elasticsearch.index.query.QueryBuilders.queryStringQuery; -import static org.elasticsearch.index.query.QueryBuilders.termsQuery; - -import java.util.Map; - -import org.apache.commons.collections4.CollectionUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.data.domain.Page; -import org.springframework.data.domain.PageRequest; -import org.springframework.data.elasticsearch.core.ElasticsearchOperations; -import org.springframework.data.elasticsearch.core.SearchHit; -import org.springframework.data.elasticsearch.core.query.NativeSearchQuery; -import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder; -import org.springframework.data.support.PageableExecutionUtils; import org.springframework.stereotype.Service; import de.itvsh.ozg.pluto.vorgang.FindVorgangRequest; import de.itvsh.ozg.pluto.vorgang.Vorgang; +import de.itvsh.ozg.pluto.vorgang.Vorgang.Status; import de.itvsh.ozg.pluto.vorgang.VorgangHeader; @Service @ConditionalOnProperty(prefix = "spring.elasticsearch", name = "uris") public class SearchService { - private static final float HALF_BOOST = 0.5f; - private static final float DOUBLE_BOOST = 2f; - - private static final Map<String, Float> FIELD_MAP = Map.of( - FIELD_NAME, HALF_BOOST, - FIELD_VORGANG_NUMMER, DOUBLE_BOOST, - FIELD_AKTENZEICHEN, DOUBLE_BOOST, - FIELD_ANTRAGSTELLER_NAME, DEFAULT_BOOST, - FIELD_ANTRAGSTELLTER_VORNAME, DEFAULT_BOOST); @Autowired private SearchVorgangRepostitory repostitory; @Autowired - private IndexedVorgangMapper mapper; + private SearchVorgangCustomRepostitory searchRepostitory; @Autowired - private ElasticsearchOperations elasticsearchOperations; + private IndexedVorgangMapper mapper; public void addVorgang(Vorgang vorgang) { repostitory.save(mapper.fromVorgang(vorgang)); @@ -64,36 +36,16 @@ public class SearchService { } public Page<VorgangHeader> find(FindVorgangRequest request) { - // FIXME search by for POSTSTELLE var searchRequest = SearchRequest.builder() .limit(request.getLimit()) .offSet(request.getOffset()) .query(request.getSearchBy()) - .organisationseinheitIds(request.getFilterBy().getOrganisationseinheitIds()) - .build(); - - return searchBy(searchRequest); - } - - Page<VorgangHeader> searchBy(SearchRequest request) { - var pageable = PageRequest.of(request.getOffSet(), request.getLimit()); - var searchQuery = createQuery(request, pageable); - - var result = elasticsearchOperations.search(searchQuery, IndexedVorgang.class); - var mappedResult = result.get().map(SearchHit::getContent).map(mapper::toVorgangHeader).toList(); + .status(request.getFilterBy().getStatus().stream().map(Status::name).toList()) + .filterByOrganisationseinheitIds(request.getFilterBy().isFilterByOrganisationseinheitenId()) + .assignedTo(request.getFilterBy().getAssignedTo()) + .organisationseinheitIds(request.getFilterBy().getOrganisationseinheitIds()).build(); - return PageableExecutionUtils.getPage(mappedResult, pageable, result::getTotalHits); + return searchRepostitory.searchBy(searchRequest); } - NativeSearchQuery createQuery(SearchRequest request, PageRequest pageable) { - NativeSearchQueryBuilder searchQueryBuilder = new NativeSearchQueryBuilder(); - - // FIXME security issue - missing orgaId should not lead to deliver all items - if (CollectionUtils.isNotEmpty(request.getOrganisationseinheitIds())) { - searchQueryBuilder.withFilter(boolQuery().must(termsQuery(FIELD_ORGANISATIONSEINHEITEN_ID, request.getOrganisationseinheitIds()))); - } - - return searchQueryBuilder.withQuery(queryStringQuery("*" + request.getQuery() + "*").fields(FIELD_MAP)) - .withPageable(pageable).build(); - } } diff --git a/pluto-server/src/main/java/de/itvsh/ozg/pluto/common/search/SearchVorgangCustomRepositoryImpl.java b/pluto-server/src/main/java/de/itvsh/ozg/pluto/common/search/SearchVorgangCustomRepositoryImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..e02db82b6bd33c886132385098316cbfbd07f865 --- /dev/null +++ b/pluto-server/src/main/java/de/itvsh/ozg/pluto/common/search/SearchVorgangCustomRepositoryImpl.java @@ -0,0 +1,93 @@ +package de.itvsh.ozg.pluto.common.search; + +import static de.itvsh.ozg.pluto.common.search.IndexedVorgang.*; +import static org.elasticsearch.index.query.AbstractQueryBuilder.*; +import static org.elasticsearch.index.query.QueryBuilders.*; + +import java.util.Arrays; +import java.util.Map; + +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.lang3.StringUtils; +import org.elasticsearch.index.query.BoolQueryBuilder; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.elasticsearch.core.ElasticsearchOperations; +import org.springframework.data.elasticsearch.core.SearchHit; +import org.springframework.data.elasticsearch.core.query.NativeSearchQuery; +import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder; +import org.springframework.data.support.PageableExecutionUtils; +import org.springframework.stereotype.Repository; + +import de.itvsh.ozg.pluto.vorgang.VorgangHeader; + +@Repository +class SearchVorgangCustomRepositoryImpl implements SearchVorgangCustomRepostitory { + private static final String KEYWORD = ".keyword"; + private static final float HALF_BOOST = 0.5f; + private static final float DOUBLE_BOOST = 2f; + + static final Map<String, Float> FIELD_MAP = Map.of( + FIELD_NAME, HALF_BOOST, + FIELD_VORGANG_NUMMER, DOUBLE_BOOST, + FIELD_AKTENZEICHEN, DOUBLE_BOOST, + FIELD_ANTRAGSTELLER_NAME, DEFAULT_BOOST, + FIELD_ANTRAGSTELLTER_VORNAME, DEFAULT_BOOST); + + @Autowired + private ElasticsearchOperations elasticsearchOperations; + + @Autowired + private IndexedVorgangMapper mapper; + + @Override + public Page<VorgangHeader> searchBy(SearchRequest request) { + var pageable = PageRequest.of(request.getOffSet(), request.getLimit()); + var searchQuery = createQuery(request, pageable); + + var result = elasticsearchOperations.search(searchQuery, IndexedVorgang.class); + var mappedResult = result.get().map(SearchHit::getContent).map(mapper::toVorgangHeader).toList(); + + return PageableExecutionUtils.getPage(mappedResult, pageable, result::getTotalHits); + } + + NativeSearchQuery createQuery(SearchRequest request, PageRequest pageable) { + NativeSearchQueryBuilder searchQueryBuilder = new NativeSearchQueryBuilder(); + + setFilter(request, searchQueryBuilder); + + Arrays.stream(request.getQuery().strip().split(" ")).forEach(query -> searchQueryBuilder + .withQuery(queryStringQuery("*" + query + "*").fields(SearchVorgangCustomRepositoryImpl.FIELD_MAP))); + + return searchQueryBuilder.withPageable(pageable).build(); + } + + private boolean dontFilterByOrganisationseinheitIds(SearchRequest request) { + return !request.isFilterByOrganisationseinheitIds(); + } + + private void setFilter(SearchRequest request, NativeSearchQueryBuilder searchQueryBuilder) { + var query = boolQuery(); + + if (request.isFilterByOrganisationseinheitIds()) { + addByOrganisationseinheitIdsFilters(request, query); + } else if (CollectionUtils.isNotEmpty(request.getStatus())) { + query.must(termsQuery(FIELD_STATUS + KEYWORD, request.getStatus())); + } + + searchQueryBuilder.withFilter(query); + } + + private void addByOrganisationseinheitIdsFilters(SearchRequest request, BoolQueryBuilder query) { + query.must(termsQuery(FIELD_ORGANISATIONSEINHEITEN_ID, request.getOrganisationseinheitIds())); + + if (CollectionUtils.isNotEmpty(request.getStatus())) { + query.must(termsQuery(FIELD_STATUS + KEYWORD, request.getStatus())); + } + + if (StringUtils.isNotEmpty(request.getAssignedTo())) { + query.must(termsQuery(FIELD_ASSIGNED_TO + KEYWORD, request.getAssignedTo())); + } + } +} diff --git a/pluto-server/src/main/java/de/itvsh/ozg/pluto/common/search/SearchVorgangCustomRepostitory.java b/pluto-server/src/main/java/de/itvsh/ozg/pluto/common/search/SearchVorgangCustomRepostitory.java new file mode 100644 index 0000000000000000000000000000000000000000..fc5267891c94539d2074375023b8a8d938bbd46f --- /dev/null +++ b/pluto-server/src/main/java/de/itvsh/ozg/pluto/common/search/SearchVorgangCustomRepostitory.java @@ -0,0 +1,11 @@ +package de.itvsh.ozg.pluto.common.search; + +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.data.domain.Page; + +import de.itvsh.ozg.pluto.vorgang.VorgangHeader; + +@ConditionalOnProperty(prefix = "spring.elasticsearch", name = "uris") +interface SearchVorgangCustomRepostitory { + Page<VorgangHeader> searchBy(SearchRequest request); +} diff --git a/pluto-server/src/main/java/de/itvsh/ozg/pluto/common/search/SearchVorgangRepostitory.java b/pluto-server/src/main/java/de/itvsh/ozg/pluto/common/search/SearchVorgangRepostitory.java index bd63a8ce7bb9ef898814aa7a2320ef9aa3f8cb13..7ea58ed7c223132775a754f2efdd5968ea750661 100644 --- a/pluto-server/src/main/java/de/itvsh/ozg/pluto/common/search/SearchVorgangRepostitory.java +++ b/pluto-server/src/main/java/de/itvsh/ozg/pluto/common/search/SearchVorgangRepostitory.java @@ -5,5 +5,4 @@ import org.springframework.data.elasticsearch.repository.ElasticsearchRepository @ConditionalOnProperty(prefix = "spring.elasticsearch", name = "uris") interface SearchVorgangRepostitory extends ElasticsearchRepository<IndexedVorgang, String> { - } diff --git a/pluto-server/src/test/java/de/itvsh/ozg/pluto/common/search/SearchInitializer.java b/pluto-server/src/test/java/de/itvsh/ozg/pluto/common/search/SearchInitializer.java index 3f363d32b77a4f1719a5db53a153c9e243daa413..70af34e924f436b12dacfd9cf3aae1633eca53a3 100644 --- a/pluto-server/src/test/java/de/itvsh/ozg/pluto/common/search/SearchInitializer.java +++ b/pluto-server/src/test/java/de/itvsh/ozg/pluto/common/search/SearchInitializer.java @@ -1,6 +1,5 @@ package de.itvsh.ozg.pluto.common.search; -import java.io.InputStream; import java.time.Duration; import org.springframework.boot.test.util.TestPropertyValues; @@ -54,13 +53,7 @@ class SearchInitializer implements ApplicationContextInitializer<ConfigurableApp TestPropertyValues.of( "spring.elasticsearch.uris= http://" + container.getHost() + ":" + container.getFirstMappedPort(), "spring.elasticsearch.username= " + USERNAME, - "spring.elasticserach.password= " + PASSWORD, - // "kop.elasticsearch.initEnabled=false", + "spring.elasticsearch.password= " + PASSWORD, "kop.elasticsearch.index=test").applyTo(applicationContext); } - - private String getRootCa(ElasticsearchContainer container) { - return new String(container.copyFileFromContainer("/usr/share/elasticsearch/config/certs/http_ca.crt", InputStream::readAllBytes)); - } - } diff --git a/pluto-server/src/test/java/de/itvsh/ozg/pluto/common/search/SearchRequestTestFactory.java b/pluto-server/src/test/java/de/itvsh/ozg/pluto/common/search/SearchRequestTestFactory.java index cdb7ab358281aea76a1f48da43fad9b02d3c0c5d..a746dc49fef3a64d37d076e92f1d2c0d7316c459 100644 --- a/pluto-server/src/test/java/de/itvsh/ozg/pluto/common/search/SearchRequestTestFactory.java +++ b/pluto-server/src/test/java/de/itvsh/ozg/pluto/common/search/SearchRequestTestFactory.java @@ -2,6 +2,7 @@ package de.itvsh.ozg.pluto.common.search; import org.elasticsearch.core.List; +import de.itvsh.ozg.pluto.command.UserTestFactory; import de.itvsh.ozg.pluto.vorgang.FindVorgangRequestTestFactory; import de.itvsh.ozg.pluto.vorgang.ZustaendigeStelleTestFactory; @@ -11,6 +12,8 @@ class SearchRequestTestFactory { static final String QUERY = "vors"; static final String ORGANISATIONSEINHEIT_ID = ZustaendigeStelleTestFactory.ORGANISATIONSEINHEIT_ID; + static final boolean FILTER_BY_ORGANISATIONSEINHEIT_ID = true; + static final String ASSIGNED_TO = UserTestFactory.ID; static SearchRequest create() { return createBuilder().build(); @@ -21,6 +24,8 @@ class SearchRequestTestFactory { .limit(LIMIT) .offSet(OFFSET) .query(QUERY) + .filterByOrganisationseinheitIds(FILTER_BY_ORGANISATIONSEINHEIT_ID) + .assignedTo(ASSIGNED_TO) .organisationseinheitIds(List.of(ORGANISATIONSEINHEIT_ID)); } } diff --git a/pluto-server/src/test/java/de/itvsh/ozg/pluto/common/search/SearchServiceITCase.java b/pluto-server/src/test/java/de/itvsh/ozg/pluto/common/search/SearchServiceITCase.java index d2eec722ea133d13c5bb3ed4546236c94946bf8b..a2272b0aae88875ba17ff896306affba498a2bd1 100644 --- a/pluto-server/src/test/java/de/itvsh/ozg/pluto/common/search/SearchServiceITCase.java +++ b/pluto-server/src/test/java/de/itvsh/ozg/pluto/common/search/SearchServiceITCase.java @@ -1,7 +1,7 @@ package de.itvsh.ozg.pluto.common.search; -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.when; +import static org.assertj.core.api.Assertions.*; +import static org.mockito.Mockito.*; import java.io.IOException; import java.util.Optional; @@ -20,6 +20,9 @@ import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates; import de.itvsh.ozg.pluto.command.Command; import de.itvsh.ozg.pluto.command.CommandService; import de.itvsh.ozg.pluto.command.CommandTestFactory; +import de.itvsh.ozg.pluto.command.UserTestFactory; +import de.itvsh.ozg.pluto.vorgang.FilterCriteriaTestFactory; +import de.itvsh.ozg.pluto.vorgang.FindVorgangRequestTestFactory; import de.itvsh.ozg.pluto.vorgang.Vorgang.Status; import de.itvsh.ozg.pluto.vorgang.VorgangTestFactory; @@ -78,16 +81,13 @@ public class SearchServiceITCase { @Nested class TestSearchVorgang { -// private static final String DATE_1 = "2007-11-03T10:15:30Z"; -// private static final String DATE_2 = "2012-11-03T10:15:30Z"; -// private static final String DATE_3 = "2022-11-03T10:15:30Z"; - @BeforeEach void init() { elasticsearchOperations.indexOps(IndexedVorgang.class).delete(); searchService.addVorgang(VorgangTestFactory.create()); - searchService.addVorgang(VorgangTestFactory.createBuilder().id(new ObjectId().toHexString()).aktenzeichen("test").build()); + searchService.addVorgang(VorgangTestFactory.createBuilder().id(new ObjectId().toHexString()).aktenzeichen("test") + .assignedTo(UUID.randomUUID().toString()).build()); searchService.addVorgang(VorgangTestFactory.createBuilder().id(new ObjectId().toHexString()).aktenzeichen("vors").build()); searchService.addVorgang(VorgangTestFactory.createBuilder().id(new ObjectId().toHexString()).aktenzeichen("vOrsi").build()); @@ -96,40 +96,142 @@ public class SearchServiceITCase { elasticsearchOperations.save(IndexedVorgangTestFactory.create(), IndexCoordinates.of("other-test")); } - @Test - void shouldFind() { - var res = searchService.searchBy(SearchRequestTestFactory.create()); - - assertThat(res).hasSize(2); + @Nested + class filteredByOrganisationseinheitenId { + @Test + void shouldFindByAktenzeichen() { + var res = searchService.find(FindVorgangRequestTestFactory.createBuilder() + .searchBy("vors") + .filterBy(FilterCriteriaTestFactory.createBuilder().clearStatus().assignedTo(null).build()) + .build()); + + assertThat(res).hasSize(2); + } + + @Test + void shouldFindByAntragstellerName() { + var res = searchService + .find(FindVorgangRequestTestFactory.createBuilder() + .searchBy(IndexedVorgangTestFactory.ANTRAGSTELLER_NAME) + .filterBy(FilterCriteriaTestFactory.createBuilder().clearStatus().assignedTo(null).build()) + .build()); + + assertThat(res).hasSize(4); + } + + @Test + void shouldFindByAntragstellerNameAndAktenzeichen() { + var res = searchService + .find(FindVorgangRequestTestFactory.createBuilder() + .searchBy(IndexedVorgangTestFactory.ANTRAGSTELLER_NAME + " test") + .filterBy(FilterCriteriaTestFactory.createBuilder().clearStatus().assignedTo(null).build()) + .build()); + + assertThat(res).hasSize(1); + } + + @Test + void shouldFindByAntragstellerNameAndereOrganisationEinheitId() { + searchService + .addVorgang( + VorgangTestFactory.createWithOrganisationEinheitId("12345678").toBuilder().id(UUID.randomUUID().toString()).build()); + + var res = searchService.find(FindVorgangRequestTestFactory.createBuilder() + .searchBy(IndexedVorgangTestFactory.ANTRAGSTELLER_NAME).filterBy( + FilterCriteriaTestFactory.createBuilder() + .clearOrganisationseinheitIds() + .organisationseinheitIds(List.of("12345678")) + .clearStatus().build()) + .build()); + + assertThat(res).hasSize(1); + } + + @Test + void shouldDoPageination() { + var res = searchService + .find(FindVorgangRequestTestFactory.createBuilder() + .searchBy(IndexedVorgangTestFactory.ANTRAGSTELLER_NAME) + .limit(2) + .filterBy(FilterCriteriaTestFactory.createBuilder().clearStatus().build()) + .build()); + + assertThat(res).hasSize(2); + assertThat(res.getTotalPages()).isEqualTo(2); + } } - @Test - void shouldFindByAntragstellerName() { - var res = searchService - .searchBy(SearchRequestTestFactory.createBuilder().query(IndexedVorgangTestFactory.ANTRAGSTELLER_NAME).build()); - - assertThat(res).hasSize(4); + @Nested + class filteredForStatus { + @Test + void shouldFindByAntragstellerName() { + searchService + .addVorgang( + VorgangTestFactory.createWithOrganisationEinheitId("12345678").toBuilder().id(UUID.randomUUID().toString()).build()); + + var res = searchService + .find(FindVorgangRequestTestFactory.createBuilder() + .searchBy(IndexedVorgangTestFactory.ANTRAGSTELLER_NAME) + .filterBy(FilterCriteriaTestFactory.createBuilder().filterByOrganisationseinheitenId(false).build()) + .build()); + + assertThat(res).hasSize(5); + } } - @Test - void shouldFindByAntragstellerNameAndereOrganisationEinheitId() { - searchService - .addVorgang(VorgangTestFactory.createWithOrganisationEinheitId("12345678").toBuilder().id(UUID.randomUUID().toString()).build()); - - var res = searchService.searchBy(SearchRequestTestFactory.createBuilder() - .query(IndexedVorgangTestFactory.ANTRAGSTELLER_NAME).organisationseinheitIds(List.of("12345678")).build()); - - assertThat(res).hasSize(1); + @Nested + class filteredForStatusAndOrganisationEinheitId { + @BeforeEach + void init() { + searchService + .addVorgang( + VorgangTestFactory.createWithOrganisationEinheitId("12345678").toBuilder().id(UUID.randomUUID().toString()).build()); + searchService + .addVorgang(VorgangTestFactory.createBuilder().status(Status.ANGENOMMEN).id(UUID.randomUUID().toString()).build()); + } + + @Test + void shouldFindByAktenzeichen() { + var res = searchService + .find(FindVorgangRequestTestFactory.createBuilder() + .searchBy("test") + .filterBy(FilterCriteriaTestFactory.createBuilder().assignedTo(null).build()) + .build()); + + assertThat(res).hasSize(1); + } + + @Test + void shouldFindByAntragstellerName() { + var res = searchService + .find(FindVorgangRequestTestFactory.createBuilder() + .searchBy(IndexedVorgangTestFactory.ANTRAGSTELLER_NAME) + .filterBy(FilterCriteriaTestFactory.createBuilder().assignedTo(null).build()) + .build()); + + assertThat(res).hasSize(4); + } } - @Test - void shouldDoPageination() { - var res = searchService - .searchBy(SearchRequestTestFactory.createBuilder().query(IndexedVorgangTestFactory.ANTRAGSTELLER_NAME).limit(2).build()); - - assertThat(res).hasSize(2); - assertThat(res.getTotalPages()).isEqualTo(2); + @Nested + class filteredForAssignedToAndOrganisationEinheitId { + @BeforeEach + void init() { + searchService + .addVorgang( + VorgangTestFactory.createWithOrganisationEinheitId("12345678").toBuilder().id(UUID.randomUUID().toString()).build()); + } + + @Test + void shouldFindByAntragstellerName() { + var res = searchService + .find(FindVorgangRequestTestFactory.createBuilder() + .searchBy(IndexedVorgangTestFactory.ANTRAGSTELLER_NAME) + .filterBy(FilterCriteriaTestFactory.createBuilder().clearStatus().assignedTo(UserTestFactory.ID).build()) + .build()); + + assertThat(res).hasSize(3); + } } } - } diff --git a/pluto-server/src/test/java/de/itvsh/ozg/pluto/common/search/SearchServiceTest.java b/pluto-server/src/test/java/de/itvsh/ozg/pluto/common/search/SearchServiceTest.java index 22509834e00ce11d999dcdf22b35d31088c0f1d8..c1e0476cb146672d0c7a96238e09aa5e5255f936 100644 --- a/pluto-server/src/test/java/de/itvsh/ozg/pluto/common/search/SearchServiceTest.java +++ b/pluto-server/src/test/java/de/itvsh/ozg/pluto/common/search/SearchServiceTest.java @@ -1,27 +1,15 @@ package de.itvsh.ozg.pluto.common.search; -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.atLeastOnce; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import org.elasticsearch.core.List; -import org.junit.jupiter.api.BeforeEach; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; + import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.springframework.data.domain.PageRequest; -import org.springframework.data.domain.Sort; import org.springframework.data.elasticsearch.core.ElasticsearchOperations; -import org.springframework.data.elasticsearch.core.SearchHit; -import org.springframework.data.elasticsearch.core.SearchHits; -import org.springframework.data.elasticsearch.core.query.NativeSearchQuery; -import de.itvsh.ozg.pluto.vorgang.VorgangHeader; -import de.itvsh.ozg.pluto.vorgang.VorgangHeaderTestFactory; +import de.itvsh.ozg.pluto.vorgang.FindVorgangRequestTestFactory; import de.itvsh.ozg.pluto.vorgang.VorgangTestFactory; class SearchServiceTest { @@ -31,6 +19,10 @@ class SearchServiceTest { @Mock private SearchVorgangRepostitory repostitory; + + @Mock + private SearchVorgangCustomRepostitory searchRepostitory; + @Mock private IndexedVorgangMapper mapper; @@ -66,74 +58,11 @@ class SearchServiceTest { @Nested class TestSearch { - - @Mock - private SearchHits<IndexedVorgang> searchHits; - @Mock - private SearchHit<IndexedVorgang> searchHit; - - @BeforeEach - void init() { - when(searchHit.getContent()).thenReturn(IndexedVorgangTestFactory.create()); - when(searchHits.get()).thenReturn(List.of(searchHit).stream()); - when(operations.search(any(NativeSearchQuery.class), eq(IndexedVorgang.class))).thenReturn(searchHits); - } - @Test void shouldCallSearch() { - searchService.searchBy(SearchRequestTestFactory.create()); + searchService.find(FindVorgangRequestTestFactory.create()); - verify(mapper, atLeastOnce()).toVorgangHeader(any()); - verify(operations).search(any(NativeSearchQuery.class), eq(IndexedVorgang.class)); - } - - @Test - void shouldMapResult() { - when(mapper.toVorgangHeader(any())).thenReturn(VorgangHeaderTestFactory.create()); - - var res = searchService.searchBy(SearchRequestTestFactory.create()); - - assertThat(res).isNotEmpty().hasAtLeastOneElementOfType(VorgangHeader.class); + verify(searchRepostitory).searchBy(any(SearchRequest.class)); } } - - @Nested - class TestQuery { - @Test - void shouldHaveFilter() { - var request = SearchRequestTestFactory.create(); - var query = searchService.createQuery(request, PageRequest.of(request.getOffSet(), request.getLimit())); - - assertThat(query.getFilter()).isNotNull(); - assertThat(query.getFilter()).asString().contains("organisationseinheitenId"); - assertThat(query.getFilter()).asString().contains("08150815"); - } - - @Test - void shouldHavePaging() { - var request = SearchRequestTestFactory.create(); - var query = searchService.createQuery(request, PageRequest.of(request.getOffSet(), request.getLimit())); - - assertThat(query.getPageable()).isNotNull(); - assertThat(query.getPageable().getOffset()).isZero(); - assertThat(query.getPageable().getPageSize()).isEqualTo(SearchRequestTestFactory.LIMIT); - assertThat(query.getPageable().getSort()).isEqualTo(Sort.unsorted()); - } - - @Test - void shouldHaveQuery() { - var request = SearchRequestTestFactory.create(); - var query = searchService.createQuery(request, PageRequest.of(request.getOffSet(), request.getLimit())); - - assertThat(query.getQuery()).isNotNull(); - assertThat(query.getQuery()).asString().contains("\"query\" : \"*vors*\""); - assertThat(query.getQuery()).asString().contains("aktenzeichen^2.0"); - assertThat(query.getQuery()).asString().contains("antragstellerName^1.0"); - assertThat(query.getQuery()).asString().contains("antragstellerVorname^1.0"); - assertThat(query.getQuery()).asString().contains("vorgangName^0.5"); - assertThat(query.getQuery()).asString().contains("vorgangNummer^2.0"); - } - - } - } diff --git a/pluto-server/src/test/java/de/itvsh/ozg/pluto/common/search/SearchVorgangCustomRepositoryImplTest.java b/pluto-server/src/test/java/de/itvsh/ozg/pluto/common/search/SearchVorgangCustomRepositoryImplTest.java new file mode 100644 index 0000000000000000000000000000000000000000..20a67199ff2ceabe22f83460f5f4a2c7907dca30 --- /dev/null +++ b/pluto-server/src/test/java/de/itvsh/ozg/pluto/common/search/SearchVorgangCustomRepositoryImplTest.java @@ -0,0 +1,158 @@ +package de.itvsh.ozg.pluto.common.search; + +import static org.assertj.core.api.Assertions.*; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; + +import org.elasticsearch.core.List; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Sort; +import org.springframework.data.elasticsearch.core.ElasticsearchOperations; +import org.springframework.data.elasticsearch.core.SearchHit; +import org.springframework.data.elasticsearch.core.SearchHits; +import org.springframework.data.elasticsearch.core.query.NativeSearchQuery; + +import de.itvsh.ozg.pluto.vorgang.Vorgang.Status; +import de.itvsh.ozg.pluto.vorgang.VorgangHeader; +import de.itvsh.ozg.pluto.vorgang.VorgangHeaderTestFactory; + +class SearchVorgangCustomRepositoryImplTest { + @InjectMocks + private SearchVorgangCustomRepositoryImpl searchRepostitory; + + @Mock + private IndexedVorgangMapper mapper; + + @Mock + private SearchProperties properties; + + @Mock + private ElasticsearchOperations operations; + + @Nested + class TestSearch { + + @Mock + private SearchHits<IndexedVorgang> searchHits; + @Mock + private SearchHit<IndexedVorgang> searchHit; + + @BeforeEach + void init() { + when(searchHit.getContent()).thenReturn(IndexedVorgangTestFactory.create()); + when(searchHits.get()).thenReturn(List.of(searchHit).stream()); + when(operations.search(any(NativeSearchQuery.class), eq(IndexedVorgang.class))).thenReturn(searchHits); + } + + @Test + void shouldCallSearch() { + searchRepostitory.searchBy(SearchRequestTestFactory.create()); + + verify(mapper, atLeastOnce()).toVorgangHeader(any()); + verify(operations).search(any(NativeSearchQuery.class), eq(IndexedVorgang.class)); + } + + @Test + void shouldMapResult() { + when(mapper.toVorgangHeader(any())).thenReturn(VorgangHeaderTestFactory.create()); + + var res = searchRepostitory.searchBy(SearchRequestTestFactory.create()); + + assertThat(res).isNotEmpty().hasAtLeastOneElementOfType(VorgangHeader.class); + } + } + + @Nested + class TestQuery { + @Test + void shouldHavePaging() { + var request = SearchRequestTestFactory.create(); + var query = searchRepostitory.createQuery(request, PageRequest.of(request.getOffSet(), request.getLimit())); + + assertThat(query.getPageable()).isNotNull(); + assertThat(query.getPageable().getOffset()).isZero(); + assertThat(query.getPageable().getPageSize()).isEqualTo(SearchRequestTestFactory.LIMIT); + assertThat(query.getPageable().getSort()).isEqualTo(Sort.unsorted()); + } + + @Test + void shouldHaveQuery() { + var request = SearchRequestTestFactory.create(); + var query = searchRepostitory.createQuery(request, PageRequest.of(request.getOffSet(), request.getLimit())); + + assertThat(query.getQuery()).isNotNull(); + assertThat(query.getQuery()).asString().contains("\"query\" : \"*vors*\""); + assertThat(query.getQuery()).asString().contains("aktenzeichen^2.0"); + assertThat(query.getQuery()).asString().contains("antragstellerName^1.0"); + assertThat(query.getQuery()).asString().contains("antragstellerVorname^1.0"); + assertThat(query.getQuery()).asString().contains("vorgangName^0.5"); + assertThat(query.getQuery()).asString().contains("vorgangNummer^2.0"); + } + + @Nested + class forRolePoststelle { + @Test + void shouldHaveStatusFilterOnly() { + var request = SearchRequestTestFactory.createBuilder().filterByOrganisationseinheitIds(false).organisationseinheitIds(null) + .status(List.of(Status.ABGESCHLOSSEN.name())) + .build(); + var query = searchRepostitory.createQuery(request, PageRequest.of(request.getOffSet(), request.getLimit())); + + assertThat(query.getFilter()).isNotNull(); + assertThat(query.getFilter()).asString().contains("status"); + assertThat(query.getFilter()).asString().contains(Status.ABGESCHLOSSEN.name()); + assertThat(query.getFilter()).asString().doesNotContain("organisationseinheitenId"); + } + + @Test + void shouldHaveOrganisationseinheitenIdFilterOnly() { + var request = SearchRequestTestFactory.create(); + var query = searchRepostitory.createQuery(request, PageRequest.of(request.getOffSet(), request.getLimit())); + + assertThat(query.getFilter()).isNotNull(); + assertThat(query.getFilter()).asString().contains("organisationseinheitenId"); + assertThat(query.getFilter()).asString().contains(SearchRequestTestFactory.ORGANISATIONSEINHEIT_ID); + } + + @Test + void shouldHaveOrganisationseinheitenAndStatusFilter() { + var request = SearchRequestTestFactory.createBuilder().status(List.of(Status.ABGESCHLOSSEN.name())).build(); + var query = searchRepostitory.createQuery(request, PageRequest.of(request.getOffSet(), request.getLimit())); + + assertThat(query.getFilter()).isNotNull(); + assertThat(query.getFilter()).asString().contains("organisationseinheitenId"); + assertThat(query.getFilter()).asString().contains(SearchRequestTestFactory.ORGANISATIONSEINHEIT_ID); + assertThat(query.getFilter()).asString().contains("status"); + assertThat(query.getFilter()).asString().contains(Status.ABGESCHLOSSEN.name()); + } + } + + @Nested + class forOtherRoles { + @Test + void shouldHaveOrganisationseinheitenIdFilter() { + var request = SearchRequestTestFactory.create(); + var query = searchRepostitory.createQuery(request, PageRequest.of(request.getOffSet(), request.getLimit())); + + assertThat(query.getFilter()).isNotNull(); + assertThat(query.getFilter()).asString().contains("organisationseinheitenId"); + assertThat(query.getFilter()).asString().contains(SearchRequestTestFactory.ORGANISATIONSEINHEIT_ID); + } + + @Test + void shouldHaveAssignedToFilter() { + var request = SearchRequestTestFactory.create(); + var query = searchRepostitory.createQuery(request, PageRequest.of(request.getOffSet(), request.getLimit())); + + assertThat(query.getFilter()).isNotNull(); + assertThat(query.getFilter()).asString().contains("assignedTo"); + assertThat(query.getFilter()).asString().contains(SearchRequestTestFactory.ASSIGNED_TO); + } + } + } +}