Skip to main content

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.

Component model

The Zenoo Hub employs a component model to enable a development model where an onboarding solution is composed of components. Components are reusable building blocks that are configurable and testable. Each component provides a cohesive piece of functionality that is well tested, documented and can be reused in different contexts or clients. This approach reduces complexity in many aspects of development. Building from smaller, well-tested pieces of functionality becomes significantly simpler and more manageable. Let’s review an example onboarding project that is assembled from several components. Two of those, otp and document-check, are ready-to-use components.
  • huddle component contains the main workflow, business logic and project-specific configuration.
  • huddle.routes component contains project-specific route definitions.
  • document-check component provides ID document and liveness check functionality via a set of workflows and functions, the configuration includes the country and API credentials.
  • otp component provides a workflow to verify an SMS OTP code using a customer mobile, the configuration includes a number of retries, country code, OTP provider, etc.
alt text

Hub Component

A Hub Component is a reusable building block that provides a cohesive piece of functionality which is well documented, configurable and testable. Each component is identified by a unique name and version. It explicitly declares its dependencies of other components that are used. It defines one or more DSL closures that used for execution by the DSL engine, these include target, worfklow, function, mapper, route and exchange. A Hub component is defined as follows:
  • name a unique name of a component,
  • version a component version, if omitted SHA-256 hash is generated,
  • definition a set of DSL closure definitions using Component DSL,
  • dependencies a set of resolved references to other components and connectors used in the definition.
  • configuration a JSON-like configuration data, see more
  • configuration schema a JSON schema for configuration data, see more

Component repository

Hub components are stored in a component repository. The components are then retrieved by the Execution Engine when a new execution is triggered. The components are stored using a cloud-agnostic storage layer that supports multiple providers (AWS DynamoDB, in-memory storage, etc.). The storage implementation is selected via configuration and provides versioned component storage with optimistic locking. For more details, see Cloud Provider Support. The process of registering a new Hub component consist of several steps:
  • validates component definition based on Component DSL,
  • checks the availability of component dependencies,
  • validates each DSL closure based on Hub DSL,
  • resolves component dependencies versions,
  • generates the component version if omitted, using SHA-256 hash of the component,
  • registers the component.
The component repository provides a REST API for registering, validating and querying Hub components, see Admin API. Additionally, Hub components can be registered automatically at the application start using ComponentConfigurer.

Component DSL

A Hub component defines one or more DSL closures; and a set of dependencies to other components and connectors using Component DSL. Each DSL closure defines one of the following

DSL reference

Each DSL closure (except for target) can be referenced by its name and corresponding component as name@component. E.g. a target references a workflow defined is a component named workflows
dependencies {
    component 'workflows'
}

target {
    workflow('test@workflows')
}
The component can be omitted if referencing a DSL closure defined in the same component. E.g. a target references a workflow in the same component
target {
    workflow('test')
}

workflow('test') {
    defintion
}

Target

A target acts as an entry point for a given onboarding process and can be executed via the Hub Client API. A target can reference an existing workflow or define one using using the Hub DSL. Each Hub component can define at most one target.
target {
    workflow('reference')
}

Exposed

Defines a function that can be executed directly through Hub Client API. Each Hub component can define multiple exposed functions. An exposed function can reference an existing function or define one using using the Hub DSL. E.g. referencing existing function
exposed('reference') {
    function('existing@component')
}

exposed('inline') {
    sharable {
        function('existing.')
    }
}
In addition to functions, exposed functions can specify HTTP response details, like headers and status code.
exposed('store') {
    function('create-record@crm')
        .onDecision('ACCEPTED') {
            payload ->
                response {
                    status HttpStatus.ACCEPTED
                    header HttpHeaders.LOCATION, payload.url
                    body payload
                }
        }
    error()
}

Workflow

Defines a workflow with name using the Hub DSL as a series of routes, exchanges, workflows, functions, etc. In the workflow definition, it is possible to use DSL closures defined within the component and declared dependencies.
workflow('name') {
    definition
}

Function

Defines a function with name using the Hub DSL as a series of exchanges, functions, data mappings and transformations. In the function definition, it is possible to reference DSL closures defined within the component and declared dependencies. A valid function does not contain any route or workflow.
function('name') {
    definition
}

Route

A route corresponds to a user interaction, a web page or a screen, depending on a Hub Client implementation. A route can be used in a workflow using the route reference, see more details.
route('name') {
    uri '/uri'
    export data
    validate { payload specification }
    checkpoint()
    terminal()
}
  • uri identifies a route for a Hub Client and is mandatory
  • export is used to pass data to a Hub Client, like lists and key-value maps
  • validate specifies data constrains for route result submitted by a Hub Client
  • checkpoint() marks the route as a checkpoint, disables going back
  • terminal() marks the route as terminal and checkpoint

Mapper

An attribute mapper transforms an input into a result using expression. It be used for data mappings, transformations, calculations, etc.
    mapper("name") {
        input ->
            expression
    }
A mapper can be used in a function or workflow using the mapper reference, see more details. As an example, the following mapper generates a client full-name using the firstname and lastname.
    mapper("client-fullname") {
        input -> [ fullname: "$input.client.firstname $input.client.lastname" ]
   }

Transport

Specifies an HTTP client that can be reused by multiple http calls. The client configures the following:
  • base URL
  • authentication
    • basic
    • bearer
  • more to come:)

Basic authentication

Basic Authentication is a simple authentication mechanism where the client includes a username and password in the request header to authenticate with the server. These credentials are sent as a Base64-encoded string.
  • username the username used for authentication
  • password the password associated with the username
transport('id-provider') {
    auth {
        basic {
            username 'username'
            password 'password'
        }
    }
}

Bearer authentication

The bearer (OAuth2) authentication has the following configuration
  • url the token endpoint URL where the authentication request is sent
  • type the grant type used for authentication (e.g. password, client_credentials), the default is password
  • scope the level of access requested, as a space-separated list of permissions
  • username the username of the resource owner (used in password grant type).
  • password the password of the resource owner (used in password grant type).
  • clientCredentials the client credentials (client_id:client_secret) for the application, encoded in Base64
transport('id-provider') {
    auth {
        bearer {
            url "$baseUrl/token"
            username 'username'
            password 'password'
            scope 'transactions/get'
        }
    }
}

Dependencies

A Hub component explicitly specifies its dependencies to other components and connectors. The dependencies are declared as part of the component definition using dependencies block. A component dependency is referenced using a component ID component-name:revision. The latest component revision is used when the revision is omitted. A connector dependency is referenced using a fully classified connector name, i.e. connector-name@component-name:revision. Optionally, it is possible to configure component dependencies as below. The configuration is then accessible as a component configuration. For example
dependencies {
    connector 'sms@otp:1.2.0'
    component 'zenoo.playground'
    component 'zenoo.otp:2.4', [countryCode: '+420', tries: 3]
}

Component configuration

Each Hub component can be configured using a JSON-like configuration data. In a component definition, it is possible to access a component configuration, the same way context attributes are accessed in the DSL. There are two ways to configure a component:
  • set using a component config service
  • set when declaring a component dependency in the dependencies section

Component config service

A component configuration comes from a component config service when a component is exposed, i.e. defines target or exposed functions. The config service looks up a component configuration using the following sources:
  • Spring environment, looks up a component configuration under hub.components.config using the component name
  • Cloud provider configuration storage (e.g., AWS Secrets Manager, in-memory storage, etc.)
It is possible to set a component configuration when registering a component. The configuration is then stored using the component config service. e.g. ‘playground.test’ component configuration set using Spring environment
hub:
  components:
    config:
      stage:
          playground.test:
            service: dummy
In a component definition, it is possible to reference and retrieve a component configuration stored in the Component config service:
dependencies {
    component 'zenoo.playground', config('stage.zenoo.playground')
}
Optionally, you can specify a configuration namespace to retrieve.
dependencies {
    component 'zenoo-otp:2.4', config('zenoo-otp@sandbox.config')
}

Component dependency config

You can specify a custom configuration when declaring a component dependency. The custom configuration is then merged with a component default configuration can used for configuring the component dependency. The default component configuration comes from the config service using the component id/name; E.g. a component configuration used for dependencies configuration and in a workflow definition, the component configuration is [code:1234, tries:3]
dependencies {
    component 'zenoo.playground'
    component 'zenoo.otp:2.4', [countryCode: code]
}

workflow('play') {
    loop(tries) {
        workflow('do-something')
    }
}
If a component defines a target and/or exposed functions, a configuration validation is performed on the component and its dependencies. It checks is the root component and all its child/dependency components are configured correctly. It is possible to used JSON-schema to specify a component configuration or require check in the Component DSL. If a component is not configured by its parent (using the dependencies), it is configured using the config provided at the component registration. Which acts as a component default/fallback configuration.

Attributes

In addition, it is possible to set and update a component configuration directly in a component definition using attributes. E.g. a component dependency and a workflow definition using an attribute tries
tries << tries ?: 3

dependencies {
    component 'zenoo.playground'
    component 'zenoo.otp:2.4', [countryCode: '+420', tries: tries]
}

workflow('play') {
    loop(tries) {
        workflow('do-something')
    }
}
It is possible to use require to check if a component configuration matches a payload specification, see more E.g.
require {
    api {
        url
        username
        password
    }
}

exchange('exchange') {
    http {
        url api.url
        basicAuth api.username, api.password
    }
}

Component api key

Each Hub component can be secured using an api key. When registering an component through Admin API it is possible to specify component api key. Another option is to set this auth key using Spring environment. If api key is specified then when executing Target or Exposed function through Client API, the client must provide the component api key in the request header otherwise 401 (no api key) or 403 (wrong api key) status code is returned. e.g. ‘playground.test’ component configuration set using Spring environment
hub:
  components:
    security:
      "playground.test": "secret-key"