Skip to content

Commit

Permalink
Merge pull request #177 from shanedewael/make-various-docs-edits
Browse files Browse the repository at this point in the history
Edits to Getting Started and Basic Info docs
  • Loading branch information
shaydewael authored May 3, 2019
2 parents b58bbe2 + b666c86 commit 47244b0
Show file tree
Hide file tree
Showing 23 changed files with 231 additions and 3,293 deletions.
1 change: 1 addition & 0 deletions docs/.gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
_site
Gemfile.lock
.env
72 changes: 39 additions & 33 deletions docs/_advanced/authorization.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,49 +5,55 @@ order: 2
---

<div class="section-content">
Custom apps installed on just one team can define `token` (and optionally `botId` and `botUserId`) at the time of app initialization. But in the case you want to handle multiple tokens, authorization functions can be passed in rather than a `token`. Authorization functions must have `source` and `body` as parameters and are expected to return a Promise that resolves to an `AuthorizeResult`.
Authorization is the process of deciding which Slack credentials (such as a bot token) should be available while processing a specific incoming event.

* `source` will be provided to an authorize function and will always contain a `teamId`, and will contain an `enterpriseId`, a `userId`, and a `conversationId` when they exist for the event.
Custom apps installed on a single workspace can simply use the `token` option at the time of `App` initialization. However, when your app needs to handle multiple tokens, such as cases where it will be installed on multiple workspaces or needs access to more than one user token, the `authorize` option should be used instead.

* `body` will be provided as the request body of the event.
The `authorize` option should be set to a function that takes an event source as its input, and should return a Promise for an object containing the authorized credentials. The source contains information about who and where the event is coming from using properties like `teamId` (always available), `userId`, `conversationId`, and `enterpriseId`.

After you app is initialized, the authorization function will be run as an incoming event is passed from the Receiver. This will give opportunity to add the proper token and bot user and app IDs (which are used for the builtin `ignoreSelf` middleware).
The authorized credentials should also have a few specific properties: `botToken`, `userToken`, `botId` (required for app to ignore messages from itself), and `botUserId`. You can also include any other properties you'd like to make available on the [`context`](#context).

You should always provide either one or both of the `botToken` and `userToken` properties. One of them is necessary to make helpers like `say()` work. If they are both given, then `botToken` will take precedence.
</div>

```javascript
const app = new App({ authorize: authorizeFn, signingSecret: process.env.SLACK_SIGNING_SECRET });

// NOTE: This is for demonstration purposes only.
// All sensitive data should be stored in a secure database
let teamInfo = {
'E1234A12AB': {
'T12345': {
botToken: 'xoxb-123abc',
botId: 'B1251',
botUserId: 'U12385'
}
// Assuming this app only uses bot tokens, the following object represents a model for storing the credentials as the app is installed into multiple workspaces.

const installations = [
{
enterpriseId: 'E1234A12AB',
teamId: 'T12345',
botToken: 'xoxb-123abc',
botId: 'B1251',
botUserId: 'U12385',
},
'E5678B23CD': {
'T77712': {
botToken: 'xoxb-102anc',
botId: 'B5910',
botUserId: 'U1239'
{
teamId: 'T77712',
botToken: 'xoxb-102anc',
botId: 'B5910',
botUserId: 'U1239',
},
];

const authorizeFn = async ({ teamId, enterpriseId }) => {
// Fetch team info from database
for (const team in installations) {
// Check for matching teamId and enterpriseId in the installations array
if ((team.teamId === teamId) && (team.enterpriseId === enterpriseId)) {
// This is a match. Use these installaton credentials.
return {
// You could also set userToken instead
botToken: team.botToken,
botId: team.botId,
botUserId: team.botUserId
};
}
}
}

const authorizeFn = async (source, body) => {
// Fetch team info from database. You could also set userToken instead.
const fetchAuthorizedTeam = new Promise((resolve, reject) => {
teamInfo[source.enterpriseId][source.teamId] !== undefined ?
Promise.resolve(teamInfo[source.enterpriseId][source.teamId]) :
Promise.reject();
});

const authorizedTeam = await fetchAuthorizedTeam;

return () => ({
botToken: authorizedTeam.botToken,
botId: authorizedTeam.botId,
botUserId: authorizedTeam.botUserId,
});

throw new Error('No matching authorizations');
}
```
4 changes: 2 additions & 2 deletions docs/_advanced/error_handling.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ order: 1
---

<div class="section-content">
If an error occurs in a listener middleware, it’s recommended you handle it directly. However, there are cases where errors may occur after your listener middleware has returned (like `say()` and `respond()`, or not calling `ack()`). By default, these errors will be logged to the console. To handle them yourself, you can attach a global error handler to your app using the `App.prototype.error(fn)` method.
If an error occurs in a listener, it’s recommended you handle it directly. However, there are cases where errors may occur after your listener middleware has returned (such as `say()`, `respond()`, or not calling `ack()`). By default, these errors will be logged to the console. To handle them yourself, you can attach a global error handler to your app using the `error(fn)` method.

If you want more control over errors, it’s advised to use the [`chat.postMessage`](https://api.slack.com/methods/chat.postMessage) method attached to your app under the `client` key. This returns a `Promise` that can be caught to handle the error.
If you want more control over errors, it’s advised to use the [`chat.postMessage`](https://api.slack.com/methods/chat.postMessage) method attached to your app under the `client` key (instead of `say()` or `respond()`). This returns a `Promise` that can be caught to handle the error.
</div>

```javascript
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
---
title: Acknowleding actions
slug: action-acknowledge
order: 6
title: Acknowledging requests
slug: acknowledge
order: 7
---

<div class="section-content">
Actions must **always** be acknowledged using the `ack()` function. This lets Slack know that the action was received and updates the Slack user interface accordingly. Depending on the type of action, your acknowledgement may be different. For example, when responding to a dialog submission you will call `ack()` with validation errors if the submission contains errors, or with no parameters if the submission is valid.
Actions, commands, and options requests must **always** be acknowledged using the `ack()` function. This lets Slack know that the event was received and updates the Slack user interface accordingly. Depending on the type of event, your acknowledgement may be different. For example, when acknowledging a dialog submission you will call `ack()` with validation errors if the submission contains errors, or with no parameters if the submission is valid.

We recommend calling `ack()` right away before sending a new message or fetching information from your database since you only have 3 seconds to respond.
</div>
Expand All @@ -28,4 +28,4 @@ app.action({ callback_id: 'ticket_submit' }, ({ action, ack }) => {
});
}
});
```
```
2 changes: 1 addition & 1 deletion docs/_basic/listening_actions.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ Your app can listen to user actions like button clicks, menu selects, and messag

Actions can be filtered on an `action_id` of type string or RegExp. `action_id`s act as unique identifiers for interactive components on the Slack platform.

You’ll notice in all `action()` examples, `ack()` is used. It is required to call the `ack()` function within an action listener to acknowledge the event was received from Slack. This is discussed in the [acknowledging actions section](#action-acknowledging).
You’ll notice in all `action()` examples, `ack()` is used. It is required to call the `ack()` function within an action listener to acknowledge the event was received from Slack. This is discussed in the [acknowledging requests section](#acknowledge).

</div>

Expand Down
6 changes: 3 additions & 3 deletions docs/_basic/listening_events.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,9 @@ You can filter on subtypes of events by using the built-in `matchEventSubtype()`
</div>

```javascript
// event('message') is the same as message()
app.message(matchEventSubtype('bot_message'), ({ event }) => {
console.log(`The bot user ${event.user} said ${event.text}`);
// Matches all messages from bot users
app.message(subtype('bot_message'), ({ message }) => {
console.log(`The bot user ${message.user} said ${message.text}`);
});
```

Expand Down
4 changes: 3 additions & 1 deletion docs/_basic/listening_responding_commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ order: 8
<div class="section-content">
Your app can use the `command()` method to listen to incoming slash command payloads. The method requires a `commandName` of type string.

Similar to actions, there are two ways to respond to slash command requests. The first way is to use `say()`, which accepts a string or JSON payload. The second is `respond()` which uses the `response_url`.
Commands must be acknowledged with `ack()` to inform Slack your app has received the event.

There are two ways to respond to slash commands. The first way is to use `say()`, which accepts a string or JSON payload. The second is `respond()` which is a utility for the `response_url`. These are explained in more depth in the [responding to actions](#action-respond) section.
</div>

```javascript
Expand Down
7 changes: 4 additions & 3 deletions docs/_basic/responding_actions.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
title: Responding to actions
slug: action-respond
order: 7
order: 6
---

<div class="section-content">
Expand All @@ -12,7 +12,8 @@ The second way to respond to actions is using `respond()`, which is a simple uti

```javascript
// Your middleware will be called every time an interactive component with the action_id “approve_button” is triggered
app.action('approve_button', async ({ ack, say }) => {
app.action('approve_button', ({ ack, say }) => {
// Acknowledge action request
ack();
say('Request approved 👍');
});
Expand All @@ -29,7 +30,7 @@ Since `respond()` is a utility for calling the `response_url`, it behaves in the

```javascript
// Listens to actions triggered with action_id of “user_select”
app.action('user_choice', async ({ action, ack, respond }) => {
app.action('user_choice', ({ action, ack, respond }) => {
ack();
respond(`You selected <@${action.selected_user}>`);
});
Expand Down
2 changes: 1 addition & 1 deletion docs/_basic/web_api.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ app.message('wake me up', async ({ message, context }) => {
try {
// Call the chat.scheduleMessage method with a token
const result = await app.client.chat.scheduleMessage({
// The token you used to initalize your app is placed in `context`
// The token you used to initalize your app is stored in the `context` object
token: context.botToken,
channel: message.channel.id,
post_at: whenSeptemberEnds,
Expand Down
Binary file removed docs/_site/assets/basic-information-page.png
Binary file not shown.
Binary file removed docs/_site/assets/bolt-logo.png
Binary file not shown.
Binary file removed docs/_site/assets/bot-token.png
Binary file not shown.
Loading

0 comments on commit 47244b0

Please sign in to comment.