Переглянути джерело

Merge pull request #4070 from weseek/feat/6450-6845-check-expire

Feat/6450 6845 check expire
itizawa 4 роки тому
батько
коміт
e13463ed8a

+ 21 - 10
packages/slackbot-proxy/src/controllers/slack.ts

@@ -163,24 +163,35 @@ export class SlackCtrl {
     // See https://api.slack.com/apis/connections/events-api#the-events-api__responding-to-events
     res.send();
 
-    body.growiUrisForSingleUse = relations.filter((relation) => {
-      // TODO GW-6845 retrieve commands if it has expired
-      return this.relationsService.isSupportedGrowiCommandForSingleUse(relation, growiCommand.growiCommandType);
-    }).map(relation => relation.growiUri);
+    const baseDate = new Date();
 
-    if (body.growiUrisForSingleUse.length > 0) {
+    const relationsForSingleUse:Relation[] = [];
+    await Promise.all(relations.map(async(relation) => {
+      const isSupported = await this.relationsService.isSupportedGrowiCommandForSingleUse(relation, growiCommand.growiCommandType, baseDate);
+      if (isSupported) {
+        relationsForSingleUse.push(relation);
+      }
+    }));
+
+    if (relationsForSingleUse.length > 0) {
+      body.growiUrisForSingleUse = relationsForSingleUse.map(v => v.growiUri);
       return this.selectGrowiService.process(growiCommand, authorizeResult, body);
     }
 
-    const relationsForBroadcastUse = relations.filter((relation) => {
-      // TODO GW-6845 retrieve commands if it has expired
-      return this.relationsService.isSupportedGrowiCommandForBroadcastUse(relation, growiCommand.growiCommandType);
-    });
+    const relationsForBroadcastUse:Relation[] = [];
+    await Promise.all(relations.map(async(relation) => {
+      const isSupported = await this.relationsService.isSupportedGrowiCommandForBroadcastUse(relation, growiCommand.growiCommandType, baseDate);
+      if (isSupported) {
+        relationsForBroadcastUse.push(relation);
+      }
+    }));
 
     /*
      * forward to GROWI server
      */
-    this.sendCommand(growiCommand, relationsForBroadcastUse, body);
+    if (relationsForBroadcastUse.length > 0) {
+      this.sendCommand(growiCommand, relationsForBroadcastUse, body);
+    }
   }
 
   @Post('/interactions')

+ 5 - 0
packages/slackbot-proxy/src/entities/relation.ts

@@ -1,6 +1,7 @@
 import {
   Column, CreateDateColumn, Entity, PrimaryGeneratedColumn, UpdateDateColumn, ManyToOne, Index,
 } from 'typeorm';
+import { differenceInMilliseconds } from 'date-fns';
 import { Installation } from './installation';
 
 @Entity()
@@ -44,4 +45,8 @@ export class Relation {
     return this.expiredAtCommands.getTime() < now;
   }
 
+  getDistanceInMillisecondsToExpiredAt(baseDate:Date):number {
+    return differenceInMilliseconds(this.expiredAtCommands, baseDate);
+  }
+
 }

+ 70 - 5
packages/slackbot-proxy/src/services/RelationsService.ts

@@ -1,15 +1,80 @@
-import { Service } from '@tsed/di';
+import { Inject, Service } from '@tsed/di';
+import axios from 'axios';
+import { addHours } from 'date-fns';
+
 import { Relation } from '~/entities/relation';
+import { RelationRepository } from '~/repositories/relation';
+
+import loggerFactory from '~/utils/logger';
+
+const logger = loggerFactory('slackbot-proxy:services:RelationsService');
 
 @Service()
 export class RelationsService {
 
-  isSupportedGrowiCommandForSingleUse(relation:Relation, growiCommandType:string):boolean {
-    return !relation.isExpiredCommands() && relation.supportedCommandsForSingleUse.includes(growiCommandType);
+  @Inject()
+  relationRepository: RelationRepository;
+
+  async getSupportedGrowiCommands(relation:Relation):Promise<any> {
+    // generate API URL
+    const url = new URL('/_api/v3/slack-integration/supported-commands', relation.growiUri);
+    return axios.get(url.toString(), {
+      headers: {
+        'x-growi-ptog-tokens': relation.tokenPtoG,
+      },
+    });
+  }
+
+  async syncSupportedGrowiCommands(relation:Relation): Promise<Relation> {
+    const res = await this.getSupportedGrowiCommands(relation);
+    const { supportedCommandsForBroadcastUse, supportedCommandsForSingleUse } = res.data;
+    relation.supportedCommandsForBroadcastUse = supportedCommandsForBroadcastUse;
+    relation.supportedCommandsForSingleUse = supportedCommandsForSingleUse;
+    relation.expiredAtCommands = addHours(new Date(), 48);
+
+    return this.relationRepository.save(relation);
+  }
+
+  async syncRelation(relation:Relation, baseDate:Date):Promise<Relation|null> {
+    const distanceMillisecondsToExpiredAt = relation.getDistanceInMillisecondsToExpiredAt(baseDate);
+
+    if (distanceMillisecondsToExpiredAt < 0) {
+      try {
+        return await this.syncSupportedGrowiCommands(relation);
+      }
+      catch (err) {
+        logger.error(err);
+        return null;
+      }
+    }
+
+    // 24 hours
+    if (distanceMillisecondsToExpiredAt < 1000 * 60 * 60 * 24) {
+      try {
+        this.syncSupportedGrowiCommands(relation);
+      }
+      catch (err) {
+        logger.error(err);
+      }
+    }
+
+    return relation;
+  }
+
+  async isSupportedGrowiCommandForSingleUse(relation:Relation, growiCommandType:string, baseDate:Date):Promise<boolean> {
+    const syncedRelation = await this.syncRelation(relation, baseDate);
+    if (syncedRelation == null) {
+      return false;
+    }
+    return relation.supportedCommandsForSingleUse.includes(growiCommandType);
   }
 
-  isSupportedGrowiCommandForBroadcastUse(relation:Relation, growiCommandType:string):boolean {
-    return !relation.isExpiredCommands() && relation.supportedCommandsForBroadcastUse.includes(growiCommandType);
+  async isSupportedGrowiCommandForBroadcastUse(relation:Relation, growiCommandType:string, baseDate:Date):Promise<boolean> {
+    const syncedRelation = await this.syncRelation(relation, baseDate);
+    if (syncedRelation == null) {
+      return false;
+    }
+    return relation.supportedCommandsForBroadcastUse.includes(growiCommandType);
   }
 
 }

+ 7 - 0
src/server/routes/apiv3/slack-integration.js

@@ -225,5 +225,12 @@ module.exports = (crowi) => {
     return handleInteractions(req, res);
   });
 
+  router.get('/supported-commands', verifyAccessTokenFromProxy, async(req, res) => {
+    const tokenPtoG = req.headers['x-growi-ptog-tokens'];
+    const slackAppIntegration = await SlackAppIntegration.findOne({ tokenPtoG });
+
+    return res.send(slackAppIntegration);
+  });
+
   return router;
 };