ACS-3429 post support for actions (#1305)

* ACS-3429: Initial attemp to add action mappings for create rule.

* ACS-3429: Adding rule action validation (TBC).

* ACS-3429: Adding some basic unit tests.

* ACS-3429: Fixing existing unit tests.

* ACS-3429: Adding some unit tests, small fixes.

* ACS-3429: Fixing existing TAS tests.

* ACS-3429: Fixing test after merge from master.

* ACS-3429: Updating TAS REST dependency.

* ACS-3429: Small fixes after review.

* ACS-3429: Refactoring conversion of action parameters + unit tests adjustments.

* ACS-3429: More unit tests for action parameter conversions.

* ACS-3429: More unit tests for action parameter conversions.

* ACS-3429: Removing unused imports.

* ACS-3429: Adding missing bean definition.
This commit is contained in:
Maciej Pichura
2022-08-19 16:29:37 +02:00
committed by GitHub
parent 27d3701c8b
commit 0b94042b6f
11 changed files with 1016 additions and 79 deletions

View File

@@ -78,7 +78,7 @@ public abstract class AbstractRuleWebScript extends DeclarativeWebScript
private static final String RULE_OUTBOUND = "outbound";
private static final String ACTION_CHECK_OUT = "check-out";
private static final String CANNOT_CREATE_RULE = "cannot.create.rule.checkout.outbound";
public static final String CANNOT_CREATE_RULE = "cannot.create.rule.checkout.outbound";
protected NodeService nodeService;
protected RuleService ruleService;

View File

@@ -0,0 +1,132 @@
/*
* #%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 <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.rest.api.impl.rules;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.alfresco.rest.framework.core.exceptions.InvalidArgumentException;
import org.alfresco.rest.framework.core.exceptions.NotFoundException;
import org.alfresco.service.Experimental;
import org.alfresco.service.cmr.action.ActionService;
import org.alfresco.service.cmr.action.ParameterDefinition;
import org.alfresco.service.cmr.action.ParameterizedItemDefinition;
import org.alfresco.service.cmr.dictionary.DataTypeDefinition;
import org.alfresco.service.cmr.dictionary.DictionaryException;
import org.alfresco.service.cmr.dictionary.DictionaryService;
import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
import org.json.JSONArray;
import org.json.JSONException;
@Experimental
public class ActionParameterConverter
{
private final DictionaryService dictionaryService;
private final ActionService actionService;
private final NamespaceService namespaceService;
public ActionParameterConverter(DictionaryService dictionaryService, ActionService actionService,
NamespaceService namespaceService)
{
this.dictionaryService = dictionaryService;
this.actionService = actionService;
this.namespaceService = namespaceService;
}
Map<String, Serializable> getConvertedParams(Map<String, Serializable> params, String name) {
final Map<String, Serializable> parameters = new HashMap<>(params.size());
final ParameterizedItemDefinition definition = actionService.getActionDefinition(name);
if (definition == null)
{
throw new NotFoundException(NotFoundException.DEFAULT_MESSAGE_ID, new String[]{name});
}
for (Map.Entry<String, Serializable> param : params.entrySet())
{
final ParameterDefinition paramDef = definition.getParameterDefintion(param.getKey());
if (paramDef == null && !definition.getAdhocPropertiesAllowed())
{
throw new InvalidArgumentException(InvalidArgumentException.DEFAULT_MESSAGE_ID, new String[]{param.getKey(), name});
}
if (paramDef != null)
{
final QName typeQName = paramDef.getType();
parameters.put(param.getKey(), convertValue(typeQName, param.getValue()));
} else
{
parameters.put(param.getKey(), param.getValue().toString());
}
}
return parameters;
}
private Serializable convertValue(QName typeQName, Object propertyValue) throws JSONException
{
Serializable value;
final DataTypeDefinition typeDef = dictionaryService.getDataType(typeQName);
if (typeDef == null)
{
throw new NotFoundException(NotFoundException.DEFAULT_MESSAGE_ID, new String[]{typeQName.toPrefixString()});
}
if (propertyValue instanceof JSONArray)
{
final String javaClassName = typeDef.getJavaClassName();
try
{
Class.forName(javaClassName);
} catch (ClassNotFoundException e)
{
throw new DictionaryException("Java class " + javaClassName + " of property type " + typeDef.getName() + " is invalid", e);
}
final int length = ((JSONArray) propertyValue).length();
final List<Serializable> list = new ArrayList<>(length);
for (int i = 0; i < length; i++)
{
list.add(convertValue(typeQName, ((JSONArray) propertyValue).get(i)));
}
value = (Serializable) list;
} else
{
if (typeQName.equals(DataTypeDefinition.QNAME) && typeQName.toString().contains(":"))
{
value = QName.createQName(propertyValue.toString(), namespaceService);
} else
{
value = (Serializable) DefaultTypeConverter.INSTANCE.convert(dictionaryService.getDataType(typeQName), propertyValue);
}
}
return value;
}
}

View File

@@ -0,0 +1,85 @@
/*
* #%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 <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.rest.api.impl.rules;
import static org.alfresco.repo.web.scripts.rule.AbstractRuleWebScript.CANNOT_CREATE_RULE;
import static org.alfresco.service.cmr.rule.RuleType.OUTBOUND;
import java.util.Collections;
import java.util.List;
import org.alfresco.repo.action.CompositeActionImpl;
import org.alfresco.repo.action.RuntimeActionService;
import org.alfresco.repo.action.access.ActionAccessRestriction;
import org.alfresco.repo.action.executer.CheckOutActionExecuter;
import org.alfresco.rest.framework.core.exceptions.InvalidArgumentException;
import org.alfresco.service.Experimental;
import org.alfresco.service.cmr.action.Action;
import org.alfresco.service.cmr.rule.Rule;
import org.apache.commons.collections.CollectionUtils;
@Experimental
public class ActionPermissionValidator
{
private final RuntimeActionService runtimeActionService;
public ActionPermissionValidator(RuntimeActionService runtimeActionService)
{
this.runtimeActionService = runtimeActionService;
}
Rule validateRulePermissions(Rule rule)
{
final List<Action> actions = ((CompositeActionImpl) rule.getAction()).getActions();
checkRestrictedAccessActions(actions);
checkRuleOutboundHasNoCheckOutAction(rule, actions);
return rule;
}
private void checkRestrictedAccessActions(List<Action> actions) {
actions.forEach(action -> {
ActionAccessRestriction.setActionContext(action, ActionAccessRestriction.RULE_ACTION_CONTEXT);
runtimeActionService.verifyActionAccessRestrictions(action);
});
}
private void checkRuleOutboundHasNoCheckOutAction(Rule rule, List<Action> actions) {
//TODO: rule types should never be empty in final implementation
if (CollectionUtils.isNotEmpty(rule.getRuleTypes()) && rule.getRuleTypes().contains(OUTBOUND))
{
for (Action action : actions)
{
if (action.getActionDefinitionName().equalsIgnoreCase(CheckOutActionExecuter.NAME))
{
throw new InvalidArgumentException(CANNOT_CREATE_RULE);
}
}
}
}
}

View File

@@ -37,6 +37,7 @@ import org.alfresco.rest.framework.resource.parameters.CollectionWithPagingInfo;
import org.alfresco.rest.framework.resource.parameters.ListPage;
import org.alfresco.rest.framework.resource.parameters.Paging;
import org.alfresco.service.Experimental;
import org.alfresco.service.cmr.action.CompositeAction;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.rule.RuleService;
import org.slf4j.Logger;
@@ -51,6 +52,8 @@ public class RulesImpl implements Rules
private RuleService ruleService;
private NodeValidator validator;
private RuleLoader ruleLoader;
private ActionParameterConverter actionParameterConverter;
private ActionPermissionValidator actionPermissionValidator;
@Override
public CollectionWithPagingInfo<Rule> getRules(final String folderNodeId,
@@ -62,8 +65,8 @@ public class RulesImpl implements Rules
validator.validateRuleSetNode(ruleSetId, folderNodeRef);
final List<Rule> rules = ruleService.getRules(folderNodeRef).stream()
.map(ruleModel -> ruleLoader.loadRule(ruleModel, includes))
.collect(Collectors.toList());
.map(ruleModel -> ruleLoader.loadRule(ruleModel, includes))
.collect(Collectors.toList());
return ListPage.of(rules, paging);
}
@@ -89,10 +92,10 @@ public class RulesImpl implements Rules
}
return rules.stream()
.map(rule -> rule.toServiceModel(nodes))
.map(rule -> ruleService.saveRule(folderNodeRef, rule))
.map(rule -> ruleLoader.loadRule(rule, includes))
.collect(Collectors.toList());
.map(this::mapToServiceModelAndValidateActions)
.map(rule -> ruleService.saveRule(folderNodeRef, rule))
.map(rule -> ruleLoader.loadRule(rule, includes))
.collect(Collectors.toList());
}
@Override
@@ -117,6 +120,16 @@ public class RulesImpl implements Rules
ruleService.removeRule(folderNodeRef, rule);
}
private org.alfresco.service.cmr.rule.Rule mapToServiceModelAndValidateActions(Rule rule)
{
final org.alfresco.service.cmr.rule.Rule serviceModelRule = rule.toServiceModel(nodes);
final CompositeAction compositeAction = (CompositeAction) serviceModelRule.getAction();
compositeAction.getActions().forEach(action -> action.setParameterValues(
actionParameterConverter.getConvertedParams(action.getParameterValues(), action.getActionDefinitionName())));
return actionPermissionValidator.validateRulePermissions(serviceModelRule);
}
public void setNodes(Nodes nodes)
{
this.nodes = nodes;
@@ -136,4 +149,14 @@ public class RulesImpl implements Rules
{
this.ruleLoader = ruleLoader;
}
public void setActionParameterConverter(ActionParameterConverter actionParameterConverter)
{
this.actionParameterConverter = actionParameterConverter;
}
public void setActionPermissionValidator(ActionPermissionValidator actionPermissionValidator)
{
this.actionPermissionValidator = actionPermissionValidator;
}
}

View File

@@ -34,7 +34,6 @@ import java.util.Objects;
import org.alfresco.repo.action.ActionImpl;
import org.alfresco.repo.action.CompositeActionImpl;
import org.alfresco.repo.action.executer.SetPropertyValueActionExecuter;
import org.alfresco.service.Experimental;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.util.GUID;
@@ -75,7 +74,7 @@ public class Action
*/
public org.alfresco.service.cmr.action.Action toServiceModel(final NodeRef nodeRef)
{
return new ActionImpl(nodeRef, GUID.generate(), SetPropertyValueActionExecuter.NAME, params);
return new ActionImpl(nodeRef, GUID.generate(), this.actionDefinitionId, params);
}
/**

View File

@@ -891,11 +891,23 @@
<property name="ruleSets" ref="RuleSets" />
</bean>
<bean id="actionParameterConverter" class="org.alfresco.rest.api.impl.rules.ActionParameterConverter">
<constructor-arg name="actionService" ref="ActionService"/>
<constructor-arg name="dictionaryService" ref="DictionaryService"/>
<constructor-arg name="namespaceService" ref="NamespaceService"/>
</bean>
<bean id="actionPermissionValidator" class="org.alfresco.rest.api.impl.rules.ActionPermissionValidator">
<constructor-arg name="runtimeActionService" ref="actionService"/>
</bean>
<bean id="rules" class="org.alfresco.rest.api.impl.rules.RulesImpl">
<property name="nodes" ref="Nodes" />
<property name="validator" ref="nodeValidator"/>
<property name="ruleService" ref="RuleService" />
<property name="ruleLoader" ref="ruleLoader" />
<property name="ruleLoader" ref="ruleLoader"/>
<property name="actionParameterConverter" ref="actionParameterConverter"/>
<property name="actionPermissionValidator" ref="actionPermissionValidator"/>
</bean>
<bean id="Rules" class="org.springframework.aop.framework.ProxyFactoryBean">

View File

@@ -0,0 +1,563 @@
/*
* #%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 <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.rest.api.impl.rules;
import static org.alfresco.service.cmr.repository.StoreRef.STORE_REF_WORKSPACE_SPACESSTORE;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.BDDMockito.given;
import static org.mockito.BDDMockito.then;
import static org.mockito.Mockito.times;
import java.io.Serializable;
import java.util.Map;
import org.alfresco.repo.action.executer.AddFeaturesActionExecuter;
import org.alfresco.repo.action.executer.CheckInActionExecuter;
import org.alfresco.repo.action.executer.CheckOutActionExecuter;
import org.alfresco.repo.action.executer.CopyActionExecuter;
import org.alfresco.repo.action.executer.LinkCategoryActionExecuter;
import org.alfresco.repo.action.executer.MoveActionExecuter;
import org.alfresco.repo.action.executer.RemoveFeaturesActionExecuter;
import org.alfresco.repo.action.executer.ScriptActionExecuter;
import org.alfresco.repo.action.executer.SetPropertyValueActionExecuter;
import org.alfresco.repo.action.executer.SimpleWorkflowActionExecuter;
import org.alfresco.service.Experimental;
import org.alfresco.service.cmr.action.ActionDefinition;
import org.alfresco.service.cmr.action.ActionService;
import org.alfresco.service.cmr.action.ParameterDefinition;
import org.alfresco.service.cmr.dictionary.DataTypeDefinition;
import org.alfresco.service.cmr.dictionary.DictionaryService;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
@Experimental
@RunWith(MockitoJUnitRunner.class)
public class ActionParameterConverterTest
{
private static final String VERSIONABLE = "versionable";
private static final String VERSIONABLE_ASPECT = NamespaceService.CONTENT_MODEL_PREFIX + QName.NAMESPACE_PREFIX + VERSIONABLE;
private static final String CHECKOUT = "checkout";
private static final String CHECKOUT_ASPECT = NamespaceService.CONTENT_MODEL_PREFIX + QName.NAMESPACE_PREFIX + CHECKOUT;
private static final String CONTAINS = "contains";
private static final String CONTAINS_ASPECT = NamespaceService.CONTENT_MODEL_PREFIX + QName.NAMESPACE_PREFIX + CONTAINS;
private static final String CLASSIFIABLE = "generalclassifiable";
private static final String CLASSIFIABLE_ASPECT = NamespaceService.CONTENT_MODEL_PREFIX + QName.NAMESPACE_PREFIX + CLASSIFIABLE;
private static final String IDENTIFIER = "identifier";
private static final String IDENTIFIER_ASPECT = NamespaceService.CONTENT_MODEL_PREFIX + QName.NAMESPACE_PREFIX + IDENTIFIER;
private static final String DUMMY_FOLDER_NODE_ID = "dummy-folder-node";
private static final String DUMMY_FOLDER_NODE_REF = STORE_REF_WORKSPACE_SPACESSTORE + "/" + DUMMY_FOLDER_NODE_ID;
private static final String DUMMY_SCRIPT_NODE_ID = "dummy-script-ref";
private static final String DUMMY_SCRIPT_NODE_REF = STORE_REF_WORKSPACE_SPACESSTORE + "/" + DUMMY_SCRIPT_NODE_ID;
@Mock
private DictionaryService dictionaryService;
@Mock
private ActionService actionService;
@Mock
private NamespaceService namespaceService;
@Mock
private ActionDefinition actionDefinition;
@Mock
private ParameterDefinition actionDefinitionParam1;
@Mock
private ParameterDefinition actionDefinitionParam2;
@Mock
private ParameterDefinition actionDefinitionParam3;
@Mock
private DataTypeDefinition dataTypeDefinition1;
@Mock
private DataTypeDefinition dataTypeDefinition2;
@Mock
private DataTypeDefinition dataTypeDefinition3;
@InjectMocks
private ActionParameterConverter objectUnderTest;
@Test
public void testAddAspectConversion()
{
final String name = AddFeaturesActionExecuter.NAME;
final String aspectNameKey = AddFeaturesActionExecuter.PARAM_ASPECT_NAME;
final Map<String, Serializable> params = Map.of(aspectNameKey, VERSIONABLE_ASPECT);
given(actionService.getActionDefinition(name)).willReturn(actionDefinition);
given(actionDefinition.getParameterDefintion(aspectNameKey)).willReturn(actionDefinitionParam1);
final QName qname = DataTypeDefinition.QNAME;
given(actionDefinitionParam1.getType()).willReturn(qname);
given(dictionaryService.getDataType(qname)).willReturn(dataTypeDefinition1);
given(namespaceService.getNamespaceURI(any())).willReturn(NamespaceService.DICTIONARY_MODEL_1_0_URI);
//when
final Map<String, Serializable> convertedParams = objectUnderTest.getConvertedParams(params, name);
then(actionService).should().getActionDefinition(name);
then(actionService).shouldHaveNoMoreInteractions();
then(actionDefinition).should().getParameterDefintion(aspectNameKey);
then(actionDefinition).shouldHaveNoMoreInteractions();
then(dictionaryService).should().getDataType(qname);
then(dictionaryService).shouldHaveNoMoreInteractions();
then(namespaceService).should().getNamespaceURI(any());
then(namespaceService).shouldHaveNoMoreInteractions();
final Serializable convertedParam = convertedParams.get(aspectNameKey);
assertTrue(convertedParam instanceof QName);
assertEquals(VERSIONABLE, ((QName) convertedParam).getLocalName());
assertEquals(VERSIONABLE_ASPECT, ((QName) convertedParam).getPrefixString());
assertEquals(NamespaceService.DICTIONARY_MODEL_1_0_URI, ((QName) convertedParam).getNamespaceURI());
}
@Test
public void testCopyConversion()
{
final String name = CopyActionExecuter.NAME;
final String destinationFolderKey = CopyActionExecuter.PARAM_DESTINATION_FOLDER;
final String deepCopyKey = CopyActionExecuter.PARAM_DEEP_COPY;
final Map<String, Serializable> params = Map.of(destinationFolderKey, DUMMY_FOLDER_NODE_REF, deepCopyKey, true);
given(actionService.getActionDefinition(name)).willReturn(actionDefinition);
given(actionDefinition.getParameterDefintion(destinationFolderKey)).willReturn(actionDefinitionParam1);
given(actionDefinition.getParameterDefintion(deepCopyKey)).willReturn(actionDefinitionParam2);
final QName nodeRef = DataTypeDefinition.NODE_REF;
given(actionDefinitionParam1.getType()).willReturn(nodeRef);
final QName bool = DataTypeDefinition.BOOLEAN;
given(actionDefinitionParam2.getType()).willReturn(bool);
given(dictionaryService.getDataType(nodeRef)).willReturn(dataTypeDefinition1);
given(dataTypeDefinition1.getJavaClassName()).willReturn(NodeRef.class.getName());
given(dictionaryService.getDataType(bool)).willReturn(dataTypeDefinition2);
given(dataTypeDefinition2.getJavaClassName()).willReturn(Boolean.class.getName());
//when
final Map<String, Serializable> convertedParams = objectUnderTest.getConvertedParams(params, name);
then(actionService).should().getActionDefinition(name);
then(actionService).shouldHaveNoMoreInteractions();
then(actionDefinition).should().getParameterDefintion(destinationFolderKey);
then(actionDefinition).should().getParameterDefintion(deepCopyKey);
then(actionDefinition).shouldHaveNoMoreInteractions();
then(dictionaryService).should(times(2)).getDataType(bool);
then(dictionaryService).should(times(2)).getDataType(nodeRef);
then(dictionaryService).shouldHaveNoMoreInteractions();
then(namespaceService).shouldHaveNoInteractions();
final Serializable convertedCopyParam = convertedParams.get(destinationFolderKey);
assertTrue(convertedCopyParam instanceof NodeRef);
assertEquals(STORE_REF_WORKSPACE_SPACESSTORE, ((NodeRef) convertedCopyParam).getStoreRef());
assertEquals(DUMMY_FOLDER_NODE_ID, ((NodeRef) convertedCopyParam).getId());
final Serializable convertedDeepCopyParam = convertedParams.get(deepCopyKey);
assertThat(convertedDeepCopyParam instanceof Boolean).isTrue();
assertTrue(((Boolean) convertedDeepCopyParam));
}
@Test
public void testExecuteScriptConversion()
{
final String name = ScriptActionExecuter.NAME;
final String executeScriptKey = ScriptActionExecuter.PARAM_SCRIPTREF;
final Map<String, Serializable> params = Map.of(executeScriptKey, DUMMY_SCRIPT_NODE_REF);
given(actionService.getActionDefinition(name)).willReturn(actionDefinition);
given(actionDefinition.getParameterDefintion(executeScriptKey)).willReturn(actionDefinitionParam1);
final QName scriptNodeRef = DataTypeDefinition.NODE_REF;
given(actionDefinitionParam1.getType()).willReturn(scriptNodeRef);
given(dictionaryService.getDataType(scriptNodeRef)).willReturn(dataTypeDefinition1);
given(dataTypeDefinition1.getJavaClassName()).willReturn(NodeRef.class.getName());
//when
final Map<String, Serializable> convertedParams = objectUnderTest.getConvertedParams(params, name);
then(actionService).should().getActionDefinition(name);
then(actionService).shouldHaveNoMoreInteractions();
then(actionDefinition).should().getParameterDefintion(executeScriptKey);
then(actionDefinition).shouldHaveNoMoreInteractions();
then(dictionaryService).should(times(2)).getDataType(scriptNodeRef);
then(dictionaryService).shouldHaveNoMoreInteractions();
then(namespaceService).shouldHaveNoInteractions();
final Serializable convertedCopyParam = convertedParams.get(executeScriptKey);
assertTrue(convertedCopyParam instanceof NodeRef);
assertEquals(STORE_REF_WORKSPACE_SPACESSTORE, ((NodeRef) convertedCopyParam).getStoreRef());
assertEquals(DUMMY_SCRIPT_NODE_ID, ((NodeRef) convertedCopyParam).getId());
}
@Test
public void testMoveConversion()
{
final String name = MoveActionExecuter.NAME;
final String destinationFolderKey = MoveActionExecuter.PARAM_DESTINATION_FOLDER;
final Map<String, Serializable> params = Map.of(destinationFolderKey, DUMMY_FOLDER_NODE_REF);
given(actionService.getActionDefinition(name)).willReturn(actionDefinition);
given(actionDefinition.getParameterDefintion(destinationFolderKey)).willReturn(actionDefinitionParam1);
final QName nodeRef = DataTypeDefinition.NODE_REF;
given(actionDefinitionParam1.getType()).willReturn(nodeRef);
given(dictionaryService.getDataType(nodeRef)).willReturn(dataTypeDefinition1);
given(dataTypeDefinition1.getJavaClassName()).willReturn(NodeRef.class.getName());
//when
final Map<String, Serializable> convertedParams = objectUnderTest.getConvertedParams(params, name);
then(actionService).should().getActionDefinition(name);
then(actionService).shouldHaveNoMoreInteractions();
then(actionDefinition).should().getParameterDefintion(destinationFolderKey);
then(actionDefinition).shouldHaveNoMoreInteractions();
then(dictionaryService).should(times(2)).getDataType(nodeRef);
then(dictionaryService).shouldHaveNoMoreInteractions();
then(namespaceService).shouldHaveNoInteractions();
final Serializable convertedCopyParam = convertedParams.get(destinationFolderKey);
assertTrue(convertedCopyParam instanceof NodeRef);
assertEquals(STORE_REF_WORKSPACE_SPACESSTORE, ((NodeRef) convertedCopyParam).getStoreRef());
assertEquals(DUMMY_FOLDER_NODE_ID, ((NodeRef) convertedCopyParam).getId());
}
@Test
public void testCheckInConversion()
{
final String name = CheckInActionExecuter.NAME;
final String descriptionKey = CheckInActionExecuter.PARAM_DESCRIPTION;
final String minorChangeKey = CheckInActionExecuter.PARAM_MINOR_CHANGE;
String description = "dummy description";
final Map<String, Serializable> params = Map.of(descriptionKey, description, minorChangeKey, true);
given(actionService.getActionDefinition(name)).willReturn(actionDefinition);
given(actionDefinition.getParameterDefintion(descriptionKey)).willReturn(actionDefinitionParam1);
given(actionDefinition.getParameterDefintion(minorChangeKey)).willReturn(actionDefinitionParam2);
final QName text = DataTypeDefinition.TEXT;
given(actionDefinitionParam1.getType()).willReturn(text);
final QName bool = DataTypeDefinition.BOOLEAN;
given(actionDefinitionParam2.getType()).willReturn(bool);
given(dictionaryService.getDataType(text)).willReturn(dataTypeDefinition1);
given(dataTypeDefinition1.getJavaClassName()).willReturn(String.class.getName());
given(dictionaryService.getDataType(bool)).willReturn(dataTypeDefinition2);
given(dataTypeDefinition2.getJavaClassName()).willReturn(Boolean.class.getName());
//when
final Map<String, Serializable> convertedParams = objectUnderTest.getConvertedParams(params, name);
then(actionService).should().getActionDefinition(name);
then(actionService).shouldHaveNoMoreInteractions();
then(actionDefinition).should().getParameterDefintion(descriptionKey);
then(actionDefinition).should().getParameterDefintion(minorChangeKey);
then(actionDefinition).shouldHaveNoMoreInteractions();
then(dictionaryService).should(times(2)).getDataType(bool);
then(dictionaryService).should(times(2)).getDataType(text);
then(dictionaryService).shouldHaveNoMoreInteractions();
then(namespaceService).shouldHaveNoInteractions();
final Serializable convertedDescriptionParam = convertedParams.get(descriptionKey);
assertTrue(convertedDescriptionParam instanceof String);
assertEquals(description, convertedDescriptionParam);
final Serializable convertedMinorChangeParam = convertedParams.get(minorChangeKey);
assertTrue(convertedMinorChangeParam instanceof Boolean);
assertTrue((Boolean) convertedMinorChangeParam);
}
@Test
public void testCheckOutConversion()
{
final String name = CheckOutActionExecuter.NAME;
final String destinationFolderKey = CheckOutActionExecuter.PARAM_DESTINATION_FOLDER;
final String assocNameKey = CheckOutActionExecuter.PARAM_ASSOC_QNAME;
final String assocTypeKey = CheckOutActionExecuter.PARAM_ASSOC_TYPE_QNAME;
final Map<String, Serializable> params =
Map.of(destinationFolderKey, DUMMY_FOLDER_NODE_REF, assocNameKey, CHECKOUT_ASPECT, assocTypeKey, CONTAINS_ASPECT);
given(actionService.getActionDefinition(name)).willReturn(actionDefinition);
given(actionDefinition.getParameterDefintion(destinationFolderKey)).willReturn(actionDefinitionParam1);
final QName nodeRef = DataTypeDefinition.NODE_REF;
given(actionDefinitionParam1.getType()).willReturn(nodeRef);
given(actionDefinition.getParameterDefintion(assocNameKey)).willReturn(actionDefinitionParam2);
final QName qname = DataTypeDefinition.QNAME;
given(actionDefinitionParam2.getType()).willReturn(qname);
given(actionDefinition.getParameterDefintion(assocTypeKey)).willReturn(actionDefinitionParam3);
given(actionDefinitionParam3.getType()).willReturn(qname);
given(dictionaryService.getDataType(nodeRef)).willReturn(dataTypeDefinition1);
given(dataTypeDefinition1.getJavaClassName()).willReturn(NodeRef.class.getName());
given(dictionaryService.getDataType(qname)).willReturn(dataTypeDefinition2);
given(namespaceService.getNamespaceURI(any())).willReturn(NamespaceService.DICTIONARY_MODEL_1_0_URI);
//when
final Map<String, Serializable> convertedParams = objectUnderTest.getConvertedParams(params, name);
then(actionService).should().getActionDefinition(name);
then(actionService).shouldHaveNoMoreInteractions();
then(actionDefinition).should().getParameterDefintion(destinationFolderKey);
then(actionDefinition).should().getParameterDefintion(assocNameKey);
then(actionDefinition).should().getParameterDefintion(assocTypeKey);
then(actionDefinition).shouldHaveNoMoreInteractions();
then(dictionaryService).should(times(2)).getDataType(qname);
then(dictionaryService).should(times(2)).getDataType(nodeRef);
then(dictionaryService).shouldHaveNoMoreInteractions();
then(namespaceService).should(times(2)).getNamespaceURI(any());
then(namespaceService).shouldHaveNoMoreInteractions();
final Serializable convertedDestinationParam = convertedParams.get(destinationFolderKey);
assertTrue(convertedDestinationParam instanceof NodeRef);
assertEquals(STORE_REF_WORKSPACE_SPACESSTORE, ((NodeRef) convertedDestinationParam).getStoreRef());
assertEquals(DUMMY_FOLDER_NODE_ID, ((NodeRef) convertedDestinationParam).getId());
final Serializable convertedAssocNameParam = convertedParams.get(assocNameKey);
assertTrue(convertedAssocNameParam instanceof QName);
assertEquals(CHECKOUT, ((QName) convertedAssocNameParam).getLocalName());
assertEquals(CHECKOUT_ASPECT, ((QName) convertedAssocNameParam).getPrefixString());
assertEquals(NamespaceService.DICTIONARY_MODEL_1_0_URI, ((QName) convertedAssocNameParam).getNamespaceURI());
final Serializable convertedAssocTypeParam = convertedParams.get(assocTypeKey);
assertTrue(convertedAssocTypeParam instanceof QName);
assertEquals(CONTAINS, ((QName) convertedAssocTypeParam).getLocalName());
assertEquals(CONTAINS_ASPECT, ((QName) convertedAssocTypeParam).getPrefixString());
assertEquals(NamespaceService.DICTIONARY_MODEL_1_0_URI, ((QName) convertedAssocTypeParam).getNamespaceURI());
}
@Test
public void testCategoryLinkConversion()
{
final String name = LinkCategoryActionExecuter.NAME;
final String categoryAspectKey = LinkCategoryActionExecuter.PARAM_CATEGORY_ASPECT;
final String categoryValueKey = LinkCategoryActionExecuter.PARAM_CATEGORY_VALUE;
final Map<String, Serializable> params = Map.of(categoryAspectKey, CLASSIFIABLE_ASPECT, categoryValueKey, DUMMY_FOLDER_NODE_REF);
given(actionService.getActionDefinition(name)).willReturn(actionDefinition);
given(actionDefinition.getParameterDefintion(categoryAspectKey)).willReturn(actionDefinitionParam1);
final QName qname = DataTypeDefinition.QNAME;
given(actionDefinitionParam1.getType()).willReturn(qname);
given(actionDefinition.getParameterDefintion(categoryValueKey)).willReturn(actionDefinitionParam2);
final QName nodeRef = DataTypeDefinition.NODE_REF;
given(actionDefinitionParam2.getType()).willReturn(nodeRef);
given(dictionaryService.getDataType(nodeRef)).willReturn(dataTypeDefinition1);
given(dataTypeDefinition1.getJavaClassName()).willReturn(NodeRef.class.getName());
given(dictionaryService.getDataType(qname)).willReturn(dataTypeDefinition2);
given(namespaceService.getNamespaceURI(any())).willReturn(NamespaceService.DICTIONARY_MODEL_1_0_URI);
//when
final Map<String, Serializable> convertedParams = objectUnderTest.getConvertedParams(params, name);
then(actionService).should().getActionDefinition(name);
then(actionService).shouldHaveNoMoreInteractions();
then(actionDefinition).should().getParameterDefintion(categoryAspectKey);
then(actionDefinition).should().getParameterDefintion(categoryValueKey);
then(actionDefinition).shouldHaveNoMoreInteractions();
then(dictionaryService).should().getDataType(qname);
then(dictionaryService).should(times(2)).getDataType(nodeRef);
then(dictionaryService).shouldHaveNoMoreInteractions();
then(namespaceService).should().getNamespaceURI(any());
then(namespaceService).shouldHaveNoMoreInteractions();
final Serializable convertedCatValueParam = convertedParams.get(categoryAspectKey);
assertTrue(convertedCatValueParam instanceof QName);
assertEquals(CLASSIFIABLE, ((QName) convertedCatValueParam).getLocalName());
assertEquals(CLASSIFIABLE_ASPECT, ((QName) convertedCatValueParam).getPrefixString());
assertEquals(NamespaceService.DICTIONARY_MODEL_1_0_URI, ((QName) convertedCatValueParam).getNamespaceURI());
final Serializable convertedDestinationParam = convertedParams.get(categoryValueKey);
assertTrue(convertedDestinationParam instanceof NodeRef);
assertEquals(STORE_REF_WORKSPACE_SPACESSTORE, ((NodeRef) convertedDestinationParam).getStoreRef());
assertEquals(DUMMY_FOLDER_NODE_ID, ((NodeRef) convertedDestinationParam).getId());
}
@Test
public void testRemoveAspectConversion()
{
final String name = RemoveFeaturesActionExecuter.NAME;
final String aspectNameKey = RemoveFeaturesActionExecuter.PARAM_ASPECT_NAME;
final Map<String, Serializable> params = Map.of(aspectNameKey, VERSIONABLE_ASPECT);
given(actionService.getActionDefinition(name)).willReturn(actionDefinition);
given(actionDefinition.getParameterDefintion(aspectNameKey)).willReturn(actionDefinitionParam1);
final QName qname = DataTypeDefinition.QNAME;
given(actionDefinitionParam1.getType()).willReturn(qname);
given(dictionaryService.getDataType(qname)).willReturn(dataTypeDefinition1);
given(namespaceService.getNamespaceURI(any())).willReturn(NamespaceService.DICTIONARY_MODEL_1_0_URI);
//when
final Map<String, Serializable> convertedParams = objectUnderTest.getConvertedParams(params, name);
then(actionService).should().getActionDefinition(name);
then(actionService).shouldHaveNoMoreInteractions();
then(actionDefinition).should().getParameterDefintion(aspectNameKey);
then(actionDefinition).shouldHaveNoMoreInteractions();
then(dictionaryService).should().getDataType(qname);
then(dictionaryService).shouldHaveNoMoreInteractions();
then(namespaceService).should().getNamespaceURI(any());
then(namespaceService).shouldHaveNoMoreInteractions();
final Serializable convertedParam = convertedParams.get(aspectNameKey);
assertTrue(convertedParam instanceof QName);
assertEquals(VERSIONABLE, ((QName) convertedParam).getLocalName());
assertEquals(VERSIONABLE_ASPECT, ((QName) convertedParam).getPrefixString());
assertEquals(NamespaceService.DICTIONARY_MODEL_1_0_URI, ((QName) convertedParam).getNamespaceURI());
}
@Test
public void testAddWorkflowConversion()
{
final String name = SimpleWorkflowActionExecuter.NAME;
final String approveStepKey = SimpleWorkflowActionExecuter.PARAM_APPROVE_STEP;
final String approveFolderKey = SimpleWorkflowActionExecuter.PARAM_APPROVE_FOLDER;
final String approveMoveKey = SimpleWorkflowActionExecuter.PARAM_APPROVE_MOVE;
final String rejectStepKey = SimpleWorkflowActionExecuter.PARAM_REJECT_STEP;
final String rejectFolderKey = SimpleWorkflowActionExecuter.PARAM_REJECT_FOLDER;
final String rejectMoveKey = SimpleWorkflowActionExecuter.PARAM_REJECT_MOVE;
final String approve = "Approve";
final String reject = "Reject";
final Map<String, Serializable> params =
Map.of(approveStepKey, approve, approveFolderKey, DUMMY_FOLDER_NODE_REF, approveMoveKey, true,
rejectStepKey, reject, rejectFolderKey, DUMMY_FOLDER_NODE_REF, rejectMoveKey, true);
given(actionService.getActionDefinition(name)).willReturn(actionDefinition);
given(actionDefinition.getParameterDefintion(rejectStepKey)).willReturn(actionDefinitionParam1);
given(actionDefinition.getParameterDefintion(approveStepKey)).willReturn(actionDefinitionParam1);
final QName text = DataTypeDefinition.TEXT;
given(actionDefinitionParam1.getType()).willReturn(text, text);
given(actionDefinition.getParameterDefintion(rejectFolderKey)).willReturn(actionDefinitionParam2);
given(actionDefinition.getParameterDefintion(approveFolderKey)).willReturn(actionDefinitionParam2);
final QName nodeRef = DataTypeDefinition.NODE_REF;
given(actionDefinitionParam2.getType()).willReturn(nodeRef, nodeRef);
given(actionDefinition.getParameterDefintion(rejectMoveKey)).willReturn(actionDefinitionParam3);
given(actionDefinition.getParameterDefintion(approveMoveKey)).willReturn(actionDefinitionParam3);
final QName bool = DataTypeDefinition.BOOLEAN;
given(actionDefinitionParam3.getType()).willReturn(bool, bool);
given(dictionaryService.getDataType(nodeRef)).willReturn(dataTypeDefinition1);
given(dataTypeDefinition1.getJavaClassName()).willReturn(NodeRef.class.getName());
given(dictionaryService.getDataType(text)).willReturn(dataTypeDefinition2);
given(dataTypeDefinition2.getJavaClassName()).willReturn(String.class.getName());
given(dictionaryService.getDataType(bool)).willReturn(dataTypeDefinition3);
given(dataTypeDefinition3.getJavaClassName()).willReturn(Boolean.class.getName());
//when
final Map<String, Serializable> convertedParams = objectUnderTest.getConvertedParams(params, name);
then(actionService).should().getActionDefinition(name);
then(actionService).shouldHaveNoMoreInteractions();
then(actionDefinition).should().getParameterDefintion(approveStepKey);
then(actionDefinition).should().getParameterDefintion(approveFolderKey);
then(actionDefinition).should().getParameterDefintion(approveMoveKey);
then(actionDefinition).should().getParameterDefintion(rejectStepKey);
then(actionDefinition).should().getParameterDefintion(rejectFolderKey);
then(actionDefinition).should().getParameterDefintion(rejectMoveKey);
then(actionDefinition).shouldHaveNoMoreInteractions();
then(dictionaryService).should(times(4)).getDataType(text);
then(dictionaryService).should(times(4)).getDataType(nodeRef);
then(dictionaryService).should(times(4)).getDataType(bool);
then(dictionaryService).shouldHaveNoMoreInteractions();
then(namespaceService).shouldHaveNoInteractions();
final Serializable convertedApproveStepParam = convertedParams.get(approveStepKey);
assertTrue(convertedApproveStepParam instanceof String);
assertEquals(approve, convertedApproveStepParam);
final Serializable convertedRejectStepParam = convertedParams.get(rejectStepKey);
assertTrue(convertedRejectStepParam instanceof String);
assertEquals(reject, convertedRejectStepParam);
final Serializable convertedApproveFolderParam = convertedParams.get(approveFolderKey);
assertTrue(convertedApproveFolderParam instanceof NodeRef);
assertEquals(STORE_REF_WORKSPACE_SPACESSTORE, ((NodeRef) convertedApproveFolderParam).getStoreRef());
assertEquals(DUMMY_FOLDER_NODE_ID, ((NodeRef) convertedApproveFolderParam).getId());
final Serializable convertedRejectFolderParam = convertedParams.get(rejectFolderKey);
assertTrue(convertedRejectFolderParam instanceof NodeRef);
assertEquals(STORE_REF_WORKSPACE_SPACESSTORE, ((NodeRef) convertedRejectFolderParam).getStoreRef());
assertEquals(DUMMY_FOLDER_NODE_ID, ((NodeRef) convertedRejectFolderParam).getId());
final Serializable convertedApproveMoveParam = convertedParams.get(approveMoveKey);
assertTrue(convertedApproveMoveParam instanceof Boolean);
assertTrue((Boolean) convertedApproveMoveParam);
final Serializable convertedRejectMoveParam = convertedParams.get(rejectMoveKey);
assertTrue(convertedRejectMoveParam instanceof Boolean);
assertTrue((Boolean) convertedRejectMoveParam);
}
@Test
public void testSetPropertyConversion()
{
final String name = SetPropertyValueActionExecuter.NAME;
final String propertyNameKey = SetPropertyValueActionExecuter.PARAM_PROPERTY;
final String propertyValueKey = SetPropertyValueActionExecuter.PARAM_VALUE;
final String propertyTypeKey = "prop_type";
final String dummy_key_value = "dummy_key_value";
final String propType = "d:text";
final Map<String, Serializable> params =
Map.of(propertyNameKey, IDENTIFIER_ASPECT, propertyValueKey, dummy_key_value, propertyTypeKey, propType);
given(actionService.getActionDefinition(name)).willReturn(actionDefinition);
given(actionDefinition.getParameterDefintion(propertyNameKey)).willReturn(actionDefinitionParam1);
final QName qname = DataTypeDefinition.QNAME;
given(actionDefinitionParam1.getType()).willReturn(qname);
given(actionDefinition.getParameterDefintion(propertyValueKey)).willReturn(actionDefinitionParam2);
final QName any = DataTypeDefinition.ANY;
given(actionDefinitionParam2.getType()).willReturn(any);
given(actionDefinition.getParameterDefintion(propertyTypeKey)).willReturn(null);
given(actionDefinition.getAdhocPropertiesAllowed()).willReturn(true);
given(dictionaryService.getDataType(qname)).willReturn(dataTypeDefinition1);
given(dictionaryService.getDataType(any)).willReturn(dataTypeDefinition2);
given(dataTypeDefinition2.getJavaClassName()).willReturn(Object.class.getName());
given(namespaceService.getNamespaceURI(any())).willReturn(NamespaceService.DICTIONARY_MODEL_1_0_URI);
//when
final Map<String, Serializable> convertedParams = objectUnderTest.getConvertedParams(params, name);
then(actionService).should().getActionDefinition(name);
then(actionService).shouldHaveNoMoreInteractions();
then(actionDefinition).should().getParameterDefintion(propertyNameKey);
then(actionDefinition).should().getParameterDefintion(propertyValueKey);
then(actionDefinition).should().getParameterDefintion(propertyTypeKey);
then(actionDefinition).should().getAdhocPropertiesAllowed();
then(actionDefinition).shouldHaveNoMoreInteractions();
then(dictionaryService).should().getDataType(qname);
then(dictionaryService).should(times(2)).getDataType(any);
then(dictionaryService).shouldHaveNoMoreInteractions();
then(namespaceService).should().getNamespaceURI(any());
then(namespaceService).shouldHaveNoMoreInteractions();
final Serializable convertedPropNameParam = convertedParams.get(propertyNameKey);
assertTrue(convertedPropNameParam instanceof QName);
assertEquals(IDENTIFIER, ((QName) convertedPropNameParam).getLocalName());
assertEquals(IDENTIFIER_ASPECT, ((QName) convertedPropNameParam).getPrefixString());
assertEquals(NamespaceService.DICTIONARY_MODEL_1_0_URI, ((QName) convertedPropNameParam).getNamespaceURI());
final Serializable convertedPropValParam = convertedParams.get(propertyValueKey);
assertTrue(convertedPropValParam instanceof String);
assertEquals(dummy_key_value, convertedPropValParam);
final Serializable convertedPropTypeParam = convertedParams.get(propertyTypeKey);
assertTrue(convertedPropTypeParam instanceof String);
assertEquals(propType, convertedPropTypeParam);
}
}

View File

@@ -0,0 +1,84 @@
/*
* #%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 <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.rest.api.impl.rules;
import static org.mockito.BDDMockito.then;
import java.util.ArrayList;
import java.util.List;
import junit.framework.TestCase;
import org.alfresco.repo.action.ActionImpl;
import org.alfresco.repo.action.CompositeActionImpl;
import org.alfresco.repo.action.RuntimeActionService;
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.Rule;
import org.assertj.core.api.Assertions;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
@Experimental
@RunWith(MockitoJUnitRunner.class)
public class ActionPermissionValidatorTest extends TestCase
{
private static final String DUMMY_NODE_ID = "dummy-node-id";
@Mock
private RuntimeActionService runtimeActionService;
@InjectMocks
private ActionPermissionValidator objectUnderTest;
@Test
public void testPositiveRulePermissionValidation() {
//given
final CompositeActionImpl compositeAction = new CompositeActionImpl(new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, DUMMY_NODE_ID), "composite-id");
final Action action1 = new ActionImpl(new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, DUMMY_NODE_ID), "id-1", "actionDef-1");
compositeAction.addAction(action1);
final Action action2 = new ActionImpl(new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, DUMMY_NODE_ID), "id-2", "actionDef-2");
compositeAction.addAction(action2);
final Rule inputRule = new Rule();
inputRule.setAction(compositeAction);
//when
final Rule validatedRule = objectUnderTest.validateRulePermissions(inputRule);
then(runtimeActionService).should().verifyActionAccessRestrictions(action1);
then(runtimeActionService).should().verifyActionAccessRestrictions(action2);
then(runtimeActionService).shouldHaveNoMoreInteractions();
((CompositeActionImpl) validatedRule.getAction()).getActions()
.forEach(action -> Assertions.assertThat(action.getParameterValue("actionContext")).isEqualTo("rule"));
}
//TODO: when Rule mappings are done - we need to add negative test(s) here
}

View File

@@ -36,13 +36,18 @@ import static org.mockito.ArgumentMatchers.anyBoolean;
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.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.stream.IntStream;
import junit.framework.TestCase;
import org.alfresco.repo.action.ActionImpl;
import org.alfresco.repo.action.CompositeActionImpl;
import org.alfresco.rest.api.Nodes;
import org.alfresco.rest.api.model.rules.Rule;
import org.alfresco.rest.framework.core.exceptions.EntityNotFoundException;
@@ -52,6 +57,7 @@ import org.alfresco.rest.framework.core.exceptions.RelationshipResourceNotFoundE
import org.alfresco.rest.framework.resource.parameters.CollectionWithPagingInfo;
import org.alfresco.rest.framework.resource.parameters.Paging;
import org.alfresco.service.Experimental;
import org.alfresco.service.cmr.action.CompositeAction;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.service.cmr.rule.RuleService;
@@ -61,7 +67,6 @@ import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.mockito.junit.MockitoJUnitRunner;
@Experimental
@@ -76,6 +81,8 @@ public class RulesImplTest extends TestCase
private static final NodeRef RULE_NODE_REF = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, RULE_ID);
private static final Paging PAGING = Paging.DEFAULT;
private static final List<String> INCLUDE = emptyList();
private static final String ACTION_DEFINITION_NAME = "actionDefinitionName";
private static final Map<String, Serializable> DUMMY_PARAMS = Map.of("dummy-key", "dummy-value");
@Mock
private Nodes nodesMock;
@@ -85,17 +92,24 @@ public class RulesImplTest extends TestCase
private RuleService ruleServiceMock;
@Mock
private RuleLoader ruleLoaderMock;
@InjectMocks
private RulesImpl rules;
@Mock
private ActionParameterConverter actionParameterConverterMock;
@Mock
private ActionPermissionValidator actionPermissionValidatorMock;
@Mock
private org.alfresco.service.cmr.rule.Rule serviceRuleMock;
@Mock
private Rule ruleMock;
private org.alfresco.service.cmr.rule.Rule ruleModel = createRule(RULE_ID);
private CompositeAction compositeAction = new CompositeActionImpl(RULE_NODE_REF, "compositeActionId");
@InjectMocks
private RulesImpl rules;
@Before
@Override
public void setUp() throws Exception
{
MockitoAnnotations.openMocks(this);
given(nodeValidatorMock.validateFolderNode(any(), anyBoolean())).willReturn(FOLDER_NODE_REF);
given(nodeValidatorMock.validateRuleSetNode(any(), any())).willReturn(RULE_SET_NODE_REF);
given(nodeValidatorMock.validateRuleNode(any(), any())).willReturn(RULE_NODE_REF);
@@ -104,6 +118,8 @@ public class RulesImplTest extends TestCase
given(ruleServiceMock.getRules(FOLDER_NODE_REF)).willReturn(List.of(ruleModel));
given(ruleLoaderMock.loadRule(ruleModel, INCLUDE)).willReturn(ruleMock);
compositeAction.addAction(new ActionImpl(FOLDER_NODE_REF, "actionId", ACTION_DEFINITION_NAME, DUMMY_PARAMS));
}
@Test
@@ -119,9 +135,9 @@ public class RulesImplTest extends TestCase
then(ruleServiceMock).shouldHaveNoMoreInteractions();
assertThat(rulesPage)
.isNotNull()
.extracting(CollectionWithPagingInfo::getCollection)
.extracting(CollectionWithPagingInfo::getCollection)
.isNotNull()
.extracting(Collection::size)
.extracting(Collection::size)
.isEqualTo(1);
assertThat(rulesPage.getCollection().stream().findFirst().get()).isEqualTo(ruleMock);
}
@@ -137,11 +153,11 @@ public class RulesImplTest extends TestCase
then(ruleServiceMock).should().getRules(FOLDER_NODE_REF);
then(ruleServiceMock).shouldHaveNoMoreInteractions();
assertThat(rulesPage)
.isNotNull()
.extracting(CollectionWithPagingInfo::getCollection)
.isNotNull()
.extracting(Collection::isEmpty)
.isEqualTo(true);
.isNotNull()
.extracting(CollectionWithPagingInfo::getCollection)
.isNotNull()
.extracting(Collection::isEmpty)
.isEqualTo(true);
}
@Test
@@ -154,7 +170,7 @@ public class RulesImplTest extends TestCase
// when
assertThatExceptionOfType(exception.getClass()).isThrownBy(
() -> rules.getRules(FOLDER_NODE_ID, RULE_SET_ID, INCLUDE, PAGING));
() -> rules.getRules(FOLDER_NODE_ID, RULE_SET_ID, INCLUDE, PAGING));
then(nodeValidatorMock).should().validateFolderNode(FOLDER_NODE_ID, false);
then(nodeValidatorMock).shouldHaveNoMoreInteractions();
@@ -173,7 +189,7 @@ public class RulesImplTest extends TestCase
// when
assertThatExceptionOfType(exception.getClass()).isThrownBy(
() -> rules.getRules(FOLDER_NODE_ID, RULE_SET_ID, INCLUDE, PAGING));
() -> rules.getRules(FOLDER_NODE_ID, RULE_SET_ID, INCLUDE, PAGING));
then(nodeValidatorMock).should().validateFolderNode(FOLDER_NODE_ID, false);
then(nodeValidatorMock).should().validateRuleSetNode(RULE_SET_ID, FOLDER_NODE_REF);
@@ -208,7 +224,7 @@ public class RulesImplTest extends TestCase
// when
assertThatExceptionOfType(exception.getClass()).isThrownBy(
() -> rules.getRuleById(FOLDER_NODE_ID, RULE_SET_ID, RULE_ID, INCLUDE));
() -> rules.getRuleById(FOLDER_NODE_ID, RULE_SET_ID, RULE_ID, INCLUDE));
then(nodeValidatorMock).should().validateFolderNode(FOLDER_NODE_ID, false);
then(nodeValidatorMock).shouldHaveNoMoreInteractions();
@@ -227,7 +243,7 @@ public class RulesImplTest extends TestCase
// when
assertThatExceptionOfType(exception.getClass()).isThrownBy(
() -> rules.getRuleById(FOLDER_NODE_ID, RULE_SET_ID, RULE_ID, INCLUDE));
() -> rules.getRuleById(FOLDER_NODE_ID, RULE_SET_ID, RULE_ID, INCLUDE));
then(nodeValidatorMock).should().validateFolderNode(FOLDER_NODE_ID, false);
then(nodeValidatorMock).should().validateRuleSetNode(RULE_SET_ID, FOLDER_NODE_REF);
@@ -248,7 +264,7 @@ public class RulesImplTest extends TestCase
// when
assertThatExceptionOfType(exception.getClass()).isThrownBy(
() -> rules.getRuleById(FOLDER_NODE_ID, RULE_SET_ID, RULE_ID, INCLUDE));
() -> rules.getRuleById(FOLDER_NODE_ID, RULE_SET_ID, RULE_ID, INCLUDE));
then(nodeValidatorMock).should().validateFolderNode(FOLDER_NODE_ID, false);
then(nodeValidatorMock).should().validateRuleSetNode(RULE_SET_ID, FOLDER_NODE_REF);
@@ -258,17 +274,18 @@ public class RulesImplTest extends TestCase
}
}
/** Create a single rule. */
/**
* Create a single rule.
*/
@Test
public void testCreateRules()
{
Rule ruleBody = mock(Rule.class);
List<Rule> 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(FOLDER_NODE_REF, serviceRuleBody)).willReturn(serviceRule);
given(ruleLoaderMock.loadRule(serviceRule, INCLUDE)).willReturn(ruleMock);
List<Rule> ruleList = List.of(ruleMock);
given(ruleMock.toServiceModel(nodesMock)).willReturn(serviceRuleMock);
given(serviceRuleMock.getAction()).willReturn(compositeAction);
given(ruleServiceMock.saveRule(FOLDER_NODE_REF, serviceRuleMock)).willAnswer(arg -> arg.getArguments()[1]);
given(ruleLoaderMock.loadRule(serviceRuleMock, INCLUDE)).willReturn(ruleMock);
given(actionPermissionValidatorMock.validateRulePermissions(any())).willAnswer(arg -> arg.getArguments()[0]);
// when
List<Rule> actual = rules.createRules(FOLDER_NODE_REF.getId(), RULE_SET_NODE_REF.getId(), ruleList, INCLUDE);
@@ -276,32 +293,42 @@ public class RulesImplTest extends TestCase
then(nodeValidatorMock).should().validateFolderNode(FOLDER_NODE_ID, true);
then(nodeValidatorMock).should().validateRuleSetNode(RULE_SET_ID, FOLDER_NODE_REF);
then(nodeValidatorMock).shouldHaveNoMoreInteractions();
then(ruleServiceMock).should().saveRule(FOLDER_NODE_REF, ruleBody.toServiceModel(nodesMock));
then(actionParameterConverterMock).should().getConvertedParams(DUMMY_PARAMS, ACTION_DEFINITION_NAME);
then(actionParameterConverterMock).shouldHaveNoMoreInteractions();
then(actionPermissionValidatorMock).should().validateRulePermissions(serviceRuleMock);
then(actionPermissionValidatorMock).shouldHaveNoMoreInteractions();
then(ruleServiceMock).should().saveRule(FOLDER_NODE_REF, ruleMock.toServiceModel(nodesMock));
then(ruleServiceMock).shouldHaveNoMoreInteractions();
List<Rule> expected = List.of(ruleMock);
assertThat(actual).isEqualTo(expected);
}
/** Check that when passing the default rule set then we don't perform any validation around the rule set node. */
/**
* Check that when passing the default rule set then we don't perform any validation around the rule set node.
*/
@Test
public void testCreateRules_defaultRuleSet()
{
Rule ruleBody = mock(Rule.class);
List<Rule> 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(FOLDER_NODE_REF, serviceRuleBody)).willReturn(serviceRule);
given(ruleLoaderMock.loadRule(serviceRule, INCLUDE)).willReturn(ruleMock);
List<Rule> ruleList = List.of(ruleMock);
given(ruleMock.toServiceModel(nodesMock)).willReturn(serviceRuleMock);
given(ruleServiceMock.saveRule(FOLDER_NODE_REF, serviceRuleMock)).willAnswer(arg -> arg.getArguments()[1]);
given(ruleLoaderMock.loadRule(serviceRuleMock, INCLUDE)).willReturn(ruleMock);
given(serviceRuleMock.getAction()).willReturn(compositeAction);
given(actionPermissionValidatorMock.validateRulePermissions(any())).willAnswer(arg -> arg.getArguments()[0]);
// when
List<Rule> actual = rules.createRules(FOLDER_NODE_REF.getId(), DEFAULT_ID, ruleList, INCLUDE);
then(nodeValidatorMock).should().validateFolderNode(FOLDER_NODE_ID, true);
then(nodeValidatorMock).shouldHaveNoMoreInteractions();
then(ruleServiceMock).should().saveRule(FOLDER_NODE_REF, ruleBody.toServiceModel(nodesMock));
then(actionParameterConverterMock).should().getConvertedParams(DUMMY_PARAMS, ACTION_DEFINITION_NAME);
then(actionParameterConverterMock).shouldHaveNoMoreInteractions();
then(actionPermissionValidatorMock).should().validateRulePermissions(serviceRuleMock);
then(actionPermissionValidatorMock).shouldHaveNoMoreInteractions();
then(ruleServiceMock).should().saveRule(FOLDER_NODE_REF, ruleMock.toServiceModel(nodesMock));
then(ruleServiceMock).shouldHaveNoMoreInteractions();
assertThat(actual).isEqualTo(List.of(ruleMock));
List<Rule> expected = List.of(ruleMock);
assertThat(actual).isEqualTo(expected);
}
@Test
@@ -316,22 +343,27 @@ public class RulesImplTest extends TestCase
assertThat(actual).isEqualTo(emptyList());
}
/** Create three rules in a single call and check they are all passed to the RuleService. */
/**
* Create three rules in a single call and check they are all passed to the RuleService.
*/
@Test
public void testCreateRules_createMultipleRules()
{
List<Rule> ruleBodyList = new ArrayList<>();
List<Rule> expected = new ArrayList<>();
IntStream.range(0, 3).forEach(i -> {
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(FOLDER_NODE_REF, serviceRuleBody)).willReturn(serviceRule);
Rule ruleMock = mock(Rule.class);
given(ruleLoaderMock.loadRule(serviceRule, INCLUDE)).willReturn(ruleMock);
expected.add(ruleMock);
Rule ruleBodyMock = mock(Rule.class);
ruleBodyList.add(ruleBodyMock);
org.alfresco.service.cmr.rule.Rule serviceRuleMockInner = mock(org.alfresco.service.cmr.rule.Rule.class);
given(ruleBodyMock.toServiceModel(nodesMock)).willReturn(serviceRuleMockInner);
final CompositeAction compositeActionInner = new CompositeActionImpl(RULE_NODE_REF, "compositeActionInnerId");
compositeActionInner.addAction(new ActionImpl(FOLDER_NODE_REF, "actionInnerId", ACTION_DEFINITION_NAME, DUMMY_PARAMS));
given(serviceRuleMockInner.getAction()).willReturn(compositeActionInner);
given(ruleServiceMock.saveRule(FOLDER_NODE_REF, serviceRuleMockInner)).willAnswer(arg -> arg.getArguments()[1]);
Rule ruleMockInner = mock(Rule.class);
given(ruleLoaderMock.loadRule(serviceRuleMockInner, INCLUDE)).willReturn(ruleMockInner);
expected.add(ruleMockInner);
given(actionPermissionValidatorMock.validateRulePermissions(any())).willAnswer(arg -> arg.getArguments()[0]);
});
// when
@@ -342,8 +374,12 @@ public class RulesImplTest extends TestCase
then(nodeValidatorMock).shouldHaveNoMoreInteractions();
for (Rule ruleBody : ruleBodyList)
{
then(actionPermissionValidatorMock).should().validateRulePermissions(ruleBody.toServiceModel(nodesMock));
then(ruleServiceMock).should().saveRule(FOLDER_NODE_REF, ruleBody.toServiceModel(nodesMock));
}
then(actionParameterConverterMock).should(times(3)).getConvertedParams(DUMMY_PARAMS, ACTION_DEFINITION_NAME);
then(actionParameterConverterMock).shouldHaveNoMoreInteractions();
then(actionPermissionValidatorMock).shouldHaveNoMoreInteractions();
then(ruleServiceMock).shouldHaveNoMoreInteractions();
assertThat(actual).isEqualTo(expected);
}
@@ -358,7 +394,7 @@ public class RulesImplTest extends TestCase
// when
assertThatExceptionOfType(exception.getClass()).isThrownBy(
() -> rules.createRules(FOLDER_NODE_REF.getId(), RULE_SET_NODE_REF.getId(), emptyList(), INCLUDE));
() -> rules.createRules(FOLDER_NODE_REF.getId(), RULE_SET_NODE_REF.getId(), emptyList(), INCLUDE));
then(nodeValidatorMock).should().validateFolderNode(FOLDER_NODE_ID, true);
then(nodeValidatorMock).shouldHaveNoMoreInteractions();
@@ -377,7 +413,7 @@ public class RulesImplTest extends TestCase
// when
assertThatExceptionOfType(exception.getClass()).isThrownBy(
() -> rules.createRules(FOLDER_NODE_REF.getId(), RULE_SET_NODE_REF.getId(), emptyList(), INCLUDE));
() -> rules.createRules(FOLDER_NODE_REF.getId(), RULE_SET_NODE_REF.getId(), emptyList(), INCLUDE));
then(nodeValidatorMock).should().validateFolderNode(FOLDER_NODE_ID, true);
then(nodeValidatorMock).should().validateRuleSetNode(RULE_SET_ID, FOLDER_NODE_REF);
@@ -386,7 +422,9 @@ public class RulesImplTest extends TestCase
}
}
/** Check that we can update a rule. */
/**
* Check that we can update a rule.
*/
@Test
public void testUpdateRuleById()
{
@@ -419,7 +457,7 @@ public class RulesImplTest extends TestCase
// when
assertThatExceptionOfType(exception.getClass()).isThrownBy(
() -> rules.updateRuleById(FOLDER_NODE_REF.getId(), RULE_SET_NODE_REF.getId(), RULE_ID, mock(Rule.class), INCLUDE));
() -> rules.updateRuleById(FOLDER_NODE_REF.getId(), RULE_SET_NODE_REF.getId(), RULE_ID, mock(Rule.class), INCLUDE));
then(nodeValidatorMock).should().validateFolderNode(FOLDER_NODE_ID, true);
then(nodeValidatorMock).shouldHaveNoMoreInteractions();
@@ -438,7 +476,7 @@ public class RulesImplTest extends TestCase
// when
assertThatExceptionOfType(exception.getClass()).isThrownBy(
() -> rules.updateRuleById(FOLDER_NODE_REF.getId(), RULE_SET_NODE_REF.getId(), RULE_ID, mock(Rule.class), INCLUDE));
() -> rules.updateRuleById(FOLDER_NODE_REF.getId(), RULE_SET_NODE_REF.getId(), RULE_ID, mock(Rule.class), INCLUDE));
then(nodeValidatorMock).should().validateFolderNode(FOLDER_NODE_ID, true);
then(nodeValidatorMock).should().validateRuleSetNode(RULE_SET_ID, FOLDER_NODE_REF);
@@ -459,7 +497,7 @@ public class RulesImplTest extends TestCase
// when
assertThatExceptionOfType(exception.getClass()).isThrownBy(
() -> rules.updateRuleById(FOLDER_NODE_REF.getId(), RULE_SET_NODE_REF.getId(), RULE_ID, mock(Rule.class), INCLUDE));
() -> rules.updateRuleById(FOLDER_NODE_REF.getId(), RULE_SET_NODE_REF.getId(), RULE_ID, mock(Rule.class), INCLUDE));
then(nodeValidatorMock).should().validateFolderNode(FOLDER_NODE_ID, true);
then(nodeValidatorMock).should().validateRuleSetNode(RULE_SET_ID, FOLDER_NODE_REF);
@@ -470,7 +508,8 @@ public class RulesImplTest extends TestCase
}
@Test
public void testDeleteRuleById() {
public void testDeleteRuleById()
{
//when
rules.deleteRuleById(FOLDER_NODE_ID, RULE_SET_ID, RULE_ID);
@@ -494,7 +533,7 @@ public class RulesImplTest extends TestCase
// when
assertThatExceptionOfType(exception.getClass()).isThrownBy(
() -> rules.deleteRuleById(FOLDER_NODE_ID, RULE_SET_ID, RULE_ID));
() -> rules.deleteRuleById(FOLDER_NODE_ID, RULE_SET_ID, RULE_ID));
then(nodeValidatorMock).should().validateFolderNode(FOLDER_NODE_ID, true);
then(nodeValidatorMock).shouldHaveNoMoreInteractions();
@@ -513,7 +552,7 @@ public class RulesImplTest extends TestCase
// when
assertThatExceptionOfType(exception.getClass()).isThrownBy(
() -> rules.deleteRuleById(FOLDER_NODE_ID, RULE_SET_ID, RULE_ID));
() -> rules.deleteRuleById(FOLDER_NODE_ID, RULE_SET_ID, RULE_ID));
then(nodeValidatorMock).should().validateFolderNode(FOLDER_NODE_ID, true);
then(nodeValidatorMock).should().validateRuleSetNode(RULE_SET_ID, FOLDER_NODE_REF);
@@ -534,7 +573,7 @@ public class RulesImplTest extends TestCase
// when
assertThatExceptionOfType(exception.getClass()).isThrownBy(
() -> rules.deleteRuleById(FOLDER_NODE_ID, RULE_SET_ID, RULE_ID));
() -> rules.deleteRuleById(FOLDER_NODE_ID, RULE_SET_ID, RULE_ID));
then(nodeValidatorMock).should().validateFolderNode(FOLDER_NODE_ID, true);
then(nodeValidatorMock).should().validateRuleSetNode(RULE_SET_ID, FOLDER_NODE_REF);
@@ -544,7 +583,8 @@ public class RulesImplTest extends TestCase
}
}
private static org.alfresco.service.cmr.rule.Rule createRule(final String id) {
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(nodeRef);
@@ -556,27 +596,27 @@ public class RulesImplTest extends TestCase
private static List<Exception> folderValidationExceptions()
{
return List.of(
new EntityNotFoundException(FOLDER_NODE_ID),
new InvalidArgumentException(),
new PermissionDeniedException()
new EntityNotFoundException(FOLDER_NODE_ID),
new InvalidArgumentException(),
new PermissionDeniedException()
);
}
private static List<Exception> ruleSetValidationExceptions()
{
return List.of(
new EntityNotFoundException(RULE_SET_ID),
new InvalidArgumentException(),
new RelationshipResourceNotFoundException(RULE_SET_ID, "fake-relationship-id")
new EntityNotFoundException(RULE_SET_ID),
new InvalidArgumentException(),
new RelationshipResourceNotFoundException(RULE_SET_ID, "fake-relationship-id")
);
}
private static List<Exception> ruleValidationExceptions()
{
return List.of(
new EntityNotFoundException(RULE_ID),
new InvalidArgumentException(),
new RelationshipResourceNotFoundException(RULE_ID, "fake-relationship-id")
new EntityNotFoundException(RULE_ID),
new InvalidArgumentException(),
new RelationshipResourceNotFoundException(RULE_ID, "fake-relationship-id")
);
}
}