Fluent Commerce Logo
Docs

Write your first custom Rule using the Fluent Rules SDK (Hello World)

How-to Guide

Author:

Lesley Dean

Changed on:

1 July 2024

Key Points

  • A step-by-Step guide to creating your first custom Rule to log "Hello World"

Steps

Step arrow right iconOverview

As with many "Hello World" code examples, we will write the simplest implementation of a Rule that produces a log saying "Hello World".The following steps will provide a simple rule that simply produces a `LogAction`.`LogAction` creates an `ORCHESTRATION_AUDIT` Event which can be retrieved using the Event API.

Step arrow right iconPrerequisites


Step arrow right iconStep 1: Create a new Rule class

Workflow Rules are written in Java, and implement the Rules SDK's Rule interface. Rules should be annotated with metadata to describe the Rule internally to the Workflow Engine and the Workflow Builder UI.Rules produce actions. Actions are the outcome of a rule. There are 4 actions supported by the Rules SDK:
  • SendEventAction, which produces events to be executed either inline to the current execution, separately in another workflow, or on a scheduled date and time.
  • MutateAction, which creates or updates entities within the platform.
  • WebhookAction, which sends an Event payload to an external endpoint.
  • LogAction, which creates custom orchestration audit event.

Read more about Rule Actions here.
For this example, we will use the simplest action for our rule - LogAction.
  • Create a new Java class in the `rule` folder of your Plugin Project.

    For example: `src/main/java/com/acme/rule`

  • Name the class `LogHelloWorld.java`
  • Implement the `com.fluentretail.rubix.v2.rule.Rule` interface, and implement the `run` method.

    Note: Make sure to fully qualify the name of the context parameter: `run(C context)`

  • Annotate the Rule class with the `@RuleInfo` annotation.

    You will need to import `com.fluentretail.rubix.rule.meta.RuleInfo`
    • Set the `name` field of the `@RuleInfo` to "LogHelloWorld"
    • Set the `description` field to "Say Hello to the World"

  • Inside the `run` method, produce a `LogAction` by accessing the `ActionFactory` on the incoming `context`:

    `context.action().log();`

    • Set the `message` parameter to "Hello World"
    • Set the `detailedMessage` parameter to "This is a log message that says Hello World"
    • Set the `attributes` parameter to an empty ArrayList: `new ArrayList<>()`


Step arrow right iconStep 2: Create a Unit Test

Unit Tests facilitate quality assurance for your rules.In this example, we will use the Mockito mocking framework together with JUnit to test that the rule successfully produces a LogAction with the "Hello World" message.
  • Create a new JUnit Test class in the `rule` folder under your `src/test/java`.

    For example: `src/test/java/com/acme/rule`

  • Name the class `LogHelloWorldTest.java`
  • Add the following private fields to the Test class:
    • Add a new private final instance of the rule under test:

      `private final LogHelloWorld rule = new LogHelloWorld();`

    • Add an AutoClosable to manage the mock object lifecycle:

      `private AutoCloseable mocks;`

    • Add a Mock for the Rule Context:

      `private Context context;`

    • Add a Mock for the ActionFactory:

      `private ActionFactory actionFactory;`

  • Add a Test `setUp` method, annotated with `@Before`:
    • Initialise the mocks:

      `mocks = MockitoAnnotations.openMocks(this);`

    • Setup the Context ActionFactory stub:

      `when(context.action()).thenReturn(actionFactory);`

  • Add a Test `tearDown` method, annotated with `@After`:
    • Close the mocks: `mocks.close();`

  • Create a new Test method called `run_producesHelloWorldLog`:

    `@Test public void run_producesHelloWorldLog()`

    • Call the Rule's `run` method: `rule.run(context);`
    • Verify that a single `LogAction` was produced with a message equal to "Hello World":

      `verify(actionFactory, times(1)).log(eq("Hello World"), anyString(), anyList());`


Step arrow right iconStep 3: Create an Executor Test

The Rules SDK provides a TestExecutor which simulates running your rule in a workflow engine on your local development environment.The TestExecutor provides access to the TestContext which can be used to assert certain outcomes of your rule.
  • Create another new JUnit Test class in the `rule` folder under your `src/test/java`.

    For example: `src/test/java/com/acme/rule`

  • Name the class `LogHelloWorldExecutorTest.java`
  • Add the following private fields to the Test class:
    • Add a new constant field to hold the Order Id:

      `private static final String ORDER_ID = "123";`

    • Add a new constant field to hold the Order Ref:

      `private static final String ORDER_REF = "HD_123";`

    • Add a private TestExecutor field:

      `private TestExecutor executor;`

    • Add a private Event field:

      `private Event event;`

  • Add a Test `setUp` method, annotated with `@Before`:
    • Use the RubixEntity builder to create an instance of an Entity to simulate an Order record:

      `RubixEntity entity = RubixEntity.builder()`:

      • Set the entityType: `.entityType("ORDER")`
      • Set the id: `.id(ORDER_ID)`
      • Set the ref: `.ref(ORDER_REF)`
      • Set the status: `.status("BOOKED")`
      • Set the type: `.type("HD")`
      • Set the flexType: `.flexType("ORDER::HD")`
      • Set the flexVersion: `.flexVersion(1)`
      • Build the instance: `.build();`

    • Use the Event builder to create an instance of an Event to trigger the Workflow:

      `event = Event.builder()`

      • Set the retailerId: `.retailerId("1")`
      • Set the entityId: `.entityId(entity.getId())`
      • Set the entityRef: `.entityRef(entity.getRef())`
      • Set the entityType: `.entityType(entity.getEntityType())`
      • Set the name: `.name("test-event")`
      • Build the instance: `.build();`

    • Create an instance of a Ruleset, passing in the Event and the Rule name:

      `RuleSet ruleSet = ruleSet(event, ruleInstance(LogHelloWorld.class.getSimpleName()));`

    • Use the TestExecutor builder to create an instance of the TextExecutor:

      `executor = TestExecutor.builder()`

      • Set the rule: `.rule(LogHelloWorld.class)`
      • Set the ruleset: `.ruleset(ruleSet)`
      • Set the entity: `.entity(entity)`
      • Build the instance: `.build();`

  • Create a new Test method called `run_producesLogEvent`:

    `@Test public void run_producesLogEvent()`

    • Call the Executor's `execute` method:

      `TestContext testContext = executor.execute(event);`

    • Assert a single LogAction was produced:

      `assertEquals(1, testContext.countActionsOfType(TestContext.LogAction.class));`


Step arrow right iconWhat's next?

You are now ready to Deploy and Run your rule.