Process library

The process library (library.process) contains utilities for building up a process from a set of scripts. The processes can:

  • Orchestrate calling scripts and passing data between them.
  • Present a simplified interface to a set of more complicated scripts.
  • Be extended to provide variants for different purposes.
  • Persist data, allowing the script to support a long-running process.

The main component in the process library is the Process node type, in which the process is defined in JSON. An alternative Process builder, in which the process is defined in nodes, is also available.

Process definition format

Each process is defined as a JSON structure. At a high level it defines an array of actions or tasks, which together form a process.

The template has the following format:

[
{
"name": "action name",
"script": "binding ref",
"comment": "optional comment",
"next": next clause,
"config": {
: any config
}
},
... another action
]
name Identifies the action.
script Binding reference for script that fulfils the action, known as a task script. Defaults to the name. May be null, which indicates that no script should be run.
comment Optional comment, shown on documentation.
next

The next clause identifies what should happen if the script completes successfully.

It can be:

  • true – run the next action. This is the default.
  • false – exit the process.
  • The name of another action which should be run.
  • A lookup of states and actions. In this, the property names are the states, and the property value can be:
    • true – run the next action. This is the default.
    • false – exit the process.
    • The name of another action which should be run.
    Use a state of "*" to specify a default. If not given, a default of true is assumed.

Exampe 1: Run the next step. This is the default

"next": true

Example 2: Exit the process

"next": false

Example 3: Jump to "Other action"

"next": "Other action"

Example 4: If the state is "a", jump to action_a, if it is "x", end the process, otherwise jump to action_b

"next": {
"a": "action_a",
"x": false,
"*": "action_b
}

config

The config object is used to configure the called script. This allows generic scripts to be included in the process and then configured for different role.

The config object can be in any format. If any of the properties are of the form "${reference}", they are replaced with node bound to reference. This binding takes place as the process definition is derived, allowing bindings to be overridden in extended processes.

The action property of the config object is used when calling sub processes (see below).

The Process type provides a documentation tab which summarises the template definition.

How the definition is interpreted

The process acts as a script.

During script initialisation, an empty object is created and stored as the application attribute "data".

If the script is run with an action parameter, control is passed to the action identified by the action parameter. If there is no action parameter, control is passed to the first action. The script bound to the action name in the bindings is called, in the following way:

  • The application attribute "config" is set to the config object of the action definition, or removed if there is none.
  • The application attribute "process" is set the the process node.
  • The application attribute "state" is removed.
  • The script is called, using application.call(). This preserves the application attributes.
  • If the script returns an error, end the process.
  • Retrieve the application attribute "state".
  • Use the combination of the name of the action and the state to look up the next action to be invoked.
  • If an action is defined for the combination of predecessor and state, look for a default for the predecessor.
  • If no action is defined, proceed to the next action.

Some action names have a special meaning:

  • @start – run at the start of the process, before the requested action.
  • @end – run at the end of the process, assuming there is no error.
  • @default – run if no action parameter is passed, rather than running the first action.
  • @error – run if there is an error. @end is not run after @error. The @error action must reset the error to run any further actions.

Task script requirements

The scripts called from the process (the task scripts) can access the request sent to the process and can set a response.

They may also:

  • Read configuration from the application attribute "config".
  • Identify the process from application attribute "process",
  • Read and write process data from the application attribute "data".
  • Set an outcome in the application attribute "state".

Using a process

A process is just a script, and it can be run, executed, included or called like any other script. The actions are identified by the request parameter "action".

Sub processes

Each process can also be called as a script. In this case, it looks for the action in the config attribute for an initial action to run, rather than the request attribute. (It uses the presence of the data attribute to determine whether to look in the requests attribute or the config attribute.)

The sub process may change the data. The state and return values from the last script in the sub process will be the state and return value from the sub process.

A task script may invoke an action on its own process using the following:

application.put('config',{action:requiredAction});
application.call(application.get('process'));

Application attributes, such as data and request, will be available to the action.

The same convention can be used to invoke a process through application call() or execute(), for example the following code executes the process associated with a node and runs the display action.

application.put('config',{action:'display'});
application.execute(context);

Extending processes

Processes can be extended as follows:

  • By inheriting from a process template and overriding the bindings, to change action scripts and sub processes.
  • By creating a new process template and listing process templates on which it is based in the Extends list. These are applied in turn, each one overriding any like-named actions and bindings. The bindings and process definition for the process template itself override all the ones it extends.

Data persistence

Of itself, a process does not persist data, though the tasks are free to write data as required. However, standard tasks are available to assist with data persistence.

Save data

Save the data attribute, either to the context node or to a temporary node.

Load data

Load previously saved data into the "data" attribute.

Delete data node

Delete the data node identified by application attribute "dataNode".

The data persistence tasks are useful when the process supports user interaction. In outline, to interact with the user:

  • Use Save data to save the data.
  • Run a task that creates a response with content. The links or forms with which the user restarts the process should pass the reference of the data node in the request parameter "data".
  • When restarting the process, use Load data to reload the saved data.

Process instance

The Process Instance can be used to create instances of processes. The process is run when the instance is executed. They provide a Data member for the process to save data.

Other types can be created based on Process Instance.

Progress indicator

More complex processes may benefit from a progress indicator to entertain and inform the user while the process is in progress.

See the Progress Indicator Script for general introduction to the progress indicator. Task scripts are available to support the progress indicator process.

Start progress

Start or restart a progress indicator, and set a redirect to the progress indicator node.

Show progress

Write a message to the progress indicator.

End progress

End the progress indicator, optionally redirecting to a URL.

Progress error

End the progress indicator with an error read from the application error fields, optionally redirecting to a URL

Render progress

Standard script to render the progress indicator. This must be present in the process with an action name of "renderProgress".

Check for progress indicator

Test whether a progress indicator should be shown.

Execute in background

Re-execute the current context in the background. This is used to continue the process in the background after starting a progress indicator.

End progress with execute

End the progress indicator, redirecting to an execute of the current context.

Response handling

Task scripts can output a response to the user in the standard way, setting the title, head and content of the response.

However, it is sometimes useful to bulid a response in one script and then output it in another. For example, it can be useful to build a response in a background script, and then end a progress indicator with a redirect to an action that shows the response. You can achieve this by saving the response to the data area, and then rebuliding the response from the data area later in the process.

Two tasks scripts are available.

Copy response to data

Copy a response to the data attribute.

Copy data to response

Recreate a response that has previously been copied to the data area.

Copy data to response can also be used to output a response built up by previous scripts in the data.response object. This can be useful if a process uses multiple tasks to build a page to send to the user.

Authority

Of itself, a process does nothing about authority. All the tasks run under the authority of whichever user called the process.

For executed scripts, it can be useful to run the process under the node owner's authority. You can achieve this by calling the process as a script from a wrapper script, and preceding the call with application.runAsOwner().

In many cases, the process will want to know about the original caller, and may want to call services using the original caller's credentials. In this case, the wrapper script can use the Caller Credentials Script. This provides two methods:

  • store() – store the current user as a Script User object in the application attribute "user" and their credentials as a Service Credentials object in application attribute "credentials".
  • remove() – cancel the credentials and remove the "user" and "credentials" application attributes.

The wrapper script below will run the process bound to 'process' as the node owner, but with access to the caller in the "user" and "credentials" attributes.

application.include(application.getBinding('scriptCallerCredentials')); // bind to Caller Credentials Script
CallerCredentials.store();
application.runAsOwner();
application.call(application.getBinding('process'));
CallerCredentials.remove();

Caller Credentials Script also provides more granular methods for storing and removing just the user or just the credentials.

Looking up processes using dynamic bindings

In complex solutions, processes and status rules (see below) may be identified by dynamic bindings.

Derive Process And Status Rules is a specialised type for creating a derivation field that will look up a process and status rules using dynamic bindings, and write them to Process and Status rules (library.process.StatusRulesLink, not library.process.StatusRules) respectively, taking defaults specified on the derivation field. Add this to the types in a solution so that the instances of those types will have Process and Status rules populated.

Status rules

Status rules rules provide a way of controlling options offered to the user, which can be used alongside a process.