diff --git a/packaging/tests/tas-restapi/src/main/java/org/alfresco/rest/model/RestCategoryModel.java b/packaging/tests/tas-restapi/src/main/java/org/alfresco/rest/model/RestCategoryModel.java index 5c94132075..e25c56e22a 100644 --- a/packaging/tests/tas-restapi/src/main/java/org/alfresco/rest/model/RestCategoryModel.java +++ b/packaging/tests/tas-restapi/src/main/java/org/alfresco/rest/model/RestCategoryModel.java @@ -128,5 +128,52 @@ This must be unique within the parent category. ", count=" + count + '}'; } + + public static Builder builder() + { + return new Builder(); + } + + public static class Builder + { + private String id; + private String name; + private String parentId; + private boolean hasChildren; + + public Builder id(String id) + { + this.id = id; + return this; + } + + public Builder name(String name) + { + this.name = name; + return this; + } + + public Builder parentId(String parentId) + { + this.parentId = parentId; + return this; + } + + public Builder hasChildren(boolean hasChildren) + { + this.hasChildren = hasChildren; + return this; + } + + public RestCategoryModel create() + { + final RestCategoryModel category = new RestCategoryModel(); + category.setId(id); + category.setName(name); + category.setParentId(parentId); + category.setHasChildren(hasChildren); + return category; + } + } } diff --git a/packaging/tests/tas-restapi/src/main/java/org/alfresco/rest/requests/Categories.java b/packaging/tests/tas-restapi/src/main/java/org/alfresco/rest/requests/Categories.java index 33697d5a65..9dbdb38ab1 100644 --- a/packaging/tests/tas-restapi/src/main/java/org/alfresco/rest/requests/Categories.java +++ b/packaging/tests/tas-restapi/src/main/java/org/alfresco/rest/requests/Categories.java @@ -71,7 +71,7 @@ public class Categories extends ModelRequest /** * Create single category. * - * @param restCategoryModel The categories to create. + * @param restCategoryModel The category to create. * @return Created category with additional data populated by the repository. */ public RestCategoryModel createSingleCategory(RestCategoryModel restCategoryModel) @@ -93,8 +93,19 @@ public class Categories extends ModelRequest } /** - * Delete category. + * Update single category. + * - PUT /categories/{categoryId} * + * @param restCategoryModel The categories to update. + * @return Created category with additional data populated by the repository. + */ + public RestCategoryModel updateCategory(RestCategoryModel restCategoryModel) + { + RestRequest request = RestRequest.requestWithBody(HttpMethod.PUT, restCategoryModel.toJson(), "categories/{categoryId}", category.getId()); + return restWrapper.processModel(RestCategoryModel.class, request); + } + /** + * Delete category. * - DELETE /categories/{categoryId} */ public void deleteCategory() 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 new file mode 100644 index 0000000000..660729bfb6 --- /dev/null +++ b/packaging/tests/tas-restapi/src/test/java/org/alfresco/rest/categories/CategoriesRestTest.java @@ -0,0 +1,91 @@ +/* + * #%L + * Alfresco Remote API + * %% + * Copyright (C) 2005 - 2022 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ + +package org.alfresco.rest.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 org.alfresco.rest.RestTest; +import org.alfresco.rest.model.RestCategoryModel; +import org.alfresco.utility.model.UserModel; +import org.testng.annotations.BeforeClass; + +abstract class CategoriesRestTest extends RestTest +{ + 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 UserModel user; + + @BeforeClass(alwaysRun = true) + public void dataPreparation() throws Exception + { + STEP("Create a user"); + user = dataUser.createRandomTestUser(); + } + + protected RestCategoryModel prepareCategoryUnderRoot() + { + return prepareCategoryUnder(ROOT_CATEGORY_ID); + } + + protected RestCategoryModel prepareCategoryUnder(final String parentId) + { + final RestCategoryModel parentCategory = createCategoryModelWithId(parentId); + final RestCategoryModel categoryModel = createCategoryModelWithName(getRandomName(CATEGORY_NAME_PREFIX)); + final RestCategoryModel createdCategory = restClient.authenticateUser(dataUser.getAdminUser()) + .withCoreAPI() + .usingCategory(parentCategory) + .createSingleCategory(categoryModel); + restClient.assertStatusCodeIs(CREATED); + + return createdCategory; + } + + protected RestCategoryModel createCategoryModelWithId(final String id) + { + return createCategoryModelWithIdAndName(id, null); + } + + protected RestCategoryModel createCategoryModelWithName(final String name) + { + return createCategoryModelWithIdAndName(null, name); + } + + protected RestCategoryModel createCategoryModelWithIdAndName(final String id, final String name) + { + return RestCategoryModel.builder() + .id(id) + .name(name) + .create(); + } +} 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 68e199dcf9..92ccb56d44 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 @@ -36,32 +36,17 @@ import java.util.List; import java.util.stream.Collectors; import java.util.stream.IntStream; -import org.alfresco.rest.RestTest; 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; -import org.alfresco.utility.model.UserModel; import org.apache.commons.lang3.StringUtils; -import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; -public class CreateCategoriesTests extends RestTest +public class CreateCategoriesTests extends CategoriesRestTest { - private static final String FIELD_NAME = "name"; - private static final String FIELD_PARENT_ID = "parentId"; - private static final String FIELD_HAS_CHILDREN = "hasChildren"; - private static final String FIELD_ID = "id"; - private UserModel user; - - @BeforeClass(alwaysRun = true) - public void dataPreparation() throws Exception - { - STEP("Create a user"); - user = dataUser.createRandomTestUser(); - } /** * Check we can create a category as direct child of root category @@ -70,10 +55,8 @@ public class CreateCategoriesTests extends RestTest public void testCreateCategoryUnderRoot() { STEP("Create a category under root category (as admin)"); - final RestCategoryModel rootCategory = new RestCategoryModel(); - rootCategory.setId("-root-"); - final RestCategoryModel aCategory = new RestCategoryModel(); - aCategory.setName(RandomData.getRandomName("Category")); + final RestCategoryModel rootCategory = createCategoryModelWithId(ROOT_CATEGORY_ID); + final RestCategoryModel aCategory = createCategoryModelWithName(RandomData.getRandomName("Category")); final RestCategoryModel createdCategory = restClient.authenticateUser(dataUser.getAdminUser()) .withCoreAPI() .usingCategory(rootCategory) @@ -92,10 +75,8 @@ public class CreateCategoriesTests extends RestTest public void testCreateCategoryWithoutName_andFail() { STEP("Create a category under root category (as admin)"); - final RestCategoryModel rootCategory = new RestCategoryModel(); - rootCategory.setId("-root-"); - final RestCategoryModel aCategory = new RestCategoryModel(); - aCategory.setName(StringUtils.EMPTY); + final RestCategoryModel rootCategory = createCategoryModelWithId(ROOT_CATEGORY_ID); + final RestCategoryModel aCategory = createCategoryModelWithName(StringUtils.EMPTY); restClient.authenticateUser(dataUser.getAdminUser()) .withCoreAPI() .usingCategory(rootCategory) @@ -110,10 +91,8 @@ public class CreateCategoriesTests extends RestTest public void testCreateSeveralSubCategories() { STEP("Create a category under root category (as admin)"); - final RestCategoryModel rootCategory = new RestCategoryModel(); - rootCategory.setId("-root-"); - final RestCategoryModel aCategory = new RestCategoryModel(); - aCategory.setName(RandomData.getRandomName("Category")); + final RestCategoryModel rootCategory = createCategoryModelWithId(ROOT_CATEGORY_ID); + final RestCategoryModel aCategory = createCategoryModelWithName(RandomData.getRandomName("Category")); final RestCategoryModel createdCategory = restClient.authenticateUser(dataUser.getAdminUser()) .withCoreAPI() .usingCategory(rootCategory) @@ -160,10 +139,8 @@ public class CreateCategoriesTests extends RestTest public void testCreateOver100SubCategories() { STEP("Create a category under root category (as admin)"); - final RestCategoryModel rootCategory = new RestCategoryModel(); - rootCategory.setId("-root-"); - final RestCategoryModel aCategory = new RestCategoryModel(); - aCategory.setName(RandomData.getRandomName("Category")); + final RestCategoryModel rootCategory = createCategoryModelWithId(ROOT_CATEGORY_ID); + final RestCategoryModel aCategory = createCategoryModelWithName(RandomData.getRandomName("Category")); final RestCategoryModel createdCategory = restClient.authenticateUser(dataUser.getAdminUser()) .withCoreAPI() .usingCategory(rootCategory) @@ -208,15 +185,13 @@ public class CreateCategoriesTests extends RestTest public void testCreateCategoryUnderRootAsRegularUser_andFail() { STEP("Create a category under root category (as user)"); - final RestCategoryModel rootCategory = new RestCategoryModel(); - rootCategory.setId("-root-"); - final RestCategoryModel aCategory = new RestCategoryModel(); - aCategory.setName(RandomData.getRandomName("Category")); + final RestCategoryModel rootCategory = createCategoryModelWithId(ROOT_CATEGORY_ID); + final RestCategoryModel aCategory = createCategoryModelWithName(RandomData.getRandomName("Category")); restClient.authenticateUser(user) .withCoreAPI() .usingCategory(rootCategory) .createSingleCategory(aCategory); - restClient.assertStatusCodeIs(FORBIDDEN).assertLastError().containsSummary("Current user does not have permission to create a category"); + restClient.assertStatusCodeIs(FORBIDDEN).assertLastError().containsSummary("Current user does not have permission to manage a category"); } /** @@ -226,11 +201,9 @@ public class CreateCategoriesTests extends RestTest public void testCreateCategoryUnderNonExistingParent_andFail() { STEP("Create a category under non existing category node (as admin)"); - final RestCategoryModel rootCategory = new RestCategoryModel(); final String id = "non-existing-node-id"; - rootCategory.setId(id); - final RestCategoryModel aCategory = new RestCategoryModel(); - aCategory.setName(RandomData.getRandomName("Category")); + final RestCategoryModel rootCategory = createCategoryModelWithId(id); + final RestCategoryModel aCategory = createCategoryModelWithName(RandomData.getRandomName("Category")); restClient.authenticateUser(dataUser.getAdminUser()) .withCoreAPI() .usingCategory(rootCategory) @@ -249,10 +222,8 @@ public class CreateCategoriesTests extends RestTest final FolderModel folder = dataContent.usingUser(user).usingSite(site).createFolder(); STEP("Create a category under folder node (as admin)"); - final RestCategoryModel rootCategory = new RestCategoryModel(); - rootCategory.setId(folder.getNodeRef()); - final RestCategoryModel aCategory = new RestCategoryModel(); - aCategory.setName(RandomData.getRandomName("Category")); + final RestCategoryModel rootCategory = createCategoryModelWithId(folder.getNodeRef()); + final RestCategoryModel aCategory = createCategoryModelWithName(RandomData.getRandomName("Category")); restClient.authenticateUser(dataUser.getAdminUser()) .withCoreAPI() .usingCategory(rootCategory) @@ -263,11 +234,7 @@ public class CreateCategoriesTests extends RestTest static List getCategoriesToCreate(final int count) { return IntStream.range(0, count) - .mapToObj(i -> { - final RestCategoryModel aSubCategory = new RestCategoryModel(); - aSubCategory.setName((RandomData.getRandomName("SubCategory"))); - return aSubCategory; - }) - .collect(Collectors.toList()); + .mapToObj(i -> RestCategoryModel.builder().name(RandomData.getRandomName("SubCategory")).create()) + .collect(Collectors.toList()); } } diff --git a/packaging/tests/tas-restapi/src/test/java/org/alfresco/rest/categories/DeleteCategoriesTests.java b/packaging/tests/tas-restapi/src/test/java/org/alfresco/rest/categories/DeleteCategoriesTests.java index e0b11e582c..779876017b 100644 --- a/packaging/tests/tas-restapi/src/test/java/org/alfresco/rest/categories/DeleteCategoriesTests.java +++ b/packaging/tests/tas-restapi/src/test/java/org/alfresco/rest/categories/DeleteCategoriesTests.java @@ -26,34 +26,20 @@ package org.alfresco.rest.categories; -import org.alfresco.rest.RestTest; -import org.alfresco.rest.model.RestCategoryModel; -import org.alfresco.utility.data.RandomData; -import org.alfresco.utility.model.FolderModel; -import org.alfresco.utility.model.SiteModel; -import org.alfresco.utility.model.TestGroup; -import org.alfresco.utility.model.UserModel; -import org.testng.annotations.BeforeClass; -import org.testng.annotations.Test; - import static org.alfresco.utility.report.log.Step.STEP; import static org.springframework.http.HttpStatus.BAD_REQUEST; -import static org.springframework.http.HttpStatus.CREATED; import static org.springframework.http.HttpStatus.FORBIDDEN; import static org.springframework.http.HttpStatus.NOT_FOUND; import static org.springframework.http.HttpStatus.NO_CONTENT; -public class DeleteCategoriesTests extends RestTest { +import org.alfresco.rest.model.RestCategoryModel; +import org.alfresco.utility.model.FolderModel; +import org.alfresco.utility.model.SiteModel; +import org.alfresco.utility.model.TestGroup; +import org.testng.annotations.Test; - private UserModel user; - - - @BeforeClass(alwaysRun = true) - public void dataPreparation() throws Exception - { - STEP("Create a user"); - user = dataUser.createRandomTestUser(); - } +public class DeleteCategoriesTests extends CategoriesRestTest +{ /** * Check we can delete a category. @@ -62,7 +48,7 @@ public class DeleteCategoriesTests extends RestTest { public void testDeleteCategory() { STEP("Create a category and send a request to delete it."); - RestCategoryModel aCategory = createCategory(); + RestCategoryModel aCategory = prepareCategoryUnderRoot(); restClient.authenticateUser(dataUser.getAdminUser()).withCoreAPI().usingCategory(aCategory).deleteCategory(); restClient.assertStatusCodeIs(NO_CONTENT); @@ -77,9 +63,9 @@ public class DeleteCategoriesTests extends RestTest { @Test(groups = {TestGroup.REST_API}) public void testDeleteCategoryAsRegularUser_andFail() { - RestCategoryModel aCategory = createCategory(); + RestCategoryModel aCategory = prepareCategoryUnderRoot(); restClient.authenticateUser(user).withCoreAPI().usingCategory(aCategory).deleteCategory(); - restClient.assertStatusCodeIs(FORBIDDEN).assertLastError().containsSummary("Current user does not have permission to delete a category"); + restClient.assertStatusCodeIs(FORBIDDEN).assertLastError().containsSummary("Current user does not have permission to manage a category"); } /** @@ -89,9 +75,8 @@ public class DeleteCategoriesTests extends RestTest { public void testDeleteNonExistentCategory() { STEP("Get category with non-existent id"); - final RestCategoryModel rootCategory = new RestCategoryModel(); final String id = "non-existing-dummy-id"; - rootCategory.setId(id); + final RestCategoryModel rootCategory = createCategoryModelWithId(id); STEP("Attempt to delete category with non-existent id and receive 404"); restClient.authenticateUser(dataUser.getAdminUser()).withCoreAPI().usingCategory(rootCategory).deleteCategory(); @@ -110,24 +95,8 @@ public class DeleteCategoriesTests extends RestTest { String id = folder.getNodeRef(); STEP("Create a category, set its id to the folder id and attempt to delete it"); - final RestCategoryModel aCategory = new RestCategoryModel(); - aCategory.setId(id); + final RestCategoryModel aCategory = createCategoryModelWithId(id); restClient.authenticateUser(dataUser.getAdminUser()).withCoreAPI().usingCategory(aCategory).deleteCategory(); restClient.assertStatusCodeIs(BAD_REQUEST).assertLastError().containsSummary("Node id does not refer to a valid category"); } - - public RestCategoryModel createCategory() - { - final RestCategoryModel rootCategory = new RestCategoryModel(); - rootCategory.setId("-root-"); - final RestCategoryModel aCategory = new RestCategoryModel(); - aCategory.setName(RandomData.getRandomName("Category")); - final RestCategoryModel createdCategory = restClient.authenticateUser(dataUser.getAdminUser()) - .withCoreAPI() - .usingCategory(rootCategory) - .createSingleCategory(aCategory); - restClient.assertStatusCodeIs(CREATED); - - return createdCategory; - } } diff --git a/packaging/tests/tas-restapi/src/test/java/org/alfresco/rest/categories/GetCategoriesTests.java b/packaging/tests/tas-restapi/src/test/java/org/alfresco/rest/categories/GetCategoriesTests.java index a8d2da0340..ac1e7c666e 100644 --- a/packaging/tests/tas-restapi/src/test/java/org/alfresco/rest/categories/GetCategoriesTests.java +++ b/packaging/tests/tas-restapi/src/test/java/org/alfresco/rest/categories/GetCategoriesTests.java @@ -35,35 +35,20 @@ import static org.testng.Assert.assertTrue; import java.util.List; import java.util.stream.Collectors; -import java.util.stream.IntStream; -import org.alfresco.rest.RestTest; -import org.alfresco.rest.core.RestResponse; import org.alfresco.rest.model.RestCategoryModel; import org.alfresco.rest.model.RestCategoryModelsCollection; import org.alfresco.utility.data.RandomData; import org.alfresco.utility.model.FolderModel; import org.alfresco.utility.model.SiteModel; import org.alfresco.utility.model.TestGroup; -import org.alfresco.utility.model.UserModel; -import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; -public class GetCategoriesTests extends RestTest +public class GetCategoriesTests extends CategoriesRestTest { private static final List DEFAULT_ROOT_CATEGORIES = List.of("Software Document Classification", "Languages", "Regions", "Tags"); - private static final String ROOT = "-root-"; private static final String NON_EXISTING_CATEGORY_ID = "non-existing-category-id"; - private UserModel user; - - @BeforeClass(alwaysRun = true) - public void dataPreparation() throws Exception - { - STEP("Create a user"); - user = dataUser.createRandomTestUser(); - } - /** * Check we can get a category which we just created in as direct child of root category */ @@ -71,22 +56,17 @@ public class GetCategoriesTests extends RestTest public void testGetCategoryById() { STEP("Create a category under root category (as admin)"); - final RestCategoryModel rootCategory = new RestCategoryModel(); - rootCategory.setId(ROOT); - final RestCategoryModel aCategory = new RestCategoryModel(); - aCategory.setName(RandomData.getRandomName("Category")); + final RestCategoryModel rootCategory = createCategoryModelWithId(ROOT_CATEGORY_ID); + final RestCategoryModel aCategory = createCategoryModelWithName(RandomData.getRandomName("Category")); final RestCategoryModel createdCategory = restClient.authenticateUser(dataUser.getAdminUser()) .withCoreAPI() .usingCategory(rootCategory) .createSingleCategory(aCategory); restClient.assertStatusCodeIs(CREATED); - createdCategory.assertThat() - .field("name").is(aCategory.getName()); - createdCategory.assertThat() - .field("parentId").is(rootCategory.getId()); - createdCategory.assertThat() - .field("hasChildren").is(false); + createdCategory.assertThat().field("name").is(aCategory.getName()); + createdCategory.assertThat().field("parentId").is(rootCategory.getId()); + createdCategory.assertThat().field("hasChildren").is(false); STEP("Get the created category (as regular user)"); final RestCategoryModel categoryFromGet = @@ -102,8 +82,7 @@ public class GetCategoriesTests extends RestTest public void testGetCategoryByIdProvidingRootAsId() { STEP("Get category with -root- as id"); - final RestCategoryModel rootCategory = new RestCategoryModel(); - rootCategory.setId(ROOT); + final RestCategoryModel rootCategory = createCategoryModelWithId(ROOT_CATEGORY_ID); restClient.authenticateUser(user).withCoreAPI().usingCategory(rootCategory).getCategory(); restClient.assertStatusCodeIs(BAD_REQUEST).assertLastError().containsSummary("Node id does not refer to a valid category"); } @@ -119,8 +98,7 @@ public class GetCategoriesTests extends RestTest final FolderModel folder = dataContent.usingUser(user).usingSite(site).createFolder(); STEP("Get category with folder id passed as id"); - final RestCategoryModel rootCategory = new RestCategoryModel(); - rootCategory.setId(folder.getNodeRef()); + final RestCategoryModel rootCategory = createCategoryModelWithId(folder.getNodeRef()); restClient.authenticateUser(user).withCoreAPI().usingCategory(rootCategory).getCategory(); restClient.assertStatusCodeIs(BAD_REQUEST).assertLastError().containsSummary("Node id does not refer to a valid category"); } @@ -132,9 +110,8 @@ public class GetCategoriesTests extends RestTest public void testGetCategoryByIdProvidingNonExistingId() { STEP("Get category with id which does not exist"); - final RestCategoryModel rootCategory = new RestCategoryModel(); final String id = NON_EXISTING_CATEGORY_ID; - rootCategory.setId(id); + final RestCategoryModel rootCategory = createCategoryModelWithId(id); restClient.authenticateUser(user).withCoreAPI().usingCategory(rootCategory).getCategory(); restClient.assertStatusCodeIs(NOT_FOUND).assertLastError().containsSummary(id); } @@ -146,8 +123,7 @@ public class GetCategoriesTests extends RestTest public void testGetCategoryChildren() { STEP("Get category children with -root- as parent id"); - final RestCategoryModel rootCategory = new RestCategoryModel(); - rootCategory.setId(ROOT); + final RestCategoryModel rootCategory = createCategoryModelWithId(ROOT_CATEGORY_ID); RestCategoryModelsCollection childCategoriesList = restClient.authenticateUser(user).withCoreAPI().usingCategory(rootCategory).getCategoryChildren(); restClient.assertStatusCodeIs(OK); @@ -159,8 +135,7 @@ public class GetCategoriesTests extends RestTest .collect(Collectors.toList()) .containsAll(DEFAULT_ROOT_CATEGORIES)); STEP("Create a new category under root and make sure it is returned as one of root's children"); - final RestCategoryModel aCategory = new RestCategoryModel(); - aCategory.setName((RandomData.getRandomName("newCategoryUnderRoot"))); + final RestCategoryModel aCategory = createCategoryModelWithName(RandomData.getRandomName("newCategoryUnderRoot")); final RestCategoryModel createdCategory = restClient.authenticateUser(dataUser.getAdminUser()) .withCoreAPI() .usingCategory(rootCategory) @@ -199,8 +174,7 @@ public class GetCategoriesTests extends RestTest final FolderModel folder = dataContent.usingUser(user).usingSite(site).createFolder(); STEP("Get category children with folder id passed as parent id"); - final RestCategoryModel parentCategory = new RestCategoryModel(); - parentCategory.setId(folder.getNodeRef()); + final RestCategoryModel parentCategory = createCategoryModelWithId(folder.getNodeRef()); restClient.authenticateUser(user).withCoreAPI().usingCategory(parentCategory).getCategoryChildren(); restClient.assertStatusCodeIs(BAD_REQUEST).assertLastError().containsSummary("Node id does not refer to a valid category"); } @@ -213,9 +187,8 @@ public class GetCategoriesTests extends RestTest { STEP("Get category with folder id passed as id"); - final RestCategoryModel parentCategory = new RestCategoryModel(); final String parentId = NON_EXISTING_CATEGORY_ID; - parentCategory.setId(parentId); + final RestCategoryModel parentCategory = createCategoryModelWithId(parentId); restClient.authenticateUser(user).withCoreAPI().usingCategory(parentCategory).getCategoryChildren(); restClient.assertStatusCodeIs(NOT_FOUND).assertLastError().containsSummary(parentId); } 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 new file mode 100644 index 0000000000..f439c07b6a --- /dev/null +++ b/packaging/tests/tas-restapi/src/test/java/org/alfresco/rest/categories/UpdateCategoriesTests.java @@ -0,0 +1,214 @@ +/* + * #%L + * Alfresco Remote API + * %% + * Copyright (C) 2005 - 2022 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ + +package org.alfresco.rest.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.FORBIDDEN; +import static org.springframework.http.HttpStatus.NOT_FOUND; +import static org.springframework.http.HttpStatus.OK; + +import org.alfresco.rest.model.RestCategoryModel; +import org.alfresco.utility.model.FolderModel; +import org.alfresco.utility.model.SiteModel; +import org.alfresco.utility.model.TestGroup; +import org.alfresco.utility.model.UserModel; +import org.testng.annotations.Test; + +public class UpdateCategoriesTests extends CategoriesRestTest +{ + private static final String CATEGORY_NEW_NAME_PREFIX = "NewCategoryName"; + private static final String IGNORE_FIELD_NAME = FIELD_NAME; + + /** + * Update a category (direct child of root category) + */ + @Test(groups = { TestGroup.REST_API}) + public void testUpdateCategory_asAdmin() + { + STEP("Prepare as admin a category under root category"); + final RestCategoryModel createdCategory = prepareCategoryUnderRoot(); + + STEP("Update as admin newly created category"); + final String categoryNewName = getRandomName(CATEGORY_NEW_NAME_PREFIX); + final RestCategoryModel fixedCategoryModel = createCategoryModelWithName(categoryNewName); + final RestCategoryModel updatedCategory = restClient.authenticateUser(dataUser.getAdminUser()) + .withCoreAPI() + .usingCategory(createdCategory) + .updateCategory(fixedCategoryModel); + + restClient.assertStatusCodeIs(OK); + updatedCategory.assertThat().isEqualTo(createdCategory, IGNORE_FIELD_NAME); + updatedCategory.assertThat().field(FIELD_NAME).isNot(createdCategory.getName()); + updatedCategory.assertThat().field(FIELD_NAME).is(categoryNewName); + } + + /** + * Update a subcategory of root's child category + */ + @Test(groups = { TestGroup.REST_API}) + public void testUpdateSubcategory_asAdmin() + { + STEP("Prepare as admin a category under root category"); + final RestCategoryModel createdCategory = prepareCategoryUnderRoot(); + + STEP("Prepare as admin a subcategory of root's child category"); + final RestCategoryModel createdSubcategory = prepareCategoryUnder(createdCategory.getId()); + + STEP("Update as admin newly created subcategory"); + final String categoryNewName = getRandomName(CATEGORY_NEW_NAME_PREFIX); + final RestCategoryModel fixedCategoryModel = createCategoryModelWithName(categoryNewName); + final RestCategoryModel updatedCategory = restClient.authenticateUser(dataUser.getAdminUser()) + .withCoreAPI() + .usingCategory(createdSubcategory) + .updateCategory(fixedCategoryModel); + + restClient.assertStatusCodeIs(OK); + updatedCategory.assertThat().isEqualTo(createdSubcategory, IGNORE_FIELD_NAME); + updatedCategory.assertThat().field(FIELD_NAME).is(categoryNewName); + } + + /** + * Try to update a category as a user and expect 403 (Forbidden) in response + */ + @Test(groups = { TestGroup.REST_API}) + public void testUpdateCategory_asUserAndExpect403() + { + STEP("Prepare as admin a category under root category"); + final RestCategoryModel createdCategory = prepareCategoryUnderRoot(); + + STEP("Try to update as user newly created category"); + final RestCategoryModel fixedCategoryModel = createCategoryModelWithName(getRandomName(CATEGORY_NEW_NAME_PREFIX)); + restClient.authenticateUser(user) + .withCoreAPI() + .usingCategory(createdCategory) + .updateCategory(fixedCategoryModel); + + restClient.assertStatusCodeIs(FORBIDDEN); + } + + /** + * Try to update a non-existing category and receive 404 (Not Found) + */ + @Test(groups = { TestGroup.REST_API}) + public void testUpdateCategory_usingNonExistingCategoryAndExpect404() + { + STEP("Create a fake parent category"); + final RestCategoryModel nonExistingCategory = createCategoryModelWithIdAndName("non-existing-dummy-id", getRandomName(CATEGORY_NAME_PREFIX)); + + STEP("Try to update as admin fake category"); + final RestCategoryModel fixedCategoryModel = createCategoryModelWithName(getRandomName(CATEGORY_NEW_NAME_PREFIX)); + restClient.authenticateUser(dataUser.getAdminUser()) + .withCoreAPI() + .usingCategory(nonExistingCategory) + .updateCategory(fixedCategoryModel); + + restClient.assertStatusCodeIs(NOT_FOUND); + } + + /** + * Try to update a non-category (folder) node and receive 400 (Bad Request) + */ + @Test(groups = { TestGroup.REST_API}) + public void testUpdateCategory_usingFolderNodeAndExpect400() + { + STEP("Prepare a site and a folder inside it"); + final UserModel user = dataUser.createRandomTestUser(); + final SiteModel site = dataSite.usingUser(user).createPublicRandomSite(); + final FolderModel folder = dataContent.usingUser(user).usingSite(site).createFolder(); + final RestCategoryModel categoryWithFolderId = createCategoryModelWithIdAndName(folder.getNodeRef(), getRandomName(CATEGORY_NAME_PREFIX)); + + STEP("Try to update as admin folder node as category"); + final RestCategoryModel fixedCategoryModel = createCategoryModelWithName(getRandomName(CATEGORY_NEW_NAME_PREFIX)); + restClient.authenticateUser(dataUser.getAdminUser()) + .withCoreAPI() + .usingCategory(categoryWithFolderId) + .updateCategory(fixedCategoryModel); + + restClient.assertStatusCodeIs(BAD_REQUEST); + } + + /** + * Try to update a root category and receive 400 (Bad Request) + */ + @Test(groups = { TestGroup.REST_API}) + public void testUpdateCategory_usingRootCategoryAndExpect400() + { + STEP("Create root category model"); + final RestCategoryModel rootCategoryModel = createCategoryModelWithId(ROOT_CATEGORY_ID); + + STEP("Try to update as admin root category"); + final RestCategoryModel fixedCategoryModel = createCategoryModelWithName(getRandomName(CATEGORY_NEW_NAME_PREFIX)); + restClient.authenticateUser(dataUser.getAdminUser()) + .withCoreAPI() + .usingCategory(rootCategoryModel) + .updateCategory(fixedCategoryModel); + + restClient.assertStatusCodeIs(BAD_REQUEST); + } + + /** + * Try to update a category with an empty name and receive 400 (Bad Request) + */ + @Test(groups = { TestGroup.REST_API}) + public void testUpdateCategory_withEmptyNameAndExpect400() + { + STEP("Prepare as admin a category under root category"); + final RestCategoryModel createdCategory = prepareCategoryUnderRoot(); + + STEP("Try to update as admin newly created category with a category without name"); + final RestCategoryModel fixedCategoryModel = createCategoryModelWithName(null); + restClient.authenticateUser(dataUser.getAdminUser()) + .withCoreAPI() + .usingCategory(createdCategory) + .updateCategory(fixedCategoryModel); + + restClient.assertStatusCodeIs(BAD_REQUEST); + } + + /** + * Try to update a category with an invalid, but not important ID in body and receive 200 (OK) + */ + @Test(groups = { TestGroup.REST_API}) + public void testUpdateCategory_withIgnoredInvalidIdInBodyAndExpect200() + { + STEP("Prepare as admin a category under root category"); + final RestCategoryModel createdCategory = prepareCategoryUnderRoot(); + + STEP("Try to update as admin newly created category with a category with invalid ID and receive 200"); + final String categoryNewName = getRandomName(CATEGORY_NEW_NAME_PREFIX); + final RestCategoryModel fixedCategoryModel = createCategoryModelWithIdAndName("non-existing-dummy-id", categoryNewName); + final RestCategoryModel updatedCategory = restClient.authenticateUser(dataUser.getAdminUser()) + .withCoreAPI() + .usingCategory(createdCategory) + .updateCategory(fixedCategoryModel); + + restClient.assertStatusCodeIs(OK); + updatedCategory.assertThat().field(FIELD_NAME).is(categoryNewName); + } +} 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 a3babc5905..c5714f7acb 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 @@ -36,12 +36,21 @@ import org.alfresco.service.Experimental; @Experimental public interface Categories { - Category getCategoryById(String id, Parameters params); + Category getCategoryById(String id, Parameters parameters); List createSubcategories(String parentCategoryId, List categories, Parameters parameters); - CollectionWithPagingInfo getCategoryChildren(String parentCategoryId, Parameters params); + CollectionWithPagingInfo getCategoryChildren(String parentCategoryId, Parameters parameters); - void deleteCategoryById(String id, Parameters params); + /** + * Update category by ID. Currently, it's possible only to update the name of category. + * + * @param id Category ID. + * @param fixedCategoryModel Fixed category model. + * @return Updated category. + */ + Category updateCategoryById(String id, Category fixedCategoryModel); + + void deleteCategoryById(String id, Parameters 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 df8483958f..23b79067ac 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 @@ -42,7 +42,8 @@ import org.alfresco.rest.framework.resource.parameters.Parameters; */ @EntityResource(name = "categories", title = "Categories") public class CategoriesEntityResource implements EntityResourceAction.ReadById, - EntityResourceAction.Delete + EntityResourceAction.Update, + EntityResourceAction.Delete { private final Categories categories; @@ -51,20 +52,45 @@ public class CategoriesEntityResource implements EntityResourceAction.ReadById, RelationshipResourceAction.Read +public class SubcategoriesRelation implements RelationshipResourceAction.Create, + RelationshipResourceAction.Read { private final Categories categories; @@ -49,6 +50,9 @@ public class SubcategoriesRelation implements RelationshipResourceAction.Create< this.categories = categories; } + /** + * POST /categories/{categoryId}/subcategories + */ @WebApiDescription(title = "Create a category", description = "Creates one or more categories under a parent category", successStatus = HttpServletResponse.SC_CREATED) @@ -58,6 +62,9 @@ public class SubcategoriesRelation implements RelationshipResourceAction.Create< return categories.createSubcategories(parentCategoryId, categoryList, parameters); } + /** + * GET /categories/{categoryId}/subcategories + */ @WebApiDescription(title = "List category direct children", description = "Lists direct children of a parent category", successStatus = HttpServletResponse.SC_OK) 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 f00cede7a9..515e2263e4 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 @@ -49,6 +49,7 @@ import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.repository.StoreRef; import org.alfresco.service.cmr.search.CategoryService; import org.alfresco.service.cmr.security.AuthorityService; +import org.alfresco.service.namespace.QName; import org.alfresco.service.namespace.RegexQNamePattern; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang3.StringUtils; @@ -57,9 +58,9 @@ import org.apache.commons.lang3.StringUtils; public class CategoriesImpl implements Categories { static final String NOT_A_VALID_CATEGORY = "Node id does not refer to a valid category"; - static final String NO_PERMISSION_TO_CREATE_A_CATEGORY = "Current user does not have permission to create a category"; - static final String NO_PERMISSION_TO_DELETE_A_CATEGORY = "Current user does not have permission to delete a category"; - private static final String NOT_NULL_OR_EMPTY = "Category name must not be null or empty"; + static final String NO_PERMISSION_TO_MANAGE_A_CATEGORY = "Current user does not have permission to manage a category"; + static final String NOT_NULL_OR_EMPTY = "Category name must not be null or empty"; + static final String FIELD_NOT_MATCH = "Category field: %s does not match the original one"; private final AuthorityService authorityService; private final CategoryService categoryService; @@ -89,10 +90,7 @@ public class CategoriesImpl implements Categories @Override public List createSubcategories(String parentCategoryId, List categories, Parameters parameters) { - if (!authorityService.hasAdminAuthority()) - { - throw new PermissionDeniedException(NO_PERMISSION_TO_CREATE_A_CATEGORY); - } + verifyAdminAuthority(); final NodeRef parentNodeRef = getCategoryNodeRef(parentCategoryId); final List categoryNodeRefs = categories.stream() .map(c -> createCategoryNodeRef(parentNodeRef, c)) @@ -116,15 +114,26 @@ public class CategoriesImpl implements Categories } @Override - public void deleteCategoryById(String id, Parameters params) + public Category updateCategoryById(final String id, final Category fixedCategoryModel) { - if (!authorityService.hasAdminAuthority()) + verifyAdminAuthority(); + final NodeRef categoryNodeRef = getCategoryNodeRef(id); + if (isRootCategory(categoryNodeRef)) { - throw new PermissionDeniedException(NO_PERMISSION_TO_DELETE_A_CATEGORY); + throw new InvalidArgumentException(NOT_A_VALID_CATEGORY, new String[]{id}); } - final NodeRef nodeRef = nodes.validateNode(id); - if (isNotACategory(nodeRef) || isRootCategory(nodeRef)) + verifyCategoryFields(fixedCategoryModel); + + return mapToCategory(changeCategoryName(categoryNodeRef, fixedCategoryModel.getName())); + } + + @Override + public void deleteCategoryById(String id, Parameters parameters) + { + verifyAdminAuthority(); + final NodeRef nodeRef = getCategoryNodeRef(id); + if (isRootCategory(nodeRef)) { throw new InvalidArgumentException(NOT_A_VALID_CATEGORY, new String[]{id}); } @@ -132,6 +141,14 @@ public class CategoriesImpl implements Categories nodeService.deleteNode(nodeRef); } + private void verifyAdminAuthority() + { + if (!authorityService.hasAdminAuthority()) + { + throw new PermissionDeniedException(NO_PERMISSION_TO_MANAGE_A_CATEGORY); + } + } + /** * 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} @@ -164,17 +181,10 @@ public class CategoriesImpl implements Categories private NodeRef createCategoryNodeRef(NodeRef parentNodeRef, Category c) { - if (StringUtils.isEmpty(c.getName())) { - throw new InvalidArgumentException(NOT_NULL_OR_EMPTY); - } + verifyCategoryFields(c); return categoryService.createCategory(parentNodeRef, c.getName()); } - private boolean isNotACategory(NodeRef nodeRef) - { - return !nodes.isSubClass(nodeRef, ContentModel.TYPE_CATEGORY, false); - } - private Category mapToCategory(NodeRef nodeRef) { final Node categoryNode = nodes.getNode(nodeRef.getId()); @@ -188,6 +198,11 @@ public class CategoriesImpl implements Categories .create(); } + private boolean isNotACategory(NodeRef nodeRef) + { + return !nodes.isSubClass(nodeRef, ContentModel.TYPE_CATEGORY, false); + } + private boolean isRootCategory(final NodeRef nodeRef) { final List parentAssocs = nodeService.getParentAssocs(nodeRef); @@ -199,4 +214,37 @@ public class CategoriesImpl implements Categories final NodeRef parentRef = nodeService.getPrimaryParent(nodeRef).getParentRef(); return isRootCategory(parentRef) ? PATH_ROOT : parentRef.getId(); } + + /** + * Change category qualified name. + * + * @param categoryNodeRef Category node reference. + * @param newName New name. + * @return Updated category. + */ + private NodeRef changeCategoryName(final NodeRef categoryNodeRef, final String newName) + { + final ChildAssociationRef parentAssociation = nodeService.getPrimaryParent(categoryNodeRef); + if (parentAssociation == null) + { + throw new InvalidArgumentException(NOT_A_VALID_CATEGORY, new String[]{categoryNodeRef.getId()}); + } + + nodeService.setProperty(categoryNodeRef, ContentModel.PROP_NAME, newName); + final QName newQName = QName.createQName(parentAssociation.getQName().getNamespaceURI(), QName.createValidLocalName(newName)); + return nodeService.moveNode(parentAssociation.getChildRef(), parentAssociation.getParentRef(), parentAssociation.getTypeQName(), newQName).getChildRef(); + } + + /** + * Verify if fixed category name is not empty. + * + * @param fixedCategoryModel Fixed category model. + */ + private void verifyCategoryFields(final Category fixedCategoryModel) + { + if (StringUtils.isEmpty(fixedCategoryModel.getName())) + { + throw new InvalidArgumentException(NOT_NULL_OR_EMPTY); + } + } } 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 d9b3b6d959..b2f6eeb851 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 @@ -137,5 +137,4 @@ public class Category return category; } } - } 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 4988ae4f32..56d2e72eca 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 @@ -26,7 +26,9 @@ package org.alfresco.rest.api.categories; +import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.assertEquals; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.BDDMockito.given; import static org.mockito.BDDMockito.then; @@ -65,4 +67,17 @@ public class CategoriesEntityResourceTest then(categoriesMock).shouldHaveNoMoreInteractions(); assertEquals(categoryMock, category); } + + @Test + public void testUpdateCategoryById() + { + given(categoriesMock.updateCategoryById(any(), any())).willReturn(categoryMock); + + // when + final Category actualCategory = objectUnderTest.update(CATEGORY_ID, categoryMock, parametersMock); + + then(categoriesMock).should().updateCategoryById(CATEGORY_ID, categoryMock); + then(categoriesMock).shouldHaveNoMoreInteractions(); + assertThat(actualCategory).isNotNull(); + } } 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 f4e5dd6f62..ccd35b5b17 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,8 +27,15 @@ package org.alfresco.rest.api.impl; import static org.alfresco.rest.api.Nodes.PATH_ROOT; +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.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; 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.eq; import static org.mockito.BDDMockito.given; import static org.mockito.BDDMockito.then; import static org.mockito.Mockito.mock; @@ -56,7 +63,9 @@ import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.repository.StoreRef; import org.alfresco.service.cmr.search.CategoryService; import org.alfresco.service.cmr.security.AuthorityService; +import org.alfresco.service.namespace.QName; import org.alfresco.service.namespace.RegexQNamePattern; +import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; @@ -70,6 +79,8 @@ public class CategoriesImplTest private static final String CATEGORY_NAME = "categoryName"; 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); @Mock private Nodes nodesMock; @@ -89,12 +100,19 @@ public class CategoriesImplTest @InjectMocks private CategoriesImpl objectUnderTest; + @Before + public void setUp() throws Exception + { + given(authorityServiceMock.hasAdminAuthority()).willReturn(true); + given(nodesMock.validateNode(eq(CATEGORY_ID))).willReturn(CATEGORY_NODE_REF); + given(nodesMock.isSubClass(any(), any(), anyBoolean())).willReturn(true); + } + @Test public void shouldNotGetRootCategoryById() { final NodeRef categoryRootNodeRef = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, 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); given(nodeServiceMock.getParentAssocs(categoryRootNodeRef)).willReturn(List.of(categoryChildAssociationRefMock)); @@ -107,7 +125,7 @@ public class CategoriesImplTest then(nodeServiceMock).should().getParentAssocs(categoryRootNodeRef); then(nodeServiceMock).shouldHaveNoMoreInteractions(); then(categoryServiceMock).shouldHaveNoInteractions(); - then(authorityServiceMock).shouldHaveNoMoreInteractions(); + then(authorityServiceMock).shouldHaveNoInteractions(); } @Test @@ -115,7 +133,6 @@ public class CategoriesImplTest { final NodeRef categoryNodeRef = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, 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); @@ -158,7 +175,6 @@ public class CategoriesImplTest { final NodeRef categoryNodeRef = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, 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); @@ -319,11 +335,10 @@ public class CategoriesImplTest @Test public void testCreateCategoryUnderRoot() { - given(authorityServiceMock.hasAdminAuthority()).willReturn(true); final NodeRef parentCategoryNodeRef = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, PATH_ROOT); given(categoryServiceMock.getRootCategoryNodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE)) .willReturn(Optional.of(parentCategoryNodeRef)); - final NodeRef categoryNodeRef = prepareCategoryNodeRef(); + 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); @@ -361,11 +376,9 @@ public class CategoriesImplTest @Test public void testCreateCategory() { - given(authorityServiceMock.hasAdminAuthority()).willReturn(true); final NodeRef parentCategoryNodeRef = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, PARENT_ID); given(nodesMock.validateNode(PARENT_ID)).willReturn(parentCategoryNodeRef); - given(nodesMock.isSubClass(parentCategoryNodeRef, ContentModel.TYPE_CATEGORY, false)).willReturn(true); - final NodeRef categoryNodeRef = prepareCategoryNodeRef(); + 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); @@ -420,7 +433,6 @@ public class CategoriesImplTest @Test public void testCreateCategories_wrongParentNodeType() { - given(authorityServiceMock.hasAdminAuthority()).willReturn(true); final NodeRef parentCategoryNodeRef = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, PARENT_ID); given(nodesMock.validateNode(PARENT_ID)).willReturn(parentCategoryNodeRef); given(nodesMock.isSubClass(parentCategoryNodeRef, ContentModel.TYPE_CATEGORY, false)).willReturn(false); @@ -441,7 +453,6 @@ public class CategoriesImplTest @Test public void testCreateCategories_nonExistingParentNode() { - given(authorityServiceMock.hasAdminAuthority()).willReturn(true); given(nodesMock.validateNode(PARENT_ID)).willThrow(EntityNotFoundException.class); //when @@ -590,10 +601,177 @@ public class CategoriesImplTest then(authorityServiceMock).shouldHaveNoInteractions(); } - private Node prepareCategoryNode() + @Test + public void testUpdateCategoryById() { - final NodeRef parentNodeRef = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, PARENT_ID); - return prepareCategoryNode(CATEGORY_NAME, CATEGORY_ID, parentNodeRef); + final String categoryNewName = "categoryNewName"; + final Category fixedCategory = createCategoryOnlyWithName(categoryNewName); + 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(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); + + then(authorityServiceMock).should().hasAdminAuthority(); + then(authorityServiceMock).shouldHaveNoMoreInteractions(); + then(nodesMock).should().validateNode(CATEGORY_ID); + then(nodesMock).should().isSubClass(CATEGORY_NODE_REF, ContentModel.TYPE_CATEGORY, false); + then(nodesMock).should().getNode(CATEGORY_ID); + then(nodesMock).shouldHaveNoMoreInteractions(); + then(nodeServiceMock).should().getParentAssocs(CATEGORY_NODE_REF); + then(nodeServiceMock).should().getChildAssocs(CATEGORY_NODE_REF, RegexQNamePattern.MATCH_ALL, RegexQNamePattern.MATCH_ALL, false); + then(nodeServiceMock).should().setProperty(CATEGORY_NODE_REF, ContentModel.PROP_NAME, categoryNewName); + then(nodeServiceMock).should(times(2)).getPrimaryParent(CATEGORY_NODE_REF); + final QName expectedNewQName = createCmQNameOf(categoryNewName); + then(nodeServiceMock).should().moveNode(CATEGORY_NODE_REF, parentCategoryNodeRef, ContentModel.ASSOC_SUBCATEGORIES, expectedNewQName); + then(nodeServiceMock).should().getParentAssocs(parentCategoryNodeRef); + then(nodeServiceMock).shouldHaveNoMoreInteractions(); + then(categoryServiceMock).shouldHaveNoInteractions(); + final Category expectedCategory = createDefaultCategoryWithName(categoryNewName); + assertThat(actualCategory) + .isNotNull().usingRecursiveComparison() + .isEqualTo(expectedCategory); + } + + @Test + public void testUpdateCategoryById_noPermission() + { + given(authorityServiceMock.hasAdminAuthority()).willReturn(false); + + // when + assertThatExceptionOfType(PermissionDeniedException.class).isThrownBy(() -> objectUnderTest.updateCategoryById(CATEGORY_ID, CATEGORY)); + + then(nodesMock).shouldHaveNoInteractions(); + then(nodeServiceMock).shouldHaveNoInteractions(); + } + + @Test + public void testUpdateCategoryById_categoryNodeNotFound() + { + given(nodesMock.validateNode(any(String.class))).willThrow(EntityNotFoundException.class); + + // when + assertThatExceptionOfType(EntityNotFoundException.class).isThrownBy(() -> objectUnderTest.updateCategoryById(CATEGORY_ID, CATEGORY)); + + then(nodeServiceMock).shouldHaveNoInteractions(); + } + + @Test + public void testUpdateCategoryById_notACategory() + { + given(nodesMock.isSubClass(any(), any(), eq(false))).willReturn(false); + + // when + assertThatExceptionOfType(InvalidArgumentException.class).isThrownBy(() -> objectUnderTest.updateCategoryById(CATEGORY_ID, CATEGORY)) + .withMessageContaining(NOT_A_VALID_CATEGORY); + + then(nodeServiceMock).shouldHaveNoInteractions(); + } + + @Test + public void testUpdateCategoryById_isRootCategory() + { + given(categoryServiceMock.getRootCategoryNodeRef(any())).willReturn(Optional.of(createNodeRefWithId(PATH_ROOT))); + given(nodeServiceMock.getParentAssocs(any())).willReturn(List.of(categoryChildAssociationRefMock)); + given(categoryChildAssociationRefMock.getQName()).willReturn(ContentModel.ASPECT_GEN_CLASSIFIABLE); + + // when + assertThatExceptionOfType(InvalidArgumentException.class).isThrownBy(() -> objectUnderTest.updateCategoryById(PATH_ROOT, CATEGORY)) + .withMessageContaining(NOT_A_VALID_CATEGORY); + + then(categoryServiceMock).should().getRootCategoryNodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE); + then(categoryServiceMock).shouldHaveNoMoreInteractions(); + } + + private List getInvalidCategoryNames() + { + final List invalidNames = new ArrayList<>(); + invalidNames.add(null); + invalidNames.add(""); + return invalidNames; + } + + @Test + public void testUpdateCategoryById_emptyName() + { + for (String invalidName : getInvalidCategoryNames()) + { + final Category categoryWithoutName = createCategoryOnlyWithName(invalidName); + + // when + assertThatExceptionOfType(InvalidArgumentException.class).isThrownBy(() -> objectUnderTest.updateCategoryById(CATEGORY_ID, categoryWithoutName)) + .withMessageContaining(NOT_NULL_OR_EMPTY); + } + } + + @Test + public void testUpdateCategoryById_notMatchingIdField() + { + final String categoryNewName = "categoryNewName"; + final Category categoryWithInvalidId = createCategoryOnlyWithName(categoryNewName); + categoryWithInvalidId.setId("different-" + CATEGORY_ID); + 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(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 expectedCategory = createDefaultCategoryWithName(categoryNewName); + assertThat(actualCategory) + .isNotNull().usingRecursiveComparison() + .isEqualTo(expectedCategory); + } + + @Test + public void testUpdateCategoryById_notMatchingParentIdField() + { + final String categoryNewName = "categoryNewName"; + final Category categoryWithInvalidParentId = createCategoryOnlyWithName(categoryNewName); + categoryWithInvalidParentId.setParentId("different-" + PARENT_ID); + 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(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 expectedCategory = createDefaultCategoryWithName(categoryNewName); + assertThat(actualCategory) + .isNotNull().usingRecursiveComparison() + .isEqualTo(expectedCategory); + } + + @Test + public void testUpdateCategoryById_notMatchingHasChildrenField() + { + final String categoryNewName = "categoryNewName"; + final Category categoryWithInvalidHasChildren = createCategoryOnlyWithName(categoryNewName); + categoryWithInvalidHasChildren.setHasChildren(true); + 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(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 expectedCategory = createDefaultCategoryWithName(categoryNewName); + assertThat(actualCategory) + .isNotNull().usingRecursiveComparison() + .isEqualTo(expectedCategory); } private Node prepareCategoryNode(final String name, final String id, final NodeRef parentNodeRef) @@ -605,9 +783,15 @@ public class CategoriesImplTest return categoryNode; } - private NodeRef prepareCategoryNodeRef() + private Node prepareCategoryNode(final String name) { - return new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, CATEGORY_ID); + final NodeRef parentNodeRef = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, PARENT_ID); + return prepareCategoryNode(name, CATEGORY_ID, parentNodeRef); + } + + private Node prepareCategoryNode() + { + return prepareCategoryNode(CATEGORY_NAME); } private List prepareCategories() @@ -626,8 +810,7 @@ public class CategoriesImplTest .willReturn(new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, CATEGORY_ID + "-" + i)); given(dummyChildAssocMock.getParentRef()).willReturn(parentCategoryNodeRef); return dummyChildAssocMock; - }) - .collect(Collectors.toList()); + }).collect(Collectors.toList()); } private void prepareCategoryNodeMocks(ChildAssociationRef childAssociationRef) @@ -652,4 +835,34 @@ public class CategoriesImplTest .create(); assertEquals(expectedCategory, category); } + + private static NodeRef createNodeRefWithId(final String id) + { + return new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, id); + } + + private static Category createCategoryOnlyWithName(final String name) + { + return Category.builder().name(name).create(); + } + + private static Category createDefaultCategoryWithName(final String name) + { + return Category.builder() + .id(CATEGORY_ID) + .name(name) + .parentId(PARENT_ID) + .hasChildren(false) + .create(); + } + + private static QName createCmQNameOf(final String name) + { + return QName.createQName(ContentModel.TYPE_CATEGORY.getNamespaceURI(), QName.createValidLocalName(name)); + } + + private static ChildAssociationRef createAssociationOf(final NodeRef parentNode, final NodeRef childNode, final QName childNodeName) + { + return new ChildAssociationRef(ContentModel.ASSOC_SUBCATEGORIES, parentNode, childNodeName, childNode); + } }