Skip to content

Howto develop your own HTTP/JSON Botium Connector

While it is possible to create your very own Botium Connector for your own Conversational AI API from scratch, for low to medium complex APIs based on HTTP/JSON it is usually easier to leverage the included Generic HTTP(S)/JSON Connector and adapt it to your own needs.

Using the SIMPLEREST_ capabilities

You can use the exported capabilities prefixed with SIMPLEREST_ in your botium.json (Botium Core) or in the Botium Box Connector Settings screen:

Configuring a Botium Connector this way has some drawbacks:

  • For accessing multiple endpoints of your API (Dev/Test/Prod) you have to duplicate all of the connector configuration settings

  • When extending behaviour with Javascript code, there is no source code management, no syntax highlighting, no other developer tools - just a plain field holding Javascript code (like back in the 80s)

  • Hard to handover Botium Connector logic to other developers

  • Users can “change” (=break) the Botium Connector logic

Extend Generic HTTP/JSON Connector

The better option is to develop your own Botium Connector based on the Generic HTTP/JSON Connector. All that is needed is one single Javascript file. This approach solves most of the issues described before:

  • The Javascript file can be added to source control and edited with developer tool

  • It exposes the configurable parts of your API to Botium as your custom capabilities

  • Can be used as any other connector in Botium Core and Botium Box

Boilerplate Code

Here is some boilerplate code you can use to build your own Botium Connector. The only function that contains real logic is the Validate function, which is used to dynamically generate the configuration for the Generic HTTP/JSON Connector, tailored to your custom API:

const SimpleRestContainer = require('botium-core/src/containers/plugins/SimpleRestContainer.js')
const CoreCapabilities = require('botium-core/src/Capabilities')

const Capabilities = {
  MYAPI_URL: 'MYAPI_URL',
  MYAPI_TOKEN: 'MYAPI_TOKEN'
}

class BotiumConnectorMyApi {
  constructor ({ queueBotSays, caps }) {
    this.queueBotSays = queueBotSays
    this.caps = caps
    this.delegateContainer = null
    this.delegateCaps = null
    this.userSaysCounter = 0
  }

  Validate () {
    if (!this.caps[Capabilities.MYAPI_URL]) throw new Error('MYAPI_URL capability required')

    if (!this.delegateContainer) {
      this.delegateCaps = {
        [CoreCapabilities.SIMPLEREST_URL]: this.caps[Capabilities.MYAPI_URL],
        [CoreCapabilities.SIMPLEREST_METHOD]: 'POST',
        [CoreCapabilities.SIMPLEREST_RESPONSE_JSONPATH]: '$.reply',
        [CoreCapabilities.SIMPLEREST_BODY_TEMPLATE]: JSON.stringify({ 
          username: 'botium',
          message: '{{msg.messageText}}',
          session: '{{botium.conversationId}}',
          startsession: false,
          quickreply: null
         })
      }
      if (this.caps[Capabilities.MYAPI_TOKEN]) {
        this.delegateCaps[CoreCapabilities.SIMPLEREST_HEADERS_TEMPLATE] = `{ "Authorization": "Token ${this.caps[Capabilities.MYAPI_TOKEN]}"}`
      }

      this.delegateCaps[CoreCapabilities.SIMPLEREST_REQUEST_HOOK] = ({ msg, requestOptions, context }) => {
        if (this.userSaysCounter === 0) {
          requestOptions.body.startsession = true
        } else {
          requestOptions.body.startsession = false
        }
        if (msg.buttons && msg.buttons.length > 0) {
          delete requestOptions.body.message
          requestOptions.body.quickreply = msg.buttons[0].payload || msg.buttons[0].text
        }
      }
      this.delegateCaps[CoreCapabilities.SIMPLEREST_RESPONSE_HOOK] = ({ botMsg, botMsgRoot }) => {
        if (botMsgRoot.status === 'error') throw new Error(`MyAPI Error: ${botMsgRoot.message}`)

        if (botMsgRoot.quickreplies && botMsgRoot.quickreplies.length > 0) {
          botMsg.buttons = botMsgRoot.quickreplies.map(b => ({ text: b.title, payload: b.value }))
        }
      }

      this.delegateCaps = Object.assign({}, this.caps, this.delegateCaps)
      this.delegateContainer = new SimpleRestContainer({ queueBotSays: this.queueBotSays, caps: this.delegateCaps })
    }
    return this.delegateContainer.Validate()
  }

  async Build () {
    await this.delegateContainer.Build()
  }

  async Start () {
    this.userSaysCounter = 0
    await this.delegateContainer.Start()
  }

  async UserSays (msg) {
    await this.delegateContainer.UserSays(msg)
    this.userSaysCounter++
  }

  async Stop () {
    await this.delegateContainer.Stop()
  }

  async Clean () {
    await this.delegateContainer.Clean()
  }
}

module.exports = {
  PluginVersion: 1,
  PluginClass: BotiumConnectorMyApi,
  PluginDesc: {
    name: 'My API',
    provider: 'Me',
    capabilities: [
      {
        name: 'MYAPI_URL',
        label: 'MyAPI Endpoint',
        type: 'url',
        required: true
      },
      {
        name: 'MYAPI_TOKEN',
        label: 'MyAPI Authorization Token',
        type: 'secret',
        required: false
      }
    ]
  }
}

Note

You can find this code in the Botium Core repository on Github.

Code Walkthrough

The custom API in this case is a simple HTTP/JSON endpoint which receives a flat JSON document and responds with a flat JSON document.

  • The endpoint is given by the MYAPI_URL capability
  • The SIMPLEREST_BODY_TEMPLATE capability is used for building the main structure of the JSON document sent to the endpoint

  • The SIMPLEREST_REQUEST_HOOK capability is a function doing some additional stuff on the JSON document:

    • If it is the first message sent by the user, a startsession flag is activated

    • If there are button clicks (quick replies) to simulate instead of message text the quickreply-payload is sent

  • If an authorization token is given in the MYAPI_TOKEN capability, it is added as a HTTP header

When receiving the response from the endpoint, the JSON response document is evaluated:

  • The SIMPLEREST_RESPONSE_JSONPATH capability extracts the response text from the JSON response document

  • The SIMPLEREST_RESPONSE_HOOK capability is a function for dynamic evaluation of the JSON response document:

    • Detect if there is a processing error

    • Detect additional fields in the JSON response document, in this case if there are quickreply buttons available

Note

For detailed documentation of the available SIMPLEREST_ capabilities see here: Generic HTTP(S)/JSON Connector

Configuration Options (Capabilities)

The boilerplate code also contains a plugin descriptor holding the name of the connector as well as the capabilities metadata. This is not only for documentation, but Botium Box will use this to show a nice configuration screen (see below).

Note

For detailed documentation of the capability metadata see here: Howto develop your own Botium Connector

Botium Connector Deployment

See Howto deploy my own Botium Connector