Просмотр исходного кода

Merge pull request #7464 from arafubeatbox/feat/116540-117514-save-questionnaire-answer-when-failed-to-send

refs 117514: save questionnaire answers when failed to send
Ryoji Shimizu 3 лет назад
Родитель
Сommit
7e975d4924

+ 13 - 0
packages/app/src/interfaces/questionnaire/proactive-questionnaire-answer.ts

@@ -0,0 +1,13 @@
+import { IGrowiInfo } from './growi-info';
+import { IUserInfo } from './user-info';
+
+export interface IProactiveQuestionnaireAnswer {
+  satisfaction: number,
+  commentText: string,
+  growiInfo: IGrowiInfo,
+  userInfo: IUserInfo,
+  answeredAt: Date,
+  lengthOfExperience?: string,
+  position?: string,
+  occupation?: string,
+}

+ 26 - 0
packages/app/src/server/models/questionnaire/proactive-questionnaire-answer.ts

@@ -0,0 +1,26 @@
+import { Model, Schema } from 'mongoose';
+
+import { IProactiveQuestionnaireAnswer } from '~/interfaces/questionnaire/proactive-questionnaire-answer';
+import { getOrCreateModel } from '~/server/util/mongoose-utils';
+
+import { growiInfoSchema } from './schema/growi-info';
+import { userInfoSchema } from './schema/user-info';
+
+interface ProactiveQuestionnaireAnswerDocument extends IProactiveQuestionnaireAnswer, Document {}
+
+type ProactiveQuestionnaireAnswerModel = Model<ProactiveQuestionnaireAnswerDocument>
+
+export const proactiveQuestionnaireAnswerSchema = new Schema<ProactiveQuestionnaireAnswerDocument>({
+  satisfaction: { type: Number, required: true },
+  lengthOfExperience: { type: String },
+  position: { type: String },
+  occupation: { type: String },
+  commentText: { type: String, required: true },
+  growiInfo: { type: growiInfoSchema, required: true },
+  userInfo: { type: userInfoSchema, required: true },
+  answeredAt: { type: Date },
+}, { timestamps: true });
+
+export default getOrCreateModel<ProactiveQuestionnaireAnswerDocument, ProactiveQuestionnaireAnswerModel>(
+  'ProactiveQuestionnaireAnswer', proactiveQuestionnaireAnswerSchema,
+);

+ 21 - 0
packages/app/src/server/models/questionnaire/questionnaire-answer.ts

@@ -0,0 +1,21 @@
+import { Document, Model, Schema } from 'mongoose';
+
+import { IQuestionnaireAnswer } from '~/interfaces/questionnaire/questionnaire-answer';
+import { getOrCreateModel } from '~/server/util/mongoose-utils';
+
+import { answerSchema } from './schema/answer';
+import { growiInfoSchema } from './schema/growi-info';
+import { userInfoSchema } from './schema/user-info';
+
+interface QuestionnaireAnswerDocument extends IQuestionnaireAnswer, Document {}
+
+type QuestionnaireAnswerModel = Model<QuestionnaireAnswerDocument>
+
+const questionnaireAnswerSchema = new Schema<QuestionnaireAnswerDocument>({
+  answers: [answerSchema],
+  answeredAt: { type: Date, required: true },
+  growiInfo: { type: growiInfoSchema, required: true },
+  userInfo: { type: userInfoSchema, required: true },
+}, { timestamps: true });
+
+export default getOrCreateModel<QuestionnaireAnswerDocument, QuestionnaireAnswerModel>('QuestionnaireAnswer', questionnaireAnswerSchema);

+ 9 - 0
packages/app/src/server/models/questionnaire/schema/answer.ts

@@ -0,0 +1,9 @@
+import { Schema } from 'mongoose';
+
+import { IAnswer } from '~/interfaces/questionnaire/answer';
+import { ObjectIdLike } from '~/server/interfaces/mongoose-utils';
+
+export const answerSchema = new Schema<IAnswer<ObjectIdLike>>({
+  question: { type: Schema.Types.ObjectId, ref: 'Question', required: true },
+  value: { type: String, required: true },
+});

+ 24 - 0
packages/app/src/server/models/questionnaire/schema/growi-info.ts

@@ -0,0 +1,24 @@
+import { Schema } from 'mongoose';
+
+import {
+  GrowiAttachmentType, GrowiDeploymentType, GrowiExternalAuthProviderType, GrowiServiceType, GrowiWikiType, IGrowiInfo,
+} from '~/interfaces/questionnaire/growi-info';
+
+export const growiInfoSchema = new Schema<IGrowiInfo>({
+  version: { type: String, required: true },
+  appSiteUrl: { type: String },
+  appSiteUrlHashed: { type: String, required: true },
+  type: { type: String, required: true, enum: Object.values(GrowiServiceType) },
+  currentUsersCount: { type: Number, required: true },
+  currentActiveUsersCount: { type: Number, required: true },
+  wikiType: { type: String, required: true, enum: Object.values(GrowiWikiType) },
+  attachmentType: { type: String, required: true, enum: Object.values(GrowiAttachmentType) },
+  activeExternalAccountTypes: [{ type: String, enum: Object.values(GrowiExternalAuthProviderType) }],
+  osInfo: {
+    type: { type: String },
+    platform: String,
+    arch: String,
+    totalmem: Number,
+  },
+  deploymentType: { type: String, enum: (<(string | null)[]>Object.values(GrowiDeploymentType)).concat([null]) },
+});

+ 9 - 0
packages/app/src/server/models/questionnaire/schema/user-info.ts

@@ -0,0 +1,9 @@
+import { Schema } from 'mongoose';
+
+import { IUserInfo, UserType } from '~/interfaces/questionnaire/user-info';
+
+export const userInfoSchema = new Schema<IUserInfo>({
+  userIdHash: { type: String },
+  type: { type: String, required: true, enum: Object.values(UserType) },
+  userCreatedAt: { type: Date },
+});

+ 31 - 6
packages/app/src/server/routes/apiv3/questionnaire.ts

@@ -1,8 +1,14 @@
 import { Router, Request } from 'express';
 import { body, validationResult } from 'express-validator';
 
+import { IAnswer } from '~/interfaces/questionnaire/answer';
+import { IProactiveQuestionnaireAnswer } from '~/interfaces/questionnaire/proactive-questionnaire-answer';
+import { IQuestionnaireAnswer } from '~/interfaces/questionnaire/questionnaire-answer';
 import { StatusType } from '~/interfaces/questionnaire/questionnaire-answer-status';
+import { IUserHasId } from '~/interfaces/user';
 import Crowi from '~/server/crowi';
+import ProactiveQuestionnaireAnswer from '~/server/models/questionnaire/proactive-questionnaire-answer';
+import QuestionnaireAnswer from '~/server/models/questionnaire/questionnaire-answer';
 import QuestionnaireAnswerStatus from '~/server/models/questionnaire/questionnaire-answer-status';
 import axios from '~/utils/axios';
 import loggerFactory from '~/utils/logger';
@@ -55,7 +61,6 @@ module.exports = (crowi: Crowi): Router => {
     const growiInfo = await crowi.questionnaireService!.getGrowiInfo();
     const userInfo = crowi.questionnaireService!.getUserInfo(req.user ?? null, growiInfo.appSiteUrlHashed);
 
-    // TODO: add condition
     try {
       const questionnaireOrders = await crowi.questionnaireService!.getQuestionnaireOrdersToShow(userInfo, growiInfo, req.user?._id ?? null);
 
@@ -78,7 +83,7 @@ module.exports = (crowi: Crowi): Router => {
       const growiInfo = await crowi.questionnaireService!.getGrowiInfo();
       const userInfo = crowi.questionnaireService!.getUserInfo(req.user ?? null, growiInfo.appSiteUrlHashed);
 
-      const body = {
+      const proactiveQuestionnaireAnswer: IProactiveQuestionnaireAnswer = {
         satisfaction: req.body.satisfaction,
         lengthOfExperience: req.body.lengthOfExperience,
         position: req.body.position,
@@ -89,7 +94,17 @@ module.exports = (crowi: Crowi): Router => {
         answeredAt: new Date(),
       };
 
-      await axios.post(`${growiQuestionnaireServerOrigin}/questionnaire-answer/proactive`, body);
+      try {
+        await axios.post(`${growiQuestionnaireServerOrigin}/questionnaire-answer/proactive`, proactiveQuestionnaireAnswer);
+      }
+      catch (err) {
+        if (err.request != null) {
+          await ProactiveQuestionnaireAnswer.create(proactiveQuestionnaireAnswer);
+        }
+        else {
+          throw err;
+        }
+      }
     };
 
     const errors = validationResult(req);
@@ -108,19 +123,29 @@ module.exports = (crowi: Crowi): Router => {
   });
 
   router.put('/answer', accessTokenParser, loginRequired, validators.answer, async(req: AuthorizedRequest, res: ApiV3Response) => {
-    const sendQuestionnaireAnswer = async(user, answers) => {
+    const sendQuestionnaireAnswer = async(user: IUserHasId, answers: IAnswer[]) => {
       const growiQuestionnaireServerOrigin = crowi.configManager?.getConfig('crowi', 'app:growiQuestionnaireServerOrigin');
       const growiInfo = await crowi.questionnaireService!.getGrowiInfo();
       const userInfo = crowi.questionnaireService!.getUserInfo(user, growiInfo.appSiteUrlHashed);
 
-      const questionnaireAnswer = {
+      const questionnaireAnswer: IQuestionnaireAnswer = {
         growiInfo,
         userInfo,
         answers,
         answeredAt: new Date(),
       };
 
-      await axios.post(`${growiQuestionnaireServerOrigin}/questionnaire-answer`, questionnaireAnswer);
+      try {
+        await axios.post(`${growiQuestionnaireServerOrigin}/questionnaire-answer`, questionnaireAnswer);
+      }
+      catch (err) {
+        if (err.request != null) {
+          await QuestionnaireAnswer.create(questionnaireAnswer);
+        }
+        else {
+          throw err;
+        }
+      }
     };
 
     const errors = validationResult(req);