Skip to content

Commit

Permalink
Add "resume" bulk action
Browse files Browse the repository at this point in the history
Also created a sample bulk action to resume tasks carrying
failed executions of recently approved requests.
  • Loading branch information
mederly committed Apr 13, 2018
1 parent aab70d6 commit 2610fc4
Show file tree
Hide file tree
Showing 6 changed files with 278 additions and 0 deletions.
Expand Up @@ -18,6 +18,7 @@

import com.evolveum.midpoint.model.api.ModelExecuteOptions;
import com.evolveum.midpoint.model.api.ModelService;
import com.evolveum.midpoint.model.api.TaskService;
import com.evolveum.midpoint.model.impl.scripting.ActionExecutor;
import com.evolveum.midpoint.model.impl.scripting.PipelineData;
import com.evolveum.midpoint.model.impl.scripting.ExecutionContext;
Expand Down Expand Up @@ -70,6 +71,7 @@ public abstract class BaseActionExecutor implements ActionExecutor {
@Autowired protected ModelService modelService;
@Autowired protected SecurityEnforcer securityEnforcer;
@Autowired protected SecurityContextManager securityContextManager;
@Autowired protected TaskService taskService;

// todo move to some helper?

Expand Down
@@ -0,0 +1,85 @@
/*
* Copyright (c) 2010-2018 Evolveum
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.evolveum.midpoint.model.impl.scripting.actions;

import com.evolveum.midpoint.model.api.PipelineItem;
import com.evolveum.midpoint.model.api.ScriptExecutionException;
import com.evolveum.midpoint.model.impl.scripting.ExecutionContext;
import com.evolveum.midpoint.model.impl.scripting.PipelineData;
import com.evolveum.midpoint.prism.PrismObject;
import com.evolveum.midpoint.prism.PrismObjectValue;
import com.evolveum.midpoint.prism.PrismValue;
import com.evolveum.midpoint.schema.result.OperationResult;
import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.TaskType;
import com.evolveum.midpoint.xml.ns._public.model.scripting_3.ActionExpressionType;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;

import static java.util.Collections.singleton;

/**
* @author mederly
*/
@Component
public class ResumeExecutor extends BaseActionExecutor {

//private static final Trace LOGGER = TraceManager.getTrace(ResumeExecutor.class);

private static final String NAME = "resume";

@PostConstruct
public void init() {
scriptingExpressionEvaluator.registerActionExecutor(NAME, this);
}

@Override
public PipelineData execute(ActionExpressionType expression, PipelineData input, ExecutionContext context, OperationResult globalResult) throws ScriptExecutionException {

for (PipelineItem item: input.getData()) {
PrismValue value = item.getValue();
OperationResult result = operationsHelper.createActionResult(item, this, context, globalResult);
context.checkTaskStop();
if (value instanceof PrismObjectValue) {
@SuppressWarnings({"unchecked", "raw"})
PrismObject<? extends ObjectType> prismObject = ((PrismObjectValue) value).asPrismObject();
ObjectType object = prismObject.asObjectable();
if (object instanceof TaskType) {
long started = operationsHelper.recordStart(context, object);
Throwable exception = null;
try {
taskService.resumeTasks(singleton(object.getOid()), context.getTask(), result);
operationsHelper.recordEnd(context, object, started, null);
} catch (Throwable ex) {
operationsHelper.recordEnd(context, object, started, ex);
exception = processActionException(ex, NAME, value, context);
}
context.println((exception != null ? "Attempted to resume " : "Resumed ") + prismObject.toString() + exceptionSuffix(exception));
} else {
//noinspection ThrowableNotThrown
processActionException(new ScriptExecutionException("Item is not a task"), NAME, value, context);
}
} else {
//noinspection ThrowableNotThrown
processActionException(new ScriptExecutionException("Item is not a PrismObject"), NAME, value, context);
}
operationsHelper.trimAndCloneResult(result, globalResult, context);
}
return input;
}
}
Expand Up @@ -56,6 +56,8 @@
import java.util.*;
import java.util.stream.Collectors;

import static com.evolveum.midpoint.prism.xml.XmlTypeConverter.createDuration;
import static com.evolveum.midpoint.prism.xml.XmlTypeConverter.fromNow;
import static java.util.Collections.emptyMap;
import static java.util.Collections.singleton;
import static org.apache.commons.collections4.CollectionUtils.emptyIfNull;
Expand Down Expand Up @@ -104,6 +106,9 @@ public class TestScriptingBasic extends AbstractInitializedModelIntegrationTest
private static final File GENERATE_PASSWORDS_3_FILE = new File(TEST_DIR, "generate-passwords-3.xml");
private static final File ECHO_FILE = new File(TEST_DIR, "echo.xml");
private static final File USE_VARIABLES_FILE = new File(TEST_DIR, "use-variables.xml");
private static final File TASK_TO_RESUME_FILE = new File(TEST_DIR, "task-to-resume.xml");
private static final File TASK_TO_KEEP_SUSPENDED_FILE = new File(TEST_DIR, "task-to-keep-suspended.xml");
private static final File RESUME_SUSPENDED_TASKS_FILE = new File(TEST_DIR, "resume-suspended-tasks.xml");
private static final QName USER_NAME_TASK_EXTENSION_PROPERTY = new QName("http://midpoint.evolveum.com/xml/ns/samples/piracy", "userName");
private static final QName USER_DESCRIPTION_TASK_EXTENSION_PROPERTY = new QName("http://midpoint.evolveum.com/xml/ns/samples/piracy", "userDescription");
private static final QName STUDY_GROUP_TASK_EXTENSION_PROPERTY = new QName("http://midpoint.evolveum.com/xml/ns/samples/piracy", "studyGroup");
Expand Down Expand Up @@ -1076,6 +1081,38 @@ public void test560StartTaskFromTemplate() throws Exception {
taskManager.suspendTasks(singleton(TASK_TRIGGER_SCANNER_OID), 10000L, result);
}

@Test
public void test570ResumeTask() throws Exception {
final String TEST_NAME = "test570ResumeTask";
TestUtil.displayTestTitle(this, TEST_NAME);

// GIVEN
Task task = createTask(DOT_CLASS + TEST_NAME);
task.setOwner(getUser(USER_ADMINISTRATOR_OID));
OperationResult result = task.getResult();

addObject(TASK_TO_KEEP_SUSPENDED_FILE);

PrismObject<TaskType> taskToResume = prismContext.parseObject(TASK_TO_RESUME_FILE);
taskToResume.asObjectable().getWorkflowContext().setEndTimestamp(fromNow(createDuration(-1000L)));
addObject(taskToResume);
display("task to resume", taskToResume);

ExecuteScriptType exec = prismContext.parserFor(RESUME_SUSPENDED_TASKS_FILE).parseRealValue();

// WHEN
ExecutionContext output = scriptingExpressionEvaluator.evaluateExpression(exec, emptyMap(), task, result);

// THEN
dumpOutput(output, result);
result.computeStatus();
// the task should be there
assertEquals("Unexpected # of items in output", 1, output.getFinalOutput().getData().size());

PrismObject<TaskType> taskAfter = getObject(TaskType.class, taskToResume.getOid());
assertTrue("Task is still suspended", taskAfter.asObjectable().getExecutionStatus() != TaskExecutionStatusType.SUSPENDED);
}

private void assertNoOutputData(ExecutionContext output) {
assertTrue("Script returned unexpected data", output.getFinalOutput() == null || output.getFinalOutput().getData().isEmpty());
}
Expand Down
@@ -0,0 +1,60 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<!--
~ Copyright (c) 2010-2018 Evolveum
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<s:executeScript
xmlns:s="http://midpoint.evolveum.com/xml/ns/public/model/scripting-3"
xmlns:c="http://midpoint.evolveum.com/xml/ns/public/common/common-3"
xmlns:q="http://prism.evolveum.com/xml/ns/public/query-3">
<s:search>
<s:type>TaskType</s:type>
<s:query>
<q:filter>
<q:and>
<q:equal>
<q:path>category</q:path>
<q:value>Workflow</q:value>
</q:equal>
<q:equal>
<q:path>handlerUri</q:path>
<!-- use http://midpoint.evolveum.com/xml/ns/public/model/operation/handler-3 for real wf tasks -->
<q:value>http://midpoint.evolveum.com/xml/ns/public/task/noop/handler-3</q:value>
</q:equal>
<q:equal>
<q:path>executionStatus</q:path>
<q:value>suspended</q:value>
</q:equal>
<!-- Beware: this condition is applicable only when the model operation carrying task is the same
as the task wrapping the approvals. This is the case only when "execute after all approvals" is turned
off, i.e. if all changes are executed immediately after being approved. If "execute after all approvals"
is turned on then the filtering has to be done in a more elaborate way. -->
<q:greaterOrEqual>
<q:path>workflowContext/endTimestamp</q:path>
<c:expression>
<c:script>
<c:code>
basic.fromNow("-P14D")
</c:code>
</c:script>
</c:expression>
</q:greaterOrEqual>
</q:and>
</q:filter>
</s:query>
<s:action>
<s:type>resume</s:type>
</s:action>
</s:search>
</s:executeScript>
@@ -0,0 +1,47 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright (c) 2010-2018 Evolveum
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->

<task xmlns="http://midpoint.evolveum.com/xml/ns/public/common/common-3"
xmlns:c="http://midpoint.evolveum.com/xml/ns/public/common/common-3"
xmlns:org="http://midpoint.evolveum.com/xml/ns/public/common/org-3"
oid="e6ad137e-23f6-444d-9478-c10a0b255b68"
version="1">
<name>Approval and execution of: Assigning role "abc" to user "def" (old)</name>
<taskIdentifier>2222222222222-0-1</taskIdentifier>
<ownerRef oid="00000000-0000-0000-0000-000000000002"
relation="org:default"
type="c:UserType"><!-- administrator --></ownerRef>
<channel>http://midpoint.evolveum.com/xml/ns/public/gui/channels-3#user</channel>
<executionStatus>suspended</executionStatus>
<category>Workflow</category>
<handlerUri>http://midpoint.evolveum.com/xml/ns/public/task/noop/handler-3</handlerUri>
<otherHandlersUriStack/>
<resultStatus>partial_error</resultStatus>
<lastRunStartTimestamp>2018-01-02T12:24:33.635+02:00</lastRunStartTimestamp>
<lastRunFinishTimestamp>2018-01-02T12:25:46.587+02:00</lastRunFinishTimestamp>
<recurrence>single</recurrence>
<binding>tight</binding>
<modelOperationContext>
<state>primary</state>
</modelOperationContext>
<workflowContext>
<processInstanceId>12345678</processInstanceId>
<processName>ItemApproval</processName>
<startTimestamp>2018-01-01T10:34:30.087+02:00</startTimestamp>
<endTimestamp>2018-01-12T12:24:23.192+02:00</endTimestamp>
</workflowContext>
</task>
47 changes: 47 additions & 0 deletions model/model-intest/src/test/resources/scripting/task-to-resume.xml
@@ -0,0 +1,47 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright (c) 2010-2018 Evolveum
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->

<task xmlns="http://midpoint.evolveum.com/xml/ns/public/common/common-3"
xmlns:c="http://midpoint.evolveum.com/xml/ns/public/common/common-3"
xmlns:org="http://midpoint.evolveum.com/xml/ns/public/common/org-3"
oid="c77c458d-0719-4bef-aa8d-dbf0460cd77c"
version="35">
<name>Approval and execution of: Assigning role "abc" to user "def"</name>
<taskIdentifier>1522830887681-0-1</taskIdentifier>
<ownerRef oid="00000000-0000-0000-0000-000000000002"
relation="org:default"
type="c:UserType"><!-- administrator --></ownerRef>
<channel>http://midpoint.evolveum.com/xml/ns/public/gui/channels-3#user</channel>
<executionStatus>suspended</executionStatus>
<category>Workflow</category>
<handlerUri>http://midpoint.evolveum.com/xml/ns/public/task/noop/handler-3</handlerUri>
<otherHandlersUriStack/>
<resultStatus>partial_error</resultStatus>
<lastRunStartTimestamp>2018-04-11T12:24:33.635+02:00</lastRunStartTimestamp>
<lastRunFinishTimestamp>2018-04-11T12:25:46.587+02:00</lastRunFinishTimestamp>
<recurrence>single</recurrence>
<binding>tight</binding>
<modelOperationContext>
<state>primary</state>
</modelOperationContext>
<workflowContext>
<processInstanceId>5246642</processInstanceId>
<processName>ItemApproval</processName>
<startTimestamp>2018-04-04T10:34:30.087+02:00</startTimestamp>
<endTimestamp>2018-04-11T12:24:23.192+02:00</endTimestamp>
</workflowContext>
</task>

0 comments on commit 2610fc4

Please sign in to comment.