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

Merge pull request #4185 from weseek/imprv/autojoin-when-not-in-channel

Imprv/autojoin when not in channel
Yuki Takei 4 лет назад
Родитель
Сommit
cad3c524f9

+ 4 - 4
packages/app/src/server/routes/apiv3/notification-setting.js

@@ -1,6 +1,8 @@
 import loggerFactory from '~/utils/logger';
 import loggerFactory from '~/utils/logger';
 import { removeNullPropertyFromObject } from '~/utils/object-utils';
 import { removeNullPropertyFromObject } from '~/utils/object-utils';
 
 
+import UpdatePost from '../../models/update-post';
+
 // eslint-disable-next-line no-unused-vars
 // eslint-disable-next-line no-unused-vars
 const logger = loggerFactory('growi:routes:apiv3:notification-setting');
 const logger = loggerFactory('growi:routes:apiv3:notification-setting');
 
 
@@ -107,7 +109,6 @@ module.exports = (crowi) => {
   const csrf = require('../../middlewares/csrf')(crowi);
   const csrf = require('../../middlewares/csrf')(crowi);
   const apiV3FormValidator = require('../../middlewares/apiv3-form-validator')(crowi);
   const apiV3FormValidator = require('../../middlewares/apiv3-form-validator')(crowi);
 
 
-  const UpdatePost = crowi.model('UpdatePost');
   const GlobalNotificationSetting = crowi.model('GlobalNotificationSetting');
   const GlobalNotificationSetting = crowi.model('GlobalNotificationSetting');
 
 
   const GlobalNotificationMailSetting = crowi.models.GlobalNotificationMailSetting;
   const GlobalNotificationMailSetting = crowi.models.GlobalNotificationMailSetting;
@@ -220,12 +221,11 @@ module.exports = (crowi) => {
   */
   */
   router.post('/user-notification', loginRequiredStrictly, adminRequired, csrf, validator.userNotification, apiV3FormValidator, async(req, res) => {
   router.post('/user-notification', loginRequiredStrictly, adminRequired, csrf, validator.userNotification, apiV3FormValidator, async(req, res) => {
     const { pathPattern, channel } = req.body;
     const { pathPattern, channel } = req.body;
-    const UpdatePost = crowi.model('UpdatePost');
 
 
     try {
     try {
       logger.info('notification.add', pathPattern, channel);
       logger.info('notification.add', pathPattern, channel);
       const responseParams = {
       const responseParams = {
-        createdUser: await UpdatePost.create(pathPattern, channel, req.user),
+        createdUser: await UpdatePost.createUpdatePost(pathPattern, channel, req.user),
         userNotifications: await UpdatePost.findAll(),
         userNotifications: await UpdatePost.findAll(),
       };
       };
       return res.apiv3({ responseParams }, 201);
       return res.apiv3({ responseParams }, 201);
@@ -267,7 +267,7 @@ module.exports = (crowi) => {
     const { id } = req.params;
     const { id } = req.params;
 
 
     try {
     try {
-      const deletedNotificaton = await UpdatePost.remove(id);
+      const deletedNotificaton = await UpdatePost.findOneAndRemove({ _id: id });
       return res.apiv3(deletedNotificaton);
       return res.apiv3(deletedNotificaton);
     }
     }
     catch (err) {
     catch (err) {

+ 2 - 1
packages/app/src/server/routes/page.js

@@ -1,6 +1,8 @@
 import { pagePathUtils } from '@growi/core';
 import { pagePathUtils } from '@growi/core';
 import loggerFactory from '~/utils/logger';
 import loggerFactory from '~/utils/logger';
 
 
+import UpdatePost from '../models/update-post';
+
 const { isCreatablePage } = pagePathUtils;
 const { isCreatablePage } = pagePathUtils;
 const { serializePageSecurely } = require('../models/serializers/page-serializer');
 const { serializePageSecurely } = require('../models/serializers/page-serializer');
 const { serializeRevisionSecurely } = require('../models/serializers/revision-serializer');
 const { serializeRevisionSecurely } = require('../models/serializers/revision-serializer');
@@ -1118,7 +1120,6 @@ module.exports = function(crowi, app) {
    */
    */
   api.getUpdatePost = function(req, res) {
   api.getUpdatePost = function(req, res) {
     const path = req.query.path;
     const path = req.query.path;
-    const UpdatePost = crowi.model('UpdatePost');
 
 
     if (!path) {
     if (!path) {
       return res.json(ApiResponse.error({}));
       return res.json(ApiResponse.error({}));

+ 1 - 0
packages/slack/src/utils/required-scopes.ts

@@ -2,6 +2,7 @@ export const requiredScopes: string[] = [
   'commands',
   'commands',
   'team:read',
   'team:read',
   'chat:write',
   'chat:write',
+  'chat:write.public',
   'channels:join',
   'channels:join',
   'channels:history',
   'channels:history',
   'groups:history',
   'groups:history',

+ 21 - 6
packages/slack/src/utils/webclient-factory.ts

@@ -1,12 +1,27 @@
-import { LogLevel, WebClient } from '@slack/web-api';
+import { LogLevel, WebClient, WebClientOptions } from '@slack/web-api';
 
 
 const isProduction = process.env.NODE_ENV === 'production';
 const isProduction = process.env.NODE_ENV === 'production';
+const logLevel: LogLevel = isProduction ? LogLevel.DEBUG : LogLevel.INFO;
 
 
 /**
 /**
  * Generate WebClilent instance
  * Generate WebClilent instance
- * @param token Slack Bot Token or Proxy Server URI
- * @returns
+ * @param token
+ * @param serverUri Slack Bot Token or Proxy Server URI
+ * @param headers
  */
  */
-export const generateWebClient = (token?: string, serverUri?: string, headers?:{[key:string]:string}): WebClient => {
-  return new WebClient(token, { slackApiUrl: serverUri, logLevel: isProduction ? LogLevel.DEBUG : LogLevel.INFO, headers });
-};
+export function generateWebClient(token?: string, serverUri?: string, headers?:{[key:string]:string}): WebClient;
+
+/**
+ * Generate WebClilent instance
+ * @param token
+ * @param opts
+ */
+export function generateWebClient(token?: string, opts?: WebClientOptions): WebClient;
+
+export function generateWebClient(token?: string, ...args: any[]): WebClient {
+  if (typeof args[0] === 'string') {
+    return new WebClient(token, { logLevel, slackApiUrl: args[0], headers: args[1] });
+  }
+
+  return new WebClient(token, { logLevel, ...args });
+}

+ 20 - 10
packages/slackbot-proxy/src/controllers/growi-to-slack.ts

@@ -5,10 +5,10 @@ import axios from 'axios';
 import createError from 'http-errors';
 import createError from 'http-errors';
 import { addHours } from 'date-fns';
 import { addHours } from 'date-fns';
 
 
-import { WebAPICallResult } from '@slack/web-api';
+import { ErrorCode, WebAPICallResult } from '@slack/web-api';
 
 
 import {
 import {
-  verifyGrowiToSlackRequest, getConnectionStatuses, getConnectionStatus, generateWebClient, REQUEST_TIMEOUT_FOR_PTOG,
+  verifyGrowiToSlackRequest, getConnectionStatuses, getConnectionStatus, REQUEST_TIMEOUT_FOR_PTOG, generateWebClient,
 } from '@growi/slack';
 } from '@growi/slack';
 
 
 import { WebclientRes, AddWebclientResponseToRes } from '~/middlewares/growi-to-slack/add-webclient-response-to-res';
 import { WebclientRes, AddWebclientResponseToRes } from '~/middlewares/growi-to-slack/add-webclient-response-to-res';
@@ -246,13 +246,13 @@ export class GrowiToSlackCtrl {
   @UseBefore(AddWebclientResponseToRes, verifyGrowiToSlackRequest)
   @UseBefore(AddWebclientResponseToRes, verifyGrowiToSlackRequest)
   async callSlackApi(
   async callSlackApi(
     @PathParams('method') method: string, @Req() req: GrowiReq, @Res() res: WebclientRes,
     @PathParams('method') method: string, @Req() req: GrowiReq, @Res() res: WebclientRes,
-  ): Promise<void|WebAPICallResult> {
+  ): Promise<WebclientRes> {
     const { tokenGtoPs } = req;
     const { tokenGtoPs } = req;
 
 
     logger.debug('Slack API called: ', { method });
     logger.debug('Slack API called: ', { method });
 
 
     if (tokenGtoPs.length !== 1) {
     if (tokenGtoPs.length !== 1) {
-      return res.webClientErr('tokenGtoPs is invalid', 'invalid_tokenGtoP');
+      return res.simulateWebAPIPlatformError('tokenGtoPs is invalid', 'invalid_tokenGtoP');
     }
     }
 
 
     const tokenGtoP = tokenGtoPs[0];
     const tokenGtoP = tokenGtoPs[0];
@@ -264,15 +264,18 @@ export class GrowiToSlackCtrl {
       .getOne();
       .getOne();
 
 
     if (relation == null) {
     if (relation == null) {
-      return res.webClientErr('relation is invalid', 'invalid_relation');
+      return res.simulateWebAPIPlatformError('relation is invalid', 'invalid_relation');
     }
     }
 
 
     const token = relation.installation.data.bot?.token;
     const token = relation.installation.data.bot?.token;
     if (token == null) {
     if (token == null) {
-      return res.webClientErr('installation is invalid', 'invalid_installation');
+      return res.simulateWebAPIPlatformError('installation is invalid', 'invalid_installation');
     }
     }
 
 
-    const client = generateWebClient(token);
+    // generate WebClient with no retry because GROWI main side will do
+    const client = generateWebClient(token, {
+      retryConfig: { retries: 0 },
+    });
 
 
     try {
     try {
       this.injectGrowiUri(req, relation.growiUri);
       this.injectGrowiUri(req, relation.growiUri);
@@ -281,12 +284,19 @@ export class GrowiToSlackCtrl {
       opt.headers = req.headers;
       opt.headers = req.headers;
 
 
       logger.debug({ method, opt });
       logger.debug({ method, opt });
-      // !! DO NOT REMOVE `await ` or it does not enter catch block even when error occured !! -- 2021.08.22 Yuki Takei
-      return await client.apiCall(method, opt);
+      // !! DO NOT REMOVE `await ` or it does not enter catch block even when axios error occured !! -- 2021.08.22 Yuki Takei
+      const result = await client.apiCall(method, opt);
+
+      return res.send(result);
     }
     }
     catch (err) {
     catch (err) {
       logger.error(err);
       logger.error(err);
-      return res.webClientErr(`failed to send to slack. err: ${err.message}`, 'fail_api_call');
+
+      if (err.code === ErrorCode.PlatformError) {
+        return res.simulateWebAPIPlatformError(err.message, err.code);
+      }
+
+      return res.simulateWebAPIRequestError(err.message, err.response?.status);
     }
     }
   }
   }
 
 

+ 9 - 2
packages/slackbot-proxy/src/middlewares/growi-to-slack/add-webclient-response-to-res.ts

@@ -1,10 +1,12 @@
+import { ErrorCode } from '@slack/web-api';
 import {
 import {
   IMiddleware, Middleware, Next, Req, Res,
   IMiddleware, Middleware, Next, Req, Res,
 } from '@tsed/common';
 } from '@tsed/common';
 
 
 
 
 export type WebclientRes = Res & {
 export type WebclientRes = Res & {
-  webClientErr: (message?:string, errorCode?:string) => void
+  simulateWebAPIRequestError: (error: string, statusCode: number) => WebclientRes
+  simulateWebAPIPlatformError: (error: string, errorCode?:string) => WebclientRes
 };
 };
 
 
 
 
@@ -13,7 +15,12 @@ export class AddWebclientResponseToRes implements IMiddleware {
 
 
   use(@Req() req: Req, @Res() res: WebclientRes, @Next() next: Next): void {
   use(@Req() req: Req, @Res() res: WebclientRes, @Next() next: Next): void {
 
 
-    res.webClientErr = (error?:string, errorCode?:string) => {
+    // https://github.com/slackapi/node-slack-sdk/blob/7b95663a9ef31036367c066ccbf0021423278f40/packages/web-api/src/WebClient.ts#L356-L358
+    res.simulateWebAPIRequestError = (error: string, statusCode?: number) => {
+      return res.status(statusCode || 500).send({ error, errorCode: ErrorCode.RequestError });
+    };
+    // https://github.com/slackapi/node-slack-sdk/blob/7b95663a9ef31036367c066ccbf0021423278f40/packages/web-api/src/WebClient.ts#L197-L199
+    res.simulateWebAPIPlatformError = (error: string, errorCode?: string) => {
       return res.send({ ok: false, error, errorCode });
       return res.send({ ok: false, error, errorCode });
     };
     };