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

Merge branch 'feat/growi-bot' into fix/GW-6050-design-fix

# Conflicts:
#	resource/locales/ja_JP/admin/admin.json
#	resource/locales/zh_CN/admin/admin.json
Shun Miyazawa 4 лет назад
Родитель
Сommit
3dcd3dd487

+ 3 - 3
resource/locales/en_US/admin/admin.json

@@ -295,7 +295,7 @@
       "generate": "Generate"
     },
     "delete": "Delete",
-    "cooperation_procedure": "Cooperation procedure",
+    "integration_procedure": "Integration Procedure",
     "official_bot_settings": "Official bot Settings",
     "reset": "Reset",
     "reset_all_settings": "Reset all settings",
@@ -330,12 +330,12 @@
       "send_message_to_slack_work_space": "Send message to Slack work space.",
       "add_slack_workspace": "Add a Slack Workspace"
     },
-    "custom_bot_without_proxy_integration": "Custom bot without proxy integration",
+    "custom_bot_without_proxy_integration": "Custom Bot Without Proxy Integration",
     "integration_sentence": {
       "integration_is_not_complete": "Integration is not complete.<br>Proceed with the following integration procedure.",
       "integration_successful": "Integration successful"
     },
-    "custom_bot_with_proxy_integration": "Custom bot with proxy integration",
+    "custom_bot_with_proxy_integration": "Custom Bot With Proxy Integration",
     "official_bot_integration": "Official bot integration"
   },
   "user_management": {

+ 2 - 1
resource/locales/ja_JP/admin/admin.json

@@ -293,7 +293,8 @@
       "generate": "発行"
     },
     "delete": "削除",
-    "cooperation_procedure": "連携手順",
+    "integration_procedure": "連携手順",
+    "custom_bot_without_proxy_settings": "Custom Bot (Without-Proxy) 設定",
     "reset": "リセット",
     "reset_all_settings": "全ての設定をリセット",
     "delete_slackbot_settings": "Slack Bot 設定をリセットする",

+ 2 - 1
resource/locales/zh_CN/admin/admin.json

@@ -303,7 +303,8 @@
       "generate": "生成"
     },
     "delete": "取消",
-    "cooperation_procedure": "协作程序",
+    "integration_procedure": "协作程序",
+    "custom_bot_without_proxy_settings": "Custom Bot (Without-Proxy) 设置",
     "reset":"重置",
     "reset_all_settings": "重置所有设置",
     "delete_slackbot_settings": "重置 Slack Bot 设置",

+ 40 - 7
src/client/js/components/Admin/SlackIntegration/CustomBotWithProxySettings.jsx

@@ -14,12 +14,14 @@ import DeleteSlackBotSettingsModal from './DeleteSlackBotSettingsModal';
 const logger = loggerFactory('growi:SlackBotSettings');
 
 const CustomBotWithProxySettings = (props) => {
-  // eslint-disable-next-line no-unused-vars
   const { appContainer } = props;
   const [isDeleteConfirmModalShown, setIsDeleteConfirmModalShown] = useState(false);
   const [proxyUri, setProxyUri] = useState(null);
 
   const { t } = useTranslation();
+  // TODO: Multiple accordion logic
+  const [tokenPtoG, setTokenPtoG] = useState(null);
+  const [tokenGtoP, setTokenGtoP] = useState(null);
 
   const retrieveProxyUri = useCallback(async() => {
     try {
@@ -37,7 +39,6 @@ const CustomBotWithProxySettings = (props) => {
     retrieveProxyUri();
   }, [retrieveProxyUri]);
 
-
   // TODO: Multiple accordion logic
   const [accordionComponentsCount, setAccordionComponentsCount] = useState(0);
   const addAccordionHandler = () => {
@@ -52,6 +53,31 @@ const CustomBotWithProxySettings = (props) => {
     );
   };
 
+  const discardTokenHandler = async() => {
+    try {
+      await appContainer.apiv3.delete('/slack-integration-settings/slack-app-integration', { tokenGtoP, tokenPtoG });
+      setTokenGtoP(null);
+      setTokenPtoG(null);
+    }
+    catch (err) {
+      toastError(err);
+      logger(err);
+    }
+  };
+
+  const generateTokenHandler = async() => {
+    try {
+      const { data: { tokenGtoP, tokenPtoG } } = await appContainer.apiv3.put('/slack-integration-settings/access-tokens');
+      setTokenGtoP(tokenGtoP);
+      setTokenPtoG(tokenPtoG);
+    }
+    catch (err) {
+      toastError(err);
+      logger(err);
+    }
+
+  };
+
   const deleteSlackSettingsHandler = async() => {
     try {
       // TODO imple delete PtoG and GtoP Token at GW 5861
@@ -116,12 +142,13 @@ const CustomBotWithProxySettings = (props) => {
         </div>
       </div>
 
-      <h2 className="admin-setting-header">{t('admin:slack_integration.cooperation_procedure')}</h2>
+      <h2 className="admin-setting-header">{t('admin:slack_integration.integration_procedure')}</h2>
       <div className="mx-3">
 
-        {/* // TODO: Multiple accordion logic */}
+        {/* TODO: Multiple accordion logic */}
+        {/* TODO: Undefined key fix */}
         {Array(...Array(accordionComponentsCount)).map(i => (
-          <>
+          <React.Fragment key={i}>
             <div className="d-flex justify-content-end">
               <button
                 className="my-3 btn btn-outline-danger"
@@ -132,8 +159,14 @@ const CustomBotWithProxySettings = (props) => {
                 {t('admin:slack_integration.delete')}
               </button>
             </div>
-            <WithProxyAccordions botType="customBotWithProxy" key={i} />
-          </>
+            <WithProxyAccordions
+              botType="customBotWithProxy"
+              discardTokenHandler={discardTokenHandler}
+              generateTokenHandler={generateTokenHandler}
+              tokenPtoG={tokenPtoG}
+              tokenGtoP={tokenGtoP}
+            />
+          </React.Fragment>
         ))}
 
         {/* TODO: Disable button when integration is incomplete */}

+ 101 - 63
src/client/js/components/Admin/SlackIntegration/WithProxyAccordions.jsx

@@ -8,7 +8,7 @@ import AppContainer from '../../../services/AppContainer';
 import Accordion from '../Common/Accordion';
 
 
-export const BotCreateProcess = () => {
+const BotCreateProcess = () => {
   const { t } = useTranslation();
   return (
     <div className="my-5 d-flex flex-column align-items-center">
@@ -29,7 +29,7 @@ export const BotCreateProcess = () => {
   );
 };
 
-export const BotInstallProcess = () => {
+const BotInstallProcess = () => {
   const { t } = useTranslation();
   return (
     <div className="my-5 d-flex flex-column align-items-center">
@@ -51,7 +51,7 @@ export const BotInstallProcess = () => {
   );
 };
 
-export const RegisteringProxyUrlProcess = () => {
+const RegisteringProxyUrlProcess = () => {
   const { t } = useTranslation();
   return (
     <div className="container w-75 py-5">
@@ -70,9 +70,21 @@ export const RegisteringProxyUrlProcess = () => {
   );
 };
 
-export const GenelatingTokensAndRegisteringProxyServiceProcess = withUnstatedContainers((props) => {
+const GeneratingTokensAndRegisteringProxyServiceProcess = withUnstatedContainers((props) => {
   const { t } = useTranslation();
-  const growiUrl = props.appContainer.config.crowi.url;
+
+  const generateTokenHandler = () => {
+    if (props.generateTokenHandler != null) {
+      props.generateTokenHandler();
+    }
+  };
+
+  const discardTokenHandler = () => {
+    if (props.discardTokenHandler != null) {
+      props.discardTokenHandler();
+    }
+  };
+
   return (
     <div className="py-4 px-5">
       <p className="font-weight-bold">1. {t('admin:slack_integration.accordion.generate_access_token')}</p>
@@ -81,7 +93,7 @@ export const GenelatingTokensAndRegisteringProxyServiceProcess = withUnstatedCon
         <div className="col-md-6">
           <div className="input-group-prepend mx-1">
             {/* TODO: show tokenPtoG GW-5899 */}
-            <input className="form-control" type="text" value="tokenPtoG" readOnly />
+            <input className="form-control" type="text" value={props.tokenPtoG || ''} readOnly />
             <CopyToClipboard text="tokenPtoG" onCopy={() => toastSuccess(t('admin:slack_integration.copied_to_clipboard'))}>
               <div className="btn input-group-text">
                 <i className="fa fa-clipboard mx-1" aria-hidden="true"></i>
@@ -95,7 +107,7 @@ export const GenelatingTokensAndRegisteringProxyServiceProcess = withUnstatedCon
         <div className="col-md-6">
           <div className="input-group-prepend mx-1">
             {/* TODO: show tokenGtoP GW-5899 */}
-            <input className="form-control" type="text" value="tokenGtoP" readOnly />
+            <input className="form-control" type="text" value={props.tokenGtoP || ''} readOnly />
             <CopyToClipboard text="tokenGtoP" onCopy={() => toastSuccess(t('admin:slack_integration.copied_to_clipboard'))}>
               <div className="btn input-group-text">
                 <i className="fa fa-clipboard mx-1" aria-hidden="true"></i>
@@ -107,8 +119,21 @@ export const GenelatingTokensAndRegisteringProxyServiceProcess = withUnstatedCon
 
       <div className="row my-3">
         <div className="mx-auto">
-          <button type="button" className="btn btn-outline-secondary mx-2">{ t('admin:slack_integration.access_token_settings.discard') }</button>
-          <button type="button" className="btn btn-primary mx-2">{ t('admin:slack_integration.access_token_settings.generate') }</button>
+          <button
+            type="button"
+            className="btn btn-outline-secondary mx-2"
+            onClick={discardTokenHandler}
+            disabled={props.tokenGtoP == null || props.tokenPtoG == null}
+          >
+            { t('admin:slack_integration.access_token_settings.discard') }
+          </button>
+          <button
+            type="button"
+            className="btn btn-primary mx-2"
+            onClick={generateTokenHandler}
+          >
+            { t('admin:slack_integration.access_token_settings.generate') }
+          </button>
         </div>
       </div>
       <p className="font-weight-bold">2. {t('admin:slack_integration.accordion.register_for_growi_official_bot_proxy_service')}</p>
@@ -131,8 +156,8 @@ export const GenelatingTokensAndRegisteringProxyServiceProcess = withUnstatedCon
             />
             <div className="input-group align-items-center ml-2 mb-3">
               <div className="input-group-prepend mx-1">
-                <input className="form-control" type="text" value={growiUrl} readOnly />
-                <CopyToClipboard text={growiUrl} onCopy={() => toastSuccess(t('admin:slack_integration.copied_to_clipboard'))}>
+                <input className="form-control" type="text" value={props.growiUrl} readOnly />
+                <CopyToClipboard text={props.growiUrl} onCopy={() => toastSuccess(t('admin:slack_integration.copied_to_clipboard'))}>
                   <div className="btn input-group-text">
                     <i className="fa fa-clipboard mx-1" aria-hidden="true"></i>
                   </div>
@@ -156,12 +181,7 @@ export const GenelatingTokensAndRegisteringProxyServiceProcess = withUnstatedCon
   );
 }, [AppContainer]);
 
-GenelatingTokensAndRegisteringProxyServiceProcess.propTypes = {
-  appContainer: PropTypes.instanceOf(AppContainer).isRequired,
-};
-
-
-export const TestProcess = () => {
+const TestProcess = () => {
   const { t } = useTranslation();
   const [testChannel, setTestChannel] = useState('');
   /* eslint-disable no-unused-vars */
@@ -239,57 +259,72 @@ export const TestProcess = () => {
   );
 };
 
-const CustomBotCooperationProcedure = {
-  '①': {
-    title: 'create_bot',
-    content: <BotCreateProcess />,
-  },
-  '②': {
-    title: 'install_bot_to_slack',
-    content: <BotInstallProcess />,
-  },
-  '③': {
-    title: 'register_for_growi_official_bot_proxy_service',
-    content: <GenelatingTokensAndRegisteringProxyServiceProcess />,
-  },
-  '④': {
-    title: 'set_proxy_url_on_growi',
-    content: <RegisteringProxyUrlProcess />,
-  },
-  '⑤': {
-    title: 'test_connection',
-    content: <TestProcess />,
-  },
-};
-
-const officialBotCooperationProcedure = {
-  '①': {
-    title: 'install_bot_to_slack',
-    content: <BotInstallProcess />,
-  },
-  '②': {
-    title: 'register_for_growi_official_bot_proxy_service',
-    content: <GenelatingTokensAndRegisteringProxyServiceProcess />,
-  },
-  '③': {
-    title: 'set_proxy_url_on_growi',
-    content: <RegisteringProxyUrlProcess />,
-  },
-  '④': {
-    title: 'test_connection',
-    content: <TestProcess />,
-  },
-};
-
 
 const WithProxyAccordions = (props) => {
   const { t } = useTranslation();
-  const cooperationProcedureMapping = props.botType === 'officialBot' ? officialBotCooperationProcedure : CustomBotCooperationProcedure;
 
+  const officialBotIntegrationProcedure = {
+    '①': {
+      title: 'install_bot_to_slack',
+      content: <BotInstallProcess />,
+    },
+    '②': {
+      title: 'register_for_growi_official_bot_proxy_service',
+      content: <GeneratingTokensAndRegisteringProxyServiceProcess
+        growiUrl={props.appContainer.config.crowi.url}
+        discardTokenHandler={props.discardTokenHandler}
+        generateTokenHandler={props.generateTokenHandler}
+        tokenPtoG={props.tokenPtoG}
+        tokenGtoP={props.tokenGtoP}
+      />,
+    },
+    '③': {
+      title: 'set_proxy_url_on_growi',
+      content: <RegisteringProxyUrlProcess />,
+    },
+    '④': {
+      title: 'test_connection',
+      content: <TestProcess />,
+    },
+  };
+
+  const CustomBotIntegrationProcedure = {
+    '①': {
+      title: 'create_bot',
+      content: <BotCreateProcess />,
+    },
+    '②': {
+      title: 'install_bot_to_slack',
+      content: <BotInstallProcess />,
+    },
+    '③': {
+      title: 'register_for_growi_official_bot_proxy_service',
+      content: <GeneratingTokensAndRegisteringProxyServiceProcess
+        growiUrl={props.appContainer.config.crowi.url}
+        discardTokenHandler={props.discardTokenHandler}
+        generateTokenHandler={props.generateTokenHandler}
+        tokenPtoG={props.tokenPtoG}
+        tokenGtoP={props.tokenGtoP}
+      />,
+    },
+    '④': {
+      title: 'set_proxy_url_on_growi',
+      content: <RegisteringProxyUrlProcess />,
+    },
+    '⑤': {
+      title: 'test_connection',
+      content: <TestProcess />,
+    },
+  };
+
+  const integrationProcedureMapping = props.botType === 'officialBot' ? officialBotIntegrationProcedure : CustomBotIntegrationProcedure;
 
   return (
-    <div className="card border-0 rounded-lg shadow overflow-hidden">
-      {Object.entries(cooperationProcedureMapping).map(([key, value]) => {
+    <div
+      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}`)}</>}
@@ -307,11 +342,14 @@ const WithProxyAccordions = (props) => {
 /**
  * Wrapper component for using unstated
  */
-
 const OfficialBotSettingsAccordionsWrapper = withUnstatedContainers(WithProxyAccordions, [AppContainer]);
 WithProxyAccordions.propTypes = {
   appContainer: PropTypes.instanceOf(AppContainer).isRequired,
   botType: PropTypes.string.isRequired,
+  discardTokenHandler: PropTypes.func,
+  generateTokenHandler: PropTypes.func,
+  tokenPtoG: PropTypes.string,
+  tokenGtoP: PropTypes.string,
 };
 
 export default OfficialBotSettingsAccordionsWrapper;

+ 6 - 5
src/server/routes/apiv3/slack-integration-settings.js

@@ -1,6 +1,6 @@
 const mongoose = require('mongoose');
 const express = require('express');
-const { body } = require('express-validator');
+const { body, query } = require('express-validator');
 const axios = require('axios');
 const urljoin = require('url-join');
 const loggerFactory = require('@alias/logger');
@@ -56,10 +56,10 @@ module.exports = (crowi) => {
         .isIn(['officialBot', 'customBotWithoutProxy', 'customBotWithProxy']),
     ],
     AccessTokens: [
-      body('tokenGtoP').trim().not().isEmpty()
+      query('tokenGtoP').trim().not().isEmpty()
         .isString()
         .isLength({ min: 1 }),
-      body('tokenPtoG').trim().not().isEmpty()
+      query('tokenPtoG').trim().not().isEmpty()
         .isString()
         .isLength({ min: 1 }),
     ],
@@ -404,9 +404,10 @@ module.exports = (crowi) => {
    */
   router.delete('/slack-app-integration', validator.AccessTokens, apiV3FormValidator, async(req, res) => {
     const SlackAppIntegration = mongoose.model('SlackAppIntegration');
-    const { tokenGtoP, tokenPtoG } = req.body;
+    const { tokenGtoP, tokenPtoG } = req.query;
     try {
-      await SlackAppIntegration.findOneAndDelete({ tokenGtoP, tokenPtoG });
+      const response = await SlackAppIntegration.findOneAndDelete({ tokenGtoP, tokenPtoG });
+      return res.apiv3({ response });
     }
     catch (error) {
       const msg = 'Error occured in deleting access token for slack app tokens';