Skip to content

Commit

Permalink
Add task customizer to scripting rules
Browse files Browse the repository at this point in the history
1) Task customizer is now working.
2) Added also task archetypes for iterative/single-run scripting.
3) Skipping minus evaluation for another method in ExpressionUtil.
4) Removed "has no parent" consistency check for triples for
PrismObjectValues.

A part of MID-5967 implementation.
  • Loading branch information
mederly committed May 29, 2020
1 parent 4d6e29c commit 649a597
Show file tree
Hide file tree
Showing 14 changed files with 295 additions and 40 deletions.
Expand Up @@ -206,7 +206,11 @@ public void checkConsistence() {

Processor<V> 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);
Expand Down
Expand Up @@ -2872,8 +2872,10 @@
<xsd:annotation>
<xsd:documentation>
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.

Expand Down
Expand Up @@ -394,7 +394,7 @@ private PrismValueDeltaSetTriple<V> evaluateRelativeExpression(final List<Source
SourceTriple<PrismValue,?> 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");
}
Expand All @@ -420,10 +420,8 @@ private PrismValueDeltaSetTriple<V> evaluateRelativeExpression(final List<Source
}
}

if (LOGGER.isTraceEnabled()) {
LOGGER.trace("Processing value combination {} in {}\n hasPlus={}, hasZero={}, hasMinus={}, skipEvaluationPlus={}, skipEvaluationMinus={}",
dumpValueCombination(pvalues, sourceTriples), contextDescription, hasPlus, hasZero, hasMinus, skipEvaluationPlus, skipEvaluationMinus);
}
LOGGER.trace("Processing value combination {} in {}\n hasPlus={}, hasZero={}, hasMinus={}, skipEvaluationPlus={}, skipEvaluationMinus={}",
dumpValueCombination(pvalues, sourceTriples), contextDescription, hasPlus, hasZero, hasMinus, skipEvaluationPlus, skipEvaluationMinus);

if (!hasPlus && !hasMinus && !hasZero && !MiscUtil.isAllNull(pvalues)) {
throw new IllegalStateException("Internal error! The impossible has happened! pvalues="+pvalues+"; source triples: "+sourceTriples+"; in "+contextDescription);
Expand Down
Expand Up @@ -7,9 +7,11 @@

package com.evolveum.midpoint.model.impl.lens.projector.policy.scriptExecutor;

import com.evolveum.midpoint.xml.ns._public.common.common_3.ArchetypeType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.SystemObjectsType;

import org.jetbrains.annotations.NotNull;

import com.evolveum.midpoint.model.api.ModelPublicConstants;
import com.evolveum.midpoint.schema.result.OperationResult;
import com.evolveum.midpoint.util.exception.*;
import com.evolveum.midpoint.xml.ns._public.common.common_3.TaskType;
Expand All @@ -30,20 +32,15 @@ TaskType createTaskForSingleRunScript(ExecuteScriptType executeScript, Operation
SecurityViolationException, ExpressionEvaluationException {
TaskType newTask = createArchetypedTask(result);
setScriptInTask(newTask, executeScript);
return newTask;
return customizeTask(newTask, result);
}

@NotNull
private TaskType createArchetypedTask(OperationResult result) throws ObjectNotFoundException, SchemaException, CommunicationException,
ConfigurationException, SecurityViolationException, ExpressionEvaluationException {

TaskType newTask = createEmptyTask(result);
newTask.setHandlerUri(ModelPublicConstants.SCRIPT_EXECUTION_TASK_HANDLER_URI);

// TODO task customizer
// TODO task archetype

return newTask;
return super.createEmptyTask(result)
.beginAssignment()
.targetRef(SystemObjectsType.ARCHETYPE_SINGLE_BULK_ACTION_TASK.value(), ArchetypeType.COMPLEX_TYPE)
.end();
}

}
Expand Up @@ -9,9 +9,11 @@

import javax.xml.namespace.QName;

import com.evolveum.midpoint.xml.ns._public.common.common_3.ArchetypeType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.SystemObjectsType;

import org.jetbrains.annotations.NotNull;

import com.evolveum.midpoint.model.api.ModelPublicConstants;
import com.evolveum.midpoint.prism.PrismProperty;
import com.evolveum.midpoint.prism.PrismPropertyDefinition;
import com.evolveum.midpoint.prism.schema.SchemaRegistry;
Expand Down Expand Up @@ -51,20 +53,16 @@ public TaskType createTask(ExecuteScriptType executeScript, OperationResult resu
setQueryInTask(newTask, completeQuery);
setScriptInTask(newTask, executeScript);

return newTask;
return customizeTask(newTask, result);
}

@NotNull
private TaskType createArchetypedTask(OperationResult result) throws ObjectNotFoundException, SchemaException, CommunicationException,
ConfigurationException, SecurityViolationException, ExpressionEvaluationException {

TaskType newTask = super.createEmptyTask(result);
newTask.setHandlerUri(ModelPublicConstants.ITERATIVE_SCRIPT_EXECUTION_TASK_HANDLER_URI);

// TODO task customizer
// TODO task archetype

return newTask;
return super.createEmptyTask(result)
.beginAssignment()
.targetRef(SystemObjectsType.ARCHETYPE_ITERATIVE_BULK_ACTION_TASK.value(), ArchetypeType.COMPLEX_TYPE)
.end();
}

@NotNull
Expand Down
Expand Up @@ -16,7 +16,7 @@
import com.evolveum.midpoint.model.common.expression.ExpressionEnvironment;
import com.evolveum.midpoint.model.common.expression.ModelExpressionThreadLocalHolder;
import com.evolveum.midpoint.model.impl.util.ModelImplUtils;
import com.evolveum.midpoint.prism.PrismObject;
import com.evolveum.midpoint.prism.*;
import com.evolveum.midpoint.repo.common.expression.ExpressionUtil;
import com.evolveum.midpoint.repo.common.expression.ExpressionVariables;
import com.evolveum.midpoint.schema.expression.ExpressionProfile;
Expand All @@ -25,8 +25,6 @@

import org.jetbrains.annotations.NotNull;

import com.evolveum.midpoint.prism.PrismProperty;
import com.evolveum.midpoint.prism.PrismPropertyDefinition;
import com.evolveum.midpoint.schema.constants.SchemaConstants;
import com.evolveum.midpoint.schema.result.OperationResult;
import com.evolveum.midpoint.security.api.MidPointPrincipal;
Expand All @@ -43,10 +41,14 @@ abstract class ScriptingTaskCreator {

@NotNull final ActionContext actx;
@NotNull final PolicyRuleScriptExecutor beans;
@NotNull private final AsynchronousScriptExecutionType asynchronousScriptExecution;

private static final String VAR_PREPARED_TASK = "preparedTask";

ScriptingTaskCreator(@NotNull ActionContext actx) {
this.actx = actx;
this.beans = actx.beans;
this.asynchronousScriptExecution = actx.action.getAsynchronousExecution();
}

/**
Expand Down Expand Up @@ -114,6 +116,41 @@ private ExpressionVariables createVariables() throws SchemaException {
return variables;
}

TaskType customizeTask(TaskType preparedTask, OperationResult result) throws SchemaException,
ObjectNotFoundException, SecurityViolationException, CommunicationException,
ConfigurationException, ExpressionEvaluationException {
ExpressionType customizer = asynchronousScriptExecution.getTaskCustomizer();
if (customizer == null) {
return preparedTask;
} else {
ModelExpressionThreadLocalHolder.pushExpressionEnvironment(
new ExpressionEnvironment<>(actx.context, null, actx.task, result));
try {
PrismObjectDefinition<TaskType> 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.
*/
Expand Down
Expand Up @@ -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);
}
}
Expand Up @@ -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;
Expand All @@ -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 {
Expand All @@ -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<ArchetypeType> 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<ArchetypeType> ARCHETYPE_TASK_SINGLE_BULK_ACTION = new TestResource<>(COMMON_DIR, "archetype-task-single-bulk-action.xml", SystemObjectsType.ARCHETYPE_SINGLE_BULK_ACTION_TASK.value());

protected PrismObject<UserType> 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)
Expand Down Expand Up @@ -88,4 +97,12 @@ protected PrismObject<SystemConfigurationType> 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);
}
}
Expand Up @@ -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);
Expand Down Expand Up @@ -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")
Expand Down

0 comments on commit 649a597

Please sign in to comment.