diff --git a/packaging/tests/tas-restapi/src/test/java/org/alfresco/rest/rules/GetInheritedRulesTests.java b/packaging/tests/tas-restapi/src/test/java/org/alfresco/rest/rules/GetInheritedRulesTests.java
new file mode 100644
index 0000000000..ebe1017b2c
--- /dev/null
+++ b/packaging/tests/tas-restapi/src/test/java/org/alfresco/rest/rules/GetInheritedRulesTests.java
@@ -0,0 +1,142 @@
+/*
+ * #%L
+ * Alfresco Repository
+ * %%
+ * Copyright (C) 2005 - 2016 Alfresco Software Limited
+ * %%
+ * This file is part of the Alfresco software.
+ * If the software was purchased under a paid Alfresco license, the terms of
+ * the paid license agreement will prevail. Otherwise, the software is
+ * provided under the following open source license terms:
+ *
+ * Alfresco is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Alfresco is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Alfresco. If not, see .
+ * #L%
+ */
+package org.alfresco.rest.rules;
+
+import static org.alfresco.rest.rules.RulesTestsUtils.createRuleModelWithModifiedValues;
+import static org.alfresco.utility.report.log.Step.STEP;
+import static org.testng.Assert.assertEquals;
+
+import java.util.List;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+
+import org.alfresco.rest.RestTest;
+import org.alfresco.rest.model.RestRuleModel;
+import org.alfresco.rest.model.RestRuleModelsCollection;
+import org.alfresco.rest.model.RestRuleSetLinkModel;
+import org.alfresco.rest.model.RestRuleSetModel;
+import org.alfresco.rest.model.RestRuleSetModelsCollection;
+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;
+
+/**
+ * Tests for GET /nodes/{nodeId}/rule-sets/{ruleSetId}/rules with rule inheritance.
+ */
+@Test(groups = {TestGroup.RULES})
+public class GetInheritedRulesTests extends RestTest
+{
+ private UserModel user;
+ private SiteModel site;
+
+ @BeforeClass(alwaysRun = true)
+ public void dataPreparation()
+ {
+ STEP("Create a user and site");
+ user = dataUser.createRandomTestUser();
+ site = dataSite.usingUser(user).createPublicRandomSite();
+ }
+
+ /**
+ * Check we can get all the rules for the folder by providing the different rule set ids.
+ */
+ @Test (groups = { TestGroup.REST_API, TestGroup.RULES, TestGroup.SANITY })
+ public void getInheritedRules()
+ {
+ STEP("Create a parent and child folder, each with inheriting rules");
+ FolderModel parent = dataContent.usingUser(user).usingSite(site).createFolder();
+ FolderModel child = dataContent.usingUser(user).usingResource(parent).createFolder();
+ RestRuleModel parentRule = createRuleModelWithModifiedValues();
+ parentRule = restClient.authenticateUser(user).withCoreAPI().usingNode(parent).usingDefaultRuleSet().createSingleRule(parentRule);
+ RestRuleModel childRule = createRuleModelWithModifiedValues();
+ childRule = restClient.authenticateUser(user).withCoreAPI().usingNode(child).usingDefaultRuleSet().createSingleRule(childRule);
+
+ STEP("Get the rules in the default rule set for the child folder");
+ RestRuleModelsCollection rules = restClient.authenticateUser(user).withCoreAPI().usingNode(child).usingDefaultRuleSet().getListOfRules();
+ rules.assertThat().entriesListContains("id", childRule.getId())
+ .and().entriesListCountIs(1);
+
+ STEP("Get the rules in the inherited rule set for the child folder");
+ RestRuleSetModelsCollection ruleSets = restClient.authenticateUser(user).withCoreAPI().usingNode(child).include("inclusionType").getListOfRuleSets();
+ String inheritedRuleSetId = ruleSets.getEntries().stream()
+ .filter(ruleSet -> ruleSet.onModel().getInclusionType().equals("inherited"))
+ .findFirst().get().onModel().getId();
+ RestRuleModelsCollection inheritedRules = restClient.authenticateUser(user).withCoreAPI().usingNode(child).usingRuleSet(inheritedRuleSetId).getListOfRules();
+ inheritedRules.assertThat().entriesListContains("id", parentRule.getId())
+ .and().entriesListCountIs(1);
+ }
+
+ /**
+ * Check that we only get each rule once with linking and inheritance, and the order is correct.
+ *
+ * The folder structure for this test is as follows:
+ *
+ * A --[links]-> DRuleSet
+ * +-B --[owns]-> BRuleSet
+ * +-C --[owns]-> CRuleSet
+ * +-D --[owns]--> DRuleSet
+ *
+ */
+ @Test (groups = { TestGroup.REST_API, TestGroup.RULES, TestGroup.SANITY })
+ public void rulesReturnedAreUnique()
+ {
+ STEP("Create four folders with rules");
+ FolderModel folderA = dataContent.usingUser(user).usingSite(site).createFolder();
+ FolderModel folderB = dataContent.usingUser(user).usingResource(folderA).createFolder();
+ FolderModel folderC = dataContent.usingUser(user).usingResource(folderB).createFolder();
+ FolderModel folderD = dataContent.usingUser(user).usingResource(folderC).createFolder();
+ RestRuleModel ruleB = restClient.authenticateUser(user).withCoreAPI().usingNode(folderB).usingDefaultRuleSet().createSingleRule(createRuleModelWithModifiedValues());
+ RestRuleModel ruleC = restClient.authenticateUser(user).withCoreAPI().usingNode(folderC).usingDefaultRuleSet().createSingleRule(createRuleModelWithModifiedValues());
+ RestRuleModel ruleD = restClient.authenticateUser(user).withCoreAPI().usingNode(folderD).usingDefaultRuleSet().createSingleRule(createRuleModelWithModifiedValues());
+ STEP("Link folderA to ruleSetD");
+ RestRuleSetLinkModel linkModel = new RestRuleSetLinkModel();
+ linkModel.setId(folderD.getNodeRef());
+ restClient.authenticateUser(user).withCoreAPI().usingNode(folderA).createRuleLink(linkModel);
+
+ STEP("Get the rule sets for the folderD");
+ List ruleSets = restClient.authenticateUser(user).withCoreAPI().usingNode(folderD).getListOfRuleSets().getEntries();
+
+ STEP("Check the rules for each rule set are as expected");
+ List expectedRuleIds = List.of(ruleD, ruleB, ruleC);
+ IntStream.range(0, 2).forEach(index -> {
+ String ruleSetId = ruleSets.get(index).onModel().getId();
+ List rules = restClient.authenticateUser(user)
+ .withCoreAPI()
+ .usingNode(folderD)
+ .usingRuleSet(ruleSetId)
+ .getListOfRules()
+ .getEntries()
+ .stream()
+ .map(RestRuleModel::onModel)
+ .collect(Collectors.toList());
+ assertEquals(rules, List.of(expectedRuleIds.get(index)), "Unexpected rules found for rule set " + ruleSetId);
+ });
+ assertEquals(ruleSets.size(), 3, "Expected three unique rule sets to be returned but got " + ruleSets);
+ }
+}
diff --git a/remote-api/src/main/java/org/alfresco/rest/api/impl/rules/RulesImpl.java b/remote-api/src/main/java/org/alfresco/rest/api/impl/rules/RulesImpl.java
index 5f63d37cd7..81efa535e8 100644
--- a/remote-api/src/main/java/org/alfresco/rest/api/impl/rules/RulesImpl.java
+++ b/remote-api/src/main/java/org/alfresco/rest/api/impl/rules/RulesImpl.java
@@ -63,9 +63,10 @@ public class RulesImpl implements Rules
final Paging paging)
{
final NodeRef folderNodeRef = validator.validateFolderNode(folderNodeId, false);
- validator.validateRuleSetNode(ruleSetId, folderNodeRef);
+ NodeRef ruleSetNode = validator.validateRuleSetNode(ruleSetId, folderNodeRef);
+ NodeRef owningFolder = ruleService.getOwningNodeRef(ruleSetNode);
- final List rules = ruleService.getRules(folderNodeRef).stream()
+ final List rules = ruleService.getRules(owningFolder, false).stream()
.map(ruleModel -> loadRuleAndConvertActionParams(ruleModel, includes))
.collect(Collectors.toList());
diff --git a/remote-api/src/test/java/org/alfresco/rest/api/impl/rules/RulesImplTest.java b/remote-api/src/test/java/org/alfresco/rest/api/impl/rules/RulesImplTest.java
index f7c4aad7d5..cd6c8b67f4 100644
--- a/remote-api/src/test/java/org/alfresco/rest/api/impl/rules/RulesImplTest.java
+++ b/remote-api/src/test/java/org/alfresco/rest/api/impl/rules/RulesImplTest.java
@@ -115,7 +115,8 @@ public class RulesImplTest extends TestCase
given(nodeValidatorMock.validateRuleNode(any(), any())).willReturn(RULE_NODE_REF);
given(ruleServiceMock.getRule(RULE_NODE_REF)).willReturn(ruleModel);
- given(ruleServiceMock.getRules(FOLDER_NODE_REF)).willReturn(List.of(ruleModel));
+ given(ruleServiceMock.getRules(FOLDER_NODE_REF, false)).willReturn(List.of(ruleModel));
+ given(ruleServiceMock.getOwningNodeRef(RULE_SET_NODE_REF)).willReturn(FOLDER_NODE_REF);
given(ruleLoaderMock.loadRule(ruleModel, INCLUDE)).willReturn(ruleMock);
@@ -126,13 +127,15 @@ public class RulesImplTest extends TestCase
public void testGetRules()
{
given(ruleLoaderMock.loadRule(ruleModel, emptyList())).willReturn(ruleMock);
+
// when
final CollectionWithPagingInfo rulesPage = rules.getRules(FOLDER_NODE_ID, RULE_SET_ID, INCLUDE, PAGING);
then(nodeValidatorMock).should().validateFolderNode(FOLDER_NODE_ID, false);
then(nodeValidatorMock).should().validateRuleSetNode(RULE_SET_ID, FOLDER_NODE_REF);
then(nodeValidatorMock).shouldHaveNoMoreInteractions();
- then(ruleServiceMock).should().getRules(FOLDER_NODE_REF);
+ then(ruleServiceMock).should().getOwningNodeRef(RULE_SET_NODE_REF);
+ then(ruleServiceMock).should().getRules(FOLDER_NODE_REF, false);
then(ruleServiceMock).shouldHaveNoMoreInteractions();
then(ruleLoaderMock).should().loadRule(ruleModel, emptyList());
then(ruleLoaderMock).shouldHaveNoMoreInteractions();
@@ -148,12 +151,13 @@ public class RulesImplTest extends TestCase
@Test
public void testGetRules_emptyResult()
{
- given(ruleServiceMock.getRules(any())).willReturn(emptyList());
+ given(ruleServiceMock.getRules(FOLDER_NODE_REF, false)).willReturn(emptyList());
// when
final CollectionWithPagingInfo rulesPage = rules.getRules(FOLDER_NODE_ID, RULE_SET_ID, INCLUDE, PAGING);
- then(ruleServiceMock).should().getRules(FOLDER_NODE_REF);
+ then(ruleServiceMock).should().getOwningNodeRef(RULE_SET_NODE_REF);
+ then(ruleServiceMock).should().getRules(FOLDER_NODE_REF, false);
then(ruleServiceMock).shouldHaveNoMoreInteractions();
assertThat(rulesPage)
.isNotNull()
diff --git a/repository/src/main/java/org/alfresco/repo/rule/RuleServiceImpl.java b/repository/src/main/java/org/alfresco/repo/rule/RuleServiceImpl.java
index 646ef4352a..fa92c02204 100644
--- a/repository/src/main/java/org/alfresco/repo/rule/RuleServiceImpl.java
+++ b/repository/src/main/java/org/alfresco/repo/rule/RuleServiceImpl.java
@@ -26,6 +26,7 @@
package org.alfresco.repo.rule;
import static org.alfresco.repo.rule.RuleModel.ASPECT_IGNORE_INHERITED_RULES;
+import static org.alfresco.repo.rule.RuleModel.ASSOC_RULE_FOLDER;
import java.io.Serializable;
import java.util.ArrayList;
@@ -35,6 +36,9 @@ import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.stream.Collectors;
+
+import com.google.common.collect.Sets;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.action.ActionImpl;
@@ -257,7 +261,7 @@ public class RuleServiceImpl
policyComponent.bindAssociationBehaviour(
NodeServicePolicies.OnCreateChildAssociationPolicy.QNAME,
RuleModel.ASPECT_RULES,
- RuleModel.ASSOC_RULE_FOLDER,
+ ASSOC_RULE_FOLDER,
new JavaBehaviour(this, "onCreateChildAssociation"));
policyComponent.bindClassBehaviour(
NodeServicePolicies.OnAddAspectPolicy.QNAME,
@@ -349,8 +353,8 @@ public class RuleServiceImpl
List assocs = this.runtimeNodeService.getChildAssocs(
nodeRef,
- RuleModel.ASSOC_RULE_FOLDER,
- RuleModel.ASSOC_RULE_FOLDER);
+ ASSOC_RULE_FOLDER,
+ ASSOC_RULE_FOLDER);
if (assocs.size() > 1)
{
throw new ActionServiceException("There is more than one rule folder, which is invalid.");
@@ -1568,6 +1572,12 @@ public class RuleServiceImpl
return this.nodeService.getPrimaryParent(systemFolder).getParentRef();
}
+ @Override
+ public NodeRef getOwningNodeRef(NodeRef ruleSet)
+ {
+ return nodeService.getPrimaryParent(ruleSet).getParentRef();
+ }
+
@Override
public NodeRef getOwningNodeRef(final Action action)
{
@@ -1660,7 +1670,7 @@ public class RuleServiceImpl
@Experimental
public NodeRef getRuleSetNode(final NodeRef folderNodeRef)
{
- return runtimeNodeService.getChildAssocs(folderNodeRef, RuleModel.ASSOC_RULE_FOLDER, RuleModel.ASSOC_RULE_FOLDER).stream()
+ return runtimeNodeService.getChildAssocs(folderNodeRef, ASSOC_RULE_FOLDER, ASSOC_RULE_FOLDER).stream()
.map(ChildAssociationRef::getChildRef)
.findFirst()
.orElse(null);
@@ -1670,7 +1680,10 @@ public class RuleServiceImpl
@Experimental
public boolean isRuleSetAssociatedWithFolder(final NodeRef ruleSetNodeRef, final NodeRef folderNodeRef)
{
- return isChildOf(ruleSetNodeRef, RuleModel.ASSOC_RULE_FOLDER, folderNodeRef);
+ List associations = runtimeNodeService.getParentAssocs(ruleSetNodeRef, ASSOC_RULE_FOLDER, ASSOC_RULE_FOLDER);
+ Set associatedFolders = associations.stream().map(ChildAssociationRef::getParentRef).collect(Collectors.toSet());
+ Set supplyingFolders = new HashSet<>(getNodesSupplyingRuleSets(folderNodeRef));
+ return !Sets.intersection(associatedFolders, supplyingFolders).isEmpty();
}
@Override
diff --git a/repository/src/main/java/org/alfresco/service/cmr/rule/RuleService.java b/repository/src/main/java/org/alfresco/service/cmr/rule/RuleService.java
index 6434dfc07b..03a82e80df 100644
--- a/repository/src/main/java/org/alfresco/service/cmr/rule/RuleService.java
+++ b/repository/src/main/java/org/alfresco/service/cmr/rule/RuleService.java
@@ -311,7 +311,17 @@ public interface RuleService
*/
@Auditable(parameters = {"action"})
public NodeRef getOwningNodeRef(Action action);
-
+
+ /**
+ * Returns the owning node reference for a rule.
+ *
+ * @param ruleSet The rule set node.
+ * @return the owning node reference
+ */
+ @Auditable (parameters = { "ruleSet" })
+ @Experimental
+ NodeRef getOwningNodeRef(NodeRef ruleSet);
+
/**
* Indicates whether the passed rule node reference is linked to another
* rule node.
@@ -353,7 +363,7 @@ public interface RuleService
NodeRef getRuleSetNode(final NodeRef folderNodeRef);
/**
- * Check if rule set's associated parent matches folder node.
+ * Check if rule set is associated (owned/linked/inherited) with the given folder node.
*
* @param ruleSetNodeRef - node reference of a rule set
* @param folderNodeRef - node reference of a folder
diff --git a/repository/src/test/java/org/alfresco/repo/rule/RuleServiceImplUnitTest.java b/repository/src/test/java/org/alfresco/repo/rule/RuleServiceImplUnitTest.java
index 868f355506..11f7caebc2 100644
--- a/repository/src/test/java/org/alfresco/repo/rule/RuleServiceImplUnitTest.java
+++ b/repository/src/test/java/org/alfresco/repo/rule/RuleServiceImplUnitTest.java
@@ -254,6 +254,8 @@ public class RuleServiceImplUnitTest
boolean associated = ruleService.isRuleSetAssociatedWithFolder(RULE_SET_NODE, FOLDER_NODE);
then(runtimeNodeService).should().getParentAssocs(RULE_SET_NODE, ASSOC_RULE_FOLDER, ASSOC_RULE_FOLDER);
+ then(runtimeNodeService).should().hasAspect(FOLDER_NODE, ASPECT_IGNORE_INHERITED_RULES);
+ then(runtimeNodeService).should().getParentAssocs(FOLDER_NODE);
then(runtimeNodeService).shouldHaveNoMoreInteractions();
then(nodeService).shouldHaveNoInteractions();
assertThat(associated).isTrue();
@@ -268,6 +270,8 @@ public class RuleServiceImplUnitTest
boolean associated = ruleService.isRuleSetAssociatedWithFolder(RULE_SET_NODE, FOLDER_NODE);
then(runtimeNodeService).should().getParentAssocs(RULE_SET_NODE, ASSOC_RULE_FOLDER, ASSOC_RULE_FOLDER);
+ then(runtimeNodeService).should().hasAspect(FOLDER_NODE, ASPECT_IGNORE_INHERITED_RULES);
+ then(runtimeNodeService).should().getParentAssocs(FOLDER_NODE);
then(runtimeNodeService).shouldHaveNoMoreInteractions();
then(nodeService).shouldHaveNoInteractions();
assertThat(associated).isFalse();
@@ -283,11 +287,45 @@ public class RuleServiceImplUnitTest
boolean associated = ruleService.isRuleSetAssociatedWithFolder(RULE_SET_NODE, FOLDER_NODE);
then(runtimeNodeService).should().getParentAssocs(RULE_SET_NODE, ASSOC_RULE_FOLDER, ASSOC_RULE_FOLDER);
+ then(runtimeNodeService).should().hasAspect(FOLDER_NODE, ASPECT_IGNORE_INHERITED_RULES);
+ then(runtimeNodeService).should().getParentAssocs(FOLDER_NODE);
then(runtimeNodeService).shouldHaveNoMoreInteractions();
then(nodeService).shouldHaveNoInteractions();
assertThat(associated).isFalse();
}
+ /**
+ * Check that a rule set is associated with the folder in the following case:
+ *
+ * parent --[link]-> rule set <-[owned]-- owningFolder
+ * +- child
+ *
+ */
+ @Test
+ public void testIsRuleSetAssociatedWithFolder_inheritedLinkedAssociation()
+ {
+ // The rule is owned by one node.
+ NodeRef owningFolder = new NodeRef("owning://node/");
+ // The rule is linked to by the parent node.
+ NodeRef parent = new NodeRef("parent://node/");
+ List ruleAssociations = List.of(createAssociation(owningFolder, RULE_SET_NODE), createAssociation(parent, RULE_SET_NODE));
+ given(runtimeNodeService.getParentAssocs(RULE_SET_NODE, ASSOC_RULE_FOLDER, ASSOC_RULE_FOLDER)).willReturn(ruleAssociations);
+ // The parent and the child both supply rule sets.
+ given(runtimeNodeService.getParentAssocs(FOLDER_NODE)).willReturn(List.of(createAssociation(parent, FOLDER_NODE)));
+
+ // when
+ boolean associated = ruleService.isRuleSetAssociatedWithFolder(RULE_SET_NODE, FOLDER_NODE);
+
+ then(runtimeNodeService).should().getParentAssocs(RULE_SET_NODE, ASSOC_RULE_FOLDER, ASSOC_RULE_FOLDER);
+ then(runtimeNodeService).should().hasAspect(FOLDER_NODE, ASPECT_IGNORE_INHERITED_RULES);
+ then(runtimeNodeService).should().getParentAssocs(FOLDER_NODE);
+ then(runtimeNodeService).should().hasAspect(parent, ASPECT_IGNORE_INHERITED_RULES);
+ then(runtimeNodeService).should().getParentAssocs(parent);
+ then(runtimeNodeService).shouldHaveNoMoreInteractions();
+ then(nodeService).shouldHaveNoInteractions();
+ assertThat(associated).isTrue();
+ }
+
@Test
public void testIsRuleAssociatedWithRuleSet()
{