diff --git a/docs/Camunda.adoc b/docs/Camunda.adoc index c25eece..a47eec3 100644 --- a/docs/Camunda.adoc +++ b/docs/Camunda.adoc @@ -1,4 +1,6 @@ :figure-caption!: +:source-highlighter: highlight.js +:source-language: java :imagesdir: res :toc2: @@ -122,9 +124,18 @@ If you don't do that and use assertions about BPMN elements that are after the s === Testing +* How can we test that a task exited through the boundary event ? + +.Not working +---- +assertThat(processInstance) + .hasPassedElement("BoundaryEvent_InvalidCardExpiryDate") +---- + +==== Java + * How can we avoid redeploying the BPMN diagram before each test ? -[source, java] ---- @ZeebeProcessTest public class ProcessTest { diff --git a/pom.xml b/pom.xml index 0c57812..0439fbc 100644 --- a/pom.xml +++ b/pom.xml @@ -13,6 +13,7 @@ java + spring-boot diff --git a/spring-boot/error-handling/README.md b/spring-boot/error-handling/README.md new file mode 100644 index 0000000..b27611c --- /dev/null +++ b/spring-boot/error-handling/README.md @@ -0,0 +1,3 @@ +# Error Handling + +This is the implementation for [Camunda 8 - Error Handling](https://academy.camunda.com/c8-error-handling). \ No newline at end of file diff --git a/spring-boot/error-handling/docs/Details.adoc b/spring-boot/error-handling/docs/Details.adoc new file mode 100644 index 0000000..693bd23 --- /dev/null +++ b/spring-boot/error-handling/docs/Details.adoc @@ -0,0 +1,107 @@ +:figure-caption!: +:source-highlighter: highlight.js +:source-language: java +:imagesdir: res +:toc2: + += Error Handling + +.Process +image::paymentProcess.png[Process, role="thumb"] + +This example shows: + +* how to configure the client + +[cols="2a,2a"] +|=== +|Java |Spring Boot +| +.`Main.java` +---- +String ZEEBE_ADDRESS = "..."; +String ZEEBE_CLIENT_ID = "..."; +String ZEEBE_CLIENT_SECRET = "..."; +String ZEEBE_AUTHORIZATION_SERVER_URL = "..."; +String ZEEBE_TOKEN_AUDIENCE = "..."; + +var credentialsProvider = new OAuthCredentialsProviderBuilder() + .authorizationServerUrl(ZEEBE_AUTHORIZATION_SERVER_URL) + .audience(ZEEBE_TOKEN_AUDIENCE) + .clientId(ZEEBE_CLIENT_ID) + .clientSecret(ZEEBE_CLIENT_SECRET) + .build(); + +ZeebeClient client = ZeebeClient.newClientBuilder() + .gatewayAddress(ZEEBE_ADDRESS) + .credentialsProvider(credentialsProvider) + .build()) +---- +| +[source, yaml] +.`application.yaml` +---- +zeebe.client: + cloud: + region: ... + clusterId: ... + clientId: ... + clientSecret: ... +---- +|=== + +* how to deploy process + +@SpringBootApplication +@EnableZeebeClient +@Deployment(resources = "classpath*:*.bpmn") + + +* how to _wire_ a service task: + +[cols="1, 2a,2a"] +|=== +||Java |Spring Boot + +| +.`Main.java` +---- +final var credentialsProvider = new OAuthCredentialsProviderBuilder() + .authorizationServerUrl(ZEEBE_AUTHORIZATION_SERVER_URL) + .audience(ZEEBE_TOKEN_AUDIENCE) + .clientId(ZEEBE_CLIENT_ID) + .clientSecret(ZEEBE_CLIENT_SECRET) + .build(); + +final ZeebeClient client = ZeebeClient.newClientBuilder() + .gatewayAddress(ZEEBE_ADDRESS) + .credentialsProvider(credentialsProvider) + .build()) +---- +|=== + +* how to handle a task + +.`CreditCardChargingHandler.java` +---- +@Override +public void handle(JobClient client, ActivatedJob job) throws Exception { <1> + var reference = (String) job.getVariable("orderReference"); <2> + + var confirmationNumber = creditCardService.chargeCreditCard(reference); <3> + + var outputVariables = Map.of("confirmation", confirmationNumber); <4> + + client.newCompleteCommand(job.getKey()) <5> + .variables(outputVariables) <4> + .send() + .join(); +} +---- +<1> Implement the ``JobHandler``'s `handle` method +<2> Get variables from the `ActivatedJob` +<3> Call the service +<4> Add job's output to the ``ActivatedJob``'s variables +<5> Notify the engine that the job completed successfully + +@JobWorker(type = "send-rejection") \ No newline at end of file diff --git a/spring-boot/error-handling/docs/res/ServiceTask-Definition.png b/spring-boot/error-handling/docs/res/ServiceTask-Definition.png new file mode 100644 index 0000000..a279d3e Binary files /dev/null and b/spring-boot/error-handling/docs/res/ServiceTask-Definition.png differ diff --git a/spring-boot/error-handling/docs/res/paymentProcess.png b/spring-boot/error-handling/docs/res/paymentProcess.png new file mode 100644 index 0000000..0fcc2ac Binary files /dev/null and b/spring-boot/error-handling/docs/res/paymentProcess.png differ diff --git a/spring-boot/error-handling/pom.xml b/spring-boot/error-handling/pom.xml new file mode 100644 index 0000000..3100237 --- /dev/null +++ b/spring-boot/error-handling/pom.xml @@ -0,0 +1,26 @@ + + + 4.0.0 + + + com.micasa.tutorial + camunda-spring-boot + 0.0.1-SNAPSHOT + + + camunda-spring-boot-error-handling + 0.0.1-SNAPSHOT + error-handling + jar + + + + io.camunda.spring + spring-boot-starter-camunda-test + ${camunda.version} + test + + + + diff --git a/spring-boot/error-handling/src/main/java/com/micasa/tutorial/PaymentApplication.java b/spring-boot/error-handling/src/main/java/com/micasa/tutorial/PaymentApplication.java new file mode 100644 index 0000000..1f7236f --- /dev/null +++ b/spring-boot/error-handling/src/main/java/com/micasa/tutorial/PaymentApplication.java @@ -0,0 +1,15 @@ +package com.micasa.tutorial; + +import io.camunda.zeebe.spring.client.EnableZeebeClient; +import io.camunda.zeebe.spring.client.annotation.Deployment; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +@EnableZeebeClient +@Deployment(resources = "classpath*:*.bpmn") +public class PaymentApplication { + public static void main(String[] args) { + SpringApplication.run(PaymentApplication.class, args); + } +} \ No newline at end of file diff --git a/spring-boot/error-handling/src/main/java/com/micasa/tutorial/exceptions/CreditCardServiceException.java b/spring-boot/error-handling/src/main/java/com/micasa/tutorial/exceptions/CreditCardServiceException.java new file mode 100644 index 0000000..f436a4c --- /dev/null +++ b/spring-boot/error-handling/src/main/java/com/micasa/tutorial/exceptions/CreditCardServiceException.java @@ -0,0 +1,9 @@ +package com.micasa.tutorial.exceptions; + +public class CreditCardServiceException extends RuntimeException { + + public CreditCardServiceException(String message) { + super(message); + } + +} diff --git a/spring-boot/error-handling/src/main/java/com/micasa/tutorial/exceptions/InvalidCreditCardException.java b/spring-boot/error-handling/src/main/java/com/micasa/tutorial/exceptions/InvalidCreditCardException.java new file mode 100644 index 0000000..f197ea2 --- /dev/null +++ b/spring-boot/error-handling/src/main/java/com/micasa/tutorial/exceptions/InvalidCreditCardException.java @@ -0,0 +1,13 @@ +package com.micasa.tutorial.exceptions; + +public class InvalidCreditCardException extends Exception { + + public InvalidCreditCardException() { + this("Invalid credit card"); + } + + public InvalidCreditCardException(String message) { + super(message); + } + +} \ No newline at end of file diff --git a/spring-boot/error-handling/src/main/java/com/micasa/tutorial/handlers/CreditCardChargingHandler.java b/spring-boot/error-handling/src/main/java/com/micasa/tutorial/handlers/CreditCardChargingHandler.java new file mode 100644 index 0000000..92a5325 --- /dev/null +++ b/spring-boot/error-handling/src/main/java/com/micasa/tutorial/handlers/CreditCardChargingHandler.java @@ -0,0 +1,41 @@ +package com.micasa.tutorial.handlers; + +import com.micasa.tutorial.exceptions.CreditCardServiceException; +import com.micasa.tutorial.exceptions.InvalidCreditCardException; +import com.micasa.tutorial.services.CreditCard; +import com.micasa.tutorial.services.CreditCardService; +import io.camunda.zeebe.client.api.response.ActivatedJob; +import io.camunda.zeebe.spring.client.annotation.JobWorker; +import io.camunda.zeebe.spring.client.exception.ZeebeBpmnError; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.time.YearMonth; +import java.time.format.DateTimeFormatter; +import java.util.Map; + +@Component +public class CreditCardChargingHandler { + + @Autowired + private CreditCardService creditCardService; + + @JobWorker(type = "chargeCreditCard", autoComplete = true) + public Map handle(ActivatedJob job) throws CreditCardServiceException { + System.out.println("charge credit card [ retries = " + job.getRetries() + " ]"); + var reference = (String) job.getVariable("orderReference"); + var amount = (Double) job.getVariable("orderAmount"); + var creditCard = new CreditCard( + (String) job.getVariable("cardNumber"), + YearMonth.parse((String) job.getVariable("cardExpiry"), DateTimeFormatter.ofPattern("MM/yyyy")), + (String) job.getVariable("cardCVC") + ); + + try { + var confirmationNumber = creditCardService.chargeCreditCard(reference, amount, creditCard); + return Map.of("confirmation", confirmationNumber); + } catch (InvalidCreditCardException icce) { + throw new ZeebeBpmnError("invalidCreditCardException", icce.getMessage()); + } + } +} diff --git a/spring-boot/error-handling/src/main/java/com/micasa/tutorial/services/CreditCard.java b/spring-boot/error-handling/src/main/java/com/micasa/tutorial/services/CreditCard.java new file mode 100644 index 0000000..7b22cc3 --- /dev/null +++ b/spring-boot/error-handling/src/main/java/com/micasa/tutorial/services/CreditCard.java @@ -0,0 +1,6 @@ +package com.micasa.tutorial.services; + +import java.time.YearMonth; + +public record CreditCard(String cardNumber, YearMonth expiryDate, String CVC) { +} diff --git a/spring-boot/error-handling/src/main/java/com/micasa/tutorial/services/CreditCardService.java b/spring-boot/error-handling/src/main/java/com/micasa/tutorial/services/CreditCardService.java new file mode 100644 index 0000000..5ab5fc7 --- /dev/null +++ b/spring-boot/error-handling/src/main/java/com/micasa/tutorial/services/CreditCardService.java @@ -0,0 +1,27 @@ +package com.micasa.tutorial.services; + +import com.micasa.tutorial.exceptions.CreditCardServiceException; +import com.micasa.tutorial.exceptions.InvalidCreditCardException; +import org.springframework.stereotype.Component; + +import java.time.YearMonth; +import java.util.UUID; + +@Component +public class CreditCardService { + + public String chargeCreditCard(String transactionNumber, double amount, CreditCard creditCard) throws InvalidCreditCardException, CreditCardServiceException { + System.out.println(STR. "Charging \{ amount } to credit card \{ creditCard } for transaction \{ transactionNumber }" ); + if (creditCard.expiryDate().isBefore(YearMonth.now())) { + System.out.println("The credit card's expiry date is invalid: " + creditCard.expiryDate()); + throw new InvalidCreditCardException(); + } + if (transactionNumber.equalsIgnoreCase("invalid")) { + var message = "The transaction number is invalid: " + transactionNumber; + System.out.println(message); + throw new CreditCardServiceException(message); + } + return UUID.randomUUID().toString(); + } + +} \ No newline at end of file diff --git a/spring-boot/error-handling/src/main/resources/application.yaml b/spring-boot/error-handling/src/main/resources/application.yaml new file mode 100644 index 0000000..045d638 --- /dev/null +++ b/spring-boot/error-handling/src/main/resources/application.yaml @@ -0,0 +1,9 @@ +# https://github.com/camunda-community-hub/spring-zeebe#configuring-camunda-platform-8-saas-connection +# https://github.com/camunda-community-hub/spring-zeebe#additional-configuration-options + +zeebe.client: + cloud: + region: ont-1 + clusterId: 4784612d-1495-4f2a-951d-049c8b985f06 + clientId: jMNcIFp.GJ1-ZYK~Av-8zLHM7xmYCTLs + clientSecret: dbz--V.YX9JzRzF_44iL9DNz.h0_1S-qpPbl~2xYt6f0ua5h3CWQ2wVulQ68b-Rm \ No newline at end of file diff --git a/spring-boot/error-handling/src/main/resources/checkError.form b/spring-boot/error-handling/src/main/resources/checkError.form new file mode 100644 index 0000000..253f6b3 --- /dev/null +++ b/spring-boot/error-handling/src/main/resources/checkError.form @@ -0,0 +1,76 @@ +{ + "executionPlatform": "Camunda Cloud", + "executionPlatformVersion": "8.2.0", + "exporter": { + "name": "Camunda Web Modeler", + "version": "8516401" + }, + "schemaVersion": 10, + "components": [ + { + "text": "## Check Credit Card Details", + "type": "text", + "id": "Field_1y7r1ul", + "layout": { + "row": "Row_1yzjcyo" + } + }, + { + "label": "Reference", + "type": "textfield", + "layout": { + "row": "Row_1ammndy", + "columns": null + }, + "id": "Field_035kqla", + "key": "reference" + }, + { + "label": "Amount", + "type": "textfield", + "id": "Field_0p8a9xa", + "key": "amount", + "layout": { + "row": "Row_0h6eq07" + } + }, + { + "label": "Card Number", + "type": "textfield", + "id": "Field_1j2py1a", + "key": "cardNumber", + "layout": { + "row": "Row_15opzdy" + } + }, + { + "label": "Card Expiry", + "type": "textfield", + "id": "Field_1l2tmgg", + "key": "cardExpiry", + "layout": { + "row": "Row_0a2rkgg" + } + }, + { + "label": "Card CVC", + "type": "textfield", + "id": "Field_0pydzhj", + "key": "cardCVC", + "layout": { + "row": "Row_1nh9icr" + } + }, + { + "label": "Valid Credit Card?", + "type": "checkbox", + "id": "Field_08wi408", + "key": "isValidCreditCard", + "layout": { + "row": "Row_0fw9363" + } + } + ], + "type": "default", + "id": "checkError" +} \ No newline at end of file diff --git a/spring-boot/error-handling/src/main/resources/paymentProcess.bpmn b/spring-boot/error-handling/src/main/resources/paymentProcess.bpmn new file mode 100644 index 0000000..22b4b81 --- /dev/null +++ b/spring-boot/error-handling/src/main/resources/paymentProcess.bpmn @@ -0,0 +1,200 @@ + + + + + { + "executionPlatform": "Camunda Cloud", + "executionPlatformVersion": "8.2.0", + "exporter": { + "name": "Camunda Web Modeler", + "version": "8516401" + }, + "schemaVersion": 10, + "components": [ + { + "text": "## Check Credit Card Details", + "type": "text", + "id": "Field_1y7r1ul", + "layout": { + "row": "Row_1yzjcyo" + } + }, + { + "label": "Reference", + "type": "textfield", + "layout": { + "row": "Row_1c38750", + "columns": null + }, + "id": "Field_18do177", + "key": "reference", + "readonly": false + }, + { + "label": "Amount", + "type": "textfield", + "id": "Field_0p8a9xa", + "key": "amount", + "layout": { + "row": "Row_0h6eq07" + } + }, + { + "label": "Card Number", + "type": "textfield", + "id": "Field_1j2py1a", + "key": "cardNumber", + "layout": { + "row": "Row_15opzdy" + } + }, + { + "label": "Card Expiry", + "type": "textfield", + "id": "Field_1l2tmgg", + "key": "cardExpiry", + "layout": { + "row": "Row_0a2rkgg" + } + }, + { + "label": "Card CVC", + "type": "textfield", + "id": "Field_0pydzhj", + "key": "cardCVC", + "layout": { + "row": "Row_1nh9icr" + } + }, + { + "label": "Valid Credit Card?", + "type": "checkbox", + "id": "Field_08wi408", + "key": "isValidCreditCard", + "layout": { + "row": "Row_0fw9363" + } + } + ], + "type": "default", + "id": "checkError" +} + + + Flow_paymentRequired-chargeCreditCard + + + + + + + Flow_paymentRequired-chargeCreditCard + Flow_Gateway_Resolved-Task_ChargeCreditCard + Flow_chargeCreditCard-paymentSuccessful + + + Flow_chargeCreditCard-paymentSuccessful + + + + Flow_00zdqb3 + + + + + + + Flow_00zdqb3 + Flow_09afd84 + + + Flow_09afd84 + Flow_GatewayResolved-EndEvent_PaymentCancelled + Flow_Gateway_Resolved-Task_ChargeCreditCard + + + Flow_GatewayResolved-EndEvent_PaymentCancelled + + + + + =isValidCreditCard = false + + + =isValidCreditCard = true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/spring-boot/error-handling/src/test/java/com/micasa/tutorial/PaymentProcessTest.java b/spring-boot/error-handling/src/test/java/com/micasa/tutorial/PaymentProcessTest.java new file mode 100644 index 0000000..5c11f88 --- /dev/null +++ b/spring-boot/error-handling/src/test/java/com/micasa/tutorial/PaymentProcessTest.java @@ -0,0 +1,176 @@ +package com.micasa.tutorial; + +import io.camunda.zeebe.client.ZeebeClient; +import io.camunda.zeebe.client.api.response.ProcessInstanceEvent; +import io.camunda.zeebe.process.test.api.ZeebeTestEngine; +import io.camunda.zeebe.spring.test.ZeebeSpringTest; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; + +import java.time.Duration; +import java.util.HashMap; +import java.util.Map; + +import static com.micasa.tutorial.Utils.*; +import static io.camunda.zeebe.process.test.assertions.BpmnAssert.assertThat; +import static io.camunda.zeebe.spring.test.ZeebeTestThreadSupport.waitForProcessInstanceCompleted; + +@SpringBootTest +@ZeebeSpringTest +@DisplayName("Payment Process Test") +public class PaymentProcessTest { + + @Autowired + private ZeebeTestEngine engine; + + @Autowired + private ZeebeClient client; + + @Test + @DisplayName("Successful payment") + public void success() throws Exception { + var variables = Map.of( + "orderAmount", 60.0, + "orderReference", "Order-1", + "cardExpiry", "01/2026", + "cardNumber", "1234567812345678", + "cardCVC", "111" + ); + + ProcessInstanceEvent processInstance = startProcess(client, "PaymentProcess", variables); + + // Wait for the engine to progress through the flow +// engine.waitForIdleState(Duration.ofSeconds(1)); + waitForProcessInstanceCompleted(processInstance); + + assertThat(processInstance) + .hasPassedElement("Task_ChargeCreditCard") + .hasPassedElement("EndEvent_PaymentCompleted") + .isCompleted(); + } + + @Test + @DisplayName("Unexpected handler failure -> incident") + public void failure() throws Exception { + var variables = Map.of( + "orderAmount", 60.0, + "cardExpiry", "01/2026", + "cardNumber", "1234567812345678", + "cardCVC", "111" + ); + + // Start the process after the Deduct Credit task + ProcessInstanceEvent processInstance = startProcess(client, "PaymentProcess", variables); + + // Wait for the engine to progress through the flow + engine.waitForIdleState(Duration.ofSeconds(10)); + + assertThat(processInstance) + .hasNotPassedElement("Task_ChargeCreditCard") + .hasNotPassedElement("EndEvent_PaymentCompleted") + .hasAnyIncidents(); + } + + @Test + @DisplayName("Service failure -> incident") + public void incident() throws Exception { + var variables = Map.of( + "orderAmount", 60.0, + "orderReference", "invalid", + "cardExpiry", "01/2026", + "cardNumber", "1234567812345678", + "cardCVC", "111" + ); + + // Start the process after the Deduct Credit task + ProcessInstanceEvent processInstance = startProcess(client, "PaymentProcess", variables); + + // Wait for the engine to progress through the flow + engine.waitForIdleState(Duration.ofSeconds(10)); + + assertThat(processInstance) + .hasNotPassedElement("Task_ChargeCreditCard") + .hasNotPassedElement("EndEvent_PaymentCompleted") + .hasAnyIncidents(); + } + + @Test + @DisplayName("Invalid expiry date -> BPMN Error with fix") + public void errorWithFix() throws Exception { + Map variables = Map.of( + "orderAmount", 60.0, + "orderReference", "Order-1", + "cardExpiry", "01/2023", + "cardNumber", "1234567812345678", + "cardCVC", "111" + ); + + // Start the process after the Deduct Credit task + ProcessInstanceEvent processInstance = startProcess(client, "PaymentProcess", variables); + + // Wait for the engine to progress through the flow + engine.waitForIdleState(Duration.ofSeconds(1)); + + assertThat(processInstance) + .hasNotPassedElement("Task_ChargeCreditCard") + .hasNoIncidents() + .isActive(); + + var newVariables = new HashMap<>(variables); + newVariables.replace("cardExpiry", "01/2033"); + newVariables.put("isValidCreditCard", true); + + completeUserTask(client, newVariables); + + waitForProcessInstanceCompleted(processInstance); + + assertThat(processInstance) + .hasPassedElement("Task_ChargeCreditCard") + .hasPassedElement("EndEvent_PaymentCompleted") + .hasNoIncidents() + .isCompleted(); + + } + + @Test + @DisplayName("Invalid expiry date -> BPMN Error without fix") + public void errorWithoutFix() throws Exception { + Map variables = Map.of( + "orderAmount", 60.0, + "orderReference", "Order-1", + "cardExpiry", "01/2023", + "cardNumber", "1234567812345678", + "cardCVC", "111" + ); + + // Start the process after the Deduct Credit task + ProcessInstanceEvent processInstance = startProcess(client, "PaymentProcess", variables); + + // Wait for the engine to progress through the flow + engine.waitForIdleState(Duration.ofSeconds(1)); + + assertThat(processInstance) + .hasNotPassedElement("Task_ChargeCreditCard") + .hasNoIncidents() + .isActive(); + + var newVariables = new HashMap<>(variables); + newVariables.put("isValidCreditCard", false); + + completeUserTask(client, newVariables); + + waitForProcessInstanceCompleted(processInstance); + + assertThat(processInstance) + .hasNotPassedElement("Task_ChargeCreditCard") + .hasNotPassedElement("EndEvent_PaymentCompleted") + .hasPassedElement("EndEvent_PaymentCancelled") + .hasNoIncidents() + .isCompleted(); + + } + +} diff --git a/spring-boot/error-handling/src/test/java/com/micasa/tutorial/Utils.java b/spring-boot/error-handling/src/test/java/com/micasa/tutorial/Utils.java new file mode 100644 index 0000000..c5c5146 --- /dev/null +++ b/spring-boot/error-handling/src/test/java/com/micasa/tutorial/Utils.java @@ -0,0 +1,68 @@ +package com.micasa.tutorial; + +import io.camunda.zeebe.client.ZeebeClient; +import io.camunda.zeebe.client.api.response.ActivateJobsResponse; +import io.camunda.zeebe.client.api.response.ActivatedJob; +import io.camunda.zeebe.client.api.response.ProcessInstanceEvent; +import io.camunda.zeebe.client.api.worker.JobHandler; +import io.camunda.zeebe.process.test.assertions.BpmnAssert; + +import java.util.Map; + +public class Utils { + + private static final String USER_TASK = "io.camunda.zeebe:userTask"; + + public static ProcessInstanceEvent startProcess(ZeebeClient client, String processId, Map variables) { + ProcessInstanceEvent processInstance = client.newCreateInstanceCommand() + .bpmnProcessId(processId) + .latestVersion() + .variables(variables) + .send() + .join(); + + BpmnAssert.assertThat(processInstance).isStarted(); + + return processInstance; + } + + public static ProcessInstanceEvent startProcessBefore(ZeebeClient client, String processId, String startingPointId, Map variables) { + ProcessInstanceEvent processInstance = client.newCreateInstanceCommand() + .bpmnProcessId(processId) + .latestVersion() + .variables(variables) + .startBeforeElement(startingPointId) + .send() + .join(); + + BpmnAssert.assertThat(processInstance).isStarted(); + + return processInstance; + } + + public static void completeServiceTask(ZeebeClient client, String jobType, JobHandler handler) throws Exception { + ActivateJobsResponse activateJobsResponse = client.newActivateJobsCommand() + .jobType(jobType) + .maxJobsToActivate(1) + .send() + .join(); + + ActivatedJob firstJob = activateJobsResponse.getJobs().get(0); + handler.handle(client, firstJob); + } + + public static void completeUserTask(ZeebeClient client, Map variables) throws Exception { + ActivateJobsResponse activateJobsResponse = client.newActivateJobsCommand() + .jobType(USER_TASK) + .maxJobsToActivate(1) + .send() + .join(); + + ActivatedJob firstJob = activateJobsResponse.getJobs().get(0); + client.newCompleteCommand(firstJob) + .variables(variables) + .send() + .join(); + } + +} diff --git a/spring-boot/pom.xml b/spring-boot/pom.xml index c6ac551..5a52e6e 100644 --- a/spring-boot/pom.xml +++ b/spring-boot/pom.xml @@ -14,6 +14,10 @@ spring-boot pom + + error-handling + + io.camunda.spring