Skip to content

Overview

To fully focus on our award winning Web and Mobile Messaging, Analytics and Deferred Deep Linking solutions, Backend-as-a-Service will be retired on 1st July 2023. Please see here for more details.

KScript allows you to use Kumulos to encapsulate business logic in custom server-side jobs, written in JavaScript. KScripts can be deployed and accessed in the same way as your existing API methods but offer much greater flexibility and control over what happens on the server-side of Kumulos.

KScript methods can also be triggered by Automations, which allows you to use events as a trigger to fire actions. For example, to send an email when a user completes the checkout process.

Enabling KScript

KScript is not enabled by default as the majority of use cases can be satisfied via API Methods. If you wish to add server-side jobs, please contact technical support who will be happy to enable KScripts for you.

Once you receive an email confirming that KScript has been enabled for your app, you can then create KScript methods from the API tab of your application in Kumulos. Underneath the "Create API" button, there will now be a "Create Some KScript" button once its enabled on your app.

Although used by numerous apps in production, our KScript feature is still under ongoing development and as such may change in future. However, we will contact you in advance of any significant changes.

Working with KScripts

KScript methods are listed in the 'API' section of 'Build'. KScript methods will appear above API methods created with the drag-and-drop editor.

KScript Methods

Kumulos provides two ways to add and edit KScript methods. For quick, simple changes, you can use the inline code editor. Alternatively, for prolonged development work or non-trivial changes, we suggest using the KScript IDE (Integrated Development Environment). Both utilize the Monaco Editor from Microsoft.

Inline code editor

To view an existing KScript method in the inline code editor, click the edit/pencil icon under 'Actions' next to that method in the list. To use the inline code editor to add a new KScript method, click the KSCRIPT <> button next to 'Create some KScript' in the right hand pane.

Inline Code Editor

For prolonged development work or non-trivial changes, we recommend using the full-screen KScript IDE instead of the inline code editor.

KScript IDE

To open the KScript Integrated Development Environment (IDE), click on the IDE ^ icon next to 'Open the IDE' in the right hand pane.

KScript IDE

Toggle theme

In both the inline code editor and the KScript IDE, you can switch between light and dark theme by clicking the Toggle Theme icon at the bottom right of the code window.

KScript IDE

Testing a KScript Method

You can test a KScript Method by clicking the play icon under 'Actions' next to that method in the list, or by clicking Save and Run within the KScript IDE. Both will bring up a 'Run API' screen, prompting you to enter any input parameters. Click Run It! to run the KScript Method and see Execution Time, Results (e.g. number of rows updated in a query) and any debug logs.

Run KScript Method

Once you are happy that your KScript methods are working as intended, don't forget to hit DEPLOY either in the right hand pane or the KScript IDE, to deploy your methods to the Kumulos API servers so they can be called from your mobile app.


Creating and Editing KScripts

Adding a KScript Method

To add a new KScript method, click the KSCRIPT <> button next to 'Create some KScript' in the right hand pane or, click on the IDE ^ icon next to 'Open the IDE' in the right hand pane to open the KScript IDE and click on the orange add primary action button.

Title and Return Type

Give the method a title and decide whether you'll be returning objects (for example database records), or a number.

Add KScript Method

The method will be listed under 'New Unsaved KScript Methods' until you hit SAVE.

Input Parameters

Next you can add typed input parameters to the method. Each parameter should have a name and a type set for it. These parameters will be available to use from your KScript code from K.params.

Input Parameters

Writing Code

Now, you are all set to start writing your KScript method in the code editor. The INSERT button can be used to insert the alias (unique identifier) of any of your tables or other API methods for use with functions in your method. Press Ctrl + Space to see the auto-complete for any function.

Insert Table Alias

There are also buttons to SAVE, SAVE & RUN and DEPLOY your method.

Editing a KScript Method

To edit an existing KScript method in the inline code editor, click the edit/pencil icon under 'Actions' next to that method in the list. Alternatively, click on the IDE ^ icon next to 'Open the IDE' in the right hand pane to open the KScript IDE and click on the method.

Whenever you make any changes to a method, a black dot icon will appear next to that method in the IDE to indicate there are unsaved changes. The status bar will also show that the method has unsaved changes along with a button to 'Discard' these changes. If you do not wish to 'Discard' these changes, click SAVE.

Unsaved Changes

All edits to KScript Methods are written to your browser's local storage. If your network connection should be interrupted prior to saving, then the next time you edit that method, any unsaved changes will be retrieved from local storage so that they may be saved (or discarded) as appropriate.

Viewing Revision History

At the bottom of both the inline editor and the KScript IDE, you will see the current revision, identified by a version number that is increased each time you save the method.

If you click on the revision, you can view the revision history for this method showing the time and date that previous versions were saved. Clicking on each version will show that version of the method allowing you to retrieve any previous code as required.

Revision History

If a revision (for example revision 2) of the same KScript method is open in two browser tabs at the same time and edits made in the first browser tab are saved (thus creating revision 3), then should the second browser tab attempt to save any edits made there, a warning will prompt that a newer version of the script has been saved after you started editing, giving you the option to proceed and overwrite the newer version or to return and discard edits. This will avoid the situation where the same script is open in two different tabs/windows and a set of edits on an older revision overwrite edits made to a newer revision.

Newer Version

Don't forget to copy any code you have edited to add to the newer revision and the discard the older revision.

Deleting a KScript Method

To delete a KScript Method, simply click the minus icon under 'Actions' next to that method in the list and click 'OK' when prompted to confirm.


Function Reference

KScript has access to the parameters posted to the script, and a library of useful functions which can be used whilst writing your scripts. These methods are all exposed through the global variable K.

K.params

K.params will give you access to the parameters posted to your script. For example, a parameter called userID would be referenced as: K.params.userID.

K.params.has(key) will tell you if there is a non-empty value passed for the given key.

K.log(...)

It is possible to log information to the console during development of your KScripts. This can be useful for seeing what's going on at runtime. To log information to the console, call K.log with any value you want to log out. It will accept a variable number of arguments and will show them as an array in the log output on screen.

K.error(...)

This function is designed to log any arguments passed to it and also log a stack trace. It will also print the function that called the .error() method.This can be useful for tracing errors in KScripts.

K.JSON

This class replaces the standard Javascript JSON parser and is recommended for use in KScript methods. All of the usual methods included in the standard Javascript JSON parser are incuded such as K.JSON.parse(...) and K.JSON.stringify(...) etc.

K.stackTrace()

Calling this method will output a stack trace of function names and line numbers called up to this point.

K.callAPIMethod(title, paramsObject)

This method allows you to call existing API methods from inside the KScript, and then operate on the results. For example:

var results = K.callAPIMethod('getUserByID', {userID, K.params.userID});
// Do stuff with the results

K.setResponse(result)

This is how a KScript returns data to the API endpoint. You can pass objects, arrays of objects, or numbers to this method. For example:

var users = K.select('users').run();
K.setResponse(users);

This method does not halt execution of the script, so you should plan to generate a result object and set it as the response at the end of execution.

If you plan to return binary data (for example: from base64 encoded data fields), then you must use the K.setResponseTypeMap() method to tell the API servers which fields in the result set contain binary data.

failed(result)

This function can be used to check for failure of any database operations. It returns true or false depending on if the call failed or not.

include(alias)

You can include other KScript files at runtime with the include function. Any functions or global variables inside the included KScript file will be loaded into the current scope. For example:

// In _init_ KScript method
function init() {
  K.log('Initialising');
}

// In doSomething KScript method
include('_init_');
init();
K.setResponse(0);

throw

If you want to halt execution on an error, you can throw an exception:

var results = false;
if (failed(results)) {
  throw "Results failed!";
}

Database Functions

Note that when querying tables using any of the following methods, you must use the table alias rather than the table name.

K.query(sql, parameters)

This function allows you to run custom queries on your database. Queries should be written in the MySQL syntax and should use placeholders where you wish to use a value passed as a parameter. For example, if you wanted to retrieve a digest of all new or edited posts (using the timeCreated and timeUpdated magic fields) after a given UNIX timestamp:

// Check we have an integer for our timestamp...
var timeUpdated=parseInt(K.params.timeUpdated, 10);
if ( isNaN(timeUpdated) ) {
  throw("Invalid timeUpdated argument");
}

// Execute the query...
var results = K.query(
  "SELECT *             \
  FROM 1234_1234_posts  \
  WHERE ((timeCreated >= FROM_UNIXTIME(?)) OR (timeUpdated >= FROM_UNIXTIME(?)))",
  [timeUpdated, timeUpdated]
 );

 if (failed(results)) {
     // See what the SQL error was
     K.log(results.error);
 }

// Return the results...
K.setResponse(results);

Parameters will be bound in order, and the second argument must be an array. If you don't use any placeholders in your query then you should pass an empty array, like so: [].

Any records created via K.query(INSERT INTO...) will not populate the timeCreated field automatically - you must set this field to NOW() manually in your query.

Date/Time fields

In the above example, you can see that when querying the timeCreated and timeUpdated magic fields you must to use the FROM_UNIXTIME() function in your SQL query (as these fields are timestamps).

Date/Time fields on the other hand are signed integers that are intended to store dates and times as seconds relative to (i.e. before or after) the Unix epoch. You do not need to use FROM_UNIXTIME() when querying these fields.

However, please note that the getTime() method of the Javascript Date object will return timestamps in milliseconds and so you will need to divide this by 1000 before using in a query. For example, to get all events that have not yet started:

var seconds = Math.round(new Date().getTime() / 1000);
var results = K.query(
  "SELECT *             \
  FROM 1234_1234_events \
  WHERE start >= ?",
  [seconds]
);
K.setResponse(results);

K.select(table_alias)

K.select will return a select action object that can be used to query tables in the database. For example:

var select = K.select('1234_1234_users').filter('userID', K.params.userID);
var results = select.run();

Alternatively you can chain the .run() call:

var results = K.select('1234_1234_users').filter('userID', K.params.userID).run();

Filtering

It is possible to use the .filter() method on select, update and delete action objects to filter the data each action will operate on.

Multiple filters can be chained together (in logical AND) by chaining calls, like: .filter('userID', K.params.userID).filter(...)

The filter method returns the original select object so you can chain on a call to run() if you want.

Sorting

With the select action, it's possible to sort the records with a call to .sort(field, direction). field must be the lowerCamelCase name of a field in the table, and direction must be one of 'ASC' for ascending or 'DESC' for descending.

Limiting

With the select action, it is also possible to limit the records returned with a call to limit(n, offset) where n is the number of rows you want returned and offset is the starting row. For example:

// Get the total number of rows we want to iterate over
var rows=K.query( 'SELECT COUNT(*) AS total FROM 1_1_customers');

// Specify how many rows we want in each iteration
var limit=5;
var offset=0;

// Select rows in blocks of 5 until all rows have been selected
while ( offset < rows.first().total ) {
    // Select (at most) 5 rows
    var customers = K.select( '1_1_customers' ).limit(limit, offset).run();
    var customer;

    // Iterate over rows selected
    while(customers.hasNext()) {
        customer=customers.next();
        K.log( customer.customerID );
    }
    // IMPORTANT update offset (to avoid infinite loop)
    offset += limit;
}

Keep reading for more details and examples of result handling...

Result Handling

The K.query and K.select methods return array-like objects that can be iterated over and/or looped through and returned as responses. The following properties and methods are supported:

  • .length
  • .itemAtIndex(i)
  • .first()
  • .last()
  • .next()
  • .hasNext()
  • .push(object)

You can iterate through the returned rows like so:

var users = K.select('1234_1234_users').run();
var user;
while(users.hasNext()) {
  user=users.next();
  // Do something with user object e.g. set an additional property?
  user.expireCache = Date.now() + 3600;
}
// All user objects in users returned to the client now have an expiry time of one hour from now appended (i.e. we have advised the client they can cache the results of this query locally for one hour)
K.setResponse(users);

The .next() method returns the current item and then increments the iteration pointer (so the result set can only be iterated through once).

If you wish to loop through the result set multiple times within the same Kscript method, without re-running the query, then you can use the .length property and .itemAtIndex() as follows:

var users = K.select('1234_1234_users').run();
for (var i=0; i < users.length; i++) {
  K.log( users.itemAtIndex(i).name );
}

Data fields

If the table you are selecting from contains data fields (for example base64 encoded images), then we need to tell our API servers to treat these fields as binary and base64 encode them rather than JSON encode them. This is done with the K.setResponseTypeMap() method, which is used to set strict types for fields in a result set.

The available constants that you can use to set strict types are:

  • K.Types.BOOL
  • K.Types.DATA
  • K.Types.DATE_TIME
  • K.Types.FLOAT
  • K.Types.INT
  • K.Types.POSITIVE_FLOAT
  • K.Types.POSITIVE_INT
  • K.Types.STRING
  • K.Types.TEXT

In theory you could set the strict type for every field in the result set. However, it is only necessary to do this for data fields to tell the API servers to treat these fields as binary and base64 encode rather than JSON encode). This is explicitly done for drag and drop API methods, but you have to manually do this for KScript methods, for example:

var users = K.select('1234_1234_users').run();
K.setResponseTypeMap({backdrop:K.Types.DATA,profilePhoto:K.Types.DATA});
K.setResponse(users);

K.insert(table_alias, object)

This method creates a record in the database and returns the ID of the newly created record. For example:

var userID = K.insert('1234_1234_users', {username: K.params.username});

K.update(table_alias, object)

To update records, use this method. It can have filters chained on to it just as K.select. If you're updating, do remember to define filters. An example to update a user:

var result = K.update('1234_1234_users',
  {username: K.params.username}).filter('userID', K.params.userID).run();

The result is the number of updated records, not the number of matching records. So if you call an update and there's nothing to change, it will return 0.

K.delete(table_aias)

This method allows you to delete records from a table. If you call it with no filters, it will not run unless you also call .forceDeleteAll(). For example:

// Will run...
var deleteUser =
  K.delete('1234_1234_users').filter('userID', K.params.userID).run();
// Will throw an exception to the log...
var deleteAllUsers = K.delete('1234_1234_users').run();
// Will also run...
var deleteAllUsers = K.delete('1234_1234_users').forceDeleteAll().run();

This method returns the number of records deleted.

Security Functions

Hashing

KScript has md5() and sha1() available as global functions. These should be faster than JavaScript implementations of these functions.

Passwords

For storing passwords, bcrypt hashing functions are available. To store a password, use the K.sec.passwordHash() function:

var hashedPassword = K.sec.passwordHash(K.params.password);

var userID = K.insert('1234_1234_users', {name: K.params.name, email: K.params.email, password: hashedPassword});

K.setResponse(userID);

To verify a password, use the K.sec.passwordVerify() function as follows:

K.setResponse(null);

var users = K.select('1234_1234_users').filter('email', K.params.email).run();

if ( users.length === 0) {
    K.log( "Unknown email address!");
}
else {
    if ( K.sec.passwordVerify(K.params.password, users.first().password) ){
        K.setResponse(users.first());
    }
    else {
        K.log("Incorrect password!");
    }
}

While providing different error messages for unknown email address and incorrect password is a good user experience for your users, it does mean that a potential hacker could mine your users' email addresses. Therefore, you may wish to just provide a generic 'login failed' error.

Encryption

Kumulos offers symmetric encryption using AES. These functions are suitable for protecting secrets.

The following example shows how to encrypt some plaintext, and then decrypt it again.

var PASSWORD = 'secret';

var ciphertext = K.sec.aesEncrypt('Plans to take over the world!', PASSWORD);
K.log(ciphertext);

var plaintext = K.sec.aesDecrypt(ciphertext, PASSWORD);

if (failed(plaintext)) {
    // Decryption wasn't possible for some reason (wrong password/corrupted ciphertext)
}

K.log(plaintext);

Due to the increased length of ciphertext versus plaintext, we recommend storing encrypted strings in a Text field rather than a String field to avoid any possible truncation.

JSON Web Tokens (JWT)

Parsing a JWT

In order to integrate with a 3rd party user authentication service, such as Auth0, it is possible to verify JSON Web Tokens with KScript.

The following example will show how to create a reusable include to authenticate your KScript APIs.

First, create a script to parse & verify the JWT (_Jwt_Auth_):

// Create a parser using the given signature algorithm & secret
var parser = K.jwt.createParser(K.jwt.Signature.HS256, 'secret');
// Assume the JWT string is passed in a parameter called 'jwt'
var token = parser.parseAndVerify(K.params.jwt);

if (failed(token)) {
    throw 'Unauthorized!';
}

// Unpack any global variables you want from the token
var CURRENT_USER_ID = token.getClaim('uid', null);

Next, in any KScript you want to authenticate with a JWT, you can do:

include('_Jwt_Auth_');

if (CURRENT_USER_ID !== K.params.userId) {
    throw 'Unauthorized';
}

// Continue to use the passed parameters knowing they belong to the authenticated user

It is also possible to validate the issuer, subject, audience, or id claims in the token as they are being parsed:

...
var token = parser.parseAndVerify(K.params.jwt, {
    issuer: 'your-issuer',
    subject: 'user-123',
    audience: 'kumulos-app',
    id: 'your-expected-id'
});

All tokens are checked for expiry at time of parsing. All additional validation claims are optional.

Generating a JWT

To hand out authentication tokens for your app, it is possible to generate signed JSON Web Tokens with Kumulos.

The following example shows how to use the JWT builder interface to create a token for your app.

// Create a token builder using the given signing algorithm & secret
var builder = K.jwt.createBuilder(K.jwt.Signature.HS256, 'secret');

// Set some common claims
builder
    .setIssuedAt(Date.now() / 1000)
    .setExpiry((Date.now() / 1000) + 3600) // Expire in 1hr
    .setIssuer('https://your-app.com')
    .setSubject('1337-user');

// Add some custom claims
builder.addClaim('roleIds', [1,2,3]);

// Get the signed token as a string
var tokenStr = builder.getToken();

CSV File Handling

KScript makes it possible to work with CSV files. You can read & write CSV files from a temporary filesystem available to your script context.

Reading CSV Files

The following example shows retrieving a CSV file from an API and parsing records from it in a loop:

var httpClient = K.http.createClient('https://your.api');

var response = httpClient.get('/game-scores.csv');

if (failed(response) || response.isError()) {
    throw 'Failed to fetch file';
}

var file = K.fs.createTmpFile(response.getBody());

if (failed(file)) {
    throw 'Failed to create temp file with content';
}

var reader = K.csv.createReader(file, {
    // Assumes the CSV has 3 columns in the order specified
    // These fields will then be used to map objects from the columns in each row of the CSV file
    recordFieldNames: ['homeTeamName', 'awayTeamName', 'score']
});

if (failed(reader)) {
    throw 'Failed to create a CSV reader';
}

var row;
while ((row = reader.readRecord())) {
    if (failed(row)) {
        K.log(row);
        // row.details will contain the KScriptArray with the data that was read from the CSV row
        break; // Stop processing (or use continue to skip the broken row)
    }
    // Process the row object which will have the shape {homeTeamName, awayTeamName, score}
}

Options

The K.csv.createReader(file, options) method takes an optional configuration object:

{
    delimiter: ',',
    enclosure: '"',
    escape: '\\',
    // Array of columns in CSV file, will be used as object keys by the reader.readRecord() method
    recordFieldNames: []
}

Writing CSV Files

The following example shows writing lines to a temporary CSV file. This file can then be passed to other routines such as the FTP functions.

var file = K.fs.createTmpFile();

if (failed(file)) {
    throw 'Failed to create temp file';
}

var writer = K.csv.createWriter(file);

if (failed(writer)) {
    throw 'Failed to create CSV writer';
}

// Write a header row
writer.writeRow(['date', 'weather', 'rainfallMm']);

// Write a data row
writer.writeRow(['2017-01-01', 'Sunny', 0]);

// Upload the file somewhere?

Options

The K.csv.createWriter(file, options) method takes an optional configuration object:

{
    delimiter: ',',
    enclosure: '"',
    escape: '\\'
}

FTP Functions

KScript offers the ability to work with text files stored on remote FTP servers. The FTP functions can interoperate with the file handling functions already defined in KScript.

The following example shows the available FTP operations.

var client = K.ftp.createFtpClient('ftp.yourhost.com', 'yourUsername', 'yourPassword');

if (failed(client)) {
    throw 'Failed to connect to the FTP server';
}

// List the files on the remote
K.log(client.listFiles('.'));

// Download a file from the remote and open as a temporary file
var file = client.getFile('orders.csv');

if (failed(file)) {
    throw 'Failed to download & open the file';
}

// Maybe process the file

// Upload the file back with a different name
var uploaded = client.putFile(file, 'orders-new.csv');

if (failed(uploaded)) {
    throw 'Failed to upload the file';
}

// Delete the original file
var deleted = client.deleteFile('orders.csv');

if (failed(deleted)) {
    throw 'Failed to delete remote file';
}

Session Support

KScript introduces persistent sessions to Kumulos and allows you to use them in your apps. KScript sessions are in-memory key-value stores which are designed to be fast and simple to use.

K.session.has(key)

This method returns true if the session has a non-empty value for the given key.

K.session.remove(key)

If you need to unset a session variable, you can use this method. Subsequent accesses to the session key after removing it will return null.

K.session.destroy()

All session values stored for the user's session will be deleted.

By default, session values will persist for one hour after the session was last accessed.

K.session

To access and set values in the session, you can use this object. It behaves like the K.params object, but it allows setting values too. For example:

var response = 0;
if (!K.session.has('userID')) {
  var results = K.select('1234_1234_users').filter({
    username: K.params.username,
    passwordHash: K.params.hashedPassword
  }).run();

  if (!failed(results) && results.length == 1) {
    K.session.userID = results.itemAtIndex(0).userID;
    response = 1;
  }
  else {
    response = 0;
  }
}
else {
  // Already logged in
  response = 1;
}
K.setResponse(response);

Push Notifications

You can use the Kumulos Push service from KScript to trigger targeted push notifications programmatically.

K.push.newMessage()

The push notification builder allows you to construct a notification, add content and target installs, audience segments or channels. For example:

K.push.newMessage().broadcast().withContent('the title', 'the message').send();

Targeting

To send a broadcast notification or to target specific installs, segments or channels, use one of the following methods.

.toInstallIds(installIds)
.toUserIds(userIds)
.broadcast(broadcast = true)
.toSegmentId(segmentId)
.toSegmentUuid(segmentUuid)
.toChannelIds(channelIds)
.toChannelUuids(channelUuids)

See the relevant integration guide for your chosen platforms to see how to access the unique installation ID

Content

To add content to the push notification, you can use one of the following methods.

.inBackground(inBackground = true)
.withData(data)
.withContent(title, message)

Notification sound

To set notification sound, you can use the following method. Each parameter is the sound file to play instead of the default device notification sound. In order to disable vibration and sound for the notification, pass 'none'.

.withSound(iosSound : string): this;

Collapsing notifications

Collapse Id corresponds to the apns-collapse-id and the collapse_key options for APNS/FCM respectively. Setting a collapse Id allows the device operating system to group and flatten related messages, it can be set with the following method.

.withCollapseId(collapseId : string): this;

iOS Options

Badges

To increment the badge count, or to set it to a specific value, you can use the following method.

.withBadge(badgeType, badgeCount)

Category

To set the iOS category field, use the following method.

.withIosCategory(categoryString)

Android Options

High Priority

To set the priority flag and wake up Android devices, use this method.

.withAndroidHighPriority()

Sending on a schedule

To send the notification at a time in the future, set a schedule with the following method.

.withSchedule(sendAt, strategy, pastTimes)

Send

Finally, to send the notification, use the send() method, for example:

K.push.newMessage().broadcast().withContent('the title', 'the message').send();

Facebook SDK

It is possible to use the Facebook SDK through KScript if you want to add Facebook Login to your apps for example. In order to use the SDK in KScript you need to initialize it.

K.initFB(appID, appSecret, userAccessToken, graphVersion)

This initialization method sets up the Facebook SDK for use in your KScript. You need to pass in your facebook app details along with a valid user access token. This access token can be retrieved from a logged in user by using one of Facebook's various SDKs. With those items, you can initialize KScript's Facebook SDK like so:

var FB_APP_ID = '';
var FB_APP_SECRET = '';
K.initFB(FB_APP_ID, FB_APP_SECRET, K.params.fbAccessToken);

We recommend storing the access token in the KScript session after a successful login action.

It also makes sense to store this initialization in a KScript you can include whenever you need access to the Facebook SDK.

K.FB.api(path, method, data)

After initialization, the api() method can be used to query the Facebook graph API as the currently logged in user, in accordance with the permissions the user has granted your app. The results it returns are dependent on the query you issue.

This method can return an error which can be checked with the failed() function.

You can learn more about the Graph API in the Graph API Reference.

An example that retrieves the user's profile information is shown:

// In setFacebookAccessToken
K.session.fbAccessToken = K.params.fbAccessToken;

// In _facebook_
var FB_APP_ID = '';
var FB_APP_SECRET = '';
// Assume session storage of access token set by setFacebookAccessToken
K.initFB(FB_APP_ID, FB_APP_SECRET, K.session.fbAccessToken);

// In getFacebookProfile
include('_facebook_');

var userProfile = K.FB.api('/me?fields=id,name,gender');

if (failed(userProfile)) {
  K.log('Failed...');
  // Handle as needed
}

K.log(userProfile);
K.setResponse(userProfile);

Convenience functions are also provided for each HTTP method:

  • K.FB.get(path)
  • K.FB.post(path, data)
  • K.FB.put(path, data)
  • K.FB.delete(path)

K.FB.getUserId()

This method returns the currently logged in user's FBUID. The logged in user is determined by the currently initialized access token passed to K.initFB().

AWS SDK

It is possible to use the Amazon SDK through KScript if you want to, for example, upload files to S3 service or sign Amazon CloudFront URLs.

Amazon S3

You can upload/download files to/from S3 buckets.

Upload files to S3

To upload a file to S3 you need to, first, create an S3 Client passing region in which your bucket is located and Access Key ID and Secret Access Key. The complete example is shown below.

//get S3 Client
var s3client = K.aws.createS3Client(region, accessKey, secretKey);

//upload file
var data = 'hello world';

var params = {
     bucket: 'my-bucket',
     body : K.fs.createTmpFile(data),
     key : 'text.txt',
     contentType: 'application/json'
 };
var result = s3client.putObject(params);

if (failed(result)) {
    K.log('Failed...');
}

Note that the only accepted parameters are

  • bucket (name of the S3 bucket. Required.)
  • body (contents of the file being uploaded, must be a KFilePointer. Required.)
  • key (name of the file created on S3, i.e. AWS object key. Required.).
  • contentType (A standard MIME type describing the format of the object data. Defaults to 'application/octet-stream')

As usual you can check success of the operation with failed function.

Download files from S3

//get S3 Client
var s3client = K.aws.createS3Client(accessKey, secretKey, region);

//download file
var params = {
    bucket: 'my-bucket',
    key : 'text.txt'
};
var result = s3client.getObject(params);

if (failed(result)) {
    K.log('Failed...');
}
else{
    var contents  = K.fs.read(result);
}

Note that you cannot download files larger than 10 MB.

Amazon CloudFront

You can sign CloudFront URLs using canned policy.

Sign CloudFront URLs

First, create a CloudFront client passing region to connect to. Next, sign it using getSignedUrl method. Complete example is shown below.

//create CloudFront client
var cloudFrontClient = K.aws.createCloudFrontClient(region);

//sign url
var signedUrl = cloudFrontClient.getSignedUrl(url, expires, privateKey, keyPairId);

if (failed(signedUrl)) {
    K.log('Failed...');
}

getSignedUrl method accepts 4 parameters.

  • url is the link you wish to sign.
  • expires is a unix timestamp indicating expiration time of the link.

In order to sign URLs you need to obtain a CloudFront key pair.

  • privateKey is the contents of your private key file. To deal with linebreaks, you may encode the private key to base64, paste it to the KScript editor and decode it before using. KScript exposes base64_decode function.
  • keyPairId is the Access Key ID of your key pair.

HTTP Client

Kumulos includes an HTTP client so you can make web requests to any web accessible API (such as Mailgun or Mandrill).

Creating an instance of a HTTP Client

You can create an instance of a blocking HTTP client by calling the K.http.createClient() method and passing in the baseURI (and optional timeout configuration object).

var client = K.http.createClient('https://api.external-service.com', {timeout: 10});

Similarly, you can create a streaming HTTP client by calling K.http.createStreamingClient() and passing in the baseURI (and optional timeout configuration object).

Making a request

The HTTP client has methods for head, get, post, patch, put and delete requests to a URI relative to the baseURI with given headers and parameters. For example, to perform a post request to an API using BASIC authentication.

Remember you can press Ctrl + space to see the auto-complete for any function.

var client = K.http.createClient('https://api.external-service.com', {timeout: 10});

var username = 'username for external API service';
var password = 'password for external API service';
var auth = base64_encode(username + ':' + password);

var headers =
{
  'Authorization': 'Basic ' + auth,
};

var params =
{
  'param1': 'Hello',
  'param2': 'World'
};

var response = client.post('api/v1/endpoint', params, headers);

If the API expects JSON then use K.JSON.stringify(params) to encode the parameters.

Parsing Results

The HTTP client will return a response object with the following methods:

  • isSuccess()
  • isError()
  • getStatusCode()
  • getHeader(header)
  • getHeaders()
  • getBody()

The isSuccess() method will return true if the response had a status code in the range 200 - 299. The isError() method will return true if the status code is outside of this range.

If the API you are calling returns JSON, you can parse the results of the getBody() method using JSON.parse(). For example, if you want to allow users to login using their Google Identity, you could do something like:

var client = K.http.createClient('https://www.googleapis.com', {timeout: 10});

var response = client.get('oauth2/v1/userinfo?alt=json&access_token=' + K.params.userAccessToken);

// Handle errors
if (failed(response)) {
    K.log(response);
}
else {
    var data = JSON.parse(response.getBody());
    K.log(data);
}

Overriding default timeout

The default timeout for the HTTP Client is 2 minutes (120 seconds). You can pass in an optional configuration object to override the timeout as follows:

var httpClient = K.http.createClient('https://httpbin.org/', {timeout: 5});

var response = httpClient.get('delay/10');

K.log('start');
if (failed((response))) {
    K.log(response);
}
else {
    K.log(response.getBody());
}
K.log('end');

Analytics

You can access analytics events from KScript.

Event stream

The following example shows how to iterate over the analytics event stream, reading 10 events for an app. Events are ordered by the time (UTC) that they happened.

var options = {
    eventTypes: ['k.*'],
    happenedBetween: {
        '$gte': new Date("01 01 2001,14:52:39").toISOString(),
        '$lte': '2025-08-01T14:00:00.000Z'
    },
    include: ['data', 'user', 'userAttributes'],
    filter: {
        'data.proximity': {'$eq': 2}
    }
};

var iterator = K.analytics.createEventStreamIterator(options);
if (failed(iter)){
   throw iterator.error;
}

var max = 10;
var count = 0;
while (iterator.hasNext() && count < max){
    K.log(iterator.next());
    count++;
}

As can be seen in the example above, the iterator exposes the hasNext() and next() methods.

Options

K.analytics.createEventStreamIterator(options) takes an optional configuration object:

{
    // Filter events by event type
    // '*' denotes a wildcard and on its own resolves to no filtering
    eventTypes: ['product.viewed', 'k.core.*'],
    // Filter events by limiting date range
    // You can specify lower and/or upper bounds (inclusive)
    happenedBetween: {
        // Include events that happened after the specified ISO8601 date
        '$gte': new Date("01 01 2014,14:52:39").toISOString(),
        // Include events that happened before the specified ISO8601 date
        '$lte': '2018-08-01T14:00:00.000Z'
    },
    // Include additional data with events. Possible values are:
    // 'data'(event data)
    // 'user'(the uuid string of the user which was logged in when the event happened)
    // 'userAttributes'(attributes of the respective user)
    include: ['data', 'user', 'userAttributes'],
    // Filters events by event data and/or user attributes.
    // Available comparison operators are:
    // '$eq'(=), '$lt'(<), '$gt'(>), '$lte'(<=) and '$gte'(>=)
    // Right operand must be a string, a number or a boolean.
    // Filter keys must start with 'data.' or 'user.'
    filter: {
        'data.location.state': {'$eq': 'NY'},
        'user.age': {'$lt': 30},
        'user.gender': {'$eq': 'M'}
    }

}

User attributes

You can access user attributes via K.users.getAttributes. This can be useful when running a KScript method as an action in an Automation where the userId will be passed in as a parameter. For example: to get the users email address and name...

var attributes = K.users.getAttributes(K.params.userId);
if (!attributes.has("email")) {
    throw ("User does not have an email address.");
}

var greeting = "Hi";
if (attributes.has("name")) {
    greeting = "Hi " + attributes.get("name");
}

Automation

Automations allow you to use events as a trigger to fire actions. For example, send a confirmation email when a user completes checkout process. An automation is comprised of an availability period (for time-limited promotions) and one or more rules. Each rule is then in turn comprised of a trigger (audience and event) and one or more actions. An action can be to run a Kscript method. Please see the Automation docs for more details).

Scheduled KScripts

Kumulos includes a robust scheduling engine that can be used to run KScript methods every minute or every hour. If you wish to schedule a recurring KScript, then please contact technical support who will be happy to schedule it for you.

Known Issues

  • Unterminated strings in a KScript may result in "Built undefined API methods" and break the deployment process (look for unterminated strings in your code to fix this issue)
  • include(alias) currently has no guards on cyclic includes and as such, methods may crash if the same script is included twice
  • Calling an API method with K.callAPIMethod(alias, paramsObject) may impact the integrity of the K.params object. If encountered, refactor the logic into a helper function and use the include(alias) method to include and call the function.
  • Using a ? placeholder for integer parameters (such as those passed to LIMIT SQL statements) doesn't work because it adds quotes to the integer value. As a workaround, you can use string concatenation in your query and parseInt on the input parameter to check it is sensible.
  • Renaming KScript methods doesn't check that the name is available first, so you can have two methods with the same name

Reporting Bugs

If you experience any issues using KScript other than those listed above, please contact technical support and include as much information as possible about how to reproduce the error.