Przeglądaj źródła

Merge branch 'feat/growi-bot' into imprv/gw5556-show-proxy-url

kaori 5 lat temu
rodzic
commit
b1bdfb8f71

+ 2 - 1
packages/slack/src/index.ts

@@ -9,6 +9,7 @@ export const supportedGrowiCommands: string[] = [
 
 export * from './interfaces/growi-command';
 export * from './models/errors';
-export * from './utils/slash-command-parser';
 export * from './middlewares/verification-slack-request';
 export * from './utils/block-creater';
+export * from './utils/slash-command-parser';
+export * from './utils/webclient-factory';

+ 7 - 0
packages/slack/src/utils/webclient-factory.ts

@@ -0,0 +1,7 @@
+import { LogLevel, WebClient } from '@slack/web-api';
+
+const isProduction = process.env.NODE_ENV === 'production';
+
+export const generateWebClient = (botToken: string): WebClient => {
+  return new WebClient(botToken, { logLevel: isProduction ? LogLevel.DEBUG : LogLevel.INFO });
+};

+ 28 - 2
packages/slackbot-proxy/src/controllers/slack.ts

@@ -4,7 +4,7 @@ import {
 
 import axios from 'axios';
 
-import { parseSlashCommand } from '@growi/slack';
+import { generateMarkdownSectionBlock, generateWebClient, parseSlashCommand } from '@growi/slack';
 import { Installation } from '~/entities/installation';
 
 import { InstallationRepository } from '~/repositories/installation';
@@ -106,7 +106,7 @@ export class SlackCtrl {
     const installation = await this.installationRepository.findByTeamIdOrEnterpriseId(installationId!);
     const relations = await this.relationRepository.find({ installation: installation?.id });
 
-    await relations.map((relation: Relation) => {
+    const promises = relations.map((relation: Relation) => {
       // generate API URL
       const url = new URL('/_api/v3/slack-bot/commands', relation.growiUri);
       return axios.post(url.toString(), {
@@ -115,6 +115,32 @@ export class SlackCtrl {
         growiCommand,
       });
     });
+
+    // pickup PromiseRejectedResult only
+    const results = await Promise.allSettled(promises);
+    const rejectedResults: PromiseRejectedResult[] = results.filter((result): result is PromiseRejectedResult => result.status === 'rejected');
+
+    if (rejectedResults.length > 0) {
+      const botToken = installation?.data.bot?.token;
+
+      // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+      const client = generateWebClient(botToken!);
+
+      try {
+        client.chat.postEphemeral({
+          text: 'Error occured.',
+          channel: body.channel_id,
+          user: body.user_id,
+          blocks: [
+            generateMarkdownSectionBlock('*Error occured:*'),
+            ...rejectedResults.map(result => generateMarkdownSectionBlock(result.reason.toString())),
+          ],
+        });
+      }
+      catch (err) {
+        logger.error(err);
+      }
+    }
   }
 
   @Post('/interactions')

+ 3 - 3
src/client/js/components/Admin/SlackIntegration/CustomBotWithoutProxyIntegrationCard.jsx

@@ -23,7 +23,7 @@ const CustomBotWithoutProxyIntegrationCard = (props) => {
       </div>
 
       <div className="text-center w-25">
-        {props.isSetupSlackBot && (
+        {props.isSlackScopeSet && (
         <div className="mt-5">
           <p className="text-success small">
             <i className="fa fa-check mr-1" />
@@ -32,7 +32,7 @@ const CustomBotWithoutProxyIntegrationCard = (props) => {
           <hr className="align-self-center admin-border-success border-success"></hr>
         </div>
           )}
-        {!props.isSetupSlackBot && (
+        {!props.isSlackScopeSet && (
         <div className="mt-4">
           <small
             className="text-secondary m-0"
@@ -57,7 +57,7 @@ const CustomBotWithoutProxyIntegrationCard = (props) => {
 CustomBotWithoutProxyIntegrationCard.propTypes = {
   siteName: PropTypes.string.isRequired,
   slackWSNameInWithoutProxy: PropTypes.string,
-  isSetupSlackBot: PropTypes.bool.isRequired,
+  isSlackScopeSet: PropTypes.bool.isRequired,
 };
 
 export default CustomBotWithoutProxyIntegrationCard;

+ 2 - 2
src/client/js/components/Admin/SlackIntegration/CustomBotWithoutProxySettings.jsx

@@ -25,7 +25,7 @@ const CustomBotWithoutProxySettings = (props) => {
       <CustomBotWithoutProxyIntegrationCard
         siteName={siteName}
         slackWSNameInWithoutProxy={props.slackWSNameInWithoutProxy}
-        isSetupSlackBot={props.isSetupSlackBot}
+        isSlackScopeSet={props.isSlackScopeSet}
       />
 
       <h2 className="admin-setting-header">{t('admin:slack_integration.custom_bot_without_proxy_settings')}</h2>
@@ -51,7 +51,7 @@ CustomBotWithoutProxySettings.propTypes = {
   slackBotTokenEnv: PropTypes.string,
   isRgisterSlackCredentials: PropTypes.bool,
   isConnectedToSlack: PropTypes.bool,
-  isSetupSlackBot: PropTypes.bool,
+  isSlackScopeSet: PropTypes.bool,
   slackWSNameInWithoutProxy: PropTypes.string,
 };
 

+ 0 - 1
src/client/js/components/Admin/SlackIntegration/CustomBotWithoutProxySettingsAccordion.jsx

@@ -226,7 +226,6 @@ CustomBotWithoutProxySettingsAccordion.propTypes = {
 
   adminAppContainer: PropTypes.instanceOf(AdminAppContainer).isRequired,
   activeStep: PropTypes.oneOf(Object.values(botInstallationStep)).isRequired,
-  isSetupSlackBot: PropTypes.bool,
 };
 
 export default CustomBotWithoutProxySettingsAccordionWrapper;

+ 6 - 6
src/client/js/components/Admin/SlackIntegration/SlackIntegration.jsx

@@ -25,7 +25,6 @@ const SlackIntegration = (props) => {
   const [isConnectedToSlack, setIsConnectedToSlack] = useState(false);
   const [isRegisterSlackCredentials, setIsRegisterSlackCredentials] = useState(false);
   const [isSendTestMessage, setIsSendTestMessage] = useState(false);
-  const [isSetupSlackBot, setIsSetupSlackBot] = useState(false);
   const [slackWSNameInWithoutProxy, setSlackWSNameInWithoutProxy] = useState(null);
   const [isSlackScopeSet, setIsSlackScopeSet] = useState(false);
 
@@ -36,10 +35,11 @@ const SlackIntegration = (props) => {
     try {
       const res = await appContainer.apiv3.get('/slack-integration/custom-bot-without-proxy/slack-workspace-name');
       setSlackWSNameInWithoutProxy(res.data.slackWorkSpaceName);
-      isSlackScopeSet(true);
+      setIsSlackScopeSet(true);
     }
     catch (err) {
       if (err[0].message === 'missing_scope') {
+        setSlackWSNameInWithoutProxy(null);
         setIsSlackScopeSet(false);
         toastError(err, t('admin:slack_integration.set_scope'));
       }
@@ -47,7 +47,7 @@ const SlackIntegration = (props) => {
         toastError(err);
       }
     }
-  }, [appContainer.apiv3, isConnectedToSlack, isSlackScopeSet, t]);
+  }, [appContainer.apiv3, isConnectedToSlack, t]);
 
   const fetchSlackIntegrationData = useCallback(async() => {
     try {
@@ -55,7 +55,7 @@ const SlackIntegration = (props) => {
       const { currentBotType, customBotWithoutProxySettings } = response.data.slackBotSettingParams;
       const {
         slackSigningSecret, slackBotToken, slackSigningSecretEnvVars, slackBotTokenEnvVars,
-        isSetupSlackBot, isConnectedToSlack,
+        isConnectedToSlack,
       } = customBotWithoutProxySettings;
 
       setCurrentBotType(currentBotType);
@@ -63,7 +63,6 @@ const SlackIntegration = (props) => {
       setSlackBotToken(slackBotToken);
       setSlackSigningSecretEnv(slackSigningSecretEnvVars);
       setSlackBotTokenEnv(slackBotTokenEnvVars);
-      setIsSetupSlackBot(isSetupSlackBot);
       setIsConnectedToSlack(isConnectedToSlack);
 
       fetchSlackWorkSpaceNameInWithoutProxy();
@@ -119,6 +118,7 @@ const SlackIntegration = (props) => {
       setIsConnectedToSlack(false);
       setIsSendTestMessage(false);
       setSlackWSNameInWithoutProxy(null);
+      setIsSlackScopeSet(false);
     }
     catch (err) {
       toastError(err);
@@ -137,7 +137,7 @@ const SlackIntegration = (props) => {
           isSendTestMessage={isSendTestMessage}
           isRegisterSlackCredentials={isRegisterSlackCredentials}
           isConnectedToSlack={isConnectedToSlack}
-          isSetupSlackBot={isSetupSlackBot}
+          isSlackScopeSet={isSlackScopeSet}
           slackBotTokenEnv={slackBotTokenEnv}
           slackBotToken={slackBotToken}
           slackSigningSecretEnv={slackSigningSecretEnv}

+ 0 - 1
src/server/routes/apiv3/slack-integration.js

@@ -105,7 +105,6 @@ module.exports = (crowi) => {
         slackBotTokenEnvVars: crowi.configManager.getConfigFromEnvVars('crowi', 'slackbot:token'),
         slackSigningSecret: crowi.configManager.getConfig('crowi', 'slackbot:signingSecret'),
         slackBotToken: crowi.configManager.getConfig('crowi', 'slackbot:token'),
-        isSetupSlackBot: crowi.slackBotService.isSetupSlackBot,
         isConnectedToSlack: crowi.slackBotService.isConnectedToSlack,
       },
       // TODO imple when creating with proxy

+ 0 - 3
src/server/service/slackbot.js

@@ -19,7 +19,6 @@ class SlackBotService extends S2sMessageHandlable {
     this.client = null;
     this.searchService = null;
 
-    this.isSetupSlackBot = false;
     this.isConnectedToSlack = false;
 
     this.lastLoadedAt = null;
@@ -28,14 +27,12 @@ class SlackBotService extends S2sMessageHandlable {
   }
 
   async initialize() {
-    this.isSetupSlackBot = false;
     this.isConnectedToSlack = false;
     const token = this.crowi.configManager.getConfig('crowi', 'slackbot:token');
 
     if (token != null) {
       this.client = new WebClient(token, { logLevel: LogLevel.DEBUG });
       logger.debug('SlackBot: setup is done');
-      this.isSetupSlackBot = true;
       await this.sendAuthTest();
     }