Connector SDK UI Components

Connectors and the Cloud Studio UI

The Cloud Studio UI for a connector created with the Jitterbit Harmony Connector SDK is defined by a JSON file included in the JAR file that packages the connector. By default, this is named adapter.json.

Here is an overall schematic of the JSON file that defines a connector UI, with placeholder values shown as <name>:

{
  "name": <name>,
  "sandbox": true,
  "defaultActivityIcon": <path-to-SVG-file>,
  "endpoint": {
    "displayName": <display-name>,
    "icon": <path-to-SVG-file>,
    "properties": [ <properties> ]
  },
  "activities": {
    "activity-1": {
      "displayName": <display-name-1>,
      "icon": <path-to-SVG-file>,
      "properties": [ <properties> ]
    },
    "activity-2": {
      "displayName": <display-name-2>,
      "properties": [ <properties> ]
    },
    . . .
  }
}

In this schematic, a connector has been defined with properties for the endpoint and its first two activities. Additional activities can be added as required.

For the connection (which is what appears when an end user first configures the connector in the Cloud Studio UI), a set of properties can be defined that will be used to generate one or more steps that the end user completes to configure the connection to the endpoint.

Note that the name used must be the same name that the connector is registered under (see Connector Registration).

The sandbox property can be set to true or false. Set to true means that the connector is in development, and will not be cached by Jitterbit Harmony. Instead, each invocation will reload the connector from the Jitterbit Harmony Private Agent. Once you have a production version, you can set this to false so that caching can be used to speed use.

A set of properties is defined for each activity, generating steps for configuring the activity after it is added to an operation in Cloud Studio.

Endpoint Icons

As can be seen in the above schematic, you can define icons for the connection and each of the activities (together referred to as an endpoint). Icons are defined using SVG, and are provided as a path to an SVG file.

These keys can be provided in the adapter.json:

Key Description
defaultActivityIcon Path to an SVG file, used as the icon if an icon is not defined for the endpoint or an activity
icon Path to an SVG file, used as the icon for the endpoint or for an activity

In the schematic example above, a default icon has been defined, with the connection and the first activity using other—perhaps different—icons. The second activity does not have an icon defined, and instead uses the default icon.

This screen capture of the Dropbox connector shows how different icons can be used:

One icon (a folder) is used for the connection with a different icon used for the activities. It’s possible to have a different icon for each activity, though a common theme of colors and shapes for an endpoint is advised. Note that text describing the activity is overlaid on the icon. It’s best to leave the bottom half of the icon a solid color so that the white text has a background to appear against.

Notice that the order of the icons in the UI follows the order of the activities in the JSON. If the number of activities exceeds three, additional rows of activities are added in the UI underneath the original one.

Our recommendation for the order of activities is to follow these common patterns:

  • Read, Write
  • Query (or Search), Create, Update, Upsert, Delete
  • Get, Post, Put, Delete
  • Request, Response
  • Download, Upload

If there are any custom, special, or entity-specific activities outside of the above examples (such as Get List), put them toward the beginning of the order if they are intended to provide data as a source in an operation, and toward the end if they are intended to receive data as a target in an operation.

Pagination

For the connection and each activity, properties are defined in a JSON structure. These create the configuration steps (pages) that an end user goes through to configure the connection and each of the particular activities that they choose to use.

For connections, there is often only one step, though the developer may add as many as needed.

For activities, there are at least two steps: a beginning page with a name field and an ending page showing the data schemas for review. Both of these pages are created automatically by the infrastructure and are not included or defined in the JSON file.

Default Pagination

Connections do not have any pagination by default.

For activities, here are typical examples of starting (page 1) and ending (page 2) steps. Each activity configuration has a name field (on the first page) and a data schema (on the last page). These are not defined or included in the JSON file that specifies the UI; instead, they are automatically generated and included by the framework:

Single Page

If the connection or activity can be configured on a single page and pagination is not required, a simpler structure can be used:

. . .
"properties": [
  {
    "name": <name-1>,
    "displayName": <display-name-1>,
    "type": "string",
    "validators": [
      {
        "name": "required"
      }
    ]
  },
  {
    "name": <name-2>,
    "displayName": <display-name-2>,
    "type": "string",
    "validators": [
      {
        "name": "required"
      }
    ]
  }
]
. . .

Multiple Pages

However, if the configuration is longer, with multiple values to be set or reviewed, pagination is recommended to keep the views smaller in depth:

. . .
"properties": [
  {
    "name": "page1",
    "displayName": <display-name-page-1>,
    "type": "pagination",
    "children": [
      {
        "name": <name-1-1>,
        "displayName": <display-name-1-1>,
        . . .
      },
      . . .
    ]
  },
  {
    "name": "page2",
    "displayName": <display-name-page-2>,
    "type": "pagination",
    "children": [
      {
        "name": <name-2-1>,
        "displayName": <display-name-2-1>,
        . . .
      },
      . . .
    ]
  }
]
. . .

Note

Though pagination is supported, the display name is not currently displayed in the Cloud Studio UI for each step. Instead, this can be used by the developer to distinguish the pages in the JSON file.

Available UI Components

These UI components are available:

All UI components can have a defaultValue and validators; see the first string field for an example. Validators are described in Adding and Creating Validation.

Fields that show a variable icon Variable icon are UI component examples that support variable replacement. When a user enters an opening square bracket ([), a list of possible variable completions (Jitterbit, project, and global variables) will be displayed. Currently, only the String Entry UI component supports variable replacement.

This sample shows the available components, with detailed code fragments in the examples that follow:

String Entry With Default Value

{
  "name": "example_string_with_default",
  "type": "string",
  "defaultValue": "/",
  "displayName": "Example string...required validator",
  "validators": [
    {
      "name": "required"
    }
  ]
}

String Entry Without Default Value

{
  "name": "example_string_without_default",
  "type": "string",
  "displayName": "Example string without a default value"
}

String Entry With Obscured Entry (“Password”)

{
  "name": "example_password_string",
  "type": "string",
  "displayName": "Example password string",
  "multiple": false,
  "widgetHint": "password"
}

String Entry With Hidden Entry (No Visible UI Element)

In this case, no visible UI element is displayed. A value is available for setting, either as a default value or programmatically by other components.

{
  "name": "example_hidden_string",
  "type": "string",
  "displayName": "Example hidden string",
  "defaultValue": "hidden_value",
  "hidden": true,
  "multiple": false
}

Text Area

Designed for multiple lines of text.

{
  "name": "example_textarea",
  "type": "textarea",
  "displayName": "Example text area",
  "location": "last",
  "multiple": false,
  "widgetHint": "textarea"
}

Number Entry

For entering numbers, with increment/decrement arrows (visible either on mouse-over or when the field is in focus) and keyboard arrow activation (up and down arrow keys increase and decrease the value).

{
  "name": "example_number",
  "type": "number",
  "displayName": "Example number",
  "multiple": false
}

Boolean Entry (Checkbox)

{
  "name": "example_boolean",
  "type": "boolean",
  "displayName": "Example boolean",
  "defaultValue": true,
  "multiple": false
}

Radio Choice

Used to create radio button groups.

{
  "type": "string",
  "multiple": false,
  "name": "radio_choice_example",
  "widgetHint": "radio-choice",
  "displayName": "Example radio choice",
  "enumValues": [
    {
      "enumValue": "Binary",
      "realValue": "1"
    },
    {
      "enumValue": "ASCII",
      "realValue": "2"
    },
    {
      "enumValue": "Other",
      "realValue": "3"
    }
  ],
  "defaultValue": "2"
}

Used to create dropdown menu groups.

{
  "name": "enum_example",
  "displayName": "Example menu using enum of values",
  "enumValues": [
    {
      "enumValue": "First Item",
      "realValue": "0"
    },
    {
      "enumValue": "Second Item (default)",
      "realValue": "1"
    },
    {
      "enumValue": "Third Item",
      "realValue": "2"
    }
  ],
  "defaultValue": "1"
}

AWS Region Dropdown Menu Example

This is a longer example showing how properties that a user will need to provide when configuring a connection or activity are not hard-coded. In this sample, the AWS Region for connecting to the Amazon S3 endpoint is something that the user will need to provide. To specify it in the adapter.json and in the UI you could provide a dropdown selection for specifying this:

This code fragment defines the dropdown menu:

{
    "name": "region",
    "displayName": "AWS Region",
    "type": "string",
    "defaultValue": "us-east-1",
    "enumValues": [
        {"realValue": "us-gov-west-1",  "enumValue": "AWS GovCloud (US)"},
        {"realValue": "us-east-1",      "enumValue": "US East (N. Virginia)"},
        {"realValue": "us-east-2",      "enumValue": "US East (Ohio)"},
        {"realValue": "us-west-1",      "enumValue": "US West (N. California)"},
        {"realValue": "us-west-2",      "enumValue": "US West (Oregon)"},
        {"realValue": "eu-west-1",      "enumValue": "EU (Ireland)"},
        {"realValue": "eu-west-2",      "enumValue": "EU (London)"},
        {"realValue": "eu-west-3",      "enumValue": "EU (Paris)"},
        {"realValue": "eu-central-1",   "enumValue": "EU (Frankfurt)"},
        {"realValue": "eu-north-1",     "enumValue": "EU (Stockholm)"},
        {"realValue": "ap-south-1",     "enumValue": "Asia Pacific (Mumbai)"},
        {"realValue": "ap-southeast-1", "enumValue": "Asia Pacific (Singapore)"},
        {"realValue": "ap-southeast-2", "enumValue": "Asia Pacific (Sydney)"},
        {"realValue": "ap-northeast-1", "enumValue": "Asia Pacific (Tokyo)"},
        {"realValue": "ap-northeast-2", "enumValue": "Asia Pacific (Seoul)"},
        {"realValue": "sa-east-1",      "enumValue": "South America (Sao Paulo)"},
        {"realValue": "cn-north-1",     "enumValue": "China (Beijing)"},
        {"realValue": "cn-northwest-1", "enumValue": "China (Ningxia)"},
        {"realValue": "ca-central-1",   "enumValue": "Canada (Central)"}
    ],
    "validators": [{
        "name": "required"
    }]
}

List Object

The List Object is a more sophisticated UI component. At configuration time, it can dynamically generate a list and use a user selection to determine the behavior of the connector:

{
  "name": "list-object",
  "displayName": "Select an XML Schema structure for validating the file",
  "type": "list-object",
  "use": {
    "ui": {
      "pageDescription": "Select an XML Schema structure for validating the file",
      "selectObjectLabel": "Selected Dropbox Object: ",
      "tableHeaders": ["Name", "Description", "Type"],
      "tableItems": ["name", "description", "type"]
     },
     "discoveryType": "provided",
     "orientation": "output",
     "documentIdPath": "this"
   },
   "validators": [{"name": "required"}]
 }

In this code sample, from ProcessFileActivity of the Dropbox connector, the discoverable objects are mapped to data for display. In this example, the values are hard-coded strings. However, they could be dynamically generated and pulled from a database or other data source. The results of the selection at configuration time are used to determine which XSD schema file is used to process the data the connector receives.

  @Override
  public List<DiscoverableObject> getObjectList(DiscoverContextRequest<DiscoverableObjectRequest> objectListRequest)
      throws DiscoveryException {
    List result = new ArrayList<DiscoverableObject>();
    DiscoverableObject obj = new DiscoverableObject();
    obj.setObjectName("account")
      .setObjectType("xsd")
      .setObjectDesc("XML schema associated with Account objects");
    result.add(obj);
    obj = new DiscoverableObject();
    obj.setObjectName("contacts")
      .setObjectType("xml")
      .setObjectDesc("XML schema associated with Contact objects");
    result.add(obj);
    obj = new DiscoverableObject();
    obj.setObjectName("customer")
      .setObjectType("json")
      .setObjectDesc("JSON schema associated with Customer objects");
    result.add(obj);
    return result;
  }

Key-Value Pairs

Used to define a set of properties that a user configuring a connector can enter as a set of key and value pairs.

In this example, from the Jitterbit Harmony Cloud Studio Zendesk Connector, a key (jobStatusMaxRetries) is associated with a display value (enumValue). The Add and Remove buttons are automatically added by the Cloud Studio UI. Note that when first displayed, there will be no values until a user adds them using the Add button.

{
  "name": "key_value_pairs_example",
  "displayName": "Example Key Value Pairs",
  "type": "object",
  "default": "",
  "multiple": true,
  "properties": [
    {
      "name": "name",
      "displayName": "Name",
      "enumValues": [
        {
          "enumValue": "Job Status Maximum Retries",
          "realValue": "jobStatusMaxRetries"
        }
      ],
      "validators": [
        {
          "name": "required"
        }
      ]
    },
    {
      "name": "value",
      "displayName": "Value",
      "type": "number",
      "defaultValue": "300",
      "validators": [
        {
          "name": "required"
        }
      ]
    }
  ]
}

To use this value in a connector, you obtain the value by passing in the connector properties to code such as this:

private String getJobStatusMaxRetries(Map<String, String> props) {
  if (!Objects.isNull(props.get(OPTIONAL_SETTINGS_CONFIGURATION + "." + JOB_STATUS_MAX_RETRIES))) {
    return props.get(OPTIONAL_SETTINGS_CONFIGURATION + "." + JOB_STATUS_MAX_RETRIES);
  } else {
    String config = props.get(OPTIONAL_SETTINGS_CONFIGURATION);
    if (StringUtils.isEmpty(config) || config.equals("[]")) {
      return null;
    }
    config = config.substring(1, config.length() - 1);
    ObjectMapper mapper = createMapper();
    try {
      NameValue nv = mapper.readValue(config, NameValue.class);
      return nv.getValue();
    } catch (Exception e) {
      e.printStackTrace();
      return null;
    }
  }
}

This code relies on the use of a JSON parser (such as Jackson), returned by the call to createMapper(), to convert JSON into a Java object. The properties are organized and called using “dot” notation, passing the name of the UI element and the name of the enum’s realvalue field. In the above example, the strings OPTIONAL_SETTINGS_CONFIGURATION and JOB_STATUS_MAX_RETRIES would be:

String OPTIONAL_SETTINGS_CONFIGURATION = "key_value_pairs_example";
String JOB_STATUS_MAX_RETRIES = "jobStatusMaxRetries";

Grouping

Items can be grouped, as shown below. When the checkbox is false, the group that follows it is inactive:

Selecting the checkbox activates the group, allowing entry in both the second and third items:

The code fragment for this:

{
  "name": "example_group",
  "type": "group",
  "displayName": "Example Group",
  "children": [
    {
      "name": "example_group_first_item",
      "type": "boolean",
      "multiple": false,
      "displayName": "Example Group First Item",
      "defaultValue": false
    },
    {
      "name": "example_group_second_item",
      "type": "string",
      "multiple": false,
      "displayName": "Example Group Second Item",
      "location": "last",
      "defaultValue": "1",
      "enumValues": [
        {
          "enumValue": "Postal Code",
          "realValue": "0"
        },
        {
          "enumValue": "ZIP",
          "realValue": "1"
        }
      ],
      "deps": {
        "disabled": {
          "op": "not",
          "args": {
            "op": "prop",
            "args": "example_group_first_item"
          }
        }
      }
    },
    {
      "name": "example_group_third_item",
      "type": "string",
      "multiple": false,
      "displayName": "Example Group Third Item",
      "widgetHint": "password",
      "defaultValue": "",
      "deps": {
        "disabled": {
          "op": "not",
          "args": {
            "op": "prop",
            "args": "example_group_first_item"
          }
        }
      }
    }
  ]
}

Adding and Creating Validation

A field can include a list of one or more validators that checks a user entry and confirms its correctness. If it fails, an error message is presented to the user. Validators are triggered when a user exits a field, typically using the tab key:

Invalid number format example

Examples are requiring an entry, using only digits, requiring an email address, or entering a ZIP code. Standard validators for common situations such as string length and numeric value are available, and pattern validators can be created as required.

The validators are based on the Angular Validators.

All validators are called following the pattern shown by this example:

{
    "displayName": "Number of columns",
    "name": "noOfColumns",
    "type": "number",
    "validators": [
        {
            "name": "required"
        },
        {
            "args": [
                "^([1-9]|[1-9]\\d|1\\d\\d|2[0-4]\\d|25[0-6])$"
            ],
            "errorMessage": "Only whole numbers 1 to 256 are allowed.",
            "name": "pattern"
        },
        {
            "errorMessage": "${displayName} must be an integer.",
            "name": "int"
        }
    ]
}

Here, two validators are shown for the noOfColumns field. The first is a required validator, which requires only that the name be specified to invoke it. This means that the field must be completed (is required).

The second is a pattern validator, which is looking for a string that matches the regular expression pattern given by ^([1-9]|[1-9]\\d|1\\d\\d|2[0-4]\\d|25[0-6])$. If this validator fails, the error message will be shown to the user. Note that the actual pattern used will be ^([1-9]|[1-9]\d|1\d\d|2[0-4]\d|25[0-6])$, as any backslashes in the pattern must be escaped.

A validator is called by name, and any arguments are passed as a list, as shown in the above examples. An error message that is to be returned can optionally be specified. If not specified, a default error message is used (as covered for each validator below). Note how the error message can access the attributes of the field such as displayName and args to create validators that are extensible and not fragile to code changes.

These validators are available by name:

  • required

    • Takes no arguments
    • Default message: ${displayName} is required
  • requiredTrue

    • Takes no arguments
    • Default message: ${displayName} is required
    • This validator is commonly used for required checkboxes
  • email

    • Takes no arguments
    • Default message: ${displayName} must be a valid email address
  • hasValue

    • Takes no arguments
    • Default message: ${displayName} is required
  • int

    • Takes no arguments
    • Default message: ${displayName} must be an integer
  • min

    • Takes one argument: the minimum numeric value
    • Default message: ${displayName} must have a value of at least ${args[0]}
  • minlength

    • Takes one argument: the minimum number of characters
    • Default message: ${displayName} must be at least ${args[0]} characters
  • max

    • Takes one argument: the maximum numeric value
    • Default message: ${displayName} has a value greater than ${args[0]}
  • maxlength

    • Takes one argument: the maximum number of characters
    • Default message: ${displayName} exceeds ${args[0]} characters
  • pattern

    • Takes one argument: a string JavaScript regular expression pattern that the entry must match
    • Any backslashes in the pattern must be escaped: the pattern [^\s]+ must be entered as [^\\s]+
    • Default message: ${displayName} is invalid
  • requiredExpr

    • Takes one argument: an expression that must be satisfied
    • Default message: ${displayName} is required

Example Patterns

Pattern Description
^[0-9]+$ Only whole numbers 1 or greater are allowed.
^[1-9][0-9]{0,6}$ Invalid number format or maximum length exceeded.
^([1-9]|[1-9]\\d|1\\d\\d|2[0-4]\\d|25[0-6])$ Only whole numbers 1 through 256 are allowed.
[^\\s]+ A valid string is required.

Any backslashes in the pattern must be escaped: the pattern [^\s]+ must be entered as [^\\s]+

Working Example

See the Dropbox connector’s adapter.json file for a working example using many of these UI components.