User Guide
Synapse-Workflow-Examples User Guide
Synapse-Workflow-Examples provides examples and documentation to assist in building Optic Workflows.
To try out the example Workflows, ensure the Synapse-Workflow-Examples Power-Up is installed
and navigate to the Workflows tool in Optic. From there, expand the synapse-workflow-examples
package to view the Workflows.
To view the YAML for each example, use the Workflow Example Code
Workflow. It contains a datatable
Element listing
the examples and will display the example Workflow YAML in a textarea
Element.
It is highly recommended to validate Workflow YAML with the provided JSON Schemas in both development (in editor) and CI/CD test suites.
Optic Workflows
Workflows can be used to build dynamic UX from provided Elements and Storm, and are defined using a declarative API that is native to Optic. The defined UX is then integrated directly into Optic via Storm packages with one or more Workflows being defined in the package definition.
A Workflow primarily consists of a set of Elements that display and allow interaction with data in a variety of ways. The Elements defined in a Workflow will specify a type that will dictate its display and capabilities. Some Element types include a configurable form, two types of tables, a node viewer, and a dynamic syntax highlighting textarea. Most types of Elements can execute Actions in response to user input to propagate and display dynamic data, allowing Elements to share data in a reactive way. Other types of Actions include updating internal variables, opening other Workflows, and running Storm queries to modify and pull in additional data.
Elements typically execute Actions in response to Events or user input. An Element can subscribe to Events from other Elements in the Workflow. Once a subscription has been established, the Element can define a set of Actions that will be executed in response to a particular Event from the source Element. There are several types of Events, with more likely to be added. Some Events carry data that can be accessed while executing Actions (onvars and onnodes).
Elements additionally maintain a set of variables (vars) that can be manipulated using Actions and passed to and from Storm queries. These vars are
kept in the Browser and are accessible by the Element for the duration of the Workflow session. This allows for dynamic behavior based on the vars
that may come from user input, other Elements, Storm queries, or any combination of the three. Elements can reference these vars for get and set
operations in their opts and Actions they execute. They can be referenced simply by name myvar
, or using a $
for clarity: $myvar
.
Nested dict and array values can be referenced using dotted syntax e.g. $mydict.somekey
and $myarray.0
.
Events and Actions together a flexible framework to allow Elements to react to user input and changes in other Elements, all the way through to running Storm with user provided input.
Workflows can also be built to work together to allow opening other Workflows in a modal, navigating to other Workflows, and allow opening a Workflow in the Research tool via special Node actions.
When designing a Workflow one should consider the following:
Elements - What to display and how to display it.
Layout - How will the Elements be visually laid out in the grid.
Events - Which Events are suitable for distributing the necessary data between the selected Elements.
Actions - What types of Actions will executed by each element when receiving a particular Event or User input.
Coordination - What other Workflows may be opened as a modal or navigated to. What other Workflows may want to be able to open or navigate to this one.
Workflow Events
A Workflow Event is a message that will be broadcast through the Workflow, and sometimes into other Workflows. Elements can Subscribe to Events from other Elements to receive them and then define their Actions to be executed based on (new) data contained in the Event(s).
There are currently two types of Events each with a specific use.
onvars
This Event can be fired by other Elements or External Workflows. Its purpose is to propagate the source Elements
vars to any other subscribed Elements. Any Element that has a subscription to onvars Events from a particular source Element will automatically get the inbound vars set on itself. When reacting to an onvars
Event, Elements will often rerender themselves with new data, either directly from the Events vars or after making additional Storm queries to gather more data or Nodes to be rendered.
onnodes
This Event can fired by other Elements to pass one or more Nodes between Elements.
When executing Actions in response to this Event, the nodes will be available for both updatevars
and nodes
operations and in Storm queries as inbound nodes via the idens
query opt.
Workflow Actions
An Action is an operation executed by an Element in response to either an Event or user input. There are many types of Actions that can have a wide variety of effects on the UX of the Workflow.
eventfire
Fire an Event into the Workflow, allowing other subscribed Elements to react to the provided data.
nodesfire
Fire an onnodes
Event into the Workflow.
Typically using the selected Nodes in an Element that supports Node selection e.g stormtable
using the selected
opt or the Nodes in an onnodes
Event being handled nodes using propagate
.
openmodal
Open another Workflow in a modal. There are additional opts to provide data to the target Workflow, and to make temporary subscriptions to its Events so that the source Element can update as desired.
closemodal
Close a specific modal. Mostly useful in the Actions defined in a temporary subscription so that the source Workflow has control over when the modal will be closed.
researchquery
Provide a query or a var to get a query from, and navigate the user to the Research tool to execute it.
storm
Run a Storm query and render the yielded Nodes according to the Element type. The executing Element’s vars will be passed in for the query to use. This Action has a variety of opts that can change its behavior.
One of either query
or queryvar
must be set to provide the query to execute. All other opts are optional.
query
The Storm query that should be executed. Nodes yielded from the query will be consumed by the executing Element to be rendered according to the Element type.
queryvar
Resolve the Storm query to execute from a var on the Element.
inboundnodes
Allows supplying inbound nodes to the query from a var when not handling an
onnodes
Event. This is useful when it is necessary to inbound nodes to a query that is not in direct reaction to anonnodes
event. Nodes are inbounded to the query in the same way as handling anonnodes
Event via the Storm query optidens
.Note that this opt will be ignored when the Action is handling an
onnodes
Event.Example:
# handle an onnodes event and stash the nodes in the $stashednodes var. events: onnodes: - type: updatevars opts: operations: - type: nodes name: $stashednodes path: $nodes ... the rest of your Workflow ... # later (e.g. user button click or other interaction) run a Storm query with the nodes from $stashednodes inbound. type: storm opts: # pivot from the inbound nodes to inet:ipv4 nodes query: -> inet:ipv4 inboundnodes: $stashednodes
render
Can be set to
false
to cause astorm
Action rendering Element to not render the results of the Storm query.
spinner
Configure any spinners that should reflect the running status of the query.
Can be set to
true
to bind to the spinner on the executing Element.Can be set to the iden of another Element to bind to the spinner on that Element.
Can be set to a dict with
element
andbutton
values to spin a button on a particular Element (the Element can be itself as well).
button
requiresidx
which is the index of the button in the Element. Element titlebar buttons are first, followed by buttons created by the Element type. Can also provide temporaryspinningtext
to update the text of the button while the spinner is active.Example:
spinner: element: <elementiden>, button: idx: 0 spinningtext: 'Updating...'
feed
Configure other Elements to feed messages coming out of the Storm query. Messages fed to other Elements can optionally be limited by type. This allows other Elements to be in control of the
storm
Action, while still feeding relevant messages to other Elements in the Workflow. e.g. aquerybar
executing a query that yields nodes would want to feed those nodes into astormtable
or some other Element, while additionally feeding messages to other suitable elements.Example:
feed: ex-stormtable: [node] ex-other-selective: [node, warn, err] ex-other-all: true
console
Optionally configure Storm messages to be output to either a
stormconsole
Element in the Workflow or the ‘global’ Console tool Console.Workflow Element Example:
console: iden: ex-stormconsole clear: true # optionally output only specified message types # mesgs: [print, warn, err]Global Console Example:
console: global: true # optionally output only specified message types # mesgs: [print, warn, err]
queryopts
Extra query opts to be used when executing the Storm query. These opts are documented in the Synapse Storm Opts documentation. Note:
view
,idens
, andvars
opts are not allowed.
callstorm
Run a query using callStorm and handle the return value according to the Element type. The executing Element’s vars will be passed in for the query to use.
query
Required Storm query that will be executed. The query must
return(<something>)
. The value the query should return is dependent on the executing Element.
inboundnodes
Allows supplying inbound nodes to the query from a var when not handling an
onnodes
Event. This is useful when it is necessary to inbound nodes to a query that is not in direct reaction to anonnodes
event. Nodes are inbounded to the query in the same way as handling anonnodes
Event via the Storm query optidens
.Note that this opt will be ignored when the Action is handling an
onnodes
Event.Example:
# handle an onnodes event and stash the nodes in the $stashednodes var. events: onnodes: - type: updatevars opts: operations: - type: nodes name: $stashednodes path: $nodes ... the rest of your Workflow ... # later (e.g. user button click or other interaction) run a callStorm query with the nodes from $stashednodes inbound. type: callstorm opts: inboundnodes: $stashednodes # do some operation on the nodes query: | [ :someprop=$newvalue ] return($lib.true)
loadnodes
Render the Nodes present in an onnodes
Event payload according to the Element type.
selectnodes
Select Nodes that have already been rendered in the Element. Note this is only useful in Elements that support Node selection, e.g. tables.
getvars
Run a query using callStorm that returns a $lib.dict of vars that will be set onto the Element.
updatevars
Update vars on the Element with a series of operations of different types. Each operation has type
to specify the type of operation and name
to specify the var to update.
set
Set a var to a static value.
Example:
type: updatevars opts: operations: - type: set name: $myvar value: 'My Static Value'
var
Set a var based on another var. Useful for plucking out nested vars to the top level as needed.
Example:
type: updatevars opts: operations: - type: var name: $myvar var: $somenesteddata.subkey.foo
fmtstr
Set a var to a string using a format string syntax. Existing vars can be referenced, along with nodes in a
onnodes
Event currently being handled.Var Example:
type: updatevars opts: operations: # using the $somevar var - type: fmtstr name: $mystr fmtstr: 'Hello {$somevar}!'Nodes Example:
# handling on onnodes Event events: onnodes: - type: updatevars opts: operations: # using the first node from the onnodes Event we are currently handling - type: fmtstr name: $mystr fmtstr: 'Hello {$nodes.0.props.name}!'
toggle
Toggle the boolean value of a var. If the var is not set, the first toggle will set the value to
true
. If the var exists and is a boolean, it will be toggled. If the var exists and is not boolean a warning will be logged.Example:
type: updatevars opts: operations: - type: toggle name: $mytoggle
del
Delete a var, resetting it to be undefined.
Example:
type: updatevars opts: operations: - type: del name: $bye
callstorm
Set a var to the return value of a Storm query executed with callStorm.
Example:
type: updatevars opts: operations: - type: callstorm name: $mystormreturn query: return(([1, 2, 3]))
nodes
Set a var from the nodes in the
onnodes
Event currently being handled.Example:
# handling on onnodes Event events: onnodes: - type: updatevars opts: operations: # store the :name prop of the first node - type: nodes name: $nodename path: $nodes.0.props.name # store the entire node(s) for use with storm / callstorm Action inboundnodes opt. - type: nodes name: $stashednodes path: $nodes
updateopts
Update opts on the Element with a series of operations of different types.
refresh
Refresh the Element from potentially updated opts and vars.
hidenodes
Hide all Nodes or the Nodes in the onnodes
Event if reacting to an onnodes
event.
toast
Display a toast popup message to let the user know about success, warnings or errors.
stop
Stop the current chain of Actions. Typically used with a filter to bail out of an Action chain after a warn/error toast.
loadingmodal
Display or hide a modal loading spinner during the wait for other operations to complete.
sendfocus
Send focus to a field in a stormform
, querybar
, queryeditor
, or tabs
Element. For a stormform
the field
opt is required and for tabs
Element the tabname
opt is required.
Elements
Elements are the primary unit of composition in a Workflow. Each Element can have one or more responsibilities in the Workflow, including displaying data/nodes, accepting user input, running Storm queries that CRUD nodes. Every Element has a set of common top-level properties:
type - The Element Type for the Element.
title - The title for the Element.
doc - Can provide a tooltip and/or markdown content to be displayed with the Element.
layout - The position and dimensions of the Element in the grid.
disabled - If the Element should be disabled. This can be a var to make it dynamic when the var is updated, e.g.
disabled: $foodisabled
hidden - If the Element should be hidden. This can be a var to make it dynamic when the var is updated, e.g.
hidden: $foohidden
oninit - A set of Actions to execute before the Element is rendered.
onload - A set of Actions to execute immediately after creation of the Element.
subs - Defines subscriptions to other Elements Events.
events - Defines Actions to be taken when receiving Events.
Element Lifecycle
An Element can optionally define oninit
and/or onload
to execute Actions at specific points in its lifecycle. The oninit
hook allows executing Actions before the Element is rendered, and the onload
hook allows executing Actions just after all Elements in the Workflow have been rendered.
Element Types
TextArea: textarea
An Element that provides a textarea powered by CodeMirror.
It can be populated statically, from a var, from a Node prop, or from a callStorm query.
There are additionally options to control display, including syntax highlighting modes and line wrapping.
Actions to be executed when the TextArea value is edited can be defined in onchange
.
DataTable: datatable
An Element that can can display arbitrary data in a record
format as a list of Storm $lib.dict()
objects. This is achieved using the $lib.fire() API and a type of optic:datatable:row
.
Columns are defined in the columns
opt, and should specify a key
that will grab the appropriate property from each record for the column.
The Element can additionally configure its opts
dynamically using either an updateopts
Action, or when streaming messages in a storm
Action if appropriate. If a storm
Action fires a optic:datatable:init
message then it can contain an opts
dictionary that will be used to update opts
and refresh, before continuing to process optic:datatable:row
messages.
See the Datatable
and Datatable - Dynamic
Workflows for example usage.
NodeEditor: nodeeditor
An Element that can present fields for creating/editing Nodes.
Fields can be automatically generated based on a node form
, or custom defined fields.
The values are gathered into the $fields
var and will be handed into the provided query.
NodeViewer: nodeviewer
An Element that can display primary values, props, embeds, tags and tagprops of a particular Node.
StormForm: stormform
An Element that can display a defined set of fields in a form. Fields can be of different types, including input
, dropdown
and synforminput
.
Each field is mapped to a var by name
and will be updated anytime the field’s value changes. Actions can optionally be executed
whenever a field’s value is changed using onchange
, which allows propagating vars to other Elements, and causing an internal refresh to update
other fields in the form Element. All fields are additionally bundled into a $fields
dict containing populated values by each field’s name for convenience.
A set of buttons can also be provided using the buttons
opt. Buttons are defined in the same way as the buttons
Element.
In addition to the normal callstorm
behavior where the query can return a dict that will be used to update the Elements vars. It is also possible to provide a fielderrs
key in the returned dict. If provided, the Element will expect the value to be a dict of field name
to an error message to be displayed just below the field to provide user feedback in validation / error situations.
StormTable: stormtable
An Element that displays Nodes of a particular form. The table behaves similarly to the Tabular display mode.
Columns can be defined, to replicate any configuration achievable in the Tabular display mode.
Actions can additionally be defined for onselect
and menu options, to execute Actions with the target Node(s).
QueryBar: querybar
A single line <input> based query bar with a query running spinner. <Enter> will cause the Actions defined in onrun
to be executed.
QueryEditor: queryeditor
A multiline syntax highlighting query editor with a query running spinner. <Shift+Enter> will cause the Actions defined in onrun
to be executed.
Tabs: tabs
A tabs layout Element that allows defining tabs with Elements to be displayed in them when the tab is selected. The tab Elements will be displayed in their own Grid so all layout is relative to the parent.
JSON Schemas
JSON Schemas that document and validate Workflows and Packages containing Workflows are available to download from your Optic instance:
- Schema for a standalone Workflow yaml file
https://<youroptic>/schemas/workflow.schema.json
- Schema for a package yaml file
https://<youroptic>/schemas/pkg-workflows.schema.json
Examples
Workflows are defined under the top-level optic
key in a Storm package definition.
Individual workflow definitions can be included in-line, or as a separate file in workflows/<workiden>.yaml
.
Files from the workflows/
directory are automatically embedded when the Synapse genpkg tool is used.
Workflow and Element iden
values (keys in definition) must be unique,
and can only include alphanumeric, _
, or -
characters.
To avoid URL path collisions, workflow idens should be namespaced, e.g. <pkgname>-<workflow name>
.
name: foopkg
version: 1.0.0
modules:
- ...
commands:
- ...
optic:
displays:
- title: Workflow title for Optic sidebar
workiden: foopkg-inline
- title: Another title
workiden: foopkg-fromdir
workflows:
foopkg-inline:
desc: An inline complete workflow definition
...
# Additional workflows from ``workflows/`` directory would be included here by the Synapse genpkg tool
Packages can be generated (and pushed) using the Synapse genpkg tool.