Procházet zdrojové kódy

Merge pull request #7468 from arafubeatbox/feat/116540-117515-resend-questionnaire-answers-that-failed-to-send

Feat/116540 117515 resend questionnaire answers that failed to send
Ryoji Shimizu před 3 roky
rodič
revize
c129497f1d

+ 23 - 1
packages/app/src/server/service/questionnaire-cron.ts

@@ -6,6 +6,8 @@ import loggerFactory from '~/utils/logger';
 import { getRandomIntInRange } from '~/utils/rand';
 import { sleep } from '~/utils/sleep';
 
+import ProactiveQuestionnaireAnswer from '../models/questionnaire/proactive-questionnaire-answer';
+import QuestionnaireAnswer from '../models/questionnaire/questionnaire-answer';
 import QuestionnaireAnswerStatus from '../models/questionnaire/questionnaire-answer-status';
 import QuestionnaireOrder from '../models/questionnaire/questionnaire-order';
 
@@ -21,6 +23,7 @@ axiosRetry(axios, { retries: 3 });
  *  1. fetches QuestionnaireOrders from questionnaire server
  *  2. updates QuestionnaireOrder collection to contain only the ones that exist in the fetched list and is not finished (doesn't have to be started)
  *  3. changes QuestionnaireAnswerStatuses which are 'skipped' to 'not_answered'
+ *  4. resend QuestionnaireAnswers & ProactiveQuestionnaireAnswers which failed to reach questionnaire server
  */
 class QuestionnaireCronService {
 
@@ -49,8 +52,9 @@ class QuestionnaireCronService {
   }
 
   async executeJob(): Promise<void> {
+    const growiQuestionnaireServerOrigin = this.crowi.configManager?.getConfig('crowi', 'app:growiQuestionnaireServerOrigin');
+
     const fetchQuestionnaireOrders = async(): Promise<IQuestionnaireOrder[]> => {
-      const growiQuestionnaireServerOrigin = this.crowi.configManager?.getConfig('crowi', 'app:growiQuestionnaireServerOrigin');
       const response = await axios.get(`${growiQuestionnaireServerOrigin}/questionnaire-order/index`);
       return response.data.questionnaireOrders;
     };
@@ -68,8 +72,26 @@ class QuestionnaireCronService {
       );
     };
 
+    const resendQuestionnaireAnswers = async() => {
+      const questionnaireAnswers = await QuestionnaireAnswer.find()
+        .select('-_id -answers._id  -growiInfo._id -userInfo._id');
+      const proactiveQuestionnaireAnswers = await ProactiveQuestionnaireAnswer.find()
+        .select('-_id -growiInfo._id -userInfo._id');
+
+      axios.post(`${growiQuestionnaireServerOrigin}/questionnaire-answer/batch`, { questionnaireAnswers })
+        .then(async() => {
+          await QuestionnaireAnswer.deleteMany();
+        });
+      axios.post(`${growiQuestionnaireServerOrigin}/questionnaire-answer/proactive/batch`, { proactiveQuestionnaireAnswers })
+        .then(async() => {
+          await ProactiveQuestionnaireAnswer.deleteMany();
+        });
+    };
+
     const questionnaireOrders: IQuestionnaireOrder[] = await fetchQuestionnaireOrders();
 
+    resendQuestionnaireAnswers();
+
     // reset QuestionnaireOrder collection and save unfinished ones that exist on questionnaire server
     await QuestionnaireOrder.deleteMany();
     await saveUnfinishedOrders(questionnaireOrders);

+ 68 - 2
packages/app/test/integration/service/questionnaire-cron.test.ts

@@ -1,6 +1,10 @@
 import mongoose from 'mongoose';
 
+import { IProactiveQuestionnaireAnswer } from '../../../src/interfaces/questionnaire/proactive-questionnaire-answer';
+import { IQuestionnaireAnswer } from '../../../src/interfaces/questionnaire/questionnaire-answer';
 import { StatusType } from '../../../src/interfaces/questionnaire/questionnaire-answer-status';
+import ProactiveQuestionnaireAnswer from '../../../src/server/models/questionnaire/proactive-questionnaire-answer';
+import QuestionnaireAnswer from '../../../src/server/models/questionnaire/questionnaire-answer';
 import QuestionnaireAnswerStatus from '../../../src/server/models/questionnaire/questionnaire-answer-status';
 import QuestionnaireOrder from '../../../src/server/models/questionnaire/questionnaire-order';
 import { getInstance } from '../setup-crowi';
@@ -12,13 +16,18 @@ const spyAxiosGet = jest.spyOn<typeof axios, 'get'>(
   'get',
 );
 
+const spyAxiosPost = jest.spyOn<typeof axios, 'post'>(
+  axios,
+  'post',
+);
+
 describe('QuestionnaireCronService', () => {
   let crowi;
 
   const mockResponse = {
     data: {
       questionnaireOrders: [
-        // saved in db、not finished (user types is updated from the time it was saved)
+        // saved in db、not finished (user.types is updated from the time it was saved)
         {
           _id: '63a8354837e7aa378e16f0b1',
           shortTitle: {
@@ -249,16 +258,71 @@ describe('QuestionnaireCronService', () => {
       },
     ]);
 
+    const validQuestionnaireAnswer: IQuestionnaireAnswer = {
+      answers: [{
+        question: '63c6da88143e531d95346188',
+        value: '1',
+      }],
+      answeredAt: new Date(),
+      growiInfo: {
+        version: '1.0',
+        appSiteUrlHashed: 'c83e8d2a1aa87b2a3f90561be372ca523bb931e2d00013c1d204879621a25b90',
+        type: 'cloud',
+        currentUsersCount: 100,
+        currentActiveUsersCount: 50,
+        wikiType: 'open',
+        attachmentType: 'aws',
+      },
+      userInfo: {
+        userIdHash: '542bcc3bc5bc61b840017a18',
+        type: 'general',
+        userCreatedAt: new Date(),
+      },
+    };
+
+    await QuestionnaireAnswer.insertMany([
+      validQuestionnaireAnswer,
+      validQuestionnaireAnswer,
+      validQuestionnaireAnswer,
+    ]);
+
+    const validProactiveQuestionnaireAnswer: IProactiveQuestionnaireAnswer = {
+      satisfaction: 1,
+      commentText: 'answer text',
+      growiInfo: {
+        version: '1.0',
+        appSiteUrlHashed: 'c83e8d2a1aa87b2a3f90561be372ca523bb931e2d00013c1d204879621a25b90',
+        type: 'cloud',
+        currentUsersCount: 100,
+        currentActiveUsersCount: 50,
+        wikiType: 'open',
+        attachmentType: 'aws',
+      },
+      userInfo: {
+        userIdHash: '542bcc3bc5bc61b840017a18',
+        type: 'general',
+        userCreatedAt: new Date(),
+      },
+      answeredAt: new Date(),
+    };
+
+    await ProactiveQuestionnaireAnswer.insertMany([
+      validProactiveQuestionnaireAnswer,
+      validProactiveQuestionnaireAnswer,
+      validProactiveQuestionnaireAnswer,
+    ]);
+
     crowi.setupCron();
 
     spyAxiosGet.mockResolvedValue(mockResponse);
+    spyAxiosPost.mockResolvedValue({ data: { result: 'success' } });
   });
 
   afterAll(() => {
     crowi.questionnaireCronService.stopCron(); // jest will not finish until cronjob stops
   });
 
-  test('Job execution should save(update) quesionnaire orders, delete outdated ones, and update skipped answer statuses', async() => {
+  test('Job execution should save(update) quesionnaire orders, delete outdated ones, update skipped answer statuses, and delete resent answers', async() => {
     // testing the cronjob from schedule has untrivial overhead, so test job execution in place
     await crowi.questionnaireCronService.executeJob();
 
@@ -337,5 +401,7 @@ describe('QuestionnaireCronService', () => {
     ]);
 
     expect((await QuestionnaireAnswerStatus.find({ status: StatusType.not_answered })).length).toEqual(2);
+    expect((await QuestionnaireAnswer.find()).length).toEqual(0);
+    expect((await ProactiveQuestionnaireAnswer.find()).length).toEqual(0);
   });
 });