Просмотр исходного кода

Merge branch 'feat/growi-bot' into imprv/6083-show-accordion

Steven Fukase 4 лет назад
Родитель
Сommit
6b5dfbbeee

+ 2 - 2
.github/workflows/release-slackbot.yml → .github/workflows/release-slackbot-proxy.yml

@@ -1,9 +1,9 @@
-name: Release Docker Image for growi-slackbot
+name: Release Docker Image for @growi/slackbot-proxy
 
 on:
   push:
     branches:
-      - release/slackbot/**
+      - release/slackbot-proxy/**
 
 jobs:
 

+ 6 - 5
packages/slackbot-proxy/docker/Dockerfile

@@ -33,7 +33,7 @@ RUN npx lerna bootstrap
 FROM deps-resolver-base AS deps-resolver-prod
 RUN npx lerna bootstrap -- --production
 # make artifacts
-RUN tar cf node_modules.tar node_modules
+RUN tar cf dependencies.tar node_modules packages/slackbot-proxy/node_modules
 
 
 ##
@@ -79,8 +79,9 @@ ENV NODE_ENV production
 
 ENV appDir /opt
 
+# copy artifacts
 COPY --from=deps-resolver-prod --chown=node:node \
-  ${appDir}/node_modules.tar ${appDir}/
+  ${appDir}/dependencies.tar ${appDir}/
 COPY --from=builder --chown=node:node \
   ${appDir}/packages.tar ${appDir}/
 
@@ -88,11 +89,11 @@ RUN chown node:node ${appDir}
 
 USER node
 
-# extract node_modules.tar
+# extract artifacts
 WORKDIR ${appDir}
-RUN tar xf node_modules.tar
+RUN tar xf dependencies.tar
 RUN tar xf packages.tar
-RUN rm node_modules.tar packages.tar
+RUN rm dependencies.tar packages.tar
 
 WORKDIR ${appDir}/packages/slackbot-proxy
 

+ 14 - 7
packages/slackbot-proxy/src/controllers/growi-to-slack.ts

@@ -3,7 +3,7 @@ import {
 } from '@tsed/common';
 import axios from 'axios';
 
-import { WebAPICallResult } from '@slack/web-api';
+import { WebAPICallOptions, WebAPICallResult } from '@slack/web-api';
 
 import {
   verifyGrowiToSlackRequest, getConnectionStatuses, testToSlack, generateWebClient,
@@ -115,7 +115,7 @@ export class GrowiToSlackCtrl {
         return res.status(400).send({ message: `failed to test. err: ${err.message}` });
       }
 
-      return res.send({ relation });
+      return res.send({ relation, slackBotToken: token });
     }
 
     // retrieve latest Order with Installation
@@ -160,7 +160,7 @@ export class GrowiToSlackCtrl {
       installation: order.installation, tokenGtoP: order.tokenGtoP, tokenPtoG: order.tokenPtoG, growiUri: order.growiUrl,
     });
 
-    return res.send({ relation: createdRelation });
+    return res.send({ relation: createdRelation, slackBotToken: token });
   }
 
   @Post('/*')
@@ -190,10 +190,17 @@ export class GrowiToSlackCtrl {
     }
 
     const client = generateWebClient(token);
-    await client.chat.postMessage({
-      channel: req.body.channel,
-      blocks: req.body.blocks,
-    });
+
+    try {
+      // TODO: GW-6133
+      const opt = req.body as WebAPICallOptions;
+      await client.apiCall('put', opt);
+    }
+    catch (err) {
+      // TODO: GW-6133
+      // logger.error()
+      return res.status(500).send({ message: err.message });
+    }
 
     logger.debug('postMessage is success');
 

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

@@ -126,6 +126,7 @@ const CustomBotWithProxySettings = (props) => {
               </div>
               <WithProxyAccordions
                 botType="customBotWithProxy"
+                slackAppIntegrationId={slackAppIntegration._id}
                 onClickGenerateTokenBtn={generateAccessTokens}
                 tokenGtoP={tokenGtoP}
                 tokenPtoG={tokenPtoG}

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

@@ -122,12 +122,7 @@ const SlackIntegration = (props) => {
 
   switch (currentBotType) {
     case 'officialBot':
-      settingsComponent = (
-        <OfficialBotSettings
-          slackAppIntegrations={slackAppIntegrations}
-          proxyServerUri={proxyServerUri}
-        />
-      );
+      settingsComponent = <OfficialBotSettings slackAppIntegrations={slackAppIntegrations} proxyServerUri={proxyServerUri} />;
       break;
     case 'customBotWithoutProxy':
       settingsComponent = (

+ 29 - 32
src/client/js/components/Admin/SlackIntegration/WithProxyAccordions.jsx

@@ -1,12 +1,16 @@
+/* eslint-disable react/prop-types */
 import React, { useState } from 'react';
 import PropTypes from 'prop-types';
 import { useTranslation } from 'react-i18next';
 import { CopyToClipboard } from 'react-copy-to-clipboard';
+import loggerFactory from '@alias/logger';
+
 import { withUnstatedContainers } from '../../UnstatedUtils';
 import { toastSuccess } from '../../../util/apiNotification';
 import AppContainer from '../../../services/AppContainer';
 import Accordion from '../Common/Accordion';
 
+const logger = loggerFactory('growi:SlackIntegration:WithProxyAccordionsWrapper');
 
 const BotCreateProcess = () => {
   const { t } = useTranslation();
@@ -163,34 +167,27 @@ const GeneratingTokensAndRegisteringProxyServiceProcess = withUnstatedContainers
   );
 }, [AppContainer]);
 
-const TestProcess = () => {
+const TestProcess = ({ apiv3Post, slackAppIntegrationId }) => {
   const { t } = useTranslation();
   const [testChannel, setTestChannel] = useState('');
-  /* eslint-disable no-unused-vars */
-  // TODO: Add connection Logs
-  const [connectionErrorCode, setConnectionErrorCode] = useState(null);
-  const [connectionErrorMessage, setConnectionErrorMessage] = useState(null);
-  const [connectionSuccessMessage, setConnectionSuccessMessage] = useState(null);
+  const [connectionError, setConnectionError] = useState(null);
 
-  // TODO: Show test logs
   let value = '';
-  if (connectionErrorMessage != null) {
-    value = [connectionErrorCode, connectionErrorMessage];
-  }
-  if (connectionSuccessMessage != null) {
-    value = connectionSuccessMessage;
+  if (connectionError != null) {
+    value = [connectionError.code, connectionError.message];
   }
 
-
-  // TODO: Handle test button
-  const submitForm = (e) => {
+  const submitForm = async(e) => {
     e.preventDefault();
-    // eslint-disable-next-line no-console
-    console.log('Form Submitted');
-  };
+    setConnectionError(null);
 
-  const inputTestChannelHandler = (channel) => {
-    setTestChannel(channel);
+    try {
+      await apiv3Post('/slack-integration-settings/with-proxy/relation-test', { slackAppIntegrationId, channel: testChannel });
+    }
+    catch (error) {
+      setConnectionError(error[0]);
+      logger.error(error);
+    }
   };
 
   return (
@@ -207,22 +204,22 @@ const TestProcess = () => {
               type="text"
               value={testChannel}
               placeholder="Slack Channel"
-            // TODO: Handle test button
-              onChange={e => inputTestChannelHandler(e.target.value)}
+              onChange={e => setTestChannel(e.target.value)}
             />
           </div>
           <button
             type="submit"
             className="btn btn-info mx-3 font-weight-bold"
             disabled={testChannel.trim() === ''}
-          >Test
+          >
+            Test
           </button>
         </form>
       </div>
-      {connectionErrorMessage != null
-      && <p className="text-danger text-center my-4">{t('admin:slack_integration.accordion.error_check_logs_below')}</p>}
-      {connectionSuccessMessage != null
-      && <p className="text-info text-center my-4">{t('admin:slack_integration.accordion.send_message_to_slack_work_space')}</p>}
+      {connectionError == null
+        ? <p className="text-info text-center my-4">{t('admin:slack_integration.accordion.send_message_to_slack_work_space')}</p>
+        : <p className="text-danger text-center my-4">{t('admin:slack_integration.accordion.error_check_logs_below')}</p>
+      }
       <form>
         <div className="row my-3 justify-content-center">
           <div className="form-group slack-connection-log col-md-4">
@@ -230,7 +227,6 @@ const TestProcess = () => {
             <textarea
               className="form-control card border-info slack-connection-log-body rounded-lg"
               rows="5"
-            // TODO: Show test logs
               value={value}
               readOnly
             />
@@ -293,7 +289,7 @@ const WithProxyAccordions = (props) => {
     },
     '⑤': {
       title: 'test_connection',
-      content: <TestProcess />,
+      content: <TestProcess apiv3Post={props.appContainer.apiv3.post} slackAppIntegrationId={props.slackAppIntegrationId} />,
     },
   };
 
@@ -304,7 +300,6 @@ const WithProxyAccordions = (props) => {
       className="card border-0 rounded-lg shadow overflow-hidden"
     >
       {Object.entries(integrationProcedureMapping).map(([key, value]) => {
-
         return (
           <Accordion
             title={<><span className="mr-2">{key}</span>{t(`admin:slack_integration.accordion.${value.title}`)}</>}
@@ -322,13 +317,15 @@ const WithProxyAccordions = (props) => {
 /**
  * Wrapper component for using unstated
  */
-const OfficialBotSettingsAccordionsWrapper = withUnstatedContainers(WithProxyAccordions, [AppContainer]);
+const WithProxyAccordionsWrapper = withUnstatedContainers(WithProxyAccordions, [AppContainer]);
 WithProxyAccordions.propTypes = {
   appContainer: PropTypes.instanceOf(AppContainer).isRequired,
   botType: PropTypes.string.isRequired,
+
+  slackAppIntegrationId: PropTypes.string.isRequired,
   onClickGenerateTokenBtn: PropTypes.func,
   tokenPtoG: PropTypes.string,
   tokenGtoP: PropTypes.string,
 };
 
-export default OfficialBotSettingsAccordionsWrapper;
+export default WithProxyAccordionsWrapper;

+ 30 - 23
src/server/routes/apiv3/slack-integration-settings.js

@@ -60,7 +60,8 @@ module.exports = (crowi) => {
         .isURL({ require_tld: false }),
     ],
     RelationTest: [
-      body('slackappintegrationsId').isMongoId(),
+      body('slackAppIntegrationId').isMongoId(),
+      body('channel').trim().isString(),
     ],
     deleteIntegration: [
       query('integrationIdToDelete').isMongoId(),
@@ -165,26 +166,20 @@ module.exports = (crowi) => {
       }
     }
     else {
-      try {
-        const slackAppIntegrations = await SlackAppIntegration.find();
-        settings.slackAppIntegrations = slackAppIntegrations;
-      }
-      catch (error) {
-        const msg = 'Error occured in getting connection statuses';
-        logger.error('Error', error);
-        return res.apiv3Err(new ErrorV3(msg, 'get-connection-failed'), 500);
-      }
-
       const proxyServerUri = settings.proxyServerUri;
+
       if (proxyServerUri != null) {
         try {
-          if (settings.slackAppIntegrations.length > 0) {
-            const tokenGtoPs = settings.slackAppIntegrations.map(slackAppIntegration => slackAppIntegration.tokenGtoP);
+          const slackAppIntegrations = await SlackAppIntegration.find();
+          settings.slackAppIntegrations = slackAppIntegrations;
+
+          if (slackAppIntegrations.length > 0) {
+            const tokenGtoPs = slackAppIntegrations.map(slackAppIntegration => slackAppIntegration.tokenGtoP);
             connectionStatuses = (await getConnectionStatusesFromProxy(tokenGtoPs)).connectionStatuses;
           }
         }
         catch (error) {
-          const msg = 'Error occured in testing connection statuses';
+          const msg = 'Error occured in getting connection statuses';
           logger.error('Error', error);
           return res.apiv3Err(new ErrorV3(msg, 'get-connection-failed'), 500);
         }
@@ -453,7 +448,7 @@ module.exports = (crowi) => {
    *            application/json:
    *              schema:
    *                properties:
-   *                  slackappintegrationsId:
+   *                  slackAppIntegrationId:
    *                    type: string
    *        responses:
    *           200:
@@ -466,23 +461,35 @@ module.exports = (crowi) => {
       return res.apiv3Err(new ErrorV3(msg, 'not-proxy-type'), 400);
     }
 
-    const { slackappintegrationsId } = req.body;
-
+    const { slackAppIntegrationId } = req.body;
+    let slackBotToken;
     try {
-      const slackAppIntegration = await SlackAppIntegration.findOne({ _id: slackappintegrationsId });
+      const slackAppIntegration = await SlackAppIntegration.findOne({ _id: slackAppIntegrationId });
       if (slackAppIntegration == null) {
         const msg = 'Could not find SlackAppIntegration by id';
         return res.apiv3Err(new ErrorV3(msg, 'find-slackAppIntegration-failed'), 400);
       }
-      const response = await postRelationTest(slackAppIntegration.tokenGtoP);
-
-      return res.apiv3({ response });
+      const result = await postRelationTest(slackAppIntegration.tokenGtoP);
+      slackBotToken = result.slackBotToken;
+      if (slackBotToken == null) {
+        const msg = 'Could not find slackBotToken by relation';
+        return res.apiv3Err(new ErrorV3(msg, 'find-slackBotToken-failed'), 400);
+      }
     }
     catch (error) {
-      const msg = 'Error occured in updating Custom bot setting';
       logger.error('Error', error);
-      return res.apiv3Err(new ErrorV3(msg, 'update-CustomBotSetting-failed'), 500);
+      return res.apiv3Err(new ErrorV3(`Error occured while testing. Cause: ${error.message}`, 'test-failed', error.stack));
     }
+
+    const { channel } = req.body;
+    const appSiteURL = crowi.configManager.getConfig('crowi', 'app:siteUrl');
+    try {
+      await sendSuccessMessage(slackBotToken, channel, appSiteURL);
+    }
+    catch (error) {
+      return res.apiv3Err(new ErrorV3(`Error occured while sending message. Cause: ${error.message}`, 'send-message-failed', error.stack));
+    }
+
   });
 
   /**