ACS-3290: V1 REST API endpoint for linking to ruleset (#1269)

* ACS-3290: Endpoint for linking to ruleset

* ACS-3290: Small fixes to REST API endpoint definition.

* ACS-3290: Small fixes in bean initialization.

* ACS-3290: Fix changes after rebasing and add exception message

* ACS-3290: Added unit tests

* ACS-3290: Refactoring and moving logic to RulesImpl interface

* ACS-3290: Remove unused imports and refactoring

* ACS-3290: Formatting

Co-authored-by: mpichura <maciej.pichura@hyland.com>
This commit is contained in:
George Evangelopoulos
2022-08-15 13:08:40 +01:00
committed by GitHub
parent b057455cde
commit 984bc151af
7 changed files with 335 additions and 20 deletions

View File

@@ -27,8 +27,8 @@ package org.alfresco.rest.api;
import java.util.List;
import org.alfresco.rest.api.model.rules.Rule;
import org.alfresco.rest.api.model.rules.RuleSet;
import org.alfresco.rest.api.model.rules.RuleSetLink;
import org.alfresco.rest.framework.resource.parameters.CollectionWithPagingInfo;
import org.alfresco.rest.framework.resource.parameters.Paging;
import org.alfresco.service.Experimental;
@@ -58,4 +58,9 @@ public interface RuleSets
* @return {@link RuleSet} definition
*/
RuleSet getRuleSetById(String folderNodeId, String ruleSetId, List<String> includes);
/**
* Link a rule set to a folder
*/
RuleSetLink linkToRuleSet(String folderNodeId, String linkToNodeId);
}

View File

@@ -31,13 +31,18 @@ import static java.util.stream.Collectors.toList;
import java.util.List;
import java.util.Optional;
import org.alfresco.repo.rule.RuleModel;
import org.alfresco.repo.rule.RuntimeRuleService;
import org.alfresco.rest.api.RuleSets;
import org.alfresco.rest.api.model.rules.RuleSet;
import org.alfresco.rest.api.model.rules.RuleSetLink;
import org.alfresco.rest.framework.core.exceptions.InvalidArgumentException;
import org.alfresco.rest.framework.resource.parameters.CollectionWithPagingInfo;
import org.alfresco.rest.framework.resource.parameters.ListPage;
import org.alfresco.rest.framework.resource.parameters.Paging;
import org.alfresco.service.Experimental;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.rule.RuleService;
@Experimental
@@ -46,6 +51,8 @@ public class RuleSetsImpl implements RuleSets
private RuleSetLoader ruleSetLoader;
private RuleService ruleService;
private NodeValidator validator;
private NodeService nodeService;
private RuntimeRuleService runtimeRuleService;
@Override
public CollectionWithPagingInfo<RuleSet> getRuleSets(String folderNodeId, List<String> includes, Paging paging)
@@ -69,6 +76,34 @@ public class RuleSetsImpl implements RuleSets
return ruleSetLoader.loadRuleSet(ruleSetNode, folderNode, includes);
}
@Override
public RuleSetLink linkToRuleSet(String folderNodeId, String linkToNodeId)
{
final NodeRef folderNodeRef = validator.validateFolderNode(folderNodeId,true);
final NodeRef linkToNodeRef = validator.validateFolderNode(linkToNodeId, true);
//The target node should have pre-existing rules to link to
if (!ruleService.hasRules(linkToNodeRef)) {
throw new InvalidArgumentException("The target node has no rules to link.");
}
//The folder shouldn't have any pre-existing rules
if (ruleService.hasRules(folderNodeRef)) {
throw new InvalidArgumentException("Unable to link to a ruleset because the folder has pre-existing rules or is already linked to a ruleset.");
}
// Create the destination folder as a secondary child of the first
NodeRef ruleSetNodeRef = runtimeRuleService.getSavedRuleFolderAssoc(linkToNodeRef).getChildRef();
// The required aspect will automatically be added to the node
nodeService.addChild(folderNodeRef, ruleSetNodeRef, RuleModel.ASSOC_RULE_FOLDER, RuleModel.ASSOC_RULE_FOLDER);
RuleSetLink ruleSetLink = new RuleSetLink();
ruleSetLink.setId(ruleSetNodeRef.getId());
return ruleSetLink;
}
public void setRuleSetLoader(RuleSetLoader ruleSetLoader)
{
this.ruleSetLoader = ruleSetLoader;
@@ -83,4 +118,14 @@ public class RuleSetsImpl implements RuleSets
{
this.ruleService = ruleService;
}
public void setNodeService(NodeService nodeService)
{
this.nodeService = nodeService;
}
public void setRuntimeRuleService(RuntimeRuleService runtimeRuleService)
{
this.runtimeRuleService = runtimeRuleService;
}
}

View File

@@ -0,0 +1,46 @@
/*
* #%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.rules;
public class RuleSetLink
{
/**
* This id is referring to the node id of the linked-to-folder which contains the ruleset(s)
*/
private String id;
public void setId(String id)
{
this.id = id;
}
public String getId()
{
return id;
}
}

View File

@@ -0,0 +1,74 @@
/*
* #%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.nodes;
import java.util.List;
import java.util.stream.Collectors;
import javax.servlet.http.HttpServletResponse;
import org.alfresco.rest.api.RuleSets;
import org.alfresco.rest.api.model.rules.RuleSetLink;
import org.alfresco.rest.framework.WebApiDescription;
import org.alfresco.rest.framework.WebApiParam;
import org.alfresco.rest.framework.core.ResourceParameter;
import org.alfresco.rest.framework.resource.RelationshipResource;
import org.alfresco.rest.framework.resource.actions.interfaces.RelationshipResourceAction;
import org.alfresco.rest.framework.resource.parameters.Parameters;
import org.alfresco.util.PropertyCheck;
import org.springframework.beans.factory.InitializingBean;
@RelationshipResource(name = "rule-set-links", entityResource = NodesEntityResource.class, title = "Linking to a rule set")
public class NodeRuleSetLinksRelation implements InitializingBean, RelationshipResourceAction.Create<RuleSetLink>
{
private final RuleSets ruleSets;
@Override
public void afterPropertiesSet() throws Exception
{
PropertyCheck.mandatory(this, "ruleSets", ruleSets);
}
@WebApiParam(name = "ruleSetLinkRequest", title = "Request body - rule set id",
description = "Request body with rule set id", kind = ResourceParameter.KIND.HTTP_BODY_OBJECT)
@WebApiDescription(title = "Link a rule set to a folder node",
description = "Submits a request to link a rule set to folder",
successStatus = HttpServletResponse.SC_CREATED)
@Override
public List<RuleSetLink> create(String nodeId, List<RuleSetLink> ruleSetLinksBody, Parameters parameters)
{
return ruleSetLinksBody.stream()
.map(r -> ruleSets.linkToRuleSet(nodeId, r.getId()))
.collect(Collectors.toList());
}
public NodeRuleSetLinksRelation(RuleSets ruleSets)
{
this.ruleSets = ruleSets;
}
}

View File

@@ -868,6 +868,8 @@
<property name="ruleSetLoader" ref="ruleSetLoader" />
<property name="validator" ref="nodeValidator" />
<property name="ruleService" ref="RuleService" />
<property name="nodeService" ref="NodeService"/>
<property name="runtimeRuleService" ref="ruleService"/>
</bean>
<bean id="RuleSets" class="org.springframework.aop.framework.ProxyFactoryBean">
@@ -906,6 +908,10 @@
</property>
</bean>
<bean class="org.alfresco.rest.api.nodes.NodeRuleSetLinksRelation">
<constructor-arg name="ruleSets" ref="RuleSets" />
</bean>
<bean id="publicapi.mimeTypePropertyLookup" class="org.alfresco.rest.api.lookups.MimeTypePropertyLookup">
<property name="serviceRegistry" ref="ServiceRegistry"/>
<property name="supported">

View File

@@ -27,6 +27,8 @@ package org.alfresco.rest.api.impl.rules;
import static java.util.Collections.emptyList;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.BDDMockito.given;
@@ -36,6 +38,9 @@ import java.util.Collection;
import java.util.List;
import junit.framework.TestCase;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.repo.rule.RuleModel;
import org.alfresco.repo.rule.RuntimeRuleService;
import org.alfresco.rest.api.model.rules.RuleSet;
import org.alfresco.rest.framework.resource.parameters.CollectionWithPagingInfo;
import org.alfresco.rest.framework.resource.parameters.Paging;
@@ -58,10 +63,12 @@ import org.mockito.junit.MockitoJUnitRunner;
@RunWith (MockitoJUnitRunner.class)
public class RuleSetsImplTest extends TestCase
{
private static final String FOLDER_ID = "dummy-folder-id";
private static final String FOLDER_NODE_ID = "dummy-folder-node-id";
private static final String LINK_TO_NODE_ID = "dummy-link-to-node-id";
private static final String RULE_SET_ID = "dummy-rule-set-id";
private static final NodeRef FOLDER_NODE = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, FOLDER_ID);
private static final NodeRef RULE_SET_NODE = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, RULE_SET_ID);
private static final NodeRef FOLDER_NODE_REF = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, FOLDER_NODE_ID);
private static final NodeRef LINK_TO_NODE_REF = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, LINK_TO_NODE_ID);
private static final NodeRef RULE_SET_NODE_REF = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, RULE_SET_ID);
private static final Paging PAGING = Paging.DEFAULT;
private static final List<String> INCLUDES = List.of("dummy-includes");
@@ -72,34 +79,39 @@ public class RuleSetsImplTest extends TestCase
@Mock
private NodeValidator nodeValidatorMock;
@Mock
private NodeService nodeServiceMock;
@Mock
private RuleService ruleServiceMock;
@Mock
private RuntimeRuleService runtimeRuleServiceMock;
@Mock
private RuleSet ruleSetMock;
@Mock
private ChildAssociationRef assocRef;
@Before
@Override
public void setUp()
{
MockitoAnnotations.openMocks(this);
given(nodeValidatorMock.validateFolderNode(eq(LINK_TO_NODE_ID), anyBoolean())).willReturn(LINK_TO_NODE_REF);
given(nodeValidatorMock.validateFolderNode(eq(FOLDER_NODE_ID), anyBoolean())).willReturn(FOLDER_NODE_REF);
given(nodeValidatorMock.validateRuleSetNode(RULE_SET_ID, FOLDER_NODE_REF)).willReturn(RULE_SET_NODE_REF);
given(nodeValidatorMock.validateFolderNode(eq(FOLDER_ID), anyBoolean())).willReturn(FOLDER_NODE);
//given(nodeValidatorMock.validateFolderNode(eq(RULE_SET_ID), anyBoolean())).willReturn(RULE_SET_NODE);
given(nodeValidatorMock.validateRuleSetNode(RULE_SET_ID, FOLDER_NODE)).willReturn(RULE_SET_NODE);
given(ruleServiceMock.getRuleSetNode(FOLDER_NODE)).willReturn(RULE_SET_NODE);
given(ruleSetLoaderMock.loadRuleSet(RULE_SET_NODE, FOLDER_NODE, INCLUDES)).willReturn(ruleSetMock);
given(ruleServiceMock.getRuleSetNode(FOLDER_NODE_REF)).willReturn(RULE_SET_NODE_REF);
given(ruleSetLoaderMock.loadRuleSet(RULE_SET_NODE_REF, FOLDER_NODE_REF, INCLUDES)).willReturn(ruleSetMock);
}
@Test
public void testGetRuleSets()
{
// Call the method under test.
CollectionWithPagingInfo<RuleSet> actual = ruleSets.getRuleSets(FOLDER_ID, INCLUDES, PAGING);
CollectionWithPagingInfo<RuleSet> actual = ruleSets.getRuleSets(FOLDER_NODE_ID, INCLUDES, PAGING);
then(nodeValidatorMock).should().validateFolderNode(FOLDER_ID, false);
then(nodeValidatorMock).should().validateFolderNode(FOLDER_NODE_ID, false);
then(nodeValidatorMock).shouldHaveNoMoreInteractions();
then(ruleServiceMock).should().getRuleSetNode(FOLDER_NODE);
then(ruleServiceMock).should().getRuleSetNode(FOLDER_NODE_REF);
then(ruleServiceMock).shouldHaveNoMoreInteractions();
Collection<RuleSet> expected = List.of(ruleSetMock);
@@ -111,15 +123,15 @@ public class RuleSetsImplTest extends TestCase
public void testGetZeroRuleSets()
{
// Simulate no rule sets for the folder.
given(ruleServiceMock.getRuleSetNode(FOLDER_NODE)).willReturn(null);
given(ruleServiceMock.getRuleSetNode(FOLDER_NODE_REF)).willReturn(null);
// Call the method under test.
CollectionWithPagingInfo<RuleSet> actual = ruleSets.getRuleSets(FOLDER_ID, INCLUDES, PAGING);
CollectionWithPagingInfo<RuleSet> actual = ruleSets.getRuleSets(FOLDER_NODE_ID, INCLUDES, PAGING);
then(nodeValidatorMock).should().validateFolderNode(FOLDER_ID, false);
then(nodeValidatorMock).should().validateFolderNode(FOLDER_NODE_ID, false);
then(nodeValidatorMock).shouldHaveNoMoreInteractions();
then(ruleServiceMock).should().getRuleSetNode(FOLDER_NODE);
then(ruleServiceMock).should().getRuleSetNode(FOLDER_NODE_REF);
then(ruleServiceMock).shouldHaveNoMoreInteractions();
assertEquals(emptyList(), actual.getCollection());
@@ -130,12 +142,64 @@ public class RuleSetsImplTest extends TestCase
public void testGetRuleSetById()
{
// Call the method under test.
RuleSet actual = ruleSets.getRuleSetById(FOLDER_ID, RULE_SET_ID, INCLUDES);
RuleSet actual = ruleSets.getRuleSetById(FOLDER_NODE_ID, RULE_SET_ID, INCLUDES);
then(nodeValidatorMock).should().validateFolderNode(FOLDER_ID, false);
then(nodeValidatorMock).should().validateRuleSetNode(RULE_SET_ID, FOLDER_NODE);
then(nodeValidatorMock).should().validateFolderNode(FOLDER_NODE_ID, false);
then(nodeValidatorMock).should().validateRuleSetNode(RULE_SET_ID, FOLDER_NODE_REF);
then(nodeValidatorMock).shouldHaveNoMoreInteractions();
assertEquals(ruleSetMock, actual);
}
@Test
public void testLinkingToRuleSet()
{
NodeRef childNodeRef = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, "dummy-child-id");
given(ruleServiceMock.hasRules(any(NodeRef.class))).willReturn(true, false);
given(runtimeRuleServiceMock.getSavedRuleFolderAssoc(any(NodeRef.class))).willReturn(assocRef);
given(assocRef.getChildRef()).willReturn(childNodeRef);
//when
assertEquals(ruleSets.linkToRuleSet(FOLDER_NODE_ID,LINK_TO_NODE_ID).getId(), childNodeRef.getId());
then(ruleServiceMock).should().hasRules(LINK_TO_NODE_REF);
then(ruleServiceMock).should().hasRules(FOLDER_NODE_REF);
then(runtimeRuleServiceMock).should().getSavedRuleFolderAssoc(LINK_TO_NODE_REF);
then(runtimeRuleServiceMock).shouldHaveNoMoreInteractions();
then(nodeServiceMock).should().addChild(FOLDER_NODE_REF, childNodeRef, RuleModel.ASSOC_RULE_FOLDER, RuleModel.ASSOC_RULE_FOLDER);
then(nodeServiceMock).shouldHaveNoMoreInteractions();
}
@Test
public void testLinkToRuleSet_targetFolderHasNoRules()
{
given(ruleServiceMock.hasRules(LINK_TO_NODE_REF)).willReturn(false);
//when
assertThatExceptionOfType(AlfrescoRuntimeException.class).isThrownBy(
() -> ruleSets.linkToRuleSet(FOLDER_NODE_ID, LINK_TO_NODE_ID)
);
then(nodeServiceMock).shouldHaveNoMoreInteractions();
then(ruleServiceMock).should().hasRules(LINK_TO_NODE_REF);
then(ruleServiceMock).shouldHaveNoMoreInteractions();
then(runtimeRuleServiceMock).shouldHaveNoInteractions();
}
@Test
public void testLinkToRuleSet_folderShouldntHavePreExistingRules()
{
given(ruleServiceMock.hasRules(any(NodeRef.class))).willReturn(true, true);
//when
assertThatExceptionOfType(AlfrescoRuntimeException.class).isThrownBy(
() -> ruleSets.linkToRuleSet(FOLDER_NODE_ID, LINK_TO_NODE_ID));
then(nodeServiceMock).shouldHaveNoMoreInteractions();
then(ruleServiceMock).should().hasRules(LINK_TO_NODE_REF);
then(ruleServiceMock).should().hasRules(FOLDER_NODE_REF);
then(ruleServiceMock).shouldHaveNoMoreInteractions();
then(runtimeRuleServiceMock).shouldHaveNoInteractions();
}
}

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.nodes;
import junit.framework.TestCase;
import org.alfresco.rest.api.RuleSets;
import org.alfresco.rest.api.model.rules.RuleSetLink;
import org.alfresco.rest.framework.resource.parameters.Parameters;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import java.util.List;
import static org.mockito.Mockito.when;
@RunWith(MockitoJUnitRunner.class)
public class NodeRuleSetsRelationTest extends TestCase
{
private static final String FOLDER_NODE_ID = "dummy-folder-node-id";
private static final String LINK_TO_NODE_ID = "dummy-link-to-node-id";
@Mock
private RuleSets ruleSets;
@Mock
private Parameters parameters;
@InjectMocks
private NodeRuleSetLinksRelation nodeRuleSetLinksRelation;
@Test
public void shouldProperlyCreateLink()
{
RuleSetLink ruleSetLink = new RuleSetLink();
List<RuleSetLink> ruleResult = List.of(ruleSetLink);
RuleSetLink requestBody = new RuleSetLink();
requestBody.setId(LINK_TO_NODE_ID);
when(ruleSets.linkToRuleSet(FOLDER_NODE_ID, LINK_TO_NODE_ID)).thenReturn(ruleSetLink);
List<RuleSetLink> actual = nodeRuleSetLinksRelation.create(FOLDER_NODE_ID,List.of(requestBody), parameters);
Assert.assertEquals(ruleResult, actual);
}
}