Placeholders
Placeholders may be used at several places in Ditto where something should be resolved by a variable.
The general syntax of a placeholder is {{ prefix:name }}
.
Which placeholder values are available depends on the context where the placeholder is used.
Entity Placeholder
Placeholder | Description |
---|---|
{{ entity:id }} |
full ID composed of ‘‘namespace’’ + ‘’:’’ as a separator + ‘‘name’’ for things and policies |
{{ entity:namespace }} |
Namespace (i.e. first part of an ID) for things and policies |
{{ entity:name }} |
Name (i.e. second part of an ID ) for things and policies |
Thing Placeholder
Placeholder | Description |
---|---|
{{ thing:id }} |
full ID composed of ‘‘namespace’’ + ‘’:’’ as a separator + ‘‘name’’ |
{{ thing:namespace }} |
the namespace (i.e. first part of an ID) of the related thing |
{{ thing:name }} |
the name (i.e. second part of an ID ) of the related thing |
Thing JSON Placeholder
Placeholder | Description |
---|---|
{{ thing-json:<json-pointer> }} |
Value (in string representation) of the JSON identified by the provided ‘‘json-pointer’’ in JsonPointer notation - e.g., thing-json:attributes/location for the “location” attribute or thing-json:features/temperature/properties/value for the temperature property. |
Feature Placeholder
Placeholder | Description |
---|---|
{{ feature:id }} |
the ID of the feature (only available if the processed signal was related to a feature) |
Policy Placeholder
Placeholder | Description |
---|---|
{{ policy:id }} |
full ID composed of ‘‘namespace’’ + ‘’:’’ as a separator + ‘‘name’’ |
{{ policy:namespace }} |
the namespace (i.e. first part of an ID) of the related policy |
{{ policy:name }} |
the name (i.e. second part of an ID ) of the related policy |
Policy Entry Placeholder
Placeholder | Description |
---|---|
{{ policy-entry:label }} |
label of the policy entry in which the token integration subject is injected |
Connection Placeholder
Placeholder | Description |
---|---|
{{ connection:id }} |
the ID of the connection which receives/publishes a signal |
Header Placeholder
Placeholder | Description |
---|---|
{{ header:<header-name> }} |
Value of the header with the given name. For incoming signals the headers of the used protocol are used. For outgoing values the Ditto protocol headers are used.(both case-insensitive) |
Topic Placeholder
Placeholder | Description |
---|---|
{{ topic:full }} |
full Ditto Protocol topic path in the form {namespace}/{entityName}/{group}/ {channel}/{criterion}/{action-subject} |
{{ topic:namespace }} |
Ditto Protocol topic namespace |
{{ topic:entityName }} |
Ditto Protocol topic entity name |
{{ topic:group }} |
Ditto Protocol topic group |
{{ topic:channel }} |
Ditto Protocol topic channel |
{{ topic:criterion }} |
Ditto Protocol topic criterion |
{{ topic:action }} |
Ditto Protocol topic action |
{{ topic:subject }} |
Ditto Protocol topic subject (for message commands) |
{{ topic:action-subject }} |
either Ditto Protocol topic action or topic subject (for message commands) |
Resource Placeholder
Placeholder | Description |
---|---|
{{ resource:type }} |
the type of the Ditto Protocol path , one of: "thing" "policy" "message" "connection" |
{{ resource:path }} |
the affected resource’s path being the Ditto Protocol path in JsonPointer notation, e.g. / when a complete thing was created/modified/deleted |
Request Placeholder
Placeholder | Description |
---|---|
{{ request:subjectId }} |
the first authenticated subjectId which sent the command / did the request |
Time Placeholder
Placeholder | Description |
---|---|
{{ time:now }} |
the current timestamp in ISO-8601 format as string in UTC timezone |
{{ time:now_epoch_millis }} |
the current timestamp in “milliseconds since epoch” formatted as string |
{{ time:now<+-offset> }} |
the current timestamp in ISO-8601 format as string in UTC timezone plus or minus the offset in format <integer><unit> where unit is one of ms s m h d w mo y |
{{ time:now_epoch_millis<+-offset> }} |
the current timestamp in “milliseconds since epoch” formatted as string plus or minus the offset in format <integer><unit> where unit is one of ms s m h d w mo y |
{{ time:now[<truncation-unit>] }} |
the current timestamp in ISO-8601 format as string in UTC timezone, truncated to the unit defined in square brackets, being one of ms s m h d w mo y |
{{ time:now_epoch_millis[<truncation-unit>] }} |
the current timestamp in “milliseconds since epoch” formatted as string, truncated to the unit defined in square brackets, being one of ms s m h d w mo y |
{{ time:now<+-offset>[<truncation-unit>] }} |
the current timestamp in ISO-8601 format as string in UTC timezone plus or minus the offset in format <integer><unit> where unit is one of ms s m h d w mo y , truncated to the unit defined in square brackets, being one of ms s m h d w mo y |
{{ time:now_epoch_millis<+-offset>[<truncation-unit>] }} |
the current timestamp in “milliseconds since epoch” formatted as string plus or minus the offset in format <integer><unit> where unit is one of ms s m h d w mo y , truncated to the unit defined in square brackets, being one of ms s m h d w mo y |
Examples - assuming that the now
timestamp is: 2024-01-06T14:23:42.123Z
{{ time:now }} # current ts: 2024-01-06T14:23:42.123Z
{{ time:now+1h }} # current ts, +1 hour: 2024-01-06T15:23:42.123Z
{{ time:now-7d }} # current ts, -7 days: 2023-12-31T14:23:42.123Z
{{ time:now[h] }} # current ts, truncated to the hour: 2024-01-06T14:00:00.000Z
{{ time:now[d] }} # current ts, truncated to the day: 2024-01-06T00:00:00.000Z
{{ time:now-25m[m] }} # current ts, -25 minutes, truncated to the minute: 2024-01-06T13:58:00.000Z
{{ time:now+3d[s] }} # current ts, +3 days, truncated to the second: 2024-01-09T14:23:42.000Z
The same offset and truncation can be done with the now_epoch_millis
.
JWT Placeholder
Placeholder | Description |
---|---|
{{ jwt:<claim-pointer> }} |
Any standard or custom claims in the body of the authenticated JWT in JsonPointer notation - e.g., jwt:sub for the JWT “subject” or jwt:extra/roles for (potentially multiple) “roles” extracted from a JsonObject in the JWT named extra .JWT claims being JSON arrays are expanded (each value of the array is added) and provided functions are performed on each array element. |
Scope: Entity creation / modification
Whenever creating or modifying things or policies, the following placeholders may be used:
Scope: OpenID Connect configuration
When configuring OpenID connect providers in a Ditto setup, the jwt:
placeholder can be used in order to extract certain claims from the
JWT in order to use them as
authorization subjects for Ditto’s policy based authorization.
The following placeholders may be used inside the configuration file:
In combination with functions, use of this placeholder can provide and modify the subjects to
extract from a JWT in a very powerful way.
The fn:split(' ')
function may for example be used in order to further split the default claim “scope”, which is by
default a whitespace separated list of scopes, as an array, producing multiple entries for each provided scope.
Example:
Assuming the provided JWT looks like this:
{
"sub": "u123456789",
"iss": "https://<the-issuer-domain>/",
"exp": 1490922820,
"iat": 1490886820,
"email": "john.doe@eclipse.org",
"aud": "client-id-0815",
"scopes": "openid email",
"extra": {
"roles": [
"administrator",
"super-moderator",
"moderator"
]
}
}
Applied on the example JWT, the following jwt:
placeholders in combination with functions
are resolved in the following way:
{{ jwt:sub }}
- resolves to"u123456789"
{{ jwt:sub }}@{{ jwt:aud }}
- resolves to"u123456789@client-id-0815"
{{ jwt:extra/roles }}
- resolves to 3 entries:"administrator"
,"super-moderator"
and"moderator"
{{ jwt:scopes | fn:split(' ') }}
- resolves to 2 entries:"openid"
and"email"
{{ jwt:extra/roles | fn:filter('like','*moderator') }}@{{ jwt:aud }}
- resolves to 2 entries:"super-moderator@client-id-0815"
and"moderator@client-id-0815"
Scope: Policy actions
In policy actions, the following placeholders are available in general:
Scope: RQL expressions when filtering for Ditto Protocol messages
When using RQL expressions in scope of either change notifications or subscriptions for live messages, the following placeholders are available in general:
- topic placeholder
- resource placeholder
- time placeholder Note that in RQL expressions placeholders must not be surrounded by curly braces.
Scope: Websocket Signal Enrichment
When declaring extra fields which should be enriched to a published signal for a Websocket connection, the following placeholders are available in general:
- entity placeholder
- thing placeholder
- policy placeholder
- feature placeholder
- header placeholder
- request placeholder
- resource placeholder
- topic placeholder
- time placeholder
Scope: SSE Signal Enrichment
When declaring extra fields which should be enriched to a published signal for an SSE subscription, the following placeholders are available in general:
- entity placeholder
- thing placeholder
- policy placeholder
- feature placeholder
- header placeholder
- request placeholder
- resource placeholder
- topic placeholder
- time placeholder
Scope: Connections
In connections, the following placeholders are available in general:
- entity placeholder
- thing placeholder
- thing-json placeholder
- policy placeholder
- connection placeholder
- feature placeholder
- header placeholder
- request placeholder
- resource placeholder
- topic placeholder
- time placeholder
Examples
For a topic path with the intention of creating a Thing org.eclipse.ditto/device-123/things/twin/commands/create these placeholders would be resolved as follows:
Placeholder | Resolved value |
---|---|
topic:full |
org.eclipse.ditto/device-123/things/twin/commands/create |
topic:namespace |
org.eclipse.ditto |
topic:entityName |
device-123 |
topic:group |
things |
topic:channel |
twin |
topic:criterion |
commands |
topic:action |
create |
topic:subject |
❌ |
topic:action-subject |
create |
For a topic path with the intention of sending a message to a Thing org.eclipse.ditto/device-123/things/live/messages/hello.world these placeholders are resolved as follows:
Placeholder | Resolved value |
---|---|
topic:full |
org.eclipse.ditto/device-123/things/live/messages/hello.world |
topic:namespace |
org.eclipse.ditto |
topic:entityName |
device-123 |
topic:group |
things |
topic:channel |
live |
topic:criterion |
messages |
topic:action |
❌ |
topic:subject |
hello.world |
topic:action-subject |
hello.world |
Function expressions
Whenever placeholders can be used (e.g. for connections), function expressions may additionally be specified.
The syntax of such function expressions are specified similar to a UNIX pipe
, e.g.:
{{ thing:name | fn:substring-before('-') | fn:default('fallback') | fn:upper() }}
Normally, the first expression in such a pipeline would be a placeholder to start with, in the example above thing:name
.
Functions could follow it separated by the pipe (|
) symbol - each function in the pipeline receives the value of the
previous expression (which may also be empty
).
A function in the beginning of a pipeline would get empty
as its input.
The function either contains no parameters or contains parameters which are either string constants or could also be placeholders again.
Function library
The following functions are provided by Ditto out of the box:
Name | Signature | Description | Examples |
---|---|---|---|
fn:filter |
(String filterValue, String rqlFunction, String comparedValue) |
Removes the result of the previous expression in the pipeline unless the condition specified by the parameters is satisfied. The first parameter filterValue may also be omitted in which case the 2 passed parameters will be appplied on the previous pipeline expression. |
fn:filter('like','allowlist1|foobar|include') fn:filter(header:response-required,'eq','true') fn:filter(header:response-required,'exists') fn:filter(header:response-required,'exists','false') |
fn:default |
(String defaultValue) |
Provides the passed defaultValue when the previous expression in a pipeline resolved to empty (e.g. due to a non-defined header placeholder key).Another placeholder may be specified which is resolved to a String and inserted as defaultValue . |
fn:default('fallback') fn:default("fallback") fn:default(thing:id) |
fn:substring-before |
(String givenString) |
Parses the result of the previous expression and passes along only the characters before the first occurrence of givenString .If givenString is not contained, this function will resolve to empty . |
fn:substring-before(':') fn:substring-before(":") |
fn:substring-after |
(String givenString) |
Parses the result of the previous expression and passes along only the characters after the first occurrence of givenString .If givenString is not contained, this function will resolve to empty . |
fn:substring-after(':') fn:substring-after(":") |
fn:lower |
() |
Makes the String result of the previous expression lowercase in total. | fn:lower() |
fn:upper |
() |
Makes the String result of the previous expression uppercase in total. | fn:upper() |
fn:trim |
() |
Trims the String result of the previous expression, removing leading and trailing whitespaces. | fn:trim() |
fn:url-encode |
() |
Applies URL encoding to the String result of the previous expression. | fn:url-encode() |
fn:url-decode |
() |
Applies URL decoding to the String result of the previous expression. | fn:url-decode() |
fn:base64-encode |
() |
Applies Base64 encoding to the String result of the previous expression. | fn:base64-encode() |
fn:base64-decode |
() |
Applies Base64 decoding to the String result of the previous expression. | fn:base64-decode() |
fn:delete |
() |
Deletes the result of the previous pipeline expression unconditionally. Any following expressions are ignored. | fn:delete() |
fn:replace |
(String from, String to) |
Replaces a string with another using Java’s String::replace method. |
fn:replace('foo', 'bar') |
fn:split |
(String separator) |
Splits the previous pipeline using the passed separator resulting an “array” pipeline output containing several elements.May only be used in combination with the JWT placeholder as input placeholder. |
fn:split(' ') fn:split(',') |
fn:join |
(String delimiter) |
Joins the previous pipeline elements (when they were an array) resulting a single string pipeline output delimited with the passed delimiter . |
fn:join(':') fn:join(',') |
RQL functions
The following RQL functions are available for fn:filter
Name | Signatures | Description |
---|---|---|
eq |
(String filterValue, 'eq', String comparedValue) , ('eq', String comparedValue) |
If 3 parameters are passed in, the function filters on the first parameter being equal to the last. If 2 parameters are passed in, the function filters the previous pipeline element being equal to the last parameter. |
ne |
(String filterValue, 'ne', String comparedValue) , ('ne', String comparedValue) |
If 3 parameters are passed in, the function filters on the first parameter being not equal to the last. If 2 parameters are passed in, the function filters the previous pipeline element being not equal to the last parameter. |
exists |
(String filterValue, 'exists', String true|false) , ('exists', String true/false) , ('exists') |
If 3 parameters are passed in, the function filters on the first parameter being existent/non-existent. If 2 parameters are passed in, the function filters the previous pipeline element being being existent/non-existent. If 1 parameter is passed in, the function filters the previous pipeline element being true. |
like |
(String filterValue, 'like', String regex) , ('like', String regex) |
If 3 parameters are passed in, the function filters on the first parameter matching the last. If 2 parameters are passed in, the function filters the previous pipeline element matching the last parameter. |
The like
function can be used with different expressions:
*
: One or more arbitrary characters?
: One arbitrary character|
: Element prior to the operator OR element after the operator