Skip to content

Overview

Concepts and Principles

Development

Overview

IDEs

API Explorer

Releases

Release Notes

TORO Integrate

Coder Studio

Coder Cloud

Bug Reports

Search

Building a Flux Service

Flux files are distinguished by the .flux file extension. A Flux file's content is written in JSON format and defines the service's anatomy and behavior. Similar to other services, Flux files reside in a package's code folder. In this page, we will guide you through the process of creating a simple workflow to get you started with Flux.

Creating a New Flux Service

  1. Under your target package, right click on the code directory or any subdirectory underneath it where you'd like your workflow to reside.
  2. From the context menu, select New > Flux Service.
  3. Enter the name of the Flux Service in the dialog or modal and hit Create.

Creating a Flux Service

Creating a Flux Service

Once you've successfully created a Flux Service, the new file will be opened automatically and a blank canvas will be presented.

Creating States and Defining Transitions

  1. Right click any blank spot on the canvas and click Add State from the appearing context menu. If you're using Coder Cloud and you have no prior states existing in your Flux Service, click on the Add New State button.
  2. Provide a name for the state you want to add and click on Save State once done.
  3. Repeat steps 1 and 2 to add other states.
  4. To create a transition, click on the edge of the source state and drag the mouse towards the target state and then release the mouse.
  5. Coder will ask you to provide an event the transition will correspond to. In this case, you can leave it blank if you want.

Creating states and defining transitions

Creating states and defining transitions

Default starting state

By default, the first state created will automatically be the starting state. It is possible however to change which state starts the workflow by right clicking the state and click Set Start State.

Setting the Action for a State

A workflow with transitions between action-less states won't do much; it fact, it won't do anything at all. Actions tell states what to do and by default, states are configured to map via Map Steps. Map Steps in Flux work the same way they do in Gloop Services – their main purpose is to transform data, and in this case, data between states. Aside from mapping, you can have a state call another service instead. In Flux, you can invoke any type of service that Gloop Services can invoke because underneath the covers, the Flux Engine uses Gloop.

Now, let's create a state invoking a Groovy service. For this example, we'll set them to call Groovy methods that print something to the terminal.

  1. Select the state whose action you want to (re-)specify. You'll know the state has been selected when its circle's color has changed.
  2. Press . to trigger content assist and by doing so, you will be given a list of available services that you can call from the state.
  3. Select the service you want to invoke. For this example, we'll select String LoggerMethods.println( String ).
  4. Under the Mapper view, you'll see that you have the ability to set the values of the arguments you will be feeding to the selected service. Do this if you need to.

    For this example, we'll set the value of message by double clicking the parameter shown in the view. We'll do this for our first and second states.

Setting a Flux state's action

Setting a Flux state's action

Creating a state with action and transition

To quickly create a state with an action and transition, drag a transition to a blank part of the canvas. This will show dialogs for creating them and for setting the action.

Creating state with action and transition

Creating state with action and transition

And just like that, you already have a workflow that transitions from one step to another executing an action at each step. Albeit simple, this example demonstrates how easy it is to get started on creating workflows. Of course, you'd like to create something more powerful. Let's continue by being able to set the message through an input.

Setting Inputs for Your Flux

  1. Under the Flux Input/Output view's Input panel, click on the add button and select the type of input you want to add to your Flux. In this case, click on Add String because we want a string input.
  2. Set the input name to anything you like. For this example, we'll use greeting.
  3. To use the input in one of your Flux states, select the target state and under the Mapper view, create a line from the input to the target state service parameter. In our case, we'll map greeting to message.

Here's a GIF showing you the procedure:

Setting Flux service inputs

Setting Flux service inputs

To test, run the workflow. This time, you will be asked to provide the inputs you added to your service. Type anything you like and then click Run.

Running Flux with inputs

Running Flux with inputs

With that, you could provide data to kick off your workflow. These inputs could be anything from simple strings to Gloop Models. The data here becomes available to all states. But most times however, your states will be doing something more useful than just printing out a message to the console. Each state would have inputs to work with and outputs to pass around and make decisions from.

Passing Data from One State to Another

Of course, your Flux actions are not limited to services that just consume data, you could take that input and process it to pass around to other states. For this example, let's say you have two states. The first state is set to the action String StringMethods.upperCase(String), the second state is set to call String LoggerMethods.println(String), and there is a blank transition between them. Let us also say, that there is a single Flux input called input that is of type String.

For your first state:

  1. Click on your first state. Once the state is selected, Flux Input/Output will become State Input/Output.
  2. Under Output, right click and select Add > Add String and provide a name. In this case, we will be using upperCased.
  3. In the Mapper, map input to the action's input.
  4. As for the output, map the action's output to upperCased.

Passing data between states - first state

Passing data between states - first state

For your second state:

  1. Under the State Input/Output view, create an input of type String with name toPrint.
  2. Going back to the Mapper, map toPrint to message.
  3. Finally, click on the transition arrow and in the Mapper, map upperCased to toPrint.

Passing data between states - second state

Passing data between states - second state

What we've done here is send data between two states using a transition. Data becomes available via a state's inputs and outputs and the transition acts as the bridge for data between states. This way, each state is isolated. They perform their own action without having to rely on other states' implementations. Transitions act as adapters between states.

Making Decisions

So far, we've only been creating workflows that have two states and don't diverge in any way. Flux has the capability to decide which transition to take based on events. For this example, we'll be making a workflow of a payment system. A starting state simulates a checkout from a client. Another two states are for handling the chosen payment method. One is for retrieving card details while the other is for displaying bank information. How do we invoke a state based on a client's payment method? To do this, we create a transition for each payment method. We'll use a Flux input called payment that defines the chosen payment method and map it to $fluxEvent.

  1. Create a transition from the Checkout state to the Get Card Details state. When prompted for the event, type in card.
  2. Create another transition from the Checkout state to the Display Bank Information state. When prompted for the event, type in bank. Notice that checkout state shows a validation error. This is because we have defined the events of the transitions and $fluxEvent is not set. We'll fix that next.
  3. Select the Checkout state and go to the Mapper. Map payment from the Flux input to the $fluxEvent variable. At this point, the validation error should be gone.
  4. Provide an action for retrieving card details and displaying bank information state. For this example, we'll just log the process.

Creating a multi-transition Flux Service

Creating a multi-transition Flux Service

To test, run the Flux Service and set the value of payment to card. Notice that the card details state's action is invoked. Rerun the Flux Service and this time set the value of payment to bank. This time, the bank detail state's action is invoked instead.

Running multi-transition Flux states

Running multi-transition Flux states

And just like that, you've told Flux how to make a decision. $fluxEvent is the variable that Flux checks and compares against all available transitions to decide how to proceed. All outcomes should be accounted for. This allows you to assign a transition that catches all other events.

Delaying Transitions

Transitions can be delayed for a particular amount of time or even wait for a certain date. This can be configured on the Wait property of a transition. The syntax for its value is duration|date:<expression>. duration is in seconds while date can be a GloopDate or a timestamp. <expression> can be a Gloovy expression and can use any transition inputs.

Examples:

  • duration:5 - waits 5 seconds before invoking the transition.
  • date:1735660800000 - waits for January 1, 2025.
  • date:${myDate} - waits for myDate, a transition input of type GloopDate.

Looping Back

Loop Back Transition

You might find that there are times when you'd like to try again. If a connection failed you might want to try a couple of times before giving up entirely. Flux allows you to do this by creating transition that loops back to its state. To do that, simply click and drag from the edge of a state to create a transition and point it back to the originating state. Together with transition delay, you can create a state that retries its action after certain amount of time.

Handling Undefined Events and Exceptions

If you try to give an event when there is no transition, Flux will throw an io.toro.fluxengine.exception.runtime.UndefinedEventException. If you'd like to respond to events that are not declared, you could create a transition with the $else event. This is useful when you have a dynamic value for $fluxEvent.

Flux also allows the catching of exceptions that may occur when a state's action is invoked. To do this, create a transition with $exception. Having this event provides the transition with an additional input called $fluxException, a GloopModel of reference io.toro.fluxengine.exception.runtime.FluxRuntimeException, that provides the details of the thrown exception. In particular, these are its properties:

  1. message - a String that contains the exception message
  2. type - a String that contains the simple class name of the exception
  3. className - a String that contains the class name of the exception
  4. realException - an Object that contains the actual exception
  5. fluxStackTrace - a GloopModel array of state and transition calls

fluxStackTrace contains an array of stacktrace elements that describes which states and transitions were invoked from the start of a Flux execution until the exception was thrown. A Flux stacktrace element consists of the following properties:

  1. fluxName - name of the Flux Service
  2. stateName - name of the invoked state
  3. transition - event of the transition, null if the stacktrace element pertains to a state invocation
  4. stateIndex - index of the state on the Flux definition

Reserved Variables

Flux uses predefined variables for different purposes. Some variables are used by the engine while some are populated for you for convenience purposes. Do note of these variables because they might come in handy in some cases. Also make sure that your variable names don't conflict with them.

Variable Name Data Type Description
$fluxEvent String Used by states to know which transition to invoke next
$else String Used as a transition event to handle any undefined events
$exception String Used as a transition event to handle any exceptions thrown by a state
$fluxException GloopModel Used to identify what exception occurred on a state, available as transition input only if $exception is used
$fluxContextId Long ID of the currently running service, can be used on any state
$fluxName String Name of the currently running service, can be used on any state
$fluxStateName String Name of the current state, can be used on any state
$triggerFluxEvent String Event that was used to trigger the Flux service, available as Flux input