ACS-4923 Add support for include=count on GET and PUT tag. (#1880)

This commit is contained in:
Tom Page
2023-04-17 10:12:09 +01:00
committed by GitHub
parent 6be773ba18
commit b1466915af
9 changed files with 205 additions and 63 deletions

View File

@@ -26,7 +26,7 @@ public class GetTagTests extends TagsDataPrep
@TestRail(section = { TestGroup.REST_API, TestGroup.TAGS }, executionType = ExecutionType.REGRESSION, description = "Verify admin user gets tag using REST API and status code is OK (200)") @TestRail(section = { TestGroup.REST_API, TestGroup.TAGS }, executionType = ExecutionType.REGRESSION, description = "Verify admin user gets tag using REST API and status code is OK (200)")
@Test(groups = { TestGroup.REST_API, TestGroup.TAGS, TestGroup.REGRESSION }) @Test(groups = { TestGroup.REST_API, TestGroup.TAGS, TestGroup.REGRESSION })
public void adminIsAbleToGetTag() throws Exception public void adminIsAbleToGetTag()
{ {
RestTagModel returnedTag = restClient.authenticateUser(adminUserModel).withCoreAPI().getTag(documentTag); RestTagModel returnedTag = restClient.authenticateUser(adminUserModel).withCoreAPI().getTag(documentTag);
restClient.assertStatusCodeIs(HttpStatus.OK); restClient.assertStatusCodeIs(HttpStatus.OK);
@@ -35,7 +35,7 @@ public class GetTagTests extends TagsDataPrep
@TestRail(section = { TestGroup.REST_API, TestGroup.TAGS }, executionType = ExecutionType.SANITY, description = "Verify user with Manager role gets tag using REST API and status code is OK (200)") @TestRail(section = { TestGroup.REST_API, TestGroup.TAGS }, executionType = ExecutionType.SANITY, description = "Verify user with Manager role gets tag using REST API and status code is OK (200)")
@Test(groups = { TestGroup.REST_API, TestGroup.TAGS, TestGroup.SANITY }) @Test(groups = { TestGroup.REST_API, TestGroup.TAGS, TestGroup.SANITY })
public void userWithManagerRoleIsAbleToGetTag() throws Exception public void userWithManagerRoleIsAbleToGetTag()
{ {
restClient.authenticateUser(usersWithRoles.getOneUserWithRole(UserRole.SiteManager)); restClient.authenticateUser(usersWithRoles.getOneUserWithRole(UserRole.SiteManager));
@@ -47,7 +47,7 @@ public class GetTagTests extends TagsDataPrep
@TestRail(section = { TestGroup.REST_API, TestGroup.TAGS }, executionType = ExecutionType.REGRESSION, description = "Verify user with Collaborator role gets tag using REST API and status code is OK (200)") @TestRail(section = { TestGroup.REST_API, TestGroup.TAGS }, executionType = ExecutionType.REGRESSION, description = "Verify user with Collaborator role gets tag using REST API and status code is OK (200)")
@Test(groups = { TestGroup.REST_API, TestGroup.TAGS, TestGroup.REGRESSION }) @Test(groups = { TestGroup.REST_API, TestGroup.TAGS, TestGroup.REGRESSION })
public void userWithCollaboratorRoleIsAbleToGetTag() throws Exception public void userWithCollaboratorRoleIsAbleToGetTag()
{ {
restClient.authenticateUser(usersWithRoles.getOneUserWithRole(UserRole.SiteCollaborator)); restClient.authenticateUser(usersWithRoles.getOneUserWithRole(UserRole.SiteCollaborator));
RestTagModel returnedTag = restClient.withCoreAPI().getTag(documentTag); RestTagModel returnedTag = restClient.withCoreAPI().getTag(documentTag);
@@ -57,7 +57,7 @@ public class GetTagTests extends TagsDataPrep
@TestRail(section = { TestGroup.REST_API, TestGroup.TAGS }, executionType = ExecutionType.REGRESSION, description = "Verify user with Contributor role gets tag using REST API and status code is OK (200)") @TestRail(section = { TestGroup.REST_API, TestGroup.TAGS }, executionType = ExecutionType.REGRESSION, description = "Verify user with Contributor role gets tag using REST API and status code is OK (200)")
@Test(groups = { TestGroup.REST_API, TestGroup.TAGS, TestGroup.REGRESSION }) @Test(groups = { TestGroup.REST_API, TestGroup.TAGS, TestGroup.REGRESSION })
public void userWithContributorRoleIsAbleToGetTag() throws Exception public void userWithContributorRoleIsAbleToGetTag()
{ {
restClient.authenticateUser(usersWithRoles.getOneUserWithRole(UserRole.SiteContributor)); restClient.authenticateUser(usersWithRoles.getOneUserWithRole(UserRole.SiteContributor));
RestTagModel returnedTag = restClient.withCoreAPI().getTag(documentTag); RestTagModel returnedTag = restClient.withCoreAPI().getTag(documentTag);
@@ -67,7 +67,7 @@ public class GetTagTests extends TagsDataPrep
@TestRail(section = { TestGroup.REST_API, TestGroup.TAGS }, executionType = ExecutionType.REGRESSION, description = "Verify user with Consumer role gets tag using REST API and status code is OK (200)") @TestRail(section = { TestGroup.REST_API, TestGroup.TAGS }, executionType = ExecutionType.REGRESSION, description = "Verify user with Consumer role gets tag using REST API and status code is OK (200)")
@Test(groups = { TestGroup.REST_API, TestGroup.TAGS, TestGroup.REGRESSION }) @Test(groups = { TestGroup.REST_API, TestGroup.TAGS, TestGroup.REGRESSION })
public void userWithConsumerRoleIsAbleToGetTag() throws Exception public void userWithConsumerRoleIsAbleToGetTag()
{ {
restClient.authenticateUser(usersWithRoles.getOneUserWithRole(UserRole.SiteConsumer)); restClient.authenticateUser(usersWithRoles.getOneUserWithRole(UserRole.SiteConsumer));
RestTagModel returnedTag = restClient.withCoreAPI().getTag(documentTag); RestTagModel returnedTag = restClient.withCoreAPI().getTag(documentTag);
@@ -78,7 +78,7 @@ public class GetTagTests extends TagsDataPrep
@TestRail(section = { TestGroup.REST_API, TestGroup.TAGS }, executionType = ExecutionType.SANITY, description = "Verify Manager user gets status code 401 if authentication call fails") @TestRail(section = { TestGroup.REST_API, TestGroup.TAGS }, executionType = ExecutionType.SANITY, description = "Verify Manager user gets status code 401 if authentication call fails")
@Test(groups = { TestGroup.REST_API, TestGroup.TAGS, TestGroup.SANITY }) @Test(groups = { TestGroup.REST_API, TestGroup.TAGS, TestGroup.SANITY })
// @Bug(id="MNT-16904", description = "It fails only on environment with tenants") // @Bug(id="MNT-16904", description = "It fails only on environment with tenants")
public void managerIsNotAbleToGetTagIfAuthenticationFails() throws Exception public void managerIsNotAbleToGetTagIfAuthenticationFails()
{ {
UserModel managerUser = dataUser.usingAdmin().createRandomTestUser(); UserModel managerUser = dataUser.usingAdmin().createRandomTestUser();
String managerPassword = managerUser.getPassword(); String managerPassword = managerUser.getPassword();
@@ -92,7 +92,7 @@ public class GetTagTests extends TagsDataPrep
@TestRail(section = { TestGroup.REST_API, TestGroup.TAGS }, executionType = ExecutionType.REGRESSION, @TestRail(section = { TestGroup.REST_API, TestGroup.TAGS }, executionType = ExecutionType.REGRESSION,
description = "Verify that if tag id is invalid status code returned is 400") description = "Verify that if tag id is invalid status code returned is 400")
@Test(groups = { TestGroup.REST_API, TestGroup.TAGS, TestGroup.REGRESSION }) @Test(groups = { TestGroup.REST_API, TestGroup.TAGS, TestGroup.REGRESSION })
public void invalidTagIdTest() throws Exception public void invalidTagIdTest()
{ {
String tagId = documentTag.getId(); String tagId = documentTag.getId();
documentTag.setId("random_tag_value"); documentTag.setId("random_tag_value");
@@ -104,7 +104,7 @@ public class GetTagTests extends TagsDataPrep
@TestRail(section = { TestGroup.REST_API, TestGroup.TAGS }, @TestRail(section = { TestGroup.REST_API, TestGroup.TAGS },
executionType = ExecutionType.REGRESSION, description = "Check that properties filter is applied when getting tag using Manager user.") executionType = ExecutionType.REGRESSION, description = "Check that properties filter is applied when getting tag using Manager user.")
@Test(groups = { TestGroup.REST_API, TestGroup.TAGS, TestGroup.REGRESSION }) @Test(groups = { TestGroup.REST_API, TestGroup.TAGS, TestGroup.REGRESSION })
public void checkPropertiesFilterIsApplied() throws Exception public void checkPropertiesFilterIsApplied()
{ {
RestTagModel returnedTag = restClient.authenticateUser(usersWithRoles.getOneUserWithRole(UserRole.SiteManager)) RestTagModel returnedTag = restClient.authenticateUser(usersWithRoles.getOneUserWithRole(UserRole.SiteManager))
.withParams("properties=id,tag").withCoreAPI().getTag(documentTag); .withParams("properties=id,tag").withCoreAPI().getTag(documentTag);
@@ -117,7 +117,7 @@ public class GetTagTests extends TagsDataPrep
@TestRail(section = { TestGroup.REST_API, TestGroup.TAGS }, @TestRail(section = { TestGroup.REST_API, TestGroup.TAGS },
executionType = ExecutionType.REGRESSION, description = "Check that Manager user can get tag of a folder.") executionType = ExecutionType.REGRESSION, description = "Check that Manager user can get tag of a folder.")
@Test(groups = { TestGroup.REST_API, TestGroup.TAGS, TestGroup.REGRESSION }) @Test(groups = { TestGroup.REST_API, TestGroup.TAGS, TestGroup.REGRESSION })
public void getTagOfAFolder() throws Exception public void getTagOfAFolder()
{ {
RestTagModel returnedTag = restClient.authenticateUser(usersWithRoles.getOneUserWithRole(UserRole.SiteManager)) RestTagModel returnedTag = restClient.authenticateUser(usersWithRoles.getOneUserWithRole(UserRole.SiteManager))
.withCoreAPI().getTag(folderTag); .withCoreAPI().getTag(folderTag);
@@ -128,7 +128,7 @@ public class GetTagTests extends TagsDataPrep
@TestRail(section = { TestGroup.REST_API, TestGroup.TAGS }, @TestRail(section = { TestGroup.REST_API, TestGroup.TAGS },
executionType = ExecutionType.REGRESSION, description = "Check default error model schema. Use invalid skipCount parameter.") executionType = ExecutionType.REGRESSION, description = "Check default error model schema. Use invalid skipCount parameter.")
@Test(groups = { TestGroup.REST_API, TestGroup.TAGS, TestGroup.REGRESSION }) @Test(groups = { TestGroup.REST_API, TestGroup.TAGS, TestGroup.REGRESSION })
public void checkDefaultErrorModelSchema() throws Exception public void checkDefaultErrorModelSchema()
{ {
restClient.authenticateUser(usersWithRoles.getOneUserWithRole(UserRole.SiteManager)) restClient.authenticateUser(usersWithRoles.getOneUserWithRole(UserRole.SiteManager))
.withParams("skipCount=abc").withCoreAPI().getTag(documentTag); .withParams("skipCount=abc").withCoreAPI().getTag(documentTag);
@@ -140,13 +140,14 @@ public class GetTagTests extends TagsDataPrep
} }
/** /**
* Verify that count field is not present for searched tag. * Verify that count field is not present for searched tag when not requested.
*/ */
@Test(groups = {TestGroup.REST_API, TestGroup.TAGS, TestGroup.REGRESSION}) @Test(groups = {TestGroup.REST_API, TestGroup.TAGS, TestGroup.REGRESSION})
public void testGetTag_notIncludingCount() public void testGetTag_notIncludingCount()
{ {
STEP("Create single tag as admin"); STEP("Create single tag as admin");
final RestTagModel tagModel = createTagModelWithName(getRandomName(TAG_NAME_PREFIX).toLowerCase()); String tagName = getRandomName(TAG_NAME_PREFIX).toLowerCase();
final RestTagModel tagModel = createTagModelWithName(tagName);
final RestTagModel createdTag = restClient.authenticateUser(adminUserModel).withCoreAPI().createSingleTag(tagModel); final RestTagModel createdTag = restClient.authenticateUser(adminUserModel).withCoreAPI().createSingleTag(tagModel);
restClient.assertStatusCodeIs(CREATED); restClient.assertStatusCodeIs(CREATED);
@@ -155,8 +156,26 @@ public class GetTagTests extends TagsDataPrep
final RestTagModel searchedTag = restClient.withCoreAPI().getTag(createdTag); final RestTagModel searchedTag = restClient.withCoreAPI().getTag(createdTag);
restClient.assertStatusCodeIs(OK); restClient.assertStatusCodeIs(OK);
searchedTag.assertThat().field(FIELD_TAG).is(tagModel.getTag()) RestTagModel expected = RestTagModel.builder().id(createdTag.getId()).tag(tagName).create();
.assertThat().field(FIELD_ID).isNotEmpty() searchedTag.assertThat().isEqualTo(expected);
.assertThat().field(FIELD_COUNT).isNull(); }
/**
* Check that the count field can be included.
*/
@Test (groups = { TestGroup.REST_API, TestGroup.TAGS, TestGroup.REGRESSION })
public void testGetTag_includeCount()
{
STEP("Create unused tag as admin");
String tagName = getRandomName(TAG_NAME_PREFIX).toLowerCase();
RestTagModel tagModel = createTagModelWithName(tagName);
RestTagModel createdTag = restClient.authenticateUser(adminUserModel).withCoreAPI().createSingleTag(tagModel);
STEP("Get a single tag with the count field");
RestTagModel searchedTag = restClient.withCoreAPI().include("count").getTag(createdTag);
restClient.assertStatusCodeIs(OK);
RestTagModel expected = RestTagModel.builder().id(createdTag.getId()).tag(tagName).count(0).create();
searchedTag.assertThat().isEqualTo(expected);
} }
} }

View File

@@ -305,7 +305,24 @@ public class UpdateTagTests extends TagsDataPrep
returnedModel = restClient.authenticateUser(adminUserModel).withCoreAPI().usingTag(orphanTag).update(newTagName); returnedModel = restClient.authenticateUser(adminUserModel).withCoreAPI().usingTag(orphanTag).update(newTagName);
restClient.assertStatusCodeIs(HttpStatus.OK); restClient.assertStatusCodeIs(HttpStatus.OK);
returnedModel.assertThat().field("tag").is(newTagName); RestTagModel expected = RestTagModel.builder().id(orphanTag.getId()).tag(newTagName).create();
returnedModel.assertThat().field("id").isNotNull(); returnedModel.assertThat().isEqualTo(expected);
}
@Test (groups = { TestGroup.REST_API, TestGroup.TAGS })
public void canUpdateTagAndGetCount()
{
STEP("Create an orphaned tag");
String tagName = RandomData.getRandomName("tag").toLowerCase();
RestTagModel createdTag = RestTagModel.builder().tag(tagName).create();
RestTagModel tag = restClient.authenticateUser(adminUserModel).withCoreAPI().createSingleTag(createdTag);
STEP("Update tag and request the count field");
String newTagName = RandomData.getRandomName("new").toLowerCase();
returnedModel = restClient.authenticateUser(adminUserModel).withCoreAPI().include("count").usingTag(tag).update(newTagName);
restClient.assertStatusCodeIs(HttpStatus.OK);
RestTagModel expected = RestTagModel.builder().id(tag.getId()).tag(newTagName).count(0).create();
returnedModel.assertThat().isEqualTo(expected);
} }
} }

View File

@@ -38,10 +38,10 @@ import org.alfresco.service.cmr.repository.StoreRef;
public interface Tags public interface Tags
{ {
List<Tag> addTags(String nodeId, List<Tag> tags, Parameters parameters); List<Tag> addTags(String nodeId, List<Tag> tags, Parameters parameters);
Tag getTag(StoreRef storeRef, String tagId); Tag getTag(StoreRef storeRef, String tagId, Parameters parameters);
void deleteTag(String nodeId, String tagId); void deleteTag(String nodeId, String tagId);
CollectionWithPagingInfo<Tag> getTags(StoreRef storeRef, Parameters params); CollectionWithPagingInfo<Tag> getTags(StoreRef storeRef, Parameters params);
Tag changeTag(StoreRef storeRef, String tagId, Tag tag); Tag changeTag(StoreRef storeRef, String tagId, Tag tag, Parameters parameters);
CollectionWithPagingInfo<Tag> getTags(String nodeId, Parameters params); CollectionWithPagingInfo<Tag> getTags(String nodeId, Parameters params);
@Experimental @Experimental

View File

@@ -30,6 +30,7 @@ import static java.util.stream.Collectors.toList;
import static org.alfresco.rest.antlr.WhereClauseParser.EQUALS; import static org.alfresco.rest.antlr.WhereClauseParser.EQUALS;
import static org.alfresco.rest.antlr.WhereClauseParser.IN; import static org.alfresco.rest.antlr.WhereClauseParser.IN;
import static org.alfresco.rest.antlr.WhereClauseParser.MATCHES; import static org.alfresco.rest.antlr.WhereClauseParser.MATCHES;
import static org.alfresco.service.cmr.repository.StoreRef.STORE_REF_WORKSPACE_SPACESSTORE;
import static org.alfresco.service.cmr.tagging.TaggingService.TAG_ROOT_NODE_REF; import static org.alfresco.service.cmr.tagging.TaggingService.TAG_ROOT_NODE_REF;
import java.util.ArrayList; import java.util.ArrayList;
@@ -80,7 +81,7 @@ import org.apache.commons.collections.CollectionUtils;
*/ */
public class TagsImpl implements Tags public class TagsImpl implements Tags
{ {
private static final String PARAM_INCLUDE_COUNT = "count"; public static final String PARAM_INCLUDE_COUNT = "count";
private static final String PARAM_WHERE_TAG = "tag"; private static final String PARAM_WHERE_TAG = "tag";
static final String NOT_A_VALID_TAG = "An invalid parameter has been supplied"; static final String NOT_A_VALID_TAG = "An invalid parameter has been supplied";
static final String NO_PERMISSION_TO_MANAGE_A_TAG = "Current user does not have permission to manage a tag"; static final String NO_PERMISSION_TO_MANAGE_A_TAG = "Current user does not have permission to manage a tag";
@@ -129,12 +130,13 @@ public class TagsImpl implements Tags
List<Pair<String, NodeRef>> tagNodeRefs = taggingService.addTags(nodeRef, tagValues); List<Pair<String, NodeRef>> tagNodeRefs = taggingService.addTags(nodeRef, tagValues);
List<Tag> ret = new ArrayList<>(tags.size()); List<Tag> ret = new ArrayList<>(tags.size());
List<Pair<String, Integer>> tagsCountPairList = taggingService.findTaggedNodesAndCountByTagName(nodeRef.getStoreRef()); List<Pair<String, Integer>> tagsCountPairList = taggingService.findTaggedNodesAndCountByTagName(nodeRef.getStoreRef());
Map<String, Integer> tagsCountMap = tagsCountPairList.stream().collect(Collectors.toMap(Pair::getFirst,Pair::getSecond)); Map<String, Long> tagsCountMap = tagsCountPairList.stream().collect(Collectors.toMap(Pair::getFirst, pair -> Long.valueOf(pair.getSecond())));
for (Pair<String, NodeRef> pair : tagNodeRefs) for (Pair<String, NodeRef> pair : tagNodeRefs)
{ {
Tag createdTag = new Tag(pair.getSecond(), pair.getFirst()); Tag createdTag = new Tag(pair.getSecond(), pair.getFirst());
if (parameters.getInclude().contains(PARAM_INCLUDE_COUNT)) { if (parameters.getInclude().contains(PARAM_INCLUDE_COUNT))
createdTag.setCount(Optional.ofNullable(tagsCountMap.get(createdTag.getTag())).orElse(0) + 1); {
createdTag.setCount(Optional.ofNullable(tagsCountMap.get(createdTag.getTag())).orElse(0L) + 1);
} }
ret.add(createdTag); ret.add(createdTag);
} }
@@ -149,7 +151,7 @@ public class TagsImpl implements Tags
public void deleteTag(String nodeId, String tagId) public void deleteTag(String nodeId, String tagId)
{ {
NodeRef nodeRef = nodes.validateNode(nodeId); NodeRef nodeRef = nodes.validateNode(nodeId);
getTag(tagId); getTag(STORE_REF_WORKSPACE_SPACESSTORE, tagId, null);
NodeRef existingTagNodeRef = validateTag(tagId); NodeRef existingTagNodeRef = validateTag(tagId);
String tagValue = taggingService.getTagName(existingTagNodeRef); String tagValue = taggingService.getTagName(existingTagNodeRef);
taggingService.removeTag(nodeRef, tagValue); taggingService.removeTag(nodeRef, tagValue);
@@ -182,15 +184,15 @@ public class TagsImpl implements Tags
if (params.getInclude().contains(PARAM_INCLUDE_COUNT)) if (params.getInclude().contains(PARAM_INCLUDE_COUNT))
{ {
List<Pair<String, Integer>> tagsByCount = taggingService.findTaggedNodesAndCountByTagName(storeRef); List<Pair<String, Integer>> tagsByCount = taggingService.findTaggedNodesAndCountByTagName(storeRef);
Map<String, Integer> tagsByCountMap = new HashMap<>(); Map<String, Long> tagsByCountMap = new HashMap<>();
if (tagsByCount != null) if (tagsByCount != null)
{ {
for (Pair<String, Integer> tagByCountElem : tagsByCount) for (Pair<String, Integer> tagByCountElem : tagsByCount)
{ {
tagsByCountMap.put(tagByCountElem.getFirst(), tagByCountElem.getSecond()); tagsByCountMap.put(tagByCountElem.getFirst(), Long.valueOf(tagByCountElem.getSecond()));
} }
} }
tags.forEach(tag -> tag.setCount(Optional.ofNullable(tagsByCountMap.get(tag.getTag())).orElse(0))); tags.forEach(tag -> tag.setCount(Optional.ofNullable(tagsByCountMap.get(tag.getTag())).orElse(0L)));
} }
return CollectionWithPagingInfo.asPaged(paging, tags, results.hasMoreItems(), totalItems); return CollectionWithPagingInfo.asPaged(paging, tags, results.hasMoreItems(), totalItems);
@@ -208,15 +210,35 @@ public class TagsImpl implements Tags
return checkTagRootAsNodePrimaryParent(tagId, tagNodeRef); return checkTagRootAsNodePrimaryParent(tagId, tagNodeRef);
} }
public Tag changeTag(StoreRef storeRef, String tagId, Tag tag) /**
* Find the number of times the given tag is used (if requested).
*
* @param storeRef The store the tag is in.
* @param tagName The name of the tag.
* @param parameters The request parameters object containing the includes parameter.
* @return The number of times the tag is applied, or null if "count" wasn't in the include parameter.
*/
private Long findCountIfRequested(StoreRef storeRef, String tagName, Parameters parameters)
{
Long count = null;
if (parameters != null && parameters.getInclude() != null && parameters.getInclude().contains(PARAM_INCLUDE_COUNT))
{
count = taggingService.findCountByTagName(storeRef, tagName);
}
return count;
}
@Override
public Tag changeTag(StoreRef storeRef, String tagId, Tag tag, Parameters parameters)
{ {
try try
{ {
NodeRef existingTagNodeRef = validateTag(storeRef, tagId); NodeRef existingTagNodeRef = validateTag(storeRef, tagId);
String existingTagName = taggingService.getTagName(existingTagNodeRef); String existingTagName = taggingService.getTagName(existingTagNodeRef);
String newTagName = tag.getTag(); Long count = findCountIfRequested(storeRef, existingTagName, parameters);
NodeRef newTagNodeRef = taggingService.changeTag(storeRef, existingTagName, newTagName); String newTagName = tag.getTag();
return new Tag(newTagNodeRef, newTagName); NodeRef newTagNodeRef = taggingService.changeTag(storeRef, existingTagName, newTagName);
return Tag.builder().nodeRef(newTagNodeRef).tag(newTagName).count(count).create();
} }
catch(NonExistentTagException e) catch(NonExistentTagException e)
{ {
@@ -232,18 +254,16 @@ public class TagsImpl implements Tags
} }
} }
public Tag getTag(String tagId) @Override
{ public Tag getTag(StoreRef storeRef, String tagId, Parameters parameters)
return getTag(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, tagId);
}
public Tag getTag(StoreRef storeRef, String tagId)
{ {
NodeRef tagNodeRef = validateTag(storeRef, tagId); NodeRef tagNodeRef = validateTag(storeRef, tagId);
String tagValue = taggingService.getTagName(tagNodeRef); String tagName = taggingService.getTagName(tagNodeRef);
return new Tag(tagNodeRef, tagValue); Long count = findCountIfRequested(storeRef, tagName, parameters);
return Tag.builder().nodeRef(tagNodeRef).tag(tagName).count(count).create();
} }
@Override
public CollectionWithPagingInfo<Tag> getTags(String nodeId, Parameters params) public CollectionWithPagingInfo<Tag> getTags(String nodeId, Parameters params)
{ {
NodeRef nodeRef = nodes.validateOrLookupNode(nodeId); NodeRef nodeRef = nodes.validateOrLookupNode(nodeId);
@@ -280,7 +300,7 @@ public class TagsImpl implements Tags
.peek(tag -> { .peek(tag -> {
if (parameters.getInclude().contains(PARAM_INCLUDE_COUNT)) if (parameters.getInclude().contains(PARAM_INCLUDE_COUNT))
{ {
tag.setCount(0); tag.setCount(0L);
} }
}).collect(toList()); }).collect(toList());
} }

View File

@@ -42,7 +42,7 @@ public class Tag implements Comparable<Tag>
{ {
private NodeRef nodeRef; private NodeRef nodeRef;
private String tag; private String tag;
private Integer count; private Long count;
public Tag() public Tag()
{ {
@@ -76,13 +76,13 @@ public class Tag implements Comparable<Tag>
this.tag = Optional.ofNullable(tag).map(String::toLowerCase).orElse(null); this.tag = Optional.ofNullable(tag).map(String::toLowerCase).orElse(null);
} }
public Integer getCount() public Long getCount()
{ {
return count; return count;
} }
public void setCount(Integer count) public void setCount(Long count)
{ {
this.count = count; this.count = count;
} }
@@ -133,7 +133,7 @@ public class Tag implements Comparable<Tag>
{ {
private NodeRef nodeRef; private NodeRef nodeRef;
private String tag; private String tag;
private Integer count; private Long count;
public Builder nodeRef(NodeRef nodeRef) public Builder nodeRef(NodeRef nodeRef)
{ {
@@ -147,7 +147,7 @@ public class Tag implements Comparable<Tag>
return this; return this;
} }
public Builder count(Integer count) public Builder count(Long count)
{ {
this.count = count; this.count = count;
return this; return this;

View File

@@ -25,8 +25,8 @@
*/ */
package org.alfresco.rest.api.tags; package org.alfresco.rest.api.tags;
import javax.servlet.http.HttpServletResponse;
import java.util.List; import java.util.List;
import javax.servlet.http.HttpServletResponse;
import org.alfresco.rest.api.Tags; import org.alfresco.rest.api.Tags;
import org.alfresco.rest.api.model.Tag; import org.alfresco.rest.api.model.Tag;
@@ -73,13 +73,13 @@ public class TagsEntityResource implements EntityResourceAction.Read<Tag>,
@WebApiDescription(title="Updates a tag by unique Id") @WebApiDescription(title="Updates a tag by unique Id")
public Tag update(String id, Tag entity, Parameters parameters) public Tag update(String id, Tag entity, Parameters parameters)
{ {
return tags.changeTag(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, id, entity); return tags.changeTag(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, id, entity, parameters);
} }
@Override @Override
public Tag readById(String id, Parameters parameters) throws EntityNotFoundException public Tag readById(String id, Parameters parameters) throws EntityNotFoundException
{ {
return tags.getTag(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, id); return tags.getTag(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, id, parameters);
} }
/** /**

View File

@@ -29,6 +29,7 @@ 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.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.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.alfresco.service.cmr.repository.StoreRef.STORE_REF_WORKSPACE_SPACESSTORE;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.catchThrowable; import static org.assertj.core.api.Assertions.catchThrowable;
@@ -115,6 +116,7 @@ public class TagsImplTest
given(nodesMock.validateNode(STORE_REF_WORKSPACE_SPACESSTORE, TAG_ID)).willReturn(TAG_NODE_REF); given(nodesMock.validateNode(STORE_REF_WORKSPACE_SPACESSTORE, TAG_ID)).willReturn(TAG_NODE_REF);
given(taggingServiceMock.getTagName(TAG_NODE_REF)).willReturn(TAG_NAME); given(taggingServiceMock.getTagName(TAG_NODE_REF)).willReturn(TAG_NAME);
given(nodeServiceMock.getPrimaryParent(TAG_NODE_REF)).willReturn(primaryParentMock); given(nodeServiceMock.getPrimaryParent(TAG_NODE_REF)).willReturn(primaryParentMock);
given(primaryParentMock.getParentRef()).willReturn(TAG_PARENT_NODE_REF);
} }
@Test @Test
@@ -146,7 +148,7 @@ public class TagsImplTest
then(taggingServiceMock).should().findTaggedNodesAndCountByTagName(STORE_REF_WORKSPACE_SPACESSTORE); then(taggingServiceMock).should().findTaggedNodesAndCountByTagName(STORE_REF_WORKSPACE_SPACESSTORE);
final List<Tag> expectedTags = createTagsWithNodeRefs(List.of(TAG_NAME)).stream() final List<Tag> expectedTags = createTagsWithNodeRefs(List.of(TAG_NAME)).stream()
.peek(tag -> tag.setCount(0)) .peek(tag -> tag.setCount(0L))
.collect(toList()); .collect(toList());
assertEquals(expectedTags, actualTags.getCollection()); assertEquals(expectedTags, actualTags.getCollection());
} }
@@ -170,8 +172,8 @@ public class TagsImplTest
final CollectionWithPagingInfo<Tag> actualTags = objectUnderTest.getTags(STORE_REF_WORKSPACE_SPACESSTORE, parametersMock); final CollectionWithPagingInfo<Tag> actualTags = objectUnderTest.getTags(STORE_REF_WORKSPACE_SPACESSTORE, parametersMock);
then(taggingServiceMock).should().findTaggedNodesAndCountByTagName(STORE_REF_WORKSPACE_SPACESSTORE); then(taggingServiceMock).should().findTaggedNodesAndCountByTagName(STORE_REF_WORKSPACE_SPACESSTORE);
final List<Tag> expectedTags = List.of(Tag.builder().tag("taga").nodeRef(tagNodeA).count(5).create(), final List<Tag> expectedTags = List.of(Tag.builder().tag("taga").nodeRef(tagNodeA).count(5L).create(),
Tag.builder().tag("tagb").nodeRef(tagNodeB).count(0).create()); Tag.builder().tag("tagb").nodeRef(tagNodeB).count(0L).create());
assertEquals(expectedTags, actualTags.getCollection()); assertEquals(expectedTags, actualTags.getCollection());
} }
@@ -266,7 +268,6 @@ public class TagsImplTest
public void testDeleteTagById() public void testDeleteTagById()
{ {
//when //when
given(primaryParentMock.getParentRef()).willReturn(TAG_PARENT_NODE_REF);
objectUnderTest.deleteTagById(STORE_REF_WORKSPACE_SPACESSTORE, TAG_ID); objectUnderTest.deleteTagById(STORE_REF_WORKSPACE_SPACESSTORE, TAG_ID);
then(authorityServiceMock).should().hasAdminAuthority(); then(authorityServiceMock).should().hasAdminAuthority();
@@ -425,7 +426,7 @@ public class TagsImplTest
final List<Tag> actualCreatedTags = objectUnderTest.createTags(tagsToCreate, parametersMock); final List<Tag> actualCreatedTags = objectUnderTest.createTags(tagsToCreate, parametersMock);
final List<Tag> expectedTags = createTagsWithNodeRefs(tagNames).stream() final List<Tag> expectedTags = createTagsWithNodeRefs(tagNames).stream()
.peek(tag -> tag.setCount(0)) .peek(tag -> tag.setCount(0L))
.collect(toList()); .collect(toList());
assertThat(actualCreatedTags) assertThat(actualCreatedTags)
.isNotNull() .isNotNull()
@@ -435,8 +436,8 @@ public class TagsImplTest
@Test(expected = EntityNotFoundException.class) @Test(expected = EntityNotFoundException.class)
public void testGetTagByIdNotFoundValidation() public void testGetTagByIdNotFoundValidation()
{ {
given(primaryParentMock.getParentRef()).willReturn(TAG_NODE_REF); given(nodesMock.validateNode(STORE_REF_WORKSPACE_SPACESSTORE, TAG_ID)).willThrow(EntityNotFoundException.class);
objectUnderTest.getTag(STORE_REF_WORKSPACE_SPACESSTORE,TAG_ID); objectUnderTest.getTag(STORE_REF_WORKSPACE_SPACESSTORE, TAG_ID, null);
then(nodeServiceMock).shouldHaveNoInteractions(); then(nodeServiceMock).shouldHaveNoInteractions();
then(nodesMock).should().validateNode(STORE_REF_WORKSPACE_SPACESSTORE, TAG_ID); then(nodesMock).should().validateNode(STORE_REF_WORKSPACE_SPACESSTORE, TAG_ID);
then(nodesMock).shouldHaveNoMoreInteractions(); then(nodesMock).shouldHaveNoMoreInteractions();
@@ -459,8 +460,8 @@ public class TagsImplTest
List<Tag> actual = objectUnderTest.addTags(CONTENT_NODE_ID, tags, parametersMock); List<Tag> actual = objectUnderTest.addTags(CONTENT_NODE_ID, tags, parametersMock);
final List<Tag> expected = List.of(Tag.builder().tag("taga").nodeRef(tagNodeA).count(5).create(), final List<Tag> expected = List.of(Tag.builder().tag("taga").nodeRef(tagNodeA).count(5L).create(),
Tag.builder().tag("tagb").nodeRef(tagNodeB).count(1).create()); Tag.builder().tag("tagb").nodeRef(tagNodeB).count(1L).create());
assertEquals("Unexpected tags returned.", expected, actual); assertEquals("Unexpected tags returned.", expected, actual);
} }
@@ -508,6 +509,53 @@ public class TagsImplTest
objectUnderTest.getTags(CONTENT_NODE_ID, parametersMock); objectUnderTest.getTags(CONTENT_NODE_ID, parametersMock);
} }
@Test
public void testChangeTag()
{
Tag suppliedTag = Tag.builder().tag("new-name").create();
given(taggingServiceMock.changeTag(STORE_REF_WORKSPACE_SPACESSTORE, TAG_NAME, "new-name")).willReturn(TAG_NODE_REF);
Tag tag = objectUnderTest.changeTag(STORE_REF_WORKSPACE_SPACESSTORE, TAG_ID, suppliedTag, parametersMock);
Tag expected = Tag.builder().nodeRef(TAG_NODE_REF).tag("new-name").create();
assertEquals("Unexpected return value", expected, tag);
}
@Test
public void testChangeTagAndGetCount()
{
Tag suppliedTag = Tag.builder().tag("new-name").create();
given(taggingServiceMock.changeTag(STORE_REF_WORKSPACE_SPACESSTORE, TAG_NAME, "new-name")).willReturn(TAG_NODE_REF);
given(parametersMock.getInclude()).willReturn(List.of(PARAM_INCLUDE_COUNT));
given(taggingServiceMock.findCountByTagName(STORE_REF_WORKSPACE_SPACESSTORE, TAG_NAME)).willReturn(3L);
Tag tag = objectUnderTest.changeTag(STORE_REF_WORKSPACE_SPACESSTORE, TAG_ID, suppliedTag, parametersMock);
Tag expected = Tag.builder().nodeRef(TAG_NODE_REF).tag("new-name").count(3L).create();
assertEquals("Unexpected return value", expected, tag);
}
@Test
public void testGetTag()
{
Tag tag = objectUnderTest.getTag(STORE_REF_WORKSPACE_SPACESSTORE, TAG_ID, parametersMock);
Tag expected = Tag.builder().nodeRef(TAG_NODE_REF).tag(TAG_NAME).create();
assertEquals("Unexpected tag returned", expected, tag);
}
@Test
public void testGetTagWithCount()
{
given(parametersMock.getInclude()).willReturn(List.of(PARAM_INCLUDE_COUNT));
given(taggingServiceMock.findCountByTagName(STORE_REF_WORKSPACE_SPACESSTORE, TAG_NAME)).willReturn(0L);
Tag tag = objectUnderTest.getTag(STORE_REF_WORKSPACE_SPACESSTORE, TAG_ID, parametersMock);
Tag expected = Tag.builder().nodeRef(TAG_NODE_REF).tag(TAG_NAME).count(0L).create();
assertEquals("Unexpected tag returned", expected, tag);
}
private static List<Pair<String, NodeRef>> createTagAndNodeRefPairs(final List<String> tagNames) private static List<Pair<String, NodeRef>> createTagAndNodeRefPairs(final List<String> tagNames)
{ {
return tagNames.stream() return tagNames.stream()

View File

@@ -27,8 +27,10 @@ package org.alfresco.repo.tagging;
import static java.util.Collections.emptyMap; import static java.util.Collections.emptyMap;
import static org.alfresco.model.ContentModel.ASPECT_WORKING_COPY;
import static org.alfresco.model.ContentModel.ASSOC_SUBCATEGORIES; import static org.alfresco.model.ContentModel.ASSOC_SUBCATEGORIES;
import static org.alfresco.model.ContentModel.PROP_NAME; import static org.alfresco.model.ContentModel.PROP_NAME;
import static org.alfresco.service.cmr.search.SearchService.LANGUAGE_LUCENE;
import static org.alfresco.service.namespace.NamespaceService.CONTENT_MODEL_1_0_URI; import static org.alfresco.service.namespace.NamespaceService.CONTENT_MODEL_1_0_URI;
import java.io.BufferedReader; import java.io.BufferedReader;
@@ -333,7 +335,7 @@ public class TaggingServiceImpl implements TaggingService,
public void beforeDeleteNode(NodeRef nodeRef) public void beforeDeleteNode(NodeRef nodeRef)
{ {
if (this.nodeService.exists(nodeRef) == true && if (this.nodeService.exists(nodeRef) == true &&
this.nodeService.hasAspect(nodeRef, ContentModel.ASPECT_TAGGABLE) == true && !this.nodeService.hasAspect(nodeRef, ContentModel.ASPECT_WORKING_COPY)) this.nodeService.hasAspect(nodeRef, ContentModel.ASPECT_TAGGABLE) == true && !this.nodeService.hasAspect(nodeRef, ASPECT_WORKING_COPY))
{ {
updateAllScopeTags(nodeRef, Boolean.FALSE); updateAllScopeTags(nodeRef, Boolean.FALSE);
} }
@@ -1241,7 +1243,7 @@ public class TaggingServiceImpl implements TaggingService,
// Do the search for nodes // Do the search for nodes
resultSet = this.searchService.query( resultSet = this.searchService.query(
storeRef, storeRef,
SearchService.LANGUAGE_LUCENE, LANGUAGE_LUCENE,
"+PATH:\"/cm:taggable/cm:" + ISO9075.encode(tag) + "/member\""); "+PATH:\"/cm:taggable/cm:" + ISO9075.encode(tag) + "/member\"");
List<NodeRef> nodeRefs = resultSet.getNodeRefs(); List<NodeRef> nodeRefs = resultSet.getNodeRefs();
return nodeRefs; return nodeRefs;
@@ -1270,7 +1272,7 @@ public class TaggingServiceImpl implements TaggingService,
// Do query // Do query
resultSet = this.searchService.query( resultSet = this.searchService.query(
storeRef, storeRef,
SearchService.LANGUAGE_LUCENE, LANGUAGE_LUCENE,
"+PATH:\"" + pathString + "//*\" +PATH:\"/cm:taggable/cm:" + ISO9075.encode(tag) + "/member\""); "+PATH:\"" + pathString + "//*\" +PATH:\"/cm:taggable/cm:" + ISO9075.encode(tag) + "/member\"");
List<NodeRef> nodeRefs = resultSet.getNodeRefs(); List<NodeRef> nodeRefs = resultSet.getNodeRefs();
return nodeRefs; return nodeRefs;
@@ -1538,7 +1540,7 @@ public class TaggingServiceImpl implements TaggingService,
public void afterCheckOut(NodeRef workingCopy) public void afterCheckOut(NodeRef workingCopy)
{ {
if (this.nodeService.exists(workingCopy) == true && this.nodeService.hasAspect(workingCopy, ContentModel.ASPECT_TAGGABLE) == true if (this.nodeService.exists(workingCopy) == true && this.nodeService.hasAspect(workingCopy, ContentModel.ASPECT_TAGGABLE) == true
&& this.nodeService.hasAspect(workingCopy, ContentModel.ASPECT_WORKING_COPY)) && this.nodeService.hasAspect(workingCopy, ASPECT_WORKING_COPY))
{ {
updateAllScopeTags(workingCopy, Boolean.FALSE); updateAllScopeTags(workingCopy, Boolean.FALSE);
} }
@@ -1550,10 +1552,10 @@ public class TaggingServiceImpl implements TaggingService,
@Override @Override
public List<Pair<String, Integer>> findTaggedNodesAndCountByTagName(StoreRef storeRef) public List<Pair<String, Integer>> findTaggedNodesAndCountByTagName(StoreRef storeRef)
{ {
String queryTaggeble = "ASPECT:\"" + ContentModel.ASPECT_TAGGABLE + "\"" + "-ASPECT:\"" + ContentModel.ASPECT_WORKING_COPY + "\""; String queryTaggeble = "ASPECT:\"" + ContentModel.ASPECT_TAGGABLE + "\"" + "-ASPECT:\"" + ASPECT_WORKING_COPY + "\"";
SearchParameters sp = new SearchParameters(); SearchParameters sp = new SearchParameters();
sp.setQuery(queryTaggeble); sp.setQuery(queryTaggeble);
sp.setLanguage(SearchService.LANGUAGE_LUCENE); sp.setLanguage(LANGUAGE_LUCENE);
sp.addStore(storeRef); sp.addStore(storeRef);
sp.addFieldFacet(new FieldFacet("TAG")); sp.addFieldFacet(new FieldFacet("TAG"));
@@ -1573,6 +1575,32 @@ public class TaggingServiceImpl implements TaggingService,
} }
} }
/** {@inheritDoc} */
@Override
public long findCountByTagName(StoreRef storeRef, String name)
{
String query = "TAG:\"" + name + "\"" + "-ASPECT:\"" + ASPECT_WORKING_COPY + "\"";
SearchParameters sp = new SearchParameters();
sp.setQuery(query);
sp.setLanguage(LANGUAGE_LUCENE);
sp.addStore(storeRef);
ResultSet resultSet = null;
try
{
// Do the search for nodes
resultSet = this.searchService.query(sp);
return resultSet.getNumberFound();
}
finally
{
if (resultSet != null)
{
resultSet.close();
}
}
}
@Experimental @Experimental
@Override @Override
public List<Pair<String, NodeRef>> createTags(final StoreRef storeRef, final List<String> tagNames) public List<Pair<String, NodeRef>> createTags(final StoreRef storeRef, final List<String> tagNames)

View File

@@ -338,6 +338,16 @@ public interface TaggingService
@NotAuditable @NotAuditable
List<Pair<String, Integer>> findTaggedNodesAndCountByTagName(StoreRef storeRef); List<Pair<String, Integer>> findTaggedNodesAndCountByTagName(StoreRef storeRef);
/**
* Get the number of tagged nodes for a given tag.
*
* @param storeRef The store containing the nodes.
* @param name The name of the tag.
* @return The number of nodes tagged with the specified tag.
*/
@NotAuditable
long findCountByTagName(StoreRef storeRef, String name);
/** /**
* Creates orphan tags. Tag names case will be lowered. * Creates orphan tags. Tag names case will be lowered.
* *