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 new file mode 100644 index 0000000000..ca7af09363 --- /dev/null +++ b/packaging/tests/tas-restapi/src/test/java/org/alfresco/rest/rules/UpdateRulesTests.java @@ -0,0 +1,197 @@ +/* + * #%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.utility.constants.UserRole.SiteCollaborator; +import static org.alfresco.utility.report.log.Step.STEP; +import static org.junit.Assert.fail; +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.OK; + +import org.alfresco.rest.RestTest; +import org.alfresco.rest.model.RestRuleModel; +import org.alfresco.rest.model.RestRuleSetModel; +import org.alfresco.utility.constants.UserRole; +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 PUT /nodes/{nodeId}/rule-sets/{ruleSetId}/rules. + */ +@Test (groups = { TestGroup.RULES }) +public class UpdateRulesTests extends RestTest +{ + private UserModel user; + private SiteModel site; + private FolderModel ruleFolder; + + @BeforeClass (alwaysRun = true) + public void dataPreparation() + { + user = dataUser.createRandomTestUser(); + site = dataSite.usingUser(user).createPublicRandomSite(); + ruleFolder = dataContent.usingUser(user).usingSite(site).createFolder(); + } + + /** Check we can update a rule. */ + @Test (groups = { TestGroup.REST_API, TestGroup.RULES, TestGroup.SANITY }) + public void updateRule() + { + RestRuleModel rule = createRule("Rule name"); + + STEP("Try to update the rule."); + RestRuleModel updatedRuleModel = new RestRuleModel(); + updatedRuleModel.setName("Updated rule name"); + RestRuleModel updatedRule = restClient.authenticateUser(user).withCoreAPI().usingNode(ruleFolder).usingDefaultRuleSet() + .updateRule(rule.getId(), updatedRuleModel); + + restClient.assertStatusCodeIs(OK); + updatedRule.assertThat().field("id").is(rule.getId()) + .assertThat().field("name").is("Updated rule name"); + } + + /** Check we get a 404 if trying to update a rule in a folder that doesn't exist. */ + @Test (groups = { TestGroup.REST_API, TestGroup.RULES }) + public void updateRuleForNonExistentFolder() + { + RestRuleModel rule = createRule("Rule name"); + + STEP("Try to update a rule in a non-existent folder."); + FolderModel nonExistentFolder = FolderModel.getRandomFolderModel(); + nonExistentFolder.setNodeRef("fake-id"); + + RestRuleModel updatedRuleModel = new RestRuleModel(); + updatedRuleModel.setName("Updated rule name"); + restClient.authenticateUser(user).withCoreAPI().usingNode(nonExistentFolder).usingDefaultRuleSet() + .updateRule(rule.getId(), updatedRuleModel); + + restClient.assertLastError().statusCodeIs(NOT_FOUND) + .containsSummary("fake-id was not found"); + } + + /** Check we get a 404 if trying to update a rule in a rule set that doesn't exist. */ + @Test (groups = { TestGroup.REST_API, TestGroup.RULES }) + public void updateRuleForNonExistentRuleSet() + { + RestRuleModel rule = createRule("Rule name"); + + STEP("Try to update a rule in a non-existent rule set."); + RestRuleModel updatedRuleModel = new RestRuleModel(); + updatedRuleModel.setName("Updated rule name"); + restClient.authenticateUser(user).withCoreAPI().usingNode(ruleFolder).usingRuleSet("fake-id") + .updateRule(rule.getId(), updatedRuleModel); + + restClient.assertLastError().statusCodeIs(NOT_FOUND) + .containsSummary("fake-id was not found"); + } + + /** Check we get a 404 if trying to update a rule that doesn't exist. */ + @Test (groups = { TestGroup.REST_API, TestGroup.RULES }) + public void updateRuleForNonExistentRuleId() + { + STEP("Try to update a rule that doesn't exist."); + RestRuleModel updatedRuleModel = new RestRuleModel(); + updatedRuleModel.setName("Updated rule name"); + restClient.authenticateUser(user).withCoreAPI().usingNode(ruleFolder).usingDefaultRuleSet() + .updateRule("fake-id", updatedRuleModel); + + restClient.assertLastError().statusCodeIs(NOT_FOUND) + .containsSummary("fake-id was not found"); + } + + /** Check that a user without permission cannot update a rule. */ + @Test (groups = { TestGroup.REST_API, TestGroup.RULES }) + public void requirePermissionToUpdateRule() + { + STEP("Create a user and use them to create a private site containing a folder"); + UserModel privateUser = dataUser.createRandomTestUser(); + SiteModel privateSite = dataSite.usingUser(privateUser).createPrivateRandomSite(); + FolderModel privateFolder = dataContent.usingUser(privateUser).usingSite(privateSite).createFolder(); + + STEP("Create a collaborator and check they don't have permission to create a rule"); + UserModel collaborator = dataUser.createRandomTestUser(); + dataUser.addUserToSite(collaborator, privateSite, SiteCollaborator); + RestRuleModel ruleModel = new RestRuleModel(); + ruleModel.setName("ruleName"); + restClient.authenticateUser(user).withCoreAPI().usingNode(privateFolder).usingDefaultRuleSet().createSingleRule(ruleModel); + + restClient.assertStatusCodeIs(FORBIDDEN); + restClient.assertLastError().containsSummary("Insufficient permissions to manage rules"); + } + + /** Check we get an error trying to update a rule to have no name. */ + @Test (groups = { TestGroup.REST_API, TestGroup.RULES }) + public void updateRuleToHaveEmptyName() + { + RestRuleModel rule = createRule("Rule name"); + + STEP("Try to update the rule to have no name."); + RestRuleModel updatedRuleModel = new RestRuleModel(); + updatedRuleModel.setName(""); + restClient.authenticateUser(user).withCoreAPI().usingNode(ruleFolder).usingDefaultRuleSet().updateRule(rule.getId(), updatedRuleModel); + + restClient.assertLastError().statusCodeIs(BAD_REQUEST) + .containsSummary("Rule name is a mandatory parameter"); + } + + /** Check that updates to the rule's id are ignored. */ + @Test (groups = { TestGroup.REST_API, TestGroup.RULES }) + public void tryToUpdateRuleId() + { + RestRuleModel rule = createRule("Rule name"); + + STEP("Try to update the rule id and check it isn't changed."); + RestRuleModel updatedRuleModel = new RestRuleModel(); + updatedRuleModel.setId("new-rule-id"); + updatedRuleModel.setName("Rule name"); + RestRuleModel updatedRule = restClient.authenticateUser(user).withCoreAPI().usingNode(ruleFolder).usingDefaultRuleSet() + .updateRule(rule.getId(), updatedRuleModel); + + updatedRule.assertThat().field("id").is(rule.getId()); + } + + /** + * Create a rule. + * + * @param name The name for the rule. + * @return The created rule. + */ + private RestRuleModel createRule(String name) + { + STEP("Create a rule called " + name); + RestRuleModel ruleModel = new RestRuleModel(); + ruleModel.setName(name); + return restClient.authenticateUser(user).withCoreAPI().usingNode(ruleFolder).usingDefaultRuleSet() + .createSingleRule(ruleModel); + } +} 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 2dc581659e..d8b00fc405 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 @@ -74,6 +74,17 @@ public interface Rules */ List createRules(String folderNodeId, String ruleSetId, List rule); + /** + * 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 ruleId The rule id. + * @param rule The new version of the rule. + * @return The newly updated rule. + */ + Rule updateRuleById(String folderNodeId, String ruleSetId, String ruleId, Rule rule); + /** * Delete rule for rule's ID and check associations with folder node and rule set node * diff --git a/remote-api/src/main/java/org/alfresco/rest/api/impl/RulesImpl.java b/remote-api/src/main/java/org/alfresco/rest/api/impl/RulesImpl.java index 04d3710e4b..539cfa7cc7 100644 --- a/remote-api/src/main/java/org/alfresco/rest/api/impl/RulesImpl.java +++ b/remote-api/src/main/java/org/alfresco/rest/api/impl/RulesImpl.java @@ -30,6 +30,7 @@ import static org.alfresco.service.cmr.security.AccessStatus.ALLOWED; import static org.alfresco.service.cmr.security.PermissionService.CHANGE_PERMISSIONS; import java.util.List; +import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; @@ -49,10 +50,13 @@ import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.rule.RuleService; import org.alfresco.service.cmr.security.PermissionService; import org.alfresco.service.namespace.QName; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; @Experimental public class RulesImpl implements Rules { + private static final Logger LOGGER = LoggerFactory.getLogger(RulesImpl.class); private static final String RULE_SET_EXPECTED_TYPE_NAME = "rule set"; private Nodes nodes; @@ -99,6 +103,18 @@ public class RulesImpl implements Rules .collect(Collectors.toList()); } + @Override + public Rule updateRuleById(String folderNodeId, String ruleSetId, String ruleId, Rule rule) + { + LOGGER.debug("Updating rule in folder {}, rule set {}, rule {} to {}", folderNodeId, ruleSetId, ruleId, rule); + + NodeRef folderNodeRef = validateFolderNode(folderNodeId, true); + NodeRef ruleSetNodeRef = validateRuleSetNode(ruleSetId, folderNodeRef); + validateRuleNode(ruleId, ruleSetNodeRef); + + return Rule.from(ruleService.saveRule(folderNodeRef, rule.toServiceModel(nodes))); + } + @Override public void deleteRuleById(String folderNodeId, String ruleSetId, String ruleId) { diff --git a/remote-api/src/main/java/org/alfresco/rest/api/nodes/NodeRulesRelation.java b/remote-api/src/main/java/org/alfresco/rest/api/nodes/NodeRulesRelation.java index e0d0f57d10..478d2ec4ea 100644 --- a/remote-api/src/main/java/org/alfresco/rest/api/nodes/NodeRulesRelation.java +++ b/remote-api/src/main/java/org/alfresco/rest/api/nodes/NodeRulesRelation.java @@ -47,8 +47,12 @@ import java.util.List; */ @Experimental @RelationshipResource(name = "rules", entityResource = NodeRuleSetsRelation.class, title = "Folder node rules") -public class NodeRulesRelation implements RelationshipResourceAction.Read, RelationshipResourceAction.ReadById, - RelationshipResourceAction.Create, RelationshipResourceAction.Delete, InitializingBean +public class NodeRulesRelation implements RelationshipResourceAction.Read, + RelationshipResourceAction.ReadById, + RelationshipResourceAction.Create, + RelationshipResourceAction.Update, + RelationshipResourceAction.Delete, + InitializingBean { private Rules rules; @@ -107,6 +111,8 @@ public class NodeRulesRelation implements RelationshipResourceAction.Read, /** * Create one or more rules inside a given folder and rule set. + *

+ * POST /nodes/{folderNodeId}/rule-sets/{ruleSetId}/rules * * @param folderNodeId The folder in which to create the rule. * @param ruleList The list of rules to create. @@ -126,9 +132,28 @@ public class NodeRulesRelation implements RelationshipResourceAction.Read, return rules.createRules(folderNodeId, ruleSetId, ruleList); } - public void setRules(Rules rules) + /** + * Update the specified folder rule. + *

+ * PUT /nodes/{folderNodeId}/rule-sets/{ruleSetId}/rules/{ruleId} + * + * @param folderNodeId The id of the folder containing the rule. + * @param rule The updated rule. + * @param parameters List of parameters including the rule set id and rule id. + * @return The updated rule. + * @throws RelationshipResourceNotFoundException in case resource was not found + */ + @WebApiDescription ( + title = "Update folder node rule", + description = "Update a single rule definition for given node's, rule set's and rule's IDs", + successStatus = HttpServletResponse.SC_OK + ) + @Override + public Rule update(String folderNodeId, Rule rule, Parameters parameters) { - this.rules = rules; + String ruleSetId = parameters.getRelationshipId(); + String ruleId = parameters.getRelationship2Id(); + return rules.updateRuleById(folderNodeId, ruleSetId, ruleId, rule); } /** @@ -152,4 +177,9 @@ public class NodeRulesRelation implements RelationshipResourceAction.Read, final String ruleId = parameters.getRelationship2Id(); rules.deleteRuleById(folderNodeId, ruleSetId, ruleId); } + + public void setRules(Rules rules) + { + this.rules = rules; + } } diff --git a/remote-api/src/main/java/org/alfresco/rest/framework/webscripts/ResourceWebScriptPut.java b/remote-api/src/main/java/org/alfresco/rest/framework/webscripts/ResourceWebScriptPut.java index 24e6e385c4..9a63cef648 100644 --- a/remote-api/src/main/java/org/alfresco/rest/framework/webscripts/ResourceWebScriptPut.java +++ b/remote-api/src/main/java/org/alfresco/rest/framework/webscripts/ResourceWebScriptPut.java @@ -79,6 +79,7 @@ public class ResourceWebScriptPut extends AbstractResourceWebScript implements P final Map resourceVars = locator.parseTemplateVars(req.getServiceMatch().getTemplateVars()); final String entityId = resourceVars.get(ResourceLocator.ENTITY_ID); final String relationshipId = resourceVars.get(ResourceLocator.RELATIONSHIP_ID); + final String relationship2Id = resourceVars.get(ResourceLocator.RELATIONSHIP2_ID); final RecognizedParams params = getRecognizedParams(req); final ResourceOperation operation = resourceMeta.getOperation(HttpMethod.PUT); @@ -99,9 +100,16 @@ public class ResourceWebScriptPut extends AbstractResourceWebScript implements P if (StringUtils.isBlank(relationshipId)) { throw new UnsupportedResourceOperationException("PUT is executed against the instance URL"); - } else + } + Object putRel = extractJsonContent(req, assistant.getJsonHelper(), resourceMeta.getObjectType(operation)); + if (StringUtils.isNotBlank(relationship2Id)) + { + ResourceWebScriptHelper.setUniqueId(putRel, relationship2Id); + return Params.valueOf(false, entityId, relationshipId, relationship2Id, + putRel, null, null, params, null, req); + } + else { - Object putRel = extractJsonContent(req, assistant.getJsonHelper(), resourceMeta.getObjectType(operation)); ResourceWebScriptHelper.setUniqueId(putRel,relationshipId); return Params.valueOf(entityId, params, putRel, req); }