diff --git a/packaging/tests/tas-restapi/src/test/java/org/alfresco/rest/rules/CreateRulesTests.java b/packaging/tests/tas-restapi/src/test/java/org/alfresco/rest/rules/CreateRulesTests.java
index 9dfdd37bad..90471102d8 100644
--- a/packaging/tests/tas-restapi/src/test/java/org/alfresco/rest/rules/CreateRulesTests.java
+++ b/packaging/tests/tas-restapi/src/test/java/org/alfresco/rest/rules/CreateRulesTests.java
@@ -32,8 +32,8 @@ import static org.alfresco.rest.rules.RulesTestsUtils.ID;
import static org.alfresco.rest.rules.RulesTestsUtils.INVERTED;
import static org.alfresco.rest.rules.RulesTestsUtils.IS_SHARED;
import static org.alfresco.rest.rules.RulesTestsUtils.RULE_NAME_DEFAULT;
+import static org.alfresco.rest.rules.RulesTestsUtils.createAddAudioAspectAction;
import static org.alfresco.rest.rules.RulesTestsUtils.createCompositeCondition;
-import static org.alfresco.rest.rules.RulesTestsUtils.createDefaultActionModel;
import static org.alfresco.rest.rules.RulesTestsUtils.createRuleModel;
import static org.alfresco.rest.rules.RulesTestsUtils.createRuleModelWithDefaultValues;
import static org.alfresco.rest.rules.RulesTestsUtils.createRuleModelWithModifiedValues;
@@ -363,7 +363,7 @@ public class CreateRulesTests extends RestTest
STEP(String.format("Add a user with '%s' role in the private site's folder", userRole.toString()));
UserModel userWithRole = dataUser.createRandomTestUser();
dataUser.addUserToSite(userWithRole, privateSite, userRole);
- RestRuleModel ruleModel = createRuleModel("testRule", List.of(createDefaultActionModel()));
+ RestRuleModel ruleModel = createRuleModel("testRule", List.of(createAddAudioAspectAction()));
return restClient.authenticateUser(userWithRole).withCoreAPI().usingNode(privateFolder).usingDefaultRuleSet().createSingleRule(ruleModel);
}
diff --git a/packaging/tests/tas-restapi/src/test/java/org/alfresco/rest/rules/ExecuteRulesTests.java b/packaging/tests/tas-restapi/src/test/java/org/alfresco/rest/rules/ExecuteRulesTests.java
new file mode 100644
index 0000000000..c1015586bb
--- /dev/null
+++ b/packaging/tests/tas-restapi/src/test/java/org/alfresco/rest/rules/ExecuteRulesTests.java
@@ -0,0 +1,96 @@
+/*
+ * #%L
+ * Alfresco Repository
+ * %%
+ * Copyright (C) 2005 - 2022 Alfresco Software Limited
+ * %%
+ * This file is part of the Alfresco software.
+ * If the software was purchased under a paid Alfresco license, the terms of
+ * the paid license agreement will prevail. Otherwise, the software is
+ * provided under the following open source license terms:
+ *
+ * Alfresco is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Alfresco is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Alfresco. If not, see .
+ * #L%
+ */
+package org.alfresco.rest.rules;
+
+import static org.alfresco.rest.rules.RulesTestsUtils.AUDIO_ASPECT;
+import static org.alfresco.rest.rules.RulesTestsUtils.createRuleExecutionRequest;
+import static org.alfresco.rest.rules.RulesTestsUtils.createRuleModelWithDefaultValues;
+import static org.alfresco.utility.report.log.Step.STEP;
+
+import org.alfresco.dataprep.CMISUtil;
+import org.alfresco.rest.RestTest;
+import org.alfresco.rest.model.RestNodeModel;
+import org.alfresco.rest.model.RestRuleModel;
+import org.alfresco.utility.Utility;
+import org.alfresco.utility.model.FileModel;
+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.springframework.http.HttpStatus;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+/**
+ * Tests for POST /nodes/{nodeId}/rule-executions.
+ */
+@Test(groups = { TestGroup.RULES})
+public class ExecuteRulesTests extends RestTest
+{
+ private UserModel user;
+ private SiteModel site;
+ private FolderModel ruleFolder;
+ private FileModel file;
+
+ @BeforeClass(alwaysRun = true)
+ public void dataPreparation()
+ {
+ STEP("Create a user, site, folder and file in it");
+ user = dataUser.createRandomTestUser();
+ site = dataSite.usingUser(user).createPublicRandomSite();
+ ruleFolder = dataContent.usingUser(user).usingSite(site).createFolder();
+ file = dataContent.usingUser(user).usingResource(ruleFolder).createContent(CMISUtil.DocumentType.TEXT_PLAIN);
+
+ STEP("Create one rule with add audio aspect action in the folder");
+ RestRuleModel ruleModel = createRuleModelWithDefaultValues();
+ restClient.authenticateUser(user).withCoreAPI().usingNode(ruleFolder).usingDefaultRuleSet().createSingleRule(ruleModel);
+ }
+
+ /**
+ * Execute one rule with one action trying to add audio aspect to a file
+ */
+ @Test (groups = { TestGroup.REST_API, TestGroup.RULES, TestGroup.SANITY })
+ public void executeOneRuleWithOneAction() throws InterruptedException
+ {
+ STEP("Check if file aspects don't contain Audio one");
+ RestNodeModel node = restClient.authenticateUser(user).withCoreAPI().usingNode(file).getNode();
+ restClient.assertStatusCodeIs(HttpStatus.OK);
+ node.assertThat().field("aspectNames").notContains(AUDIO_ASPECT);
+
+ STEP("Execute rule");
+ restClient.authenticateUser(user).withCoreAPI().usingNode(ruleFolder).executeRules(createRuleExecutionRequest());
+ restClient.assertStatusCodeIs(HttpStatus.CREATED);
+
+ STEP("Check if file contains Audio aspect");
+ Utility.sleep(500, 10000, () -> {
+ RestNodeModel fileNode = restClient.authenticateUser(user).withCoreAPI().usingNode(file).getNode();
+ restClient.assertStatusCodeIs(HttpStatus.OK);
+ fileNode.assertThat().field("aspectNames").contains(AUDIO_ASPECT);
+ });
+ }
+
+ // TODO add more E2Es. For more see: ACS-3620
+}
diff --git a/packaging/tests/tas-restapi/src/test/java/org/alfresco/rest/rules/GetRulesTests.java b/packaging/tests/tas-restapi/src/test/java/org/alfresco/rest/rules/GetRulesTests.java
index caa93a3fea..8aceca3178 100644
--- a/packaging/tests/tas-restapi/src/test/java/org/alfresco/rest/rules/GetRulesTests.java
+++ b/packaging/tests/tas-restapi/src/test/java/org/alfresco/rest/rules/GetRulesTests.java
@@ -2,7 +2,7 @@
* #%L
* Alfresco Repository
* %%
- * Copyright (C) 2005 - 2016 Alfresco Software Limited
+ * 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
@@ -152,7 +152,7 @@ public class GetRulesTests extends RestTest
rules.getEntries().get(i).onModel()
.assertThat().field("isShared").isNotNull()
.assertThat().field("description").isNull()
- .assertThat().field("isEnabled").is(false)
+ .assertThat().field("isEnabled").is(true)
.assertThat().field("isInheritable").is(false)
.assertThat().field("isAsynchronous").is(false)
.assertThat().field("errorScript").isNull()
diff --git a/packaging/tests/tas-restapi/src/test/java/org/alfresco/rest/rules/RuleSetLinksTests.java b/packaging/tests/tas-restapi/src/test/java/org/alfresco/rest/rules/RuleSetLinksTests.java
index 765c9c44d5..cd38a5d134 100644
--- a/packaging/tests/tas-restapi/src/test/java/org/alfresco/rest/rules/RuleSetLinksTests.java
+++ b/packaging/tests/tas-restapi/src/test/java/org/alfresco/rest/rules/RuleSetLinksTests.java
@@ -26,8 +26,15 @@
package org.alfresco.rest.rules;
import static org.alfresco.rest.rules.RulesTestsUtils.createRuleModel;
+import static org.alfresco.rest.rules.RulesTestsUtils.createRuleModelWithDefaultValues;
+import static org.alfresco.utility.constants.UserRole.SiteConsumer;
import static org.alfresco.utility.report.log.Step.STEP;
-import static org.springframework.http.HttpStatus.*;
+import static org.springframework.http.HttpStatus.BAD_REQUEST;
+import static org.springframework.http.HttpStatus.CREATED;
+import static org.springframework.http.HttpStatus.FORBIDDEN;
+import static org.springframework.http.HttpStatus.NOT_FOUND;
+import static org.springframework.http.HttpStatus.NO_CONTENT;
+import static org.springframework.http.HttpStatus.OK;
import org.alfresco.dataprep.CMISUtil;
import org.alfresco.rest.RestTest;
@@ -167,7 +174,6 @@ public class RuleSetLinksTests extends RestTest
.get(0).onModel().assertThat().isEqualTo(expectedRuleSet);
}
-
/**
* Check we get 404 when linking to a non-existing rule set/folder.
*/
@@ -308,6 +314,51 @@ public class RuleSetLinksTests extends RestTest
.get(0).onModel().assertThat().isEqualTo(expectedRuleSet);
}
+ /**
+ * Check we get an error when trying to link to a rule set that we can't view.
+ */
+ @Test (groups = { TestGroup.REST_API, TestGroup.RULES })
+ public void linkToRuleSetWithoutPermission()
+ {
+ STEP("Use admin to create a private site with a folder containing a rule.");
+ SiteModel privateSite = dataSite.usingAdmin().createPrivateRandomSite();
+ FolderModel privateFolder = dataContent.usingAdmin().usingSite(privateSite).createFolder();
+ restClient.authenticateUser(dataUser.getAdminUser()).withCoreAPI().usingNode(privateFolder).usingDefaultRuleSet()
+ .createSingleRule(createRuleModelWithDefaultValues());
+
+ STEP("Use a normal user to try to link to the rule.");
+ FolderModel publicFolder = dataContent.usingUser(user).usingSite(site).createFolder();
+ RestRuleSetLinkModel request = new RestRuleSetLinkModel();
+ request.setId(privateFolder.getNodeRef());
+ restClient.authenticateUser(user).withCoreAPI().usingNode(publicFolder).createRuleLink(request);
+
+ restClient.assertStatusCodeIs(FORBIDDEN);
+ }
+
+ /**
+ * Check we are able to link to a rule set with only read permission.
+ */
+ @Test (groups = { TestGroup.REST_API, TestGroup.RULES })
+ public void linkToRuleSetWithOnlyReadPermission()
+ {
+ STEP("Use admin to create a private site with a folder containing a rule.");
+ SiteModel privateSite = dataSite.usingAdmin().createPrivateRandomSite();
+ FolderModel privateFolder = dataContent.usingAdmin().usingSite(privateSite).createFolder();
+ restClient.authenticateUser(dataUser.getAdminUser()).withCoreAPI().usingNode(privateFolder).usingDefaultRuleSet()
+ .createSingleRule(createRuleModelWithDefaultValues());
+
+ STEP("Add the normal user as a consumer.");
+ dataUser.usingAdmin().addUserToSite(user, privateSite, SiteConsumer);
+
+ STEP("Use a normal user to try to link to the rule.");
+ FolderModel publicFolder = dataContent.usingUser(user).usingSite(site).createFolder();
+ RestRuleSetLinkModel request = new RestRuleSetLinkModel();
+ request.setId(privateFolder.getNodeRef());
+ restClient.authenticateUser(user).withCoreAPI().usingNode(publicFolder).createRuleLink(request);
+
+ restClient.assertStatusCodeIs(CREATED);
+ }
+
/**
* Check we can DELETE/unlink a ruleset
*
@@ -391,4 +442,63 @@ public class RuleSetLinksTests extends RestTest
restClient.assertStatusCodeIs(NOT_FOUND)
.assertLastError().containsSummary("The entity with id:");
}
+
+ /**
+ * Check we cannot unlink from a rule set that we can't view.
+ */
+ @Test (groups = { TestGroup.REST_API, TestGroup.RULES })
+ public void unlinkFromRuleSetWithoutPermission()
+ {
+ STEP("Use admin to create a private site with a folder containing a rule.");
+ SiteModel privateSite = dataSite.usingAdmin().createPrivateRandomSite();
+ FolderModel privateFolder = dataContent.usingAdmin().usingSite(privateSite).createFolder();
+ restClient.authenticateUser(dataUser.getAdminUser()).withCoreAPI().usingNode(privateFolder).usingDefaultRuleSet()
+ .createSingleRule(createRuleModelWithDefaultValues());
+
+ STEP("Add the user as a consumer.");
+ dataUser.usingAdmin().addUserToSite(user, privateSite, SiteConsumer);
+
+ STEP("Use the consumer to create a folder with a link to the private rule set.");
+ FolderModel publicFolder = dataContent.usingUser(user).usingSite(site).createFolder();
+ RestRuleSetLinkModel request = new RestRuleSetLinkModel();
+ request.setId(privateFolder.getNodeRef());
+ restClient.authenticateUser(user).withCoreAPI().usingNode(publicFolder).createRuleLink(request);
+ restClient.assertStatusCodeIs(CREATED);
+
+ STEP("Remove the user from the private site.");
+ dataUser.usingAdmin().removeUserFromSite(user, privateSite);
+
+ STEP("Use the user to try to unlink from the rule set.");
+ restClient.authenticateUser(user).withCoreAPI().usingNode(publicFolder).unlinkRuleSet("-default-");
+
+ restClient.assertStatusCodeIs(FORBIDDEN);
+ }
+
+ /**
+ * Check we can unlink from a rule set if we only have read permission for it.
+ */
+ @Test (groups = { TestGroup.REST_API, TestGroup.RULES })
+ public void unlinkFromRuleSetWithOnlyReadPermission()
+ {
+ STEP("Use admin to create a private site with a folder containing a rule.");
+ SiteModel privateSite = dataSite.usingAdmin().createPrivateRandomSite();
+ FolderModel privateFolder = dataContent.usingAdmin().usingSite(privateSite).createFolder();
+ restClient.authenticateUser(dataUser.getAdminUser()).withCoreAPI().usingNode(privateFolder).usingDefaultRuleSet()
+ .createSingleRule(createRuleModelWithDefaultValues());
+
+ STEP("Add the user as a consumer.");
+ dataUser.usingAdmin().addUserToSite(user, privateSite, SiteConsumer);
+
+ STEP("Use the consumer to create a folder with a link to the private rule set.");
+ FolderModel publicFolder = dataContent.usingUser(user).usingSite(site).createFolder();
+ RestRuleSetLinkModel request = new RestRuleSetLinkModel();
+ request.setId(privateFolder.getNodeRef());
+ restClient.authenticateUser(user).withCoreAPI().usingNode(publicFolder).createRuleLink(request);
+ restClient.assertStatusCodeIs(CREATED);
+
+ STEP("Use the consumer to try to unlink from the rule set.");
+ restClient.authenticateUser(user).withCoreAPI().usingNode(publicFolder).unlinkRuleSet("-default-");
+
+ restClient.assertStatusCodeIs(NO_CONTENT);
+ }
}
diff --git a/packaging/tests/tas-restapi/src/test/java/org/alfresco/rest/rules/RulesTestsUtils.java b/packaging/tests/tas-restapi/src/test/java/org/alfresco/rest/rules/RulesTestsUtils.java
index 01cffe9bce..558ab919b9 100644
--- a/packaging/tests/tas-restapi/src/test/java/org/alfresco/rest/rules/RulesTestsUtils.java
+++ b/packaging/tests/tas-restapi/src/test/java/org/alfresco/rest/rules/RulesTestsUtils.java
@@ -36,6 +36,7 @@ import java.util.Map;
import org.alfresco.rest.model.RestActionBodyExecTemplateModel;
import org.alfresco.rest.model.RestCompositeConditionDefinitionModel;
+import org.alfresco.rest.model.RestRuleExecutionBodyModel;
import org.alfresco.rest.model.RestRuleModel;
import org.alfresco.rest.model.RestSimpleConditionDefinitionModel;
@@ -56,15 +57,22 @@ public class RulesTestsUtils
static final String AND = "and";
static final String ID = "id";
static final String IS_SHARED = "isShared";
+ static final String AUDIO_ASPECT = "audio:audio";
- /**
- * Create a rule model filled with default values.
- *
- * @return The created rule model.
- */
public static RestRuleModel createRuleModelWithModifiedValues()
{
- RestRuleModel ruleModel = createRuleModelWithDefaultValues();
+ return createRuleModelWithModifiedValues(List.of(createAddAudioAspectAction()));
+ }
+
+ /**
+ * Create a rule model filled with custom constant values.
+ *
+ * @param actions - rule's actions.
+ * @return The created rule model.
+ */
+ public static RestRuleModel createRuleModelWithModifiedValues(List actions)
+ {
+ RestRuleModel ruleModel = createRuleModel(RULE_NAME_DEFAULT, actions);
ruleModel.setDescription(RULE_DESCRIPTION_DEFAULT);
ruleModel.setIsEnabled(RULE_ENABLED_DEFAULT);
ruleModel.setIsInheritable(RULE_CASCADE_DEFAULT);
@@ -78,26 +86,27 @@ public class RulesTestsUtils
public static RestRuleModel createRuleModelWithDefaultValues()
{
- return createRuleModel(RULE_NAME_DEFAULT, List.of(createDefaultActionModel()));
+ return createRuleModel(RULE_NAME_DEFAULT);
}
public static RestRuleModel createRuleModel(String name)
{
- return createRuleModel(name, List.of(createDefaultActionModel()));
+ return createRuleModel(name, List.of(createAddAudioAspectAction()));
}
/**
* Create a rule model.
*
* @param name The name for the rule.
- * @param restActionModels Rule's actions.
+ * @param actions Rule's actions.
* @return The created rule model.
*/
- public static RestRuleModel createRuleModel(String name, List restActionModels)
+ public static RestRuleModel createRuleModel(String name, List actions)
{
RestRuleModel ruleModel = new RestRuleModel();
+ ruleModel.setIsEnabled(true);
ruleModel.setName(name);
- ruleModel.setActions(restActionModels);
+ ruleModel.setActions(actions);
return ruleModel;
}
@@ -106,12 +115,9 @@ public class RulesTestsUtils
*
* @return The created action model.
*/
- public static RestActionBodyExecTemplateModel createDefaultActionModel()
+ public static RestActionBodyExecTemplateModel createAddAudioAspectAction()
{
- RestActionBodyExecTemplateModel restActionModel = new RestActionBodyExecTemplateModel();
- restActionModel.setActionDefinitionId("set-property-value");
- restActionModel.setParams(Map.of("aspect-name", "cm:audio"));
- return restActionModel;
+ return createCustomActionModel("add-features", Map.of("aspect-name", AUDIO_ASPECT));
}
public static RestActionBodyExecTemplateModel createCustomActionModel(String actionDefinitionId, Map params)
@@ -143,7 +149,7 @@ public class RulesTestsUtils
createSimpleCondition("tag", "equals", "uat")
)),
createCompositeCondition(INVERTED, List.of(
- createSimpleCondition("aspect", "equals", "audio:audio"),
+ createSimpleCondition("aspect", "equals", AUDIO_ASPECT),
createSimpleCondition("cm:modelVersion", "begins", "1.")
))
));
@@ -196,6 +202,20 @@ public class RulesTestsUtils
return createCompositeCondition(AND, inverted, null, simpleConditions);
}
+ public static RestRuleExecutionBodyModel createRuleExecutionRequest()
+ {
+ return createRuleExecutionRequest(false, false);
+ }
+
+ public static RestRuleExecutionBodyModel createRuleExecutionRequest(boolean eachSubFolderIncluded, boolean eachInheritedRuleExecuted)
+ {
+ RestRuleExecutionBodyModel ruleExecutionBody = new RestRuleExecutionBodyModel();
+ ruleExecutionBody.setIsEachSubFolderIncluded(eachSubFolderIncluded);
+ ruleExecutionBody.setIsEachInheritedRuleExecuted(eachInheritedRuleExecuted);
+
+ return ruleExecutionBody;
+ }
+
private static RestCompositeConditionDefinitionModel createCompositeCondition(String booleanMode, boolean inverted,
List compositeConditions, List simpleConditions)
{
diff --git a/packaging/tests/tas-restapi/src/test/java/org/alfresco/rest/rules/UpdateRulesTests.java b/packaging/tests/tas-restapi/src/test/java/org/alfresco/rest/rules/UpdateRulesTests.java
index 638d983cdc..262e072111 100644
--- a/packaging/tests/tas-restapi/src/test/java/org/alfresco/rest/rules/UpdateRulesTests.java
+++ b/packaging/tests/tas-restapi/src/test/java/org/alfresco/rest/rules/UpdateRulesTests.java
@@ -35,7 +35,7 @@ import static org.alfresco.rest.rules.RulesTestsUtils.RULE_CASCADE_DEFAULT;
import static org.alfresco.rest.rules.RulesTestsUtils.RULE_ENABLED_DEFAULT;
import static org.alfresco.rest.rules.RulesTestsUtils.createCompositeCondition;
import static org.alfresco.rest.rules.RulesTestsUtils.createCustomActionModel;
-import static org.alfresco.rest.rules.RulesTestsUtils.createDefaultActionModel;
+import static org.alfresco.rest.rules.RulesTestsUtils.createAddAudioAspectAction;
import static org.alfresco.rest.rules.RulesTestsUtils.createRuleModel;
import static org.alfresco.rest.rules.RulesTestsUtils.createRuleModelWithModifiedValues;
import static org.alfresco.rest.rules.RulesTestsUtils.createRuleWithPrivateAction;
@@ -565,7 +565,7 @@ public class UpdateRulesTests extends RestTest
private RestRuleModel createAndSaveRule(String name)
{
- return createAndSaveRule(name, List.of(createDefaultActionModel()));
+ return createAndSaveRule(name, List.of(createAddAudioAspectAction()));
}
/**
diff --git a/pom.xml b/pom.xml
index 7354e089ee..2cddcf6a0f 100644
--- a/pom.xml
+++ b/pom.xml
@@ -123,7 +123,7 @@
2.7.4
3.0.56
5.2.0
- 1.127
+ 1.128
1.9
1.7
1.7
diff --git a/remote-api/src/main/java/org/alfresco/rest/api/Rules.java b/remote-api/src/main/java/org/alfresco/rest/api/Rules.java
index 4b299ba62e..c7386f4aa1 100644
--- a/remote-api/src/main/java/org/alfresco/rest/api/Rules.java
+++ b/remote-api/src/main/java/org/alfresco/rest/api/Rules.java
@@ -29,6 +29,7 @@ package org.alfresco.rest.api;
import java.util.List;
import org.alfresco.rest.api.model.rules.Rule;
+import org.alfresco.rest.api.model.rules.RuleExecution;
import org.alfresco.rest.framework.core.exceptions.InvalidArgumentException;
import org.alfresco.rest.framework.resource.parameters.CollectionWithPagingInfo;
import org.alfresco.rest.framework.resource.parameters.Paging;
@@ -65,10 +66,10 @@ public interface Rules
Rule getRuleById(String folderNodeId, String ruleSetId, String ruleId, List includes);
/**
- * Create new rules (and potentially a rule set if "_default_" is supplied).
+ * Create new rules (and potentially a rule set if "-default-" is supplied).
*
* @param folderNodeId The node id of a folder.
- * @param ruleSetId The id of a rule set (or "_default_" to use/create the default rule set for the folder).
+ * @param ruleSetId The id of a rule set (or "-default-" to use/create the default rule set for the folder).
* @param rule The definition of the rule.
* @param includes The list of optional fields to include in the response.
* @return The newly created rules.
@@ -81,7 +82,7 @@ public interface Rules
* Update a rule.
*
* @param folderNodeId The id of a folder.
- * @param ruleSetId The id of a rule set within the folder (or "_default_" to use the default rule set for the folder).
+ * @param ruleSetId The id of a rule set within the folder (or "-default-" to use the default rule set for the folder).
* @param ruleId The rule id.
* @param rule The new version of the rule.
* @param includes The list of optional fields to include in the response.
@@ -94,7 +95,16 @@ public interface Rules
*
* @param folderNodeId - folder node ID
* @param ruleSetId - rule set ID
- * @param ruleId - rule ID *
+ * @param ruleId - rule ID
*/
void deleteRuleById(String folderNodeId, String ruleSetId, String ruleId);
+
+ /**
+ * Execute rules for given folder node.
+ *
+ * @param folderNodeId - the ID of a folder
+ * @param eachSubFolderIncluded - indicates if rules should be executed also on sub-folders
+ * @param eachInheritedRuleExecuted - indicates if the inherited rules should be also executed
+ */
+ RuleExecution executeRules(final String folderNodeId, final boolean eachSubFolderIncluded, final boolean eachInheritedRuleExecuted);
}
diff --git a/remote-api/src/main/java/org/alfresco/rest/api/impl/rules/RuleSetsImpl.java b/remote-api/src/main/java/org/alfresco/rest/api/impl/rules/RuleSetsImpl.java
index 8b162290ca..61f673f648 100644
--- a/remote-api/src/main/java/org/alfresco/rest/api/impl/rules/RuleSetsImpl.java
+++ b/remote-api/src/main/java/org/alfresco/rest/api/impl/rules/RuleSetsImpl.java
@@ -129,8 +129,8 @@ public class RuleSetsImpl implements RuleSets
final NodeRef folderNodeRef = validator.validateFolderNode(folderNodeId,true);
final boolean isRuleSetNode = validator.isRuleSetNode(linkToNodeId);
final NodeRef linkToNodeRef = isRuleSetNode
- ? validator.validateRuleSetNode(linkToNodeId, true)
- : validator.validateFolderNode(linkToNodeId, true);
+ ? validator.validateRuleSetNode(linkToNodeId, false)
+ : validator.validateFolderNode(linkToNodeId, false);
//The target node should have pre-existing rules to link to
if (!ruleService.hasRules(linkToNodeRef)) {
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 7c9bc31193..afb0419256 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
@@ -26,29 +26,30 @@
package org.alfresco.rest.api.impl.rules;
+import java.io.Serializable;
+import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
-import org.alfresco.rest.api.Nodes;
+import org.alfresco.repo.action.ActionImpl;
+import org.alfresco.repo.action.access.ActionAccessRestriction;
+import org.alfresco.repo.action.executer.ExecuteAllRulesActionExecuter;
import org.alfresco.rest.api.Rules;
import org.alfresco.rest.api.model.mapper.RestModelMapper;
-import org.alfresco.rest.api.model.rules.CompositeCondition;
import org.alfresco.rest.api.model.rules.Rule;
+import org.alfresco.rest.api.model.rules.RuleExecution;
import org.alfresco.rest.api.model.rules.RuleSet;
import org.alfresco.rest.framework.core.exceptions.InvalidArgumentException;
-import org.alfresco.rest.api.model.rules.SimpleCondition;
-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.action.ActionCondition;
-import org.alfresco.service.cmr.action.CompositeAction;
+import org.alfresco.service.cmr.action.ActionService;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.rule.RuleService;
-import org.alfresco.util.collections.CollectionUtils;
-import org.alfresco.service.namespace.NamespaceService;
+import org.alfresco.util.GUID;
+import org.apache.commons.collections.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -58,6 +59,7 @@ public class RulesImpl implements Rules
private static final Logger LOGGER = LoggerFactory.getLogger(RulesImpl.class);
private static final String MUST_HAVE_AT_LEAST_ONE_ACTION = "A rule must have at least one action";
+ private ActionService actionService;
private RuleService ruleService;
private NodeValidator validator;
private RuleLoader ruleLoader;
@@ -130,6 +132,26 @@ public class RulesImpl implements Rules
ruleService.removeRule(folderNodeRef, rule);
}
+ @Override
+ public RuleExecution executeRules(final String folderNodeId, final boolean eachSubFolderIncluded, final boolean eachInheritedRuleExecuted)
+ {
+ final NodeRef folderNodeRef = validator.validateFolderNode(folderNodeId, false);
+ final Map parameterValues = new HashMap<>();
+ parameterValues.put(ExecuteAllRulesActionExecuter.PARAM_RUN_ALL_RULES_ON_CHILDREN, eachSubFolderIncluded);
+ parameterValues.put(ExecuteAllRulesActionExecuter.PARAM_EXECUTE_INHERITED_RULES, eachInheritedRuleExecuted);
+ final ActionImpl action = new ActionImpl(null, GUID.generate(), ExecuteAllRulesActionExecuter.NAME);
+ action.setNodeRef(folderNodeRef);
+ action.setParameterValues(parameterValues);
+
+ ActionAccessRestriction.setActionContext(action, ActionAccessRestriction.V1_ACTION_CONTEXT);
+ actionService.executeAction(action, folderNodeRef, true, false);
+
+ return RuleExecution.builder()
+ .eachSubFolderIncluded(eachSubFolderIncluded)
+ .eachInheritedRuleExecuted(eachInheritedRuleExecuted)
+ .create();
+ }
+
private org.alfresco.service.cmr.rule.Rule mapToServiceModelAndValidateActions(Rule rule)
{
if (CollectionUtils.isEmpty(rule.getActions()))
@@ -141,6 +163,11 @@ public class RulesImpl implements Rules
return actionPermissionValidator.validateRulePermissions(serviceModelRule);
}
+ public void setActionService(ActionService actionService)
+ {
+ this.actionService = actionService;
+ }
+
public void setRuleService(RuleService ruleService)
{
this.ruleService = ruleService;
diff --git a/remote-api/src/main/java/org/alfresco/rest/api/model/rules/RuleExecution.java b/remote-api/src/main/java/org/alfresco/rest/api/model/rules/RuleExecution.java
new file mode 100644
index 0000000000..88cffc29ed
--- /dev/null
+++ b/remote-api/src/main/java/org/alfresco/rest/api/model/rules/RuleExecution.java
@@ -0,0 +1,111 @@
+/*
+ * #%L
+ * Alfresco Remote API
+ * %%
+ * Copyright (C) 2005 - 2022 Alfresco Software Limited
+ * %%
+ * This file is part of the Alfresco software.
+ * If the software was purchased under a paid Alfresco license, the terms of
+ * the paid license agreement will prevail. Otherwise, the software is
+ * provided under the following open source license terms:
+ *
+ * Alfresco is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Alfresco is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Alfresco. If not, see .
+ * #L%
+ */
+package org.alfresco.rest.api.model.rules;
+
+import java.util.Objects;
+
+import org.alfresco.service.Experimental;
+
+@Experimental
+public class RuleExecution
+{
+ private boolean eachSubFolderIncluded;
+ private boolean eachInheritedRuleExecuted;
+
+ public boolean getIsEachSubFolderIncluded()
+ {
+ return eachSubFolderIncluded;
+ }
+
+ public void setIsEachSubFolderIncluded(boolean eachSubFolderIncluded)
+ {
+ this.eachSubFolderIncluded = eachSubFolderIncluded;
+ }
+
+ public boolean getIsEachInheritedRuleExecuted()
+ {
+ return eachInheritedRuleExecuted;
+ }
+
+ public void setIsEachInheritedRuleExecuted(boolean eachInheritedRuleExecuted)
+ {
+ this.eachInheritedRuleExecuted = eachInheritedRuleExecuted;
+ }
+
+ @Override
+ public String toString()
+ {
+ return "RuleExecution{" + "eachSubFolderIncluded=" + eachSubFolderIncluded + ", eachInheritedRuleExecuted=" + eachInheritedRuleExecuted + '}';
+ }
+
+ @Override
+ public boolean equals(Object o)
+ {
+ if (this == o)
+ return true;
+ if (o == null || getClass() != o.getClass())
+ return false;
+ RuleExecution that = (RuleExecution) o;
+ return eachSubFolderIncluded == that.eachSubFolderIncluded && eachInheritedRuleExecuted == that.eachInheritedRuleExecuted;
+ }
+
+ @Override
+ public int hashCode()
+ {
+ return Objects.hash(eachSubFolderIncluded, eachInheritedRuleExecuted);
+ }
+
+ public static Builder builder()
+ {
+ return new Builder();
+ }
+
+ public static class Builder
+ {
+ private boolean eachSubFolderIncluded;
+ private boolean eachInheritedRuleExecuted;
+
+ public Builder eachSubFolderIncluded(boolean eachSubFolderIncluded)
+ {
+ this.eachSubFolderIncluded = eachSubFolderIncluded;
+ return this;
+ }
+
+ public Builder eachInheritedRuleExecuted(boolean eachInheritedRuleExecuted)
+ {
+ this.eachInheritedRuleExecuted = eachInheritedRuleExecuted;
+ return this;
+ }
+
+ public RuleExecution create()
+ {
+ final RuleExecution ruleExecution = new RuleExecution();
+ ruleExecution.setIsEachSubFolderIncluded(eachSubFolderIncluded);
+ ruleExecution.setIsEachInheritedRuleExecuted(eachInheritedRuleExecuted);
+ return ruleExecution;
+ }
+ }
+}
diff --git a/remote-api/src/main/java/org/alfresco/rest/api/nodes/NodeRuleExecutionsRelation.java b/remote-api/src/main/java/org/alfresco/rest/api/nodes/NodeRuleExecutionsRelation.java
new file mode 100644
index 0000000000..a099f31057
--- /dev/null
+++ b/remote-api/src/main/java/org/alfresco/rest/api/nodes/NodeRuleExecutionsRelation.java
@@ -0,0 +1,70 @@
+/*
+ * #%L
+ * Alfresco Remote API
+ * %%
+ * Copyright (C) 2005 - 2022 Alfresco Software Limited
+ * %%
+ * This file is part of the Alfresco software.
+ * If the software was purchased under a paid Alfresco license, the terms of
+ * the paid license agreement will prevail. Otherwise, the software is
+ * provided under the following open source license terms:
+ *
+ * Alfresco is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Alfresco is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Alfresco. If not, see .
+ * #L%
+ */
+package org.alfresco.rest.api.nodes;
+
+import java.util.List;
+
+import org.alfresco.rest.api.Rules;
+import org.alfresco.rest.api.model.rules.RuleExecution;
+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.service.Experimental;
+import org.alfresco.util.PropertyCheck;
+import org.springframework.beans.factory.InitializingBean;
+
+@Experimental
+@RelationshipResource(name = "rule-executions", entityResource = NodesEntityResource.class, title = "Executing rules")
+public class NodeRuleExecutionsRelation implements RelationshipResourceAction.Create, InitializingBean
+{
+ private final Rules rules;
+
+ public NodeRuleExecutionsRelation(Rules rules)
+ {
+ this.rules = rules;
+ }
+
+ @Override
+ public void afterPropertiesSet() throws Exception
+ {
+ PropertyCheck.mandatory(this, "rules", this.rules);
+ }
+
+ /**
+ * Execute rules for given folder node.
+ *
+ * @param folderNodeId - the ID of a folder
+ * @param ruleExecutionParameters - rule execution parameters
+ * @param parameters - additional request parameters
+ * @return execution details
+ */
+ @Override
+ public List create(String folderNodeId, List ruleExecutionParameters, Parameters parameters)
+ {
+ final RuleExecution ruleExecution = ruleExecutionParameters.stream().findFirst().orElse(new RuleExecution());
+ return List.of(rules.executeRules(folderNodeId, ruleExecution.getIsEachSubFolderIncluded(), ruleExecution.getIsEachInheritedRuleExecuted()));
+ }
+}
diff --git a/remote-api/src/main/resources/alfresco/public-rest-context.xml b/remote-api/src/main/resources/alfresco/public-rest-context.xml
index a4ffe81ca8..f1de526fa1 100644
--- a/remote-api/src/main/resources/alfresco/public-rest-context.xml
+++ b/remote-api/src/main/resources/alfresco/public-rest-context.xml
@@ -907,6 +907,7 @@
+
@@ -928,6 +929,10 @@
+
+
+
+
diff --git a/remote-api/src/test/java/org/alfresco/rest/api/impl/rules/RuleSetsImplTest.java b/remote-api/src/test/java/org/alfresco/rest/api/impl/rules/RuleSetsImplTest.java
index a53dac2e87..0619f59864 100644
--- a/remote-api/src/test/java/org/alfresco/rest/api/impl/rules/RuleSetsImplTest.java
+++ b/remote-api/src/test/java/org/alfresco/rest/api/impl/rules/RuleSetsImplTest.java
@@ -98,7 +98,7 @@ public class RuleSetsImplTest extends TestCase
MockitoAnnotations.openMocks(this);
given(nodeValidatorMock.validateFolderNode(eq(LINK_TO_NODE_ID), anyBoolean())).willReturn(LINK_TO_NODE);
- given(nodeValidatorMock.validateRuleSetNode(LINK_TO_NODE_ID,true)).willReturn(LINK_TO_NODE);
+ given(nodeValidatorMock.validateRuleSetNode(LINK_TO_NODE_ID,false)).willReturn(LINK_TO_NODE);
given(nodeValidatorMock.validateFolderNode(eq(FOLDER_ID), anyBoolean())).willReturn(FOLDER_NODE);
given(nodeValidatorMock.validateRuleSetNode(RULE_SET_ID, FOLDER_NODE)).willReturn(RULE_SET_NODE);
@@ -252,7 +252,7 @@ public class RuleSetsImplTest extends TestCase
then(nodeValidatorMock).should().validateFolderNode(FOLDER_ID,true);
then(nodeValidatorMock).should().isRuleSetNode(LINK_TO_NODE_ID);
- then(nodeValidatorMock).should().validateRuleSetNode(LINK_TO_NODE_ID,true);
+ then(nodeValidatorMock).should().validateRuleSetNode(LINK_TO_NODE_ID,false);
then(nodeValidatorMock).shouldHaveNoMoreInteractions();
then(ruleServiceMock).should().hasRules(LINK_TO_NODE);
then(ruleServiceMock).should().hasRules(FOLDER_NODE);
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 24811b990b..e3d6848800 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
@@ -33,6 +33,7 @@ import static org.assertj.core.api.Assertions.assertThat;
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;
import static org.mockito.BDDMockito.then;
import static org.mockito.Mockito.mock;
@@ -40,13 +41,18 @@ import static org.mockito.Mockito.mock;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
+import java.util.Map;
import java.util.stream.IntStream;
import junit.framework.TestCase;
+import org.alfresco.repo.action.ActionImpl;
+import org.alfresco.repo.action.access.ActionAccessRestriction;
+import org.alfresco.repo.action.executer.ExecuteAllRulesActionExecuter;
import org.alfresco.rest.api.Nodes;
import org.alfresco.rest.api.model.mapper.RestModelMapper;
import org.alfresco.rest.api.model.rules.Action;
import org.alfresco.rest.api.model.rules.Rule;
+import org.alfresco.rest.api.model.rules.RuleExecution;
import org.alfresco.rest.framework.core.exceptions.EntityNotFoundException;
import org.alfresco.rest.framework.core.exceptions.InvalidArgumentException;
import org.alfresco.rest.framework.core.exceptions.PermissionDeniedException;
@@ -54,12 +60,14 @@ import org.alfresco.rest.framework.core.exceptions.RelationshipResourceNotFoundE
import org.alfresco.rest.framework.resource.parameters.CollectionWithPagingInfo;
import org.alfresco.rest.framework.resource.parameters.Paging;
import org.alfresco.service.Experimental;
+import org.alfresco.service.cmr.action.ActionService;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.service.cmr.rule.RuleService;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
@@ -77,10 +85,14 @@ public class RulesImplTest extends TestCase
private static final NodeRef RULE_NODE_REF = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, RULE_ID);
private static final Paging PAGING = Paging.DEFAULT;
private static final List INCLUDE = emptyList();
+ private static final boolean INCLUDE_SUB_FOLDERS = true;
+ private static final boolean EXECUTE_INHERITED_RULES = true;
@Mock
private Nodes nodesMock;
@Mock
+ private ActionService actionServiceMock;
+ @Mock
private RestModelMapper ruleMapper;
@Mock
private NodeValidator nodeValidatorMock;
@@ -618,6 +630,39 @@ public class RulesImplTest extends TestCase
}
}
+ @Test
+ public void testExecuteRule()
+ {
+ // when
+ final RuleExecution actualRuleExecution = rules.executeRules(FOLDER_NODE_ID, INCLUDE_SUB_FOLDERS, EXECUTE_INHERITED_RULES);
+
+ final RuleExecution expectedRuleExecution = RuleExecution.builder()
+ .eachSubFolderIncluded(INCLUDE_SUB_FOLDERS)
+ .eachInheritedRuleExecuted(EXECUTE_INHERITED_RULES)
+ .create();
+ final ActionImpl expectedAction = new ActionImpl(null, null, ExecuteAllRulesActionExecuter.NAME);
+ expectedAction.setNodeRef(FOLDER_NODE_REF);
+ expectedAction.setParameterValues(Map.of(
+ ExecuteAllRulesActionExecuter.PARAM_RUN_ALL_RULES_ON_CHILDREN, INCLUDE_SUB_FOLDERS,
+ ExecuteAllRulesActionExecuter.PARAM_EXECUTE_INHERITED_RULES, EXECUTE_INHERITED_RULES,
+ ActionAccessRestriction.ACTION_CONTEXT_PARAM_NAME, ActionAccessRestriction.V1_ACTION_CONTEXT)
+ );
+ final ArgumentCaptor actionCaptor = ArgumentCaptor.forClass(ActionImpl.class);
+ then(nodeValidatorMock).should().validateFolderNode(FOLDER_NODE_ID, false);
+ then(nodeValidatorMock).shouldHaveNoMoreInteractions();
+ then(actionServiceMock).should().executeAction(actionCaptor.capture(), eq(FOLDER_NODE_REF), eq(true), eq(false));
+ then(actionServiceMock).shouldHaveNoMoreInteractions();
+ final ActionImpl actualAction = actionCaptor.getValue();
+ assertThat(actualAction)
+ .isNotNull()
+ .usingRecursiveComparison().ignoringFields("id")
+ .isEqualTo(expectedAction);
+ assertThat(actualRuleExecution)
+ .isNotNull()
+ .usingRecursiveComparison()
+ .isEqualTo(expectedRuleExecution);
+ }
+
private static org.alfresco.service.cmr.rule.Rule createRule(final String id)
{
final NodeRef nodeRef = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, id);
diff --git a/repository/src/main/java/org/alfresco/repo/workflow/WorkflowInterpreter.java b/repository/src/main/java/org/alfresco/repo/workflow/WorkflowInterpreter.java
index 40768cf9bc..a03fe39f4f 100644
--- a/repository/src/main/java/org/alfresco/repo/workflow/WorkflowInterpreter.java
+++ b/repository/src/main/java/org/alfresco/repo/workflow/WorkflowInterpreter.java
@@ -2,7 +2,7 @@
* #%L
* Alfresco Repository
* %%
- * Copyright (C) 2005 - 2016 Alfresco Software Limited
+ * 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
@@ -34,6 +34,7 @@ import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.Optional;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.admin.BaseInterpreter;
@@ -54,6 +55,7 @@ import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter;
import org.alfresco.service.cmr.security.PersonService;
import org.alfresco.service.cmr.security.PersonService.PersonInfo;
+import org.alfresco.service.cmr.workflow.FailedWorkflowDeployment;
import org.alfresco.service.cmr.workflow.WorkflowDefinition;
import org.alfresco.service.cmr.workflow.WorkflowDeployment;
import org.alfresco.service.cmr.workflow.WorkflowException;
@@ -726,10 +728,18 @@ public class WorkflowInterpreter extends BaseInterpreter
{
out.println(problem);
}
- out.println("deployed definition id: " + def.getId() + " , name: " + def.getName() + " , title: " + def.getTitle() + " , version: " + def.getVersion());
- currentDeployEngine = command[1];
- currentDeployResource = command[2];
- out.print(executeCommand("use definition " + def.getId()));
+ final Optional possibleDeploymentFailure = FailedWorkflowDeployment.getFailure(deployment);
+ if (possibleDeploymentFailure.isPresent())
+ {
+ out.println("Failed to deploy the workflow definition.");
+ }
+ else
+ {
+ out.println("deployed definition id: " + def.getId() + " , name: " + def.getName() + " , title: " + def.getTitle() + " , version: " + def.getVersion());
+ currentDeployEngine = command[1];
+ currentDeployResource = command[2];
+ out.print(executeCommand("use definition " + def.getId()));
+ }
}
else if (command[0].equals("redeploy"))