Customization and Triggers
In order to keep customizations out of the main code base OS2mo makes use of a 'customer' directory meant to be used as a mount point for importing customer specific code into the os2mo runtime.
The intended way of customizing code is through the use of triggers.
A Trigger is a function decorated using mora.triggers.Trigger.on
. This
decorator ensures that the decorated function is called on a certain
stage of the lifecycle of a certin kind of request for a certain role
type. There are currently two such stages:
ON_BEFORE
: this is typically fired after the request\'s prepare phaseON_AFTER
: this is typically fired after the request\'s commit phase
Currently all Role Types (Organizational unit, Employee, Address to name a few) and Request Types (Create, Edit, Terminate) can be decorated, so it is possible to have functions triggered for example:
- After creating an organizational unit
- Before creating an employee
- After editing an address
Triggers deployed in OS2mo#
OS2mo itself uses triggers for internal purposes. They reside in
mora.triggers.internal
module. See the amqp_trigger.py
and
http_trigger.py
for examples.
Trigger configuration#
Inclusion of Triggers in OS2mo are handled through a single
configuration value called TRIGGER_MODULES
which is a list of fully
qualified names for trigger modules. The AMQP and HTTP Trigger modules
are loaded directly, and thus do not need to be in TRIGGER_MODULES
.
In order to convey settings directly to triggers we suggest using the fully qualified path as a prefix for configuration values which address that trigger, for example:
lets say TRIGGER_MODULES
is ["customer.triggers.andeby"]
and we want
to supply a FACTOR
value to the duck_function
inside that module, we
could specify it in the configuration like:
customer.trigger.andeby.duck_function.FACTOR
Trigger modules must have a function called register which does the actual decoration of the trigger functions. It may also access the supplied app\'s configuration to determine if specific triggers have been disabled through the use of above mentioned prefixed configuration values as well as obtain other configuration directly meant for the trigger/module.
The customer module#
A directory placed on the host machine (outside the container / module
path) is mounted inside the container (when running in a container) and
a symlink called customer
points to that directory.
In case of running os2mo directly on the host machine the symlink is just pointed directly at the location of the customer directory on the host machine
This directory will typically contain the contents of the
os2mo-data-import-and-export
repository because some proposed triggers
share code with other integrations.
Once positioned this way triggers included from this module has access to any and all code inside OS2mo\'s python environment as well as any code inside the customer directory
The Trigger function#
A trigger function will receive only one argument, the trigger_dict, which is a dictionary with information about the event
ON_BEFORE#
An ON_BEFORE
trigger_dict will typically contain at least these
items: :
{
'event_type': EventType.ON_BEFORE,
'request': {}, # the object that was received by the handler
'request_type': RequestType.EDIT,
'role_type': '' # role_type of the request
'uuid': '' # the uuid of the object being edited
}
ON_AFTER#
For an ON_AFTER
trigger_dict an additional key is added - the result:
:
{
'event_type': EventType.ON_AFTER,
'request': {}, # the object that was received by the handler
'request_type': RequestType.EDIT,
'result': '' # the result that is to be sent back to the client
'role_type': '' # role_type of the request
'uuid': '' # the uuid of the object being manipulated
}
Triggerless mode#
It can be reasonable to turn off trigger-functionality when for example
loading a complete data-set into OS2mo. In case You want that, specify
the flag: triggerless
in the request like:
http://example.com?triggerless=1
Using triggerless requests also disables amqp-messages.
using triggerless mode
is discouraged if you are not fully aware of
all implications. It is meant to be used solely for purposes like
initial data load or for integrations that prefer to do all heavy
lifting by themselves.
:::
HTTP Trigger module#
The HTTP Trigger module is loaded, but not enabled by default.
To enable the module add the following to MO's configuration:
[triggers.http_trigger]
enabled = true
http_endpoints = [
"endpoint_to_trigger:9037"
]
Then restart MO, on startup MO will now send a request to /triggers
on
each of the configured endpoints.
The /triggers
endpoint is expected to return JSON list of
MOTriggerRegister
payloads. The JSON Schema for the
MOTriggerRegister
can be generated with:
pip install os2mo-http-trigger-protocol
python3 -c "from os2mo_http_trigger_protocol import MOTriggerRegister; print(MOTriggerRegister.schema_json())"
An example of the return value is:
[{
"event_type": "ON_BEFORE",
"request_type": "CREATE",
"role_type": "org_unit",
"url": "/triggers/ou/create",
"timeout": 60,
}]
Which instructs MO to send a HTTP POST request to /triggers/ou/create
before an organizational unit is created. The request will timeout after
60
seconds, and expects a status-code 200
return if no issues were
encountered. If the request is answered with an erroneous status code,
it will block the creation in MO.
For an example implementation of a compliant endpoint receiver, please see: * https://github.com/OS2mo/OS2mo-http-trigger-example