Content
UX/UI Builder
Builder
- Create and edit form templates
- 42 configurable options
- 11 action methods
- 31 languages
- Custom controls
- XML and JSON data
Render
- Render form templates created with formBuilder
- Export userData for saving or re-use in templates
- 5 configurable options
- 5 action methods
Flowz – WorkFlow Designer
Using the Designer
Deleting Connections
To delete an existing connection, hover your mouse over it and then RIGHT-click your mouse.
Connecting to Existing Activities
To connect an outcome to an existing activity:
- Press and hold the SHIFT button while left-clicking the outcome from which you wish to connect.
- Left-click on the target activity (no need to keep pressing SHIFT)
Workflow Concepts
In order to work effectively with Flowz, it’s important to understand its terminology. Below is a list of words that represent important concepts used in Flowz.
Workflow
A workflow consists of a series of steps called activities that are connected to one another. A workflow maintains all sorts of information, such as the following:
- Which activity is currently executing.
- What variables are set.
- What activities are blocking further execution.
Once an activity is done executing, the workflow checks its outcome and if there’s another activity connected to it. If so, that activity is scheduled for execution.
This goes on until there are either no more activities to execute, or an activity is encountered that instructs the workflow runner to suspend the workflow.
Activity
An activity is an atomic building block that represents a single executable step on the workflow. At a bare minimum, an activity implements the OnExecute
method, which contains the code to execute.
Starting Activity
An activity which is the starting point of the workflow and does not have any inbound connections. They are the entry points to the workflow.
Blocking Activity
When an activity executes, it returns an activity execution result, which is somewhat analogous to an MVC/API ActionResult. There are various possible results that can be returned, but the most commonly used ones are Done
, Outcomes
and Suspend
.
When Suspend
is returned (as is typically the case with blocking activities), the workflow will enter the Suspended state and the activity will be registered as a blocking activity.
Suspended Workflow
Suspended workflows are blocked by one or more blocking activities. The only way to resume such a workflow is to trigger it with the name of one of the blocking activities.
Connection
A connection represents a connection between two activities. This is how the workflow runner knows what activities to execute next. A connection between two activities holds 3 pieces of information:
- The source activity ID.
- The source outcome name (e.g.
Done
). - The target activity ID.
For each possible outcome of a given activity, a connection can be established from that outcome to another activity.
For example, let’s say we have a workflow with three activities called Activity A
, Activity B
and Activity C
. Activity A
has 2 outcomes called Done
and Failed
, and we wish to connect the Done
outcome to Activity B
and Failed
to Activity C
.
This means we need the following two connections:
Connection 1
- Source:
Activity A
- Outcome:
Done
- Destination:
Activity B
Connection 2
- Source:
Activity A
- Outcome:
Failed
- Destination:
Activity C
Visually, this would look like this:
Long Running Workflows
A long-running workflow is a workflow that doesn’t run from start to end in one go. Instead, it might have one or more blocking activities that will instruct the workflow engine to suspend the workflow until it receives the appropriate stimulus to resume execution.
Short Running Workflows
A short-running workflow is a workflow that, in contrast to long-running workflows, does run from start to end in one go.
Burst of Execution
A burst of execution refers to the execution of a sequence of activities one after another until either one of the following occurs:
- No more activities were scheduled (the end of the workflow was reached), or
- A blocking activity was encountered.
Expressions
JavaScript Expressions
The following JavaScript expressions are supported:
Variables
Workflow Variables
Any workflow variable can be accessed directly as if they were a global variable.
For example, if the SetVariable
activity sets a variable called FirstName
to 'Luke'
, it can be accessed as follows:
`Hello ${FirstName}`
Value stored in the variable:
"Hello Luke"
This also works when setting variables using the setVariable function. Because ultimately, both the SetVariable
activity and setVariable
function use the same API under the cover to set a workflow variable.
Activity Output
Any activity might provide some output, which is accessible from any other activity using workflow expressions. To access an activity’s output property called e.g. Output
using a JavaScript expression, you can do so by specifying activities
, then the activity name followed by .Output()
. Notice that you must invoke the property as if it were a method. This is due to the way workflow storage providers work, which are potentially asynchronous in nature (such as Azure Blob Storage).
For example, if you have an activity named MyActivity
, you can access its output as follows: activities.MyActivity.Output()
.
If the output is an object, you can access its properties too. For instance, the HTTP Endpoint activity returns the HTTP request as its output which is of type HttpRequestModel. When you name this activity "MyHttpEndpoint"
, you can access the HTTP request body like this:
activities.MyHttpEndpoint.Output().Body
If you happened to post a JSON document to your HTTP endpoint that looks like this:
{
"SomeDocument": {
"Title": "About Flowz"
}
}
Then you can access the "Title"
field like this:
activities.MyHttpEndpoint.Output().Body.SomeDocument.Title
If your activity is a direct child of an HTTP Endpoint activity, you can access its output directly via the
input
variable, which will be an instance ofHttpRequestModel
.
input
Contains the input value that was received as output from the previously executed activity, if any.
input: object?
workflowInstanceId
Contains the workflow instance ID of the currently executing workflow.
workflowInstanceId: string
workflowDefinitionId
Contains the workflow definition ID of the currently executing workflow.
workflowDefinitionId: string
workflowDefinitionVersion
Contains the workflow definition version of the currently executing workflow.
workflowDefinitionVersion: number
correlationId
Contains the correlation ID of the currently executing workflow.
correlationId: string?
currentCulture
Contains the current culture.
currentCulture: CultureInfo
Currently, this value is always set to
CultureInfo.InvariantCulture
.
workflowContext
Contains the workflow context (if any) of the currently executing workflow.
workflowContext: object?
currentCulture
Returns the current culture.
currentCulture: CultureInfo
Common Functions
guid
Generates a new GUID value and returns its string representation.
guid(): string
This function is a thin wrapper around the following .NET code:
Guid.NewGuid().ToString()
.
parseGuid
Parses a string into a GUID value.
parseGuid(value: string): Guid
This function is a thin wrapper around the following .NET code:
Guid.Parse(value)
.
setVariable
Sets a workflow variable to the specified value.
setVariable(name: string, value: object): void
This function is a thin wrapper around the following .NET code:
activityContext.SetVariable(name, value)
.
getVariable
Returns a workflow variable with the specified name.
getVariable(name: string): object
Instead of using
getVariable(name: string)
, you can access workflow variables directly as described above in the Workflow Variables section.
This function is a thin wrapper around the following .NET code:
activityContext.GetVariable(name)
.
getConfig
Provides access to a .NET configuration value.
getConfig(name: string): string
As an example, let’s say you have the following JSON in appsettings.json
:
{
"Flowz": {
"Smtp": {
"Host": "localhost",
"Port": 2525
}
}
}
You can access the configured Port
value using the following expression:
getConfig("Flowz:Smtp:Port") // returns '2525'
This function is a thin wrapper around the following .NET code:
configuration.GetSection(name).Value
whereconfiguration
is an instance ofIConfiguration
.
isNullOrWhiteSpace
Returns true
if the specified string is null, empty or consists of white space only, false
otherwise.
isNullOrWhiteSpace(value: string): boolean
This function is a thin wrapper around the following .NET code:
string.IsNullOrWhiteSpace(value)
.
isNullOrEmpty
Returns true
if the specified string is null or empty, false
otherwise.
isNullOrEmpty(value: string): boolean
This function is a thin wrapper around the following .NET code:
string.IsNullOrEmpty(value)
.
Workflow Functions
getWorkflowDefinitionIdByName
Returns the ID of the specified workflow by name. This is useful when for instance you are using the RunWorkflow
activity, which requires the ID of the workflow definition to run.
getWorkflowDefinitionIdByName(name: string): string?
getWorkflowDefinitionIdByTag
Returns the ID of the specified workflow by tag. This is useful when for instance you are using the RunWorkflow
activity, which requires the ID of the workflow definition to run.
getWorkflowDefinitionIdByTag(tag: string): string?
HTTP Functions
queryString
Returns the value of the specified query string parameter.
queryString(name: string): string
absoluteUrl
Converts the specified relative path into a fully-qualified absolute URL.
absoluteUrl(path: string): string
signalUrl
Generates a fully-qualified absolute signal URL that will trigger the workflow instance from which this function is invoked.
signalUrl(signal: string): string
Date/Time Functions
instantFromDateTimeUtc
Returns a new Instant
object from the specified DateTime
value.
Make sure that the
DateTime
value’sKind
property isDateTimeKind.Utc
.
currentInstant
Returns the current date/time value in the form of a NodaTime’s Instant
object.
currentInstant(): Instant
currentYear
Returns the current year.
currentYear(): number
startOfMonth
Returns the start of the month of the specified instant
. If no instant
is specified, the current instant is used.
startOfMonth(instant: Instant?): LocalDate;
endOfMonth(instant: Instant?)
Returns the end of the month of the specified instant
. If no instant
is specified, the current instant is used.
endOfMonth(instant: Instant?): LocalDate;
startOfPreviousMonth
Returns the start of the previous month of the specified instant
. If no instant
is specified, the current instant is used.
startOfPreviousMonth(instant: Instant?): LocalDate;
plus
Adds the specified Duration
to the specified Instant
and returns the result.
plus(instant: Instant, duration: Duration): Instant
minus
Subtracts the specified Duration
from the specified Instant
and returns the result.
minus(instant: Instant, duration: Duration): Instant
durationFromDays
Returns a duration constructed from the specified number of days.
durationFromDays(days: number): Duration
formatInstant
Formats the specified Instant
using the specified format string
and CultureInfo
. If no culture info is provided, CultureInfo.InvariantCulture
is used.
formatInstant(instant: Instant, format: string, cultureInfo: CultureInfo?): string
localDateFromInstant
Returns the LocalDate
portion of the specified Instant
.
localDateFromInstant(instant: Instant): LocalDate
instantFromLocalDate
Creates an Instant
from the specified LocalDate
value (start of date).
instantFromLocalDate(localDate: LocalDate): Instant
Liquid Expressions
Accessing Custom Types
Liquid is a secure template language which will only allow a predefined set of members to be accessed, and where model members can’t be changed. Property are added to the TemplateOptions.MemberAccessStrategy property. This options object can be reused every time a template is rendered.
Alternatively, the MemberAccessStrategy
can be assigned an instance of UnsafeMemberAccessStrategy
which will allow any property to be accessed.
Allow-listing a specific type
To configure the liquid engine, implement INotificationHandler<EvaluatingLiquidExpression>
. This notification handler will be invoked every time Flowz is about to evaluate a liquid expression.
The following example demonstrates how to allow access to a custom type called MyType
from liquid expressions:
public class ConfigureLiquidEngine : INotificationHandler<EvaluatingLiquidExpression>
{
public Task Handle(EvaluatingLiquidExpression notification, CancellationToken cancellationToken)
{
notification.TemplateContext.Options.MemberAccessStrategy.Register<Mytype>();
}
}
Make sure to register your handler with DI:
services.AddNotificationHandler<EvaluatingLiquidExpression, ConfigureLiquidEngine>();
For more information about stuff you can do with Fluid, the engine used by Flowz for liquid handling, checkout their documentation.
Liquid Expressions
The following Liquid expressions are supported:
Common Variables
Workflow Variables
Use the following syntax to access a workflow variable:
{{ Variables.NameOfVariable }}
For example, given a workflow variable called FirstName
with a value of "Alice"
, the expressionHello {{ Variables.FirstName }}
will result in Hello Alice
.
Input
Input values can be accessed using the following syntax:
{{ Input }}
Activity Output
To access a named activity’s output, use the following syntax:
{{ Activities.SomeActivityName.Output }}
CorrelationId
Returns the correlation ID (if any) of the currently executing workflow.
{{ CorrelationId }}
WorkflowInstanceId
Returns the workflow instance ID of the currently executing workflow.
{{ WorkflowInstanceId }}
WorkflowDefinitionId
Returns the workflow definition ID of the currently executing workflow.
{{ WorkflowDefinitionId }}
WorkflowDefinitionVersion
Returns the workflow definition version of the currently executing workflow.
{{ WorkflowDefinitionVersion }}
Configuration
Provides access to a .NET configuration value.
{{ Configuration.SomeSection }}
As an example, let’s say you have the following JSON in appsettings.json
:
{
"Flowz": {
"Smtp": {
"Host": "localhost",
"Port": 2525
}
}
}
You can access the configured Port
value using the following expression:
{{ Configuration.Flowz.Smtp.Port }}
Common Filters
json
A liquid filter that renders the specified value as a JSON string.
{{ Input | json }}
Example output:
{
"SomeDocument": {
"Title": "About Flowz"
}
}
base64
A liquid filter that renders the specified value as a bas64 representation. The value is first converted to a string. If the value is an object, array, dictionary or datetime, it is first serialized using JsonConvert.SerializeObject
before being encoded as base64.
{{ "Some string value" | base64 }}
Example output:
U29tZSBzdHJpbmcgdmFsdWU=
Workflow Filters
workflow_definition_id
Translates the specified workflow name or workflow tag into a workflow ID. This is useful for activities such as RunWorkflow
which require a workflow ID to run.
Usage:
{{ "SomeWorkflowName" | workflow_definition_id }}
{{ "SomeWorkflowTag" | workflow_definition_id: tag }}
HTTP Variables
Request
Provides access to various properties on the current HTTP Request object:
{{ Request.QueryString }}
{{ Request.ContentType }}
{{ Request.ContentLength }}
{{ Request.Form }}
{{ Request.Protocol }}
{{ Request.Path }}
{{ Request.PathBase }}
{{ Request.Host }}
{{ Request.IsHttps }}
{{ Request.Scheme }}
{{ Request.Method }}
HTTP Filters
signal_url
A liquid filter that generates a fully-qualified absolute signal URL that will trigger the workflow instance from which this function is invoked.
Example:
{{ "MySignal" | signal_url }}
Example output:
https://localhost:5001/signals/trigger/{some base64 token}