> ## Documentation Index
> Fetch the complete documentation index at: https://platform.docs.zenoo.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Hub Domain Specific Language (DSL)

> The Hub DSL provides an implementation model for expressing digital onboarding solutions in a concise manner without superfluous details.

# Hub Domain Specific Language (DSL)

The Hub **DSL** provides an implementation model for expressing digital onboarding solutions in a concise manner without superfluous details.
Letting developers focus on the business logic.

In addition to the main purpose Hub DSL supports these objectives:

* enables transparent handling of failures,
* facilitates working with data payloads, data mapping and transformation,
* bridges the gap between developers and domain experts using a common language.

The Hub DSL provides the following features

* [Context attributes](/technical-specification/hub-backend/engine/attributes) for storing and working with JSON-like data

* [DSL commands](/technical-specification/hub-backend/engine/dsl#dsl-commands) implement the basic building blocks of a digital onboarding
  * [route](/technical-specification/hub-backend/engine/dsl#route) represents user interactions
  * [exchange](/technical-specification/hub-backend/engine/dsl#exchange) makes external calls using [connectors](/technical-specification/hub-backend/connectors/overview)
  * [http](/technical-specification/hub-backend/engine/dsl#exchange) makes external calls using [connectors](/technical-specification/hub-backend/connectors/overview)
  * [workflow](/technical-specification/hub-backend/engine/dsl#workflow) executes child workflows
  * [function](/technical-specification/hub-backend/engine/dsl#function) executes child functions
  * [mapper](/technical-specification/hub-backend/engine/dsl#mapper) used for data mappings and transformations
  * [sharable](/technical-specification/hub-backend/engine/dsl#sharable) generates and manages sharable links
  * [success / error / decision / response](/technical-specification/hub-backend/engine/dsl#execution-result) terminates the current execution
  * [logging](/technical-specification/hub-backend/engine/dsl#logging) for DSL logging
  * [Flow control](/technical-specification/hub-backend/engine/dsl#flow-control) commands allow for conditional execution
  * [Logging and metrics](/technical-specification/hub-backend/engine/dsl#logging-and-metrics) logging from DSL and support metrics events

## DSL Commands

### Route

A **route** represents an interaction with a user.

Typically, the goal is to display route-specific information and gather input from the user.
A route is rendered by a Hub Client as a web page or mobile app screen, depending on the Hub Client implementation.

A route is identified by its **name** and can be used in a [workflow](/technical-specification/hub-backend/components#workflow) definition.
A minimal definition specifies a route **uri** intended for a Hub Client.

```groovy theme={null}
route('name') {
    uri '/uri'
}
```

#### Definition and usage

It is possible provide a route definition **inline** within a workflow definition.

```groovy theme={null}
worklfow('test') {
    route('name') {
        uri '/uri'
    }
}
```

Another option is to define a route as part of a [Hub component](/technical-specification/hub-backend/components#route) and use it in a workflow by **referencing** the route *name*.
This approach facilitates route reusability and separation of concerns.

```groovy theme={null}
route('name') {
    uri '/uri'
}

worklfow('test') {
    route('name')
}
```

Additionally, it is possible to reference a route by *name* and provide additional details when used in a workflow.
This allows for separating a route definition (uri, data constrains) and usage (export, namespace, checkpoint).

```groovy theme={null}
route('client') {
    uri '/client'
    validate {
        firstname
        lastname
    }
}

worklfow('test') {
    route('client') {
        export documents
        namespace application.client
    }
}
```

#### Route Result

A route result is stored using a **namespace** attribute key.

```groovy theme={null}
route('client-info') {
  uri '/client-info'
  namespace client
}
```

If a [validate](/technical-specification/hub-backend/engine/attributes#payload-specification) block is specific, a route result is validated before storing the result and resuming the execution.
The route submit request results in a [validation error](/technical-specification/hub-backend/client-api#submit-route) if the validation fails.

```groovy theme={null}
route('client-info') {
    uri '/client-info'
    namespace client
    validate {
      firstname
      lastname
      idFront { file 'image'}
    }
}
```

#### Exporting data

In order to pass route data, the **export** is used. Any JSON-like data can be exported, using context attributes or serializable values.

```groovy theme={null}
products << [product1: "Product1", product2: "Product2"]
route('products') {
    uri '/products'
    export products
}

route('greeting') {
    uri '/greeting'
    export message: "Hello world!"
}
```

#### Route check-point

A route can be marked as a **check-point**, meaning it is disabled to go back to the previous route.

```groovy theme={null}
route('finish') {
    uri '/finish'
    checkpoint()
}
```

#### Terminal route

A **terminal** route marks the end of a workflow execution. The corresponding execution is terminated when a *terminal* route is executed.

Also, a *terminal* route is marked as a **check-point**.

```groovy theme={null}
route('finish') {
    uri '/finish'
    terminal()
}
```

In addition, it is possible to set an execution [result payload](/technical-specification/hub-backend/engine/dsl#execution-result) using a *terminal* route.

```
route('finish') {
    uri '/finish'
    terminal(payload)
}
```

#### Route functions

A **route function** allows a Hub Client to execute *functions* in the context of the given route.
A Hub client executes a *route function* via [Hub Client API](/technical-specification/hub-backend/client-api#execute-a-route-function).

Some use-cases of route functions:

* dynamic queries based on user input, like auto-complete,
* asynchronous data processing, like document OCR,
* communication between different execution.

```groovy theme={null}
route('name') {
    uri '/uri'
    function('fnc1') {
      context initial
      namespace fnc1
    }
}
```

* **initial** execution context for corresponding function execution
* **namespace** stores the result of a route function execution

It is possible to specify one or more route functions.

See [route examples](/technical-specification/hub-backend/engine/routedsl) for more details.

### Exchange

An exchange is a connector proxy. It makes external (API) calls using an [HTTP connector](/technical-specification/hub-backend/connectors/httpconnector) or a custom connector.

It provides the following tools for handling connector failures:

* **timeout**, an exchange fails with an error if the connector does not respond within the specified *timeout*
* **retry strategy**, retries failed connector requests

An exchange is executed asynchronously when marked with `async()`.

#### HTTP connector

It is possible to use built-in **HTTP connector** to make external calls, see [more details](/technical-specification/hub-backend/connectors/httpconnector).

```groovy theme={null}
http {
  definition
}
```

#### Custom connector

Optionally, an exchange can use a **custom connector** with *config*.

```groovy theme={null}
exchange('name') {
  connector('custom')
  config input
}
```

> **Connector Development Guide**
> For comprehensive guidance on developing custom connectors, see the [Custom Connector Development Guide](/technical-specification/hub-backend/connectors/custom-development) and [Plugin Connector Development Guide](/technical-specification/hub-backend/connectors/plugin-development).

#### Exchange Result

An exchange result is stored using a **namespace** attribute key.

```groovy theme={null}
http('localhost-api') {
    url "https://localhost:8080/api"
    namespace api
}
```

#### Result handlers

An `exchange` command can specify one on more result handlers

* `onError()` executed when the result is error
* `onSuccess()` executed when the result is success

In functions, each result handler can define an inline function that is executed when the handler condition is match.
In workflows, each result handler can define an inline workflow.

E.g. handling an exchange error in a workflow

```groovy theme={null}
exchange('idv') {
  connector 'idv'
}.onError {
    error ->
      route('error') {
        export error
      }
}
```

E.g. handling an exchange error in a function

```groovy theme={null}
exchange('idv') {
  connector 'idv'
}.onError {
  error ->
    function('store-error') {
      input error
      async()
    }
    error(it.message)
}
```

The built-in `http` connector provides additional handlers

* `onStatus(status)` executed when response status code matches the given `status`, the response body is passed as input
* `onResponse()` executed for any response, the response entity (headers, status, payload) is passed as input

E.g. handling an http responses

```groovy theme={null}
http {
  url "${url}/create"
  method 'POST'
  jsonBody request
}.onStatus(400) {
    match(it.header.code_error in [1108, 2001, 3001, 3020]) {
        decision('INCORECT_CPF', it)
    }
    error(it)
}.onResponse {
  match(it.status == 201) {
      success(it.headers.Location)
  }
  error(it.payload)
}

```

#### Exchange Timeout

It is possible to set an exchange **timeout** in seconds. The default value is *30 seconds*.

An exchange fails with an *error* if the underlying connector doesn't respond within the specified *timeout*

```groovy theme={null}
exchange('status-api') {
  connector('custom')
  timeout 10
}
```

#### Exchange Retry strategies

An exchange uses a **retry strategy** to retry when a connector request fails.
The default strategy uses **fixed** delays between retry attempts.

The following retry strategies are available:

##### Fixed backoff

Uses fixed delays between retry attempts, given a number of retry attempts and the backoff delay in seconds.

* **retry** a number of retry attempts, the default is 5
* **backoff** a number of seconds between retries, the default is 5

```groovy theme={null}
exchange('fixed-default') {
    http {
      url config.api.url
    }
    fixedBackoffRetry()
}

exchange('fixed-custom') {
    http {
      url config.api.url
    }
    fixedBackoffRetry {
     retry 10
     backoff 2
    }
}
```

##### Exponential backoff

Uses a randomized exponential backoff strategy, given a number of retry attempts and minimum and maximux backoff delays in seconds.

* **retry** a number of retry attempts, the default is 5
* **backoff** a minimum delay between retry attempts, the default is 5
* **maxBackoff** a maximum delay between retry attempts, the default is 50

```groovy theme={null}
exchange('exp-default') {
  http {
    url config.api.url
  }
  exponentialBackoffRetry()
}

exchange('exp-custom') {
    http {
      url config.api.url
    }
    exponentialBackoffRetry {
     retry 3
     backoff 5
     maxBackoff 10
    }
}
```

##### No retry

Does not retry when a connector request fails.

```groovy theme={null}
exchange('name') {
    http {
      url config.api.url
    }
    noRetry()
}
```

### function

* like workflow but without user interactions (route, workflow)
* can be executed asynchronously
* separate execution with different UUID, data passed using context and input

A function makes it possible to query dynamic data, perform complex calculations or make external calls using *exhange()*.
Functions can be executed from a workflow or from another function.

* **name** a function name
* **input** a function input
* **context** execution context for function execution
* **namespace** a namespace to store function result
* **async()** the function will be executed asynchronously

```groovy theme={null}
function('mobile.lookup') {
 input mobile: '325-135856984'
 context retry: 3
 namespace lookup
 async()
}
```

### workflow

Executes a sub-workflow synchronously as a separate workflow execution with different UUID.
Data is passed using context and input.
Execution is terminated if sub-workflow terminates with terminal route.

* **name** a workflow name
* **input** a workflow input
* **context** execution context for workflow execution
* **namespace** a namespace to store workflow result

```groovy theme={null}
workflow('otp') {
 input mobile: '325-135856984'
 context retry: 3
 namespace otp
}
```

### mapper

An attribute mapper transforms an input into an attribute output using mapper *expression*, see [Mapper](/technical-specification/hub-backend/components). The output gets stored in a *namespace* if specified.
Can be used for data transformations, calculations and providing default values etc.

```groovy theme={null}
mapper('name') {
  input input
  namespace namespace
}
```

### path

Executes a `path` (workflow snippet) specified by a *name*. A path is executed as part of the current execution and shares the same execution context.
It is only possible to reference a path defined within the same component as a workflow being executed.

```groovy theme={null}
path 'name'

```

### Execution result

There are several ways of terminating an execution and specifying an execution result

* `success()` for a success result with payload
* `error()` for an error result with payload
* `decision()` for a success result with a decision and payload
* `response` for exposed function response
* the result of the last command or expression

Terminates an execution successfully with a payload

```groovy theme={null}
success(application)
success firstName: checkIdp.firstName, lastName: checkIdp.lastName
```

or with an empty payload

```groovy theme={null}
success()
```

Terminates an execution with an error using the specified payload

```groovy theme={null}
error "Boom"
error otp
```

or with an empty error payload

```groovy theme={null}
error()
```

If more granularity in needed, it is possible to terminate an execution successfully using `decision()`.

Terminates an execution successfully with a decision and payload

```groovy theme={null}
decision('ACCEPTED', idp)
```

or with an empty error payload

```groovy theme={null}
decision('DENIED')
```

#### Exposed function response

Exposed functions can specify an HTTP response details, including body, headers and status code.

```groovy theme={null}
response {
    status HttpStatus.ACCEPTED
    header HttpHeaders.LOCATION, 'http://localhost'
    body payload
}
```

```groovy theme={null}
response {
    status 201
    header 'location', "http://localhost"
    header 'x-correlation-id', "345lkigzdf90234as"
}
```

### Execution result handlers

Each `function` and `workflow` command can specify one on more result handlers

* `onError()` executed when the result is error
* `onSuccess()` executed when the result is success or any decision
* `onDecision()` executed when the result is a given decision

**Only the first matching** handler is executed. When a handler is executed, the result payload is passed as an input.

E.g. handling a workflow error

```groovy theme={null}
workflow('idv')
    .onError {
      error ->
        route('error') {
          export error
        }
    }
```

E.g. handling a workflow decision result

```groovy theme={null}
workfow('liveness')
    .onDecision('LIVE') {
      payload ->
        workflow('payment') {
          input payload
        }
    }
    .onSuccess {
        workflow('try-again')
    }
    .onError {
      error ->
        route('error') {
          export error
        }
    }
```

### sharable

Generates a sharable token or a link with the token.

A sharable token can be used for starting a new workflow or function execution.
It is possible to specifies a sharable token manually. Otherwise, it is generated as 6 random alpha numerical characters.

> **For comprehensive documentation on sharables including use cases, integration patterns, and API reference, see the [Sharables Guide](/technical-specification/hub-backend/engine/sharables)**

A token expires when a corresponding execution is terminated, unless the token is marked as `reusable()`.

* **url** if provided generates a link using the sharable token
* **payload** an attribute/payload to be shared
* **function** a name of function to execute, optionally execution *input* and *context* can be provided
* **workflow** a name of workflow to execute, optionally execution *input* and *context* can be provided
* **latest()** a workflow or function reference revision is resolved when sharable token is used (rather than fixed)
* **expired()** expires specified token
* **reusable()** the token does not expire after corresponding execution terminates
* **namespace** asynchronously stores the function or workflow execution result
* **expireIn** token will expire after specified duration (use ISO-8601 duration format, default is 24 hours)

Examples of usage are following:

```groovy theme={null}
token << sharable { function 'function-name' }

sharable {
   reusable()
   function 'function-name'
   namespace result
}
```

Generates a sharable token to execute a function named `function-name`. The token gets stored in the `token` namespace.

```groovy theme={null}
link << sharable {
   url "http://localhost:1234/sharable/$token"
   workflow('workflow-name') {
      context url: 'http://localhost'
      input userId: 'dummy123'
   }
   expireIn 'PT30M'
}
```

Generates a sharable link to execute a workflow named `workflow-name` with `input` and `context`.
The link gets stored in the `link` namespace. The link will expire in 30 minutes.

```groovy theme={null}
sharable(input.token) {
   payload application
}
```

Generates a sharable payload that stores `application` attribute and can be accessed using `input.token` token.

```groovy theme={null}
sharable('vJRRTX') {
   expired()
}
```

Expires a specific sharable token.

```groovy theme={null}
sharable(execution.sharable) {
   expired()
}
```

Expires a sharable token that was used to execute the current execution.

### Exporting namespaces

A context attribute namespace can be exported and queried using Execution API

```groovy theme={null}
export config
```

## Logging

It is possible to generate application logs from the DSL using the `log` command

```groovy theme={null}
log "message"
```

The log message is logged using `com.zenoo.hub.dsl.executor.DSLLogger` class with the following format

```groovy theme={null}
{uuid}: DSL logger {executable}: {message}
```

**Example**:

a log message using context attributes

```groovy theme={null}
log "request.id=${request.request_id}"
```

## Flow control

### await

Blocks the current execution and waits until an *attribute* is set, e.g. by an asynchronous function, exchange or sharable token.

```groovy theme={null}
await attribute
```

### match

Executes a DSL script *definition* when an *expression* evaluates as true. The expression can contain context attributes.

```groovy theme={null}
match (expression) {
    definition
}
```

#### exist

Executes a DSL script *definition* when an *attribute* is set.

```groovy theme={null}
exist (attribute) {
    definition
}
```

#### switch / case

The switch statement matches expression with cases and executes the matching case. It's a fallthrough switch-case. You can share the same code for multiple matches or
use the *break* command. It uses different kinds of matching like, collection case, regular expression case, closure case and equals case.

```groovy theme={null}
switch (expression) {
    case "bar":
        route "Bar"
        break

    case ~/fo*/:
        route "Foo"
        break

    case [4, 5, 6, 'inList']:
        route "Matched"
        break

    default:
        route "Default"
}

```

#### loop-until

Executes a DSL script *definition* until an *expression* evaluates as true.

```groovy theme={null}
loop {
    definition
} until { expression }
```

The maximum number of attempts can be specified.

```groovy theme={null}
loop(3) {
  workflow
} until { expression }
```

In addition, the attempt counter can be accessed as follows:

```groovy theme={null}
loop(3) {
  attempt ->
    route('test') {
      export attempt
    }
} until { expression }
```

## Logging and metrics

### Logging

It is possible to produce logging messages in the DSL for the purpose of tracking and trouble-shooting.

The logging messages are logged as DEBUG messages via `com.zenoo.hub.dsl.executor.DSLLogger`.

In addition, the log messages are stored as part of execution events.

```groovy theme={null}
log "Request ID:${response.id}"
```

### Metrics

You can use the following commands in order for tracking visited interactions and their outputs.

```groovy theme={null}
interactionStart(String id, Map<String, String> tags)
interactionOutput(String id, String output, Map<String, String> tags)
```

e.g.

```groovy theme={null}
interactionStart('x23vzt5', [ uri: 'welcome', type: 'PageInteraction', name: 'Welcome page one' ])
interactionOutput('x23vzt5', 'default', [ uri: 'welcome', type: 'PageInteraction', name: 'Welcome page one' ])
```

### Meters

You can use the `meter` command to record named measurements with numeric values. Meters are useful for tracking usage, billing, and custom metrics. The execution context UUID is automatically included in the event.

```groovy theme={null}
meter(String name, Number value, Map<String, String> tags)
meter(String name, Number value)
meter(String name)
```

The `value` defaults to `1` and `tags` defaults to an empty map when omitted.

e.g.

```groovy theme={null}
meter('api-calls')                                                          // counter: value=1, no tags
meter('data-transferred', 2048.5)                                           // value without tags
meter('api-calls', 1, [ type: 'ApiUsage', name: 'External API Calls' ])    // full form with tags
meter('loan-value', 15000.00, [ type: 'LoanMetric', currency: 'USD' ])
```
