Choosing Where Your Requests Go

Sym makes it easy to route different flows to different channels.

πŸ“˜

This is a mini-guide

This page is intended to practically introduce a handful basic Sym concepts, all of which are covered elsewhere in greater detail.

This page includes references to:

Overview

When you make a Sym request in Slack by running /sym, three things will happen before the request shows up for approval:

  1. You will see a prompt asking you to select a Flow
  2. Depending on which Flow you choose, you will be prompted for input
  3. Depending on a combination of your chosen Flow + input, your request will be routed to a specific Slack channel

For the purposes of this guide, all Python implementation files will be referred to as impl.py. You can (of course) name your files anything you'd like, but impl.py is Sym's standard example filename.

Concepts

Every Flow must reference an impl.py

Every sym_flow resource must reference an impl.py file that contains your custom logic for the Flow. The same impl.py may be referenced by multiple Flows.

resource "sym_flow" "this" {
  name  = "approval"
  label = "Approval"

  # This is the contents of this Flow's Python Implementation file
  implementation = file("${path.module}/impl.py")

  ...
}

Workflow Handlers contain your Flow logic

Workflow Handlers are specially-decorated and -named functions that execute at specific times during a Flow's lifecycle. While your impl.py files may contain various helper functions, all logic that directly impacts a Flow will be contained in a Handler.

The only required Handler is a Reducer called get_approvers(event), which takes an event representing the Flow being executed, and returns information about the Flow's approval path.

The simplest implementation of get_approvers(event) is to explicitly declare a destination channel for all executions of a given Flow. In the below example, we are also declaring that users may not approve their own requests.

from sym.sdk.annotations import reducer
from sym.sdk.integrations import slack

@reducer
def get_approvers(event):
    return slack.channel("#sym-requests", allow_self=False)

Pass variables from Terraform to your impl.py

In some cases, you might want to extract information from your Flow's event and use it to make variable decisions. For example, if you want to check the urgency of a request, and in cases of "Emergency," route the request to a different channel, you might want to introduce Flow Variables.

For Flow Variables, we recommend the following pattern:

Define your flow_variables in a .tfvars file

flow_variables = {
  request_channel = "#sym-requests"
  emergency_channel = "#sym-emergencies"
}

Declare your flow_variables in a variables.tf file

variable "flow_variables" {
  description = "Configuration values for the Flow, available in its implementation for hooks and reducers."
  type        = map(string)
  default     = {}
}

Reference your variables in your sym_flow

Note that below, we are also introducing a prompt_field called "Urgency" through which a Flow user can declare an "Emergency."

resource "sym_flow" "aws_sso" {
  name  = "approval"
  label = "Approval"

  implementation = file("${path.module}/impl.py")

  # This ensures your vars are available to this Flow's impl.py
  vars = var.flow_variables

  params {
    strategy_id = sym_strategy.this.id

    prompt_field {
      name     = "reason"
      label    = "Why do you need access?"
      type     = "string"
      required = true
    }

    prompt_field {
      name           = "urgency"
      label          = "Urgency"
      type           = "string"
      required       = true
      allowed_values = ["Normal", "Emergency"]
    }
  }
}

Use your Flow Variables to make decisions in your impl.py

Now that we have our "Urgency" prompt and our Flow Variables, we can use both to construct a more nuanced get_approvers(event) function:

from sym.sdk.annotations import reducer
from sym.sdk.integrations import slack

@reducer
def get_approvers(event):
  # Make sure our Flow Variables are available
  fvars = event.flow.vars
  
  # Logic to route requests based on "Urgency"
  if event.payload.fields.get("urgency") == "Normal"
    return slack.channel(fvars["request_channel"], allow_self=False)
  elif event.payload.fields.get("urgency") == "Emergency"
    return slack.channel(fvars["emergency_channel"], allow_self=True)

Congratulations! Now you know how to manage the Slack destination for requests for your Sym Flows. πŸŽ‰