From 600ba6912fc18ac9bb1fa389957113b9320cfede Mon Sep 17 00:00:00 2001 From: Alexandre Fauquette <45398769+alexfauquette@users.noreply.github.com> Date: Mon, 25 Sep 2023 10:46:35 +0200 Subject: [PATCH] [core] Simplify docs feedback interaction (#39075) Co-authored-by: Jan Potoms <2109932+Janpot@users.noreply.github.com> Co-authored-by: Olivier Tassinari --- .gitignore | 2 + .../modules/components/AppLayoutDocsFooter.js | 1 + netlify/functions/feedback-management.js | 196 ++++++++++++------ 3 files changed, 140 insertions(+), 59 deletions(-) diff --git a/.gitignore b/.gitignore index 5722b6358f7885..d8f84f0b8b32f9 100644 --- a/.gitignore +++ b/.gitignore @@ -30,6 +30,8 @@ /test/regressions/screenshots /tmp .next +# created by netlify dev (to perform local debug) +.netlify build node_modules package-lock.json diff --git a/docs/src/modules/components/AppLayoutDocsFooter.js b/docs/src/modules/components/AppLayoutDocsFooter.js index 7ea0fb57e47532..ba555574e4733c 100644 --- a/docs/src/modules/components/AppLayoutDocsFooter.js +++ b/docs/src/modules/components/AppLayoutDocsFooter.js @@ -109,6 +109,7 @@ async function postFeedbackOnSlack(data) { currentLocationURL: window.location.href, commmentSectionURL: `${window.location.origin}${window.location.pathname}#${commentedSection.hash}`, commmentSectionTitle: commentedSection.text, + githubRepo: process.env.SOURCE_CODE_REPO, }; if (!comment || comment.length < 10) { return 'ignored'; diff --git a/netlify/functions/feedback-management.js b/netlify/functions/feedback-management.js index 0072704a92142e..ce176903797c64 100644 --- a/netlify/functions/feedback-management.js +++ b/netlify/functions/feedback-management.js @@ -1,5 +1,4 @@ const querystring = require('node:querystring'); -const { WebClient } = require('@slack/web-api'); const { App, AwsLambdaReceiver } = require('@slack/bolt'); const { JWT } = require('google-auth-library'); const { sheets } = require('@googleapis/sheets'); @@ -40,68 +39,87 @@ const awsLambdaReceiver = new AwsLambdaReceiver({ const app = new App({ token: process.env.SLACK_BOT_TOKEN, receiver: awsLambdaReceiver, + signingSecret: process.env.SLACK_SIGNING_SECRET, }); // Define slack actions to answer - -app.shortcut('delete_action', async ({ ack, body, client }) => { - await ack(); - await client.chat.delete({ - channel: body.channel.id, - ts: body.message_ts, - as_user: true, - token: process.env.SLACK_BOT_TOKEN, - }); +app.action('delete_action', async ({ ack, body, client, logger }) => { + try { + await ack(); + + const { + user: { username }, + channel: { id: channelId }, + message, + actions: [{ value }], + } = body; + + const { comment, currentLocationURL = '', commmentSectionURL = '' } = JSON.parse(value); + + const googleAuth = new JWT({ + email: 'service-account-804@docs-feedbacks.iam.gserviceaccount.com', + key: process.env.G_SHEET_TOKEN.replace(/\\n/g, '\n'), + scopes: ['https://www.googleapis.com/auth/spreadsheets'], + }); + const service = sheets({ version: 'v4', auth: googleAuth }); + + await service.spreadsheets.values.append({ + spreadsheetId: spreadSheetsIds.forLater, + range: 'Deleted messages!A:D', + valueInputOption: 'USER_ENTERED', + resource: { + values: [[username, comment, currentLocationURL, commmentSectionURL]], + }, + }); + await client.chat.delete({ + channel: channelId, + ts: message.ts, + as_user: true, + token: process.env.SLACK_BOT_TOKEN, + }); + } catch (error) { + logger.error(JSON.stringify(error, null, 2)); + } }); -app.shortcut('save_message', async ({ ack, body, client }) => { - await ack(); - const { - user: { username }, - channel: { id: channelId }, - // eslint-disable-next-line @typescript-eslint/naming-convention - message_ts, - message, - } = body; - const elements = message?.blocks?.[0]?.elements; - - const quote = message?.text - .split('\n\nsent from')[0] - .split('\n\n') - .slice(1) - .join('\n\n') - .replace(/>/g, ''); - - const links = elements[2].elements - .filter((element) => element.type === 'link') - .map((element) => element.url); - - const googleAuth = new JWT({ - email: 'service-account-804@docs-feedbacks.iam.gserviceaccount.com', - key: process.env.G_SHEET_TOKEN.replace(/\\n/g, '\n'), - scopes: ['https://www.googleapis.com/auth/spreadsheets'], - }); - const service = sheets({ version: 'v4', auth: googleAuth }); - - await service.spreadsheets.values.append({ - spreadsheetId: spreadSheetsIds.forLater, - range: 'Sheet1!A:D', - valueInputOption: 'USER_ENTERED', - resource: { - values: [[username, quote, links[0] ?? '', links[1] ?? '']], - }, - }); - await client.chat.postMessage({ - channel: channelId, - thread_ts: message_ts, - as_user: true, - text: `Saved in `, - }); +app.action('save_message', async ({ ack, body, client, logger }) => { + try { + await ack(); + const { + user: { username }, + channel: { id: channelId }, + message, + actions: [{ value }], + } = body; + + const { comment, currentLocationURL = '', commmentSectionURL = '' } = JSON.parse(value); + + const googleAuth = new JWT({ + email: 'service-account-804@docs-feedbacks.iam.gserviceaccount.com', + key: process.env.G_SHEET_TOKEN.replace(/\\n/g, '\n'), + scopes: ['https://www.googleapis.com/auth/spreadsheets'], + }); + const service = sheets({ version: 'v4', auth: googleAuth }); + + await service.spreadsheets.values.append({ + spreadsheetId: spreadSheetsIds.forLater, + range: 'Sheet1!A:D', + valueInputOption: 'USER_ENTERED', + resource: { + values: [[username, comment, currentLocationURL, commmentSectionURL]], + }, + }); + await client.chat.postMessage({ + channel: channelId, + thread_ts: message.ts, + as_user: true, + text: `Saved in `, + }); + } catch (error) { + logger.error(JSON.stringify(error, null, 2)); + } }); -// Slack API -const slackClient = new WebClient(process.env.SLACK_BOT_TOKEN); - /** * @param {object} event * @param {object} context @@ -112,7 +130,7 @@ exports.handler = async (event, context, callback) => { } try { const { payload } = querystring.parse(event.body); - const data = JSON.parse(decodeURIComponent(payload)); + const data = JSON.parse(payload); if (data.callback_id === 'send_feedback') { // We send the feedback to the appopiate slack channel @@ -122,6 +140,7 @@ exports.handler = async (event, context, callback) => { currentLocationURL, commmentSectionURL: inCommmentSectionURL, commmentSectionTitle, + githubRepo, } = data; const isDesignFeedback = inCommmentSectionURL.includes('#new-docs-api-feedback'); @@ -137,9 +156,68 @@ exports.handler = async (event, context, callback) => { }`, ].join('\n\n'); - await slackClient.chat.postMessage({ + const githubNewIssueParams = new URLSearchParams({ + title: '[ ] Docs feedback', + body: `Feedback received: +${comment} + +from ${commmentSectionURL} +`, + }); + + await app.client.chat.postMessage({ channel: getSlackChannelId(currentLocationURL, { isDesignFeedback }), - text: simpleSlackMessage, + text: simpleSlackMessage, // Fallback for notification + blocks: [ + { + type: 'section', + text: { + type: 'mrkdwn', + text: simpleSlackMessage, + }, + }, + { + type: 'actions', + elements: [ + { + type: 'button', + text: { + type: 'plain_text', + text: 'Create issue', + emoji: true, + }, + url: `${githubRepo}/issues/new?${githubNewIssueParams}`, + }, + { + type: 'button', + text: { + type: 'plain_text', + text: 'Save', + }, + value: JSON.stringify({ + comment, + currentLocationURL, + commmentSectionURL, + }), + action_id: 'save_message', + }, + { + type: 'button', + text: { + type: 'plain_text', + text: 'Delete', + }, + value: JSON.stringify({ + comment, + currentLocationURL, + commmentSectionURL, + }), + style: 'danger', + action_id: 'delete_action', + }, + ], + }, + ], as_user: true, unfurl_links: false, unfurl_media: false,