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

Merge pull request #3554 from weseek/imprv/slack-notification-from-slackBot

Imprv/slack notification from slack bot
Yuki Takei 5 лет назад
Родитель
Сommit
0ed3c26a5a

+ 3 - 1
package.json

@@ -77,9 +77,11 @@
     ],
     "@google-cloud/storage": "^3.3.0",
     "@kobalab/socket.io-session": "^1.0.3",
-    "@slack/bolt": "^3.0.0",
     "@promster/express": "^5.0.1",
     "@promster/server": "^6.0.0",
+    "@slack/bolt": "^3.0.0",
+    "@slack/events-api": "^3.0.0",
+    "@slack/web-api": "^6.1.0",
     "JSONStream": "^1.3.5",
     "archiver": "^3.1.1",
     "array.prototype.flatmap": "^1.2.2",

+ 14 - 0
src/server/crowi/index.js

@@ -105,6 +105,7 @@ Crowi.prototype.init = async function() {
     this.setupSearcher(),
     this.setupMailer(),
     this.setupSlack(),
+    this.setupSlackLegacy(),
     this.setupCsrf(),
     this.setUpFileUpload(),
     this.setUpFileUploaderSwitchService(),
@@ -313,6 +314,10 @@ Crowi.prototype.getSlack = function() {
   return this.slack;
 };
 
+Crowi.prototype.getSlackLegacy = function() {
+  return this.slackLegacy;
+};
+
 Crowi.prototype.getInterceptorManager = function() {
   return this.interceptorManager;
 };
@@ -385,6 +390,15 @@ Crowi.prototype.setupSlack = async function() {
   }));
 };
 
+Crowi.prototype.setupSlackLegacy = async function() {
+  const self = this;
+
+  return new Promise(((resolve, reject) => {
+    self.slackLegacy = require('../util/slack-legacy')(self);
+    resolve();
+  }));
+};
+
 Crowi.prototype.setupCsrf = async function() {
   const Tokens = require('csrf');
   this.tokens = new Tokens();

+ 1 - 1
src/server/routes/apiv3/notification-setting.js

@@ -181,7 +181,7 @@ module.exports = (crowi) => {
         isIncomingWebhookPrioritized: await crowi.configManager.getConfig('notification', 'slack:isIncomingWebhookPrioritized'),
         slackToken: await crowi.configManager.getConfig('notification', 'slack:token'),
       };
-      await crowi.setupSlack();
+      await crowi.setupSlackLegacy();
       return res.apiv3({ responseParams });
     }
     catch (err) {

+ 7 - 1
src/server/service/global-notification/global-notification-slack.js

@@ -9,6 +9,7 @@ class GlobalNotificationSlackService {
   constructor(crowi) {
     this.crowi = crowi;
     this.slack = crowi.getSlack();
+    this.slackLegacy = crowi.getSlackLegacy();
     this.type = crowi.model('GlobalNotificationSetting').TYPE.SLACK;
     this.event = crowi.model('GlobalNotificationSetting').EVENT;
   }
@@ -31,8 +32,13 @@ class GlobalNotificationSlackService {
     const attachmentBody = this.generateAttachmentBody(event, path, triggeredBy, vars);
 
     await Promise.all(notifications.map((notification) => {
-      return this.slack.sendGlobalNotification(messageBody, attachmentBody, notification.slackChannels);
+      return [
+        this.slack.sendGlobalNotification(messageBody, attachmentBody, notification.slackChannels),
+        this.slackLegacy.sendGlobalNotification(messageBody, attachmentBody, notification.slackChannels),
+      ];
     }));
+
+
   }
 
   /**

+ 5 - 1
src/server/service/user-notification/index.js

@@ -24,7 +24,9 @@ class UserNotificationService {
    * @param {Comment} comment
    */
   async fire(page, user, slackChannelsStr, mode, option, comment = {}) {
-    const { slackNotificationService, slack } = this.crowi;
+    const {
+      slackNotificationService, slackLegacy, slack,
+    } = this.crowi;
 
     const opt = option || {};
     const previousRevision = opt.previousRevision || '';
@@ -42,9 +44,11 @@ class UserNotificationService {
       let res;
       if (mode === 'comment') {
         res = await slack.postComment(comment, user, chan, page.path);
+        res = await slackLegacy.postComment(comment, user, chan, page.path);
       }
       else {
         res = await slack.postPage(page, user, chan, mode, previousRevision);
+        res = await slackLegacy.postPage(page, user, chan, mode, previousRevision);
       }
       if (res.status !== 'ok') {
         throw new Error(`fail to send slack notification to #${chan} channel`);

+ 96 - 0
src/server/util/slack-legacy.js

@@ -0,0 +1,96 @@
+const debug = require('debug')('growi:util:slack');
+// const slack = require('./slack');
+
+/**
+ * slack
+ */
+
+/* eslint-disable no-use-before-define */
+
+module.exports = function(crowi) {
+  const Slack = require('slack-node');
+
+  const { configManager } = crowi;
+  const slack = crowi.getSlack();
+
+  const slackLegacy = {};
+
+  const postWithIwh = function(messageObj) {
+    return new Promise((resolve, reject) => {
+      const client = new Slack();
+      client.setWebhook(configManager.getConfig('notification', 'slack:incomingWebhookUrl'));
+      client.webhook(messageObj, (err, res) => {
+        if (err) {
+          debug('Post error', err, res);
+          debug('Sent data to slack is:', messageObj);
+          return reject(err);
+        }
+        resolve(res);
+      });
+    });
+  };
+
+  const postWithWebApi = function(messageObj) {
+    return new Promise((resolve, reject) => {
+      const client = new Slack(configManager.getConfig('notification', 'slack:token'));
+      // stringify attachments
+      if (messageObj.attachments != null) {
+        messageObj.attachments = JSON.stringify(messageObj.attachments);
+      }
+      client.api('chat.postMessage', messageObj, (err, res) => {
+        if (err) {
+          debug('Post error', err, res);
+          debug('Sent data to slack is:', messageObj);
+          return reject(err);
+        }
+        resolve(res);
+      });
+    });
+  };
+
+  // slackLegacy.post = function (channel, message, opts) {
+  slackLegacy.postPage = (page, user, channel, updateType, previousRevision) => {
+    const messageObj = slack.prepareSlackMessageForPage(page, user, channel, updateType, previousRevision);
+
+    return slackPost(messageObj);
+  };
+
+  slackLegacy.postComment = (comment, user, channel, path) => {
+    const messageObj = slack.prepareSlackMessageForComment(comment, user, channel, path);
+
+    return slackPost(messageObj);
+  };
+
+  slackLegacy.sendGlobalNotification = async(messageBody, attachmentBody, slackChannel) => {
+    const messageObj = await slack.prepareSlackMessageForGlobalNotification(messageBody, attachmentBody, slackChannel);
+
+    return slackPost(messageObj);
+  };
+
+  const slackPost = (messageObj) => {
+    // when incoming Webhooks is prioritized
+    if (configManager.getConfig('notification', 'slack:isIncomingWebhookPrioritized')) {
+      if (configManager.getConfig('notification', 'slack:incomingWebhookUrl')) {
+        debug('posting message with IncomingWebhook');
+        return postWithIwh(messageObj);
+      }
+      if (configManager.getConfig('notification', 'slack:token')) {
+        debug('posting message with Web API');
+        return postWithWebApi(messageObj);
+      }
+    }
+    // else
+    else {
+      if (configManager.getConfig('notification', 'slack:token')) {
+        debug('posting message with Web API');
+        return postWithWebApi(messageObj);
+      }
+      if (configManager.getConfig('notification', 'slack:incomingWebhookUrl')) {
+        debug('posting message with IncomingWebhook');
+        return postWithIwh(messageObj);
+      }
+    }
+  };
+
+  return slackLegacy;
+};

+ 13 - 53
src/server/util/slack.js

@@ -8,34 +8,17 @@ const urljoin = require('url-join');
 /* eslint-disable no-use-before-define */
 
 module.exports = function(crowi) {
-  const Slack = require('slack-node');
-  const { configManager } = crowi;
+  const { WebClient, LogLevel } = require('@slack/web-api');
 
+  const { configManager } = crowi;
   const slack = {};
 
-  const postWithIwh = function(messageObj) {
+  const postWithSlackBot = function(messageObj) {
     return new Promise((resolve, reject) => {
-      const client = new Slack();
-      client.setWebhook(configManager.getConfig('notification', 'slack:incomingWebhookUrl'));
-      client.webhook(messageObj, (err, res) => {
-        if (err) {
-          debug('Post error', err, res);
-          debug('Sent data to slack is:', messageObj);
-          return reject(err);
-        }
-        resolve(res);
+      const client = new WebClient(configManager.getConfig('crowi', 'slackbot:token'), {
+        logLevel: LogLevel.DEBUG,
       });
-    });
-  };
-
-  const postWithWebApi = function(messageObj) {
-    return new Promise((resolve, reject) => {
-      const client = new Slack(configManager.getConfig('notification', 'slack:token'));
-      // stringify attachments
-      if (messageObj.attachments != null) {
-        messageObj.attachments = JSON.stringify(messageObj.attachments);
-      }
-      client.api('chat.postMessage', messageObj, (err, res) => {
+      client.chat.postMessage(messageObj, (err, res) => {
         if (err) {
           debug('Post error', err, res);
           debug('Sent data to slack is:', messageObj);
@@ -105,7 +88,7 @@ module.exports = function(crowi) {
     return body;
   };
 
-  const prepareSlackMessageForPage = function(page, user, channel, updateType, previousRevision) {
+  slack.prepareSlackMessageForPage = (page, user, channel, updateType, previousRevision) => {
     const appTitle = crowi.appService.getAppTitle();
     const url = crowi.appService.getSiteUrl();
     let body = page.revision.body;
@@ -141,7 +124,7 @@ module.exports = function(crowi) {
     return message;
   };
 
-  const prepareSlackMessageForComment = function(comment, user, channel, path) {
+  slack.prepareSlackMessageForComment = (comment, user, channel, path) => {
     const appTitle = crowi.appService.getAppTitle();
     const url = crowi.appService.getSiteUrl();
     const body = prepareAttachmentTextForComment(comment);
@@ -175,7 +158,7 @@ module.exports = function(crowi) {
    * @param {string} attachmentBody
    * @param {string} slackChannel
   */
-  const prepareSlackMessageForGlobalNotification = async(messageBody, attachmentBody, slackChannel) => {
+  slack.prepareSlackMessageForGlobalNotification = async(messageBody, attachmentBody, slackChannel) => {
     const appTitle = crowi.appService.getAppTitle();
 
     const attachment = {
@@ -217,48 +200,25 @@ module.exports = function(crowi) {
     return text;
   };
 
-  // slack.post = function (channel, message, opts) {
   slack.postPage = (page, user, channel, updateType, previousRevision) => {
-    const messageObj = prepareSlackMessageForPage(page, user, channel, updateType, previousRevision);
+    const messageObj = slack.prepareSlackMessageForPage(page, user, channel, updateType, previousRevision);
 
     return slackPost(messageObj);
   };
 
   slack.postComment = (comment, user, channel, path) => {
-    const messageObj = prepareSlackMessageForComment(comment, user, channel, path);
+    const messageObj = slack.prepareSlackMessageForComment(comment, user, channel, path);
 
     return slackPost(messageObj);
   };
 
   slack.sendGlobalNotification = async(messageBody, attachmentBody, slackChannel) => {
-    const messageObj = await prepareSlackMessageForGlobalNotification(messageBody, attachmentBody, slackChannel);
-
+    const messageObj = await slack.prepareSlackMessageForGlobalNotification(messageBody, attachmentBody, slackChannel);
     return slackPost(messageObj);
   };
 
   const slackPost = (messageObj) => {
-    // when incoming Webhooks is prioritized
-    if (configManager.getConfig('notification', 'slack:isIncomingWebhookPrioritized')) {
-      if (configManager.getConfig('notification', 'slack:incomingWebhookUrl')) {
-        debug('posting message with IncomingWebhook');
-        return postWithIwh(messageObj);
-      }
-      if (configManager.getConfig('notification', 'slack:token')) {
-        debug('posting message with Web API');
-        return postWithWebApi(messageObj);
-      }
-    }
-    // else
-    else {
-      if (configManager.getConfig('notification', 'slack:token')) {
-        debug('posting message with Web API');
-        return postWithWebApi(messageObj);
-      }
-      if (configManager.getConfig('notification', 'slack:incomingWebhookUrl')) {
-        debug('posting message with IncomingWebhook');
-        return postWithIwh(messageObj);
-      }
-    }
+    return postWithSlackBot(messageObj);
   };
 
   return slack;

+ 4 - 4
src/test/util/slack.test.js → src/test/util/slack-legacy.test.js

@@ -3,19 +3,19 @@ const { getInstance } = require('../setup-crowi');
 describe('Slack Util', () => {
 
   let crowi;
-  let slack;
+  let slackLegacy;
 
   beforeEach(async(done) => {
     crowi = await getInstance();
-    slack = require(`${crowi.libDir}/util/slack`)(crowi);
+    slackLegacy = require(`${crowi.libDir}/util/slack-legacy`)(crowi);
     done();
   });
 
   test('post comment method exists', () => {
-    expect(slack.postComment).toBeInstanceOf(Function);
+    expect(slackLegacy.postComment).toBeInstanceOf(Function);
   });
 
   test('post page method exists', () => {
-    expect(slack.postPage).toBeInstanceOf(Function);
+    expect(slackLegacy.postPage).toBeInstanceOf(Function);
   });
 });

+ 126 - 38
yarn.lock

@@ -1827,6 +1827,24 @@
     raw-body "^2.3.3"
     tsscmp "^1.0.6"
 
+"@slack/events-api@^3.0.0":
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/@slack/events-api/-/events-api-3.0.0.tgz#3e7626ceb5700cb1cce2fbfe6e1583c23a3626fd"
+  integrity sha512-4LdeVuyBCoESSMryACZNafSM4GlX9o881WShABcW+nonh97PnxUKI+hZwuYdTLQpyKu2HSp1zcWsi8QLaiXkrw==
+  dependencies:
+    "@types/debug" "^4.1.4"
+    "@types/express" "^4.17.0"
+    "@types/lodash.isstring" "^4.0.6"
+    "@types/node" ">=12.13.0 < 13"
+    "@types/yargs" "^15.0.4"
+    debug "^2.6.1"
+    lodash.isstring "^4.0.1"
+    raw-body "^2.3.3"
+    tsscmp "^1.0.6"
+    yargs "^15.3.1"
+  optionalDependencies:
+    express "^4.0.0"
+
 "@slack/logger@>=1.0.0 <3.0.0", "@slack/logger@^2.0.0":
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/@slack/logger/-/logger-2.0.0.tgz#6a4e1c755849bc0f66dac08a8be54ce790ec0e6b"
@@ -1911,6 +1929,22 @@
     p-queue "^6.6.1"
     p-retry "^4.0.0"
 
+"@slack/web-api@^6.1.0":
+  version "6.1.0"
+  resolved "https://registry.yarnpkg.com/@slack/web-api/-/web-api-6.1.0.tgz#27a17f61eb72100d6722ff17f581349c41d19b5f"
+  integrity sha512-9MVHw+rDBaFvkvzm8lDNH/nlkvJCDKRIjFGMdpbyZlVLsm4rcht4qyiL71bqdyLATHXJnWknb/sl0FQGLLobIA==
+  dependencies:
+    "@slack/logger" ">=1.0.0 <3.0.0"
+    "@slack/types" "^1.7.0"
+    "@types/is-stream" "^1.1.0"
+    "@types/node" ">=12.0.0"
+    axios "^0.21.1"
+    eventemitter3 "^3.1.0"
+    form-data "^2.5.0"
+    is-stream "^1.1.0"
+    p-queue "^6.6.1"
+    p-retry "^4.0.0"
+
 "@types/babel__core@^7.1.0":
   version "7.1.2"
   resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.2.tgz#608c74f55928033fce18b99b213c16be4b3d114f"
@@ -1956,6 +1990,11 @@
   resolved "https://registry.yarnpkg.com/@types/color-name/-/color-name-1.1.1.tgz#1c1261bbeaa10a8055bbc5d8ab84b7b2afc846a0"
   integrity sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==
 
+"@types/debug@^4.1.4":
+  version "4.1.5"
+  resolved "https://registry.yarnpkg.com/@types/debug/-/debug-4.1.5.tgz#b14efa8852b7768d898906613c23f688713e02cd"
+  integrity sha512-Q1y515GcOdTHgagaVFhHnIFQ38ygs/kmxdNpvpou+raI9UO3YZcHDngBSYKQklcKlvA7iuQlmIKbzvmxcOE9CQ==
+
 "@types/express-serve-static-core@*":
   version "4.11.0"
   resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.11.0.tgz#aaaf472777191c3e56ec7aa160034c6b55ebdd59"
@@ -1979,7 +2018,7 @@
     "@types/express-serve-static-core" "*"
     "@types/serve-static" "*"
 
-"@types/express@^4.16.1":
+"@types/express@^4.16.1", "@types/express@^4.17.0":
   version "4.17.11"
   resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.11.tgz#debe3caa6f8e5fcda96b47bd54e2f40c4ee59545"
   integrity sha512-no+R6rW60JEc59977wIxreQVsIEOAYwgCqldrA/vkpCnbD7MqTefO97lmoBe4WE0F156bC4uLSP1XHDOySnChg==
@@ -2039,6 +2078,18 @@
   dependencies:
     "@types/node" "*"
 
+"@types/lodash.isstring@^4.0.6":
+  version "4.0.6"
+  resolved "https://registry.yarnpkg.com/@types/lodash.isstring/-/lodash.isstring-4.0.6.tgz#1534d0c19a2ad79caa17558a298e366893ffd08c"
+  integrity sha512-uUGvF9G1G7jQ5H42Y38GA9rZmUoY8wI/OMSwnW0BZA+Ra0uxzpuQf4CixXl3yG3TvF6LjuduMyt1WvKl+je8QA==
+  dependencies:
+    "@types/lodash" "*"
+
+"@types/lodash@*":
+  version "4.14.168"
+  resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.168.tgz#fe24632e79b7ade3f132891afff86caa5e5ce008"
+  integrity sha512-oVfRvqHV/V6D1yifJbVRU3TMp8OT6o6BG+U9MkwuJ3U8/CsDHvalRpsxBqivn71ztOFZBTfJMvETbqHiaNSj7Q==
+
 "@types/mime@*":
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/@types/mime/-/mime-2.0.0.tgz#5a7306e367c539b9f6543499de8dd519fac37a8b"
@@ -2057,6 +2108,11 @@
   resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.21.tgz#d934aacc22424fe9622ebf6857370c052eae464e"
   integrity sha512-cHYfKsnwllYhjOzuC5q1VpguABBeecUp24yFluHpn/BQaVxB1CuQ1FSRZCzrPxrkIfWISXV2LbeoBthLWg0+0A==
 
+"@types/node@>=12.13.0 < 13":
+  version "12.20.6"
+  resolved "https://registry.yarnpkg.com/@types/node/-/node-12.20.6.tgz#7b73cce37352936e628c5ba40326193443cfba25"
+  integrity sha512-sRVq8d+ApGslmkE9e3i+D3gFGk7aZHAT+G4cIpIEdLJYPsWiSPwcAnJEjddLQQDqV3Ra2jOclX/Sv6YrvGYiWA==
+
 "@types/node@^7.0.21", "@types/node@^7.0.23":
   version "7.0.52"
   resolved "https://registry.yarnpkg.com/@types/node/-/node-7.0.52.tgz#8990d3350375542b0c21a83cd0331e6a8fc86716"
@@ -2195,6 +2251,13 @@
   dependencies:
     "@types/yargs-parser" "*"
 
+"@types/yargs@^15.0.4":
+  version "15.0.13"
+  resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-15.0.13.tgz#34f7fec8b389d7f3c1fd08026a5763e072d3c6dc"
+  integrity sha512-kQ5JNTrbDv3Rp5X2n/iUu37IJBDU2gsZ5R/g1/KHOOEc5IKfUFjXT6DENPGduh08I/pamwtEq4oul7gUqKTQDQ==
+  dependencies:
+    "@types/yargs-parser" "*"
+
 "@typescript-eslint/experimental-utils@^2.5.0":
   version "2.7.0"
   resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-2.7.0.tgz#58d790a3884df3041b5a5e08f9e5e6b7c41864b5"
@@ -5036,7 +5099,7 @@ debounce@^1.0.0:
   version "1.1.0"
   resolved "https://registry.yarnpkg.com/debounce/-/debounce-1.1.0.tgz#6a1a4ee2a9dc4b7c24bb012558dbcdb05b37f408"
 
-debug@2, debug@2.6.9, debug@^2.0.0, debug@^2.2.0, debug@^2.3.3, debug@^2.6.8, debug@^2.6.9:
+debug@2, debug@2.6.9, debug@^2.0.0, debug@^2.2.0, debug@^2.3.3, debug@^2.6.1, debug@^2.6.8, debug@^2.6.9:
   version "2.6.9"
   resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
   dependencies:
@@ -6285,6 +6348,42 @@ express-webpack-assets@^0.1.0:
   version "0.1.0"
   resolved "https://registry.yarnpkg.com/express-webpack-assets/-/express-webpack-assets-0.1.0.tgz#000fb3413eb0d512cbd6cd3f6a10b5e70dbe0079"
 
+express@^4.0.0, express@^4.16.4:
+  version "4.17.1"
+  resolved "https://registry.yarnpkg.com/express/-/express-4.17.1.tgz#4491fc38605cf51f8629d39c2b5d026f98a4c134"
+  integrity sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==
+  dependencies:
+    accepts "~1.3.7"
+    array-flatten "1.1.1"
+    body-parser "1.19.0"
+    content-disposition "0.5.3"
+    content-type "~1.0.4"
+    cookie "0.4.0"
+    cookie-signature "1.0.6"
+    debug "2.6.9"
+    depd "~1.1.2"
+    encodeurl "~1.0.2"
+    escape-html "~1.0.3"
+    etag "~1.8.1"
+    finalhandler "~1.1.2"
+    fresh "0.5.2"
+    merge-descriptors "1.0.1"
+    methods "~1.1.2"
+    on-finished "~2.3.0"
+    parseurl "~1.3.3"
+    path-to-regexp "0.1.7"
+    proxy-addr "~2.0.5"
+    qs "6.7.0"
+    range-parser "~1.2.1"
+    safe-buffer "5.1.2"
+    send "0.17.1"
+    serve-static "1.14.1"
+    setprototypeof "1.1.1"
+    statuses "~1.5.0"
+    type-is "~1.6.18"
+    utils-merge "1.0.1"
+    vary "~1.1.2"
+
 express@^4.16.1:
   version "4.16.2"
   resolved "https://registry.yarnpkg.com/express/-/express-4.16.2.tgz#e35c6dfe2d64b7dca0a5cd4f21781be3299e076c"
@@ -6355,42 +6454,6 @@ express@^4.16.3:
     utils-merge "1.0.1"
     vary "~1.1.2"
 
-express@^4.16.4:
-  version "4.17.1"
-  resolved "https://registry.yarnpkg.com/express/-/express-4.17.1.tgz#4491fc38605cf51f8629d39c2b5d026f98a4c134"
-  integrity sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==
-  dependencies:
-    accepts "~1.3.7"
-    array-flatten "1.1.1"
-    body-parser "1.19.0"
-    content-disposition "0.5.3"
-    content-type "~1.0.4"
-    cookie "0.4.0"
-    cookie-signature "1.0.6"
-    debug "2.6.9"
-    depd "~1.1.2"
-    encodeurl "~1.0.2"
-    escape-html "~1.0.3"
-    etag "~1.8.1"
-    finalhandler "~1.1.2"
-    fresh "0.5.2"
-    merge-descriptors "1.0.1"
-    methods "~1.1.2"
-    on-finished "~2.3.0"
-    parseurl "~1.3.3"
-    path-to-regexp "0.1.7"
-    proxy-addr "~2.0.5"
-    qs "6.7.0"
-    range-parser "~1.2.1"
-    safe-buffer "5.1.2"
-    send "0.17.1"
-    serve-static "1.14.1"
-    setprototypeof "1.1.1"
-    statuses "~1.5.0"
-    type-is "~1.6.18"
-    utils-merge "1.0.1"
-    vary "~1.1.2"
-
 extend-shallow@^2.0.1:
   version "2.0.1"
   resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f"
@@ -16760,6 +16823,14 @@ yargs-parser@^16.1.0:
     camelcase "^5.0.0"
     decamelize "^1.2.0"
 
+yargs-parser@^18.1.2:
+  version "18.1.3"
+  resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-18.1.3.tgz#be68c4975c6b2abf469236b0c870362fab09a7b0"
+  integrity sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==
+  dependencies:
+    camelcase "^5.0.0"
+    decamelize "^1.2.0"
+
 yargs-parser@^4.1.0, yargs-parser@^4.2.0:
   version "4.2.1"
   resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-4.2.1.tgz#29cceac0dc4f03c6c87b4a9f217dd18c9f74871c"
@@ -16871,6 +16942,23 @@ yargs@^15.0.0:
     y18n "^4.0.0"
     yargs-parser "^16.1.0"
 
+yargs@^15.3.1:
+  version "15.4.1"
+  resolved "https://registry.yarnpkg.com/yargs/-/yargs-15.4.1.tgz#0d87a16de01aee9d8bec2bfbf74f67851730f4f8"
+  integrity sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==
+  dependencies:
+    cliui "^6.0.0"
+    decamelize "^1.2.0"
+    find-up "^4.1.0"
+    get-caller-file "^2.0.1"
+    require-directory "^2.1.1"
+    require-main-filename "^2.0.0"
+    set-blocking "^2.0.0"
+    string-width "^4.2.0"
+    which-module "^2.0.0"
+    y18n "^4.0.0"
+    yargs-parser "^18.1.2"
+
 yargs@~1.2.6:
   version "1.2.6"
   resolved "https://registry.yarnpkg.com/yargs/-/yargs-1.2.6.tgz#9c7b4a82fd5d595b2bf17ab6dcc43135432fe34b"