Skip to content

Latest commit

 

History

History
183 lines (157 loc) · 7.41 KB

expressions.md

File metadata and controls

183 lines (157 loc) · 7.41 KB

Input and Output Expressions

Often there are trivial data transformations between subsequent workflow steps. For example, you might need to select a field within a larger object or normalize the input text.

To help you with these simple data transformations Fission Workflows offers support for input expressions. An input expression is an inline function to perform a trivial data transformation on an input value just before the task is invoked. In contrast to tasks, these input expressions are not recorded or stored by the workflow engine, and do not have options to retry or fallbacks. In case an input expression errors, the task will automatically fail without being invoked.

JavaScript

The workflow engine supports input expressions written in javascript. The underlying implementation uses the Golang-based
Otto Javascript interpreter to provide a JavaScript support with almost the complete standard library (including regex, json, math, and date). See the Otto documentation for a full reference on all builtin functions.

The workflow engine interprets any string input that is wrapped with { ... } as a JavaScript expression. An example of a JavaScript Expression:

{$.Tasks.MyTask.Output}

See the Examples section for more examples.

Data Model

To avoid tedious and verbose querying of the internal data structures used for workflow invocations, the workflow engine provides a compressed data model to query with the JavaScript expression.

The workflow invocation is stored in the $ variable; commonly referred to as the scope. It consists out of the following fields:

$ = {
    Workflow : Object,          // See Workflow
    Invocation : Object,        // See Invocation
    Tasks : {
        String : Object         // See Task
        // ...
    }
}

The Workflow object provides information about the workflow definition.

Workflow = {
    Id : String,                // ID of the workflow
    CreatedAt: Integer,         // Unix timestamp
    UpdatedAt: Integer,         // Unix timestamp
    Status: String,             // Status of the workflow (during input evaluation it is always 'READY')
    Tasks: {
        String : {
            Src : String,       // The user provided function reference
            Runtime : String,   // The runtime responsible for executing the function
            Resolved : String   // The runtime-specific function identifier
        }
        // ...
    }
}

The Invocation object provides information about the current invocation.

Invocation = {
    Id : String,                // ID of the workflow invocation
    CreatedAt: Integer,         // Unix timestamp
    UpdatedAt: Integer,         // Unix timestamp
    Inputs: {
        String : Object         // The input to the invocation. The value of it depends on the value type.
        // ...
    }
}

The Task object holds information about a specific task execution within the current workflow invocation.

Task = {
    Id : String,                // ID of the task (invocation)
    CreatedAt: Integer,         // Unix timestamp
    UpdatedAt: Integer,         // Unix timestamp
    Inputs: {
        String : Object         // The input to the task. The value of it depends on the value type.
        // ...
    },
    Requires: {
        String : Object         // The key is the task ID of the dependency
        // ...
    },
    Output: Object,             // The output of the function (if available)
    OutputHeaders: Object,      // The headers in the response of the function (if available)
    Resolved : {
        Src : String,           // The user provided function reference
        Runtime : String,       // The runtime responsible for executing the function
        Resolved : String       // The runtime-specific function identifier
    },
    Status: String             // Status of the task
}

Source: https://github.com/fission/fission-workflows/blob/master/pkg/controller/expr/scope.go

For convenience, the expression resolver provides the id of the current task in the taskId variable.

The variables are case-sensitive, which requires you to reference fields appropriately. Additionally, the expression is truly plain javascript, so the user is responsible for avoiding NPEs. Undefined tasks, requires or outputs will resolve to undefined. For example $.Workflow.Status is valid, whereas $.workflow.Status will error.

Note that in the case of inputs, if there is a single input without an explicit key defined, it will be stored under the default key: default.

Built-in Expression Functions

Besides the standard library of JavaScript, the expression interpreter provides a couple of additional utility functions. These functions do not have access to any additional functionality not provided to the user; they are generally short-hand functions to simplify common operations. The following functions are provided by default:

name Usage Description
uid uid() Generates a unique (string) id
input input("taskId", "key") Gets the input of a task for the given key. If no key is provided, the default key is used.
output output("taskId") Gets the output of a task. If no argument is provided the output of the current task is returned.
outputHeaders outputHeaders("taskId") Gets the headers in the response of a task. If no argument is provided the headers in response of the current task are returned.
param param("key") Gets the invocation param for the given key. If no key is provided, the default key is used.
task task("taskId") Gets the task for the given taskId. If no argument is provided the current task is returned.

Adding Custom Function

The JavaScript expression interpreter is fully extensible, allowing you to add your own functions to the existing standard library.

To do so, you will need to implement the expr.Function interface, add your function to the list of builtin functions, and compile the workflow engine again. In the future functionality will be added to allow functions to be added at runtime in the form of plugins.

Examples

This section contains various examples of common uses of JavaScript-based input expressions.

Get the timestamp of the last update to this workflow:

{ $.Workflow.UpdatedAt }

Get the default (body) input of the workflow invocation:

{ $.Invocation.Inputs.default }
// Or the function equivalent:
{ param("default") }

Get the 'Foo' header from the workflow invocation inputs, or default to 'Bar':

{ $.Invocation.Inputs.headers.Foo || "bar" }
// Or the function equivalent:
{ param("headers").Foo || "bar" }

Get the default input of the 'other' task:

{ $.Tasks.other.Inputs.default }
// Or the function equivalent:
{ input("other") }

Get the output of the 'example' task and convert it to a string:

{ String($.Tasks.example.Output) }
// Or the function equivalent:
{ String(output("example")) }

Get a unique id:

{ uid() }

Get the 'Foo' header from the output headers of a task:

{ $.Tasks.other.OutputHeaders.Foo }
// Or the function equivalent:
{ outputHeaders("other").Foo }