Workflows
A workflow defines how attributes are mapped between tubee and an endpoint. Workflows are required for both a source and a destination endpoint. For source endpoints a worklfow specifies what attributes from an endpoint are mapped to a tubee DataObject. The same is true for destination endpoint workflows but just reversed. Such a workflow defines what tubee attributes get mapped to a destination endpoint.
Each endpoint may have more than one workflow however only one workflow can get selected and executed. What workflow is selected depends on what ensure type is set or if a condition allows the worklfow to get executed.
Create workflow
This will create a workflow for the endpoint named ldap
within the collection accounts
.
name: create-update
kind: Workflow
namespace: playground
collection: accounts
endpoint: ldap
data:
priority: 0
ensure: last
condition: "core.result(core.object.data.disabled === false && core.object.data.username)"
map:
- name: entrydn
kind: script
value: "core.result('uid='+core.object.data.username+',ou='+core.object.department+',o=company,dc=example,dc=org')"
required: true
- name: uid
kind: map
value: data.username
required: false
- name: sn
kind: map
value: data.last_name
required: false
- name: givenName
kind: map
value: data.givenname
required: false
- name: cn
kind: script
value: "core.result(core.object.data.firstname +' '+core.object.data.last_name)"
required: false
- name: objectClass
kind: static
value:
- inetOrgPerson
required: true
tubectl create -f spec.yaml
Check the just created resource:
tubectl get wf accounts ldap create-update -n playground -o yaml
Ensure
The ensure type defines what the workflow actually should do. It defines in what state a DataObject or an EndpointObject should be. There are three known ensure types:
Ensure | Description |
---|---|
exists | The resource gets created if it does not already exists. |
last | The resource gets created if it does not already exists and if it does the resource gets updated. |
absent | The resource gets removed. |
The default ensure type for a workflow is always last
.
Condition
Each workflow may have a scripted condition. This condition gets execute and determines if a given workflow shall get executed.
The condition is JavaScript and gets executed by googles V8 engine.
The condition has to execute core.result(<bool>)
to define if the workflow should get executed.
Each object traverses the workflow stack and gets tested against each workflow as soon as one workflow matches.
Each currently processed object is available at core.object
.
Example:
core.result(core.object.data.disabled === false && core.object.data.username)
This workflow only gets executed if the object has a property disabled
with a value false
and has a field username
set.
Note By default there is no condition. A workflow may only match if the ensure type does match.
Priority
Besides a condition and the ensure type there is possibility to set a priority in which order the workflows get tested.
By default each workflow has the priority 0
.
0
is the highest priority. A workflow with the priority 0
gets executed before a worklfow with the priority 2
.
Mapping
The mapping is the most important fact about a workflow. The mapping defines what and how certain attributes get mapped to each other.
For example it defines what attributes from a DataObject get mapped to what attributes of an OpenLDAP object.
The mapping is defined within map
and contains a list of attribute mappings. Each attribute mapping may have different options.
Option | Default | Description |
---|---|---|
name | <required> |
The name of the destination attribute. (May also contain . to specify a deep path like data.username ). |
kind | map |
May either be map (1:1 attribute mapping), static (static inline value) or script (scripted attribute using vanilla javascript (V8). |
ensure | last |
Like a workflow itself, each attribute may have a different ensure level. |
value | null |
Defines the value of the attribute. The value depends on what kind of attribute this is. |
type | <same type as value> |
Convert the value to another type. |
rewrite | [] |
Rewrite a mapped attribute to another value (May also be done using a scripted attribute). |
filter | [] |
Chain of predefined string filters to apply |
unwind | null |
Unwind a list and operate attribute options on each list element. |
skip | false |
If true the attribute gets ignored. This may be useful if an attribute is only used for an object relation creation or relation context. |
map | Define an object relation mapping to another collection. Automatically create DataObjectReations. | |
writeonly | false |
Writeonly flag may be set to true for attributes which get not returned by endpoints (writeonly attributes). |
Name
The name is required and defines the name of the destination attribute name.
Note Important, DataObject attributes are usually in the data container.
For example, lets define an attribute from an active directory source endpoint and map the samAccountName to our DataObject.
map:
- name: data.username
from: samAccountName
The data.
prefix is required since a DataObjects data is placed within a data container. See an example of a DataObject here.
Kind
There are three different attribute kinds while map is the default one.
Kind | Description |
---|---|
map | The value will hold the name of the source attribute from which value will be taken. The value must be string in this case since all attribute names are strings. |
static | The value is a static value. It may be any supported type. |
script | The value is a javascript executed by the V8 engine. The result must set by calling core.result() while the current DataObject is accessible using core.object . |
Ensure
The ensure type on a attributes knows one more type merge
compared to workflow itself.
Ensure | Description |
---|---|
exists | The attribute gets only mapped if it does not exists yet. |
last | The attribute gets mapped with the latest value. |
absent | The attribute gets removed. |
merge | The attributes get merged, only useful if the value is an array/list. |
Defines mighty scipted attributes. The engine executes JavaScript and used the result of core.result()
as the value for the attribute.
Type
By default the source attributes type will be untouched. However you may convert the value to another type. The same attribute types as defined in a collection schema are available.
Note The type conversion takes place at the very end of the attribute mapping.
Rewrite
You may use rewrite rules to rewrite a value. Rewrite rules may either be of static matching using from
or
a regex (PCRE) matching using match
.
The rewrite processor stops as soon as either from
or a regex match
match the given attributes value.
Example:
map:
- name: entrydn
kind: script
value: "core.result('uid='+core.object.data.username+',ou='+core.object.department+',o=company,dc=example,dc=org')"
rewrite:
- from: uid=admin,ou=admins,o=company,dc=example,dc=org
to: uid=Administrator,ou=admins,o=company,dc=example,dc=org
- match: #uid=([^,]),ou=hr,o=company,dc=example,dc=org#
to: uid=$2,ou=secretariat,o=company,dc=example,dc=org
- match: #uid=([^,]),ou=assistence,o=company,dc=example,dc=org#
to: uid=$2,ou=secretariat,o=company,dc=example,dc=org
Note Rewrite rules get execute once the value is determined from either from,value or script but before a possible type conversion.
You may achive the same using scripted attributes. But rewrite rules are a more readable way and should be the preferred choice if you need to rewrite any values.
Unwind
Using unwind you may operate on each element within a list attribute. You may apply each attribute mapping option to a unwind operator.
For example, we have a DataObject with an attribute adresses which is a list of adresses and we want to map all streets to an attribute called street.
DataObject:
kind: DataObject
collection: people
data:
firstName: John
lastName: Meyer
adresses:
- street: First street 20
zip: 33445
city: New York
- street: Company street 38
zip: 3445
city: Zurich
The goal is to get an attribute called street with the value ['First street 20', 'Company street 38']
.
Lets unwind the attribute adresses
and grab the value with from
.
Note While unwindig the each list value gets available at
root
.
map:
- name: street
value: data.adresses
unwind:
from: root.street
The same is achievable using scripted attributes, but like rewrite rules, unwinding may be the preferrable solution.
### Map
Map is particulary useful to automatically create DataObjectRelations between DataObjects. DataObjectRelations represent a relationship between two (or more) DataObjects. Those DataObject may be from different collections and/or namespaces.
Lets asume we have a collection named accounts
and one named groups
.
We would like to import LDAP objects into both of them. Into accounts we import person objects and into group group objects.
While importing we'd like to automatically create tubee DataObjectRelations between group and group members.
Lets asume those LDAP group objects have an attribute member
which is a list of user distinguished names.
To automatically create a relationship we need to specify the map
keyword and pointing the destination collection to accounts
and
the key which gets used to identify the destination account object to data.dn
(data.dn will hold the account dn).
Optionally we set an attribute ou
as context.
Example:
map:
- name: data.member
value: member
map:
collection: accounts
to: data.dn
context:
- ou
skip: true
- name: data.ou
from: ou
skip: true
This mapping will automatically try to create DataObjectRelations between those two collections.
Option | Default | Description |
---|---|---|
collection | <required> |
The name of the destination collection. |
to | <required> |
The name of the DataObject in the destination collection to use as relationship reference. (Usually you will ned to specify the attribute within data. (for example data.id)). |
ensure | last |
By default the relationship gets created and updated. You may set ensure to exists or absent while on exists the relation will not get updated (for example if context data gets changed). If absent the relation gets removed. |
context | <empty list> |
A list of attributes to use as context attributes. (Note that you can also use attributes which have skip: true set.) |
Skip
Skip is by default false. An attribute which has skip on true
is declared as a virtual attribute and never gets written anywhere.
However a skip: true attribute may be useful if it gets used to write a DataObjectRelation context (See Map).
Writeonly
A password attribute is an example for such attributes. Passwords usually may be writeable but get not returned. While writeonly is set to true and DataObject already exists on the endpoint, the attribute does not get written. writeonly: true attributes may only get written during object creation. Note that it does not matter what value ensure
holds while the attribute is set on writeonly and the DataObject already exists.
Filter
Specify an optional chain of filters to apply as string filters:
- Alnum
- Alpha
- BaseName
- Digits
- Dir
- HtmlEntities
- RealPath
- StringPrefix
- StringSuffix
- StringToLower
- StringToUpper
- StringTrim
- StripNewlines
- StripTags
- UriNormalize