diff --git a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/delta/PrismValueDeltaSetTripleImpl.java b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/delta/PrismValueDeltaSetTripleImpl.java index 780d944a31f..c025cbcd79a 100644 --- a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/delta/PrismValueDeltaSetTripleImpl.java +++ b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/delta/PrismValueDeltaSetTripleImpl.java @@ -206,7 +206,11 @@ public void checkConsistence() { Processor processor = pval -> { if (pval.getParent() != null) { - throw new IllegalStateException("Value "+pval+" in triple "+PrismValueDeltaSetTripleImpl.this+" has parent, looks like it was not cloned properly"); + if (pval instanceof PrismObjectValue) { + // Object values are exceptions from this rule. They could have a parent. TODO reconsider this. + } else { + throw new IllegalStateException("Value " + pval + " in triple " + PrismValueDeltaSetTripleImpl.this + " has parent, looks like it was not cloned properly"); + } } }; foreach(processor); diff --git a/infra/schema/src/main/resources/xml/ns/public/common/common-policy-3.xsd b/infra/schema/src/main/resources/xml/ns/public/common/common-policy-3.xsd index 0a088cd38ec..ff438c66964 100644 --- a/infra/schema/src/main/resources/xml/ns/public/common/common-policy-3.xsd +++ b/infra/schema/src/main/resources/xml/ns/public/common/common-policy-3.xsd @@ -2872,8 +2872,10 @@ Task customizer: an expression that takes a task and customizes its content. - Input variable: task (of TaskType). - Output: TaskType that should be used. + + Input variable: preparedTask (of TaskType). + Output: TaskType that should be used. (It is possible to modify preparedTask and return it.) + This is the final step in task preparation. So the task is executed in the form that is prepared by this expression. diff --git a/model/model-common/src/main/java/com/evolveum/midpoint/model/common/expression/evaluator/AbstractValueTransformationExpressionEvaluator.java b/model/model-common/src/main/java/com/evolveum/midpoint/model/common/expression/evaluator/AbstractValueTransformationExpressionEvaluator.java index 26a0836f10e..90e41d4ee25 100644 --- a/model/model-common/src/main/java/com/evolveum/midpoint/model/common/expression/evaluator/AbstractValueTransformationExpressionEvaluator.java +++ b/model/model-common/src/main/java/com/evolveum/midpoint/model/common/expression/evaluator/AbstractValueTransformationExpressionEvaluator.java @@ -394,7 +394,7 @@ private PrismValueDeltaSetTriple evaluateRelativeExpression(final List sourceTriple = sourceTriplesIterator.next(); String name = sourceTriple.getName().getLocalPart(); ItemDefinition definition = sourceTriple.getSource().getDefinition(); - if (definition == null) { + if (definition == null) { // TODO reconsider @NotNull annotation on getDefinition LOGGER.error("Source '{}' without a definition; came from a source triple: {}", name, sourceTriple); throw new IllegalArgumentException("Source '"+name+"' without a definition"); } @@ -420,10 +420,8 @@ private PrismValueDeltaSetTriple evaluateRelativeExpression(final List(actx.context, null, actx.task, result)); + try { + PrismObjectDefinition taskDefinition = preparedTask.asPrismObject().getDefinition(); + + ExpressionVariables variables = createVariables(); + variables.addVariableDefinition(VAR_PREPARED_TASK, preparedTask, taskDefinition); + ExpressionProfile expressionProfile = MiscSchemaUtil.getExpressionProfile(); + PrismValue customizedTaskValue = ExpressionUtil.evaluateExpression(variables, taskDefinition, + customizer, expressionProfile, beans.expressionFactory, "task customizer", + actx.task, result); + if (customizedTaskValue == null) { + throw new IllegalStateException("Task customizer returned no value"); + } + if (!(customizedTaskValue instanceof PrismObjectValue)) { + throw new IllegalStateException("Task customizer returned a value that is not a PrismObjectValue: " + customizedTaskValue); + } + Objectable customizedTaskBean = ((PrismObjectValue) customizedTaskValue).asObjectable(); + if (!(customizedTaskBean instanceof TaskType)) { + throw new IllegalStateException("Task customizer returned a value that is not a TaskType: " + customizedTaskBean); + } + return (TaskType) customizedTaskBean; + } finally { + ModelExpressionThreadLocalHolder.popExpressionEnvironment(); + } + } + } + /** * Inserts script into task. */ diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/policy/scriptExecutor/SingleRunTaskCreator.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/policy/scriptExecutor/SingleRunTaskCreator.java index 2b4f927e27c..f063e958523 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/policy/scriptExecutor/SingleRunTaskCreator.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/policy/scriptExecutor/SingleRunTaskCreator.java @@ -34,15 +34,19 @@ class SingleRunTaskCreator extends AbstractSingleRunTaskCreator { TaskType createTask(ExecuteScriptType executeScript, OperationResult result) throws CommunicationException, ObjectNotFoundException, SchemaException, SecurityViolationException, ConfigurationException, ExpressionEvaluationException { + ExecuteScriptType executeScriptWithInput = addInputToScript(executeScript, result); + return createTaskForSingleRunScript(executeScriptWithInput, result); + } + + private ExecuteScriptType addInputToScript(ExecuteScriptType executeScript, OperationResult result) + throws CommunicationException, ObjectNotFoundException, SchemaException, SecurityViolationException, + ConfigurationException, ExpressionEvaluationException { if (executeScript.getInput() != null) { throw new UnsupportedOperationException("Explicit input with SINGLE_RUN task execution is not supported."); } - ReferenceBasedObjectSet objectSet = new ReferenceBasedObjectSet(actx, result); objectSet.collect(); ValueListType input = createInput(objectSet.asReferenceValues()); - ExecuteScriptType executeScriptWithInput = implantInput(executeScript, input); - - return createTaskForSingleRunScript(executeScriptWithInput, result); + return implantInput(executeScript, input); } } diff --git a/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/AbstractEmptyModelIntegrationTest.java b/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/AbstractEmptyModelIntegrationTest.java index c4cd27b79dc..5b5049c6084 100644 --- a/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/AbstractEmptyModelIntegrationTest.java +++ b/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/AbstractEmptyModelIntegrationTest.java @@ -14,10 +14,14 @@ import com.evolveum.midpoint.schema.internals.InternalsConfig; import com.evolveum.midpoint.schema.result.OperationResult; import com.evolveum.midpoint.task.api.Task; +import com.evolveum.midpoint.test.TestResource; import com.evolveum.midpoint.util.annotation.Experimental; import com.evolveum.midpoint.util.exception.CommonException; import com.evolveum.midpoint.util.exception.ObjectAlreadyExistsException; +import com.evolveum.midpoint.util.exception.SchemaException; +import com.evolveum.midpoint.xml.ns._public.common.common_3.ArchetypeType; import com.evolveum.midpoint.xml.ns._public.common.common_3.SystemConfigurationType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.SystemObjectsType; import com.evolveum.midpoint.xml.ns._public.common.common_3.UserType; import java.io.File; @@ -31,6 +35,8 @@ * - empty system configuration * - administrator user (from common dir) * - superuser role (from common dir) + * + * There are some standard archetypes defined, but not imported. Individual tests should import them if necessary. */ @Experimental public abstract class AbstractEmptyModelIntegrationTest extends AbstractModelIntegrationTest { @@ -43,10 +49,13 @@ public abstract class AbstractEmptyModelIntegrationTest extends AbstractModelInt static final File ROLE_SUPERUSER_FILE = new File(COMMON_DIR, "role-superuser.xml"); protected static final String ROLE_SUPERUSER_OID = "00000000-0000-0000-0000-000000000004"; + private static final TestResource ARCHETYPE_TASK_ITERATIVE_BULK_ACTION = new TestResource<>(COMMON_DIR, "archetype-task-iterative-bulk-action.xml", SystemObjectsType.ARCHETYPE_ITERATIVE_BULK_ACTION_TASK.value()); + private static final TestResource ARCHETYPE_TASK_SINGLE_BULK_ACTION = new TestResource<>(COMMON_DIR, "archetype-task-single-bulk-action.xml", SystemObjectsType.ARCHETYPE_SINGLE_BULK_ACTION_TASK.value()); + protected PrismObject userAdministrator; @Override - public void initSystem(Task initTask, OperationResult initResult) throws Exception { + public void initSystem(Task initTask, OperationResult initResult) throws Exception { logger.trace("initSystem"); // We want logging config from logback-test.xml and not from system config object (unless suppressed) @@ -88,4 +97,12 @@ protected PrismObject addSystemConfigurationObject(Oper throws IOException, CommonException, EncryptionException { return null; } + + // To be used when needed + @SuppressWarnings("WeakerAccess") + protected void importTaskArchetypes(OperationResult initResult) throws SchemaException, + ObjectAlreadyExistsException, EncryptionException, IOException { + repoAdd(ARCHETYPE_TASK_ITERATIVE_BULK_ACTION, initResult); + repoAdd(ARCHETYPE_TASK_SINGLE_BULK_ACTION, initResult); + } } diff --git a/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/TestMemberRecompute.java b/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/TestMemberRecompute.java index 59435e0d056..58c37fa0ebb 100644 --- a/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/TestMemberRecompute.java +++ b/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/TestMemberRecompute.java @@ -64,6 +64,8 @@ public class TestMemberRecompute extends AbstractEmptyModelIntegrationTest { public void initSystem(Task initTask, OperationResult initResult) throws Exception { super.initSystem(initTask, initResult); + importTaskArchetypes(initResult); + addObject(TASK_TEMPLATE_RECOMPUTE_MEMBERS, initTask, initResult); addObject(TEMPLATE_USER, initTask, initResult); @@ -127,6 +129,7 @@ public void test100ChangeCostCenter() throws Exception { Task recomputeTask = waitForTaskFinish(taskOid, false); assertTask(recomputeTask, "recompute task after") .display() + .assertArchetypeRef(SystemObjectsType.ARCHETYPE_ITERATIVE_BULK_ACTION_TASK.value()) .assertSuccess(); assertUserAfterByUsername("user-dcs-0000") diff --git a/model/model-intest/src/test/resources/common/archetype-task-iterative-bulk-action.xml b/model/model-intest/src/test/resources/common/archetype-task-iterative-bulk-action.xml new file mode 100644 index 00000000000..9afa79ee776 --- /dev/null +++ b/model/model-intest/src/test/resources/common/archetype-task-iterative-bulk-action.xml @@ -0,0 +1,115 @@ + + + Iterative bulk action task + + + + Iterative bulk action tasks + + fa fa-bullseye + green + + + + extension + vacant + + + extension/mext:objectType + visible + + + extension/mext:objectQuery + visible + + + extension/mext:searchOptions + visible + + + extension/mext:useRepositoryDirectly + visible + + + extension/scext:executeScript + visible + + + extension/mext:workerThreads + visible + + + + c:TaskType + + + + + + extension/mext:objectType + + + extension/mext:objectQuery + + + extension/mext:searchOptions + + + extension/mext:useRepositoryDirectly + + + + + + + + extension/scext:executeScript + + + extension/mext:workerThreads + + + + + + + + TaskType + + + + + + weak + + http://midpoint.evolveum.com/xml/ns/public/model/iterative-scripting/handler-3 + + + handlerUri + + + + + + + + weak + + BulkActions + + + category + + + + + diff --git a/model/model-intest/src/test/resources/common/archetype-task-single-bulk-action.xml b/model/model-intest/src/test/resources/common/archetype-task-single-bulk-action.xml new file mode 100644 index 00000000000..2fd7d731cd5 --- /dev/null +++ b/model/model-intest/src/test/resources/common/archetype-task-single-bulk-action.xml @@ -0,0 +1,74 @@ + + + Single bulk action task + + + + Single bulk action tasks + + fa fa-bullseye + green + + + + extension + vacant + + + extension/scext:executeScript + visible + + + + TaskType + + + + + + extension/scext:executeScript + + + + + + + + TaskType + + + + + + weak + + http://midpoint.evolveum.com/xml/ns/public/model/scripting/handler-3 + + + handlerUri + + + + + + + + weak + + BulkActions + + + category + + + + + diff --git a/model/model-intest/src/test/resources/member-recompute/archetype-department.xml b/model/model-intest/src/test/resources/member-recompute/archetype-department.xml index ea59c5fb431..fd6029905b6 100644 --- a/model/model-intest/src/test/resources/member-recompute/archetype-department.xml +++ b/model/model-intest/src/test/resources/member-recompute/archetype-department.xml @@ -52,9 +52,9 @@ diff --git a/repo/repo-common/src/main/java/com/evolveum/midpoint/repo/common/expression/ExpressionUtil.java b/repo/repo-common/src/main/java/com/evolveum/midpoint/repo/common/expression/ExpressionUtil.java index 09838dba579..5d3a1091af1 100644 --- a/repo/repo-common/src/main/java/com/evolveum/midpoint/repo/common/expression/ExpressionUtil.java +++ b/repo/repo-common/src/main/java/com/evolveum/midpoint/repo/common/expression/ExpressionUtil.java @@ -758,6 +758,7 @@ public static V evaluateExpress shortDesc, task, parentResult); ExpressionEvaluationContext context = new ExpressionEvaluationContext(sources, variables, shortDesc, task); + context.setSkipEvaluationMinus(true); // no need to evaluate old state; we are interested in non-negative output values anyway PrismValueDeltaSetTriple outputTriple = expression.evaluate(context, parentResult); LOGGER.trace("Result of the expression evaluation: {}", outputTriple); diff --git a/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/asserter/TaskAsserter.java b/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/asserter/TaskAsserter.java index f77804cec00..66792f0c04c 100644 --- a/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/asserter/TaskAsserter.java +++ b/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/asserter/TaskAsserter.java @@ -127,6 +127,11 @@ public TaskAsserter display(String message) { return this; } + @Override + public TaskAsserter assertArchetypeRef(String expectedArchetypeOid) { + return (TaskAsserter) super.assertArchetypeRef(expectedArchetypeOid); + } + @Override public TaskAsserter assertNoItem(ItemPath itemPath) { super.assertNoItem(itemPath);