From 285ebc29a4569aa81e36e1fc001206bd4f863c8f Mon Sep 17 00:00:00 2001 From: Krystian Dabrowski <98942253+krdabrowski@users.noreply.github.com> Date: Tue, 7 Feb 2023 11:14:35 +0100 Subject: [PATCH] ACS-4154: Add category count to relevant CRUD endpoints for categories (#1724) * ACS-4154: Add category count to relevant CRUD endpoints for categories --- .../rest/categories/CategoriesCountTests.java | 207 ++++++++++ .../rest/categories/CategoriesRestTest.java | 24 +- .../categories/CreateCategoriesTests.java | 39 +- .../categories/UpdateCategoriesTests.java | 25 +- .../org/alfresco/rest/api/Categories.java | 62 ++- .../categories/CategoriesEntityResource.java | 5 +- .../NodesCategoryLinksRelation.java | 5 +- .../api/categories/SubcategoriesRelation.java | 10 +- .../rest/api/impl/CategoriesImpl.java | 109 ++++-- .../org/alfresco/rest/api/model/Category.java | 39 +- .../alfresco/AppContextExtraTestSuite.java | 3 +- .../rest/api/CategoriesUnitTests.java | 47 +++ .../CategoriesEntityResourceTest.java | 32 +- .../NodesCategoryLinksRelationTest.java | 19 +- .../categories/SubcategoriesRelationTest.java | 41 +- .../rest/api/impl/CategoriesImplTest.java | 363 +++++++++++------- .../service/cmr/search/CategoryService.java | 2 +- 17 files changed, 791 insertions(+), 241 deletions(-) create mode 100644 packaging/tests/tas-restapi/src/test/java/org/alfresco/rest/categories/CategoriesCountTests.java create mode 100644 remote-api/src/test/java/org/alfresco/rest/api/CategoriesUnitTests.java diff --git a/packaging/tests/tas-restapi/src/test/java/org/alfresco/rest/categories/CategoriesCountTests.java b/packaging/tests/tas-restapi/src/test/java/org/alfresco/rest/categories/CategoriesCountTests.java new file mode 100644 index 0000000000..e4ff3874b3 --- /dev/null +++ b/packaging/tests/tas-restapi/src/test/java/org/alfresco/rest/categories/CategoriesCountTests.java @@ -0,0 +1,207 @@ +/* + * #%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.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 CategoriesCountTests extends CategoriesRestTest +{ + + private RestCategoryModel categoryLinkedWithFolder; + private RestCategoryModel categoryLinkedWithFile; + private RestCategoryModel categoryLinkedWithBoth; + private RestCategoryModel notLinkedCategory; + + @BeforeClass(alwaysRun = true) + 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 few categories"); + FolderModel folder = dataContent.usingUser(user).usingSite(site).createFolder(); + FileModel file = dataContent.usingUser(user).usingResource(folder).createContent(CMISUtil.DocumentType.TEXT_PLAIN); + categoryLinkedWithFolder = prepareCategoryUnderRoot(); + categoryLinkedWithFile = prepareCategoryUnderRoot(); + categoryLinkedWithBoth = prepareCategoryUnder(prepareCategoryUnderRoot()); + notLinkedCategory = prepareCategoryUnderRoot(); + + STEP("Link folder and file to categories"); + linkContentToCategories(folder, categoryLinkedWithFolder, categoryLinkedWithBoth); + linkContentToCategories(file, categoryLinkedWithFile, categoryLinkedWithBoth); + + STEP("Wait for indexing to complete"); + Utility.sleep(1000, 60000, () -> restClient.authenticateUser(user) + .withCoreAPI() + .usingCategory(categoryLinkedWithFolder) + .include(INCLUDE_COUNT_PARAM) + .getCategory() + .assertThat() + .field(FIELD_COUNT) + .isNot(0)); + } + + /** + * Verify count for a category linked with file and folder. + */ + @Test(groups = { TestGroup.REST_API }) + public void testGetCategoryById_includeCount() + { + STEP("Get linked category and verify if count is higher than 0"); + final RestCategoryModel actualCategory = restClient.authenticateUser(user) + .withCoreAPI() + .usingCategory(categoryLinkedWithBoth) + .include(INCLUDE_COUNT_PARAM) + .getCategory(); + + restClient.assertStatusCodeIs(OK); + actualCategory.assertThat().field(FIELD_ID).is(categoryLinkedWithBoth.getId()); + actualCategory.assertThat().field(FIELD_COUNT).is(2); + } + + /** + * Verify count for a category not linked with any content. + */ + @Test(groups = { TestGroup.REST_API }) + public void testGetCategoryById_includeCountForNonLinkedCategory() + { + STEP("Get non-linked category and verify if count is 0"); + final RestCategoryModel actualCategory = restClient.authenticateUser(user) + .withCoreAPI() + .usingCategory(notLinkedCategory) + .include(INCLUDE_COUNT_PARAM) + .getCategory(); + + restClient.assertStatusCodeIs(OK); + actualCategory.assertThat().field(FIELD_ID).is(notLinkedCategory.getId()); + actualCategory.assertThat().field(FIELD_COUNT).is(0); + } + + /** + * Verify count for three categories: linked with file, linked with folder and third not linked to any content. + */ + @Test(groups = { TestGroup.REST_API }) + public void testGetCategories_includeCount() + { + STEP("Get few categories and verify its counts"); + final RestCategoryModel parentCategory = createCategoryModelWithId(ROOT_CATEGORY_ID); + final RestCategoryModelsCollection actualCategories = restClient.authenticateUser(user) + .withCoreAPI() + .usingCategory(parentCategory) + .include(INCLUDE_COUNT_PARAM) + .getCategoryChildren(); + + restClient.assertStatusCodeIs(OK); + assertTrue(actualCategories.getEntries().stream() + .map(RestCategoryModel::onModel) + .anyMatch(category -> category.getId().equals(categoryLinkedWithFolder.getId()) && category.getCount() == 1)); + assertTrue(actualCategories.getEntries().stream() + .map(RestCategoryModel::onModel) + .anyMatch(category -> category.getId().equals(categoryLinkedWithFile.getId()) && category.getCount() == 1)); + assertTrue(actualCategories.getEntries().stream() + .map(RestCategoryModel::onModel) + .anyMatch(category -> category.getId().equals(notLinkedCategory.getId()) && category.getCount() == 0)); + } + + /** + * Create category and verify that its count is 0. + */ + @Test(groups = { TestGroup.REST_API }) + public void testCreateCategory_includingCount() + { + STEP("Create a category under root and verify if count is 0"); + 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_COUNT_PARAM) + .usingCategory(rootCategory) + .createSingleCategory(aCategory); + + STEP("Create a category under root category (as admin)"); + restClient.assertStatusCodeIs(CREATED); + createdCategory.assertThat().field(FIELD_NAME).is(categoryName); + createdCategory.assertThat().field(FIELD_COUNT).is(0); + } + + /** + * Update category linked to file and folder and verify that its count is 2. + */ + @Test(groups = { TestGroup.REST_API }) + public void testUpdateCategory_includeCount() + { + STEP("Update linked category and verify if count is higher than 0"); + final String categoryNewName = getRandomName("NewCategoryName"); + final RestCategoryModel fixedCategoryModel = createCategoryModelWithName(categoryNewName); + final RestCategoryModel updatedCategory = restClient.authenticateUser(dataUser.getAdminUser()) + .withCoreAPI() + .usingCategory(categoryLinkedWithBoth) + .include(INCLUDE_COUNT_PARAM) + .updateCategory(fixedCategoryModel); + + restClient.assertStatusCodeIs(OK); + updatedCategory.assertThat().field(FIELD_ID).is(categoryLinkedWithBoth.getId()); + updatedCategory.assertThat().field(FIELD_COUNT).is(2); + } + + /** + * Update category not linked to any content and verify that its count is 0. + */ + @Test(groups = { TestGroup.REST_API }) + public void testUpdateCategory_includeCountForNonLinkedCategory() + { + STEP("Update non-linked category and verify if count is 0"); + final String categoryNewName = getRandomName("NewCategoryName"); + final RestCategoryModel fixedCategoryModel = createCategoryModelWithName(categoryNewName); + final RestCategoryModel updatedCategory = restClient.authenticateUser(dataUser.getAdminUser()) + .withCoreAPI() + .usingCategory(notLinkedCategory) + .include(INCLUDE_COUNT_PARAM) + .updateCategory(fixedCategoryModel); + + restClient.assertStatusCodeIs(OK); + updatedCategory.assertThat().field(FIELD_ID).is(notLinkedCategory.getId()); + updatedCategory.assertThat().field(FIELD_COUNT).is(0); + } +} 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 f424f5724b..c98c9802f7 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 @@ -30,6 +30,7 @@ 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 java.util.Arrays; import java.util.List; import java.util.stream.Collectors; import java.util.stream.IntStream; @@ -37,18 +38,21 @@ import java.util.stream.IntStream; import org.alfresco.rest.RestTest; import org.alfresco.rest.model.RestCategoryLinkBodyModel; import org.alfresco.rest.model.RestCategoryModel; +import org.alfresco.rest.model.RestCategoryModelsCollection; import org.alfresco.utility.model.RepoTestModel; import org.alfresco.utility.model.UserModel; import org.testng.annotations.BeforeClass; abstract class CategoriesRestTest extends RestTest { + protected static final String INCLUDE_COUNT_PARAM = "count"; protected static final String ROOT_CATEGORY_ID = "-root-"; protected static final String CATEGORY_NAME_PREFIX = "CategoryName"; protected static final String FIELD_NAME = "name"; protected static final String FIELD_ID = "id"; protected static final String FIELD_PARENT_ID = "parentId"; protected static final String FIELD_HAS_CHILDREN = "hasChildren"; + protected static final String FIELD_COUNT = "count"; protected UserModel user; @@ -59,14 +63,26 @@ abstract class CategoriesRestTest extends RestTest user = dataUser.createRandomTestUser(); } - protected RestCategoryModel prepareCategoryUnderRoot() + protected RestCategoryModelsCollection linkContentToCategories(final RepoTestModel node, final RestCategoryModel... categories) { - return prepareCategoryUnder(ROOT_CATEGORY_ID); + final List categoryLinkModels = Arrays.stream(categories) + .map(RestCategoryModel::getId) + .map(this::createCategoryLinkModelWithId) + .collect(Collectors.toList()); + final RestCategoryModelsCollection linkedCategories = restClient.authenticateUser(user).withCoreAPI().usingNode(node).linkToCategories(categoryLinkModels); + + restClient.assertStatusCodeIs(CREATED); + + return linkedCategories; } - protected RestCategoryModel prepareCategoryUnder(final String parentId) + protected RestCategoryModel prepareCategoryUnderRoot() + { + return prepareCategoryUnder(createCategoryModelWithId(ROOT_CATEGORY_ID)); + } + + protected RestCategoryModel prepareCategoryUnder(final RestCategoryModel parentCategory) { - final RestCategoryModel parentCategory = createCategoryModelWithId(parentId); final RestCategoryModel categoryModel = createCategoryModelWithName(getRandomName(CATEGORY_NAME_PREFIX)); final RestCategoryModel createdCategory = restClient.authenticateUser(dataUser.getAdminUser()) .withCoreAPI() 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 92ccb56d44..53b4a5d952 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 @@ -2,7 +2,7 @@ * #%L * Alfresco Remote API * %% - * Copyright (C) 2005 - 2022 Alfresco Software Limited + * 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 @@ -26,6 +26,7 @@ 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.BAD_REQUEST; import static org.springframework.http.HttpStatus.CREATED; @@ -38,7 +39,6 @@ import java.util.stream.IntStream; 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; @@ -56,7 +56,7 @@ public class CreateCategoriesTests extends CategoriesRestTest { STEP("Create a category under root category (as admin)"); final RestCategoryModel rootCategory = createCategoryModelWithId(ROOT_CATEGORY_ID); - final RestCategoryModel aCategory = createCategoryModelWithName(RandomData.getRandomName("Category")); + final RestCategoryModel aCategory = createCategoryModelWithName(getRandomName("Category")); final RestCategoryModel createdCategory = restClient.authenticateUser(dataUser.getAdminUser()) .withCoreAPI() .usingCategory(rootCategory) @@ -92,7 +92,7 @@ public class CreateCategoriesTests extends CategoriesRestTest { STEP("Create a category under root category (as admin)"); final RestCategoryModel rootCategory = createCategoryModelWithId(ROOT_CATEGORY_ID); - final RestCategoryModel aCategory = createCategoryModelWithName(RandomData.getRandomName("Category")); + final RestCategoryModel aCategory = createCategoryModelWithName(getRandomName("Category")); final RestCategoryModel createdCategory = restClient.authenticateUser(dataUser.getAdminUser()) .withCoreAPI() .usingCategory(rootCategory) @@ -140,7 +140,7 @@ public class CreateCategoriesTests extends CategoriesRestTest { STEP("Create a category under root category (as admin)"); final RestCategoryModel rootCategory = createCategoryModelWithId(ROOT_CATEGORY_ID); - final RestCategoryModel aCategory = createCategoryModelWithName(RandomData.getRandomName("Category")); + final RestCategoryModel aCategory = createCategoryModelWithName(getRandomName("Category")); final RestCategoryModel createdCategory = restClient.authenticateUser(dataUser.getAdminUser()) .withCoreAPI() .usingCategory(rootCategory) @@ -186,7 +186,7 @@ public class CreateCategoriesTests extends CategoriesRestTest { STEP("Create a category under root category (as user)"); final RestCategoryModel rootCategory = createCategoryModelWithId(ROOT_CATEGORY_ID); - final RestCategoryModel aCategory = createCategoryModelWithName(RandomData.getRandomName("Category")); + final RestCategoryModel aCategory = createCategoryModelWithName(getRandomName("Category")); restClient.authenticateUser(user) .withCoreAPI() .usingCategory(rootCategory) @@ -203,7 +203,7 @@ public class CreateCategoriesTests extends CategoriesRestTest STEP("Create a category under non existing category node (as admin)"); final String id = "non-existing-node-id"; final RestCategoryModel rootCategory = createCategoryModelWithId(id); - final RestCategoryModel aCategory = createCategoryModelWithName(RandomData.getRandomName("Category")); + final RestCategoryModel aCategory = createCategoryModelWithName(getRandomName("Category")); restClient.authenticateUser(dataUser.getAdminUser()) .withCoreAPI() .usingCategory(rootCategory) @@ -223,7 +223,7 @@ public class CreateCategoriesTests extends CategoriesRestTest STEP("Create a category under folder node (as admin)"); final RestCategoryModel rootCategory = createCategoryModelWithId(folder.getNodeRef()); - final RestCategoryModel aCategory = createCategoryModelWithName(RandomData.getRandomName("Category")); + final RestCategoryModel aCategory = createCategoryModelWithName(getRandomName("Category")); restClient.authenticateUser(dataUser.getAdminUser()) .withCoreAPI() .usingCategory(rootCategory) @@ -231,10 +231,31 @@ public class CreateCategoriesTests extends CategoriesRestTest restClient.assertStatusCodeIs(BAD_REQUEST).assertLastError().containsSummary("Node id does not refer to a valid category"); } + /** + * Check weather count present in create category request will be ignored. + */ + @Test(groups = { TestGroup.REST_API }) + public void testCreateCategoryUnderRoot_verifyIfCountInRequestIsIgnored() + { + STEP("Try to create a category with filled count under root"); + final RestCategoryModel rootCategory = createCategoryModelWithId(ROOT_CATEGORY_ID); + final RestCategoryModel aCategory = createCategoryModelWithName(getRandomName("Category")); + aCategory.setCount(2); + final RestCategoryModel createdCategory = restClient.authenticateUser(dataUser.getAdminUser()) + .withCoreAPI() + .usingCategory(rootCategory) + .include(INCLUDE_COUNT_PARAM) + .createSingleCategory(aCategory); + + restClient.assertStatusCodeIs(CREATED); + createdCategory.assertThat().field(FIELD_NAME).is(aCategory.getName()); + createdCategory.assertThat().field(FIELD_COUNT).is(0); + } + static List getCategoriesToCreate(final int count) { return IntStream.range(0, count) - .mapToObj(i -> RestCategoryModel.builder().name(RandomData.getRandomName("SubCategory")).create()) + .mapToObj(i -> RestCategoryModel.builder().name(getRandomName("SubCategory")).create()) .collect(Collectors.toList()); } } diff --git a/packaging/tests/tas-restapi/src/test/java/org/alfresco/rest/categories/UpdateCategoriesTests.java b/packaging/tests/tas-restapi/src/test/java/org/alfresco/rest/categories/UpdateCategoriesTests.java index 969990c7de..8b4c81d115 100644 --- a/packaging/tests/tas-restapi/src/test/java/org/alfresco/rest/categories/UpdateCategoriesTests.java +++ b/packaging/tests/tas-restapi/src/test/java/org/alfresco/rest/categories/UpdateCategoriesTests.java @@ -79,7 +79,7 @@ public class UpdateCategoriesTests extends CategoriesRestTest final RestCategoryModel createdCategory = prepareCategoryUnderRoot(); STEP("Prepare as admin a subcategory of root's child category"); - final RestCategoryModel createdSubcategory = prepareCategoryUnder(createdCategory.getId()); + final RestCategoryModel createdSubcategory = prepareCategoryUnder(createdCategory); STEP("Update as admin newly created subcategory"); final String categoryNewName = getRandomName(CATEGORY_NEW_NAME_PREFIX); @@ -233,4 +233,27 @@ public class UpdateCategoriesTests extends CategoriesRestTest restClient.assertStatusCodeIs(OK); updatedCategory.assertThat().field(FIELD_NAME).is(categoryNewName); } + + /** + * Check whether count present in update category request will be ignored. + */ + @Test(groups = { TestGroup.REST_API }) + public void testUpdateCategory_verifyIfCountInRequestIsIgnored() + { + STEP("Prepare a category under root category"); + final RestCategoryModel createdCategory = prepareCategoryUnderRoot(); + + STEP("Try to update newly created category providing new name and count number"); + final RestCategoryModel fixedCategoryModel = createCategoryModelWithName(getRandomName(CATEGORY_NEW_NAME_PREFIX)); + fixedCategoryModel.setCount(2); + final RestCategoryModel updatedCategory = restClient.authenticateUser(dataUser.getAdminUser()) + .withCoreAPI() + .usingCategory(createdCategory) + .include(INCLUDE_COUNT_PARAM) + .updateCategory(fixedCategoryModel); + + restClient.assertStatusCodeIs(OK); + updatedCategory.assertThat().field(FIELD_ID).is(createdCategory.getId()); + updatedCategory.assertThat().field(FIELD_COUNT).is(0); + } } 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 e591bf9dd5..08ec10e4ec 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 @@ -26,57 +26,101 @@ package org.alfresco.rest.api; +import static org.alfresco.service.cmr.repository.StoreRef.STORE_REF_WORKSPACE_SPACESSTORE; + 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.StoreRef; @Experimental public interface Categories { - Category getCategoryById(String id, Parameters parameters); + Category getCategoryById(StoreRef storeRef, String id, Parameters parameters); - List createSubcategories(String parentCategoryId, List categories, Parameters parameters); + default Category getCategoryById(String id, Parameters parameters) + { + return getCategoryById(STORE_REF_WORKSPACE_SPACESSTORE, id, parameters); + } - CollectionWithPagingInfo getCategoryChildren(String parentCategoryId, Parameters parameters); + List createSubcategories(StoreRef storeRef, String parentCategoryId, List categories, Parameters parameters); + + default List createSubcategories(String parentCategoryId, List categories, Parameters parameters) + { + return createSubcategories(STORE_REF_WORKSPACE_SPACESSTORE, parentCategoryId, categories, parameters); + } + + List getCategoryChildren(StoreRef storeRef, String parentCategoryId, Parameters parameters); + + default List getCategoryChildren(String parentCategoryId, Parameters parameters) + { + return getCategoryChildren(STORE_REF_WORKSPACE_SPACESSTORE, parentCategoryId, parameters); + } /** * Update category by ID. Currently, it's possible only to update the name of category. * + * @param storeRef Reference to node store. * @param id Category ID. * @param fixedCategoryModel Fixed category model. + * @param parameters Additional parameters. * @return Updated category. */ - Category updateCategoryById(String id, Category fixedCategoryModel); + Category updateCategoryById(StoreRef storeRef, String id, Category fixedCategoryModel, Parameters parameters); - void deleteCategoryById(String id, Parameters parameters); + default Category updateCategoryById(String id, Category fixedCategoryModel, Parameters parameters) + { + return updateCategoryById(STORE_REF_WORKSPACE_SPACESSTORE, id, fixedCategoryModel, parameters); + } + + void deleteCategoryById(StoreRef storeRef, String id, Parameters parameters); + + default void deleteCategoryById(String id, Parameters parameters) + { + deleteCategoryById(STORE_REF_WORKSPACE_SPACESSTORE, id, parameters); + } /** * Get categories linked from node. Read permission on node is required. * Node type is restricted to specified vales from: {@link org.alfresco.util.TypeConstraint}. * * @param nodeId Node ID. + * @param parameters Additional parameters. * @return Categories linked from node. */ - List listCategoriesForNode(String nodeId); + List listCategoriesForNode(String nodeId, Parameters parameters); /** * Link node to categories. Change permission on node is required. * Node types allowed for categorization are specified within {@link org.alfresco.util.TypeConstraint}. * + * @param storeRef Reference to node store. * @param nodeId Node ID. * @param categoryLinks Category IDs to which content should be linked to. + * @param parameters Additional parameters. * @return Linked to categories. */ - List linkNodeToCategories(String nodeId, List categoryLinks); + List linkNodeToCategories(StoreRef storeRef, String nodeId, List categoryLinks, Parameters parameters); + + default List linkNodeToCategories(String nodeId, List categoryLinks, Parameters parameters) + { + return linkNodeToCategories(STORE_REF_WORKSPACE_SPACESSTORE, nodeId, categoryLinks, parameters); + } /** * Unlink node from a category. * + * @param storeRef Reference to node store. * @param nodeId Node ID. * @param categoryId Category ID from which content node should be unlinked from. + * @param parameters Additional parameters. */ - void unlinkNodeFromCategory(String nodeId, String categoryId, Parameters parameters); + void unlinkNodeFromCategory(StoreRef storeRef, String nodeId, String categoryId, Parameters parameters); + + default void unlinkNodeFromCategory(String nodeId, String categoryId, Parameters parameters) + { + unlinkNodeFromCategory(STORE_REF_WORKSPACE_SPACESSTORE, nodeId, categoryId, parameters); + } } diff --git a/remote-api/src/main/java/org/alfresco/rest/api/categories/CategoriesEntityResource.java b/remote-api/src/main/java/org/alfresco/rest/api/categories/CategoriesEntityResource.java index 23b79067ac..e67bf9801f 100644 --- a/remote-api/src/main/java/org/alfresco/rest/api/categories/CategoriesEntityResource.java +++ b/remote-api/src/main/java/org/alfresco/rest/api/categories/CategoriesEntityResource.java @@ -2,7 +2,7 @@ * #%L * Alfresco Remote API * %% - * Copyright (C) 2005 - 2016 Alfresco Software Limited + * 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 @@ -45,6 +45,7 @@ public class CategoriesEntityResource implements EntityResourceAction.ReadById, EntityResourceAction.Delete { + private final Categories categories; public CategoriesEntityResource(Categories categories) @@ -77,7 +78,7 @@ public class CategoriesEntityResource implements EntityResourceAction.ReadById readAll(String nodeId, Parameters parameters) { - return ListPage.of(categories.listCategoriesForNode(nodeId), parameters.getPaging()); + return ListPage.of(categories.listCategoriesForNode(nodeId, parameters), parameters.getPaging()); } /** @@ -77,7 +77,7 @@ public class NodesCategoryLinksRelation implements RelationshipResourceAction.Cr @Override public List create(String nodeId, List categoryLinks, Parameters parameters) { - return categories.linkNodeToCategories(nodeId, categoryLinks); + return categories.linkNodeToCategories(nodeId, categoryLinks, parameters); } /** @@ -93,5 +93,4 @@ public class NodesCategoryLinksRelation implements RelationshipResourceAction.Cr { categories.unlinkNodeFromCategory(nodeId, categoryId, parameters); } - } 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 8ef264ea35..7214597916 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 @@ -2,7 +2,7 @@ * #%L * Alfresco Remote API * %% - * Copyright (C) 2005 - 2022 Alfresco Software Limited + * 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 @@ -26,9 +26,8 @@ package org.alfresco.rest.api.categories; -import java.util.List; - import javax.servlet.http.HttpServletResponse; +import java.util.List; import org.alfresco.rest.api.Categories; import org.alfresco.rest.api.model.Category; @@ -36,6 +35,7 @@ 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.ListPage; import org.alfresco.rest.framework.resource.parameters.Parameters; @RelationshipResource(name = "subcategories", entityResource = CategoriesEntityResource.class, title = "Subcategories") @@ -69,8 +69,8 @@ public class SubcategoriesRelation implements RelationshipResourceAction.Create< description = "Lists direct children of a parent category", successStatus = HttpServletResponse.SC_OK) @Override - public CollectionWithPagingInfo readAll(String parentCategoryId, Parameters params) + public CollectionWithPagingInfo readAll(String parentCategoryId, Parameters parameters) { - return categories.getCategoryChildren(parentCategoryId, params); + return ListPage.of(categories.getCategoryChildren(parentCategoryId, parameters), parameters.getPaging()); } } 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 e8e6ff4470..e78f7856d4 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 @@ -46,10 +46,8 @@ import org.alfresco.rest.api.model.Category; 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.core.exceptions.InvalidNodeTypeException; -import org.alfresco.rest.framework.resource.parameters.CollectionWithPagingInfo; -import org.alfresco.rest.framework.resource.parameters.ListPage; +import org.alfresco.rest.framework.core.exceptions.PermissionDeniedException; import org.alfresco.rest.framework.resource.parameters.Parameters; import org.alfresco.service.Experimental; import org.alfresco.service.cmr.repository.ChildAssociationRef; @@ -62,6 +60,7 @@ import org.alfresco.service.cmr.security.AuthorityService; import org.alfresco.service.cmr.security.PermissionService; import org.alfresco.service.namespace.QName; import org.alfresco.service.namespace.RegexQNamePattern; +import org.alfresco.util.Pair; import org.alfresco.util.TypeConstraint; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang3.StringUtils; @@ -69,6 +68,7 @@ import org.apache.commons.lang3.StringUtils; @Experimental public class CategoriesImpl implements Categories { + static final String INCLUDE_COUNT_PARAM = "count"; static final String NOT_A_VALID_CATEGORY = "Node id does not refer to a valid category"; static final String NO_PERMISSION_TO_MANAGE_A_CATEGORY = "Current user does not have permission to manage a category"; static final String NO_PERMISSION_TO_READ_CONTENT = "Current user does not have read permission to content"; @@ -83,8 +83,8 @@ public class CategoriesImpl implements Categories private final PermissionService permissionService; private final TypeConstraint typeConstraint; - public CategoriesImpl(AuthorityService authorityService, CategoryService categoryService, Nodes nodes, NodeService nodeService, PermissionService permissionService, - TypeConstraint typeConstraint) + public CategoriesImpl(AuthorityService authorityService, CategoryService categoryService, Nodes nodes, NodeService nodeService, + PermissionService permissionService, TypeConstraint typeConstraint) { this.authorityService = authorityService; this.categoryService = categoryService; @@ -95,63 +95,89 @@ public class CategoriesImpl implements Categories } @Override - public Category getCategoryById(final String id, final Parameters params) + public Category getCategoryById(final StoreRef storeRef, final String id, final Parameters parameters) { - final NodeRef nodeRef = getCategoryNodeRef(id); + final NodeRef nodeRef = getCategoryNodeRef(storeRef, id); if (isRootCategory(nodeRef)) { throw new InvalidArgumentException(NOT_A_VALID_CATEGORY, new String[]{id}); } - return mapToCategory(nodeRef); + final Category category = mapToCategory(nodeRef); + + if (parameters.getInclude().contains(INCLUDE_COUNT_PARAM)) + { + final Map categoriesCount = getCategoriesCount(storeRef); + category.setCount(categoriesCount.getOrDefault(category.getId(), 0)); + } + + return category; } @Override - public List createSubcategories(String parentCategoryId, List categories, Parameters parameters) + public List createSubcategories(final StoreRef storeRef, final String parentCategoryId, final List categories, final Parameters parameters) { verifyAdminAuthority(); - final NodeRef parentNodeRef = getCategoryNodeRef(parentCategoryId); - final List categoryNodeRefs = categories.stream() + final NodeRef parentNodeRef = getCategoryNodeRef(storeRef, parentCategoryId); + + return categories.stream() .map(c -> createCategoryNodeRef(parentNodeRef, c)) - .collect(Collectors.toList()); - return categoryNodeRefs.stream() .map(this::mapToCategory) + .peek(category -> { + if (parameters.getInclude().contains(INCLUDE_COUNT_PARAM)) + { + category.setCount(0); + } + }) .collect(Collectors.toList()); } @Override - public CollectionWithPagingInfo getCategoryChildren(String parentCategoryId, Parameters params) + public List getCategoryChildren(final StoreRef storeRef, final String parentCategoryId, final Parameters parameters) { - 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()); + 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()); + + if (parameters.getInclude().contains(INCLUDE_COUNT_PARAM)) + { + final Map categoriesCount = getCategoriesCount(storeRef); + categories.forEach(category -> category.setCount(categoriesCount.getOrDefault(category.getId(), 0))); + } + + return categories; } @Override - public Category updateCategoryById(final String id, final Category fixedCategoryModel) + public Category updateCategoryById(final StoreRef storeRef, final String id, final Category fixedCategoryModel, final Parameters parameters) { verifyAdminAuthority(); - final NodeRef categoryNodeRef = getCategoryNodeRef(id); + final NodeRef categoryNodeRef = getCategoryNodeRef(storeRef, id); if (isRootCategory(categoryNodeRef)) { throw new InvalidArgumentException(NOT_A_VALID_CATEGORY, new String[]{id}); } validateCategoryFields(fixedCategoryModel); + final Category category = mapToCategory(changeCategoryName(categoryNodeRef, fixedCategoryModel.getName())); - return mapToCategory(changeCategoryName(categoryNodeRef, fixedCategoryModel.getName())); + if (parameters.getInclude().contains(INCLUDE_COUNT_PARAM)) + { + final Map categoriesCount = getCategoriesCount(storeRef); + category.setCount(categoriesCount.getOrDefault(category.getId(), 0)); + } + + return category; } @Override - public void deleteCategoryById(String id, Parameters parameters) + public void deleteCategoryById(final StoreRef storeRef, final String id, final Parameters parameters) { verifyAdminAuthority(); - final NodeRef nodeRef = getCategoryNodeRef(id); + final NodeRef nodeRef = getCategoryNodeRef(storeRef, id); if (isRootCategory(nodeRef)) { throw new InvalidArgumentException(NOT_A_VALID_CATEGORY, new String[]{id}); @@ -161,7 +187,7 @@ public class CategoriesImpl implements Categories } @Override - public List listCategoriesForNode(final String nodeId) + public List listCategoriesForNode(final String nodeId, final Parameters parameters) { final NodeRef contentNodeRef = nodes.validateNode(nodeId); verifyReadPermission(contentNodeRef); @@ -178,7 +204,7 @@ public class CategoriesImpl implements Categories } @Override - public List linkNodeToCategories(final String nodeId, final List categoryLinks) + public List linkNodeToCategories(final StoreRef storeRef, final String nodeId, final List categoryLinks, final Parameters parameters) { if (CollectionUtils.isEmpty(categoryLinks)) { @@ -194,7 +220,7 @@ public class CategoriesImpl implements Categories .map(Category::getId) .filter(StringUtils::isNotEmpty) .distinct() - .map(this::getCategoryNodeRef) + .map(id -> getCategoryNodeRef(storeRef, id)) .collect(Collectors.toList()); if (CollectionUtils.isEmpty(categoryNodeRefs) || isRootCategoryPresent(categoryNodeRefs)) @@ -208,9 +234,9 @@ public class CategoriesImpl implements Categories } @Override - public void unlinkNodeFromCategory(final String nodeId, final String categoryId, Parameters parameters) + public void unlinkNodeFromCategory(final StoreRef storeRef, final String nodeId, final String categoryId, final Parameters parameters) { - final NodeRef categoryNodeRef = getCategoryNodeRef(categoryId); + final NodeRef categoryNodeRef = getCategoryNodeRef(storeRef, categoryId); final NodeRef contentNodeRef = nodes.validateNode(nodeId); verifyChangePermission(contentNodeRef); verifyNodeType(contentNodeRef); @@ -272,13 +298,14 @@ public class CategoriesImpl implements Categories * 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 storeRef Reference to node store. * @param nodeId category node id * @return NodRef of category node */ - private NodeRef getCategoryNodeRef(String nodeId) + private NodeRef getCategoryNodeRef(StoreRef storeRef, String nodeId) { return PATH_ROOT.equals(nodeId) ? - categoryService.getRootCategoryNodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE) + categoryService.getRootCategoryNodeRef(storeRef) .orElseThrow(() -> new EntityNotFoundException(nodeId)) : validateCategoryNode(nodeId); } @@ -434,4 +461,18 @@ public class CategoriesImpl implements Categories nodeService.setProperty(nodeRef, ContentModel.PROP_CATEGORIES, (Serializable) allCategories); } } + + /** + * Get categories by usage count. Result is a map of category IDs (short form - UUID) as key and usage count as value. + * + * @param storeRef Reference to node store. + * @return Map of categories IDs and usage count. + */ + private Map getCategoriesCount(final StoreRef storeRef) + { + final String idPrefix = storeRef + "/"; + return categoryService.getTopCategories(storeRef, ContentModel.ASPECT_GEN_CLASSIFIABLE, Integer.MAX_VALUE) + .stream() + .collect(Collectors.toMap(pair -> pair.getFirst().toString().replace(idPrefix, StringUtils.EMPTY), Pair::getSecond)); + } } 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 1d80e34410..812d362a96 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 @@ -2,7 +2,7 @@ * #%L * Alfresco Remote API * %% - * Copyright (C) 2005 - 2022 Alfresco Software Limited + * 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 @@ -34,6 +34,7 @@ public class Category private String name; private String parentId; private boolean hasChildren; + private Integer count; public String getId() { @@ -80,20 +81,38 @@ public class Category this.hasChildren = hasChildren; } + public Integer getCount() + { + return count; + } + + public void setCount(Integer count) + { + this.count = count; + } + @Override public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; + if (this == o) + return true; + if (o == null || getClass() != o.getClass()) + return false; Category category = (Category) o; - return hasChildren == category.hasChildren && Objects.equals(id, category.id) && name.equals(category.name) && - Objects.equals(parentId, category.parentId); + return hasChildren == category.hasChildren && Objects.equals(id, category.id) && Objects.equals(name, category.name) && Objects.equals(parentId, category.parentId) + && Objects.equals(count, category.count); } @Override public int hashCode() { - return Objects.hash(id, name, parentId, hasChildren); + return Objects.hash(id, name, parentId, hasChildren, count); + } + + @Override + public String toString() + { + return "Category{" + "id='" + id + '\'' + ", name='" + name + '\'' + ", parentId='" + parentId + '\'' + ", hasChildren=" + hasChildren + ", count=" + count + '}'; } public static Builder builder() @@ -107,6 +126,7 @@ public class Category private String name; private String parentId; private boolean hasChildren; + private Integer count; public Builder id(String id) { @@ -132,6 +152,12 @@ public class Category return this; } + public Builder count(Integer count) + { + this.count = count; + return this; + } + public Category create() { final Category category = new Category(); @@ -139,6 +165,7 @@ public class Category category.setName(name); category.setParentId(parentId); category.setHasChildren(hasChildren); + category.setCount(count); return category; } } diff --git a/remote-api/src/test/java/org/alfresco/AppContextExtraTestSuite.java b/remote-api/src/test/java/org/alfresco/AppContextExtraTestSuite.java index 4dcdda433a..1963f197c6 100644 --- a/remote-api/src/test/java/org/alfresco/AppContextExtraTestSuite.java +++ b/remote-api/src/test/java/org/alfresco/AppContextExtraTestSuite.java @@ -2,7 +2,7 @@ * #%L * Alfresco Repository * %% - * Copyright (C) 2005 - 2022 Alfresco Software Limited + * 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 @@ -49,6 +49,7 @@ import org.junit.runners.Suite; org.alfresco.repo.webdav.WebDAVHelperTest.class, org.alfresco.repo.webdav.WebDAVLockServiceImplTest.class, org.alfresco.rest.api.RulesUnitTests.class, + org.alfresco.rest.api.CategoriesUnitTests.class, org.alfresco.rest.api.impl.ContentStorageInformationImplTest.class, org.alfresco.rest.api.nodes.NodeStorageInfoRelationTest.class, org.alfresco.rest.api.search.ResultMapperTests.class, diff --git a/remote-api/src/test/java/org/alfresco/rest/api/CategoriesUnitTests.java b/remote-api/src/test/java/org/alfresco/rest/api/CategoriesUnitTests.java new file mode 100644 index 0000000000..9f79b10a5a --- /dev/null +++ b/remote-api/src/test/java/org/alfresco/rest/api/CategoriesUnitTests.java @@ -0,0 +1,47 @@ +/* + * #%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.api; + +import org.alfresco.rest.api.categories.CategoriesEntityResourceTest; +import org.alfresco.rest.api.categories.NodesCategoryLinksRelationTest; +import org.alfresco.rest.api.categories.SubcategoriesRelationTest; +import org.alfresco.rest.api.impl.CategoriesImplTest; +import org.alfresco.service.Experimental; +import org.junit.runner.RunWith; +import org.junit.runners.Suite; + +@Experimental +@RunWith(Suite.class) +@Suite.SuiteClasses({ + CategoriesImplTest.class, + CategoriesEntityResourceTest.class, + SubcategoriesRelationTest.class, + NodesCategoryLinksRelationTest.class +}) +public class CategoriesUnitTests +{ +} diff --git a/remote-api/src/test/java/org/alfresco/rest/api/categories/CategoriesEntityResourceTest.java b/remote-api/src/test/java/org/alfresco/rest/api/categories/CategoriesEntityResourceTest.java index 56d2e72eca..990852f623 100644 --- a/remote-api/src/test/java/org/alfresco/rest/api/categories/CategoriesEntityResourceTest.java +++ b/remote-api/src/test/java/org/alfresco/rest/api/categories/CategoriesEntityResourceTest.java @@ -2,7 +2,7 @@ * #%L * Alfresco Remote API * %% - * Copyright (C) 2005 - 2022 Alfresco Software Limited + * 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 @@ -26,15 +26,21 @@ package org.alfresco.rest.api.categories; +import static org.alfresco.service.cmr.repository.StoreRef.STORE_REF_WORKSPACE_SPACESSTORE; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.assertEquals; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.BDDMockito.given; import static org.mockito.BDDMockito.then; +import static org.mockito.BDDMockito.willCallRealMethod; + +import java.util.List; import org.alfresco.rest.api.Categories; import org.alfresco.rest.api.model.Category; import org.alfresco.rest.framework.resource.parameters.Parameters; +import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; @@ -45,6 +51,7 @@ import org.mockito.junit.MockitoJUnitRunner; public class CategoriesEntityResourceTest { private static final String CATEGORY_ID = "category-node-id"; + @Mock private Categories categoriesMock; @Mock @@ -58,26 +65,37 @@ public class CategoriesEntityResourceTest @Test public void testReadCategoryById() { - given(categoriesMock.getCategoryById(CATEGORY_ID, parametersMock)).willReturn(categoryMock); + given(categoriesMock.getCategoryById(any(), any())).willCallRealMethod(); + given(categoriesMock.getCategoryById(any(), any(), any())).willReturn(categoryMock); //when final Category category = objectUnderTest.readById(CATEGORY_ID, parametersMock); - then(categoriesMock).should().getCategoryById(CATEGORY_ID, parametersMock); - then(categoriesMock).shouldHaveNoMoreInteractions(); + then(categoriesMock).should().getCategoryById(STORE_REF_WORKSPACE_SPACESSTORE, CATEGORY_ID, parametersMock); assertEquals(categoryMock, category); } @Test public void testUpdateCategoryById() { - given(categoriesMock.updateCategoryById(any(), any())).willReturn(categoryMock); + given(categoriesMock.updateCategoryById(any(), any(), any())).willCallRealMethod(); + given(categoriesMock.updateCategoryById(any(), any(), any(), any())).willReturn(categoryMock); // when final Category actualCategory = objectUnderTest.update(CATEGORY_ID, categoryMock, parametersMock); - then(categoriesMock).should().updateCategoryById(CATEGORY_ID, categoryMock); - then(categoriesMock).shouldHaveNoMoreInteractions(); + then(categoriesMock).should().updateCategoryById(STORE_REF_WORKSPACE_SPACESSTORE, CATEGORY_ID, categoryMock, parametersMock); assertThat(actualCategory).isNotNull(); } + + @Test + public void testDeleteCategoryById() + { + willCallRealMethod().given(categoriesMock).deleteCategoryById(any(), any()); + + // when + objectUnderTest.delete(CATEGORY_ID, parametersMock); + + then(categoriesMock).should().deleteCategoryById(STORE_REF_WORKSPACE_SPACESSTORE, CATEGORY_ID, parametersMock); + } } diff --git a/remote-api/src/test/java/org/alfresco/rest/api/categories/NodesCategoryLinksRelationTest.java b/remote-api/src/test/java/org/alfresco/rest/api/categories/NodesCategoryLinksRelationTest.java index 6aa9b4afde..08152d478f 100644 --- a/remote-api/src/test/java/org/alfresco/rest/api/categories/NodesCategoryLinksRelationTest.java +++ b/remote-api/src/test/java/org/alfresco/rest/api/categories/NodesCategoryLinksRelationTest.java @@ -47,6 +47,7 @@ import org.mockito.junit.MockitoJUnitRunner; public class NodesCategoryLinksRelationTest { private static final String CONTENT_ID = "content-node-id"; + private static final String CATEGORY_ID = "category-id"; @Mock private Categories categoriesMock; @@ -61,12 +62,12 @@ public class NodesCategoryLinksRelationTest @Test public void testReadAll() { - given(categoriesMock.listCategoriesForNode(any())).willReturn(List.of(categoryMock)); + given(categoriesMock.listCategoriesForNode(any(), any())).willReturn(List.of(categoryMock)); // when final CollectionWithPagingInfo actualCategoriesPage = objectUnderTest.readAll(CONTENT_ID, parametersMock); - then(categoriesMock).should().listCategoriesForNode(CONTENT_ID); + then(categoriesMock).should().listCategoriesForNode(CONTENT_ID, parametersMock); then(categoriesMock).shouldHaveNoMoreInteractions(); assertThat(actualCategoriesPage) .isNotNull() @@ -77,15 +78,25 @@ public class NodesCategoryLinksRelationTest @Test public void testCreate() { - given(categoriesMock.linkNodeToCategories(any(), any())).willReturn(List.of(categoryMock)); + given(categoriesMock.linkNodeToCategories(any(), any(), any())).willReturn(List.of(categoryMock)); // when final List actualCategories = objectUnderTest.create(CONTENT_ID, List.of(categoryMock), parametersMock); - then(categoriesMock).should().linkNodeToCategories(CONTENT_ID, List.of(categoryMock)); + then(categoriesMock).should().linkNodeToCategories(CONTENT_ID, List.of(categoryMock), parametersMock); then(categoriesMock).shouldHaveNoMoreInteractions(); assertThat(actualCategories) .isNotNull() .isEqualTo(List.of(categoryMock)); } + + @Test + public void testDelete() + { + // when + objectUnderTest.delete(CONTENT_ID, CATEGORY_ID, parametersMock); + + then(categoriesMock).should().unlinkNodeFromCategory(CONTENT_ID, CATEGORY_ID, parametersMock); + then(categoriesMock).shouldHaveNoMoreInteractions(); + } } \ No newline at end of file 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 index 7b1948f7d2..8592017d63 100644 --- 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 @@ -2,7 +2,7 @@ * #%L * Alfresco Remote API * %% - * Copyright (C) 2005 - 2022 Alfresco Software Limited + * 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 @@ -26,7 +26,10 @@ package org.alfresco.rest.api.categories; +import static org.alfresco.service.cmr.repository.StoreRef.STORE_REF_WORKSPACE_SPACESSTORE; import static org.junit.Assert.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.BDDMockito.given; import static org.mockito.BDDMockito.then; @@ -37,8 +40,8 @@ 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.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; @@ -67,39 +70,37 @@ public class SubcategoriesRelationTest 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)); + given(categoriesMock.createSubcategories(any(), any(), any())).willCallRealMethod(); + given(categoriesMock.createSubcategories(any(), any(), any(), any())).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(); + then(categoriesMock).should().createSubcategories(STORE_REF_WORKSPACE_SPACESSTORE, PARENT_CATEGORY_ID, categoriesToCreate, parametersMock); assertEquals(List.of(category), categories); } @Test public void testGetCategoryChildren() { - final CollectionWithPagingInfo categoryChildren = getCategories(3); - given(categoriesMock.getCategoryChildren(PARENT_CATEGORY_ID, parametersMock)).willReturn(categoryChildren); + final List categoryChildren = getCategories(3); + given(categoriesMock.getCategoryChildren(any(), any())).willCallRealMethod(); + given(categoriesMock.getCategoryChildren(any(), any(), any())).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); + then(categoriesMock).should().getCategoryChildren(STORE_REF_WORKSPACE_SPACESSTORE, PARENT_CATEGORY_ID, parametersMock); + assertEquals(categoryChildren, returnedChildren.getCollection()); } - private CollectionWithPagingInfo getCategories(final int count) + private List 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()) - ); + return 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 80d110571a..9852d7da91 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 @@ -27,11 +27,13 @@ package org.alfresco.rest.api.impl; import static org.alfresco.rest.api.Nodes.PATH_ROOT; +import static org.alfresco.rest.api.impl.CategoriesImpl.INCLUDE_COUNT_PARAM; import static org.alfresco.rest.api.impl.CategoriesImpl.INVALID_NODE_TYPE; import static org.alfresco.rest.api.impl.CategoriesImpl.NOT_A_VALID_CATEGORY; import static org.alfresco.rest.api.impl.CategoriesImpl.NOT_NULL_OR_EMPTY; import static org.alfresco.rest.api.impl.CategoriesImpl.NO_PERMISSION_TO_CHANGE_CONTENT; import static org.alfresco.rest.api.impl.CategoriesImpl.NO_PERMISSION_TO_READ_CONTENT; +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.assertThatExceptionOfType; import static org.assertj.core.api.Assertions.catchThrowable; @@ -39,6 +41,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertThrows; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.BDDMockito.given; import static org.mockito.BDDMockito.then; @@ -64,7 +67,6 @@ import org.alfresco.rest.framework.core.exceptions.EntityNotFoundException; import org.alfresco.rest.framework.core.exceptions.InvalidArgumentException; import org.alfresco.rest.framework.core.exceptions.InvalidNodeTypeException; 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; @@ -77,6 +79,7 @@ import org.alfresco.service.cmr.security.PermissionService; import org.alfresco.service.namespace.NamespaceService; import org.alfresco.service.namespace.QName; import org.alfresco.service.namespace.RegexQNamePattern; +import org.alfresco.util.Pair; import org.alfresco.util.TypeConstraint; import org.apache.commons.lang3.StringUtils; import org.junit.Before; @@ -94,7 +97,7 @@ public class CategoriesImplTest private static final String PARENT_ID = "parent-node-id"; private static final String CAT_ROOT_NODE_ID = "cat-root-node-id"; private static final NodeRef CATEGORY_NODE_REF = createNodeRefWithId(CATEGORY_ID); - private static final Category CATEGORY = createDefaultCategoryWithName(CATEGORY_NAME); + 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); @@ -103,8 +106,6 @@ public class CategoriesImplTest @Mock private NodeService nodeServiceMock; @Mock - private Parameters parametersMock; - @Mock private AuthorityService authorityServiceMock; @Mock private CategoryService categoryServiceMock; @@ -116,6 +117,8 @@ public class CategoriesImplTest private PermissionService permissionServiceMock; @Mock private TypeConstraint typeConstraint; + @Mock + private Parameters parametersMock; @InjectMocks private CategoriesImpl objectUnderTest; @@ -130,12 +133,13 @@ public class CategoriesImplTest given(typeConstraint.matches(any())).willReturn(true); given(permissionServiceMock.hasReadPermission(any())).willReturn(AccessStatus.ALLOWED); given(permissionServiceMock.hasPermission(any(), any())).willReturn(AccessStatus.ALLOWED); + //given(parametersMock.getInclude()).willReturn(Co); } @Test public void shouldNotGetRootCategoryById() { - final NodeRef categoryRootNodeRef = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, CAT_ROOT_NODE_ID); + final NodeRef categoryRootNodeRef = createNodeRefWithId(CAT_ROOT_NODE_ID); given(nodesMock.validateNode(CAT_ROOT_NODE_ID)).willReturn(categoryRootNodeRef); given(categoryChildAssociationRefMock.getQName()).willReturn(ContentModel.ASPECT_GEN_CLASSIFIABLE); given(nodeServiceMock.getParentAssocs(categoryRootNodeRef)).willReturn(List.of(categoryChildAssociationRefMock)); @@ -155,31 +159,26 @@ public class CategoriesImplTest @Test public void testGetCategoryById_withChildren() { - final NodeRef categoryNodeRef = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, CATEGORY_ID); - given(nodesMock.validateNode(CATEGORY_ID)).willReturn(categoryNodeRef); - 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); - categoryNode.setParentId(parentNodeRef); + final NodeRef parentNodeRef = createNodeRefWithId(PARENT_ID); + final Node categoryNode = createNode(CATEGORY_NAME, CATEGORY_ID, parentNodeRef); given(nodesMock.getNode(CATEGORY_ID)).willReturn(categoryNode); - final ChildAssociationRef parentAssoc = new ChildAssociationRef(null, parentNodeRef, null, categoryNodeRef); - given(nodeServiceMock.getPrimaryParent(categoryNodeRef)).willReturn(parentAssoc); + final ChildAssociationRef parentAssoc = new ChildAssociationRef(null, parentNodeRef, null, CATEGORY_NODE_REF); + given(nodeServiceMock.getPrimaryParent(CATEGORY_NODE_REF)).willReturn(parentAssoc); final List dummyChildren = List.of(dummyChildAssociationRefMock); - given(nodeServiceMock.getChildAssocs(categoryNodeRef, RegexQNamePattern.MATCH_ALL, RegexQNamePattern.MATCH_ALL, false)) + given(nodeServiceMock.getChildAssocs(CATEGORY_NODE_REF, RegexQNamePattern.MATCH_ALL, RegexQNamePattern.MATCH_ALL, false)) .willReturn(dummyChildren); //when final Category category = objectUnderTest.getCategoryById(CATEGORY_ID, parametersMock); then(nodesMock).should().validateNode(CATEGORY_ID); - then(nodesMock).should().isSubClass(categoryNodeRef, ContentModel.TYPE_CATEGORY, false); + then(nodesMock).should().isSubClass(CATEGORY_NODE_REF, ContentModel.TYPE_CATEGORY, false); then(nodesMock).should().getNode(CATEGORY_ID); then(nodesMock).shouldHaveNoMoreInteractions(); - then(nodeServiceMock).should().getPrimaryParent(categoryNodeRef); + then(nodeServiceMock).should().getPrimaryParent(CATEGORY_NODE_REF); then(nodeServiceMock).should().getParentAssocs(parentNodeRef); - then(nodeServiceMock).should().getParentAssocs(categoryNodeRef); - then(nodeServiceMock).should().getChildAssocs(categoryNodeRef, RegexQNamePattern.MATCH_ALL, RegexQNamePattern.MATCH_ALL, false); + then(nodeServiceMock).should().getParentAssocs(CATEGORY_NODE_REF); + then(nodeServiceMock).should().getChildAssocs(CATEGORY_NODE_REF, RegexQNamePattern.MATCH_ALL, RegexQNamePattern.MATCH_ALL, false); then(nodeServiceMock).shouldHaveNoMoreInteractions(); then(categoryServiceMock).shouldHaveNoInteractions(); @@ -197,30 +196,25 @@ public class CategoriesImplTest @Test public void testGetCategoryById_withoutChildren() { - final NodeRef categoryNodeRef = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, CATEGORY_ID); - given(nodesMock.validateNode(CATEGORY_ID)).willReturn(categoryNodeRef); - 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); - categoryNode.setParentId(parentNodeRef); + final NodeRef parentNodeRef = createNodeRefWithId(PARENT_ID); + final Node categoryNode = createNode(CATEGORY_NAME, CATEGORY_ID, parentNodeRef); given(nodesMock.getNode(CATEGORY_ID)).willReturn(categoryNode); - final ChildAssociationRef parentAssoc = new ChildAssociationRef(null, parentNodeRef, null, categoryNodeRef); - given(nodeServiceMock.getPrimaryParent(categoryNodeRef)).willReturn(parentAssoc); - given(nodeServiceMock.getChildAssocs(categoryNodeRef, RegexQNamePattern.MATCH_ALL, RegexQNamePattern.MATCH_ALL, false)) + final ChildAssociationRef parentAssoc = new ChildAssociationRef(null, parentNodeRef, null, CATEGORY_NODE_REF); + given(nodeServiceMock.getPrimaryParent(CATEGORY_NODE_REF)).willReturn(parentAssoc); + given(nodeServiceMock.getChildAssocs(CATEGORY_NODE_REF, RegexQNamePattern.MATCH_ALL, RegexQNamePattern.MATCH_ALL, false)) .willReturn(Collections.emptyList()); //when final Category category = objectUnderTest.getCategoryById(CATEGORY_ID, parametersMock); then(nodesMock).should().validateNode(CATEGORY_ID); - then(nodesMock).should().isSubClass(categoryNodeRef, ContentModel.TYPE_CATEGORY, false); + then(nodesMock).should().isSubClass(CATEGORY_NODE_REF, ContentModel.TYPE_CATEGORY, false); then(nodesMock).should().getNode(CATEGORY_ID); then(nodesMock).shouldHaveNoMoreInteractions(); - then(nodeServiceMock).should().getPrimaryParent(categoryNodeRef); + then(nodeServiceMock).should().getPrimaryParent(CATEGORY_NODE_REF); then(nodeServiceMock).should().getParentAssocs(parentNodeRef); - then(nodeServiceMock).should().getParentAssocs(categoryNodeRef); - then(nodeServiceMock).should().getChildAssocs(categoryNodeRef, RegexQNamePattern.MATCH_ALL, RegexQNamePattern.MATCH_ALL, false); + then(nodeServiceMock).should().getParentAssocs(CATEGORY_NODE_REF); + then(nodeServiceMock).should().getChildAssocs(CATEGORY_NODE_REF, RegexQNamePattern.MATCH_ALL, RegexQNamePattern.MATCH_ALL, false); then(nodeServiceMock).shouldHaveNoMoreInteractions(); then(categoryServiceMock).shouldHaveNoInteractions(); @@ -235,6 +229,30 @@ public class CategoriesImplTest assertEquals(expectedCategory, category); } + @Test + public void testGetCategoryById_includeCount() + { + 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(INCLUDE_COUNT_PARAM)); + given(categoryServiceMock.getTopCategories(any(), any(), anyInt())).willReturn(List.of(new Pair<>(CATEGORY_NODE_REF, 1))); + + // when + final Category actualCategory = objectUnderTest.getCategoryById(CATEGORY_ID, parametersMock); + + then(categoryServiceMock).should().getTopCategories(STORE_REF_WORKSPACE_SPACESSTORE, ContentModel.ASPECT_GEN_CLASSIFIABLE, Integer.MAX_VALUE); + then(categoryServiceMock).shouldHaveNoMoreInteractions(); + + assertThat(actualCategory) + .isNotNull() + .extracting(Category::getCount) + .isNotNull() + .isEqualTo(1); + } + @Test public void testGetCategoryById_notACategory() { @@ -274,14 +292,9 @@ public class CategoriesImplTest public void testDeleteCategoryById_asAdmin() { given(authorityServiceMock.hasAdminAuthority()).willReturn(true); - final NodeRef categoryNodeRef = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, CATEGORY_ID); + final NodeRef categoryNodeRef = createNodeRefWithId(CATEGORY_ID); given(nodesMock.validateNode(CATEGORY_ID)).willReturn(categoryNodeRef); given(nodesMock.isSubClass(categoryNodeRef, ContentModel.TYPE_CATEGORY, false)).willReturn(true); - 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); - categoryNode.setParentId(parentNodeRef); //when objectUnderTest.deleteCategoryById(CATEGORY_ID, parametersMock); @@ -317,7 +330,7 @@ public class CategoriesImplTest public void testDeleteCategoryById_nonCategoryId() { given(authorityServiceMock.hasAdminAuthority()).willReturn(true); - final NodeRef categoryNodeRef = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, CATEGORY_ID); + final NodeRef categoryNodeRef = createNodeRefWithId(CATEGORY_ID); given(nodesMock.validateNode(CATEGORY_ID)).willReturn(categoryNodeRef); given(nodesMock.isSubClass(categoryNodeRef, ContentModel.TYPE_CATEGORY, false)).willReturn(false); @@ -336,7 +349,7 @@ public class CategoriesImplTest public void testDeleteCategoryById_rootCategory() { given(authorityServiceMock.hasAdminAuthority()).willReturn(true); - final NodeRef categoryRootNodeRef = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, CAT_ROOT_NODE_ID); + final NodeRef categoryRootNodeRef = createNodeRefWithId(CAT_ROOT_NODE_ID); given(nodesMock.validateNode(CAT_ROOT_NODE_ID)).willReturn(categoryRootNodeRef); given(nodesMock.isSubClass(categoryRootNodeRef, ContentModel.TYPE_CATEGORY, false)).willReturn(true); given(categoryChildAssociationRefMock.getQName()).willReturn(ContentModel.ASPECT_GEN_CLASSIFIABLE); @@ -359,13 +372,13 @@ public class CategoriesImplTest @Test public void testCreateCategoryUnderRoot() { - final NodeRef parentCategoryNodeRef = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, PATH_ROOT); + final NodeRef parentCategoryNodeRef = createNodeRefWithId(PATH_ROOT); given(categoryServiceMock.getRootCategoryNodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE)) .willReturn(Optional.of(parentCategoryNodeRef)); final NodeRef categoryNodeRef = createNodeRefWithId(CATEGORY_ID); given(categoryServiceMock.createCategory(parentCategoryNodeRef, CATEGORY_NAME)).willReturn(categoryNodeRef); - given(nodesMock.getNode(CATEGORY_ID)).willReturn(prepareCategoryNode()); - final ChildAssociationRef parentAssoc = new ChildAssociationRef(null, parentCategoryNodeRef, null, categoryNodeRef); + given(nodesMock.getNode(CATEGORY_ID)).willReturn(createNode()); + final ChildAssociationRef parentAssoc = createAssociationOf(parentCategoryNodeRef, categoryNodeRef); given(nodeServiceMock.getPrimaryParent(categoryNodeRef)).willReturn(parentAssoc); given(nodeServiceMock.getParentAssocs(parentCategoryNodeRef)).willReturn(List.of(parentAssoc)); given(nodeServiceMock.getChildAssocs(categoryNodeRef, RegexQNamePattern.MATCH_ALL, RegexQNamePattern.MATCH_ALL, false)) @@ -400,12 +413,12 @@ public class CategoriesImplTest @Test public void testCreateCategory() { - final NodeRef parentCategoryNodeRef = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, PARENT_ID); + final NodeRef parentCategoryNodeRef = createNodeRefWithId(PARENT_ID); given(nodesMock.validateNode(PARENT_ID)).willReturn(parentCategoryNodeRef); final NodeRef categoryNodeRef = createNodeRefWithId(CATEGORY_ID); given(categoryServiceMock.createCategory(parentCategoryNodeRef, CATEGORY_NAME)).willReturn(categoryNodeRef); - given(nodesMock.getNode(CATEGORY_ID)).willReturn(prepareCategoryNode()); - final ChildAssociationRef parentAssoc = new ChildAssociationRef(null, parentCategoryNodeRef, null, categoryNodeRef); + given(nodesMock.getNode(CATEGORY_ID)).willReturn(createNode()); + final ChildAssociationRef parentAssoc = createAssociationOf(parentCategoryNodeRef, categoryNodeRef); given(nodeServiceMock.getPrimaryParent(categoryNodeRef)).willReturn(parentAssoc); given(nodeServiceMock.getParentAssocs(parentCategoryNodeRef)).willReturn(List.of(parentAssoc)); given(nodeServiceMock.getChildAssocs(categoryNodeRef, RegexQNamePattern.MATCH_ALL, RegexQNamePattern.MATCH_ALL, false)) @@ -438,6 +451,35 @@ public class CategoriesImplTest assertEquals(expectedCategory, createdCategory); } + @Test + public void testCreateCategory_includeCount() + { + 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(INCLUDE_COUNT_PARAM)); + final List categoryModels = prepareCategories().stream().peek(category -> category.setCount(1)).collect(Collectors.toList()); + + // 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::getCount) + .isNotNull() + .isEqualTo(0); + } + @Test public void testCreateCategories_noPermissions() { @@ -494,7 +536,7 @@ public class CategoriesImplTest @Test public void testGetRootCategoryChildren() { - final NodeRef parentCategoryNodeRef = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, PATH_ROOT); + final NodeRef parentCategoryNodeRef = createNodeRefWithId(PATH_ROOT); given(categoryServiceMock.getRootCategoryNodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE)) .willReturn(Optional.of(parentCategoryNodeRef)); final int childrenCount = 3; @@ -503,7 +545,7 @@ public class CategoriesImplTest childAssociationRefMocks.forEach(this::prepareCategoryNodeMocks); //when - final CollectionWithPagingInfo categoryChildren = objectUnderTest.getCategoryChildren(PATH_ROOT, parametersMock); + final List categoryChildren = objectUnderTest.getCategoryChildren(PATH_ROOT, parametersMock); then(categoryServiceMock).should().getRootCategoryNodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE); then(categoryServiceMock).shouldHaveNoMoreInteractions(); @@ -521,16 +563,14 @@ public class CategoriesImplTest 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)); + assertEquals(childAssociationRefMocks.size(), categoryChildren.size()); + IntStream.range(0, childrenCount).forEach(i -> doCategoryAssertions(categoryChildren.get(i), i, PATH_ROOT)); } @Test public void testGetCategoryChildren() { - final NodeRef parentCategoryNodeRef = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, PARENT_ID); + 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; @@ -539,7 +579,7 @@ public class CategoriesImplTest childAssociationRefMocks.forEach(this::prepareCategoryNodeMocks); //when - final CollectionWithPagingInfo categoryChildren = objectUnderTest.getCategoryChildren(PARENT_ID, parametersMock); + final List categoryChildren = objectUnderTest.getCategoryChildren(PARENT_ID, parametersMock); then(nodesMock).should().validateNode(PARENT_ID); then(nodesMock).should().isSubClass(parentCategoryNodeRef, ContentModel.TYPE_CATEGORY, false); @@ -558,24 +598,47 @@ public class CategoriesImplTest 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)); + assertEquals(childAssociationRefMocks.size(), categoryChildren.size()); + IntStream.range(0, childrenCount).forEach(i -> doCategoryAssertions(categoryChildren.get(i), i, PARENT_ID)); + } + + @Test + public void testGetCategoryChildren_includeCount() + { + 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(INCLUDE_COUNT_PARAM)); + given(categoryServiceMock.getTopCategories(any(), any(), anyInt())).willReturn(List.of(new Pair<>(createNodeRefWithId(CATEGORY_ID.concat("-1")), 2))); + + // when + final List actualCategoryChildren = objectUnderTest.getCategoryChildren(PARENT_ID, parametersMock); + + then(categoryServiceMock).should().getTopCategories(STORE_REF_WORKSPACE_SPACESSTORE, ContentModel.ASPECT_GEN_CLASSIFIABLE, Integer.MAX_VALUE); + then(categoryServiceMock).shouldHaveNoMoreInteractions(); + + assertThat(actualCategoryChildren) + .isNotNull() + .hasSize(3) + .extracting(Category::getCount) + .isNotNull() + .isEqualTo(List.of(0, 2, 0)); } @Test public void testGetCategoryChildren_noChildren() { - final NodeRef parentCategoryNodeRef = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, PARENT_ID); + final NodeRef parentCategoryNodeRef = createNodeRefWithId(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); + final List categoryChildren = objectUnderTest.getCategoryChildren(PARENT_ID, parametersMock); then(nodesMock).should().validateNode(PARENT_ID); then(nodesMock).should().isSubClass(parentCategoryNodeRef, ContentModel.TYPE_CATEGORY, false); @@ -586,13 +649,13 @@ public class CategoriesImplTest then(authorityServiceMock).shouldHaveNoInteractions(); then(categoryServiceMock).shouldHaveNoInteractions(); - assertEquals(0, categoryChildren.getTotalItems().intValue()); + assertEquals(0, categoryChildren.size()); } @Test public void testGetCategoryChildren_wrongParentNodeType() { - final NodeRef parentCategoryNodeRef = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, PARENT_ID); + final NodeRef parentCategoryNodeRef = createNodeRefWithId(PARENT_ID); given(nodesMock.validateNode(PARENT_ID)).willReturn(parentCategoryNodeRef); given(nodesMock.isSubClass(parentCategoryNodeRef, ContentModel.TYPE_CATEGORY, false)).willReturn(false); @@ -633,12 +696,12 @@ public class CategoriesImplTest 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(prepareCategoryNode(categoryNewName)); + 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))); // when - final Category actualCategory = objectUnderTest.updateCategoryById(CATEGORY_ID, fixedCategory); + final Category actualCategory = objectUnderTest.updateCategoryById(CATEGORY_ID, fixedCategory, parametersMock); then(authorityServiceMock).should().hasAdminAuthority(); then(authorityServiceMock).shouldHaveNoMoreInteractions(); @@ -661,13 +724,41 @@ public class CategoriesImplTest .isEqualTo(expectedCategory); } + @Test + public void testUpdateCategoryById_includeCount() + { + final String categoryNewName = "categoryNewName"; + final Category fixedCategory = createCategoryOnlyWithName(categoryNewName); + fixedCategory.setCount(9); + 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(INCLUDE_COUNT_PARAM)); + given(categoryServiceMock.getTopCategories(any(), any(), anyInt())).willReturn(List.of(new Pair<>(CATEGORY_NODE_REF, 1))); + + // when + final Category actualCategory = objectUnderTest.updateCategoryById(CATEGORY_ID, fixedCategory, parametersMock); + + then(categoryServiceMock).should().getTopCategories(STORE_REF_WORKSPACE_SPACESSTORE, ContentModel.ASPECT_GEN_CLASSIFIABLE, Integer.MAX_VALUE); + then(categoryServiceMock).shouldHaveNoMoreInteractions(); + + assertThat(actualCategory) + .isNotNull() + .extracting(Category::getCount) + .isNotNull() + .isEqualTo(1); + } + @Test public void testUpdateCategoryById_noPermission() { given(authorityServiceMock.hasAdminAuthority()).willReturn(false); // when - assertThatExceptionOfType(PermissionDeniedException.class).isThrownBy(() -> objectUnderTest.updateCategoryById(CATEGORY_ID, CATEGORY)); + assertThatExceptionOfType(PermissionDeniedException.class).isThrownBy(() -> objectUnderTest.updateCategoryById(CATEGORY_ID, CATEGORY, parametersMock)); then(nodesMock).shouldHaveNoInteractions(); then(nodeServiceMock).shouldHaveNoInteractions(); @@ -679,7 +770,7 @@ public class CategoriesImplTest given(nodesMock.validateNode(any(String.class))).willThrow(EntityNotFoundException.class); // when - assertThatExceptionOfType(EntityNotFoundException.class).isThrownBy(() -> objectUnderTest.updateCategoryById(CATEGORY_ID, CATEGORY)); + assertThatExceptionOfType(EntityNotFoundException.class).isThrownBy(() -> objectUnderTest.updateCategoryById(CATEGORY_ID, CATEGORY, parametersMock)); then(nodeServiceMock).shouldHaveNoInteractions(); } @@ -690,7 +781,7 @@ public class CategoriesImplTest given(nodesMock.isSubClass(any(), any(), eq(false))).willReturn(false); // when - assertThatExceptionOfType(InvalidArgumentException.class).isThrownBy(() -> objectUnderTest.updateCategoryById(CATEGORY_ID, CATEGORY)) + assertThatExceptionOfType(InvalidArgumentException.class).isThrownBy(() -> objectUnderTest.updateCategoryById(CATEGORY_ID, CATEGORY, parametersMock)) .withMessageContaining(NOT_A_VALID_CATEGORY); then(nodeServiceMock).shouldHaveNoInteractions(); @@ -704,7 +795,7 @@ public class CategoriesImplTest given(categoryChildAssociationRefMock.getQName()).willReturn(ContentModel.ASPECT_GEN_CLASSIFIABLE); // when - assertThatExceptionOfType(InvalidArgumentException.class).isThrownBy(() -> objectUnderTest.updateCategoryById(PATH_ROOT, CATEGORY)) + assertThatExceptionOfType(InvalidArgumentException.class).isThrownBy(() -> objectUnderTest.updateCategoryById(PATH_ROOT, CATEGORY, parametersMock)) .withMessageContaining(NOT_A_VALID_CATEGORY); then(categoryServiceMock).should().getRootCategoryNodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE); @@ -727,7 +818,7 @@ public class CategoriesImplTest final Category categoryWithoutName = createCategoryOnlyWithName(invalidName); // when - assertThatExceptionOfType(InvalidArgumentException.class).isThrownBy(() -> objectUnderTest.updateCategoryById(CATEGORY_ID, categoryWithoutName)) + assertThatExceptionOfType(InvalidArgumentException.class).isThrownBy(() -> objectUnderTest.updateCategoryById(CATEGORY_ID, categoryWithoutName, parametersMock)) .withMessageContaining(NOT_NULL_OR_EMPTY); } } @@ -741,12 +832,12 @@ public class CategoriesImplTest 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(prepareCategoryNode(categoryNewName)); + 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))); // when - final Category actualCategory = objectUnderTest.updateCategoryById(CATEGORY_ID, categoryWithInvalidId); + final Category actualCategory = objectUnderTest.updateCategoryById(CATEGORY_ID, categoryWithInvalidId, parametersMock); final Category expectedCategory = createDefaultCategoryWithName(categoryNewName); assertThat(actualCategory) @@ -763,12 +854,12 @@ public class CategoriesImplTest 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(prepareCategoryNode(categoryNewName)); + 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))); // when - final Category actualCategory = objectUnderTest.updateCategoryById(CATEGORY_ID, categoryWithInvalidParentId); + final Category actualCategory = objectUnderTest.updateCategoryById(CATEGORY_ID, categoryWithInvalidParentId, parametersMock); final Category expectedCategory = createDefaultCategoryWithName(categoryNewName); assertThat(actualCategory) @@ -785,12 +876,12 @@ public class CategoriesImplTest 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(prepareCategoryNode(categoryNewName)); + 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))); // when - final Category actualCategory = objectUnderTest.updateCategoryById(CATEGORY_ID, categoryWithInvalidHasChildren); + final Category actualCategory = objectUnderTest.updateCategoryById(CATEGORY_ID, categoryWithInvalidHasChildren, parametersMock); final Category expectedCategory = createDefaultCategoryWithName(categoryNewName); assertThat(actualCategory) @@ -804,11 +895,11 @@ public class CategoriesImplTest final List categoryLinks = List.of(CATEGORY); final NodeRef categoryParentNodeRef = createNodeRefWithId(PARENT_ID); final ChildAssociationRef parentAssociation = createAssociationOf(categoryParentNodeRef, CATEGORY_NODE_REF); - given(nodesMock.getNode(any())).willReturn(prepareCategoryNode()); + given(nodesMock.getNode(any())).willReturn(createNode()); given(nodeServiceMock.getPrimaryParent(any())).willReturn(parentAssociation); // when - final List actualLinkedCategories = objectUnderTest.linkNodeToCategories(CONTENT_NODE_ID, categoryLinks); + final List actualLinkedCategories = objectUnderTest.linkNodeToCategories(CONTENT_NODE_ID, categoryLinks, parametersMock); then(nodesMock).should().validateNode(CONTENT_NODE_ID); then(permissionServiceMock).should().hasPermission(CONTENT_NODE_REF, PermissionService.CHANGE_PERMISSIONS); @@ -838,12 +929,12 @@ public class CategoriesImplTest { final NodeRef categoryParentNodeRef = createNodeRefWithId(PARENT_ID); final ChildAssociationRef parentAssociation = createAssociationOf(categoryParentNodeRef, CATEGORY_NODE_REF); - given(nodesMock.getNode(any())).willReturn(prepareCategoryNode()); + given(nodesMock.getNode(any())).willReturn(createNode()); given(nodeServiceMock.getPrimaryParent(any())).willReturn(parentAssociation); given(nodeServiceMock.hasAspect(any(), any())).willReturn(true); // when - final List actualLinkedCategories = objectUnderTest.linkNodeToCategories(CONTENT_NODE_ID, List.of(CATEGORY)); + final List actualLinkedCategories = objectUnderTest.linkNodeToCategories(CONTENT_NODE_ID, List.of(CATEGORY), parametersMock); then(nodesMock).should().getNode(CATEGORY_ID); then(nodeServiceMock).should().getChildAssocs(CATEGORY_NODE_REF, RegexQNamePattern.MATCH_ALL, RegexQNamePattern.MATCH_ALL, false); @@ -873,11 +964,11 @@ public class CategoriesImplTest final ChildAssociationRef categoryParentAssociation = createAssociationOf(categoryParentNodeRef, CATEGORY_NODE_REF); final ChildAssociationRef secondCategoryParentAssociation = createAssociationOf(categoryParentNodeRef, secondCategoryNodeRef); given(nodesMock.validateNode(secondCategoryId)).willReturn(secondCategoryNodeRef); - given(nodesMock.getNode(any())).willReturn(prepareCategoryNode(), prepareCategoryNode(secondCategoryName)); + given(nodesMock.getNode(any())).willReturn(createNode(), createNode(secondCategoryName)); given(nodeServiceMock.getPrimaryParent(any())).willReturn(categoryParentAssociation, secondCategoryParentAssociation); // when - final List actualLinkedCategories = objectUnderTest.linkNodeToCategories(CONTENT_NODE_ID, categoryLinks); + final List actualLinkedCategories = objectUnderTest.linkNodeToCategories(CONTENT_NODE_ID, categoryLinks, parametersMock); then(nodesMock).should().validateNode(CATEGORY_ID); then(nodesMock).should().validateNode(secondCategoryId); @@ -902,13 +993,13 @@ public class CategoriesImplTest final Serializable previousCategories = (Serializable) List.of(otherCategoryNodeRef); final NodeRef categoryParentNodeRef = createNodeRefWithId(PARENT_ID); final ChildAssociationRef parentAssociation = createAssociationOf(categoryParentNodeRef, CATEGORY_NODE_REF); - given(nodesMock.getNode(any())).willReturn(prepareCategoryNode()); + given(nodesMock.getNode(any())).willReturn(createNode()); given(nodeServiceMock.getPrimaryParent(any())).willReturn(parentAssociation); given(nodeServiceMock.hasAspect(any(), any())).willReturn(true); given(nodeServiceMock.getProperty(any(), eq(ContentModel.PROP_CATEGORIES))).willReturn(previousCategories); // when - final List actualLinkedCategories = objectUnderTest.linkNodeToCategories(CONTENT_NODE_ID, List.of(CATEGORY)); + final List actualLinkedCategories = objectUnderTest.linkNodeToCategories(CONTENT_NODE_ID, List.of(CATEGORY), parametersMock); final Serializable expectedCategories = (Serializable) Set.of(otherCategoryNodeRef, CATEGORY_NODE_REF); then(nodeServiceMock).should().setProperty(CONTENT_NODE_REF, ContentModel.PROP_CATEGORIES, expectedCategories); @@ -924,7 +1015,7 @@ public class CategoriesImplTest given(nodesMock.validateNode(CONTENT_NODE_ID)).willThrow(EntityNotFoundException.class); // when - final Throwable actualException = catchThrowable(() -> objectUnderTest.linkNodeToCategories(CONTENT_NODE_ID, List.of(CATEGORY))); + final Throwable actualException = catchThrowable(() -> objectUnderTest.linkNodeToCategories(CONTENT_NODE_ID, List.of(CATEGORY), parametersMock)); then(nodesMock).should().validateNode(CONTENT_NODE_ID); then(permissionServiceMock).shouldHaveNoInteractions(); @@ -939,7 +1030,7 @@ public class CategoriesImplTest given(permissionServiceMock.hasPermission(any(), any())).willReturn(AccessStatus.DENIED); // when - final Throwable actualException = catchThrowable(() -> objectUnderTest.linkNodeToCategories(CONTENT_NODE_ID, List.of(CATEGORY))); + final Throwable actualException = catchThrowable(() -> objectUnderTest.linkNodeToCategories(CONTENT_NODE_ID, List.of(CATEGORY), parametersMock)); then(nodesMock).should().validateNode(CONTENT_NODE_ID); then(permissionServiceMock).should().hasPermission(CONTENT_NODE_REF, PermissionService.CHANGE_PERMISSIONS); @@ -954,9 +1045,8 @@ public class CategoriesImplTest { given(typeConstraint.matches(any())).willReturn(false); - // when - final Throwable actualException = catchThrowable(() -> objectUnderTest.linkNodeToCategories(CONTENT_NODE_ID, List.of(CATEGORY))); + final Throwable actualException = catchThrowable(() -> objectUnderTest.linkNodeToCategories(CONTENT_NODE_ID, List.of(CATEGORY), parametersMock)); then(typeConstraint).should().matches(CONTENT_NODE_REF); then(nodeServiceMock).shouldHaveNoInteractions(); @@ -971,7 +1061,7 @@ public class CategoriesImplTest final List categoryLinks = Collections.emptyList(); // when - final Throwable actualException = catchThrowable(() -> objectUnderTest.linkNodeToCategories(CONTENT_NODE_ID, categoryLinks)); + final Throwable actualException = catchThrowable(() -> objectUnderTest.linkNodeToCategories(CONTENT_NODE_ID, categoryLinks, parametersMock)); then(nodesMock).shouldHaveNoInteractions(); then(permissionServiceMock).shouldHaveNoInteractions(); @@ -992,7 +1082,7 @@ public class CategoriesImplTest categoryLinks.add(categoryLinkWithEmptyId); // when - final Throwable actualException = catchThrowable(() -> objectUnderTest.linkNodeToCategories(CONTENT_NODE_ID, categoryLinks)); + final Throwable actualException = catchThrowable(() -> objectUnderTest.linkNodeToCategories(CONTENT_NODE_ID, categoryLinks, parametersMock)); then(nodeServiceMock).shouldHaveNoInteractions(); assertThat(actualException) @@ -1006,11 +1096,11 @@ public class CategoriesImplTest final List categoryLinks = List.of(CATEGORY, CATEGORY); final NodeRef categoryParentNodeRef = createNodeRefWithId(PARENT_ID); final ChildAssociationRef parentAssociation = createAssociationOf(categoryParentNodeRef, CATEGORY_NODE_REF); - given(nodesMock.getNode(any())).willReturn(prepareCategoryNode()); + given(nodesMock.getNode(any())).willReturn(createNode()); given(nodeServiceMock.getPrimaryParent(any())).willReturn(parentAssociation); // when - final List actualLinkedCategories = objectUnderTest.linkNodeToCategories(CONTENT_NODE_ID, categoryLinks); + final List actualLinkedCategories = objectUnderTest.linkNodeToCategories(CONTENT_NODE_ID, categoryLinks, parametersMock); final Map expectedProperties = Map.of(ContentModel.PROP_CATEGORIES, (Serializable) List.of(CATEGORY_NODE_REF)); then(nodeServiceMock).should().addAspect(CONTENT_NODE_REF, ContentModel.ASPECT_GEN_CLASSIFIABLE, expectedProperties); @@ -1045,7 +1135,7 @@ public class CategoriesImplTest given(nodeServiceMock.hasAspect(CONTENT_NODE_REF, ContentModel.ASPECT_GEN_CLASSIFIABLE)).willReturn(false); //when - final Throwable actualException = catchThrowable(() -> objectUnderTest.unlinkNodeFromCategory(CONTENT_NODE_ID,CATEGORY_ID, parametersMock)); + final Throwable actualException = catchThrowable(() -> objectUnderTest.unlinkNodeFromCategory(CONTENT_NODE_ID, CATEGORY_ID, parametersMock)); then(nodeServiceMock).should().hasAspect(CONTENT_NODE_REF,ContentModel.ASPECT_GEN_CLASSIFIABLE); then(nodeServiceMock).shouldHaveNoMoreInteractions(); @@ -1060,11 +1150,11 @@ public class CategoriesImplTest 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(prepareCategoryNode()); + given(nodesMock.getNode(any())).willReturn(createNode()); given(nodeServiceMock.getPrimaryParent(any())).willReturn(parentAssociation); // when - final List actualCategories = objectUnderTest.listCategoriesForNode(CONTENT_NODE_ID); + final List actualCategories = objectUnderTest.listCategoriesForNode(CONTENT_NODE_ID, parametersMock); then(nodesMock).should().validateNode(CONTENT_NODE_ID); then(permissionServiceMock).should().hasReadPermission(CONTENT_NODE_REF); @@ -1090,7 +1180,7 @@ public class CategoriesImplTest given(nodesMock.validateNode(CONTENT_NODE_ID)).willThrow(EntityNotFoundException.class); // when - final Throwable actualException = catchThrowable(() -> objectUnderTest.listCategoriesForNode(CONTENT_NODE_ID)); + final Throwable actualException = catchThrowable(() -> objectUnderTest.listCategoriesForNode(CONTENT_NODE_ID, parametersMock)); then(nodesMock).should().validateNode(CONTENT_NODE_ID); then(nodeServiceMock).shouldHaveNoInteractions(); @@ -1104,7 +1194,7 @@ public class CategoriesImplTest given(permissionServiceMock.hasReadPermission(any())).willReturn(AccessStatus.DENIED); // when - final Throwable actualException = catchThrowable(() -> objectUnderTest.listCategoriesForNode(CONTENT_NODE_ID)); + final Throwable actualException = catchThrowable(() -> objectUnderTest.listCategoriesForNode(CONTENT_NODE_ID, parametersMock)); then(nodesMock).should().validateNode(CONTENT_NODE_ID); then(permissionServiceMock).should().hasReadPermission(CONTENT_NODE_REF); @@ -1120,7 +1210,7 @@ public class CategoriesImplTest given(typeConstraint.matches(any())).willReturn(false); // when - final Throwable actualException = catchThrowable(() -> objectUnderTest.listCategoriesForNode(CONTENT_NODE_ID)); + final Throwable actualException = catchThrowable(() -> objectUnderTest.listCategoriesForNode(CONTENT_NODE_ID, parametersMock)); then(typeConstraint).should().matches(CONTENT_NODE_REF); then(nodeServiceMock).shouldHaveNoInteractions(); @@ -1136,45 +1226,19 @@ public class CategoriesImplTest given(nodeServiceMock.getProperty(any(), eq(ContentModel.PROP_CATEGORIES))).willReturn((Serializable) nullOrEmptyList); // when - final List actualCategories = objectUnderTest.listCategoriesForNode(CONTENT_NODE_ID); + final List actualCategories = objectUnderTest.listCategoriesForNode(CONTENT_NODE_ID, parametersMock); assertThat(actualCategories).isNotNull().isEmpty(); }); } - 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; - } - - private Node prepareCategoryNode(final String name) - { - return prepareCategoryNode(name, CATEGORY_ID, createNodeRefWithId(PARENT_ID)); - } - - private Node prepareCategoryNode() - { - return prepareCategoryNode(CATEGORY_NAME); - } - - private List prepareCategories() - { - return List.of(Category.builder() - .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)); + .willReturn(createNodeRefWithId(CATEGORY_ID + "-" + i)); given(dummyChildAssocMock.getParentRef()).willReturn(parentCategoryNodeRef); return dummyChildAssocMock; }).collect(Collectors.toList()); @@ -1186,8 +1250,8 @@ public class CategoriesImplTest 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(nodesMock.getNode(id)).willReturn(createNode(name, id, parentRef)); + final ChildAssociationRef parentAssoc = createAssociationOf(parentRef, childRef); given(nodeServiceMock.getPrimaryParent(childRef)).willReturn(parentAssoc); given(nodeServiceMock.getParentAssocs(parentRef)).willReturn(List.of(parentAssoc)); } @@ -1195,14 +1259,38 @@ public class CategoriesImplTest 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(); + .id(CATEGORY_ID + "-" + index) + .name(CATEGORY_NAME + "-" + index) + .parentId(parentId) + .hasChildren(false) + .create(); assertEquals(expectedCategory, category); } + private List prepareCategories() + { + return List.of(createCategoryOnlyWithName(CATEGORY_NAME)); + } + + private static Node createNode(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; + } + + private static Node createNode(final String name) + { + return createNode(name, CATEGORY_ID, createNodeRefWithId(PARENT_ID)); + } + + private static Node createNode() + { + return createNode(CATEGORY_NAME); + } + private static NodeRef createNodeRefWithId(final String id) { return new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, id); @@ -1213,6 +1301,11 @@ public class CategoriesImplTest return Category.builder().name(name).create(); } + private static Category createDefaultCategory() + { + return createDefaultCategoryWithName(CATEGORY_NAME); + } + private static Category createDefaultCategoryWithName(final String name) { return Category.builder() diff --git a/repository/src/main/java/org/alfresco/service/cmr/search/CategoryService.java b/repository/src/main/java/org/alfresco/service/cmr/search/CategoryService.java index 410221f23d..91c9853aea 100644 --- a/repository/src/main/java/org/alfresco/service/cmr/search/CategoryService.java +++ b/repository/src/main/java/org/alfresco/service/cmr/search/CategoryService.java @@ -2,7 +2,7 @@ * #%L * Alfresco Repository * %% - * Copyright (C) 2005 - 2016 Alfresco Software Limited + * 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