Skip to content

Commit

Permalink
Add error handling to the REST connector sample
Browse files Browse the repository at this point in the history
  • Loading branch information
aowss-bp committed Jan 8, 2024
1 parent 54d4822 commit 261e3b1
Show file tree
Hide file tree
Showing 3 changed files with 103 additions and 5 deletions.
3 changes: 3 additions & 0 deletions docs/Camunda.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,9 @@ The GraphQL API will be deprecated soon as mentioned in https://academy.camunda.

* Every path must be tested and in particular every FEEL expression

Use the https://github.com/camunda-community-hub/camunda-process-test-coverage[Camunda Process Test Coverage] dependency to assess the coverage. If possible use the Sonar Plugin as documented https://camunda-community-hub.github.io/camunda-process-test-coverage/snapshot/getting-started.html#sonarqube-plugin[here].
The report is located in the `process-test-coverage` folder.

* For system tests, mock external APIs using https://wiremock.org[WireMock] instead of mocking the services using https://github.com/mockito/mockito[Mockito]

* For system tests, start the process from the start and avoid using the `startBeforeElement` API
Expand Down
43 changes: 39 additions & 4 deletions spring-boot/rest-connector/src/main/resources/restConnector.bpmn
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<?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:di="http://www.omg.org/spec/DD/20100524/DI" xmlns:modeler="http://camunda.org/schema/modeler/1.0" id="Definitions_11w5z4p" targetNamespace="http://bpmn.io/schema/bpmn" exporter="Camunda Modeler" exporterVersion="5.18.0" modeler:executionPlatform="Camunda Cloud" modeler:executionPlatformVersion="8.3.0">
<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:di="http://www.omg.org/spec/DD/20100524/DI" xmlns:bioc="http://bpmn.io/schema/bpmn/biocolor/1.0" xmlns:color="http://www.omg.org/spec/BPMN/non-normative/color/1.0" xmlns:modeler="http://camunda.org/schema/modeler/1.0" id="Definitions_11w5z4p" targetNamespace="http://bpmn.io/schema/bpmn" exporter="Camunda Modeler" exporterVersion="5.18.0" modeler:executionPlatform="Camunda Cloud" modeler:executionPlatformVersion="8.3.0">
<bpmn:process id="Process_RESTConnector" name="REST Connector" isExecutable="true">
<bpmn:startEvent id="Start_ExchangeRateRequest" name="Exchange Rate Request">
<bpmn:extensionElements>
Expand All @@ -16,7 +16,8 @@
<bpmn:sequenceFlow id="Flow_1necipk" sourceRef="Start_ExchangeRateRequest" targetRef="Task_CallExchangeRateAPI" />
<bpmn:serviceTask id="Task_CallExchangeRateAPI" name="Call Exchange Rate API" zeebe:modelerTemplate="io.camunda.connectors.HttpJson.v2" zeebe:modelerTemplateVersion="5" zeebe:modelerTemplateIcon="data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTgiIGhlaWdodD0iMTgiIHZpZXdCb3g9IjAgMCAxOCAxOCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTE3LjAzMzUgOC45OTk5N0MxNy4wMzM1IDEzLjQ0NzUgMTMuNDI4MSAxNy4wNTI5IDguOTgwNjUgMTcuMDUyOUM0LjUzMzE2IDE3LjA1MjkgMC45Mjc3NjUgMTMuNDQ3NSAwLjkyNzc2NSA4Ljk5OTk3QzAuOTI3NzY1IDQuNTUyNDggNC41MzMxNiAwLjk0NzA4MyA4Ljk4MDY1IDAuOTQ3MDgzQzEzLjQyODEgMC45NDcwODMgMTcuMDMzNSA0LjU1MjQ4IDE3LjAzMzUgOC45OTk5N1oiIGZpbGw9IiM1MDU1NjIiLz4KPHBhdGggZD0iTTQuOTMxMjYgMTQuMTU3MUw2Ljc4MTA2IDMuNzE0NzFIMTAuMTM3NUMxMS4xOTE3IDMuNzE0NzEgMTEuOTgyNCAzLjk4MzIzIDEyLjUwOTUgNC41MjAyN0MxMy4wNDY1IDUuMDQ3MzYgMTMuMzE1IDUuNzMzNTggMTMuMzE1IDYuNTc4OTJDMTMuMzE1IDcuNDQ0MTQgMTMuMDcxNCA4LjE1NTIyIDEyLjU4NDEgOC43MTIxNUMxMi4xMDY3IDkuMjU5MTMgMTEuNDU1MyA5LjYzNzA1IDEwLjYyOTggOS44NDU5TDEyLjA2MTkgMTQuMTU3MUgxMC4zMzE1TDkuMDMzNjQgMTAuMDI0OUg3LjI0MzUxTDYuNTEyNTQgMTQuMTU3MUg0LjkzMTI2Wk03LjQ5NzExIDguNTkyODFIOS4yNDI0OEM5Ljk5ODMyIDguNTkyODEgMTAuNTkwMSA4LjQyMzc0IDExLjAxNzcgOC4wODU2MUMxMS40NTUzIDcuNzM3NTMgMTEuNjc0MSA3LjI2NTEzIDExLjY3NDEgNi42Njg0MkMxMS42NzQxIDYuMTkxMDYgMTEuNTI0OSA1LjgxODExIDExLjIyNjUgNS41NDk1OUMxMC45MjgyIDUuMjcxMTMgMTAuNDU1OCA1LjEzMTkgOS44MDkzNiA1LjEzMTlIOC4xMDg3NEw3LjQ5NzExIDguNTkyODFaIiBmaWxsPSJ3aGl0ZSIvPgo8L3N2Zz4K">
<bpmn:extensionElements>
<zeebe:taskDefinition type="io.camunda:http-json:1" retries="1" />
<!-- <zeebe:taskDefinition type="io.camunda:http-json:1" retries="0" /> -->
<zeebe:taskDefinition type="io.camunda:http-json:1" />
<zeebe:ioMapping>
<zeebe:input source="noAuth" target="authentication.type" />
<zeebe:input source="GET" target="method" />
Expand All @@ -27,18 +28,34 @@
</zeebe:ioMapping>
<zeebe:taskHeaders>
<zeebe:header key="resultExpression" value="={ exchangeRate: response.body.rate }" />
<zeebe:header key="retryBackoff" value="PT0S" />
<zeebe:header key="errorExpression" value="=if starts with(error.code, &#34;5&#34;) then&#10; bpmnError(&#34;5XX&#34;, &#34;Server Error&#34;)&#10;else&#10; null" />
<!-- <zeebe:header key="retryBackoff" value="PT0S" /> -->
</zeebe:taskHeaders>
</bpmn:extensionElements>
<bpmn:incoming>Flow_1necipk</bpmn:incoming>
<bpmn:outgoing>Flow_1oyfejh</bpmn:outgoing>
</bpmn:serviceTask>
<bpmn:endEvent id="EndEvent_Done" name="Done">
<bpmn:incoming>Flow_1oyfejh</bpmn:incoming>
<bpmn:incoming>Flow_14rbqge</bpmn:incoming>
</bpmn:endEvent>
<bpmn:sequenceFlow id="Flow_1oyfejh" sourceRef="Task_CallExchangeRateAPI" targetRef="EndEvent_Done" />
<bpmn:scriptTask id="Task-DefaultRate" name="Default Rate">
<bpmn:extensionElements>
<zeebe:script expression="=1.3" resultVariable="exchangeRate" />
</bpmn:extensionElements>
<bpmn:incoming>Flow_09fsnwm</bpmn:incoming>
<bpmn:outgoing>Flow_14rbqge</bpmn:outgoing>
</bpmn:scriptTask>
<bpmn:sequenceFlow id="Flow_14rbqge" sourceRef="Task-DefaultRate" targetRef="EndEvent_Done" />
<bpmn:boundaryEvent id="Event_APIError" name="API Error" attachedToRef="Task_CallExchangeRateAPI">
<bpmn:outgoing>Flow_09fsnwm</bpmn:outgoing>
<bpmn:errorEventDefinition id="ErrorEventDefinition_05f0ti7" errorRef="Error_0jz0pjw" />
</bpmn:boundaryEvent>
<bpmn:sequenceFlow id="Flow_09fsnwm" sourceRef="Event_APIError" targetRef="Task-DefaultRate" />
</bpmn:process>
<bpmn:message id="Message_3c5v476" name="Exchange Rate Request" />
<bpmn:error id="Error_0jz0pjw" name="Error_APIError" errorCode="5XX" />
<bpmndi:BPMNDiagram id="BPMNDiagram_1">
<bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="Process_RESTConnector">
<bpmndi:BPMNShape id="Event_1w6mwrg_di" bpmnElement="Start_ExchangeRateRequest">
Expand All @@ -54,7 +71,16 @@
<bpmndi:BPMNShape id="Event_0jjrxrz_di" bpmnElement="EndEvent_Done">
<dc:Bounds x="432" y="99" width="36" height="36" />
<bpmndi:BPMNLabel>
<dc:Bounds x="437" y="142" width="27" height="14" />
<dc:Bounds x="436" y="75" width="27" height="14" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Activity_0rm93wk_di" bpmnElement="Task-DefaultRate">
<dc:Bounds x="270" y="230" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Event_0o1er5t_di" bpmnElement="Event_APIError" bioc:stroke="#831311" bioc:fill="#ffcdd2" color:background-color="#ffcdd2" color:border-color="#831311">
<dc:Bounds x="302" y="139" width="36" height="36" />
<bpmndi:BPMNLabel color:color="#831311">
<dc:Bounds x="327" y="182" width="46" height="14" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge id="Flow_1necipk_di" bpmnElement="Flow_1necipk">
Expand All @@ -65,6 +91,15 @@
<di:waypoint x="370" y="117" />
<di:waypoint x="432" y="117" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_14rbqge_di" bpmnElement="Flow_14rbqge">
<di:waypoint x="370" y="270" />
<di:waypoint x="450" y="270" />
<di:waypoint x="450" y="135" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_09fsnwm_di" bpmnElement="Flow_09fsnwm" bioc:stroke="#831311" color:border-color="#831311">
<di:waypoint x="320" y="175" />
<di:waypoint x="320" y="230" />
</bpmndi:BPMNEdge>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</bpmn:definitions>
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import com.github.tomakehurst.wiremock.junit5.WireMockTest;
import com.micasa.tutorial.model.ExchangeRateRequest;
import com.micasa.tutorial.start.ZeebeController;
import io.camunda.zeebe.process.test.api.ZeebeTestEngine;
import io.camunda.zeebe.process.test.assertions.BpmnAssert;
import io.camunda.zeebe.process.test.inspections.InspectionUtility;
import io.camunda.zeebe.process.test.inspections.model.InspectedProcessInstance;
Expand All @@ -17,19 +18,25 @@

import org.springframework.test.context.ActiveProfiles;

import java.time.Duration;
import java.util.concurrent.TimeoutException;

@SpringBootTest
@ZeebeSpringTest
@WireMockTest(httpPort = 9999)
@ActiveProfiles("test")
@DisplayName("REST Connector Test")
class RESTConnectorProcessTest {

@Autowired
private ZeebeTestEngine engine;

@Autowired
private ZeebeController controller;

@Test
@DisplayName("Using the Zeebe Controller")
void zeebeController() throws Exception {
void zeebeController() {
stubFor(
get(urlPathEqualTo("/exchangeRates"))
.withQueryParam("fromCurrency", equalTo("USD"))
Expand Down Expand Up @@ -69,4 +76,57 @@ void zeebeController() throws Exception {
.hasVariableWithValue("exchangeRate", 1.32466);
}

@Test
@DisplayName("API error -> default rate")
void error() {
stubFor(
get(urlPathEqualTo("/exchangeRates"))
.withQueryParam("fromCurrency", equalTo("INVALID"))
.withQueryParam("toCurrency", equalTo("CAD"))
.withQueryParam("amount", equalTo("1000"))
.willReturn(serverError())
);

controller.startProcess(new ExchangeRateRequest("INVALID", "CAD", 1000));

InspectedProcessInstance processInstance = InspectionUtility
.findProcessInstances()
.withBpmnProcessId("Process_RESTConnector")
.findFirstProcessInstance()
.get();

waitForProcessInstanceCompleted(processInstance);

BpmnAssert.assertThat(processInstance)
.hasPassedElement("Task-DefaultRate")
.hasVariableWithValue("fromCurrency", "INVALID")
.hasVariableWithValue("exchangeRate", 1.3); // default rate
}

@Test
@DisplayName("Request error -> incident")
void incident() throws InterruptedException, TimeoutException {
stubFor(
get(urlPathEqualTo("/exchangeRates"))
.withQueryParam("fromCurrency", equalTo("USD"))
.withQueryParam("toCurrency", equalTo("CAD"))
.withQueryParam("amount", equalTo("0"))
.willReturn(badRequest())
);

controller.startProcess(new ExchangeRateRequest("USD", "CAD", 0));

InspectedProcessInstance processInstance = InspectionUtility
.findProcessInstances()
.withBpmnProcessId("Process_RESTConnector")
.findFirstProcessInstance()
.get();

engine.waitForBusyState(Duration.ofSeconds(10));

BpmnAssert.assertThat(processInstance)
.hasNotPassedElement("Task_CallExchangeRateAPI")
.hasAnyIncidents();
}

}

0 comments on commit 261e3b1

Please sign in to comment.