@ubio/sdk

ubio sdk application bundle (prototype)

Usage no npm install needed!

<script type="module">
  import ubioSdk from 'https://cdn.skypack.dev/@ubio/sdk';
</script>

README

SDK application bundle

Application bundle using @ubio/client-library.

This repository contains an example application which uses the ubio API with the JavaScript client-library. It is split into two broad parts:

  • The code in the /src directory consumes configuration and functions which produce DOM Nodes to render a sequence of sections containing forms.
  • The /templates directory contains functions which render sections from templates, which are sometimes nested. These templates use lit-html as an example rendering engine.

Creating a new project

This project also contains a CLI which can be used to generate a fresh project. With Node.js installed, you can run the following in a terminal:

npx @ubio/sdk --domain hotel-booking --name my-integration

where hotel-booking should be replaced with the domain of your choice, and the name is up to you and a fresh directory will be created and named using it. The templates of the appropriate domain will be copied into your new project. This means that they will use lit-html by default, but you may use whichever technology to create Nodes that you like. More explicity, your section templates should return a Node, which may be such things as HTMLElement, Text, and DocumentFragment instances.

The templates directory

The templates directory contains sets of templates for a variety of domains. The generator CLI uses these to create new projects, but they also serve as examples on which to base your own templates.

There are also *.config.js modules, which are configuration of layouts, pages, and caches. The fields of these configuration objects are a subset of the fields required by the createApp function.

The bundle API

createApp({ mountPoint, sdk, layout, pages, input, error, cache, local })

Where:

fieldname description
mountpoint A reference to a DOM element to append the app layout to.
sdk An end-user sdk, initialised with the job ID, service ID, and end-user token.
input An object of key value pairs representing input that the job was created with.
layout An object with values as layout templates for the header, the footer, and the summary.
error A template to render errors.
notFound A template to render whenever a page does not exist.
pages A list of configurations for pages to be rendered.
cache A list of specifications for outputs of previous jobs to cache.
local Supplimentary data.

The createApp method is the principal interface of the bundle. By passing it various templates, an SDK object, and a flow, this function appends elements to the DOM as a child of mountpoint. As data becomes available, pages are rendered in sequence to acquire more input to progress the job onward to completion.

This function requires both an sdk object and an inputs object, which means that the job must already exist before this function is called. It is up to your server to perform job creation with initial inputs, and send data to the user browser to create the sdk object, and subsequently call createApp.

createApp returns a promise which resolves when the final page in the sequence has finished.

sdk

For documentation regarding the sdk object, see the documentation. This library requires the end-user form of the sdk.

input

This is exactly the same inputs object which the job is created with.

layout

Layout consists of a headers template, a footers template, and a summary template. The summary template is likely to be the most complex. Each set of domain templates included with this module has a layout templates, and it is recommended to base your own off of these.

pages

Pages represent the steps of your automation. Each is composed of the fields:

  • name: Then name of the page.
  • route: The route of the page (appears after a # in the URL).
  • title: The title of the page, rendered in the layout.
  • sections: An array of section specfications.

Sections are composed of the fields:

  • name: The name of the section.
  • template: A function which returns a DOM element or a DocumentFragment instance.
  • waitFor: List of data (inputs, outputs, etc.) required in order to render the section.

A template here is a function with an options parameter with the following fields:

  • name: The name of the template.
  • storage: An object with a get(type, key) method. Use this to get input and output data.
    • type: The storage type, for example "input" or "output". An array may be used, in which case each type is checked until a result (if any) for the given key is found. A common use case for this is ['output', 'cache'].
    • key: The key of the datum to retrieve.
  • skip: A skip function. renderer will render next section without waiting for end-user's input submission.
  • sdk: The sdk instance.
  • inputFields: A list of fields from input that this job will use. Each "field" is a path in the same format as form input name (see below).

As mentioned, the template must return an element or a document fragment. This means that if async work needs to happen, then a placeholder element or fragment must be returned, and parts of it replaced upon resolution of the async work. This is uncommon, but happens when the sdk must be used.

Existing templates in this repository serve as examples. Since the returned value is always the reference to a DOM element or a document fragment, you may use any frontend framework you wish so long as the result is rendered. We've used lit-html to demonstrate this principle.

cache

This is an array of objects containing key, sourceInputKeys, and variabilityThreshold fields.

  • key: the equivalent output key.
  • sourceInputKeys: An array of input keys. When data for every listed input is known a request is made for cached data for key.
  • variabilityThreshold: A value between 0 and 1, defaulting to 1, which limits how variable a cached output can be. For example, 0 corresponds to a high confidence that the cached output is the same as the equvalent output, and 1 corresponds to a low confidence. When this is set lower than a cached value which comes back, the cached value will not be used when waiting for outputs before rendering a section, but its data will still be available via storage from templates.

Using the cache can enable you optimise your SDK use to provide a better user experience.

local

Local is a set of optional fallback data. When nothing is found in active data or the cache, local is checked last.

error

:warning: This will be changed to bring it in line with other templates. :warning:

This is a special template for rendering flash errors. It must be a function which takes a single selector parameter for it to target an element to append elements to, and an optional sdk parameter.

It should return an object with an init method. This method should build and append DOM to the element targeted by the selector.

notFound

:warning: This will be changed to bring it in line with other templates. :warning:

This is a special templates for rendering pages which aren't registered. It must be a function which takes a single selector parameter for it to target an element to append elements to.

It should return an object with a renderer field. This field should be an object with an init method. This method should build and append DOM to the element targeted by the selector.

Building forms

Form name conventions for building input data

To create inputs, you need to structure the form in a way that mirrors our protocol. For example, if you want to create a PetInsurance.policyOptions input, you will need to send JSON data like:

{
    "policyOptions": {
        "coverStartDate": "2019-06-01",
        "numberOfPetsOwned": 1,
        "jointPolicyHolder": false
    }
}

To build that structure, you need to specify the correct name for the input field in your form, addressing objects with brackets([]), if you need to build an array, use numbers in the brackets such as pets[0][name].

<div name="policy-options">
    <input
        type="date"
        name="policy-options[cover-start-date]" value="2019-06-01">

    <input
        type="number"
        name="policy-options[number-of-pets-owned-$number]"
        value="1">

    <input
        type="radio"
        name="policy-options[joint-policy-holder-$boolean]"
        value="true">

    <input
        type="radio"
        name="policy-options[joint-policy-holder-$boolean]"
        value="false"
        checked>
</div>

If you need to convert the value to integer or boolean, suffix the name with -$number, -$boolean or -$object to let the form serializer know that it needs to parse the value. If parsing to a type fails, the string value will be used by default.

When the form is submitted, input names will be camel-cased to produce the expected object fields in the JSON which is posted to our API.

Using the hosted payment card form

All payment cards must be vaulted and tokenised before you can use them with the Automation Cloud services. For more information, see the payment card documentation.

We've integrated our hosted payment form as a template for your convenience. This template provides a nicer API than indicated in the documentation.

import templates from '../helpers/index.js';

// See: https://docs.automationcloud.net/docs/vaulting-payment-card
function hostedForm(otp) {
    // Styling and configuring the form
    const x = {
        // css (required) An absolute URL
        css: 'https://ubio-application-bundle-test-server.glitch.me/style.css',

        // fields (optional, defaults shown)
        // https://docs.automationcloud.net/docs/vaulting-payment-card#section-fields-optional-
        // https://docs.automationcloud.net/docs/vaulting-payment-card#section-interacting-with-the-form
        //
        // The form has 4 fields: name, pan (card number), expiry date, cvv.
        // - change the order of fields
        // - change the labels for fields
        // - specify expiry date fields to be input (expiry) or select
        //   (expiry-select)
        fields: ['pan', 'name', 'expiry-select' , 'cvv'],
        // Use objects to rename fields, for example:
        // fields: [{ field: 'pan', label: 'the pan' }, 'name', 'expiry-select', 'cvv']

        // brands (optional, defaults shown).
        //
        // restricts aaceptable card brands list
        brands: ['visa', 'mastercard', 'amex'],

        // validateOnInput (default: true).
        //
        // Whenever each field is updated, you receive the validation result
        // message and an "invalid" class gets added or removed accordingly. Set
        // to false to disable this behaviour. The form will be validated on
        // submission regardless of this setting.
        validateOnInput: true
    };

    const iframeStyles = {
        width: '100%',
        height: '380',
        scrolling: 'no'
    };

    return templates.hostedPaymentCardForm(otp, formConfiguration, iframeStyles);
}

You must provide valid OTP (one-time password) as a parameter when you embed the form. In the following example, waitFor: ['_.otp'] insures that the checkout template receives the otp.

    pages: [
        ...,
        {
            name: 'payment',
            route: '/payment',
            title: 'Payment Details',
            sections: [
                {
                    name: 'checkout',
                    template: checkout,
                    waitFor: ['_.otp']
                }
            ]
        }
        ...
    ]

You must style and configure your payment form separately by providing the options via query parameters. The css parameter takes the absolute URL to a CSS file which you must host. See the styling and configuration documentation for more information.

Troubleshooting tests

In case if tests are stuck run npx testem, follow the link it suggests and check network tab of developer tools.