Skip to content

Functions#

About Functions#

Functions enable dynamic call processing based on customized serverless function and callflow document as input.

Pivot allows you to receive a per-call HTTP request to decide what to do, generally to access data sources not available to KAZOO. However, if your business logic is self-contained and side-effect free (and not directly representable via the callflow JSON structure), functions allow you to create that custom logic and have it run within the KAZOO infrastructure. This minimizes the time spent processing the function (as it is run "locally") and doesn't require you to have to stand up separate infrastructure to handle the HTTP request and generate the JSON response.

Schema#

Functions that javascript function to process a callflow document.

Key Description Type Default Required Support Level
enabled Is the function enabled and running boolean() true false supported
function_js Serverless function in Javascript string() true supported
language_pack The language and version are required for the function runtime. string() javascript false supported
name A descriptive name for the function. string() true supported

User-defined Functions#

Effectively, KAZOO gives you function(call) {...} and asks you to supply the .... You have the call object containing the same request parameters as you would in Pivot (such as call['To'] or call['Call-ID']).

Example 1: Say Hi!#

{"name":"say hi"
 ,"function_js":"return {'module':'tts', 'data':{'text':'Thanks for calling us ' + call['Caller-ID-Name']}};"
}

All calls to this function would then run the TTS engine to say hi to the caller using the supplied Caller ID Name.

Example 2: Dates#

{"name":"fun day"
 ,"function_js":"var d = new Date(); var days = ['Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday']; return {'module':'tts', 'data':{'text':days[d.getDay()] + ' fun day!'}};"
}

Javascript functionality available#

As of now, ECMA-262 5th edition (“ES5”) of the language, is supported.

Fetch available functions on the system#

Depending on the version of the KAZOO system running, the available functions may differ. Use this API to query the system for available functions.

GET /v2/functions

curl -v -X GET \
    -H "Content-Type:application/json" \
    -H "X-Auth-Token: {AUTH_TOKEN} \
    http://{SERVER}:8000/v2/functions

Get sample payloads of all function events#

GET /v2/functions/samples

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/functions/samples

Example#

Request:

curl -H 'Content-Type: application/json' 'http://{SERVER}:8000/v2/functions/samples'

List functions#

GET /v2/accounts/{ACCOUNT_ID}/functions

Any functions with disable_reason in the summary has been auto-disabled.

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/functions

Create function#

PUT /v2/accounts/{ACCOUNT_ID}/functions

curl -v -X PUT \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -H "Content-Type: application/json" \
    -d '{"data": {
      "name":"Say hi!"
      ,"function_js":"return {"module":"tts", "data":{"text": "Thanks for calling " + call['To']}};"
    }}' \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/functions

Get details of the function#

GET /v2/accounts/{ACCOUNT_ID}/functions/{FUNCTION_ID}

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/functions/{FUNCTION_ID}

Update the function#

POST /v2/accounts/{ACCOUNT_ID}/functions/{FUNCTION_ID}

curl -v -X POST \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/functions/{FUNCTION_ID}

Patch function#

PATCH /v2/accounts/{ACCOUNT_ID}/functions/{FUNCTION_ID}

You can also patch an existing function:

curl -v -X PATCH \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -H "Content-Type: application/json" \
    -d '{"data":{"enabled":true}}' \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/functions/{FUNCTION_ID}

Delete a function#

DELETE /v2/accounts/{ACCOUNT_ID}/functions/{FUNCTION_ID}

curl -v -X DELETE \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/functions/{FUNCTION_ID}

Administration#

Enabling the API endpoint#

To add the module to your auto-started list of API endpoints:

sup crossbar_maintenance start_module cb_functions

If you just want to play with it on a local node but not persist the endpoint on restart:

sup cb_functions init

CouchDB#

We leverage CouchDB's show function to provide hosting and processing of the user's Javascript function.

The client will create a "function" document containing the Javascript function as a string. The show function functions/wh will be run with the "function"'s doc id and the call will be placed in the request's query string parameters.

The wh show function will create a new function (using Javascript's new Function()) from the stringified version on the doc and pass it the query string object from the req argument. The user's function should return the new flow JSON (as you would using Pivot).

Work around to enable new Function() in CouchDB#

Javascript's new Function(), which is similar to eval, is used to parse a piece of Javascript code in a string into a function object, which is invoked with the call object to return the new flow JSON. It is considered more secure than eval since the new Function() code runs in a separate scope.

However, both eval and new Function() are blocked by Content Security Policy in CouchDB (see this commit. A workaround has to be applied to enable new Function().

CouchDB 2.3#

Make sure --eval is explicitly added under query_servers of etc/local.ini.

[query_servers]
COUCHDB_QUERY_SERVER_JAVASCRIPT="<absolute path>/bin/couchjs --eval <absolute path>/share/server/main.js"

If CouchDB is started by dev/run from the CouchDB repo, please update dev/run with

qs_javascript = toposixpath("%s --eval %s" % (couchjs, mainjs))

CouchDB 2.2 or earlier versions or BigCouch#

Make sure --eval is explicitly added under query_servers of etc/local.ini.

[query_servers]
javascript = "<absolute path>/bin/couchjs --eval <absolute path>/share/server/main.js"
Security#

It is recommended that you do not run the eval/new Function() enabled CouchDB instance in your main DB cluster.

Fortunately, KAZOO allows you to define alternate CouchDB connections for specific databases or database classifications. Since all function documents are stored in the functions aggregate database, you can create a connection just for the functions database that points to a entirely separated CouchDB server or cluster:

Create a 32-char UUID: > UUID = kz_binary:rand_hex(16). => "9ee5860b4d717907dfc63274af0d06e9"

Create the connection and storage plan:

{
  "connections": {
    "{UUID}": {
      "driver": "kazoo_couch",
      "name": "functions-connection",
      "settings": {
        "ip": "localhost",
        "port": 55984
      }
    }
  },
  "plan": {
    "aggregate": {
      "connection": "{UUID}",
      "database": {
        "names": [
          "functions"
        ]
      },
      "types": {
        "function": {
          "connection": "{UUID}"
        }
      }
    }
  }
}

Apply that to the global storage plan:

curl -v -X PATCH -H "X-Auth-Token: {ADMIN_AUTH_TOKEN}" http://localhost:8000/v2/storage \
-d '{"plan":{"aggregate":{"connection":"{UUID}","database":{"names":["functions"]},"types":{"function":{"connection":"{UUID}"}}}},"connections":{"{UUID}":{"driver":"kazoo_couch","name":"functions-connection","settings":{"ip":"localhost","port":55984}}}}'

Engineering#

Other design options#

Besides CouchDB's show function, we could directly communicate with couchjs that is CouchDB's external Javascript engine. The benefit is only to expose the eval risk to Functions instead of all views and show functions in CouchDB.

Erlang_V8 has been toyed with to accommodate Javascript.

In the long term, to support a range of languages, we might look into Docker based web server to bootstrap functions implemented by the language of choice.