Browse Source

Merge pull request #3934 from weseek/imprv/6533-refactoring

Imprv/6533 refactoring
Yuki Takei 4 years ago
parent
commit
b748219352

+ 48 - 33
packages/slackbot-proxy/src/controllers/slack.ts

@@ -7,7 +7,7 @@ import axios from 'axios';
 import { WebAPICallResult } from '@slack/web-api';
 
 import {
-  generateMarkdownSectionBlock, parseSlashCommand, postEphemeralErrors, verifySlackRequest,
+  generateMarkdownSectionBlock, GrowiCommand, parseSlashCommand, postEphemeralErrors, verifySlackRequest,
 } from '@growi/slack';
 
 import { Relation } from '~/entities/relation';
@@ -19,7 +19,7 @@ import { AddSigningSecretToReq } from '~/middlewares/slack-to-growi/add-signing-
 import { AuthorizeCommandMiddleware, AuthorizeInteractionMiddleware } from '~/middlewares/slack-to-growi/authorizer';
 import { ExtractGrowiUriFromReq } from '~/middlewares/slack-to-growi/extract-growi-uri-from-req';
 import { InstallerService } from '~/services/InstallerService';
-import { SelectRequestService } from '~/services/SelectRequestService';
+import { SelectGrowiService } from '~/services/SelectGrowiService';
 import { RegisterService } from '~/services/RegisterService';
 import { UnregisterService } from '~/services/UnregisterService';
 import { InvalidUrlError } from '../models/errors';
@@ -45,7 +45,7 @@ export class SlackCtrl {
   orderRepository: OrderRepository;
 
   @Inject()
-  selectRequestService: SelectRequestService;
+  selectGrowiService: SelectGrowiService;
 
   @Inject()
   registerService: RegisterService;
@@ -74,6 +74,45 @@ export class SlackCtrl {
       + '</a>';
   }
 
+  /**
+   * Send command to specified GROWIs
+   * @param growiCommand
+   * @param relations
+   * @param body
+   * @returns
+   */
+  private async sendCommand(growiCommand: GrowiCommand, relations: Relation[], body: any) {
+    if (relations.length === 0) {
+      throw new Error('relations must be set');
+    }
+    const botToken = relations[0].installation?.data.bot?.token; // relations[0] should be exist
+
+    const promises = relations.map((relation: Relation) => {
+      // generate API URL
+      const url = new URL('/_api/v3/slack-integration/proxied/commands', relation.growiUri);
+      return axios.post(url.toString(), {
+        ...body,
+        growiCommand,
+      }, {
+        headers: {
+          'x-growi-ptog-tokens': relation.tokenPtoG,
+        },
+      });
+    });
+
+    // pickup PromiseRejectedResult only
+    const results = await Promise.allSettled(promises);
+    const rejectedResults: PromiseRejectedResult[] = results.filter((result): result is PromiseRejectedResult => result.status === 'rejected');
+
+    try {
+      // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+      return postEphemeralErrors(rejectedResults, body.channel_id, body.user_id, botToken!);
+    }
+    catch (err) {
+      logger.error(err);
+    }
+  }
+
   @Post('/commands')
   @UseBefore(AddSigningSecretToReq, verifySlackRequest, AuthorizeCommandMiddleware)
   async handleCommand(@Req() req: SlackOauthReq, @Res() res: Res): Promise<void|string|Res|WebAPICallResult> {
@@ -146,37 +185,13 @@ export class SlackCtrl {
     });
 
     if (body.growiUris != null && body.growiUris.length > 0) {
-      return this.selectRequestService.process(growiCommand, authorizeResult, body);
+      return this.selectGrowiService.process(growiCommand, authorizeResult, body);
     }
 
     /*
      * forward to GROWI server
      */
-    const promises = relations.map((relation: Relation) => {
-      // generate API URL
-      const url = new URL('/_api/v3/slack-integration/proxied/commands', relation.growiUri);
-      return axios.post(url.toString(), {
-        ...body,
-        growiCommand,
-      }, {
-        headers: {
-          'x-growi-ptog-tokens': relation.tokenPtoG,
-        },
-      });
-    });
-
-    // pickup PromiseRejectedResult only
-    const results = await Promise.allSettled(promises);
-    const rejectedResults: PromiseRejectedResult[] = results.filter((result): result is PromiseRejectedResult => result.status === 'rejected');
-    const botToken = installation?.data.bot?.token;
-
-    try {
-      // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
-      return postEphemeralErrors(rejectedResults, body.channel_id, body.user_id, botToken!);
-    }
-    catch (err) {
-      logger.error(err);
-    }
+    this.sendCommand(growiCommand, relations, body);
   }
 
   @Post('/interactions')
@@ -206,7 +221,7 @@ export class SlackCtrl {
     // register
     if (callBackId === 'register') {
       try {
-        await this.registerService.insertOrderRecord(this.orderRepository, installation, authorizeResult.botToken, payload);
+        await this.registerService.insertOrderRecord(installation, authorizeResult.botToken, payload);
       }
       catch (err) {
         if (err instanceof InvalidUrlError) {
@@ -222,14 +237,14 @@ export class SlackCtrl {
 
     // unregister
     if (callBackId === 'unregister') {
-      await this.unregisterService.unregister(this.relationRepository, installation, authorizeResult, payload);
+      await this.unregisterService.unregister(installation, authorizeResult, payload);
       return;
     }
 
     // forward to GROWI server
     if (callBackId === 'select_growi') {
-      await this.selectRequestService.forwardRequest(this.relationRepository, installation, payload);
-      return;
+      const selectedGrowiInformation = await this.selectGrowiService.handleSelectInteraction(installation, payload);
+      return this.sendCommand(selectedGrowiInformation.growiCommand, [selectedGrowiInformation.relation], selectedGrowiInformation.sendCommandBody);
     }
 
     /*

+ 6 - 3
packages/slackbot-proxy/src/services/RegisterService.ts

@@ -1,4 +1,4 @@
-import { Service } from '@tsed/di';
+import { Inject, Service } from '@tsed/di';
 import { WebClient, LogLevel } from '@slack/web-api';
 import { generateInputSectionBlock, GrowiCommand, generateMarkdownSectionBlock } from '@growi/slack';
 import { AuthorizeResult } from '@slack/oauth';
@@ -12,6 +12,9 @@ const isProduction = process.env.NODE_ENV === 'production';
 @Service()
 export class RegisterService implements GrowiCommandProcessor {
 
+  @Inject()
+  orderRepository: OrderRepository;
+
   async process(growiCommand: GrowiCommand, authorizeResult: AuthorizeResult, body: {[key:string]:string}): Promise<void> {
     const { botToken } = authorizeResult;
 
@@ -45,7 +48,7 @@ export class RegisterService implements GrowiCommandProcessor {
   }
 
   async insertOrderRecord(
-      orderRepository: OrderRepository, installation: Installation | undefined,
+      installation: Installation | undefined,
       // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
       botToken: string | undefined, payload: any,
   ): Promise<void> {
@@ -79,7 +82,7 @@ export class RegisterService implements GrowiCommandProcessor {
       throw new InvalidUrlError(growiUrl);
     }
 
-    orderRepository.save({
+    this.orderRepository.save({
       installation, growiUrl, tokenPtoG, tokenGtoP,
     });
   }

+ 25 - 22
packages/slackbot-proxy/src/services/SelectRequestService.ts → packages/slackbot-proxy/src/services/SelectGrowiService.ts

@@ -1,16 +1,25 @@
-import { Service } from '@tsed/di';
-import axios from 'axios';
+import { Inject, Service } from '@tsed/di';
 
 import { GrowiCommand, generateWebClient } from '@growi/slack';
 import { AuthorizeResult } from '@slack/oauth';
 
 import { GrowiCommandProcessor } from '~/interfaces/slack-to-growi/growi-command-processor';
-import { RelationRepository } from '~/repositories/relation';
 import { Installation } from '~/entities/installation';
+import { Relation } from '~/entities/relation';
+import { RelationRepository } from '~/repositories/relation';
 
 
+export type SelectedGrowiInformation = {
+  relation: Relation,
+  growiCommand: GrowiCommand,
+  sendCommandBody: any,
+}
+
 @Service()
-export class SelectRequestService implements GrowiCommandProcessor {
+export class SelectGrowiService implements GrowiCommandProcessor {
+
+  @Inject()
+  relationRepository: RelationRepository;
 
   async process(growiCommand: GrowiCommand, authorizeResult: AuthorizeResult, body: {[key:string]:string } & {growiUris:string[]}): Promise<void> {
     const { botToken } = authorizeResult;
@@ -68,40 +77,34 @@ export class SelectRequestService implements GrowiCommandProcessor {
   }
 
   // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
-  async forwardRequest(relationRepository:RelationRepository, installation:Installation | undefined, payload:any):Promise<void> {
+  async handleSelectInteraction(installation:Installation | undefined, payload:any): Promise<SelectedGrowiInformation> {
     const { trigger_id: triggerId } = payload;
     const { state, private_metadata: privateMetadata } = payload?.view;
     const { value: growiUri } = state?.values?.select_growi?.growi_app?.selected_option;
 
     const parsedPrivateMetadata = JSON.parse(privateMetadata);
-    const { growiCommand, body } = parsedPrivateMetadata;
+    const { growiCommand, body: sendCommandBody } = parsedPrivateMetadata;
 
-    if (growiCommand == null || body == null) {
-      throw new Error('growiCommand and body are required.');
+    if (growiCommand == null || sendCommandBody == null) {
+      // TODO: postEphemeralErrors
+      throw new Error('growiCommand and body params are required in private_metadata.');
     }
 
     // ovverride trigger_id
-    body.trigger_id = triggerId;
+    sendCommandBody.trigger_id = triggerId;
 
-    const relation = await relationRepository.findOne({ installation, growiUri });
+    const relation = await this.relationRepository.findOne({ installation, growiUri });
 
     if (relation == null) {
+      // TODO: postEphemeralErrors
       throw new Error('No relation found.');
     }
 
-    /*
-     * forward to GROWI server
-     */
-    // generate API URL
-    const url = new URL('/_api/v3/slack-integration/proxied/commands', relation.growiUri);
-    await axios.post(url.toString(), {
-      ...body,
+    return {
+      relation,
       growiCommand,
-    }, {
-      headers: {
-        'x-growi-ptog-tokens': relation.tokenPtoG,
-      },
-    });
+      sendCommandBody,
+    };
   }
 
 }

+ 6 - 3
packages/slackbot-proxy/src/services/UnregisterService.ts

@@ -1,4 +1,4 @@
-import { Service } from '@tsed/di';
+import { Inject, Service } from '@tsed/di';
 import { WebClient, LogLevel } from '@slack/web-api';
 import { GrowiCommand, generateMarkdownSectionBlock } from '@growi/slack';
 import { AuthorizeResult } from '@slack/oauth';
@@ -11,6 +11,9 @@ const isProduction = process.env.NODE_ENV === 'production';
 @Service()
 export class UnregisterService implements GrowiCommandProcessor {
 
+  @Inject()
+  relationRepository: RelationRepository;
+
   async process(growiCommand: GrowiCommand, authorizeResult: AuthorizeResult, body: {[key:string]:string}): Promise<void> {
     const { botToken } = authorizeResult;
     const client = new WebClient(botToken, { logLevel: isProduction ? LogLevel.DEBUG : LogLevel.INFO });
@@ -42,12 +45,12 @@ export class UnregisterService implements GrowiCommandProcessor {
   }
 
   // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
-  async unregister(relationRepository:RelationRepository, installation:Installation | undefined, authorizeResult: AuthorizeResult, payload: any):Promise<void> {
+  async unregister(installation: Installation | undefined, authorizeResult: AuthorizeResult, payload: any):Promise<void> {
     const { botToken } = authorizeResult;
     const { channel, growiUrls } = JSON.parse(payload.view.private_metadata);
     const client = new WebClient(botToken, { logLevel: isProduction ? LogLevel.DEBUG : LogLevel.INFO });
 
-    const deleteResult = await relationRepository.createQueryBuilder('relation')
+    const deleteResult = await this.relationRepository.createQueryBuilder('relation')
       .where('relation.growiUri IN (:uris)', { uris: growiUrls })
       .andWhere('relation.installationId = :installationId', { installationId: installation?.id })
       .delete()