Prefetch: Typeahead Fields
Prefetch Reducers are used to dynamically populate a Slack prompt from an external data source.
Overview
Prefetch Reducers are useful for scenarios when you might not know every option for a given Prompt Fields ahead of time.
For example:
- Growing datasets that live outside of Terraform, like lists of customer-specific AWS instances
- Sensitive situations where you might want to check information about a requestor before rendering
- Datasets that you do not control directly, as in the example below
Implementation
Prefetch Reducers are two steps to their implementation:
- Marking a Prompt Field with
prefetch = true
indicates that it will be filled via Prefetch. - If using Prefetch for an Access Target, you must name your Prompt Field to match the
settings
attribute expected by that Target, and bind the corresponding Prompt Field to your Strategy viafield_bindings
(see example below) - Each Prompt Field marked with
prefetch = true
must have a corresponding Prefetch Reducer, and there may be exactly one Prefetch Reducer for a given Prompt Field.
A Prefetch Reducer must return a list of FieldOptions, where a FieldOption
defines a given item to be displayed in Slack.
The list of FieldOption
returned by your reducer will dictate the allowed values a requester may select from for that Prompt Field. When filling out the request form, the requester's input will be used to search for a FieldOption
based on its label
.
Prefetch Reducer Limits
Prefetch Reducers may return up to a maximum of 1000 items. If your reducer returns more than 1000 items, your Flow will terminate with an error.
Prefetch Reducers are run before displaying the request form to users. If your reducer is slow, it may impact the performance of your requests!
Prefetch reducers will be validated at runtime, and not during
terraform apply
. If there is a missing or duplicate prefetch reducer, an error will be raised as the user makes the request in Slack.

A demo of a Prompt Field that returns Pokemon names as FieldOptions
Prefetch for non-Target fields
Given the following Prompt Field definition:
prompt_field {
name = "pokemon"
label = "What Pokemon do you want?"
type = "string"
prefetch = true
required = true
}
You could define the following Prefetch Reducer:
import requests
from sym.sdk.annotations import prefetch
from sym.sdk.field_option import FieldOption
@prefetch(field_name="pokemon")
def get_pokemon(evt):
# Make an API Call or even invoke an AWS Lambda
response = requests.get(url="https://pokeapi.co/api/v2/pokemon?limit=100")
all_pokemon = response.json()["results"]
# Return a list of FieldOption
return [
FieldOption(value=pokemon["name"], label=pokemon["name"].upper())
for pokemon
in all_pokemon
]
Prefetch for Access Targets
Prefetching values for an Access Target will vary depending on integration. In the below, we will show how to create a dynamic, typeahead field that uses Prefetch for a list of GitHub repos.
Static GitHub Strategy
The sym_target
associated with Sym's GitHub Strategy expects a repo_name
in the settings
field.
resource "sym_target" "private-repo" {
type = "github_repo"
name = "main-private-repo-access"
label = "Private Repo"
settings = {
# `type=github_repo` sym_targets have a required setting `repo_nae`,
# which must be name of the Repository the requester will be escalated to when this target is selected
repo_name = "private-repo"
}
}
Dynamic GitHub Strategy with Prefetch
To make the above example dynamic, we will need to:
- Create a Prompt Field named
repo_name
and mark it asprefetch = true
- Bind the field to our
sym_target
- Provide a Prefetch Reducer to populate the field
See this example in full in our Examples Repo
# 1. Create a Prompt Field named `repo_name` and mark it as `prefetch = true`
resource "sym_flow" "this" {
name = "github"
label = "GitHub Repo Access"
implementation = file("${path.module}/impl.py")
environment_id = sym_environment.this.id
params {
strategy_id = sym_strategy.github.id
prompt_field {
# This prompt_field will be used to populate the `repo_name` setting of the GitHub Access Target.
# The name must match the setting name and type.
name = "repo_name"
label = "Repository Name"
type = "string"
required = true
# Setting prefetch = true means that this field's allowed values will be populated by
# a prefetch reducer defined in the impl.py
prefetch = true
}
prompt_field {
name = "duration"
type = "duration"
allowed_values = ["1h", "1d", "10d"]
required = true
}
}
}
# 2. Bind the `repo_name` field to our `sym_target`
resource "sym_target" "private-repo" {
type = "github_repo"
name = "private-repos"
label = "Private Repos"
# A special attribute indicating which settings will be dynamically populated by prompt fields.
# In this case, the setting is the required `repo_name` setting. The value will be populated by the
# `repo_name` field in the `sym_flow.params.prompt_fields` attribute.
field_bindings = ["repo_name"]
}
# 3. Provide a Prefetch Reducer to populate the field
@prefetch(field_name="repo_name")
def get_repos(event):
github_token = event.flow.environment.integrations["github"].settings["api_token_secret"].retrieve_value()
headers = {"Authorization": f"Token {github_token}"}
response_options = []
while True:
# Use the GitHub API to get all the private repos in the Sym Test organization
# Note, we are skipping over pagination in this example
response = requests.get(
url=f"https://api.github.com/orgs/sym-test/repos&type=private",
headers=headers
)
# For this example, the value and the label are the same for the options.
response_options = [
FieldOption(value=repo["name"], label=repo["name"]) for repo in response.json()
]
return response_options
Updated about 1 month ago