Callable Functions - LiquidAnalytics/ld-api-examples GitHub Wiki
##Callable Functions
Callable Functions allow the functionality of LiquidServer to be expanded with custom event driven or schedule driven microservices.
##Terminology
- Callable Function - A small program written in Node.js (javascript) that follows certain conventions outlined below, that can be executed in response to various system events in order to fulfill some piece of business logic.
- system.Trigger - A common ItemSchema available in all communities. Items of this type can cause Callable Functions to be executed on demand in response to an item being created, updated or deleted.
- system.ScheduledTrigger - A common ItemSchema available in all communities. Items of this type can cause Callable Functions to be executed based on date interval filters that can be applied to the data of a given item type.
- system.ScheduledTask - A common ItemSchema available in all communities. Items of this type can cause Callable Functions to be executed based on a static schedule.
Callable Function Details
Currently we support callable functions defined in Node.js. When creating a callable function, any existing Node.js libraries can be used and leveraged to make development of your callable functions easier.
Deployment Limits
- The maximum size of any Callable Function deployment package (.jar/.zip file) is 50MB.
- The maximum size of code and dependencies (uncompressed .jar / .zip) is 250MB.
Resource Limits
- The maximum ephemeral disk space that can be used in the course of a Callable Function being executed is 512MB.
- The maximum number of file descriptors that can be used at once by a Callable Function is 1024.
- The maximum number of processes and threads that can be used at once by a Callable Function is 1024.
- The maximum execution duration of a Callable Function is 60 seconds.
- The maximum request payload that can be sent to a Callable Function is 6MB.
Keeping the above limits in mind, you may first need to decide whether a Callable Function is suitable for the task at hand. If complex calculations or lengthy communication with outside systems is a standard part of the business logic you are trying to implement, and it is unlikely that 60 seconds will be enough for the individual execution of any single run of a Callable Function, then other options may need to be explored for implementation of the business logic.
Code Template
A default code template for Callable functions is available for download in Mission Control. Navigate to the Callable Functions Panel inside the configurations tab, and you'll see a "Download Code Template Button"
Pressing this button initiates the download of a zip file which contains the template code for a Hello World! example.
We recommend unzipping the zip file to a convenient location, and opening up the directory in your javascript IDE of choice in order to continue working on the code.
Inside the zip, you'll find a number of files
- itemTrigger.js
- ldsAPI.js
- function.js
- package.json
- node_modules
function.js is where modifications to the Callable Function should begin. It contains a function called "handleEvent", which is where implementation of the business logic of the Callable Function should start.
itemTrigger.js and ldsAPI.js should not be altered, but can be examined to see what is available.
itemTrigger.js sets up the input to the Callable Function in a standard way and then makes a call to the handleEvent function inside function.js.
ldsAPI.js contains implementations of publicly available API's that can be made to the LiquidServer, that may be helpful or necessary in the logic of the Callable Function.
package.json is the standard way to define modules for a Node.js project. See https://nodejs.org/api/modules.html for more information on node.js modules.
The package.json that comes in the code template has some dependent modules already defined. Additional Node.js modules can be added to package.json as needed. Once package.json is updated with new modules, the npm utility can be used to install the new modules to node_modules directory. (https://docs.npmjs.com/getting-started/what-is-npm)
You can add additional files if it is helpful from a code organization stance, however the "handleEvent" function in function.js will always be the entry point for the function call. From there code present in other files that you added to the project can be used the same as in any other node.js project.
function.js
The default code for function.js from template is:
function handleEvent(context) {
console.log("Hello World");
//business logic starts here
context.succeed();
}
exports.handler = function (context) {
handleEvent(context);
};
You'll see the that the handleEvent method takes in an object called context. context is used to refer to the input of the Callable Function as well as to access the functions defined in ldsAPI.js for calling Liquid Server API's.
context
The following references can be made from context:
- context.event.triggerItem.newVersion - If this function was executed by a Trigger or ScheduledTrigger, this refers to the item that the Callable Function was executed in response to. This would not be populated for a function executed from a ScheduledTask
- context.event.triggerItem.previousVersion - If this function was executed by a Trigger or ScheduledTrigger, this refers to the previous version of the item the Callable Function was executed in response to. This may not be populated in the case that the item that triggered the function call has no previous version (i.e. it is newly created). This is also not populated for a function executed from a ScheduledTask
- context.event.relatedItems - A reference to a map of relatedItems that may have been sent to the function as input based on Trigger or ScheduledTrigger configuration. (see relatedItems below)
- context.event.parameters - A json map of parameters that can be configurably passed to the callable functions. (see parameters below)
- context.response.create - an array of items that should be created as a result of the function execution
- context.response.update - an array of items that should be updated as a result of the function execution
- context.response.delete - an array of items that should be deleted as a result of the function execution
- context.api - the collection of common functions defined in LDSApi.js that wrap Liquid Server api calls
- context.succeed() - the function to be called when processing is done
Example
Here is a quick example with comments to help demonstrate what has been defined so far, and then below we will dive deeper into how this is all configured.
At a high level this function would be setup to trigger when a Contact item is updated, and create an Insight item as a result
function handleEvent(context) {
console.log("Starting Function"); //logging
context.response.create = []; // initialize the context.response.create array
var contactItem = context.event.triggerItem.newVersion; // get the Contact item that triggered the function call
var dueDate = new Date(); //create a dueDate for the Insight we will create
dueDate.setMonth(dueDate.getMonth() + 1); // set it to 1 month in the future
var insightItem = { //define the json of the insightItem we are creating
"headers": {
"category": "entity",
"type": "Insight",
"state": "AdminUpdate"
},
"data": {
"insightId": "Insight-" + contactItem.data.contactId, // we can reference any data or header fields available on the item passed into the trigger
"status": "open",
"style": "contactStyle2",
"subject": "Contact has been created for: " + contactItem.data.workEmail,
"userId": contactItem.data.workEmail,
"dismissed": false,
"dueDate": dueDate.toISOString()
}
}
context.response.create.push(insightItem); // add the insightItem to the context.response.create array
console.log("Ending Function");
context.succeed(); // call context.succeed() to signify processing has completed
}
exports.handler = function (context) {
handleEvent(context);
};
After context.succeed is called, the Liquid Server that initiated the execution of the Callable Function will receive the result of the function call. In this case, the result will include the Insight item added to the context.response.create array. Liquid Server will process any items in those arrays when the context.succeed() is called, and will process the items with the corresponding action depending on what array they were placed in (create / update / delete).
Creating, Updating and Deleting Callable Functions
Callable Functions can be created, updated, deleted entirely inside Mission Control.
Creating a new Callable Function
There are 2 methods for creating a brand new callable function.
1. On the Callable Function panel, hit the Create New Function button. This will initiate a dialog asking for some details of the function you are creating:
Fill out the name and description for your function, ignore the "Choose File" button and hit Create. A dialog will pop up telling you that since you did not provide a zip file, the default code template will be used to create the function, confirm this.
At this point you have created a new callable function that executes Hello World! when called. Now that the function exists, it can be updated further with real logic. See "Updating a Callable Function" below.
2. Download a code template and unzip it to some directory as detailed above. Modify the code in function.js. When you are done, rezip the directory with the updated code so it has the same structure as the template you downloaded. Now follow the steps detailed above however when the Create New Function dialogue comes up, make sure to hit the "Choose File" button and select the zip file you created. Now hit Create.
At this point you have created a new callable function that executes the code you packaged in your zip file.
Updating an existing Callable Function Code
There are 2 methods for updating an existing callable function
1. On the Callable Function panel in Mission Control, right click on the existing callable function you would like to modify, and hit "Edit Code Inline". This will download the existing code for the selected callable function, and display the contents of function.js in the text area to the right.
From here you can modify the code directly, and when you are done, use the "Save Changes" or "Discard Changes" buttons to save or discard the changes you made. If you chose to save the changes, what will happen is that all the existing code other than the contents of function.js will remain the same, and the contents of function.js will be replaced with whatever is in the text panel.
2. If you are more comfortable making changes in a javascript IDE, or you need to make changes to files other than function.js that you may have added during the development process, you can download the complete current code for your function by right clicking on the existing callable function and selecting "Download Function"
This will download a zip file containing the up to date version of the function, which you can unzip and open up in an IDE. You can now make any modifications to the code you'd like, and once you are done, rezip the directory so it has the same structure as the template you downloaded. Next, in Mission Control right click on the function and select "Update Code"
Select the zip file with the updated code you have created. Once uploading has completed, the callable function now has all the updates you juts made.
Update an existing Callable Function Configuration
You can make some minor updates to an existing callable functions configuration. For example, you can update the description to be more accurate, or update the Timeout Time, which controls the amount of time the callable function has to run before timing out (subject to the limits described above), or the Memory Size the callable function has to work with during execution (subject to the limits described above).
To make these changes, right click on the existing Callable Function, and select "Update Configuration". A dialogue will popup allowing you to make changes and make the update.
Upgrade an existing Callable Function to latest template
From time to time, updates will be made to the default code template that is provided for download. These changes may consist of, but are not limited to:
- Additional helper function to ldsAPI to handle new API calls to Liquid Server.
- Additional node.js modules to the default code template
- Additional input that can be used and referenced from the context object that is passed into function.js "handleEvent" function
By updating the code template, existing callable functions will not be automatically modified in any way. If a callable function had been created at an earlier time, it will continue to function as normal, but it will not have access to any of the additions that have been made to the template.
In Mission Control, to update an existing callable function to have the latest template code, find the callable function in the Callable Function panel, right click and select "Upgrade To Latest Template", and confirm the change.
This action will take the latest code template, and copy it into the latest code from the Callable Function you selected. The contents of function.js will remain untouched, as well as any other files you may have added in the course of development, however everything else that comes in the code template will be updated to the latest available versions.
In general, any updates made to the code template will strictly be additions that should not cause any backwards compatibility issues with existing callable functions, so it will always be safe to update an existing callable function to the latest template code.
Delete a Callable Function
To delete a callable function, go to the Callable Function panel in Mission Control, right click on the function you would like to delete, and select "Delete Function". This will delete the Callable Function. Any Trigger, ScheduledTrigger or ScheduledTask items that may have made calls to the now deleted Callable Function will cease to work.
Configuring Callable Functions to be Executed
Now that we have gone over the basics of creating a callable function, we will cover how you can configure these callable functions to actually run.
###system.Trigger
system.Trigger is a common schema available in every community. Trigger items dictate Callable Functions that should be executed in response to items of a certain type being created, updated or deleted.
The system.Trigger schema is defined as follows:
{
"type": "Trigger",
"fields": [
{
"name": "name",
"primary": true
},
{
"name": "triggerFunction"
},
{
"name": "itemType"
},
{
"dataType": "enum",
"name": "triggerType"
},
{
"enumClass": "com.liquidanalytics.entity.ItemState",
"name": "itemState"
},
{
"enumClass": "com.liquidanalytics.entity.ItemAction",
"name": "itemAction"
},
{
"dataType": "boolean",
"name": "global"
},
{
"dataType": "jsonElement",
"name": "itemFilter"
},
{
"listElementType": "block",
"dataType": "list",
"name": "relatedItems",
"blockType": "RelatedItems"
},
{
"dataType": "jsonElement",
"name": "parameters"
},
{
"dataType": "boolean",
"name": "enabled"
},
{
"environmentsList": true,
"listElementType": "block",
"dataType": "list",
"name": "environments",
"blockType": "TriggerParamsOverride"
}
],
"permissionType": "None",
"category": "system"
}
The most important fields on system.Trigger for configuring a callable function are
- name - required field, the primary key of the Trigger schema, must be unique and should be descriptive in regards to what this trigger is for
- itemType - the type of item that this Trigger responds to. Must reference an item type with an existing Schema.
- triggerFunction - references the name of the Callable Function that this Trigger item will call in response to item updates of the specified itemType. Must reference an existing Callable Function name as can be viewed in the Callable Function panel in Mission Control
- enabled - a boolean field dictating whether the trigger is enabled or not. Callable functions will only be executed if enabled is set to true
These 4 fields are the minimum necessary to have a working Trigger execute a Callable Function.
There are additional fields that are not required, but may be helpful or necessary depending on the criteria that should be used to decide when your callable function should execute.
- itemState - a filter on itemState, by specifying itemState, we limit the items that can be used to trigger a Callable Function execution to items that have the specified state as a header value
- itemAction - same as itemState, except filtering for items that have the specified action as a header value
- itemFilter - a json map that represents field / value pairs used to filter the items that should be sent to the Callable Function for execution
- relatedItems - a list of RelatedItems blocks, which can be used to gather related items that should be sent to Callable Functions as input available to be referenced from "context"
- parameters - a json object that can be defined containing default parameters that should be sent to the Callable Function as input, available to be referenced from "context"
Example Trigger
Here is a quick example of a system.Trigger item to demonstrate what has been defined so far:
{
"headers": {
"type": "Trigger",
"category": "system"
},
"data": {
"name": "ContactInviteTrigger",
"itemType": "Contact",
"triggerFunction": "ContactInvite",
"enabled": true
}
}
This example shows a Trigger item, with name "ContactInviteTrigger", that acts on items of type "Contact", with triggerFunction set to "ContactInvite". Since enabled is set to true, what this means is, every time an item of type Contact is created, updated, or deleted, Liquid Server will execute the Callable Function named ContactInvite. The new and previous versions of the Contact item that was updated will be sent to the ContactInvite function as input. The new and previous version of the Contact item can be referenced in the javascript code as context.event.triggerItem.newVersion and context.event.triggerItem.previousVersion, respectively.
Now that we see how a simple example works, we will further define how to use itemFilterse, relatedItems and parameters, and then show a more complex example.
itemFilter
By adding an item Filter to a Trigger item, we limit which items will result in an execution of a Callable Function.
Here is an example of an itemFilter.
"itemFilter": {
"sendUserInvite": "true",
"contactType": "Supplier"
}
This means that when an item of the corresponding itemType for the Trigger is updated, if it does not have fields in its data matching sendUserInvite = true, and contactType = Supplier, no Callable Function will be executed. If it does have data that matches those fields, an execution of the Callable Function will occur.
In general, we want to apply itemFilters to our Triggers that will limit the number of calls made to Callable Functions as much as possible. It is true that such filtering can be done within the java script logic of the Callable Function that you define. In function.js at the beginning of handleEvent, you could put your own logic there that says if sendUserInvite is not true, and contactType is not equal to "Supplier" return immediately.
This would be extremely inefficient, imagine a system where millions of items are updated per day, however only 1% of those match the criteria that we would actually want to run our Callable Function business logic on. By putting the filter logic inside the callable function, we will make millions of needless calls to the Callable Function, 99% of which will return right away and do nothing. This would be a large waste of processing time and resources. Instead if we define the filter on the Trigger item, we can filter out items that we know should be ignored before the Callable Function is even executed, saving time and resources.
relatedItems
The relatedItems field on the Trigger schema is a list of blocks of type RelatedItems. The RelatedItems block schema is defined as follows:
{
"type": "RelatedItems",
"fields": [
{
"name": "itemType"
},
{
"name": "relationshipPath"
},
{
"name": "name"
}
],
"category": "block"
}
By defining RelatedItems on your Trigger item, you can have the server look up additional data to be sent to the Callable Function as input, in addition to the trigger item, to be used in the logic of the Callable Function.
- itemType - the type of the relatedItems that will be sent along
- relationshipPath - A "." separated list of relationships that begin with the itemType of the trigger, and end with the itemType of the RelatedItems block
- name - optional label that can be used to reference the relatedItems from the javascript code. If name is not specified, this defaults to itemType
Example Trigger with Related Items
A trigger could be defined as follows:
{
"headers": {
"type": "Trigger",
"category": "system"
},
"data": {
"itemType": "Shipment",
"name": "ShipmentInsightTrigger",
"triggerFunction": "ShipmentTrigger",
"enabled": true,
"relatedItems": [
{
"itemType": "Site",
"relationshipPath": "SiteToShipmentDestination"
},
{
"itemType": "Account",
"relationshipPath": "ShipmentToMaterial.MaterialToSupplierAccount"
},
{
"itemType": "Material",
"relationshipPath": "ShipmentToMaterial"
}
]
}
}
This trigger is for items of type Shipment. Under relatedItems we have 3 RelatedItems blocks defined. The first one finds related Site items, using the relationship SiteToShipmentDestination. The second one finds related Account items using the relationship path ShipmentToMaterial.MaterialToSupplierAccount. The third one finds related Material items using the relationship ShipmentToMaterial.
If an item of type Shipment is updated, prior to the Callable Function being executed, the Liquid Server will take the Shipment item that has been updated, and follow each of the relationship paths defined in the 3 RelatedItems blocks in order to find relatedItems that can be used in the logic of the Callable Function. In this example, because "name" is not specified on any of the RelatedItems blocks, the itemType will be used to reference the relatedItems in the javascript code.
In the javascript, context.event.relatedItems.Site would refer to an array of related Site items that were found using the relationshipPath defined above. context.event.relatedItems.Account would refer to an array of Account items, and context.event.relatedItems.Material would refer to Material items.
Using names on the RelatedItems block can be useful for more descriptively describing the related items.
{
"headers": {
"type": "Trigger",
"category": "system"
},
"data": {
"itemType": "Shipment",
"name": "ShipmentInsightTrigger",
"triggerFunction": "ShipmentTrigger",
"enabled": true,
"relatedItems": [
{
"name": "Destination",
"itemType": "Site",
"relationshipPath": "SiteToShipmentDestination"
},
{
"name": "OEM",
"itemType": "Account",
"relationshipPath": "ShipmentToMaterial.MaterialToProviderAccount"
},
{
"name": "Supplier",
"itemType": "Account",
"relationshipPath": "ShipmentToMaterial.MaterialToSupplierAccount"
},
{
"name": "Materials",
"itemType": "Material",
"relationshipPath": "ShipmentToMaterial"
}
]
}
}
In this example which is very similar to our first example, each of the RelatedItems blocks has a name specified. You'll also notice that 2 of the RelatedItems blocks have itemType set to Account with different relationshipPaths defined for each. In this case setting the name value on the block is required to distinguish between them. In javascript code, you would refer to the four arrays of relatedItems in order as:
- context.event.relatedItems.Destination - array of Site items
- context.event.relatedItems.OEM - array of Account items found using the path ShipmentToMaterial.MaterialToProviderAccount
- context.event.relatedItems.Supplier - array of Account items found using the path ShipmentToMaterial.MaterialToSupplierAccount
- context.event.relatedItems.Materials - array of Material items
parameters
By setting parameters on a Trigger item, you are defining some static input that will be provided to the Callable Function as input.
Example Trigger with parameters specified
{
"headers": {
"type": "Trigger",
"category": "system"
},
"data": {
"name": "ContactInviteTrigger",
"itemType": "Contact",
"triggerFunction": "ContactInviteTrigger",
"enabled": true,
"parameters": {
"param1" : "Val1",
"param2" : "Val2"
}
}
}
When the ContactInviteTrigger Callable Function is executed from this example Trigger, the javascript code will be able to reference these parameters from
- context.event.parameters.param1 and
- context.event.parameters.param2
and they will be set to the values defined in the Trigger item. These parameters can be used to control certain logic within your Callable Function, and rather than hardcoding values, you can pass them in as parameters. The parameters can then be adjusted through updates to the Trigger item, without actually touching the Callable Function code.
A possible use for this is you may want to make function calls in a DEV environment with different parameters than what you would use to call it in a QA or PROD environment. In this way the same function can behave differently in different environments due to the parameters that are passed in.
See https://github.com/LiquidAnalytics/ld-api-examples/wiki/Item-Environments-List for more information on how to override an item to have different values in different environments.
Example Trigger using all of the above
{
"headers": {
"type": "Trigger",
"category": "system"
},
"data": {
"name": "ContactInviteTrigger",
"itemType": "Contact",
"triggerFunction": "ContactInviteTrigger",
"enabled": true,
"itemFilter": {
"sendUserInvite": "true",
"contactType": "Supplier"
}
"parameters": {
"param1" : "Val1",
"param2" : "Val2"
},
"relatedItems": [
{
"name": "RelatedUsers",
"itemType": "User",
"relationshipPath": "ContactToUser"
},
"itemState" : "TransactionCompleted",
"itemAction": "Create"
}
}
The above Trigger would execute the Callable Function ContactInviteTrigger if a Contact item is updated that matches the criteria of:
- sendUserInvite data field is set to true
- contactType data field is set to Supplier
- state header field is set to TransactionCompleted
- action header field is set to Create
If all of the above criteria does not match, nothing will happen. If it does match, a call to ContactInviteTrigger will be made. The Contact item itself will be accessible in javascript through
- context.event.triggerItem.newVersion and
- context.event.triggerItem.previousVersion
The related User items found using relationship path ContactToUser will be accessible through
- context.event.relatedItems.RelatedUsers
The parameter values will be sent to the Callable Function and can be referenced as
- context.event.parameters.param1 and
- context.event.parameters.param2
###system.ScheduledTrigger
system.ScheduledTrigger is a common schema available in every community. ScheduledTrigger items dictate Callable Functions that should be executed based on when the current date passes some delta from the date field on an item.
A simple example to better explain this concept is, imagine we have Product items that have a date field called expirationDate. We want some Callable Function to execute when the current date is within one month of any given products expirationDate, in order to warn the users that a Product is expiring soon. This can be configured with ScheduledTriggers.
####ScheduledTrigger vs. Trigger The main difference between ScheduledTriggers and Triggers is, Triggers are used to execute Callable Functions at the moment some Item is created/updated/deleted. A ScheduledTrigger does not need an item to be updated in order to execute, it identifies items that should lead to a Callable Function execution based on the Date Interval Filter specified, and schedules them to execute at the right time.
The system.ScheduledTrigger schema is defined as follows:
{
"type": "ScheduledTrigger",
"fields":
[
{
"name": "name",
"primary": true
},
{
"name": "callableFunction",
"dataType": "string"
},
{
"name": "itemType",
"dataType": "string"
},
{
"name": "itemState",
"dataType": "string",
"enumClass": "com.liquidanalytics.entity.ItemState"
},
{
"name": "itemAction",
"dataType": "string",
"enumClass": "com.liquidanalytics.entity.ItemAction"
},
{
"name": "itemFilter",
"dataType": "jsonElement"
},
{
"name": "parameters",
"dataType": "jsonElement"
},
{
"name":"enabled",
"dataType":"boolean"
},
{
"name": "cooldownTime",
"dataType": "int"
},
{
"name": "cooldownTimeUnit",
"dataType": "string",
"enumClass": "com.liquidanalytics.managementmetrics.AggregationTimeUnit"
},
{
"name": "dateIntervalFilters",
"dataType": "list",
"blockType":"DateIntervalFilter",
"listElementType": "block"
},
{
"name": "relatedItems",
"dataType": "list",
"listElementType": "block",
"blockType": "RelatedItems"
},
{
"environmentsList": true,
"listElementType": "block",
"dataType": "list",
"blockType":"TriggerParamsOverride",
"name": "environments"
}
],
"category": "system"
}
Many of the fields on the ScheduledTrigger schema have the same names and meanings as those on the Trigger schema.
The most important fields on system.ScheduledTrigger for configuring a callable function are
- name - required field, the primary key of the ScheduledTrigger schema, must be unique and should be descriptive in regards to what this trigger is for
- itemType - the type of item that this ScheduledTrigger is based off of. Must reference an item type with an existing Schema.
- callableFunction - references the name of the Callable Function that this ScheduledTrigger item will execute. Must reference an existing Callable Function name as can be viewed in the Callable Function panel in Mission Control
- enabled - a boolean field dictating whether the trigger is enabled or not. Callable functions will only be executed if enabled is set to true
- dateIntervalFilters - a list of DateIntervalFilter blocks, used to define the date fields of the itemType that we will be basing the execution schedule of the Callable Function on
- cooldownTime - The amount of time that must pass before the same item can be used for execution of the Callable Function
- cooldownTimeUnit - The time unit for the cooldownTime. Valid time units are Minute, Hour, Day, Week, Month, Year
These fields are all necessary to have a working ScheduledTrigger execute a Callable Function.
There are additional fields that are not required, but may be helpful or necessary depending on the criteria that should be used to decide when your callable function should execute. These all have the same exact meaning as for Trigger items, see above for details on these fields.
- itemState
- itemAction
- itemFilter
- relatedItems
- parameters
dateIntervalFilters
The dateIntervalFilters field on the ScheduledTrigger schema is a list of blocks of type DateIntervalFilter. The DateIntervalFilter block schema is defined as follows:
{
"type": "DateIntervalFilter",
"fields": [
{
"name": "dateFieldName"
},
{
"name":"intervalStartDelta",
"dataType":"int"
},
{
"name":"intervalEndDelta",
"dataType":"int"
},
{
"name":"timeUnit",
"dataType":"string",
"enumClass": "com.liquidanalytics.managementmetrics.AggregationTimeUnit"
},
{
"name":"acceptNull",
"dataType":"boolean"
}
],
"category": "block"
}
The fields on DateIntervalFilter mean the following
- dateFieldName - the name of the field on the schema of the itemType that this DateIntervalFilter will be based off of. Must reference a real fieldName of dataType DATE or DATETIME
- timeUnit - the time unit for the intervalStartDelta and intervalEndDelta. Valid time units are Minute, Hour, Day, Week, Month, Year
- intervalStartDelta - used in conjunction with timeUnit to calculate the beginning time range that an item would be applicable to run based on its dateField value
- intervalStartDelta - used in conjunction with timeUnit to calculate the ending time range that an item would be applicable to run based on its dateField value
- acceptNull - boolean that says whether a null value for the dateField should be applicable to run
DateIntervalFilter examples
To better illustrate how DateIntervalFilter work, here are a couple of examples. All of these are based off of an example Product schema, that has a date field called expirationDate
DateIntervalFilter example 1
...
“dateIntervalFilters”: [
{
“dateFieldName”:”expirationDate”,
“intervalStartDelta”: 0,
“timeUnit”:”Day”
}
]
...
This filter would mean that a Product item would be applicable to run when expirationDate > currentDate + 0 days. This means any Product with expirationDate in the past is not applicable to run, and any expirationDate in the future is appicable to run.
If a Product has expiration date of 2030-06-01 00:00:00. It would pass this DateIntervalFilter from now until that time, at which point it would no longer pass the date interval filter for all time going forward.
Note that since intervalStartDelta is set to 0, the timeUnit specified does not make a difference. This is only the case with a delta of 0.
DateIntervalFilter example 2
...
“dateIntervalFilters”: [
{
“dateFieldName”:”expirationDate”,
“intervalStartDelta”: 10,
“timeUnit”:”Day”
}
]
...
This filter would mean that a Product item would be applicable to run when expirationDate > currentDate + 10 days. This means any Product with expirationDate greater than 10 days from current time is applicable to run.
If a Product has expiration date of 2030-06-20 00:00:00. It would pass this DateIntervalFilter from now until 2030-06-10 00:00:00. After that point in time, the expiration Date is no longer greater than currentDate + 10 days, and it would no longer pass the date interval filter for all time going forward.
DateIntervalFilter example 3
...
“dateIntervalFilters”: [
{
“dateFieldName”:”expirationDate”,
“intervalStartDelta”: -10,
“timeUnit”:”Day”
}
]
...
This filter would mean that a Product item would be applicable to run when expirationDate > currentDate - 10 days. This means any Product with expirationDate is greater than 10 days ago, based on current time is applicable to run.
If a Product has expiration date of 2030-06-01 00:00:00. It would pass this DateIntervalFilter from now until 2030-06-11 00:00:00. After that point in time, the expiration Date is no longer greater than currentDate - 10 days, and it would no longer pass the date interval filter for all time going forward.
DateIntervalFilter example 4
...
“dateIntervalFilters”: [
{
“dateFieldName”:”expirationDate”,
“intervalEndDelta”: 0,
“timeUnit”:”Day”
}
]
...
This filter would mean that a Product item would be applicable to run when expirationDate < currentDate + 0 days. This means any Product with expirationDate in the past is applicable to run, and any expirationDate in the future is not appicable to run.
If a Product has expiration date of 2030-06-01 00:00:00. It would not pass this date interval filter until the expiration Date is reached, at which point it would pass the date interval filter for all time going forward.
Note that since intervalStartDelta is set to 0, the timeUnit specified does not make a difference. This is only the case with a delta of 0.
DateIntervalFilter example 5
...
“dateIntervalFilters”: [
{
“dateFieldName”:”expirationDate”,
“intervalStartDelta”: 1,
“intervalEndDelta”: 2,
“timeUnit”:”Day”
}
]
...
This filter would mean that a Product item would be applicable to run in the time range when expirationDate > currentDate + 1 day and expirationDate < currentDate + 2 days.
If a Product has expiration date of 2030-06-10 00:00:00, the product would be applicable for this filter between 2030-06-08 and 2030-06-09. Only between these 2 days is the expirationDate more than 1 day away and less than 2 days away.
Note that there is a difference between not specifying an intervalStart/EndDelta, and specifying the delta to 0. If it is not specified, that means the interval filter is not bound by the Start/End. If it is specified as 0, that means that the interval filter is bound by a Start/End of currentDate.
Valid DateIntervalFilters
For a DateIntervalFilter to be valid it must meet the following criteria:
- timeUnit must not be null, and must be set to a valid value
- one of intervalStartDelta or intervalEndDelta must be specified
- if both intervalStartDelta and intervalEndDelta are specified, intervalEndDelta must be greater than intervalStartDelta
- dateFieldName must reference a valid field on the schema of the itemType the ScheduledTrigger refers to, and that field must have dataType of DATE or DATETIME.
At least one valid DateIntervalFilter block must be specified on a ScheduledTrigger, otherwise the ScheduledTrigger itself is invalid.
DateIntervalFilters is an array, so multiple DateIntervalFilters can be defined on a single ScheduledTrigger in order to satisfy more complex criteria involving multiple date fields.
Cooldown Time
Due to the nature of DateIntervalFilters, a given item may be continuously applicable to run during a given time range. In DateIntervalFilter example 1 above, we said that a Product with expirationDate of 2030-06-01 00:00:00 would be applicable to run for all time from now until 2030-06-01. Since the item is continuously applicable to run, we need a way to define how often the Callable Function should actually be executed for this item in that given time range. This is what cooldownTime controls.
cooldownTime along with cooldownTimeUnit, specifies how much time must pass between successive executions of a Callable Function for a given item.
A valid cooldown time has
- cooldownTime > 0
- cooldownTimeUnit not null and set to a valid value.
If cooldownTime is found to be invalid, a default cooldownTime of 1 day will be used.
Full ScheduledTrigger example
{
"headers": {
"type": "ScheduledTrigger",
"category": "system"
},
"data": {
"itemType": "Product",
"name": "ProductExpirationScheduledTrigger",
"triggerFunction": "ProductExpiration",
"enabled": true,
“dateIntervalFilters”: [
{
“dateFieldName”:”expirationDate”,
“intervalEndDelta”: 0,
“timeUnit”:”Day”
},
"cooldownTime": 1,
"cooldownTimeUnit": "Month"
]
},
}
This ScheduledTrigger example means that, for any Product with expirationDate in the past (expirationDate < currentDate + 0), execute the Callable Function ProductExpiration for that Product. The Callable Function will not be executed more than 1 time per month for any individual Product.
Item Updates
If a ScheduledTrigger exists for a given item type, as items of that type are updated, Liquid Server calculates whether or not the update makes the item appicable or inapplicable to run based on the Date Interval Filters present. If the update makes the item applicable to run right now, and all other criteria to run the Callable Function is passed (cooldown time, itemState, itemAction, itemFilters, etc.) the Callable Function will be executed right away. If the update makes the item applicable to run not right now, but sometime in the future, it will be scheduled to run at the proper time.
###system.ScheduledTask
system.ScheduledTask is a common schema available in every community. ScheduledTask items dictate Callable Functions that should be executed based on a static cron schedule.
####ScheduledTask vs. Trigger and ScheduledTrigger The main difference between ScheduledTasks and ScheduledTriggers/Triggers is, ScheduledTriggers and Triggers are based off of items, ScheduledTasks are not. ScheduledTasks simply execute a callable function every so often based on a cron schedule. It is not tied to any individual item type, item updates, or the data on a given item, as ScheduledTrigger and Trigger are.
The system.ScheduledTask schema is defined as follows:
{
"type": "ScheduledTask",
"fields":
[
{
"name": "name",
"primary": true
},
{
"name": "callableFunction",
"dataType": "string"
},
{
"name": "cronExpression",
"dataType": "string"
},
{
"name": "parameters",
"dataType": "jsonElement"
},
{
"name":"enabled",
"dataType":"boolean"
},
{
"environmentsList": true,
"listElementType": "block",
"dataType": "list",
"blockType":"TriggerParamsOverride",
"name": "environments"
}
],
"category": "system"
}
The important fields on system.ScheduledTask for scheduling a callable function are
- name - required field, the primary key of the ScheduledTask schema, must be unique and should be descriptive in regards to what this task is for
- callableFunction - references the name of the Callable Function that this ScheduledTaskwill call based on the schedule. Must reference an existing Callable Function name as can be viewed in the Callable Function panel in Mission Control
- cronExpression - a cron expression representing the schedule with which this ScheduledTask should execute the Callable Function. (See http://www.quartz-scheduler.org/documentation/quartz-1.x/tutorials/crontrigger for more information on cron expressions).
- enabled - a boolean field dictating whether the ScheduledTask is enabled or not. Callable functions will only be executed if enabled is set to true
- parameters - same meaning as on system.Trigger, see above for details
ScheduledTask Example
{
"headers": {
"type": "ScheduledTask",
"category": "system"
},
"data": {
"callableFunction": "HelloWorld",
"cronExpression": "0 0 0/6 * * ? *",
"name": "HelloWorldTask",
"enabled": true
}
}
This ScheduledTask will execute the Callable Function "HelloWorld" every 6 hours.
Data passed in with ScheduledTasks
Since ScheduledTasks do not execute Callable Functions with regards to an item, in the javascript code,
- context.event.triggerItem.newVersion
- context.event.triggerItem.previousVersion
- context.event.triggerItem.relatedItems
will always be unpopulated. Since no data, other than parameters, is automatically sent to the Callable Function for a ScheduledTask, it is likely that any non trivial ScheduledTask based Callable Function would need to make use of Liquid Server apis in order to gather some data in order to accomplish useful work.
Multiple Triggers, ScheduledTriggers and ScheduledTasks per Callable Function
Due to the way our Trigger, ScheduledTrigger and ScheduledTask schemas are defined, the name of the Trigger or Task is stored separately from where we store the name of the Callable Function that the Trigger or Task will execute. Name is the primary key on these schemas, and so it is possible to have multiple Trigger or Task items defined that will all call the same Callable Function.
In this example, two separate Triggers reference the same Callable Function called "InsightTrigger". The Triggers are actually based on different item types as well, one Trigger is for items of type ScanCode, the other for Material items. This means that the InsightTrigger Callable Function is handling items of multiple type.
It is possible for a single Callable Function to be references by multiple Trigger, ScheduledTrigger and ScheduledTask items, with varied ItemTypes and relatedItems applied to each individual Trigger. Of course the logic of such a Callable Function would have to handle a large number of scenarios in this case. Depending on the circumstances, it may or may not be logical to define a single Callable Function to handle so many scenarios versus dividing it into multiple smaller Callable Functions, but it is possible.
##Monitoring and Viewing Callable Function execution in Mission Control
###Function Call Log FunctionCallLog is a common schema available in all communities that is used as a logging resource to view various function calls that occur on Liquid Server, this includes execution of Callable Functions initiated by Triggers, ScheduledTriggers or ScheduledTasks.
Open up the FunctionCallLog item blotter in Mission Control, and by filtering on functionType = "Trigger", "ScheduledTrigger", or "ScheduledTask" you can see a record of the executions of Callable Functions. The "Function" field of the FunctionCallLog will match the name field of the Trigger, ScheduledTrigger, or ScheduledTask item that initiated the execution.
Examining an individual functionCallLog, you can see the request sent to and the response received from the Callable Function execution. You can also see any logs that may have occurred throughout the execution of the Callable Function.
###Item History For ScheduledTriggers and Triggers, any time an item leads to the execution of a Callable Function, a history entry is recorded for that item with state = "SendintToApplication" and applicationInterface set to the name of the Trigger or ScheduledTrigger.
In this picture we can see the selected Project was used to execute a Callable Function by a ScheduledTrigger named "Test1" 2 separate times.
This is not applicable for ScheduledTasks as those are not initiated by an individual item.
Package Creation / Deployment
Callable Functions in Packages
Whenever a package is created within a community, all the Callable Functions that exist in the community are automatically added to the package.
If you promote a package that contains callable functions from DEV to QA, or QA to PROD, those callable functions will automatically become available for use in the new environment.
Trigger, ScheduledTrigger and ScheduledTasks in Packages
In order for Trigger, ScheduledTrigger, and ScheduledTask items to be included as part of the package, they must be added to communityConfig.completeConfigData, the same as any other item type. Once they are specified in communityConfig, they will be a part of any package created going forward.
No Conflict Between Environments and Communities
When you promote a Callable Function from DEV to QA, both environments will have a Callable Function with the same name existing. Even though they have the same name, they are stored separate from each other. Once you have promoted a Callable Function to QA, you can continue to make further updates to it in Dev, and these changes will not affect QA until a new package is created and it is deployed to QA.
This similarly applies if two separate communities have a Callable Function defined with the same name. They are stored separately and modifying the Callable Function in one community will have no affect on the Callable Function in the other community.