mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-07-24 17:32:48 +00:00
ACS-3280 Get inherited rule sets. [tas] (#1323)
* ACS-3280 Get inherited rule sets. [tas] This needs to work the exact same way as get inherited rules. * ACS-3280 Replace LinkedList with ArrayList. * ACS-3280 Don't return duplicated rule sets when there are links.
This commit is contained in:
@@ -35,6 +35,7 @@ import org.alfresco.rest.RestTest;
|
|||||||
import org.alfresco.rest.model.RestRuleModel;
|
import org.alfresco.rest.model.RestRuleModel;
|
||||||
import org.alfresco.rest.model.RestRuleSetModel;
|
import org.alfresco.rest.model.RestRuleSetModel;
|
||||||
import org.alfresco.rest.model.RestRuleSetModelsCollection;
|
import org.alfresco.rest.model.RestRuleSetModelsCollection;
|
||||||
|
import org.alfresco.rest.model.RestRuleSettingsModel;
|
||||||
import org.alfresco.utility.model.FolderModel;
|
import org.alfresco.utility.model.FolderModel;
|
||||||
import org.alfresco.utility.model.SiteModel;
|
import org.alfresco.utility.model.SiteModel;
|
||||||
import org.alfresco.utility.model.TestGroup;
|
import org.alfresco.utility.model.TestGroup;
|
||||||
@@ -51,6 +52,8 @@ public class GetRuleSetsTests extends RestTest
|
|||||||
private UserModel user;
|
private UserModel user;
|
||||||
private SiteModel site;
|
private SiteModel site;
|
||||||
private FolderModel ruleFolder;
|
private FolderModel ruleFolder;
|
||||||
|
private FolderModel inheritingChildFolder;
|
||||||
|
private FolderModel notInheritingChildFolder;
|
||||||
private RestRuleModel rule;
|
private RestRuleModel rule;
|
||||||
private String ruleSetId;
|
private String ruleSetId;
|
||||||
|
|
||||||
@@ -62,6 +65,14 @@ public class GetRuleSetsTests extends RestTest
|
|||||||
site = dataSite.usingUser(user).createPublicRandomSite();
|
site = dataSite.usingUser(user).createPublicRandomSite();
|
||||||
ruleFolder = dataContent.usingUser(user).usingSite(site).createFolder();
|
ruleFolder = dataContent.usingUser(user).usingSite(site).createFolder();
|
||||||
|
|
||||||
|
STEP("Create two children of the folder - one that inherits rules and one that doesn't");
|
||||||
|
inheritingChildFolder = dataContent.usingUser(user).usingResource(ruleFolder).createFolder();
|
||||||
|
notInheritingChildFolder = dataContent.usingUser(user).usingResource(ruleFolder).createFolder();
|
||||||
|
RestRuleSettingsModel doesntInherit = new RestRuleSettingsModel();
|
||||||
|
doesntInherit.setValue(false);
|
||||||
|
restClient.authenticateUser(user).withCoreAPI().usingNode(notInheritingChildFolder)
|
||||||
|
.usingIsInheritanceEnabledRuleSetting().updateSetting(doesntInherit);
|
||||||
|
|
||||||
STEP("Create a rule in the folder.");
|
STEP("Create a rule in the folder.");
|
||||||
RestRuleModel ruleModel = createRuleModel("ruleName");
|
RestRuleModel ruleModel = createRuleModel("ruleName");
|
||||||
rule = restClient.authenticateUser(user).withCoreAPI().usingNode(ruleFolder).usingDefaultRuleSet()
|
rule = restClient.authenticateUser(user).withCoreAPI().usingNode(ruleFolder).usingDefaultRuleSet()
|
||||||
@@ -133,7 +144,7 @@ public class GetRuleSetsTests extends RestTest
|
|||||||
|
|
||||||
/** Check we can get the reason that a rule set is included in the list. */
|
/** Check we can get the reason that a rule set is included in the list. */
|
||||||
@Test (groups = { TestGroup.REST_API, TestGroup.RULES })
|
@Test (groups = { TestGroup.REST_API, TestGroup.RULES })
|
||||||
public void getRuleSetsAndInclusionType()
|
public void getRuleSetsAndOwnedInclusionType()
|
||||||
{
|
{
|
||||||
STEP("Get the rule sets and inclusion type");
|
STEP("Get the rule sets and inclusion type");
|
||||||
RestRuleSetModelsCollection ruleSets = restClient.authenticateUser(user).withCoreAPI()
|
RestRuleSetModelsCollection ruleSets = restClient.authenticateUser(user).withCoreAPI()
|
||||||
@@ -148,6 +159,36 @@ public class GetRuleSetsTests extends RestTest
|
|||||||
ruleSets.assertThat().entriesListCountIs(1);
|
ruleSets.assertThat().entriesListCountIs(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Check we can tell that a rule set has been inherited. */
|
||||||
|
@Test (groups = { TestGroup.REST_API, TestGroup.RULES })
|
||||||
|
public void getRuleSetsAndInheritedInclusionType()
|
||||||
|
{
|
||||||
|
STEP("Get the rule sets and inclusion type");
|
||||||
|
RestRuleSetModelsCollection ruleSets = restClient.authenticateUser(user).withCoreAPI()
|
||||||
|
.usingNode(inheritingChildFolder)
|
||||||
|
.include("inclusionType")
|
||||||
|
.getListOfRuleSets();
|
||||||
|
|
||||||
|
restClient.assertStatusCodeIs(OK);
|
||||||
|
ruleSets.getEntries().get(0).onModel()
|
||||||
|
.assertThat().field("inclusionType").is("inherited")
|
||||||
|
.assertThat().field("id").is(ruleSetId);
|
||||||
|
ruleSets.assertThat().entriesListCountIs(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Check that a rule set is not inherited if inheriting is disabled. */
|
||||||
|
@Test (groups = { TestGroup.REST_API, TestGroup.RULES })
|
||||||
|
public void getRuleSetsWithoutInheriting()
|
||||||
|
{
|
||||||
|
STEP("Get the rule sets and inclusion type");
|
||||||
|
RestRuleSetModelsCollection ruleSets = restClient.authenticateUser(user).withCoreAPI()
|
||||||
|
.usingNode(notInheritingChildFolder)
|
||||||
|
.getListOfRuleSets();
|
||||||
|
|
||||||
|
restClient.assertStatusCodeIs(OK);
|
||||||
|
ruleSets.assertThat().entriesListCountIs(0);
|
||||||
|
}
|
||||||
|
|
||||||
/** Check we can get a rule set by its id. */
|
/** Check we can get a rule set by its id. */
|
||||||
@Test (groups = { TestGroup.REST_API, TestGroup.RULES, TestGroup.SANITY })
|
@Test (groups = { TestGroup.REST_API, TestGroup.RULES, TestGroup.SANITY })
|
||||||
public void getRuleSetById()
|
public void getRuleSetById()
|
||||||
|
@@ -29,7 +29,7 @@ package org.alfresco.rest.api.impl.rules;
|
|||||||
import static java.util.stream.Collectors.toList;
|
import static java.util.stream.Collectors.toList;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Objects;
|
||||||
|
|
||||||
import org.alfresco.repo.rule.RuleModel;
|
import org.alfresco.repo.rule.RuleModel;
|
||||||
import org.alfresco.repo.rule.RuntimeRuleService;
|
import org.alfresco.repo.rule.RuntimeRuleService;
|
||||||
@@ -59,10 +59,13 @@ public class RuleSetsImpl implements RuleSets
|
|||||||
{
|
{
|
||||||
NodeRef folderNode = validator.validateFolderNode(folderNodeId, false);
|
NodeRef folderNode = validator.validateFolderNode(folderNodeId, false);
|
||||||
|
|
||||||
NodeRef ruleSetNode = ruleService.getRuleSetNode(folderNode);
|
List<RuleSet> ruleSets = ruleService.getNodesSupplyingRuleSets(folderNode)
|
||||||
List<RuleSet> ruleSets = Optional.ofNullable(ruleSetNode)
|
.stream()
|
||||||
.map(nodeRef -> ruleSetLoader.loadRuleSet(nodeRef, folderNode, includes))
|
.map(ruleService::getRuleSetNode)
|
||||||
.stream().collect(toList());
|
.filter(Objects::nonNull)
|
||||||
|
.map(nodeRef -> ruleSetLoader.loadRuleSet(nodeRef, folderNode, includes))
|
||||||
|
.distinct()
|
||||||
|
.collect(toList());
|
||||||
|
|
||||||
return ListPage.of(ruleSets, paging);
|
return ListPage.of(ruleSets, paging);
|
||||||
}
|
}
|
||||||
|
@@ -33,6 +33,7 @@ import static org.mockito.ArgumentMatchers.anyBoolean;
|
|||||||
import static org.mockito.ArgumentMatchers.eq;
|
import static org.mockito.ArgumentMatchers.eq;
|
||||||
import static org.mockito.BDDMockito.given;
|
import static org.mockito.BDDMockito.given;
|
||||||
import static org.mockito.BDDMockito.then;
|
import static org.mockito.BDDMockito.then;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -101,6 +102,8 @@ public class RuleSetsImplTest extends TestCase
|
|||||||
given(nodeValidatorMock.validateRuleSetNode(RULE_SET_ID, FOLDER_NODE)).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(ruleServiceMock.getRuleSetNode(FOLDER_NODE)).willReturn(RULE_SET_NODE);
|
||||||
|
given(ruleServiceMock.getNodesSupplyingRuleSets(FOLDER_NODE)).willReturn(List.of(FOLDER_NODE));
|
||||||
|
|
||||||
given(ruleSetLoaderMock.loadRuleSet(RULE_SET_NODE, FOLDER_NODE, INCLUDES)).willReturn(ruleSetMock);
|
given(ruleSetLoaderMock.loadRuleSet(RULE_SET_NODE, FOLDER_NODE, INCLUDES)).willReturn(ruleSetMock);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -113,6 +116,7 @@ public class RuleSetsImplTest extends TestCase
|
|||||||
then(nodeValidatorMock).should().validateFolderNode(FOLDER_ID, false);
|
then(nodeValidatorMock).should().validateFolderNode(FOLDER_ID, false);
|
||||||
then(nodeValidatorMock).shouldHaveNoMoreInteractions();
|
then(nodeValidatorMock).shouldHaveNoMoreInteractions();
|
||||||
|
|
||||||
|
then(ruleServiceMock).should().getNodesSupplyingRuleSets(FOLDER_NODE);
|
||||||
then(ruleServiceMock).should().getRuleSetNode(FOLDER_NODE);
|
then(ruleServiceMock).should().getRuleSetNode(FOLDER_NODE);
|
||||||
then(ruleServiceMock).shouldHaveNoMoreInteractions();
|
then(ruleServiceMock).shouldHaveNoMoreInteractions();
|
||||||
|
|
||||||
@@ -133,6 +137,7 @@ public class RuleSetsImplTest extends TestCase
|
|||||||
then(nodeValidatorMock).should().validateFolderNode(FOLDER_ID, false);
|
then(nodeValidatorMock).should().validateFolderNode(FOLDER_ID, false);
|
||||||
then(nodeValidatorMock).shouldHaveNoMoreInteractions();
|
then(nodeValidatorMock).shouldHaveNoMoreInteractions();
|
||||||
|
|
||||||
|
then(ruleServiceMock).should().getNodesSupplyingRuleSets(FOLDER_NODE);
|
||||||
then(ruleServiceMock).should().getRuleSetNode(FOLDER_NODE);
|
then(ruleServiceMock).should().getRuleSetNode(FOLDER_NODE);
|
||||||
then(ruleServiceMock).shouldHaveNoMoreInteractions();
|
then(ruleServiceMock).shouldHaveNoMoreInteractions();
|
||||||
|
|
||||||
@@ -140,6 +145,62 @@ public class RuleSetsImplTest extends TestCase
|
|||||||
assertEquals(PAGING, actual.getPaging());
|
assertEquals(PAGING, actual.getPaging());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Check that a folder with a parent and grandparent can inherit rule sets from the grandparent, even if the parent has no rules. */
|
||||||
|
@Test
|
||||||
|
public void testGetInheritedRuleSets()
|
||||||
|
{
|
||||||
|
// Simulate a parent node without a rule set.
|
||||||
|
NodeRef parentNode = new NodeRef("parent://node/");
|
||||||
|
// Simulate a grandparent node providing a rule set.
|
||||||
|
NodeRef grandparentNode = new NodeRef("grandparent://node/");
|
||||||
|
RuleSet grandparentRuleSet = mock(RuleSet.class);
|
||||||
|
NodeRef grandparentRuleSetNode = new NodeRef("grandparent://rule-set/");
|
||||||
|
given(ruleServiceMock.getRuleSetNode(grandparentNode)).willReturn(grandparentRuleSetNode);
|
||||||
|
given(ruleSetLoaderMock.loadRuleSet(grandparentRuleSetNode, FOLDER_NODE, INCLUDES)).willReturn(grandparentRuleSet);
|
||||||
|
// These should be returned with the highest in hierarchy first.
|
||||||
|
given(ruleServiceMock.getNodesSupplyingRuleSets(FOLDER_NODE)).willReturn(List.of(grandparentNode, parentNode, FOLDER_NODE));
|
||||||
|
|
||||||
|
// Call the method under test.
|
||||||
|
CollectionWithPagingInfo<RuleSet> actual = ruleSets.getRuleSets(FOLDER_ID, INCLUDES, PAGING);
|
||||||
|
|
||||||
|
then(nodeValidatorMock).should().validateFolderNode(FOLDER_ID, false);
|
||||||
|
then(nodeValidatorMock).shouldHaveNoMoreInteractions();
|
||||||
|
|
||||||
|
then(ruleServiceMock).should().getNodesSupplyingRuleSets(FOLDER_NODE);
|
||||||
|
then(ruleServiceMock).should().getRuleSetNode(grandparentNode);
|
||||||
|
then(ruleServiceMock).should().getRuleSetNode(parentNode);
|
||||||
|
then(ruleServiceMock).should().getRuleSetNode(FOLDER_NODE);
|
||||||
|
then(ruleServiceMock).shouldHaveNoMoreInteractions();
|
||||||
|
|
||||||
|
Collection<RuleSet> expected = List.of(grandparentRuleSet, ruleSetMock);
|
||||||
|
assertEquals(expected, actual.getCollection());
|
||||||
|
assertEquals(PAGING, actual.getPaging());
|
||||||
|
}
|
||||||
|
|
||||||
|
/** When getting rule sets then only the first instance of each rule set should be included (ancestor first). */
|
||||||
|
@Test
|
||||||
|
public void testGetDuplicateRuleSets()
|
||||||
|
{
|
||||||
|
// Simulate a grandparent, parent and child with the grandparent linking to the child's rule set.
|
||||||
|
NodeRef grandparentNode = new NodeRef("grandparent://node/");
|
||||||
|
given(ruleServiceMock.getRuleSetNode(grandparentNode)).willReturn(RULE_SET_NODE);
|
||||||
|
NodeRef parentNode = new NodeRef("parent://node/");
|
||||||
|
RuleSet parentRuleSet = mock(RuleSet.class);
|
||||||
|
NodeRef parentRuleSetNode = new NodeRef("parent://rule-set/");
|
||||||
|
given(ruleServiceMock.getRuleSetNode(parentNode)).willReturn(parentRuleSetNode);
|
||||||
|
given(ruleSetLoaderMock.loadRuleSet(parentRuleSetNode, FOLDER_NODE, INCLUDES)).willReturn(parentRuleSet);
|
||||||
|
// These should be returned with the highest in hierarchy first.
|
||||||
|
given(ruleServiceMock.getNodesSupplyingRuleSets(FOLDER_NODE)).willReturn(List.of(grandparentNode, parentNode, FOLDER_NODE));
|
||||||
|
|
||||||
|
// Call the method under test.
|
||||||
|
CollectionWithPagingInfo<RuleSet> actual = ruleSets.getRuleSets(FOLDER_ID, INCLUDES, PAGING);
|
||||||
|
|
||||||
|
// The grandparent's linked rule set should be first and only appear once.
|
||||||
|
Collection<RuleSet> expected = List.of(ruleSetMock, parentRuleSet);
|
||||||
|
assertEquals(expected, actual.getCollection());
|
||||||
|
assertEquals(PAGING, actual.getPaging());
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testGetRuleSetById()
|
public void testGetRuleSetById()
|
||||||
{
|
{
|
||||||
|
@@ -25,6 +25,8 @@
|
|||||||
*/
|
*/
|
||||||
package org.alfresco.repo.rule;
|
package org.alfresco.repo.rule;
|
||||||
|
|
||||||
|
import static org.alfresco.repo.rule.RuleModel.ASPECT_IGNORE_INHERITED_RULES;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
@@ -479,7 +481,7 @@ public class RuleServiceImpl
|
|||||||
// Node has gone or is not the correct type
|
// Node has gone or is not the correct type
|
||||||
return rules;
|
return rules;
|
||||||
}
|
}
|
||||||
if (includeInherited == true && runtimeNodeService.hasAspect(nodeRef, RuleModel.ASPECT_IGNORE_INHERITED_RULES) == false)
|
if (includeInherited && !runtimeNodeService.hasAspect(nodeRef, ASPECT_IGNORE_INHERITED_RULES))
|
||||||
{
|
{
|
||||||
// Get any inherited rules
|
// Get any inherited rules
|
||||||
for (Rule rule : getInheritedRules(nodeRef, ruleTypeName, null))
|
for (Rule rule : getInheritedRules(nodeRef, ruleTypeName, null))
|
||||||
@@ -597,10 +599,53 @@ public class RuleServiceImpl
|
|||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** {@inheritDoc} */
|
||||||
|
@Override
|
||||||
|
@Experimental
|
||||||
|
public List<NodeRef> getNodesSupplyingRuleSets(NodeRef nodeRef)
|
||||||
|
{
|
||||||
|
return getNodesSupplyingRuleSets(nodeRef, new ArrayList<>());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Traverse the folder hierarchy find all the folder nodes that could supply rules by inheritance.
|
||||||
|
* <p>
|
||||||
|
* The order of nodes returned by this methods has to match the order used by {@link #getInheritedRules}.
|
||||||
|
*
|
||||||
|
* @param nodeRef The starting node ref.
|
||||||
|
* @param visitedNodeRefs All the visited node refs (will be modified).
|
||||||
|
* @return A list of node refs, starting with the first parent of the first parent of ... and ending with the object generated by the
|
||||||
|
* given node ref.
|
||||||
|
*/
|
||||||
|
private List<NodeRef> getNodesSupplyingRuleSets(NodeRef nodeRef, List<NodeRef> visitedNodeRefs)
|
||||||
|
{
|
||||||
|
List<NodeRef> returnList = new ArrayList<>();
|
||||||
|
// This check prevents stack over flow when we have a cyclic node graph
|
||||||
|
if (!visitedNodeRefs.contains(nodeRef))
|
||||||
|
{
|
||||||
|
visitedNodeRefs.add(nodeRef);
|
||||||
|
if (!runtimeNodeService.hasAspect(nodeRef, ASPECT_IGNORE_INHERITED_RULES))
|
||||||
|
{
|
||||||
|
List<ChildAssociationRef> parents = runtimeNodeService.getParentAssocs(nodeRef);
|
||||||
|
for (ChildAssociationRef parent : parents)
|
||||||
|
{
|
||||||
|
// We are not interested in following potentially massive person group membership trees!
|
||||||
|
if (!IGNORE_PARENT_ASSOC_TYPES.contains(parent.getTypeQName()))
|
||||||
|
{
|
||||||
|
// Update visitedNodeRefs with all the ancestors.
|
||||||
|
returnList.addAll(getNodesSupplyingRuleSets(parent.getParentRef(), visitedNodeRefs));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
returnList.add(nodeRef);
|
||||||
|
}
|
||||||
|
return returnList;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the inherited rules for a given node reference
|
* Gets the inherited rules for a given node reference
|
||||||
*
|
*
|
||||||
* @param nodeRef the nodeRef
|
* @param nodeRef the nodeRef
|
||||||
* @param ruleTypeName the rule type (null if all applicable)
|
* @param ruleTypeName the rule type (null if all applicable)
|
||||||
* @return a list of inherited rules (empty if none)
|
* @return a list of inherited rules (empty if none)
|
||||||
@@ -608,20 +653,20 @@ public class RuleServiceImpl
|
|||||||
private List<Rule> getInheritedRules(NodeRef nodeRef, String ruleTypeName, Set<NodeRef> visitedNodeRefs)
|
private List<Rule> getInheritedRules(NodeRef nodeRef, String ruleTypeName, Set<NodeRef> visitedNodeRefs)
|
||||||
{
|
{
|
||||||
List<Rule> inheritedRules = new ArrayList<Rule>();
|
List<Rule> inheritedRules = new ArrayList<Rule>();
|
||||||
|
|
||||||
if (this.runtimeNodeService.hasAspect(nodeRef, RuleModel.ASPECT_IGNORE_INHERITED_RULES) == false)
|
if (this.runtimeNodeService.hasAspect(nodeRef, RuleModel.ASPECT_IGNORE_INHERITED_RULES) == false)
|
||||||
{
|
{
|
||||||
// Create the visited nodes set if it has not already been created
|
// Create the visited nodes set if it has not already been created
|
||||||
if (visitedNodeRefs == null)
|
if (visitedNodeRefs == null)
|
||||||
{
|
{
|
||||||
visitedNodeRefs = new HashSet<NodeRef>();
|
visitedNodeRefs = new HashSet<NodeRef>();
|
||||||
}
|
}
|
||||||
|
|
||||||
// This check prevents stack over flow when we have a cyclic node graph
|
// This check prevents stack over flow when we have a cyclic node graph
|
||||||
if (visitedNodeRefs.contains(nodeRef) == false)
|
if (visitedNodeRefs.contains(nodeRef) == false)
|
||||||
{
|
{
|
||||||
visitedNodeRefs.add(nodeRef);
|
visitedNodeRefs.add(nodeRef);
|
||||||
|
|
||||||
List<Rule> allInheritedRules = new ArrayList<Rule>();
|
List<Rule> allInheritedRules = new ArrayList<Rule>();
|
||||||
List<ChildAssociationRef> parents = this.runtimeNodeService.getParentAssocs(nodeRef);
|
List<ChildAssociationRef> parents = this.runtimeNodeService.getParentAssocs(nodeRef);
|
||||||
for (ChildAssociationRef parent : parents)
|
for (ChildAssociationRef parent : parents)
|
||||||
@@ -641,7 +686,7 @@ public class RuleServiceImpl
|
|||||||
allInheritedRules.add(rule);
|
allInheritedRules.add(rule);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Rule> rules = getRules(parent.getParentRef(), false);
|
List<Rule> rules = getRules(parent.getParentRef(), false);
|
||||||
for (Rule rule : rules)
|
for (Rule rule : rules)
|
||||||
{
|
{
|
||||||
@@ -652,7 +697,7 @@ public class RuleServiceImpl
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ruleTypeName == null)
|
if (ruleTypeName == null)
|
||||||
{
|
{
|
||||||
inheritedRules = allInheritedRules;
|
inheritedRules = allInheritedRules;
|
||||||
@@ -670,7 +715,7 @@ public class RuleServiceImpl
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return inheritedRules;
|
return inheritedRules;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -215,7 +215,18 @@ public interface RuleService
|
|||||||
*/
|
*/
|
||||||
@Auditable(parameters = {"nodeRef"})
|
@Auditable(parameters = {"nodeRef"})
|
||||||
public int countRules(NodeRef nodeRef);
|
public int countRules(NodeRef nodeRef);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Traverse the folder hierarchy find all the folder nodes that could supply rules by inheritance.
|
||||||
|
*
|
||||||
|
* @param nodeRef The starting node ref.
|
||||||
|
* @return A list of node refs, starting with the first parent of the first parent of ... and ending with the object generated by the
|
||||||
|
* given node ref.
|
||||||
|
*/
|
||||||
|
@Auditable (parameters = { "nodeRef" })
|
||||||
|
@Experimental
|
||||||
|
List<NodeRef> getNodesSupplyingRuleSets(NodeRef nodeRef);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the rule given its node reference
|
* Get the rule given its node reference
|
||||||
*
|
*
|
||||||
|
@@ -25,7 +25,12 @@
|
|||||||
*/
|
*/
|
||||||
package org.alfresco.repo.rule;
|
package org.alfresco.repo.rule;
|
||||||
|
|
||||||
|
import static java.util.stream.Collectors.joining;
|
||||||
|
import static java.util.stream.Collectors.toList;
|
||||||
|
|
||||||
import static org.alfresco.model.ContentModel.ASSOC_CONTAINS;
|
import static org.alfresco.model.ContentModel.ASSOC_CONTAINS;
|
||||||
|
import static org.alfresco.model.ContentModel.ASSOC_MEMBER;
|
||||||
|
import static org.alfresco.repo.rule.RuleModel.ASPECT_IGNORE_INHERITED_RULES;
|
||||||
import static org.alfresco.repo.rule.RuleModel.ASSOC_ACTION;
|
import static org.alfresco.repo.rule.RuleModel.ASSOC_ACTION;
|
||||||
import static org.alfresco.repo.rule.RuleModel.ASSOC_RULE_FOLDER;
|
import static org.alfresco.repo.rule.RuleModel.ASSOC_RULE_FOLDER;
|
||||||
import static org.alfresco.repo.rule.RuleModel.TYPE_RULE;
|
import static org.alfresco.repo.rule.RuleModel.TYPE_RULE;
|
||||||
@@ -33,6 +38,7 @@ import static org.alfresco.service.cmr.security.AccessStatus.ALLOWED;
|
|||||||
import static org.alfresco.service.cmr.security.AccessStatus.DENIED;
|
import static org.alfresco.service.cmr.security.AccessStatus.DENIED;
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.mockito.ArgumentMatchers.any;
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
import static org.mockito.ArgumentMatchers.eq;
|
import static org.mockito.ArgumentMatchers.eq;
|
||||||
import static org.mockito.ArgumentMatchers.nullable;
|
import static org.mockito.ArgumentMatchers.nullable;
|
||||||
@@ -47,8 +53,13 @@ import static org.mockito.MockitoAnnotations.openMocks;
|
|||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import org.alfresco.model.ContentModel;
|
||||||
import org.alfresco.repo.action.RuntimeActionService;
|
import org.alfresco.repo.action.RuntimeActionService;
|
||||||
import org.alfresco.repo.cache.SimpleCache;
|
import org.alfresco.repo.cache.SimpleCache;
|
||||||
import org.alfresco.service.cmr.action.Action;
|
import org.alfresco.service.cmr.action.Action;
|
||||||
@@ -61,6 +72,7 @@ import org.alfresco.service.cmr.rule.RuleService;
|
|||||||
import org.alfresco.service.cmr.rule.RuleServiceException;
|
import org.alfresco.service.cmr.rule.RuleServiceException;
|
||||||
import org.alfresco.service.cmr.security.PermissionService;
|
import org.alfresco.service.cmr.security.PermissionService;
|
||||||
import org.alfresco.service.namespace.QName;
|
import org.alfresco.service.namespace.QName;
|
||||||
|
import org.apache.commons.collections.MapUtils;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.mockito.InjectMocks;
|
import org.mockito.InjectMocks;
|
||||||
@@ -356,4 +368,174 @@ public class RuleServiceImplUnitTest
|
|||||||
{
|
{
|
||||||
return new ChildAssociationRef(null, parentRef, null, childRef, isPrimary, 1);
|
return new ChildAssociationRef(null, parentRef, null, childRef, isPrimary, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Check that a straight chain of nodes is traversed correctly. */
|
||||||
|
@Test
|
||||||
|
public void testGetNodesSupplyingRuleSets_chain()
|
||||||
|
{
|
||||||
|
Map<String, NodeRef> nodes = createParentChildHierarchy("A,B", "B,C", "C,D", "D,E");
|
||||||
|
|
||||||
|
List<NodeRef> actual = ruleService.getNodesSupplyingRuleSets(nodes.get("E"));
|
||||||
|
|
||||||
|
Map<NodeRef, String> invertedMap = MapUtils.invertMap(nodes);
|
||||||
|
String nodeNames = actual.stream().map(invertedMap::get).collect(joining(","));
|
||||||
|
|
||||||
|
assertEquals("A,B,C,D,E", nodeNames);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Check that ordered parents are returned in the correct order. */
|
||||||
|
@Test
|
||||||
|
public void testGetNodesSupplyingRuleSets_multipleParents()
|
||||||
|
{
|
||||||
|
Map<String, NodeRef> nodes = createParentChildHierarchy("A,E", "B,E", "C,E", "D,E");
|
||||||
|
|
||||||
|
List<NodeRef> actual = ruleService.getNodesSupplyingRuleSets(nodes.get("E"));
|
||||||
|
|
||||||
|
Map<NodeRef, String> invertedMap = MapUtils.invertMap(nodes);
|
||||||
|
String nodeNames = actual.stream().map(invertedMap::get).collect(joining(","));
|
||||||
|
|
||||||
|
assertEquals("A,B,C,D,E", nodeNames);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Check that the ASPECT_IGNORE_INHERITED_RULES aspect breaks the chain. */
|
||||||
|
@Test
|
||||||
|
public void testGetNodesSupplyingRuleSets_brokenChain()
|
||||||
|
{
|
||||||
|
Map<String, NodeRef> nodes = createParentChildHierarchy("A,B", "B,C", "C,D", "D,E");
|
||||||
|
given(runtimeNodeService.hasAspect(nodes.get("C"), ASPECT_IGNORE_INHERITED_RULES)).willReturn(true);
|
||||||
|
|
||||||
|
List<NodeRef> actual = ruleService.getNodesSupplyingRuleSets(nodes.get("E"));
|
||||||
|
|
||||||
|
Map<NodeRef, String> invertedMap = MapUtils.invertMap(nodes);
|
||||||
|
String nodeNames = actual.stream().map(invertedMap::get).collect(joining(","));
|
||||||
|
|
||||||
|
assertEquals("C,D,E", nodeNames);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Check that the user group hierarchy is not traversed. */
|
||||||
|
@Test
|
||||||
|
public void testGetNodesSupplyingRuleSets_userGroupHierarchy()
|
||||||
|
{
|
||||||
|
Map<String, NodeRef> nodes = createParentChildHierarchy("A,B", "B,C", "C,D", "D,E");
|
||||||
|
// Replace the B,C association with a user group membership association.
|
||||||
|
ChildAssociationRef memberAssoc = new ChildAssociationRef(ASSOC_MEMBER, nodes.get("B"), ContentModel.TYPE_FOLDER, nodes.get("C"));
|
||||||
|
given(runtimeNodeService.getParentAssocs(nodes.get("C"))).willReturn(List.of(memberAssoc));
|
||||||
|
|
||||||
|
List<NodeRef> actual = ruleService.getNodesSupplyingRuleSets(nodes.get("E"));
|
||||||
|
|
||||||
|
Map<NodeRef, String> invertedMap = MapUtils.invertMap(nodes);
|
||||||
|
String nodeNames = actual.stream().map(invertedMap::get).collect(joining(","));
|
||||||
|
|
||||||
|
assertEquals("C,D,E", nodeNames);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Check that a cycle doesn't cause a problem. */
|
||||||
|
@Test
|
||||||
|
public void testGetNodesSupplyingRuleSets_infiniteCycle()
|
||||||
|
{
|
||||||
|
Map<String, NodeRef> nodes = createParentChildHierarchy("A,B", "B,C", "C,A");
|
||||||
|
|
||||||
|
List<NodeRef> actual = ruleService.getNodesSupplyingRuleSets(nodes.get("C"));
|
||||||
|
|
||||||
|
Map<NodeRef, String> invertedMap = MapUtils.invertMap(nodes);
|
||||||
|
String nodeNames = actual.stream().map(invertedMap::get).collect(joining(","));
|
||||||
|
|
||||||
|
assertEquals("A,B,C", nodeNames);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Check that a diamond of nodes is traversed correctly. */
|
||||||
|
@Test
|
||||||
|
public void testGetNodesSupplyingRuleSets_diamond()
|
||||||
|
{
|
||||||
|
Map<String, NodeRef> nodes = createParentChildHierarchy("A,B", "A,C", "B,D", "C,D");
|
||||||
|
|
||||||
|
List<NodeRef> actual = ruleService.getNodesSupplyingRuleSets(nodes.get("D"));
|
||||||
|
|
||||||
|
Map<NodeRef, String> invertedMap = MapUtils.invertMap(nodes);
|
||||||
|
String nodeNames = actual.stream().map(invertedMap::get).collect(joining(","));
|
||||||
|
|
||||||
|
assertEquals("A,B,C,D", nodeNames);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check that hierarchy of nodes is traversed correctly. Parent-child associations are created in alphabetical order.
|
||||||
|
* <pre>
|
||||||
|
* A
|
||||||
|
* /|\
|
||||||
|
* B C D
|
||||||
|
* | |\|
|
||||||
|
* E | F
|
||||||
|
* \|/
|
||||||
|
* G
|
||||||
|
* </pre>
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testGetNodesSupplyingRuleSets_alphabetical()
|
||||||
|
{
|
||||||
|
Map<String, NodeRef> nodes = createParentChildHierarchy("A,B", "A,C", "A,D", "B,E", "C,F", "C,G", "D,F", "E,G", "F,G");
|
||||||
|
|
||||||
|
List<NodeRef> actual = ruleService.getNodesSupplyingRuleSets(nodes.get("G"));
|
||||||
|
|
||||||
|
Map<NodeRef, String> invertedMap = MapUtils.invertMap(nodes);
|
||||||
|
String nodeNames = actual.stream().map(invertedMap::get).collect(joining(","));
|
||||||
|
|
||||||
|
assertEquals("A,C,B,E,D,F,G", nodeNames);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check that hierarchy of nodes is traversed correctly. Parent-child associations are created in reverse alphabetical order.
|
||||||
|
* <pre>
|
||||||
|
* A
|
||||||
|
* /|\
|
||||||
|
* B C D
|
||||||
|
* | |\|
|
||||||
|
* E | F
|
||||||
|
* \|/
|
||||||
|
* G
|
||||||
|
* </pre>
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testGetNodesSupplyingRuleSets_reversedAssociationOrder()
|
||||||
|
{
|
||||||
|
Map<String, NodeRef> nodes = createParentChildHierarchy("F,G", "E,G", "D,F", "C,G", "C,F", "B,E", "A,D", "A,C", "A,B");
|
||||||
|
|
||||||
|
List<NodeRef> actual = ruleService.getNodesSupplyingRuleSets(nodes.get("G"));
|
||||||
|
|
||||||
|
Map<NodeRef, String> invertedMap = MapUtils.invertMap(nodes);
|
||||||
|
String nodeNames = actual.stream().map(invertedMap::get).collect(joining(","));
|
||||||
|
|
||||||
|
assertEquals("A,D,C,F,B,E,G", nodeNames);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a mock hierarchy of nodes using the supplied parent child associations.
|
||||||
|
*
|
||||||
|
* @param parentChildAssociations A list of strings of the form "Parent,Child". Associations will be created in this order.
|
||||||
|
* @return A map from the node name to the new NodeRef object.
|
||||||
|
*/
|
||||||
|
private Map<String, NodeRef> createParentChildHierarchy(String... parentChildAssociations)
|
||||||
|
{
|
||||||
|
// Find all the node names mentioned.
|
||||||
|
Set<String> nodeNames = new HashSet<>();
|
||||||
|
List.of(parentChildAssociations).forEach(parentChildAssociation -> {
|
||||||
|
String[] parentChildPair = parentChildAssociation.split(",");
|
||||||
|
nodeNames.addAll(List.of(parentChildPair));
|
||||||
|
});
|
||||||
|
// Create the NodeRefs.
|
||||||
|
Map<String, NodeRef> nodeRefMap = nodeNames.stream().collect(
|
||||||
|
Collectors.toMap(nodeName -> nodeName, nodeName -> new NodeRef("node://" + nodeName + "/")));
|
||||||
|
// Mock the associations.
|
||||||
|
nodeNames.forEach(nodeName -> {
|
||||||
|
NodeRef nodeRef = nodeRefMap.get(nodeName);
|
||||||
|
List<ChildAssociationRef> parentAssocs = List.of(parentChildAssociations)
|
||||||
|
.stream()
|
||||||
|
.filter(assoc -> assoc.endsWith(nodeName))
|
||||||
|
.map(assoc -> assoc.split(",")[0])
|
||||||
|
.map(nodeRefMap::get)
|
||||||
|
.map(parentRef -> new ChildAssociationRef(ASSOC_CONTAINS, parentRef, ContentModel.TYPE_FOLDER, nodeRef))
|
||||||
|
.collect(toList());
|
||||||
|
given(runtimeNodeService.getParentAssocs(nodeRef)).willReturn(parentAssocs);
|
||||||
|
});
|
||||||
|
return nodeRefMap;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user