-
Notifications
You must be signed in to change notification settings - Fork 571
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Business rule task can evaluate a DMN decision #8755
Conversation
🤔 It looks like the
This is strange, because the jenkins build does succeed. It looks like it is able to find the other classes from EDIT: I've had another look. It isn't a problem with |
Recently, we encountered compilation errors in the github checks: OS Smoke Tests and Code QL checks, while the Jenkins pipeline was able to complete succesfully. It seems that the difference might stem from the multi threadedness of these builds, i.e. the mvn command is ran with -T1C. See #8755 (comment) for an example of the compilation error.
The business rule task will work as both job worker task as well as a being able to call a decision, depending on how it's modeled. We'll need to modify its transformer slightly. This extracts the common parts (TaskDefinition and TaskHeaders transformers) and reuses them in a new BusinessRuleTaskTransformer
The zeebe transformers used slightly different implementation patterns, which leads to different call patterns. Should the tranformer be called if the specific extension element is not in the model? I think the validators should make sure that all model parts are defined as expected. The transformers should simply be able to deal with the case that it was optional. This matters, because we're going to change 1 of the tasks that currently always has a task definition but it will be optional after the future changes (i.e. business rule task). Therefore, I wanted to align the implementation of these classes so their usage can be kept the same over multiple elements that may contain these extension elements. Lastly, I chose to move the getSingleExtensionElement call to the caller side, to make it more clear at the caller side what this transformer does. This also makes it very easy to move the 'if element is null do nothing' check into the caller side, if we do happen to decide that the transformers should only be called when they have the element.
The business rule task is either a job worker task or it has a called decision. To represent this, the class should be able to be both: it is a job worker task and it can have a called decision. In reality only 1 of these should be present. This should at the minimum be checked by the validators, but we could also consider adding a safety check in this class. For now, I've left it out.
The business rule task can be either a job worker task or have a called decision. If it's a job worker task, we can simply reuse the existing JobWorkerTaskProcessor. If it has a decision to call, we can process it differently. This requires some specialised behavior classes, because we can't simply use something like: ``` private final BpmnElementProcessor<ExecutableJobWorkerTask> jobWorkerTaskBehavior; private final BpmnElementProcessor<ExecutableBusinessRuleTask> calledDecisionBehavior; ``` This would fail because at the calling site of the .onX methods we'll notice that the types don't match anymore. Instead, we can simply define a BusinessRuleTaskBehavior. We can implement it for the job worker task using delegation, while we can implement it independently for the called decision behavior.
This adds the failing tests for the called decision behavior when processing a business rule task that specifies a called decision id. These tests are inspired by the JobWorkerElementTest and follow a similar structure, with the major difference that instead of verifying the creation of a job, it verifies the writing of the result variable.
This adds the failing tests for the called decision behavior related to incident when processing a business rule task that specifies a called decision id. Specifically, it makes sure that failing to evaluate the decision leads to a resolvable incident. In addition, it makes sure that incidents due to output mappings behave the same for such business rule tasks as for service tasks. Small refactorings of the OutputMappingIncidentTest were necessary to deploy the dmn along with the process model.
This method can be used to easily convert an Optional into an Either. Optional provides an .orElse method to successfully retrieve a result from it, but this always has to be the same type as the Optional's type parameter. In the case of Either, we want to be able to express the 'other' value (i.e. left) with a potentially different type. Users are already used to using .orElse in the context of Optionals. When converting an Optional into an Either, it then makes sense to provide a similarly named method to supply the left value.
This introduces a new BpmnDecisionBehavior class to provide decision behavior to the bpmn processors, and it is immediately used in the BusinessRuleTaskProcessor with called decision behavior. The implementatino is pretty straightforward. It needs to: - find the drg in the state (this has some potential failure cases) - evaluate the decision in the drg (this can also fail) - trigger a process event with the result variable if successfully evaluated. The process event triggering record is used to accomodate the async nature of evaluating the decision on element activations, while the result variable is only used on completino of the element. That is, the output mapping behavior uses it to determine whether it will propagate it as a process variable, or write it as a local variable and use it in the output mappings. This combined with the potential to raise incidents as part of the output mappings behavior, means that the value must be available in state and thus on record. The event trigger that this creates is cleaned up by the ProcessInstanceElementCompletedApplier as the normal part of cleanup involved with the element being completed. All failure cases mentioned above result in an incident on the business rule task during while activating, with the exception of the incidents raised during output mapping. Lastly, it felt a bit cleaner to provide a smaller interface to the BpmnDecisionBehavior.evaluateDecision method. So, I extracted the ExecutableCalledDecision interface from the ExecutableBusinessRuleTask.
9005ee8
to
1af80b8
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@korthout well done 🥇
The code looks simple and clean. Thanks for splitting the PR into the commits and adding the descriptions 👍
I just have some minor suggestions. Feel free to merge 🚀
|
||
public final class ExecutableBusinessRuleTask extends ExecutableJobWorkerTask { | ||
|
||
private String decisionId; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ℹ️ I created a follow-up issue to support an expression as decision id #8779.
...ne/src/main/java/io/camunda/zeebe/engine/processing/bpmn/task/BusinessRuleTaskProcessor.java
Outdated
Show resolved
Hide resolved
...ne/src/main/java/io/camunda/zeebe/engine/processing/bpmn/task/BusinessRuleTaskProcessor.java
Outdated
Show resolved
Hide resolved
engine/src/test/java/io/camunda/zeebe/engine/processing/bpmn/activity/BusinessRuleTaskTest.java
Outdated
Show resolved
Hide resolved
engine/src/test/java/io/camunda/zeebe/engine/processing/bpmn/activity/BusinessRuleTaskTest.java
Outdated
Show resolved
Hide resolved
""" | ||
Expected to evaluate decision 'jedi-or-sith', \ | ||
but failed to evaluate expression 'lightsaberColor': \ | ||
no variable found for name 'lightsaberColor'\ | ||
"""); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice usage of the new multi-line strings 🤩
engine/src/test/java/io/camunda/zeebe/engine/processing/incident/OutputMappingIncidentTest.java
Outdated
Show resolved
Hide resolved
|
||
private final Optional<R> right; | ||
|
||
public EitherOptional(final Optional<R> right) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🔧 We could change the visibility modifier to private
.
We don't expect that the EitherOptional
class is created directly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@saig0 We also turn it into a record
.
record EitherOptional<R>(Optional<R> right) {
public <L> Either<L, R> orElse(final L left) {
return right.<Either<L, R>>map(Either::right).orElse(Either.left(left));
}
}
In this case, you have an extra method available after using Either.ofOptional
that allows the caller to retrieve the wrapped Optional:
WDYT?
I will already merge, so feel free to answer this any time that suits you and we can always give it a try later.
Using the BpmnProcessingException we can easily print the related context. This can be useful during debugging.
This was a copy-pasta mistake.
These test cases used the same scenario and could easily be merged.
This makes for a cleaner test.
The constructor should only be used by the Either class itself.
bors merge |
8755: Business rule task can evaluate a DMN decision r=korthout a=korthout ## Description This adds the ability to process business rule tasks that refer to a called decision (see #8060). Business rule tasks that refer to a [task definition](https://docs.camunda.io/docs/components/modeler/bpmn/business-rule-tasks/business-rule-tasks/#defining-a-task) instead of a called decision work like they did before, i.e. by creating a job for job workers. On activation of a business rule task with called decision, the decision specified by `decisionId` is retrieved from the deployed decisions state. If present, the engine uses the `dmn` module to evaluate the decision. If that is successful, the decision result's output is stored in a result variable (defined by `resultVariable`). The result variable stores the decision result's output according to the following rules: - if the business rule task defines no output mappings, the result variable is [propagated](https://docs.camunda.io/docs/components/concepts/variables/#variable-propagation) - if there are [output mappings](https://docs.camunda.io/docs/components/concepts/variables/#output-mappings) defined on the business rule task, the result variable is set as a [local variable](https://docs.camunda.io/docs/components/concepts/variables/#local-variables) of the business rule task If any of this fails, Zeebe raises an incident on the business rule task. For example, if the decision was not deployed yet, then an incident is raised. This allows you to: 1. deploy the decision to fix the problem 2. retry activating the business rule task by resolving the incident ## Related issues <!-- Which issues are closed by this PR or are related --> closes #8080 Co-authored-by: Nico Korthout <nico.korthout@camunda.com>
Build failed: |
bors retry |
8755: Business rule task can evaluate a DMN decision r=korthout a=korthout ## Description This adds the ability to process business rule tasks that refer to a called decision (see #8060). Business rule tasks that refer to a [task definition](https://docs.camunda.io/docs/components/modeler/bpmn/business-rule-tasks/business-rule-tasks/#defining-a-task) instead of a called decision work like they did before, i.e. by creating a job for job workers. On activation of a business rule task with called decision, the decision specified by `decisionId` is retrieved from the deployed decisions state. If present, the engine uses the `dmn` module to evaluate the decision. If that is successful, the decision result's output is stored in a result variable (defined by `resultVariable`). The result variable stores the decision result's output according to the following rules: - if the business rule task defines no output mappings, the result variable is [propagated](https://docs.camunda.io/docs/components/concepts/variables/#variable-propagation) - if there are [output mappings](https://docs.camunda.io/docs/components/concepts/variables/#output-mappings) defined on the business rule task, the result variable is set as a [local variable](https://docs.camunda.io/docs/components/concepts/variables/#local-variables) of the business rule task If any of this fails, Zeebe raises an incident on the business rule task. For example, if the decision was not deployed yet, then an incident is raised. This allows you to: 1. deploy the decision to fix the problem 2. retry activating the business rule task by resolving the incident ## Related issues <!-- Which issues are closed by this PR or are related --> closes #8080 Co-authored-by: Nico Korthout <nico.korthout@camunda.com>
Build failed: |
Ugh, aborted due to connection error. bors merge |
Description
This adds the ability to process business rule tasks that refer to a called decision (see #8060). Business rule tasks that refer to a task definition instead of a called decision work like they did before, i.e. by creating a job for job workers.
On activation of a business rule task with called decision, the decision specified by
decisionId
is retrieved from the deployed decisions state. If present, the engine uses thedmn
module to evaluate the decision. If that is successful, the decision result's output is stored in a result variable (defined byresultVariable
).The result variable stores the decision result's output according to the following rules:
If any of this fails, Zeebe raises an incident on the business rule task. For example, if the decision was not deployed yet, then an incident is raised. This allows you to:
Related issues
closes #8080
Definition of Done
Not all items need to be done depending on the issue and the pull request.
Code changes:
backport stable/1.3
) to the PR, in case that fails you need to create backports manually.Testing:
Documentation: