Serverless Functions for Business Logic

This section discusses how to implement business behavior using serverless functions.

What is a Trillo Serverless Function?

Trillo serverless function implements some business logic and it can be invoked using restful API.
A serverless function is automatically published as an API.

Create a Function

Creating a serverless function using Trillo Workbench is simple. Simply select the Functions menu options in the left navigation. Select + New Function.
Currently, Trillo Workbench supports only Java and Python languages.
Say, you create a function by giving it a name as MyFunction. The function will be created with the following stubbed code.
Java
Python
import java.util.Map;
import com.collager.trillo.util.Api;
import com.collager.trillo.util.ServerlessFunction;
public class MyFunction extends ServerlessFunction {
@Api(httpMethod="post")
public Object postMethodChangeMe(Map<String, Object> parameters) {
return parameters;
}
}
In the above code, MyFunction is the name of the function. It has a method 'postMethodChangeMe'. This serves as an API endpoint. It is of type HTTP post due to the annotation above the method. Note that the function implements the ServerlessFunction class (which implements TrilloFunction). More about it below.
THe URL that will target this function and its method would be as follows:
<hostname>/ds/function/shared/MyFunction/postMethodChangeMe
where,
  1. 1.
    ds/function indicates the service of Trillo Workbench (data dervice and its function module).
  2. 2.
    shared is package (folder) containing the function.
  3. 3.
    MyFunction is the name of the function that is the target of URL.
  4. 4.
    postMethodChangeMe is the method that will be invoked due to API call.
A function can support more than one endpoint (callable methods as API). The names of the methods can be anything (generally reflect the purpose). Say the above MyFunction needs to support the following 4 methods:
  1. 1.
    saveValue
  2. 2.
    updateValue
  3. 3.
    getValue
  4. 4.
    removeOverheads
The refined MyFunction will be as follows.
import java.util.Map;
import com.collager.trillo.util.Api;
import com.collager.trillo.util.ServerlessFunction;
public class MyFunction extends ServerlessFunction {
@Api(httpMethod="post")
public Object saveValue(Map<String, Object> parameters) {
return parameters;
}
@Api(httpMethod="put")
public Object updateValue(Map<String, Object> parameters) {
return parameters;
}
@Api(httpMethod="get")
public Object getValue(Map<String, Object> parameters) {
return parameters;
}
@Api(httpMethod="delete")
public Object removeOverheads(Map<String, Object> parameters) {
return parameters;
}
}
In this case, the function will publish 4 endpoints.
  1. 1.
    a post endpoint as /ds/function/shared/MyFunction/saveValue.
  2. 2.
    a put endpoint as /ds/function/shared/MyFunction/updateValue.
  3. 3.
    a get endpoint as /ds/function/shared/MyFunction/getValue.
  4. 4.
    a delete endpoint as /ds/function/shared/MyFunction/removeOverheads.
def handle (params):
return params
In the above code, MyFunction is the name of the function. It has a method 'handle'. This serves as an API endpoint. It is of type HTTP post due to the annotation above the method. Note that the function implements the ServerlessFunction class (which implements TrilloFunction). More about it below.
The URL that will target this function and its method would be as follows:
<hostname>/ds/function/shared/MyFunction/handle
where,
  1. 1.
    ds/function indicates the service of Trillo Workbench (data service and its function module).
  2. 2.
    shared is package (folder) containing the function.
  3. 3.
    MyFunction is the name of the function that is the target of URL.
  4. 4.
    handle is the method that will be invoked due to API call.

ServerlessFunction (BaseClass of Trillo Function)

ServerlessFunction is the base class of Trillo functions class. This encapsulates runtime context provides access to its properties such as user identity, task id, stateMap. All These properties are available to any subclass using accessor methods.
Parameter Name
Description
isOfUser
Internal identifier of the current user (on whose behalf the call is made, it may be a system user).
userId
User id (assigned to the user when it was created, also login userId)
firstName
First name of the user
lastName
Last name of the user.
email
Email of the user.
role
Roles assigned to the user.
emailVerified
A flag indicating if the user's email has been verified or not.
tenantId
If the deployment is a multi-tenant then it returns the id of the tenant.
tenantName
If the deployment is a multi-tenant then it returns the name of the tenant.
executionId
If the function is running as a background job then it gives the identifier which is used to track the task in the database (it is internal is you can ignore it barring an advanced use-case).
taskName
If the function is running as a background job then it gives the name of the task. It may be useful for logging.
stateMap
An arbitrary java.util.map/Dict can be used to store some state information. This is useful when a function calls another function. This map is passed back to the calling function with any updates made by the caller function. This is useful, for example, for building a lookup cache in a chain of function.
All Trillo serverless function runs within a transaction boundary. By default, the transaction is rolled back if the function invocation returns a Result object with an error. Since a function may run a few steps that may be committed irrespective of the final result, Trillo SDK provides an API to commit the transaction. After a transaction is committed, a new transaction is created.

Sample Hello World Function

The sample code with a method called "sayHello" as the endpoint:
Java
Python
@Api(httpMethod="get")
private Object sayHello(Map<String, Object> parameters) {
String hello = "Hello from, " + getUserId();
if (parameters.containsKey("youSaid")) {
hello += "\n You said, " + parameters.get("youSaid");
}
return hello;
}
def handle(parameters):
hello = "Hello from, " + getUserId()
if "youSaid" in parameters:
hello += "\n You said, " + parameters["youSaid"]
return hello
Notice that this method simply echos "Hello from <current user id>". The current user id is accessed from the runtime context. The function adds the parameter "youSaid" if it is passed.

Test Function

The sample code with a method called "sayHello" as the endpoint:
Java
Testing Function by Selecting Execute Tab, Enter Parameter and Click Execute

Result

Notice in the above figure, that the actual result is wrapped inside an object and passed as its data attribute. The wrapper class is Result (a Java class/ python class). It contains the actual result in the data attribute. It has other attributes for status, error message, detailed message, etc. A special attribute called _rtag is included with a special value _r_ to assist a JavaScript client in identifying if the returned value is an instance of Result.

Log Messages In a Function

The serverless function can use logging APIs available in Trillo SDK. See the modified example code below:
Java
private Object _handle(ScriptParameter params) {
//Insert the correct biz logic
LogApi.info("Entered _handle()");
Map<String, Object> functionParameters = (Map<String, Object>)params.getV();
String hello = "Hello, " + params.getUserId();
if (functionParameters.containsKey("youSaid")) {
hello += "\n you said, " + functionParameters.get("youSaid");
}
LogApi.loginfo("Exiting _handle()");
return hello;
}

Audit Logs

Audit logs are similar to the log messages discussed above with the distinction that these calls also log messages in the database. This is useful for auditing important business events. It is also useful to track, troubleshoot, and monitor the status of long-running jobs.
The following code shows how to use logging vs audit logs.
Java
Tapix.auditInfo("actionName", "Entered _handle()"); // this is a log
Tapix.auditInfo("actionName", "Entered _handle()"); // this is audit log
"actionName" can be any text. It is generally the action that caused this log, such as "BucketFileWrite". It is used to filter logs.

Samples

Refer to Trillo Workbench Tutorial for sample functions.

Trillo SDK (APIs) in a Function

Trillo SDK provides several APIs to simplify writing a serverless function code. These APIs include:
  • Database access
  • Google cloud service
  • External restful service
  • CVS, JSON file processing
  • ...
  • ...
  • many more
Refer to Trillo Workbench Java SDK for a detailed list of all APIs.
Not only Trillo SDK functions but several open-source libraries that are integrated with the Trillo runtime, become available for use in your serverless functions (such as Apache Commons for example).

Version 1 Serverless Function

Trillo function Version 1 (V1) supported only one endpoint per function, landing into a default method called "handle". They were always invoked using HTTP-Post irrespective of API semantics. An example of V1 is shown below.
Java
import com.collager.trillo.pojo.Result;
import com.collager.trillo.pojo.ScriptParameter;
import com.collager.trillo.util.LogApi;
import com.collager.trillo.util.TrilloFunction;
import java.util.Map;
public class MyFunction implements TrilloFunction {
public Object handle(ScriptParameter scriptParameter) {
try {
// actual implementation inside _handle()
return _handle(scriptParameter);
} catch (Exception e) {
LogApi.error("Failed", e);
return Result.getFailedResult(e.getMessage());
}
}
@SuppressWarnings("unchecked")
private Object _handle(ScriptParameter scriptParameter) {
Map<String, Object> parameters = (Map<String, Object>)scriptParameter.getV();
return parameters;
}
}

Methods of Version 1 Function

A V1 serverless functions generally have the following two methods. The first, method called 'handle', is the default method and always required. The second method is optional. It could have been renamed or merged into first one.
  • handle: This is the entry point of the function that is invoked by the runtime by passing ScriptParameter (see below).
  • _handle: A private method that is invoked by the method handle.
  1. 1.
    The 'handle' method catches all exceptions, unwraps all error messages, and passes as a Result object.
  2. 2.
    You should implement your code in the _handle (you can rename it). T
  3. 3.
    he endpoint of published by the function is /ds/function/MyFunction.

ScriptParameter

ScriptParamter provides equivalent functionality as provided by the superclass (ServerlessFunction) of Version 2 Trillo functions. It provides the same accessor methods as provided by the ServerlessFunction. In addition to it, it provides a method "getV" to retrieve the parameter passed to the function.