Skip to content

Commit aae11c6

Browse files
moved page
1 parent a750470 commit aae11c6

File tree

2 files changed

+248
-0
lines changed

2 files changed

+248
-0
lines changed
Lines changed: 247 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,247 @@
1+
# Custom Steps dynamic options for Workflow Builder
2+
3+
## Background {#background}
4+
5+
[Legacy steps from apps](https://docs.slack.dev/changelog/2023-08-workflow-steps-from-apps-step-back) previously enabled Slack apps to create and process custom workflow steps, which could then be shared and used by anyone in Workflow Builder. To support your transition away from them, custom steps used as dynamic options are available. These allow you to use data defined when referencing the step in Workflow Builder as inputs to the step.
6+
7+
## Example use case {#use-case}
8+
9+
Let's say a builder wants to add a custom step in Workflow Builder that creates an issue in an external issue-tracking system. First, they'll need to specify a project. Once a project is selected, a project-specific list of fields can be presented to them to choose from when creating the issue.
10+
11+
As a developer, dynamic options allow you to supply data to input parameters of custom steps so that you can provide builders with varying sets of fields based on the builders' selections.
12+
13+
In this example, the primary step would invoke a separate project selection step that retrieves the list of available projects. The builder-selected item from the retrieved list would then be used as the input to the secondary issue creation step.
14+
15+
There are two parts necessary for Slack apps to support dynamic options: custom step definitions, and handling custom step dynamic options. We'll take a look at both in the following sections.
16+
17+
## Custom step definitions {#custom-step-definitions}
18+
19+
When defining an input to a custom step intended to be dynamic (rather than explicitly defining a set of input parameters up front), you'll define a `dynamic_options` property that points to another custom step designed to return the set of dynamic elements once this step is added to a workflow from Workflow Builder.
20+
21+
An input parameter for a custom step can reference a different custom step that defines what data is available for it to return. One Slack app could even use another Slack app’s custom step to define dynamic options for one of its inputs.
22+
23+
The following code snippet from our issue creation example discussed above shows a `create-issue` custom step that will be used as a workflow step. Another custom step, the `get-projects` step, will dynamically populate the project input parameter to be configured by a builder. This `get-projects` step provides an `array` containing projects fetched dynamically from the external issue-tracking system.
24+
25+
```js
26+
"functions": {
27+
"create-issue": {
28+
"title": "Create Issue",
29+
"description": "",
30+
"input_parameters": {
31+
"support_channel": {
32+
"type": "slack#/types/channel_id",
33+
"title": "Support Channel",
34+
"description": "",
35+
"name": "support_channel"
36+
},
37+
"project": {
38+
"type": "string",
39+
"title": "Project",
40+
"description": "A project from the issue tracking system",
41+
"is_required": true,
42+
"dynamic_options": {
43+
"function": "#/functions/get-projects",
44+
"inputs": {}
45+
}
46+
},
47+
},
48+
"output_parameters": {}
49+
},
50+
"get-projects": {
51+
"title": "Get Projects",
52+
"description": "Get the available project from the issue tracking system",
53+
"input_parameters": {},
54+
"output_parameters": {
55+
"options": {
56+
"type": "slack#/types/options_select",
57+
"title": "Project Options",
58+
}
59+
}
60+
}
61+
},
62+
```
63+
### Defining the `function` and `inputs` attributes {#define-attributes}
64+
65+
Defining the `function` and `inputs` attributes of the `dynamic_options` property would look as follows:
66+
67+
```
68+
"dynamic_options": {
69+
"function": "#/functions/get-projects",
70+
"inputs": {}
71+
}
72+
```
73+
74+
The `function` attribute specifies the step reference used to resolve the options of the input parameter. For example: `"#/functions/get-projects"`.
75+
76+
The `inputs` attribute defines the parameters to be passed as inputs to the step referenced by the `function` attribute. For example:
77+
78+
```
79+
"inputs": {
80+
"selected_user_id": {
81+
"value": "{{input_parameters.user_id}}"
82+
},
83+
"query": {
84+
"value": "{{client.query}}"
85+
}
86+
}
87+
```
88+
89+
The following format can be used to reference any input parameter defined by the step: `{{input_parameters.<PARAMETER_NAME>}}`.
90+
91+
In addition, the `{{client.query}}` parameter can be used as a placeholder for an input value. The `{{client.builder_context}}` parameter will inject the [`slack#/types/user_context`](https://tools.slack.dev/deno-slack-sdk/reference/slack-types/#usercontext) of the user building the workflow as the value to the input parameter.
92+
93+
### Types of dynamic options UIs {#dynamic-option-UIs}
94+
95+
The above example demonstrates one possible UI to be rendered for builders: a single-select drop-down menu of dynamic options. However, dynamic options in Workflow Builder can be rendered in one of two ways: as a drop-down menu (single-select or multi-select), or as a set of fields.
96+
97+
The type is dictated by the output parameter of the custom step used as a dynamic option. In order to use a custom step in a dynamic option context, its output must adhere to a defined interface, that is, it must have an `options` parameter of type [`options_select`](https://tools.slack.dev/deno-slack-sdk/reference/slack-types#options_select) or [`options_field`](https://tools.slack.dev/deno-slack-sdk/reference/slack-types#options_field), as shown in the following code snippet.
98+
99+
```js
100+
"output_parameters": {
101+
"options": {
102+
"type": "slack#/types/options_select" or "slack#/types/options_field",
103+
"title": "Custom Options",
104+
"description": "Options to be used in a dynamic context",
105+
}
106+
...
107+
}
108+
```
109+
110+
#### Drop-down menus {#drop-down}
111+
112+
Your dynamic input parameter can be rendered as a drop-down menu, which will use the options obtained from a custom step with an `options` output parameter of the type [`options_select`](https://tools.slack.dev/deno-slack-sdk/reference/slack-types#options_select).
113+
114+
The drop-down menu UI component can be rendered in two ways: single-select, or multi-select. To render the dynamic input as a single-select menu, the input parameter defining the dynamic option must be of the type [`string`](https://tools.slack.dev/deno-slack-sdk/reference/slack-types#string).
115+
116+
```js
117+
"step-with-dynamic-input": {
118+
"title": "Step that uses a dynamic input",
119+
"description": "This step uses a dynamic input rendered as a single-select menu",
120+
"input_parameters": {
121+
"dynamic_single_select": {
122+
"type": "string", // this must be of type string for single-select
123+
"title": "dynamic single select drop-down menu",
124+
"description": "A dynamically-populated single-select drop-down menu",
125+
"is_required": true,
126+
"dynamic_options": {
127+
"function": "#/functions/get-options",
128+
"inputs": {},
129+
},
130+
}
131+
},
132+
"output_parameters": {}
133+
}
134+
```
135+
136+
To render the dynamic input as a multi-select menu, the input parameter defining the dynamic option must be of the type [`array`](https://tools.slack.dev/deno-slack-sdk/reference/slack-types#array), and its `items` must be of type [`string`](https://tools.slack.dev/deno-slack-sdk/reference/slack-types#string).
137+
138+
```js
139+
"step-with-dynamic-input": {
140+
"title": "Step that uses a dynamic input",
141+
"description": "This step uses a dynamic input rendered as a multi-select menu",
142+
"input_parameters": {
143+
"dynamic_multi_select": {
144+
"type": "array", // this must be of type array for multi-select
145+
"items": {
146+
"type": "string"
147+
},
148+
"title": "dynamic single select drop-down menu",
149+
"description": "A dynamically-populated multi-select drop-down menu",
150+
"dynamic_options": {
151+
"function": "#/functions/get-options",
152+
"inputs": {},
153+
},
154+
}
155+
},
156+
"output_parameters": {}
157+
}
158+
```
159+
160+
#### Fields {#fields}
161+
162+
In the code snippet below, the input parameter is rendered as a set of fields with keys and values. The option fields are obtained from a custom step with an `options` output parameter of type [`options_field`](https://tools.slack.dev/deno-slack-sdk/reference/slack-types#options_field).
163+
164+
The input parameter that defines the dynamic option must be of type [`object`](https://tools.slack.dev/deno-slack-sdk/reference/slack-types#object), as the completed set of fields in Workflow Builder will be passed to the custom step as an [untyped object](https://tools.slack.dev/deno-slack-sdk/reference/slack-types#untyped-object) during workflow execution.
165+
166+
```js
167+
"test-field-dynamic-options": {
168+
"title": "Test dynamic field options",
169+
"description": "",
170+
"input_parameters": {
171+
"dynamic_fields": {
172+
"type": "object",
173+
"title": "Dynamic custom field options",
174+
"description": "A dynamically-populated section of input fields",
175+
"dynamic_options": {
176+
"function": "#/functions/get-field-options",
177+
"inputs": {}
178+
"selection_type": "key-value",
179+
}
180+
}
181+
},
182+
"output_parameters": {}
183+
}
184+
```
185+
186+
### Dynamic option types {#dynamic-option-types}
187+
188+
As mentioned earlier, in order to use a custom step as a dynamic option, its output must adhere to a defined interface: it must have an `options` output parameter of the type either [`options_select`](https://tools.slack.dev/deno-slack-sdk/reference/slack-types#options_select) or [`options_field`](https://tools.slack.dev/deno-slack-sdk/reference/slack-types#options_field).
189+
190+
To take a look at these in more detail, refer to our [Options Slack type](https://tools.slack.dev/deno-slack-sdk/reference/slack-types#options) documentation.
191+
192+
## Dynamic options handler {#dynamic-option-handler}
193+
194+
Each custom step defined in the manifest needs a corresponding handler in your Slack app. Although implemented similarly to existing function execution event handlers, there are two key differences between regular custom step invocations and those used for dynamic options:
195+
196+
* The custom step must have an `options` output parameter that is of type [`options_select`](https://tools.slack.dev/deno-slack-sdk/reference/slack-types#options_select) or [`options_field`](https://tools.slack.dev/deno-slack-sdk/reference/slack-types#options_field).
197+
* The [`function_executed`](https://docs.slack.dev/reference/events/function_executed) event must be handled synchronously. This optimizes the response time of returned dynamic options and provides a crisp builder experience.
198+
199+
### Asynchronous event handling {#async}
200+
201+
By default, the [Bolt family of frameworks](https://tools.slack.dev/) handles `function_executed` events asynchronously.
202+
203+
For example, the various modal-related API methods provide two ways to update a view: synchronously using a `response_action` HTTP response, or asynchronously using a separate HTTP API call. Using the asynchronous approach allows developers to handle events free of timeouts, but this isn't desired for dynamic options as it introduces delays and violates our stated goal of providing a crisp builder experience.
204+
205+
### Synchronous event handling {#sync}
206+
207+
Dynamic options support synchronous handling of `function_executed` events. By ensuring that the function execution’s state is complete with output parameters provided before responding to the `function_executed` event, Slack can quickly provide Workflow Builder with the requisite dynamic options.
208+
209+
### Implementation {#implementation}
210+
211+
To optimize the response time of dynamic options, you must acknowledge the incoming event after calling the [`function.completeSuccess`](https://docs.slack.dev/reference/methods/functions.completeSuccess) or [`function.completeError`](https://docs.slack.dev/reference/methods/functions.completeError) API methods, minimizing asynchronous latency. The `function.completeSuccess` and `function.completeError` API methods are invoked in the complete and fail helper functions. ([For example](https://github.com/slackapi/bolt-python?tab=readme-ov-file#making-things-happen)).
212+
213+
A new `auto_acknowledge` flag allows you more granular control over whether specific event handlers should operate in synchronous or asynchronous response modes in order to enable a smooth dynamic options experience.
214+
215+
#### Example {#bolt-py}
216+
217+
In [Bolt for Python](https://tools.slack.dev/bolt-python/), you can set `auto_acknowledge=False` on a specific function decorator. This allows you to manually control when the `ack()` event acknowledgement helper function is executed. It flips Bolt to synchronous `function_executed` event handling mode for the specific handler.
218+
219+
```py
220+
@app.function("get-projects", auto_acknowledge=False)
221+
def handle_get_projects(ack: Ack, complete: Complete):
222+
try:
223+
complete(
224+
outputs={
225+
"options": [
226+
{
227+
"text": {
228+
"type": "plain_text",
229+
"text": "Secret Squirrel Project",
230+
},
231+
"value": "p1",
232+
},
233+
{
234+
"text": {
235+
"type": "plain_text",
236+
"text": "Public Kangaroo Project",
237+
},
238+
"value": "p2",
239+
},
240+
]
241+
}
242+
)
243+
finally:
244+
ack()
245+
```
246+
247+
**To learn more about the Bolt family of frameworks and tools**, check out our [Slack Developer Tools](https://tools.slack.dev/).

docs/sidebars.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ const sidebars = {
4141
},
4242
"concepts/ai-apps",
4343
"concepts/custom-steps",
44+
"concepts/custom-steps-dynamic-options",
4445
{
4546
type: "category",
4647
label: "App Configuration",

0 commit comments

Comments
 (0)