Workflow Handlers

Use Sym's workflow handlers to route, streamline, and automate you Flows with custom logic.

Overview

Under the hood, each Sym Flow consists of a series of discrete steps that can be interrupted or redirected with custom logic wrapped in Handler functions. The two types of Handlers are reducers and hooks.

Reducers are prefixed with get_, and take in an Event and return a single value.
Hooks are prefixed with on_ or after_, and allow you to alter control flow inserting custom logic before or after an Event is processed.

Handlers in context

Handlers fire at specific moments in a Sym Flow, giving you the power to define things like:

  • Where requests are routed
  • Scenarios in which requests are automatically approved or rejected
  • How to handle third-party identity lookups
  • What to do after a request cycle is completed
28012801

πŸ“˜

Handlers are defined in the sym.sdk.annotations module.

Reducers

A Reducer injects key logic into Flows by taking an Event as input, and returning a single value.

For example, when using the sym:approval Template, you need a way to let Sym know who to route a specific request to. This is exactly what the get_approvers reducer does! To implement it, you write a Python function which takes in an Event (containing the requesting user and requested Target, and return a SlackChannel indicating where to route the request.

from sym.sdk.annotations import reducer
from sym.sdk.integrations import pagerduty, okta, slack

@reducer 
def get_approvers(event):
    """Returns a set of approvers, given a user and a target."""
   
    if pagerduty.is_on_call(event.user, schedule_name="prod_on_call_schedule"):
        # This is a self-approval in a DM
        return slack.user(event.user)

    if event.payload.fields["urgency"] == "High":
        # This is a self-approval in a channel
        return slack.channel("#break-glass", allow_self=True)

    on_call_mgrs = okta.users_in_group(group_id="00g12345689")
    # This would create a group DM for on-call managers
    return slack.group(on_call_mgrs)

On-Hooks

Hooks are executed before the default implementation of an Event handler in a Template. They offer an opportunity to bypass, short-circuit, or alter control flow, by emitting Events.

For example, when using the sym:approval Template, you may want to auto-approve any requests that come from an on-call engineer.

from sym.sdk.annotations import hook
from sym.sdk.integrations import pagerduty
from sym.sdk.templates import ApprovalTemplate

# Hooks are optional, and can change control flow by returning Events

@hook
def on_request(event):
  # Auto-approve urgent requests for access by the person on call
  if event.payload.fields["urgency"] == "Urgent" and pagerduty.is_on_call(event.user):
    return ApprovalTemplate.approve()

After-Hooks

After Hooks are executed after the default implementation of an Event handler in a Template. They offer an opportunity to execute additional side-effects, such as logging or notifications.

For example, when using the sym:approval Template, you may want to log every approval to a private Slack channel.

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

# After-hooks are optional, and let you execute side-effects after an Event

@hook
def after_approve(event):
    """Executed after an approved event has been fired."""
    message = f"{event.user.username} has been approved for {event.payload.fields["target"].name}!"
    slack.send_message(slack.channel("#private-audit-log"), message)

What’s Next
Did this page help you?