event logo
Kaa Documentation

Rule Engine Context

Context in Rule Engine

What is Context in General?

Context refers to a shared data set between different parts of a system.
This shared data ensures seamless communication and operation across various components, enabling efficient data handling and decision-making processes.

In case of Kaa Rule Engine, these components are “endpoint”, “trigger”, “rule”, and “action”.
The context is crucial for ensuring smooth transitions and data sharing between these components. Without it, each component would operate in isolation.
By using context, we can pass additional data and the state of one component to the next.

Although the structure of the context may seem complex, this article will explain it step by step.


Understanding the Context

To fully grasp the concept of context, we need to address a few key points:

  • The execution flow of the rule engine and how context plays a role in it.
  • The prerequisites for a “trigger” to execute and the type of context created based on the trigger type.
  • What the context scope is and how it affects rule engine execution.
  • The period during which the context is available in the rule’s lifecycle.

Jump on board, and let’s dive in!

First, let’s examine the Rule Engine execution flow to understand why context is such an integral part of it.

Context in a Rule Execution Flow

diagram

When we create our rule, we attach it to the “trigger” and “action”, specify related “endpoints”, and let the “Rule engine” do its job. When “trigger” executes, “Rule Engine” can create a context for the rule execution, inject it into the execution workflow, and run the rule.

As you can see in the diagram, the “context” is isolated for each “rule” execution. It means that each rule has its own context, and the objects are not shared between rules. But for the “action” execution, the context is the same as for the “rule” that performed it.

Endpoint Scope Execution

At the “Endpoint Scope”, triggers respond to events from specific endpoints. Rules execute additional logic and perform actions directly related to the endpoint. The injected context contains additional endpoint and event-specific data.

Application Version Scope Execution

At the “Application Version Scope”, triggers execute rules for each endpoint registered in a particular application version. All rules execute the same logic, and actions are performed for each endpoint belonging to the application version. Contexts are not shared between rules. All next steps are similar to the Endpoint level execution.

Application Scope Execution

The same as the “Application Version Scope” execution, but the rules execute for all endpoints registered in the application.

Schedule Scope Execution

For scheduled tasks, triggers execute rules based on a fixed schedule, independent of endpoint events. These rules perform predefined actions. Unlike endpoint, application version, and application level execution, context does not contain any event-specific data, as there are no events involved.

Context lifecycle

When the “trigger” receives an event from the attached endpoint, the “context” is created. It does not exist forever. It will be destroyed after the “rule” and “action” execute. We have a few scenarios of how the context is created.

Event-based Context Lifecycle

In this section, we will describe the context lifecycle when the “trigger” that is attached to the “endpoint”, “application”, or “application version” is executed. The diagrams below describe the context lifecycle in a high-level view and skip steps not involved in a context perspective to reduce overall complexity.

diagram

Note: The Common Context is not an official term. It is used to describe the context part that is not related to the endpoint or specific trigger. It contains “toolbox” and “data” objects.

  1. Event Received: The Rule Engine receives an event, e.g., endpoint posted data sample.
  2. Trigger is Invoked: The trigger processes the event and launches a rule.
  3. Context Created: A common context is created.
  4. Endpoint Context Created: A context specific to the endpoint is created.
  5. Trigger Context Created: A context specific to the trigger and its data is created.
  6. Context Injected: The context is injected into the rule and action execution environment.
  7. Rule Executed: The rule is executed using the merged context.
  8. Action Performed: The action is performed using the merged context.
  9. Event Processing Complete: The event processing is complete.

Schedule-based Context Lifecycle

In this section, we will describe the context lifecycle when the Scheduled (cron) “trigger” is executed.

diagram

  1. Schedule Triggered: The schedule is triggered, e.g., at 12:15 PM.
  2. Common Context Created: A common context is created.
  3. Rule Executed: The rule is executed using the common context.
  4. Action Performed: The action is performed using the common context.
  5. Event Processing Complete: The event processing is complete.

Comparison

The main difference between the first and second flow is that the “Event-based context lifecycle” has additional steps for creating endpoint and trigger context objects. But when the scheduled trigger is invoked, no endpoints are attached, so there is no source to create endpoints and trigger context objects from.

Context Structure

The “ctx” object is available in JavaScript expressions and serves as a comprehensive toolkit for various operations. It contains the following objects:

Structure:

diagram

  • ctx.trigger: The trigger object. This object contains information about the event that triggered the rule.
  • ctx.endpoint: The endpoint object. This object contains information about the endpoint that the rule triggered on.
  • ctx.data: The data object. This object stores data that is passed between the rule and action.
  • ctx.toolbox: The toolbox object. This object contains various utility objects that can be used in the scripts. Some of them are: HTTP client, template engine, GEO utility, CSV parser.

Let’s take a closer look at each of these objects.

Trigger Context Object

This context object varies based on the invoked trigger, providing relevant data structures for different scenarios. It is used to store information about the event that triggered the rule.

Let’s take a look at the trigger context object structure.

diagram

When you write scripts, only one of the “trigger” objects is available in the context, based on the trigger type. Each of them has a boolean property to check if the trigger is of the type that we expect.

Let’s zoom in to the diagram parts to look at the structure of each possible “trigger” object.

Endpoint Metadata Updated

diagram

JSON representation:

{
  "isEndpointMetadataUpdated": true,
  "endpointMetadataUpdated": {
    "endpointId": "{endpointId}",
    "appVersionName": "{appVersionName}",
    "added": "{added}",
    "removed": "{removed}",
    "updated": "{updated}"
  }
}

This object provides details on metadata changes for an endpoint, including additions, removals, and updates.

Example:

// Get the endpoint ID from the endpoint metadata updated context
let epID = ctx.trigger.endpointMetadataUpdated.endpointId;
console.log(`Metadata updated in endpoint[${epID}]`);

// Handle the changes in a metadata
let added = ctx.trigger.endpointMetadataUpdated.added;
let removed = ctx.trigger.endpointMetadataUpdated.removed;
let updated = ctx.trigger.endpointMetadataUpdated.updated;

// Perform actions based on the changes
if (added) {
  // ... Perform actions for added metadata
  console.log("Added metadata:", added);
} else if (removed) {
  // ... Perform actions for removed metadata
  console.log("Removed metadata:", removed);
} else if (updated) {
  // ... Perform actions for updated metadata
  console.log("Updated metadata:", updated);
} else {
  // ... Handle other cases
  console.log("No changes in metadata");
}
Endpoint Data Sample Received

diagram

JSON representation:

{
  "isEndpointDataSamplesReceived": true,
  "endpointDataSamplesReceived": {
    "endpointId": "{endpointId}",
    "appVersionName": "{appVersionName}",
    "dataSamples": "{dataSamples}"
  }
}

This object delivers information about new data samples received from an endpoint, helping to monitor real-time data flows.

Example:

// Get the endpoint ID from the endpoint data samples received context
let epID = ctx.trigger.endpointDataSamplesReceived.endpointId;
console.log(`Endpoint[${epId}] received data samples`);

// Handle the received data samples
let dataSamples = ctx.trigger.endpointDataSamplesReceived.dataSamples;

// Process the data samples
for (let sample of dataSamples) {
  // ... Perform actions with the data sample
  console.log(`Received data sample: ${sample}`);
}
Endpoint Time Series Updated

diagram

Note: The “{timeSeriesName}” property key in the “EndpointTimeSeriesUpdated” object is replaced with an actual time series name during rule execution.

JSON representation:

{
  "isEndpointTimeSeriesUpdated": true,
  "endpointTimeSeriesUpdated": {
    "endpointId": "{endpointId}",
    "appVersionName": "{appVersionName}",
    "timeSeriesName": "{timeSeriesName}",
    "{timeSeriesName}": "{valuesByTimestamp}"
  }
}

Time series updates are captured in this object, providing structured data linked to timestamps for historical analysis.

Example:

// Get the endpoint ID from the endpoint time series updated context
let epID = ctx.trigger.endpointTimeSeriesUpdated.endpointId;
console.log(`Endpoint[${epID}] was updated`);

// Handle the time series updates
let timeSeriesName = ctx.trigger.endpointTimeSeriesUpdated.timeSeriesName;
let valuesByTimestamp = ctx.trigger.endpointTimeSeriesUpdated[timeSeriesName];
console.log(`Updated timeseries: ${valuesByTimestamp}`);

// Process the time series data
for (let timestamp in valuesByTimestamp) {
  // ... Perform actions with the time series data
  console.log(`Time series data for ${timeSeriesName} at ${timestamp}: ${valuesByTimestamp[timestamp]}`);
}
Endpoint Command Dispatched

diagram

JSON representation:

{
  "isCommandDispatched": true,
  "commandDispatched": {
    "endpointId": "{endpointId}",
    "appVersionName": "{appVersionName}",
    "commandType": "{commandType}",
    "commands": "{commands}"
  }
}

Commands dispatched to an endpoint are detailed in this object, enabling task tracking and management.

Example:

// Get the endpoint ID from the endpoint command dispatched context
let epID = ctx.trigger.commandDispatched.endpointId;
console.log(`Command dispatched to endpoint[${epID}]`);

// Handle the dispatched commands
let commandType = ctx.trigger.commandDispatched.commandType;
console.log(`Dispatched command type: ${commandType}`);

let commands = ctx.trigger.commandDispatched.commands;

// Process the dispatched commands
for (command of commands) {
  // ... Perform actions with the command
  console.log(`Dispatched command: ${command}`);
}
Endpoint Command Result Received

diagram

JSON representation:

{
  "isCommandResult": true,
  "commandResult": {
    "endpointId": "{endpointId}",
    "appVersionName": "{appVersionName}",
    "commandType": "{commandType}",
    "results": "{results}"
  }
}

This object contains the outcomes of executed commands, providing insights into the execution status and results.

Example:

// Get the endpoint ID from the endpoint command result received context
let epID = ctx.trigger.commandResult.endpointId;
console.log(`Command result received for endpoint[${epID}]`);

// Handle the command results
let commandType = ctx.trigger.commandResult.commandType;
console.log(`Command result type: ${commandType}`);

let results = ctx.trigger.commandResult.results;

// Process the command results
for (result of results) {
  // ... Perform actions with the command result
  console.log(`Command result: ${result}`);
}
Alert Lifecycle Event

diagram

{
  "isAlertLifecycleEvent": true,
  "alertLifecycleEvent": {
    "alertId": "{alertId}",
    "alertType": "{alertType}",
    "severityLevel": "{severityLevel}",
    "eventType": "{eventType}",
    "timestamp": "{timestamp}",
    "metadata": "{metadata}",
    "systemMetadata": "{systemMetadata}"
  }
}

Alert lifecycle events are captured in this object, detailing the alert’s type, severity, and associated metadata.

Example:

// Get the alert ID from the alert lifecycle event context
let alertID = ctx.trigger.alertLifecycleEvent.alertId;

// Handle the alert lifecycle event
let alertType = ctx.trigger.alertLifecycleEvent.alertType;
console.log(`Alert type: ${alertType}`);

let severityLevel = ctx.trigger.alertLifecycleEvent.severityLevel;
console.log(`Alert severity level: ${severityLevel}`);

let eventType = ctx.trigger.alertLifecycleEvent.eventType;
console.log(`Alert event type: ${eventType}`);

let timestamp = ctx.trigger.alertLifecycleEvent.timestamp;
console.log(`Alert timestamp: ${timestamp}`);

let metadata = ctx.trigger.alertLifecycleEvent.metadata;
console.log(`Alert metadata: ${metadata}`);

let systemMetadata = ctx.trigger.alertLifecycleEvent.systemMetadata;
console.log(`Alert system metadata: ${systemMetadata}`);

// Process the alert lifecycle event based on severity level and event type
if (severityLevel === "CRITICAL" && eventType === "CREATED") {
  // ... Perform actions for critical alerts
  console.log(`Critical alert created: ${alertID}`);
}

Data Context Object

Data is basically a mutable empty object that can be used to store data that is passed between the rule and action. It has no predefined structure.

Example:

ctx.trigger.data = {
  "key1": true,
  "key2": 0,
  "key3": "value"
};

Endpoint Context Object

When a rule is executed for a particular endpoint, the “endpoint” object is created and passed to the rule context. The “endpoint” object contains information about the endpoint, such as its ID, metadata, and associated application and application version. It also provides methods to retrieve time series data, analytics aggregations, and relations.

As you can see, the “endpoint” is a rich object that provides access to a huge amount of endpoint-related data.

diagram

From the above diagram, you can see the “endpoint” methods to retrieve time series data, analytics aggregations, and relations.

Let’s take a closer look at the objects that can be retrieved from the “endpoint”.

Application

The application to which the endpoint belongs.

It contains the following methods:

  • getName() - Returns the name of the application.
ApplicationVersion

The application version to which the endpoint belongs.

It contains the following methods:

  • getName() - Returns the name of the application version.
TimeSeries

The time series data produced by the endpoint.

It contains the following methods:

  • last() - Returns the last data point in the time series.
  • last(String beforeDate) - Returns the last data point in the time series before the specified date.
  • last(Integer count) - Returns the last count data points in the time series.
  • last(String beforeDate, Integer count) - Returns the last count data points in the time series before the specified date.
  • data(String fromDate, String toDate) - Returns the data points in the time series between the specified dates.
  • data(String fromDate, String toDate, String sort) - Returns the data points in the time series between the specified dates and sorted by the specified field.
AnalyticsAggregation

The analytics aggregations for the endpoint.

It contains the following methods:

  • min(String fieldName, String fromDate, String toDate) - Returns the minimum value of the specified field in the time series between the specified dates.
  • max(String fieldName, String fromDate, String toDate) - Returns the maximum value of the specified field in the time series between the specified dates.
  • avg(String fieldName, String fromDate, String toDate) - Returns the average value of the specified field in the time series between the specified dates.
  • sum(String fieldName, String fromDate, String toDate) - Returns the sum of the specified field in the time series between the specified dates.
Relation

The relations for the endpoint.

It contains the following methods:

  • getEntityId() - Returns the ID of the entity.
  • getEntityType() - Returns the type of the entity.
  • getRelations(String relation) - Returns the relations of the specified type.

Toolbox Context Object

A toolbox is a collection of utility functions that can be used to perform various tasks.

More about Toolbox Objects:

When Endpoint Or Trigger Objects Are Not Available In The Context

In the previous section, we skipped the “Schedule” trigger. This is because the complex “trigger” object is not available for this trigger. Only ctx.trigger.isCron boolean flag is present.

The context object structure in this case looks as displayed in Context Structure main diagram.

For those of you who have already read the Triggers article, you know that the Schedule trigger could have no relation to any endpoint. Consequently, the “endpoint” object of context is unavailable for the Schedule trigger in this type of setup.

The context object structure in this case looks like this:

diagram

The absence of endpoint-related context in such cases underscores the need for adaptable handling mechanisms in your rule engine workflows. For instance, tasks executed on a fixed schedule often rely on predefined data or external resources rather than dynamic endpoint data. Understanding such nuances helps in designing effective and flexible systems.

Conclusion

That was a long ride, but we made it! We took a deep dive into the context structure, scope, and lifetime. We explored the context object in detail. We discussed the different types of objects that are placed in context and how they can be created and used. We also discussed the different types of triggers and how the type of trigger affects the context structure.

Now you know almost everything about the context. If you have any questions, please feel free to ask us.

Next Steps