Flows
Flows contain your automation logic in the form of flow scripts. With them, you define what you want to automate and how to do so. This makes them your most important resource in Engine.
The flow script is written in Python. Executions of flows will process the handler
function of the flow script.
Depending on the complexity of your automated process, it can consists of one or many flows. Executions of flows can start executions of other flows and pass inputs and outputs between each other.
You don't need to know Python to use Engine. Engine provides many features that make it possible to run and monitor flows without coding experience. (e.g. Dependency Visualisation, Script Visualisation, Execution Live Monitor)
Creating or modifying flows, on the other hand, requires some knowledge of Python.
Small flows with well-defined scope can be re-used by many automated processes. We recommend breaking automated processes down into several flows which automate specific tasks. In addition to simpler re-use, it also makes it easier to maintain and extend your automated processes.
Working with Flows
You can create flows in the User Interface by pressing the "Create" button and selecting "Flow":
The buttons to create a flow
The new flow is opened and you can directly modify the script.
The flow script editor provided in the Engine user interface comes with built-in Linting, supports Breakpoints and Step Through. If you prefer to use an offline editor of your choice you can also do so. You can synchronise your offline work with Engine using the Git Integration.
When making changes, Engine does not store older versions of your flows. Make sure to have a backup of your flows if you make significant changes. Or even better: use the Git Integration.
Clicking the "Run" button in the flow view will create an execution of the flow and automatically redirect you to the execution view. Read more about executing flows in the documentation on Development and Productive Mode.
Interaction between Flows
Executions of flows can create executions of other flows.
Create a child execution.
import flow_api
def handler(system: flow_api.System, this: flow_api.Execution, inputs: dict):
my_flow = this.flow('my_other_flow_name')
return this.success('all done')
When an execution creates another execution, a parent-child relation is established between them:
The parent execution lists all children in the "Children" tab
The child execution has a button to navigate to the parent
Passing values
Interactions between flows can be leveraged to build big and complex processes from small an maintainable elements.
Let's observe how you can break up a process into parts. Note that we are passing values between flows multiple times, and one flow's result influences what the other flow does.
Flow 1 - Main Process
import flow_api
def handler(system: flow_api.System, this: flow_api.Execution, inputs: dict):
initial_value = 42
new_value = this.flow(
'Calculator',
name='calculate new value',
initial_value=initial_value, # passing initial_value to the flow 'Calculator'
).get('output_value')['result'] # getting 'result' field of the output_value of the flow 'Calculator'
this.flow(
'Messenger',
name='create message with result',
message_value=new_value, # passing new_value to the flow 'Messenger'
)
return this.success('all done')
Flow 2 - Calculator
import flow_api
def handler(system: flow_api.System, this: flow_api.Execution, inputs: dict):
# getting initial_value from the 'initial_value' field of the input_value
initial_value = inputs['initial_value']
# doing some (not very) complex calculations
result = initial_value * 1
# saving result into the 'result' field of the output_value
this.set_output(result=result)
return this.success('all done')
Flow 3 - Messenger
import flow_api
def handler(system: flow_api.System, this: flow_api.Execution, inputs: dict):
# getting message_value from the 'message_value' field of the input_value
message_value = inputs['message_value']
# creating message
this.message(
subject='Message',
message=f' The value is: {message_value}',
)
return this.success('all done')
In the previous example we passed numbers as values. It is also possible to pass Cloudomation Resources.
Flow 1 - Main Process
import flow_api
def handler(system: flow_api.System, this: flow_api.Execution, inputs: dict):
# get a Cloudomation resource
setting = system.setting('my-setting')
# pass the Cloudomation resource to the flow
this.flow(
'Setting Writer',
name='write setting',
setting=setting,
)
return this.success('all done')
Flow 2 - Setting Writer
import flow_api
def handler(system: flow_api.System, this: flow_api.Execution, inputs: dict):
# get the passed Cloudomation resource
setting = inputs['setting']
# write the setting
setting.save(value='my value')
return this.success('all done')
These examples are of course very simple and it might be easier to implement all functionality within a single flow. Breaking up processes becomes relevant with increasing complexity and when thinking about future scalability.
Handling Errors
If a child execution fails, it will raise an exception, causing the parent execution to fail as well. To avoid the parent failing, you can catch the exception:
Create a child execution and handle errors.
import flow_api
def handler(system: flow_api.System, this: flow_api.Execution, inputs: dict):
try:
my_flow = this.flow('my_other_flow_name')
except flow_api.DependencyFailedError:
this.log('my_other_flow_name failed')
else:
this.log('my_other_flow_name succeeded')
return this.success('all done')
Please refer to Exceptions for details about error handling.
Interaction with other Services: Connectors
In order to connect with services outside of the Engine platform, you can create connection executions from your flow.
Creating a connection execution from your flow is simple:
import flow_api
def handler(system: flow_api.System, this: flow_api.Execution, inputs: dict):
my_connection = this.connect(
connector_type='GIT',
command='get',
repository_url='https://example.com/path/to/repo.git',
)
# read the outputs of the connection and log them
this.log(my_connection.get('output_value'))
return this.success('all done')