Skip to main content

Structured template properties with Templatable

Experimental - For advanced users and developers

The behavior described in this document may change without notice.

To learn the basics of alert templates, see use templates.

ThirdEye alert templates support variables and properties. At execution time, variables in a template are replaced by their properties value. In an alert configuration json, a variable looks like "${var}" and properties are defined in templateProperties. By default, variables only work in String fields, and properties are expected to be String.

But any type of field, like a list or a map, can be templatized. This is possible with the so-called structured template properties.


Structured template properties are enabled with the Templatable Class. A Templatable<T>, with T being any type, makes the field behave like a Union[T, String], where the string can only be written as a variable, in format "${var}".

An example of a structured template property is rcaExcludedDimensions in startree-threshold template.
The field is a Templatable<List<String>>. The field is set as a String:

"rcaExcludedDimensions": "${rcaExcludedDimensions}",

and the property is set as a List:

  "defaultProperties": {
"rcaExcludedDimensions": [],

When the template is saved, the String value is accepted.
At runtime the string is replaced by the list.

Making a field compatible with structured properties

Even if all fields could be made Templatable, it was decided that fields should be made Templatable on a per-need basis.
Don't make String fields Templatable, this is not necessary: Templatable<String> = Union[String, String] = String.

Plan node Spec fields

There is nothing to do.
The values of the params map of alert nodes are templatable.
Class extending AbstractSpec to parse the params map don't have to implement templatable.
Templatable is resolved internally and a Map<String, Object> is passed when mapping params to the Spec.
This is implemented here.

Other fields

Wrap the field you want to make compatible with structured properties with Templatable. Most likely, you'll have to update a DTO and an API class. Then update the code that uses this field.

An example PR can be found here.

The implementation is such that wrapping a field with Templatable will not break the existing entities in the database. See Serialization and Deserialization.


  • Multiple level of nested Templatable fields is supported
  • Passing the Templatable structure directly for deserialization is not supported. Eg for a component of type Templatable<Map<Int, Int>>
    Such json is not valid
    "component": {"templatedValue": "${var}", "value": {1:2, 3:4}}
    Valid jsons are:
    "component": "${var}"
    "component": {1:2, 3:4}

This behavior ensures the Templatable truly behaves like a union at the serialization level: it is not possible to set the String value and the T value of the Templatable at the same time.


Original PR is here.

Serialization and Deserialization

A custom jackson (de)serialization mechanism is used to get the Union-like behavior.
The implementing class are ApiTemplatableSerializer and ApiTemplatableDeserializer.

You should not have to use these class directly. Use ThirdEyeSerialization helpers.
In most context (API level, API json reading/writing, persistence level), you should use ThirdEyeSerialization.newObjectMapper to get an ObjectMapper.
If you need a jackson.databind.Module with the custom (de)serializations, use ThirdEyeSerialization.TEMPLATABLE.

Applying structured properties

Applying structured template properties is implemented in TemplateEngineTemplatableSerializer. It exploits the recursive visitor pattern of jackson serialization to make the implementation easy, but it's an internal detail and this serialization should not be exposed nor used elsewhere.