diff --git a/packaging/tests/tas-restapi/src/main/java/org/alfresco/rest/model/RestCategoryModel.java b/packaging/tests/tas-restapi/src/main/java/org/alfresco/rest/model/RestCategoryModel.java
index e25c56e22a..337c11f2da 100644
--- a/packaging/tests/tas-restapi/src/main/java/org/alfresco/rest/model/RestCategoryModel.java
+++ b/packaging/tests/tas-restapi/src/main/java/org/alfresco/rest/model/RestCategoryModel.java
@@ -52,6 +52,11 @@ This must be unique within the parent category.
*/
private long count;
+ /**
+ The path to this category.
+ */
+ private String path;
+
public String getId()
{
return this.id;
@@ -102,6 +107,14 @@ This must be unique within the parent category.
this.count = count;
}
+ public String getPath() {
+ return path;
+ }
+
+ public void setPath(String path) {
+ this.path = path;
+ }
+
@Override
public boolean equals(Object o)
{
@@ -126,6 +139,7 @@ This must be unique within the parent category.
", parentId='" + parentId + '\'' +
", hasChildren=" + hasChildren +
", count=" + count +
+ ", path=" + path +
'}';
}
diff --git a/packaging/tests/tas-restapi/src/test/java/org/alfresco/rest/categories/CategoriesPathTests.java b/packaging/tests/tas-restapi/src/test/java/org/alfresco/rest/categories/CategoriesPathTests.java
new file mode 100644
index 0000000000..55d986276f
--- /dev/null
+++ b/packaging/tests/tas-restapi/src/test/java/org/alfresco/rest/categories/CategoriesPathTests.java
@@ -0,0 +1,223 @@
+/*
+ * #%L
+ * Alfresco Remote API
+ * %%
+ * Copyright (C) 2005 - 2023 Alfresco Software Limited
+ * %%
+ * This file is part of the Alfresco software.
+ * If the software was purchased under a paid Alfresco license, the terms of
+ * the paid license agreement will prevail. Otherwise, the software is
+ * provided under the following open source license terms:
+ *
+ * Alfresco is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Alfresco is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Alfresco. If not, see .
+ * #L%
+ */
+
+package org.alfresco.rest.categories;
+
+import static org.alfresco.utility.data.RandomData.getRandomName;
+import static org.alfresco.utility.report.log.Step.STEP;
+import static org.springframework.http.HttpStatus.CREATED;
+import static org.springframework.http.HttpStatus.OK;
+import static org.testng.Assert.assertTrue;
+
+import org.alfresco.dataprep.CMISUtil;
+import org.alfresco.rest.model.RestCategoryLinkBodyModel;
+import org.alfresco.rest.model.RestCategoryModel;
+import org.alfresco.rest.model.RestCategoryModelsCollection;
+import org.alfresco.utility.Utility;
+import org.alfresco.utility.model.FileModel;
+import org.alfresco.utility.model.FolderModel;
+import org.alfresco.utility.model.SiteModel;
+import org.alfresco.utility.model.TestGroup;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+public class CategoriesPathTests extends CategoriesRestTest
+{
+ private FileModel file;
+ private RestCategoryModel category;
+
+ @BeforeClass(alwaysRun = true)
+ @Override
+ public void dataPreparation() throws Exception
+ {
+ STEP("Create user and site");
+ user = dataUser.createRandomTestUser();
+ SiteModel site = dataSite.usingUser(user).createPublicRandomSite();
+
+ STEP("Create a folder, file in it and a category");
+ FolderModel folder = dataContent.usingUser(user).usingSite(site).createFolder();
+ file = dataContent.usingUser(user).usingResource(folder).createContent(CMISUtil.DocumentType.TEXT_PLAIN);
+ category = prepareCategoryUnderRoot();
+
+ STEP("Wait for indexing to complete");
+ Utility.sleep(1000, 60000, () -> restClient.authenticateUser(user)
+ .withCoreAPI()
+ .usingCategory(category)
+ .include(INCLUDE_PATH_PARAM)
+ .getCategory()
+ .assertThat()
+ .field(FIELD_PATH)
+ .isNotNull());
+ }
+
+ /**
+ * Verify path for a category got by ID.
+ */
+ @Test(groups = { TestGroup.REST_API })
+ public void testGetCategoryById_includePath()
+ {
+ STEP("Get category and verify if path is a general path for categories");
+ final RestCategoryModel actualCategory = restClient.authenticateUser(user)
+ .withCoreAPI()
+ .usingCategory(category)
+ .include(INCLUDE_PATH_PARAM)
+ .getCategory();
+
+ restClient.assertStatusCodeIs(OK);
+ actualCategory.assertThat().field(FIELD_ID).is(category.getId());
+ actualCategory.assertThat().field(FIELD_PATH).is("/categories/General");
+ }
+
+ /**
+ * Verify path for category.
+ */
+ @Test(groups = { TestGroup.REST_API })
+ public void testGetCategories_includePath()
+ {
+ STEP("Get few categories and verify its paths");
+ final RestCategoryModel parentCategory = createCategoryModelWithId(ROOT_CATEGORY_ID);
+ final RestCategoryModelsCollection actualCategories = restClient.authenticateUser(user)
+ .withCoreAPI()
+ .usingCategory(parentCategory)
+ .include(INCLUDE_PATH_PARAM)
+ .getCategoryChildren();
+
+ restClient.assertStatusCodeIs(OK);
+ assertTrue(actualCategories.getEntries().stream()
+ .map(RestCategoryModel::onModel)
+ .allMatch(cat -> cat.getPath().equals("/categories/General")));
+ }
+
+ /**
+ * Verify path for child category.
+ */
+ @Test(groups = { TestGroup.REST_API })
+ public void testGetChildCategory_includePath()
+ {
+ STEP("Create parent and child categories");
+ final RestCategoryModel parentCategory = prepareCategoryUnderRoot();
+ final RestCategoryModel childCategory = prepareCategoryUnder(parentCategory);
+
+ STEP("Verify path for created child categories");
+ final RestCategoryModelsCollection actualCategories = restClient.authenticateUser(user)
+ .withCoreAPI()
+ .usingCategory(parentCategory)
+ .include(INCLUDE_PATH_PARAM)
+ .getCategoryChildren();
+
+ restClient.assertStatusCodeIs(OK);
+ actualCategories.getEntries().stream()
+ .map(RestCategoryModel::onModel)
+ .forEach(cat -> cat.assertThat().field(FIELD_PATH).is("/categories/General/" + parentCategory.getName()));
+ }
+
+ /**
+ * Create category and verify that it has a path.
+ */
+ @Test(groups = { TestGroup.REST_API })
+ public void testCreateCategory_includingPath()
+ {
+ STEP("Create a category under root and verify if path is a general path for categories");
+ final String categoryName = getRandomName("Category");
+ final RestCategoryModel rootCategory = createCategoryModelWithId(ROOT_CATEGORY_ID);
+ final RestCategoryModel aCategory = createCategoryModelWithName(categoryName);
+ final RestCategoryModel createdCategory = restClient.authenticateUser(dataUser.getAdminUser())
+ .withCoreAPI()
+ .include(INCLUDE_PATH_PARAM)
+ .usingCategory(rootCategory)
+ .createSingleCategory(aCategory);
+
+ restClient.assertStatusCodeIs(CREATED);
+ createdCategory.assertThat().field(FIELD_NAME).is(categoryName);
+ createdCategory.assertThat().field(FIELD_PATH).is("/categories/General");
+ }
+
+ /**
+ * Update category and verify that it has a path.
+ */
+ @Test(groups = { TestGroup.REST_API })
+ public void testUpdateCategory_includePath()
+ {
+ STEP("Update linked category and verify if path is a general path for categories");
+ final String categoryNewName = getRandomName("NewCategoryName");
+ final RestCategoryModel fixedCategoryModel = createCategoryModelWithName(categoryNewName);
+ final RestCategoryModel updatedCategory = restClient.authenticateUser(dataUser.getAdminUser())
+ .withCoreAPI()
+ .usingCategory(category)
+ .include(INCLUDE_PATH_PARAM)
+ .updateCategory(fixedCategoryModel);
+
+ restClient.assertStatusCodeIs(OK);
+ updatedCategory.assertThat().field(FIELD_ID).is(category.getId());
+ updatedCategory.assertThat().field(FIELD_PATH).is("/categories/General");
+ }
+
+ /**
+ * Link node to categories and verify that they have path.
+ */
+ @Test(groups = { TestGroup.REST_API })
+ public void testLinkNodeToCategories_includePath()
+ {
+ STEP("Link node to categories and verify if path is a general path");
+ final RestCategoryLinkBodyModel categoryLinkModel = createCategoryLinkModelWithId(category.getId());
+ final RestCategoryModel linkedCategory = restClient.authenticateUser(dataUser.getAdminUser())
+ .withCoreAPI()
+ .usingNode(file)
+ .include(INCLUDE_PATH_PARAM)
+ .linkToCategory(categoryLinkModel);
+
+ restClient.assertStatusCodeIs(CREATED);
+ linkedCategory.assertThat().field(FIELD_ID).is(category.getId());
+ linkedCategory.assertThat().field(FIELD_PATH).is("/categories/General");
+ }
+
+ /**
+ * List categories for given node and verify that they have a path.
+ */
+ @Test(groups = { TestGroup.REST_API })
+ public void testListCategoriesForNode_includePath()
+ {
+ STEP("Link file to category");
+ final RestCategoryLinkBodyModel categoryLink = createCategoryLinkModelWithId(category.getId());
+ final RestCategoryModel linkedCategory = restClient.authenticateUser(dataUser.getAdminUser())
+ .withCoreAPI()
+ .usingNode(file)
+ .include(INCLUDE_PATH_PARAM)
+ .linkToCategory(categoryLink);
+
+ STEP("Get linked category and verify if path is a general path");
+ final RestCategoryModelsCollection linkedCategories = restClient.authenticateUser(dataUser.getAdminUser())
+ .withCoreAPI()
+ .usingNode(file)
+ .include(INCLUDE_PATH_PARAM)
+ .getLinkedCategories();
+
+ restClient.assertStatusCodeIs(OK);
+ linkedCategories.assertThat().entriesListCountIs(1);
+ linkedCategories.getEntries().get(0).onModel().assertThat().field(FIELD_ID).is(category.getId());
+ linkedCategories.getEntries().get(0).onModel().assertThat().field(FIELD_PATH).is("/categories/General");
+ }
+}
diff --git a/packaging/tests/tas-restapi/src/test/java/org/alfresco/rest/categories/CategoriesRestTest.java b/packaging/tests/tas-restapi/src/test/java/org/alfresco/rest/categories/CategoriesRestTest.java
index c98c9802f7..792cec4b3e 100644
--- a/packaging/tests/tas-restapi/src/test/java/org/alfresco/rest/categories/CategoriesRestTest.java
+++ b/packaging/tests/tas-restapi/src/test/java/org/alfresco/rest/categories/CategoriesRestTest.java
@@ -46,6 +46,7 @@ import org.testng.annotations.BeforeClass;
abstract class CategoriesRestTest extends RestTest
{
protected static final String INCLUDE_COUNT_PARAM = "count";
+ protected static final String INCLUDE_PATH_PARAM = "path";
protected static final String ROOT_CATEGORY_ID = "-root-";
protected static final String CATEGORY_NAME_PREFIX = "CategoryName";
protected static final String FIELD_NAME = "name";
@@ -53,6 +54,7 @@ abstract class CategoriesRestTest extends RestTest
protected static final String FIELD_PARENT_ID = "parentId";
protected static final String FIELD_HAS_CHILDREN = "hasChildren";
protected static final String FIELD_COUNT = "count";
+ protected static final String FIELD_PATH = "path";
protected UserModel user;
diff --git a/remote-api/src/main/java/org/alfresco/rest/api/impl/CategoriesImpl.java b/remote-api/src/main/java/org/alfresco/rest/api/impl/CategoriesImpl.java
index 53f9eddab8..c733d71ab1 100644
--- a/remote-api/src/main/java/org/alfresco/rest/api/impl/CategoriesImpl.java
+++ b/remote-api/src/main/java/org/alfresco/rest/api/impl/CategoriesImpl.java
@@ -111,6 +111,11 @@ public class CategoriesImpl implements Categories
category.setCount(categoriesCount.getOrDefault(category.getId(), 0));
}
+ if (parameters.getInclude().contains(Nodes.PARAM_INCLUDE_PATH))
+ {
+ category.setPath(getCategoryPath(category));
+ }
+
return category;
}
@@ -128,6 +133,10 @@ public class CategoriesImpl implements Categories
{
category.setCount(0);
}
+ if (parameters.getInclude().contains(Nodes.PARAM_INCLUDE_PATH))
+ {
+ category.setPath(getCategoryPath(category));
+ }
})
.collect(Collectors.toList());
}
@@ -136,11 +145,18 @@ public class CategoriesImpl implements Categories
public List getCategoryChildren(final StoreRef storeRef, final String parentCategoryId, final Parameters parameters)
{
final NodeRef parentNodeRef = getCategoryNodeRef(storeRef, parentCategoryId);
- final List categories = nodeService.getChildAssocs(parentNodeRef).stream()
- .filter(ca -> ContentModel.ASSOC_SUBCATEGORIES.equals(ca.getTypeQName()))
- .map(ChildAssociationRef::getChildRef)
- .map(this::mapToCategory)
- .collect(Collectors.toList());
+ final List categories = nodeService.getChildAssocs(parentNodeRef)
+ .stream()
+ .filter(ca -> ContentModel.ASSOC_SUBCATEGORIES.equals(ca.getTypeQName()))
+ .map(ChildAssociationRef::getChildRef)
+ .map(this::mapToCategory)
+ .peek(category -> {
+ if (parameters.getInclude().contains(Nodes.PARAM_INCLUDE_PATH))
+ {
+ category.setPath(getCategoryPath(category));
+ }
+ })
+ .collect(Collectors.toList());
if (parameters.getInclude().contains(INCLUDE_COUNT_PARAM))
{
@@ -170,6 +186,11 @@ public class CategoriesImpl implements Categories
category.setCount(categoriesCount.getOrDefault(category.getId(), 0));
}
+ if (parameters.getInclude().contains(Nodes.PARAM_INCLUDE_PATH))
+ {
+ category.setPath(getCategoryPath(category));
+ }
+
return category;
}
@@ -200,7 +221,16 @@ public class CategoriesImpl implements Categories
}
final Collection actualCategories = DefaultTypeConverter.INSTANCE.getCollection(NodeRef.class, currentCategories);
- return actualCategories.stream().map(this::mapToCategory).collect(Collectors.toList());
+ return actualCategories
+ .stream()
+ .map(this::mapToCategory)
+ .peek(category -> {
+ if (parameters.getInclude().contains(Nodes.PARAM_INCLUDE_PATH))
+ {
+ category.setPath(getCategoryPath(category));
+ }
+ })
+ .collect(Collectors.toList());
}
@Override
@@ -230,7 +260,16 @@ public class CategoriesImpl implements Categories
linkNodeToCategories(contentNodeRef, categoryNodeRefs);
- return categoryNodeRefs.stream().map(this::mapToCategory).collect(Collectors.toList());
+ return categoryNodeRefs
+ .stream()
+ .map(this::mapToCategory)
+ .peek(category -> {
+ if (parameters.getInclude().contains(Nodes.PARAM_INCLUDE_PATH))
+ {
+ category.setPath(getCategoryPath(category));
+ }
+ })
+ .collect(Collectors.toList());
}
@Override
@@ -475,4 +514,16 @@ public class CategoriesImpl implements Categories
.stream()
.collect(Collectors.toMap(pair -> pair.getFirst().toString().replace(idPrefix, StringUtils.EMPTY), Pair::getSecond));
}
+
+ /**
+ * Get path for a given category in human-readable form.
+ *
+ * @param category Category to provide path for.
+ * @return Path for a category in human-readable form.
+ */
+ private String getCategoryPath(final Category category)
+ {
+ final NodeRef categoryNodeRef = nodes.getNode(category.getId()).getNodeRef();
+ return nodeService.getPath(categoryNodeRef).toDisplayPath(nodeService, permissionService);
+ }
}
diff --git a/remote-api/src/main/java/org/alfresco/rest/api/model/Category.java b/remote-api/src/main/java/org/alfresco/rest/api/model/Category.java
index 812d362a96..d65b22b8a8 100644
--- a/remote-api/src/main/java/org/alfresco/rest/api/model/Category.java
+++ b/remote-api/src/main/java/org/alfresco/rest/api/model/Category.java
@@ -35,6 +35,7 @@ public class Category
private String parentId;
private boolean hasChildren;
private Integer count;
+ private String path;
public String getId()
{
@@ -91,6 +92,14 @@ public class Category
this.count = count;
}
+ public String getPath() {
+ return path;
+ }
+
+ public void setPath(String path) {
+ this.path = path;
+ }
+
@Override
public boolean equals(Object o)
{
@@ -100,19 +109,20 @@ public class Category
return false;
Category category = (Category) o;
return hasChildren == category.hasChildren && Objects.equals(id, category.id) && Objects.equals(name, category.name) && Objects.equals(parentId, category.parentId)
- && Objects.equals(count, category.count);
+ && Objects.equals(count, category.count) && Objects.equals(path, category.path);
}
@Override
public int hashCode()
{
- return Objects.hash(id, name, parentId, hasChildren, count);
+ return Objects.hash(id, name, parentId, hasChildren, count, path);
}
@Override
public String toString()
{
- return "Category{" + "id='" + id + '\'' + ", name='" + name + '\'' + ", parentId='" + parentId + '\'' + ", hasChildren=" + hasChildren + ", count=" + count + '}';
+ return "Category{" + "id='" + id + '\'' + ", name='" + name + '\'' + ", parentId='" + parentId + '\'' + ", hasChildren=" + hasChildren
+ + ", count=" + count + ", path=" + path + '}';
}
public static Builder builder()
@@ -127,6 +137,7 @@ public class Category
private String parentId;
private boolean hasChildren;
private Integer count;
+ private String path;
public Builder id(String id)
{
@@ -158,6 +169,12 @@ public class Category
return this;
}
+ public Builder path(String path)
+ {
+ this.path = path;
+ return this;
+ }
+
public Category create()
{
final Category category = new Category();
@@ -166,6 +183,7 @@ public class Category
category.setParentId(parentId);
category.setHasChildren(hasChildren);
category.setCount(count);
+ category.setPath(path);
return category;
}
}
diff --git a/remote-api/src/test/java/org/alfresco/rest/api/impl/CategoriesImplTest.java b/remote-api/src/test/java/org/alfresco/rest/api/impl/CategoriesImplTest.java
index 6d9dc98410..1ce88415ce 100644
--- a/remote-api/src/test/java/org/alfresco/rest/api/impl/CategoriesImplTest.java
+++ b/remote-api/src/test/java/org/alfresco/rest/api/impl/CategoriesImplTest.java
@@ -60,6 +60,7 @@ import java.util.stream.IntStream;
import java.util.stream.Stream;
import org.alfresco.model.ContentModel;
+import org.alfresco.repo.transfer.PathHelper;
import org.alfresco.rest.api.Nodes;
import org.alfresco.rest.api.model.Category;
import org.alfresco.rest.api.model.Node;
@@ -71,6 +72,7 @@ import org.alfresco.rest.framework.resource.parameters.Parameters;
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.Path;
import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.service.cmr.search.CategoryService;
import org.alfresco.service.cmr.security.AccessStatus;
@@ -100,6 +102,9 @@ public class CategoriesImplTest
private static final Category CATEGORY = createDefaultCategory();
private static final String CONTENT_NODE_ID = "content-node-id";
private static final NodeRef CONTENT_NODE_REF = createNodeRefWithId(CONTENT_NODE_ID);
+ private static final String MOCK_ROOT_LEVEL = "/{mockRootLevel}";
+ private static final String MOCK_CHILD_LEVEL = "/{mockChild}";
+ private static final String MOCK_CATEGORY_PATH = "//" + MOCK_ROOT_LEVEL + "//" + MOCK_CHILD_LEVEL;
@Mock
private Nodes nodesMock;
@@ -252,6 +257,27 @@ public class CategoriesImplTest
.isEqualTo(1);
}
+ @Test
+ public void testGetCategoryById_includePath()
+ {
+ final QName categoryQName = createCmQNameOf(CATEGORY_NAME);
+ final NodeRef parentCategoryNodeRef = createNodeRefWithId(PARENT_ID);
+ final ChildAssociationRef parentAssociation = createAssociationOf(parentCategoryNodeRef, CATEGORY_NODE_REF, categoryQName);
+ given(nodesMock.getNode(any())).willReturn(createNode());
+ given(nodeServiceMock.getPrimaryParent(any())).willReturn(parentAssociation);
+ given(parametersMock.getInclude()).willReturn(List.of(Nodes.PARAM_INCLUDE_PATH));
+ given(nodeServiceMock.getPath(any())).willReturn(mockCategoryPath());
+
+ // when
+ final Category actualCategory = objectUnderTest.getCategoryById(CATEGORY_ID, parametersMock);
+
+ assertThat(actualCategory)
+ .isNotNull()
+ .extracting(Category::getPath)
+ .isNotNull()
+ .isEqualTo(MOCK_CATEGORY_PATH);
+ }
+
@Test
public void testGetCategoryById_notACategory()
{
@@ -479,6 +505,36 @@ public class CategoriesImplTest
.isEqualTo(0);
}
+ @Test
+ public void testCreateCategory_includePath()
+ {
+ final QName categoryQName = createCmQNameOf(CATEGORY_NAME);
+ final NodeRef categoryNodeRef = createNodeRefWithId(CATEGORY_ID);
+ final NodeRef parentCategoryNodeRef = createNodeRefWithId(PARENT_ID);
+ final ChildAssociationRef parentAssociation = createAssociationOf(parentCategoryNodeRef, CATEGORY_NODE_REF, categoryQName);
+ given(nodesMock.validateNode(PARENT_ID)).willReturn(parentCategoryNodeRef);
+ given(categoryServiceMock.createCategory(parentCategoryNodeRef, CATEGORY_NAME)).willReturn(categoryNodeRef);
+ given(nodesMock.getNode(any())).willReturn(createNode());
+ given(nodeServiceMock.getPrimaryParent(any())).willReturn(parentAssociation);
+ given(parametersMock.getInclude()).willReturn(List.of(Nodes.PARAM_INCLUDE_PATH));
+ given(nodeServiceMock.getPath(any())).willReturn(mockCategoryPath());
+ final List categoryModels = new ArrayList<>(prepareCategories());
+
+ // when
+ final List actualCreatedCategories = objectUnderTest.createSubcategories(PARENT_ID, categoryModels, parametersMock);
+
+ then(categoryServiceMock).should().createCategory(any(), any());
+ then(categoryServiceMock).shouldHaveNoMoreInteractions();
+
+ assertThat(actualCreatedCategories)
+ .isNotNull()
+ .hasSize(1)
+ .element(0)
+ .extracting(Category::getPath)
+ .isNotNull()
+ .isEqualTo(MOCK_CATEGORY_PATH);
+ }
+
@Test
public void testCreateCategories_noPermissions()
{
@@ -628,6 +684,30 @@ public class CategoriesImplTest
.isEqualTo(List.of(0, 2, 0));
}
+ @Test
+ public void testGetCategoryChildren_includePath()
+ {
+ final NodeRef parentCategoryNodeRef = createNodeRefWithId(PARENT_ID);
+ given(nodesMock.validateNode(PARENT_ID)).willReturn(parentCategoryNodeRef);
+ given(nodesMock.isSubClass(parentCategoryNodeRef, ContentModel.TYPE_CATEGORY, false)).willReturn(true);
+ final int childrenCount = 3;
+ final List childAssociationRefMocks = prepareChildAssocMocks(childrenCount, parentCategoryNodeRef);
+ given(nodeServiceMock.getChildAssocs(parentCategoryNodeRef)).willReturn(childAssociationRefMocks);
+ childAssociationRefMocks.forEach(this::prepareCategoryNodeMocks);
+ given(parametersMock.getInclude()).willReturn(List.of(Nodes.PARAM_INCLUDE_PATH));
+ given(nodeServiceMock.getPath(any())).willReturn(mockCategoryPath());
+
+ // when
+ final List actualCategoryChildren = objectUnderTest.getCategoryChildren(PARENT_ID, parametersMock);
+
+ assertThat(actualCategoryChildren)
+ .isNotNull()
+ .hasSize(3)
+ .extracting(Category::getPath)
+ .isNotNull()
+ .isEqualTo(List.of(MOCK_CATEGORY_PATH, MOCK_CATEGORY_PATH, MOCK_CATEGORY_PATH));
+ }
+
@Test
public void testGetCategoryChildren_noChildren()
{
@@ -751,6 +831,32 @@ public class CategoriesImplTest
.isEqualTo(1);
}
+ @Test
+ public void testUpdateCategoryById_includePath()
+ {
+ final String categoryNewName = "categoryNewName";
+ final Category fixedCategory = createCategoryOnlyWithName(categoryNewName);
+ // simulate path provided by client to check if it will be ignored
+ fixedCategory.setPath("/test/TestCat");
+ final QName categoryQName = createCmQNameOf(CATEGORY_NAME);
+ final NodeRef parentCategoryNodeRef = createNodeRefWithId(PARENT_ID);
+ final ChildAssociationRef parentAssociation = createAssociationOf(parentCategoryNodeRef, CATEGORY_NODE_REF, categoryQName);
+ given(nodesMock.getNode(any())).willReturn(createNode(categoryNewName));
+ given(nodeServiceMock.getPrimaryParent(any())).willReturn(parentAssociation);
+ given(nodeServiceMock.moveNode(any(), any(), any(), any())).willReturn(createAssociationOf(parentCategoryNodeRef, CATEGORY_NODE_REF, createCmQNameOf(categoryNewName)));
+ given(parametersMock.getInclude()).willReturn(List.of(Nodes.PARAM_INCLUDE_PATH));
+ given(nodeServiceMock.getPath(any())).willReturn(mockCategoryPath());
+
+ // when
+ final Category actualCategory = objectUnderTest.updateCategoryById(CATEGORY_ID, fixedCategory, parametersMock);
+
+ assertThat(actualCategory)
+ .isNotNull()
+ .extracting(Category::getPath)
+ .isNotNull()
+ .isEqualTo(MOCK_CATEGORY_PATH);
+ }
+
@Test
public void testUpdateCategoryById_noPermission()
{
@@ -918,6 +1024,7 @@ public class CategoriesImplTest
then(nodeServiceMock).should().getParentAssocs(categoryParentNodeRef);
then(nodeServiceMock).shouldHaveNoMoreInteractions();
final List expectedLinkedCategories = List.of(CATEGORY);
+ expectedLinkedCategories.get(0).setPath(null);
assertThat(actualLinkedCategories)
.isNotNull().usingRecursiveComparison()
.isEqualTo(expectedLinkedCategories);
@@ -984,6 +1091,36 @@ public class CategoriesImplTest
.isEqualTo(expectedLinkedCategories);
}
+ @Test
+ public void testLinkNodeToCategories_includePath()
+ {
+ final NodeRef categoryParentNodeRef = createNodeRefWithId(PARENT_ID);
+ final ChildAssociationRef parentAssociation = createAssociationOf(categoryParentNodeRef, CATEGORY_NODE_REF);
+ given(nodesMock.getNode(any())).willReturn(createNode());
+ given(nodeServiceMock.getPrimaryParent(any())).willReturn(parentAssociation);
+ given(nodeServiceMock.hasAspect(any(), any())).willReturn(true);
+ given(parametersMock.getInclude()).willReturn(List.of(Nodes.PARAM_INCLUDE_PATH));
+ given(nodeServiceMock.getPath(any())).willReturn(mockCategoryPath());
+
+ // when
+ final List actualLinkedCategories = objectUnderTest.linkNodeToCategories(CONTENT_NODE_ID, List.of(CATEGORY), parametersMock);
+
+ then(nodesMock).should(times(2)).getNode(CATEGORY_ID);
+ then(nodeServiceMock).should().getChildAssocs(CATEGORY_NODE_REF, RegexQNamePattern.MATCH_ALL, RegexQNamePattern.MATCH_ALL, false);
+ then(nodeServiceMock).should().getPrimaryParent(CATEGORY_NODE_REF);
+ then(nodeServiceMock).should().getParentAssocs(CATEGORY_NODE_REF);
+ then(nodeServiceMock).should().hasAspect(CONTENT_NODE_REF, ContentModel.ASPECT_GEN_CLASSIFIABLE);
+ then(nodeServiceMock).should().getProperty(CONTENT_NODE_REF, ContentModel.PROP_CATEGORIES);
+ final Serializable expectedCategories = (Serializable) List.of(CATEGORY_NODE_REF);
+ then(nodeServiceMock).should().setProperty(CONTENT_NODE_REF, ContentModel.PROP_CATEGORIES, expectedCategories);
+ then(nodeServiceMock).should().getParentAssocs(categoryParentNodeRef);
+ final List expectedLinkedCategories = List.of(CATEGORY);
+ expectedLinkedCategories.get(0).setPath(MOCK_CATEGORY_PATH);
+ assertThat(actualLinkedCategories)
+ .isNotNull().usingRecursiveComparison()
+ .isEqualTo(expectedLinkedCategories);
+ }
+
@Test
public void testLinkNodeToCategories_withPreviouslyLinkedCategories()
{
@@ -1168,11 +1305,46 @@ public class CategoriesImplTest
then(nodeServiceMock).should().getParentAssocs(categoryParentNodeRef);
then(nodeServiceMock).shouldHaveNoMoreInteractions();
final List expectedCategories = List.of(CATEGORY);
+ expectedCategories.get(0).setPath(null);
assertThat(actualCategories)
.isNotNull().usingRecursiveComparison()
.isEqualTo(expectedCategories);
}
+ @Test
+ public void testListCategoriesForNode_includePath()
+ {
+ final NodeRef categoryParentNodeRef = createNodeRefWithId(PARENT_ID);
+ final ChildAssociationRef parentAssociation = createAssociationOf(categoryParentNodeRef, CATEGORY_NODE_REF);
+ given(nodeServiceMock.getProperty(any(), eq(ContentModel.PROP_CATEGORIES))).willReturn((Serializable) List.of(CATEGORY_NODE_REF));
+ given(nodesMock.getNode(any())).willReturn(createNode());
+ given(nodeServiceMock.getPrimaryParent(any())).willReturn(parentAssociation);
+ given(parametersMock.getInclude()).willReturn(List.of(Nodes.PARAM_INCLUDE_PATH));
+ given(nodeServiceMock.getPath(any())).willReturn(mockCategoryPath());
+
+ // when
+ final List actualCategories = objectUnderTest.listCategoriesForNode(CONTENT_NODE_ID, parametersMock);
+
+ then(nodesMock).should().validateOrLookupNode(CONTENT_NODE_ID);
+ then(permissionServiceMock).should().hasReadPermission(CONTENT_NODE_REF);
+ then(permissionServiceMock).shouldHaveNoMoreInteractions();
+ then(typeConstraint).should().matches(CONTENT_NODE_REF);
+ then(typeConstraint).shouldHaveNoMoreInteractions();
+ then(nodesMock).should(times(2)).getNode(CATEGORY_ID);
+ then(nodesMock).shouldHaveNoMoreInteractions();
+ then(nodeServiceMock).should().getProperty(CONTENT_NODE_REF, ContentModel.PROP_CATEGORIES);
+ then(nodeServiceMock).should().getChildAssocs(CATEGORY_NODE_REF, RegexQNamePattern.MATCH_ALL, RegexQNamePattern.MATCH_ALL, false);
+ then(nodeServiceMock).should().getPrimaryParent(CATEGORY_NODE_REF);
+ then(nodeServiceMock).should().getParentAssocs(categoryParentNodeRef);
+ then(nodeServiceMock).should().getPath(any());
+ then(nodeServiceMock).shouldHaveNoMoreInteractions();
+ final List expectedCategories = List.of(CATEGORY);
+ expectedCategories.get(0).setPath(MOCK_CATEGORY_PATH);
+ assertThat(actualCategories)
+ .isNotNull().usingRecursiveComparison()
+ .isEqualTo(expectedCategories);
+ }
+
@Test
public void testListCategoriesForNode_withInvalidNodeId()
{
@@ -1329,4 +1501,13 @@ public class CategoriesImplTest
{
return new ChildAssociationRef(ContentModel.ASSOC_SUBCATEGORIES, parentNode, childNodeName, childNode);
}
+
+ private Path mockCategoryPath()
+ {
+ final Path mockPath = new Path();
+ mockPath.append(PathHelper.stringToPath(MOCK_ROOT_LEVEL));
+ mockPath.append(PathHelper.stringToPath(MOCK_CHILD_LEVEL));
+ mockPath.append(PathHelper.stringToPath("/"));
+ return mockPath;
+ }
}