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.
This commit is contained in:
Maciej Pichura
2022-12-07 10:32:08 +01:00
committed by GitHub
parent e48315e4b5
commit 90ca0dea6c
21 changed files with 1153 additions and 33 deletions

View File

@@ -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<RestCategoryBodyModel>
{
@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;
}
}

View File

@@ -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<RestCategoryLinkBodyModel>
{
@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;
}
}

View File

@@ -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<RestCategoryModel>
{
@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;
}
}

View File

@@ -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 <http://www.gnu.org/licenses/>.
* #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<Categories>
{
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);
}
}

View File

@@ -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<RestCoreAPI>
{
return new Tags(tag, restWrapper).getTag();
}
public Categories usingCategory(RestCategoryModel categoryModel)
{
return new Categories(restWrapper, categoryModel);
}
public Queries usingQueries()
{

View File

@@ -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 <http://www.gnu.org/licenses/>.
* #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);
}
}

View File

@@ -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;

View File

@@ -16,6 +16,7 @@
<package name="org.alfresco.rest.trashcan.*"/>
<package name="org.alfresco.rest.workflow.*"/>
<package name="org.alfresco.rest.models.*"/>
<package name="org.alfresco.rest.categories.*"/>
</packages>
</test>
</suite>

View File

@@ -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 <http://www.gnu.org/licenses/>.
* #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);
}

View File

@@ -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 <http://www.gnu.org/licenses/>.
* #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<Category>
{
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);
}
}

View File

@@ -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 <http://www.gnu.org/licenses/>.
* #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;

View File

@@ -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 <http://www.gnu.org/licenses/>.
* #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<ChildAssociationRef> 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();
}
}

View File

@@ -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 <http://www.gnu.org/licenses/>.
* #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;
}
}

View File

@@ -828,7 +828,26 @@
</list>
</property>
</bean>
<bean id="categories" class="org.alfresco.rest.api.impl.CategoriesImpl">
<constructor-arg name="nodes" ref="nodes"/>
<constructor-arg name="nodeService" ref="NodeService"/>
</bean>
<bean id="Categories" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="proxyInterfaces">
<value>org.alfresco.rest.api.Categories</value>
</property>
<property name="target">
<ref bean="categories"/>
</property>
<property name="interceptorNames">
<list>
<idref bean="legacyExceptionInterceptor"/>
</list>
</property>
</bean>
<bean id="tags" class="org.alfresco.rest.api.impl.TagsImpl">
<property name="nodes" ref="nodes" />
<property name="taggingService" ref="TaggingService" />
@@ -1075,11 +1094,15 @@
<bean class="org.alfresco.rest.api.people.PersonGroupsRelation">
<property name="groups" ref="Groups" />
</bean>
<bean class="org.alfresco.rest.api.categories.CategoriesEntityResource">
<constructor-arg name="categories" ref="Categories"/>
</bean>
<bean class="org.alfresco.rest.api.tags.TagsEntityResource">
<property name="tags" ref="Tags" />
</bean>
<bean class="org.alfresco.rest.api.people.PersonSitesRelation">
<property name="sites" ref="Sites" />
</bean>

View File

@@ -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 <http://www.gnu.org/licenses/>.
* #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);
}
}

View File

@@ -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 <http://www.gnu.org/licenses/>.
* #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<ChildAssociationRef> 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();
}
}

View File

@@ -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<Pair<NodeRef, Integer>> getTopCategories(StoreRef storeRef, QName aspectName, int count);
@Override
@Experimental
public Optional<NodeRef> 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<ChildAssociationRef> 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();
}
}

View File

@@ -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 <http://www.gnu.org/licenses/>.
* #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 <http://www.gnu.org/licenses/>.
* #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<Pair<NodeRef, Integer>> getTopCategories(StoreRef storeRef, QName aspectName, int count);
/**
* Get a root category NodeRef
*
* @return NodeRef for category root node
*/
@Experimental
@Auditable(parameters = {"storeRef"})
default Optional<NodeRef> getRootCategoryNodeRef(final StoreRef storeRef)
{
return Optional.empty();
}
}

View File

@@ -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 <http://www.gnu.org/licenses/>.
* #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);
}
}

View File

@@ -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

View File

@@ -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 <http://www.gnu.org/licenses/>.
* #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<NodeRef> 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());
}
}