From 6dedb5e2c3970ee69e10324952208656949bab2f Mon Sep 17 00:00:00 2001 From: Marco Schaeck Date: Tue, 16 Jun 2026 14:29:16 +0200 Subject: [PATCH 1/2] feat: add StartProcessByMessageAtElementCmd to process API Adds a new start command analogous to StartProcessByDefinitionAtElementCmd that starts a process at a specific element via a message name, for processes that have no plain (none) start event and are entered only via message start events. - new StartProcessByMessageAtElementCmd (messageName, elementId, payload, restrictions) with Java-friendly constructor overloads - document the new start variant in docs/process-api.md - add 'Start Process by message at' row to the README adapter feature matrix (C7 embedded/remote and CIB7 in development, C8 unsupported) plus an icon legend Refs #292 --- .gitignore | 1 + README.md | 3 + .../StartProcessByMessageAtElementCmd.kt | 65 +++++++++++++++++++ docs/process-api.md | 15 ++++- 4 files changed, 83 insertions(+), 1 deletion(-) create mode 100644 api/src/main/kotlin/dev/bpmcrafters/processengineapi/process/StartProcessByMessageAtElementCmd.kt diff --git a/.gitignore b/.gitignore index 3123bc40..958d923f 100644 --- a/.gitignore +++ b/.gitignore @@ -44,3 +44,4 @@ build/ ### DEV _tmp/ .repository/ +.claude/logs/ diff --git a/README.md b/README.md index 855142d3..98d30dbe 100644 --- a/README.md +++ b/README.md @@ -52,6 +52,7 @@ If you want to try the API, please refer to one of the adapter implementations m | Start Process by definition | ✅ | ✅ | ✅ | ✅ | | Start Process by definition at | ✅ | ✅ | ✅ | ✅ | | Start Process by message | ✅ | ✅ | ✅ | ✅ | +| Start Process by message at | 🚧 | 🚧 | 🚧 | ❌ | | Correlate message | ✅ | ✅ | ✅ | ✅ | | Send signal | ✅ | ✅ | ✅ | ✅ | | Subscribe to task | ✅ | ✅ | ✅ | ✅ | @@ -62,6 +63,8 @@ If you want to try the API, please refer to one of the adapter implementations m | Complete service task by error | ✅ | ✅ | ✅ | ✅ | | Fail service task | ✅ | ✅ | ✅ | ✅ | +*✅ supported · 🚧 in development · ❌ not supported* + ## Process Engine Worker If you are using the Process Engine API to provide external task workers using Spring Boot, there is a library with improved support for it: diff --git a/api/src/main/kotlin/dev/bpmcrafters/processengineapi/process/StartProcessByMessageAtElementCmd.kt b/api/src/main/kotlin/dev/bpmcrafters/processengineapi/process/StartProcessByMessageAtElementCmd.kt new file mode 100644 index 00000000..80087dba --- /dev/null +++ b/api/src/main/kotlin/dev/bpmcrafters/processengineapi/process/StartProcessByMessageAtElementCmd.kt @@ -0,0 +1,65 @@ +package dev.bpmcrafters.processengineapi.process + +import dev.bpmcrafters.processengineapi.PayloadSupplier + +/** + * Command to start a new process instance by message at a specific element. + * Useful for processes that have no plain (none) start event and are entered + * only via one or multiple message start events. + * @since 1.7 + */ +data class StartProcessByMessageAtElementCmd( + /** + * Name of the message. + */ + val messageName: String, + /** + * ID of the element to start the process at. + */ + val elementId: String, + /** + * Payload supplier to pass to the new process instance. + */ + val payloadSupplier: PayloadSupplier, + /** + * Restrictions applied for this start command. + */ + val restrictions: Map = emptyMap() +) : StartProcessCommand, PayloadSupplier by payloadSupplier { + /** + * Constructs a start command with a message name, element ID, payload, and restrictions. + * @param messageName message name. + * @param elementId element ID to start the process at. + * @param payload payload to use. + * @param restrictions restrictions for the start command. + */ + constructor(messageName: String, elementId: String, payload: Map, restrictions: Map) : this( + messageName = messageName, + elementId = elementId, + payloadSupplier = PayloadSupplier { payload }, + restrictions = restrictions + ) + /** + * Constructs a start command with a message name, element ID, and payload. + * @param messageName message name. + * @param elementId element ID to start the process at. + * @param payload payload to use. + */ + constructor(messageName: String, elementId: String, payload: Map) : this( + messageName = messageName, + elementId = elementId, + payloadSupplier = PayloadSupplier { payload } , + restrictions = mapOf() + ) + + /** + * Constructs a start command with message name, element ID, and no payload. + * @param messageName message name. + * @param elementId element ID to start the process at. + */ + constructor(messageName: String, elementId: String) : this( + messageName = messageName, + elementId = elementId, + payload = mapOf(), + ) +} diff --git a/docs/process-api.md b/docs/process-api.md index fcbc63a7..1212084e 100644 --- a/docs/process-api.md +++ b/docs/process-api.md @@ -7,10 +7,11 @@ It allows to start new process instances. It is intended to be used in outbound adapters of the port/adapter architecture to control the process engine from your application. -There are three ways to start processes: +There are four ways to start processes: * by providing a process definition key * by providing a start message * by providing a process definition key and a specific activity to start at +* by providing a start message and a specific activity to start at In all cases, you might provide a process payload passed to the started process instance. @@ -57,6 +58,18 @@ public class ProcessStarter { ).get(); } + @SneakyThrows + public void startByMessageAtActivity(Order order) { + startProcessApi.startProcess( + new StartProcessByMessageAtElementCmd( + "Msg_OrderReceived", + "Activity_ProcessOrder", + () -> Map.of("order", order), + Map.of() + ) + ).get(); + } + } ``` From 474049190d66413d7c0d202602f99e3ad9fbd7db Mon Sep 17 00:00:00 2001 From: Marco Schaeck Date: Tue, 16 Jun 2026 14:36:53 +0200 Subject: [PATCH 2/2] test: cover StartProcessByMessageAtElementCmd constructors Adds unit tests exercising all constructor overloads, payload delegation and restrictions, to satisfy codecov patch coverage on the new command. Refs #292 --- .../StartProcessByMessageAtElementCmdTest.kt | 59 +++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 api/src/test/kotlin/dev/bpmcrafters/processengineapi/process/StartProcessByMessageAtElementCmdTest.kt diff --git a/api/src/test/kotlin/dev/bpmcrafters/processengineapi/process/StartProcessByMessageAtElementCmdTest.kt b/api/src/test/kotlin/dev/bpmcrafters/processengineapi/process/StartProcessByMessageAtElementCmdTest.kt new file mode 100644 index 00000000..78e3cc96 --- /dev/null +++ b/api/src/test/kotlin/dev/bpmcrafters/processengineapi/process/StartProcessByMessageAtElementCmdTest.kt @@ -0,0 +1,59 @@ +package dev.bpmcrafters.processengineapi.process + +import dev.bpmcrafters.processengineapi.CommonRestrictions +import dev.bpmcrafters.processengineapi.PayloadSupplier +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class StartProcessByMessageAtElementCmdTest { + + @Test + fun `should create command with payload supplier and restrictions`() { + val payload = mapOf("order" to "4711") + val restrictions = mapOf(CommonRestrictions.TENANT_ID to "myTenant") + val cmd = StartProcessByMessageAtElementCmd( + messageName = "Msg_OrderReceived", + elementId = "Activity_ProcessOrder", + payloadSupplier = PayloadSupplier { payload }, + restrictions = restrictions + ) + + assertThat(cmd.messageName).isEqualTo("Msg_OrderReceived") + assertThat(cmd.elementId).isEqualTo("Activity_ProcessOrder") + assertThat(cmd.get()).isEqualTo(payload) + assertThat(cmd.restrictions).isEqualTo(restrictions) + } + + @Test + fun `should create command from payload and restrictions`() { + val payload = mapOf("order" to "4711") + val restrictions = mapOf(CommonRestrictions.TENANT_ID to "myTenant") + val cmd = StartProcessByMessageAtElementCmd("Msg_OrderReceived", "Activity_ProcessOrder", payload, restrictions) + + assertThat(cmd.messageName).isEqualTo("Msg_OrderReceived") + assertThat(cmd.elementId).isEqualTo("Activity_ProcessOrder") + assertThat(cmd.get()).isEqualTo(payload) + assertThat(cmd.restrictions).isEqualTo(restrictions) + } + + @Test + fun `should create command from payload with empty restrictions`() { + val payload = mapOf("order" to "4711") + val cmd = StartProcessByMessageAtElementCmd("Msg_OrderReceived", "Activity_ProcessOrder", payload) + + assertThat(cmd.messageName).isEqualTo("Msg_OrderReceived") + assertThat(cmd.elementId).isEqualTo("Activity_ProcessOrder") + assertThat(cmd.get()).isEqualTo(payload) + assertThat(cmd.restrictions).isEmpty() + } + + @Test + fun `should create command without payload and restrictions`() { + val cmd = StartProcessByMessageAtElementCmd("Msg_OrderReceived", "Activity_ProcessOrder") + + assertThat(cmd.messageName).isEqualTo("Msg_OrderReceived") + assertThat(cmd.elementId).isEqualTo("Activity_ProcessOrder") + assertThat(cmd.get()).isEmpty() + assertThat(cmd.restrictions).isEmpty() + } +}