diff --git a/services/pom.xml b/services/pom.xml index 6bb25f18f6..e81c2aab66 100644 --- a/services/pom.xml +++ b/services/pom.xml @@ -36,7 +36,7 @@ 1.0 News addon used Rest endpoints - 0.41 + 0.43 diff --git a/services/src/main/java/org/exoplatform/news/queryBuilder/NewsQueryBuilder.java b/services/src/main/java/org/exoplatform/news/queryBuilder/NewsQueryBuilder.java index 7939aa8d7e..f6cd8f82e7 100644 --- a/services/src/main/java/org/exoplatform/news/queryBuilder/NewsQueryBuilder.java +++ b/services/src/main/java/org/exoplatform/news/queryBuilder/NewsQueryBuilder.java @@ -60,16 +60,17 @@ public StringBuilder buildQuery(NewsFilter filter) throws Exception { String escapedQuoteFuzzyText = fuzzyText.replace("'", "''").replace("\"", "\"\""); String escapedQuoteSearchText = filter.getSearchText().replace("'", "''").replace("\"", "\"\""); sqlQuery.append("(CONTAINS(.,'").append(escapedQuoteFuzzyText).append("') OR (exo:body LIKE '%").append(escapedQuoteSearchText).append("%'))"); - if (filter.getTagNames() != null && !filter.getTagNames().isEmpty()){ - sqlQuery.append(" OR ("); + sqlQuery.append("AND "); + } else { + if (filter.getTagNames() != null && !filter.getTagNames().isEmpty()) { for (String tagName : filter.getTagNames()) { sqlQuery.append(" exo:body LIKE '%#").append(tagName).append("%'"); - if (filter.getTagNames().indexOf(tagName) != filter.getTagNames().size() -1) { + if (filter.getTagNames().indexOf(tagName) != filter.getTagNames().size() - 1) { sqlQuery.append(" OR"); } } - sqlQuery.append(" ) AND "); - } else sqlQuery.append("AND "); + sqlQuery.append(" AND "); + } } if (filter.isPublishedNews()) { sqlQuery.append("exo:pinned = 'true' AND "); diff --git a/services/src/main/java/org/exoplatform/news/rest/NewsRestResourcesV1.java b/services/src/main/java/org/exoplatform/news/rest/NewsRestResourcesV1.java index 8a5d5ba6ed..71a4bd421b 100644 --- a/services/src/main/java/org/exoplatform/news/rest/NewsRestResourcesV1.java +++ b/services/src/main/java/org/exoplatform/news/rest/NewsRestResourcesV1.java @@ -483,8 +483,12 @@ public Response getNews(@Context HttpServletRequest request, String lang = request.getLocale().getLanguage(); TagService tagService = CommonsUtils.getService(TagService.class); long userIdentityId = RestUtils.getCurrentUserIdentityId(); - List tagNames = tagService.findTags(new TagFilter(text, 0), userIdentityId); - if (tagNames != null && !tagNames.isEmpty()) newsFilter.setTagNames(tagNames.stream().map(e -> e.getName()).toList()); + if (text.indexOf("#") == 0) { + String tagName = text.replace("#",""); + List tagNames = tagService.findTags(new TagFilter(tagName, 0), userIdentityId); + if (tagNames != null && !tagNames.isEmpty()) newsFilter.setTagNames(tagNames.stream().map(e -> e.getName()).toList()); + } + news = newsService.searchNews(newsFilter, lang); } else { org.exoplatform.services.security.Identity currentIdentity = ConversationState.getCurrent().getIdentity(); @@ -1008,7 +1012,7 @@ private NewsFilter buildFilter(List spaces, String filter, String text, } } // Set text to search news with - if (StringUtils.isNotEmpty(text)) { + if (StringUtils.isNotEmpty(text) && text.indexOf("#") != 0) { newsFilter.setSearchText(text); } diff --git a/services/src/test/java/org/exoplatform/news/queryBuilder/NewsQueryBuilderTest.java b/services/src/test/java/org/exoplatform/news/queryBuilder/NewsQueryBuilderTest.java index 09e137b2b7..ce5bcfac54 100644 --- a/services/src/test/java/org/exoplatform/news/queryBuilder/NewsQueryBuilderTest.java +++ b/services/src/test/java/org/exoplatform/news/queryBuilder/NewsQueryBuilderTest.java @@ -2,16 +2,19 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; -import static org.mockito.Mockito.mockStatic; +import static org.mockito.Mockito.*; import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import org.exoplatform.commons.utils.CommonsUtils; +import org.exoplatform.social.core.identity.model.Identity; +import org.exoplatform.social.core.identity.provider.OrganizationIdentityProvider; +import org.exoplatform.social.core.manager.IdentityManager; import org.junit.AfterClass; import org.junit.Test; import org.junit.runner.RunWith; -import org.mockito.Mock; import org.mockito.MockedStatic; import org.mockito.junit.MockitoJUnitRunner; @@ -20,19 +23,17 @@ import org.exoplatform.services.security.ConversationState; import org.exoplatform.services.security.MembershipEntry; import org.exoplatform.social.core.space.model.Space; -import org.exoplatform.social.core.space.spi.SpaceService; @RunWith(MockitoJUnitRunner.class) public class NewsQueryBuilderTest { private static final MockedStatic NEWS_UTILS = mockStatic(NewsUtils.class); - - @Mock - SpaceService spaceService; + private static final MockedStatic COMMONS_UTILS = mockStatic(CommonsUtils.class); @AfterClass public static void afterRunBare() throws Exception { // NOSONAR NEWS_UTILS.close(); + COMMONS_UTILS.close(); } @Test @@ -48,6 +49,14 @@ public void shouldCreateQueryWithPinnedStateAndSearchTextAndAuthorAndOneSpaceAnd List spaces = new ArrayList<>(); spaces.add("1"); filter.setSpaces(spaces); + filter.setDraftNews(true); + Space space1 = new Space(); + space1.setId("1"); + List allowedDraftNewsSpaces = new ArrayList<>(); + allowedDraftNewsSpaces.add(space1); + NEWS_UTILS.when(() -> NewsUtils.getAllowedDraftNewsSpaces(any())).thenReturn(allowedDraftNewsSpaces); + Identity identity = new Identity(OrganizationIdentityProvider.NAME, "jean"); + identity.setRemoteId("jean"); org.exoplatform.services.security.Identity currentIdentity = new org.exoplatform.services.security.Identity("john"); MembershipEntry membershipentry = new MembershipEntry("/platform/web-contributors", "redactor"); List memberships = new ArrayList(); @@ -55,13 +64,16 @@ public void shouldCreateQueryWithPinnedStateAndSearchTextAndAuthorAndOneSpaceAnd currentIdentity.setMemberships(memberships); ConversationState state = new ConversationState(currentIdentity); ConversationState.setCurrent(state); + IdentityManager identityMock = mock(IdentityManager.class); + COMMONS_UTILS.when(() -> CommonsUtils.getService(IdentityManager.class)).thenReturn(identityMock); + when(identityMock.getOrCreateIdentity(anyString(), anyString())).thenReturn(identity); // when StringBuilder query = queryBuilder.buildQuery(filter); // then assertNotNull(query); - assertEquals("SELECT * FROM exo:news WHERE ( exo:archived IS NULL OR exo:archived = 'false' OR ( exo:archived = 'true' AND exo:author = 'john')) AND (CONTAINS(.,'text~0.6') OR (exo:body LIKE '%text%')) OR ( exo:body LIKE '%#text%' OR exo:body LIKE '%#tex%' ) AND exo:pinned = 'true' AND ( exo:spaceId = '1') AND exo:author = 'john' AND (publication:currentState = 'published' OR (publication:currentState = 'draft' AND exo:activities <> '' )) AND jcr:path LIKE '/Groups/spaces/%' ORDER BY jcr:score DESC", + assertEquals("SELECT * FROM exo:news WHERE ( exo:archived IS NULL OR exo:archived = 'false' OR ( exo:archived = 'true' AND exo:author = 'john')) AND (CONTAINS(.,'text~0.6') OR (exo:body LIKE '%text%'))AND exo:pinned = 'true' AND ( exo:spaceId = '1') AND publication:currentState = 'draft' AND (('null' IN exo:newsModifiersIds AND exo:activities <> '') OR ( exo:author = 'john' AND exo:activities = '')OR (exo:spaceId = '1') AND exo:draftVisible = 'true') AND jcr:path LIKE '/Groups/spaces/%' ORDER BY jcr:score DESC", query.toString()); } @@ -221,6 +233,27 @@ public void shouldCreateQueryWithNoFilter() throws Exception { assertNotNull(query); assertEquals("SELECT * FROM exo:news WHERE ", query.toString()); } + + @Test + public void testBuildQueryWithTagNames() throws Exception { + // Given + NewsQueryBuilder queryBuilder = new NewsQueryBuilder(); + org.exoplatform.services.security.Identity currentIdentity = new org.exoplatform.services.security.Identity("john"); + ConversationState state = new ConversationState(currentIdentity); + ConversationState.setCurrent(state); + NewsFilter filter = new NewsFilter(); + filter.setAuthor("john"); + filter.setTagNames(Arrays.asList(new String[]{"text"})); + + // when + StringBuilder query = queryBuilder.buildQuery(filter); + + // then + assertNotNull(query); + assertEquals("SELECT * FROM exo:news WHERE ( exo:archived IS NULL OR exo:archived = 'false' OR ( exo:archived = 'true' AND exo:author = 'john')) AND exo:body LIKE '%#text%' AND exo:author = 'john' AND (publication:currentState = 'published' OR (publication:currentState = 'draft' AND exo:activities <> '' )) AND jcr:path LIKE '/Groups/spaces/%' ORDER BY null DESC", + query.toString()); + } + @Test public void shouldAddFuzzySyntaxWhitQuotedWord() throws Exception { // Given @@ -232,7 +265,7 @@ public void shouldAddFuzzySyntaxWhitQuotedWord() throws Exception { // Then assertNotNull(result); - assertEquals(result, "\"quoted\"~0.6"); + assertEquals("\"quoted\"~0.6", result); } @Test @@ -246,7 +279,7 @@ public void shouldAddFuzzySyntaxWhenTextContainsMultiWords() throws Exception { // Then assertNotNull(result); - assertEquals(result, "search~0.6 OR text~0.6"); + assertEquals("search~0.6 OR text~0.6", result); } @Test @@ -260,7 +293,7 @@ public void shouldAddFuzzySyntaxWhenTextContainsQuotedMultiWords() throws Except // Then assertNotNull(result); - assertEquals(result, "\"search text\"~0.6"); + assertEquals("\"search text\"~0.6", result); } @Test public void shouldAddFuzzySyntaxWhenTextContainsMultiWordsAndQuotedOne() throws Exception { @@ -273,6 +306,19 @@ public void shouldAddFuzzySyntaxWhenTextContainsMultiWordsAndQuotedOne() throws // Then assertNotNull(result); - assertEquals(result, "this~0.6 OR is~0.6 OR a~0.6 OR \"search text\"~0.6"); + assertEquals("this~0.6 OR is~0.6 OR a~0.6 OR \"search text\"~0.6", result); + } + + @Test + public void testAddFuzzySyntaxAndORQuoteInsideQuote() { + // Given + NewsQueryBuilder queryBuilder = new NewsQueryBuilder(); + String text = "This \"quoted\" text."; + + // when + String result = queryBuilder.addFuzzySyntaxAndOR(text); + + // then + assertEquals("This~0.6 OR \"quoted\"~0.6 OR text.~0.6", result); } } diff --git a/services/src/test/java/org/exoplatform/news/rest/NewsRestResourcesV1Test.java b/services/src/test/java/org/exoplatform/news/rest/NewsRestResourcesV1Test.java index cc7b4d5c3f..a0ef7611cb 100644 --- a/services/src/test/java/org/exoplatform/news/rest/NewsRestResourcesV1Test.java +++ b/services/src/test/java/org/exoplatform/news/rest/NewsRestResourcesV1Test.java @@ -1576,7 +1576,6 @@ public void shouldGetAllNewsWhenSearchingWithTextInTheGivenSpaces() throws Excep allNews.add(news3); COMMONS_UTILS.when(()-> CommonsUtils.getService(TagService.class)).thenReturn(tagService); REST_UTILS.when(()-> RestUtils.getCurrentUserIdentityId()).thenReturn(1L); - when(tagService.findTags(new TagFilter(text, 0), 1L)).thenReturn(new ArrayList<>()); lenient().when(newsService.searchNews(any(), any())).thenReturn(allNews); lenient().when(spaceService.isMember(any(Space.class), any())).thenReturn(true); lenient().when(spaceService.getSpaceById(anyString())).thenReturn(new Space()); @@ -1632,7 +1631,6 @@ public void shouldGetAllNewsWhenSearchingWithTextInTheGivenSpace() throws Except allNews.add(news3); COMMONS_UTILS.when(()-> CommonsUtils.getService(TagService.class)).thenReturn(tagService); REST_UTILS.when(()-> RestUtils.getCurrentUserIdentityId()).thenReturn(1L); - when(tagService.findTags(new TagFilter(text, 0), 1L)).thenReturn(new ArrayList<>()); lenient().when(newsService.searchNews(any(), any())).thenReturn(allNews); lenient().when(spaceService.isMember(any(Space.class), any())).thenReturn(true); lenient().when(spaceService.getSpaceById(anyString())).thenReturn(new Space()); @@ -1681,7 +1679,6 @@ public void shouldGetAllNewsWhenSearchingWithTagTextInTheGivenSpace() throws Exc allNews.add(news2); COMMONS_UTILS.when(()-> CommonsUtils.getService(TagService.class)).thenReturn(tagService); REST_UTILS.when(()-> RestUtils.getCurrentUserIdentityId()).thenReturn(1L); - when(tagService.findTags(new TagFilter(tagText, 0), 1L)).thenReturn(Arrays.asList(new TagName("tagText"))); lenient().when(newsService.searchNews(any(), any())).thenReturn(allNews); lenient().when(spaceService.isMember(any(Space.class), any())).thenReturn(true); lenient().when(spaceService.getSpaceById(anyString())).thenReturn(new Space()); @@ -1733,7 +1730,6 @@ public void shouldGetPinnedNewsWhenSearchingWithTextInTheGivenSpaces() throws Ex allNews.add(news3); COMMONS_UTILS.when(()-> CommonsUtils.getService(TagService.class)).thenReturn(tagService); REST_UTILS.when(()-> RestUtils.getCurrentUserIdentityId()).thenReturn(1L); - when(tagService.findTags(new TagFilter(text, 0), 1L)).thenReturn(new ArrayList<>()); lenient().when(newsService.searchNews(any(), any())).thenReturn(allNews); lenient().when(spaceService.isMember(any(Space.class), any())).thenReturn(true); lenient().when(spaceService.getSpaceById(anyString())).thenReturn(new Space()); @@ -1918,7 +1914,6 @@ public void shouldGetMyPostedNewsWhenSearchingWithTheGivenSpaces() throws Except allNews.add(news3); COMMONS_UTILS.when(()-> CommonsUtils.getService(TagService.class)).thenReturn(tagService); REST_UTILS.when(()-> RestUtils.getCurrentUserIdentityId()).thenReturn(1L); - when(tagService.findTags(new TagFilter(text, 0), 1L)).thenReturn(new ArrayList<>()); lenient().when(newsService.searchNews(any(), any())).thenReturn(allNews); lenient().when(spaceService.isMember(any(Space.class), any())).thenReturn(true); lenient().when(spaceService.getSpaceById(anyString())).thenReturn(new Space()); diff --git a/webapp/src/main/webapp/services/newsServices.js b/webapp/src/main/webapp/services/newsServices.js index e897991cce..72e84e1c83 100644 --- a/webapp/src/main/webapp/services/newsServices.js +++ b/webapp/src/main/webapp/services/newsServices.js @@ -55,6 +55,9 @@ export function markNewsAsRead(newsId){ export function getNews(filter, spaces, searchText, offset, limit, returnSize) { let url = `${newsConstants.NEWS_API}?author=${newsConstants.userName}&publicationState=published&filter=${filter}`; if (searchText) { + if (searchText.indexOf('#') === 0) { + searchText = searchText.replace('#', '%23'); + } url += `&text=${searchText}`; } if (spaces) {