-
Notifications
You must be signed in to change notification settings - Fork 554
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
10442: feat: support terminate end events r=saig0 a=saig0 ## Description Add support for BPMN terminate end events. See #8789 (comment) on how the BPMN element should work. The implementation doesn't follow the BPMN spec in one point: the flow scope that contains the terminate end event is not terminated but completed. Reasoning: - The state of the flow scope is a detail that doesn't influence the core behavior. In both cases, the process instance should continue, for example, by taking the outgoing sequence flow. The difference is not visible to process participants but only when monitoring the process instance, for example, in Operate. - It fits better with the existing implementation. It would be a bigger effort to continue the process instance (e.g. taking the outgoing sequence flow) when the flow scope is terminated. As a result, we would end up in more complex code. - It aligns with the behavior of Camunda Platform 7. Side note: I implemented the major parts during a [Live Hacking session](https://www.twitch.tv/videos/1584245006). 🎥 ## Related issues closes #8789 Co-authored-by: Philipp Ossler <philipp.ossler@gmail.com>
- Loading branch information
Showing
12 changed files
with
770 additions
and
54 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
148 changes: 148 additions & 0 deletions
148
...del/src/test/java/io/camunda/zeebe/model/bpmn/validation/ZeebeEndEventValidationTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,148 @@ | ||
/* | ||
* Copyright © 2017 camunda services GmbH (info@camunda.com) | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
package io.camunda.zeebe.model.bpmn.validation; | ||
|
||
import static io.camunda.zeebe.model.bpmn.validation.ExpectedValidationResult.expect; | ||
|
||
import io.camunda.zeebe.model.bpmn.Bpmn; | ||
import io.camunda.zeebe.model.bpmn.BpmnModelInstance; | ||
import io.camunda.zeebe.model.bpmn.builder.EndEventBuilder; | ||
import io.camunda.zeebe.model.bpmn.builder.StartEventBuilder; | ||
import io.camunda.zeebe.model.bpmn.instance.CancelEventDefinition; | ||
import io.camunda.zeebe.model.bpmn.instance.CompensateEventDefinition; | ||
import io.camunda.zeebe.model.bpmn.instance.EndEvent; | ||
import io.camunda.zeebe.model.bpmn.instance.ErrorEventDefinition; | ||
import io.camunda.zeebe.model.bpmn.instance.EscalationEventDefinition; | ||
import io.camunda.zeebe.model.bpmn.instance.EventDefinition; | ||
import io.camunda.zeebe.model.bpmn.instance.MessageEventDefinition; | ||
import io.camunda.zeebe.model.bpmn.instance.SignalEventDefinition; | ||
import io.camunda.zeebe.model.bpmn.instance.TerminateEventDefinition; | ||
import java.util.function.UnaryOperator; | ||
import java.util.stream.Stream; | ||
import org.junit.jupiter.api.DisplayName; | ||
import org.junit.jupiter.api.Test; | ||
import org.junit.jupiter.params.ParameterizedTest; | ||
import org.junit.jupiter.params.provider.MethodSource; | ||
|
||
class ZeebeEndEventValidationTest { | ||
|
||
private static final String END_EVENT_ID = "end"; | ||
|
||
@ParameterizedTest(name = "[{index}] event type = {0}") | ||
@MethodSource("supportedEndEventTypes") | ||
@DisplayName("Should support end event of the given type") | ||
void supportedEndEventTypes(final EndEventTypeBuilder endEventTypeBuilder) { | ||
// given | ||
final BpmnModelInstance process = createProcessWithEndEvent(endEventTypeBuilder); | ||
|
||
// when/then | ||
ProcessValidationUtil.assertThatProcessIsValid(process); | ||
} | ||
|
||
@ParameterizedTest(name = "[{index}] event type = {0}") | ||
@MethodSource("unsupportedEndEventTypes") | ||
@DisplayName("Should not support end event of the given type") | ||
void unsupportedEndEventTypes(final EndEventTypeBuilder endEventTypeBuilder) { | ||
// given | ||
final BpmnModelInstance process = createProcessWithEndEvent(endEventTypeBuilder); | ||
|
||
// when/then | ||
ProcessValidationUtil.assertThatProcessHasViolations( | ||
process, | ||
expect(END_EVENT_ID, "End events must be one of: none, error, message, or terminate"), | ||
expect(endEventTypeBuilder.eventType, "Event definition of this type is not supported")); | ||
} | ||
|
||
@Test | ||
@DisplayName("An end event should not have an outgoing sequence flow") | ||
void outgoingSequenceFlow() { | ||
// given | ||
final BpmnModelInstance process = | ||
Bpmn.createExecutableProcess("process") | ||
.startEvent() | ||
.endEvent(END_EVENT_ID) | ||
// an activity after the end event | ||
.manualTask() | ||
.done(); | ||
|
||
// when/then | ||
ProcessValidationUtil.assertThatProcessHasViolations( | ||
process, | ||
expect( | ||
EndEvent.class, "End events must not have outgoing sequence flows to other elements.")); | ||
} | ||
|
||
private static BpmnModelInstance createProcessWithEndEvent( | ||
final EndEventTypeBuilder endEventTypeBuilder) { | ||
final StartEventBuilder processBuilder = Bpmn.createExecutableProcess("process").startEvent(); | ||
endEventTypeBuilder.build(processBuilder.endEvent(END_EVENT_ID)); | ||
return processBuilder.done(); | ||
} | ||
|
||
private static Stream<EndEventTypeBuilder> supportedEndEventTypes() { | ||
return Stream.of( | ||
new EndEventTypeBuilder(null, endEvent -> endEvent), | ||
new EndEventTypeBuilder( | ||
ErrorEventDefinition.class, endEvent -> endEvent.error("error-code")), | ||
new EndEventTypeBuilder( | ||
MessageEventDefinition.class, | ||
endEvent -> endEvent.message("message-name").zeebeJobType("job-type")), | ||
new EndEventTypeBuilder(TerminateEventDefinition.class, EndEventBuilder::terminate)); | ||
} | ||
|
||
private static Stream<EndEventTypeBuilder> unsupportedEndEventTypes() { | ||
return Stream.of( | ||
new EndEventTypeBuilder( | ||
SignalEventDefinition.class, endEvent -> endEvent.signal("signal-name")), | ||
new EndEventTypeBuilder( | ||
EscalationEventDefinition.class, endEvent -> endEvent.escalation("escalation-code")), | ||
new EndEventTypeBuilder( | ||
CompensateEventDefinition.class, | ||
endEvent -> endEvent.compensateEventDefinition().compensateEventDefinitionDone()), | ||
new EndEventTypeBuilder( | ||
CancelEventDefinition.class, | ||
endEvent -> { | ||
// currently, we don't have a builder for cancel events | ||
final CancelEventDefinition cancelEventDefinition = | ||
endEvent.getElement().getModelInstance().newInstance(CancelEventDefinition.class); | ||
endEvent.getElement().getEventDefinitions().add(cancelEventDefinition); | ||
return endEvent; | ||
})); | ||
} | ||
|
||
private static final class EndEventTypeBuilder { | ||
private final String eventTypeName; | ||
private final Class<? extends EventDefinition> eventType; | ||
private final UnaryOperator<EndEventBuilder> elementModifier; | ||
|
||
private EndEventTypeBuilder( | ||
final Class<? extends EventDefinition> eventType, | ||
final UnaryOperator<EndEventBuilder> elementModifier) { | ||
this.eventType = eventType; | ||
eventTypeName = eventType == null ? "none" : eventType.getSimpleName(); | ||
this.elementModifier = elementModifier; | ||
} | ||
|
||
public EndEventBuilder build(final EndEventBuilder endEventBuilder) { | ||
return elementModifier.apply(endEventBuilder); | ||
} | ||
|
||
@Override | ||
public String toString() { | ||
return eventTypeName; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.