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

Merge branch 'feat/growi-bot' into fix/not-get-slack-ws-name

zahmis 5 лет назад
Родитель
Сommit
86de5ee600

+ 5 - 4
packages/app-for-hoisting/package.json

@@ -2,8 +2,7 @@
   "name": "@growi/app-for-hoisting",
   "version": "0.9.0-RC",
   "license": "MIT",
-  "scripts": {
-  },
+  "scripts": {},
   "// comments for dependencies": {
     "openid-client": "Node.js 12 or higher is required for openid-client@3 and above."
   },
@@ -21,7 +20,8 @@
     "aws-sdk": "^2.88.0",
     "axios": "^0.21.1",
     "body-parser": "^1.18.2",
-    "bunyan": "^1.8.12",
+    "browser-bunyan": "^1.6.3",
+    "bunyan": "^1.8.15",
     "bunyan-format": "^0.2.1",
     "check-node-version": "^4.1.0",
     "connect-flash": "~0.1.1",
@@ -89,6 +89,7 @@
     "string-width": "^4.1.0",
     "swig-templates": "^2.0.2",
     "uglifycss": "^0.0.29",
+    "universal-bunyan": "^0.9.2",
     "unzipper": "^0.10.5",
     "url-join": "^4.0.0",
     "validator": "^12.0.0",
@@ -120,7 +121,7 @@
     "babel-plugin-lodash": "^3.3.4",
     "babel-plugin-transform-imports": "^2.0.0",
     "bootstrap": "^4.5.0",
-    "browser-bunyan": "^1.3.0",
+    "browser-bunyan": "^1.6.3",
     "browser-sync": "^2.26.3",
     "bunyan-debug": "^2.0.0",
     "cli": "~1.0.1",

+ 5 - 4
packages/app/package.json

@@ -2,8 +2,7 @@
   "name": "@growi/app",
   "version": "0.9.0-RC",
   "license": "MIT",
-  "scripts": {
-  },
+  "scripts": {},
   "// comments for dependencies": {
     "openid-client": "Node.js 12 or higher is required for openid-client@3 and above."
   },
@@ -21,7 +20,8 @@
     "aws-sdk": "^2.88.0",
     "axios": "^0.21.1",
     "body-parser": "^1.18.2",
-    "bunyan": "^1.8.12",
+    "browser-bunyan": "^1.6.3",
+    "bunyan": "^1.8.15",
     "bunyan-format": "^0.2.1",
     "check-node-version": "^4.1.0",
     "connect-flash": "~0.1.1",
@@ -89,6 +89,7 @@
     "string-width": "^4.1.0",
     "swig-templates": "^2.0.2",
     "uglifycss": "^0.0.29",
+    "universal-bunyan": "^0.9.2",
     "unzipper": "^0.10.5",
     "url-join": "^4.0.0",
     "validator": "^12.0.0",
@@ -120,7 +121,7 @@
     "babel-plugin-lodash": "^3.3.4",
     "babel-plugin-transform-imports": "^2.0.0",
     "bootstrap": "^4.5.0",
-    "browser-bunyan": "^1.3.0",
+    "browser-bunyan": "^1.6.3",
     "browser-sync": "^2.26.3",
     "bunyan-debug": "^2.0.0",
     "cli": "~1.0.1",

+ 5 - 1
packages/slack/package.json

@@ -15,13 +15,17 @@
     "test:lint:fix": "eslint src --ext .ts --fix"
   },
   "dependencies": {
-    "dotenv-flow": "^3.2.0"
+    "browser-bunyan": "^1.6.3",
+    "bunyan": "^1.8.15",
+    "dotenv-flow": "^3.2.0",
+    "universal-bunyan": "^0.9.2"
   },
   "devDependencies": {
     "@slack/bolt": "^3.3.0",
     "@types/jest": "^26.0.22",
     "@typescript-eslint/eslint-plugin": "^4.18.0",
     "@typescript-eslint/parser": "^4.18.0",
+    "browser-bunyan": "^1.6.3",
     "cross-env": "^7.0.0",
     "eslint-import-resolver-typescript": "^2.4.0",
     "eslint-plugin-jest": "^24.3.2",

+ 5 - 1
packages/slackbot-proxy/package.json

@@ -26,13 +26,16 @@
     "@tsed/platform-express": "^6.43.0",
     "@tsed/swagger": "^6.43.0",
     "@tsed/typeorm": "^6.43.0",
+    "browser-bunyan": "^1.6.3",
+    "bunyan": "^1.8.15",
     "compression": "^1.7.4",
     "cookie-parser": "^1.4.5",
     "cross-env": "^7.0.0",
     "dotenv-flow": "^3.2.0",
     "method-override": "^3.0.0",
     "mysql2": "^2.2.5",
-    "typeorm": "^0.2.31"
+    "typeorm": "^0.2.31",
+    "universal-bunyan": "^0.9.2"
   },
   "devDependencies": {
     "@tsed/core": "^6.43.0",
@@ -41,6 +44,7 @@
     "@tsed/schema": "^6.43.0",
     "@typescript-eslint/eslint-plugin": "^4.18.0",
     "@typescript-eslint/parser": "^4.18.0",
+    "browser-bunyan": "^1.6.3",
     "eslint-import-resolver-typescript": "^2.4.0",
     "ts-jest": "^26.5.4",
     "ts-node": "^9.1.1",

+ 16 - 0
packages/slackbot-proxy/src/config/logger/config.dev.ts

@@ -0,0 +1,16 @@
+import { UniversalBunyanConfig } from 'universal-bunyan';
+
+const config: UniversalBunyanConfig = {
+  default: 'info',
+
+  // 'express-session': 'debug',
+
+  /*
+   * configure level for server
+   */
+  // 'express:*': 'debug',
+  // 'slackbot-proxy:*': 'debug',
+
+};
+
+export default config;

+ 16 - 0
packages/slackbot-proxy/src/config/logger/config.prod.ts

@@ -0,0 +1,16 @@
+import { UniversalBunyanConfig } from 'universal-bunyan';
+
+const config: UniversalBunyanConfig = {
+  default: 'info',
+
+  // 'express-session': 'debug',
+
+  /*
+   * configure level for server
+   */
+  // 'express:*': 'debug',
+  // 'slackbot-proxy:*': 'debug',
+
+};
+
+export default config;

+ 32 - 9
packages/slackbot-proxy/src/controllers/slack.ts

@@ -10,6 +10,9 @@ import { OrderRepository } from '~/repositories/order';
 import { InstallerService } from '~/services/InstallerService';
 import { RegisterService } from '~/services/RegisterService';
 
+import loggerFactory from '~/utils/logger';
+
+const logger = loggerFactory('slackbot-proxy:controllers:slack');
 
 @Controller('/slack')
 export class SlackCtrl {
@@ -74,8 +77,11 @@ export class SlackCtrl {
 
   @Post('/events')
   async handleEvent(@BodyParams() body:{[key:string]:string}, @Res() res: Res): Promise<string> {
-    // Send response immediately to avoid opelation_timeout error
-    // See https://api.slack.com/apis/connections/events-api#the-events-api__responding-to-events
+    // eslint-disable-next-line max-len
+    // see: https://api.slack.com/apis/connections/events-api#the-events-api__subscribing-to-event-types__events-api-request-urls__request-url-configuration--verification
+    if (body.type === 'url_verification') {
+      return body.challenge;
+    }
 
     if (body.text == null) {
       return 'No text.';
@@ -87,9 +93,13 @@ export class SlackCtrl {
     if (executeGrowiCommand == null) {
       return 'No executeGrowiCommand';
     }
-    await executeGrowiCommand(body);
+
+    // Send response immediately to avoid opelation_timeout error
+    // See https://api.slack.com/apis/connections/events-api#the-events-api__responding-to-events
     res.send();
 
+    await executeGrowiCommand(body);
+
     const installation = await this.installationRepository.findByID('1');
     if (installation == null) {
       throw new Error('installation is reqiured');
@@ -114,22 +124,35 @@ export class SlackCtrl {
   @Get('/oauth_redirect')
   async handleOauthRedirect(@Req() req: Req, @Res() res: Res): Promise<void> {
 
-    // illegal state
-    // TODO: https://youtrack.weseek.co.jp/issue/GW-5543
-    if (req.query.state !== 'init') {
+    if (req.query.state === '') {
       res.writeHead(500, { 'Content-Type': 'text/html; charset=utf-8' });
       res.end('<html>'
       + '<head><meta name="viewport" content="width=device-width,initial-scale=1"></head>'
       + '<body style="text-align:center; padding-top:20%;">'
       + '<h1>Illegal state, try it again.</h1>'
       + '<a href="/slack/install">'
-      + 'go to install page'
+      + 'Go to install page'
       + '</a>'
       + '</body></html>');
     }
 
-    this.installerService.installer.handleCallback(req, res, {
-      // success: (installation, metadata, req, res) => {},
+    await this.installerService.installer.handleCallback(req, res, {
+      success: (installation, metadata, req, res) => {
+        logger.info('Success to install', { installation, metadata });
+
+        const appPageUrl = `https://slack.com/apps/${installation.appId}`;
+
+        res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
+        res.end('<html>'
+        + '<head><meta name="viewport" content="width=device-width,initial-scale=1"></head>'
+        + '<body style="text-align:center; padding-top:20%;">'
+        + '<h1>Congratulations!</h1>'
+        + '<p>GROWI Bot installation has succeeded.</p>'
+        + `<a href="${appPageUrl}">`
+        + 'Access to Slack App detail page.'
+        + '</a>'
+        + '</body></html>');
+      },
       failure: (error, installOptions, req, res) => {
         res.writeHead(500, { 'Content-Type': 'text/html; charset=utf-8' });
         res.end('<html>'

+ 17 - 0
packages/slackbot-proxy/src/utils/logger/index.ts

@@ -0,0 +1,17 @@
+import Logger from 'bunyan';
+import { createLogger } from 'universal-bunyan';
+
+import configForDev from '~/config/logger/config.dev';
+import configForProd from '~/config/logger/config.prod';
+
+const isProduction = process.env.NODE_ENV === 'production';
+const config = isProduction ? configForProd : configForDev;
+
+const loggerFactory = function(name: string): Logger {
+  return createLogger({
+    name,
+    config,
+  });
+};
+
+export default loggerFactory;

+ 36 - 0
public/images/slack-integration/slackbot-difficulty-level-easy.svg

@@ -0,0 +1,36 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="60" height="60" viewBox="0 0 60 60">
+  <defs>
+    <style>
+      .cls-1, .cls-4 {
+        fill: none;
+      }
+
+      .cls-1 {
+        stroke: #81d5b8;
+        stroke-width: 3px;
+      }
+
+      .cls-2 {
+        font-family: NotoSansCJKjp-Bold, Noto Sans CJK JP;
+        font-size: 16px;
+        font-weight: 700;
+        fill: #81d5b8;
+      }
+
+      .cls-3 {
+        stroke: none;
+      }
+    </style>
+  </defs>
+  <g id="Group_4361" data-name="Group 4361" transform="translate(-71.69 -49.04)">
+    <g id="Group_4358" data-name="Group 4358">
+      <g id="Group_4357" data-name="Group 4357">
+        <g id="Ellipse_97" data-name="Ellipse 97" class="cls-1" transform="translate(71.69 49.04)">
+          <circle class="cls-3" cx="30" cy="30" r="30"/>
+          <circle class="cls-4" cx="30" cy="30" r="28.5"/>
+        </g>
+        <text id="EASY" class="cls-2" transform="translate(83.884 89.632) rotate(-11)"><tspan x="0" y="0">EASY</tspan></text>
+      </g>
+    </g>
+  </g>
+</svg>

+ 32 - 0
public/images/slack-integration/slackbot-difficulty-level-hard.svg

@@ -0,0 +1,32 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="60" height="60" viewBox="0 0 60 60">
+  <defs>
+    <style>
+      .cls-1, .cls-4 {
+        fill: none;
+      }
+
+      .cls-1 {
+        stroke: #ff8080;
+        stroke-width: 3px;
+      }
+
+      .cls-2 {
+        fill: #ff8080;
+        font-size: 16px;
+        font-family: NotoSansCJKjp-Bold, Noto Sans CJK JP;
+        font-weight: 700;
+      }
+
+      .cls-3 {
+        stroke: none;
+      }
+    </style>
+  </defs>
+  <g id="Group_4360" data-name="Group 4360" transform="translate(-303.305 -23.039)">
+    <g id="Ellipse_99" data-name="Ellipse 99" class="cls-1" transform="translate(303.305 23.039)">
+      <circle class="cls-3" cx="30" cy="30" r="30"/>
+      <circle class="cls-4" cx="30" cy="30" r="28.5"/>
+    </g>
+    <text id="HARD" class="cls-2" transform="translate(312.93 64.277) rotate(-11)"><tspan x="0" y="0">HARD</tspan></text>
+  </g>
+</svg>

+ 32 - 0
public/images/slack-integration/slackbot-difficulty-level-normal.svg

@@ -0,0 +1,32 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="60" height="60" viewBox="0 0 60 60">
+  <defs>
+    <style>
+      .cls-1, .cls-4 {
+        fill: none;
+      }
+
+      .cls-1 {
+        stroke: #ffce60;
+        stroke-width: 3px;
+      }
+
+      .cls-2 {
+        fill: #ffce60;
+        font-size: 11px;
+        font-family: NotoSansCJKjp-Bold, Noto Sans CJK JP;
+        font-weight: 700;
+      }
+
+      .cls-3 {
+        stroke: none;
+      }
+    </style>
+  </defs>
+  <g id="Group_4359" data-name="Group 4359" transform="translate(-163.69 -44.039)">
+    <g id="Ellipse_98" data-name="Ellipse 98" class="cls-1" transform="translate(163.69 44.039)">
+      <circle class="cls-3" cx="30" cy="30" r="30"/>
+      <circle class="cls-4" cx="30" cy="30" r="28.5"/>
+    </g>
+    <text id="NORMAL" class="cls-2" transform="translate(171.836 83.813) rotate(-11)"><tspan x="0" y="0">NORMAL</tspan></text>
+  </g>
+</svg>

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

@@ -305,8 +305,9 @@
     },
     "custom_bot_without_proxy_integration": "Custom bot without proxy integration",
     "integration_sentence": {
-      "integration_is_not_complete": "Integration is not complete.",
-      "proceed_with_the_following_integration_procedure": "Proceed with the following integration procedure."
+      "integration_is_not_complete": "Integration is not complete.<br>Proceed with the following integration procedure.",
+      "proceed_with_the_following_integration_procedure": "Proceed with the following integration procedure.",
+      "integration_sucessed": "integration sucessed"
     },
     "custom_bot_with_proxy_integration": "Custom bot with proxy integration"
   },

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

@@ -303,8 +303,9 @@
     },
     "custom_bot_without_proxy_integration": "Custom bot without proxy 連携",
     "integration_sentence": {
-      "integration_is_not_complete": "連携は完了していません。",
-      "proceed_with_the_following_integration_procedure": "下記の連携手順を進めてください。"
+      "integration_is_not_complete": "連携は完了していません。<br>下記の連携手順を進めてください。",
+      "proceed_with_the_following_integration_procedure": "下記の連携手順を進めてください。",
+      "integration_sucessed": "連携が完了しました。"
     },
     "custom_bot_with_proxy_integration": "Custom bot with proxy 連携"
   },

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

@@ -313,8 +313,9 @@
     },
     "custom_bot_without_proxy_integration": "Custom bot without proxy 一体化",
     "integration_sentence": {
-      "integration_is_not_complete": "一体化未完成。",
-      "proceed_with_the_following_integration_procedure": "进行以下一体化程序。"
+      "integration_is_not_complete": "一体化未完成。<br>进行以下一体化程序。",
+      "proceed_with_the_following_integration_procedure": "进行以下一体化程序。",
+      "integration_sucessed": "一体化成功"
     },
     "custom_bot_with_proxy_integration": "Custom bot with proxy 一体化"
   },

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

@@ -67,13 +67,7 @@ const BotTypeCard = (props) => {
       <div className="card-body p-4">
         <div className="card-text">
           <div className="my-2">
-            <div className="d-flex justify-content-between mb-3">
-              {/* TODO add image of difficulties by GW-5638
-               <span>{t('admin:slack_integration.selecting_bot_types.set_up')}</span>
-               <span className={`bot-type-disc-${value.setUp}`}>{t(`admin:slack_integration.selecting_bot_types.${value.setUp}`)}</span>  */}
-
-
-            </div>
+            <img className="d-block mx-auto mb-4" src={`/images/slack-integration/slackbot-difficulty-level-${botDetails[props.botType].setUp}.svg`}></img>
             <div className="d-flex justify-content-between mb-3">
               <span>{t('admin:slack_integration.selecting_bot_types.multiple_workspaces_integration')}</span>
               <img className="bot-type-disc" src={`/images/slack-integration/${botDetails[props.botType].multiWSIntegration}.png`} alt="" />

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

@@ -42,10 +42,23 @@ const CustomBotWithoutProxySettings = (props) => {
           </div>
         </div>
 
-        <div className="text-center w-25 mt-4">
-          <p className="text-secondary m-0"><small>{t('admin:slack_integration.integration_sentence.integration_is_not_complete')}</small></p>
-          <p className="text-secondary"><small>{t('admin:slack_integration.integration_sentence.proceed_with_the_following_integration_procedure')}</small></p>
-          <hr className="border-danger align-self-center admin-border"></hr>
+        <div className="text-center w-25">
+          {props.isSetupSlackBot && (
+            <div className="mt-5">
+              <p className="text-success"><small className="fa fa-check"> {t('admin:slack_integration.integration_sentence.integration_sucessed')}</small></p>
+              <hr className="align-self-center admin-border-success border-success"></hr>
+            </div>
+          )}
+          {!props.isSetupSlackBot && (
+            <div className="mt-4">
+              <small
+                className="text-secondary m-0"
+                // eslint-disable-next-line react/no-danger
+                dangerouslySetInnerHTML={{ __html: t('admin:slack_integration.integration_sentence.integration_is_not_complete') }}
+              />
+              <hr className="align-self-center admin-border-danger border-danger"></hr>
+            </div>
+          )}
         </div>
 
         <div className="card rounded-lg shadow border-0 w-50 admin-bot-card">

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

@@ -38,15 +38,16 @@ const CustomBotWithoutProxySettingsAccordion = ({
         slackBotToken,
         currentBotType,
       });
+
       fetchSlackWorkSpaceName();
-      if (!isConnectedToSlack) {
-        return (
-          onSetIsRegisterSlackCredentials(false),
-          onSetIsSendTestMessage(false)
-        );
-      }
-      onSetIsRegisterSlackCredentials(true);
 
+      if (isConnectedToSlack) {
+        onSetIsRegisterSlackCredentials(true);
+      }
+      else {
+        onSetIsRegisterSlackCredentials(false);
+        onSetIsSendTestMessage(false);
+      }
       toastSuccess(t('toaster.update_successed', { target: t('admin:slack_integration.custom_bot_without_proxy_settings') }));
     }
     catch (err) {
@@ -64,7 +65,6 @@ const CustomBotWithoutProxySettingsAccordion = ({
   const onChangeBotTokenHandler = (botTokenInput) => {
     if (onSetSlackBotToken != null) {
       onSetSlackBotToken(botTokenInput);
-
     }
   };
 

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

@@ -22,7 +22,7 @@ const SlackIntegration = (props) => {
   const [slackBotToken, setSlackBotToken] = useState(null);
   const [slackSigningSecretEnv, setSlackSigningSecretEnv] = useState('');
   const [slackBotTokenEnv, setSlackBotTokenEnv] = useState('');
-  const [isConnectedToSlack, setIsConnectedToSlack] = useState(null);
+  const [isConnectedToSlack, setIsConnectedToSlack] = useState(false);
   const [isRegisterSlackCredentials, setIsRegisterSlackCredentials] = useState(false);
   const [isSendTestMessage, setIsSendTestMessage] = useState(false);
   const [isSetupSlackBot, setIsSetupSlackBot] = useState(false);
@@ -43,14 +43,12 @@ const SlackIntegration = (props) => {
       setSlackBotToken(slackBotToken);
       setSlackSigningSecretEnv(slackSigningSecretEnvVars);
       setSlackBotTokenEnv(slackBotTokenEnvVars);
-      setIsConnectedToSlack(isConnectedToSlack);
       setIsSetupSlackBot(isSetupSlackBot);
+      setIsConnectedToSlack(isConnectedToSlack);
 
-      if (!isConnectedToSlack) {
-        return setIsRegisterSlackCredentials(false);
+      if (isConnectedToSlack) {
+        setIsRegisterSlackCredentials(true);
       }
-      setIsRegisterSlackCredentials(true);
-
     }
     catch (err) {
       toastError(err);
@@ -105,7 +103,6 @@ const SlackIntegration = (props) => {
       setIsRegisterSlackCredentials(false);
       setSlackSigningSecret(null);
       setSlackBotToken(null);
-      setIsConnectedToSlack(null);
       setIsSendTestMessage(false);
     }
     catch (err) {

+ 4 - 1
src/client/styles/scss/_admin.scss

@@ -122,10 +122,13 @@ $slack-work-space-name-card-border: #efc1f6;
     .admin-bot-card {
       border-radius: 8px !important;
     }
-    .admin-border {
+    .admin-border-danger {
       border-style : dashed;
       border-width : 2px;
     }
+    .admin-border-success {
+      border-width : 3px;
+    }
     .circle{
       width: 100px;
       height: 100px;

+ 24 - 1
yarn.lock

@@ -4679,7 +4679,7 @@ brorand@^1.0.1:
   version "1.1.0"
   resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f"
 
-browser-bunyan@^1.3.0:
+browser-bunyan@^1.6.3:
   version "1.6.3"
   resolved "https://registry.yarnpkg.com/browser-bunyan/-/browser-bunyan-1.6.3.tgz#0e58c51ff48507317ba8e5cf579e8b6bad7281e0"
   integrity sha512-HRg+acpwO3dsY2RWgtjw2wPVHV+uzbCrdhUxD25+qo5NFSTpbfJekrRP0yFNypAhG5LwXFV1Dc5FIc8cxwU5rQ==
@@ -4950,6 +4950,16 @@ bunyan@^1.8.12, bunyan@^1.8.3:
     mv "~2"
     safe-json-stringify "~1"
 
+bunyan@^1.8.15:
+  version "1.8.15"
+  resolved "https://registry.yarnpkg.com/bunyan/-/bunyan-1.8.15.tgz#8ce34ca908a17d0776576ca1b2f6cbd916e93b46"
+  integrity sha512-0tECWShh6wUysgucJcBAoYegf3JJoZWibxdqhTm7OHPeT42qdjkZ29QCMcKwbgU1kiH+auSIasNRXMLWXafXig==
+  optionalDependencies:
+    dtrace-provider "~0.8"
+    moment "^2.19.3"
+    mv "~2"
+    safe-json-stringify "~1"
+
 busboy@^0.2.11:
   version "0.2.14"
   resolved "https://registry.yarnpkg.com/busboy/-/busboy-0.2.14.tgz#6c2a622efcf47c57bbbe1e2a9c37ad36c7925453"
@@ -12726,6 +12736,11 @@ moment@>=2.26.0:
   resolved "https://registry.yarnpkg.com/moment/-/moment-2.26.0.tgz#5e1f82c6bafca6e83e808b30c8705eed0dcbd39a"
   integrity sha512-oIixUO+OamkUkwjhAVE18rAMfRJNsNe/Stid/gwHSOfHrOtw9EhAY2AHvdKZ/k/MggcYELFCJz/Sn2pL8b8JMw==
 
+moment@^2.19.3:
+  version "2.29.1"
+  resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.1.tgz#b2be769fa31940be9eeea6469c075e35006fa3d3"
+  integrity sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==
+
 mongodb@3.6.2, mongodb@^3.1.0, mongodb@^3.6.2:
   version "3.6.2"
   resolved "https://registry.yarnpkg.com/mongodb/-/mongodb-3.6.2.tgz#1154a4ac107bf1375112d83a29c5cf97704e96b6"
@@ -19215,6 +19230,14 @@ unist-util-visit@^1.1.0:
   dependencies:
     unist-util-visit-parents "^2.0.0"
 
+universal-bunyan@^0.9.2:
+  version "0.9.2"
+  resolved "https://registry.yarnpkg.com/universal-bunyan/-/universal-bunyan-0.9.2.tgz#4cf09dc34070390d8f5df4fe9af6a80fcd0dd574"
+  integrity sha512-MkyO17+5AVCpFfhMtYLODvSZmPxV8eHcoOAWobEXXzlXrSnf5YgCV5lBWcMV3VPaaKyZPQ0oG5PSWYmGSBGtIg==
+  dependencies:
+    bunyan-format "^0.2.1"
+    minimatch "^3.0.4"
+
 universal-user-agent@^6.0.0:
   version "6.0.0"
   resolved "https://registry.yarnpkg.com/universal-user-agent/-/universal-user-agent-6.0.0.tgz#3381f8503b251c0d9cd21bc1de939ec9df5480ee"