-
Notifications
You must be signed in to change notification settings - Fork 401
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
docs: document custom step usage #2198
Changes from 18 commits
a050256
188813c
f31d8df
3faab87
b46f470
1b79873
630f188
caec4b1
247b4c7
11a42c0
e933eda
87730ef
bea900d
b10bbb0
e3ac1eb
717028f
f15413a
60261d0
23ef516
3aa9f34
5132ad4
7f14da1
90ce812
0451502
0110bf2
960d18e
d73f717
5fa091a
70240f9
56ab543
44da1d1
b353056
34fefa2
6779dbb
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,103 @@ | ||||||||||||||||||||
--- | ||||||||||||||||||||
title: Listening and responding to custom steps | ||||||||||||||||||||
lang: en | ||||||||||||||||||||
slug: /concepts/custom-steps | ||||||||||||||||||||
--- | ||||||||||||||||||||
|
||||||||||||||||||||
Your app can use the `function()` method to listen to incoming [custom step requests](https://api.slack.com/automation/functions/custom-bolt). Custom steps are used in Workflow Builder to build workflows. The method requires a step `callback_id` of type string. This `callback_id` must also be defined in your [Function](https://api.slack.com/concepts/manifests#functions) definition. Custom steps must be finalized using the `complete()` or `fail()` listener arguments to notify Slack that your app has processed the request. | ||||||||||||||||||||
|
||||||||||||||||||||
* `complete()` requires one argument: an `outputs` object. It completes your custom step **successfully** and provides an object containing the outputs of your custom step as per its definition. | ||||||||||||||||||||
* `fail()` requires **one** argument: `error` of type string. It completes your custom step **unsuccessfully** and provides a message containing information regarding why your custom step failed. | ||||||||||||||||||||
WilliamBergamin marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||
|
||||||||||||||||||||
You can reference your custom step's inputs using the `inputs` listener argument. | ||||||||||||||||||||
|
||||||||||||||||||||
```js | ||||||||||||||||||||
// This sample custom step formats an input and outputs it | ||||||||||||||||||||
app.function('sample_custom_step', async ({ ack, inputs, complete, fail, logger }) => { | ||||||||||||||||||||
try { | ||||||||||||||||||||
await ack(); | ||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
Removing the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I prefer to keep it in the docs, there are a number of issues opened related to calling ack before executing custom logic Keeping this in hopes that it will help developers avoid a bug 🙏 We can remove it in a follow up PR as well 🤔 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||||||||||||||||||||
const { message } = inputs; | ||||||||||||||||||||
|
||||||||||||||||||||
await complete({ | ||||||||||||||||||||
outputs: { | ||||||||||||||||||||
message: `:wave: You submitted the following message: \n\n>${message}` | ||||||||||||||||||||
} | ||||||||||||||||||||
}); | ||||||||||||||||||||
} catch (error) { | ||||||||||||||||||||
logger.error(error); | ||||||||||||||||||||
await fail({ error: `Failed to handle a function request: ${error}` }); | ||||||||||||||||||||
} | ||||||||||||||||||||
}); | ||||||||||||||||||||
``` | ||||||||||||||||||||
|
||||||||||||||||||||
<details> | ||||||||||||||||||||
filmaj marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||
<summary> | ||||||||||||||||||||
Listening to custom step actions | ||||||||||||||||||||
WilliamBergamin marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||
</summary> | ||||||||||||||||||||
Your app can listen to user actions, like button clicks, created from `custom steps` using the `action` method. | ||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Suggested tweak to the introduction here:
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is good 🙇 provides more information without duplicating documentation from api.slack.com I had to adapt this slightly to because open modal views are not yet supported, but hopefully soon |
||||||||||||||||||||
|
||||||||||||||||||||
Actions can be filtered on an `action_id` of type string or RegExp object. `action_id`s act as unique identifiers for interactive components on the Slack platform. | ||||||||||||||||||||
WilliamBergamin marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||
|
||||||||||||||||||||
Your app can skip calling `complete()` or `fail()` in the `function()` listener if the custom step creates an `action` that waits for user interaction. However, in the `action()` method, your app must invoke `complete()` or `fail()` to notify Slack that the custom step has been processed. | ||||||||||||||||||||
WilliamBergamin marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||
|
||||||||||||||||||||
You’ll notice in all `action()` examples, `ack()` is used. It is required to call the `ack()` function within an action listener to acknowledge that the request was received from Slack. This is discussed in the [acknowledging requests section](/concepts/acknowledge). | ||||||||||||||||||||
WilliamBergamin marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||
|
||||||||||||||||||||
```js | ||||||||||||||||||||
/** This sample custom step posts a message with a button */ | ||||||||||||||||||||
app.function('sample_function', async ({ ack, client, inputs, fail, logger }) => { | ||||||||||||||||||||
try { | ||||||||||||||||||||
await ack(); | ||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||||||||
const { user_id } = inputs; | ||||||||||||||||||||
|
||||||||||||||||||||
await client.chat.postMessage({ | ||||||||||||||||||||
channel: user_id, | ||||||||||||||||||||
text: 'Click the button to signal the function has completed', | ||||||||||||||||||||
blocks: [ | ||||||||||||||||||||
{ | ||||||||||||||||||||
type: 'section', | ||||||||||||||||||||
text: { | ||||||||||||||||||||
type: 'mrkdwn', | ||||||||||||||||||||
text: 'Click the button to signal the function has completed', | ||||||||||||||||||||
}, | ||||||||||||||||||||
accessory: { | ||||||||||||||||||||
type: 'button', | ||||||||||||||||||||
text: { | ||||||||||||||||||||
type: 'plain_text', | ||||||||||||||||||||
text: 'Complete function', | ||||||||||||||||||||
}, | ||||||||||||||||||||
action_id: 'sample_button', | ||||||||||||||||||||
}, | ||||||||||||||||||||
}, | ||||||||||||||||||||
], | ||||||||||||||||||||
}); | ||||||||||||||||||||
} catch (error) { | ||||||||||||||||||||
logger.error(error); | ||||||||||||||||||||
await fail({ error: `Failed to handle a function request: ${error}` }); | ||||||||||||||||||||
} | ||||||||||||||||||||
}); | ||||||||||||||||||||
|
||||||||||||||||||||
/** Your listener will be called every time a block element with the action_id "sample_button" is triggered */ | ||||||||||||||||||||
app.action('sample_button', async ({ ack, body, client, complete, fail, logger }) => { | ||||||||||||||||||||
try { | ||||||||||||||||||||
await ack(); | ||||||||||||||||||||
|
||||||||||||||||||||
const { channel, message, user } = body; | ||||||||||||||||||||
// Functions should be marked as successfully completed using `complete` or | ||||||||||||||||||||
// as having failed using `fail`, else they'll remain in an 'In progress' state. | ||||||||||||||||||||
await complete({ outputs: { user_id: user.id } }); | ||||||||||||||||||||
|
||||||||||||||||||||
await client.chat.update({ | ||||||||||||||||||||
channel: channel.id, | ||||||||||||||||||||
ts: message.ts, | ||||||||||||||||||||
text: 'Function completed successfully!', | ||||||||||||||||||||
}); | ||||||||||||||||||||
} catch (error) { | ||||||||||||||||||||
logger.error(error); | ||||||||||||||||||||
await fail({ error: `Failed to handle a function request: ${error}` }); | ||||||||||||||||||||
} | ||||||||||||||||||||
}); | ||||||||||||||||||||
``` | ||||||||||||||||||||
|
||||||||||||||||||||
Learn more about responding to interactivity, see the [Slack API documentation](https://api.slack.com/automation/functions/custom-bolt#interactivity). | ||||||||||||||||||||
|
||||||||||||||||||||
</details> |
This file was deleted.
This file was deleted.
This file was deleted.
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
--- | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Will need help with the translation 🙏 |
||
title: Listening and responding to custom steps | ||
lang: ja-jp | ||
slug: /concepts/custom-steps | ||
--- | ||
|
||
Your app can use the `function()` method to listen to incoming [custom step requests](https://api.slack.com/automation/functions/custom-bolt). The method requires a step `callback_id` of type string. This `callback_id` must also be defined in your [Function](https://api.slack.com/concepts/manifests#functions) definition. Custom steps must be finalized using the `complete()` or `fail()` listener arguments to notify Slack that your app has processed the request. | ||
|
||
* `complete()` requires one argument: an `outputs` object. It completes your custom step **successfully** and provides an object containing the outputs of your custom step as per its definition. | ||
* `fail()` requires **one** argument: `error` of type string. It completes your custom step **unsuccessfully** and provides a message containing information regarding why your custom step failed. | ||
|
||
You can reference your custom step's inputs using the `inputs` listener argument. | ||
|
||
```js | ||
// This sample custom step formats an input and outputs it | ||
app.function('sample_custom_step', async ({ ack, inputs, complete, fail, logger }) => { | ||
try { | ||
await ack(); | ||
const { message } = inputs; | ||
|
||
await complete({ | ||
outputs: { | ||
message: `:wave: You submitted the following message: \n\n>${message}` | ||
} | ||
}); | ||
} catch (error) { | ||
logger.error(error); | ||
await fail({ error: `Failed to handle a function request: ${error}` }); | ||
} | ||
}); | ||
``` | ||
|
||
<details> | ||
<summary> | ||
Listening to custom step actions | ||
</summary> | ||
Your app can listen to user actions, like button clicks, created from `custom steps` using the `action` method. | ||
|
||
Actions can be filtered on an `action_id` of type string or RegExp object. `action_id`s act as unique identifiers for interactive components on the Slack platform. | ||
|
||
Your app can skip calling `complete()` or `fail()` in the `function()` listener if the custom step creates an `action` that waits for user interaction. However, in the `action()` method, your app must invoke `complete()` or `fail()` to notify Slack that the custom step has been processed. | ||
|
||
You’ll notice in all `action()` examples, `ack()` is used. It is required to call the `ack()` function within an action listener to acknowledge that the request was received from Slack. This is discussed in the [acknowledging requests section](/concepts/acknowledge). | ||
|
||
```js | ||
/** This sample custom step posts a message with a button */ | ||
app.function('sample_function', async ({ ack, client, inputs, fail, logger }) => { | ||
try { | ||
await ack(); | ||
const { user_id } = inputs; | ||
|
||
await client.chat.postMessage({ | ||
channel: user_id, | ||
text: 'Click the button to signal the function has completed', | ||
blocks: [ | ||
{ | ||
type: 'section', | ||
text: { | ||
type: 'mrkdwn', | ||
text: 'Click the button to signal the function has completed', | ||
}, | ||
accessory: { | ||
type: 'button', | ||
text: { | ||
type: 'plain_text', | ||
text: 'Complete function', | ||
}, | ||
action_id: 'sample_button', | ||
}, | ||
}, | ||
], | ||
}); | ||
} catch (error) { | ||
logger.error(error); | ||
await fail({ error: `Failed to handle a function request: ${error}` }); | ||
} | ||
}); | ||
|
||
/** Your listener will be called every time a block element with the action_id "sample_button" is triggered */ | ||
app.action('sample_button', async ({ ack, body, client, complete, fail, logger }) => { | ||
try { | ||
await ack(); | ||
|
||
const { channel, message, user } = body; | ||
// Functions should be marked as successfully completed using `complete` or | ||
// as having failed using `fail`, else they'll remain in an 'In progress' state. | ||
await complete({ outputs: { user_id: user.id } }); | ||
|
||
await client.chat.update({ | ||
channel: channel.id, | ||
ts: message.ts, | ||
text: 'Function completed successfully!', | ||
}); | ||
} catch (error) { | ||
logger.error(error); | ||
await fail({ error: `Failed to handle a function request: ${error}` }); | ||
} | ||
}); | ||
``` | ||
|
||
</details> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hoping to set a bit more context for custom steps in just the first paragraph! Also wanting to frame the following one as more instruction instead of reference, but feel free to change it or ignore 🙏
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also not certain about the "method" vs "listener" naming!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The rest of the documentation uses the
method
terminology instead of thelistener
I think its better to be consistent and stick to
method
We can send a follow up PR to update all the terms 👍