The run Method
Essential knowledge
Author:
Fluent Commerce
Changed on:
30 June 2024
Overview
The`run` method is called by the Workflow Engine when executing each Rule in a Ruleset triggered by an Orchestration Event.Key points
- Rules are the smallest building block for logic, it is important to remember that this method should be implemented to perform 1 simple task
- You may not need all of these steps within your Rule. Some rules produce an action without a condition. Some rules do not need to query additional data. Make sure your Rule is as lean as possible, and simple to read.
- The Rubix Plugin SDK provides a useful Util class to facilitate validation: RuleUtils
- You can retrieve the data by using Context / Entity
- Typically, a Rule will produce an output action.
Since Rules are the smallest building block for logic, it is important to remember that this method should be implemented to perform 1 simple task. Rarely should your Rule code ever be more than a few lines of code.Rules are meant to be composable, and this means they should be self-contained, and not depend on any other Rule.The To learn more, see Working with GraphQL.To learn more, see Working with GraphQL.
`run` method receives a `Context` instance, which provides all the necessary contextual inputs to the Rule, as well as access to the Fluent API Client, and the Workflow Engine ActionFactory.Rules are singletons, meaning the same single instance of the Rule is processing multiple threads. To this point, make sure you do not declare any runtime-specific values in the Rule Class properties, as these will not be threadsafe.To implement the Rule, the following steps are usually followed within the `run` method:- Validation - validate the incoming parameters and event attributes are present
- Retrieve additional Data - if the data required to evaluate or perform the Rule Action is not already available on the Context Entity or Event, retrieve it as efficiently as possible.
- Conditional Logic - if the Rule Action is dependent on an evaluation, implement the simple conditional logic
- Build the resulting Action - build the Action data
- Produce the Action - call the
`context.action()`ActionFactory to produce the Action
Validation
This stage typically involves validating all the required inputs to the Rule are both present, and valid. For example, if you have declared a number of Parameters or Event Attributes for use within the Rule logic, you should validate these first.The Rubix Plugin SDK provides a useful Util class to facilitate validation:
`RuleUtils`For example, this snippet validates that the Order Id parameter exists:1// imports & Rule Info annotation...
2@ParamInteger(name = "MyInt", description = "An Integer value for the Rule")
3public class MyCustomRule implements Rule {
4
5 // local fields...
6
7 public void run(C context) {
8
9 // Validation:
10 RuleUtils.validateRuleProps(context, "MyInt");
11
12 // continuing logic...
13
14 }
15}If any required validation fails, your Rule should exit immediately. To do this, you have 2 options:
- Use a
`return`statement to immediately exist the Rule but continue processing the Ruleset. - Throw an Exception, and stop processing the Ruleset.
`RuleUtils` has a mix of both boolean response and thrown exceptions.Retrieving Data
Once you've completed the Validation phase, you may wish to retrieve any additional data you may need to execute your Rule logic.The
`Context` does already contain an Entity (`context.getEntity()`), however, this is a subset of the Entity itself.It only contains the primary information (the common generic fields) of an Orchestrateable Entity:- EntityType - e.g: ORDER, FULFILMENT, etc.
- EntityId
- EntityRef
- Type - e.g: HD, CC, etc.
- Status
- WorkflowInfo - e.g: the type and version of the application workflow.
`Context`.For example, if you are writing a rule that needs to operate on a field or attribute of the Event Entity, you can retrieve this via a GraphQL query.1// imports & Rule Info annotation...
2@ParamInteger(name = "MyInt", description = "An Integer value for the Rule")
3public class MyCustomRule implements Rule {
4
5 // local fields...
6
7 public void run(C context) {
8
9 // ...preceding logic
10
11 // Retrieve Data:
12 String orderId = context.getEntity().getId();
13
14 GetOrderByIdQuery query = GetOrderByIdQuery.builder().id(orderId).build();
15 GetOrderByIdQuery.Data data = (GetOrderByIdQuery.Data) context.api().query(query);
16
17 RuleUtils.validateQueryResult(context, data, this.getClass());
18
19 // continuing logic...
20
21 }
22}Conditional Logic
The next stage within the`run` method is to perform any conditional logic required prior to building and producing an action.Let's say that for example, you only wished to continue the action of this rule if the
`totalPrice` of the Order is greater than a `threshold` parameter with a value of $100.1// imports & annotations...
2public class MyCustomRule implements Rule {
3
4 // local fields...
5
6 public void run(C context) {
7
8 // preceding logic...
9
10 // Simple Logic:
11 if (data.orderById().totalPrice() <= threshold) {
12 return;
13 }
14
15 // continuing logic...
16
17 }
18}Build the Action
Typically, a Rule will produce an output action. See Rule Actions here.Some typical examples of output actions include:- Mutation - e.g: To update or save some new or changed data to the backend via a GraphQL API Mutation.
- SendEvent - e.g: To send an event to trigger another workflow.
- SendWebhook - e.g: To send an even to an external system.
1// imports & annotations...
2public class MyCustomRule implements Rule {
3
4 public void run(C context) {
5
6 // preceding logic...
7
8 // Prepare for Action:
9 AddAttributeToOrderMutation addAttributeToOrderMutation = AddAttributeToOrderMutation.builder()
10 .orderId(orderId)
11 .attributeName(IS_HIGH_VALUE_ORDER_ATTRIBUTE_NAME)
12 .attributeType(Attribute.Type.BOOLEAN.getValueClass().getSimpleName().toUpperCase())
13 .attributeValue(Boolean.TRUE)
14 .build();
15
16 // continuing logic...
17
18 }
19}Produce the Action
The final stage of the`run` method is to produce the output action.1// imports & annotations...
2public class MyCustomRule implements Rule {
3
4 // local fields...
5
6 public void run(C context) {
7
8 // preceding logic...
9
10 // Produce Action:
11 context.action().mutation(addAttributeToOrderMutation);
12
13 }
14}