Browse Source

refs 113224: request to growi-questionnaire on the server side

Futa Arai 3 years ago
parent
commit
a0e97b6b9a

+ 11 - 66
packages/app/src/components/Questionnaire/QuestionnaireModal.tsx

@@ -8,13 +8,9 @@ import {
 import { apiv3Put } from '~/client/util/apiv3-client';
 import { toastSuccess, toastError } from '~/client/util/toastr';
 import { IAnswer } from '~/interfaces/questionnaire/answer';
-import { IGrowiInfo } from '~/interfaces/questionnaire/growi-info';
-import { IQuestionnaireAnswer } from '~/interfaces/questionnaire/questionnaire-answer';
 import { IQuestionnaireOrderHasId } from '~/interfaces/questionnaire/questionnaire-order';
-import { IUserInfo, UserType } from '~/interfaces/questionnaire/user-info';
-import { useCurrentUser, useGrowiVersion } from '~/stores/context';
+import { useCurrentUser } from '~/stores/context';
 import { useQuestionnaireModal } from '~/stores/modal';
-import axios from '~/utils/axios';
 import loggerFactory from '~/utils/logger';
 
 import Question from './Question';
@@ -26,55 +22,23 @@ type QuestionnaireModalProps = {
   growiQuestionnaireServerOrigin: string
 }
 
-const QuestionnaireModal = ({ questionnaireOrder, growiQuestionnaireServerOrigin }: QuestionnaireModalProps): JSX.Element => {
+const QuestionnaireModal = ({ questionnaireOrder }: QuestionnaireModalProps): JSX.Element => {
   const { data: currentUser } = useCurrentUser();
   const lang = currentUser?.lang;
 
   const { data: questionnaireModalData, close: closeQuestionnaireModal } = useQuestionnaireModal();
   const isOpened = questionnaireModalData?.openedQuestionnaireId === questionnaireOrder._id;
 
-  const { data: growiVersion } = useGrowiVersion();
-
   const inputNamePrefix = 'question-';
 
   const { t } = useTranslation();
 
-  // TODO: モック化されている箇所を実装
-  const getGrowiInfo = useCallback((): IGrowiInfo => {
-    return {
-      version: growiVersion || '',
-      osInfo: {
-        type: 'Linux',
-        platform: 'linux',
-        arch: 'arm',
-        totalmem: 8,
-      },
-      appSiteUrlHashed: 'c83e8d2a1aa87b2a3f90561be372ca523bb931e2d00013c1d204879621a25b90',
-      type: 'cloud',
-      currentUsersCount: 100,
-      currentActiveUsersCount: 50,
-      wikiType: 'open',
-      attachmentType: 'aws',
-      activeExternalAccountTypes: 'sample account type',
-      deploymentType: 'official-helm-chart',
-    };
-  }, [growiVersion]);
-
-  // TODO: モック化されている箇所を実装
-  const getUserInfo = useCallback((): IUserInfo => {
-    if (currentUser) {
-      return {
-        userIdHash: '542bcc3bc5bc61b840017a18',
-        type: currentUser?.admin ? UserType.admin : UserType.general,
-        userCreatedAt: currentUser?.createdAt,
-      };
-    }
-    return { type: UserType.guest };
-  }, [currentUser]);
-
-  const sendQuestionnaireAnswer = useCallback(async(questionnaireAnswer: IQuestionnaireAnswer) => {
+  const sendAnswer = useCallback(async(answers: IAnswer[]) => {
     try {
-      await axios.post(`${growiQuestionnaireServerOrigin}/questionnaire-answer`, questionnaireAnswer);
+      await apiv3Put('/questionnaire/answer', {
+        questionnaireOrderId: questionnaireOrder._id,
+        answers,
+      });
       toastSuccess(
         <>
           <div className="font-weight-bold">{t('questionnaire.thank_you_for_answering')}</div>
@@ -90,39 +54,20 @@ const QuestionnaireModal = ({ questionnaireOrder, growiQuestionnaireServerOrigin
       logger.error(e);
       toastError(t('questionnaire.failed_to_send'));
     }
+  }, [questionnaireOrder._id, t]);
 
-    try {
-      await apiv3Put('/questionnaire/answer', {
-        questionnaireOrderId: questionnaireOrder._id,
-      });
-    }
-    catch (e) {
-      logger.error(e);
-      toastError(t('questionnaire.failed_to_update_answer_status'));
-    }
-  }, [growiQuestionnaireServerOrigin, questionnaireOrder._id, t]);
-
-  const submitHandler = useCallback((event) => {
+  const submitHandler = useCallback(async(event) => {
     event.preventDefault();
 
-    const growiInfo = getGrowiInfo();
-    const userInfo = getUserInfo();
     const answers: IAnswer[] = questionnaireOrder.questions.map((question) => {
       const answerValue = event.target[`${inputNamePrefix + question._id}`].value;
       return { question: question._id, value: answerValue };
     });
 
-    const questionnaireAnswer: IQuestionnaireAnswer = {
-      growiInfo,
-      userInfo,
-      answers,
-      answeredAt: new Date(),
-    };
-
-    sendQuestionnaireAnswer(questionnaireAnswer);
+    sendAnswer(answers);
 
     closeQuestionnaireModal();
-  }, [closeQuestionnaireModal, getGrowiInfo, getUserInfo, questionnaireOrder.questions, sendQuestionnaireAnswer]);
+  }, [closeQuestionnaireModal, questionnaireOrder.questions, sendAnswer]);
 
   const skipBtnClickHandler = useCallback(async() => {
     try {

+ 2 - 2
packages/app/src/pages/_private-legacy-pages.page.tsx

@@ -10,7 +10,7 @@ import { DrawioViewerScript } from '~/components/Script/DrawioViewerScript';
 import type { CrowiRequest } from '~/interfaces/crowi-request';
 import type { RendererConfig } from '~/interfaces/services/renderer';
 import type { ISidebarConfig } from '~/interfaces/sidebar-config';
-import type { IUserHasId } from '~/interfaces/user';
+import type { IUser, IUserHasId } from '~/interfaces/user';
 import type { IUserUISettings } from '~/interfaces/user-ui-settings';
 import type { UserUISettingsModel } from '~/server/models/user-ui-settings';
 import {
@@ -29,7 +29,7 @@ import {
 const SearchResultLayout = dynamic(() => import('~/components/Layout/SearchResultLayout'), { ssr: false });
 
 type Props = CommonProps & {
-  currentUser: IUserHasId,
+  currentUser: IUser,
 
   isSearchServiceConfigured: boolean,
   isSearchServiceReachable: boolean,

+ 2 - 2
packages/app/src/pages/_search.page.tsx

@@ -11,7 +11,7 @@ import { DrawioViewerScript } from '~/components/Script/DrawioViewerScript';
 import type { CrowiRequest } from '~/interfaces/crowi-request';
 import type { RendererConfig } from '~/interfaces/services/renderer';
 import type { ISidebarConfig } from '~/interfaces/sidebar-config';
-import type { IUserHasId } from '~/interfaces/user';
+import type { IUser, IUserHasId } from '~/interfaces/user';
 import type { IUserUISettings } from '~/interfaces/user-ui-settings';
 import type { UserUISettingsModel } from '~/server/models/user-ui-settings';
 import {
@@ -32,7 +32,7 @@ import {
 
 
 type Props = CommonProps & {
-  currentUser: IUserHasId,
+  currentUser: IUser,
 
   isSearchServiceConfigured: boolean,
   isSearchServiceReachable: boolean,

+ 2 - 2
packages/app/src/pages/invited.page.tsx

@@ -1,6 +1,6 @@
 import React from 'react';
 
-import type { IUserHasId } from '@growi/core';
+import type { IUserHasId, IUser } from '@growi/core';
 import { USER_STATUS } from '@growi/core';
 import { NextPage, GetServerSideProps, GetServerSidePropsContext } from 'next';
 import { useTranslation } from 'next-i18next';
@@ -21,7 +21,7 @@ import {
 const InvitedForm = dynamic<InvitedFormProps>(() => import('~/components/InvitedForm').then(mod => mod.InvitedForm), { ssr: false });
 
 type Props = CommonProps & {
-  currentUser: IUserHasId,
+  currentUser: IUser,
   invitedFormUsername: string,
   invitedFormName: string,
 }

+ 2 - 2
packages/app/src/pages/maintenance.page.tsx

@@ -1,4 +1,4 @@
-import type { IUserHasId } from '@growi/core';
+import type { IUser, IUserHasId } from '@growi/core';
 import { NextPage, GetServerSideProps, GetServerSidePropsContext } from 'next';
 import { useTranslation } from 'next-i18next';
 import { serverSideTranslations } from 'next-i18next/serverSideTranslations';
@@ -14,7 +14,7 @@ import {
 
 
 type Props = CommonProps & {
-  currentUser: IUserHasId,
+  currentUser: IUser,
 };
 
 const MaintenancePage: NextPage<CommonProps> = (props: Props) => {

+ 2 - 2
packages/app/src/pages/tags.page.tsx

@@ -1,6 +1,6 @@
 import React, { useState, useCallback } from 'react';
 
-import type { IUserHasId } from '@growi/core';
+import type { IUser, IUserHasId } from '@growi/core';
 import { NextPage, GetServerSideProps, GetServerSidePropsContext } from 'next';
 import { useTranslation } from 'next-i18next';
 import { serverSideTranslations } from 'next-i18next/serverSideTranslations';
@@ -33,7 +33,7 @@ import {
 const PAGING_LIMIT = 10;
 
 type Props = CommonProps & {
-  currentUser: IUserHasId,
+  currentUser: IUser,
   isSearchServiceConfigured: boolean,
   isSearchServiceReachable: boolean,
   isSearchScopeChildrenAsDefault: boolean,

+ 2 - 2
packages/app/src/pages/trash.page.tsx

@@ -1,6 +1,6 @@
 import React from 'react';
 
-import type { IUserHasId } from '@growi/core';
+import type { IUser, IUserHasId } from '@growi/core';
 import { NextPage, GetServerSideProps, GetServerSidePropsContext } from 'next';
 import { useTranslation } from 'next-i18next';
 import { serverSideTranslations } from 'next-i18next/serverSideTranslations';
@@ -35,7 +35,7 @@ const EmptyTrashModal = dynamic(() => import('~/components/EmptyTrashModal'), {
 const PutbackPageModal = dynamic(() => import('~/components/PutbackPageModal'), { ssr: false });
 
 type Props = CommonProps & {
-  currentUser: IUserHasId,
+  currentUser: IUser,
   isSearchServiceConfigured: boolean,
   isSearchServiceReachable: boolean,
   isSearchScopeChildrenAsDefault: boolean,

+ 2 - 2
packages/app/src/pages/utils/commons.ts

@@ -1,4 +1,4 @@
-import type { ColorScheme, IUserHasId } from '@growi/core';
+import type { ColorScheme, IUser, IUserHasId } from '@growi/core';
 import {
   DevidedPagePath, Lang, AllLang,
 } from '@growi/core';
@@ -22,7 +22,7 @@ export type CommonProps = {
   isMaintenanceMode: boolean,
   redirectDestination: string | null,
   isDefaultLogo: boolean,
-  currentUser?: IUserHasId,
+  currentUser?: IUser,
   forcedColorScheme?: ColorScheme,
 } & Partial<SSRConfig>;
 

+ 60 - 17
packages/app/src/server/routes/apiv3/questionnaire.js

@@ -1,34 +1,76 @@
+import * as os from 'node:os';
+
 import { Router } from 'express';
 
 import { StatusType } from '~/interfaces/questionnaire/questionnaire-answer-status';
+import { UserType } from '~/interfaces/questionnaire/user-info';
 import QuestionnaireAnswerStatus from '~/server/models/questionnaire/questionnaire-answer-status';
 import QuestionnaireOrder from '~/server/models/questionnaire/questionnaire-order';
+import axios from '~/utils/axios';
 import loggerFactory from '~/utils/logger';
 
+
 const logger = loggerFactory('growi:routes:apiv3:questionnaire');
 
 const router = Router();
 
-const changeAnswerStatus = async(user, questionnaireOrderId, status) => {
-  const result = await QuestionnaireAnswerStatus.updateOne({
-    user,
-    questionnaireOrderId,
-  }, {
-    status,
-  }, { upsert: true });
-
-  if (result.modifiedCount === 1) {
-    return 204;
-  }
-  if (result.upsertedCount === 1) {
-    return 201;
-  }
-  return 404;
-};
-
 module.exports = (crowi) => {
   const loginRequired = require('../../middlewares/login-required')(crowi, true);
 
+  const changeAnswerStatus = async(user, questionnaireOrderId, status) => {
+    const result = await QuestionnaireAnswerStatus.updateOne({
+      user,
+      questionnaireOrderId,
+    }, {
+      status,
+    }, { upsert: true });
+
+    if (result.modifiedCount === 1) {
+      return 204;
+    }
+    if (result.upsertedCount === 1) {
+      return 201;
+    }
+    return 404;
+  };
+
+  const sendQuestionnaireAnswer = async(user, answers) => {
+    const growiQuestionnaireServerOrigin = crowi.configManager?.getConfig('crowi', 'app:growiQuestionnaireServerOrigin');
+
+    const growiInfo = {
+      version: crowi.version,
+      osInfo: {
+        type: os.type(),
+        platform: os.platform(),
+        arch: os.arch(),
+        totalmem: os.totalmem(),
+      },
+      appSiteUrlHashed: 'c83e8d2a1aa87b2a3f90561be372ca523bb931e2d00013c1d204879621a25b90',
+      type: 'cloud',
+      currentUsersCount: 100,
+      currentActiveUsersCount: 50,
+      wikiType: 'open',
+      attachmentType: 'aws',
+      activeExternalAccountTypes: 'sample account type',
+      deploymentType: 'official-helm-chart',
+    };
+
+    const userInfo = user ? {
+      userIdHash: '542bcc3bc5bc61b840017a18',
+      type: user.admin ? UserType.admin : UserType.general,
+      userCreatedAt: user.createdAt,
+    } : { type: UserType.guest };
+
+    const questionnaireAnswer = {
+      growiInfo,
+      userInfo,
+      answers,
+      answeredAt: new Date(),
+    };
+
+    axios.post(`${growiQuestionnaireServerOrigin}/questionnaire-answer`, questionnaireAnswer);
+  };
+
   router.get('/orders', async(req, res) => {
     const currentDate = new Date();
     try {
@@ -48,6 +90,7 @@ module.exports = (crowi) => {
 
   router.put('/answer', loginRequired, async(req, res) => {
     try {
+      await sendQuestionnaireAnswer(req.user, req.body.answers);
       const status = await changeAnswerStatus(req.user, req.body.questionnaireOrderId, StatusType.answered);
       return res.apiv3({}, status);
     }

+ 3 - 3
packages/app/src/stores/context.tsx

@@ -1,4 +1,4 @@
-import type { ColorScheme, IUser, IUserHasId } from '@growi/core';
+import type { ColorScheme, IUser } from '@growi/core';
 import { Key, SWRResponse } from 'swr';
 import useSWRImmutable from 'swr/immutable';
 
@@ -36,8 +36,8 @@ export const useConfidential = (initialData?: string): SWRResponse<string, Error
   return useContextSWR('confidential', initialData);
 };
 
-export const useCurrentUser = (initialData?: Nullable<IUserHasId>): SWRResponse<Nullable<IUserHasId>, Error> => {
-  return useContextSWR<Nullable<IUserHasId>, Error>('currentUser', initialData);
+export const useCurrentUser = (initialData?: Nullable<IUser>): SWRResponse<Nullable<IUser>, Error> => {
+  return useContextSWR<Nullable<IUser>, Error>('currentUser', initialData);
 };
 
 export const useCurrentPathname = (initialData?: string): SWRResponse<string, Error> => {