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

refactor SlackIntegrationService to notify

Yuki Takei 4 лет назад
Родитель
Сommit
8ec1fdd199

+ 3 - 26
packages/app/src/server/crowi/index.js

@@ -13,6 +13,7 @@ import { getMongoUri, mongoOptions } from '~/server/util/mongoose-utils';
 import { projectRoot } from '~/utils/project-dir-utils';
 
 import ConfigManager from '../service/config-manager';
+import AppService from '../service/app';
 import AclService from '../service/acl';
 import AttachmentService from '../service/attachment';
 import { SlackIntegrationService } from '../service/slack-integration';
@@ -93,12 +94,10 @@ Crowi.prototype.init = async function() {
 
   // customizeService depends on AppService and XssService
   // passportService depends on appService
-  // slack depends on setupSlackIntegrationService
   // export and import depends on setUpGrowiBridge
   await Promise.all([
     this.setUpApp(),
     this.setUpXss(),
-    this.setupSlackIntegrationService(),
     this.setUpGrowiBridge(),
   ]);
 
@@ -107,8 +106,7 @@ Crowi.prototype.init = async function() {
     this.setupPassport(),
     this.setupSearcher(),
     this.setupMailer(),
-    this.setupSlack(),
-    this.setupSlackLegacy(),
+    this.setupSlackIntegrationService(),
     this.setupCsrf(),
     this.setUpFileUpload(),
     this.setUpFileUploaderSwitchService(),
@@ -136,11 +134,9 @@ Crowi.prototype.initForTest = async function() {
 
   // // customizeService depends on AppService and XssService
   // // passportService depends on appService
-  // // slack depends on setUpSlacklNotification
   await Promise.all([
     this.setUpApp(),
     this.setUpXss(),
-    // this.setUpSlacklNotification(),
     // this.setUpGrowiBridge(),
   ]);
 
@@ -149,7 +145,7 @@ Crowi.prototype.initForTest = async function() {
     this.setupPassport(),
     // this.setupSearcher(),
     // this.setupMailer(),
-    // this.setupSlack(),
+    // this.setupSlackIntegrationService(),
     // this.setupCsrf(),
     // this.setUpFileUpload(),
     this.setupAttachmentService(),
@@ -382,24 +378,6 @@ Crowi.prototype.setupMailer = async function() {
   }
 };
 
-Crowi.prototype.setupSlack = async function() {
-  const self = this;
-
-  return new Promise(((resolve, reject) => {
-    self.slack = require('../util/slack')(self);
-    resolve();
-  }));
-};
-
-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();
@@ -570,7 +548,6 @@ Crowi.prototype.setUpCustomize = async function() {
  * setup AppService
  */
 Crowi.prototype.setUpApp = async function() {
-  const AppService = require('../service/app');
   if (this.appService == null) {
     this.appService = new AppService(this);
 

+ 1 - 3
packages/app/src/server/service/app.ts

@@ -12,7 +12,7 @@ const logger = loggerFactory('growi:service:AppService');
 /**
  * the service class of AppService
  */
-class AppService implements S2sMessageHandlable {
+export default class AppService implements S2sMessageHandlable {
 
   crowi!: any;
 
@@ -131,5 +131,3 @@ class AppService implements S2sMessageHandlable {
   }
 
 }
-
-module.exports = AppService;

+ 11 - 7
packages/app/src/server/service/global-notification/global-notification-slack.js

@@ -2,6 +2,10 @@ import { pagePathUtils } from '@growi/core';
 import loggerFactory from '~/utils/logger';
 
 
+import {
+  prepareSlackMessageForGlobalNotification,
+} from '../../util/slack';
+
 const logger = loggerFactory('growi:service:GlobalNotificationSlackService'); // eslint-disable-line no-unused-vars
 const urljoin = require('url-join');
 
@@ -14,8 +18,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;
   }
@@ -33,20 +36,21 @@ class GlobalNotificationSlackService {
    * @param {{ comment: Comment, oldPath: string }} _ event specific vars
    */
   async fire(event, id, path, triggeredBy, vars) {
+    const { appService, slackIntegrationService } = this.crowi;
+
     const GlobalNotification = this.crowi.model('GlobalNotificationSetting');
     const notifications = await GlobalNotification.findSettingByPathAndEvent(event, path, this.type);
 
     const messageBody = this.generateMessageBody(event, id, path, triggeredBy, vars);
     const attachmentBody = this.generateAttachmentBody(event, id, path, triggeredBy, vars);
 
+    const appTitle = appService.getAppTitle();
+
     await Promise.all(notifications.map((notification) => {
-      return [
-        this.slack.sendGlobalNotification(messageBody, attachmentBody, notification.slackChannels),
-        this.slackLegacy.sendGlobalNotification(messageBody, attachmentBody, notification.slackChannels),
-      ];
+      const messageObj = prepareSlackMessageForGlobalNotification(messageBody, attachmentBody, appTitle, notification.slackChannels);
+      return slackIntegrationService.postMessage(messageObj);
     }));
 
-
   }
 
   /**

+ 10 - 8
packages/app/src/server/service/slack-integration.ts

@@ -147,17 +147,19 @@ export class SlackIntegrationService implements S2sMessageHandlable {
     return generateWebClient(undefined, serverUri.toString(), headers);
   }
 
+  async postMessage(messageArgs: ChatPostMessageArguments): Promise<void> {
+    // TODO: determine target slack workspace
+    const tokenPtoG = '...';
+    const client = await this.generateClient(tokenPtoG);
 
-  async postToSlackForPage(page, user, channel, updateType, previousRevision): Promise<void> {
-
+    try {
+      await client.chat.postMessage(messageArgs);
   }
-
-  async postToSlackForComment(comment, user, channel, path): Promise<void> {
-
+    catch (error) {
+      logger.debug('Post error', error);
+      logger.debug('Sent data to slack is:', messageArgs);
+      throw error;
   }
-
-  async sendGlobalNotification(messageBody, attachmentBody, slackChannel): Promise<void> {
-
   }
 
   /**

+ 15 - 8
packages/app/src/server/service/user-notification/index.js

@@ -1,4 +1,9 @@
-const toArrayFromCsv = require('~/utils/to-array-from-csv');
+import { toArrayFromCsv } from '~/utils/to-array-from-csv';
+
+import {
+  prepareSlackMessageForPage,
+  prepareSlackMessageForComment,
+} from '../../util/slack';
 
 /**
  * service class of UserNotification
@@ -25,7 +30,7 @@ class UserNotificationService {
    */
   async fire(page, user, slackChannelsStr, mode, option, comment = {}) {
     const {
-      slackIntegrationService, slackLegacy, slack,
+      appService, slackIntegrationService,
     } = this.crowi;
 
     const opt = option || {};
@@ -40,17 +45,19 @@ class UserNotificationService {
     // "dev,slacktest" => [dev,slacktest]
     const slackChannels = toArrayFromCsv(slackChannelsStr);
 
+    const appTitle = appService.getAppTitle();
+    const siteUrl = appService.getSiteUrl();
+
     const promises = slackChannels.map(async(chan) => {
-      let res;
+      let messageObj;
       if (mode === 'comment') {
-        res = await slack.postComment(comment, user, chan, page.path);
-        res = await slackLegacy.postComment(comment, user, chan, page.path);
+        messageObj = prepareSlackMessageForComment(comment, user, appTitle, siteUrl, chan, page.path);
       }
       else {
-        res = await slack.postPage(page, user, chan, mode, previousRevision);
-        res = await slackLegacy.postPage(page, user, chan, mode, previousRevision);
+        messageObj = prepareSlackMessageForPage(page, user, appTitle, siteUrl, chan, mode, previousRevision);
       }
-      return res;
+
+      return slackIntegrationService.postMessage(messageObj);
     });
 
     return Promise.allSettled(promises);

+ 123 - 182
packages/app/src/server/util/slack.js

@@ -7,219 +7,160 @@ const urljoin = require('url-join');
 
 /* eslint-disable no-use-before-define */
 
-module.exports = function(crowi) {
-  const { WebClient } = require('@slack/web-api');
+const convertMarkdownToMarkdown = function(body, siteUrl) {
+  return body
+    .replace(/\n\*\s(.+)/g, '\n• $1')
+    .replace(/#{1,}\s?(.+)/g, '\n*$1*')
+    .replace(/(\[(.+)\]\((https?:\/\/.+)\))/g, '<$3|$2>')
+    .replace(/(\[(.+)\]\((\/.+)\))/g, `<${siteUrl}$3|$2>`);
+};
+
+const prepareAttachmentTextForCreate = function(page, siteUrl) {
+  let body = page.revision.body;
+  if (body.length > 2000) {
+    body = `${body.substr(0, 2000)}...`;
+  }
+
+  return convertMarkdownToMarkdown(body, siteUrl);
+};
 
-  const { configManager } = crowi;
-  const slack = {};
+const prepareAttachmentTextForUpdate = function(page, siteUrl, previousRevision) {
+  const diff = require('diff');
+  let diffText = '';
 
-  const postWithWebApi = async(messageObj) => {
-    const client = new WebClient(configManager.getConfig('notification', 'slack:token'));
-    // stringify attachments
-    if (messageObj.attachments != null) {
-      messageObj.attachments = JSON.stringify(messageObj.attachments);
+  diff.diffLines(previousRevision.body, page.revision.body).forEach((line) => {
+    debug('diff line', line);
+    const value = line.value.replace(/\r\n|\r/g, '\n'); // eslint-disable-line no-unused-vars
+    if (line.added) {
+      diffText += `${line.value} ... :lower_left_fountain_pen:`;
     }
-    try {
-      await client.chat.postMessage(messageObj);
+    else if (line.removed) {
+      // diffText += '-' + line.value.replace(/(.+)?\n/g, '- $1\n');
+      // 1以下は無視
+      if (line.count > 1) {
+        diffText += `:wastebasket: ... ${line.count} lines\n`;
+      }
     }
-    catch (error) {
-      debug('Post error', error);
-      debug('Sent data to slack is:', messageObj);
-      throw error;
+    else {
+      // diffText += '...\n';
     }
-  };
+  });
 
-  const convertMarkdownToMarkdown = function(body) {
-    const url = crowi.appService.getSiteUrl();
+  debug('diff is', diffText);
 
-    return body
-      .replace(/\n\*\s(.+)/g, '\n• $1')
-      .replace(/#{1,}\s?(.+)/g, '\n*$1*')
-      .replace(/(\[(.+)\]\((https?:\/\/.+)\))/g, '<$3|$2>')
-      .replace(/(\[(.+)\]\((\/.+)\))/g, `<${url}$3|$2>`);
-  };
+  return diffText;
+};
 
-  const prepareAttachmentTextForCreate = function(page, user) {
-    let body = page.revision.body;
-    if (body.length > 2000) {
-      body = `${body.substr(0, 2000)}...`;
-    }
+const prepareAttachmentTextForComment = function(comment) {
+  let body = comment.comment;
+  if (body.length > 2000) {
+    body = `${body.substr(0, 2000)}...`;
+  }
 
+  if (comment.isMarkdown) {
     return convertMarkdownToMarkdown(body);
-  };
+  }
 
-  const prepareAttachmentTextForUpdate = function(page, user, previousRevision) {
-    const diff = require('diff');
-    let diffText = '';
-
-    diff.diffLines(previousRevision.body, page.revision.body).forEach((line) => {
-      debug('diff line', line);
-      const value = line.value.replace(/\r\n|\r/g, '\n'); // eslint-disable-line no-unused-vars
-      if (line.added) {
-        diffText += `${line.value} ... :lower_left_fountain_pen:`;
-      }
-      else if (line.removed) {
-        // diffText += '-' + line.value.replace(/(.+)?\n/g, '- $1\n');
-        // 1以下は無視
-        if (line.count > 1) {
-          diffText += `:wastebasket: ... ${line.count} lines\n`;
-        }
-      }
-      else {
-        // diffText += '...\n';
-      }
-    });
+  return body;
+};
 
-    debug('diff is', diffText);
+const generateSlackMessageTextForPage = function(path, pageId, user, siteUrl, updateType) {
+  let text;
 
-    return diffText;
-  };
+  const pageUrl = `<${urljoin(siteUrl, pageId)}|${path}>`;
+  if (updateType === 'create') {
+    text = `:rocket: ${user.username} created a new page! ${pageUrl}`;
+  }
+  else {
+    text = `:heavy_check_mark: ${user.username} updated ${pageUrl}`;
+  }
 
-  const prepareAttachmentTextForComment = function(comment) {
-    let body = comment.comment;
-    if (body.length > 2000) {
-      body = `${body.substr(0, 2000)}...`;
-    }
-
-    if (comment.isMarkdown) {
-      return convertMarkdownToMarkdown(body);
-    }
+  return text;
+};
 
-    return body;
+export const prepareSlackMessageForPage = (page, user, appTitle, siteUrl, channel, updateType, previousRevision) => {
+  let body = page.revision.body;
+
+  if (updateType === 'create') {
+    body = prepareAttachmentTextForCreate(page, siteUrl);
+  }
+  else {
+    body = prepareAttachmentTextForUpdate(page, siteUrl, previousRevision);
+  }
+
+  const attachment = {
+    color: '#263a3c',
+    author_name: `@${user.username}`,
+    author_link: urljoin(siteUrl, 'user', user.username),
+    author_icon: user.image,
+    title: page.path,
+    title_link: urljoin(siteUrl, page.id),
+    text: body,
+    mrkdwn_in: ['text'],
   };
-
-  slack.prepareSlackMessageForPage = (page, user, channel, updateType, previousRevision) => {
-    const appTitle = crowi.appService.getAppTitle();
-    const url = crowi.appService.getSiteUrl();
-    let body = page.revision.body;
-
-    if (updateType === 'create') {
-      body = prepareAttachmentTextForCreate(page, user);
-    }
-    else {
-      body = prepareAttachmentTextForUpdate(page, user, previousRevision);
-    }
-
-    const attachment = {
-      color: '#263a3c',
-      author_name: `@${user.username}`,
-      author_link: urljoin(url, 'user', user.username),
-      author_icon: user.image,
-      title: page.path,
-      title_link: urljoin(url, page.id),
-      text: body,
-      mrkdwn_in: ['text'],
-    };
-    if (user.image) {
-      attachment.author_icon = user.image;
-    }
-
-    const message = {
-      channel: (channel != null) ? `#${channel}` : undefined,
-      username: appTitle,
-      text: getSlackMessageTextForPage(page.path, page.id, user, updateType),
-      attachments: [attachment],
-    };
-
-    return message;
+  if (user.image) {
+    attachment.author_icon = user.image;
+  }
+
+  const message = {
+    channel: (channel != null) ? `#${channel}` : undefined,
+    username: appTitle,
+    text: generateSlackMessageTextForPage(page.path, page.id, user, siteUrl, updateType),
+    attachments: [attachment],
   };
 
-  slack.prepareSlackMessageForComment = (comment, user, channel, path) => {
-    const appTitle = crowi.appService.getAppTitle();
-    const url = crowi.appService.getSiteUrl();
-    const body = prepareAttachmentTextForComment(comment);
-
-    const attachment = {
-      color: '#263a3c',
-      author_name: `@${user.username}`,
-      author_link: urljoin(url, 'user', user.username),
-      author_icon: user.image,
-      text: body,
-      mrkdwn_in: ['text'],
-    };
-    if (user.image) {
-      attachment.author_icon = user.image;
-    }
+  return message;
+};
 
-    const message = {
-      channel: (channel != null) ? `#${channel}` : undefined,
-      username: appTitle,
-      text: getSlackMessageTextForComment(path, String(comment.page), user),
-      attachments: [attachment],
-    };
+export const prepareSlackMessageForComment = (comment, user, appTitle, siteUrl, channel, path) => {
+  const body = prepareAttachmentTextForComment(comment);
 
-    return message;
+  const attachment = {
+    color: '#263a3c',
+    author_name: `@${user.username}`,
+    author_link: urljoin(siteUrl, 'user', user.username),
+    author_icon: user.image,
+    text: body,
+    mrkdwn_in: ['text'],
+  };
+  if (user.image) {
+    attachment.author_icon = user.image;
+  }
+
+  const pageUrl = `<${urljoin(siteUrl, String(comment.page))}|${path}>`;
+  const text = `:speech_balloon: ${user.username} commented on ${pageUrl}`;
+
+  const message = {
+    channel: (channel != null) ? `#${channel}` : undefined,
+    username: appTitle,
+    text,
+    attachments: JSON.stringify([attachment]),
   };
 
-  /**
+  return message;
+};
+
+/**
    * For GlobalNotification
    *
    * @param {string} messageBody
    * @param {string} attachmentBody
    * @param {string} slackChannel
   */
-  slack.prepareSlackMessageForGlobalNotification = async(messageBody, attachmentBody, slackChannel) => {
-    const appTitle = crowi.appService.getAppTitle();
-
-    const attachment = {
-      color: '#263a3c',
-      text: attachmentBody,
-      mrkdwn_in: ['text'],
-    };
-
-    const message = {
-      channel: (slackChannel != null) ? `#${slackChannel}` : undefined,
-      username: appTitle,
-      text: messageBody,
-      attachments: [attachment],
-    };
-
-    return message;
-  };
-
-  const getSlackMessageTextForPage = function(path, pageId, user, updateType) {
-    let text;
-    const url = crowi.appService.getSiteUrl();
-
-    const pageUrl = `<${urljoin(url, pageId)}|${path}>`;
-    if (updateType === 'create') {
-      text = `:rocket: ${user.username} created a new page! ${pageUrl}`;
-    }
-    else {
-      text = `:heavy_check_mark: ${user.username} updated ${pageUrl}`;
-    }
-
-    return text;
-  };
-
-  const getSlackMessageTextForComment = function(path, pageId, user) {
-    const url = crowi.appService.getSiteUrl();
-    const pageUrl = `<${urljoin(url, pageId)}|${path}>`;
-    const text = `:speech_balloon: ${user.username} commented on ${pageUrl}`;
-
-    return text;
-  };
-
-  slack.postPage = (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 = slack.prepareSlackMessageForComment(comment, user, channel, path);
-
-    return slackPost(messageObj);
-  };
+export const prepareSlackMessageForGlobalNotification = (messageBody, attachmentBody, appTitle, slackChannel) => {
 
-  slack.sendGlobalNotification = async(messageBody, attachmentBody, slackChannel) => {
-    const messageObj = await slack.prepareSlackMessageForGlobalNotification(messageBody, attachmentBody, slackChannel);
-    return slackPost(messageObj);
+  const attachment = {
+    color: '#263a3c',
+    text: attachmentBody,
+    mrkdwn_in: ['text'],
   };
 
-  const slackPost = (messageObj) => {
-    return postWithWebApi(messageObj);
+  const message = {
+    channel: (slackChannel != null) ? `#${slackChannel}` : undefined,
+    username: appTitle,
+    text: messageBody,
+    attachments: JSON.stringify([attachment]),
   };
 
-  return slack;
+  return message;
 };