From 49ea0e512bc239dfc7b07b443c754bf271d72336 Mon Sep 17 00:00:00 2001 From: Stephen Kirby Date: Tue, 14 Jan 2025 22:41:56 +0000 Subject: [PATCH 1/5] reduced noise of outdated experiment callout in notifications doc --- docs/admin/monitoring/notifications/index.md | 87 +++++++++----------- 1 file changed, 37 insertions(+), 50 deletions(-) diff --git a/docs/admin/monitoring/notifications/index.md b/docs/admin/monitoring/notifications/index.md index aa54ad9c143dc..bb083332eaae7 100644 --- a/docs/admin/monitoring/notifications/index.md +++ b/docs/admin/monitoring/notifications/index.md @@ -3,23 +3,6 @@ Notifications are sent by Coder in response to specific internal events, such as a workspace being deleted or a user being created. -## Enable experiment - -In order to activate the notifications feature on Coder v2.15.X, you'll need to -enable the `notifications` experiment. Notifications are enabled by default -starting in v2.16.0. - -```bash -# Using the CLI flag -$ coder server --experiments=notifications - -# Alternatively, using the `CODER_EXPERIMENTS` environment variable -$ CODER_EXPERIMENTS=notifications coder server -``` - -More information on experiments can be found -[here](https://coder.com/docs/contributing/feature-stages#experimental-features). - ## Event Types Notifications are sent in response to internal events, to alert the affected @@ -171,33 +154,33 @@ Here is an example payload for Coder's webhook notification: ```json { - "_version": "1.0", - "msg_id": "88750cad-77d4-4663-8bc0-f46855f5019b", - "payload": { - "_version": "1.0", - "notification_name": "Workspace Deleted", - "user_id": "4ac34fcb-8155-44d5-8301-e3cd46e88b35", - "user_email": "danny@coder.com", - "user_name": "danny", - "user_username": "danny", - "actions": [ - { - "label": "View workspaces", - "url": "https://et23ntkhpueak.pit-1.try.coder.app/workspaces" - }, - { - "label": "View templates", - "url": "https://et23ntkhpueak.pit-1.try.coder.app/templates" - } - ], - "labels": { - "initiator": "danny", - "name": "my-workspace", - "reason": "initiated by user" - } - }, - "title": "Workspace \"my-workspace\" deleted", - "body": "Hi danny\n\nYour workspace my-workspace was deleted.\nThe specified reason was \"initiated by user (danny)\"." + "_version": "1.0", + "msg_id": "88750cad-77d4-4663-8bc0-f46855f5019b", + "payload": { + "_version": "1.0", + "notification_name": "Workspace Deleted", + "user_id": "4ac34fcb-8155-44d5-8301-e3cd46e88b35", + "user_email": "danny@coder.com", + "user_name": "danny", + "user_username": "danny", + "actions": [ + { + "label": "View workspaces", + "url": "https://et23ntkhpueak.pit-1.try.coder.app/workspaces" + }, + { + "label": "View templates", + "url": "https://et23ntkhpueak.pit-1.try.coder.app/templates" + } + ], + "labels": { + "initiator": "danny", + "name": "my-workspace", + "reason": "initiated by user" + } + }, + "title": "Workspace \"my-workspace\" deleted", + "body": "Hi danny\n\nYour workspace my-workspace was deleted.\nThe specified reason was \"initiated by user (danny)\"." } ``` @@ -267,12 +250,16 @@ To resume sending notifications, execute If notifications are not being delivered, use the following methods to troubleshoot: -1. Ensure notifications are being added to the `notification_messages` table -2. Review any error messages in the `status_reason` column, should an error have - occurred -3. Review the logs (search for the term `notifications`) for diagnostic - information
_If you do not see any relevant logs, set - `CODER_VERBOSE=true` or `--verbose` to output debug logs_ +1. Ensure notifications are being added to the `notification_messages` table +2. Review any error messages in the `status_reason` column, should an error + have occurred +3. Review the logs (search for the term `notifications`) for diagnostic + information
_If you do not see any relevant logs, set + `CODER_VERBOSE=true` or `--verbose` to output debug logs_ +4. If you are on version 2.15.x, notifications must be enabled using the + `notifications` + [experiment](https://coder.com/docs/contributing/feature-stages#experimental-features). + Notifications are enabled by default starting in v2.16.0. ## Internals From 9902373f98c1a58b9dcc7a973695534eae8bd7e8 Mon Sep 17 00:00:00 2001 From: EdwardAngert Date: Wed, 15 Jan 2025 16:45:17 +0000 Subject: [PATCH 2/5] make{lint,fmt} --- docs/admin/monitoring/notifications/index.md | 62 ++++++++++---------- 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/docs/admin/monitoring/notifications/index.md b/docs/admin/monitoring/notifications/index.md index bb083332eaae7..02c9314a5797e 100644 --- a/docs/admin/monitoring/notifications/index.md +++ b/docs/admin/monitoring/notifications/index.md @@ -154,33 +154,33 @@ Here is an example payload for Coder's webhook notification: ```json { - "_version": "1.0", - "msg_id": "88750cad-77d4-4663-8bc0-f46855f5019b", - "payload": { - "_version": "1.0", - "notification_name": "Workspace Deleted", - "user_id": "4ac34fcb-8155-44d5-8301-e3cd46e88b35", - "user_email": "danny@coder.com", - "user_name": "danny", - "user_username": "danny", - "actions": [ - { - "label": "View workspaces", - "url": "https://et23ntkhpueak.pit-1.try.coder.app/workspaces" - }, - { - "label": "View templates", - "url": "https://et23ntkhpueak.pit-1.try.coder.app/templates" - } - ], - "labels": { - "initiator": "danny", - "name": "my-workspace", - "reason": "initiated by user" - } - }, - "title": "Workspace \"my-workspace\" deleted", - "body": "Hi danny\n\nYour workspace my-workspace was deleted.\nThe specified reason was \"initiated by user (danny)\"." + "_version": "1.0", + "msg_id": "88750cad-77d4-4663-8bc0-f46855f5019b", + "payload": { + "_version": "1.0", + "notification_name": "Workspace Deleted", + "user_id": "4ac34fcb-8155-44d5-8301-e3cd46e88b35", + "user_email": "danny@coder.com", + "user_name": "danny", + "user_username": "danny", + "actions": [ + { + "label": "View workspaces", + "url": "https://et23ntkhpueak.pit-1.try.coder.app/workspaces" + }, + { + "label": "View templates", + "url": "https://et23ntkhpueak.pit-1.try.coder.app/templates" + } + ], + "labels": { + "initiator": "danny", + "name": "my-workspace", + "reason": "initiated by user" + } + }, + "title": "Workspace \"my-workspace\" deleted", + "body": "Hi danny\n\nYour workspace my-workspace was deleted.\nThe specified reason was \"initiated by user (danny)\"." } ``` @@ -250,13 +250,13 @@ To resume sending notifications, execute If notifications are not being delivered, use the following methods to troubleshoot: -1. Ensure notifications are being added to the `notification_messages` table -2. Review any error messages in the `status_reason` column, should an error +1. Ensure notifications are being added to the `notification_messages` table +2. Review any error messages in the `status_reason` column, should an error have occurred -3. Review the logs (search for the term `notifications`) for diagnostic +3. Review the logs (search for the term `notifications`) for diagnostic information
_If you do not see any relevant logs, set `CODER_VERBOSE=true` or `--verbose` to output debug logs_ -4. If you are on version 2.15.x, notifications must be enabled using the +4. If you are on version 2.15.x, notifications must be enabled using the `notifications` [experiment](https://coder.com/docs/contributing/feature-stages#experimental-features). Notifications are enabled by default starting in v2.16.0. From 78adb905683bc51150fa71fed674569fb4c1bd09 Mon Sep 17 00:00:00 2001 From: Stephen Kirby Date: Wed, 15 Jan 2025 17:47:32 +0000 Subject: [PATCH 3/5] remove experiment links --- docs/admin/monitoring/notifications/slack.md | 236 +++++++++---------- docs/admin/monitoring/notifications/teams.md | 85 ++++--- 2 files changed, 158 insertions(+), 163 deletions(-) diff --git a/docs/admin/monitoring/notifications/slack.md b/docs/admin/monitoring/notifications/slack.md index 58bf9338ea2ae..f60d8cfe91736 100644 --- a/docs/admin/monitoring/notifications/slack.md +++ b/docs/admin/monitoring/notifications/slack.md @@ -17,8 +17,7 @@ consistent between Slack and their Coder login. Before setting up Slack notifications, ensure that you have the following: - Administrator access to the Slack platform to create apps -- Coder platform v2.15.0 or greater with - [notifications enabled](./index.md#enable-experiment) for versions =v2.16.0 ## Create Slack Application @@ -34,9 +33,9 @@ To integrate Slack with Coder, follow these steps to create a Slack application: 3. Under "OAuth & Permissions", add the following OAuth scopes: - - `chat:write`: To send messages as the app. - - `users:read`: To find the user details. - - `users:read.email`: To find user emails. + - `chat:write`: To send messages as the app. + - `users:read`: To find the user details. + - `users:read.email`: To find user emails. 4. Install the app to your workspace and note down the **Bot User OAuth Token** from the "OAuth & Permissions" section. @@ -52,128 +51,130 @@ To build the server to receive webhooks and interact with Slack: 1. Initialize your project by running: - ```bash - npm init -y - ``` + ```bash + npm init -y + ``` 2. Install the Bolt library: - ```bash - npm install @slack/bolt - ``` + ```bash + npm install @slack/bolt + ``` 3. Create and edit the `app.js` file. Below is an example of the basic structure: - ```js - const { App, LogLevel, ExpressReceiver } = require("@slack/bolt"); - const bodyParser = require("body-parser"); - - const port = process.env.PORT || 6000; - - // Create a Bolt Receiver - const receiver = new ExpressReceiver({ - signingSecret: process.env.SLACK_SIGNING_SECRET, - }); - receiver.router.use(bodyParser.json()); - - // Create the Bolt App, using the receiver - const app = new App({ - token: process.env.SLACK_BOT_TOKEN, - logLevel: LogLevel.DEBUG, - receiver, - }); - - receiver.router.post("/v1/webhook", async (req, res) => { - try { - if (!req.body) { - return res.status(400).send("Error: request body is missing"); - } - - const { title, body } = req.body; - if (!title || !body) { - return res.status(400).send('Error: missing fields: "title", or "body"'); - } - - const payload = req.body.payload; - if (!payload) { - return res.status(400).send('Error: missing "payload" field'); - } - - const { user_email, actions } = payload; - if (!user_email || !actions) { - return res - .status(400) - .send('Error: missing fields: "user_email", "actions"'); - } - - // Get the user ID using Slack API - const userByEmail = await app.client.users.lookupByEmail({ - email: user_email, - }); - - const slackMessage = { - channel: userByEmail.user.id, - text: body, - blocks: [ - { - type: "header", - text: { type: "plain_text", text: title }, - }, - { - type: "section", - text: { type: "mrkdwn", text: body }, - }, - ], - }; - - // Add action buttons if they exist - if (actions && actions.length > 0) { - slackMessage.blocks.push({ - type: "actions", - elements: actions.map((action) => ({ - type: "button", - text: { type: "plain_text", text: action.label }, - url: action.url, - })), - }); - } - - // Post message to the user on Slack - await app.client.chat.postMessage(slackMessage); - - res.status(204).send(); - } catch (error) { - console.error("Error sending message:", error); - res.status(500).send(); - } - }); - - // Acknowledge clicks on link_button, otherwise Slack UI - // complains about missing events. - app.action("button_click", async ({ body, ack, say }) => { - await ack(); // no specific action needed - }); - - // Start the Bolt app - (async () => { - await app.start(port); - console.log("⚡️ Coder Slack bot is running!"); - })(); - ``` + ```js + const { App, LogLevel, ExpressReceiver } = require("@slack/bolt"); + const bodyParser = require("body-parser"); + + const port = process.env.PORT || 6000; + + // Create a Bolt Receiver + const receiver = new ExpressReceiver({ + signingSecret: process.env.SLACK_SIGNING_SECRET, + }); + receiver.router.use(bodyParser.json()); + + // Create the Bolt App, using the receiver + const app = new App({ + token: process.env.SLACK_BOT_TOKEN, + logLevel: LogLevel.DEBUG, + receiver, + }); + + receiver.router.post("/v1/webhook", async (req, res) => { + try { + if (!req.body) { + return res.status(400).send("Error: request body is missing"); + } + + const { title, body } = req.body; + if (!title || !body) { + return res + .status(400) + .send('Error: missing fields: "title", or "body"'); + } + + const payload = req.body.payload; + if (!payload) { + return res.status(400).send('Error: missing "payload" field'); + } + + const { user_email, actions } = payload; + if (!user_email || !actions) { + return res + .status(400) + .send('Error: missing fields: "user_email", "actions"'); + } + + // Get the user ID using Slack API + const userByEmail = await app.client.users.lookupByEmail({ + email: user_email, + }); + + const slackMessage = { + channel: userByEmail.user.id, + text: body, + blocks: [ + { + type: "header", + text: { type: "plain_text", text: title }, + }, + { + type: "section", + text: { type: "mrkdwn", text: body }, + }, + ], + }; + + // Add action buttons if they exist + if (actions && actions.length > 0) { + slackMessage.blocks.push({ + type: "actions", + elements: actions.map((action) => ({ + type: "button", + text: { type: "plain_text", text: action.label }, + url: action.url, + })), + }); + } + + // Post message to the user on Slack + await app.client.chat.postMessage(slackMessage); + + res.status(204).send(); + } catch (error) { + console.error("Error sending message:", error); + res.status(500).send(); + } + }); + + // Acknowledge clicks on link_button, otherwise Slack UI + // complains about missing events. + app.action("button_click", async ({ body, ack, say }) => { + await ack(); // no specific action needed + }); + + // Start the Bolt app + (async () => { + await app.start(port); + console.log("⚡️ Coder Slack bot is running!"); + })(); + ``` 4. Set environment variables to identify the Slack app: - ```bash - export SLACK_BOT_TOKEN=xoxb-... - export SLACK_SIGNING_SECRET=0da4b... - ``` + ```bash + export SLACK_BOT_TOKEN=xoxb-... + export SLACK_SIGNING_SECRET=0da4b... + ``` 5. Start the web application by running: - ```bash - node app.js - ``` + ```bash + node app.js + ``` ## Enable Interactivity in Slack @@ -192,11 +193,8 @@ must respond appropriately. ## Enable Webhook Integration in Coder -To enable webhook integration in Coder, ensure the "notifications" -[experiment is activated](./index.md#enable-experiment) (only required in -v2.15.X). - -Then, define the POST webhook endpoint matching the deployed Slack bot: +To enable webhook integration in Coder, define the POST webhook endpoint +matching the deployed Slack bot: ```bash export CODER_NOTIFICATIONS_WEBHOOK_ENDPOINT=http://localhost:6000/v1/webhook` diff --git a/docs/admin/monitoring/notifications/teams.md b/docs/admin/monitoring/notifications/teams.md index 5846cfc83bc48..77483aae27534 100644 --- a/docs/admin/monitoring/notifications/teams.md +++ b/docs/admin/monitoring/notifications/teams.md @@ -15,7 +15,7 @@ Before setting up Microsoft Teams notifications, ensure that you have the following: - Administrator access to the Teams platform -- Coder platform with [notifications enabled](./index.md#enable-experiment) +- Coder platform >=v2.16.0 ## Build Teams Workflow @@ -36,44 +36,44 @@ The process of setting up a Teams workflow consists of three key steps: ```json { - "type": "object", - "properties": { - "_version": { - "type": "string" - }, - "payload": { - "type": "object", - "properties": { - "_version": { - "type": "string" - }, - "user_email": { - "type": "string" - }, - "actions": { - "type": "array", - "items": { - "type": "object", - "properties": { - "label": { - "type": "string" - }, - "url": { - "type": "string" - } - }, - "required": ["label", "url"] - } - } - } - }, - "title": { - "type": "string" - }, - "body": { - "type": "string" - } - } + "type": "object", + "properties": { + "_version": { + "type": "string" + }, + "payload": { + "type": "object", + "properties": { + "_version": { + "type": "string" + }, + "user_email": { + "type": "string" + }, + "actions": { + "type": "array", + "items": { + "type": "object", + "properties": { + "label": { + "type": "string" + }, + "url": { + "type": "string" + } + }, + "required": ["label", "url"] + } + } + } + }, + "title": { + "type": "string" + }, + "body": { + "type": "string" + } + } } ``` @@ -133,11 +133,8 @@ The process of setting up a Teams workflow consists of three key steps: ## Enable Webhook Integration -To enable webhook integration in Coder, ensure the "notifications" -[experiment is activated](./index.md#enable-experiment) (only required in -v2.15.X). - -Then, define the POST webhook endpoint created by your Teams workflow: +To enable webhook integration in Coder, define the POST webhook endpoint created +by your Teams workflow: ```bash export CODER_NOTIFICATIONS_WEBHOOK_ENDPOINT=https://prod-16.eastus.logic.azure.com:443/workflows/f8fbe3e8211e4b638...` From 3c1e96b7fb24203cbf62da6e8788cf05142dd067 Mon Sep 17 00:00:00 2001 From: EdwardAngert Date: Tue, 21 Jan 2025 21:38:28 +0000 Subject: [PATCH 4/5] re-lint and fmt --- docs/admin/monitoring/notifications/slack.md | 142 +++++++++---------- docs/admin/monitoring/notifications/teams.md | 76 +++++----- 2 files changed, 109 insertions(+), 109 deletions(-) diff --git a/docs/admin/monitoring/notifications/slack.md b/docs/admin/monitoring/notifications/slack.md index f60d8cfe91736..e7cad847faad4 100644 --- a/docs/admin/monitoring/notifications/slack.md +++ b/docs/admin/monitoring/notifications/slack.md @@ -72,94 +72,94 @@ To build the server to receive webhooks and interact with Slack: // Create a Bolt Receiver const receiver = new ExpressReceiver({ - signingSecret: process.env.SLACK_SIGNING_SECRET, + signingSecret: process.env.SLACK_SIGNING_SECRET, }); receiver.router.use(bodyParser.json()); // Create the Bolt App, using the receiver const app = new App({ - token: process.env.SLACK_BOT_TOKEN, - logLevel: LogLevel.DEBUG, - receiver, + token: process.env.SLACK_BOT_TOKEN, + logLevel: LogLevel.DEBUG, + receiver, }); receiver.router.post("/v1/webhook", async (req, res) => { - try { - if (!req.body) { - return res.status(400).send("Error: request body is missing"); - } - - const { title, body } = req.body; - if (!title || !body) { - return res - .status(400) - .send('Error: missing fields: "title", or "body"'); - } - - const payload = req.body.payload; - if (!payload) { - return res.status(400).send('Error: missing "payload" field'); - } - - const { user_email, actions } = payload; - if (!user_email || !actions) { - return res - .status(400) - .send('Error: missing fields: "user_email", "actions"'); - } - - // Get the user ID using Slack API - const userByEmail = await app.client.users.lookupByEmail({ - email: user_email, - }); - - const slackMessage = { - channel: userByEmail.user.id, - text: body, - blocks: [ - { - type: "header", - text: { type: "plain_text", text: title }, - }, - { - type: "section", - text: { type: "mrkdwn", text: body }, - }, - ], - }; - - // Add action buttons if they exist - if (actions && actions.length > 0) { - slackMessage.blocks.push({ - type: "actions", - elements: actions.map((action) => ({ - type: "button", - text: { type: "plain_text", text: action.label }, - url: action.url, - })), - }); - } - - // Post message to the user on Slack - await app.client.chat.postMessage(slackMessage); - - res.status(204).send(); - } catch (error) { - console.error("Error sending message:", error); - res.status(500).send(); - } + try { + if (!req.body) { + return res.status(400).send("Error: request body is missing"); + } + + const { title, body } = req.body; + if (!title || !body) { + return res + .status(400) + .send('Error: missing fields: "title", or "body"'); + } + + const payload = req.body.payload; + if (!payload) { + return res.status(400).send('Error: missing "payload" field'); + } + + const { user_email, actions } = payload; + if (!user_email || !actions) { + return res + .status(400) + .send('Error: missing fields: "user_email", "actions"'); + } + + // Get the user ID using Slack API + const userByEmail = await app.client.users.lookupByEmail({ + email: user_email, + }); + + const slackMessage = { + channel: userByEmail.user.id, + text: body, + blocks: [ + { + type: "header", + text: { type: "plain_text", text: title }, + }, + { + type: "section", + text: { type: "mrkdwn", text: body }, + }, + ], + }; + + // Add action buttons if they exist + if (actions && actions.length > 0) { + slackMessage.blocks.push({ + type: "actions", + elements: actions.map((action) => ({ + type: "button", + text: { type: "plain_text", text: action.label }, + url: action.url, + })), + }); + } + + // Post message to the user on Slack + await app.client.chat.postMessage(slackMessage); + + res.status(204).send(); + } catch (error) { + console.error("Error sending message:", error); + res.status(500).send(); + } }); // Acknowledge clicks on link_button, otherwise Slack UI // complains about missing events. app.action("button_click", async ({ body, ack, say }) => { - await ack(); // no specific action needed + await ack(); // no specific action needed }); // Start the Bolt app (async () => { - await app.start(port); - console.log("⚡️ Coder Slack bot is running!"); + await app.start(port); + console.log("⚡️ Coder Slack bot is running!"); })(); ``` diff --git a/docs/admin/monitoring/notifications/teams.md b/docs/admin/monitoring/notifications/teams.md index 77483aae27534..0b874a997c54a 100644 --- a/docs/admin/monitoring/notifications/teams.md +++ b/docs/admin/monitoring/notifications/teams.md @@ -36,44 +36,44 @@ The process of setting up a Teams workflow consists of three key steps: ```json { - "type": "object", - "properties": { - "_version": { - "type": "string" - }, - "payload": { - "type": "object", - "properties": { - "_version": { - "type": "string" - }, - "user_email": { - "type": "string" - }, - "actions": { - "type": "array", - "items": { - "type": "object", - "properties": { - "label": { - "type": "string" - }, - "url": { - "type": "string" - } - }, - "required": ["label", "url"] - } - } - } - }, - "title": { - "type": "string" - }, - "body": { - "type": "string" - } - } + "type": "object", + "properties": { + "_version": { + "type": "string" + }, + "payload": { + "type": "object", + "properties": { + "_version": { + "type": "string" + }, + "user_email": { + "type": "string" + }, + "actions": { + "type": "array", + "items": { + "type": "object", + "properties": { + "label": { + "type": "string" + }, + "url": { + "type": "string" + } + }, + "required": ["label", "url"] + } + } + } + }, + "title": { + "type": "string" + }, + "body": { + "type": "string" + } + } } ``` From bec9b3658e5e79c925d01a9d791535b77cba9978 Mon Sep 17 00:00:00 2001 From: EdwardAngert Date: Wed, 22 Jan 2025 22:21:44 +0000 Subject: [PATCH 5/5] copy edit, link fix --- docs/admin/monitoring/notifications/index.md | 21 ++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/docs/admin/monitoring/notifications/index.md b/docs/admin/monitoring/notifications/index.md index 02c9314a5797e..a7eeab44d4b79 100644 --- a/docs/admin/monitoring/notifications/index.md +++ b/docs/admin/monitoring/notifications/index.md @@ -250,16 +250,17 @@ To resume sending notifications, execute If notifications are not being delivered, use the following methods to troubleshoot: -1. Ensure notifications are being added to the `notification_messages` table -2. Review any error messages in the `status_reason` column, should an error - have occurred -3. Review the logs (search for the term `notifications`) for diagnostic - information
_If you do not see any relevant logs, set - `CODER_VERBOSE=true` or `--verbose` to output debug logs_ -4. If you are on version 2.15.x, notifications must be enabled using the +1. Ensure notifications are being added to the `notification_messages` table. +1. Review any available error messages in the `status_reason` column +1. Review the logs. Search for the term `notifications` for diagnostic information. + + - If you do not see any relevant logs, set + `CODER_VERBOSE=true` or `--verbose` to output debug logs. +1. If you are on version 2.15.x, notifications must be enabled using the `notifications` - [experiment](https://coder.com/docs/contributing/feature-stages#experimental-features). - Notifications are enabled by default starting in v2.16.0. + [experiment](../../../contributing/feature-stages.md#experimental-features). + + Notifications are enabled by default in Coder v2.16.0 and later. ## Internals @@ -270,7 +271,7 @@ concurrency. All messages are stored in the `notification_messages` table. -Messages older than 7 days are deleted. +Messages older than seven days are deleted. ### Message States