-
Notifications
You must be signed in to change notification settings - Fork 399
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
Merged
Merged
Changes from all commits
Commits
Show all changes
34 commits
Select commit
Hold shift + click to select a range
a050256
[WIP] Add function listener support
misscoded 188813c
[WIP] Add foundation of CLI hooks support
misscoded f31d8df
[WIP] Add support for interactivity
misscoded 3faab87
[WIP] Add attachFunctionToken opt-out option
misscoded b46f470
[WIP] Revert CLI hook support within Bolt
zimeg 1b79873
Incorporate PR comments
misscoded 630f188
Add CustomFunction test + adjust naming
misscoded caec4b1
Update error description
misscoded 247b4c7
Bump the @slack/web-api version to v6.12.0
zimeg 11a42c0
v3.17.1-customFunctionBeta.0
zimeg e933eda
chore: merge w main
zimeg 87730ef
chore: merge w main
zimeg bea900d
Fixes and polish for stable release (#2128)
misscoded b10bbb0
chore: merge main
e3ac1eb
docs: document custom step usage
WilliamBergamin 717028f
Remove old docs
WilliamBergamin f15413a
Improve readme based on feedback
WilliamBergamin 60261d0
Merge branch 'feat-functions' into document-custom-steps
WilliamBergamin 23ef516
[Bug] Fix chained function actions (#2200)
misscoded 3aa9f34
Update docs/content/basic/custom-steps.md
WilliamBergamin 5132ad4
Update docs/content/basic/custom-steps.md
WilliamBergamin 7f14da1
Update docs/content/basic/custom-steps.md
WilliamBergamin 90ce812
Remote functions: typescript integration test with bolt-ts-starter-te…
filmaj 0451502
Improve based on feedback
WilliamBergamin 0110bf2
Merge branch 'main' into feat-functions
960d18e
Merge branch 'feat-functions' into document-custom-steps
WilliamBergamin d73f717
Update docs/content/basic/custom-steps.md
WilliamBergamin 5fa091a
Update I18
WilliamBergamin 70240f9
Merge branch 'document-custom-steps' of https://github.com/slackapi/b…
WilliamBergamin 56ab543
Add the definition in a dropdown
WilliamBergamin 44da1d1
Improve example
WilliamBergamin b353056
Update I18 version
WilliamBergamin 34fefa2
Remove ack from example
WilliamBergamin 6779dbb
Merge branch 'main' into document-custom-steps
WilliamBergamin File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,167 @@ | ||
--- | ||
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 ends 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 ends 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 ({ inputs, complete, fail, logger }) => { | ||
try { | ||
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> | ||
Example app manifest definition | ||
</summary> | ||
|
||
```json | ||
... | ||
"functions": { | ||
"sample_custom_step": { | ||
"title": "Sample custom step", | ||
"description": "Run a sample custom step", | ||
"input_parameters": { | ||
"message": { | ||
"type": "string", | ||
"title": "Message", | ||
"description": "A message to be formatted by a custom step", | ||
"is_required": true, | ||
} | ||
}, | ||
"output_parameters": { | ||
"message": { | ||
"type": "string", | ||
"title": "Messge", | ||
"description": "A formatted message", | ||
"is_required": true, | ||
} | ||
} | ||
} | ||
} | ||
``` | ||
|
||
</details> | ||
|
||
--- | ||
|
||
### Listening to custom step interactivity events | ||
|
||
Your app's custom steps may create interactivity points for users, for example: Post a message with a button | ||
|
||
If such interaction points originate from a custom step execution, the events sent to your app representing the end-user interaction with these points are considered to be _function-scoped interactivity events_. These interactivity events can be handled by your app using the same concepts we covered earlier, such as [Listening to actions](/concepts/action-listening). | ||
|
||
_function-scoped interactivity events_ will contain data related to the custom step (`function_executed` event) they were spawned from, such as custom step `inputs` and access to `complete()` and `fail()` listener arguments. | ||
|
||
Your app can skip calling `complete()` or `fail()` in the `function()` handler method if the custom step creates an interaction point that requires user interaction before the step can end. However, in the relevant interactivity handler method, your app must invoke `complete()` or `fail()` to notify Slack that the custom step has been processed. | ||
|
||
You’ll notice in all interactivity handler examples, `ack()` is used. It is required to call the `ack()` function within an interactivity 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('custom_step_button', async ({ client, inputs, fail, logger }) => { | ||
try { | ||
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> | ||
<summary> | ||
Example app manifest definition | ||
</summary> | ||
|
||
```json | ||
... | ||
"functions": { | ||
"custom_step_button": { | ||
"title": "Custom step with a button", | ||
"description": "Custom step that waits for a button click", | ||
"input_parameters": { | ||
"user_id": { | ||
"type": "slack#/types/user_id", | ||
"title": "User", | ||
"description": "The recipient of a message with a button", | ||
"is_required": true, | ||
} | ||
}, | ||
"output_parameters": { | ||
"user_id": { | ||
"type": "slack#/types/user_id", | ||
"title": "User", | ||
"description": "The user that completed the function", | ||
"is_required": true | ||
} | ||
} | ||
} | ||
} | ||
``` | ||
|
||
</details> | ||
|
||
Learn more about responding to interactivity, see the [Slack API documentation](https://api.slack.com/automation/functions/custom-bolt#interactivity). |
16 changes: 0 additions & 16 deletions
16
docs/content/custom-functions/creating-custom-functions.md
This file was deleted.
Oops, something went wrong.
39 changes: 0 additions & 39 deletions
39
docs/content/custom-functions/defining-custom-functions.md
This file was deleted.
Oops, something went wrong.
25 changes: 0 additions & 25 deletions
25
docs/content/custom-functions/listening-to-custom-functions.md
This file was deleted.
Oops, something went wrong.
19 changes: 0 additions & 19 deletions
19
docs/content/custom-functions/responding-to-interactivity.md
This file was deleted.
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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 👍