Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

new example: save assignee via el #554

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
147 changes: 147 additions & 0 deletions save-assignee-via-el/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
<?xml version="1.0" encoding="UTF-8"?>
<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>

<groupId>com.camunda.consulting</groupId>
<artifactId>save-assignee-via-el</artifactId>
<version>1.0-SNAPSHOT</version>

<properties>
<maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>21</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>

<dependencies>
<dependency>
<groupId>io.camunda</groupId>
<artifactId>spring-boot-starter-camunda-sdk</artifactId>
<version>8.6.5</version>
</dependency>
<dependency>
<groupId>io.camunda</groupId>
<artifactId>spring-boot-starter-camunda-tasklist</artifactId>
<version>8.6.6</version>
</dependency>
<dependency>
<groupId>io.camunda</groupId>
<artifactId>camunda-process-test-spring</artifactId>
<version>8.6.5</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>3.3.5</version>
</dependency>
</dependencies>
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>com.diffplug.spotless</groupId>
<artifactId>spotless-maven-plugin</artifactId>
<version>2.43.0</version>
<configuration>
<formats>
<format>
<includes>
<include>*.md</include>
<include>.gitignore</include>
</includes>
<trimTrailingWhitespace/>
<endWithNewline/>
<indent>
<spaces>true</spaces>
<spacesPerTab>2</spacesPerTab>
</indent>
</format>
</formats>
<java>
<googleJavaFormat/>
</java>
<pom/>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.5.2</version>
</plugin>
</plugins>
</pluginManagement>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>3.3.5</version>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<compilerArgs>
<arg>-parameters</arg>
</compilerArgs>
</configuration>
</plugin>
</plugins>
</build>
<profiles>
<!-- profile to auto format -->
<profile>
<id>autoFormat</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<build>
<plugins>
<plugin>
<groupId>com.diffplug.spotless</groupId>
<artifactId>spotless-maven-plugin</artifactId>
<executions>
<execution>
<id>spotless-format</id>
<goals>
<goal>apply</goal>
</goals>
<phase>process-sources</phase>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>

<!-- profile to perform strict validation checks -->
<profile>
<id>checkFormat</id>
<build>
<plugins>
<plugin>
<groupId>com.diffplug.spotless</groupId>
<artifactId>spotless-maven-plugin</artifactId>
<executions>
<execution>
<id>spotless-check</id>
<goals>
<goal>check</goal>
</goals>
<phase>validate</phase>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>
</project>
19 changes: 19 additions & 0 deletions save-assignee-via-el/src/main/java/com/camunda/consulting/App.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.camunda.consulting;

import io.camunda.zeebe.spring.client.annotation.JobWorker;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class App {
private static final Logger LOG = LoggerFactory.getLogger(App.class);

public static void main(String[] args) {
SpringApplication.run(App.class, args);
}

@JobWorker
public void thisShouldNotAutoComplete() {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.camunda.consulting;

import io.camunda.tasklist.CamundaTaskListClient;
import io.camunda.tasklist.dto.TaskList;
import io.camunda.tasklist.dto.TaskSearch;
import io.camunda.tasklist.exception.TaskListException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class AppController {
private final CamundaTaskListClient taskListClient;

@Autowired
public AppController(CamundaTaskListClient taskListClient) {
this.taskListClient = taskListClient;
}

@GetMapping("/tasks")
public TaskList getTasks() throws TaskListException {
return taskListClient.getTasks(new TaskSearch());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package com.camunda.consulting;

import io.camunda.tasklist.CamundaTaskListClient;
import io.camunda.tasklist.dto.Task;
import io.camunda.tasklist.dto.TaskList;
import io.camunda.tasklist.dto.TaskSearch;
import io.camunda.tasklist.dto.TaskState;
import io.camunda.tasklist.exception.TaskListException;
import io.camunda.tasklist.generated.model.TaskByVariables;
import io.camunda.tasklist.generated.model.TaskByVariables.OperatorEnum;
import io.camunda.zeebe.client.api.response.ActivatedJob;
import io.camunda.zeebe.client.api.worker.JobClient;
import io.camunda.zeebe.spring.client.annotation.JobWorker;
import java.time.Duration;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class UserTaskExecutionListener {
private static final Logger LOG = LoggerFactory.getLogger(UserTaskExecutionListener.class);
private final CamundaTaskListClient taskListClient;

@Autowired
public UserTaskExecutionListener(CamundaTaskListClient taskListClient) {
this.taskListClient = taskListClient;
}

@JobWorker(autoComplete = false)
public void saveAssignee(JobClient jobClient, ActivatedJob job) throws TaskListException {
String uniqueTaskKey = (String) job.getVariablesAsMap().get("uniqueTaskKey");
long processInstanceKey = job.getProcessInstanceKey();
String elementId = job.getElementId();
long elementInstanceKey = job.getElementInstanceKey();
LOG.info("Handling assignee for user task {}", elementInstanceKey);
TaskList tasks =
taskListClient.getTasks(
new TaskSearch()
.setProcessInstanceKey(String.valueOf(processInstanceKey))
.setTaskDefinitionId(elementId)
.setTaskVariables(
List.of(
new TaskByVariables()
.name("uniqueTaskKey")
// the escaping is a workaround until this is fixed
.value('"' + uniqueTaskKey + '"')
.operator(OperatorEnum.EQ)))
.setState(TaskState.COMPLETED));
Task matchingTask = extractMatchingTask(tasks);
if (matchingTask != null) {
LOG.info("Found one matching user task, extracting assignee");
jobClient
.newCompleteCommand(job.getKey())
.variables(new AssignmentInformation(matchingTask.getAssignee()))
.send()
.join();
LOG.info("Set assignee to process instance");
} else {
LOG.warn("Fond no matching user task, retrying in 5 seconds");
jobClient
.newFailCommand(job.getKey())
.retries(job.getRetries() - 1)
.retryBackoff(Duration.ofSeconds(5))
.send()
.join();
}
}

private Task extractMatchingTask(TaskList tasks) {
if (tasks.getItems().isEmpty()) {
LOG.info("No tasks present");
return null;
}
if (tasks.getItems().size() > 1) {
// this will only happen if the same task in the same process instance gets the same randomly
// generated uniqueTaskKey
throw new IllegalStateException("Found more than one matching task");
}
return tasks.get(0);
}

public record AssignmentInformation(String assignee) {}
}
6 changes: 6 additions & 0 deletions save-assignee-via-el/src/main/resources/application.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
tasklist:
client:
profile: simple
camunda:
client:
mode: selfmanaged
88 changes: 88 additions & 0 deletions save-assignee-via-el/src/main/resources/userTask.bpmn
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
<?xml version="1.0" encoding="UTF-8"?>
<bpmn:definitions xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:zeebe="http://camunda.org/schema/zeebe/1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" xmlns:modeler="http://camunda.org/schema/modeler/1.0" id="Definitions_1viscpy" targetNamespace="http://bpmn.io/schema/bpmn" exporter="Camunda Modeler" exporterVersion="5.29.0" modeler:executionPlatform="Camunda Cloud" modeler:executionPlatformVersion="8.6.0">
<bpmn:process id="Process_0iaonm6" isExecutable="true">
<bpmn:startEvent id="StartEvent_1">
<bpmn:extensionElements>
<zeebe:ioMapping>
<zeebe:output source="=true" target="repeat" />
</zeebe:ioMapping>
</bpmn:extensionElements>
<bpmn:outgoing>Flow_0shseob</bpmn:outgoing>
</bpmn:startEvent>
<bpmn:sequenceFlow id="Flow_0shseob" sourceRef="StartEvent_1" targetRef="Activity_0nhuyco" />
<bpmn:userTask id="Activity_0nhuyco" name="Some user task">
<bpmn:extensionElements>
<zeebe:userTask />
<zeebe:executionListeners>
<zeebe:executionListener eventType="end" type="saveAssignee" />
</zeebe:executionListeners>
<zeebe:assignmentDefinition assignee="demo" />
<zeebe:ioMapping>
<zeebe:input source="=uuid()" target="uniqueTaskKey" />
</zeebe:ioMapping>
</bpmn:extensionElements>
<bpmn:incoming>Flow_0shseob</bpmn:incoming>
<bpmn:incoming>Flow_1qhqsv6</bpmn:incoming>
<bpmn:outgoing>Flow_0mwbgix</bpmn:outgoing>
</bpmn:userTask>
<bpmn:endEvent id="Event_05y89ke">
<bpmn:incoming>Flow_1nh36fo</bpmn:incoming>
</bpmn:endEvent>
<bpmn:sequenceFlow id="Flow_0mwbgix" sourceRef="Activity_0nhuyco" targetRef="Gateway_0pgx2ze" />
<bpmn:exclusiveGateway id="Gateway_0pgx2ze" name="Repeat?">
<bpmn:incoming>Flow_0mwbgix</bpmn:incoming>
<bpmn:outgoing>Flow_1nh36fo</bpmn:outgoing>
<bpmn:outgoing>Flow_1qhqsv6</bpmn:outgoing>
</bpmn:exclusiveGateway>
<bpmn:sequenceFlow id="Flow_1nh36fo" name="no" sourceRef="Gateway_0pgx2ze" targetRef="Event_05y89ke">
<bpmn:conditionExpression xsi:type="bpmn:tFormalExpression">=not(repeat)</bpmn:conditionExpression>
</bpmn:sequenceFlow>
<bpmn:sequenceFlow id="Flow_1qhqsv6" name="yes" sourceRef="Gateway_0pgx2ze" targetRef="Activity_0nhuyco">
<bpmn:conditionExpression xsi:type="bpmn:tFormalExpression">=repeat</bpmn:conditionExpression>
</bpmn:sequenceFlow>
</bpmn:process>
<bpmndi:BPMNDiagram id="BPMNDiagram_1">
<bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="Process_0iaonm6">
<bpmndi:BPMNShape id="StartEvent_1_di" bpmnElement="StartEvent_1">
<dc:Bounds x="182" y="102" width="36" height="36" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Activity_17703bv_di" bpmnElement="Activity_0nhuyco">
<dc:Bounds x="270" y="80" width="100" height="80" />
<bpmndi:BPMNLabel />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Event_05y89ke_di" bpmnElement="Event_05y89ke">
<dc:Bounds x="582" y="102" width="36" height="36" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Gateway_0pgx2ze_di" bpmnElement="Gateway_0pgx2ze" isMarkerVisible="true">
<dc:Bounds x="435" y="95" width="50" height="50" />
<bpmndi:BPMNLabel>
<dc:Bounds x="439" y="65" width="42" height="14" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge id="Flow_0shseob_di" bpmnElement="Flow_0shseob">
<di:waypoint x="218" y="120" />
<di:waypoint x="270" y="120" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_0mwbgix_di" bpmnElement="Flow_0mwbgix">
<di:waypoint x="370" y="120" />
<di:waypoint x="435" y="120" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_1nh36fo_di" bpmnElement="Flow_1nh36fo">
<di:waypoint x="485" y="120" />
<di:waypoint x="582" y="120" />
<bpmndi:BPMNLabel>
<dc:Bounds x="527" y="102" width="13" height="14" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_1qhqsv6_di" bpmnElement="Flow_1qhqsv6">
<di:waypoint x="460" y="145" />
<di:waypoint x="460" y="210" />
<di:waypoint x="320" y="210" />
<di:waypoint x="320" y="160" />
<bpmndi:BPMNLabel>
<dc:Bounds x="381" y="192" width="18" height="14" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</bpmn:definitions>