Jean-Francois Leveque

https://tree.taiga.io/project/jr-utily-grog-v3/us/188 Faire une recherche global…

…e qui renvoie les résultats full text sur toutes les entités
...@@ -2,5 +2,8 @@ package org.legrog.entities; ...@@ -2,5 +2,8 @@ package org.legrog.entities;
2 2
3 import org.springframework.data.jpa.repository.JpaRepository; 3 import org.springframework.data.jpa.repository.JpaRepository;
4 4
5 +import java.util.List;
6 +
5 public interface AccountRepository extends JpaRepository<Account, Integer> { 7 public interface AccountRepository extends JpaRepository<Account, Integer> {
8 + List<Account> findByUserIdIn(List<Integer> integers);
6 } 9 }
......
1 +package org.legrog.entities;
2 +
3 +import java.util.List;
4 +
5 +/**
6 + *
7 + */
8 +public interface AccountSearchRepository {
9 + /**
10 + *
11 + * @param string String searched in indexed Accounts
12 + * @return Accounts that match the string
13 + */
14 + List<IndexedAccount> search(String string) throws SearchingException;
15 +}
1 +package org.legrog.entities;
2 +
3 +import org.apache.solr.client.solrj.SolrClient;
4 +import org.apache.solr.client.solrj.SolrQuery;
5 +import org.apache.solr.client.solrj.SolrServerException;
6 +import org.apache.solr.client.solrj.response.QueryResponse;
7 +import org.slf4j.Logger;
8 +import org.slf4j.LoggerFactory;
9 +
10 +import javax.inject.Inject;
11 +import java.io.IOException;
12 +import java.util.ArrayList;
13 +import java.util.List;
14 +
15 +/**
16 + *
17 + */
18 +public class AccountSearchRepositorySolrj implements AccountSearchRepository {
19 + Logger logger = LoggerFactory.getLogger(getClass());
20 +
21 + SolrClient solrClient;
22 +
23 + @Inject
24 + AccountSearchRepositorySolrj(SolrClient solrClient) {
25 + this.solrClient = solrClient;
26 + }
27 +
28 + //no args constructor to make it proxyable
29 + AccountSearchRepositorySolrj() {
30 + }
31 +
32 + @Override
33 + public List<IndexedAccount> search(String string) throws SearchingException {
34 + SolrQuery solrQuery = new SolrQuery(string);
35 + QueryResponse queryResponse;
36 + try {
37 + queryResponse = solrClient.query("accounts", solrQuery);
38 + } catch (IOException ioe) {
39 + throw new SearchingException(ioe);
40 + } catch (SolrServerException sse) {
41 + logger.error("SolrServerException {}", sse);
42 + throw new SearchingException(sse.getRootCause());
43 + }
44 +
45 + if (queryResponse != null) {
46 + return queryResponse.getBeans(IndexedAccount.class);
47 + } else {
48 + return new ArrayList<>();
49 + }
50 + }
51 +}
1 package org.legrog.web.account; 1 package org.legrog.web.account;
2 2
3 import org.legrog.entities.Account; 3 import org.legrog.entities.Account;
4 +import org.legrog.entities.IndexedAccount;
5 +import org.legrog.entities.PublisherVersion;
6 +import org.legrog.entities.SearchingException;
4 7
8 +import javax.validation.constraints.NotNull;
5 import java.util.List; 9 import java.util.List;
6 10
7 public interface AccountService { 11 public interface AccountService {
...@@ -12,4 +16,18 @@ public interface AccountService { ...@@ -12,4 +16,18 @@ public interface AccountService {
12 Account findUserById(int id); 16 Account findUserById(int id);
13 17
14 void updateUser(Account account); 18 void updateUser(Account account);
19 +
20 + /**
21 + *
22 + * @param string String searched in indexed Accounts
23 + * @return indexed IndexedAccounts that match the String
24 + */
25 + List<Account> search(@NotNull String string) throws SearchingException;
26 +
27 + /**
28 + *
29 + * @param indexedAccounts IndexedAccount to convert
30 + * @return Accounts
31 + */
32 + List<Account> convert(List<IndexedAccount> indexedAccounts);
15 } 33 }
......
1 package org.legrog.web.account; 1 package org.legrog.web.account;
2 2
3 -import org.legrog.entities.Account; 3 +import org.legrog.entities.*;
4 -import org.legrog.entities.AccountRepository;
5 4
6 import javax.ejb.Stateless; 5 import javax.ejb.Stateless;
7 import javax.inject.Inject; 6 import javax.inject.Inject;
7 +import javax.validation.constraints.NotNull;
8 +import java.util.ArrayList;
8 import java.util.List; 9 import java.util.List;
9 10
10 @Stateless 11 @Stateless
...@@ -12,6 +13,26 @@ public class AccountServiceDefault implements AccountService { ...@@ -12,6 +13,26 @@ public class AccountServiceDefault implements AccountService {
12 @Inject 13 @Inject
13 AccountRepository accountRepository; 14 AccountRepository accountRepository;
14 15
16 + @Inject
17 + AccountSearchRepository accountSearchRepository;
18 +
19 + /**
20 + * Le service s'appuie concrètement sur un ensemble de dépôts et sur le service auxiliaire SharedService.
21 + *
22 + * @param accountRepository
23 + * @param accountSearchRepository
24 + */
25 + @Inject
26 + public AccountServiceDefault(AccountRepository accountRepository,
27 + AccountSearchRepository accountSearchRepository) {
28 + this.accountRepository = accountRepository;
29 + this.accountSearchRepository = accountSearchRepository;
30 + }
31 +
32 + AccountServiceDefault() {
33 + //no args constructor to make it proxyable
34 + }
35 +
15 public void addUser(Account account) { 36 public void addUser(Account account) {
16 accountRepository.save(account); 37 accountRepository.save(account);
17 } 38 }
...@@ -27,4 +48,21 @@ public class AccountServiceDefault implements AccountService { ...@@ -27,4 +48,21 @@ public class AccountServiceDefault implements AccountService {
27 public void updateUser(Account account) { 48 public void updateUser(Account account) {
28 accountRepository.save(account); 49 accountRepository.save(account);
29 } 50 }
51 +
52 + @Override
53 + public List<Account> search(@NotNull String string) throws SearchingException {
54 + return convert(accountSearchRepository.search(string));
55 + }
56 +
57 + @Override
58 + public List<Account> convert(List<IndexedAccount> indexedAccounts) {
59 + List<Integer> integers = new ArrayList<>(indexedAccounts.size());
60 + indexedAccounts.forEach(indexedAccount -> integers.add(indexedAccount.getUserId()));
61 +
62 + if (integers.size() > 0) {
63 + return accountRepository.findByUserIdIn(integers);
64 + }
65 +
66 + return new ArrayList<>();
67 + }
30 } 68 }
...\ No newline at end of file ...\ No newline at end of file
......
...@@ -43,12 +43,8 @@ public class PublisherSearchView implements Serializable { ...@@ -43,12 +43,8 @@ public class PublisherSearchView implements Serializable {
43 /** 43 /**
44 * 44 *
45 */ 45 */
46 - public void search() { 46 + public void search() throws SearchingException {
47 - try {
48 this.publisherVersions = publisherService.search(this.searchString); 47 this.publisherVersions = publisherService.search(this.searchString);
49 - } catch (SearchingException se) {
50 - // TODO Handle Exception
51 - }
52 } 48 }
53 49
54 public String getSearchString() { 50 public String getSearchString() {
......
1 +package org.legrog.web.xyz;
2 +
3 +import org.legrog.entities.Account;
4 +import org.legrog.entities.PublisherVersion;
5 +import org.legrog.entities.SearchingException;
6 +import org.legrog.web.account.AccountService;
7 +import org.legrog.web.publisher.PublisherService;
8 +
9 +import javax.enterprise.context.RequestScoped;
10 +import javax.inject.Inject;
11 +import javax.inject.Named;
12 +import java.util.ArrayList;
13 +import java.util.List;
14 +
15 +/**
16 + * View behind search.xhtml
17 + */
18 +@Named
19 +@RequestScoped
20 +public class SearchView {
21 + List<PublisherVersion> publisherVersions = new ArrayList<>();
22 + List<Account> accounts = new ArrayList<>();
23 + String searchString = new String();
24 +
25 + transient PublisherService publisherService;
26 + transient AccountService accountService;
27 +
28 +
29 + /**
30 + * Uses PublisherService to access search repository
31 + *
32 + * @param publisherService injected PublisherService
33 + */
34 + @Inject
35 + public SearchView(PublisherService publisherService, AccountService accountService) {
36 + this.publisherService = publisherService;
37 + this.accountService = accountService;
38 + }
39 +
40 + SearchView() {
41 + //no args constructor to make it proxyable
42 + }
43 +
44 + public boolean publisherSearchEmpty() {
45 + return ((!searchString.isEmpty()) && publisherVersions.isEmpty());
46 + }
47 +
48 + public boolean accountSearchEmpty() {
49 + return ((!searchString.isEmpty()) && accounts.isEmpty());
50 + }
51 +
52 + public void search() throws SearchingException {
53 + this.publisherVersions = publisherService.search(this.searchString);
54 + this.accounts = accountService.search(this.searchString);
55 + }
56 +
57 + public String getSearchString() {
58 + return searchString;
59 + }
60 +
61 + public void setSearchString(String searchString) {
62 + this.searchString = searchString;
63 + }
64 +
65 + public List<Account> getAccounts() {
66 + return accounts;
67 + }
68 +
69 + public void setAccounts(List<Account> accounts) {
70 + this.accounts = accounts;
71 + }
72 +
73 + public List<PublisherVersion> getPublisherVersions() {
74 + return publisherVersions;
75 + }
76 +
77 + public void setPublisherVersions(List<PublisherVersion> publisherVersions) {
78 + this.publisherVersions = publisherVersions;
79 + }
80 +}
1 +<?xml version="1.0" encoding="UTF-8"?>
2 +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
3 + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
4 +<html xmlns="http://www.w3.org/1999/xhtml"
5 + xmlns:h="http://xmlns.jcp.org/jsf/html"
6 + xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
7 + xmlns:f="http://xmlns.jcp.org/jsf/core"
8 + xmlns:jsf="http://xmlns.jcp.org/jsf">
9 +<head>
10 + <link rel="stylesheet" type="text/css" href="/minimal.css"/>
11 +</head>
12 +<body>
13 +
14 +<ul>
15 + <li>
16 + <a jsf:outcome="/index">Menu principal</a>
17 + </li>
18 +<li>Éditeurs
19 +<ul>
20 + <li>
21 + <a jsf:outcome="/publisher/publisherSearch">Recherche dans les éditeurs</a>
22 + </li>
23 + <li>
24 + <a jsf:outcome="/publisher/listPublisherVersions">Liste des versions des éditeurs</a>
25 + </li>
26 + <li>
27 + <a jsf:outcome="/publisher/listPublisherActions">Liste des actions des éditeurs</a>
28 + </li>
29 + <li>
30 + <a jsf:outcome="/publisher/publisherVersion">Ajouter un éditeur</a>
31 + </li>
32 +</ul>
33 + </li>
34 + <li>Pays
35 + <ul>
36 + <li>
37 + <a jsf:outcome="listCountries">Liste des pays</a>
38 + </li>
39 + <li>
40 + <a jsf:outcome="addCountry">Ajouter un pays</a>
41 + </li>
42 + </ul>
43 + </li>
44 +</ul>
45 +
46 +<form action="" jsf:id="search">
47 + <h:panelGrid columns="2">
48 + <label for="searchString">Élément recherché dans l'ensemble de l'encyclopédie</label>
49 + <input type="text" id="searchString" jsf:value='#{searchView.searchString}'/>
50 +
51 + <button jsf:action="#{searchView.search}">Rechercher</button>
52 + </h:panelGrid>
53 +</form>
54 +
55 +<p jsf:rendered="#{searchView.publisherSearchEmpty()}">Aucun éditeur ne correspond à votre recherche : ${searchView.searchString}.</p>
56 +
57 +<h:dataTable rendered="#{!searchView.publisherVersions.isEmpty()}" value="#{searchView.publisherVersions}" var="version">
58 + <h:column>
59 + <f:facet name="header">Name</f:facet>
60 + ${version.publisherName}
61 + </h:column>
62 + <h:column>
63 + <f:facet name="header">Address</f:facet>
64 + ${version.publisherPostOfficeBoxNumber}<br />
65 + ${version.publisherStreetAddress}<br />
66 + ${version.publisherPostalCode} ${version.publisherAddressLocality}<br />
67 + ${version.publisherAddressRegion}<br />
68 + ${version.publisherAddressCountry.countryName}
69 + </h:column>
70 + <h:column>
71 + <f:facet name="header">Telephone</f:facet>
72 + ${version.publisherTelephone}
73 + </h:column>
74 + <h:column>
75 + <f:facet name="header">Email</f:facet>
76 + ${version.publisherEmail}
77 + </h:column>
78 + <h:column>
79 + <f:facet name="header">URL</f:facet>
80 + ${version.publisherURL}
81 + </h:column>
82 + <h:column>
83 + <f:facet name="header">History</f:facet>
84 + <h:outputText escape="false" value="#{version.publisherHistory}"/>
85 + </h:column>
86 +</h:dataTable>
87 +
88 +<p jsf:rendered="#{searchView.accountSearchEmpty()}">Aucun compte ne correspond à votre recherche : ${searchView.searchString}.</p>
89 +
90 +<h:dataTable rendered="#{!searchView.accounts.isEmpty()}" value="#{searchView.accounts}" var="account">
91 + <h:column>
92 + <f:facet name="header">Name</f:facet>
93 + ${account.getDisplayName()}
94 + </h:column>
95 + <h:column>
96 + <f:facet name="header">Presentation</f:facet>
97 + <h:outputText escape="false" value="#{account.presentation}"/>
98 + </h:column>
99 +</h:dataTable>
100 +</body>
101 +</html>
1 +package org.legrog.web.account;
2 +
3 +import org.junit.jupiter.api.BeforeEach;
4 +import org.junit.jupiter.api.DisplayName;
5 +import org.junit.jupiter.api.Nested;
6 +import org.junit.jupiter.api.Test;
7 +import org.legrog.entities.*;
8 +import org.mockito.Mock;
9 +import org.mockito.Mockito;
10 +
11 +import java.util.ArrayList;
12 +import java.util.List;
13 +
14 +import static org.assertj.core.api.Assertions.assertThat;
15 +import static org.mockito.Mockito.times;
16 +import static org.mockito.Mockito.verify;
17 +import static org.mockito.Mockito.when;
18 +
19 +/**
20 + * Classe testing AccountServiceDefault.
21 + */
22 +public class AccountServiceDefaultTest {
23 + AccountServiceDefault accountServiceDefault;
24 +
25 + @BeforeEach
26 + public void setUp(@Mock AccountRepository accountRepository,
27 + @Mock AccountSearchRepository accountSearchRepository) throws Exception {
28 + accountServiceDefault = Mockito.spy(new AccountServiceDefault(accountRepository, accountSearchRepository));
29 + }
30 +
31 + @Nested
32 + @DisplayName("search method")
33 + class SearchTests {
34 +
35 + @DisplayName("When called, should delegate search to AccountSearchRepository")
36 + @Test
37 + public void testDelegateSearchToAccountSearchRepository(@Mock AccountSearchRepository accountSearchRepository) throws SearchingException {
38 + accountServiceDefault.search("3");
39 + Mockito.verify(accountSearchRepository).search("3");
40 +
41 + }
42 +
43 + @DisplayName("When getting IndexedAccounts from search, should convert them")
44 + @Test
45 + public void testConvertReturnedIndexedAccounts(@Mock AccountSearchRepository accountSearchRepository) throws SearchingException {
46 + List<IndexedAccount> indexedAccounts = new ArrayList<>();
47 +
48 + when(accountSearchRepository.search("4")).thenReturn(indexedAccounts);
49 + accountServiceDefault.search("4");
50 + verify(accountServiceDefault, times(1)).convert(indexedAccounts);
51 + }
52 +
53 + @DisplayName("When called, should return the Accounts it gets from convert")
54 + @Test
55 + public void testReturnFromConvert(@Mock AccountSearchRepository accountSearchRepository) throws SearchingException {
56 + List<Account> accounts = new ArrayList<>();
57 + List<IndexedAccount> indexedAccounts = new ArrayList<>();
58 + Mockito.doReturn(accounts).when(accountServiceDefault).convert(indexedAccounts);
59 + when(accountSearchRepository.search("5")).thenReturn(indexedAccounts);
60 + assertThat(accountServiceDefault.search("5")).isEqualTo(accounts);
61 + }
62 +
63 + }
64 +
65 + @Nested
66 + @DisplayName("convert method")
67 + class ConvertTests {
68 + @DisplayName("When called, should return the Accounts it gets from findByUserIdIn")
69 + @Test
70 + public void testReturnFromFind(@Mock AccountRepository accountRepository) {
71 + List<Integer> integers = new ArrayList<>();
72 + List<Account> accounts = new ArrayList<>();
73 + List<IndexedAccount> indexedAccounts = new ArrayList<>();
74 + when(accountRepository.findByUserIdIn(Mockito.any())).thenReturn(accounts);
75 + assertThat(accountServiceDefault.convert(indexedAccounts)).isEqualTo(accounts);
76 + }
77 +
78 + }
79 +}
1 +package org.legrog.web.xyz;
2 +
3 +import org.junit.jupiter.api.BeforeEach;
4 +import org.junit.jupiter.api.DisplayName;
5 +import org.junit.jupiter.api.Nested;
6 +import org.junit.jupiter.api.Test;
7 +import org.junit.jupiter.api.extension.ExtendWith;
8 +import org.junit.platform.runner.JUnitPlatform;
9 +import org.junit.runner.RunWith;
10 +import org.legrog.entities.Account;
11 +import org.legrog.entities.PublisherVersion;
12 +import org.legrog.entities.SearchingException;
13 +import org.legrog.test.MockitoExtension;
14 +import org.legrog.web.account.AccountService;
15 +import org.legrog.web.publisher.PublisherSearchView;
16 +import org.legrog.web.publisher.PublisherService;
17 +import org.mockito.Mock;
18 +import org.mockito.Mockito;
19 +
20 +import java.util.ArrayList;
21 +import java.util.List;
22 +
23 +import static org.assertj.core.api.Assertions.assertThat;
24 +import static org.mockito.Mockito.when;
25 +
26 +/**
27 + * Classe testant SearchView
28 + */
29 +@RunWith(JUnitPlatform.class)
30 +@ExtendWith(MockitoExtension.class)
31 +@DisplayName("Searches for an indexed publisher")
32 +public class SearchViewTest {
33 +
34 + private SearchView searchView;
35 + private PublisherService publisherService;
36 + private AccountService accountService;
37 +
38 + @BeforeEach
39 + public void setUp(@Mock PublisherService publisherService, @Mock AccountService accountService) {
40 + this.publisherService = publisherService;
41 + this.accountService = accountService;
42 + this.searchView = new SearchView(publisherService, accountService);
43 + }
44 +
45 + @Nested
46 + @DisplayName("search method")
47 + class SearchTests {
48 +
49 + @Test
50 + @DisplayName("when called, should delegate search to PublisherService and AccountService with same string")
51 + public void searchUsesPublisherService(@Mock PublisherService publisherService, @Mock AccountService accountService) throws SearchingException{
52 + searchView.setSearchString("1");
53 + searchView.search();
54 + Mockito.verify(publisherService).search("1");
55 + Mockito.verify(accountService).search("1");
56 + }
57 +
58 + @Test
59 + @DisplayName("when called, should return the answer it gets from PublisherService")
60 + public void searchReturnsDataFromPublisherService(@Mock PublisherService publisherService) throws SearchingException {
61 + List<PublisherVersion> publisherVersionList = new ArrayList<>();
62 + when(publisherService.search("2")).thenReturn(publisherVersionList);
63 + searchView.setSearchString("2");
64 + searchView.search();
65 + assertThat(searchView.getPublisherVersions()).isEqualTo(publisherVersionList);
66 + }
67 +
68 + @Test
69 + @DisplayName("when called, should return the answer it gets from AccountService")
70 + public void searchReturnsDataFromAccountService(@Mock AccountService accountService) throws SearchingException {
71 + List<Account> publisherVersionList = new ArrayList<>();
72 + when(accountService.search("3")).thenReturn(publisherVersionList);
73 + searchView.setSearchString("3");
74 + searchView.search();
75 + assertThat(searchView.getPublisherVersions()).isEqualTo(publisherVersionList);
76 + }
77 +
78 + }
79 +}