> ## 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.

# Context Attributes

> The execution context attributes store JSON-like data related to an execution, like user input, connector responses and configuration.

# Context Attributes

The execution context **attributes** stores JSON-like data related to an execution, like user input, connector responses and configuration.
The attributes are used for sharing data between different DSL commands and making flow control decisions.

An attribute is accessed by its **key** using `.` for hierarchical access, e.g. `client.address.city`.
An attribute with no content is returned if there's no data for a given `key`.

## Command namespace

Throughout a workflow execution, DSL commands store their results as *context attributes* using **namespace** as attribute key.

The whole attribute namespace is **overwritten** and any existing attributes stored within the namespace is lost.

e.g. a client route *result* will be stored in the `application.client` namespace.

```
route('client') {
   uri '/client'
   namespace application.client
}
```

## Execution namespace

It is possible to get more details on the current execution using the `execution` namespace.

`execution.uuid` to get current execution UUID
`execution.sharable` to a sharable token used for starting the current execution

## Shared namespace

A shared namespace starts with `share`. It can be accessed anywhere throughout the execution.
The shared namespace can be thought of as a global namespace.

It eliminates the need for passing data between functions and workflows explicitly. And can be used for asynchronous coordination between different parts of the DSL.

```
sharable(token) {
    function ('mirror@parent') {
        namespace share.set
    }
}

share.interaction.get('xyz') << response

await(share.set)
```

## Setting using \<\<

In addition, it is possible to set context attributes directly using `<<`.

```
config.logo << 'http://logo.png'
products << [product1: "Product1", product2: "Product2"]
```

The `<<` operator **merges** existing namespace with the specified payload, unlike using a command *namespace*.
It can be used for gathering data from multiple commands using the same namespace.

```
application << route('basic info')
application << route('advanced info')
```

## Default values

The diamond operator can be used for providing a default value when an attribute is not set.

```
config.retries ?: 3
```

## Lists

Attributes can store lists. It is possible to access list items using an item index as following:

`list[index]`

It is possible to use positive as well as negative index to access items from a list start and from a list end.

e.g. get a list item using an index

```groovy theme={null}
list << ['a', 'b', 'c']
list[0] == 'a'
list[2] == 'c'
```

e.g. get an item from a list end using a reverse index

```groovy theme={null}
list << ['a', 'b', 'c']
list[-1] == 'a'
list[-2] == 'b'
```

An attribute with no content is returned if an invalid index is used or an attribute does not store a list.

## Attribute methods

* `isEmpty()` determines is attribute is empty, e.g empty string, list, map
* `hasContent()` determines is attribute has content
* `removeNulls()` removes nulls (hierarchically) and returns a new attribute

## Remove attribute

To remove an attribute namespace use `remove namespace` DSL command.

```
remove client.test
remove 'toRemove'
```

## Query and filtering

### `find { condition }`

finds the first item in a list or map that matches the given closure condition otherwise returns empty attributes

E.g. find a document with type `'SELFIE'`

```groovy theme={null}
    type << 'SELFIE'
    documents << [[id: 1, type: 'ID'], [id: 2, type: 'DRIVING'], [id: 3, type: 'SELFIE']]
    documents.find { it.type == type } // [id: 3, type: 'SELFIE']
```

### `findAll { condition }`

finds all items in a list or map that matches the given closure condition otherwise returns empty list

E.g. find all documents with types `'SELFIE'` or 'ID'

```groovy theme={null}
    types << ['SELFIE', 'ID']
    documents << [[id: 1, type: 'ID'], [id: 2, type: 'DRIVING'], [id: 3, type: 'SELFIE']]
    documents.findAll { it.type in types } // [[id: 1, type: 'ID'], [id: 3, type: 'SELFIE']]
```

## Transformation

### `collect { transform }`

iterates through this list transforming each item into a new value using the transform closure, returning a list of transformed values.

E.g. get all documents types

```groovy theme={null}
    documents << [[id: 1, type: 'ID'], [id: 2, type: 'DRIVING'], [id: 3, type: 'SELFIE']]
    documents.collect { it.type } // ['ID', 'DRIVING', 'SELFIE']
```

## Payload validation

It is possible to specify data structure and constrains for attribute payload, see [Payload specification](/technical-specification/hub-backend/engine/attributes#payload-specification) for more details.

The payload specification is then used for payload validation using the `validate` or `require` blocks.

* `validate` is used in DSL commands, like *route* and *exchange*, to validate a command result.
* `require` is used in a workflow definition to enforce data-constrains, like input attributes.

### Require payload

Checks if a given attribute is set and matches a payload specification. Otherwise, the corresponding execution terminates with an error.
It can be used for enforcing attribute data-constrains during an execution, like checking a function input or workflow configuration.
Also, it is possible to use the `require` result for setting another attribute. The result only contains fields that are specified in the payload specification.

Example: check if `input.test` is not empty and set the `test` attribute:

```
input ->
    test << require(input.test)
```

Example: check if `input` contains `firstname` and `lastname`:

```
input ->
    require(input) {
        firstname
        lastname
    }
```

The current execution context is checked if attribute is omitted:

```
    config <<
        require {
            api {
                username
                password
            }
        }
```

## Payload specification

A **payload specification** defines attribute payload data structure and constrains.

For **key-value** maps, it is possible to specify each key *name* and corresponding data constrains for values using the provided validators.
If no validator is specified, a `required()` validator is used by default.

You can use the following validators:

* **required()** must not be empty,
* **optional()** may or may not be empty,
* **string()** must be a string,
* **number()** must be a number,
* **list()** must be a list,
* **truefalse()** must be a boolean,
* **file()** must be a file descriptor,
* **file mimeType** must be a file descriptor and match the mime type, e.g. `application/pdf`, `image`, etc.,
* **oneOf value1, value2, ...** must be one of the specified values,
* **regex \~/pattern/** must match a regular expression, the expression can be a string or a groovy regex pattern syntax.

A validate example:

```
validate {
    firstname
    lastname
    address {
            city { oneOf "Prague", "Paris" }
            zip { regex ~/^[0-9]{5}(?:-[0-9]{4})?$/ }
        }
    }
    age { number() }
    idFront { file 'image'}
}
```

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