private ServiceTaskBuilder addServiceTask( final AbstractFlowNodeBuilder<?, ?> builder, YamlTask task) { final String id = task.getId(); final String taskType = task.getType(); final Integer taskRetries = task.getRetries(); final ServiceTaskBuilder serviceTaskBuilder = builder.serviceTask(id).zeebeTaskType(taskType).zeebeTaskRetries(taskRetries); for (Entry<String, String> header : task.getHeaders().entrySet()) { serviceTaskBuilder.zeebeTaskHeader(header.getKey(), header.getValue()); } addInputOutputMappingToTask(task, serviceTaskBuilder); return serviceTaskBuilder; }
@Test public void shouldCreateIncidentForInAndOutputMappingAndNoTaskCompletePayload() { // given testClient.deploy( Bpmn.createExecutableProcess("process") .startEvent() .serviceTask( "failingTask", t -> t.zeebeTaskType("external") .zeebeInput("$.jsonObject", "$") .zeebeOutput("$.foo", "$")) .done()); testClient.createWorkflowInstance("process", MSGPACK_PAYLOAD); // when testClient.completeJobOfType("external"); // then incident is created final Record incidentEvent = testClient.receiveFirstIncidentEvent(IncidentIntent.CREATED); assertThat(incidentEvent.getKey()).isGreaterThan(0); assertIncidentContainErrorDetails(incidentEvent, "No data found for query $.foo."); }
@Override public ServiceTaskBuilder builder() { return new ServiceTaskBuilder((BpmnModelInstance) modelInstance, this); }
private void addInputOutputMappingToTask(YamlTask task, ServiceTaskBuilder serviceTaskBuilder) { final String outputBehaviorString = task.getOutputBehavior(); final ZeebeOutputBehavior outputBehavior; try { outputBehavior = ZeebeOutputBehavior.valueOf(outputBehaviorString.toLowerCase()); } catch (IllegalArgumentException e) { throw new RuntimeException( String.format( "Expected output behavior to be one of %s, but actual output behavior was '%s'", outputBehaviorString, Arrays.toString(ZeebeOutputBehavior.values()))); } serviceTaskBuilder.zeebeOutputBehavior(outputBehavior); for (YamlMapping inputMapping : task.getInputs()) { serviceTaskBuilder.zeebeInput(inputMapping.getSource(), inputMapping.getTarget()); } for (YamlMapping outputMapping : task.getOutputs()) { serviceTaskBuilder.zeebeOutput(outputMapping.getSource(), outputMapping.getTarget()); } } }
public long createJob(final String type, Consumer<ServiceTaskBuilder> consumer, String payload) { deploy( Bpmn.createExecutableProcess("process") .startEvent() .serviceTask( "task", b -> { b.zeebeTaskType(type).zeebeTaskRetries(3); consumer.accept(b); }) .done()); final long workflowInstance = createWorkflowInstance("process", payload); return RecordingExporter.jobRecords(JobIntent.CREATED) .withType(type) .filter(j -> j.getValue().getHeaders().getWorkflowInstanceKey() == workflowInstance) .getFirst() .getKey(); }
@Test public void shouldCompleteInstanceAfterParallelSplit() { // given testClient.deploy( Bpmn.createExecutableProcess(PROCESS_ID) .startEvent() .parallelGateway() .serviceTask("task-1", t -> t.zeebeTaskType("task-1")) .endEvent("end-1") .moveToLastGateway() .serviceTask("task-2", t -> t.zeebeTaskType("task-2")) .endEvent("end-2") .done()); testClient.createWorkflowInstance(PROCESS_ID); // when testClient.completeJobOfType("task-1"); testClient.completeJobOfType("task-2"); // then assertThatWorkflowInstanceCompletedAfter("end-2", WorkflowInstanceIntent.EVENT_ACTIVATED); }
@Test public void shouldCreateIncidentForInvalidResultOnInputMapping() { // given testClient.deploy( Bpmn.createExecutableProcess("process") .startEvent() .serviceTask( "failingTask", t -> t.zeebeTaskType("external").zeebeInput("$.string", "$")) .done()); // when testClient.createWorkflowInstance("process", MSGPACK_PAYLOAD); // then incident is created final Record<IncidentRecordValue> incidentEvent = testClient.receiveFirstIncidentEvent(IncidentIntent.CREATED); assertThat(incidentEvent.getKey()).isGreaterThan(0); assertIncidentContainErrorDetails(incidentEvent); }
@Test public void shouldNotCompleteInstanceAfterIncidentIsRaisedOnActivity() { // given testClient.deploy( Bpmn.createExecutableProcess(PROCESS_ID) .startEvent() .parallelGateway() .serviceTask("task-1", t -> t.zeebeTaskType("task-1")) .endEvent("end-1") .moveToLastGateway() .serviceTask("task-2", t -> t.zeebeTaskType("task-2").zeebeOutput("$.result", "$.r")) .endEvent("end-2") .done()); testClient.createWorkflowInstance(PROCESS_ID); // when testClient.completeJobOfType("task-2"); final Record<IncidentRecordValue> incident = RecordingExporter.incidentRecords(IncidentIntent.CREATED).getFirst(); testClient.completeJobOfType("task-1"); testClient.updatePayload(incident.getValue().getElementInstanceKey(), "{'result':'123'}"); testClient.resolveIncident(incident.getKey()); // then assertThatWorkflowInstanceCompletedAfter("end-2", WorkflowInstanceIntent.EVENT_ACTIVATED); }
@Test public void shouldCreateIncidentForOutputMappingAndNoTaskCompletePayload() { // given testClient.deploy( Bpmn.createExecutableProcess("process") .startEvent() .serviceTask( "failingTask", t -> t.zeebeTaskType("external").zeebeOutput("$.testAttr", "$")) .done()); testClient.createWorkflowInstance("process", MSGPACK_PAYLOAD); // when testClient.completeJobOfType("external"); // then incident is created final Record incidentEvent = testClient.receiveFirstIncidentEvent(IncidentIntent.CREATED); assertThat(incidentEvent.getKey()).isGreaterThan(0); assertIncidentContainErrorDetails(incidentEvent, "No data found for query $.testAttr."); }
@Override BpmnModelInstance modelInstance() { return Bpmn.createExecutableProcess(processId()) .startEvent() .serviceTask("task", b -> b.zeebeTaskType(taskType())) .boundaryEvent(elementId()) .timerWithDuration("PT0.01S") .endEvent() .done(); } },
@Test public void shouldCreateJobWithWorkflowInstanceAndCustomHeaders() { // given testClient.deploy( Bpmn.createExecutableProcess(PROCESS_ID) .startEvent() .serviceTask( "foo", t -> t.zeebeTaskType("bar").zeebeTaskHeader("a", "b").zeebeTaskHeader("c", "d")) .endEvent() .done()); // when final long workflowInstanceKey = testClient.createWorkflowInstance(PROCESS_ID); // then final Record<JobRecordValue> event = testClient.receiveFirstJobCommand(JobIntent.CREATE); assertJobHeaders(workflowInstanceKey, "foo", event); final Map<String, Object> customHeaders = event.getValue().getCustomHeaders(); assertThat(customHeaders).containsEntry("a", "b").containsEntry("c", "d"); }
@Test public void shouldUseScopeToExtractCorrelationKeys() { // given final String processId = "shouldHaveScopeKeyIfBoundaryEvent"; final BpmnModelInstance workflow = Bpmn.createExecutableProcess(processId) .startEvent() .serviceTask("task", c -> c.zeebeTaskType("type").zeebeInput("$.bar", "$.foo")) .boundaryEvent( "event", b -> b.message(m -> m.zeebeCorrelationKey("$.foo").name("message"))) .endEvent() .moveToActivity("task") .endEvent() .done(); testClient.deploy(workflow); // when testClient.createWorkflowInstance( processId, MsgPackUtil.asMsgPack(m -> m.put("foo", 1).put("bar", 2))); testClient.publishMessage("message", "1"); // then // if correlation key was extracted from the task, then foo in the task scope would be 2 and // no event occurred would be published assertThat(testClient.receiveElementInState("task", WorkflowInstanceIntent.EVENT_OCCURRED)) .isNotNull(); }
@Override BpmnModelInstance modelInstance() { return Bpmn.createExecutableProcess(processId()) .startEvent() .serviceTask(elementId(), b -> b.zeebeTaskType(taskType())) .done(); }
@Test public void shouldCreateTaskWhenServiceTaskIsActivated() { // given testClient.deploy( Bpmn.createExecutableProcess(PROCESS_ID) .startEvent() .serviceTask("foo", t -> t.zeebeTaskType("bar").zeebeTaskRetries(5)) .endEvent() .done()); // when testClient.createWorkflowInstance(PROCESS_ID); // then final Record<WorkflowInstanceRecordValue> activityActivated = testClient.receiveElementInState("foo", WorkflowInstanceIntent.ELEMENT_ACTIVATED); final Record<JobRecordValue> createJobCmd = testClient.receiveFirstJobCommand(JobIntent.CREATE); assertThat(createJobCmd.getKey()).isEqualTo(ExecuteCommandResponseDecoder.keyNullValue()); assertThat(createJobCmd.getSourceRecordPosition()).isEqualTo(activityActivated.getPosition()); assertJobRecord(createJobCmd); }
Bpmn.createExecutableProcess("process") .startEvent("start") .serviceTask("task1", b -> b.zeebeTaskType("type1").zeebeTaskHeader("key", testValue)) .endEvent("end") .moveToActivity("task1") .serviceTask("task2", b -> b.zeebeTaskType("type2").zeebeTaskHeader(testValue, "value")) .connectTo("end") .moveToActivity("task1") .serviceTask( "task3", b -> b.zeebeTaskType("type3").zeebeTaskHeader(testValue, testValue)) .connectTo("end") .done();
@Test public void shouldSplitWithUncontrolledFlow() { // given final BpmnModelInstance process = Bpmn.createExecutableProcess(PROCESS_ID) .startEvent("start") .serviceTask("task1", b -> b.zeebeTaskType("type1")) .moveToNode("start") .serviceTask("task2", b -> b.zeebeTaskType("type2")) .done(); testClient.deploy(process); // when testClient.createWorkflowInstance(PROCESS_ID); // then final List<Record<WorkflowInstanceRecordValue>> taskEvents = testClient .receiveWorkflowInstances() .withIntent(WorkflowInstanceIntent.ELEMENT_ACTIVATED) .filter(e -> isServiceTaskInProcess(e.getValue().getElementId(), process)) .limit(2) .collect(Collectors.toList()); assertThat(taskEvents).hasSize(2); assertThat(taskEvents) .extracting(e -> e.getValue().getElementId()) .containsExactlyInAnyOrder("task1", "task2"); assertThat(taskEvents.get(0).getKey()).isNotEqualTo(taskEvents.get(1).getKey()); }
@Test public void shouldCreateIncidentForNonMatchingAndMatchingValueOnInputMapping() { // given testClient.deploy( Bpmn.createExecutableProcess("process") .startEvent() .serviceTask( "service", t -> t.zeebeTaskType("external") .zeebeInput("$.notExisting", "$.nullVal") .zeebeInput(NODE_STRING_PATH, "$.existing")) .endEvent() .done()); // when testClient.createWorkflowInstance("process", MSGPACK_PAYLOAD); final Record incidentEvent = testClient.receiveFirstIncidentEvent(IncidentIntent.CREATED); // then incident is created assertThat(incidentEvent.getKey()).isGreaterThan(0); assertIncidentRecordValue( ErrorType.IO_MAPPING_ERROR.name(), "No data found for query $.notExisting.", "service", incidentEvent); }
@Test public void shouldMergeAndSplitInOneGateway() { // given final BpmnModelInstance process = Bpmn.createExecutableProcess(PROCESS_ID) .startEvent() .parallelGateway("fork") .parallelGateway("join-fork") .moveToNode("fork") .connectTo("join-fork") .serviceTask("task1", b -> b.zeebeTaskType("type1")) .moveToLastGateway() .serviceTask("task2", b -> b.zeebeTaskType("type2")) .done(); testClient.deploy(process); // when testClient.createWorkflowInstance(PROCESS_ID); // then final List<Record<WorkflowInstanceRecordValue>> elementInstances = testClient .receiveWorkflowInstances() .filter(r -> r.getMetadata().getIntent() == WorkflowInstanceIntent.ELEMENT_ACTIVATED) .limit(3) .collect(Collectors.toList()); assertThat(elementInstances) .extracting(e -> e.getValue().getElementId()) .contains(PROCESS_ID, "task1", "task2"); }
@Test public void shouldCompleteInstanceAfterParallelJoin() { // given testClient.deploy( Bpmn.createExecutableProcess(PROCESS_ID) .startEvent() .parallelGateway("fork") .serviceTask("task-1", t -> t.zeebeTaskType("task-1")) .parallelGateway("join") .endEvent("end") .moveToNode("fork") .serviceTask("task-2", t -> t.zeebeTaskType("task-2")) .connectTo("join") .done()); testClient.createWorkflowInstance(PROCESS_ID); // when testClient.completeJobOfType("task-1"); testClient.completeJobOfType("task-2"); // then assertThatWorkflowInstanceCompletedAfter("end", WorkflowInstanceIntent.EVENT_ACTIVATED); }
.parallelGateway("join") .moveToNode("fork") .serviceTask("waitState", b -> b.zeebeTaskType("type")) .sequenceFlowId("joinFlow2") .connectTo("join") .endEvent() .done();