From 90ca0dea6c1f1a6f503d16c59dfca85d3d57d973 Mon Sep 17 00:00:00 2001 From: Maciej Pichura <41297682+mpichura@users.noreply.github.com> Date: Wed, 7 Dec 2022 10:32:08 +0100 Subject: [PATCH] ACS-4028 GET Category by id (#1601) * ACS-4028 Get category by id (#1591) * ACS-4028: Get category by id endpoint. * ACS-4028: Get category by id endpoint. * ACS-4028: Get category by id endpoint - integration TAS tests. * ACS-4028: Get category by id endpoint - refactoring. * ACS-4028: Adding test to test suite. * ACS-4028: Fixes after code review. * ACS-4019: Removing redundant test group. --- .../rest/model/RestCategoryBodyModel.java | 43 ++++ .../rest/model/RestCategoryLinkBodyModel.java | 40 ++++ .../rest/model/RestCategoryModel.java | 104 +++++++++ .../alfresco/rest/requests/Categories.java | 55 +++++ .../rest/requests/coreAPI/RestCoreAPI.java | 8 + .../rest/categories/GetCategoriesTests.java | 83 ++++++++ .../rest/rules/ReorderRulesTests.java | 1 + .../resources/test-suites/part3-suite.xml | 1 + .../org/alfresco/rest/api/Categories.java | 38 ++++ .../categories/CategoriesEntityResource.java | 61 ++++++ .../rest/api/categories/package-info.java | 30 +++ .../rest/api/impl/CategoriesImpl.java | 93 ++++++++ .../org/alfresco/rest/api/model/Category.java | 75 +++++++ .../alfresco/public-rest-context.xml | 29 ++- .../CategoriesEntityResourceTest.java | 68 ++++++ .../rest/api/impl/CategoriesImplTest.java | 201 ++++++++++++++++++ .../impl/AbstractCategoryServiceImpl.java | 26 +++ .../service/cmr/search/CategoryService.java | 72 ++++--- .../cmr/search/CategoryServiceException.java | 61 ++++++ .../alfresco/repo/search/SearchTestSuite.java | 4 +- .../solr/SolrCategoryServiceImplTest.java | 93 ++++++++ 21 files changed, 1153 insertions(+), 33 deletions(-) create mode 100644 packaging/tests/tas-restapi/src/main/java/org/alfresco/rest/model/RestCategoryBodyModel.java create mode 100644 packaging/tests/tas-restapi/src/main/java/org/alfresco/rest/model/RestCategoryLinkBodyModel.java create mode 100644 packaging/tests/tas-restapi/src/main/java/org/alfresco/rest/model/RestCategoryModel.java create mode 100644 packaging/tests/tas-restapi/src/main/java/org/alfresco/rest/requests/Categories.java create mode 100644 packaging/tests/tas-restapi/src/test/java/org/alfresco/rest/categories/GetCategoriesTests.java create mode 100644 remote-api/src/main/java/org/alfresco/rest/api/Categories.java create mode 100644 remote-api/src/main/java/org/alfresco/rest/api/categories/CategoriesEntityResource.java create mode 100644 remote-api/src/main/java/org/alfresco/rest/api/categories/package-info.java create mode 100644 remote-api/src/main/java/org/alfresco/rest/api/impl/CategoriesImpl.java create mode 100644 remote-api/src/main/java/org/alfresco/rest/api/model/Category.java create mode 100644 remote-api/src/test/java/org/alfresco/rest/api/categories/CategoriesEntityResourceTest.java create mode 100644 remote-api/src/test/java/org/alfresco/rest/api/impl/CategoriesImplTest.java create mode 100644 repository/src/main/java/org/alfresco/service/cmr/search/CategoryServiceException.java create mode 100644 repository/src/test/java/org/alfresco/repo/search/impl/solr/SolrCategoryServiceImplTest.java diff --git a/packaging/tests/tas-restapi/src/main/java/org/alfresco/rest/model/RestCategoryBodyModel.java b/packaging/tests/tas-restapi/src/main/java/org/alfresco/rest/model/RestCategoryBodyModel.java new file mode 100644 index 0000000000..42fa09ce1d --- /dev/null +++ b/packaging/tests/tas-restapi/src/main/java/org/alfresco/rest/model/RestCategoryBodyModel.java @@ -0,0 +1,43 @@ +package org.alfresco.rest.model; + +import com.fasterxml.jackson.annotation.JsonProperty; +import org.alfresco.rest.core.IRestModel; +import org.alfresco.utility.model.TestModel; + +/** + * Generated by 'mpichura' on '2022-12-01 13:41' from 'Alfresco Content Services REST API' swagger file + * Generated from 'Alfresco Content Services REST API' swagger file + * Base Path {@linkplain /alfresco/api/-default-/public/alfresco/versions/1} + */ +public class RestCategoryBodyModel extends TestModel implements IRestModel +{ + @JsonProperty(value = "entry") + RestCategoryBodyModel model; + + @Override + public RestCategoryBodyModel onModel() + { + return model; + } + + /** + The name of the category. + +This must be unique within the parent category. + + */ + + @JsonProperty(required = true) + private String name; + + public String getName() + { + return this.name; + } + + public void setName(String name) + { + this.name = name; + } +} + diff --git a/packaging/tests/tas-restapi/src/main/java/org/alfresco/rest/model/RestCategoryLinkBodyModel.java b/packaging/tests/tas-restapi/src/main/java/org/alfresco/rest/model/RestCategoryLinkBodyModel.java new file mode 100644 index 0000000000..03d22185ab --- /dev/null +++ b/packaging/tests/tas-restapi/src/main/java/org/alfresco/rest/model/RestCategoryLinkBodyModel.java @@ -0,0 +1,40 @@ +package org.alfresco.rest.model; + +import com.fasterxml.jackson.annotation.JsonProperty; +import org.alfresco.rest.core.IRestModel; +import org.alfresco.utility.model.TestModel; + +/** + * Generated by 'mpichura' on '2022-12-01 13:41' from 'Alfresco Content Services REST API' swagger file + * Generated from 'Alfresco Content Services REST API' swagger file + * Base Path {@linkplain /alfresco/api/-default-/public/alfresco/versions/1} + */ +public class RestCategoryLinkBodyModel extends TestModel implements IRestModel +{ + @JsonProperty(value = "entry") + RestCategoryLinkBodyModel model; + + @Override + public RestCategoryLinkBodyModel onModel() + { + return model; + } + + /** + The identifier of the category. + */ + + @JsonProperty(required = true) + private String id; + + public String getId() + { + return this.id; + } + + public void setId(String id) + { + this.id = id; + } +} + 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 new file mode 100644 index 0000000000..805b20cf4f --- /dev/null +++ b/packaging/tests/tas-restapi/src/main/java/org/alfresco/rest/model/RestCategoryModel.java @@ -0,0 +1,104 @@ +package org.alfresco.rest.model; + +import com.fasterxml.jackson.annotation.JsonProperty; +import org.alfresco.rest.core.IRestModel; +import org.alfresco.utility.model.TestModel; + +/** + * Generated by 'mpichura' on '2022-12-01 13:41' from 'Alfresco Content Services REST API' swagger file + * Generated from 'Alfresco Content Services REST API' swagger file + * Base Path {@linkplain /alfresco/api/-default-/public/alfresco/versions/1} + */ +public class RestCategoryModel extends TestModel implements IRestModel +{ + @JsonProperty(value = "entry") + RestCategoryModel model; + + @Override + public RestCategoryModel onModel() + { + return model; + } + + /** + The identifier for the category. + */ + + @JsonProperty(required = true) + private String id; + /** + The name of the category. + +This must be unique within the parent category. + + */ + + @JsonProperty(required = true) + private String name; + /** + The id of the parent category (or -root- if this is a top level category). + */ + + private String parentId; + /** + True if the category has at least one child category. + */ + + private boolean hasChildren; + /** + The number of nodes that are assigned to this category. + */ + + private long count; + + public String getId() + { + return this.id; + } + + public void setId(String id) + { + this.id = id; + } + + public String getName() + { + return this.name; + } + + public void setName(String name) + { + this.name = name; + } + + public String getParentId() + { + return this.parentId; + } + + public void setParentId(String parentId) + { + this.parentId = parentId; + } + + public boolean getHasChildren() + { + return this.hasChildren; + } + + public void setHasChildren(boolean hasChildren) + { + this.hasChildren = hasChildren; + } + + public long getCount() + { + return this.count; + } + + public void setCount(long count) + { + this.count = count; + } +} + 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 new file mode 100644 index 0000000000..87f5ba0da5 --- /dev/null +++ b/packaging/tests/tas-restapi/src/main/java/org/alfresco/rest/requests/Categories.java @@ -0,0 +1,55 @@ +/*- + * #%L + * alfresco-tas-restapi + * %% + * 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.requests; + +import org.alfresco.rest.core.RestRequest; +import org.alfresco.rest.core.RestWrapper; +import org.alfresco.rest.model.RestCategoryModel; +import org.springframework.http.HttpMethod; + +public class Categories extends ModelRequest +{ + private RestCategoryModel category; + + public Categories(RestWrapper restWrapper, RestCategoryModel category) + { + super(restWrapper); + this.category = category; + } + + /** + * Retrieves a category with ID using GET call on using GET call on "/tags/{tagId}" + * + * @return + */ + public RestCategoryModel getCategory() + { + RestRequest request = RestRequest + .simpleRequest(HttpMethod.GET, "categories/{categoryId}?{parameters}", category.getId(), restWrapper.getParameters()); + return restWrapper.processModel(RestCategoryModel.class, request); + } + +} diff --git a/packaging/tests/tas-restapi/src/main/java/org/alfresco/rest/requests/coreAPI/RestCoreAPI.java b/packaging/tests/tas-restapi/src/main/java/org/alfresco/rest/requests/coreAPI/RestCoreAPI.java index 21864e92e3..8dec5f6da0 100644 --- a/packaging/tests/tas-restapi/src/main/java/org/alfresco/rest/requests/coreAPI/RestCoreAPI.java +++ b/packaging/tests/tas-restapi/src/main/java/org/alfresco/rest/requests/coreAPI/RestCoreAPI.java @@ -26,12 +26,14 @@ package org.alfresco.rest.requests.coreAPI; import org.alfresco.rest.core.RestWrapper; +import org.alfresco.rest.model.RestCategoryModel; import org.alfresco.rest.model.RestDownloadsModel; import org.alfresco.rest.model.RestSiteModelsCollection; import org.alfresco.rest.model.RestTagModel; import org.alfresco.rest.model.RestTagModelsCollection; import org.alfresco.rest.requests.Actions; import org.alfresco.rest.requests.Audit; +import org.alfresco.rest.requests.Categories; import org.alfresco.rest.requests.ContentStorageInformation; import org.alfresco.rest.requests.Downloads; import org.alfresco.rest.requests.Groups; @@ -184,6 +186,12 @@ public class RestCoreAPI extends ModelRequest { return new Tags(tag, restWrapper).getTag(); } + + + public Categories usingCategory(RestCategoryModel categoryModel) + { + return new Categories(restWrapper, categoryModel); + } public Queries usingQueries() { 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 new file mode 100644 index 0000000000..d7ca0a70f3 --- /dev/null +++ b/packaging/tests/tas-restapi/src/test/java/org/alfresco/rest/categories/GetCategoriesTests.java @@ -0,0 +1,83 @@ +/* + * #%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.report.log.Step.STEP; +import static org.springframework.http.HttpStatus.BAD_REQUEST; +import static org.springframework.http.HttpStatus.NOT_FOUND; + +import org.alfresco.rest.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.alfresco.utility.model.UserModel; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; + +public class GetCategoriesTests extends RestTest +{ + private UserModel user; + + @BeforeClass(alwaysRun = true) + public void dataPreparation() throws Exception + { + STEP("Create a user"); + user = dataUser.createRandomTestUser(); + } + + /** + * Check we get an error when passing -root- as category id + */ + @Test(groups = {TestGroup.REST_API}) + public void testGetCategoryByIdProvidingRootAsId() + { + STEP("Get category with -root- as id (which does not exist)"); + final RestCategoryModel rootCategory = new RestCategoryModel(); + rootCategory.setId("-root-"); + restClient.authenticateUser(user).withCoreAPI().usingCategory(rootCategory).getCategory(); + restClient.assertStatusCodeIs(NOT_FOUND); + } + + /** + * Check we get an error when passing as category id + */ + @Test(groups = {TestGroup.REST_API}) + public void testGetCategoryByIdProvidingFolderAsId() + { + STEP("Create a site and a folder inside it"); + final SiteModel site = dataSite.usingUser(user).createPublicRandomSite(); + final FolderModel folder = dataContent.usingUser(user).usingSite(site).createFolder(); + + STEP("Get category with folder id passed as id"); + final RestCategoryModel rootCategory = new RestCategoryModel(); + rootCategory.setId(folder.getNodeRef()); + restClient.authenticateUser(user).withCoreAPI().usingCategory(rootCategory).getCategory(); + restClient.assertStatusCodeIs(BAD_REQUEST); + } + +} diff --git a/packaging/tests/tas-restapi/src/test/java/org/alfresco/rest/rules/ReorderRulesTests.java b/packaging/tests/tas-restapi/src/test/java/org/alfresco/rest/rules/ReorderRulesTests.java index 58b8ef3420..df19d4471c 100644 --- a/packaging/tests/tas-restapi/src/test/java/org/alfresco/rest/rules/ReorderRulesTests.java +++ b/packaging/tests/tas-restapi/src/test/java/org/alfresco/rest/rules/ReorderRulesTests.java @@ -26,6 +26,7 @@ package org.alfresco.rest.rules; import static java.util.stream.Collectors.toList; + import static org.alfresco.utility.report.log.Step.STEP; import static org.springframework.http.HttpStatus.FORBIDDEN; import static org.springframework.http.HttpStatus.OK; diff --git a/packaging/tests/tas-restapi/src/test/resources/test-suites/part3-suite.xml b/packaging/tests/tas-restapi/src/test/resources/test-suites/part3-suite.xml index cf98c3730c..23dd5f4f37 100644 --- a/packaging/tests/tas-restapi/src/test/resources/test-suites/part3-suite.xml +++ b/packaging/tests/tas-restapi/src/test/resources/test-suites/part3-suite.xml @@ -16,6 +16,7 @@ + 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 new file mode 100644 index 0000000000..f138450851 --- /dev/null +++ b/remote-api/src/main/java/org/alfresco/rest/api/Categories.java @@ -0,0 +1,38 @@ +/* + * #%L + * Alfresco Remote API + * %% + * Copyright (C) 2005 - 2022 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ + +package org.alfresco.rest.api; + +import org.alfresco.rest.api.model.Category; +import org.alfresco.rest.framework.resource.parameters.Parameters; +import org.alfresco.service.Experimental; +import org.alfresco.service.cmr.repository.NodeRef; + +@Experimental +public interface Categories +{ + Category getCategoryById(String id, Parameters params); +} 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 new file mode 100644 index 0000000000..3bde251ca0 --- /dev/null +++ b/remote-api/src/main/java/org/alfresco/rest/api/categories/CategoriesEntityResource.java @@ -0,0 +1,61 @@ +/* + * #%L + * Alfresco Remote API + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ +package org.alfresco.rest.api.categories; + +import javax.servlet.http.HttpServletResponse; + +import org.alfresco.rest.api.Categories; +import org.alfresco.rest.api.model.Category; +import org.alfresco.rest.framework.WebApiDescription; +import org.alfresco.rest.framework.core.exceptions.EntityNotFoundException; +import org.alfresco.rest.framework.resource.EntityResource; +import org.alfresco.rest.framework.resource.actions.interfaces.EntityResourceAction; +import org.alfresco.rest.framework.resource.parameters.Parameters; + +/** + * Entity Resource for Categories + * + * @author mpichura + */ +@EntityResource(name = "categories", title = "Categories") +public class CategoriesEntityResource implements EntityResourceAction.ReadById +{ + private final Categories categories; + + public CategoriesEntityResource(Categories categories) + { + this.categories = categories; + } + + @WebApiDescription(title = "Get category by its id", + description = "Retrieves a category given category node id", + successStatus = HttpServletResponse.SC_OK) + @Override + public Category readById(String id, Parameters parameters) throws EntityNotFoundException + { + return categories.getCategoryById(id, parameters); + } +} diff --git a/remote-api/src/main/java/org/alfresco/rest/api/categories/package-info.java b/remote-api/src/main/java/org/alfresco/rest/api/categories/package-info.java new file mode 100644 index 0000000000..bed1713dee --- /dev/null +++ b/remote-api/src/main/java/org/alfresco/rest/api/categories/package-info.java @@ -0,0 +1,30 @@ +/* + * #%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% + */ +@WebApi(name = "alfresco", scope = Api.SCOPE.PUBLIC, version = 1) +package org.alfresco.rest.api.categories; + +import org.alfresco.rest.framework.Api; +import org.alfresco.rest.framework.WebApi; 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 new file mode 100644 index 0000000000..a8ae22f76a --- /dev/null +++ b/remote-api/src/main/java/org/alfresco/rest/api/impl/CategoriesImpl.java @@ -0,0 +1,93 @@ +/* + * #%L + * Alfresco Remote API + * %% + * Copyright (C) 2005 - 2022 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ + +package org.alfresco.rest.api.impl; + +import static org.alfresco.rest.api.Nodes.PATH_ROOT; + +import java.util.List; + +import org.alfresco.model.ContentModel; +import org.alfresco.rest.api.Categories; +import org.alfresco.rest.api.Nodes; +import org.alfresco.rest.api.model.Category; +import org.alfresco.rest.api.model.Node; +import org.alfresco.rest.framework.core.exceptions.InvalidArgumentException; +import org.alfresco.rest.framework.resource.parameters.Parameters; +import org.alfresco.service.Experimental; +import org.alfresco.service.cmr.repository.ChildAssociationRef; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.namespace.RegexQNamePattern; +import org.apache.commons.collections.CollectionUtils; + +@Experimental +public class CategoriesImpl implements Categories +{ + static final String NOT_A_VALID_CATEGORY = "Node id does not refer to a valid category"; + + private final Nodes nodes; + private final NodeService nodeService; + + public CategoriesImpl(Nodes nodes, NodeService nodeService) + { + this.nodes = nodes; + this.nodeService = nodeService; + } + + @Override + public Category getCategoryById(final String id, final Parameters params) + { + final NodeRef nodeRef = nodes.validateNode(id); + final boolean isCategory = nodes.isSubClass(nodeRef, ContentModel.TYPE_CATEGORY, false); + if (!isCategory || isRootCategory(nodeRef)) + { + throw new InvalidArgumentException(NOT_A_VALID_CATEGORY, new String[]{id}); + } + final Node categoryNode = nodes.getNode(nodeRef.getId()); + final Category category = new Category(); + category.setId(nodeRef.getId()); + category.setName(categoryNode.getName()); + category.setParentId(getParentId(nodeRef)); + final boolean hasChildren = CollectionUtils + .isNotEmpty(nodeService.getChildAssocs(nodeRef, RegexQNamePattern.MATCH_ALL, RegexQNamePattern.MATCH_ALL, false)); + category.setHasChildren(hasChildren); + + return category; + } + + private boolean isRootCategory(final NodeRef nodeRef) + { + final List parentAssocs = nodeService.getParentAssocs(nodeRef); + return parentAssocs.stream().anyMatch(pa -> pa.getQName().equals(ContentModel.ASPECT_GEN_CLASSIFIABLE)); + } + + private String getParentId(final NodeRef nodeRef) + { + final NodeRef parentRef = nodeService.getPrimaryParent(nodeRef).getParentRef(); + return isRootCategory(parentRef) ? PATH_ROOT : parentRef.getId(); + } +} 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 new file mode 100644 index 0000000000..e1baf15b08 --- /dev/null +++ b/remote-api/src/main/java/org/alfresco/rest/api/model/Category.java @@ -0,0 +1,75 @@ +/* + * #%L + * Alfresco Remote API + * %% + * Copyright (C) 2005 - 2022 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ + +package org.alfresco.rest.api.model; + +public class Category +{ + private String id; + private String name; + private String parentId; + private boolean hasChildren; + + public String getId() + { + return id; + } + + public void setId(String id) + { + this.id = id; + } + + public String getName() + { + return name; + } + + public void setName(String name) + { + this.name = name; + } + + public String getParentId() + { + return parentId; + } + + public void setParentId(String parentId) + { + this.parentId = parentId; + } + + public boolean getHasChildren() + { + return hasChildren; + } + + public void setHasChildren(boolean hasChildren) + { + this.hasChildren = hasChildren; + } +} diff --git a/remote-api/src/main/resources/alfresco/public-rest-context.xml b/remote-api/src/main/resources/alfresco/public-rest-context.xml index cf34746046..6f702951f1 100644 --- a/remote-api/src/main/resources/alfresco/public-rest-context.xml +++ b/remote-api/src/main/resources/alfresco/public-rest-context.xml @@ -828,7 +828,26 @@ - + + + + + + + + + org.alfresco.rest.api.Categories + + + + + + + + + + + @@ -1075,11 +1094,15 @@ - + + + + + - + 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 new file mode 100644 index 0000000000..4988ae4f32 --- /dev/null +++ b/remote-api/src/test/java/org/alfresco/rest/api/categories/CategoriesEntityResourceTest.java @@ -0,0 +1,68 @@ +/* + * #%L + * Alfresco Remote API + * %% + * Copyright (C) 2005 - 2022 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ + +package org.alfresco.rest.api.categories; + +import static org.junit.Assert.assertEquals; +import static org.mockito.BDDMockito.given; +import static org.mockito.BDDMockito.then; + +import org.alfresco.rest.api.Categories; +import org.alfresco.rest.api.model.Category; +import org.alfresco.rest.framework.resource.parameters.Parameters; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; + +@RunWith(MockitoJUnitRunner.class) +public class CategoriesEntityResourceTest +{ + private static final String CATEGORY_ID = "category-node-id"; + @Mock + private Categories categoriesMock; + @Mock + private Category categoryMock; + @Mock + private Parameters parametersMock; + + @InjectMocks + private CategoriesEntityResource objectUnderTest; + + @Test + public void testReadCategoryById() + { + given(categoriesMock.getCategoryById(CATEGORY_ID, parametersMock)).willReturn(categoryMock); + + //when + final Category category = objectUnderTest.readById(CATEGORY_ID, parametersMock); + + then(categoriesMock).should().getCategoryById(CATEGORY_ID, parametersMock); + then(categoriesMock).shouldHaveNoMoreInteractions(); + assertEquals(categoryMock, category); + } +} diff --git a/remote-api/src/test/java/org/alfresco/rest/api/impl/CategoriesImplTest.java b/remote-api/src/test/java/org/alfresco/rest/api/impl/CategoriesImplTest.java new file mode 100644 index 0000000000..ad01d33da8 --- /dev/null +++ b/remote-api/src/test/java/org/alfresco/rest/api/impl/CategoriesImplTest.java @@ -0,0 +1,201 @@ +/* + * #%L + * Alfresco Remote API + * %% + * Copyright (C) 2005 - 2022 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ + +package org.alfresco.rest.api.impl; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertThrows; +import static org.junit.Assert.assertTrue; +import static org.mockito.BDDMockito.given; +import static org.mockito.BDDMockito.then; + +import java.util.Collections; +import java.util.List; + +import org.alfresco.model.ContentModel; +import org.alfresco.rest.api.Nodes; +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.resource.parameters.Parameters; +import org.alfresco.service.cmr.repository.ChildAssociationRef; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.repository.StoreRef; +import org.alfresco.service.namespace.RegexQNamePattern; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; + +@RunWith(MockitoJUnitRunner.class) +public class CategoriesImplTest +{ + private static final String CATEGORY_ID = "category-node-id"; + 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"; + + @Mock + private Nodes nodesMock; + @Mock + private NodeService nodeServiceMock; + @Mock + private Parameters parametersMock; + @Mock + private ChildAssociationRef dummyChildAssociationRefMock; + @Mock + private ChildAssociationRef categoryChildAssociationRefMock; + + @InjectMocks + private CategoriesImpl objectUnderTest; + + @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)); + + //when + assertThrows(InvalidArgumentException.class, () -> objectUnderTest.getCategoryById(CAT_ROOT_NODE_ID, parametersMock)); + + then(nodesMock).should().validateNode(CAT_ROOT_NODE_ID); + then(nodesMock).should().isSubClass(categoryRootNodeRef, ContentModel.TYPE_CATEGORY, false); + then(nodesMock).shouldHaveNoMoreInteractions(); + then(nodeServiceMock).should().getParentAssocs(categoryRootNodeRef); + then(nodeServiceMock).shouldHaveNoMoreInteractions(); + } + + @Test + public void testGetCategoryById_withChildren() + { + 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); + final NodeRef parentNodeRef = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, PARENT_ID); + categoryNode.setParentId(parentNodeRef); + given(nodesMock.getNode(CATEGORY_ID)).willReturn(categoryNode); + final ChildAssociationRef parentAssoc = new ChildAssociationRef(null, parentNodeRef, null, categoryNodeRef); + given(nodeServiceMock.getPrimaryParent(categoryNodeRef)).willReturn(parentAssoc); + final List dummyChildren = List.of(dummyChildAssociationRefMock); + given(nodeServiceMock.getChildAssocs(categoryNodeRef, 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().getNode(CATEGORY_ID); + then(nodesMock).shouldHaveNoMoreInteractions(); + + then(nodeServiceMock).should().getPrimaryParent(categoryNodeRef); + then(nodeServiceMock).should().getParentAssocs(parentNodeRef); + then(nodeServiceMock).should().getParentAssocs(categoryNodeRef); + then(nodeServiceMock).should().getChildAssocs(categoryNodeRef, RegexQNamePattern.MATCH_ALL, RegexQNamePattern.MATCH_ALL, false); + then(nodeServiceMock).shouldHaveNoMoreInteractions(); + + assertEquals(categoryNode.getName(), category.getName()); + assertEquals(CATEGORY_ID, category.getId()); + assertEquals(PARENT_ID, category.getParentId()); + assertTrue(category.getHasChildren()); + } + + @Test + public void testGetCategoryById_withoutChildren() + { + 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); + final NodeRef parentNodeRef = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, PARENT_ID); + categoryNode.setParentId(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)) + .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().getNode(CATEGORY_ID); + then(nodesMock).shouldHaveNoMoreInteractions(); + + then(nodeServiceMock).should().getPrimaryParent(categoryNodeRef); + then(nodeServiceMock).should().getParentAssocs(parentNodeRef); + then(nodeServiceMock).should().getParentAssocs(categoryNodeRef); + then(nodeServiceMock).should().getChildAssocs(categoryNodeRef, RegexQNamePattern.MATCH_ALL, RegexQNamePattern.MATCH_ALL, false); + then(nodeServiceMock).shouldHaveNoMoreInteractions(); + + assertEquals(categoryNode.getName(), category.getName()); + assertEquals(CATEGORY_ID, category.getId()); + assertEquals(PARENT_ID, category.getParentId()); + assertFalse(category.getHasChildren()); + } + + @Test + public void testGetCategoryById_notACategory() + { + 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(false); + + //when + assertThrows(InvalidArgumentException.class, () -> objectUnderTest.getCategoryById(CATEGORY_ID, parametersMock)); + + then(nodesMock).should().validateNode(CATEGORY_ID); + then(nodesMock).should().isSubClass(categoryNodeRef, ContentModel.TYPE_CATEGORY, false); + then(nodesMock).shouldHaveNoMoreInteractions(); + + then(nodeServiceMock).shouldHaveNoInteractions(); + } + + @Test + public void testGetCategoryById_nodeNotExists() + { + given(nodesMock.validateNode(CATEGORY_ID)).willThrow(EntityNotFoundException.class); + + //when + assertThrows(EntityNotFoundException.class, () -> objectUnderTest.getCategoryById(CATEGORY_ID, parametersMock)); + + then(nodesMock).should().validateNode(CATEGORY_ID); + then(nodesMock).shouldHaveNoMoreInteractions(); + + then(nodeServiceMock).shouldHaveNoInteractions(); + } +} diff --git a/repository/src/main/java/org/alfresco/repo/search/impl/AbstractCategoryServiceImpl.java b/repository/src/main/java/org/alfresco/repo/search/impl/AbstractCategoryServiceImpl.java index 858040df54..864fb27276 100644 --- a/repository/src/main/java/org/alfresco/repo/search/impl/AbstractCategoryServiceImpl.java +++ b/repository/src/main/java/org/alfresco/repo/search/impl/AbstractCategoryServiceImpl.java @@ -32,6 +32,7 @@ import java.util.HashSet; import java.util.Iterator; import java.util.LinkedList; import java.util.List; +import java.util.Optional; import java.util.Set; import org.alfresco.error.AlfrescoRuntimeException; @@ -42,6 +43,7 @@ import org.alfresco.query.PagingResults; import org.alfresco.repo.search.IndexerAndSearcher; import org.alfresco.repo.search.IndexerException; import org.alfresco.repo.tenant.TenantService; +import org.alfresco.service.Experimental; import org.alfresco.service.cmr.dictionary.DictionaryService; import org.alfresco.service.cmr.repository.ChildAssociationRef; import org.alfresco.service.cmr.repository.InvalidNodeRefException; @@ -50,6 +52,7 @@ import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.repository.Path; import org.alfresco.service.cmr.repository.StoreRef; import org.alfresco.service.cmr.search.CategoryService; +import org.alfresco.service.cmr.search.CategoryServiceException; import org.alfresco.service.cmr.search.LimitBy; import org.alfresco.service.cmr.search.ResultSet; import org.alfresco.service.cmr.search.ResultSetRow; @@ -59,6 +62,7 @@ import org.alfresco.service.namespace.NamespacePrefixResolver; import org.alfresco.service.namespace.QName; import org.alfresco.util.ISO9075; import org.alfresco.util.Pair; +import org.apache.commons.collections.CollectionUtils; /** * Category service implementation @@ -67,6 +71,9 @@ import org.alfresco.util.Pair; */ public abstract class AbstractCategoryServiceImpl implements CategoryService { + static final String CATEGORY_ROOT_NODE_NOT_FOUND = "Category root node not found"; + static final String NODE_WITH_CATEGORY_ROOT_TYPE_NOT_FOUND = "Node with category_root type not found"; + protected NodeService nodeService; protected NodeService publicNodeService; @@ -538,4 +545,23 @@ public abstract class AbstractCategoryServiceImpl implements CategoryService } public abstract List> getTopCategories(StoreRef storeRef, QName aspectName, int count); + + @Override + @Experimental + public Optional getRootCategoryNodeRef(final StoreRef storeRef) + { + final NodeRef rootNode = nodeService.getRootNode(storeRef); + final ChildAssociationRef categoryRoot = nodeService.getChildAssocs(rootNode, Set.of(ContentModel.TYPE_CATEGORYROOT)).stream() + .findFirst() + .orElseThrow(() -> new CategoryServiceException(NODE_WITH_CATEGORY_ROOT_TYPE_NOT_FOUND)); + final List categoryRootAssocs = nodeService.getChildAssocs(categoryRoot.getChildRef()); + if (CollectionUtils.isEmpty(categoryRootAssocs)) + { + throw new CategoryServiceException(CATEGORY_ROOT_NODE_NOT_FOUND); + } + return categoryRootAssocs.stream() + .filter(ca -> ca.getQName().equals(ContentModel.ASPECT_GEN_CLASSIFIABLE)) + .map(ChildAssociationRef::getChildRef) + .findFirst(); + } } 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 0010b20723..410221f23d 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 @@ -1,37 +1,39 @@ -/* - * #%L - * Alfresco Repository - * %% - * Copyright (C) 2005 - 2016 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% - */ +/* + * #%L + * Alfresco Repository + * %% + * Copyright (C) 2005 - 2016 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.service.cmr.search; import java.util.Collection; import java.util.List; +import java.util.Optional; import org.alfresco.api.AlfrescoPublicApi; import org.alfresco.query.PagingRequest; import org.alfresco.query.PagingResults; import org.alfresco.service.Auditable; +import org.alfresco.service.Experimental; import org.alfresco.service.cmr.repository.ChildAssociationRef; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.StoreRef; @@ -234,10 +236,10 @@ public interface CategoryService */ @Auditable(parameters = {"nodeRef"}) public void deleteCategory(NodeRef nodeRef); - - /** - * Get the most polular categories - * + + /** + * Get the most polular categories + * * @param storeRef StoreRef * @param aspectName QName * @param count int @@ -245,4 +247,16 @@ public interface CategoryService */ @Auditable(parameters = {"storeRef", "aspectName", "count"}) public List> getTopCategories(StoreRef storeRef, QName aspectName, int count); + + /** + * Get a root category NodeRef + * + * @return NodeRef for category root node + */ + @Experimental + @Auditable(parameters = {"storeRef"}) + default Optional getRootCategoryNodeRef(final StoreRef storeRef) + { + return Optional.empty(); + } } diff --git a/repository/src/main/java/org/alfresco/service/cmr/search/CategoryServiceException.java b/repository/src/main/java/org/alfresco/service/cmr/search/CategoryServiceException.java new file mode 100644 index 0000000000..35ca502631 --- /dev/null +++ b/repository/src/main/java/org/alfresco/service/cmr/search/CategoryServiceException.java @@ -0,0 +1,61 @@ +/* + * #%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.service.cmr.search; + +import org.alfresco.error.AlfrescoRuntimeException; + +/** + * Category Service Exception Class + */ +public class CategoryServiceException extends AlfrescoRuntimeException +{ + /** + * Serial version UID + */ + private static final long serialVersionUID = 3257571687441467958L; + + /** + * Construtor + * + * @param message the message string + */ + public CategoryServiceException(String message) + { + super(message); + } + + /** + * Constructor + * + * @param message the message string + * @param source the source exception + */ + public CategoryServiceException(String message, Throwable source) + { + super(message, source); + } +} diff --git a/repository/src/test/java/org/alfresco/repo/search/SearchTestSuite.java b/repository/src/test/java/org/alfresco/repo/search/SearchTestSuite.java index 4c6dbaf53c..d2560512d1 100644 --- a/repository/src/test/java/org/alfresco/repo/search/SearchTestSuite.java +++ b/repository/src/test/java/org/alfresco/repo/search/SearchTestSuite.java @@ -28,6 +28,7 @@ package org.alfresco.repo.search; import org.alfresco.repo.search.impl.parsers.CMISTest; import org.alfresco.repo.search.impl.parsers.CMIS_FTSTest; import org.alfresco.repo.search.impl.parsers.FTSTest; +import org.alfresco.repo.search.impl.solr.SolrCategoryServiceImplTest; import org.alfresco.repo.search.impl.solr.SolrChildApplicationContextFactoryTest; import org.alfresco.repo.search.impl.solr.SolrSubsystemTest; import org.alfresco.util.NumericEncodingTest; @@ -46,7 +47,8 @@ import org.junit.runners.Suite; CMISTest.class, FTSTest.class, SolrChildApplicationContextFactoryTest.class, - SolrSubsystemTest.class + SolrSubsystemTest.class, + SolrCategoryServiceImplTest.class }) public class SearchTestSuite diff --git a/repository/src/test/java/org/alfresco/repo/search/impl/solr/SolrCategoryServiceImplTest.java b/repository/src/test/java/org/alfresco/repo/search/impl/solr/SolrCategoryServiceImplTest.java new file mode 100644 index 0000000000..5d62ca5b85 --- /dev/null +++ b/repository/src/test/java/org/alfresco/repo/search/impl/solr/SolrCategoryServiceImplTest.java @@ -0,0 +1,93 @@ +/* + * #%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.repo.search.impl.solr; + +import static org.junit.Assert.*; +import static org.mockito.BDDMockito.given; +import static org.mockito.BDDMockito.then; + +import java.util.List; +import java.util.Optional; +import java.util.Set; + +import org.alfresco.model.ContentModel; +import org.alfresco.repo.search.impl.noindex.NoIndexCategoryServiceImpl; +import org.alfresco.service.cmr.repository.ChildAssociationRef; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.repository.StoreRef; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; + +/** + * This test class covers part of the code implemented in abstract class {@link org.alfresco.repo.search.impl.AbstractCategoryServiceImpl}. + * That's because abstract class cannot be instantiated and directly tested. + */ +@RunWith(MockitoJUnitRunner.class) +public class SolrCategoryServiceImplTest +{ + private static final String PATH_ROOT = "-root-"; + private static final String CAT_ROOT_NODE_ID = "cat-root-node-id"; + + @Mock + private NodeService nodeServiceMock; + @Mock + private ChildAssociationRef categoryRootChildAssociationRefMock; + @Mock + private ChildAssociationRef categoryChildAssociationRefMock; + + @InjectMocks + private NoIndexCategoryServiceImpl objectUnderTest; + + @Test + public void testGetRootCategoryNodeRef() + { + final NodeRef rootNodeRef = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, PATH_ROOT); + given(nodeServiceMock.getRootNode(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE)).willReturn(rootNodeRef); + given(nodeServiceMock.getChildAssocs(rootNodeRef, Set.of(ContentModel.TYPE_CATEGORYROOT))) + .willReturn(List.of(categoryRootChildAssociationRefMock)); + given(categoryChildAssociationRefMock.getQName()).willReturn(ContentModel.ASPECT_GEN_CLASSIFIABLE); + final NodeRef categoryRootNodeRef = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, CAT_ROOT_NODE_ID); + given(categoryChildAssociationRefMock.getChildRef()).willReturn(categoryRootNodeRef); + given(nodeServiceMock.getChildAssocs(categoryRootChildAssociationRefMock.getChildRef())) + .willReturn(List.of(categoryChildAssociationRefMock)); + + //when + final Optional rooCategoryNodeRef = objectUnderTest.getRootCategoryNodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE); + + then(nodeServiceMock).should().getRootNode(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE); + then(nodeServiceMock).should().getChildAssocs(rootNodeRef, Set.of(ContentModel.TYPE_CATEGORYROOT)); + then(nodeServiceMock).should().getChildAssocs(categoryRootChildAssociationRefMock.getChildRef()); + then(nodeServiceMock).shouldHaveNoMoreInteractions(); + + assertTrue(rooCategoryNodeRef.isPresent()); + assertEquals(CAT_ROOT_NODE_ID, rooCategoryNodeRef.get().getId()); + } +}