Action Concepts & Best Practices

An action is defined by a Developer Platform application and implements functionality end-to-end, such as functionality for merging a change on Jenkins, getting the last 10 git commits, and updating the StatusPage incident page.

Concepts#

An action application always consists of an execute operation and an optional input operation.

Let's take a look at these operations.

Our action template application follows this document and includes many of the best practices we reference here. We recommend forking it to start building with.

Execute Operations#

Execute operations run when an action is run, manually or automatically. This is where most of the action logic is written and most external API calls are made.

An execute operation generally contains code that:

  • Runs any necessary logic, passing through parameters it has defined
  • Retrieves data from operations that call external integrations
  • Formats our response message so that it is useful for the user
  • Posts that response message to the activity time and in Slack

An operation calling external integrations generally contains code that:

  • Makes calls to external APIs, sometimes based on user input

Typically, these are named based on their operations they are taking with an integration.

Specifying and retrieving inputs#

The parameters sidebar (located on the right side) is where you will specify any inputs your operations require. For the specifically deployed execute operation, these parameters are special in that they will appear in the runbook edit page for the runbook author to pre-configure. They may also appear at runbook runtime for the user to fill in.

Make sure you also have a parameter of type object named context in your execute operation, as well as in your input prompt operation if you choose to use one. This parameter is automatically passed through and contains information like the activity id and team name.

    context = params.get("context")
input_param_1 = params.get("input_param_1")

Posting messages and specifying output parameters#

Use workflow.log.done({display}, standardOutputParams={}, customOutputParams={}) to log results and mark the successful completion of an entire action run. These logs will show up automatically in the runbook run and in any connected activity and Slack channels.

The customOutputParams object in the done or fail logs, along with the developer-defined schema, allow your action output to be passed through to subsequent actions in the chain.

    custom_output_params = {"output_1": "this can be passed to subsequent actions"}
workflow.log.done("Restarted ECS instance", {}, custom_output_params)

Make sure to always include a done in your action, and a fail if appropriate.

See more about logging in the Python operations doc.

Input Prompt Operations#

An input prompt operation is used when you would like to explicitly provide
a block kit input to the user instead of a "plain" text box. For example, if you'd like the user to provide input by selecting an option from a dropdown, you would utilize the input prompt operation to build and return that dropdown.

The schema for the return value of the input prompt operation is as follows:

    return {
"inputs": {
"input_param_1": {"blockKitOverride": <a single block kit block>}
}
}

where "input_param_1" is the name of a parameter defined in this action's execute operation.

You can also add parameters to this operation, if they are needed to construct your input.
For example: adding a parameter for a Jira project key if you're planning on showing a dropdown of Jiras

If you do not need to explicitly provide block kit, you can simply add parameters to the action's execute operation as defined above, and an input field will be auto-generated for the end user.

Error handling in input prompt operations#

If you've detected an invalid input or otherwise need to prevent the user from running the action's execute operation, you can return an error block like so:

    return {"error": {"blockKitOverride": <a single block kit block>}}

Here's a helper function you can copy-and-paste into the bottom of your input prompt operation to return errors:

def make_error(error_message):    
error_block = api.run("block_kit_lib.markdown_text_section",
{"text": error_message})[0]
return {
"error": {"blockKitOverride": error_block}
}

Important: Use the Block Kit library to make it easier to form Slack Block Kit UI. You will need to add it as a connector in your action application. Under Code > Data connectors, search for Slack Block Kit. Select the "-- None --" operation and save. The Block Kit library docs also have examples you can use. Make sure to only copy single block objects to use in your return object.

Note: If the Block Kit JSON is not valid, it will fail silently.

Integrations#

Integrations (also known as data connectors in the Transposit Developer Platform) are how Transposit is able to connect your existing services and APIs together to interactive runbooks.

Authentication#

Authentication for an integration is located in the top right gear icon in Transposit, not in the Transposit Developer Platform.

You will need to check the box to enable the specific integration and either connect:

  • Authorization for actions that don’t require a specific user. Only one user on the team needs to provide this connection.
  • Authorization for actions that are performed as you

For authorizations for action applications that don’t require a specific user, you are done here after you connect.

If you are building "actions that are performed as you" and want a user to connect to other services as themselves, so that they can, for example, post to GitHub as themselves or create a Jira ticket with their credentials, you also need to do the following:

  1. Make sure the user creates a Transposit account with Slack login
  2. Make sure this account is added to the team
  3. If the team integration auth was set up before (I.e. “Authorization for actions that don’t require a specific user. Only one user on the team needs to provide this connection.”), make sure the application on the Transposit Developer Platform is set up to use the user based authentication under Users > User Configuration, as opposed to team based authentication. This box needs to be checked as seen below:
    Require users to authenticate with these data connections box

Lastly, no matter what authorization type you use, you can go to your Transposit Developer Platform action application and add corresponding data connector for the integration. You will be prompted to use an existing auth for the integration, which you will need to confirm.

Overriding Data Connector Config#

This is not commonly needed, but only BaseUrl or Region can be overridden/managed at this time. In order to make use of this new feature, the action application needs to have declared that the Region (E.g. AWS Region) or Base URL "can be managed" or "can be overridden." Under Code > your specific data connector > Configuration > Edit:

Check box for Allow consumers to override this value

Note: After you have checked this box, the value for this configuration is set in the Integration section of the main Transposit site, not in the action itself.

Debugging errors while creating action applications#

Most of your debugging with happen by adding print() statements in Python and viewing them in the Monitor section on the left sidebar in the Transposit Developer Platform.

Note: Some server side errors may be only viewable by the Transposit team at this time, please feel free to reach out to support if you are experiencing an error and don't see anything in the Monitor section.

Best Practices#

Sharing errors with users#

Execute operation errors

This case is good for errors that involve API calls. It can also be used for validation.

Example: There is a bad API response and you want to let the user know something is wrong. Since the modal is already closed, you will need to share it in the channel and logs:

    workflow.log.fail("Failed to restart VM")

See the catching API errors section below for an example.

Catching API errors#

Here’s an example from the GitHub commit log action:

    try:
commits = api.run("this.list_commits", {
"repo": repo_name,
"owner": owner_name
})
except ValueError as ex:
message = "Error from GitHub API: " + str(ex)
return workflow.log.fail("Error performing an action: {message}")

Input operation errors

A way to provide a good error message to the user in the input prompt step (for example, if you have no options in your dropdown for the. user to choose from) is coming soon.