mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-09-24 14:32:01 +00:00
Compare commits
7 Commits
17.132
...
feature/AC
Author | SHA1 | Date | |
---|---|---|---|
|
d57d232656 | ||
|
bcdf2d6f44 | ||
|
e38dfda84e | ||
|
1433b1c18c | ||
|
3d0a191be7 | ||
|
2a1e825f45 | ||
|
31efee4c39 |
@@ -7,7 +7,7 @@
|
||||
<parent>
|
||||
<groupId>org.alfresco</groupId>
|
||||
<artifactId>alfresco-community-repo-amps</artifactId>
|
||||
<version>17.132</version>
|
||||
<version>17.128-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<modules>
|
||||
|
@@ -7,7 +7,7 @@
|
||||
<parent>
|
||||
<groupId>org.alfresco</groupId>
|
||||
<artifactId>alfresco-governance-services-community-parent</artifactId>
|
||||
<version>17.132</version>
|
||||
<version>17.128-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<modules>
|
||||
|
@@ -7,7 +7,7 @@
|
||||
<parent>
|
||||
<groupId>org.alfresco</groupId>
|
||||
<artifactId>alfresco-governance-services-automation-community-repo</artifactId>
|
||||
<version>17.132</version>
|
||||
<version>17.128-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<build>
|
||||
|
@@ -7,7 +7,7 @@
|
||||
<parent>
|
||||
<groupId>org.alfresco</groupId>
|
||||
<artifactId>alfresco-governance-services-community-parent</artifactId>
|
||||
<version>17.132</version>
|
||||
<version>17.128-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<modules>
|
||||
|
@@ -8,7 +8,7 @@
|
||||
<parent>
|
||||
<groupId>org.alfresco</groupId>
|
||||
<artifactId>alfresco-governance-services-community-repo-parent</artifactId>
|
||||
<version>17.132</version>
|
||||
<version>17.128-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<properties>
|
||||
|
@@ -38,12 +38,9 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.alfresco.model.ContentModel;
|
||||
import org.alfresco.module.org_alfresco_module_rm.capability.CapabilityService;
|
||||
import org.alfresco.module.org_alfresco_module_rm.capability.impl.ViewRecordsCapability;
|
||||
import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionAction;
|
||||
import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionActionDefinition;
|
||||
import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionService;
|
||||
import org.alfresco.module.org_alfresco_module_rm.event.EventCompletionDetails;
|
||||
import org.alfresco.module.org_alfresco_module_rm.fileplan.FilePlanComponentKind;
|
||||
@@ -79,7 +76,6 @@ import org.json.simple.JSONObject;
|
||||
*
|
||||
* @author Roy Wetherall
|
||||
*/
|
||||
@Slf4j
|
||||
public class JSONConversionComponent extends org.alfresco.repo.jscript.app.JSONConversionComponent
|
||||
implements NodeServicePolicies.OnDeleteNodePolicy,
|
||||
NodeServicePolicies.OnCreateNodePolicy
|
||||
@@ -519,25 +515,17 @@ public class JSONConversionComponent extends org.alfresco.repo.jscript.app.JS
|
||||
|
||||
AuthenticationUtil.runAsSystem((RunAsWork<Void>) () -> {
|
||||
//Add details of the next incomplete event in the disposition schedule
|
||||
DispositionAction nextDispositionAction = dispositionService.getNextDispositionAction(nodeRef);
|
||||
if (nextDispositionAction != null)
|
||||
if (dispositionService.getNextDispositionAction(nodeRef) != null)
|
||||
{
|
||||
for (EventCompletionDetails details : nextDispositionAction.getEventCompletionDetails())
|
||||
for (EventCompletionDetails details : dispositionService.getNextDispositionAction(nodeRef).getEventCompletionDetails())
|
||||
{
|
||||
if (!details.isEventComplete())
|
||||
{
|
||||
DispositionActionDefinition dispositionActionDefinition = nextDispositionAction.getDispositionActionDefinition();
|
||||
HashMap properties = (HashMap) rmNodeValues.get("properties");
|
||||
properties.put("combineDispositionStepConditions", nodeService.getProperty(dispositionService.getNextDispositionAction(nodeRef).getDispositionActionDefinition().getNodeRef(), PROP_COMBINE_DISPOSITION_STEP_CONDITIONS));
|
||||
properties.put("incompleteDispositionEvent", details.getEventName());
|
||||
if(dispositionActionDefinition == null)
|
||||
{
|
||||
log.debug("Disposition action definition for disposition action "+ nextDispositionAction.getName() +" has been removed or never exist");
|
||||
}
|
||||
else
|
||||
{
|
||||
properties.put("combineDispositionStepConditions", nodeService.getProperty(dispositionActionDefinition.getNodeRef(), PROP_COMBINE_DISPOSITION_STEP_CONDITIONS));
|
||||
properties.put("dispositionEventCombination", nodeService.getProperty(dispositionActionDefinition.getNodeRef(), PROP_DISPOSITION_EVENT_COMBINATION));
|
||||
}
|
||||
properties.put("dispositionEventCombination", nodeService.getProperty(dispositionService.getNextDispositionAction(nodeRef).getDispositionActionDefinition().getNodeRef(), PROP_DISPOSITION_EVENT_COMBINATION));
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@@ -7,7 +7,7 @@
|
||||
<parent>
|
||||
<groupId>org.alfresco</groupId>
|
||||
<artifactId>alfresco-governance-services-community-repo-parent</artifactId>
|
||||
<version>17.132</version>
|
||||
<version>17.128-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<build>
|
||||
|
@@ -7,7 +7,7 @@
|
||||
<parent>
|
||||
<groupId>org.alfresco</groupId>
|
||||
<artifactId>alfresco-community-repo</artifactId>
|
||||
<version>17.132</version>
|
||||
<version>17.128-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<modules>
|
||||
|
@@ -8,7 +8,7 @@
|
||||
<parent>
|
||||
<groupId>org.alfresco</groupId>
|
||||
<artifactId>alfresco-community-repo-amps</artifactId>
|
||||
<version>17.132</version>
|
||||
<version>17.128-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<properties>
|
||||
|
@@ -7,7 +7,7 @@
|
||||
<parent>
|
||||
<groupId>org.alfresco</groupId>
|
||||
<artifactId>alfresco-community-repo</artifactId>
|
||||
<version>17.132</version>
|
||||
<version>17.128-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<dependencies>
|
||||
|
@@ -7,7 +7,7 @@
|
||||
<parent>
|
||||
<groupId>org.alfresco</groupId>
|
||||
<artifactId>alfresco-community-repo</artifactId>
|
||||
<version>17.132</version>
|
||||
<version>17.128-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<properties>
|
||||
|
@@ -7,7 +7,7 @@
|
||||
<parent>
|
||||
<groupId>org.alfresco</groupId>
|
||||
<artifactId>alfresco-community-repo</artifactId>
|
||||
<version>17.132</version>
|
||||
<version>17.128-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<dependencies>
|
||||
|
@@ -9,6 +9,6 @@
|
||||
<parent>
|
||||
<groupId>org.alfresco</groupId>
|
||||
<artifactId>alfresco-community-repo-packaging</artifactId>
|
||||
<version>17.132</version>
|
||||
<version>17.128-SNAPSHOT</version>
|
||||
</parent>
|
||||
</project>
|
||||
|
@@ -7,7 +7,7 @@
|
||||
<parent>
|
||||
<groupId>org.alfresco</groupId>
|
||||
<artifactId>alfresco-community-repo-packaging</artifactId>
|
||||
<version>17.132</version>
|
||||
<version>17.128-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<properties>
|
||||
|
@@ -7,7 +7,7 @@
|
||||
<parent>
|
||||
<groupId>org.alfresco</groupId>
|
||||
<artifactId>alfresco-community-repo</artifactId>
|
||||
<version>17.132</version>
|
||||
<version>17.128-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<modules>
|
||||
|
@@ -6,7 +6,7 @@
|
||||
<parent>
|
||||
<groupId>org.alfresco</groupId>
|
||||
<artifactId>alfresco-community-repo-packaging</artifactId>
|
||||
<version>17.132</version>
|
||||
<version>17.128-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<modules>
|
||||
|
@@ -9,7 +9,7 @@
|
||||
<parent>
|
||||
<groupId>org.alfresco</groupId>
|
||||
<artifactId>alfresco-community-repo-tests</artifactId>
|
||||
<version>17.132</version>
|
||||
<version>17.128-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<developers>
|
||||
|
@@ -9,7 +9,7 @@
|
||||
<parent>
|
||||
<groupId>org.alfresco</groupId>
|
||||
<artifactId>alfresco-community-repo-tests</artifactId>
|
||||
<version>17.132</version>
|
||||
<version>17.128-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<developers>
|
||||
|
@@ -9,7 +9,7 @@
|
||||
<parent>
|
||||
<groupId>org.alfresco</groupId>
|
||||
<artifactId>alfresco-community-repo-tests</artifactId>
|
||||
<version>17.132</version>
|
||||
<version>17.128-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<developers>
|
||||
|
@@ -9,7 +9,7 @@
|
||||
<parent>
|
||||
<groupId>org.alfresco</groupId>
|
||||
<artifactId>alfresco-community-repo-tests</artifactId>
|
||||
<version>17.132</version>
|
||||
<version>17.128-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<developers>
|
||||
|
@@ -93,6 +93,7 @@ public class CreateRulesTests extends RestTest
|
||||
.createSingleRule(ruleModel);
|
||||
|
||||
RestRuleModel expectedRuleModel = createRuleModelWithModifiedValues();
|
||||
expectedRuleModel.setConditions(createEmptyConditionModel());
|
||||
restClient.assertStatusCodeIs(CREATED);
|
||||
rule.assertThat().isEqualTo(expectedRuleModel, ID, IS_SHARED)
|
||||
.assertThat().field(ID).isNotNull()
|
||||
@@ -382,6 +383,7 @@ public class CreateRulesTests extends RestTest
|
||||
|
||||
final RestRuleModel expectedRuleModel = createRuleModelWithDefaultValues();
|
||||
expectedRuleModel.setActions(Arrays.asList(copyAction, checkOutAction, scriptAction));
|
||||
expectedRuleModel.setConditions(createEmptyConditionModel());
|
||||
expectedRuleModel.setTriggers(List.of("inbound"));
|
||||
|
||||
restClient.assertStatusCodeIs(CREATED);
|
||||
@@ -457,6 +459,7 @@ public class CreateRulesTests extends RestTest
|
||||
.createSingleRule(ruleModel);
|
||||
|
||||
RestRuleModel expectedRuleModel = createRuleModelWithDefaultValues();
|
||||
expectedRuleModel.setConditions(createCompositeCondition(null));
|
||||
expectedRuleModel.setTriggers(List.of("inbound"));
|
||||
restClient.assertStatusCodeIs(CREATED);
|
||||
rule.assertThat().isEqualTo(expectedRuleModel, ID, IS_SHARED);
|
||||
|
@@ -190,6 +190,7 @@ public class GetRulesTests extends RestTest
|
||||
|
||||
RestRuleModel expectedRuleModel = createRuleModelWithModifiedValues();
|
||||
expectedRuleModel.setTriggers(List.of("update"));
|
||||
expectedRuleModel.setConditions(createEmptyConditionModel());
|
||||
|
||||
restClient.assertStatusCodeIs(CREATED);
|
||||
rule.assertThat().isEqualTo(expectedRuleModel, IGNORE_ID, IGNORE_IS_SHARED)
|
||||
@@ -211,6 +212,7 @@ public class GetRulesTests extends RestTest
|
||||
|
||||
RestRuleModel expectedRuleModel = createRuleModelWithDefaultValues();
|
||||
expectedRuleModel.setTriggers(List.of("inbound"));
|
||||
expectedRuleModel.setConditions(createEmptyConditionModel());
|
||||
|
||||
restClient.assertStatusCodeIs(CREATED);
|
||||
|
||||
|
@@ -324,7 +324,7 @@ public class UpdateRulesTests extends RestTest
|
||||
final RestRuleModel rule = createAndSaveRule(createRuleModelWithModifiedValues());
|
||||
|
||||
STEP("Try to update the rule and add null conditions.");
|
||||
rule.setConditions(null);
|
||||
rule.setConditions(createCompositeCondition(null));
|
||||
|
||||
final RestRuleModel updatedRule = restClient.authenticateUser(user).withCoreAPI().usingNode(ruleFolder).usingDefaultRuleSet()
|
||||
.updateRule(rule.getId(), rule);
|
||||
@@ -369,6 +369,8 @@ public class UpdateRulesTests extends RestTest
|
||||
final RestRuleModel updatedRule = restClient.authenticateUser(user).withCoreAPI().usingNode(ruleFolder).usingDefaultRuleSet()
|
||||
.updateRule(rule.getId(), rule);
|
||||
|
||||
//set expected object
|
||||
rule.setConditions(createCompositeCondition(null));
|
||||
restClient.assertStatusCodeIs(OK);
|
||||
updatedRule.assertThat().isEqualTo(rule, ID)
|
||||
.assertThat().field(ID).isNotNull();
|
||||
@@ -395,7 +397,7 @@ public class UpdateRulesTests extends RestTest
|
||||
restClient.assertLastError().containsSummary("Category in condition is invalid");
|
||||
}
|
||||
|
||||
/** Check we get a 400 error when using the POST response and update rule by adding condition without comparator when it is required. */
|
||||
/** Check we get a 500 error when using the POST response and update rule by adding condition without comparator. */
|
||||
@Test (groups = { TestGroup.REST_API, TestGroup.RULES, TestGroup.SANITY })
|
||||
public void updateRuleWithConditionWithoutComparatorAndFail()
|
||||
{
|
||||
@@ -403,7 +405,7 @@ public class UpdateRulesTests extends RestTest
|
||||
ruleModelWithInitialValues.setConditions(createVariousConditions());
|
||||
final RestRuleModel rule = createAndSaveRule(ruleModelWithInitialValues);
|
||||
|
||||
STEP("Try to update the rule with invalid condition (null comparator when required non-null).");
|
||||
STEP("Try to update the rule with invalid condition.");
|
||||
final RestCompositeConditionDefinitionModel conditions = createCompositeCondition(
|
||||
List.of(createCompositeCondition(!INVERTED, List.of(createSimpleCondition("size", null, "65500")))));
|
||||
rule.setConditions(conditions);
|
||||
@@ -412,11 +414,11 @@ public class UpdateRulesTests extends RestTest
|
||||
.include(IS_SHARED)
|
||||
.updateRule(rule.getId(), rule);
|
||||
|
||||
restClient.assertStatusCodeIs(BAD_REQUEST);
|
||||
restClient.assertLastError().containsSummary("Comparator in condition must not be blank");
|
||||
//TODO: in next iteration of mapper refactoring this error code will change to 400
|
||||
restClient.assertStatusCodeIs(INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
|
||||
/** Check we get a 400 error when using the POST response and update rule by adding condition without field. */
|
||||
/** Check we get a 500 error when using the POST response and update rule by adding condition without field. */
|
||||
@Test (groups = { TestGroup.REST_API, TestGroup.RULES, TestGroup.SANITY })
|
||||
public void updateRuleWithConditionWithoutFieldAndFail()
|
||||
{
|
||||
@@ -424,7 +426,7 @@ public class UpdateRulesTests extends RestTest
|
||||
ruleModelWithInitialValues.setConditions(createVariousConditions());
|
||||
final RestRuleModel rule = createAndSaveRule(ruleModelWithInitialValues);
|
||||
|
||||
STEP("Try to update the rule with invalid condition (null field).");
|
||||
STEP("Try to update the rule with invalid condition.");
|
||||
final RestCompositeConditionDefinitionModel conditions = createCompositeCondition(
|
||||
List.of(createCompositeCondition(!INVERTED, List.of(createSimpleCondition(null, "greater_than", "65500")))));
|
||||
rule.setConditions(conditions);
|
||||
@@ -433,11 +435,11 @@ public class UpdateRulesTests extends RestTest
|
||||
.include(IS_SHARED)
|
||||
.updateRule(rule.getId(), rule);
|
||||
|
||||
restClient.assertStatusCodeIs(BAD_REQUEST);
|
||||
restClient.assertLastError().containsSummary("Field in condition must not be blank");
|
||||
//TODO: in next iteration of mapper refactoring this error code will change to 400
|
||||
restClient.assertStatusCodeIs(INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
|
||||
/** Check we get a 400 error when using the POST response and update rule by adding condition without parameter value. */
|
||||
/** Check we get a 500 error when using the POST response and update rule by adding condition without parameter value. */
|
||||
@Test (groups = { TestGroup.REST_API, TestGroup.RULES, TestGroup.SANITY })
|
||||
public void updateRuleWithConditionWithoutParamValueAndFail()
|
||||
{
|
||||
@@ -445,17 +447,17 @@ public class UpdateRulesTests extends RestTest
|
||||
ruleModelWithInitialValues.setConditions(createVariousConditions());
|
||||
final RestRuleModel rule = createAndSaveRule(ruleModelWithInitialValues);
|
||||
|
||||
STEP("Try to update the rule with invalid condition (null parameter).");
|
||||
STEP("Try to update the rule with invalid condition.");
|
||||
final RestCompositeConditionDefinitionModel conditions = createCompositeCondition(
|
||||
List.of(createCompositeCondition(!INVERTED, List.of(createSimpleCondition("size", "greater_than", "")))));
|
||||
List.of(createCompositeCondition(!INVERTED, List.of(createSimpleCondition("size", "greater_than", null)))));
|
||||
rule.setConditions(conditions);
|
||||
|
||||
restClient.authenticateUser(user).withCoreAPI().usingNode(ruleFolder).usingDefaultRuleSet()
|
||||
.include(IS_SHARED)
|
||||
.updateRule(rule.getId(), rule);
|
||||
|
||||
restClient.assertStatusCodeIs(BAD_REQUEST);
|
||||
restClient.assertLastError().containsSummary("Parameter in condition must not be blank");
|
||||
//TODO: in next iteration of mapper refactoring this error code will change to 400
|
||||
restClient.assertStatusCodeIs(INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
|
||||
private RestRuleModel createAndSaveRule(String name)
|
||||
|
@@ -35,11 +35,10 @@ public class GetDeploymentsSanityTests extends RestTest
|
||||
restClient.assertStatusCodeIs(HttpStatus.OK);
|
||||
deployments.assertThat().entriesListIsNotEmpty();
|
||||
deployments.getOneRandomEntry().onModel().assertThat()
|
||||
.fieldsCount().is(4).and()
|
||||
.fieldsCount().is(3).and()
|
||||
.field("id").isNotEmpty().and()
|
||||
.field("deployedAt").isNotEmpty().and()
|
||||
.field("name").isNotEmpty().and()
|
||||
.field("category").isNotEmpty();
|
||||
.field("name").isNotEmpty();
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -9,7 +9,7 @@
|
||||
<parent>
|
||||
<groupId>org.alfresco</groupId>
|
||||
<artifactId>alfresco-community-repo-tests</artifactId>
|
||||
<version>17.132</version>
|
||||
<version>17.128-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<developers>
|
||||
|
@@ -7,7 +7,7 @@
|
||||
<parent>
|
||||
<groupId>org.alfresco</groupId>
|
||||
<artifactId>alfresco-community-repo-packaging</artifactId>
|
||||
<version>17.132</version>
|
||||
<version>17.128-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<properties>
|
||||
|
4
pom.xml
4
pom.xml
@@ -2,7 +2,7 @@
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>alfresco-community-repo</artifactId>
|
||||
<version>17.132</version>
|
||||
<version>17.128-SNAPSHOT</version>
|
||||
<packaging>pom</packaging>
|
||||
<name>Alfresco Community Repo Parent</name>
|
||||
|
||||
@@ -151,7 +151,7 @@
|
||||
<connection>scm:git:https://github.com/Alfresco/alfresco-community-repo.git</connection>
|
||||
<developerConnection>scm:git:https://github.com/Alfresco/alfresco-community-repo.git</developerConnection>
|
||||
<url>https://github.com/Alfresco/alfresco-community-repo</url>
|
||||
<tag>17.132</tag>
|
||||
<tag>HEAD</tag>
|
||||
</scm>
|
||||
|
||||
<distributionManagement>
|
||||
|
@@ -7,7 +7,7 @@
|
||||
<parent>
|
||||
<groupId>org.alfresco</groupId>
|
||||
<artifactId>alfresco-community-repo</artifactId>
|
||||
<version>17.132</version>
|
||||
<version>17.128-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<dependencies>
|
||||
|
@@ -1,151 +0,0 @@
|
||||
/*
|
||||
* #%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.mapper.rules;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.alfresco.rest.api.model.mapper.RestModelMapper;
|
||||
import org.alfresco.rest.api.model.rules.CompositeCondition;
|
||||
import org.alfresco.rest.api.model.rules.ConditionOperator;
|
||||
import org.alfresco.rest.api.model.rules.SimpleCondition;
|
||||
import org.alfresco.service.Experimental;
|
||||
import org.alfresco.service.cmr.action.ActionCondition;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
|
||||
@Experimental
|
||||
public class RestRuleCompositeConditionModelMapper implements RestModelMapper<CompositeCondition, ActionCondition>
|
||||
{
|
||||
private final RestModelMapper<SimpleCondition, ActionCondition> simpleConditionMapper;
|
||||
|
||||
public RestRuleCompositeConditionModelMapper(
|
||||
RestModelMapper<SimpleCondition, ActionCondition> simpleConditionMapper)
|
||||
{
|
||||
this.simpleConditionMapper = simpleConditionMapper;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts Action conditions (service POJO) list to composite condition (REST model).
|
||||
*
|
||||
* @param actionConditions - list of {@link ActionCondition} service POJOs
|
||||
* @return {@link CompositeCondition} REST model
|
||||
*/
|
||||
@Override
|
||||
public CompositeCondition toRestModel(final Collection<ActionCondition> actionConditions)
|
||||
{
|
||||
if (CollectionUtils.isEmpty(actionConditions))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
final CompositeCondition conditions = new CompositeCondition();
|
||||
conditions.setCompositeConditions(new ArrayList<>());
|
||||
// group action conditions by inversion flag
|
||||
actionConditions.stream().filter(Objects::nonNull).collect(Collectors.groupingBy(ActionCondition::getInvertCondition))
|
||||
// map action condition sub lists
|
||||
.forEach((inverted, actionConditionsPart) -> Optional
|
||||
.ofNullable(ofActionConditions(actionConditionsPart, inverted, ConditionOperator.AND))
|
||||
// if composite condition present add to final list
|
||||
.ifPresent(compositeCondition -> conditions.getCompositeConditions().add(compositeCondition)));
|
||||
|
||||
if (CollectionUtils.isEmpty(conditions.getCompositeConditions()))
|
||||
{
|
||||
conditions.setCompositeConditions(null);
|
||||
}
|
||||
return conditions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ActionCondition> toServiceModels(final CompositeCondition compositeCondition)
|
||||
{
|
||||
final List<ActionCondition> actionConditions = new ArrayList<>();
|
||||
if (compositeCondition == null)
|
||||
{
|
||||
return actionConditions;
|
||||
}
|
||||
if (CollectionUtils.isNotEmpty(compositeCondition.getSimpleConditions()))
|
||||
{
|
||||
compositeCondition.getSimpleConditions()
|
||||
.forEach(simpleCondition -> actionConditions.add(mapSimpleCondition(simpleCondition, compositeCondition.isInverted())));
|
||||
}
|
||||
if (CollectionUtils.isNotEmpty(compositeCondition.getCompositeConditions()))
|
||||
{
|
||||
compositeCondition.getCompositeConditions().forEach(condition -> actionConditions.addAll(toServiceModels(condition)));
|
||||
}
|
||||
|
||||
return actionConditions;
|
||||
}
|
||||
|
||||
private ActionCondition mapSimpleCondition(final SimpleCondition simpleCondition, final boolean inverted)
|
||||
{
|
||||
final ActionCondition actionCondition = simpleConditionMapper.toServiceModel(simpleCondition);
|
||||
actionCondition.setInvertCondition(inverted);
|
||||
return actionCondition;
|
||||
}
|
||||
|
||||
private CompositeCondition ofActionConditions(final List<ActionCondition> actionConditions, final boolean inverted,
|
||||
final ConditionOperator conditionOperator)
|
||||
{
|
||||
if (actionConditions == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return ofSimpleConditions(simpleConditionMapper.toRestModels(actionConditions), inverted, conditionOperator);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a composite condition instance of simple conditions.
|
||||
*
|
||||
* @param simpleConditions - list of {@link SimpleCondition}
|
||||
* @param inverted - determines if condition should be inverted
|
||||
* @param conditionOperator - determines the operation, see {@link ConditionOperator}
|
||||
* @return {@link CompositeCondition}
|
||||
*/
|
||||
private CompositeCondition ofSimpleConditions(final List<SimpleCondition> simpleConditions, final boolean inverted,
|
||||
final ConditionOperator conditionOperator)
|
||||
{
|
||||
return of(simpleConditions, null, inverted, conditionOperator);
|
||||
}
|
||||
|
||||
private CompositeCondition of(final List<SimpleCondition> simpleConditions, final List<CompositeCondition> compositeConditions,
|
||||
final boolean inverted, final ConditionOperator conditionOperator)
|
||||
{
|
||||
if (CollectionUtils.isEmpty(simpleConditions) && CollectionUtils.isEmpty(compositeConditions))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return CompositeCondition.builder()
|
||||
.inverted(inverted)
|
||||
.booleanMode(conditionOperator)
|
||||
.simpleConditions(simpleConditions)
|
||||
.compositeConditions(compositeConditions)
|
||||
.create();
|
||||
}
|
||||
}
|
@@ -31,7 +31,6 @@ import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
import com.rometools.utils.Strings;
|
||||
import org.alfresco.model.ContentModel;
|
||||
import org.alfresco.repo.action.ActionConditionImpl;
|
||||
import org.alfresco.repo.action.evaluator.CompareMimeTypeEvaluator;
|
||||
@@ -58,12 +57,6 @@ import org.apache.commons.collections.MapUtils;
|
||||
@Experimental
|
||||
public class RestRuleSimpleConditionModelMapper implements RestModelMapper<SimpleCondition, ActionCondition>
|
||||
{
|
||||
static final String CATEGORY_INVALID_MSG = "Category in condition is invalid";
|
||||
static final String PARAM_CATEGORY = "category";
|
||||
static final String PARAM_MIMETYPE = "mimetype";
|
||||
static final String FIELD_NOT_NULL = "Field in condition must not be blank";
|
||||
static final String PARAMETER_NOT_NULL = "Parameter in condition must not be blank";
|
||||
static final String COMPARATOR_NOT_NULL = "Comparator in condition must not be blank";
|
||||
private final NamespaceService namespaceService;
|
||||
private final Nodes nodes;
|
||||
|
||||
@@ -106,12 +99,14 @@ public class RestRuleSimpleConditionModelMapper implements RestModelMapper<Simpl
|
||||
public ActionCondition toServiceModel(SimpleCondition restModel)
|
||||
{
|
||||
final String field = restModel.getField();
|
||||
checkStringNotBlank(field, FIELD_NOT_NULL);
|
||||
if (field == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
final Map<String, Serializable> parameterValues = new HashMap<>();
|
||||
Map<String, Serializable> parameterValues = new HashMap<>();
|
||||
String conditionDefinitionId;
|
||||
final String parameter = restModel.getParameter();
|
||||
checkStringNotBlank(parameter, PARAMETER_NOT_NULL);
|
||||
String parameter = restModel.getParameter();
|
||||
|
||||
switch (field)
|
||||
{
|
||||
@@ -123,21 +118,21 @@ public class RestRuleSimpleConditionModelMapper implements RestModelMapper<Simpl
|
||||
conditionDefinitionId = HasTagEvaluator.NAME;
|
||||
parameterValues.put(HasTagEvaluator.PARAM_TAG, parameter);
|
||||
break;
|
||||
case PARAM_CATEGORY:
|
||||
case SimpleCondition.PARAM_CATEGORY:
|
||||
conditionDefinitionId = InCategoryEvaluator.NAME;
|
||||
parameterValues.put(InCategoryEvaluator.PARAM_CATEGORY_ASPECT, ContentModel.ASPECT_GEN_CLASSIFIABLE);
|
||||
try
|
||||
{
|
||||
parameterValues.put(InCategoryEvaluator.PARAM_CATEGORY_VALUE, nodes.validateOrLookupNode(parameter, null));
|
||||
} catch (EntityNotFoundException e) {
|
||||
throw new InvalidArgumentException(CATEGORY_INVALID_MSG);
|
||||
throw new InvalidArgumentException(SimpleCondition.CATEGORY_INVALID_MSG);
|
||||
}
|
||||
break;
|
||||
case IsSubTypeEvaluator.PARAM_TYPE:
|
||||
conditionDefinitionId = IsSubTypeEvaluator.NAME;
|
||||
parameterValues.put(IsSubTypeEvaluator.PARAM_TYPE, QName.createQName(parameter, namespaceService));
|
||||
break;
|
||||
case PARAM_MIMETYPE:
|
||||
case SimpleCondition.PARAM_MIMETYPE:
|
||||
conditionDefinitionId = CompareMimeTypeEvaluator.NAME;
|
||||
parameterValues.put(ComparePropertyValueEvaluator.PARAM_PROPERTY, ContentModel.TYPE_CONTENT);
|
||||
parameterValues.put(ComparePropertyValueEvaluator.PARAM_VALUE, parameter);
|
||||
@@ -156,7 +151,6 @@ public class RestRuleSimpleConditionModelMapper implements RestModelMapper<Simpl
|
||||
// else create common property evaluator
|
||||
parameterValues.put(ComparePropertyValueEvaluator.PARAM_PROPERTY, QName.createQName(field, namespaceService));
|
||||
}
|
||||
checkStringNotBlank(restModel.getComparator(), COMPARATOR_NOT_NULL);
|
||||
parameterValues.put(ComparePropertyValueEvaluator.PARAM_OPERATION, restModel.getComparator().toUpperCase());
|
||||
parameterValues.put(ComparePropertyValueEvaluator.PARAM_VALUE, parameter);
|
||||
break;
|
||||
@@ -164,13 +158,6 @@ public class RestRuleSimpleConditionModelMapper implements RestModelMapper<Simpl
|
||||
return new ActionConditionImpl(UUID.randomUUID().toString(), conditionDefinitionId, parameterValues);
|
||||
}
|
||||
|
||||
private void checkStringNotBlank(final String string, final String message) {
|
||||
if (Strings.isBlank(string))
|
||||
{
|
||||
throw new InvalidArgumentException(message);
|
||||
}
|
||||
}
|
||||
|
||||
private static SimpleCondition createComparePropertyValueCondition(final ActionCondition actionCondition, final NamespaceService namespaceService)
|
||||
{
|
||||
final SimpleCondition.Builder builder = SimpleCondition.builder();
|
||||
@@ -189,7 +176,7 @@ public class RestRuleSimpleConditionModelMapper implements RestModelMapper<Simpl
|
||||
private static SimpleCondition createCompareMimeTypeCondition(final ActionCondition actionCondition)
|
||||
{
|
||||
return SimpleCondition.builder()
|
||||
.field(PARAM_MIMETYPE)
|
||||
.field(SimpleCondition.PARAM_MIMETYPE)
|
||||
.comparator(ComparePropertyValueOperation.EQUALS.toString().toLowerCase())
|
||||
.parameter(actionCondition.getParameterValues().get(ComparePropertyValueEvaluator.PARAM_VALUE).toString())
|
||||
.create();
|
||||
@@ -216,7 +203,7 @@ public class RestRuleSimpleConditionModelMapper implements RestModelMapper<Simpl
|
||||
private static SimpleCondition createInCategoryCondition(final ActionCondition actionCondition)
|
||||
{
|
||||
return SimpleCondition.builder()
|
||||
.field(PARAM_CATEGORY)
|
||||
.field(SimpleCondition.PARAM_CATEGORY)
|
||||
.comparator(ComparePropertyValueOperation.EQUALS.toString().toLowerCase())
|
||||
.parameter(((NodeRef) actionCondition.getParameterValues().get(InCategoryEvaluator.PARAM_CATEGORY_VALUE)).getId())
|
||||
.create();
|
||||
|
@@ -28,13 +28,13 @@ package org.alfresco.rest.api.impl.rules;
|
||||
import java.util.List;
|
||||
|
||||
import org.alfresco.rest.api.model.mapper.RestModelMapper;
|
||||
import org.alfresco.rest.api.model.rules.CompositeCondition;
|
||||
import org.alfresco.rest.api.model.rules.Rule;
|
||||
import org.alfresco.rest.api.model.rules.SimpleCondition;
|
||||
import org.alfresco.service.Experimental;
|
||||
import org.alfresco.service.cmr.action.ActionCondition;
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
import org.alfresco.service.cmr.rule.RuleService;
|
||||
import org.alfresco.service.namespace.NamespaceService;
|
||||
|
||||
/** Responsible for creating {@link Rule} objects. */
|
||||
@Experimental
|
||||
@@ -43,11 +43,11 @@ public class RuleLoader
|
||||
public static final String IS_SHARED = "isShared";
|
||||
private RuleService ruleService;
|
||||
private NodeValidator nodeValidator;
|
||||
private RestModelMapper<CompositeCondition, ActionCondition> compositeConditionMapper;
|
||||
private RestModelMapper<SimpleCondition, ActionCondition> simpleConditionMapper;
|
||||
|
||||
public Rule loadRule(org.alfresco.service.cmr.rule.Rule ruleModel, List<String> includes)
|
||||
{
|
||||
Rule rule = Rule.from(ruleModel, compositeConditionMapper);
|
||||
Rule rule = Rule.from(ruleModel, simpleConditionMapper);
|
||||
if (includes != null && includes.contains(IS_SHARED))
|
||||
{
|
||||
NodeRef ruleSet = ruleService.getRuleSetNode(ruleModel.getNodeRef());
|
||||
@@ -67,9 +67,9 @@ public class RuleLoader
|
||||
this.nodeValidator = nodeValidator;
|
||||
}
|
||||
|
||||
public void setCompositeConditionMapper(
|
||||
RestModelMapper<CompositeCondition, ActionCondition> compositeConditionMapper)
|
||||
public void setSimpleConditionMapper(
|
||||
RestModelMapper<SimpleCondition, ActionCondition> simpleConditionMapper)
|
||||
{
|
||||
this.compositeConditionMapper = compositeConditionMapper;
|
||||
this.simpleConditionMapper = simpleConditionMapper;
|
||||
}
|
||||
}
|
||||
|
@@ -33,7 +33,6 @@ import java.util.stream.Collectors;
|
||||
import org.alfresco.rest.api.Nodes;
|
||||
import org.alfresco.rest.api.Rules;
|
||||
import org.alfresco.rest.api.model.mapper.RestModelMapper;
|
||||
import org.alfresco.rest.api.model.rules.CompositeCondition;
|
||||
import org.alfresco.rest.api.model.rules.Rule;
|
||||
import org.alfresco.rest.api.model.rules.RuleSet;
|
||||
import org.alfresco.rest.framework.core.exceptions.InvalidArgumentException;
|
||||
@@ -64,7 +63,7 @@ public class RulesImpl implements Rules
|
||||
private RuleLoader ruleLoader;
|
||||
private ActionParameterConverter actionParameterConverter;
|
||||
private ActionPermissionValidator actionPermissionValidator;
|
||||
private RestModelMapper<CompositeCondition, ActionCondition> compositeConditionMapper;
|
||||
private RestModelMapper<SimpleCondition, ActionCondition> simpleConditionMapper;
|
||||
|
||||
@Override
|
||||
public CollectionWithPagingInfo<Rule> getRules(final String folderNodeId,
|
||||
@@ -138,7 +137,7 @@ public class RulesImpl implements Rules
|
||||
{
|
||||
throw new InvalidArgumentException(MUST_HAVE_AT_LEAST_ONE_ACTION);
|
||||
}
|
||||
final org.alfresco.service.cmr.rule.Rule serviceModelRule = rule.toServiceModel(nodes, compositeConditionMapper);
|
||||
final org.alfresco.service.cmr.rule.Rule serviceModelRule = rule.toServiceModel(nodes, simpleConditionMapper);
|
||||
final CompositeAction compositeAction = (CompositeAction) serviceModelRule.getAction();
|
||||
compositeAction.getActions().forEach(action -> action.setParameterValues(
|
||||
actionParameterConverter.getConvertedParams(action.getParameterValues(), action.getActionDefinitionName())));
|
||||
@@ -189,9 +188,9 @@ public class RulesImpl implements Rules
|
||||
this.actionPermissionValidator = actionPermissionValidator;
|
||||
}
|
||||
|
||||
public void setCompositeConditionMapper(
|
||||
RestModelMapper<CompositeCondition, ActionCondition> compositeConditionMapper)
|
||||
public void setSimpleConditionMapper(
|
||||
RestModelMapper<SimpleCondition, ActionCondition> simpleConditionMapper)
|
||||
{
|
||||
this.compositeConditionMapper = compositeConditionMapper;
|
||||
this.simpleConditionMapper = simpleConditionMapper;
|
||||
}
|
||||
}
|
||||
|
@@ -36,12 +36,8 @@ import org.apache.commons.lang3.NotImplementedException;
|
||||
@Experimental
|
||||
public interface RestModelMapper<R, S>
|
||||
{
|
||||
default R toRestModel(S serviceModel) {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
default S toServiceModel(R restModel) {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
R toRestModel(S serviceModel);
|
||||
S toServiceModel(R restModel);
|
||||
default R toRestModel(Collection<S> serviceModels) {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
@@ -54,10 +50,4 @@ public interface RestModelMapper<R, S>
|
||||
default List<S> toServiceModels(Collection<R> restModels) {
|
||||
return restModels.stream().map(this::toServiceModel).collect(Collectors.toList());
|
||||
}
|
||||
default List<R> toRestModels(S serviceModel) {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
default List<S> toServiceModels(R restModel) {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
@@ -26,10 +26,16 @@
|
||||
|
||||
package org.alfresco.rest.api.model.rules;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.alfresco.rest.api.model.mapper.RestModelMapper;
|
||||
import org.alfresco.service.Experimental;
|
||||
import org.alfresco.service.cmr.action.ActionCondition;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
|
||||
@Experimental
|
||||
public class CompositeCondition
|
||||
@@ -39,6 +45,91 @@ public class CompositeCondition
|
||||
private List<CompositeCondition> compositeConditions;
|
||||
private List<SimpleCondition> simpleConditions;
|
||||
|
||||
/**
|
||||
* Converts Action conditions (service POJO) list to composite condition (REST model).
|
||||
*
|
||||
* @param actionConditions - list of {@link ActionCondition} service POJOs
|
||||
* @return {@link CompositeCondition} REST model
|
||||
*/
|
||||
public static CompositeCondition from(final List<ActionCondition> actionConditions, final RestModelMapper<SimpleCondition, ActionCondition> simpleConditionMapper)
|
||||
{
|
||||
if (actionConditions == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
final CompositeCondition conditions = new CompositeCondition();
|
||||
conditions.compositeConditions = new ArrayList<>();
|
||||
// group action conditions by inversion flag
|
||||
actionConditions.stream().filter(Objects::nonNull).collect(Collectors.groupingBy(ActionCondition::getInvertCondition))
|
||||
// map action condition sub lists
|
||||
.forEach((inverted, actionConditionsPart) -> Optional.ofNullable(CompositeCondition.ofActionConditions(actionConditionsPart, simpleConditionMapper, inverted, ConditionOperator.AND))
|
||||
// if composite condition present add to final list
|
||||
.ifPresent(compositeCondition -> conditions.compositeConditions.add(compositeCondition)));
|
||||
|
||||
if (conditions.compositeConditions.isEmpty()) {
|
||||
conditions.compositeConditions = null;
|
||||
}
|
||||
|
||||
return conditions;
|
||||
}
|
||||
|
||||
private static CompositeCondition ofActionConditions(final List<ActionCondition> actionConditions,
|
||||
final RestModelMapper<SimpleCondition, ActionCondition> simpleConditionMapper,
|
||||
final boolean inverted, final ConditionOperator conditionOperator)
|
||||
{
|
||||
if (actionConditions == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return ofSimpleConditions(SimpleCondition.listOf(actionConditions, simpleConditionMapper), inverted, conditionOperator);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a composite condition instance of simple conditions.
|
||||
*
|
||||
* @param simpleConditions - list of {@link SimpleCondition}
|
||||
* @param inverted - determines if condition should be inverted
|
||||
* @param conditionOperator - determines the operation, see {@link ConditionOperator}
|
||||
* @return {@link CompositeCondition}
|
||||
*/
|
||||
public static CompositeCondition ofSimpleConditions(final List<SimpleCondition> simpleConditions, final boolean inverted, final ConditionOperator conditionOperator)
|
||||
{
|
||||
return of(simpleConditions, null, inverted, conditionOperator);
|
||||
}
|
||||
|
||||
private static CompositeCondition of(final List<SimpleCondition> simpleConditions, final List<CompositeCondition> compositeConditions,
|
||||
final boolean inverted, final ConditionOperator conditionOperator)
|
||||
{
|
||||
if (CollectionUtils.isEmpty(simpleConditions) && CollectionUtils.isEmpty(compositeConditions))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return builder()
|
||||
.inverted(inverted)
|
||||
.booleanMode(conditionOperator)
|
||||
.simpleConditions(simpleConditions)
|
||||
.compositeConditions(compositeConditions)
|
||||
.create();
|
||||
}
|
||||
|
||||
public List<ActionCondition> toServiceModels(final RestModelMapper<SimpleCondition, ActionCondition> simpleConditionMapper)
|
||||
{
|
||||
final List<ActionCondition> actionConditions = new ArrayList<>();
|
||||
if (CollectionUtils.isNotEmpty(simpleConditions))
|
||||
{
|
||||
simpleConditions.forEach(simpleCondition -> actionConditions.add(simpleCondition.toServiceModel(inverted, simpleConditionMapper)));
|
||||
}
|
||||
if (CollectionUtils.isNotEmpty(compositeConditions))
|
||||
{
|
||||
compositeConditions.forEach(compositeCondition -> actionConditions.addAll(compositeCondition.toServiceModels(simpleConditionMapper)));
|
||||
}
|
||||
|
||||
return actionConditions;
|
||||
}
|
||||
|
||||
public boolean isInverted()
|
||||
{
|
||||
return inverted;
|
||||
|
@@ -63,7 +63,7 @@ public class 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 RestModelMapper<CompositeCondition, ActionCondition> compositeConditionMapper)
|
||||
public static Rule from(final org.alfresco.service.cmr.rule.Rule ruleModel, final RestModelMapper<SimpleCondition, ActionCondition> simpleConditionMapper)
|
||||
{
|
||||
if (ruleModel == null)
|
||||
{
|
||||
@@ -86,7 +86,7 @@ public class Rule
|
||||
}
|
||||
if (ruleModel.getAction() != null)
|
||||
{
|
||||
builder.conditions(compositeConditionMapper.toRestModel(ruleModel.getAction().getActionConditions()));
|
||||
builder.conditions(CompositeCondition.from(ruleModel.getAction().getActionConditions(), simpleConditionMapper));
|
||||
if (ruleModel.getAction().getCompensatingAction() != null && ruleModel.getAction().getCompensatingAction().getParameterValue(ScriptActionExecuter.PARAM_SCRIPTREF) != null)
|
||||
{
|
||||
builder.errorScript(ruleModel.getAction().getCompensatingAction().getParameterValue(ScriptActionExecuter.PARAM_SCRIPTREF).toString());
|
||||
@@ -106,7 +106,7 @@ public class Rule
|
||||
* @param nodes The nodes API.
|
||||
* @return The rule service POJO.
|
||||
*/
|
||||
public org.alfresco.service.cmr.rule.Rule toServiceModel(final Nodes nodes, final RestModelMapper<CompositeCondition, ActionCondition> compositeConditionMapper)
|
||||
public org.alfresco.service.cmr.rule.Rule toServiceModel(final Nodes nodes, final RestModelMapper<SimpleCondition, ActionCondition> simpleConditionMapper)
|
||||
{
|
||||
final org.alfresco.service.cmr.rule.Rule ruleModel = new org.alfresco.service.cmr.rule.Rule();
|
||||
final NodeRef nodeRef = (id != null) ? nodes.validateOrLookupNode(id, null) : null;
|
||||
@@ -129,7 +129,7 @@ public class Rule
|
||||
}
|
||||
if (conditions != null)
|
||||
{
|
||||
compositeConditionMapper.toServiceModels(conditions).forEach(condition -> ruleModel.getAction().addActionCondition(condition));
|
||||
conditions.toServiceModels(simpleConditionMapper).forEach(condition -> ruleModel.getAction().addActionCondition(condition));
|
||||
}
|
||||
|
||||
return ruleModel;
|
||||
|
@@ -59,10 +59,49 @@ import org.apache.commons.collections.CollectionUtils;
|
||||
@Experimental
|
||||
public class SimpleCondition
|
||||
{
|
||||
public static final String CATEGORY_INVALID_MSG = "Category in condition is invalid";
|
||||
public static final String PARAM_CATEGORY = "category";
|
||||
public static final String PARAM_MIMETYPE = "mimetype";
|
||||
|
||||
private String field;
|
||||
private String comparator;
|
||||
private String parameter;
|
||||
|
||||
/**
|
||||
* Converts list of service POJO action conditions to list of REST model simple conditions.
|
||||
*
|
||||
* @param actionConditions - list of {@link ActionCondition} service POJOs
|
||||
* @return list of {@link SimpleCondition} REST models
|
||||
*/
|
||||
public static List<SimpleCondition> listOf(final List<ActionCondition> actionConditions,
|
||||
final RestModelMapper<SimpleCondition, ActionCondition> simpleConditionMapper)
|
||||
{
|
||||
if (CollectionUtils.isEmpty(actionConditions))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return simpleConditionMapper.toRestModels(actionConditions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates simple condition REST model instance from service POJO action condition.
|
||||
*
|
||||
* @param actionCondition - {@link ActionCondition} service POJO
|
||||
* @return {@link SimpleCondition} REST model
|
||||
*/
|
||||
public static SimpleCondition from(final ActionCondition actionCondition,
|
||||
final RestModelMapper<SimpleCondition, ActionCondition> simpleConditionMapper)
|
||||
{
|
||||
return simpleConditionMapper.toRestModel(actionCondition);
|
||||
}
|
||||
|
||||
public ActionCondition toServiceModel(final boolean inverted, final RestModelMapper<SimpleCondition, ActionCondition> mapper)
|
||||
{
|
||||
final ActionCondition actionCondition = mapper.toServiceModel(this);
|
||||
actionCondition.setInvertCondition(inverted);
|
||||
return actionCondition;
|
||||
}
|
||||
|
||||
public String getField()
|
||||
{
|
||||
return field;
|
||||
|
@@ -887,7 +887,7 @@
|
||||
<bean id="ruleLoader" class="org.alfresco.rest.api.impl.rules.RuleLoader">
|
||||
<property name="ruleService" ref="RuleService" />
|
||||
<property name="nodeValidator" ref="nodeValidator" />
|
||||
<property name="compositeConditionMapper" ref="compositeConditionMapper"/>
|
||||
<property name="simpleConditionMapper" ref="simpleConditionMapper"/>
|
||||
</bean>
|
||||
|
||||
<bean class="org.alfresco.rest.api.nodes.NodeRuleSetsRelation">
|
||||
@@ -911,7 +911,7 @@
|
||||
<property name="ruleLoader" ref="ruleLoader"/>
|
||||
<property name="actionParameterConverter" ref="actionParameterConverter"/>
|
||||
<property name="actionPermissionValidator" ref="actionPermissionValidator"/>
|
||||
<property name="compositeConditionMapper" ref="compositeConditionMapper"/>
|
||||
<property name="simpleConditionMapper" ref="simpleConditionMapper"/>
|
||||
</bean>
|
||||
|
||||
<bean id="Rules" class="org.springframework.aop.framework.ProxyFactoryBean">
|
||||
@@ -951,9 +951,6 @@
|
||||
<constructor-arg name="namespaceService" ref="NamespaceService"/>
|
||||
<constructor-arg name="nodes" ref="Nodes"/>
|
||||
</bean>
|
||||
<bean id="compositeConditionMapper" class="org.alfresco.rest.api.impl.mapper.rules.RestRuleCompositeConditionModelMapper">
|
||||
<constructor-arg name="simpleConditionMapper" ref="simpleConditionMapper"/>
|
||||
</bean>
|
||||
|
||||
<bean id="publicapi.mimeTypePropertyLookup" class="org.alfresco.rest.api.lookups.MimeTypePropertyLookup">
|
||||
<property name="serviceRegistry" ref="ServiceRegistry"/>
|
||||
|
@@ -26,7 +26,6 @@
|
||||
|
||||
package org.alfresco.rest.api;
|
||||
|
||||
import org.alfresco.rest.api.impl.mapper.rules.RestRuleCompositeConditionModelMapperTest;
|
||||
import org.alfresco.rest.api.impl.mapper.rules.RestRuleSimpleConditionModelMapperTest;
|
||||
import org.alfresco.rest.api.impl.rules.ActionParameterConverterTest;
|
||||
import org.alfresco.rest.api.impl.rules.ActionPermissionValidatorTest;
|
||||
@@ -34,8 +33,10 @@ import org.alfresco.rest.api.impl.rules.NodeValidatorTest;
|
||||
import org.alfresco.rest.api.impl.rules.RuleLoaderTest;
|
||||
import org.alfresco.rest.api.impl.rules.RuleSetsImplTest;
|
||||
import org.alfresco.rest.api.model.rules.ActionTest;
|
||||
import org.alfresco.rest.api.model.rules.CompositeConditionTest;
|
||||
import org.alfresco.rest.api.impl.rules.RulesImplTest;
|
||||
import org.alfresco.rest.api.model.rules.RuleTest;
|
||||
import org.alfresco.rest.api.model.rules.SimpleConditionTest;
|
||||
import org.alfresco.rest.api.nodes.NodeRulesRelationTest;
|
||||
import org.alfresco.service.Experimental;
|
||||
import org.junit.runner.RunWith;
|
||||
@@ -50,11 +51,12 @@ import org.junit.runners.Suite;
|
||||
NodeValidatorTest.class,
|
||||
RuleTest.class,
|
||||
ActionTest.class,
|
||||
SimpleConditionTest.class,
|
||||
CompositeConditionTest.class,
|
||||
RuleLoaderTest.class,
|
||||
ActionParameterConverterTest.class,
|
||||
ActionPermissionValidatorTest.class,
|
||||
RestRuleSimpleConditionModelMapperTest.class,
|
||||
RestRuleCompositeConditionModelMapperTest.class
|
||||
RestRuleSimpleConditionModelMapperTest.class
|
||||
})
|
||||
public class RulesUnitTests
|
||||
{
|
||||
|
@@ -1,238 +0,0 @@
|
||||
/*
|
||||
* #%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.mapper.rules;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
import org.alfresco.repo.action.ActionConditionImpl;
|
||||
import org.alfresco.repo.action.evaluator.ComparePropertyValueEvaluator;
|
||||
import org.alfresco.rest.api.model.mapper.RestModelMapper;
|
||||
import org.alfresco.rest.api.model.rules.CompositeCondition;
|
||||
import org.alfresco.rest.api.model.rules.ConditionOperator;
|
||||
import org.alfresco.rest.api.model.rules.SimpleCondition;
|
||||
import org.alfresco.service.Experimental;
|
||||
import org.alfresco.service.cmr.action.ActionCondition;
|
||||
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 RestRuleCompositeConditionModelMapperTest
|
||||
{
|
||||
|
||||
@Mock
|
||||
private RestModelMapper<SimpleCondition, ActionCondition> simpleConditionMapperMock;
|
||||
|
||||
@InjectMocks
|
||||
RestRuleCompositeConditionModelMapper objectUnderTest;
|
||||
|
||||
@Test
|
||||
public void testToRestModel()
|
||||
{
|
||||
final List<ActionCondition> actionConditions = List.of(
|
||||
createActionCondition("value1"),
|
||||
createActionCondition("value3"),
|
||||
createActionCondition("value2", true)
|
||||
);
|
||||
final List<SimpleCondition> simpleConditions = List.of(
|
||||
createSimpleCondition("value1"),
|
||||
createSimpleCondition("value3"),
|
||||
createSimpleCondition("value2")
|
||||
);
|
||||
|
||||
final CompositeCondition expectedCompositeCondition = createCompositeCondition(List.of(
|
||||
createCompositeCondition(false, simpleConditions.subList(0,2)),
|
||||
createCompositeCondition(true, simpleConditions.subList(2,3))
|
||||
));
|
||||
|
||||
when(simpleConditionMapperMock.toRestModels(actionConditions.subList(0,2))).thenReturn(simpleConditions.subList(0,2));
|
||||
when(simpleConditionMapperMock.toRestModels(actionConditions.subList(2,3))).thenReturn(simpleConditions.subList(2,3));
|
||||
|
||||
// when
|
||||
final CompositeCondition actualCompositeCondition = objectUnderTest.toRestModel(actionConditions);
|
||||
|
||||
assertThat(actualCompositeCondition).isNotNull().usingRecursiveComparison().isEqualTo(expectedCompositeCondition);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testToRestModel_fromEmptyList()
|
||||
{
|
||||
final List<ActionCondition> actionConditions = Collections.emptyList();
|
||||
|
||||
// when
|
||||
final CompositeCondition actualCompositeCondition = objectUnderTest.toRestModel(actionConditions);
|
||||
|
||||
assertThat(actualCompositeCondition).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testToRestModel_fromNullValue()
|
||||
{
|
||||
// when
|
||||
final CompositeCondition actualCompositeCondition = objectUnderTest.toRestModel((Collection<ActionCondition>) null);
|
||||
|
||||
assertThat(actualCompositeCondition).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testToRestModel_fromListContainingNull()
|
||||
{
|
||||
final List<ActionCondition> actionConditions = new ArrayList<>();
|
||||
actionConditions.add(null);
|
||||
final CompositeCondition expectedCompositeCondition = CompositeCondition.builder().create();
|
||||
|
||||
// when
|
||||
final CompositeCondition actualCompositeCondition = objectUnderTest.toRestModel(actionConditions);
|
||||
|
||||
assertThat(actualCompositeCondition).isNotNull().usingRecursiveComparison().isEqualTo(expectedCompositeCondition);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testToServiceModels() {
|
||||
final List<SimpleCondition> simpleConditions = List.of(
|
||||
createSimpleCondition("value1"),
|
||||
createSimpleCondition("value3"),
|
||||
createSimpleCondition("value2")
|
||||
);
|
||||
final CompositeCondition compositeCondition = createCompositeCondition(List.of(
|
||||
createCompositeCondition(false, simpleConditions.subList(0,2)),
|
||||
createCompositeCondition(true, simpleConditions.subList(2,3))
|
||||
));
|
||||
final List<ActionCondition> actionConditions = List.of(
|
||||
createActionCondition("value1"),
|
||||
createActionCondition("value3"),
|
||||
createActionCondition("value2", true)
|
||||
);
|
||||
|
||||
IntStream.rangeClosed(0, 2)
|
||||
.forEach(i -> when(simpleConditionMapperMock.toServiceModel(simpleConditions.get(i))).thenReturn(actionConditions.get(i)));
|
||||
|
||||
final List<ActionCondition> actualActionConditions = objectUnderTest.toServiceModels(compositeCondition);
|
||||
assertThat(actualActionConditions).isEqualTo(actionConditions);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testToServiceModels_simpleNonInvertedConditionsOnly() {
|
||||
final List<SimpleCondition> simpleConditions = List.of(
|
||||
createSimpleCondition("value1"),
|
||||
createSimpleCondition("value2"),
|
||||
createSimpleCondition("value3")
|
||||
);
|
||||
final CompositeCondition compositeCondition = createCompositeCondition(false, simpleConditions);
|
||||
final List<ActionCondition> actionConditions = List.of(
|
||||
createActionCondition("value1"),
|
||||
createActionCondition("value2"),
|
||||
createActionCondition("value3")
|
||||
);
|
||||
|
||||
IntStream.rangeClosed(0, 2)
|
||||
.forEach(i -> when(simpleConditionMapperMock.toServiceModel(simpleConditions.get(i))).thenReturn(actionConditions.get(i)));
|
||||
|
||||
final List<ActionCondition> actualActionConditions = objectUnderTest.toServiceModels(compositeCondition);
|
||||
assertThat(actualActionConditions).isEqualTo(actionConditions);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testToServiceModels_nullSimpleConditions() {
|
||||
final CompositeCondition compositeCondition = createCompositeCondition(false, null);
|
||||
|
||||
final List<ActionCondition> actualActionConditions = objectUnderTest.toServiceModels(compositeCondition);
|
||||
assertThat(actualActionConditions).isNotNull().isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testToServiceModels_emptyCompositeCondition() {
|
||||
final CompositeCondition compositeCondition = CompositeCondition.builder().create();
|
||||
|
||||
final List<ActionCondition> actualActionConditions = objectUnderTest.toServiceModels(compositeCondition);
|
||||
assertThat(actualActionConditions).isNotNull().isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testToServiceModels_nullCompositeCondition() {
|
||||
final CompositeCondition compositeCondition = null;
|
||||
|
||||
final List<ActionCondition> actualActionConditions = objectUnderTest.toServiceModels(compositeCondition);
|
||||
assertThat(actualActionConditions).isNotNull().isEmpty();
|
||||
}
|
||||
|
||||
private static ActionCondition createActionCondition(final String value)
|
||||
{
|
||||
return createActionCondition(value, false);
|
||||
}
|
||||
|
||||
private static ActionCondition createActionCondition(final String value, final boolean inverted)
|
||||
{
|
||||
final ActionCondition actionCondition = new ActionConditionImpl("fake-id", ComparePropertyValueEvaluator.NAME);
|
||||
actionCondition.setInvertCondition(inverted);
|
||||
final Map<String, Serializable> parameterValues = new HashMap<>();
|
||||
parameterValues.put(ComparePropertyValueEvaluator.PARAM_CONTENT_PROPERTY, "content-property");
|
||||
parameterValues.put(ComparePropertyValueEvaluator.PARAM_OPERATION, "operation");
|
||||
parameterValues.put(ComparePropertyValueEvaluator.PARAM_VALUE, value);
|
||||
actionCondition.setParameterValues(parameterValues);
|
||||
return actionCondition;
|
||||
}
|
||||
|
||||
private static SimpleCondition createSimpleCondition(final String value) {
|
||||
return SimpleCondition.builder()
|
||||
.field("content-property")
|
||||
.comparator("operation")
|
||||
.parameter(value)
|
||||
.create();
|
||||
}
|
||||
|
||||
private static CompositeCondition createCompositeCondition(final List<CompositeCondition> compositeConditions) {
|
||||
return createCompositeCondition(false, ConditionOperator.AND, compositeConditions, null);
|
||||
}
|
||||
|
||||
private static CompositeCondition createCompositeCondition(final boolean inverted, final List<SimpleCondition> simpleConditions) {
|
||||
return createCompositeCondition(inverted, ConditionOperator.AND, null, simpleConditions);
|
||||
}
|
||||
|
||||
private static CompositeCondition createCompositeCondition(final boolean inverted, final ConditionOperator conditionOperator,
|
||||
final List<CompositeCondition> compositeConditions, final List<SimpleCondition> simpleConditions) {
|
||||
return CompositeCondition.builder()
|
||||
.inverted(inverted)
|
||||
.booleanMode(conditionOperator)
|
||||
.compositeConditions(compositeConditions)
|
||||
.simpleConditions(simpleConditions)
|
||||
.create();
|
||||
}
|
||||
}
|
@@ -26,25 +26,19 @@
|
||||
|
||||
package org.alfresco.rest.api.impl.mapper.rules;
|
||||
|
||||
import static org.alfresco.rest.api.impl.mapper.rules.RestRuleSimpleConditionModelMapper.COMPARATOR_NOT_NULL;
|
||||
import static org.alfresco.rest.api.impl.mapper.rules.RestRuleSimpleConditionModelMapper.FIELD_NOT_NULL;
|
||||
import static org.alfresco.rest.api.impl.mapper.rules.RestRuleSimpleConditionModelMapper.PARAMETER_NOT_NULL;
|
||||
import static org.alfresco.rest.api.impl.mapper.rules.RestRuleSimpleConditionModelMapper.PARAM_CATEGORY;
|
||||
import static org.alfresco.rest.api.impl.mapper.rules.RestRuleSimpleConditionModelMapper.PARAM_MIMETYPE;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
import org.alfresco.model.ContentModel;
|
||||
import org.alfresco.repo.action.ActionConditionImpl;
|
||||
import org.alfresco.repo.action.evaluator.CompareMimeTypeEvaluator;
|
||||
@@ -60,7 +54,6 @@ import org.alfresco.repo.action.evaluator.compare.ComparePropertyValueOperation;
|
||||
import org.alfresco.repo.action.evaluator.compare.ContentPropertyName;
|
||||
import org.alfresco.rest.api.Nodes;
|
||||
import org.alfresco.rest.api.model.rules.SimpleCondition;
|
||||
import org.alfresco.rest.framework.core.exceptions.InvalidArgumentException;
|
||||
import org.alfresco.service.Experimental;
|
||||
import org.alfresco.service.cmr.action.ActionCondition;
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
@@ -76,7 +69,7 @@ import org.mockito.junit.MockitoJUnitRunner;
|
||||
|
||||
@Experimental
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class RestRuleSimpleConditionModelMapperTest
|
||||
public class RestRuleSimpleConditionModelMapperTest extends TestCase
|
||||
{
|
||||
private static final boolean NULL_RESULT = true;
|
||||
private static final String PARAMETER_DEFAULT = "value";
|
||||
@@ -163,8 +156,7 @@ public class RestRuleSimpleConditionModelMapperTest
|
||||
public void testToRestModelListOfNullActionConditions()
|
||||
{
|
||||
// when
|
||||
assertThatExceptionOfType(NullPointerException.class).isThrownBy(() -> objectUnderTest.toRestModels(
|
||||
(Collection<ActionCondition>) null));
|
||||
assertThatExceptionOfType(NullPointerException.class).isThrownBy(() -> objectUnderTest.toRestModels(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -220,7 +212,7 @@ public class RestRuleSimpleConditionModelMapperTest
|
||||
@Test
|
||||
public void testToServiceModel_compareMimetype()
|
||||
{
|
||||
final SimpleCondition simpleCondition = createSimpleCondition(PARAM_MIMETYPE);
|
||||
final SimpleCondition simpleCondition = createSimpleCondition(SimpleCondition.PARAM_MIMETYPE);
|
||||
|
||||
// when
|
||||
final ActionCondition actualActionCondition = objectUnderTest.toServiceModel(simpleCondition);
|
||||
@@ -272,7 +264,7 @@ public class RestRuleSimpleConditionModelMapperTest
|
||||
@Test
|
||||
public void testToServiceModel_inCategory()
|
||||
{
|
||||
final SimpleCondition simpleCondition = createSimpleCondition(PARAM_CATEGORY);
|
||||
final SimpleCondition simpleCondition = createSimpleCondition(SimpleCondition.PARAM_CATEGORY);
|
||||
final NodeRef defaultNodeRef = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, PARAMETER_DEFAULT);
|
||||
given(nodesMock.validateOrLookupNode(PARAMETER_DEFAULT, null)).willReturn(defaultNodeRef);
|
||||
|
||||
@@ -305,67 +297,6 @@ public class RestRuleSimpleConditionModelMapperTest
|
||||
.isEqualTo(expectedActionCondition);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testToServiceModel_nullOrBlankParameter()
|
||||
{
|
||||
final SimpleCondition simpleConditionNullParam = createSimpleCondition(IsSubTypeEvaluator.PARAM_TYPE, null);
|
||||
|
||||
// when
|
||||
assertThatThrownBy(() -> objectUnderTest.toServiceModel(simpleConditionNullParam))
|
||||
.isInstanceOf(InvalidArgumentException.class)
|
||||
.hasMessageContaining(PARAMETER_NOT_NULL);
|
||||
|
||||
final SimpleCondition simpleConditionEmptyParam = createSimpleCondition(IsSubTypeEvaluator.PARAM_TYPE, " ");
|
||||
|
||||
assertThatThrownBy(() -> objectUnderTest.toServiceModel(simpleConditionEmptyParam))
|
||||
.isInstanceOf(InvalidArgumentException.class)
|
||||
.hasMessageContaining(PARAMETER_NOT_NULL);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testToServiceModel_nullOrEmptyField()
|
||||
{
|
||||
final SimpleCondition simpleConditionNullField = createSimpleCondition(null);
|
||||
|
||||
// when
|
||||
assertThatThrownBy(() -> objectUnderTest.toServiceModel(simpleConditionNullField))
|
||||
.isInstanceOf(InvalidArgumentException.class)
|
||||
.hasMessageContaining(FIELD_NOT_NULL);
|
||||
|
||||
final SimpleCondition simpleConditionEmptyField = createSimpleCondition("");
|
||||
|
||||
// when
|
||||
assertThatThrownBy(() -> objectUnderTest.toServiceModel(simpleConditionEmptyField))
|
||||
.isInstanceOf(InvalidArgumentException.class)
|
||||
.hasMessageContaining(FIELD_NOT_NULL);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testToServiceModel_nullOrEmptyComparatorWhenRequired()
|
||||
{
|
||||
final SimpleCondition simpleConditionNullComparator = SimpleCondition.builder()
|
||||
.field("size")
|
||||
.comparator(null)
|
||||
.parameter("65000")
|
||||
.create();
|
||||
|
||||
// when
|
||||
assertThatThrownBy(() -> objectUnderTest.toServiceModel(simpleConditionNullComparator))
|
||||
.isInstanceOf(InvalidArgumentException.class)
|
||||
.hasMessageContaining(COMPARATOR_NOT_NULL);
|
||||
|
||||
final SimpleCondition simpleConditionEmptyComparator = SimpleCondition.builder()
|
||||
.field("size")
|
||||
.comparator(" ")
|
||||
.parameter("65000")
|
||||
.create();
|
||||
|
||||
// when
|
||||
assertThatThrownBy(() -> objectUnderTest.toServiceModel(simpleConditionEmptyComparator))
|
||||
.isInstanceOf(InvalidArgumentException.class)
|
||||
.hasMessageContaining(COMPARATOR_NOT_NULL);
|
||||
}
|
||||
|
||||
private static ActionCondition createActionCondition(final String actionDefinitionName)
|
||||
{
|
||||
return new ActionConditionImpl("fake-id", actionDefinitionName, createParameterValues());
|
||||
|
@@ -51,7 +51,6 @@ import org.alfresco.repo.action.CompositeActionImpl;
|
||||
import org.alfresco.rest.api.Nodes;
|
||||
import org.alfresco.rest.api.model.rules.Action;
|
||||
import org.alfresco.rest.api.model.mapper.RestModelMapper;
|
||||
import org.alfresco.rest.api.model.rules.CompositeCondition;
|
||||
import org.alfresco.rest.api.model.rules.Rule;
|
||||
import org.alfresco.rest.api.model.rules.SimpleCondition;
|
||||
import org.alfresco.rest.framework.core.exceptions.EntityNotFoundException;
|
||||
@@ -92,7 +91,7 @@ public class RulesImplTest extends TestCase
|
||||
@Mock
|
||||
private Nodes nodesMock;
|
||||
@Mock
|
||||
private RestModelMapper<CompositeCondition, ActionCondition> compositeConditionMapperMock;
|
||||
private RestModelMapper<SimpleCondition, ActionCondition> simpleConditionMapperMock;
|
||||
@Mock
|
||||
private NodeValidator nodeValidatorMock;
|
||||
@Mock
|
||||
@@ -298,7 +297,7 @@ public class RulesImplTest extends TestCase
|
||||
public void testCreateRules()
|
||||
{
|
||||
List<Rule> ruleList = List.of(ruleMock);
|
||||
given(ruleMock.toServiceModel(nodesMock, compositeConditionMapperMock)).willReturn(serviceRuleMock);
|
||||
given(ruleMock.toServiceModel(nodesMock, simpleConditionMapperMock)).willReturn(serviceRuleMock);
|
||||
given(ruleMock.getActions()).willReturn(List.of(actionMock));
|
||||
given(serviceRuleMock.getAction()).willReturn(compositeAction);
|
||||
given(ruleServiceMock.saveRule(FOLDER_NODE_REF, serviceRuleMock)).willAnswer(arg -> arg.getArguments()[1]);
|
||||
@@ -315,7 +314,7 @@ public class RulesImplTest extends TestCase
|
||||
then(actionParameterConverterMock).shouldHaveNoMoreInteractions();
|
||||
then(actionPermissionValidatorMock).should().validateRulePermissions(serviceRuleMock);
|
||||
then(actionPermissionValidatorMock).shouldHaveNoMoreInteractions();
|
||||
then(ruleServiceMock).should().saveRule(FOLDER_NODE_REF, ruleMock.toServiceModel(nodesMock, compositeConditionMapperMock));
|
||||
then(ruleServiceMock).should().saveRule(FOLDER_NODE_REF, ruleMock.toServiceModel(nodesMock, simpleConditionMapperMock));
|
||||
then(ruleServiceMock).shouldHaveNoMoreInteractions();
|
||||
List<Rule> expected = List.of(ruleMock);
|
||||
assertThat(actual).isEqualTo(expected);
|
||||
@@ -328,7 +327,7 @@ public class RulesImplTest extends TestCase
|
||||
public void testCreateRules_defaultRuleSet()
|
||||
{
|
||||
List<Rule> ruleList = List.of(ruleMock);
|
||||
given(ruleMock.toServiceModel(nodesMock, compositeConditionMapperMock)).willReturn(serviceRuleMock);
|
||||
given(ruleMock.toServiceModel(nodesMock, simpleConditionMapperMock)).willReturn(serviceRuleMock);
|
||||
given(ruleMock.getActions()).willReturn(List.of(actionMock));
|
||||
given(ruleServiceMock.saveRule(FOLDER_NODE_REF, serviceRuleMock)).willAnswer(arg -> arg.getArguments()[1]);
|
||||
given(ruleLoaderMock.loadRule(serviceRuleMock, INCLUDE)).willReturn(ruleMock);
|
||||
@@ -344,7 +343,7 @@ public class RulesImplTest extends TestCase
|
||||
then(actionParameterConverterMock).shouldHaveNoMoreInteractions();
|
||||
then(actionPermissionValidatorMock).should().validateRulePermissions(serviceRuleMock);
|
||||
then(actionPermissionValidatorMock).shouldHaveNoMoreInteractions();
|
||||
then(ruleServiceMock).should().saveRule(FOLDER_NODE_REF, ruleMock.toServiceModel(nodesMock, compositeConditionMapperMock));
|
||||
then(ruleServiceMock).should().saveRule(FOLDER_NODE_REF, ruleMock.toServiceModel(nodesMock, simpleConditionMapperMock));
|
||||
then(ruleServiceMock).shouldHaveNoMoreInteractions();
|
||||
List<Rule> expected = List.of(ruleMock);
|
||||
assertThat(actual).isEqualTo(expected);
|
||||
@@ -375,7 +374,7 @@ public class RulesImplTest extends TestCase
|
||||
given(ruleBodyMock.getActions()).willReturn(List.of(actionMock));
|
||||
ruleBodyList.add(ruleBodyMock);
|
||||
org.alfresco.service.cmr.rule.Rule serviceRuleMockInner = mock(org.alfresco.service.cmr.rule.Rule.class);
|
||||
given(ruleBodyMock.toServiceModel(nodesMock, compositeConditionMapperMock)).willReturn(serviceRuleMockInner);
|
||||
given(ruleBodyMock.toServiceModel(nodesMock, simpleConditionMapperMock)).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);
|
||||
@@ -394,9 +393,8 @@ public class RulesImplTest extends TestCase
|
||||
then(nodeValidatorMock).shouldHaveNoMoreInteractions();
|
||||
for (Rule ruleBody : ruleBodyList)
|
||||
{
|
||||
then(actionPermissionValidatorMock).should().validateRulePermissions(ruleBody.toServiceModel(nodesMock,
|
||||
compositeConditionMapperMock));
|
||||
then(ruleServiceMock).should().saveRule(FOLDER_NODE_REF, ruleBody.toServiceModel(nodesMock, compositeConditionMapperMock));
|
||||
then(actionPermissionValidatorMock).should().validateRulePermissions(ruleBody.toServiceModel(nodesMock, simpleConditionMapperMock));
|
||||
then(ruleServiceMock).should().saveRule(FOLDER_NODE_REF, ruleBody.toServiceModel(nodesMock, simpleConditionMapperMock));
|
||||
}
|
||||
then(actionParameterConverterMock).should(times(3)).getConvertedParams(DUMMY_PARAMS, ACTION_DEFINITION_NAME);
|
||||
then(actionParameterConverterMock).shouldHaveNoMoreInteractions();
|
||||
@@ -470,7 +468,7 @@ public class RulesImplTest extends TestCase
|
||||
@Test
|
||||
public void testUpdateRuleById()
|
||||
{
|
||||
given(ruleMock.toServiceModel(nodesMock, compositeConditionMapperMock)).willReturn(serviceRuleMock);
|
||||
given(ruleMock.toServiceModel(nodesMock, simpleConditionMapperMock)).willReturn(serviceRuleMock);
|
||||
given(ruleMock.getActions()).willReturn(List.of(actionMock));
|
||||
given(ruleServiceMock.saveRule(FOLDER_NODE_REF, serviceRuleMock)).willAnswer(a -> a.getArguments()[1]);
|
||||
given(serviceRuleMock.getAction()).willReturn(compositeAction);
|
||||
|
@@ -0,0 +1,193 @@
|
||||
/*
|
||||
* #%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.model.rules;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.alfresco.repo.action.ActionConditionImpl;
|
||||
import org.alfresco.repo.action.evaluator.ComparePropertyValueEvaluator;
|
||||
import org.alfresco.rest.api.impl.mapper.rules.RestRuleSimpleConditionModelMapper;
|
||||
import org.alfresco.rest.api.model.mapper.RestModelMapper;
|
||||
import org.alfresco.service.Experimental;
|
||||
import org.alfresco.service.cmr.action.ActionCondition;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
|
||||
@Experimental
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class CompositeConditionTest
|
||||
{
|
||||
|
||||
private final RestModelMapper<SimpleCondition, ActionCondition> simpleConditionMapper = mock(RestRuleSimpleConditionModelMapper.class);
|
||||
|
||||
@Test
|
||||
public void testFrom()
|
||||
{
|
||||
final List<ActionCondition> actionConditions = List.of(
|
||||
createActionCondition("value1"),
|
||||
createActionCondition("value3"),
|
||||
createActionCondition("value2", true)
|
||||
);
|
||||
final List<SimpleCondition> simpleConditions = List.of(
|
||||
createSimpleCondition("value1"),
|
||||
createSimpleCondition("value3"),
|
||||
createSimpleCondition("value2")
|
||||
);
|
||||
|
||||
final CompositeCondition expectedCompositeCondition = createCompositeCondition(List.of(
|
||||
createCompositeCondition(false, simpleConditions.subList(0,2)),
|
||||
createCompositeCondition(true, simpleConditions.subList(2,3))
|
||||
));
|
||||
|
||||
Mockito.when(simpleConditionMapper.toRestModels(actionConditions.subList(0,2))).thenReturn(simpleConditions.subList(0,2));
|
||||
Mockito.when(simpleConditionMapper.toRestModels(actionConditions.subList(2,3))).thenReturn(simpleConditions.subList(2,3));
|
||||
|
||||
// when
|
||||
final CompositeCondition actualCompositeCondition = CompositeCondition.from(actionConditions, simpleConditionMapper);
|
||||
|
||||
assertThat(actualCompositeCondition).isNotNull().usingRecursiveComparison().isEqualTo(expectedCompositeCondition);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFromEmptyList()
|
||||
{
|
||||
final List<ActionCondition> actionConditions = Collections.emptyList();
|
||||
final CompositeCondition expectedCompositeCondition = CompositeCondition.builder().create();
|
||||
|
||||
// when
|
||||
final CompositeCondition actualCompositeCondition = CompositeCondition.from(actionConditions, simpleConditionMapper);
|
||||
|
||||
assertThat(actualCompositeCondition).isNotNull().usingRecursiveComparison().isEqualTo(expectedCompositeCondition);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFromNullValue()
|
||||
{
|
||||
// when
|
||||
final CompositeCondition actualCompositeCondition = CompositeCondition.from(null, simpleConditionMapper);
|
||||
|
||||
assertThat(actualCompositeCondition).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFromListContainingNull()
|
||||
{
|
||||
final List<ActionCondition> actionConditions = new ArrayList<>();
|
||||
actionConditions.add(null);
|
||||
final CompositeCondition expectedCompositeCondition = CompositeCondition.builder().create();
|
||||
|
||||
// when
|
||||
final CompositeCondition actualCompositeCondition = CompositeCondition.from(actionConditions, simpleConditionMapper);
|
||||
|
||||
assertThat(actualCompositeCondition).isNotNull().usingRecursiveComparison().isEqualTo(expectedCompositeCondition);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOfSimpleConditions()
|
||||
{
|
||||
final List<SimpleCondition> simpleConditions = List.of(SimpleCondition.builder().field("field").comparator("comparator").parameter("param").create());
|
||||
final boolean inverted = true;
|
||||
final ConditionOperator conditionOperator = ConditionOperator.OR;
|
||||
final CompositeCondition expectedCondition = createCompositeCondition(inverted, conditionOperator, null, simpleConditions);
|
||||
|
||||
// when
|
||||
final CompositeCondition actualCompositeCondition = CompositeCondition.ofSimpleConditions(simpleConditions, inverted, conditionOperator);
|
||||
|
||||
assertThat(actualCompositeCondition).isNotNull().usingRecursiveComparison().isEqualTo(expectedCondition);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOfEmptySimpleConditions()
|
||||
{
|
||||
// when
|
||||
final CompositeCondition actualCompositeCondition = CompositeCondition.ofSimpleConditions(Collections.emptyList(), false, ConditionOperator.AND);
|
||||
|
||||
assertThat(actualCompositeCondition).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOfNullSimpleConditions()
|
||||
{
|
||||
// when
|
||||
final CompositeCondition actualCompositeCondition = CompositeCondition.ofSimpleConditions(null, false, ConditionOperator.AND);
|
||||
|
||||
assertThat(actualCompositeCondition).isNull();
|
||||
}
|
||||
|
||||
private static ActionCondition createActionCondition(final String value)
|
||||
{
|
||||
return createActionCondition(value, false);
|
||||
}
|
||||
|
||||
private static ActionCondition createActionCondition(final String value, final boolean inverted)
|
||||
{
|
||||
final ActionCondition actionCondition = new ActionConditionImpl("fake-id", ComparePropertyValueEvaluator.NAME);
|
||||
actionCondition.setInvertCondition(inverted);
|
||||
final Map<String, Serializable> parameterValues = new HashMap<>();
|
||||
parameterValues.put(ComparePropertyValueEvaluator.PARAM_CONTENT_PROPERTY, "content-property");
|
||||
parameterValues.put(ComparePropertyValueEvaluator.PARAM_OPERATION, "operation");
|
||||
parameterValues.put(ComparePropertyValueEvaluator.PARAM_VALUE, value);
|
||||
actionCondition.setParameterValues(parameterValues);
|
||||
return actionCondition;
|
||||
}
|
||||
|
||||
private static SimpleCondition createSimpleCondition(final String value) {
|
||||
return SimpleCondition.builder()
|
||||
.field("content-property")
|
||||
.comparator("operation")
|
||||
.parameter(value)
|
||||
.create();
|
||||
}
|
||||
|
||||
private static CompositeCondition createCompositeCondition(final List<CompositeCondition> compositeConditions) {
|
||||
return createCompositeCondition(false, ConditionOperator.AND, compositeConditions, null);
|
||||
}
|
||||
|
||||
private static CompositeCondition createCompositeCondition(final boolean inverted, final List<SimpleCondition> simpleConditions) {
|
||||
return createCompositeCondition(inverted, ConditionOperator.AND, null, simpleConditions);
|
||||
}
|
||||
|
||||
private static CompositeCondition createCompositeCondition(final boolean inverted, final ConditionOperator conditionOperator,
|
||||
final List<CompositeCondition> compositeConditions, final List<SimpleCondition> simpleConditions) {
|
||||
return CompositeCondition.builder()
|
||||
.inverted(inverted)
|
||||
.booleanMode(conditionOperator)
|
||||
.compositeConditions(compositeConditions)
|
||||
.simpleConditions(simpleConditions)
|
||||
.create();
|
||||
}
|
||||
}
|
@@ -37,7 +37,6 @@ import org.alfresco.repo.action.ActionConditionImpl;
|
||||
import org.alfresco.repo.action.ActionImpl;
|
||||
import org.alfresco.repo.action.executer.ScriptActionExecuter;
|
||||
import org.alfresco.rest.api.Nodes;
|
||||
import org.alfresco.rest.api.impl.mapper.rules.RestRuleCompositeConditionModelMapper;
|
||||
import org.alfresco.rest.api.impl.mapper.rules.RestRuleSimpleConditionModelMapper;
|
||||
import org.alfresco.rest.api.model.mapper.RestModelMapper;
|
||||
import org.alfresco.service.Experimental;
|
||||
@@ -45,6 +44,7 @@ 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.alfresco.service.namespace.NamespaceService;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
@@ -63,7 +63,7 @@ public class RuleTest
|
||||
private static final String ACTION_DEFINITION_NAME = "action-def-name";
|
||||
private static final String ERROR_SCRIPT = "error-script-ref";
|
||||
|
||||
private final RestModelMapper<CompositeCondition, ActionCondition> compositeConditionMapper = mock(RestRuleCompositeConditionModelMapper.class);
|
||||
private final RestModelMapper<SimpleCondition, ActionCondition> simpleConditionMapper = mock(RestRuleSimpleConditionModelMapper.class);
|
||||
|
||||
@Test
|
||||
public void testFrom()
|
||||
@@ -72,7 +72,7 @@ public class RuleTest
|
||||
final Rule expectedRule = createRuleWithDefaultValues();
|
||||
|
||||
// when
|
||||
final Rule actualRule = Rule.from(ruleModel, compositeConditionMapper);
|
||||
final Rule actualRule = Rule.from(ruleModel, simpleConditionMapper);
|
||||
|
||||
assertThat(actualRule).isNotNull().usingRecursiveComparison().isEqualTo(expectedRule);
|
||||
|
||||
@@ -85,7 +85,7 @@ public class RuleTest
|
||||
final Rule expectedRule = Rule.builder().enabled(true).create();
|
||||
|
||||
// when
|
||||
final Rule actualRule = Rule.from(ruleModel, compositeConditionMapper);
|
||||
final Rule actualRule = Rule.from(ruleModel, simpleConditionMapper);
|
||||
|
||||
assertThat(actualRule).isNotNull().usingRecursiveComparison().isEqualTo(expectedRule);
|
||||
|
||||
@@ -101,7 +101,7 @@ public class RuleTest
|
||||
final org.alfresco.service.cmr.action.Action expectedCompensatingActionModel = createCompensatingActionModel();
|
||||
|
||||
// when
|
||||
final org.alfresco.service.cmr.rule.Rule actualRuleModel = rule.toServiceModel(nodesMock, compositeConditionMapper);
|
||||
final org.alfresco.service.cmr.rule.Rule actualRuleModel = rule.toServiceModel(nodesMock, simpleConditionMapper);
|
||||
|
||||
then(nodesMock).should().validateOrLookupNode(RULE_ID, null);
|
||||
then(nodesMock).shouldHaveNoMoreInteractions();
|
||||
@@ -126,7 +126,7 @@ public class RuleTest
|
||||
expectedRuleModel.setRuleDisabled(true);
|
||||
|
||||
// when
|
||||
final org.alfresco.service.cmr.rule.Rule actualRuleModel = rule.toServiceModel(nodesMock, compositeConditionMapper);
|
||||
final org.alfresco.service.cmr.rule.Rule actualRuleModel = rule.toServiceModel(nodesMock, simpleConditionMapper);
|
||||
|
||||
then(nodesMock).shouldHaveNoInteractions();
|
||||
assertThat(actualRuleModel)
|
||||
@@ -146,7 +146,7 @@ public class RuleTest
|
||||
.asynchronous(RULE_ASYNC)
|
||||
.triggers(List.of(RuleTrigger.INBOUND, RuleTrigger.UPDATE))
|
||||
.errorScript(ERROR_SCRIPT)
|
||||
.conditions(compositeConditionMapper.toRestModel(Collections.emptyList()))
|
||||
.conditions(CompositeCondition.from(Collections.emptyList(), simpleConditionMapper))
|
||||
.create();
|
||||
}
|
||||
|
||||
|
@@ -0,0 +1,169 @@
|
||||
/*
|
||||
* #%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.model.rules;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.BDDMockito.then;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.alfresco.repo.action.ActionConditionImpl;
|
||||
import org.alfresco.repo.action.evaluator.ComparePropertyValueEvaluator;
|
||||
import org.alfresco.repo.action.evaluator.compare.ComparePropertyValueOperation;
|
||||
import org.alfresco.rest.api.impl.mapper.rules.RestRuleSimpleConditionModelMapper;
|
||||
import org.alfresco.rest.api.model.mapper.RestModelMapper;
|
||||
import org.alfresco.service.Experimental;
|
||||
import org.alfresco.service.cmr.action.ActionCondition;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
|
||||
@Experimental
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class SimpleConditionTest
|
||||
{
|
||||
private static final boolean INVERTED = true;
|
||||
private static final String VALUE = "value";
|
||||
private static final String KEY = "key";
|
||||
|
||||
private final RestModelMapper<SimpleCondition, ActionCondition> simpleConditionMapperMock = mock(RestRuleSimpleConditionModelMapper.class);
|
||||
|
||||
@Test
|
||||
public void testFrom()
|
||||
{
|
||||
final ActionCondition actionCondition = createActionCondition(ComparePropertyValueEvaluator.NAME);
|
||||
final SimpleCondition simpleConditionMock = mock(SimpleCondition.class);
|
||||
given(simpleConditionMapperMock.toRestModel(actionCondition)).willReturn(simpleConditionMock);
|
||||
|
||||
//when
|
||||
final SimpleCondition actualSimpleCondition = SimpleCondition.from(actionCondition, simpleConditionMapperMock);
|
||||
|
||||
then(simpleConditionMapperMock).should().toRestModel(actionCondition);
|
||||
then(simpleConditionMapperMock).shouldHaveNoMoreInteractions();
|
||||
assertThat(actualSimpleCondition).isEqualTo(simpleConditionMock);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testListOf()
|
||||
{
|
||||
final List<ActionCondition> actionConditionsMock = mock(List.class);
|
||||
final List<SimpleCondition> simpleConditionsMock = mock(List.class);
|
||||
given(simpleConditionMapperMock.toRestModels(actionConditionsMock)).willReturn(simpleConditionsMock);
|
||||
|
||||
// when
|
||||
final List<SimpleCondition> actualSimpleConditions = SimpleCondition.listOf(actionConditionsMock, simpleConditionMapperMock);
|
||||
|
||||
then(simpleConditionMapperMock).should().toRestModels(actionConditionsMock);
|
||||
then(simpleConditionMapperMock).shouldHaveNoMoreInteractions();
|
||||
assertThat(actualSimpleConditions)
|
||||
.isNotNull()
|
||||
.containsExactlyElementsOf(simpleConditionsMock);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testListOfEmptyActionConditions()
|
||||
{
|
||||
// when
|
||||
final List<SimpleCondition> actualSimpleConditions = SimpleCondition.listOf(Collections.emptyList(), simpleConditionMapperMock);
|
||||
|
||||
assertThat(actualSimpleConditions).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testListOfNullActionConditions()
|
||||
{
|
||||
// when
|
||||
final List<SimpleCondition> actualSimpleConditions = SimpleCondition.listOf(null, simpleConditionMapperMock);
|
||||
|
||||
then(simpleConditionMapperMock).shouldHaveNoInteractions();
|
||||
assertThat(actualSimpleConditions).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testListOfActionConditionsContainingNull()
|
||||
{
|
||||
final List<ActionCondition> actionConditions = new ArrayList<>();
|
||||
actionConditions.add(null);
|
||||
|
||||
// when
|
||||
final List<SimpleCondition> actualSimpleConditions = SimpleCondition.listOf(actionConditions, simpleConditionMapperMock);
|
||||
|
||||
then(simpleConditionMapperMock).should().toRestModels(actionConditions);
|
||||
then(simpleConditionMapperMock).shouldHaveNoMoreInteractions();
|
||||
assertThat(actualSimpleConditions).isNotNull().isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testToServiceModel_notInverted()
|
||||
{
|
||||
final SimpleCondition simpleCondition = createSimpleCondition("field");
|
||||
final ActionCondition actionCondition = createActionCondition(ComparePropertyValueEvaluator.NAME);
|
||||
given(simpleConditionMapperMock.toServiceModel(simpleCondition)).willReturn(actionCondition);
|
||||
|
||||
// when
|
||||
final ActionCondition actualActionCondition = simpleCondition.toServiceModel(!INVERTED, simpleConditionMapperMock);
|
||||
|
||||
assertThat(actualActionCondition).isEqualTo(actionCondition);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testToServiceModel_inverted()
|
||||
{
|
||||
final SimpleCondition simpleCondition = createSimpleCondition("field");
|
||||
final ActionCondition actionCondition = createActionCondition(ComparePropertyValueEvaluator.NAME);
|
||||
given(simpleConditionMapperMock.toServiceModel(simpleCondition)).willReturn(actionCondition);
|
||||
|
||||
// when
|
||||
final ActionCondition actualActionCondition = simpleCondition.toServiceModel(INVERTED, simpleConditionMapperMock);
|
||||
|
||||
assertThat(actualActionCondition).isEqualTo(actionCondition);
|
||||
}
|
||||
|
||||
private static ActionCondition createActionCondition(final String actionDefinitionName)
|
||||
{
|
||||
return new ActionConditionImpl("fake-id", actionDefinitionName, Map.of(KEY, VALUE));
|
||||
}
|
||||
|
||||
private static SimpleCondition createSimpleCondition(final String field)
|
||||
{
|
||||
return createSimpleCondition(field, VALUE);
|
||||
}
|
||||
|
||||
private static SimpleCondition createSimpleCondition(final String field, final String parameter)
|
||||
{
|
||||
return SimpleCondition.builder()
|
||||
.field(field)
|
||||
.comparator(ComparePropertyValueOperation.EQUALS.toString().toLowerCase())
|
||||
.parameter(parameter)
|
||||
.create();
|
||||
}
|
||||
}
|
@@ -38,7 +38,6 @@ import java.util.Map;
|
||||
import org.alfresco.repo.security.authentication.AuthenticationUtil;
|
||||
import org.alfresco.repo.tenant.TenantUtil;
|
||||
import org.alfresco.repo.transaction.RetryingTransactionHelper;
|
||||
import org.alfresco.repo.workflow.WorkflowDeployer;
|
||||
import org.alfresco.rest.api.tests.AbstractTestFixture;
|
||||
import org.alfresco.rest.api.tests.RepoService.TestNetwork;
|
||||
import org.alfresco.rest.api.tests.client.PublicApiClient.ListResponse;
|
||||
@@ -129,7 +128,7 @@ public class DeploymentWorkflowApiTest extends EnterpriseWorkflowTestApi
|
||||
Deployment adhocDeployment = deploymentMap.get("adhoc.bpmn20.xml");
|
||||
|
||||
assertEquals(activitiDeployment.getId(), adhocDeployment.getId());
|
||||
assertEquals(activitiDeployment.getCategory(), WorkflowDeployer.CATEGORY_FULL_ACCESS);
|
||||
assertEquals(activitiDeployment.getCategory(), adhocDeployment.getCategory());
|
||||
assertEquals(activitiDeployment.getName(), adhocDeployment.getName());
|
||||
assertEquals(activitiDeployment.getDeploymentTime(), adhocDeployment.getDeployedAt());
|
||||
|
||||
@@ -252,7 +251,7 @@ public class DeploymentWorkflowApiTest extends EnterpriseWorkflowTestApi
|
||||
assertNotNull(deployment);
|
||||
|
||||
assertEquals(activitiDeployment.getId(), deployment.getId());
|
||||
assertEquals(activitiDeployment.getCategory(), WorkflowDeployer.CATEGORY_FULL_ACCESS);
|
||||
assertEquals(activitiDeployment.getCategory(), deployment.getCategory());
|
||||
assertEquals(activitiDeployment.getName(), deployment.getName());
|
||||
assertEquals(activitiDeployment.getDeploymentTime(), deployment.getDeployedAt());
|
||||
|
||||
|
@@ -7,7 +7,7 @@
|
||||
<parent>
|
||||
<groupId>org.alfresco</groupId>
|
||||
<artifactId>alfresco-community-repo</artifactId>
|
||||
<version>17.132</version>
|
||||
<version>17.128-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<dependencies>
|
||||
|
@@ -46,7 +46,6 @@ public class PropTablesCleaner
|
||||
{
|
||||
private static final String PROPERTY_PROP_TABLE_CLEANER_ALG = "system.prop_table_cleaner.algorithm";
|
||||
private static final String PROP_TABLE_CLEANER_ALG_V2 = "V2";
|
||||
private static final String PROP_TABLE_CLEANER_ALG_V3 = "V3";
|
||||
|
||||
private PropertyValueDAO propertyValueDAO;
|
||||
private JobLockService jobLockService;
|
||||
@@ -101,10 +100,6 @@ public class PropTablesCleaner
|
||||
{
|
||||
propertyValueDAO.cleanupUnusedValuesV2();
|
||||
}
|
||||
else if (PROP_TABLE_CLEANER_ALG_V3.equalsIgnoreCase(getAlgorithm()))
|
||||
{
|
||||
propertyValueDAO.cleanupUnusedValuesV3();
|
||||
}
|
||||
else
|
||||
{
|
||||
propertyValueDAO.cleanupUnusedValues();
|
||||
|
@@ -2,7 +2,7 @@
|
||||
* #%L
|
||||
* Alfresco Repository
|
||||
* %%
|
||||
* Copyright (C) 2005 - 2018 Alfresco Software Limited
|
||||
* Copyright (C) 2005 - 2022 Alfresco Software Limited
|
||||
* %%
|
||||
* This file is part of the Alfresco software.
|
||||
* If the software was purchased under a paid Alfresco license, the terms of
|
||||
@@ -25,7 +25,6 @@
|
||||
*/
|
||||
package org.alfresco.repo.descriptor;
|
||||
|
||||
import java.security.Principal;
|
||||
import java.util.Date;
|
||||
import java.util.Properties;
|
||||
|
||||
@@ -125,6 +124,15 @@ public class DescriptorStartupLog extends AbstractLifecycleBean
|
||||
{
|
||||
msg += ", NO CLUSTER";
|
||||
}
|
||||
|
||||
if(license.isCustomEmbeddedWorkflowEnabled())
|
||||
{
|
||||
msg += ", customEmbeddedWorkflow:enabled";
|
||||
}
|
||||
else
|
||||
{
|
||||
msg += ", NO CUSTOM EMBEDDED WORKFLOW";
|
||||
}
|
||||
|
||||
String holder = license.getHolderOrganisation();
|
||||
if (holder != null)
|
||||
|
@@ -377,6 +377,4 @@ public interface PropertyValueDAO
|
||||
void cleanupUnusedValues();
|
||||
|
||||
void cleanupUnusedValuesV2();
|
||||
|
||||
void cleanupUnusedValuesV3();
|
||||
}
|
||||
|
@@ -758,23 +758,4 @@ public class PropertyValueDAOImpl extends AbstractPropertyValueDAOImpl
|
||||
clearCaches();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cleanupUnusedValuesV3()
|
||||
{
|
||||
// Run the main script
|
||||
try
|
||||
{
|
||||
scriptExecutor.exec(false, "alfresco/dbscripts/utility/${db.script.dialect}", "CleanAlfPropTablesV3.sql");
|
||||
}
|
||||
catch (RuntimeException e)
|
||||
{
|
||||
logger.error("The cleanup script failed: ", e);
|
||||
throw e;
|
||||
}
|
||||
finally
|
||||
{
|
||||
clearCaches();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -73,7 +73,7 @@ public class DeleteNotExistsExecutor implements StatementExecutor
|
||||
private String sql;
|
||||
private int line;
|
||||
private File scriptFile;
|
||||
protected Properties globalProperties;
|
||||
private Properties globalProperties;
|
||||
|
||||
protected boolean readOnly;
|
||||
protected int deleteBatchSize;
|
||||
|
@@ -1,501 +0,0 @@
|
||||
/*
|
||||
* #%L
|
||||
* Alfresco Repository
|
||||
* %%
|
||||
* Copyright (C) 2005 - 2022 Alfresco Software Limited
|
||||
* %%
|
||||
* This file is part of the Alfresco software.
|
||||
* If the software was purchased under a paid Alfresco license, the terms of
|
||||
* the paid license agreement will prevail. Otherwise, the software is
|
||||
* provided under the following open source license terms:
|
||||
*
|
||||
* Alfresco is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Alfresco is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||
* #L%
|
||||
*/
|
||||
|
||||
package org.alfresco.repo.domain.schema.script;
|
||||
|
||||
import java.io.File;
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Date;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.alfresco.repo.domain.dialect.Dialect;
|
||||
import org.alfresco.repo.domain.dialect.MySQLInnoDBDialect;
|
||||
import org.alfresco.util.Pair;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
/**
|
||||
* Same logic as DeleteNotExistsExecutor with the following changes:
|
||||
* <p/>
|
||||
* - filters the queries by unique values
|
||||
* <p/>
|
||||
* - eager close of result sets
|
||||
* <p/>
|
||||
* - we store all the ids in memory and process them from there - the secondary ids are stored in a unique list without
|
||||
* duplicate values.
|
||||
* <p/>
|
||||
* - we only cross 2 sets (the potential ids to delete from the primary table with the set of all secondary ids in that
|
||||
* range) removing all elements from the second set from the first set
|
||||
* <p/>
|
||||
* - every {pauseAndRecoverBatchSize} rows deleted we close all prepared statements and close the connection and sleep
|
||||
* for {pauseAndRecoverTime} milliseconds. This is necessary to allow the DBMS to perform the background tasks without
|
||||
* load from ACS. When we do not do this and if we are performing millions of deletes, the connection eventually gets
|
||||
* aborted.
|
||||
*
|
||||
* @author Eva Vasques
|
||||
*/
|
||||
public class DeleteNotExistsV3Executor extends DeleteNotExistsExecutor
|
||||
{
|
||||
private static Log logger = LogFactory.getLog(DeleteNotExistsV3Executor.class);
|
||||
|
||||
public static final String PROPERTY_PAUSE_AND_RECOVER_BATCHSIZE = "system.delete_not_exists.pauseAndRecoverBatchSize";
|
||||
public static final String PROPERTY_PAUSE_AND_RECOVER_TIME = "system.delete_not_exists.pauseAndRecoverTime";
|
||||
public static final long DEFAULT_PAUSE_AND_RECOVER_BATCHSIZE = 500000;
|
||||
public static final long DEFAULT_PAUSE_AND_RECOVER_TIME = 300000;
|
||||
|
||||
private Dialect dialect;
|
||||
private final DataSource dataSource;
|
||||
private long pauseAndRecoverTime;
|
||||
private long pauseAndRecoverBatchSize;
|
||||
private boolean pauseAndRecover = false;
|
||||
private int processedCounter = 0;
|
||||
|
||||
public DeleteNotExistsV3Executor(Dialect dialect, Connection connection, String sql, int line, File scriptFile,
|
||||
Properties globalProperties, DataSource dataSource)
|
||||
{
|
||||
super(connection, sql, line, scriptFile, globalProperties);
|
||||
this.dialect = dialect;
|
||||
this.dataSource = dataSource;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute() throws Exception
|
||||
{
|
||||
checkProperties();
|
||||
|
||||
String pauseAndRecoverBatchSizeString = globalProperties.getProperty(PROPERTY_PAUSE_AND_RECOVER_BATCHSIZE);
|
||||
pauseAndRecoverBatchSize = pauseAndRecoverBatchSizeString == null ? DEFAULT_PAUSE_AND_RECOVER_BATCHSIZE
|
||||
: Long.parseLong(pauseAndRecoverBatchSizeString);
|
||||
|
||||
String pauseAndRecoverTimeString = globalProperties.getProperty(PROPERTY_PAUSE_AND_RECOVER_TIME);
|
||||
pauseAndRecoverTime = pauseAndRecoverTimeString == null ? DEFAULT_PAUSE_AND_RECOVER_TIME
|
||||
: Long.parseLong(pauseAndRecoverTimeString);
|
||||
|
||||
super.execute();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void process(Pair<String, String>[] tableColumn, Long[] tableUpperLimits, String[] optionalWhereClauses)
|
||||
throws SQLException
|
||||
{
|
||||
String primaryTableName = tableColumn[0].getFirst();
|
||||
String primaryColumnName = tableColumn[0].getSecond();
|
||||
String primaryWhereClause = optionalWhereClauses[0];
|
||||
|
||||
Long primaryId = 0L;
|
||||
|
||||
deletedCount = 0L;
|
||||
startTime = new Date();
|
||||
|
||||
processBatch(primaryTableName, primaryColumnName, primaryWhereClause, primaryId, tableColumn, tableUpperLimits,
|
||||
optionalWhereClauses);
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
{
|
||||
String msg = ((readOnly) ? "Script would have" : "Script") + " deleted a total of " + deletedCount
|
||||
+ " items from table " + primaryTableName + ".";
|
||||
logger.debug(msg);
|
||||
}
|
||||
}
|
||||
|
||||
private void processBatch(String primaryTableName, String primaryColumnName, String primaryWhereClause, Long primaryId,
|
||||
Pair<String, String>[] tableColumn, Long[] tableUpperLimits, String[] optionalWhereClauses) throws SQLException
|
||||
{
|
||||
PreparedStatement primaryPrepStmt = null;
|
||||
PreparedStatement[] secondaryPrepStmts = null;
|
||||
PreparedStatement deletePrepStmt = null;
|
||||
Set<Long> deleteIds = new HashSet<>();
|
||||
pauseAndRecover = false;
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
connection.setAutoCommit(false);
|
||||
|
||||
primaryPrepStmt = connection
|
||||
.prepareStatement(createPreparedSelectStatement(primaryTableName, primaryColumnName, primaryWhereClause));
|
||||
primaryPrepStmt.setFetchSize(batchSize);
|
||||
primaryPrepStmt.setLong(1, primaryId);
|
||||
primaryPrepStmt.setLong(2, tableUpperLimits[0]);
|
||||
|
||||
boolean hasResults = primaryPrepStmt.execute();
|
||||
|
||||
if (hasResults)
|
||||
{
|
||||
|
||||
// Prepared statements for secondary tables for the next batch
|
||||
secondaryPrepStmts = new PreparedStatement[tableColumn.length];
|
||||
for (int i = 1; i < tableColumn.length; i++)
|
||||
{
|
||||
PreparedStatement secStmt = connection.prepareStatement(createPreparedSelectStatement(
|
||||
tableColumn[i].getFirst(), tableColumn[i].getSecond(), optionalWhereClauses[i]));
|
||||
secStmt.setFetchSize(batchSize);
|
||||
secondaryPrepStmts[i] = secStmt;
|
||||
}
|
||||
|
||||
deletePrepStmt = connection.prepareStatement(
|
||||
createPreparedDeleteStatement(primaryTableName, primaryColumnName, deleteBatchSize, primaryWhereClause));
|
||||
|
||||
// Timeout is only checked at each batch start.
|
||||
// It can be further refined by being verified at each primary row processing.
|
||||
while (hasResults && !isTimeoutExceeded())
|
||||
{
|
||||
// Process batch
|
||||
primaryId = processPrimaryTableResultSet(primaryPrepStmt, secondaryPrepStmts, deletePrepStmt, deleteIds,
|
||||
primaryTableName, primaryColumnName, tableColumn);
|
||||
connection.commit();
|
||||
|
||||
// If we have no more results (next primaryId is null) or job is marked for pause and recover, do
|
||||
// not start the next batch
|
||||
if (primaryId == null || pauseAndRecover)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
// Prepare for next batch
|
||||
primaryPrepStmt.setLong(1, primaryId + 1);
|
||||
primaryPrepStmt.setLong(2, tableUpperLimits[0]);
|
||||
|
||||
// Query the primary table for the next batch
|
||||
hasResults = primaryPrepStmt.execute();
|
||||
}
|
||||
|
||||
// Check if we have any more ids to delete
|
||||
if (!deleteIds.isEmpty())
|
||||
{
|
||||
deleteFromPrimaryTable(deletePrepStmt, deleteIds, primaryTableName);
|
||||
connection.commit();
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
closeQuietly(deletePrepStmt);
|
||||
closeQuietly(secondaryPrepStmts);
|
||||
closeQuietly(primaryPrepStmt);
|
||||
|
||||
closeQuietly(connection);
|
||||
}
|
||||
|
||||
if (pauseAndRecover)
|
||||
{
|
||||
pauseAndRecoverJob(dataSource);
|
||||
logger.info("Resuming the job on primary table " + primaryTableName + " picking up after id " + primaryId);
|
||||
processBatch(primaryTableName, primaryColumnName, primaryWhereClause, primaryId, tableColumn, tableUpperLimits,
|
||||
optionalWhereClauses);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String createPreparedSelectStatement(String tableName, String columnName, String whereClause)
|
||||
{
|
||||
StringBuilder sqlBuilder = new StringBuilder("SELECT " + columnName + " FROM " + tableName + " WHERE ");
|
||||
|
||||
if (whereClause != null && !whereClause.isEmpty())
|
||||
{
|
||||
sqlBuilder.append(whereClause + " AND ");
|
||||
}
|
||||
|
||||
sqlBuilder.append(
|
||||
columnName + " >= ? AND " + columnName + " <= ? GROUP BY " + columnName + " ORDER BY " + columnName + " ASC ");
|
||||
|
||||
if (dialect instanceof MySQLInnoDBDialect)
|
||||
{
|
||||
sqlBuilder.append(" LIMIT " + batchSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
sqlBuilder.append(" OFFSET 0 ROWS FETCH FIRST " + batchSize + " ROWS ONLY");
|
||||
}
|
||||
|
||||
return sqlBuilder.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Long processPrimaryTableResultSet(PreparedStatement primaryPrepStmt, PreparedStatement[] secondaryPrepStmts,
|
||||
PreparedStatement deletePrepStmt, Set<Long> deleteIds, String primaryTableName, String primaryColumnName,
|
||||
Pair<String, String>[] tableColumn) throws SQLException
|
||||
{
|
||||
Long primaryId = null;
|
||||
Long minSecId = 0L;
|
||||
Long maxSecId = 0L;
|
||||
|
||||
// Set all rows retrieved from the primary table as our potential ids to delete
|
||||
Set<Long> potentialIdsToDelete = new HashSet<Long>();
|
||||
Long minPotentialId = 0L;
|
||||
Long maxPotentialId = 0L;
|
||||
try (ResultSet resultSet = primaryPrepStmt.getResultSet())
|
||||
{
|
||||
while (resultSet.next())
|
||||
{
|
||||
primaryId = resultSet.getLong(primaryColumnName);
|
||||
potentialIdsToDelete.add(primaryId);
|
||||
|
||||
minPotentialId = (minPotentialId == 0L || primaryId < minPotentialId) ? primaryId : minPotentialId;
|
||||
maxPotentialId = primaryId > maxPotentialId ? primaryId : maxPotentialId;
|
||||
}
|
||||
}
|
||||
|
||||
if (potentialIdsToDelete.size() == 0)
|
||||
{
|
||||
// Nothing more to do
|
||||
return primaryId;
|
||||
}
|
||||
|
||||
int rowsInBatch = potentialIdsToDelete.size();
|
||||
processedCounter = processedCounter + rowsInBatch;
|
||||
|
||||
// Get a combined list of the ids present in the secondary tables
|
||||
SecondaryResultsInfo secondaryResultsInfo = getSecondaryResults(secondaryPrepStmts, tableColumn, minPotentialId,
|
||||
maxPotentialId);
|
||||
|
||||
Set<Long> secondaryResults = secondaryResultsInfo.getValues();
|
||||
|
||||
if (secondaryResultsInfo.getSize() > 0)
|
||||
{
|
||||
minSecId = secondaryResultsInfo.getMinValue();
|
||||
maxSecId = secondaryResultsInfo.getMaxValue();
|
||||
|
||||
// From our potentialIdsToDelete list, remove all non-eligible ids: any id that is in a secondary table or
|
||||
// any ID past the last ID we were able to access in the secondary tables (maxSecId)
|
||||
Iterator<Long> it = potentialIdsToDelete.iterator();
|
||||
while (it.hasNext())
|
||||
{
|
||||
Long id = it.next();
|
||||
if (id > maxSecId || secondaryResults.contains(id))
|
||||
{
|
||||
it.remove();
|
||||
}
|
||||
}
|
||||
|
||||
// The next starting primary ID for the next batch will either be the next last id evaluated from the
|
||||
// primary table or, in case the secondary queries did not get that far, the last secondary table id
|
||||
// evaluated (maxSecId)
|
||||
primaryId = primaryId < maxSecId ? primaryId : maxSecId;
|
||||
}
|
||||
|
||||
// Delete the ids that are eligble from the primary table
|
||||
if (potentialIdsToDelete.size() > 0)
|
||||
{
|
||||
deleteInBatches(potentialIdsToDelete, deleteIds, primaryTableName, deletePrepStmt);
|
||||
}
|
||||
|
||||
if (logger.isTraceEnabled())
|
||||
{
|
||||
logger.trace("Rows processed " + rowsInBatch + " from primary table " + primaryTableName + ". Primary: ["
|
||||
+ minPotentialId + "," + maxPotentialId + "] Secondary rows processed: " + secondaryResultsInfo.getSize()
|
||||
+ " [" + minSecId + "," + maxSecId + "] Total Deleted: " + deletedCount);
|
||||
}
|
||||
|
||||
// If the total rows processed from all batches so far is greater that the defined pauseAndRecoverBatchSize,
|
||||
// mark the job to pause and recover after completing this batch
|
||||
if (processedCounter >= pauseAndRecoverBatchSize)
|
||||
{
|
||||
pauseAndRecover = true;
|
||||
}
|
||||
|
||||
// Return the last primary id processed for the next batch
|
||||
return primaryId;
|
||||
}
|
||||
|
||||
private void deleteInBatches(Set<Long> potentialIdsToDelete, Set<Long> deleteIds, String primaryTableName,
|
||||
PreparedStatement deletePrepStmt) throws SQLException
|
||||
{
|
||||
Iterator<Long> potentialIdsIt = potentialIdsToDelete.iterator();
|
||||
while (potentialIdsIt.hasNext())
|
||||
{
|
||||
Long idToDelete = (Long) potentialIdsIt.next();
|
||||
deleteIds.add(idToDelete);
|
||||
|
||||
if (deleteIds.size() == deleteBatchSize)
|
||||
{
|
||||
deleteFromPrimaryTable(deletePrepStmt, deleteIds, primaryTableName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Get a combined list of the ids present in all the secondary tables
|
||||
*/
|
||||
private SecondaryResultsInfo getSecondaryResults(PreparedStatement[] preparedStatements, Pair<String, String>[] tableColumn,
|
||||
Long minPotentialId, Long maxPotentialId) throws SQLException
|
||||
{
|
||||
Set<Long> secondaryResultValues = new HashSet<Long>();
|
||||
Long lowestUpperValue = 0L;
|
||||
for (int i = 1; i < preparedStatements.length; i++)
|
||||
{
|
||||
String columnId = tableColumn[i].getSecond();
|
||||
PreparedStatement secStmt = preparedStatements[i];
|
||||
secStmt.setLong(1, minPotentialId);
|
||||
secStmt.setLong(2, maxPotentialId);
|
||||
|
||||
// Execute the query on each secondary table
|
||||
boolean secHasResults = secStmt.execute();
|
||||
if (secHasResults)
|
||||
{
|
||||
try (ResultSet secResultSet = secStmt.getResultSet())
|
||||
{
|
||||
Long thisId = 0L;
|
||||
Long resultSize = 0L;
|
||||
Long upperValue = 0L;
|
||||
while (secResultSet.next())
|
||||
{
|
||||
resultSize++;
|
||||
thisId = secResultSet.getLong(columnId);
|
||||
|
||||
// Add to the list if it's not there yet
|
||||
if (!secondaryResultValues.contains(thisId))
|
||||
{
|
||||
secondaryResultValues.add(thisId);
|
||||
}
|
||||
|
||||
upperValue = thisId > upperValue ? thisId : upperValue;
|
||||
}
|
||||
|
||||
// Set the upper min value. We need to gather the last ID processed, so on the next batch on the
|
||||
// primary table we can resume from there. We only need to do this if the number of results of the
|
||||
// secondary table matches the batch size (if not, it means that there aren't more results up to
|
||||
// maxPotentialId). Example on why this is needed: Primary table batch has 100k results from id's 1
|
||||
// to 250000. Secondary table on that interval returns 100k results from id 3 to 210000. Next batch
|
||||
// needs to start on id 210001
|
||||
if (upperValue > 0 && resultSize == batchSize)
|
||||
{
|
||||
lowestUpperValue = (lowestUpperValue == 0L || upperValue < lowestUpperValue) ? upperValue
|
||||
: lowestUpperValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If lowestUpperValue is still 0 (because a secondary table never had more or the same number of results as the
|
||||
// primary table), the next id should be the last max id evaluated from the primary table (maxPotentialId)
|
||||
lowestUpperValue = lowestUpperValue == 0 ? maxPotentialId : lowestUpperValue;
|
||||
|
||||
// Remove all values after the lower upper value of a secondary table
|
||||
long minSecId = 0L;
|
||||
Iterator<Long> it = secondaryResultValues.iterator();
|
||||
while (it.hasNext())
|
||||
{
|
||||
long secondaryId = it.next();
|
||||
if (secondaryId > lowestUpperValue)
|
||||
{
|
||||
it.remove();
|
||||
}
|
||||
else
|
||||
{
|
||||
minSecId = (minSecId == 0L || secondaryId < minSecId) ? secondaryId : minSecId;
|
||||
}
|
||||
}
|
||||
|
||||
// Return a combined list of the ids present in all the secondary tables
|
||||
return new SecondaryResultsInfo(secondaryResultValues, minSecId, lowestUpperValue);
|
||||
}
|
||||
|
||||
private class SecondaryResultsInfo
|
||||
{
|
||||
Set<Long> values;
|
||||
long minValue;
|
||||
long maxValue;
|
||||
long size;
|
||||
|
||||
public SecondaryResultsInfo(Set<Long> values, long minValue, long maxValue)
|
||||
{
|
||||
super();
|
||||
this.values = values;
|
||||
this.minValue = minValue;
|
||||
this.maxValue = maxValue;
|
||||
this.size = values.size();
|
||||
}
|
||||
|
||||
public Set<Long> getValues()
|
||||
{
|
||||
return values;
|
||||
}
|
||||
|
||||
public long getMinValue()
|
||||
{
|
||||
return minValue;
|
||||
}
|
||||
|
||||
public long getMaxValue()
|
||||
{
|
||||
return maxValue;
|
||||
}
|
||||
|
||||
public long getSize()
|
||||
{
|
||||
return size;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Sleep for {pauseAndRecoverTime} before opening a new connection and continue to process new batches
|
||||
*/
|
||||
private void pauseAndRecoverJob(DataSource dataSource) throws SQLException
|
||||
{
|
||||
if (logger.isDebugEnabled())
|
||||
{
|
||||
logger.debug("Reached batch size for pause and recovery. Job will resume in " + pauseAndRecoverTime + " ms");
|
||||
}
|
||||
// Wait
|
||||
try
|
||||
{
|
||||
Thread.sleep(pauseAndRecoverTime);
|
||||
}
|
||||
catch (InterruptedException e)
|
||||
{
|
||||
// Do nothing
|
||||
}
|
||||
// Start another connection and continue where we left off
|
||||
connection = dataSource.getConnection();
|
||||
pauseAndRecover = false;
|
||||
processedCounter = 0;
|
||||
}
|
||||
|
||||
protected void closeQuietly(Connection connection)
|
||||
{
|
||||
try
|
||||
{
|
||||
connection.close();
|
||||
}
|
||||
catch (SQLException e)
|
||||
{
|
||||
// Do nothing
|
||||
}
|
||||
finally
|
||||
{
|
||||
connection = null;
|
||||
}
|
||||
}
|
||||
}
|
@@ -196,6 +196,7 @@ public class MySQLDeleteNotExistsExecutor extends DeleteNotExistsExecutor
|
||||
if (deleteIds.size() == deleteBatchSize)
|
||||
{
|
||||
deleteFromPrimaryTable(deletePrepStmt, deleteIds, primaryTableName);
|
||||
connection.commit();
|
||||
}
|
||||
|
||||
if (!resultSet.next())
|
||||
@@ -207,12 +208,12 @@ public class MySQLDeleteNotExistsExecutor extends DeleteNotExistsExecutor
|
||||
primaryId = resultSet.getLong(primaryColumnName);
|
||||
}
|
||||
|
||||
updateSecondaryIds(primaryId, secondaryIds, secondaryPrepStmts, secondaryOffsets, secondaryResultSets, tableColumn);
|
||||
}
|
||||
if (logger.isTraceEnabled())
|
||||
{
|
||||
logger.trace("RowsProcessed " + rowsProcessed + " from primary table " + primaryTableName);
|
||||
}
|
||||
|
||||
if (logger.isTraceEnabled())
|
||||
{
|
||||
logger.trace("RowsProcessed " + rowsProcessed + " from primary table " + primaryTableName);
|
||||
updateSecondaryIds(primaryId, secondaryIds, secondaryPrepStmts, secondaryOffsets, secondaryResultSets, tableColumn);
|
||||
}
|
||||
}
|
||||
finally
|
||||
|
@@ -274,8 +274,6 @@ public class ScriptExecutorImpl implements ScriptExecutor
|
||||
|
||||
while(true)
|
||||
{
|
||||
connection = refreshConnection(connection);
|
||||
|
||||
String sqlOriginal = reader.readLine();
|
||||
line++;
|
||||
|
||||
@@ -350,24 +348,6 @@ public class ScriptExecutorImpl implements ScriptExecutor
|
||||
}
|
||||
continue;
|
||||
}
|
||||
else if (sql.startsWith("--DELETE_NOT_EXISTS_V3"))
|
||||
{
|
||||
DeleteNotExistsV3Executor deleteNotExistsFiltered = createDeleteNotExistV3Executor(dialect, connection, sql,
|
||||
line, scriptFile);
|
||||
deleteNotExistsFiltered.execute();
|
||||
|
||||
// Reset
|
||||
sb.setLength(0);
|
||||
fetchVarName = null;
|
||||
fetchColumnName = null;
|
||||
defaultFetchValue = null;
|
||||
batchTableName = null;
|
||||
doBatch = false;
|
||||
batchUpperLimit = 0;
|
||||
batchSize = 1;
|
||||
|
||||
continue;
|
||||
}
|
||||
else if (sql.startsWith("--DELETE_NOT_EXISTS"))
|
||||
{
|
||||
DeleteNotExistsExecutor deleteNotExists = createDeleteNotExistsExecutor(dialect, connection, sql, line, scriptFile);
|
||||
@@ -558,17 +538,6 @@ public class ScriptExecutorImpl implements ScriptExecutor
|
||||
}
|
||||
}
|
||||
|
||||
private Connection refreshConnection(Connection connection) throws SQLException
|
||||
{
|
||||
if (connection == null || connection.isClosed())
|
||||
{
|
||||
connection = dataSource.getConnection();
|
||||
connection.setAutoCommit(true);
|
||||
}
|
||||
|
||||
return connection;
|
||||
}
|
||||
|
||||
private DeleteNotExistsExecutor createDeleteNotExistsExecutor(Dialect dialect, Connection connection, String sql, int line, File scriptFile)
|
||||
{
|
||||
if (dialect instanceof MySQLInnoDBDialect)
|
||||
@@ -579,12 +548,6 @@ public class ScriptExecutorImpl implements ScriptExecutor
|
||||
return new DeleteNotExistsExecutor(connection, sql, line, scriptFile, globalProperties);
|
||||
}
|
||||
|
||||
private DeleteNotExistsV3Executor createDeleteNotExistV3Executor(Dialect dialect, Connection connection, String sql, int line,
|
||||
File scriptFile)
|
||||
{
|
||||
return new DeleteNotExistsV3Executor(dialect, connection, sql, line, scriptFile, globalProperties, dataSource);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the given SQL statement, absorbing exceptions that we expect during
|
||||
* schema creation or upgrade.
|
||||
|
@@ -270,33 +270,25 @@ public class RhinoScriptProcessor extends BaseProcessor implements ScriptProcess
|
||||
*/
|
||||
public Object executeString(String source, Map<String, Object> model)
|
||||
{
|
||||
return executeString(source, model, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.alfresco.service.cmr.repository.ScriptProcessor#executeString(java.lang.String, java.util.Map, boolean)
|
||||
*/
|
||||
public Object executeString(String source, Map<String, Object> model, boolean secure)
|
||||
{
|
||||
try
|
||||
{
|
||||
// compile the script based on the node content
|
||||
Script script;
|
||||
Context cx = Context.enter();
|
||||
try
|
||||
{
|
||||
script = cx.compileString(resolveScriptImports(source), "AlfrescoJS", 1, null);
|
||||
}
|
||||
finally
|
||||
{
|
||||
Context.exit();
|
||||
}
|
||||
return executeScriptImpl(script, model, secure, "string script");
|
||||
}
|
||||
catch (Throwable err)
|
||||
{
|
||||
throw new ScriptException("Failed to execute supplied script: " + err.getMessage(), err);
|
||||
}
|
||||
try
|
||||
{
|
||||
// compile the script based on the node content
|
||||
Script script;
|
||||
Context cx = Context.enter();
|
||||
try
|
||||
{
|
||||
script = cx.compileString(resolveScriptImports(source), "AlfrescoJS", 1, null);
|
||||
}
|
||||
finally
|
||||
{
|
||||
Context.exit();
|
||||
}
|
||||
return executeScriptImpl(script, model, true, "string script");
|
||||
}
|
||||
catch (Throwable err)
|
||||
{
|
||||
throw new ScriptException("Failed to execute supplied script: " + err.getMessage(), err);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -188,15 +188,6 @@ public class ScriptServiceImpl implements ScriptService
|
||||
throws ScriptException
|
||||
{
|
||||
return executeScriptString(this.defaultScriptProcessor, script, model);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.alfresco.service.cmr.repository.ScriptService#executeScriptString(java.lang.String, java.util.Map, boolean)
|
||||
*/
|
||||
public Object executeScriptString(String script, Map<String, Object> model, boolean secure)
|
||||
throws ScriptException
|
||||
{
|
||||
return executeScriptString(this.defaultScriptProcessor, script, model, secure);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -206,23 +197,12 @@ public class ScriptServiceImpl implements ScriptService
|
||||
throws ScriptException
|
||||
{
|
||||
ScriptProcessor scriptProcessor = lookupScriptProcessor(engine);
|
||||
return executeString(scriptProcessor, script, model, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.alfresco.service.cmr.repository.ScriptService#executeScriptString(java.lang.String, java.util.Map, boolean)
|
||||
*/
|
||||
public Object executeScriptString(String engine, String script, Map<String, Object> model, boolean secure)
|
||||
throws ScriptException
|
||||
{
|
||||
ScriptProcessor scriptProcessor = lookupScriptProcessor(engine);
|
||||
return executeString(scriptProcessor, script, model, secure);
|
||||
}
|
||||
return executeString(scriptProcessor, script, model);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute script
|
||||
*
|
||||
* @param processor the script processor that will be responsible for supplied script execution
|
||||
*
|
||||
* @param location the location of the script
|
||||
* @param model context model
|
||||
* @return Object the result of the script
|
||||
@@ -247,7 +227,6 @@ public class ScriptServiceImpl implements ScriptService
|
||||
/**
|
||||
* Execute script
|
||||
*
|
||||
* @param processor the script processor that will be responsible for supplied script execution
|
||||
* @param scriptRef the script node reference
|
||||
* @param contentProp the content property of the script
|
||||
* @param model the context model
|
||||
@@ -272,8 +251,7 @@ public class ScriptServiceImpl implements ScriptService
|
||||
|
||||
/**
|
||||
* Execute script
|
||||
*
|
||||
* @param processor the script processor that will be responsible for supplied script execution
|
||||
*
|
||||
* @param location the classpath string locating the script
|
||||
* @param model the context model
|
||||
* @return Object the result of the script
|
||||
@@ -297,15 +275,12 @@ public class ScriptServiceImpl implements ScriptService
|
||||
|
||||
/**
|
||||
* Execute script string
|
||||
*
|
||||
* @param processor the script processor that will be responsible for supplied script execution
|
||||
*
|
||||
* @param script the script string
|
||||
* @param model the context model
|
||||
* @param secure the flag indicating if string script is considered secure (e.g., if it comes from classpath)
|
||||
* if true it will have access to the full execution context, if false the script will be executed in a sandbox context
|
||||
* @param model the context model
|
||||
* @return Object the result of the script
|
||||
*/
|
||||
protected Object executeString(ScriptProcessor processor, String script, Map<String, Object> model, boolean secure)
|
||||
protected Object executeString(ScriptProcessor processor, String script, Map<String, Object> model)
|
||||
{
|
||||
ParameterCheck.mandatoryString("script", script);
|
||||
|
||||
@@ -315,7 +290,7 @@ public class ScriptServiceImpl implements ScriptService
|
||||
}
|
||||
try
|
||||
{
|
||||
return processor.executeString(script, model, secure);
|
||||
return processor.executeString(script, model);
|
||||
}
|
||||
catch (Throwable err)
|
||||
{
|
||||
|
@@ -74,18 +74,6 @@ public interface WorkflowComponent
|
||||
* @since 4.0
|
||||
*/
|
||||
public WorkflowDeployment deployDefinition(InputStream workflowDefinition, String mimetype, String name);
|
||||
|
||||
/**
|
||||
* Deploy a Workflow Definition
|
||||
*
|
||||
* @param workflowDefinition the content object containing the definition
|
||||
* @param mimetype (optional) the mime type of the workflow definition
|
||||
* @param name (optional) a name to represent the deployment
|
||||
* @param fullAccess true if category should be defined in order to consider the deployment secure
|
||||
* @return workflow deployment descriptor
|
||||
* @since 4.0
|
||||
*/
|
||||
public WorkflowDeployment deployDefinition(InputStream workflowDefinition, String mimetype, String name, boolean fullAccess);
|
||||
|
||||
/**
|
||||
* Is the specified Workflow Definition already deployed?
|
||||
@@ -98,14 +86,7 @@ public interface WorkflowComponent
|
||||
* @return true => already deployed
|
||||
*/
|
||||
public boolean isDefinitionDeployed(InputStream workflowDefinition, String mimetype);
|
||||
|
||||
/**
|
||||
* Sets the deployment category if applicable to allow the workflow to have full access
|
||||
*
|
||||
* @param workflowDefinition the definition to check
|
||||
*/
|
||||
public void checkDeploymentCategory(InputStream workflowDefinition);
|
||||
|
||||
|
||||
/**
|
||||
* Undeploy an exisiting Workflow Definition
|
||||
*
|
||||
|
@@ -85,7 +85,6 @@ public class WorkflowDeployer extends AbstractLifecycleBean
|
||||
public static final String REDEPLOY = "redeploy";
|
||||
|
||||
public static final String CATEGORY_ALFRESCO_INTERNAL = "http://alfresco.org/workflows/internal";
|
||||
public static final String CATEGORY_FULL_ACCESS = "http://alfresco.org/workflows/fullAccess";
|
||||
|
||||
// Dependencies
|
||||
private TransactionService transactionService;
|
||||
@@ -307,14 +306,12 @@ public class WorkflowDeployer extends AbstractLifecycleBean
|
||||
if (!redeploy && workflowService.isDefinitionDeployed(engineId, workflowResource.getInputStream(), mimetype))
|
||||
{
|
||||
if (logger.isDebugEnabled())
|
||||
{
|
||||
logger.debug("Workflow deployer: Definition '" + location + "' already deployed. Checking deploymentcategory...");
|
||||
}
|
||||
workflowService.checkDeploymentCategory(engineId, workflowResource.getInputStream());
|
||||
logger.debug("Workflow deployer: Definition '" + location + "' already deployed");
|
||||
}
|
||||
else
|
||||
{
|
||||
WorkflowDeployment deployment = workflowService.deployDefinition(engineId, workflowResource.getInputStream(), mimetype, workflowResource.getFilename(), true);
|
||||
WorkflowDeployment deployment = workflowService.deployDefinition(engineId, workflowResource.getInputStream(),
|
||||
mimetype, workflowResource.getFilename());
|
||||
logDeployment(location, deployment);
|
||||
}
|
||||
}
|
||||
|
@@ -232,20 +232,9 @@ public class WorkflowServiceImpl implements WorkflowService
|
||||
* .lang.String, java.io.InputStream, java.lang.String, java.lang.String)
|
||||
*/
|
||||
public WorkflowDeployment deployDefinition(String engineId, InputStream workflowDefinition, String mimetype, String name)
|
||||
{
|
||||
return deployDefinition(engineId, workflowDefinition, mimetype, name, false);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see
|
||||
* org.alfresco.service.cmr.workflow.WorkflowService#deployDefinition(java
|
||||
* .lang.String, java.io.InputStream, java.lang.String, java.lang.String, boolean)
|
||||
*/
|
||||
public WorkflowDeployment deployDefinition(String engineId, InputStream workflowDefinition, String mimetype, String name, boolean fullAccess)
|
||||
{
|
||||
WorkflowComponent component = getWorkflowComponent(engineId);
|
||||
WorkflowDeployment deployment = component.deployDefinition(workflowDefinition, mimetype, name, fullAccess);
|
||||
WorkflowDeployment deployment = component.deployDefinition(workflowDefinition, mimetype, name);
|
||||
|
||||
if (logger.isDebugEnabled() && deployment.getProblems().length > 0)
|
||||
{
|
||||
@@ -288,18 +277,6 @@ public class WorkflowServiceImpl implements WorkflowService
|
||||
return component.isDefinitionDeployed(workflowDefinition, mimetype);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see
|
||||
* org.alfresco.service.cmr.workflow.WorkflowService#checkDeploymentCategory
|
||||
* (java.lang.String, java.io.InputStream)
|
||||
*/
|
||||
public void checkDeploymentCategory(String engineId, InputStream workflowDefinition)
|
||||
{
|
||||
WorkflowComponent component = getWorkflowComponent(engineId);
|
||||
component.checkDeploymentCategory(workflowDefinition);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see
|
||||
|
@@ -1,28 +1,28 @@
|
||||
/*
|
||||
* #%L
|
||||
* Alfresco Repository
|
||||
* %%
|
||||
* Copyright (C) 2005 - 2016 Alfresco Software Limited
|
||||
* %%
|
||||
* This file is part of the Alfresco software.
|
||||
* If the software was purchased under a paid Alfresco license, the terms of
|
||||
* the paid license agreement will prevail. Otherwise, the software is
|
||||
* provided under the following open source license terms:
|
||||
*
|
||||
* Alfresco is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Alfresco is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||
* #L%
|
||||
*/
|
||||
/*
|
||||
* #%L
|
||||
* Alfresco Repository
|
||||
* %%
|
||||
* Copyright (C) 2005 - 2016 Alfresco Software Limited
|
||||
* %%
|
||||
* This file is part of the Alfresco software.
|
||||
* If the software was purchased under a paid Alfresco license, the terms of
|
||||
* the paid license agreement will prevail. Otherwise, the software is
|
||||
* provided under the following open source license terms:
|
||||
*
|
||||
* Alfresco is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Alfresco is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||
* #L%
|
||||
*/
|
||||
|
||||
package org.alfresco.repo.workflow.activiti;
|
||||
|
||||
@@ -242,45 +242,45 @@ public class ActivitiWorkflowEngine extends BPMEngine implements WorkflowEngine
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public WorkflowInstance cancelWorkflow(String workflowId)
|
||||
{
|
||||
String localId = createLocalId(workflowId);
|
||||
try
|
||||
{
|
||||
ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(localId).singleResult();
|
||||
if (processInstance == null)
|
||||
{
|
||||
throw new WorkflowException(messageService.getMessage(ERR_CANCEL_UNEXISTING_WORKFLOW));
|
||||
}
|
||||
|
||||
// TODO: Cancel VS delete?
|
||||
// Delete the process instance
|
||||
runtimeService.deleteProcessInstance(processInstance.getId(), WorkflowConstants.PROP_CANCELLED);
|
||||
|
||||
// Convert historic process instance
|
||||
HistoricProcessInstance deletedInstance = historyService.createHistoricProcessInstanceQuery().processInstanceId(processInstance.getId())
|
||||
.singleResult();
|
||||
WorkflowInstance result = typeConverter.convert(deletedInstance);
|
||||
|
||||
// Delete the historic process instance
|
||||
// MNT-15498
|
||||
if (!activitiUtil.isRetentionHistoricProcessInstanceEnabled())
|
||||
{
|
||||
historyService.deleteHistoricProcessInstance(deletedInstance.getId());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
catch (ActivitiException ae)
|
||||
{
|
||||
String msg = messageService.getMessage(ERR_CANCEL_WORKFLOW);
|
||||
if (logger.isDebugEnabled())
|
||||
{
|
||||
logger.debug(msg, ae);
|
||||
}
|
||||
throw new WorkflowException(msg, ae);
|
||||
}
|
||||
}
|
||||
public WorkflowInstance cancelWorkflow(String workflowId)
|
||||
{
|
||||
String localId = createLocalId(workflowId);
|
||||
try
|
||||
{
|
||||
ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(localId).singleResult();
|
||||
if (processInstance == null)
|
||||
{
|
||||
throw new WorkflowException(messageService.getMessage(ERR_CANCEL_UNEXISTING_WORKFLOW));
|
||||
}
|
||||
|
||||
// TODO: Cancel VS delete?
|
||||
// Delete the process instance
|
||||
runtimeService.deleteProcessInstance(processInstance.getId(), WorkflowConstants.PROP_CANCELLED);
|
||||
|
||||
// Convert historic process instance
|
||||
HistoricProcessInstance deletedInstance = historyService.createHistoricProcessInstanceQuery().processInstanceId(processInstance.getId())
|
||||
.singleResult();
|
||||
WorkflowInstance result = typeConverter.convert(deletedInstance);
|
||||
|
||||
// Delete the historic process instance
|
||||
// MNT-15498
|
||||
if (!activitiUtil.isRetentionHistoricProcessInstanceEnabled())
|
||||
{
|
||||
historyService.deleteHistoricProcessInstance(deletedInstance.getId());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
catch (ActivitiException ae)
|
||||
{
|
||||
String msg = messageService.getMessage(ERR_CANCEL_WORKFLOW);
|
||||
if (logger.isDebugEnabled())
|
||||
{
|
||||
logger.debug(msg, ae);
|
||||
}
|
||||
throw new WorkflowException(msg, ae);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
@@ -336,14 +336,6 @@ public class ActivitiWorkflowEngine extends BPMEngine implements WorkflowEngine
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public WorkflowDeployment deployDefinition(InputStream workflowDefinition, String mimetype, String name)
|
||||
{
|
||||
return deployDefinition(workflowDefinition, mimetype, name, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public WorkflowDeployment deployDefinition(InputStream workflowDefinition, String mimetype, String name, boolean fullAccess)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -371,10 +363,6 @@ public class ActivitiWorkflowEngine extends BPMEngine implements WorkflowEngine
|
||||
{
|
||||
repoService.setDeploymentCategory(deployment.getId(), WorkflowDeployer.CATEGORY_ALFRESCO_INTERNAL);
|
||||
}
|
||||
else if (fullAccess)
|
||||
{
|
||||
repoService.setDeploymentCategory(deployment.getId(), WorkflowDeployer.CATEGORY_FULL_ACCESS);
|
||||
}
|
||||
}
|
||||
|
||||
// No problems can be added to the WorkflowDeployment, warnings are
|
||||
@@ -1017,46 +1005,6 @@ public class ActivitiWorkflowEngine extends BPMEngine implements WorkflowEngine
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public void checkDeploymentCategory(InputStream workflowDefinition)
|
||||
{
|
||||
try
|
||||
{
|
||||
String key = getProcessKey(workflowDefinition);
|
||||
ProcessDefinition pd = activitiUtil.getProcessDefinitionByKey(key);
|
||||
String deploymentId = pd.getDeploymentId();
|
||||
|
||||
List<ProcessDefinition> definitionList = repoService.createProcessDefinitionQuery().deploymentId(deploymentId).list();
|
||||
if (definitionList != null && definitionList.size() > 0)
|
||||
{
|
||||
boolean internalCategory = true;
|
||||
for (ProcessDefinition processDefinition : definitionList)
|
||||
{
|
||||
if (WorkflowDeployer.CATEGORY_ALFRESCO_INTERNAL.equals(processDefinition.getCategory()) == false)
|
||||
{
|
||||
internalCategory = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!internalCategory)
|
||||
{
|
||||
repoService.setDeploymentCategory(deploymentId, WorkflowDeployer.CATEGORY_FULL_ACCESS);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
if (logger.isDebugEnabled())
|
||||
{
|
||||
logger.debug("Category was not set: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String getProcessKey(InputStream workflowDefinition) throws Exception
|
||||
{
|
||||
try
|
||||
|
@@ -32,10 +32,8 @@ import org.activiti.engine.delegate.VariableScope;
|
||||
import org.activiti.engine.impl.cfg.ProcessEngineConfigurationImpl;
|
||||
import org.activiti.engine.impl.context.Context;
|
||||
import org.activiti.engine.impl.el.Expression;
|
||||
import org.activiti.engine.impl.persistence.entity.DeploymentEntity;
|
||||
import org.alfresco.repo.security.authentication.AuthenticationUtil;
|
||||
import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
|
||||
import org.alfresco.repo.workflow.WorkflowDeployer;
|
||||
import org.alfresco.repo.workflow.activiti.ActivitiConstants;
|
||||
import org.alfresco.repo.workflow.activiti.ActivitiScriptNode;
|
||||
import org.alfresco.service.ServiceRegistry;
|
||||
@@ -104,18 +102,14 @@ public class ActivitiScriptBase
|
||||
{
|
||||
// Execute the script using the appropriate processor
|
||||
Object scriptResult = null;
|
||||
|
||||
// Checks if current workflow is secure
|
||||
boolean secure = isSecure();
|
||||
|
||||
if (scriptProcessorName != null)
|
||||
{
|
||||
scriptResult = getServiceRegistry().getScriptService().executeScriptString(scriptProcessorName, theScript, model, secure);
|
||||
scriptResult = getServiceRegistry().getScriptService().executeScriptString(scriptProcessorName, theScript, model);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Use default script-processor
|
||||
scriptResult = getServiceRegistry().getScriptService().executeScriptString(theScript, model, secure);
|
||||
scriptResult = getServiceRegistry().getScriptService().executeScriptString(theScript, model);
|
||||
}
|
||||
|
||||
return scriptResult;
|
||||
@@ -148,32 +142,6 @@ public class ActivitiScriptBase
|
||||
throw new IllegalStateException("No ProcessEngineCOnfiguration found in active context");
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the workflow must be considered secure or not - based on {@link DeploymentEntity} category.
|
||||
* If it is not considered secure, the workflow will be executed in sandbox context with more restrictions
|
||||
*
|
||||
* @return true if workflow is considered secure, false otherwise
|
||||
*/
|
||||
private boolean isSecure()
|
||||
{
|
||||
String category = null;
|
||||
|
||||
try
|
||||
{
|
||||
if (Context.isExecutionContextActive())
|
||||
{
|
||||
category = Context.getExecutionContext().getDeployment().getCategory();
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
// No action required
|
||||
}
|
||||
|
||||
// If the workflow is considered secure, the deployment entity category matches the condition (either internal or full access)
|
||||
return category != null && (WorkflowDeployer.CATEGORY_ALFRESCO_INTERNAL.equals(category) || WorkflowDeployer.CATEGORY_FULL_ACCESS.equals(category));
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that the specified 'runAs' field
|
||||
* specifies a valid username.
|
||||
|
@@ -70,21 +70,10 @@ public interface ScriptProcessor extends Processor
|
||||
*
|
||||
* @param script the script string
|
||||
* @param model the context model
|
||||
* @return Object the result of the script
|
||||
* @return Obejct the result of the script
|
||||
*/
|
||||
public Object executeString(String script, Map<String, Object> model);
|
||||
|
||||
/**
|
||||
* Execute script string
|
||||
*
|
||||
* @param script the script string
|
||||
* @param model the context model
|
||||
* @param secure the flag that indicates if string is considered secure to be executed, i.e., it will have
|
||||
* access to the full execution context instead of being executed in a sandbox context
|
||||
* @return Object the result of the script
|
||||
*/
|
||||
public Object executeString(String script, Map<String, Object> model, boolean secure);
|
||||
|
||||
|
||||
/**
|
||||
* Reset the processor - such as clearing any internal caches etc.
|
||||
*/
|
||||
|
@@ -160,22 +160,7 @@ public interface ScriptService
|
||||
@Auditable(parameters = {"script", "model"})
|
||||
public Object executeScriptString(String script, Map<String, Object> model)
|
||||
throws ScriptException;
|
||||
|
||||
/**
|
||||
* Process a script against the supplied data model. Uses the default script engine.
|
||||
*
|
||||
* @param script Script content as a String.
|
||||
* @param model Object model to process script against
|
||||
* @param secure A flag indicating if string script is considered secure (e.g., if it comes from the classpath)
|
||||
* If true it will have access to the full execution context, if false the script will be executed in a sandbox context (more restricted)
|
||||
* @return output of the script (may be null or any valid wrapped JavaScript object)
|
||||
*
|
||||
* @throws ScriptException
|
||||
*/
|
||||
@Auditable(parameters = {"script", "model", "secure"})
|
||||
public Object executeScriptString(String script, Map<String, Object> model, boolean secure)
|
||||
throws ScriptException;
|
||||
|
||||
|
||||
/**
|
||||
* Process a script against the supplied data model.
|
||||
*
|
||||
@@ -190,23 +175,7 @@ public interface ScriptService
|
||||
@Auditable(parameters = {"engine", "script", "model"})
|
||||
public Object executeScriptString(String engine, String script, Map<String, Object> model)
|
||||
throws ScriptException;
|
||||
|
||||
/**
|
||||
* Process a script against the supplied data model.
|
||||
*
|
||||
* @param engine the script engine to use
|
||||
* @param script Script content as a String.
|
||||
* @param model Object model to process script against
|
||||
* @param secure A flag indicating if string script is considered secure
|
||||
*
|
||||
* @return output of the script (may be null or any valid wrapped JavaScript object)
|
||||
*
|
||||
* @throws ScriptException
|
||||
*/
|
||||
@Auditable(parameters = {"engine", "script", "model", "secure"})
|
||||
public Object executeScriptString(String engine, String script, Map<String, Object> model, boolean secure)
|
||||
throws ScriptException;
|
||||
|
||||
|
||||
/**
|
||||
* Registers a script processor with the script service
|
||||
*
|
||||
|
@@ -77,24 +77,7 @@ public interface WorkflowService
|
||||
parameters = {"engineId", "workflowDefinition", "mimetype", "name"},
|
||||
recordable = {true, false, true, true})
|
||||
public WorkflowDeployment deployDefinition(String engineId, InputStream workflowDefinition, String mimetype, String name);
|
||||
|
||||
/**
|
||||
* Deploy a Workflow Definition to the Alfresco Repository
|
||||
*
|
||||
* @param engineId the bpm engine id
|
||||
* @param workflowDefinition the workflow definition
|
||||
* @param mimetype the mimetype of the workflow definition
|
||||
* @param name a name representing the deployment
|
||||
* @parm fullAccess true if workflow should be considered secure (e.g., if it is deployed in classpath) and should have full access to the execution context,
|
||||
* false if it should be executed in a sandbox context (more restricted)
|
||||
* @return workflow deployment descriptor
|
||||
* @since 4.0
|
||||
*/
|
||||
@Auditable(
|
||||
parameters = {"engineId", "workflowDefinition", "mimetype", "name", "fullAccess"},
|
||||
recordable = {true, false, true, true, true})
|
||||
public WorkflowDeployment deployDefinition(String engineId, InputStream workflowDefinition, String mimetype, String name, boolean fullAccess);
|
||||
|
||||
|
||||
/**
|
||||
* Deploy a Workflow Definition to the Alfresco Repository
|
||||
*
|
||||
@@ -134,17 +117,6 @@ public interface WorkflowService
|
||||
parameters = {"engineId", "workflowDefinition", "mimetype"},
|
||||
recordable = {true, false, true})
|
||||
public boolean isDefinitionDeployed(String engineId, InputStream workflowDefinition, String mimetype);
|
||||
|
||||
/**
|
||||
* Checks if the deployment for supplied workflow definition has the proper category
|
||||
*
|
||||
* @param engineId the bpm engine id
|
||||
* @param workflowDefinition the definition to check
|
||||
*/
|
||||
@Auditable(
|
||||
parameters = {"engineId", "workflowDefinition"},
|
||||
recordable = {true, false})
|
||||
public void checkDeploymentCategory(String engineId, InputStream workflowDefinition);
|
||||
|
||||
/**
|
||||
* Undeploy an exisiting Workflow Definition
|
||||
|
@@ -2,7 +2,7 @@
|
||||
* #%L
|
||||
* Alfresco Repository
|
||||
* %%
|
||||
* Copyright (C) 2005 - 2016 Alfresco Software Limited
|
||||
* Copyright (C) 2005 - 2022 Alfresco Software Limited
|
||||
* %%
|
||||
* This file is part of the Alfresco software.
|
||||
* If the software was purchased under a paid Alfresco license, the terms of
|
||||
@@ -142,14 +142,10 @@ public interface LicenseDescriptor
|
||||
|
||||
/**
|
||||
* Does this license allow custom embedded workflows?
|
||||
*
|
||||
* @return <code>true</code> if the license allows custom embedded workflows
|
||||
*/
|
||||
default boolean isCustomEmbeddedWorkflowEnabled()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean isCustomEmbeddedWorkflowEnabled();
|
||||
|
||||
/**
|
||||
* ATS Transformation Server Expiry Date
|
||||
* @return the ATS Transformation Server Expiry Date or <code>null</code>
|
||||
|
@@ -1,9 +0,0 @@
|
||||
--DELETE_NOT_EXISTS_V3 alf_prop_root.id,alf_audit_app.disabled_paths_id,alf_audit_entry.audit_values_id,alf_prop_unique_ctx.prop1_id system.delete_not_exists.batchsize
|
||||
|
||||
--DELETE_NOT_EXISTS_V3 alf_prop_value.id,alf_audit_app.app_name_id,alf_audit_entry.audit_user_id,alf_prop_link.key_prop_id,alf_prop_link.value_prop_id,alf_prop_unique_ctx.value1_prop_id,alf_prop_unique_ctx.value2_prop_id,alf_prop_unique_ctx.value3_prop_id system.delete_not_exists.batchsize
|
||||
|
||||
--DELETE_NOT_EXISTS_V3 alf_prop_string_value.id,alf_prop_value.long_value."persisted_type in (3, 5, 6)",alf_audit_app.app_name_id,alf_audit_entry.audit_user_id,alf_prop_link.key_prop_id,alf_prop_link.value_prop_id,alf_prop_unique_ctx.value1_prop_id,alf_prop_unique_ctx.value2_prop_id,alf_prop_unique_ctx.value3_prop_id system.delete_not_exists.batchsize
|
||||
|
||||
--DELETE_NOT_EXISTS_V3 alf_prop_serializable_value.id,alf_prop_value.long_value.persisted_type=4,alf_audit_app.app_name_id,alf_audit_entry.audit_user_id,alf_prop_link.key_prop_id,alf_prop_link.value_prop_id,alf_prop_unique_ctx.value1_prop_id,alf_prop_unique_ctx.value2_prop_id,alf_prop_unique_ctx.value3_prop_id system.delete_not_exists.batchsize
|
||||
|
||||
--DELETE_NOT_EXISTS_V3 alf_prop_double_value.id,alf_prop_value.long_value.persisted_type=2,alf_audit_app.app_name_id,alf_audit_entry.audit_user_id,alf_prop_link.key_prop_id,alf_prop_link.value_prop_id,alf_prop_unique_ctx.value1_prop_id,alf_prop_unique_ctx.value2_prop_id,alf_prop_unique_ctx.value3_prop_id system.delete_not_exists.batchsize
|
@@ -1,9 +0,0 @@
|
||||
--DELETE_NOT_EXISTS_V3 alf_prop_root.id,alf_audit_app.disabled_paths_id,alf_audit_entry.audit_values_id,alf_prop_unique_ctx.prop1_id system.delete_not_exists.batchsize
|
||||
|
||||
--DELETE_NOT_EXISTS_V3 alf_prop_value.id,alf_audit_app.app_name_id,alf_audit_entry.audit_user_id,alf_prop_link.key_prop_id,alf_prop_link.value_prop_id,alf_prop_unique_ctx.value1_prop_id,alf_prop_unique_ctx.value2_prop_id,alf_prop_unique_ctx.value3_prop_id system.delete_not_exists.batchsize
|
||||
|
||||
--DELETE_NOT_EXISTS_V3 alf_prop_string_value.id,alf_prop_value.long_value."persisted_type in (3, 5, 6)",alf_audit_app.app_name_id,alf_audit_entry.audit_user_id,alf_prop_link.key_prop_id,alf_prop_link.value_prop_id,alf_prop_unique_ctx.value1_prop_id,alf_prop_unique_ctx.value2_prop_id,alf_prop_unique_ctx.value3_prop_id system.delete_not_exists.batchsize
|
||||
|
||||
--DELETE_NOT_EXISTS_V3 alf_prop_serializable_value.id,alf_prop_value.long_value.persisted_type=4,alf_audit_app.app_name_id,alf_audit_entry.audit_user_id,alf_prop_link.key_prop_id,alf_prop_link.value_prop_id,alf_prop_unique_ctx.value1_prop_id,alf_prop_unique_ctx.value2_prop_id,alf_prop_unique_ctx.value3_prop_id system.delete_not_exists.batchsize
|
||||
|
||||
--DELETE_NOT_EXISTS_V3 alf_prop_double_value.id,alf_prop_value.long_value.persisted_type=2,alf_audit_app.app_name_id,alf_audit_entry.audit_user_id,alf_prop_link.key_prop_id,alf_prop_link.value_prop_id,alf_prop_unique_ctx.value1_prop_id,alf_prop_unique_ctx.value2_prop_id,alf_prop_unique_ctx.value3_prop_id system.delete_not_exists.batchsize
|
@@ -1246,12 +1246,6 @@ system.delete_not_exists.read_only=false
|
||||
system.delete_not_exists.timeout_seconds=-1
|
||||
system.prop_table_cleaner.algorithm=V2
|
||||
|
||||
#Aditional options for algorithm V3
|
||||
#After how many rows processed do we pause the job to allow the DB to recover
|
||||
system.delete_not_exists.pauseAndRecoverBatchSize=500000
|
||||
#Duration of the pause in milliseconds (default 10s)
|
||||
system.delete_not_exists.pauseAndRecoverTime=10000
|
||||
|
||||
# --Node cleanup batch - default settings
|
||||
system.node_cleanup.delete_batchSize=1000
|
||||
system.node_table_cleaner.algorithm=V1
|
||||
|
@@ -88,7 +88,6 @@ import org.junit.runners.Suite;
|
||||
org.alfresco.repo.security.person.GetPeopleCannedQueryTest.class,
|
||||
|
||||
org.alfresco.repo.domain.schema.script.DeleteNotExistsExecutorTest.class,
|
||||
org.alfresco.repo.domain.schema.script.DeleteNotExistsV3ExecutorTest.class,
|
||||
org.alfresco.repo.node.cleanup.DeletedNodeBatchCleanupTest.class
|
||||
})
|
||||
public class AllDBTestsTestSuite
|
||||
|
@@ -1,201 +0,0 @@
|
||||
/*
|
||||
* #%L
|
||||
* Alfresco Repository
|
||||
* %%
|
||||
* Copyright (C) 2005 - 2022 Alfresco Software Limited
|
||||
* %%
|
||||
* This file is part of the Alfresco software.
|
||||
* If the software was purchased under a paid Alfresco license, the terms of
|
||||
* the paid license agreement will prevail. Otherwise, the software is
|
||||
* provided under the following open source license terms:
|
||||
*
|
||||
* Alfresco is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Alfresco is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||
* #L%
|
||||
*/
|
||||
package org.alfresco.repo.domain.schema.script;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.io.File;
|
||||
import java.sql.Connection;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.alfresco.repo.domain.dialect.Dialect;
|
||||
import org.alfresco.util.ApplicationContextHelper;
|
||||
import org.alfresco.util.testing.category.DBTests;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import org.junit.experimental.categories.Category;
|
||||
import org.mockito.Mockito;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
|
||||
/**
|
||||
* Integration tests for the {@link DeleteNotExistsV3Executor} class.
|
||||
*
|
||||
* @author Eva Vasques
|
||||
*/
|
||||
@Category({DBTests.class})
|
||||
public class DeleteNotExistsV3ExecutorTest
|
||||
{
|
||||
private static ApplicationContext ctx;
|
||||
private ScriptExecutor scriptExecutor;
|
||||
private DataSource dataSource;
|
||||
private Dialect dialect;
|
||||
private JdbcTemplate jdbcTmpl;
|
||||
|
||||
@BeforeClass
|
||||
public static void setUpBeforeClass()
|
||||
{
|
||||
String[] config = new String[] { "classpath:alfresco/application-context.xml", "classpath:scriptexec/script-exec-test.xml" };
|
||||
ctx = ApplicationContextHelper.getApplicationContext(config);
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception
|
||||
{
|
||||
scriptExecutor = ctx.getBean("simpleScriptExecutor", ScriptExecutorImpl.class);
|
||||
dataSource = ctx.getBean("dataSource", DataSource.class);
|
||||
dialect = ctx.getBean("dialect", Dialect.class);
|
||||
jdbcTmpl = new JdbcTemplate(dataSource);
|
||||
}
|
||||
|
||||
private DeleteNotExistsV3Executor createDeleteNotExistsV3Executor(Dialect dialect, Connection connection, String sql, int line, File scriptFile, Properties properties)
|
||||
{
|
||||
return new DeleteNotExistsV3Executor(dialect, connection, sql, line, scriptFile, properties, dataSource);
|
||||
}
|
||||
|
||||
@Test()
|
||||
public void testDefaultBehaviour() throws Exception
|
||||
{
|
||||
scriptExecutor.executeScriptUrl("scriptexec/${db.script.dialect}/delete-not-exists/test-data1.sql");
|
||||
|
||||
String sql = "--DELETE_NOT_EXISTS_V3 temp_tst_tbl_1.id,temp_tst_tbl_2.tbl_2_id,temp_tst_tbl_3.tbl_3_id,temp_tst_tbl_4.tbl_4_id system.delete_not_exists.batchsize";
|
||||
int line = 1;
|
||||
File scriptFile = Mockito.mock(File.class);
|
||||
Properties properties = Mockito.mock(Properties.class);
|
||||
|
||||
String select = "select id from temp_tst_tbl_1 order by id ASC";
|
||||
|
||||
try (Connection connection = dataSource.getConnection())
|
||||
{
|
||||
connection.setAutoCommit(true);
|
||||
|
||||
// Test read only
|
||||
{
|
||||
when(properties.getProperty(DeleteNotExistsV3Executor.PROPERTY_READ_ONLY)).thenReturn("true");
|
||||
when(properties.getProperty(DeleteNotExistsV3Executor.PROPERTY_TIMEOUT_SECONDS)).thenReturn("-1");
|
||||
DeleteNotExistsV3Executor DeleteNotExistsV3Executor = createDeleteNotExistsV3Executor(dialect, connection, sql, line, scriptFile, properties);
|
||||
DeleteNotExistsV3Executor.execute();
|
||||
|
||||
List<String> res = jdbcTmpl.queryForList(select, String.class);
|
||||
assertEquals(7, res.size());
|
||||
}
|
||||
}
|
||||
|
||||
try (Connection connection = dataSource.getConnection())
|
||||
{
|
||||
connection.setAutoCommit(true);
|
||||
|
||||
// Test with delete
|
||||
{
|
||||
when(properties.getProperty(DeleteNotExistsV3Executor.PROPERTY_READ_ONLY)).thenReturn("false");
|
||||
when(properties.getProperty(DeleteNotExistsV3Executor.PROPERTY_TIMEOUT_SECONDS)).thenReturn("-1");
|
||||
DeleteNotExistsV3Executor DeleteNotExistsV3Executor = createDeleteNotExistsV3Executor(dialect, connection, sql, line, scriptFile, properties);
|
||||
DeleteNotExistsV3Executor.execute();
|
||||
|
||||
List<String> res = jdbcTmpl.queryForList(select, String.class);
|
||||
assertEquals(5, res.size());
|
||||
|
||||
assertEquals("1", res.get(0));
|
||||
assertEquals("2", res.get(1));
|
||||
assertEquals("4", res.get(2));
|
||||
assertEquals("10", res.get(3));
|
||||
assertEquals("11", res.get(4));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test()
|
||||
public void testDeleteBatch() throws Exception
|
||||
{
|
||||
scriptExecutor.executeScriptUrl("scriptexec/${db.script.dialect}/delete-not-exists/test-data1.sql");
|
||||
|
||||
String sql = "--DELETE_NOT_EXISTS_V3 temp_tst_tbl_1.id,temp_tst_tbl_2.tbl_2_id,temp_tst_tbl_3.tbl_3_id,temp_tst_tbl_4.tbl_4_id system.delete_not_exists.batchsize";
|
||||
int line = 1;
|
||||
File scriptFile = Mockito.mock(File.class);
|
||||
Properties properties = Mockito.mock(Properties.class);
|
||||
|
||||
String select = "select id from temp_tst_tbl_1 order by id ASC";
|
||||
|
||||
try (Connection connection = dataSource.getConnection())
|
||||
{
|
||||
connection.setAutoCommit(true);
|
||||
{
|
||||
when(properties.getProperty(DeleteNotExistsV3Executor.PROPERTY_DELETE_BATCH_SIZE)).thenReturn("1");
|
||||
when(properties.getProperty(DeleteNotExistsV3Executor.PROPERTY_READ_ONLY)).thenReturn("false");
|
||||
DeleteNotExistsV3Executor DeleteNotExistsV3Executor = createDeleteNotExistsV3Executor(dialect, connection, sql, line, scriptFile, properties);
|
||||
DeleteNotExistsV3Executor.execute();
|
||||
|
||||
List<String> res = jdbcTmpl.queryForList(select, String.class);
|
||||
assertEquals(5, res.size());
|
||||
|
||||
assertEquals("1", res.get(0));
|
||||
assertEquals("2", res.get(1));
|
||||
assertEquals("4", res.get(2));
|
||||
assertEquals("10", res.get(3));
|
||||
assertEquals("11", res.get(4));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test()
|
||||
public void testBatchExecute() throws Exception
|
||||
{
|
||||
scriptExecutor.executeScriptUrl("scriptexec/${db.script.dialect}/delete-not-exists/test-data1.sql");
|
||||
|
||||
String sql = "--DELETE_NOT_EXISTS_V3 temp_tst_tbl_1.id,temp_tst_tbl_2.tbl_2_id,temp_tst_tbl_3.tbl_3_id,temp_tst_tbl_4.tbl_4_id system.delete_not_exists.batchsize";
|
||||
int line = 1;
|
||||
File scriptFile = Mockito.mock(File.class);
|
||||
Properties properties = Mockito.mock(Properties.class);
|
||||
|
||||
String select = "select id from temp_tst_tbl_1 order by id ASC";
|
||||
|
||||
try (Connection connection = dataSource.getConnection())
|
||||
{
|
||||
connection.setAutoCommit(true);
|
||||
{
|
||||
when(properties.getProperty(DeleteNotExistsV3Executor.PROPERTY_BATCH_SIZE)).thenReturn("2");
|
||||
when(properties.getProperty(DeleteNotExistsV3Executor.PROPERTY_READ_ONLY)).thenReturn("false");
|
||||
when(properties.getProperty(DeleteNotExistsV3Executor.PROPERTY_TIMEOUT_SECONDS)).thenReturn("-1");
|
||||
DeleteNotExistsV3Executor DeleteNotExistsV3Executor = createDeleteNotExistsV3Executor(dialect, connection, sql, line, scriptFile, properties);
|
||||
DeleteNotExistsV3Executor.execute();
|
||||
|
||||
List<String> res = jdbcTmpl.queryForList(select, String.class);
|
||||
assertEquals(5, res.size());
|
||||
|
||||
assertEquals("1", res.get(0));
|
||||
assertEquals("2", res.get(1));
|
||||
assertEquals("4", res.get(2));
|
||||
assertEquals("10", res.get(3));
|
||||
assertEquals("11", res.get(4));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -25,39 +25,42 @@
|
||||
*/
|
||||
package org.alfresco.repo.jscript;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import org.alfresco.error.AlfrescoRuntimeException;
|
||||
import org.alfresco.model.ContentModel;
|
||||
import org.alfresco.repo.dictionary.DictionaryComponent;
|
||||
import org.alfresco.repo.dictionary.DictionaryDAO;
|
||||
import org.alfresco.repo.dictionary.M2Model;
|
||||
import org.alfresco.model.ContentModel;
|
||||
import org.alfresco.repo.dictionary.DictionaryComponent;
|
||||
import org.alfresco.repo.dictionary.DictionaryDAO;
|
||||
import org.alfresco.repo.dictionary.M2Model;
|
||||
import org.alfresco.repo.node.BaseNodeServiceTest;
|
||||
import org.alfresco.repo.security.authentication.AuthenticationComponent;
|
||||
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
|
||||
import org.alfresco.service.ServiceRegistry;
|
||||
import org.alfresco.service.cmr.repository.ChildAssociationRef;
|
||||
import org.alfresco.service.cmr.repository.ContentService;
|
||||
import org.alfresco.service.cmr.repository.ContentWriter;
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
import org.alfresco.service.cmr.repository.NodeService;
|
||||
import org.alfresco.service.cmr.repository.ScriptProcessor;
|
||||
import org.alfresco.service.cmr.repository.ScriptService;
|
||||
import org.alfresco.service.cmr.repository.StoreRef;
|
||||
import org.alfresco.service.namespace.QName;
|
||||
import org.alfresco.service.transaction.TransactionService;
|
||||
import org.alfresco.repo.security.permissions.AccessDeniedException;
|
||||
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
|
||||
import org.alfresco.service.ServiceRegistry;
|
||||
import org.alfresco.service.cmr.repository.ChildAssociationRef;
|
||||
import org.alfresco.service.cmr.repository.ContentService;
|
||||
import org.alfresco.service.cmr.repository.ContentWriter;
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
import org.alfresco.service.cmr.repository.NodeService;
|
||||
import org.alfresco.service.cmr.repository.ScriptService;
|
||||
import org.alfresco.service.cmr.repository.StoreRef;
|
||||
import org.alfresco.service.namespace.QName;
|
||||
import org.alfresco.service.transaction.TransactionService;
|
||||
import org.alfresco.test_category.OwnJVMTestsCategory;
|
||||
import org.alfresco.util.ApplicationContextHelper;
|
||||
import org.junit.experimental.categories.Category;
|
||||
import org.mozilla.javascript.Context;
|
||||
import org.mozilla.javascript.Scriptable;
|
||||
import org.mozilla.javascript.ScriptableObject;
|
||||
import org.junit.experimental.categories.Category;
|
||||
import org.mozilla.javascript.Context;
|
||||
import org.mozilla.javascript.Scriptable;
|
||||
import org.mozilla.javascript.ScriptableObject;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
|
||||
/**
|
||||
@@ -431,51 +434,7 @@ public class RhinoScriptTest extends TestCase
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
// MNT-21638
|
||||
public void testSecureScriptString()
|
||||
{
|
||||
boolean executed = executeSecureScriptString(TESTSCRIPT2, false);
|
||||
assertFalse("Script shouldn't have been executed (secure = false)", executed);
|
||||
|
||||
executed = executeSecureScriptString(TESTSCRIPT2, null);
|
||||
assertFalse("Script shouldn't have been executed (secure = null)", executed);
|
||||
|
||||
executed = executeSecureScriptString(TESTSCRIPT2, true);
|
||||
assertTrue("Script should have been executed (secure = true)", executed);
|
||||
}
|
||||
|
||||
private boolean executeSecureScriptString(String script, Boolean secure)
|
||||
{
|
||||
return transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionCallback<Boolean>()
|
||||
{
|
||||
public Boolean execute() throws Exception
|
||||
{
|
||||
StoreRef store = nodeService.createStore(StoreRef.PROTOCOL_WORKSPACE, "rhino_" + System.currentTimeMillis());
|
||||
NodeRef root = nodeService.getRootNode(store);
|
||||
BaseNodeServiceTest.buildNodeGraph(nodeService, root);
|
||||
|
||||
try
|
||||
{
|
||||
Map<String, Object> model = new HashMap<String, Object>();
|
||||
model.put("out", System.out);
|
||||
|
||||
ScriptNode rootNode = new ScriptNode(root, serviceRegistry, null);
|
||||
model.put("root", rootNode);
|
||||
|
||||
// test executing a script directly as string
|
||||
scriptService.executeScriptString("javascript", script, model, secure);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
private static final String TESTSCRIPT_CLASSPATH1 = "org/alfresco/repo/jscript/test_script1.js";
|
||||
private static final String TESTSCRIPT_CLASSPATH2 = "org/alfresco/repo/jscript/test_script2.js";
|
||||
private static final String TESTSCRIPT_CLASSPATH3 = "org/alfresco/repo/jscript/test_script3.js";
|
||||
@@ -494,12 +453,7 @@ public class RhinoScriptTest extends TestCase
|
||||
"logger.log(\"child by name path: \" + childByNameNode.name);\r\n" +
|
||||
"var xpathResults = root.childrenByXPath(\"/*\");\r\n" +
|
||||
"logger.log(\"children of root from xpath: \" + xpathResults.length);\r\n";
|
||||
|
||||
private static final String TESTSCRIPT2 = "var exec = new org.alfresco.util.exec.RuntimeExec();\r\n"
|
||||
+ "exec.setCommand([\"/bin/ls\"]);\r\n"
|
||||
+ "var res = exec.execute();\r\n"
|
||||
+ "java.lang.System.err.println(res.getStdOut());\r\n";
|
||||
|
||||
|
||||
private static final String BASIC_JAVA =
|
||||
"var list = com.google.common.collect.Lists.newArrayList();\n" +
|
||||
"root.nodeRef.getClass().forName(\"java.lang.ProcessBuilder\")";
|
||||
|
@@ -1247,15 +1247,7 @@ public abstract class AbstractWorkflowServiceIntegrationTest extends BaseSpringT
|
||||
WorkflowDefinition definition = deployment.getDefinition();
|
||||
return definition;
|
||||
}
|
||||
|
||||
protected WorkflowDefinition deployDefinition(String resource, String name, boolean fullAccess)
|
||||
{
|
||||
InputStream input = getInputStream(resource);
|
||||
WorkflowDeployment deployment = workflowService.deployDefinition(getEngine(), input, XML, name, fullAccess);
|
||||
WorkflowDefinition definition = deployment.getDefinition();
|
||||
return definition;
|
||||
}
|
||||
|
||||
|
||||
protected abstract QName getAdhocProcessName();
|
||||
|
||||
|
||||
|
@@ -790,45 +790,6 @@ public class ActivitiWorkflowServiceIntegrationTest extends AbstractWorkflowServ
|
||||
assertNull("Workflow should not be deployed", workflowDef);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
@Test
|
||||
public void testMNT21638_1()
|
||||
{
|
||||
WorkflowDefinition definition = deployDefinition("activiti/test-MNT21638-1.bpmn20.xml");
|
||||
|
||||
personManager.setUser(USER1);
|
||||
|
||||
// Start the Workflow
|
||||
try
|
||||
{
|
||||
WorkflowPath path = workflowService.startWorkflow(definition.getId(), null);
|
||||
fail("Workflow should not have been executed");
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
// Do nothing
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
@Test
|
||||
public void testMNT21638_2()
|
||||
{
|
||||
WorkflowDefinition definition = deployDefinition("activiti/test-MNT21638-2.bpmn20.xml", "MNT21638", true);
|
||||
|
||||
personManager.setUser(USER1);
|
||||
|
||||
// Start the Workflow
|
||||
WorkflowPath path = workflowService.startWorkflow(definition.getId(), null);
|
||||
String instanceId = path.getInstance().getId();
|
||||
|
||||
assertNotNull(instanceId);
|
||||
}
|
||||
|
||||
private NodeRef findWorkflowParent()
|
||||
{
|
||||
RepositoryLocation workflowLocation = (RepositoryLocation)
|
||||
|
@@ -1,38 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<definitions
|
||||
xmlns:activiti="http://activiti.org/bpmn" xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL"
|
||||
typeLanguage="http://www.w3.org/2001/XMLSchema" expressionLanguage="http://www.w3.org/1999/XPath"
|
||||
targetNamespace="http://www.activiti.org/test">
|
||||
|
||||
<process id="test-mnt21638-1" name="test-mnt21638-1">
|
||||
|
||||
<startEvent id="start" activiti:formKey="wf:submitAdhocTask"/>
|
||||
<sequenceFlow id="flow1" sourceRef="start" targetRef="someTask2" />
|
||||
|
||||
<userTask id="someTask2" name="Activiti is awesome!" activiti:formKey="wf:adhocTask">
|
||||
<extensionElements>
|
||||
<activiti:taskListener event="create" class="org.alfresco.repo.workflow.activiti.tasklistener.ScriptTaskListener">
|
||||
<activiti:field name="script">
|
||||
<activiti:string>
|
||||
var exec = new org.alfresco.util.exec.RuntimeExec();
|
||||
exec.setCommand(["/bin/ls"]);
|
||||
var res = exec.execute();
|
||||
java.lang.System.err.println(res.getStdOut());
|
||||
</activiti:string>
|
||||
</activiti:field>
|
||||
</activiti:taskListener>
|
||||
</extensionElements>
|
||||
<humanPerformer>
|
||||
<resourceAssignmentExpression>
|
||||
<formalExpression>admin</formalExpression>
|
||||
</resourceAssignmentExpression>
|
||||
</humanPerformer>
|
||||
</userTask>
|
||||
<sequenceFlow id="flow2" sourceRef="someTask2" targetRef="end" />
|
||||
|
||||
<endEvent id="end" />
|
||||
|
||||
</process>
|
||||
|
||||
</definitions>
|
@@ -1,38 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<definitions
|
||||
xmlns:activiti="http://activiti.org/bpmn" xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL"
|
||||
typeLanguage="http://www.w3.org/2001/XMLSchema" expressionLanguage="http://www.w3.org/1999/XPath"
|
||||
targetNamespace="http://www.activiti.org/test">
|
||||
|
||||
<process id="test-mnt21638-2" name="test-mnt21638-2">
|
||||
|
||||
<startEvent id="start" activiti:formKey="wf:submitAdhocTask"/>
|
||||
<sequenceFlow id="flow1" sourceRef="start" targetRef="someTask2" />
|
||||
|
||||
<userTask id="someTask2" name="Activiti is awesome!" activiti:formKey="wf:adhocTask">
|
||||
<extensionElements>
|
||||
<activiti:taskListener event="create" class="org.alfresco.repo.workflow.activiti.tasklistener.ScriptTaskListener">
|
||||
<activiti:field name="script">
|
||||
<activiti:string>
|
||||
var exec = new org.alfresco.util.exec.RuntimeExec();
|
||||
exec.setCommand(["/bin/ls"]);
|
||||
var res = exec.execute();
|
||||
java.lang.System.err.println(res.getStdOut());
|
||||
</activiti:string>
|
||||
</activiti:field>
|
||||
</activiti:taskListener>
|
||||
</extensionElements>
|
||||
<humanPerformer>
|
||||
<resourceAssignmentExpression>
|
||||
<formalExpression>admin</formalExpression>
|
||||
</resourceAssignmentExpression>
|
||||
</humanPerformer>
|
||||
</userTask>
|
||||
<sequenceFlow id="flow2" sourceRef="someTask2" targetRef="end" />
|
||||
|
||||
<endEvent id="end" />
|
||||
|
||||
</process>
|
||||
|
||||
</definitions>
|
Reference in New Issue
Block a user