Yuki Takei 1 year ago
parent
commit
84fd05fb9b

+ 12 - 1
apps/app/src/features/questionnaire/interfaces/proactive-questionnaire-answer.ts

@@ -1,6 +1,6 @@
 import type { IGrowiInfo } from '@growi/core/dist/interfaces';
 import type { IGrowiInfo } from '@growi/core/dist/interfaces';
 
 
-import type { IGrowiAppAdditionalInfo } from './growi-app-info';
+import type { IGrowiAppAdditionalInfo, IGrowiAppInfoLegacy } from './growi-app-info';
 import type { IUserInfo } from './user-info';
 import type { IUserInfo } from './user-info';
 
 
 
 
@@ -14,3 +14,14 @@ export interface IProactiveQuestionnaireAnswer {
   position?: string,
   position?: string,
   occupation?: string,
   occupation?: string,
 }
 }
+
+export interface IProactiveQuestionnaireAnswerLegacy {
+  satisfaction: number,
+  commentText: string,
+  growiInfo: IGrowiAppInfoLegacy,
+  userInfo: IUserInfo,
+  answeredAt: Date,
+  lengthOfExperience?: string,
+  position?: string,
+  occupation?: string,
+}

+ 9 - 1
apps/app/src/features/questionnaire/interfaces/questionnaire-answer.ts

@@ -1,7 +1,7 @@
 import type { IGrowiInfo } from '@growi/core/dist/interfaces';
 import type { IGrowiInfo } from '@growi/core/dist/interfaces';
 
 
 import type { IAnswer } from './answer';
 import type { IAnswer } from './answer';
-import type { IGrowiAppAdditionalInfo } from './growi-app-info';
+import type { IGrowiAppAdditionalInfo, IGrowiAppInfoLegacy } from './growi-app-info';
 import type { IUserInfo } from './user-info';
 import type { IUserInfo } from './user-info';
 
 
 export interface IQuestionnaireAnswer<ID = string> {
 export interface IQuestionnaireAnswer<ID = string> {
@@ -11,3 +11,11 @@ export interface IQuestionnaireAnswer<ID = string> {
   userInfo: IUserInfo
   userInfo: IUserInfo
   questionnaireOrder: ID
   questionnaireOrder: ID
 }
 }
+
+export interface IQuestionnaireAnswerLegacy<ID = string> {
+  answers: IAnswer[]
+  answeredAt: Date
+  growiInfo: IGrowiAppInfoLegacy,
+  userInfo: IUserInfo
+  questionnaireOrder: ID
+}

+ 4 - 2
apps/app/src/features/questionnaire/server/service/questionnaire-cron.ts

@@ -76,9 +76,11 @@ class QuestionnaireCronService {
 
 
     const resendQuestionnaireAnswers = async() => {
     const resendQuestionnaireAnswers = async() => {
       const questionnaireAnswers = await QuestionnaireAnswer.find()
       const questionnaireAnswers = await QuestionnaireAnswer.find()
-        .select('-_id -answers._id  -growiInfo._id -userInfo._id');
+        .select('-_id -answers._id  -growiInfo._id -userInfo._id')
+        .lean();
       const proactiveQuestionnaireAnswers = await ProactiveQuestionnaireAnswer.find()
       const proactiveQuestionnaireAnswers = await ProactiveQuestionnaireAnswer.find()
-        .select('-_id -growiInfo._id -userInfo._id');
+        .select('-_id -growiInfo._id -userInfo._id')
+        .lean();
 
 
       axios.post(`${questionnaireServerOrigin}/questionnaire-answer/batch`, {
       axios.post(`${questionnaireServerOrigin}/questionnaire-answer/batch`, {
         // convert to legacy format
         // convert to legacy format

+ 15 - 13
apps/app/src/features/questionnaire/server/util/convert-to-legacy-format.ts

@@ -1,3 +1,5 @@
+import assert from 'assert';
+
 import type { IGrowiAppInfoLegacy } from '../../interfaces/growi-app-info';
 import type { IGrowiAppInfoLegacy } from '../../interfaces/growi-app-info';
 
 
 
 
@@ -6,24 +8,24 @@ type IHasGrowiAppInfoLegacy<T> = T & {
 };
 };
 
 
 // eslint-disable-next-line @typescript-eslint/no-explicit-any
 // eslint-disable-next-line @typescript-eslint/no-explicit-any
-function hasGrowiAppInfoLegacy<T extends { growiInfo: any }>(data: T): data is IHasGrowiAppInfoLegacy<T> {
+function isLegacy<T extends { growiInfo: any }>(data: T): data is IHasGrowiAppInfoLegacy<T> {
   return !('additionalInfo' in data.growiInfo);
   return !('additionalInfo' in data.growiInfo);
 }
 }
 
 
 // eslint-disable-next-line @typescript-eslint/no-explicit-any
 // eslint-disable-next-line @typescript-eslint/no-explicit-any
 export function convertToLegacyFormat<T extends { growiInfo: any }>(questionnaireAnswer: T): IHasGrowiAppInfoLegacy<T> {
 export function convertToLegacyFormat<T extends { growiInfo: any }>(questionnaireAnswer: T): IHasGrowiAppInfoLegacy<T> {
-  if (!hasGrowiAppInfoLegacy(questionnaireAnswer)) {
-    const { additionalInfo, ...rest } = questionnaireAnswer.growiInfo;
-    assert(additionalInfo != null);
-
-    return {
-      ...questionnaireAnswer,
-      growiInfo: {
-        ...rest,
-        ...additionalInfo,
-      },
-    };
+  if (isLegacy(questionnaireAnswer)) {
+    return questionnaireAnswer;
   }
   }
 
 
-  return questionnaireAnswer;
+  const { additionalInfo, ...rest } = questionnaireAnswer.growiInfo;
+  assert(additionalInfo != null);
+
+  return {
+    ...questionnaireAnswer,
+    growiInfo: {
+      ...rest,
+      ...additionalInfo,
+    },
+  };
 }
 }

+ 6 - 0
apps/app/src/server/crowi/index.js

@@ -76,6 +76,12 @@ class Crowi {
   /** @type {PassportService} */
   /** @type {PassportService} */
   passportService;
   passportService;
 
 
+  /** @type {QuestionnaireService} */
+  questionnaireService;
+
+  /** @type {QuestionnaireCronService} */
+  questionnaireCronService;
+
   /** @type {SearchService} */
   /** @type {SearchService} */
   searchService;
   searchService;
 
 

+ 69 - 8
apps/app/test/integration/service/questionnaire-cron.test.ts

@@ -1,14 +1,16 @@
+import { GrowiDeploymentType, GrowiServiceType, GrowiWikiType } from '@growi/core';
 // eslint-disable-next-line no-restricted-imports
 // eslint-disable-next-line no-restricted-imports
 import axios from 'axios';
 import axios from 'axios';
 import mongoose from 'mongoose';
 import mongoose from 'mongoose';
 
 
-import { IProactiveQuestionnaireAnswer } from '../../../src/features/questionnaire/interfaces/proactive-questionnaire-answer';
-import { IQuestionnaireAnswer } from '../../../src/features/questionnaire/interfaces/questionnaire-answer';
+import type { IProactiveQuestionnaireAnswer, IProactiveQuestionnaireAnswerLegacy } from '../../../src/features/questionnaire/interfaces/proactive-questionnaire-answer';
+import type { IQuestionnaireAnswer, IQuestionnaireAnswerLegacy } from '../../../src/features/questionnaire/interfaces/questionnaire-answer';
 import { StatusType } from '../../../src/features/questionnaire/interfaces/questionnaire-answer-status';
 import { StatusType } from '../../../src/features/questionnaire/interfaces/questionnaire-answer-status';
 import ProactiveQuestionnaireAnswer from '../../../src/features/questionnaire/server/models/proactive-questionnaire-answer';
 import ProactiveQuestionnaireAnswer from '../../../src/features/questionnaire/server/models/proactive-questionnaire-answer';
 import QuestionnaireAnswer from '../../../src/features/questionnaire/server/models/questionnaire-answer';
 import QuestionnaireAnswer from '../../../src/features/questionnaire/server/models/questionnaire-answer';
 import QuestionnaireAnswerStatus from '../../../src/features/questionnaire/server/models/questionnaire-answer-status';
 import QuestionnaireAnswerStatus from '../../../src/features/questionnaire/server/models/questionnaire-answer-status';
 import QuestionnaireOrder from '../../../src/features/questionnaire/server/models/questionnaire-order';
 import QuestionnaireOrder from '../../../src/features/questionnaire/server/models/questionnaire-order';
+import { AttachmentMethodType } from '../../../src/interfaces/attachment';
 import { getInstance } from '../setup-crowi';
 import { getInstance } from '../setup-crowi';
 
 
 const spyAxiosGet = jest.spyOn<typeof axios, 'get'>(
 const spyAxiosGet = jest.spyOn<typeof axios, 'get'>(
@@ -275,13 +277,42 @@ describe('QuestionnaireCronService', () => {
       growiInfo: {
       growiInfo: {
         version: '1.0',
         version: '1.0',
         appSiteUrlHashed: 'c83e8d2a1aa87b2a3f90561be372ca523bb931e2d00013c1d204879621a25b90',
         appSiteUrlHashed: 'c83e8d2a1aa87b2a3f90561be372ca523bb931e2d00013c1d204879621a25b90',
+        type: GrowiServiceType.cloud,
+        wikiType: GrowiWikiType.open,
+        deploymentType: GrowiDeploymentType.others,
+        additionalInfo: {
+          installedAt: new Date('2000-01-01'),
+          installedAtByOldestUser: new Date('2020-01-01'),
+          currentUsersCount: 100,
+          currentActiveUsersCount: 50,
+          attachmentType: AttachmentMethodType.aws,
+        },
+      },
+      userInfo: {
+        userIdHash: '542bcc3bc5bc61b840017a18',
+        type: 'general',
+        userCreatedAt: new Date(),
+      },
+      questionnaireOrder: '63a8354837e7aa378e16f0b1',
+    };
+
+    const validQuestionnaireAnswerLegacy: IQuestionnaireAnswerLegacy = {
+      answers: [{
+        question: '63c6da88143e531d95346188',
+        value: '1',
+      }],
+      answeredAt: new Date(),
+      growiInfo: {
+        version: '1.0',
+        appSiteUrlHashed: 'c83e8d2a1aa87b2a3f90561be372ca523bb931e2d00013c1d204879621a25b90',
+        type: GrowiServiceType.cloud,
+        wikiType: GrowiWikiType.open,
+        deploymentType: GrowiDeploymentType.others,
         installedAt: new Date('2000-01-01'),
         installedAt: new Date('2000-01-01'),
         installedAtByOldestUser: new Date('2020-01-01'),
         installedAtByOldestUser: new Date('2020-01-01'),
-        type: 'cloud',
         currentUsersCount: 100,
         currentUsersCount: 100,
         currentActiveUsersCount: 50,
         currentActiveUsersCount: 50,
-        wikiType: 'open',
-        attachmentType: 'aws',
+        attachmentType: AttachmentMethodType.aws,
       },
       },
       userInfo: {
       userInfo: {
         userIdHash: '542bcc3bc5bc61b840017a18',
         userIdHash: '542bcc3bc5bc61b840017a18',
@@ -295,6 +326,8 @@ describe('QuestionnaireCronService', () => {
       validQuestionnaireAnswer,
       validQuestionnaireAnswer,
       validQuestionnaireAnswer,
       validQuestionnaireAnswer,
       validQuestionnaireAnswer,
       validQuestionnaireAnswer,
+      validQuestionnaireAnswerLegacy,
+      validQuestionnaireAnswerLegacy,
     ]);
     ]);
 
 
     const validProactiveQuestionnaireAnswer: IProactiveQuestionnaireAnswer = {
     const validProactiveQuestionnaireAnswer: IProactiveQuestionnaireAnswer = {
@@ -303,13 +336,39 @@ describe('QuestionnaireCronService', () => {
       growiInfo: {
       growiInfo: {
         version: '1.0',
         version: '1.0',
         appSiteUrlHashed: 'c83e8d2a1aa87b2a3f90561be372ca523bb931e2d00013c1d204879621a25b90',
         appSiteUrlHashed: 'c83e8d2a1aa87b2a3f90561be372ca523bb931e2d00013c1d204879621a25b90',
+        type: GrowiServiceType.cloud,
+        wikiType: GrowiWikiType.open,
+        deploymentType: GrowiDeploymentType.others,
+        additionalInfo: {
+          installedAt: new Date('2000-01-01'),
+          installedAtByOldestUser: new Date('2020-01-01'),
+          currentUsersCount: 100,
+          currentActiveUsersCount: 50,
+          attachmentType: AttachmentMethodType.aws,
+        },
+      },
+      userInfo: {
+        userIdHash: '542bcc3bc5bc61b840017a18',
+        type: 'general',
+        userCreatedAt: new Date(),
+      },
+      answeredAt: new Date(),
+    };
+    const validProactiveQuestionnaireAnswerLegacy: IProactiveQuestionnaireAnswerLegacy = {
+      satisfaction: 1,
+      commentText: 'answer text',
+      growiInfo: {
+        version: '1.0',
+        appSiteUrlHashed: 'c83e8d2a1aa87b2a3f90561be372ca523bb931e2d00013c1d204879621a25b90',
+        type: GrowiServiceType.cloud,
+        wikiType: GrowiWikiType.open,
+        deploymentType: GrowiDeploymentType.others,
+        // legacy properties
         installedAt: new Date('2000-01-01'),
         installedAt: new Date('2000-01-01'),
         installedAtByOldestUser: new Date('2020-01-01'),
         installedAtByOldestUser: new Date('2020-01-01'),
-        type: 'cloud',
         currentUsersCount: 100,
         currentUsersCount: 100,
         currentActiveUsersCount: 50,
         currentActiveUsersCount: 50,
-        wikiType: 'open',
-        attachmentType: 'aws',
+        attachmentType: AttachmentMethodType.aws,
       },
       },
       userInfo: {
       userInfo: {
         userIdHash: '542bcc3bc5bc61b840017a18',
         userIdHash: '542bcc3bc5bc61b840017a18',
@@ -323,6 +382,8 @@ describe('QuestionnaireCronService', () => {
       validProactiveQuestionnaireAnswer,
       validProactiveQuestionnaireAnswer,
       validProactiveQuestionnaireAnswer,
       validProactiveQuestionnaireAnswer,
       validProactiveQuestionnaireAnswer,
       validProactiveQuestionnaireAnswer,
+      validProactiveQuestionnaireAnswerLegacy,
+      validProactiveQuestionnaireAnswerLegacy,
     ]);
     ]);
 
 
     crowi.setupCron();
     crowi.setupCron();

+ 13 - 8
apps/app/test/integration/service/questionnaire.test.ts

@@ -3,10 +3,11 @@ import mongoose from 'mongoose';
 import { StatusType } from '../../../src/features/questionnaire/interfaces/questionnaire-answer-status';
 import { StatusType } from '../../../src/features/questionnaire/interfaces/questionnaire-answer-status';
 import QuestionnaireAnswerStatus from '../../../src/features/questionnaire/server/models/questionnaire-answer-status';
 import QuestionnaireAnswerStatus from '../../../src/features/questionnaire/server/models/questionnaire-answer-status';
 import QuestionnaireOrder from '../../../src/features/questionnaire/server/models/questionnaire-order';
 import QuestionnaireOrder from '../../../src/features/questionnaire/server/models/questionnaire-order';
+import type Crowi from '../../../src/server/crowi';
 import { getInstance } from '../setup-crowi';
 import { getInstance } from '../setup-crowi';
 
 
 describe('QuestionnaireService', () => {
 describe('QuestionnaireService', () => {
-  let crowi;
+  let crowi: Crowi;
   let user;
   let user;
 
 
   beforeAll(async() => {
   beforeAll(async() => {
@@ -55,15 +56,19 @@ describe('QuestionnaireService', () => {
       delete growiInfo.osInfo;
       delete growiInfo.osInfo;
 
 
       expect(growiInfo).toEqual({
       expect(growiInfo).toEqual({
-        activeExternalAccountTypes: ['saml', 'github'],
+        version: crowi.version,
         appSiteUrl: 'http://growi.test.jp',
         appSiteUrl: 'http://growi.test.jp',
-        installedAt: new Date('2000-01-01'),
-        installedAtByOldestUser: new Date('2000-01-01'),
-        attachmentType: 'aws',
-        deploymentType: 'growi-docker-compose',
         type: 'on-premise',
         type: 'on-premise',
-        version: crowi.version,
         wikiType: 'closed',
         wikiType: 'closed',
+        deploymentType: 'growi-docker-compose',
+        additionalInfo: {
+          installedAt: new Date('2000-01-01'),
+          installedAtByOldestUser: new Date('2000-01-01'),
+          currentUsersCount: 7,
+          currentActiveUsersCount: 1,
+          attachmentType: 'aws',
+          activeExternalAccountTypes: ['saml', 'github'],
+        },
       });
       });
     });
     });
 
 
@@ -75,7 +80,7 @@ describe('QuestionnaireService', () => {
 
 
       test('Should return app url string', async() => {
       test('Should return app url string', async() => {
         const growiInfo = await crowi.questionnaireService.getGrowiInfo();
         const growiInfo = await crowi.questionnaireService.getGrowiInfo();
-        expect(growiInfo.appSiteUrl).toBe(null);
+        expect(growiInfo.appSiteUrl).toBeUndefined();
         expect(growiInfo.appSiteUrlHashed).not.toBe('http://growi.test.jp');
         expect(growiInfo.appSiteUrlHashed).not.toBe('http://growi.test.jp');
         expect(growiInfo.appSiteUrlHashed).toBeTruthy();
         expect(growiInfo.appSiteUrlHashed).toBeTruthy();
       });
       });

+ 1 - 1
packages/core/src/interfaces/growi-app-info.ts

@@ -21,7 +21,7 @@ export interface IGrowiAdditionalInfo {
 
 
 export interface IGrowiInfo<A extends IGrowiAdditionalInfo> {
 export interface IGrowiInfo<A extends IGrowiAdditionalInfo> {
   version: string
   version: string
-  appSiteUrl: string | undefined
+  appSiteUrl?: string
   appSiteUrlHashed: string
   appSiteUrlHashed: string
   type: GrowiServiceType
   type: GrowiServiceType
   wikiType: GrowiWikiType
   wikiType: GrowiWikiType