diff --git a/amps/ags/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/base/BaseRMRestTest.java b/amps/ags/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/base/BaseRMRestTest.java index 8830ad3fc7..ac2f98da76 100644 --- a/amps/ags/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/base/BaseRMRestTest.java +++ b/amps/ags/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/base/BaseRMRestTest.java @@ -161,7 +161,7 @@ public class BaseRMRestTest extends RestTest */ @Override @BeforeClass (alwaysRun = true) - public void checkServerHealth() throws Exception + public void checkServerHealth() { // Create RM Site if not exist createRMSiteIfNotExists(); diff --git a/amps/ags/rm-community/rm-community-repo/source/java/org/alfresco/repo/rule/ExtendedRuleServiceImpl.java b/amps/ags/rm-community/rm-community-repo/source/java/org/alfresco/repo/rule/ExtendedRuleServiceImpl.java index 1c2738abcb..0cb33b926c 100644 --- a/amps/ags/rm-community/rm-community-repo/source/java/org/alfresco/repo/rule/ExtendedRuleServiceImpl.java +++ b/amps/ags/rm-community/rm-community-repo/source/java/org/alfresco/repo/rule/ExtendedRuleServiceImpl.java @@ -156,25 +156,24 @@ public class ExtendedRuleServiceImpl extends RuleServiceImpl * @see org.alfresco.repo.rule.RuleServiceImpl#saveRule(org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.cmr.rule.Rule) */ @Override - public void saveRule(final NodeRef nodeRef, final Rule rule) + public Rule saveRule(final NodeRef nodeRef, final Rule rule) { validateWormLockRuleAction(rule); if (filePlanService.isFilePlanComponent(nodeRef)) { - AuthenticationUtil.runAsSystem(new RunAsWork() + return AuthenticationUtil.runAsSystem(new RunAsWork() { @Override - public Void doWork() + public Rule doWork() { - ExtendedRuleServiceImpl.super.saveRule(nodeRef, rule); - return null; + return ExtendedRuleServiceImpl.super.saveRule(nodeRef, rule); } }); } else { - super.saveRule(nodeRef, rule); + return super.saveRule(nodeRef, rule); } } 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 new file mode 100644 index 0000000000..525daa03f7 --- /dev/null +++ b/packaging/tests/tas-restapi/src/test/java/org/alfresco/rest/rules/CreateRulesTests.java @@ -0,0 +1,224 @@ +/* + * #%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 java.util.stream.Collectors.toList; + +import static org.alfresco.utility.model.FileModel.getRandomFileModel; +import static org.alfresco.utility.model.FileType.TEXT_PLAIN; +import static org.alfresco.utility.report.log.Step.STEP; +import static org.junit.Assert.assertEquals; +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 java.util.List; +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.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.testng.annotations.BeforeClass; +import org.testng.annotations.Test; + +/** + * Tests for POST /nodes/{nodeId}/rule-sets/{ruleSetId}/rules. + */ +@Test(groups = {TestGroup.RULES}) +public class CreateRulesTests 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 create a rule. */ + @Test (groups = { TestGroup.REST_API, TestGroup.RULES, TestGroup.SANITY }) + public void createRule() + { + RestRuleModel ruleModel = new RestRuleModel(); + ruleModel.setName("ruleName"); + + RestRuleModel rule = restClient.authenticateUser(user).withCoreAPI().usingNode(ruleFolder).usingDefaultRuleSet() + .createSingleRule(ruleModel); + + restClient.assertStatusCodeIs(CREATED); + + rule.assertThat().field("id").isNotNull() + .assertThat().field("name").is("ruleName"); + } + + /** Check creating a rule in a non-existent folder returns an error. */ + @Test (groups = { TestGroup.REST_API, TestGroup.RULES }) + public void createRuleInNonExistentFolder() + { + STEP("Try to create a rule in non-existent folder."); + FolderModel nonExistentFolder = FolderModel.getRandomFolderModel(); + nonExistentFolder.setNodeRef("fake-id"); + + RestRuleModel ruleModel = new RestRuleModel(); + ruleModel.setName("ruleName"); + + restClient.authenticateUser(user).withCoreAPI().usingNode(nonExistentFolder).usingDefaultRuleSet().createSingleRule(ruleModel); + + restClient.assertStatusCodeIs(NOT_FOUND); + restClient.assertLastError().containsSummary("fake-id was not found"); + } + + /** Check creating a rule in a non-existent rule set returns an error. */ + @Test (groups = { TestGroup.REST_API, TestGroup.RULES }) + public void createRuleInNonExistentRuleSet() + { + STEP("Try to create a rule in non-existent rule set."); + RestRuleModel ruleModel = new RestRuleModel(); + ruleModel.setName("ruleName"); + + restClient.authenticateUser(user).withCoreAPI().usingNode(ruleFolder).usingRuleSet("fake-id").createSingleRule(ruleModel); + + restClient.assertStatusCodeIs(NOT_FOUND); + restClient.assertLastError().containsSummary("fake-id was not found"); + } + + /** Try to create a rule without a name and check the error. */ + @Test (groups = { TestGroup.REST_API, TestGroup.RULES }) + public void createRuleWithEmptyName() + { + RestRuleModel ruleModel = new RestRuleModel(); + ruleModel.setName(""); + + restClient.authenticateUser(user).withCoreAPI().usingNode(ruleFolder).usingDefaultRuleSet().createSingleRule(ruleModel); + + restClient.assertStatusCodeIs(BAD_REQUEST); + restClient.assertLastError().containsSummary("Rule name is a mandatory parameter"); + } + + /** Check we can create two rules with the same name. */ + @Test (groups = { TestGroup.REST_API, TestGroup.RULES }) + public void duplicateRuleNameIsAcceptable() + { + RestRuleModel ruleModel = new RestRuleModel(); + ruleModel.setName("duplicateRuleName"); + + STEP("Create two identical rules"); + RestRuleModel ruleA = restClient.authenticateUser(user).withCoreAPI().usingNode(ruleFolder).usingDefaultRuleSet().createSingleRule(ruleModel); + RestRuleModel ruleB = restClient.authenticateUser(user).withCoreAPI().usingNode(ruleFolder).usingDefaultRuleSet().createSingleRule(ruleModel); + + // Check that the names are the same but the ids are different. + ruleA.assertThat().field("name").is(ruleB.getName()); + ruleA.assertThat().field("id").isNot(ruleB.getId()); + } + + /** Check that a user without permission to view the folder cannot create a rule in it. */ + public void requirePermissionToCreateRule() + { + 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("Try to use a different user to create a rule in the private folder"); + RestRuleModel ruleModel = new RestRuleModel(); + ruleModel.setName("ruleName"); + + restClient.authenticateUser(user).withCoreAPI().usingNode(privateFolder).usingDefaultRuleSet().createSingleRule(ruleModel); + + restClient.assertStatusCodeIs(FORBIDDEN); + restClient.assertLastError().containsSummary("Cannot read from this node"); + } + + /** Check we can't create a rule under a document node. */ + @Test (groups = { TestGroup.REST_API, TestGroup.RULES }) + public void tryToCreateRuleUnderDocument() + { + STEP("Create a document."); + FileModel fileModel = dataContent.usingUser(user).usingSite(site).createContent(getRandomFileModel(TEXT_PLAIN)); + + RestRuleModel ruleModel = new RestRuleModel(); + ruleModel.setName("ruleName"); + + restClient.authenticateUser(user).withCoreAPI().usingNode(fileModel).usingDefaultRuleSet().createSingleRule(ruleModel); + + restClient.assertStatusCodeIs(BAD_REQUEST); + restClient.assertLastError().containsSummary("folder is expected"); + } + + /** Check we can create several rules. */ + @Test (groups = { TestGroup.REST_API, TestGroup.RULES }) + public void createRules() + { + STEP("Create a list of rules in one POST request"); + List ruleNames = List.of("ruleA", "ruleB", "ruleC"); + List ruleModels = ruleNames.stream().map(ruleName -> + { + RestRuleModel ruleModel = new RestRuleModel(); + ruleModel.setName(ruleName); + return ruleModel; + }).collect(toList()); + + RestRuleModelsCollection rules = restClient.authenticateUser(user).withCoreAPI().usingNode(ruleFolder).usingDefaultRuleSet() + .createListOfRules(ruleModels); + + restClient.assertStatusCodeIs(CREATED); + + assertEquals("Unexpected number of rules received in response.", ruleNames.size(), rules.getEntries().size()); + IntStream.range(0, ruleModels.size()).forEach(i -> + rules.getEntries().get(i).onModel() + .assertThat().field("id").isNotNull() + .assertThat().field("name").is(ruleNames.get(i))); + } + + /** Try to create several rules with an error in one of them. */ + @Test (groups = { TestGroup.REST_API, TestGroup.RULES }) + public void createRulesWithOneError() + { + STEP("Try to create a three rules but the middle one has an error."); + RestRuleModel ruleA = new RestRuleModel(); + ruleA.setName("ruleA"); + RestRuleModel ruleB = new RestRuleModel(); + // Don't set a name for Rule B. + RestRuleModel ruleC = new RestRuleModel(); + ruleC.setName("ruleC"); + List ruleModels = List.of(ruleA, ruleB, ruleC); + + restClient.authenticateUser(user).withCoreAPI().usingNode(ruleFolder).usingDefaultRuleSet().createListOfRules(ruleModels); + + restClient.assertStatusCodeIs(BAD_REQUEST); + restClient.assertLastError().containsSummary("Rule name is a mandatory parameter"); + } +} 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 new file mode 100644 index 0000000000..65e5ea40ad --- /dev/null +++ b/packaging/tests/tas-restapi/src/test/java/org/alfresco/rest/rules/GetRulesTests.java @@ -0,0 +1,207 @@ +/* + * #%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 java.util.stream.Collectors.toList; + +import static org.alfresco.utility.constants.UserRole.SiteCollaborator; +import static org.alfresco.utility.report.log.Step.STEP; +import static org.junit.Assert.assertTrue; +import static org.springframework.http.HttpStatus.FORBIDDEN; +import static org.springframework.http.HttpStatus.NOT_FOUND; +import static org.springframework.http.HttpStatus.OK; + +import java.util.List; +import java.util.stream.IntStream; +import java.util.stream.Stream; + +import org.alfresco.rest.RestTest; +import org.alfresco.rest.model.RestRuleModel; +import org.alfresco.rest.model.RestRuleModelsCollection; +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 and GET /nodes/{nodeId}/rule-sets/{ruleSetId}/rules/{ruleId}. + */ +@Test(groups = {TestGroup.RULES}) +public class GetRulesTests extends RestTest +{ + private UserModel user; + private SiteModel site; + private FolderModel ruleFolder; + private List createdRules; + private RestRuleModel createdRuleA; + + @BeforeClass(alwaysRun = true) + public void dataPreparation() + { + STEP("Create a user, site and folder"); + user = dataUser.createRandomTestUser(); + site = dataSite.usingUser(user).createPublicRandomSite(); + ruleFolder = dataContent.usingUser(user).usingSite(site).createFolder(); + + STEP("Create rules in the folder"); + createdRules = Stream.of("ruleA", "ruleB").map(ruleName -> { + RestRuleModel ruleModel = new RestRuleModel(); + ruleModel.setName(ruleName); + return restClient.authenticateUser(user).withCoreAPI().usingNode(ruleFolder).usingDefaultRuleSet().createSingleRule(ruleModel); + }).collect(toList()); + createdRuleA = createdRules.get(0); + } + + /** Check we can get an empty list of rules. */ + @Test(groups = { TestGroup.REST_API, TestGroup.RULES }) + public void getEmptyRulesList() + { + STEP("Create a folder in existing site"); + FolderModel folder = dataContent.usingUser(user).usingSite(site).createFolder(); + + STEP("Get the rules that apply to the folder"); + RestRuleModelsCollection rules = restClient.authenticateUser(user).withCoreAPI().usingNode(folder).usingDefaultRuleSet().getListOfRules(); + + restClient.assertStatusCodeIs(OK); + assertTrue("Expected no rules to be present.", rules.isEmpty()); + } + + /** Check we can get all the rules for a folder. */ + @Test (groups = { TestGroup.REST_API, TestGroup.RULES, TestGroup.SANITY }) + public void getRulesList() + { + STEP("Get the rules that apply to the folder"); + RestRuleModelsCollection rules = restClient.authenticateUser(user).withCoreAPI().usingNode(ruleFolder).usingDefaultRuleSet().getListOfRules(); + + restClient.assertStatusCodeIs(OK); + rules.assertThat().entriesListCountIs(createdRules.size()); + IntStream.range(0, createdRules.size()).forEach(i -> + rules.getEntries().get(i).onModel() + .assertThat().field("id").is(createdRules.get(i).getId()) + .assertThat().field("name").is(createdRules.get(i).getName())); + } + + /** Check we get a 404 if trying to load rules for a folder that doesn't exist. */ + @Test (groups = { TestGroup.REST_API, TestGroup.RULES }) + public void getRulesForNonExistentFolder() + { + STEP("Try to load rules for a non-existent folder."); + FolderModel nonExistentFolder = FolderModel.getRandomFolderModel(); + nonExistentFolder.setNodeRef("fake-id"); + restClient.authenticateUser(user).withCoreAPI().usingNode(nonExistentFolder).usingDefaultRuleSet().getListOfRules(); + restClient.assertStatusCodeIs(NOT_FOUND); + } + + /** Check we get a 404 if trying to load rules with a rule set id that doesn't exist. */ + @Test (groups = { TestGroup.REST_API, TestGroup.RULES }) + public void getRulesFromNonExistentRuleSet() + { + STEP("Create a folder in existing site"); + FolderModel folder = dataContent.usingUser(user).usingSite(site).createFolder(); + STEP("Try to load rules for a non-existent rule set."); + restClient.authenticateUser(user).withCoreAPI().usingNode(folder).usingRuleSet("fake-id").getListOfRules(); + restClient.assertStatusCodeIs(NOT_FOUND); + } + + /** Check we can get a rule by its id. */ + @Test (groups = { TestGroup.REST_API, TestGroup.RULES, TestGroup.SANITY }) + public void getSingleRule() + { + STEP("Load a particular rule"); + RestRuleModel rule = restClient.authenticateUser(user).withCoreAPI().usingNode(ruleFolder).usingDefaultRuleSet().getSingleRule(createdRuleA.getId()); + + restClient.assertStatusCodeIs(OK); + + rule.assertThat().field("id").is(createdRuleA.getId()) + .assertThat().field("name").is(createdRuleA.getName()); + } + + /** Check we get a 404 if trying to load a rule from a folder that doesn't exist. */ + @Test (groups = { TestGroup.REST_API, TestGroup.RULES }) + public void getSingleRuleFromNonExistentFolder() + { + STEP("Try to load a rule from a non-existent folder."); + FolderModel nonExistentFolder = FolderModel.getRandomFolderModel(); + nonExistentFolder.setNodeRef("fake-id"); + restClient.authenticateUser(user).withCoreAPI().usingNode(nonExistentFolder).usingDefaultRuleSet().getSingleRule("fake-rule-id"); + restClient.assertStatusCodeIs(NOT_FOUND); + } + + /** Check we get a 404 if trying to load a rule with a rule set id that doesn't exist. */ + @Test (groups = { TestGroup.REST_API, TestGroup.RULES }) + public void getSingleRuleFromNonExistentRuleSet() + { + STEP("Create a folder in existing site"); + FolderModel folder = dataContent.usingUser(user).usingSite(site).createFolder(); + STEP("Try to load rules for a non-existent rule set."); + restClient.authenticateUser(user).withCoreAPI().usingNode(folder).usingRuleSet("fake-id").getSingleRule("fake-rule-id"); + restClient.assertStatusCodeIs(NOT_FOUND); + } + + /** Check that a user without read permission cannot view the folder rules. */ + public void requireReadPermissionToGetRule() + { + STEP("Create a user and use them to create a private site containing a folder with a rule"); + UserModel privateUser = dataUser.createRandomTestUser(); + SiteModel privateSite = dataSite.usingUser(privateUser).createPrivateRandomSite(); + FolderModel privateFolder = dataContent.usingUser(privateUser).usingSite(privateSite).createFolder(); + RestRuleModel ruleModel = new RestRuleModel(); + ruleModel.setName("Private site rule"); + restClient.authenticateUser(privateUser).withCoreAPI().usingNode(privateFolder).usingDefaultRuleSet().createSingleRule(ruleModel); + + STEP("Try to get the rule with another user"); + restClient.authenticateUser(user).withCoreAPI().usingNode(privateFolder).usingDefaultRuleSet().getListOfRules(); + + restClient.assertLastError() + .statusCodeIs(FORBIDDEN) + .containsSummary("Cannot read from this node"); + } + + /** Check that a user with only read permission can view the folder rules. */ + public void dontRequireWritePermissionToGetRule() + { + STEP("Create a user and use them to create a private site containing a folder with a rule"); + UserModel privateUser = dataUser.createRandomTestUser(); + SiteModel privateSite = dataSite.usingUser(privateUser).createPrivateRandomSite(); + FolderModel privateFolder = dataContent.usingUser(privateUser).usingSite(privateSite).createFolder(); + RestRuleModel ruleModel = new RestRuleModel(); + ruleModel.setName("Private site rule"); + restClient.authenticateUser(privateUser).withCoreAPI().usingNode(privateFolder).usingDefaultRuleSet().createSingleRule(ruleModel); + + STEP("Create a collaborator in the private site"); + UserModel collaborator = dataUser.createRandomTestUser(); + collaborator.setUserRole(SiteCollaborator); + restClient.authenticateUser(privateUser).withCoreAPI().usingSite(privateSite).addPerson(collaborator); + + STEP("Check the collaborator can view the rule"); + RestRuleModelsCollection rules = restClient.authenticateUser(collaborator).withCoreAPI().usingNode(privateFolder).usingDefaultRuleSet().getListOfRules(); + + restClient.assertStatusCodeIs(OK); + rules.assertThat().entriesListContains("name", "Private site rule"); + } +} diff --git a/packaging/tests/tas-restapi/src/test/resources/test-suites/part1-suite.xml b/packaging/tests/tas-restapi/src/test/resources/test-suites/part1-suite.xml index ff2ffaed47..e74476b4c6 100644 --- a/packaging/tests/tas-restapi/src/test/resources/test-suites/part1-suite.xml +++ b/packaging/tests/tas-restapi/src/test/resources/test-suites/part1-suite.xml @@ -1,11 +1,11 @@ - + - + @@ -14,6 +14,7 @@ + diff --git a/pom.xml b/pom.xml index 21daa372b6..982670da19 100644 --- a/pom.xml +++ b/pom.xml @@ -118,9 +118,9 @@ 8.0.29 8 2.7.4 - 3.0.48 + 3.0.49 3.3.0 - 1.87 + 1.94 1.31 1.8 1.6 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 2580936c55..2dc581659e 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 @@ -27,9 +27,13 @@ package org.alfresco.rest.api; import org.alfresco.rest.api.model.rules.Rule; +import org.alfresco.rest.framework.core.exceptions.InvalidArgumentException; 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.rule.RuleServiceException; + +import java.util.List; /** * Folder node rules API. @@ -57,4 +61,25 @@ public interface Rules * @return {@link Rule} definition */ Rule getRuleById(String folderNodeId, String ruleSetId, String ruleId); + + /** + * 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 rule The definition of the rule. + * @return The newly created rules. + * @throws InvalidArgumentException If the nodes are not the expected types, or the rule set does not correspond to the folder. + * @throws RuleServiceException If the folder is already linked to another rule set. + */ + List createRules(String folderNodeId, String ruleSetId, List rule); + + /** + * Delete rule for rule's ID and check associations with folder node and rule set node + * + * @param folderNodeId - folder node ID + * @param ruleSetId - rule set ID + * @param ruleId - rule ID * + */ + void deleteRuleById(String folderNodeId, String ruleSetId, String ruleId); } 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 5f0f38d069..11ccde66b4 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 @@ -26,6 +26,10 @@ package org.alfresco.rest.api.impl; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + import org.alfresco.model.ContentModel; import org.alfresco.repo.rule.RuleModel; import org.alfresco.rest.api.Nodes; @@ -44,10 +48,6 @@ import org.alfresco.service.cmr.security.AccessStatus; import org.alfresco.service.cmr.security.PermissionService; import org.alfresco.service.namespace.QName; -import java.util.List; -import java.util.Set; -import java.util.stream.Collectors; - @Experimental public class RulesImpl implements Rules { @@ -82,6 +82,33 @@ public class RulesImpl implements Rules return Rule.from(ruleService.getRule(ruleNodeRef)); } + @Override + public List createRules(final String folderNodeId, final String ruleSetId, final List rules) + { + final NodeRef folderNodeRef = validateFolderNode(folderNodeId); + // Don't validate the ruleset node if -default- is passed since we may need to create it. + if (RuleSet.isNotDefaultId(ruleSetId)) + { + validateRuleSetNode(ruleSetId, folderNodeRef); + } + + return rules.stream() + .map(rule -> rule.toServiceModel(nodes)) + .map(rule -> ruleService.saveRule(folderNodeRef, rule)) + .map(Rule::from) + .collect(Collectors.toList()); + } + + @Override + public void deleteRuleById(String folderNodeId, String ruleSetId, String ruleId) + { + final NodeRef folderNodeRef = validateFolderNode(folderNodeId); + final NodeRef ruleSetNodeRef = validateRuleSetNode(ruleSetId, folderNodeRef); + final NodeRef ruleNodeRef = validateRuleNode(ruleId, ruleSetNodeRef); + final org.alfresco.service.cmr.rule.Rule rule = ruleService.getRule(ruleNodeRef); + ruleService.removeRule(folderNodeRef, rule); + } + public void setNodes(Nodes nodes) { this.nodes = nodes; diff --git a/remote-api/src/main/java/org/alfresco/rest/api/model/rules/Rule.java b/remote-api/src/main/java/org/alfresco/rest/api/model/rules/Rule.java index d44291abd5..9dad8b55e1 100644 --- a/remote-api/src/main/java/org/alfresco/rest/api/model/rules/Rule.java +++ b/remote-api/src/main/java/org/alfresco/rest/api/model/rules/Rule.java @@ -26,17 +26,25 @@ package org.alfresco.rest.api.model.rules; +import java.io.Serializable; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +import org.alfresco.model.ContentModel; +import org.alfresco.repo.action.ActionImpl; +import org.alfresco.repo.action.executer.SetPropertyValueActionExecuter; +import org.alfresco.rest.api.Nodes; import org.alfresco.rest.framework.resource.UniqueId; import org.alfresco.service.Experimental; -import org.alfresco.service.cmr.action.CompositeAction; - -import java.util.List; -import java.util.stream.Collectors; +import org.alfresco.service.cmr.action.Action; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.util.GUID; @Experimental public class Rule { - private String id; private String name; @@ -45,11 +53,36 @@ public class Rule return null; } - final Rule rule = new Rule(); - rule.id = ruleModel.getNodeRef().getId(); - rule.name = ruleModel.getTitle(); + return builder() + .setId(ruleModel.getNodeRef().getId()) + .setName(ruleModel.getTitle()) + .createRule(); + } - return rule; + /** + * Convert the REST model object to the equivalent service POJO. + * + * @param nodes The nodes API. + * @return The rule service POJO. + */ + public org.alfresco.service.cmr.rule.Rule toServiceModel(Nodes nodes) + { + org.alfresco.service.cmr.rule.Rule ruleModel = new org.alfresco.service.cmr.rule.Rule(); + if (id != null) + { + NodeRef nodeRef = nodes.validateOrLookupNode(id, null); + ruleModel.setNodeRef(nodeRef); + } + ruleModel.setTitle(name); + + // TODO: Once we have actions working properly then this needs to be replaced. + Map parameters = Map.of( + SetPropertyValueActionExecuter.PARAM_PROPERTY, ContentModel.PROP_TITLE, + SetPropertyValueActionExecuter.PARAM_VALUE, "UPDATED:" + GUID.generate()); + Action action = new ActionImpl(null, GUID.generate(), SetPropertyValueActionExecuter.NAME, parameters); + ruleModel.setAction(action); + + return ruleModel; } @UniqueId @@ -73,9 +106,69 @@ public class Rule this.name = name; } + // TODO: Added stub for actions as it's a required field. Replace this implementation when we implement support for actions. + public List getActions() + { + return Collections.emptyList(); + } + + @Override + public boolean equals(Object o) + { + if (this == o) + { + return true; + } + if (!(o instanceof Rule)) + { + return false; + } + Rule rule = (Rule) o; + return Objects.equals(id, rule.id) && + Objects.equals(name, rule.name); + } + + @Override + public int hashCode() + { + return Objects.hash(id, name); + } + @Override public String toString() { return "Rule{" + "id='" + id + '\'' + ", name='" + name + '\'' + '}'; } + + public static RuleBuilder builder() + { + return new RuleBuilder(); + } + + /** Builder class. */ + public static class RuleBuilder + { + private String id; + private String name; + + public RuleBuilder setId(String id) + { + this.id = id; + return this; + } + + public RuleBuilder setName(String name) + { + this.name = name; + return this; + } + + public Rule createRule() + { + Rule rule = new Rule(); + rule.setId(id); + rule.setName(name); + return rule; + } + } } diff --git a/remote-api/src/main/java/org/alfresco/rest/api/model/rules/RuleSet.java b/remote-api/src/main/java/org/alfresco/rest/api/model/rules/RuleSet.java index 5202dfbbcd..87860356b6 100644 --- a/remote-api/src/main/java/org/alfresco/rest/api/model/rules/RuleSet.java +++ b/remote-api/src/main/java/org/alfresco/rest/api/model/rules/RuleSet.java @@ -31,7 +31,7 @@ import org.alfresco.service.Experimental; @Experimental public class RuleSet { - private static final String DEFAULT_ID = "-default-"; + public static final String DEFAULT_ID = "-default-"; private String id; 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 2345825573..e0d0f57d10 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 @@ -39,6 +39,7 @@ import org.alfresco.util.PropertyCheck; import org.springframework.beans.factory.InitializingBean; import javax.servlet.http.HttpServletResponse; +import java.util.List; /** * Folder node's rules. @@ -46,7 +47,8 @@ import javax.servlet.http.HttpServletResponse; */ @Experimental @RelationshipResource(name = "rules", entityResource = NodeRuleSetsRelation.class, title = "Folder node rules") -public class NodeRulesRelation implements RelationshipResourceAction.Read, RelationshipResourceAction.ReadById, InitializingBean +public class NodeRulesRelation implements RelationshipResourceAction.Read, RelationshipResourceAction.ReadById, + RelationshipResourceAction.Create, RelationshipResourceAction.Delete, InitializingBean { private Rules rules; @@ -103,8 +105,51 @@ public class NodeRulesRelation implements RelationshipResourceAction.Read, return rules.getRuleById(folderNodeId, ruleSetId, ruleId); } + /** + * Create one or more rules inside a given folder and rule set. + * + * @param folderNodeId The folder in which to create the rule. + * @param ruleList The list of rules to create. + * @param parameters List of parameters including the rule set id as the relationship. + * @return The newly created rules. + */ + @WebApiDescription( + title = "Create folder rule", + description = "Creates one or more folder rules for the given folder and rule set", + successStatus = HttpServletResponse.SC_CREATED + ) + @Override + public List create(String folderNodeId, List ruleList, Parameters parameters) + { + final String ruleSetId = parameters.getRelationshipId(); + + return rules.createRules(folderNodeId, ruleSetId, ruleList); + } + public void setRules(Rules rules) { this.rules = rules; } + + /** + * Delete single folder rule for given node's, rule set's and rule's IDs. + * + * - DELETE /nodes/{folderNodeId}/rule-sets/{ruleSetId}/rules/{ruleId} + * + * @param folderNodeId - entity resource context for this relationship + * @param ruleSetId - rule set node ID (associated with folder node) + * @param parameters - Should not be null. Should contain at least ruleId (relationship2Id) + * @throws RelationshipResourceNotFoundException in case resource was not found + */ + @WebApiDescription( + title="Delete folder node rule", + description = "Deletes a single rule definition for given node's, rule set's and rule's IDs", + successStatus = HttpServletResponse.SC_NO_CONTENT + ) + @Override + public void delete(String folderNodeId, String ruleSetId, Parameters parameters) + { + final String ruleId = parameters.getRelationship2Id(); + rules.deleteRuleById(folderNodeId, ruleSetId, ruleId); + } } diff --git a/remote-api/src/test/java/org/alfresco/rest/api/impl/RulesImplTest.java b/remote-api/src/test/java/org/alfresco/rest/api/impl/RulesImplTest.java index ad50bf30a5..90b3ae0efb 100644 --- a/remote-api/src/test/java/org/alfresco/rest/api/impl/RulesImplTest.java +++ b/remote-api/src/test/java/org/alfresco/rest/api/impl/RulesImplTest.java @@ -26,9 +26,26 @@ package org.alfresco.rest.api.impl; +import static java.util.Collections.emptyList; + +import static org.alfresco.rest.api.model.rules.RuleSet.DEFAULT_ID; +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.eq; +import static org.mockito.ArgumentMatchers.isNull; +import static org.mockito.BDDMockito.given; +import static org.mockito.BDDMockito.then; +import static org.mockito.Mockito.mock; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + import junit.framework.TestCase; import org.alfresco.rest.api.Nodes; import org.alfresco.rest.api.model.rules.Rule; +import org.alfresco.rest.framework.core.exceptions.EntityNotFoundException; import org.alfresco.rest.framework.core.exceptions.InvalidArgumentException; import org.alfresco.rest.framework.core.exceptions.PermissionDeniedException; import org.alfresco.rest.framework.resource.parameters.CollectionWithPagingInfo; @@ -47,22 +64,10 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.mockito.junit.MockitoJUnitRunner; -import java.util.Collection; -import java.util.List; - -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.eq; -import static org.mockito.ArgumentMatchers.isNull; -import static org.mockito.BDDMockito.given; -import static org.mockito.BDDMockito.then; - @Experimental @RunWith(MockitoJUnitRunner.class) public class RulesImplTest extends TestCase { - private static final String FOLDER_NODE_ID = "dummy-folder-node-id"; private static final String RULE_SET_ID = "dummy-rule-set-id"; private static final String RULE_ID = "dummy-rule-id"; @@ -70,6 +75,7 @@ public class RulesImplTest extends TestCase private static final NodeRef ruleSetNodeRef = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, RULE_SET_ID); private static final NodeRef ruleNodeRef = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, RULE_ID); private static final Paging paging = Paging.DEFAULT; + private static final String RULE_NAME = "Rule name"; @Mock private Nodes nodesMock; @@ -90,7 +96,7 @@ public class RulesImplTest extends TestCase MockitoAnnotations.openMocks(this); given(nodesMock.validateOrLookupNode(eq(FOLDER_NODE_ID), any())).willReturn(folderNodeRef); - given(nodesMock.validateNode(eq(RULE_SET_ID))).willReturn(ruleSetNodeRef); + given(nodesMock.validateNode(RULE_SET_ID)).willReturn(ruleSetNodeRef); given(nodesMock.nodeMatches(any(), any(), any())).willReturn(true); given(permissionServiceMock.hasReadPermission(any())).willReturn(AccessStatus.ALLOWED); } @@ -104,15 +110,15 @@ public class RulesImplTest extends TestCase // when final CollectionWithPagingInfo rulesPage = rules.getRules(FOLDER_NODE_ID, RULE_SET_ID, paging); - then(nodesMock).should().validateOrLookupNode(eq(FOLDER_NODE_ID), isNull()); - then(nodesMock).should().validateNode(eq(RULE_SET_ID)); + then(nodesMock).should().validateOrLookupNode(FOLDER_NODE_ID, null); + then(nodesMock).should().validateNode(RULE_SET_ID); then(nodesMock).should().nodeMatches(eq(folderNodeRef), any(), isNull()); then(nodesMock).should().nodeMatches(eq(ruleSetNodeRef), any(), isNull()); then(nodesMock).shouldHaveNoMoreInteractions(); - then(permissionServiceMock).should().hasReadPermission(eq(folderNodeRef)); + then(permissionServiceMock).should().hasReadPermission(folderNodeRef); then(permissionServiceMock).shouldHaveNoMoreInteractions(); - then(ruleServiceMock).should().isRuleSetAssociatedWithFolder(eq(ruleSetNodeRef), eq(folderNodeRef)); - then(ruleServiceMock).should().getRules(eq(folderNodeRef)); + then(ruleServiceMock).should().isRuleSetAssociatedWithFolder(ruleSetNodeRef, folderNodeRef); + then(ruleServiceMock).should().getRules(folderNodeRef); then(ruleServiceMock).shouldHaveNoMoreInteractions(); assertThat(rulesPage) .isNotNull() @@ -129,19 +135,18 @@ public class RulesImplTest extends TestCase @Test public void testGetRulesForDefaultRuleSet() { - final String defaultRuleSetId = "-default-"; given(ruleServiceMock.getRules(any())).willReturn(List.of(createRule(RULE_ID))); // when - final CollectionWithPagingInfo rulesPage = rules.getRules(FOLDER_NODE_ID, defaultRuleSetId, paging); + final CollectionWithPagingInfo rulesPage = rules.getRules(FOLDER_NODE_ID, DEFAULT_ID, paging); - then(nodesMock).should().validateOrLookupNode(eq(FOLDER_NODE_ID), isNull()); + then(nodesMock).should().validateOrLookupNode(FOLDER_NODE_ID, null); then(nodesMock).should().nodeMatches(eq(folderNodeRef), any(), isNull()); then(nodesMock).shouldHaveNoMoreInteractions(); - then(permissionServiceMock).should().hasReadPermission(eq(folderNodeRef)); + then(permissionServiceMock).should().hasReadPermission(folderNodeRef); then(permissionServiceMock).shouldHaveNoMoreInteractions(); - then(ruleServiceMock).should().getRuleSetNode(eq(folderNodeRef)); - then(ruleServiceMock).should().getRules(eq(folderNodeRef)); + then(ruleServiceMock).should().getRuleSetNode(folderNodeRef); + then(ruleServiceMock).should().getRules(folderNodeRef); then(ruleServiceMock).shouldHaveNoMoreInteractions(); assertThat(rulesPage) .isNotNull() @@ -189,7 +194,7 @@ public class RulesImplTest extends TestCase assertThatExceptionOfType(InvalidArgumentException.class).isThrownBy( () -> rules.getRules(FOLDER_NODE_ID, RULE_SET_ID, paging)); - then(ruleServiceMock).should().isRuleSetAssociatedWithFolder(eq(ruleSetNodeRef), eq(folderNodeRef)); + then(ruleServiceMock).should().isRuleSetAssociatedWithFolder(ruleSetNodeRef, folderNodeRef); then(ruleServiceMock).shouldHaveNoMoreInteractions(); } @@ -208,7 +213,7 @@ public class RulesImplTest extends TestCase @Test public void testGetRuleById() { - given(nodesMock.validateNode(eq(RULE_ID))).willReturn(ruleNodeRef); + given(nodesMock.validateNode(RULE_ID)).willReturn(ruleNodeRef); given(ruleServiceMock.isRuleSetAssociatedWithFolder(any(), any())).willReturn(true); given(ruleServiceMock.isRuleAssociatedWithRuleSet(any(), any())).willReturn(true); given(ruleServiceMock.getRule(any())).willReturn(createRule(RULE_ID)); @@ -216,18 +221,18 @@ public class RulesImplTest extends TestCase // when final Rule rule = rules.getRuleById(FOLDER_NODE_ID, RULE_SET_ID, RULE_ID); - then(nodesMock).should().validateOrLookupNode(eq(FOLDER_NODE_ID), isNull()); - then(nodesMock).should().validateNode(eq(RULE_SET_ID)); - then(nodesMock).should().validateNode(eq(RULE_ID)); + then(nodesMock).should().validateOrLookupNode(FOLDER_NODE_ID, null); + then(nodesMock).should().validateNode(RULE_SET_ID); + then(nodesMock).should().validateNode(RULE_ID); then(nodesMock).should().nodeMatches(eq(folderNodeRef), any(), isNull()); then(nodesMock).should().nodeMatches(eq(ruleSetNodeRef), any(), isNull()); then(nodesMock).should().nodeMatches(eq(ruleNodeRef), any(), isNull()); then(nodesMock).shouldHaveNoMoreInteractions(); - then(permissionServiceMock).should().hasReadPermission(eq(folderNodeRef)); + then(permissionServiceMock).should().hasReadPermission(folderNodeRef); then(permissionServiceMock).shouldHaveNoMoreInteractions(); - then(ruleServiceMock).should().isRuleSetAssociatedWithFolder(eq(ruleSetNodeRef), eq(folderNodeRef)); - then(ruleServiceMock).should().isRuleAssociatedWithRuleSet(eq(ruleNodeRef), eq(ruleSetNodeRef)); - then(ruleServiceMock).should().getRule(eq(ruleNodeRef)); + then(ruleServiceMock).should().isRuleSetAssociatedWithFolder(ruleSetNodeRef, folderNodeRef); + then(ruleServiceMock).should().isRuleAssociatedWithRuleSet(ruleNodeRef, ruleSetNodeRef); + then(ruleServiceMock).should().getRule(ruleNodeRef); then(ruleServiceMock).shouldHaveNoMoreInteractions(); assertThat(rule) .isNotNull() @@ -239,7 +244,7 @@ public class RulesImplTest extends TestCase public void testGetRuleByIdForDefaultRuleSet() { final String defaultRuleSetId = "-default-"; - given(nodesMock.validateNode(eq(RULE_ID))).willReturn(ruleNodeRef); + given(nodesMock.validateNode(RULE_ID)).willReturn(ruleNodeRef); given(ruleServiceMock.getRuleSetNode(any())).willReturn(ruleSetNodeRef); given(ruleServiceMock.isRuleAssociatedWithRuleSet(any(), any())).willReturn(true); given(ruleServiceMock.getRule(any())).willReturn(createRule(RULE_ID)); @@ -247,15 +252,15 @@ public class RulesImplTest extends TestCase // when final Rule rule = rules.getRuleById(FOLDER_NODE_ID, defaultRuleSetId, RULE_ID); - then(nodesMock).should().validateOrLookupNode(eq(FOLDER_NODE_ID), isNull()); - then(nodesMock).should().validateNode(eq(RULE_ID)); + then(nodesMock).should().validateOrLookupNode(FOLDER_NODE_ID, null); + then(nodesMock).should().validateNode(RULE_ID); then(nodesMock).should().nodeMatches(eq(folderNodeRef), any(), isNull()); then(nodesMock).should().nodeMatches(eq(ruleNodeRef), any(), isNull()); then(nodesMock).shouldHaveNoMoreInteractions(); - then(permissionServiceMock).should().hasReadPermission(eq(folderNodeRef)); + then(permissionServiceMock).should().hasReadPermission(folderNodeRef); then(permissionServiceMock).shouldHaveNoMoreInteractions(); - then(ruleServiceMock).should().getRuleSetNode(eq(folderNodeRef)); - then(ruleServiceMock).should().isRuleAssociatedWithRuleSet(eq(ruleNodeRef), eq(ruleSetNodeRef)); + then(ruleServiceMock).should().getRuleSetNode(folderNodeRef); + then(ruleServiceMock).should().isRuleAssociatedWithRuleSet(ruleNodeRef, ruleSetNodeRef); then(ruleServiceMock).should().getRule(eq(ruleNodeRef)); then(ruleServiceMock).shouldHaveNoMoreInteractions(); assertThat(rule) @@ -267,8 +272,8 @@ public class RulesImplTest extends TestCase @Test public void testGetRuleByIdForNotAssociatedRuleToRuleSet() { - given(nodesMock.validateNode(eq(RULE_SET_ID))).willReturn(ruleSetNodeRef); - given(nodesMock.validateNode(eq(RULE_ID))).willReturn(ruleNodeRef); + given(nodesMock.validateNode(RULE_SET_ID)).willReturn(ruleSetNodeRef); + given(nodesMock.validateNode(RULE_ID)).willReturn(ruleNodeRef); given(ruleServiceMock.isRuleSetAssociatedWithFolder(any(), any())).willReturn(true); given(ruleServiceMock.isRuleAssociatedWithRuleSet(any(), any())).willReturn(false); @@ -276,8 +281,240 @@ public class RulesImplTest extends TestCase assertThatExceptionOfType(InvalidArgumentException.class).isThrownBy( () -> rules.getRuleById(FOLDER_NODE_ID, RULE_SET_ID, RULE_ID)); - then(ruleServiceMock).should().isRuleSetAssociatedWithFolder(eq(ruleSetNodeRef), eq(folderNodeRef)); - then(ruleServiceMock).should().isRuleAssociatedWithRuleSet(eq(ruleNodeRef), eq(ruleSetNodeRef)); + then(ruleServiceMock).should().isRuleSetAssociatedWithFolder(ruleSetNodeRef, folderNodeRef); + then(ruleServiceMock).should().isRuleAssociatedWithRuleSet(ruleNodeRef, ruleSetNodeRef); + then(ruleServiceMock).shouldHaveNoMoreInteractions(); + } + + /** Create a single rule. */ + @Test + public void testSaveRules() + { + Rule ruleBody = mock(Rule.class); + List ruleList = List.of(ruleBody); + given(ruleServiceMock.isRuleSetAssociatedWithFolder(any(), any())).willReturn(true); + org.alfresco.service.cmr.rule.Rule serviceRuleBody = mock(org.alfresco.service.cmr.rule.Rule.class); + given(ruleBody.toServiceModel(nodesMock)).willReturn(serviceRuleBody); + org.alfresco.service.cmr.rule.Rule serviceRule = mock(org.alfresco.service.cmr.rule.Rule.class); + given(ruleServiceMock.saveRule(folderNodeRef, serviceRuleBody)).willReturn(serviceRule); + given(serviceRule.getNodeRef()).willReturn(ruleNodeRef); + + // when + List actual = rules.createRules(folderNodeRef.getId(), ruleSetNodeRef.getId(), ruleList); + + then(ruleServiceMock).should().isRuleSetAssociatedWithFolder(ruleSetNodeRef, folderNodeRef); + then(ruleServiceMock).should().saveRule(folderNodeRef, ruleBody.toServiceModel(nodesMock)); + then(ruleServiceMock).shouldHaveNoMoreInteractions(); + List expected = List.of(Rule.from(serviceRule)); + assertThat(actual).isEqualTo(expected); + } + + /** Check that when passing the default rule set then we don't perform any validation around the rule set node. */ + @Test + public void testSaveRules_defaultRuleSet() + { + Rule ruleBody = mock(Rule.class); + List ruleList = List.of(ruleBody); + org.alfresco.service.cmr.rule.Rule serviceRuleBody = mock(org.alfresco.service.cmr.rule.Rule.class); + given(ruleBody.toServiceModel(nodesMock)).willReturn(serviceRuleBody); + org.alfresco.service.cmr.rule.Rule serviceRule = mock(org.alfresco.service.cmr.rule.Rule.class); + given(ruleServiceMock.saveRule(folderNodeRef, serviceRuleBody)).willReturn(serviceRule); + given(serviceRule.getNodeRef()).willReturn(ruleNodeRef); + + // when + List actual = rules.createRules(folderNodeRef.getId(), DEFAULT_ID, ruleList); + + then(ruleServiceMock).should().saveRule(folderNodeRef, ruleBody.toServiceModel(nodesMock)); + then(ruleServiceMock).shouldHaveNoMoreInteractions(); + List expected = List.of(Rule.from(serviceRule)); + assertThat(actual).isEqualTo(expected); + } + + @Test + public void testSaveRules_ruleSetNotAssociatedWithFolder() + { + Rule rule = Rule.builder().setName(RULE_NAME) + .createRule(); + List ruleList = List.of(rule); + given(ruleServiceMock.isRuleSetAssociatedWithFolder(ruleSetNodeRef, folderNodeRef)).willReturn(false); + + // when + assertThatExceptionOfType(InvalidArgumentException.class).isThrownBy( + () -> rules.createRules(folderNodeRef.getId(), ruleSetNodeRef.getId(), ruleList)); + + then(ruleServiceMock).should().isRuleSetAssociatedWithFolder(ruleSetNodeRef, folderNodeRef); + then(ruleServiceMock).shouldHaveNoMoreInteractions(); + } + + @Test + public void testSaveRules_emptyRuleList() + { + given(ruleServiceMock.isRuleSetAssociatedWithFolder(any(), any())).willReturn(true); + List ruleList = emptyList(); + + // when + List actual = this.rules.createRules(folderNodeRef.getId(), ruleSetNodeRef.getId(), ruleList); + + then(ruleServiceMock).should().isRuleSetAssociatedWithFolder(ruleSetNodeRef, folderNodeRef); + then(ruleServiceMock).shouldHaveNoMoreInteractions(); + assertThat(actual).isEqualTo(emptyList()); + } + + /** Create three rules in a single call and check they are all passed to the RuleService. */ + @Test + public void testSaveRules_createMultipleRules() + { + given(ruleServiceMock.isRuleSetAssociatedWithFolder(any(), any())).willReturn(true); + List ruleBodyList = new ArrayList<>(); + List expected = new ArrayList<>(); + for (String ruleId : List.of("A", "B", "C")) + { + Rule ruleBody = mock(Rule.class); + ruleBodyList.add(ruleBody); + org.alfresco.service.cmr.rule.Rule serviceRuleBody = mock(org.alfresco.service.cmr.rule.Rule.class); + given(ruleBody.toServiceModel(nodesMock)).willReturn(serviceRuleBody); + org.alfresco.service.cmr.rule.Rule serviceRule = mock(org.alfresco.service.cmr.rule.Rule.class); + given(ruleServiceMock.saveRule(folderNodeRef, serviceRuleBody)).willReturn(serviceRule); + NodeRef ruleNodeRef = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, ruleId); + given(serviceRule.getNodeRef()).willReturn(ruleNodeRef); + expected.add(Rule.from(serviceRule)); + } + + // when + List actual = rules.createRules(folderNodeRef.getId(), ruleSetNodeRef.getId(), ruleBodyList); + + then(ruleServiceMock).should().isRuleSetAssociatedWithFolder(ruleSetNodeRef, folderNodeRef); + for (Rule ruleBody : ruleBodyList) + { + then(ruleServiceMock).should().saveRule(folderNodeRef, ruleBody.toServiceModel(nodesMock)); + } + then(ruleServiceMock).shouldHaveNoMoreInteractions(); + assertThat(actual).isEqualTo(expected); + } + + @Test + public void testDeleteRuleById() { + given(nodesMock.validateNode(RULE_SET_ID)).willReturn(ruleSetNodeRef); + given(nodesMock.validateNode(RULE_ID)).willReturn(ruleNodeRef); + given(ruleServiceMock.isRuleAssociatedWithRuleSet(any(), any())).willReturn(true); + given(ruleServiceMock.isRuleSetAssociatedWithFolder(any(), any())).willReturn(true); + org.alfresco.service.cmr.rule.Rule rule = createRule(RULE_ID); + given(ruleServiceMock.getRule(any())).willReturn(rule); + + //when + rules.deleteRuleById(FOLDER_NODE_ID, RULE_SET_ID, RULE_ID); + + then(nodesMock).should().validateOrLookupNode(FOLDER_NODE_ID, null); + then(nodesMock).should().validateNode(RULE_SET_ID); + then(nodesMock).should().validateNode(RULE_ID); + then(nodesMock).should().nodeMatches(eq(folderNodeRef), any(), isNull()); + then(nodesMock).should().nodeMatches(eq(ruleSetNodeRef), any(), isNull()); + then(nodesMock).should().nodeMatches(eq(ruleNodeRef), any(), isNull()); + then(nodesMock).shouldHaveNoMoreInteractions(); + then(permissionServiceMock).should().hasReadPermission(folderNodeRef); + then(permissionServiceMock).shouldHaveNoMoreInteractions(); + then(ruleServiceMock).should().isRuleSetAssociatedWithFolder(ruleSetNodeRef, folderNodeRef); + then(ruleServiceMock).should().isRuleAssociatedWithRuleSet(ruleNodeRef, ruleSetNodeRef); + then(ruleServiceMock).should().getRule(ruleNodeRef); + then(ruleServiceMock).should().removeRule(folderNodeRef, rule); + then(ruleServiceMock).shouldHaveNoMoreInteractions(); + } + + @Test + public void testDeleteRuleById_NonExistingRuleId() { + given(nodesMock.validateNode(RULE_SET_ID)).willReturn(ruleSetNodeRef); + given(nodesMock.validateNode(RULE_ID)).willThrow(new EntityNotFoundException(RULE_ID)); + given(ruleServiceMock.isRuleSetAssociatedWithFolder(any(), any())).willReturn(true); + + //when + assertThatExceptionOfType(EntityNotFoundException.class).isThrownBy( + () -> rules.deleteRuleById(FOLDER_NODE_ID, RULE_SET_ID, RULE_ID)); + + then(nodesMock).should().validateOrLookupNode(FOLDER_NODE_ID, null); + then(nodesMock).should().validateNode(RULE_SET_ID); + then(nodesMock).should().validateNode(RULE_ID); + then(nodesMock).should().nodeMatches(eq(folderNodeRef), any(), isNull()); + then(nodesMock).should().nodeMatches(eq(ruleSetNodeRef), any(), isNull()); + then(nodesMock).shouldHaveNoMoreInteractions(); + then(permissionServiceMock).should().hasReadPermission(folderNodeRef); + then(permissionServiceMock).shouldHaveNoMoreInteractions(); + then(ruleServiceMock).should().isRuleSetAssociatedWithFolder(ruleSetNodeRef, folderNodeRef); + then(ruleServiceMock).shouldHaveNoMoreInteractions(); + } + + @Test + public void testDeleteRuleById_RuleIdNotInRuleSet() { + given(nodesMock.validateNode(RULE_SET_ID)).willReturn(ruleSetNodeRef); + given(nodesMock.validateNode(RULE_ID)).willReturn(ruleNodeRef); + given(ruleServiceMock.isRuleSetAssociatedWithFolder(any(), any())).willReturn(true); + given(ruleServiceMock.isRuleAssociatedWithRuleSet(any(), any())).willReturn(false); + + //when + assertThatExceptionOfType(InvalidArgumentException.class).isThrownBy( + () -> rules.deleteRuleById(FOLDER_NODE_ID, RULE_SET_ID, RULE_ID)); + + then(nodesMock).should().validateOrLookupNode(FOLDER_NODE_ID, null); + then(nodesMock).should().validateNode(RULE_SET_ID); + then(nodesMock).should().validateNode(RULE_ID); + then(nodesMock).should().nodeMatches(eq(folderNodeRef), any(), isNull()); + then(nodesMock).should().nodeMatches(eq(ruleSetNodeRef), any(), isNull()); + then(nodesMock).should().nodeMatches(eq(ruleNodeRef), any(), isNull()); + then(nodesMock).shouldHaveNoMoreInteractions(); + then(permissionServiceMock).should().hasReadPermission(folderNodeRef); + then(permissionServiceMock).shouldHaveNoMoreInteractions(); + then(ruleServiceMock).should().isRuleSetAssociatedWithFolder(ruleSetNodeRef, folderNodeRef); + then(ruleServiceMock).should().isRuleAssociatedWithRuleSet(ruleNodeRef, ruleSetNodeRef); + then(ruleServiceMock).shouldHaveNoMoreInteractions(); + } + + @Test + public void testDeleteRuleById_NonExistingRuleSetId() { + given(nodesMock.validateNode(RULE_SET_ID)).willThrow(new EntityNotFoundException(RULE_SET_ID)); + + //when + assertThatExceptionOfType(EntityNotFoundException.class).isThrownBy( + () -> rules.deleteRuleById(FOLDER_NODE_ID, RULE_SET_ID, RULE_ID)); + + then(nodesMock).should().validateOrLookupNode(FOLDER_NODE_ID, null); + then(nodesMock).should().validateNode(RULE_SET_ID); + then(nodesMock).should().nodeMatches(eq(folderNodeRef), any(), isNull()); + then(nodesMock).shouldHaveNoMoreInteractions(); + then(permissionServiceMock).should().hasReadPermission(folderNodeRef); + then(permissionServiceMock).shouldHaveNoMoreInteractions(); + then(ruleServiceMock).shouldHaveNoMoreInteractions(); + } + + @Test + public void testDeleteRuleById_RuleSetNotInFolder() { + given(nodesMock.validateNode(RULE_SET_ID)).willReturn(ruleSetNodeRef); + given(ruleServiceMock.isRuleSetAssociatedWithFolder(any(), any())).willReturn(false); + + //when + assertThatExceptionOfType(InvalidArgumentException.class).isThrownBy( + () -> rules.deleteRuleById(FOLDER_NODE_ID, RULE_SET_ID, RULE_ID)); + + then(nodesMock).should().validateOrLookupNode(FOLDER_NODE_ID, null); + then(nodesMock).should().validateNode(RULE_SET_ID); + then(nodesMock).should().nodeMatches(eq(folderNodeRef), any(), isNull()); + then(nodesMock).should().nodeMatches(eq(ruleSetNodeRef), any(), isNull()); + then(nodesMock).shouldHaveNoMoreInteractions(); + then(permissionServiceMock).should().hasReadPermission(folderNodeRef); + then(permissionServiceMock).shouldHaveNoMoreInteractions(); + then(ruleServiceMock).should().isRuleSetAssociatedWithFolder(ruleSetNodeRef, folderNodeRef); + then(ruleServiceMock).shouldHaveNoMoreInteractions(); + } + + @Test + public void testDeleteRuleById_NonExistingFolderId() { + given(nodesMock.validateOrLookupNode(FOLDER_NODE_ID, null)).willThrow(new EntityNotFoundException(RULE_ID)); + + //when + assertThatExceptionOfType(EntityNotFoundException.class).isThrownBy( + () -> rules.deleteRuleById(FOLDER_NODE_ID, RULE_SET_ID, RULE_ID)); + + then(nodesMock).should().validateOrLookupNode(FOLDER_NODE_ID, null); + then(nodesMock).shouldHaveNoMoreInteractions(); + then(permissionServiceMock).shouldHaveNoMoreInteractions(); then(ruleServiceMock).shouldHaveNoMoreInteractions(); } @@ -287,4 +524,4 @@ public class RulesImplTest extends TestCase return rule; } -} \ No newline at end of file +} diff --git a/remote-api/src/test/java/org/alfresco/rest/api/nodes/NodeRulesRelationTest.java b/remote-api/src/test/java/org/alfresco/rest/api/nodes/NodeRulesRelationTest.java index 6f0dff5332..6537db85bf 100644 --- a/remote-api/src/test/java/org/alfresco/rest/api/nodes/NodeRulesRelationTest.java +++ b/remote-api/src/test/java/org/alfresco/rest/api/nodes/NodeRulesRelationTest.java @@ -89,4 +89,14 @@ public class NodeRulesRelationTest extends TestCase then(rulesMock).should().getRuleById(eq(FOLDER_NODE_ID), eq(RULE_SET_ID), eq(RULE_ID)); then(rulesMock).shouldHaveNoMoreInteractions(); } -} \ No newline at end of file + + @Test + public void testDeleteById() { + final Parameters parameters = ParamsExtender.valueOf(null, FOLDER_NODE_ID, RULE_SET_ID, RULE_ID); + // when + nodeRulesRelation.delete(FOLDER_NODE_ID, RULE_SET_ID, parameters); + + then(rulesMock).should().deleteRuleById(eq(FOLDER_NODE_ID), eq(RULE_SET_ID), eq(RULE_ID)); + then(rulesMock).shouldHaveNoMoreInteractions(); + } +} diff --git a/repository/pom.xml b/repository/pom.xml index cd08335117..86b4d41763 100644 --- a/repository/pom.xml +++ b/repository/pom.xml @@ -732,6 +732,11 @@ junit test + + org.assertj + assertj-core + test + org.alfresco alfresco-transform-model 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 0a108ab7e7..f1f85180b8 100644 --- a/repository/src/main/java/org/alfresco/repo/rule/RuleServiceImpl.java +++ b/repository/src/main/java/org/alfresco/repo/rule/RuleServiceImpl.java @@ -745,62 +745,63 @@ public class RuleServiceImpl } @Override - public void saveRule(NodeRef nodeRef, Rule rule) + public Rule saveRule(NodeRef nodeRef, Rule rule) { checkForLinkedRules(nodeRef); - if (this.permissionService.hasPermission(nodeRef, PermissionService.CHANGE_PERMISSIONS) == AccessStatus.ALLOWED) - { - disableRules(); - try - { - if (this.nodeService.exists(nodeRef) == false) - { - throw new RuleServiceException("The node does not exist."); - } - - NodeRef ruleNodeRef = rule.getNodeRef(); - if (ruleNodeRef == null) - { - if (this.nodeService.hasAspect(nodeRef, RuleModel.ASPECT_RULES) == false) - { - // Add the actionable aspect - this.nodeService.addAspect(nodeRef, RuleModel.ASPECT_RULES, null); - } - - // Create the action node - ruleNodeRef = this.nodeService.createNode( - getSavedRuleFolderRef(nodeRef), - ContentModel.ASSOC_CONTAINS, - QName.createQName(RuleModel.RULE_MODEL_URI, ASSOC_NAME_RULES_PREFIX + GUID.generate()), - RuleModel.TYPE_RULE).getChildRef(); - - // Set the rule node reference and the owning node reference - rule.setNodeRef(ruleNodeRef); - } - - // Update the properties of the rule - this.nodeService.setProperty(ruleNodeRef, ContentModel.PROP_TITLE, rule.getTitle()); - this.nodeService.setProperty(ruleNodeRef, ContentModel.PROP_DESCRIPTION, rule.getDescription()); - this.nodeService.setProperty(ruleNodeRef, RuleModel.PROP_RULE_TYPE, (Serializable)rule.getRuleTypes()); - this.nodeService.setProperty(ruleNodeRef, RuleModel.PROP_APPLY_TO_CHILDREN, rule.isAppliedToChildren()); - this.nodeService.setProperty(ruleNodeRef, RuleModel.PROP_EXECUTE_ASYNC, rule.getExecuteAsynchronously()); - this.nodeService.setProperty(ruleNodeRef, RuleModel.PROP_DISABLED, rule.getRuleDisabled()); - - // Save the rule's action - saveAction(ruleNodeRef, rule); - } - finally - { - enableRules(); - // Drop the rules from the cache - nodeRulesCache.remove(nodeRef); - } - } - else + if (this.permissionService.hasPermission(nodeRef, PermissionService.CHANGE_PERMISSIONS) != AccessStatus.ALLOWED) { throw new RuleServiceException("Insufficient permissions to save a rule."); } + + disableRules(); + try + { + if (this.nodeService.exists(nodeRef) == false) + { + throw new RuleServiceException("The node does not exist."); + } + + NodeRef ruleNodeRef = rule.getNodeRef(); + if (ruleNodeRef == null) + { + if (this.nodeService.hasAspect(nodeRef, RuleModel.ASPECT_RULES) == false) + { + // Add the actionable aspect + this.nodeService.addAspect(nodeRef, RuleModel.ASPECT_RULES, null); + } + + // Create the action node + ruleNodeRef = this.nodeService.createNode( + getSavedRuleFolderRef(nodeRef), + ContentModel.ASSOC_CONTAINS, + QName.createQName(RuleModel.RULE_MODEL_URI, ASSOC_NAME_RULES_PREFIX + GUID.generate()), + RuleModel.TYPE_RULE).getChildRef(); + + // Set the rule node reference and the owning node reference + rule.setNodeRef(ruleNodeRef); + } + + // Update the properties of the rule + String title = rule.getTitle(); + ParameterCheck.mandatoryString("Rule name", title); + this.nodeService.setProperty(ruleNodeRef, ContentModel.PROP_TITLE, title); + this.nodeService.setProperty(ruleNodeRef, ContentModel.PROP_DESCRIPTION, rule.getDescription()); + this.nodeService.setProperty(ruleNodeRef, RuleModel.PROP_RULE_TYPE, (Serializable)rule.getRuleTypes()); + this.nodeService.setProperty(ruleNodeRef, RuleModel.PROP_APPLY_TO_CHILDREN, rule.isAppliedToChildren()); + this.nodeService.setProperty(ruleNodeRef, RuleModel.PROP_EXECUTE_ASYNC, rule.getExecuteAsynchronously()); + this.nodeService.setProperty(ruleNodeRef, RuleModel.PROP_DISABLED, rule.getRuleDisabled()); + + // Save the rule's action + saveAction(ruleNodeRef, rule); + } + finally + { + enableRules(); + // Drop the rules from the cache + nodeRulesCache.remove(nodeRef); + } + return rule; } @Override @@ -901,10 +902,9 @@ public class RuleServiceImpl { checkForLinkedRules(nodeRef); - if (this.permissionService.hasPermission(nodeRef, PermissionService.CHANGE_PERMISSIONS) == AccessStatus.ALLOWED) + if (permissionService.hasPermission(nodeRef, PermissionService.CHANGE_PERMISSIONS) == AccessStatus.ALLOWED) { - if (this.nodeService.exists(nodeRef) == true && - this.nodeService.hasAspect(nodeRef, RuleModel.ASPECT_RULES) == true) + if (nodeService.exists(nodeRef) && nodeService.hasAspect(nodeRef, RuleModel.ASPECT_RULES)) { disableRules(nodeRef); try @@ -912,7 +912,7 @@ public class RuleServiceImpl NodeRef ruleNodeRef = rule.getNodeRef(); if (ruleNodeRef != null) { - this.nodeService.removeChild(getSavedRuleFolderRef(nodeRef), ruleNodeRef); + nodeService.removeChild(getSavedRuleFolderRef(nodeRef), ruleNodeRef); } } finally @@ -935,7 +935,7 @@ public class RuleServiceImpl } } - this.nodeService.removeAspect(nodeRef, RuleModel.ASPECT_RULES); + nodeService.removeAspect(nodeRef, RuleModel.ASPECT_RULES); } } // Drop the rules from the cache @@ -945,8 +945,8 @@ public class RuleServiceImpl { throw new RuleServiceException("Insufficient permissions to remove a rule."); } - } - + } + /** * Checks if rules are linked and throws an exception if they are. * 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 36e09cdd74..91c11b3337 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 @@ -235,8 +235,8 @@ public interface RuleService * @param rule Rule */ @Auditable(parameters = {"nodeRef", "rule"}) - public void saveRule(NodeRef nodeRef, Rule rule); - + public Rule saveRule(NodeRef nodeRef, Rule rule); + /** * * @param nodeRef NodeRef diff --git a/repository/src/test/java/org/alfresco/AllUnitTestsSuite.java b/repository/src/test/java/org/alfresco/AllUnitTestsSuite.java index 54924edee0..7c26b9959f 100644 --- a/repository/src/test/java/org/alfresco/AllUnitTestsSuite.java +++ b/repository/src/test/java/org/alfresco/AllUnitTestsSuite.java @@ -73,6 +73,7 @@ import org.junit.runners.Suite; org.alfresco.repo.rendition.RenditionNodeManagerTest.class, org.alfresco.repo.rendition.RenditionServiceImplTest.class, org.alfresco.repo.replication.ReplicationServiceImplTest.class, + org.alfresco.repo.rule.RuleServiceImplUnitTest.class, org.alfresco.repo.service.StoreRedirectorProxyFactoryTest.class, org.alfresco.repo.site.RoleComparatorImplTest.class, org.alfresco.repo.tenant.MultiTAdminServiceImplTest.class, diff --git a/repository/src/test/java/org/alfresco/repo/copy/CopyServiceImplTest.java b/repository/src/test/java/org/alfresco/repo/copy/CopyServiceImplTest.java index 3166d3d920..90c5874ad0 100644 --- a/repository/src/test/java/org/alfresco/repo/copy/CopyServiceImplTest.java +++ b/repository/src/test/java/org/alfresco/repo/copy/CopyServiceImplTest.java @@ -737,6 +737,7 @@ public class CopyServiceImplTest extends TestCase // Create a new rule and add it to the source noderef Rule rule = new Rule(); rule.setRuleType(RuleType.INBOUND); + rule.setTitle("Rule name"); Map props = new HashMap(1); props.put(AddFeaturesActionExecuter.PARAM_ASPECT_NAME, ContentModel.ASPECT_VERSIONABLE); @@ -1010,7 +1011,8 @@ public class CopyServiceImplTest extends TestCase Map params = new HashMap(1); params.put(MoveActionExecuter.PARAM_DESTINATION_FOLDER, nodeTwo); Rule rule = new Rule(); - rule.setRuleType(RuleType.INBOUND); + rule.setRuleType(RuleType.INBOUND); + rule.setTitle("Rule name"); Action action = actionService.createAction(CopyActionExecuter.NAME, params); ActionCondition condition = actionService.createActionCondition(NoConditionEvaluator.NAME); action.addActionCondition(condition); diff --git a/repository/src/test/java/org/alfresco/repo/rule/RuleServiceCoverageTest.java b/repository/src/test/java/org/alfresco/repo/rule/RuleServiceCoverageTest.java index c4bcad5874..27ee3b35d6 100644 --- a/repository/src/test/java/org/alfresco/repo/rule/RuleServiceCoverageTest.java +++ b/repository/src/test/java/org/alfresco/repo/rule/RuleServiceCoverageTest.java @@ -99,6 +99,7 @@ import org.alfresco.service.namespace.RegexQNamePattern; import org.alfresco.service.transaction.TransactionService; import org.alfresco.test_category.OwnJVMTestsCategory; import org.alfresco.util.ApplicationContextHelper; +import org.alfresco.util.GUID; import org.junit.experimental.categories.Category; import org.springframework.context.ApplicationContext; import org.springframework.util.StopWatch; @@ -194,25 +195,26 @@ public class RuleServiceCoverageTest extends TestCase ContentModel.ASSOC_CHILDREN, ContentModel.TYPE_CONTAINER).getChildRef(); } - - private Rule createRule( - String ruleTypeName, - String actionName, - Map actionParams, - String conditionName, - Map conditionParams) - { - Rule rule = new Rule(); - rule.setRuleType(ruleTypeName); - - Action action = this.actionService.createAction(actionName, actionParams); + + private Rule createRule( + String ruleTypeName, + String actionName, + Map actionParams, + String conditionName, + Map conditionParams) + { + Rule rule = new Rule(); + rule.setTitle(GUID.generate()); + rule.setRuleType(ruleTypeName); + + Action action = this.actionService.createAction(actionName, actionParams); ActionCondition condition = this.actionService.createActionCondition(conditionName, conditionParams); action.addActionCondition(condition); - rule.setAction(action); - + rule.setAction(action); + return rule; - } - + } + /** * Create the categories used in the tests */ diff --git a/repository/src/test/java/org/alfresco/repo/rule/RuleServiceImplUnitTest.java b/repository/src/test/java/org/alfresco/repo/rule/RuleServiceImplUnitTest.java new file mode 100644 index 0000000000..95ad87bbe4 --- /dev/null +++ b/repository/src/test/java/org/alfresco/repo/rule/RuleServiceImplUnitTest.java @@ -0,0 +1,190 @@ +/* + * #%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.repo.rule; + +import static org.alfresco.model.ContentModel.ASSOC_CONTAINS; +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.TYPE_RULE; +import static org.alfresco.service.cmr.security.AccessStatus.ALLOWED; +import static org.alfresco.service.cmr.security.AccessStatus.DENIED; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.nullable; +import static org.mockito.BDDMockito.then; +import static org.mockito.Mockito.atLeastOnce; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.when; +import static org.mockito.MockitoAnnotations.openMocks; + +import java.io.Serializable; +import java.util.List; + +import org.alfresco.repo.action.RuntimeActionService; +import org.alfresco.repo.cache.SimpleCache; +import org.alfresco.service.cmr.action.Action; +import org.alfresco.service.cmr.action.ActionServiceException; +import org.alfresco.service.cmr.repository.ChildAssociationRef; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.rule.Rule; +import org.alfresco.service.cmr.rule.RuleService; +import org.alfresco.service.cmr.rule.RuleServiceException; +import org.alfresco.service.cmr.security.PermissionService; +import org.alfresco.service.namespace.QName; +import org.junit.Before; +import org.junit.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; + +/** Unit tests for {@link RuleServiceImpl}. */ +public class RuleServiceImplUnitTest +{ + private static final NodeRef FOLDER_NODE = new NodeRef("folder://node/"); + private static final NodeRef RULE_SET_NODE = new NodeRef("rule://set/node"); + private static final NodeRef RULE_NODE = new NodeRef("rule://node/"); + private static final NodeRef ACTION_NODE = new NodeRef("action://node/"); + @InjectMocks + private RuleService ruleService = new RuleServiceImpl(); + @Mock + private NodeService nodeService; + @Mock + private PermissionService permissionService; + @Mock + private SimpleCache nodeRulesCache; + @Mock + private NodeService runtimeNodeService; + @Mock + private RuntimeActionService runtimeActionService; + @Mock + private Rule mockRule; + @Mock + private Action mockAction; + + @Before + public void setUp() + { + openMocks(this); + } + + @Test + public void saveRule() + { + when(permissionService.hasPermission(FOLDER_NODE, PermissionService.CHANGE_PERMISSIONS)).thenReturn(ALLOWED); + when(nodeService.exists(FOLDER_NODE)).thenReturn(true); + ChildAssociationRef ruleSet = mock(ChildAssociationRef.class); + when(ruleSet.getChildRef()).thenReturn(RULE_SET_NODE); + when(runtimeNodeService.getChildAssocs(FOLDER_NODE, ASSOC_RULE_FOLDER, ASSOC_RULE_FOLDER)).thenReturn(List.of(ruleSet)); + ChildAssociationRef ruleAssociation = mock(ChildAssociationRef.class); + when(ruleAssociation.getChildRef()).thenReturn(RULE_NODE); + when(nodeService.createNode(eq(RULE_SET_NODE), eq(ASSOC_CONTAINS), any(QName.class), eq(TYPE_RULE))).thenReturn(ruleAssociation); + // Set the rule title and action. + when(mockRule.getTitle()).thenReturn("Rule title"); + when(mockRule.getAction()).thenReturn(mockAction); + when(runtimeActionService.createActionNodeRef(mockAction, RULE_NODE, ASSOC_ACTION, ASSOC_ACTION)).thenReturn(ACTION_NODE); + + // Call the method under test. + ruleService.saveRule(FOLDER_NODE, mockRule); + + then(nodeService).should(times(2)).hasAspect(FOLDER_NODE, RuleModel.ASPECT_RULES); + then(nodeService).should().exists(FOLDER_NODE); + then(nodeService).should().addAspect(FOLDER_NODE, RuleModel.ASPECT_RULES, null); + then(nodeService).should().createNode(eq(RULE_SET_NODE), eq(ASSOC_CONTAINS), any(QName.class), eq(TYPE_RULE)); + then(nodeService).should(atLeastOnce()).setProperty(eq(RULE_NODE), any(QName.class), nullable(Serializable.class)); + then(nodeService).should().getChildAssocs(RULE_NODE, RuleModel.ASSOC_ACTION, RuleModel.ASSOC_ACTION); + verifyNoMoreInteractions(nodeService); + } + + @Test + public void saveRule_missingAction() + { + when(permissionService.hasPermission(FOLDER_NODE, PermissionService.CHANGE_PERMISSIONS)).thenReturn(ALLOWED); + when(nodeService.exists(FOLDER_NODE)).thenReturn(true); + ChildAssociationRef ruleSet = mock(ChildAssociationRef.class); + when(ruleSet.getChildRef()).thenReturn(RULE_SET_NODE); + when(runtimeNodeService.getChildAssocs(FOLDER_NODE, ASSOC_RULE_FOLDER, ASSOC_RULE_FOLDER)).thenReturn(List.of(ruleSet)); + ChildAssociationRef ruleAssociation = mock(ChildAssociationRef.class); + when(nodeService.createNode(eq(RULE_SET_NODE), eq(ASSOC_CONTAINS), any(QName.class), eq(TYPE_RULE))).thenReturn(ruleAssociation); + // Set the title and no action for the rule. + when(mockRule.getTitle()).thenReturn("Rule title"); + when(mockRule.getAction()).thenReturn(null); + + // Call the method under test. + assertThatExceptionOfType(RuleServiceException.class).isThrownBy(() -> ruleService.saveRule(FOLDER_NODE, mockRule)); + } + + @Test + public void saveRule_missingTitle() + { + when(permissionService.hasPermission(FOLDER_NODE, PermissionService.CHANGE_PERMISSIONS)).thenReturn(ALLOWED); + when(nodeService.exists(FOLDER_NODE)).thenReturn(true); + ChildAssociationRef ruleSet = mock(ChildAssociationRef.class); + when(ruleSet.getChildRef()).thenReturn(RULE_SET_NODE); + when(runtimeNodeService.getChildAssocs(FOLDER_NODE, ASSOC_RULE_FOLDER, ASSOC_RULE_FOLDER)).thenReturn(List.of(ruleSet)); + ChildAssociationRef ruleAssociation = mock(ChildAssociationRef.class); + when(nodeService.createNode(eq(RULE_SET_NODE), eq(ASSOC_CONTAINS), any(QName.class), eq(TYPE_RULE))).thenReturn(ruleAssociation); + // The rule has an empty title. + when(mockRule.getTitle()).thenReturn(""); + + assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> ruleService.saveRule(FOLDER_NODE, mockRule)); + } + + @Test + public void saveRule_errorIfFolderHasMultipleRuleSets() + { + when(permissionService.hasPermission(FOLDER_NODE, PermissionService.CHANGE_PERMISSIONS)).thenReturn(ALLOWED); + when(nodeService.exists(FOLDER_NODE)).thenReturn(true); + // Simulate a folder node with several rule sets. + ChildAssociationRef childA = mock(ChildAssociationRef.class); + ChildAssociationRef childB = mock(ChildAssociationRef.class); + when(runtimeNodeService.getChildAssocs( + FOLDER_NODE, + ASSOC_RULE_FOLDER, + ASSOC_RULE_FOLDER)).thenReturn(List.of(childA, childB)); + + assertThatExceptionOfType(ActionServiceException.class).isThrownBy(() -> ruleService.saveRule(FOLDER_NODE, mockRule)); + } + + @Test + public void saveRule_nodeDoesNotExist() + { + when(permissionService.hasPermission(FOLDER_NODE, PermissionService.CHANGE_PERMISSIONS)).thenReturn(ALLOWED); + when(nodeService.exists(FOLDER_NODE)).thenReturn(false); + + assertThatExceptionOfType(RuleServiceException.class).isThrownBy(() -> ruleService.saveRule(FOLDER_NODE, mockRule)); + } + + @Test + public void saveRule_accessDenied() + { + when(permissionService.hasPermission(FOLDER_NODE, PermissionService.CHANGE_PERMISSIONS)).thenReturn(DENIED); + + assertThatExceptionOfType(RuleServiceException.class).isThrownBy(() -> ruleService.saveRule(FOLDER_NODE, mockRule)); + } +} diff --git a/repository/src/test/java/org/alfresco/repo/thumbnail/ThumbnailServiceImplTest.java b/repository/src/test/java/org/alfresco/repo/thumbnail/ThumbnailServiceImplTest.java index eb40c6cf1c..7c18e6048d 100644 --- a/repository/src/test/java/org/alfresco/repo/thumbnail/ThumbnailServiceImplTest.java +++ b/repository/src/test/java/org/alfresco/repo/thumbnail/ThumbnailServiceImplTest.java @@ -495,6 +495,7 @@ public class ThumbnailServiceImplTest extends BaseAlfrescoSpringTest params.put("aspect-name", ContentModel.ASPECT_GEN_CLASSIFIABLE); Rule rule = new Rule(); rule.setRuleType(RuleType.INBOUND); + rule.setTitle("Rule name"); Action action = this.actionService.createAction(AddFeaturesActionExecuter.NAME, params); ActionCondition condition = this.actionService.createActionCondition(NoConditionEvaluator.NAME, null); action.addActionCondition(condition);