Skip to content

Introduction

Konstantin Müller edited this page Jul 28, 2023 · 3 revisions

Overview

The goal of Action Triggers is to create a common framework for executing dynamically defined actions based on activity in another service. It allows to define events emitted by a service, to define rules which are evaluated against those events and to define actions which are triggered if a rule matches an event. With Action Triggers it is possible to create advanced system integration and to notify users about interesting events based on user-defined rules.

Action Triggers Overview

The framework consists of a number of loosely-coupled components and a set of configurations which must be integrated correctly for the system to work properly, see the figure above.

A service is an external component which is generating trigger events. A trigger event is any kind of noteworthy system activity which a user or a third-party system could subscribe to in order to automatically perform an action whenever the trigger event occurs. Each service generates its own custom trigger events and defines what data is included in each event. Trigger events are submitted to a pipeline worker which evaluates them against predefined trigger rules. If a trigger rule matches a trigger event a trigger action specified in the rule is executed. If no rule matches, the event is ignored. A trigger action can be calling a Web Hook, sending out an e-mail, calling the API of another application, etc. Custom trigger actions can be implemented and made available in addition to the system-provided trigger actions. For its operation the pipeline worker fetches trigger rules, the available trigger events (trigger event definition) and available trigger actions (trigger action definition) from the trigger administration service. This service provides the interface for configuring the whole system, i.e. defining the available trigger events and trigger actions as well as creating trigger rules.

Access Control

Each external service is responsible for performing access control to the data it is processing and implements its own internal access control rules and mechanisms. A service might also generate trigger events containing data which should not be accessible to every person. Consequently, a trigger rule should not be allowed to match on arbitrary trigger events in order to avoid information leakage. But the pipeline worker does not know about a service's internal access control rules and can not perform the same access control checks when matching trigger events against trigger rules. Because of that, each trigger event must be annotated with additional access control information. This information consists of organization, access mode and scope.

  • Organization defines the owner of the trigger event and the data contained therein.
  • Access mode must be either Public, RoleBased or Private.
    • Trigger events tagged with Public should only contain data without any access restrictions.
    • RoleBased denotes trigger events where a person has general access to the organization owning the data.
    • Private should be used for the most sensitive data where access was explicitly granted to a specific person.
  • Scopes can be utilized for even finer granularity of access control, i.e. access is only allowed when a person also has access to the specified scope. Scopes are optional and not every service requires this level of granularity.

Likewise trigger rules are created with the organizations, access mode and scopes they apply to. Only if those settings correspond to the access control information given in a trigger event the rule matches that event. This means that whenever a trigger rule is created it is decided what data the rule and the trigger action defined in the rule can process. Because of that, it is important to carefully control who can create trigger rules matching specific trigger events. When creating a trigger event definition, i.e. defining the available trigger events inside the trigger administration service (see explanation below), the service specifies which permissions are required for creating a trigger rule using that event (for each access mode and each scope). Using those permissions the trigger administration service can verify that a person is allowed to create a specific trigger rule. If the person does not have those permissions the rule will not be created.

Scaling

This scaling mechanism is not implemented right now but planned for the future.

In this early prototype trigger events are submitted by services to one pipeline worker directly. This means that the worker must be deployed together with a service. But the system should be able to handle services which generate a very large amount of trigger events and it should not be required that every service has its own pipeline worker instance. In addition, submitting trigger events should be fast in order to not slow down the normal service operation. Therefore, in the future the pipeline worker will be extended to operate as a standalone component, such that multiple services can submit trigger events to the same worker. But instead of submitting them to the worker directly they will be submitted to a queue, for instance to Apache Kafka. Then multiple services can push trigger events to the same queue and also multiple worker instances can read from that queue in order to process those trigger events. This will allow horizontal scaling by scaling out the queue and/or by increasing the number of worker instances. Processing trigger events is already performed asynchronously. A service does not and should not care what happens to a trigger event once it was submitted for processing.

Trigger Events

A generated trigger event must contain a set of required properties in addition to some optional properties:

  • id (UUID, required): Uniquely identifies the trigger event.
  • timestamp (Long, required): Timestamp in milliseconds when the trigger event was created (from the epoch of 1970-01-01T00:00:00Z UTC).
  • service (String, required): Unique name of the service which created the trigger event.
  • event (String, required): Name of the event as specified in the corresponding trigger event definition. The service defines which trigger events are generated and the event name must be unique per service.
  • organization (UUID, required): Unique identifier of the organization which owns the trigger event (and data provided as context parameters).
  • accessMode (Enum, required): Access mode of the trigger event. The following values are allowed: Public, RoleBased and Private.
  • scope (String, optional): Scope of the trigger event.
  • contextParameters (key/value pairs, optional): Context parameters are values attached to a trigger event which can be referenced in trigger rules during evaluation and can be passed to a trigger action during execution. Context parameters can be used to transfer additional information inside a trigger event. The key must be a string, whereas the value can be either a primitive value (string, integer, floating-point number, ...) or a complex object.

Every service creates its own trigger events which currently requires them to implement the no.mnemonic.services.triggers.pipeline.api.TriggerEvent interface. However, this will not be necessary when the scaling mechanism described above is implemented. Then the trigger events pushed to and read from the queue must only be JSON-compatible (or any other supported serialization format). This will also allow external services which are not running on the JVM and therefore can not implement the TriggerEvent interface to use the Action Triggers framework.

Configuration files

At this early prototype stage the trigger administration service is configured manually using YAML files. It is planned to replaces this with a REST API in order to allow automation.

Trigger Event Definition

A trigger event definition describes a trigger event generated by a service, i.e. it specifies what trigger events will be generated and can be processed by the pipeline worker. Trigger event definitions must be defined in the trigger administration service before a service starts to generate corresponding trigger events. Below an example of a trigger event definition is shown.

---
id: 123e4567-e89b-12d3-a456-426655441212
service: ExampleService
name: ExampleEvent
publicPermission: allowPublicAccess
roleBasedPermission: allowRoleBasedAccess
privatePermission: allowPrivateAccess
scopes:
  global: allowGlobalScope
  local: allowLocalScope
...

Explanation:

  • id (UUID, required): Uniquely identifies the trigger event definition.
  • service (String, required): Name of the service owning this definition and generating corresponding trigger events.
  • name (String, required): Name of the generated trigger event. Together service and name must be unique.
  • publicPermission (String, optional): Permission required for creating a trigger rule for access mode Public.
  • roleBasedPermission (String, optional): Permission required for creating a trigger rule for access mode RoleBased and a specific organization.
  • privatePermission (String, optional): Permission required for creating a trigger rule for access mode Private and a specific organization.
  • scopes (key/value pairs, optional): Permission required for creating a trigger rule using a specific scope.

Note that permissions and scopes can be specified but they are currently not used. They will be relevant once trigger rules can be defined through the REST API (see access control section above). Therefore, definitions in their current form basically only specify which trigger events are available.

Trigger Action Definition

A trigger action definition specifies which trigger actions are available in the system and can be referenced from a trigger rule. When a trigger event matches a trigger rule the referenced trigger action will be executed. See the example of a trigger action definition below.

---
id: 123e4567-e89b-12d3-a456-426655442121
name: ExampleAction
description: Longer description of an action
triggerActionClass: no.mnemonic.services.triggers.action.ExampleAction
requiredPermission: allowExampleAction
initParameters:
  operation: add
  mode: extended
triggerParameters:
  id:
    description: This parameter must be set when executing the action
    required: true
    defaultValue: null
  level:
    description: This parameter is optional and defaults to the value 1
    required: false
    defaultValue: 1
...

Explanation:

  • id (UUID, required): Uniquely identifies the trigger action definition.
  • name (String, required): Unique name of trigger action, used when referenced by a trigger rule.
  • description (String, required): Longer description of trigger action (free text).
  • triggerActionClass (String, required): Fully-qualified class name of trigger action implementation class. Any implementation must implement no.mnemonic.services.triggers.action.TriggerAction and be available on the class path of the pipeline worker.
  • requiredPermission (String, required): Permission required for creating a trigger rule using this action. Note that similar to permissions on trigger event definitions this field must be specified but is currently not used.
  • initParameters (key/value pairs, optional): The initParameters will be passed to the action implementation when initializing it. They are statically defined and are the same for each trigger rule using the action.
  • triggerParameters (optional): The triggerParameters will be passed to the action implementation when executing it. They are dynamically defined when a trigger rule is evaluated and potentially populated with context parameters provided by a trigger event. In a trigger action definition the available triggerParameters are defined, specifying if they are required and with an optional default value. If a trigger parameter is not set by a trigger rule the default value given by the trigger action definition will be used instead.

Each concrete TriggerAction implementation defines which initParameters and triggerParameters it accepts and therefore only the used/required parameters need to be defined inside a trigger action definition. Consult the documentation of the TriggerAction implementation for details about the accepted parameters. The system-defined trigger actions are documented here.

Trigger rule

A trigger rule is evaluated against each generated trigger event and when a trigger rule successfully matches a trigger event the trigger action specified in the rule is executed for the matching event. An example of a trigger rule is given below.

---
id: 123e4567-e89b-12d3-a456-426655441221
service: ExampleService
events: [ ExampleEvent ]
organizations: [ 123e4567-e89b-12d3-a456-426655442112 ]
scopes: [ localScope ]
accessMode: Private
expression: ctx.name == "example"
triggerAction: ExampleAction
triggerParameters:
  id: ${ctx.id}
  level: 3
...

Explanation:

  • id (UUID, required): Uniquely identifies the trigger rule.
  • service (String, required): Name of service for which the trigger rule should be evaluated.
  • events (List, required): Name of events for which the trigger rule should be evaluated.
  • organizations (List, required): UUIDs of organizations which the trigger rule must match.
  • scopes (List, optional): Scopes which the trigger rule must match. If not specified the trigger rule will match all scopes.
  • accessMode (Enum, required): Access mode which the trigger rule must match. The following values are allowed: Public, RoleBased and Private.
  • expression (String, required): Expression to evaluate which the trigger rule must match. The expression must return true or false and can reference the context parameters provided by a trigger event.
  • triggerAction (String, required): Trigger action which will be executed when the trigger rule matches a trigger event. References a trigger action definition by its name property.
  • triggerParameters (optional): The triggerParameters will be passed to the trigger action when executing it. A parameter value can reference context parameters provided by a trigger event.

The expression property can be a JEXL expression which must evaluate to either true or false. It is meant to be a simple boolean expression such as ctx.name == "example". In this example the ctx variable is referenced which must be provided as a context parameter by the evaluated trigger event, and ctx.name will access the name property of the ctx variable. Each trigger parameter is evaluated as a JEXL template which, for instance, allows to send out custom e-mails with an e-mail action as demonstrated below.

Hello ${ctx.name}!

You have received a new private message from ${ctx.sender}.

Click on the following link to read the message: https://www.example.org?messageId=${ctx.id}

Kind regards

Everything inside a ${} statement can reference context parameters as well and will be substituted before the action is executed. Note that JEXL expressions in contrast to JEXL templates do not allow control flow statements like branches or loops. Visit the Apache Commons JEXL project for more information about the JEXL language.

Rule evaluation

For each generated trigger event the pipeline worker first fetches the corresponding trigger event definition from the trigger administration service. If the definition does not exist the trigger event is ignored. Then all trigger rules defined for the service and event given in the trigger event are retrieved and for each rule the following steps are performed to evaluate the rule against the trigger event.

  1. Verify that the rule's organization property contains the event's organization.
  2. Verify that the rule's access mode is not less restrictive than the event's access mode, with Public < RoleBased < Private where < denotes less restrictive. For example, rule access mode of RoleBased requires event access mode Public or RoleBased but does not allow Private.
  3. If the trigger event contains a scope, verify that the rule's scope property contains the event's scope.
  4. Verify that the rule's expression property evaluates to true after substitution of referenced context parameters.

Only after all four steps passed successfully the rule's trigger action is executed. This will first fetch the trigger action definition using the rule's triggerAction property and initializes the trigger action with the initParameters given in the definition. Afterwards the trigger action is executed using the triggerParameters specified in the rule (after substitution of referenced context parameters and/or using default values).