mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-07-24 17:32:48 +00:00
ACS-4025: Support include count and orderBy count for GET /tags (#1806)
* ACS-4025: Support include count and orderBy count for GET /tags * ACS-4025: add E2Es
This commit is contained in:
committed by
GitHub
parent
05df8a7582
commit
d39401a7ec
@@ -16,6 +16,11 @@ import org.alfresco.utility.testrail.annotation.TestRail;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
import static org.alfresco.utility.report.log.Step.STEP;
|
||||
|
||||
@Test(groups = {TestGroup.REQUIRE_SOLR})
|
||||
public class GetTagsTests extends TagsDataPrep
|
||||
{
|
||||
@@ -72,6 +77,130 @@ public class GetTagsTests extends TagsDataPrep
|
||||
.and().entriesListContains("tag", documentTagValue2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Include count in the query parameters and ensure count is as expected for returned tags.
|
||||
*/
|
||||
@Test(groups = { TestGroup.REST_API, TestGroup.TAGS, TestGroup.REGRESSION })
|
||||
public void testGetTags_withIncludeCount()
|
||||
{
|
||||
STEP("Get tags including count filter and ensure count is as expected for returned tags");
|
||||
returnedCollection = restClient.authenticateUser(adminUserModel)
|
||||
.withParams("include=count")
|
||||
.withCoreAPI()
|
||||
.getTags();
|
||||
restClient.assertStatusCodeIs(HttpStatus.OK);
|
||||
|
||||
returnedCollection.getEntries().stream()
|
||||
.filter(e -> e.onModel().getTag().equals(folderTagValue) || e.onModel().getTag().equals(documentTagValue))
|
||||
.forEach(e -> e.onModel().assertThat().field("count").is(2));
|
||||
|
||||
returnedCollection.getEntries().stream()
|
||||
.filter(e -> e.onModel().getTag().equals(documentTagValue2))
|
||||
.forEach(e -> e.onModel().assertThat().field("count").is(1));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get tags and order results by count. Default sort order should be ascending
|
||||
*/
|
||||
@Test(groups = { TestGroup.REST_API, TestGroup.TAGS, TestGroup.REGRESSION })
|
||||
public void testGetTags_withOrderByCountDefaultOrderShouldBeAsc()
|
||||
{
|
||||
|
||||
STEP("Get tags and order results by count. Default sort order should be ascending");
|
||||
returnedCollection = restClient.authenticateUser(adminUserModel)
|
||||
.withParams("include=count&orderBy=count")
|
||||
.withCoreAPI()
|
||||
.getTags();
|
||||
|
||||
restClient.assertStatusCodeIs(HttpStatus.OK);
|
||||
returnedCollection.assertThat().entriesListIsSortedAscBy("count");
|
||||
}
|
||||
|
||||
/**
|
||||
* Get tags and order results by count in ascending order
|
||||
*/
|
||||
@Test(groups = { TestGroup.REST_API, TestGroup.TAGS, TestGroup.REGRESSION })
|
||||
public void testGetTags_withOrderByCountAsc()
|
||||
{
|
||||
|
||||
STEP("Get tags and order results by count in ascending order");
|
||||
returnedCollection = restClient.authenticateUser(adminUserModel)
|
||||
.withParams("include=count&orderBy=count ASC")
|
||||
.withCoreAPI()
|
||||
.getTags();
|
||||
|
||||
restClient.assertStatusCodeIs(HttpStatus.OK);
|
||||
returnedCollection.assertThat().entriesListIsSortedAscBy("count");
|
||||
}
|
||||
|
||||
/**
|
||||
* Get tags and order results by count in descending order
|
||||
*/
|
||||
@Test(groups = { TestGroup.REST_API, TestGroup.TAGS, TestGroup.REGRESSION })
|
||||
public void testGetTags_withOrderByCountDesc()
|
||||
{
|
||||
|
||||
STEP("Get tags and order results by count in descending order");
|
||||
returnedCollection = restClient.authenticateUser(adminUserModel)
|
||||
.withParams("include=count&orderBy=count DESC")
|
||||
.withCoreAPI()
|
||||
.getTags();
|
||||
|
||||
restClient.assertStatusCodeIs(HttpStatus.OK);
|
||||
returnedCollection.assertThat().entriesListIsSortedDescBy("count");
|
||||
}
|
||||
|
||||
/**
|
||||
* Get tags and order results by tag name. Default sort order should be ascending
|
||||
*/
|
||||
@Test(groups = { TestGroup.REST_API, TestGroup.TAGS, TestGroup.REGRESSION })
|
||||
public void testGetTags_withOrderByTagDefaultOrderShouldBeAsc()
|
||||
{
|
||||
|
||||
STEP("Get tags and order results by tag name. Default sort order should be ascending");
|
||||
returnedCollection = restClient.authenticateUser(adminUserModel)
|
||||
.withParams("orderBy=tag")
|
||||
.withCoreAPI()
|
||||
.getTags();
|
||||
|
||||
restClient.assertStatusCodeIs(HttpStatus.OK);
|
||||
returnedCollection.assertThat().entriesListIsSortedAscBy("tag");
|
||||
}
|
||||
|
||||
/**
|
||||
* Get tags and order results by tag name in ascending order
|
||||
*/
|
||||
@Test(groups = { TestGroup.REST_API, TestGroup.TAGS, TestGroup.REGRESSION })
|
||||
public void testGetTags_withOrderByTagAsc()
|
||||
{
|
||||
|
||||
STEP("Get tags and order results by tag name in ascending order");
|
||||
returnedCollection = restClient.authenticateUser(adminUserModel)
|
||||
.withParams("orderBy=tag ASC")
|
||||
.withCoreAPI()
|
||||
.getTags();
|
||||
|
||||
restClient.assertStatusCodeIs(HttpStatus.OK);
|
||||
returnedCollection.assertThat().entriesListIsSortedAscBy("tag");
|
||||
}
|
||||
|
||||
/**
|
||||
* Get tags and order results by tag name in descending order
|
||||
*/
|
||||
@Test(groups = { TestGroup.REST_API, TestGroup.TAGS, TestGroup.REGRESSION })
|
||||
public void testGetTags_withOrderByTagDesc()
|
||||
{
|
||||
|
||||
STEP("Get tags and order results by tag name in descending order");
|
||||
returnedCollection = restClient.authenticateUser(adminUserModel)
|
||||
.withParams("orderBy=tag DESC")
|
||||
.withCoreAPI()
|
||||
.getTags();
|
||||
|
||||
restClient.assertStatusCodeIs(HttpStatus.OK);
|
||||
returnedCollection.assertThat().entriesListIsSortedDescBy("tag");
|
||||
}
|
||||
|
||||
@TestRail(section = { TestGroup.REST_API, TestGroup.TAGS }, executionType = ExecutionType.SANITY, description = "Failed authentication get tags call returns status code 401 with Manager role")
|
||||
@Test(groups = { TestGroup.REST_API, TestGroup.TAGS, TestGroup.SANITY })
|
||||
// @Bug(id="MNT-16904", description = "It fails only on environment with tenants")
|
||||
@@ -193,8 +322,7 @@ public class GetTagsTests extends TagsDataPrep
|
||||
.getPagination().assertThat().field("maxItems").is(100)
|
||||
.and().field("hasMoreItems").is("false")
|
||||
.and().field("count").is("0")
|
||||
.and().field("skipCount").is(20000)
|
||||
.and().field("totalItems").is(0);
|
||||
.and().field("skipCount").is(20000);
|
||||
}
|
||||
|
||||
@TestRail(section = { TestGroup.REST_API, TestGroup.TAGS }, executionType = ExecutionType.REGRESSION,
|
||||
|
@@ -22,7 +22,8 @@ public class TagsDataPrep extends RestTest
|
||||
protected static ListUserWithRoles usersWithRoles;
|
||||
protected static SiteModel siteModel;
|
||||
protected static FileModel document;
|
||||
protected static FolderModel folder;
|
||||
protected static FolderModel folder, folder2;
|
||||
protected static RestTagModelsCollection folder2tags;
|
||||
protected static String documentTagValue, documentTagValue2, folderTagValue;
|
||||
protected static RestTagModel documentTag, documentTag2, folderTag, orphanTag, returnedModel;
|
||||
protected static RestTagModelsCollection returnedCollection;
|
||||
@@ -38,6 +39,7 @@ public class TagsDataPrep extends RestTest
|
||||
usersWithRoles = dataUser.usingAdmin().addUsersWithRolesToSite(siteModel, UserRole.SiteManager, UserRole.SiteCollaborator, UserRole.SiteConsumer, UserRole.SiteContributor);
|
||||
document = dataContent.usingUser(adminUserModel).usingSite(siteModel).createContent(CMISUtil.DocumentType.TEXT_PLAIN);
|
||||
folder = dataContent.usingUser(adminUserModel).usingSite(siteModel).createFolder();
|
||||
folder2 = dataContent.usingUser(adminUserModel).usingSite(siteModel).createFolder();
|
||||
|
||||
documentTagValue = RandomData.getRandomName("tag").toLowerCase();
|
||||
documentTagValue2 = RandomData.getRandomName("tag").toLowerCase();
|
||||
@@ -48,6 +50,7 @@ public class TagsDataPrep extends RestTest
|
||||
documentTag2 = restClient.withCoreAPI().usingResource(document).addTag(documentTagValue2);
|
||||
folderTag = restClient.withCoreAPI().usingResource(folder).addTag(folderTagValue);
|
||||
orphanTag = restClient.withCoreAPI().createSingleTag(RestTagModel.builder().tag(RandomData.getRandomName("orphan-tag").toLowerCase()).create());
|
||||
folder2tags = restClient.withCoreAPI().usingResource(folder2).addTags(folderTagValue, documentTagValue);
|
||||
|
||||
// Allow indexing to complete.
|
||||
Utility.sleep(500, 60000, () ->
|
||||
|
@@ -36,6 +36,7 @@ import static org.alfresco.service.cmr.tagging.TaggingService.TAG_ROOT_NODE_REF;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
@@ -44,6 +45,8 @@ import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.alfresco.model.ContentModel;
|
||||
import org.alfresco.query.ListBackedPagingResults;
|
||||
import org.alfresco.query.PagingResults;
|
||||
import org.alfresco.repo.tagging.NonExistentTagException;
|
||||
import org.alfresco.repo.tagging.TagExistsException;
|
||||
@@ -60,10 +63,12 @@ import org.alfresco.rest.framework.core.exceptions.UnsupportedResourceOperationE
|
||||
import org.alfresco.rest.framework.resource.parameters.CollectionWithPagingInfo;
|
||||
import org.alfresco.rest.framework.resource.parameters.Paging;
|
||||
import org.alfresco.rest.framework.resource.parameters.Parameters;
|
||||
import org.alfresco.rest.framework.resource.parameters.SortColumn;
|
||||
import org.alfresco.rest.framework.resource.parameters.where.Query;
|
||||
import org.alfresco.rest.framework.resource.parameters.where.QueryHelper;
|
||||
import org.alfresco.rest.framework.resource.parameters.where.QueryImpl;
|
||||
import org.alfresco.service.Experimental;
|
||||
import org.alfresco.service.cmr.repository.ChildAssociationRef;
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
import org.alfresco.service.cmr.repository.NodeService;
|
||||
import org.alfresco.service.cmr.repository.StoreRef;
|
||||
@@ -166,36 +171,25 @@ public class TagsImpl implements Tags
|
||||
taggingService.deleteTag(storeRef, tagValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Override
|
||||
public CollectionWithPagingInfo<Tag> getTags(StoreRef storeRef, Parameters params)
|
||||
{
|
||||
Paging paging = params.getPaging();
|
||||
Map<Integer, Collection<String>> namesFilters = resolveTagNamesQuery(params.getQuery());
|
||||
PagingResults<Pair<NodeRef, String>> results = taggingService.getTags(storeRef, Util.getPagingRequest(paging), namesFilters.get(EQUALS), namesFilters.get(MATCHES));
|
||||
Paging paging = params.getPaging();
|
||||
Pair<String, Boolean> sorting = !params.getSorting().isEmpty() ? new Pair<>(params.getSorting().get(0).column, params.getSorting().get(0).asc) : null;
|
||||
Map<Integer, Collection<String>> namesFilters = resolveTagNamesQuery(params.getQuery());
|
||||
|
||||
Integer totalItems = results.getTotalResultCount().getFirst();
|
||||
List<Pair<NodeRef, String>> page = results.getPage();
|
||||
List<Tag> tags = new ArrayList<>(page.size());
|
||||
for (Pair<NodeRef, String> pair : page)
|
||||
{
|
||||
Tag selectedTag = new Tag(pair.getFirst(), pair.getSecond());
|
||||
tags.add(selectedTag);
|
||||
}
|
||||
if (params.getInclude().contains(PARAM_INCLUDE_COUNT))
|
||||
{
|
||||
List<Pair<String, Integer>> tagsByCount = taggingService.findTaggedNodesAndCountByTagName(storeRef);
|
||||
Map<String, Long> tagsByCountMap = new HashMap<>();
|
||||
if (tagsByCount != null)
|
||||
{
|
||||
for (Pair<String, Integer> tagByCountElem : tagsByCount)
|
||||
{
|
||||
tagsByCountMap.put(tagByCountElem.getFirst(), Long.valueOf(tagByCountElem.getSecond()));
|
||||
}
|
||||
}
|
||||
tags.forEach(tag -> tag.setCount(Optional.ofNullable(tagsByCountMap.get(tag.getTag())).orElse(0L)));
|
||||
}
|
||||
Map<NodeRef, Long> results = taggingService.getTags(storeRef, params.getInclude(), sorting, namesFilters.get(EQUALS), namesFilters.get(MATCHES));
|
||||
|
||||
return CollectionWithPagingInfo.asPaged(paging, tags, results.hasMoreItems(), totalItems);
|
||||
List<Tag> tagsList = results.entrySet().stream().map(entry -> new Tag(entry.getKey(), (String)nodeService.getProperty(entry.getKey(), ContentModel.PROP_NAME))).collect(Collectors.toList());
|
||||
|
||||
if (params.getInclude().contains(PARAM_INCLUDE_COUNT))
|
||||
{
|
||||
tagsList.forEach(tag -> tag.setCount(results.get(tag.getNodeRef())));
|
||||
}
|
||||
|
||||
ListBackedPagingResults listBackedPagingResults = new ListBackedPagingResults(tagsList, Util.getPagingRequest(params.getPaging()));
|
||||
|
||||
return CollectionWithPagingInfo.asPaged(paging, listBackedPagingResults.getPage(), listBackedPagingResults.hasMoreItems(), (Integer) listBackedPagingResults.getTotalResultCount().getFirst());
|
||||
}
|
||||
|
||||
public NodeRef validateTag(String tagId)
|
||||
|
@@ -29,7 +29,6 @@ import static java.util.stream.Collectors.toList;
|
||||
|
||||
import static org.alfresco.rest.api.impl.TagsImpl.NOT_A_VALID_TAG;
|
||||
import static org.alfresco.rest.api.impl.TagsImpl.NO_PERMISSION_TO_MANAGE_A_TAG;
|
||||
import static org.alfresco.rest.api.impl.TagsImpl.PARAM_INCLUDE_COUNT;
|
||||
import static org.alfresco.service.cmr.repository.StoreRef.STORE_REF_WORKSPACE_SPACESSTORE;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.catchThrowable;
|
||||
@@ -41,10 +40,14 @@ import static org.mockito.ArgumentMatchers.isNull;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.BDDMockito.then;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.alfresco.model.ContentModel;
|
||||
import org.alfresco.query.PagingRequest;
|
||||
import org.alfresco.query.PagingResults;
|
||||
import org.alfresco.rest.api.Nodes;
|
||||
@@ -56,6 +59,7 @@ import org.alfresco.rest.framework.core.exceptions.UnsupportedResourceOperationE
|
||||
import org.alfresco.rest.framework.resource.parameters.CollectionWithPagingInfo;
|
||||
import org.alfresco.rest.framework.resource.parameters.Paging;
|
||||
import org.alfresco.rest.framework.resource.parameters.Parameters;
|
||||
import org.alfresco.rest.framework.resource.parameters.SortColumn;
|
||||
import org.alfresco.rest.framework.resource.parameters.where.InvalidQueryException;
|
||||
import org.alfresco.rest.framework.tools.RecognizedParamsExtractor;
|
||||
import org.alfresco.service.cmr.repository.ChildAssociationRef;
|
||||
@@ -84,6 +88,8 @@ public class TagsImplTest
|
||||
private static final NodeRef TAG_PARENT_NODE_REF = new NodeRef(STORE_REF_WORKSPACE_SPACESSTORE, PARENT_NODE_ID);
|
||||
private static final String CONTENT_NODE_ID = "content-node-id";
|
||||
private static final NodeRef CONTENT_NODE_REF = new NodeRef(STORE_REF_WORKSPACE_SPACESSTORE, CONTENT_NODE_ID);
|
||||
private static final String PARAM_INCLUDE_COUNT = "count";
|
||||
|
||||
|
||||
private final RecognizedParamsExtractor queryExtractor = new RecognizedParamsExtractor() {};
|
||||
|
||||
@@ -123,13 +129,16 @@ public class TagsImplTest
|
||||
public void testGetTags()
|
||||
{
|
||||
given(parametersMock.getPaging()).willReturn(pagingMock);
|
||||
given(taggingServiceMock.getTags(any(StoreRef.class), any(PagingRequest.class), any(), any())).willReturn(pagingResultsMock);
|
||||
given(pagingResultsMock.getTotalResultCount()).willReturn(new Pair<>(Integer.MAX_VALUE, 0));
|
||||
given(pagingResultsMock.getPage()).willReturn(List.of(new Pair<>(TAG_NODE_REF, TAG_NAME)));
|
||||
given(parametersMock.getSorting()).willReturn(new ArrayList<>());
|
||||
given(parametersMock.getInclude()).willReturn(new ArrayList<>());
|
||||
|
||||
//given(taggingServiceMock.getTags(eq(STORE_REF_WORKSPACE_SPACESSTORE), any(), any(), isNull(), isNull())).willReturn(List.of(new Pair<>(TAG_NODE_REF, null)));
|
||||
given(taggingServiceMock.getTags(eq(STORE_REF_WORKSPACE_SPACESSTORE), any(), any(), isNull(), isNull())).willReturn(Map.of(TAG_NODE_REF, 0L));
|
||||
given(nodeServiceMock.getProperty(any(NodeRef.class), eq(ContentModel.PROP_NAME))).willReturn("tag-dummy-name");
|
||||
|
||||
final CollectionWithPagingInfo<Tag> actualTags = objectUnderTest.getTags(STORE_REF_WORKSPACE_SPACESSTORE, parametersMock);
|
||||
|
||||
then(taggingServiceMock).should().getTags(eq(STORE_REF_WORKSPACE_SPACESSTORE), any(PagingRequest.class), isNull(), isNull());
|
||||
then(taggingServiceMock).should().getTags(eq(STORE_REF_WORKSPACE_SPACESSTORE), any(), any(), isNull(), isNull());
|
||||
then(taggingServiceMock).shouldHaveNoMoreInteractions();
|
||||
final List<Tag> expectedTags = createTagsWithNodeRefs(List.of(TAG_NAME));
|
||||
assertEquals(expectedTags, actualTags.getCollection());
|
||||
@@ -139,14 +148,15 @@ public class TagsImplTest
|
||||
public void testGetTags_verifyIfCountIsZero()
|
||||
{
|
||||
given(parametersMock.getPaging()).willReturn(pagingMock);
|
||||
given(taggingServiceMock.getTags(any(StoreRef.class), any(PagingRequest.class), any(), any())).willReturn(pagingResultsMock);
|
||||
given(pagingResultsMock.getTotalResultCount()).willReturn(new Pair<>(Integer.MAX_VALUE, 0));
|
||||
given(pagingResultsMock.getPage()).willReturn(List.of(new Pair<>(TAG_NODE_REF, TAG_NAME)));
|
||||
given(parametersMock.getSorting()).willReturn(new ArrayList<>());
|
||||
given(parametersMock.getInclude()).willReturn(List.of(PARAM_INCLUDE_COUNT));
|
||||
given(taggingServiceMock.getTags(any(StoreRef.class), any(), any(), any(), any())).willReturn(Map.of(TAG_NODE_REF, 0L));
|
||||
|
||||
given(nodeServiceMock.getProperty(any(NodeRef.class), eq(ContentModel.PROP_NAME))).willReturn("tag-dummy-name");
|
||||
given(parametersMock.getInclude()).willReturn(List.of("count"));
|
||||
|
||||
final CollectionWithPagingInfo<Tag> actualTags = objectUnderTest.getTags(STORE_REF_WORKSPACE_SPACESSTORE, parametersMock);
|
||||
|
||||
then(taggingServiceMock).should().findTaggedNodesAndCountByTagName(STORE_REF_WORKSPACE_SPACESSTORE);
|
||||
final List<Tag> expectedTags = createTagsWithNodeRefs(List.of(TAG_NAME)).stream()
|
||||
.peek(tag -> tag.setCount(0L))
|
||||
.collect(toList());
|
||||
@@ -159,36 +169,157 @@ public class TagsImplTest
|
||||
{
|
||||
NodeRef tagNodeA = new NodeRef("tag://A/");
|
||||
NodeRef tagNodeB = new NodeRef("tag://B/");
|
||||
List<Pair<NodeRef, String>> tagPairs = List.of(new Pair<>(tagNodeA, "taga"), new Pair<>(tagNodeB, "tagb"));
|
||||
|
||||
given(parametersMock.getSorting()).willReturn(Collections.emptyList());
|
||||
given(parametersMock.getPaging()).willReturn(pagingMock);
|
||||
given(taggingServiceMock.getTags(any(StoreRef.class), any(PagingRequest.class), any(), any())).willReturn(pagingResultsMock);
|
||||
given(pagingResultsMock.getTotalResultCount()).willReturn(new Pair<>(Integer.MAX_VALUE, 0));
|
||||
given(pagingResultsMock.getPage()).willReturn(tagPairs);
|
||||
given(parametersMock.getInclude()).willReturn(List.of("count"));
|
||||
// Only taga is included in the returned list since tagb is not in use.
|
||||
given(taggingServiceMock.findTaggedNodesAndCountByTagName(STORE_REF_WORKSPACE_SPACESSTORE)).willReturn(List.of(new Pair<>("taga", 5)));
|
||||
|
||||
final LinkedHashMap<NodeRef, Long> results = new LinkedHashMap<>();
|
||||
results.put(tagNodeA, 5L);
|
||||
results.put(tagNodeB, 0L);
|
||||
|
||||
given(taggingServiceMock.getTags(any(StoreRef.class), eq(List.of(PARAM_INCLUDE_COUNT)), isNull(), any(), any())).willReturn(results);
|
||||
given(nodeServiceMock.getProperty(any(NodeRef.class), eq(ContentModel.PROP_NAME))).willReturn("taga", "tagb");
|
||||
|
||||
final CollectionWithPagingInfo<Tag> actualTags = objectUnderTest.getTags(STORE_REF_WORKSPACE_SPACESSTORE, parametersMock);
|
||||
|
||||
then(taggingServiceMock).should().findTaggedNodesAndCountByTagName(STORE_REF_WORKSPACE_SPACESSTORE);
|
||||
final List<Tag> expectedTags = List.of(Tag.builder().tag("tagA").nodeRef(tagNodeA).count(5L).create(),
|
||||
Tag.builder().tag("tagB").nodeRef(tagNodeB).count(0L).create());
|
||||
assertEquals(expectedTags, actualTags.getCollection());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetTags_orderByCountAscendingOrder()
|
||||
{
|
||||
NodeRef tagNodeA = new NodeRef("tag://A/");
|
||||
NodeRef tagNodeB = new NodeRef("tag://B/");
|
||||
NodeRef tagNodeC = new NodeRef("tag://C/");
|
||||
|
||||
given(parametersMock.getPaging()).willReturn(pagingMock);
|
||||
given(parametersMock.getInclude()).willReturn(List.of("count"));
|
||||
given(parametersMock.getSorting()).willReturn(List.of(new SortColumn("count", true)));
|
||||
|
||||
final LinkedHashMap<NodeRef, Long> results = new LinkedHashMap<>();
|
||||
results.put(tagNodeB, 0L);
|
||||
results.put(tagNodeC, 2L);
|
||||
results.put(tagNodeA, 5L);
|
||||
|
||||
given(taggingServiceMock.getTags(any(StoreRef.class), eq(List.of(PARAM_INCLUDE_COUNT)), eq(new Pair<>("count", true)), any(), any())).willReturn(results);
|
||||
given(nodeServiceMock.getProperty(tagNodeA, ContentModel.PROP_NAME)).willReturn("taga");
|
||||
given(nodeServiceMock.getProperty(tagNodeB, ContentModel.PROP_NAME)).willReturn("tagb");
|
||||
given(nodeServiceMock.getProperty(tagNodeC, ContentModel.PROP_NAME)).willReturn("tagc");
|
||||
|
||||
//when
|
||||
final CollectionWithPagingInfo<Tag> actualTags = objectUnderTest.getTags(STORE_REF_WORKSPACE_SPACESSTORE, parametersMock);
|
||||
|
||||
final List<Tag> expectedTags = List.of(Tag.builder().tag("tagb").nodeRef(tagNodeB).count(0L).create(),
|
||||
Tag.builder().tag("tagc").nodeRef(tagNodeC).count(2L).create(),
|
||||
Tag.builder().tag("taga").nodeRef(tagNodeA).count(5L).create());
|
||||
assertEquals(expectedTags, actualTags.getCollection());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetTags_orderByCountDescendingOrder()
|
||||
{
|
||||
NodeRef tagNodeA = new NodeRef("tag://A/");
|
||||
NodeRef tagNodeB = new NodeRef("tag://B/");
|
||||
NodeRef tagNodeC = new NodeRef("tag://C/");
|
||||
|
||||
given(parametersMock.getPaging()).willReturn(pagingMock);
|
||||
given(parametersMock.getInclude()).willReturn(List.of("count"));
|
||||
given(parametersMock.getSorting()).willReturn(List.of(new SortColumn("count", false)));
|
||||
|
||||
final LinkedHashMap<NodeRef, Long> results = new LinkedHashMap<>();
|
||||
results.put(tagNodeA, 5L);
|
||||
results.put(tagNodeC, 2L);
|
||||
results.put(tagNodeB, 0L);
|
||||
|
||||
given(taggingServiceMock.getTags(any(StoreRef.class), eq(List.of(PARAM_INCLUDE_COUNT)), eq(new Pair<>("count", false)), any(), any())).willReturn(results);
|
||||
given(nodeServiceMock.getProperty(tagNodeA, ContentModel.PROP_NAME)).willReturn("taga");
|
||||
given(nodeServiceMock.getProperty(tagNodeB, ContentModel.PROP_NAME)).willReturn("tagb");
|
||||
given(nodeServiceMock.getProperty(tagNodeC, ContentModel.PROP_NAME)).willReturn("tagc");
|
||||
|
||||
//when
|
||||
final CollectionWithPagingInfo<Tag> actualTags = objectUnderTest.getTags(STORE_REF_WORKSPACE_SPACESSTORE, parametersMock);
|
||||
|
||||
final List<Tag> expectedTags = List.of(Tag.builder().tag("taga").nodeRef(tagNodeA).count(5L).create(),
|
||||
Tag.builder().tag("tagc").nodeRef(tagNodeC).count(2L).create(),
|
||||
Tag.builder().tag("tagb").nodeRef(tagNodeB).count(0L).create());
|
||||
assertEquals(expectedTags, actualTags.getCollection());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetTags_orderByTagAscendingOrder()
|
||||
{
|
||||
NodeRef tagApple = new NodeRef("tag://apple/");
|
||||
NodeRef tagBanana = new NodeRef("tag://banana/");
|
||||
NodeRef tagCoconut = new NodeRef("tag://coconut/");
|
||||
|
||||
given(parametersMock.getPaging()).willReturn(pagingMock);
|
||||
given(parametersMock.getInclude()).willReturn(Collections.emptyList());
|
||||
given(parametersMock.getSorting()).willReturn(List.of(new SortColumn("tag", true)));
|
||||
|
||||
final LinkedHashMap<NodeRef, Long> results = new LinkedHashMap<>();
|
||||
results.put(tagApple, 0L);
|
||||
results.put(tagBanana, 0L);
|
||||
results.put(tagCoconut, 0L);
|
||||
|
||||
given(taggingServiceMock.getTags(any(StoreRef.class), any(), eq(new Pair<>("tag", true)), any(), any())).willReturn(results);
|
||||
given(nodeServiceMock.getProperty(tagApple, ContentModel.PROP_NAME)).willReturn("apple");
|
||||
given(nodeServiceMock.getProperty(tagBanana, ContentModel.PROP_NAME)).willReturn("banana");
|
||||
given(nodeServiceMock.getProperty(tagCoconut, ContentModel.PROP_NAME)).willReturn("coconut");
|
||||
|
||||
//when
|
||||
final CollectionWithPagingInfo<Tag> actualTags = objectUnderTest.getTags(STORE_REF_WORKSPACE_SPACESSTORE, parametersMock);
|
||||
|
||||
final List<Tag> expectedTags = List.of(Tag.builder().tag("apple").nodeRef(tagApple).create(),
|
||||
Tag.builder().tag("banana").nodeRef(tagBanana).create(),
|
||||
Tag.builder().tag("coconut").nodeRef(tagCoconut).create());
|
||||
assertEquals(expectedTags, actualTags.getCollection());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetTags_orderByTagDescendingOrder()
|
||||
{
|
||||
NodeRef tagApple = new NodeRef("tag://apple/");
|
||||
NodeRef tagBanana = new NodeRef("tag://banana/");
|
||||
NodeRef tagCoconut = new NodeRef("tag://coconut/");
|
||||
|
||||
given(parametersMock.getPaging()).willReturn(pagingMock);
|
||||
given(parametersMock.getInclude()).willReturn(Collections.emptyList());
|
||||
given(parametersMock.getSorting()).willReturn(List.of(new SortColumn("tag", false)));
|
||||
|
||||
final LinkedHashMap<NodeRef, Long> results = new LinkedHashMap<>();
|
||||
results.put(tagCoconut, 0L);
|
||||
results.put(tagBanana, 0L);
|
||||
results.put(tagApple, 0L);
|
||||
|
||||
given(taggingServiceMock.getTags(any(StoreRef.class), any(), eq(new Pair<>("tag", false)), any(), any())).willReturn(results);
|
||||
given(nodeServiceMock.getProperty(tagApple, ContentModel.PROP_NAME)).willReturn("apple");
|
||||
given(nodeServiceMock.getProperty(tagBanana, ContentModel.PROP_NAME)).willReturn("banana");
|
||||
given(nodeServiceMock.getProperty(tagCoconut, ContentModel.PROP_NAME)).willReturn("coconut");
|
||||
|
||||
//when
|
||||
final CollectionWithPagingInfo<Tag> actualTags = objectUnderTest.getTags(STORE_REF_WORKSPACE_SPACESSTORE, parametersMock);
|
||||
|
||||
final List<Tag> expectedTags = List.of(Tag.builder().tag("coconut").nodeRef(tagCoconut).create(),
|
||||
Tag.builder().tag("banana").nodeRef(tagBanana).create(),
|
||||
Tag.builder().tag("apple").nodeRef(tagApple).create());
|
||||
assertEquals(expectedTags, actualTags.getCollection());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetTags_withEqualsClauseWhereQuery()
|
||||
{
|
||||
given(parametersMock.getPaging()).willReturn(pagingMock);
|
||||
given(parametersMock.getQuery()).willReturn(queryExtractor.getWhereClause("(tag=expectedName)"));
|
||||
given(taggingServiceMock.getTags(any(StoreRef.class), any(PagingRequest.class), any(), any())).willReturn(pagingResultsMock);
|
||||
given(pagingResultsMock.getTotalResultCount()).willReturn(new Pair<>(Integer.MAX_VALUE, 0));
|
||||
given(parametersMock.getSorting()).willReturn(new ArrayList<>());
|
||||
given(parametersMock.getInclude()).willReturn(new ArrayList<>());
|
||||
|
||||
//when
|
||||
final CollectionWithPagingInfo<Tag> actualTags = objectUnderTest.getTags(STORE_REF_WORKSPACE_SPACESSTORE, parametersMock);
|
||||
|
||||
then(taggingServiceMock).should().getTags(eq(STORE_REF_WORKSPACE_SPACESSTORE), any(PagingRequest.class), eq(Set.of("expectedname")), isNull());
|
||||
then(taggingServiceMock).should().getTags(eq(STORE_REF_WORKSPACE_SPACESSTORE), eq(new ArrayList<>()), any(), eq(Set.of("expectedname")), isNull());
|
||||
then(taggingServiceMock).shouldHaveNoMoreInteractions();
|
||||
assertThat(actualTags).isNotNull();
|
||||
}
|
||||
@@ -198,13 +329,13 @@ public class TagsImplTest
|
||||
{
|
||||
given(parametersMock.getPaging()).willReturn(pagingMock);
|
||||
given(parametersMock.getQuery()).willReturn(queryExtractor.getWhereClause("(tag IN (expectedName1, expectedName2))"));
|
||||
given(taggingServiceMock.getTags(any(StoreRef.class), any(PagingRequest.class), any(), any())).willReturn(pagingResultsMock);
|
||||
given(pagingResultsMock.getTotalResultCount()).willReturn(new Pair<>(Integer.MAX_VALUE, 0));
|
||||
given(parametersMock.getSorting()).willReturn(new ArrayList<>());
|
||||
given(parametersMock.getInclude()).willReturn(new ArrayList<>());
|
||||
|
||||
//when
|
||||
final CollectionWithPagingInfo<Tag> actualTags = objectUnderTest.getTags(STORE_REF_WORKSPACE_SPACESSTORE, parametersMock);
|
||||
|
||||
then(taggingServiceMock).should().getTags(eq(STORE_REF_WORKSPACE_SPACESSTORE), any(PagingRequest.class), eq(Set.of("expectedname1", "expectedname2")), isNull());
|
||||
then(taggingServiceMock).should().getTags(eq(STORE_REF_WORKSPACE_SPACESSTORE),any(), any(), eq(Set.of("expectedname1", "expectedname2")), isNull());
|
||||
then(taggingServiceMock).shouldHaveNoMoreInteractions();
|
||||
assertThat(actualTags).isNotNull();
|
||||
}
|
||||
@@ -214,13 +345,13 @@ public class TagsImplTest
|
||||
{
|
||||
given(parametersMock.getPaging()).willReturn(pagingMock);
|
||||
given(parametersMock.getQuery()).willReturn(queryExtractor.getWhereClause("(tag MATCHES ('expectedName*'))"));
|
||||
given(taggingServiceMock.getTags(any(StoreRef.class), any(PagingRequest.class), any(), any())).willReturn(pagingResultsMock);
|
||||
given(pagingResultsMock.getTotalResultCount()).willReturn(new Pair<>(Integer.MAX_VALUE, 0));
|
||||
given(parametersMock.getSorting()).willReturn(new ArrayList<>());
|
||||
given(parametersMock.getInclude()).willReturn(new ArrayList<>());
|
||||
|
||||
//when
|
||||
final CollectionWithPagingInfo<Tag> actualTags = objectUnderTest.getTags(STORE_REF_WORKSPACE_SPACESSTORE, parametersMock);
|
||||
|
||||
then(taggingServiceMock).should().getTags(eq(STORE_REF_WORKSPACE_SPACESSTORE), any(PagingRequest.class), isNull(), eq(Set.of("expectedname*")));
|
||||
then(taggingServiceMock).should().getTags(eq(STORE_REF_WORKSPACE_SPACESSTORE), any(), any(), isNull(), eq(Set.of("expectedname*")));
|
||||
then(taggingServiceMock).shouldHaveNoMoreInteractions();
|
||||
assertThat(actualTags).isNotNull();
|
||||
}
|
||||
@@ -230,6 +361,7 @@ public class TagsImplTest
|
||||
{
|
||||
given(parametersMock.getPaging()).willReturn(pagingMock);
|
||||
given(parametersMock.getQuery()).willReturn(queryExtractor.getWhereClause("(tag=expectedName AND tag IN (expectedName1, expectedName2))"));
|
||||
given(parametersMock.getSorting()).willReturn(new ArrayList<>());
|
||||
|
||||
//when
|
||||
final Throwable actualException = catchThrowable(() -> objectUnderTest.getTags(STORE_REF_WORKSPACE_SPACESSTORE, parametersMock));
|
||||
@@ -243,6 +375,7 @@ public class TagsImplTest
|
||||
{
|
||||
given(parametersMock.getPaging()).willReturn(pagingMock);
|
||||
given(parametersMock.getQuery()).willReturn(queryExtractor.getWhereClause("(tag BETWEEN ('expectedName', 'expectedName2'))"));
|
||||
given(parametersMock.getSorting()).willReturn(new ArrayList<>());
|
||||
|
||||
//when
|
||||
final Throwable actualException = catchThrowable(() -> objectUnderTest.getTags(STORE_REF_WORKSPACE_SPACESSTORE, parametersMock));
|
||||
@@ -256,6 +389,7 @@ public class TagsImplTest
|
||||
{
|
||||
given(parametersMock.getPaging()).willReturn(pagingMock);
|
||||
given(parametersMock.getQuery()).willReturn(queryExtractor.getWhereClause("(NOT tag=expectedName)"));
|
||||
given(parametersMock.getSorting()).willReturn(new ArrayList<>());
|
||||
|
||||
//when
|
||||
final Throwable actualException = catchThrowable(() -> objectUnderTest.getTags(STORE_REF_WORKSPACE_SPACESSTORE, parametersMock));
|
||||
|
@@ -23,225 +23,225 @@
|
||||
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||
* #L%
|
||||
*/
|
||||
package org.alfresco.repo.jscript;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import org.alfresco.model.ContentModel;
|
||||
import org.alfresco.query.PagingRequest;
|
||||
import org.alfresco.service.ServiceRegistry;
|
||||
import org.alfresco.service.cmr.repository.ChildAssociationRef;
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
import org.alfresco.service.cmr.repository.StoreRef;
|
||||
import org.alfresco.service.cmr.search.CategoryService;
|
||||
import org.alfresco.service.namespace.QName;
|
||||
import org.alfresco.util.Pair;
|
||||
import org.mozilla.javascript.Context;
|
||||
import org.mozilla.javascript.Scriptable;
|
||||
|
||||
/**
|
||||
* Support class for finding categories, finding root nodes for categories and creating root categories.
|
||||
*
|
||||
* @author Andy Hind
|
||||
*/
|
||||
public final class Classification extends BaseScopableProcessorExtension
|
||||
{
|
||||
private ServiceRegistry services;
|
||||
|
||||
private StoreRef storeRef;
|
||||
|
||||
/**
|
||||
* Set the default store reference
|
||||
*
|
||||
* @param storeRef the default store reference
|
||||
*/
|
||||
public void setStoreUrl(String storeRef)
|
||||
{
|
||||
this.storeRef = new StoreRef(storeRef);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the service registry
|
||||
*
|
||||
* @param services the service registry
|
||||
*/
|
||||
public void setServiceRegistry(ServiceRegistry services)
|
||||
{
|
||||
this.services = services;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find all the category nodes in a given classification.
|
||||
*
|
||||
* @param aspect String
|
||||
* @return Scriptable
|
||||
*/
|
||||
public Scriptable getAllCategoryNodes(String aspect)
|
||||
{
|
||||
Object[] cats = buildCategoryNodes(services.getCategoryService().getCategories(
|
||||
storeRef, createQName(aspect), CategoryService.Depth.ANY));
|
||||
return Context.getCurrentContext().newArray(getScope(), cats);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all the aspects that define a classification.
|
||||
*
|
||||
* @return String[]
|
||||
*/
|
||||
public String[] getAllClassificationAspects()
|
||||
{
|
||||
Collection<QName> aspects = services.getCategoryService().getClassificationAspects();
|
||||
String[] answer = new String[aspects.size()];
|
||||
int i = 0;
|
||||
for (QName qname : aspects)
|
||||
{
|
||||
answer[i++] = qname.toPrefixString(this.services.getNamespaceService());
|
||||
}
|
||||
return answer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a root category in a classification.
|
||||
*
|
||||
* @param aspect String
|
||||
* @param name String
|
||||
*/
|
||||
public CategoryNode createRootCategory(String aspect, String name)
|
||||
{
|
||||
NodeRef categoryNodeRef = services.getCategoryService().createRootCategory(storeRef, createQName(aspect), name);
|
||||
CategoryNode categoryNode = new CategoryNode(categoryNodeRef, this.services, getScope());
|
||||
|
||||
return categoryNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the category node from the category node reference.
|
||||
*
|
||||
* @param categoryRef category node reference
|
||||
* @return {@link CategoryNode} category node
|
||||
*/
|
||||
public CategoryNode getCategory(String categoryRef)
|
||||
{
|
||||
CategoryNode result = null;
|
||||
NodeRef categoryNodeRef = new NodeRef(categoryRef);
|
||||
if (services.getNodeService().exists(categoryNodeRef) == true &&
|
||||
services.getDictionaryService().isSubClass(ContentModel.TYPE_CATEGORY, services.getNodeService().getType(categoryNodeRef)) == true)
|
||||
{
|
||||
result = new CategoryNode(categoryNodeRef, this.services, getScope());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the root categories in a classification.
|
||||
*
|
||||
* @param aspect String
|
||||
* @return Scriptable
|
||||
*/
|
||||
public Scriptable getRootCategories(String aspect)
|
||||
{
|
||||
Object[] cats = buildCategoryNodes(services.getCategoryService().getRootCategories(
|
||||
storeRef, createQName(aspect)));
|
||||
return Context.getCurrentContext().newArray(getScope(), cats);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get ordered, filtered and paged root categories in a classification.
|
||||
*
|
||||
* @param aspect
|
||||
* @param filter
|
||||
* @param maxItems
|
||||
* @param skipCount (offset)
|
||||
* @return
|
||||
*/
|
||||
public Scriptable getRootCategories(String aspect, String filter, int maxItems, int skipCount)
|
||||
{
|
||||
PagingRequest pagingRequest = new PagingRequest(skipCount, maxItems);
|
||||
List<ChildAssociationRef> rootCategories = services.getCategoryService().getRootCategories(storeRef, createQName(aspect), pagingRequest, true, filter).getPage();
|
||||
Object[] cats = buildCategoryNodes(rootCategories);
|
||||
return Context.getCurrentContext().newArray(getScope(), cats);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the category usage count.
|
||||
*
|
||||
* @param aspect String
|
||||
* @param maxCount int
|
||||
* @return Scriptable
|
||||
*/
|
||||
public Scriptable getCategoryUsage(String aspect, int maxCount)
|
||||
{
|
||||
List<Pair<NodeRef, Integer>> topCats = services.getCategoryService().getTopCategories(storeRef, createQName(aspect), maxCount);
|
||||
Object[] tags = new Object[topCats.size()];
|
||||
int i = 0;
|
||||
for (Pair<NodeRef, Integer> topCat : topCats)
|
||||
{
|
||||
tags[i++] = new Tag(new CategoryNode(topCat.getFirst(), this.services, getScope()), topCat.getSecond());
|
||||
}
|
||||
|
||||
return Context.getCurrentContext().newArray(getScope(), tags);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build category nodes.
|
||||
*
|
||||
* @param cars list of associations to category nodes
|
||||
* @return {@link Object}[] array of category nodes
|
||||
*/
|
||||
private Object[] buildCategoryNodes(Collection<ChildAssociationRef> cars)
|
||||
{
|
||||
Object[] categoryNodes = new Object[cars.size()];
|
||||
int i = 0;
|
||||
for (ChildAssociationRef car : cars)
|
||||
{
|
||||
categoryNodes[i++] = new CategoryNode(car.getChildRef(), this.services, getScope());
|
||||
}
|
||||
return categoryNodes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create QName from string
|
||||
*
|
||||
* @param s QName string value
|
||||
* @return {@link QName} qualified name object
|
||||
*/
|
||||
private QName createQName(String s)
|
||||
{
|
||||
QName qname;
|
||||
if (s.indexOf(QName.NAMESPACE_BEGIN) != -1)
|
||||
{
|
||||
qname = QName.createQName(s);
|
||||
}
|
||||
else
|
||||
{
|
||||
qname = QName.createQName(s, this.services.getNamespaceService());
|
||||
}
|
||||
return qname;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tag class returned from getCategoryUsage().
|
||||
*/
|
||||
public final class Tag
|
||||
{
|
||||
private CategoryNode categoryNode;
|
||||
private int frequency = 0;
|
||||
|
||||
public Tag(CategoryNode categoryNode, int frequency)
|
||||
{
|
||||
this.categoryNode = categoryNode;
|
||||
this.frequency = frequency;
|
||||
}
|
||||
|
||||
public CategoryNode getCategory()
|
||||
{
|
||||
return categoryNode;
|
||||
}
|
||||
|
||||
public int getFrequency()
|
||||
{
|
||||
return frequency;
|
||||
}
|
||||
}
|
||||
}
|
||||
package org.alfresco.repo.jscript;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import org.alfresco.model.ContentModel;
|
||||
import org.alfresco.query.PagingRequest;
|
||||
import org.alfresco.service.ServiceRegistry;
|
||||
import org.alfresco.service.cmr.repository.ChildAssociationRef;
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
import org.alfresco.service.cmr.repository.StoreRef;
|
||||
import org.alfresco.service.cmr.search.CategoryService;
|
||||
import org.alfresco.service.namespace.QName;
|
||||
import org.alfresco.util.Pair;
|
||||
import org.mozilla.javascript.Context;
|
||||
import org.mozilla.javascript.Scriptable;
|
||||
|
||||
/**
|
||||
* Support class for finding categories, finding root nodes for categories and creating root categories.
|
||||
*
|
||||
* @author Andy Hind
|
||||
*/
|
||||
public final class Classification extends BaseScopableProcessorExtension
|
||||
{
|
||||
private ServiceRegistry services;
|
||||
|
||||
private StoreRef storeRef;
|
||||
|
||||
/**
|
||||
* Set the default store reference
|
||||
*
|
||||
* @param storeRef the default store reference
|
||||
*/
|
||||
public void setStoreUrl(String storeRef)
|
||||
{
|
||||
this.storeRef = new StoreRef(storeRef);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the service registry
|
||||
*
|
||||
* @param services the service registry
|
||||
*/
|
||||
public void setServiceRegistry(ServiceRegistry services)
|
||||
{
|
||||
this.services = services;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find all the category nodes in a given classification.
|
||||
*
|
||||
* @param aspect String
|
||||
* @return Scriptable
|
||||
*/
|
||||
public Scriptable getAllCategoryNodes(String aspect)
|
||||
{
|
||||
Object[] cats = buildCategoryNodes(services.getCategoryService().getCategories(
|
||||
storeRef, createQName(aspect), CategoryService.Depth.ANY));
|
||||
return Context.getCurrentContext().newArray(getScope(), cats);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all the aspects that define a classification.
|
||||
*
|
||||
* @return String[]
|
||||
*/
|
||||
public String[] getAllClassificationAspects()
|
||||
{
|
||||
Collection<QName> aspects = services.getCategoryService().getClassificationAspects();
|
||||
String[] answer = new String[aspects.size()];
|
||||
int i = 0;
|
||||
for (QName qname : aspects)
|
||||
{
|
||||
answer[i++] = qname.toPrefixString(this.services.getNamespaceService());
|
||||
}
|
||||
return answer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a root category in a classification.
|
||||
*
|
||||
* @param aspect String
|
||||
* @param name String
|
||||
*/
|
||||
public CategoryNode createRootCategory(String aspect, String name)
|
||||
{
|
||||
NodeRef categoryNodeRef = services.getCategoryService().createRootCategory(storeRef, createQName(aspect), name);
|
||||
CategoryNode categoryNode = new CategoryNode(categoryNodeRef, this.services, getScope());
|
||||
|
||||
return categoryNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the category node from the category node reference.
|
||||
*
|
||||
* @param categoryRef category node reference
|
||||
* @return {@link CategoryNode} category node
|
||||
*/
|
||||
public CategoryNode getCategory(String categoryRef)
|
||||
{
|
||||
CategoryNode result = null;
|
||||
NodeRef categoryNodeRef = new NodeRef(categoryRef);
|
||||
if (services.getNodeService().exists(categoryNodeRef) == true &&
|
||||
services.getDictionaryService().isSubClass(ContentModel.TYPE_CATEGORY, services.getNodeService().getType(categoryNodeRef)) == true)
|
||||
{
|
||||
result = new CategoryNode(categoryNodeRef, this.services, getScope());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the root categories in a classification.
|
||||
*
|
||||
* @param aspect String
|
||||
* @return Scriptable
|
||||
*/
|
||||
public Scriptable getRootCategories(String aspect)
|
||||
{
|
||||
Object[] cats = buildCategoryNodes(services.getCategoryService().getRootCategories(
|
||||
storeRef, createQName(aspect)));
|
||||
return Context.getCurrentContext().newArray(getScope(), cats);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get ordered, filtered and paged root categories in a classification.
|
||||
*
|
||||
* @param aspect
|
||||
* @param filter
|
||||
* @param maxItems
|
||||
* @param skipCount (offset)
|
||||
* @return
|
||||
*/
|
||||
public Scriptable getRootCategories(String aspect, String filter, int maxItems, int skipCount)
|
||||
{
|
||||
PagingRequest pagingRequest = new PagingRequest(skipCount, maxItems);
|
||||
List<ChildAssociationRef> rootCategories = services.getCategoryService().getRootCategories(storeRef, createQName(aspect), pagingRequest, true, filter).getPage();
|
||||
Object[] cats = buildCategoryNodes(rootCategories);
|
||||
return Context.getCurrentContext().newArray(getScope(), cats);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the category usage count.
|
||||
*
|
||||
* @param aspect String
|
||||
* @param maxCount int
|
||||
* @return Scriptable
|
||||
*/
|
||||
public Scriptable getCategoryUsage(String aspect, int maxCount)
|
||||
{
|
||||
List<Pair<NodeRef, Integer>> topCats = services.getCategoryService().getTopCategories(storeRef, createQName(aspect), maxCount);
|
||||
Object[] tags = new Object[topCats.size()];
|
||||
int i = 0;
|
||||
for (Pair<NodeRef, Integer> topCat : topCats)
|
||||
{
|
||||
tags[i++] = new Tag(new CategoryNode(topCat.getFirst(), this.services, getScope()), topCat.getSecond());
|
||||
}
|
||||
|
||||
return Context.getCurrentContext().newArray(getScope(), tags);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build category nodes.
|
||||
*
|
||||
* @param cars list of associations to category nodes
|
||||
* @return {@link Object}[] array of category nodes
|
||||
*/
|
||||
private Object[] buildCategoryNodes(Collection<ChildAssociationRef> cars)
|
||||
{
|
||||
Object[] categoryNodes = new Object[cars.size()];
|
||||
int i = 0;
|
||||
for (ChildAssociationRef car : cars)
|
||||
{
|
||||
categoryNodes[i++] = new CategoryNode(car.getChildRef(), this.services, getScope());
|
||||
}
|
||||
return categoryNodes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create QName from string
|
||||
*
|
||||
* @param s QName string value
|
||||
* @return {@link QName} qualified name object
|
||||
*/
|
||||
private QName createQName(String s)
|
||||
{
|
||||
QName qname;
|
||||
if (s.indexOf(QName.NAMESPACE_BEGIN) != -1)
|
||||
{
|
||||
qname = QName.createQName(s);
|
||||
}
|
||||
else
|
||||
{
|
||||
qname = QName.createQName(s, this.services.getNamespaceService());
|
||||
}
|
||||
return qname;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tag class returned from getCategoryUsage().
|
||||
*/
|
||||
public final class Tag
|
||||
{
|
||||
private CategoryNode categoryNode;
|
||||
private int frequency = 0;
|
||||
|
||||
public Tag(CategoryNode categoryNode, int frequency)
|
||||
{
|
||||
this.categoryNode = categoryNode;
|
||||
this.frequency = frequency;
|
||||
}
|
||||
|
||||
public CategoryNode getCategory()
|
||||
{
|
||||
return categoryNode;
|
||||
}
|
||||
|
||||
public int getFrequency()
|
||||
{
|
||||
return frequency;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -414,34 +414,7 @@ public abstract class AbstractCategoryServiceImpl implements CategoryService
|
||||
int count = 0;
|
||||
boolean moreItems = false;
|
||||
|
||||
final Function<NodeRef, Collection<ChildAssociationRef>> childNodesSupplier = (nodeRef) -> {
|
||||
final Set<ChildAssociationRef> childNodes = new HashSet<>();
|
||||
if (CollectionUtils.isEmpty(exactNamesFilter) && CollectionUtils.isEmpty(alikeNamesFilter))
|
||||
{
|
||||
// lookup in DB without filtering
|
||||
childNodes.addAll(nodeService.getChildAssocs(nodeRef, ContentModel.ASSOC_SUBCATEGORIES, RegexQNamePattern.MATCH_ALL));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (CollectionUtils.isNotEmpty(exactNamesFilter))
|
||||
{
|
||||
// lookup in DB filtering by name
|
||||
childNodes.addAll(nodeService.getChildrenByName(nodeRef, ContentModel.ASSOC_SUBCATEGORIES, exactNamesFilter));
|
||||
}
|
||||
if (CollectionUtils.isNotEmpty(alikeNamesFilter))
|
||||
{
|
||||
// lookup using search engin filtering by name
|
||||
childNodes.addAll(getChildren(nodeRef, Mode.SUB_CATEGORIES, Depth.IMMEDIATE, sortByName, alikeNamesFilter, skipCount + maxItems + 1));
|
||||
}
|
||||
}
|
||||
|
||||
Stream<ChildAssociationRef> childNodesStream = childNodes.stream();
|
||||
if (sortByName)
|
||||
{
|
||||
childNodesStream = childNodesStream.sorted(Comparator.comparing(tag -> tag.getQName().getLocalName()));
|
||||
}
|
||||
return childNodesStream.collect(Collectors.toList());
|
||||
};
|
||||
final Function<NodeRef, Collection<ChildAssociationRef>> childNodesSupplier = getNodeRefCollectionFunction(sortByName, exactNamesFilter, alikeNamesFilter, skipCount, maxItems);
|
||||
|
||||
OUTER_LOOP: for(NodeRef nodeRef : nodeRefs)
|
||||
{
|
||||
@@ -468,6 +441,55 @@ public abstract class AbstractCategoryServiceImpl implements CategoryService
|
||||
return new ListBackedPagingResults<>(associations, moreItems);
|
||||
}
|
||||
|
||||
public Collection<ChildAssociationRef> getRootCategories(StoreRef storeRef, QName aspectName, Collection<String> exactNamesFilter, Collection<String> alikeNamesFilter)
|
||||
{
|
||||
final Set<NodeRef> nodeRefs = getClassificationNodes(storeRef, aspectName);
|
||||
final List<ChildAssociationRef> associations = new LinkedList<>();
|
||||
|
||||
final Function<NodeRef, Collection<ChildAssociationRef>> childNodesSupplier = getNodeRefCollectionFunction(false, exactNamesFilter, alikeNamesFilter, 0, 10000);
|
||||
|
||||
for (NodeRef nodeRef : nodeRefs)
|
||||
{
|
||||
Collection<ChildAssociationRef> children = childNodesSupplier.apply(nodeRef);
|
||||
associations.addAll(children);
|
||||
}
|
||||
|
||||
return associations;
|
||||
}
|
||||
|
||||
private Function<NodeRef, Collection<ChildAssociationRef>> getNodeRefCollectionFunction(boolean sortByName, Collection<String> exactNamesFilter, Collection<String> alikeNamesFilter, int skipCount, int maxItems)
|
||||
{
|
||||
final Function<NodeRef, Collection<ChildAssociationRef>> childNodesSupplier = (nodeRef) -> {
|
||||
final Set<ChildAssociationRef> childNodes = new HashSet<>();
|
||||
if (CollectionUtils.isEmpty(exactNamesFilter) && CollectionUtils.isEmpty(alikeNamesFilter))
|
||||
{
|
||||
// lookup in DB without filtering
|
||||
childNodes.addAll(nodeService.getChildAssocs(nodeRef, ContentModel.ASSOC_SUBCATEGORIES, RegexQNamePattern.MATCH_ALL));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (CollectionUtils.isNotEmpty(exactNamesFilter))
|
||||
{
|
||||
// lookup in DB filtering by name
|
||||
childNodes.addAll(nodeService.getChildrenByName(nodeRef, ContentModel.ASSOC_SUBCATEGORIES, exactNamesFilter));
|
||||
}
|
||||
if (CollectionUtils.isNotEmpty(alikeNamesFilter))
|
||||
{
|
||||
// lookup using search engine filtering by name
|
||||
childNodes.addAll(getChildren(nodeRef, Mode.SUB_CATEGORIES, Depth.IMMEDIATE, sortByName, alikeNamesFilter, skipCount + maxItems + 1));
|
||||
}
|
||||
}
|
||||
|
||||
Stream<ChildAssociationRef> childNodesStream = childNodes.stream();
|
||||
if (sortByName)
|
||||
{
|
||||
childNodesStream = childNodesStream.sorted(Comparator.comparing(tag -> tag.getQName().getLocalName()));
|
||||
}
|
||||
return childNodesStream.collect(Collectors.toList());
|
||||
};
|
||||
return childNodesSupplier;
|
||||
}
|
||||
|
||||
public Collection<ChildAssociationRef> getRootCategories(StoreRef storeRef, QName aspectName)
|
||||
{
|
||||
return getRootCategories(storeRef, aspectName, null);
|
||||
|
@@ -46,12 +46,16 @@ import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.TreeMap;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.alfresco.model.ContentModel;
|
||||
import org.alfresco.query.EmptyPagingResults;
|
||||
@@ -61,6 +65,7 @@ import org.alfresco.repo.audit.AuditComponent;
|
||||
import org.alfresco.repo.coci.CheckOutCheckInServicePolicies.OnCheckOut;
|
||||
import org.alfresco.repo.copy.CopyServicePolicies.BeforeCopyPolicy;
|
||||
import org.alfresco.repo.copy.CopyServicePolicies.OnCopyCompletePolicy;
|
||||
import org.alfresco.repo.domain.query.QueryException;
|
||||
import org.alfresco.repo.event2.EventGenerator;
|
||||
import org.alfresco.repo.node.NodeServicePolicies.BeforeDeleteNodePolicy;
|
||||
import org.alfresco.repo.node.NodeServicePolicies.OnCreateNodePolicy;
|
||||
@@ -138,6 +143,9 @@ public class TaggingServiceImpl implements TaggingService,
|
||||
private static final String TAG_DETAILS_DELIMITER = "|";
|
||||
/** Next tag delimiter */
|
||||
private static final String NEXT_TAG_DELIMITER = "\n";
|
||||
/** Parameters Include count */
|
||||
private static final String PARAM_INCLUDE_COUNT = "count";
|
||||
|
||||
|
||||
private static Set<String> FORBIDDEN_TAGS_SEQUENCES = new HashSet<String>(Arrays.asList(new String[] {NEXT_TAG_DELIMITER, TAG_DETAILS_DELIMITER}));
|
||||
|
||||
@@ -680,6 +688,21 @@ public class TaggingServiceImpl implements TaggingService,
|
||||
}
|
||||
}
|
||||
|
||||
public Map<String, Long> calculateCount(StoreRef storeRef)
|
||||
{
|
||||
List<Pair<String, Integer>> tagsByCount = findTaggedNodesAndCountByTagName(storeRef);
|
||||
Map<String, Long> tagsByCountMap = new HashMap<>();
|
||||
if (tagsByCount != null)
|
||||
{
|
||||
for (Pair<String, Integer> tagByCountElem : tagsByCount)
|
||||
{
|
||||
tagsByCountMap.put(tagByCountElem.getFirst(), Long.valueOf(tagByCountElem.getSecond()));
|
||||
}
|
||||
}
|
||||
return tagsByCountMap;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @see TaggingService#hasTag(NodeRef, String)
|
||||
*/
|
||||
@@ -956,7 +979,83 @@ public class TaggingServiceImpl implements TaggingService,
|
||||
exactNamesFilter, alikeNamesFilter);
|
||||
|
||||
return mapPagingResult(rootCategories,
|
||||
(childAssociation) -> new Pair<>(childAssociation.getChildRef(), childAssociation.getQName().getLocalName()));
|
||||
(childAssociation) -> new Pair<>(childAssociation.getChildRef(), childAssociation.getQName().getLocalName()));
|
||||
}
|
||||
|
||||
public Map<NodeRef, Long> getTags(StoreRef storeRef, List<String> parameterIncludes, Pair<String, Boolean> sorting, Collection<String> exactNamesFilter, Collection<String> alikeNamesFilter)
|
||||
{
|
||||
ParameterCheck.mandatory("storeRef", storeRef);
|
||||
Collection<ChildAssociationRef> rootCategories = categoryService.getRootCategories(storeRef, ContentModel.ASPECT_TAGGABLE, exactNamesFilter, alikeNamesFilter);
|
||||
|
||||
Map<String, Long> tagsMap = new TreeMap<>();
|
||||
for (ChildAssociationRef childAssociation : rootCategories)
|
||||
{
|
||||
tagsMap.put(childAssociation.getQName().getLocalName(), 0L);
|
||||
}
|
||||
|
||||
Map<String, Long> tagsByCountMap = new HashMap<>();
|
||||
|
||||
if(parameterIncludes.contains(PARAM_INCLUDE_COUNT))
|
||||
{
|
||||
tagsByCountMap = calculateCount(storeRef);
|
||||
|
||||
for (Map.Entry<String, Long> entry : tagsMap.entrySet()) {
|
||||
entry.setValue(Optional.ofNullable(tagsByCountMap.get(entry.getKey())).orElse(0L));
|
||||
}
|
||||
}
|
||||
|
||||
//check if we should sort results. Can only sort by one parameter, default order is ascending
|
||||
if (sorting != null)
|
||||
{
|
||||
if (sorting.getFirst().equals("tag"))
|
||||
{
|
||||
if (!sorting.getSecond())
|
||||
{
|
||||
Stream<Map.Entry<String,Long>> sortedTags =
|
||||
tagsMap.entrySet().stream()
|
||||
.sorted(Collections.reverseOrder(Map.Entry.comparingByKey()));
|
||||
tagsMap = sortedTags.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new));
|
||||
}
|
||||
else
|
||||
{
|
||||
Stream<Map.Entry<String,Long>> sortedTags =
|
||||
tagsMap.entrySet().stream()
|
||||
.sorted(Map.Entry.comparingByKey());
|
||||
tagsMap = sortedTags.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new));
|
||||
}
|
||||
}
|
||||
else if (sorting.getFirst().equals(PARAM_INCLUDE_COUNT))
|
||||
{
|
||||
if (tagsByCountMap.isEmpty())
|
||||
{
|
||||
throw new QueryException("Tag count should be included when ordering by count");
|
||||
}
|
||||
|
||||
if (!sorting.getSecond())
|
||||
{
|
||||
Stream<Map.Entry<String, Long>> sortedTags =
|
||||
tagsMap.entrySet().stream()
|
||||
.sorted(Collections.reverseOrder(Map.Entry.comparingByValue()));
|
||||
tagsMap = sortedTags.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new));
|
||||
}
|
||||
else
|
||||
{
|
||||
Stream<Map.Entry<String,Long>> sortedTags =
|
||||
tagsMap.entrySet().stream()
|
||||
.sorted(Map.Entry.comparingByValue());
|
||||
tagsMap = sortedTags.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Map<NodeRef, Long> tagNodeRefMap = new LinkedHashMap<>();
|
||||
|
||||
for (Map.Entry<String, Long> entry : tagsMap.entrySet())
|
||||
{
|
||||
tagNodeRefMap.put(getTagNodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, entry.getKey()), entry.getValue());
|
||||
}
|
||||
|
||||
return tagNodeRefMap;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -26,6 +26,7 @@
|
||||
package org.alfresco.service.cmr.search;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
@@ -150,11 +151,21 @@ public interface CategoryService
|
||||
*/
|
||||
@Auditable(parameters = {"storeRef", "aspectName", "pagingRequest", "sortByName", "exactNamesFilter", "alikeNamesFilter"})
|
||||
default PagingResults<ChildAssociationRef> getRootCategories(StoreRef storeRef, QName aspectName, PagingRequest pagingRequest, boolean sortByName,
|
||||
Collection<String> exactNamesFilter, Collection<String> alikeNamesFilter)
|
||||
Collection<String> exactNamesFilter, Collection<String> alikeNamesFilter)
|
||||
{
|
||||
return new EmptyPagingResults<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a collection of the root categories for an aspect/classification supporting multiple name filters.
|
||||
*/
|
||||
@Auditable(parameters = {"storeRef", "aspectName", "exactNamesFilter", "alikeNamesFilter"})
|
||||
default Collection<ChildAssociationRef> getRootCategories(StoreRef storeRef, QName aspectName, Collection<String> exactNamesFilter, Collection<String> alikeNamesFilter)
|
||||
{
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the root categories for an aspect/classification with names that start with filter
|
||||
*
|
||||
|
@@ -28,6 +28,7 @@ package org.alfresco.service.cmr.tagging;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.alfresco.api.AlfrescoPublicApi;
|
||||
import org.alfresco.query.EmptyPagingResults;
|
||||
@@ -94,6 +95,18 @@ public interface TaggingService
|
||||
{
|
||||
return new EmptyPagingResults<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a map of tag NodeRefs and their respective usage count filtered by name and sorted by tag name or count
|
||||
*
|
||||
* @param storeRef
|
||||
* @param parameterIncludes
|
||||
* @param sorting
|
||||
* @param exactNamesFilter
|
||||
* @param alikeNamesFilter
|
||||
* @return
|
||||
*/
|
||||
Map<NodeRef, Long> getTags(StoreRef storeRef, List<String>parameterIncludes, Pair<String, Boolean> sorting, Collection<String> exactNamesFilter, Collection<String> alikeNamesFilter);
|
||||
|
||||
/**
|
||||
* Get all the tags currently available that match the provided filter.
|
||||
@@ -327,8 +340,7 @@ public interface TaggingService
|
||||
*/
|
||||
@NotAuditable
|
||||
Pair<List<String>, Integer> getPagedTags(StoreRef storeRef, String filter, int fromTag, int pageSize);
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Get tagged nodes and count of nodes group by tag name
|
||||
*
|
||||
@@ -362,6 +374,13 @@ public interface TaggingService
|
||||
{
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param storeRef
|
||||
* @return a map with each tag name and its usage count
|
||||
*/
|
||||
Map<String, Long> calculateCount(StoreRef storeRef);
|
||||
}
|
||||
|
||||
|
||||
|
Reference in New Issue
Block a user