JavaScript Integration

You can integrate with external systems by writing JavaScript code in a JavaScript action. Signavio Workflow Accelerator runs the code on the server, using Node.js. As well as Node.js’ JavaScript API, scripts can use additional libraries.

JavaScript action configuration

After creating or selecting a JavaScript action, the configuration panel looks like this:

../_images/javascript-1.png

JavaScript configuration panel

The top section of the panel contains the JavaScript text editor. By default, it already contains console.log('Hello World!');. Use the console API for log output when testing scripts.

JavaScript libraries

JavaScript actions support a number of popular JavaScript libraries. To import a package, use the require function:

var moment = require('moment');

You can also choose another name for the imported library:

var stringValidator = require('validator');
Supported JavaScript libraries
Library Import Description
CSV csv CSV generation, parsing, transformation and serialization
Files files Built-in API for File variable data
Lodash lodash Convenience functions for working with collections and values
moment moment Parse, validate, manipulate, and display dates; with Twix date range, and moment-business-days support
request request Simplified HTTP request client
Users users Built-in API for User variable data
validator validator String validation and sanitization
WebDAV client webdav-client Exchange files with a WebDAV endpoint
XML xml-js XML generation and parsing

The JavaScript action always imports the _ (Lodash) and request packages, for backwards compatibility.

Testing scripts

Use the Test Runner tab to test the script. Click Start new test to execute the JavaScript code. The test runner displays the results underneath:

../_images/javascript-2.png

JavaScript test output

At the top, you see the test execution date and time. After running multiple tests, you can use this menu to select earlier test runs. The Variable updates section shows a table of process variables, with their test values and any updates. The Logs section shows console output and any errors.

Using process variables

Next, we’ll show how to work with data. Suppose that the process includes a form that has each type of field and looks like this:

../_images/javascript-3.png

Form fields that declare process variables

On the JavaScript configuration panel, the Add existing variable pick list now shows the form field variables.

../_images/javascript-4.png

Process variable selection

Select the variables you want to access in the script. The script can access the variables using the JavaScript variable name from the Variables table. To access object variables’ fields, use the field names specified for the corresponding data type: Case, Email, File or User.

In this example (below), you have selected all variables. For each variable that you select, you get an input field to specify a test value. Here you see all fields with a test value.

../_images/javascript-5.png

JavaScript test values

Clicking Start new test again to see the JSON structure of the variable data for the different variable types.

../_images/javascript-7.png

JavaScript JSON values

The contract and salesRepresentative variables have complex types, File and User, so the table only shows an ID. The Updated value column shows the result of assigning new values to these variables in the script.

Note

You can use JavaScript actions to update process variables. Then, you need to make sure you re-assign a new value to the variable instead of mutating the variable itself. Otherwise, the system will ignore the update. For example, the system ignores contactEmails.push('joan.doe@example.org'), but correctly processes contactEmails = [].concat([], 'joan.doe@example.org'). This restriction doesn’t apply to variables you only use in the context of the JavaScript action.

Reading file contents

A JavaScript action may need to read the contents of a file, in order to publish the file to an external web service. To access file content, you need to require the files API.

const files = require('files')
const fileContent = files.getContent(contract)

In this example, contract is a file variable that references the file contents that the script reads.

The getContent function returns a Node.js File object, whose buffer property provides access to the file content bytes. The following example loads a CSV file, converts the content bytes to a UTF-8 string, and parses the string:

const files = require('files')
const csv = require('csv')

// Read the reportCsv file variable
const csvFile = files.getContent(reportCsv.id)

csv.parse(csvFile.buffer.toString('utf-8'), {
    auto_parse: true,
    columns: true,
}, (error, data) => {
    console.log(data)
})

Updating case information

The process variables always include the built-in Case variable, which contains information about the current case. Sometimes, you want to update this case information using data from process variables. You can update some of the this case variable’s fields, as follows.

// Set the case name using a template.
_case.name = `Case ${_case.caseNumber}`;

// Set the case’s due date using a date variable set on a form.
_case.dueDate = releaseDate;

// Set the case’s priority, using text values '0' (high) to '3' (low)
// priorities defines constant values high, medium, normal, and low
const priorities = require('priorities')
_case.priority = priorities.low

A case name template can only use Form trigger fields to set the case name when the process starts. However, when you can set the case name directly in a JavaScript action, you don’t have this restriction.

Loading user information

In a JavaScript action, you might need to select a Workflow Accelerator user based on external data, to assign a role. To do this, you can use the built-in users API to find a user by their email address.

const users = require('users');
reviewer = users.findByEmail(reviewerEmailAddress);

This example uses the value of a previously-supplied reviewerEmailAddress Email address variable to set a reviewer User variable.

Calling an external web service

You can use variables to send process data to an external web service, using the request module. For example, the following script sends the value of the startDate variable in an HTTP POST request to an external web service.

../_images/javascript-9.png

This example uses a test endpoint configured using Mocky to return an HTTP response. This has the following result in the Workflow Accelerator test console:

../_images/javascript-8.png

Updating a variable via an external web service

The two log statements, starting with HTTP 200, show the HTTP response from the web service. The response body (as set-up in Mocky) contains JSON data that includes an updated value for the startDate variable, changing the date from 2017-08-01 to 2017-08-02.

The script then parses this JSON response using JSON.parse and updates the startDate variable in Workflow Accelerator, as shown in the Updated value column in the test console’s variables table.

Generating and parsing XML

XML is used as a common exchange format for data. In a JavaScript task you can parse and generate XML using the xml-js library.

The first example parses XML content of a local variable into a JavaScript object structure. You can also retrieve XML content from another source, like a process variable or an external web service.

The example uses the compact mode by setting the option { compact: true } when calling the conversion function. The compact mode creates an object structure which resembles XML structure. It contains special properties like _attributes and _text which allow you to access the text content of XML elements and attributes.

const convert = require('xml-js')

const xml = `
<customers>
  <customer status="silver">John Doe</customer>
  <customer status="gold">Alice Allgood</customer>
</customers>
`

// Parse the XML string to a JavaScript object using the compact mode
const obj = convert.xml2js(xml, { compact : true})

const john = obj.customers.customer[0]
console.log(`${john._text} - ${john._attributes.status}`)

The second example shows how to generate XML from a JavaScript object. Note that the structure also contains the special properties _attributes and _text to indicate which parts of the object will be XML attributes or text content.

This example use compact mode to set the respective option.

const convert = require('xml-js')

const obj = {
    customers : {
        customer: [
            {
                _text: "John Doe",
                _attributes: {
                    status: "silver"
                }
            },
            {
                _text: "Alice Allgood",
                _attributes: {
                    status: "gold"
                }
            }
        ]
    }
}

// Generate the XML string from a JavaScript object
const xml = convert.js2xml(obj, { compact: true })

console.log(xml)

See the xml-js library documentation for more information about generation and parsing options.

Exchanging files with a WebDAV endpoint

In a JavaScript action, you might want to upload a file that has been uploaded to a task form to your own file server or load a file from an external server to use the content during the script execution. If your file server supports the WebDAV protocol, you can use the webdav-client library.

In this example, the content of the file variable myFile is uploaded to the endpoint https://webdav.example.com. When uploading the file content make sure to use the buffer property as the library expects either a buffer or a string for the file content.

const webdav = require('webdav-client')
const files = require('files')

// Create a connection to your WebDAV endpoint
const connection = new webdav.Connection('https://webdav.example.com')
// Read the content of the myFile file variable
const fileContent = files.getContent(myFile)

// Upload the content to the root directory using the original file name
connection.put(`/${myFile.name}`, fileContent.buffer, (error) => {
  if (error) {
    throw new Error(`File upload did not work: ${error}`)
  }
})

As an alternative, it is also possible to generate a string with the file content and upload it.

const webdav = require('webdav-client')

// Create a connection to your WebDAV endpoint
const connection = new webdav.Connection('https://webdav.example.com')
// Create the file content
const myNotes = 'The answer is 42!'

// Upload the content to the file myNotes.txt in directory /path/to
connection.put('/path/to/myNotes.txt', myNotes, (error) => {
  if (error) {
    throw new Error(`File upload did not work: ${error}`)
  }
})

Reading a file via WebDAV is as simple as uploading one. Instead of put use get and specify the desired file. The callback offers the second parameter body which contains the file content.

const webdav = require('webdav-client')

// Create a connection to your WebDAV endpoint
const connection = new webdav.Connection('https://webdav.example.com')

connection.get(`/foobar.txt`, (error, body) => {
  if (error) {
      throw new Error(`File upload did not work: ${error}`)
  }
  console.log(body)
})

See the WebDAV library documentation for more methods to modify your files and additional request options. The documentation also explains how you can authenticate to your WebDAV endpoint.