Workflow concepts & best practices

Workflow concepts #

A workflow is defined by one Transposit Dev Platform application and implements a functionality end-to-end (E.g. merge a change on Jenkins). A workflow application is broken up into two parts: Input and execute. Let's take a look at these operations.

Input operations #

Input operations (recommended operation name: input_prompt) return the Slack Block Kit UI you will want to display when a user clicks on a workflow, like in the example below. Make sure to add a parameter (located on the right side) named context in this operation.

Input prompt example

Right now, if the Block Kit JSON is not valid, it will fail silently.

Important: Use the block_kit_lib connector to make it easier to form Slack Block Kit UI. You will need to add it as a connector in your workflow application. Under Code > Data connectors, search for Slack Block Kit. Select the "-- None --" operation and save.

Execute operations #

Execute operations (recommended operation name: execute) run when a user clicks the Run button in a workflow modal in Slack. This is where most of the workflow logic is written and most external API calls are made.

An execute operations generally contains code that:

  • Retrieves environmental variables and input prompt parameters
  • Runs any necessary logic
  • Makes API calls
  • Formats API response so that it is useful for the user
  • Posts to Slack and the timeline

Running execute operation #

We recommend using a wrapper operation to call your execute operation (recommended operation name: execute_wrapper). The wrapper operation is used to retrieve user inputted values from context and pass them to execute as parameters.

Example:

    api.run("this.execute", {
"context": context,
"param1": my_param_value
})

Retrieving results from input prompt #

Make sure to add a parameter (located on the right side) of type object named context in this operation.

The context parameter contains the input_prompt user input and other important information. In the execute operation, you will find it as params[“context”][“parameters”].

The block_kit_lib also contains a retrieve_text_input operation to help you retrieve user input. Here's an example that you might have in your execute_wrapper operation:

    context = params["context"]

# retrieve value from input_prompt
# Note: if retrieving multiple values (i.e. multi_static_select, checkboxes)
# be sure to remove '[0]' from the end of the api.run command.
my_param_value = api.run(
"block_kit_lib.retrieve_input", {
"field_name": "my_text_field",
"context": context
})[0]

Posting messages #

Use workflow.log.done(slack=output_blocks, metadata={}) to fog results and mark the successful completion of an entire workflow run. For example, in your execute operation:

    output_blocks = api.run("block_kit_lib.markdown_text_section", {
"text": f"Results are: {results}"
})

workflow.log.done(
slack = output_blocks,
metadata = {}
)

There are five types of logs (seen under the Activities in main Transposit site):

  • Log
  • Status
  • Done
  • Warn
  • Fail

See more info about them in the Python operations doc.

Try to at least include a done type. Some workflows don’t make as much sense to include the full results, like large log responses. Currently, it is only text based, but images are coming soon.

Integrations #

Integrations are how Transposit is able to connect your existing services and APIs together to interactive runbooks.

Authentication #

Authentication for an integration is done in Transposit in the Integrations section in the left sidebar.

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

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

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

For the second, where workflows are performed by a specific user:

If you want a user to connect to other services as themselves (E.g. so they can post to GitHub as themselves or create a Jira ticket with their credentials), you 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 workflows 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 Dev 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 Dev Platform workflow 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.

Configuration #

You can see the existing Transposit Dev Platform environment variables documentation here.

You will create your environment variables first in the Transposit Dev Platform, then when creating a workflow in Mission Control you can specify their values.

Think of environment variables as configuration of a workflow application for a specific workflow. You can have multiple workflows for a single application, but you might want them configured differently. This might be for different services, projects, regions, APIs, clusters, etc.

The code to get an env variable in the Transposit Dev Platform:

env.get("variable_name_here")

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 workflow 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 workflow itself.

Debugging errors while creating workflow 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 Dev 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 #

Currently, there are two ways to return errors to a user:

1. When input_prompt runs and show the error in the input modal

This case is good for validation on environment variables that does not require an API call.

Example: There is missing environment variable, and you want tell user what is missing and to go update it.

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

    blocks = []
repo_name = env.get("gh_repo_name")
if repo_name == None:
message = "Invalid settings, please verify that 'gh_repo_name' is correct."
message += " Values seen for gh_repo_name: " + str(repo_name)
blocks.append(api.run("block_kit_lib.text_section", {"text": message})[0])
return blocks

2. When execute runs and shows in the channel

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(message="Restart VM workflow failed", metadata={})

See the catching API errors section below for an example.

Catching API errors #

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

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