Skip to content

Commit

Permalink
docs: add JSDoc to and list out all available builtin middleware func…
Browse files Browse the repository at this point in the history
…tions in the docs (#2136)

* docs: add JSDoc to and list out all available builtin middleware functions in the docs.

* split up docs on built-in middleware into global vs. listener middleware lists.
  • Loading branch information
filmaj authored Jun 18, 2024
1 parent 0f9326e commit dcccf9d
Show file tree
Hide file tree
Showing 4 changed files with 56 additions and 7 deletions.
6 changes: 2 additions & 4 deletions docs/_advanced/middleware_global.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,11 @@ order: 5
---

<div class="section-content">
Global middleware is run for all incoming requests before any listener middleware. You can add any number of global middleware to your app by utilizing `app.use(fn)`. The middleware function `fn` is called with the same arguments as listeners and an additional `next` function.
Global middleware is run for all incoming requests before any [listener middleware](#listener-middleware). You can add any number of global middleware to your app by utilizing `app.use(fn)`. The middleware function `fn` is called with the same arguments as listeners and an additional `next` function.

Both global and listener middleware must call `await next()` to pass control of the execution chain to the next middleware, or call `throw` to pass an error back up the previously-executed middleware chain.
Both global and [listener middleware](#listener-middleware) must call `await next()` to pass control of the execution chain to the next middleware, or call `throw` to pass an error back up the previously-executed middleware chain.

As an example, let's say your app should only respond to users identified with a corresponding internal authentication service (an SSO provider or LDAP, for example). You may define a global middleware that looks up a user record in the authentication service and errors if the user is not found.

*Note: Since v2, global middleware was updated to support `async` functions! View the [migration guide for V2](https://slack.dev/bolt/tutorial/migration-v2) to learn about the changes.*
</div>

```javascript
Expand Down
4 changes: 1 addition & 3 deletions docs/_advanced/middleware_listener.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,11 @@ order: 6
<div class="section-content">
Listener middleware is used for logic across many listener functions (but usually not all of them). They are added as arguments before the listener function in one of the built-in methods. You can add any number of listener middleware before the listener function.

There’s a collection of built-in listener middleware that you can use like `subtype()` for filtering on a message subtype and `directMention()` which filters out any message that doesn’t directly @-mention your bot at the start of a message.
There’s a collection of [built-in listener middleware](reference#built-in-listener-middleware-functions) that you can use like `directMention` which filters out any message that doesn’t directly @-mention your bot at the start of a message.

But of course, you can write your own middleware for more custom functionality. While writing your own middleware, your function must call `await next()` to pass control to the next middleware, or `throw` to pass an error back up the previously-executed middleware chain.

As an example, let’s say your listener should only deal with messages from humans. You can write a listener middleware that excludes any bot messages.

*Note: Since v2, listener middleware was updated to support `async` functions! View the [migration guide for V2](https://slack.dev/bolt/tutorial/migration-v2) to learn about the changes.*
</div>

```javascript
Expand Down
40 changes: 40 additions & 0 deletions docs/_tutorials/reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ This guide is intended to detail the Bolt interface–including listeners and th
- [Listener function arguments](#listener-function-arguments)
- [Body and payload references](#body-and-payload-references)
- [Difference from listener middleware](#difference-from-listener-middleware)
- [Built-in middleware functions](#built-in-listener-functions)
- [Built-in global middleware functions](#built-in-global-middleware-functions)
- [Built-in listener middleware functions](#built-in-listener-middleware-functions)
- [Initialization options](#initialization-options)
- [Receiver options](#receiver-options)
- [App options](#app-options)
Expand Down Expand Up @@ -79,6 +82,43 @@ The structure of the `payload` and `body` is detailed on the API site:
### Difference from listener middleware
Listener middleware is used to implement logic across many listener functions (though usually not all of them). Listener middleware has the same arguments as the above listener functions, with one distinction: they also have a `next()` function that **must** be called in order to pass the chain of execution. Learn more about listener middleware [in the documentation](/bolt-js/concepts#listener-middleware).

## Built-in middleware functions

Bolt offers a variety of built-in middleware functions to help simplify development of your Slack applications. These middleware functions implement common patterns to help filter out or focus your own listener function implementations.

These middleware functions are exported from the main `@slack/bolt` package for you to easily `import` in your applications:

```javascript
import { matchMessage } from '@slack/bolt';
app.message(matchMessage('hello'), async ({ message, logger }) => {
// this function will now only execute if "hello" is present in the message
});
```

These middleware functions are divided into two groups: [global middleware functions](concepts#global-middleware) and [listener middleware functions](concepts#listener-middleware).

### Built-in global middleware functions

- `ignoreSelf()`: Filters out any event that originates from the app. Note that this middleware is enabled by default via the [`ignoreSelf` App initialization options](#app-options).
- `onlyActions`: Filters out any event that isn't an action.
- `onlyCommands`: Filters out any event that isn't a command.
- `onlyEvents`: Allows for only events to propagate down the middleware chain.
- `onlyOptions`: Filters out any event that isn't a drop-down-options event.
- `onlyShortcuts`: Filters out any event that isn't a shortcut.
- `onlyViewActions`: Filters out any event that isn't a `view_submission` or `view_closed` event.

### Built-in listener middleware functions

- `directMention()`: Filters out any `message` event whose text does not start with an @-mention of the handling app.
- `matchCommandName(pattern)`: Filters out any shortcut command whose name does not match the provided `pattern`; `pattern` can be a string or regular expression.
- `matchConstraints(constraint)`: Filters out any `block_action`, View or Options event that does not match the properties of the provided `constraint` object. Supported `constraint` object properties include:
- `block_id` and `action_id`: for filtering out `block_action` events that do not match the provided IDs.
- `callback_id`: for filtering out `view_*` events not matching the provided `callback_id`.
- `type`: for filtering out any event `type`s not matching the provided `type`.
- `matchEventType(pattern)`: filters out any event whose `type` does not match the provided `pattern`. `pattern` can be a string or regular expression.
- `matchMessage(pattern)`: filters out any `message` or `app_mention` events whose message contents do not match the provided `pattern`. `pattern` can be a string or regular expression.
- `subtype(type)`: Filters out any `message` event whose `subtype` does not exactly equal the provided `type`.

## Initialization options
Bolt includes a collection of initialization options to customize apps. There are two primary kinds of options: Bolt app options and receiver options. The receiver options may change based on the receiver your app uses. The following receiver options are for the default `HTTPReceiver` (so they'll work as long as you aren't using a custom receiver).

Expand Down
13 changes: 13 additions & 0 deletions src/middleware/builtin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,11 @@ export function matchEventType(pattern: EventTypePattern): Middleware<SlackEvent
};
}

// TODO: breaking change: why does this method have to be invoked as a function with no args, while other similar
// method like the `only*` ones do not require that? should make this consistent.
/**
* Filters out any event originating from the handling app.
*/
export function ignoreSelf(): Middleware<AnyMiddlewareArgs> {
return async (args) => {
const botId = args.context.botId as string;
Expand Down Expand Up @@ -321,6 +326,9 @@ export function ignoreSelf(): Middleware<AnyMiddlewareArgs> {
};
}

/**
* Filters out any message events whose subtype does not match the provided subtype.
*/
export function subtype(subtype1: string): Middleware<SlackEventMiddlewareArgs<'message'>> {
return async ({ message, next }) => {
if (message.subtype === subtype1) {
Expand All @@ -331,6 +339,11 @@ export function subtype(subtype1: string): Middleware<SlackEventMiddlewareArgs<'

const slackLink = /<(?<type>[@#!])?(?<link>[^>|]+)(?:\|(?<label>[^>]+))?>/;

// TODO: breaking change: why does this method have to be invoked as a function with no args, while other similar
// method like the `only*` ones do not require that? should make this consistent.
/**
* Filters out any message event whose text does not start with an @-mention of the handling app.
*/
export function directMention(): Middleware<SlackEventMiddlewareArgs<'message'>> {
return async ({ message, context, next }) => {
// When context does not have a botUserId in it, then this middleware cannot perform its job. Bail immediately.
Expand Down

0 comments on commit dcccf9d

Please sign in to comment.