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 0222790c0c..04d3710e4b 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 @@ -65,10 +65,11 @@ public class RulesImpl implements Rules public CollectionWithPagingInfo getRules(final String folderNodeId, final String ruleSetId, final Paging paging) { final NodeRef folderNodeRef = validateFolderNode(folderNodeId, false); - validateRuleSetNode(ruleSetId, folderNodeRef); + final NodeRef ruleSetNodeRef = validateRuleSetNode(ruleSetId, folderNodeRef); + final boolean isShared = isRuleSetNotNullAndShared(ruleSetNodeRef); final List rules = ruleService.getRules(folderNodeRef).stream() - .map(Rule::from) + .map(ruleModel -> Rule.from(ruleModel, isShared)) .collect(Collectors.toList()); return ListPage.of(rules, paging); @@ -81,7 +82,7 @@ public class RulesImpl implements Rules final NodeRef ruleSetNodeRef = validateRuleSetNode(ruleSetId, folderNodeRef); final NodeRef ruleNodeRef = validateRuleNode(ruleId, ruleSetNodeRef); - return Rule.from(ruleService.getRule(ruleNodeRef)); + return Rule.from(ruleService.getRule(ruleNodeRef), isRuleSetNotNullAndShared(ruleSetNodeRef)); } @Override @@ -89,15 +90,12 @@ public class RulesImpl implements Rules { final NodeRef folderNodeRef = validateFolderNode(folderNodeId, true); // Don't validate the ruleset node if -default- is passed since we may need to create it. - if (RuleSet.isNotDefaultId(ruleSetId)) - { - validateRuleSetNode(ruleSetId, folderNodeRef); - } + final NodeRef ruleSetNodeRef = (RuleSet.isNotDefaultId(ruleSetId)) ? validateRuleSetNode(ruleSetId, folderNodeRef) : null; return rules.stream() .map(rule -> rule.toServiceModel(nodes)) .map(rule -> ruleService.saveRule(folderNodeRef, rule)) - .map(Rule::from) + .map(rule -> Rule.from(rule, isRuleSetNotNullAndShared(ruleSetNodeRef, folderNodeRef))) .collect(Collectors.toList()); } @@ -207,11 +205,30 @@ public class RulesImpl implements Rules return nodeRef; } - private void verifyNodeType(final NodeRef nodeRef, final QName expectedType, final String expectedTypeName) { + private void verifyNodeType(final NodeRef nodeRef, final QName expectedType, final String expectedTypeName) + { final Set expectedTypes = Set.of(expectedType); if (!nodes.nodeMatches(nodeRef, expectedTypes, null)) { final String expectedTypeLocalName = (expectedTypeName != null)? expectedTypeName : expectedType.getLocalName(); throw new InvalidArgumentException(String.format("NodeId of a %s is expected!", expectedTypeLocalName)); } } + + private boolean isRuleSetNotNullAndShared(final NodeRef ruleSetNodeRef, final NodeRef folderNodeRef) + { + if (ruleSetNodeRef == null && folderNodeRef != null) + { + final NodeRef ruleSetNode = ruleService.getRuleSetNode(folderNodeRef); + return ruleSetNode != null && ruleService.isRuleSetShared(ruleSetNode); + } + else + { + return isRuleSetNotNullAndShared(ruleSetNodeRef); + } + } + + private boolean isRuleSetNotNullAndShared(final NodeRef ruleSetNodeRef) + { + return ruleSetNodeRef != null && ruleService.isRuleSetShared(ruleSetNodeRef); + } } 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 9dad8b55e1..6f7f2c4ef0 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 @@ -31,14 +31,15 @@ import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.stream.Collectors; import org.alfresco.model.ContentModel; import org.alfresco.repo.action.ActionImpl; +import org.alfresco.repo.action.executer.ScriptActionExecuter; 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.Action; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.util.GUID; @@ -47,16 +48,48 @@ public class Rule { private String id; private String name; + private String description; + private boolean enabled; + private boolean cascade; + private boolean asynchronous; + private boolean shared; + private String errorScript; + private List triggers; - public static Rule from(final org.alfresco.service.cmr.rule.Rule ruleModel) { - if (ruleModel == null) { + /** + * Converts service POJO rule to REST model rule. + * + * @param ruleModel - {@link org.alfresco.service.cmr.rule.Rule} service POJO + * @return {@link Rule} REST model + */ + public static Rule from(final org.alfresco.service.cmr.rule.Rule ruleModel, final boolean shared) + { + if (ruleModel == null) + { return null; } - return builder() - .setId(ruleModel.getNodeRef().getId()) - .setName(ruleModel.getTitle()) - .createRule(); + final Rule.Builder builder = builder() + .name(ruleModel.getTitle()) + .description(ruleModel.getDescription()) + .enabled(!ruleModel.getRuleDisabled()) + .cascade(ruleModel.isAppliedToChildren()) + .asynchronous(ruleModel.getExecuteAsynchronously()) + .shared(shared); + + if (ruleModel.getNodeRef() != null) { + builder.id(ruleModel.getNodeRef().getId()); + } + if (ruleModel.getRuleTypes() != null) + { + builder.triggers(ruleModel.getRuleTypes().stream().map(RuleTrigger::of).collect(Collectors.toList())); + } + if (ruleModel.getAction() != null && ruleModel.getAction().getCompensatingAction() != null && ruleModel.getAction().getCompensatingAction().getParameterValue(ScriptActionExecuter.PARAM_SCRIPTREF) != null) + { + builder.errorScript(ruleModel.getAction().getCompensatingAction().getParameterValue(ScriptActionExecuter.PARAM_SCRIPTREF).toString()); + } + + return builder.create(); } /** @@ -79,7 +112,7 @@ public class Rule 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); + org.alfresco.service.cmr.action.Action action = new ActionImpl(null, GUID.generate(), SetPropertyValueActionExecuter.NAME, parameters); ruleModel.setAction(action); return ruleModel; @@ -106,68 +139,190 @@ 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 String getDescription() + { + return description; + } + + public void setDescription(String description) + { + this.description = description; + } + + public boolean isEnabled() + { + return enabled; + } + + public void setEnabled(boolean enabled) + { + this.enabled = enabled; + } + + public boolean isCascade() + { + return cascade; + } + + public void setCascade(boolean cascade) + { + this.cascade = cascade; + } + + public boolean isAsynchronous() + { + return asynchronous; + } + + public void setAsynchronous(boolean asynchronous) + { + this.asynchronous = asynchronous; + } + + public String getErrorScript() + { + return errorScript; + } + + public void setErrorScript(String errorScript) + { + this.errorScript = errorScript; + } + + public boolean isShared() + { + return shared; + } + + public void setShared(boolean shared) + { + this.shared = shared; + } + + public List getTriggers() + { + return triggers; + } + + public void setTriggers(List triggers) + { + this.triggers = triggers; + } + public List getActions() { return Collections.emptyList(); } + @Override + public String toString() + { + return "Rule{" + "id='" + id + '\'' + ", name='" + name + '\'' + ", description='" + description + '\'' + ", enabled=" + enabled + ", cascade=" + cascade + + ", asynchronous=" + asynchronous + ", shared=" + shared + ", errorScript='" + errorScript + '\'' + ", triggers=" + triggers + '}'; + } + @Override public boolean equals(Object o) { if (this == o) - { return true; - } - if (!(o instanceof Rule)) - { + if (o == null || getClass() != o.getClass()) return false; - } Rule rule = (Rule) o; - return Objects.equals(id, rule.id) && - Objects.equals(name, rule.name); + return enabled == rule.enabled && cascade == rule.cascade && asynchronous == rule.asynchronous && shared == rule.shared && Objects.equals(id, rule.id) && Objects.equals( + name, rule.name) && Objects.equals(description, rule.description) && Objects.equals(errorScript, rule.errorScript) && Objects.equals(triggers, rule.triggers); } @Override public int hashCode() { - return Objects.hash(id, name); + return Objects.hash(id, name, description, enabled, cascade, asynchronous, shared, errorScript, triggers); } - @Override - public String toString() + public static Builder builder() { - return "Rule{" + "id='" + id + '\'' + ", name='" + name + '\'' + '}'; - } - - public static RuleBuilder builder() - { - return new RuleBuilder(); + return new Builder(); } /** Builder class. */ - public static class RuleBuilder + public static class Builder { private String id; private String name; + private String description; + private boolean enabled; + private boolean cascade; + private boolean asynchronous; + private boolean shared; + private String errorScript; + private List triggers; - public RuleBuilder setId(String id) + public Builder id(String id) { this.id = id; return this; } - public RuleBuilder setName(String name) + public Builder name(String name) { this.name = name; return this; } - public Rule createRule() + public Builder description(String description) + { + this.description = description; + return this; + } + + public Builder enabled(boolean enabled) + { + this.enabled = enabled; + return this; + } + + public Builder cascade(boolean cascade) + { + this.cascade = cascade; + return this; + } + + public Builder asynchronous(boolean asynchronous) + { + this.asynchronous = asynchronous; + return this; + } + + public Builder shared(boolean shared) + { + this.shared = shared; + return this; + } + + public Builder errorScript(String errorScript) + { + this.errorScript = errorScript; + return this; + } + + public Builder triggers(List triggers) + { + this.triggers = triggers; + return this; + } + + public Rule create() { Rule rule = new Rule(); rule.setId(id); rule.setName(name); + rule.setDescription(description); + rule.setEnabled(enabled); + rule.setCascade(cascade); + rule.setAsynchronous(asynchronous); + rule.setShared(shared); + rule.setErrorScript(errorScript); + rule.setTriggers(triggers); 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 87860356b6..076e0b9c81 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 @@ -26,6 +26,8 @@ package org.alfresco.rest.api.model.rules; +import java.util.Objects; + import org.alfresco.service.Experimental; @Experimental @@ -37,10 +39,9 @@ public class RuleSet public static RuleSet of(String id) { - final RuleSet ruleSet = new RuleSet(); - ruleSet.id = id; - - return ruleSet; + return builder() + .id(id) + .create(); } public boolean isNotDefaultId() { @@ -74,4 +75,44 @@ public class RuleSet { return "RuleSet{" + "id='" + id + '\'' + '}'; } + + @Override + public boolean equals(Object o) + { + if (this == o) + return true; + if (o == null || getClass() != o.getClass()) + return false; + RuleSet ruleSet = (RuleSet) o; + return Objects.equals(id, ruleSet.id); + } + + @Override + public int hashCode() + { + return Objects.hash(id); + } + + public static Builder builder() + { + return new Builder(); + } + + public static class Builder + { + private String id; + + public Builder id(String id) + { + this.id = id; + return this; + } + + public RuleSet create() + { + final RuleSet ruleSet = new RuleSet(); + ruleSet.setId(id); + return ruleSet; + } + } } diff --git a/remote-api/src/main/java/org/alfresco/rest/api/model/rules/RuleTrigger.java b/remote-api/src/main/java/org/alfresco/rest/api/model/rules/RuleTrigger.java new file mode 100644 index 0000000000..eab17ba0e5 --- /dev/null +++ b/remote-api/src/main/java/org/alfresco/rest/api/model/rules/RuleTrigger.java @@ -0,0 +1,61 @@ +/* + * #%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 org.alfresco.service.Experimental; + +@Experimental +public enum RuleTrigger +{ + INBOUND("inbound"), + UPDATE("update"), + OUTBOUND("outbound"); + + RuleTrigger(String value) + { + this.value = value; + } + + private final String value; + + public String getValue() + { + return value; + } + + public static RuleTrigger of(final String value) + { + for (RuleTrigger ruleTrigger : values()) + { + if (ruleTrigger.value.equals(value)) { + return ruleTrigger; + } + } + + return null; + } +} diff --git a/remote-api/src/test/java/org/alfresco/AppContextExtraTestSuite.java b/remote-api/src/test/java/org/alfresco/AppContextExtraTestSuite.java index ddd6508f0f..719c2ea7d4 100644 --- a/remote-api/src/test/java/org/alfresco/AppContextExtraTestSuite.java +++ b/remote-api/src/test/java/org/alfresco/AppContextExtraTestSuite.java @@ -48,9 +48,8 @@ import org.junit.runners.Suite; org.alfresco.repo.webdav.RenameShuffleDetectionTest.class, org.alfresco.repo.webdav.WebDAVHelperTest.class, org.alfresco.repo.webdav.WebDAVLockServiceImplTest.class, + org.alfresco.rest.api.RulesUnitTests.class, org.alfresco.rest.api.impl.ContentStorageInformationImplTest.class, - org.alfresco.rest.api.impl.RulesImplTest.class, - org.alfresco.rest.api.nodes.NodeRulesRelationTest.class, org.alfresco.rest.api.nodes.NodeStorageInfoRelationTest.class, org.alfresco.rest.api.search.ResultMapperTests.class, org.alfresco.rest.api.search.SearchApiWebscriptTests.class, diff --git a/remote-api/src/test/java/org/alfresco/rest/api/RulesUnitTests.java b/remote-api/src/test/java/org/alfresco/rest/api/RulesUnitTests.java new file mode 100644 index 0000000000..3c66ac386b --- /dev/null +++ b/remote-api/src/test/java/org/alfresco/rest/api/RulesUnitTests.java @@ -0,0 +1,45 @@ +/* + * #%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; + +import org.alfresco.rest.api.impl.RulesImplTest; +import org.alfresco.rest.api.model.rules.RuleTest; +import org.alfresco.rest.api.nodes.NodeRulesRelationTest; +import org.alfresco.service.Experimental; +import org.junit.runner.RunWith; +import org.junit.runners.Suite; + +@Experimental +@RunWith(Suite.class) +@Suite.SuiteClasses({ + NodeRulesRelationTest.class, + RulesImplTest.class, + RuleTest.class +}) +public class RulesUnitTests +{ +} 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 bfffd2436f..a62ceef3e6 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 @@ -40,12 +40,14 @@ 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 static org.mockito.Mockito.times; import java.util.ArrayList; import java.util.Collection; import java.util.List; import junit.framework.TestCase; +import org.alfresco.repo.action.ActionImpl; import org.alfresco.rest.api.Nodes; import org.alfresco.rest.api.model.rules.Rule; import org.alfresco.rest.framework.core.exceptions.EntityNotFoundException; @@ -54,6 +56,7 @@ import org.alfresco.rest.framework.core.exceptions.PermissionDeniedException; 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.Action; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.StoreRef; import org.alfresco.service.cmr.rule.RuleService; @@ -77,6 +80,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 Action action = new ActionImpl(folderNodeRef, "actionId", "actionDefinitionName"); private static final String RULE_NAME = "Rule name"; @Mock @@ -121,6 +125,7 @@ public class RulesImplTest extends TestCase then(permissionServiceMock).should().hasReadPermission(folderNodeRef); then(permissionServiceMock).shouldHaveNoMoreInteractions(); then(ruleServiceMock).should().isRuleSetAssociatedWithFolder(ruleSetNodeRef, folderNodeRef); + then(ruleServiceMock).should().isRuleSetShared(ruleSetNodeRef); then(ruleServiceMock).should().getRules(folderNodeRef); then(ruleServiceMock).shouldHaveNoMoreInteractions(); assertThat(rulesPage) @@ -138,6 +143,7 @@ public class RulesImplTest extends TestCase @Test public void testGetRulesForDefaultRuleSet() { + given(ruleServiceMock.getRuleSetNode(any())).willReturn(ruleSetNodeRef); given(ruleServiceMock.getRules(any())).willReturn(List.of(createRule(RULE_ID))); // when @@ -149,6 +155,7 @@ public class RulesImplTest extends TestCase then(permissionServiceMock).should().hasReadPermission(folderNodeRef); then(permissionServiceMock).shouldHaveNoMoreInteractions(); then(ruleServiceMock).should().getRuleSetNode(folderNodeRef); + then(ruleServiceMock).should().isRuleSetShared(ruleSetNodeRef); then(ruleServiceMock).should().getRules(folderNodeRef); then(ruleServiceMock).shouldHaveNoMoreInteractions(); assertThat(rulesPage) @@ -235,6 +242,7 @@ public class RulesImplTest extends TestCase then(permissionServiceMock).shouldHaveNoMoreInteractions(); then(ruleServiceMock).should().isRuleSetAssociatedWithFolder(ruleSetNodeRef, folderNodeRef); then(ruleServiceMock).should().isRuleAssociatedWithRuleSet(ruleNodeRef, ruleSetNodeRef); + then(ruleServiceMock).should().isRuleSetShared(ruleSetNodeRef); then(ruleServiceMock).should().getRule(ruleNodeRef); then(ruleServiceMock).shouldHaveNoMoreInteractions(); assertThat(rule) @@ -264,7 +272,8 @@ public class RulesImplTest extends TestCase then(permissionServiceMock).shouldHaveNoMoreInteractions(); then(ruleServiceMock).should().getRuleSetNode(folderNodeRef); then(ruleServiceMock).should().isRuleAssociatedWithRuleSet(ruleNodeRef, ruleSetNodeRef); - then(ruleServiceMock).should().getRule(eq(ruleNodeRef)); + then(ruleServiceMock).should().isRuleSetShared(ruleSetNodeRef); + then(ruleServiceMock).should().getRule(ruleNodeRef); then(ruleServiceMock).shouldHaveNoMoreInteractions(); assertThat(rule) .isNotNull() @@ -301,14 +310,16 @@ public class RulesImplTest extends TestCase 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); + given(serviceRule.getAction()).willReturn(action); // when List actual = rules.createRules(folderNodeRef.getId(), ruleSetNodeRef.getId(), ruleList); then(ruleServiceMock).should().isRuleSetAssociatedWithFolder(ruleSetNodeRef, folderNodeRef); + then(ruleServiceMock).should().isRuleSetShared(ruleSetNodeRef); then(ruleServiceMock).should().saveRule(folderNodeRef, ruleBody.toServiceModel(nodesMock)); then(ruleServiceMock).shouldHaveNoMoreInteractions(); - List expected = List.of(Rule.from(serviceRule)); + List expected = List.of(Rule.from(serviceRule, false)); assertThat(actual).isEqualTo(expected); } @@ -320,24 +331,27 @@ public class RulesImplTest extends TestCase 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); + given(ruleServiceMock.getRuleSetNode(any())).willReturn(ruleSetNodeRef); 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); + given(serviceRule.getAction()).willReturn(action); // when List actual = rules.createRules(folderNodeRef.getId(), DEFAULT_ID, ruleList); + then(ruleServiceMock).should().getRuleSetNode(folderNodeRef); + then(ruleServiceMock).should().isRuleSetShared(ruleSetNodeRef); then(ruleServiceMock).should().saveRule(folderNodeRef, ruleBody.toServiceModel(nodesMock)); then(ruleServiceMock).shouldHaveNoMoreInteractions(); - List expected = List.of(Rule.from(serviceRule)); + List expected = List.of(Rule.from(serviceRule, false)); assertThat(actual).isEqualTo(expected); } @Test public void testSaveRules_ruleSetNotAssociatedWithFolder() { - Rule rule = Rule.builder().setName(RULE_NAME) - .createRule(); + Rule rule = Rule.builder().name(RULE_NAME).create(); List ruleList = List.of(rule); given(ruleServiceMock.isRuleSetAssociatedWithFolder(ruleSetNodeRef, folderNodeRef)).willReturn(false); @@ -380,7 +394,8 @@ public class RulesImplTest extends TestCase 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)); + given(serviceRule.getAction()).willReturn(action); + expected.add(Rule.from(serviceRule, false)); } // when @@ -391,6 +406,7 @@ public class RulesImplTest extends TestCase { then(ruleServiceMock).should().saveRule(folderNodeRef, ruleBody.toServiceModel(nodesMock)); } + then(ruleServiceMock).should(times(ruleBodyList.size())).isRuleSetShared(ruleSetNodeRef); then(ruleServiceMock).shouldHaveNoMoreInteractions(); assertThat(actual).isEqualTo(expected); } @@ -552,8 +568,11 @@ public class RulesImplTest extends TestCase private static org.alfresco.service.cmr.rule.Rule createRule(final String id) { + final NodeRef nodeRef = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, id); final org.alfresco.service.cmr.rule.Rule rule = new org.alfresco.service.cmr.rule.Rule(); - rule.setNodeRef(new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, id)); + rule.setNodeRef(nodeRef); + rule.setRuleType("ruleType"); + rule.setAction(action); return rule; } diff --git a/remote-api/src/test/java/org/alfresco/rest/api/model/rules/RuleTest.java b/remote-api/src/test/java/org/alfresco/rest/api/model/rules/RuleTest.java new file mode 100644 index 0000000000..f282289fd7 --- /dev/null +++ b/remote-api/src/test/java/org/alfresco/rest/api/model/rules/RuleTest.java @@ -0,0 +1,116 @@ +/* + * #%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 static org.assertj.core.api.Assertions.assertThat; + +import java.util.List; + +import org.alfresco.repo.action.ActionConditionImpl; +import org.alfresco.repo.action.ActionImpl; +import org.alfresco.repo.action.executer.ScriptActionExecuter; +import org.alfresco.service.Experimental; +import org.alfresco.service.cmr.action.Action; +import org.alfresco.service.cmr.action.ActionCondition; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.StoreRef; +import org.alfresco.service.cmr.rule.RuleType; +import org.assertj.core.api.Condition; +import org.junit.Test; + +@Experimental +public class RuleTest +{ + private static final String RULE_ID = "fake-rule-id"; + private static final String RULE_NAME = "rule name"; + private static final String RULE_DESCRIPTION = "rule description"; + private static final boolean RULE_ENABLED = true; + private static final boolean RULE_CASCADE = true; + private static final boolean RULE_ASYNC = false; + private static final boolean RULE_SHARED = true; + private static final String ERROR_SCRIPT = "error-script-ref"; + + @Test + public void testFrom() + { + final org.alfresco.service.cmr.rule.Rule ruleModel = createRuleModel(); + final Rule expectedRule = createRuleWithDefaultValues(); + + // when + final Rule actualRule = Rule.from(ruleModel, RULE_SHARED); + + assertThat(actualRule).isNotNull().usingRecursiveComparison().isEqualTo(expectedRule); + + } + + @Test + public void testFromRuleModelWithNullValues() + { + final org.alfresco.service.cmr.rule.Rule ruleModel = new org.alfresco.service.cmr.rule.Rule(); + final Rule expectedRule = Rule.builder().enabled(true).create(); + + // when + final Rule actualRule = Rule.from(ruleModel, false); + + assertThat(actualRule).isNotNull().usingRecursiveComparison().isEqualTo(expectedRule); + + } + + private static org.alfresco.service.cmr.rule.Rule createRuleModel() { + final NodeRef nodeRef = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, RULE_ID); + final org.alfresco.service.cmr.rule.Rule ruleModel = new org.alfresco.service.cmr.rule.Rule(nodeRef); + ruleModel.setTitle(RULE_NAME); + ruleModel.setDescription(RULE_DESCRIPTION); + ruleModel.setRuleDisabled(!RULE_ENABLED); + ruleModel.applyToChildren(RULE_CASCADE); + ruleModel.setExecuteAsynchronously(RULE_ASYNC); + ruleModel.setRuleTypes(List.of(RuleType.INBOUND, RuleType.UPDATE)); + final Action compensatingAction = new ActionImpl(nodeRef, "compensatingActionId", "compensatingActionDefName"); + compensatingAction.setParameterValue(ScriptActionExecuter.PARAM_SCRIPTREF, ERROR_SCRIPT); + final ActionCondition actionCondition = new ActionConditionImpl("actionConditionId", "actionConditionDefName"); + final Action action = new ActionImpl(nodeRef, "actionId", "actionDefName"); + action.setCompensatingAction(compensatingAction); + action.addActionCondition(actionCondition); + ruleModel.setAction(action); + + return ruleModel; + } + + private static Rule createRuleWithDefaultValues() { + return Rule.builder() + .id(RULE_ID) + .name(RULE_NAME) + .description(RULE_DESCRIPTION) + .enabled(RULE_ENABLED) + .cascade(RULE_CASCADE) + .asynchronous(RULE_ASYNC) + .shared(RULE_SHARED) + .triggers(List.of(RuleTrigger.INBOUND, RuleTrigger.UPDATE)) + .errorScript(ERROR_SCRIPT) + .create(); + } +} \ No newline at end of file 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 f1f85180b8..0ffe7f7732 100644 --- a/repository/src/main/java/org/alfresco/repo/rule/RuleServiceImpl.java +++ b/repository/src/main/java/org/alfresco/repo/rule/RuleServiceImpl.java @@ -25,6 +25,15 @@ */ package org.alfresco.repo.rule; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + import org.alfresco.model.ContentModel; import org.alfresco.repo.action.ActionImpl; import org.alfresco.repo.action.ActionModel; @@ -65,15 +74,6 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.extensions.surf.util.ParameterCheck; -import java.io.Serializable; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - /** * Rule service implementation. *

@@ -1595,7 +1595,7 @@ public class RuleServiceImpl public List getLinkedFromRuleNodes(NodeRef nodeRef) { List result = new ArrayList(); - + if (nodeService.hasAspect(nodeRef, RuleModel.ASPECT_RULES) == true) { ChildAssociationRef assoc = getSavedRuleFolderAssoc(nodeRef); @@ -1616,13 +1616,9 @@ public class RuleServiceImpl @Override @Experimental - public NodeRef getRuleSetNode(final NodeRef folderNodeRef) { - return getPrimaryChildNode(folderNodeRef, RuleModel.ASSOC_RULE_FOLDER); - } - - private NodeRef getPrimaryChildNode(final NodeRef nodeRef, final QNamePattern associationType) { - return runtimeNodeService.getChildAssocs(nodeRef, associationType, associationType).stream() - .filter(ChildAssociationRef::isPrimary) + public NodeRef getRuleSetNode(final NodeRef folderNodeRef) + { + return runtimeNodeService.getChildAssocs(folderNodeRef, RuleModel.ASSOC_RULE_FOLDER, RuleModel.ASSOC_RULE_FOLDER).stream() .map(ChildAssociationRef::getChildRef) .findFirst() .orElse(null); @@ -1630,17 +1626,20 @@ public class RuleServiceImpl @Override @Experimental - public boolean isRuleSetAssociatedWithFolder(final NodeRef ruleSetNodeRef, final NodeRef folderNodeRef) { + public boolean isRuleSetAssociatedWithFolder(final NodeRef ruleSetNodeRef, final NodeRef folderNodeRef) + { return isChildOf(ruleSetNodeRef, RuleModel.ASSOC_RULE_FOLDER, folderNodeRef); } @Override @Experimental - public boolean isRuleAssociatedWithRuleSet(final NodeRef ruleNodeRef, final NodeRef ruleSetNodeRef) { + public boolean isRuleAssociatedWithRuleSet(final NodeRef ruleNodeRef, final NodeRef ruleSetNodeRef) + { return isChildOf(ruleNodeRef, null, ruleSetNodeRef); } - private boolean isChildOf(final NodeRef childNodeRef, final QNamePattern associationType, final NodeRef parentNodeRef) { + private boolean isChildOf(final NodeRef childNodeRef, final QNamePattern associationType, final NodeRef parentNodeRef) + { final List associations; if (associationType == null) { associations = runtimeNodeService.getParentAssocs(childNodeRef); @@ -1652,4 +1651,12 @@ public class RuleServiceImpl .map(ChildAssociationRef::getParentRef) .anyMatch(parentNodeRef::equals); } + + @Override + @Experimental + public boolean isRuleSetShared(final NodeRef ruleSetNodeRef) + { + return runtimeNodeService.getParentAssocs(ruleSetNodeRef).stream() + .anyMatch(association -> !association.isPrimary()); + } } 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 91c11b3337..c7b3cc6a6c 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 @@ -362,4 +362,14 @@ public interface RuleService @Auditable(parameters = {"ruleNodeRef", "ruleSetNodeRef"}) @Experimental boolean isRuleAssociatedWithRuleSet(final NodeRef ruleNodeRef, final NodeRef ruleSetNodeRef); + + /** + * Check if others folders are linked to rule set. + * + * @param ruleSetNodeRef - node reference of a rule set + * @return true if others folders are linked to rule set + */ + @Auditable(parameters = {"ruleSetNodeRef"}) + @Experimental + boolean isRuleSetShared(final NodeRef ruleSetNodeRef); } diff --git a/repository/src/test/java/org/alfresco/repo/rule/RuleLinkTest.java b/repository/src/test/java/org/alfresco/repo/rule/RuleLinkTest.java index d0d1c26d84..380ff7cd9f 100644 --- a/repository/src/test/java/org/alfresco/repo/rule/RuleLinkTest.java +++ b/repository/src/test/java/org/alfresco/repo/rule/RuleLinkTest.java @@ -25,6 +25,12 @@ */ package org.alfresco.repo.rule; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.model.ContentModel; import org.alfresco.repo.action.evaluator.ComparePropertyValueEvaluator; @@ -51,12 +57,6 @@ import org.junit.Test; import org.junit.experimental.categories.Category; import org.springframework.transaction.annotation.Transactional; -import java.io.Serializable; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - /** * Parameter definition implementation unit test. * @@ -364,6 +364,19 @@ public class RuleLinkTest extends BaseSpringTest assertEquals(expectedRuleSetNodeRef, ruleSetNodeRef); } + @Test + public void testGetRuleSetNodeForLinkedFolder() { + final Rule rule = createTestRule(false, "luke"); + this.ruleService.saveRule(folderOne, rule); + link(folderOne, folderTwo); + final NodeRef expectedRuleSetNodeRef = getRuleSetNode(folderOne); + + final NodeRef ruleSetNodeRef = ruleService.getRuleSetNode(folderTwo); + + assertNotNull(ruleSetNodeRef); + assertEquals(expectedRuleSetNodeRef, ruleSetNodeRef); + } + @Test public void testIsRuleSetAssociatedWithFolder() { @@ -425,6 +438,33 @@ public class RuleLinkTest extends BaseSpringTest assertFalse(associated); } + @Test + public void testIsRuleSetShared() + { + final Rule rule = createTestRule(false, "luke"); + this.ruleService.saveRule(folderOne, rule); + link(folderOne, folderTwo); + final NodeRef ruleSetNodeRef = ruleService.getRuleSetNode(folderOne); + + // when + final boolean shared = ruleService.isRuleSetShared(ruleSetNodeRef); + + assertTrue(shared); + } + + @Test + public void testIsRuleSetNotShared() + { + final Rule rule = createTestRule(false, "luke"); + this.ruleService.saveRule(folderOne, rule); + final NodeRef ruleSetNodeRef = ruleService.getRuleSetNode(folderOne); + + // when + final boolean shared = ruleService.isRuleSetShared(ruleSetNodeRef); + + assertFalse(shared); + } + protected Rule createTestRule(boolean isAppliedToChildren, String title) { // Rule properties