App Actions For App Platform

What are Actions?

Actions typically act upon something an external system exposes, like taking actions on an order in an OMS to update or cancel an order.

Important

An Action should encapsulate a complete operation, For example, a cancel_order Action should perform all business logic necessary to cancel the order. Put another way, there should be only one cancel_order Action exposed in your App, not one for each of your upstream systems. This ensures Gladly can expose consistent behavior regardless of where the Action is used in the user interface. I.e., whether a Customer is canceling an order via Sidekick or an Agent via Gladly Hero, the result is the same.

Retrieving data from external systems should generally be done via data pull

Most external systems have complex data models that are comprised of multiple related data types. An order, for example, may reference products and shipments, which are all stored as separate objects in an external system. For this data to be useful in Gladly, all related order data must be retrieved and made available (consistently) within Gladly. Data pull retrieves all related data in one operation and guarantees that the data is stored consistently and can be accessed consistently within Gladly.

Configuring Actions

High-Level Approach

  • Identify the inputs and outputs of the REST API for the Action that you would like to make available to the Gladly platform.

  • Create the request URL, header, and/or body go templates that reference the identified required inputs.

  • Determine whether or not the response data is in an acceptable format for use in Gladly. If not, create a template to transform the data into the desired format. The format of the transformed data should consider what parts of the output data will be helpful to the caller of the Action and how that data will be used.

  • Add the definition of the Action to the actions_schema.graphql file. This definition describes the inputs and outputs for this Action.

Action folder structure

Action configuration is specified in the app’s actions folder.

+ actions
    - actions_schema.graphql
    + [action_name]
        - config.json
        + headers
            - [header_name_1].gtpl
            - ...
            - [header_name_n].gtpl
        - request_url.gtpl
        - request_body.gtpl
        - response_transformation.gtpl

The documentation below will touch on the files in the folder structure above.

Template available to URL, header, and body Templates

The following data is available to the request URL, header, and body go templates.

Integration data

This is integration-specific data entered in the Gladly admin UI when adding the application to Gladly. This usually consists of your account and credential information necessary to execute REST calls against the external system.

{
    "integration": {
        "configuration": {
        },
        "secrets": {
        }
    }
}

For example, authentication based on shared secrets usually expects these secrets to be provided via an HTTP header. This implies that your Action configuration will have a header template that references the attribute that stores the secret in the secrets object.

For example, an API that expects the shared secret to be specified in a X-API-Key header would have a corresponding X-API-Key.gtpl template file where the template would be something like the following:

{{.integration.secrets.apiKey}}

Input parameters

These are the dynamic inputs that are needed by the REST API. The names and types that are expected by the REST API are specified as part of the input parameters of the GraphQL definition for this Action. Input parameter types can be GraphQL scalars or user-defined object types. The general best practice is to use scalar types to keep the configuration simple.

The input parameters are exposed to templates via the inputs attribute, where each input is a corresponding attribute of the inputs object made available to templates. The source of the values for these inputs will be configured via the Sidekick thread UI.

For example, GraphQL Action definition for canceling an order:

type Mutation {
    cancelOrder(orderId: String!): CancelOrderResult! @action(name: "cancel_order")
}

The corresponding input template data for this action would be the following:

{
    "inputs": {
        "orderId": ""
    }
}

And a request URL template that uses the parameter would look like:

https://hostname.com/cancel_order?customer_order_id={{.inputs.orderId}}


The complete data for action templates is as follows

{
    "integration": {
        "configuration": {
        },
        "secrets": {
        }
    },
    "inputs": {
    }
}

Create a folder for the Action configuration

All configurations associated with a given Action will reside in the same sub-folder of the actions folder within the app configuration. The name of the folder is also the name of the Action.

[app folder]/actions/[action_name]

Create the JSON configuration file for the action in the Action folder

[app folder]/actions/[action_name]/config.json

The config.json file contains all of the configuration information that is not template-related. Each template is instead stored in its own file for readability and testing:

{
 "httpMethod": "POST",
 "contentType": "some content type",
}

Attribute

Type

Description

httpMethod

String

The HTTP method used to submit the request. E.g. GET, POST, PUT.

contentType

String

The Content-Type if the request has a body.

Create the request URL template in the Action folder

[app folder]/actions/[action_name]/request_url.gtpl

This template specifies the fully qualified request URL for the REST call. The HTTP method used to submit the request is specified in the config.json file. The template can access the data outlined in the Template Data available to URL, Header and Body templates section above.

For example, request URL template:

https://hostname.com/cancel_order?customer_order_id={{.inputs.orderId}}

Depending on the HTTP method, create the request body template in the Action folder

[app folder]/actions/[action_name]/request_body.gtpl

For HTTP methods that require a body, this template specifies the content of the body. The content type associated with the body is specified in the config.json file.

For example, request body template (”contentType”: “application/json”):

{    "customer_order_id": "{{.inputs.orderId}}"}

Request Headers

Configuration for any required request headers specific to the Action is located in the Actions’ headers folder.

[app folder]/actions/[action_name]/headers

Each header is configured via a template that provides the template value. The template file for each header is named after the header.

[app folder]/actions/[action_name]/headers/[header name].gtpl

Create the response data transformation template (optional)

In many cases, the data returned as part of the response to an Action can contain some information irrelevant to Sidekick and/or not in an optimal format. The response transformation template is used to extract relevant data from the response and convert it into a more user-friendly format. The output of the template MUST be in JSON or XML format.

 [app folder]/actions/[action_name]/response_transformation.gtpl

The response data transformation template has access to all of the data provided to the URL, body, and header templates and, in addition, also has access to the associated HTTP request and response data. Currently, response data formatted as JSON (application/json) or XML is supported. The response data is accessed using the rawData attribute of the template data. In most cases, the response template will just be using the data associated with the rawData attribute.

{
    "request": {
        "url": "",
        "method": "",
        "headers": {
            "request_header_name": []
        },
        "body": ""
    },
    "response": {
        "status": "",
        "statusCode": 200,
        "headers": {
            "response_header_name": []
        },
        "body": ""
    },
    "rawData": null
}

Attribute

Type

Description

request.url

String

The fully qualified URL of the request.

request.method

String

HTTP method for the request.

request.headers

Object

Each attribute corresponds to a request header. The value associated with each header is an array of String values representing one or more header values.

request.body

String

The raw request body text.

response.status

String

The HTTP status of the response.

response.statusCode

Int

The HTTP status code of the response.

response.headers

Object

Each attribute corresponds to a response header. The value associated with each header is an array of String values representing one or more header values.

response.body

String

The raw response text.

rawData

Object

The object representation of the JSON or XML response data.

See the accompanying golang template documentation for the syntax of accessing template data.

The entire response template data looks as follows:

{
    "integration": {
        "configuration": {
        },
        "secrets": {
        }
    },
    "inputs": {
    },
    "request": {
        "url": "",
        "method": "",
        "headers": {
            "request_header_name": []
        },
        "body": ""
    },
    "response": {
        "status": "",
        "statusCode": 200,
        "headers": {
            "response_header_name": []
        },
        "body": ""
    },
    "rawData": null
}

For example, hypothetical response data:

{
    "orderId": "12345",
    "status": "cancelled",
    "refundIssued": true,
    "refundAmount": "3.99",
    "refundCurrency": "USD"
}

Response transformation template (output MUST be in JSON or XML format):

{{- if .rawData.refundIssued }}
{
    "amount": "{{.rawData.refundAmount}}",
    "currency": "{{.rawData.refundCurrency}}"
}
{{- else -}}
{{- /* return null when a refund has not been issued */ -}}
null
{{- end -}}

The resulting data format will be defined in the GraphQL schema and associated with the corresponding Action:

type Refund {
    amount: Currency
    currency: String
}

The field names and types defined in the GraphQL schema MUST match those in the JSON generated by the template.

Create the GraphQL schema for the Action configurations

The inputs and outputs associated with each configured Action are defined using a single GraphQL schema.

[app folder]/actions/actions_schema.graphql

Actions that modify (add, update, delete) data in the external system should be defined under the GraphQL Mutation type and Actions that retrieve data under the Query type. Each Action defined in the GraphQL schema is associated with its corresponding Action configuration via the @action directive. The name attribute specified in the @action directive corresponds to the folder name (Action name) in which the configuration for the corresponding Action resides.

@action directive definition

# The name argument is the action name.
directive @action(name: String!) on FIELD_DEFINITION

For example, using the above sample cancel_order Action:

# The format of the Refund return type corresponds to the output
# generated by the response_transformation.gtpl template.
type Refund {
    amount: Currency
    currency: String
}

type Mutation {
    # Input parameters defined here are exposed via the
    # "inputs" attribute of the data passed to the template.
    # Each "inputs" child attribute is named after the
    # corresponding GraphQL input parameter. I.e. a "inputs"
    # attribute referenced in any of the cancel_order templates
    # will match one of the input parameters defined here.
    #
    # cancelOrder is associated with the cancel_order action
    # configuration via an @action directive. The action
    # configuration is located in a subfolder named after the
    # cancel_order @action directive "name" value.
        """
        Cancel an order using the order ID
        """
    cancelOrder(orderId: String!): Refund @action(name: "cancel_order")
}

Gladly supports the standard GraphQL data types (Int, Float, String, Boolean, ID) and the following additional types.

Data Type

Format

Description

DateTime

String value in ISO8601 date format

DateTime fields can be used in conditionals and can be manipulated.

Currency

String representation of currency value (real number)

Representing currency values as a string ensures no loss of precision. Gladly ensures that no precision is lost when manipulating Currency values.

Action definitions and Sidekick Threads builder

Actions defined here will be in Custom Actions in the Sidekick Threads builder. Action names will be displayed as they are named in the GraphQL file. Action descriptions displayed in the UI will be taken from the descriptions associated with each field in the GraphQL schema. Choose action and input parameter names that are as self-explanatory and as descriptive as possible.

Values returned by Queries and Mutations (e.g., the Refund from cancelOrder above) become available memories in Sidekick Thread Builder. Consider this when deciding what response data to expose via the response_transformation.gtpl template.