Integration Connection

As discussed, Connection represents the most common parameters necessary to run your integration.

PASSWORD = ConnectionParam("PASSWORD", description="Password for JIRA")

Definition of Connection Param class i.e. all the parameters it accepts.

class ConnectionParam:
  id: str
  description: str
  label: str
  default=None
  optional: bool=False,
  options=None,
  data_type=DataType.STRING, # Options: STRING, NUMBER, BOOL, INT, JSON
  input_type=InputType.TEXT # Options: TEXT, TEXT_AREA, EMAIL, PASSWORD, SELECT, FILE, ENCRYPTED_FILE
  • Default - Represents the default value of the parameter, transforming it into an optional parameter, expressed as a string.
  • Optional - Indicates whether the parameter is optional or mandatory.
    • Options - If the parameter's input type is Select, you can define the available options here
    options = [
      {
    		"label": "Api Key",
        "value": "api_key"
      },
      {
        "label": "Api Key",
        "value": "api_key"
      }
    ]
    
  • Data Type - how do you want to parse the data. i.e. as a String, Number(float), Boolean, Int, Json.
  • Input Type - These refer to HTML tags that determine the appropriate rendering for each field.
    • Text - Represents a small text field.
    • TextArea - Represents a large text field spanning multiple lines.
    • Password - Represents a text field with a hidden value.
    • Select - Represents a dropdown menu.
    • Files - Represents the option for uploading a file.
    • Encrypted File - Represents the option for uploading a file without revealing its text value (useful in cases where the file contains sensitive information).

📘

Note:

The parameters mentioned above are only recognized within the class constructor and are not parsed from the docstring. All the aforementioned fields must be provided during the definition of Connection and Action parameters.

It's okay if you don't have any common parameter. But, we need at least a single action to be defined. So, following will be the bare bone custom integration.

# A top level __doc__ string in any of the files can be used to set meta information for the entire integration.
# Information in required format must be specified in only one of the files (any one file).
# Other files can contain __doc__ strings but it shouldn't be in the following format so that meta information is picked up in a predicatable manner.
"""
name: Custom Integration
description: Custom Integration Description
logoUrl: https://s3.amazonaws.com/lhub-public/integrations/default-integration-logo.svg
"""

# `lhub_integ` is already installed as a dependency in the container
# For testing, you can do `pip install -e <path to backend/custom-integrations/lhub-integ`
from lhub_integ.params import ConnectionParam, ActionParam
from lhub_integ import action

# ConnectionParams must be declared at the top level.
# To get the value of the Parameter call param_name.read()

# ConnectionParam can take the values `optional=[True/False]` and `default="some_default_string"`
"""
ConnectionParam and ActionParam must be called with an identifier
Other optional named parameters:
description="description shown in the UI"
label="label shown for parameter in UI"
optional=[True/False]
default="some_default_string"

Advanced usage:
data_type=DataType.[STRING/COLUMN/NUMBER] - defaults to STRING
input_type=InputType.[TEXT/TEXT_AREA/EMAIL/PASSWORD/SELECT/COLUMN_SELECT] - defaults to TEXT
options= [list of options in a drop down]
"""

PASSWORD = ConnectionParam("PASSWORD", description="Password for JIRA")
URL = ConnectionParam("URL", description="URL for the server")


@action
def process():
    return {
      "shared_action_param": URL.read()
    }

The connection parameters defined above comes as input while creating connection

Connection Validator:

Write a function and annotate it with @connection_validator. This option checks the validity of the connection. For example, you can check whether the input credentials entered by the integration user are valid.

For Slack Integrations, you can make an API call (using the requests library) to check whether the value entered by the user for Incoming Webhook URL is valid.

📘

Note

Utilities are available to perform validity or sanity checks, including validations, input_helpers, and helpers. You can explore them or you can write your own sanity checks

import requests
from lhub_integ import connection_validator
from lhub_integ.common import helpers, validations
from lhub_integ.params import ValidationError
from typing import Optional, List

def post_message_on_slack(incoming_webhook, message, channel_name=''):
    payload = {'text': message}
    if channel_name:
        payload['channel'] = channel_name
    response = requests.post(incoming_webhook, json=payload, headers={'Content-Type': 'application/json'})
    response.raise_for_status()


@connection_validator
def validate_connections() -> Optional[List[ValidationError]]:
    if not SLACK_INCOMING_WEBHOOK.read():
        return [ValidationError(message="Parameter must be defined", param=SLACK_INCOMING_WEBHOOK)]

    try:
        post_message_on_slack(SLACK_INCOMING_WEBHOOK.read(), 'This is a test message from LogicHub, Inc.')
    except Exception as ex:
        return [ValidationError(message=f"Incorrect Parameter: {repr(ex)}", param=SLACK_INCOMING_WEBHOOK)]

The function annotated with the connection_validator decorator must return either an empty array or None. In the case of errors, it should return an array of ValidationError.

Dynamic Connection Params

If an integration requires dynamic descriptors based on connection, they can be implemented (in your main.py file) as shown in the below example:

import json
from lhub_integ.common import input_helpers
from lhub_integ import connection_validator

def override_action_descriptors():
    descriptor_json = input_helpers.get_stripped_env_string('__integration_descriptor')

    # Custom value to be overridden at connection validation
    new_description = "My Custom Dynamic Description"

    descriptor = json.loads(descriptor_json)
    actions = descriptor['actions']
    actions[0]['instantiation']['steps'][0]['inputs'][0]['description'] = new_description
    return {"actionOverrides": actions}


@connection_validator
def validate_connections():
    result = json.dumps(override_action_descriptors())
    print("[result] {}".format(result))

© 2017-2021 LogicHub®. All Rights Reserved.