Toolbox template engine

Introduction to templates in Rule Engine

Rule Engine is a powerful tool in the Kaa platform that allows you to create custom actions executed according to specific rules. It consists of three main components: triggers, rules, and actions. Triggers react to events and send context to attached rules. Rules evaluate conditions using JavaScript expressions and execute corresponding actions. Actions perform tasks like sending commands, updating metadata, or sending notifications. For those who are new to Rule Engine, please refer to Rule Engine Getting Started article for an explanation of what Rule Engine is.

However, sometimes we need to repeatedly perform almost the same transformation a large number of times. This can be a tedious and produce a lot of repetitive text. Templating can be very handy in such cases.

In this article, we will explore how to use Mustache template engine in JavaScript expressions.

What is Mustache Template Engine?

According to Mustache Specification, Mustache is a logic-less template syntax. It can be used for HTML, config files, source code - anything. It works by expanding tags in a template using values provided in a hash or object. What does it mean and how can we use this powerful instrument in Rule-Engine expressions? Let’s find out!

First of all, let’s see what key features of Mustache template engine:

  1. Logic-less templates: There are no if statements, else clauses, or for loops. Instead, there are only tags. But it is does not mean that you can’t use logic in your templates. It just means that the logic will be described by simple tags.

  2. Variables: The most basic tag type is a variable, which is denoted by double curly braces: {{name}}, and it will be replaced with the value of the name key in the current context.

  3. Sections: Sections render blocks of text one or more times, depending on the value of the key in the current context.

  4. Inverted Sections: An inverted section begins with a caret (hat) and ends with a slash. It will only be rendered if the key doesn’t exist, is false, or is an empty list.

  5. Comments: Comments begin with a bang and are ignored.

  6. Partials: Partials begin with a greater than sign, like {{> box}}. They are used for including other templates in the current template.

  7. Lambdas: When the value of a tag is a function, it’s called a lambda. Lambdas allow for dynamic content or “computed” values. They are predefined in the context, and can be used with the {{#name}}…{{/name}} syntax.

Use cases for Mustache include:

  • Generating dynamic HTML pages
  • Creating email templates
  • Producing configuration files
  • Generating reports
  • Creating documentation

Mustache’s simplicity and flexibility make it a good choice for many templating needs.

Core features overview

Values

To use Mustache, you can use Rule Engine’s Swiss Army knife - toolbox. Toolbox — a collection of tools that can be used to manipulate data in a variety of ways. One of the instruments in a toolbox is mustache. And, as you can imagine, mustache is an object that can be used to render Mustache templates. Let’s see this in action.


// Context could be any JavaScript object, as an example retrieved from external API by `ctx.toolbox.httpClient`, or from a `ctx.data` object.
const context = {
  title: 'Mustache Template Engine',
  description: 'Mustache is a logic-less template engine...',
};

// Template also can be dynamically updated, and reused in different places.
const template = `
Title: {{title}}
Description: {{description}}`;

// To render a template, you can use the `mustache` object from the toolbox.
return ctx.toolbox.mustache.compile(template, context);

Rendered text:

Title: Mustache Template Engine
Description: Mustache is a logic-less template engine...

In this example, we define a context, simply a dictionary with key-value pairs, and a template. Then, we use the mustache object from the toolbox to render the template with provided context. Each tag be replaced with a corresponding value from the context.

But we can do more. Toolbox provides us with full functionality of Mustache template engine. Imagine we’re dealing with more complex logic and context, nested objects, arrays, etc. We can use all features of Mustache template engine, such as sections, inverted sections, comments, partials, and iterations. Let’s briefly walk through all these features.

Sections

Sections are used to render blocks of text one or more times, depending on the value of the key in the current context.


const context = {
  title: 'Mustache Template Engine',
  description: 'Mustache is a logic-less template engine...',
  additional: 'Mustache is simple',
  conditional: true,
};

// Depending on the `conditional` value, the `additional` value will be rendered or not.
const template = `
Title: {{title}}
Description: {{description}}

{{#conditional}}
In addition, {{additional}}
{{/conditional}}`;

return ctx.toolbox.mustache.compile(template, context);

In this case rendered template looks like this:

Title: Mustache Template Engine
Description: Mustache is a logic-less template engine...

In addition, Mustache is simple

Inverted Sections

Inverted sections used in addition to sections to render a block of text if the key doesn’t exist, is false, or is an empty list.


const context = {
  title: 'Mustache Template Engine',
  description: 'Mustache is a logic-less template engine...',
  additional: 'Mustache is simple',
  fallback: 'Mustache is simple yet powerful template engine.',
  conditional: false,
};

// As an example, we can use default value, if the `conditional` value is not present or `null`.
const template = `
Title: {{title}}
Description: {{description}}

{{#conditional}}
In addition, {{additional}}
{{/conditional}}
{{^conditional}}
  {{fallback}}
{{/conditional}}`;

return ctx.toolbox.mustache.compile(template, context);

In this case rendered template looks like this:

Title: Mustache Template Engine
Description: Mustache is a logic-less template engine...

Mustache is simple yet powerful template engine.

Comments

Comments are used to ignore blocks of text. They are useful for adding comments or hiding blocks of text from the rendered output. Comment block starts with {{! and ends with }}.


const context = {
  title: 'Mustache Template Engine',
  description: 'Mustache is a logic-less template engine...',
  additional: 'Mustache is simple',
  fallback: 'Mustache is simple yet powerful template engine.',
  conditional: false,
};

// Comments are used to ignore blocks of text.
const template = `
{{! This is a comment }}

Title: {{title}}
Description: {{description}}

{{#conditional}}
  {{additional}}
{{/conditional}}
{{^conditional}}
  {{fallback}}
{{/conditional}}`;

return ctx.toolbox.mustache.compile(template, context);

This is a comment will be ignored, and rendered template looks like this:

Title: Mustache Template Engine
Description: Mustache is a logic-less template engine...

Mustache is simple yet powerful template engine.

Partials

Partials are used to render nested templates. They are useful for creating reusable templates that can be used in multiple places.


const context = {
  title: 'Mustache Template Engine',
  description: 'Mustache is a logic-less template engine...',
  additional: 'Mustache is simple',
  fallback: 'Mustache is simple yet powerful template engine.',
  conditional: true,
  partial: 'In addition, {{additional}}',
};

// In this example, we can use partial to render the `additional` value with some extra text.
const template = `
Title: {{title}}
Description: {{description}}

{{#conditional}}
  {{> partial}}
{{/conditional}}
{{^conditional}}
  {{fallback}}
{{/conditional}}`;

return ctx.toolbox.mustache.compile(template, context);

In this case rendered template looks like this:

Title: Mustache Template Engine
Description: Mustache is a logic-less template engine...

In addition, Mustache is simple

Iterations

Iterations provide a way to repeat a block of text for each item in a list.


const context = {
  title: 'Mustache Template Engine',
  description: 'Mustache is a logic-less template engine...',
  authors: [
    { name: 'John Doe', email: 'john.doe@example.com' },
    { name: 'Jane Doe', email: 'jane.doe@example.com' },
  ]
};

// In this example, we can use iteration to render the list of authors.
const template = `
Title: {{title}}
Description: {{description}}

Authors:
{{#authors}}
  {{name}} - {{email}}
{{/authors}}`;

return ctx.toolbox.mustache.compile(template, context);

In this case rendered template looks like this:

Title: Mustache Template Engine
Description: Mustache is a logic-less template engine...

Authors:
John Doe - john.doe@example.com
Jane Doe - jane.doe@example.com

Lambdas

And finally, Lambda functions. It is basically a wrapper tag that allows you to modify the context value. At this moment, Rule Engine supports only a few lambdas, but we plan to add more in the future. There are the following tags:

  • timeOnly - converts UNIX timestamp to human-readable time string in format HH:mm:ss.
  • dateOnly - converts UNIX timestamp to human-readable date string in format dd MMM yyyy.
  • dateTime - converts UNIX timestamp to human-readable date and time string in format d MMM yyyy HH:mm.

Real World Example

And now you may ask: ‘That’s all great, but how can I use all this goodness in a real-world scenario?’. Let’s combine all the above examples into one real-world example. Imagine you need to email your customers, the content of which depends on the data that needs to be retrieved from the platform API, some shared context, etc. The goal can be achieved by using the following script:


let context = {
  title: 'Endpoints',
  customerName: 'Customer',
  endpoints: ctx.toolbox.httpClient.get('/epr/api/v1/endpoints', { platform: true }).body.content,
};

const template = `
<html>
  <head>
    <title>{{title}}</title>
    <style>
      body {
        font-family: Arial, sans-serif;
        background-color: #f5f5f5;
        margin: 0;
        padding: 0;
      }
      .container {
        width: 100%;
        max-width: 600px;
        margin: 0 auto;
        padding: 20px;
        background-color: #ffffff;
        border: 1px solid #e0e0e0;
      }
      .header {
        text-align: center;
        padding: 10px 0;
        background-color: #007bff;
        color: #ffffff;
      }
      .header h1 {
        margin: 0;
        font-size: 24px;
      }
      .table {
        width: 100%;
        border-collapse: collapse;
        margin: 20px 0;
      }
      .table th, .table td {
        border: 1px solid #dddddd;
        text-align: left;
        padding: 8px;
      }
      .table th {
        background-color: #f2f2f2;
      }
      .no-endpoints {
        text-align: center;
        color: #999999;
        font-size: 16px;
        padding: 20px 0;
      }
    </style>
  </head>
  <body>
    <div class="container">
      <div class="header">
        <h1>Monthly Account Summary</h1>
      </div>
      <div class="content">
        <p>Hello, {{customerName}}.</p>
        <p>Here is your monthly summary of all the endpoints:</p>
        <table class="table">
          <thead>
            <tr>
              <th>ID</th>
              <th>Created</th>
              <th>Application Name</th>
              <th>Application Version</th>
            </tr>
          </thead>
          <tbody>
            {{#endpoints}}
            <tr>
              <td>{{endpointId}}</td>
              <td>{{createdDate}}</td>
              <td>{{appName}}</td>
              <td>{{appVersion.name}}</td>
            </tr>
            {{/endpoints}}
            {{^endpoints}}
            <tr>
              <td colspan="4" class="no-endpoints">No endpoints available.</td>
            </tr>
            {{/endpoints}}
          </tbody>
        </table>
        <p>If you have any questions, feel free to contact our support team.</p>
        <p>Thank you for using our services!</p>
        <p>Sincerely yours,</p>
        <p>KaaIoT team</p>
      </div>
    </div>
  </body>
</html>`;

return ctx.toolbox.mustache.compile(template, context);

In this example, we retrieve endpoints using httpClient, render the list of endpoints into a table via iteration, and use inverted section to provide the message if there are no endpoints.

This expression can be used in Action’s Email body, which can be executed weekly by attaching it to the Rule with Schedule Trigger.

Conclusion

In this article, we have covered the basics of Mustache template engine and how to use it in your Rule-Engine scripts. We have also seen how to use it in a real world scenario.