From 0aae95b2554a53550251aa11260bf69c3b9a67f4 Mon Sep 17 00:00:00 2001 From: Maciej Pichura <41297682+mpichura@users.noreply.github.com> Date: Tue, 13 Dec 2022 17:13:02 +0100 Subject: [PATCH] ACS-4031 list category children (#1617) * ACS-4031: Get category children endpoint. * ACS-4031: Fix in E2E test. * ACS-4031: Adding more unit tests. * Fix test description Co-authored-by: Tom Page * Fix test description Co-authored-by: Tom Page * ACS-4031: Refactoring after code review. * ACS-4031: Updating E2E test after latest logic refactor. Co-authored-by: Tom Page --- .../alfresco/rest/requests/Categories.java | 18 +- .../categories/CreateCategoriesTests.java | 2 +- .../rest/categories/GetCategoriesTests.java | 143 ++++++++++++- .../org/alfresco/rest/api/Categories.java | 3 + .../api/categories/SubcategoriesRelation.java | 12 +- .../rest/api/impl/CategoriesImpl.java | 58 +++++- .../categories/SubcategoriesRelationTest.java | 105 ++++++++++ .../rest/api/impl/CategoriesImplTest.java | 189 +++++++++++++++++- 8 files changed, 501 insertions(+), 29 deletions(-) create mode 100644 remote-api/src/test/java/org/alfresco/rest/api/categories/SubcategoriesRelationTest.java diff --git a/packaging/tests/tas-restapi/src/main/java/org/alfresco/rest/requests/Categories.java b/packaging/tests/tas-restapi/src/main/java/org/alfresco/rest/requests/Categories.java index e5a3843201..27ed2f5bc7 100644 --- a/packaging/tests/tas-restapi/src/main/java/org/alfresco/rest/requests/Categories.java +++ b/packaging/tests/tas-restapi/src/main/java/org/alfresco/rest/requests/Categories.java @@ -33,7 +33,6 @@ import org.alfresco.rest.core.RestRequest; import org.alfresco.rest.core.RestWrapper; import org.alfresco.rest.model.RestCategoryModel; import org.alfresco.rest.model.RestCategoryModelsCollection; -import org.alfresco.rest.model.RestRuleModelsCollection; import org.springframework.http.HttpMethod; public class Categories extends ModelRequest @@ -75,9 +74,22 @@ public class Categories extends ModelRequest * @param restCategoryModel The categories to create. * @return Created category with additional data populated by the repository. */ - public RestCategoryModel createSingleCategory(RestCategoryModel restCategoryModel) { - RestRequest request = RestRequest.requestWithBody(HttpMethod.POST, restCategoryModel.toJson(), "categories/{categoryId}/subcategories", category.getId()); + public RestCategoryModel createSingleCategory(RestCategoryModel restCategoryModel) + { + RestRequest request = RestRequest + .requestWithBody(HttpMethod.POST, restCategoryModel.toJson(), "categories/{categoryId}/subcategories", category.getId()); return restWrapper.processModel(RestCategoryModel.class, request); } + /** + * Get parent category children. + * + * @return The list of child categories. + */ + public RestCategoryModelsCollection getCategoryChildren() + { + RestRequest request = RestRequest.simpleRequest(HttpMethod.GET, "categories/{categoryId}/subcategories", category.getId()); + return restWrapper.processModels(RestCategoryModelsCollection.class, request); + } + } diff --git a/packaging/tests/tas-restapi/src/test/java/org/alfresco/rest/categories/CreateCategoriesTests.java b/packaging/tests/tas-restapi/src/test/java/org/alfresco/rest/categories/CreateCategoriesTests.java index 74200018f0..68e199dcf9 100644 --- a/packaging/tests/tas-restapi/src/test/java/org/alfresco/rest/categories/CreateCategoriesTests.java +++ b/packaging/tests/tas-restapi/src/test/java/org/alfresco/rest/categories/CreateCategoriesTests.java @@ -260,7 +260,7 @@ public class CreateCategoriesTests extends RestTest restClient.assertStatusCodeIs(BAD_REQUEST).assertLastError().containsSummary("Node id does not refer to a valid category"); } - private List getCategoriesToCreate(final int count) + static List getCategoriesToCreate(final int count) { return IntStream.range(0, count) .mapToObj(i -> { diff --git a/packaging/tests/tas-restapi/src/test/java/org/alfresco/rest/categories/GetCategoriesTests.java b/packaging/tests/tas-restapi/src/test/java/org/alfresco/rest/categories/GetCategoriesTests.java index d02312442b..a8d2da0340 100644 --- a/packaging/tests/tas-restapi/src/test/java/org/alfresco/rest/categories/GetCategoriesTests.java +++ b/packaging/tests/tas-restapi/src/test/java/org/alfresco/rest/categories/GetCategoriesTests.java @@ -28,11 +28,20 @@ package org.alfresco.rest.categories; import static org.alfresco.utility.report.log.Step.STEP; import static org.springframework.http.HttpStatus.BAD_REQUEST; +import static org.springframework.http.HttpStatus.CREATED; import static org.springframework.http.HttpStatus.NOT_FOUND; import static org.springframework.http.HttpStatus.OK; +import static org.testng.Assert.assertTrue; + +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.IntStream; import org.alfresco.rest.RestTest; +import org.alfresco.rest.core.RestResponse; import org.alfresco.rest.model.RestCategoryModel; +import org.alfresco.rest.model.RestCategoryModelsCollection; +import org.alfresco.utility.data.RandomData; import org.alfresco.utility.model.FolderModel; import org.alfresco.utility.model.SiteModel; import org.alfresco.utility.model.TestGroup; @@ -42,6 +51,10 @@ import org.testng.annotations.Test; public class GetCategoriesTests extends RestTest { + private static final List DEFAULT_ROOT_CATEGORIES = List.of("Software Document Classification", "Languages", "Regions", "Tags"); + private static final String ROOT = "-root-"; + private static final String NON_EXISTING_CATEGORY_ID = "non-existing-category-id"; + private UserModel user; @BeforeClass(alwaysRun = true) @@ -57,11 +70,29 @@ public class GetCategoriesTests extends RestTest @Test(groups = {TestGroup.REST_API}) public void testGetCategoryById() { - STEP("Get category with -root- as id (which does not exist)"); + STEP("Create a category under root category (as admin)"); final RestCategoryModel rootCategory = new RestCategoryModel(); - rootCategory.setId("-root-"); - restClient.authenticateUser(user).withCoreAPI().usingCategory(rootCategory).getCategory(); - restClient.assertStatusCodeIs(NOT_FOUND); + rootCategory.setId(ROOT); + final RestCategoryModel aCategory = new RestCategoryModel(); + aCategory.setName(RandomData.getRandomName("Category")); + final RestCategoryModel createdCategory = restClient.authenticateUser(dataUser.getAdminUser()) + .withCoreAPI() + .usingCategory(rootCategory) + .createSingleCategory(aCategory); + restClient.assertStatusCodeIs(CREATED); + + createdCategory.assertThat() + .field("name").is(aCategory.getName()); + createdCategory.assertThat() + .field("parentId").is(rootCategory.getId()); + createdCategory.assertThat() + .field("hasChildren").is(false); + + STEP("Get the created category (as regular user)"); + final RestCategoryModel categoryFromGet = + restClient.authenticateUser(user).withCoreAPI().usingCategory(createdCategory).getCategory(); + restClient.assertStatusCodeIs(OK); + categoryFromGet.assertThat().isEqualTo(createdCategory); } /** @@ -70,15 +101,15 @@ public class GetCategoriesTests extends RestTest @Test(groups = {TestGroup.REST_API}) public void testGetCategoryByIdProvidingRootAsId() { - STEP("Get category with -root- as id (which does not exist)"); + STEP("Get category with -root- as id"); final RestCategoryModel rootCategory = new RestCategoryModel(); - rootCategory.setId("-root-"); + rootCategory.setId(ROOT); restClient.authenticateUser(user).withCoreAPI().usingCategory(rootCategory).getCategory(); - restClient.assertStatusCodeIs(NOT_FOUND); + restClient.assertStatusCodeIs(BAD_REQUEST).assertLastError().containsSummary("Node id does not refer to a valid category"); } /** - * Check we get an error when passing as category id + * Check we get an error when passing folder node id as category id */ @Test(groups = {TestGroup.REST_API}) public void testGetCategoryByIdProvidingFolderAsId() @@ -91,7 +122,101 @@ public class GetCategoriesTests extends RestTest final RestCategoryModel rootCategory = new RestCategoryModel(); rootCategory.setId(folder.getNodeRef()); restClient.authenticateUser(user).withCoreAPI().usingCategory(rootCategory).getCategory(); - restClient.assertStatusCodeIs(BAD_REQUEST); + restClient.assertStatusCodeIs(BAD_REQUEST).assertLastError().containsSummary("Node id does not refer to a valid category"); } + /** + * Check we get an error when passing non existing as category id + */ + @Test(groups = {TestGroup.REST_API}) + public void testGetCategoryByIdProvidingNonExistingId() + { + STEP("Get category with id which does not exist"); + final RestCategoryModel rootCategory = new RestCategoryModel(); + final String id = NON_EXISTING_CATEGORY_ID; + rootCategory.setId(id); + restClient.authenticateUser(user).withCoreAPI().usingCategory(rootCategory).getCategory(); + restClient.assertStatusCodeIs(NOT_FOUND).assertLastError().containsSummary(id); + } + + /** + * Check we can get children category of a root category + */ + @Test(groups = {TestGroup.REST_API}) + public void testGetCategoryChildren() + { + STEP("Get category children with -root- as parent id"); + final RestCategoryModel rootCategory = new RestCategoryModel(); + rootCategory.setId(ROOT); + RestCategoryModelsCollection childCategoriesList = + restClient.authenticateUser(user).withCoreAPI().usingCategory(rootCategory).getCategoryChildren(); + restClient.assertStatusCodeIs(OK); + + childCategoriesList.assertThat().entriesListIsNotEmpty(); + assertTrue(childCategoriesList.getEntries().stream() + .map(RestCategoryModel::onModel) + .map(RestCategoryModel::getName) + .collect(Collectors.toList()) + .containsAll(DEFAULT_ROOT_CATEGORIES)); + STEP("Create a new category under root and make sure it is returned as one of root's children"); + final RestCategoryModel aCategory = new RestCategoryModel(); + aCategory.setName((RandomData.getRandomName("newCategoryUnderRoot"))); + final RestCategoryModel createdCategory = restClient.authenticateUser(dataUser.getAdminUser()) + .withCoreAPI() + .usingCategory(rootCategory) + .createSingleCategory(aCategory); + restClient.assertStatusCodeIs(CREATED); + + childCategoriesList = restClient.authenticateUser(user).withCoreAPI().usingCategory(rootCategory).getCategoryChildren(); + restClient.assertStatusCodeIs(OK); + assertTrue(childCategoriesList.getEntries().stream() + .map(RestCategoryModel::onModel) + .map(RestCategoryModel::getId) + .collect(Collectors.toList()) + .contains(createdCategory.getId())); + + STEP("Create 2 more categories under newCategoryUnderRoot and make sure they are returned as children"); + final int categoriesCount = 2; + final List categoriesToCreate = CreateCategoriesTests.getCategoriesToCreate(categoriesCount); + final RestCategoryModelsCollection createdSubCategories = restClient.authenticateUser(dataUser.getAdminUser()) + .withCoreAPI() + .usingCategory(createdCategory) + .createCategoriesList(categoriesToCreate); + restClient.assertStatusCodeIs(CREATED); + childCategoriesList = restClient.authenticateUser(user).withCoreAPI().usingCategory(rootCategory).getCategoryChildren(); + restClient.assertStatusCodeIs(OK); + childCategoriesList.getEntries().containsAll(createdSubCategories.getEntries()); + } + + /** + * Check we get an error when passing folder node id as parent category id when getting children. + */ + @Test(groups = {TestGroup.REST_API}) + public void testGetCategoryChildrenProvidingFolderAsId() + { + STEP("Create a site and a folder inside it"); + final SiteModel site = dataSite.usingUser(user).createPublicRandomSite(); + final FolderModel folder = dataContent.usingUser(user).usingSite(site).createFolder(); + + STEP("Get category children with folder id passed as parent id"); + final RestCategoryModel parentCategory = new RestCategoryModel(); + parentCategory.setId(folder.getNodeRef()); + restClient.authenticateUser(user).withCoreAPI().usingCategory(parentCategory).getCategoryChildren(); + restClient.assertStatusCodeIs(BAD_REQUEST).assertLastError().containsSummary("Node id does not refer to a valid category"); + } + + /** + * Check we get an error when passing a non-existent node id as parent category id when getting children. + */ + @Test(groups = {TestGroup.REST_API}) + public void testGetCategoryChildrenProvidingNonExistingParent() + { + + STEP("Get category with folder id passed as id"); + final RestCategoryModel parentCategory = new RestCategoryModel(); + final String parentId = NON_EXISTING_CATEGORY_ID; + parentCategory.setId(parentId); + restClient.authenticateUser(user).withCoreAPI().usingCategory(parentCategory).getCategoryChildren(); + restClient.assertStatusCodeIs(NOT_FOUND).assertLastError().containsSummary(parentId); + } } diff --git a/remote-api/src/main/java/org/alfresco/rest/api/Categories.java b/remote-api/src/main/java/org/alfresco/rest/api/Categories.java index 6c18d06fd2..7e03067e50 100644 --- a/remote-api/src/main/java/org/alfresco/rest/api/Categories.java +++ b/remote-api/src/main/java/org/alfresco/rest/api/Categories.java @@ -29,6 +29,7 @@ package org.alfresco.rest.api; import java.util.List; import org.alfresco.rest.api.model.Category; +import org.alfresco.rest.framework.resource.parameters.CollectionWithPagingInfo; import org.alfresco.rest.framework.resource.parameters.Parameters; import org.alfresco.service.Experimental; import org.alfresco.service.cmr.repository.NodeRef; @@ -39,4 +40,6 @@ public interface Categories Category getCategoryById(String id, Parameters params); List createSubcategories(String parentCategoryId, List categories, Parameters parameters); + + CollectionWithPagingInfo getCategoryChildren(String parentCategoryId, Parameters params); } diff --git a/remote-api/src/main/java/org/alfresco/rest/api/categories/SubcategoriesRelation.java b/remote-api/src/main/java/org/alfresco/rest/api/categories/SubcategoriesRelation.java index 1516ca44b8..bd3ccbce5d 100644 --- a/remote-api/src/main/java/org/alfresco/rest/api/categories/SubcategoriesRelation.java +++ b/remote-api/src/main/java/org/alfresco/rest/api/categories/SubcategoriesRelation.java @@ -35,10 +35,11 @@ import org.alfresco.rest.api.model.Category; import org.alfresco.rest.framework.WebApiDescription; import org.alfresco.rest.framework.resource.RelationshipResource; import org.alfresco.rest.framework.resource.actions.interfaces.RelationshipResourceAction; +import org.alfresco.rest.framework.resource.parameters.CollectionWithPagingInfo; import org.alfresco.rest.framework.resource.parameters.Parameters; @RelationshipResource(name = "subcategories", entityResource = CategoriesEntityResource.class, title = "Subcategories") -public class SubcategoriesRelation implements RelationshipResourceAction.Create +public class SubcategoriesRelation implements RelationshipResourceAction.Create, RelationshipResourceAction.Read { private final Categories categories; @@ -56,4 +57,13 @@ public class SubcategoriesRelation implements RelationshipResourceAction.Create< { return categories.createSubcategories(parentCategoryId, categoryList, parameters); } + + @WebApiDescription(title = "List category direct children", + description = "Lists direct children of a parent category", + successStatus = HttpServletResponse.SC_OK) + @Override + public CollectionWithPagingInfo readAll(String parentCategoryId, Parameters params) + { + return categories.getCategoryChildren(parentCategoryId, params); + } } 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 8db2796b16..8f017d5b14 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 @@ -39,6 +39,8 @@ import org.alfresco.rest.api.model.Node; import org.alfresco.rest.framework.core.exceptions.EntityNotFoundException; import org.alfresco.rest.framework.core.exceptions.InvalidArgumentException; import org.alfresco.rest.framework.core.exceptions.PermissionDeniedException; +import org.alfresco.rest.framework.resource.parameters.CollectionWithPagingInfo; +import org.alfresco.rest.framework.resource.parameters.ListPage; import org.alfresco.rest.framework.resource.parameters.Parameters; import org.alfresco.service.Experimental; import org.alfresco.service.cmr.repository.ChildAssociationRef; @@ -74,8 +76,8 @@ public class CategoriesImpl implements Categories @Override public Category getCategoryById(final String id, final Parameters params) { - final NodeRef nodeRef = nodes.validateNode(id); - if (isNotACategory(nodeRef) || isRootCategory(nodeRef)) + final NodeRef nodeRef = getCategoryNodeRef(id); + if (isRootCategory(nodeRef)) { throw new InvalidArgumentException(NOT_A_VALID_CATEGORY, new String[]{id}); } @@ -90,14 +92,7 @@ public class CategoriesImpl implements Categories { throw new PermissionDeniedException(NO_PERMISSION_TO_CREATE_A_CATEGORY); } - final NodeRef parentNodeRef = PATH_ROOT.equals(parentCategoryId) ? - categoryService.getRootCategoryNodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE) - .orElseThrow(() -> new EntityNotFoundException(parentCategoryId)) : - nodes.validateNode(parentCategoryId); - if (isNotACategory(parentNodeRef)) - { - throw new InvalidArgumentException(NOT_A_VALID_CATEGORY, new String[]{parentCategoryId}); - } + final NodeRef parentNodeRef = getCategoryNodeRef(parentCategoryId); final List categoryNodeRefs = categories.stream() .map(c -> createCategoryNodeRef(parentNodeRef, c)) .collect(Collectors.toList()); @@ -106,6 +101,49 @@ public class CategoriesImpl implements Categories .collect(Collectors.toList()); } + @Override + public CollectionWithPagingInfo getCategoryChildren(String parentCategoryId, Parameters params) + { + final NodeRef parentNodeRef = getCategoryNodeRef(parentCategoryId); + final List childCategoriesAssocs = nodeService.getChildAssocs(parentNodeRef).stream() + .filter(ca -> ContentModel.ASSOC_SUBCATEGORIES.equals(ca.getTypeQName())) + .collect(Collectors.toList()); + final List categories = childCategoriesAssocs.stream() + .map(c -> mapToCategory(c.getChildRef())) + .collect(Collectors.toList()); + return ListPage.of(categories, params.getPaging()); + } + + /** + * This method gets category NodeRef for a given category id. + * If '-root-' is passed as category id, then it's retrieved as a call to {@link org.alfresco.service.cmr.search.CategoryService#getRootCategoryNodeRef} + * In all other cases it's retrieved as a node of a category type {@link #validateCategoryNode(String)} + * @param nodeId category node id + * @return NodRef of category node + */ + private NodeRef getCategoryNodeRef(String nodeId) + { + return PATH_ROOT.equals(nodeId) ? + categoryService.getRootCategoryNodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE) + .orElseThrow(() -> new EntityNotFoundException(nodeId)) : + validateCategoryNode(nodeId); + } + + /** + * Validates if the node exists and is a category. + * @param nodeId (presumably) category node id + * @return category NodeRef + */ + private NodeRef validateCategoryNode(String nodeId) + { + final NodeRef nodeRef = nodes.validateNode(nodeId); + if (isNotACategory(nodeRef)) + { + throw new InvalidArgumentException(NOT_A_VALID_CATEGORY, new String[]{nodeId}); + } + return nodeRef; + } + private NodeRef createCategoryNodeRef(NodeRef parentNodeRef, Category c) { if (StringUtils.isEmpty(c.getName())) { diff --git a/remote-api/src/test/java/org/alfresco/rest/api/categories/SubcategoriesRelationTest.java b/remote-api/src/test/java/org/alfresco/rest/api/categories/SubcategoriesRelationTest.java new file mode 100644 index 0000000000..7b1948f7d2 --- /dev/null +++ b/remote-api/src/test/java/org/alfresco/rest/api/categories/SubcategoriesRelationTest.java @@ -0,0 +1,105 @@ +/* + * #%L + * Alfresco Remote API + * %% + * Copyright (C) 2005 - 2022 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.api.categories; + +import static org.junit.Assert.assertEquals; +import static org.mockito.BDDMockito.given; +import static org.mockito.BDDMockito.then; + +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +import org.alfresco.rest.api.Categories; +import org.alfresco.rest.api.model.Category; +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.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; + +@RunWith(MockitoJUnitRunner.class) +public class SubcategoriesRelationTest +{ + private static final String PARENT_CATEGORY_ID = "parent-category-node-id"; + private static final String CATEGORY_ID = "category-node-id"; + private static final String CATEGORY_NAME = "categoryName"; + private static final String SUBCATEGORY_NAME_PREFIX = "childCategoryName"; + + @Mock + private Categories categoriesMock; + @Mock + private Parameters parametersMock; + + @InjectMocks + private SubcategoriesRelation objectUnderTest; + + @Test + public void testCreateSubcategory() + { + final Category categoryToCreate = Category.builder().name(CATEGORY_NAME).create(); + final Category category = Category.builder().name(CATEGORY_NAME).parentId(PARENT_CATEGORY_ID).hasChildren(false).id(CATEGORY_ID).create(); + final List categoriesToCreate = List.of(categoryToCreate); + given(categoriesMock.createSubcategories(PARENT_CATEGORY_ID, categoriesToCreate, parametersMock)).willReturn(List.of(category)); + + //when + List categories = objectUnderTest.create(PARENT_CATEGORY_ID, categoriesToCreate, parametersMock); + + then(categoriesMock).should().createSubcategories(PARENT_CATEGORY_ID, categoriesToCreate, parametersMock); + then(categoriesMock).shouldHaveNoMoreInteractions(); + assertEquals(List.of(category), categories); + } + + @Test + public void testGetCategoryChildren() { + final CollectionWithPagingInfo categoryChildren = getCategories(3); + given(categoriesMock.getCategoryChildren(PARENT_CATEGORY_ID, parametersMock)).willReturn(categoryChildren); + + //when + final CollectionWithPagingInfo returnedChildren = objectUnderTest.readAll(PARENT_CATEGORY_ID, parametersMock); + + then(categoriesMock).should().getCategoryChildren(PARENT_CATEGORY_ID, parametersMock); + then(categoriesMock).shouldHaveNoMoreInteractions(); + assertEquals(categoryChildren, returnedChildren); + } + + private CollectionWithPagingInfo getCategories(final int count) + { + return CollectionWithPagingInfo.asPaged(Paging.DEFAULT, + IntStream.range(0, count) + .mapToObj(i -> Category.builder().name(SUBCATEGORY_NAME_PREFIX + "-" + i) + .parentId(PARENT_CATEGORY_ID) + .hasChildren(false) + .id(CATEGORY_ID + "-" + i) + .create()) + .collect(Collectors.toList()) + ); + } +} 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 7787e19812..482977366e 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 @@ -31,10 +31,15 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertThrows; import static org.mockito.BDDMockito.given; import static org.mockito.BDDMockito.then; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Optional; +import java.util.stream.Collectors; +import java.util.stream.IntStream; import org.alfresco.model.ContentModel; import org.alfresco.rest.api.Nodes; @@ -43,6 +48,7 @@ import org.alfresco.rest.api.model.Node; import org.alfresco.rest.framework.core.exceptions.EntityNotFoundException; import org.alfresco.rest.framework.core.exceptions.InvalidArgumentException; import org.alfresco.rest.framework.core.exceptions.PermissionDeniedException; +import org.alfresco.rest.framework.resource.parameters.CollectionWithPagingInfo; import org.alfresco.rest.framework.resource.parameters.Parameters; import org.alfresco.service.cmr.repository.ChildAssociationRef; import org.alfresco.service.cmr.repository.NodeRef; @@ -231,7 +237,6 @@ public class CategoriesImplTest final NodeRef parentCategoryNodeRef = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, PATH_ROOT); given(categoryServiceMock.getRootCategoryNodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE)) .willReturn(Optional.of(parentCategoryNodeRef)); - given(nodesMock.isSubClass(parentCategoryNodeRef, ContentModel.TYPE_CATEGORY, false)).willReturn(true); final NodeRef categoryNodeRef = prepareCategoryNodeRef(); given(categoryServiceMock.createCategory(parentCategoryNodeRef, CATEGORY_NAME)).willReturn(categoryNodeRef); given(nodesMock.getNode(CATEGORY_ID)).willReturn(prepareCategoryNode()); @@ -246,7 +251,6 @@ public class CategoriesImplTest then(authorityServiceMock).should().hasAdminAuthority(); then(authorityServiceMock).shouldHaveNoMoreInteractions(); - then(nodesMock).should().isSubClass(parentCategoryNodeRef, ContentModel.TYPE_CATEGORY, false); then(nodesMock).should().getNode(CATEGORY_ID); then(nodesMock).shouldHaveNoMoreInteractions(); then(nodeServiceMock).should().getPrimaryParent(categoryNodeRef); @@ -366,12 +370,151 @@ public class CategoriesImplTest then(categoryServiceMock).shouldHaveNoInteractions(); } + @Test + public void testGetRootCategoryChildren() + { + final NodeRef parentCategoryNodeRef = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, PATH_ROOT); + given(categoryServiceMock.getRootCategoryNodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE)) + .willReturn(Optional.of(parentCategoryNodeRef)); + final int childrenCount = 3; + final List childAssociationRefMocks = prepareChildAssocMocks(childrenCount, parentCategoryNodeRef); + given(nodeServiceMock.getChildAssocs(parentCategoryNodeRef)).willReturn(childAssociationRefMocks); + childAssociationRefMocks.forEach(this::prepareCategoryNodeMocks); + + //when + final CollectionWithPagingInfo categoryChildren = objectUnderTest.getCategoryChildren(PATH_ROOT, parametersMock); + + then(categoryServiceMock).should().getRootCategoryNodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE); + then(categoryServiceMock).shouldHaveNoMoreInteractions(); + then(nodeServiceMock).should().getChildAssocs(parentCategoryNodeRef); + childAssociationRefMocks.forEach(ca -> { + then(nodesMock).should().getNode(ca.getChildRef().getId()); + then(nodeServiceMock).should() + .getChildAssocs(ca.getChildRef(), RegexQNamePattern.MATCH_ALL, RegexQNamePattern.MATCH_ALL, false); + then(nodeServiceMock).should().getPrimaryParent(ca.getChildRef()); + }); + then(nodeServiceMock).should(times(childrenCount)).getParentAssocs(parentCategoryNodeRef); + + then(nodesMock).shouldHaveNoMoreInteractions(); + then(nodeServiceMock).shouldHaveNoMoreInteractions(); + + then(authorityServiceMock).shouldHaveNoInteractions(); + + assertEquals(childAssociationRefMocks.size(), categoryChildren.getTotalItems().intValue()); + final List categoryChildrenList = new ArrayList<>(categoryChildren.getCollection()); + assertEquals(childAssociationRefMocks.size(), categoryChildrenList.size()); + IntStream.range(0, childrenCount).forEach(i -> doCategoryAssertions(categoryChildrenList.get(i), i, PATH_ROOT)); + } + + @Test + public void testGetCategoryChildren() + { + final NodeRef parentCategoryNodeRef = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, 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); + + //when + final CollectionWithPagingInfo categoryChildren = objectUnderTest.getCategoryChildren(PARENT_ID, parametersMock); + + then(nodesMock).should().validateNode(PARENT_ID); + then(nodesMock).should().isSubClass(parentCategoryNodeRef, ContentModel.TYPE_CATEGORY, false); + then(nodeServiceMock).should().getChildAssocs(parentCategoryNodeRef); + childAssociationRefMocks.forEach(ca -> { + then(nodesMock).should().getNode(ca.getChildRef().getId()); + then(nodeServiceMock).should() + .getChildAssocs(ca.getChildRef(), RegexQNamePattern.MATCH_ALL, RegexQNamePattern.MATCH_ALL, false); + then(nodeServiceMock).should().getPrimaryParent(ca.getChildRef()); + }); + then(nodeServiceMock).should(times(childrenCount)).getParentAssocs(parentCategoryNodeRef); + + then(nodesMock).shouldHaveNoMoreInteractions(); + then(nodeServiceMock).shouldHaveNoMoreInteractions(); + + then(authorityServiceMock).shouldHaveNoInteractions(); + then(categoryServiceMock).shouldHaveNoInteractions(); + + assertEquals(childAssociationRefMocks.size(), categoryChildren.getTotalItems().intValue()); + final List categoryChildrenList = new ArrayList<>(categoryChildren.getCollection()); + assertEquals(childAssociationRefMocks.size(), categoryChildrenList.size()); + IntStream.range(0, childrenCount).forEach(i -> doCategoryAssertions(categoryChildrenList.get(i), i, PARENT_ID)); + } + + @Test + public void testGetCategoryChildren_noChildren() + { + final NodeRef parentCategoryNodeRef = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, PARENT_ID); + given(nodesMock.validateNode(PARENT_ID)).willReturn(parentCategoryNodeRef); + given(nodesMock.isSubClass(parentCategoryNodeRef, ContentModel.TYPE_CATEGORY, false)).willReturn(true); + + given(nodeServiceMock.getChildAssocs(parentCategoryNodeRef)).willReturn(Collections.emptyList()); + + + //when + final CollectionWithPagingInfo categoryChildren = objectUnderTest.getCategoryChildren(PARENT_ID, parametersMock); + + then(nodesMock).should().validateNode(PARENT_ID); + then(nodesMock).should().isSubClass(parentCategoryNodeRef, ContentModel.TYPE_CATEGORY, false); + then(nodesMock).shouldHaveNoMoreInteractions(); + then(nodeServiceMock).should().getChildAssocs(parentCategoryNodeRef); + then(nodeServiceMock).shouldHaveNoMoreInteractions(); + + then(authorityServiceMock).shouldHaveNoInteractions(); + then(categoryServiceMock).shouldHaveNoInteractions(); + + assertEquals(0, categoryChildren.getTotalItems().intValue()); + } + + @Test + public void testGetCategoryChildren_wrongParentNodeType() + { + final NodeRef parentCategoryNodeRef = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, PARENT_ID); + given(nodesMock.validateNode(PARENT_ID)).willReturn(parentCategoryNodeRef); + given(nodesMock.isSubClass(parentCategoryNodeRef, ContentModel.TYPE_CATEGORY, false)).willReturn(false); + + //when + assertThrows(InvalidArgumentException.class, () -> objectUnderTest.getCategoryChildren(PARENT_ID, parametersMock)); + + then(nodesMock).should().validateNode(PARENT_ID); + then(nodesMock).should().isSubClass(parentCategoryNodeRef, ContentModel.TYPE_CATEGORY, false); + then(nodesMock).shouldHaveNoMoreInteractions(); + + then(nodeServiceMock).shouldHaveNoInteractions(); + then(categoryServiceMock).shouldHaveNoInteractions(); + then(authorityServiceMock).shouldHaveNoInteractions(); + } + + @Test + public void testGetCategoryChildren_nonExistingParentNode() + { + given(nodesMock.validateNode(PARENT_ID)).willThrow(EntityNotFoundException.class); + + //when + assertThrows(EntityNotFoundException.class, () -> objectUnderTest.getCategoryChildren(PARENT_ID, parametersMock)); + + + then(nodesMock).should().validateNode(PARENT_ID); + then(nodesMock).shouldHaveNoMoreInteractions(); + + then(nodeServiceMock).shouldHaveNoInteractions(); + then(categoryServiceMock).shouldHaveNoInteractions(); + then(authorityServiceMock).shouldHaveNoInteractions(); + } + private Node prepareCategoryNode() { - final Node categoryNode = new Node(); - categoryNode.setName(CATEGORY_NAME); - categoryNode.setNodeId(CATEGORY_ID); final NodeRef parentNodeRef = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, PARENT_ID); + return prepareCategoryNode(CATEGORY_NAME, CATEGORY_ID, parentNodeRef); + } + + private Node prepareCategoryNode(final String name, final String id, final NodeRef parentNodeRef) + { + final Node categoryNode = new Node(); + categoryNode.setName(name); + categoryNode.setNodeId(id); categoryNode.setParentId(parentNodeRef); return categoryNode; } @@ -387,4 +530,40 @@ public class CategoriesImplTest .name(CATEGORY_NAME) .create()); } + + private List prepareChildAssocMocks(final int count, NodeRef parentCategoryNodeRef) + { + return IntStream.range(0, count).mapToObj(i -> { + ChildAssociationRef dummyChildAssocMock = mock(ChildAssociationRef.class); + given(dummyChildAssocMock.getTypeQName()).willReturn(ContentModel.ASSOC_SUBCATEGORIES); + given(dummyChildAssocMock.getChildRef()) + .willReturn(new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, CATEGORY_ID + "-" + i)); + given(dummyChildAssocMock.getParentRef()).willReturn(parentCategoryNodeRef); + return dummyChildAssocMock; + }) + .collect(Collectors.toList()); + } + + private void prepareCategoryNodeMocks(ChildAssociationRef childAssociationRef) + { + final NodeRef childRef = childAssociationRef.getChildRef(); + final String id = childRef.getId(); + final String name = id.replace(CATEGORY_ID, CATEGORY_NAME); + final NodeRef parentRef = childAssociationRef.getParentRef(); + given(nodesMock.getNode(id)).willReturn(prepareCategoryNode(name, id, parentRef)); + final ChildAssociationRef parentAssoc = new ChildAssociationRef(null, parentRef, null, childRef); + given(nodeServiceMock.getPrimaryParent(childRef)).willReturn(parentAssoc); + given(nodeServiceMock.getParentAssocs(parentRef)).willReturn(List.of(parentAssoc)); + } + + private void doCategoryAssertions(final Category category, final int index, final String parentId) + { + final Category expectedCategory = Category.builder() + .id(CATEGORY_ID + "-" + index) + .name(CATEGORY_NAME + "-" + index) + .parentId(parentId) + .hasChildren(false) + .create(); + assertEquals(expectedCategory, category); + } }