From a07e0d752f20ce2ba4a51439b748a119f0220269 Mon Sep 17 00:00:00 2001
From: Dm1tr1y147 <me@dmitriy.icu>
Date: Sat, 10 Oct 2020 12:07:34 +0500
Subject: [PATCH] Added form submission mutation, some code refactoring

---
 src/controllers/form.ts   | 17 +++++++++++++---
 src/controllers/index.ts  |  4 +++-
 src/controllers/types.ts  |  3 +++
 src/controllers/user.ts   | 29 +++++++++++++++++++++++++++
 src/db/index.ts           | 34 ++++++++++++++++++++++++++++----
 src/resolvers/Form.ts     | 41 +++++++++++++++++++++++++++------------
 src/resolvers/User.ts     | 24 +++++++++++------------
 src/resolvers/index.ts    |  6 ++++--
 src/typeDefs/typeDefs.gql | 10 ++++------
 9 files changed, 128 insertions(+), 40 deletions(-)
 create mode 100644 src/controllers/user.ts

diff --git a/src/controllers/form.ts b/src/controllers/form.ts
index 71056aa..56ecd3e 100644
--- a/src/controllers/form.ts
+++ b/src/controllers/form.ts
@@ -1,7 +1,7 @@
-import { PrismaClient } from '@prisma/client'
+import { PrismaClient, Answer } from '@prisma/client'
 import { ApolloError } from 'apollo-server-express'
 
-import { createDBForm, getDBForm, getDBFormByUser } from '../db'
+import { createDBForm, getDBForm, getDBFormByUser, submitDBAnswer } from '../db'
 import { FullForm } from '../db/types'
 import {
   ChoisesQuestion,
@@ -9,6 +9,7 @@ import {
   FormSubmission,
   InputQuestion,
   MutationCreateFormArgs,
+  MutationFormSubmitArgs,
   Question
 } from '../typeDefs/typeDefs.gen'
 
@@ -98,4 +99,14 @@ const createFormFrom = async (
   return createDBForm(db, newForm, id)
 }
 
-export { getForm, getForms, createFormFrom }
+const submitAnswer = async (
+  db: PrismaClient,
+  { answers, formId }: MutationFormSubmitArgs,
+  userId: number
+) => {
+  const parsedAnswers = <Answer[]>JSON.parse(answers)
+
+  return submitDBAnswer(db, userId, formId, parsedAnswers)
+}
+
+export { getForm, getForms, createFormFrom, submitAnswer }
diff --git a/src/controllers/index.ts b/src/controllers/index.ts
index f438d4d..3fc4770 100644
--- a/src/controllers/index.ts
+++ b/src/controllers/index.ts
@@ -1,5 +1,6 @@
 import { getForm, getForms, createFormFrom } from './form'
 import { checkRightsAndResolve, getFormAuthor, sendTokenEmail } from './auth'
+import { findUserBy } from './user'
 
 export {
   checkRightsAndResolve,
@@ -7,5 +8,6 @@ export {
   getFormAuthor,
   getForms,
   createFormFrom,
-  sendTokenEmail
+  sendTokenEmail,
+  findUserBy
 }
diff --git a/src/controllers/types.ts b/src/controllers/types.ts
index 50ee251..c31013f 100644
--- a/src/controllers/types.ts
+++ b/src/controllers/types.ts
@@ -1,5 +1,8 @@
+import { AnswerType } from '@prisma/client'
 import {
+  Answer,
   ChoisesQuestion,
+  FormSubmission,
   InputQuestion,
   Variant
 } from '../typeDefs/typeDefs.gen'
diff --git a/src/controllers/user.ts b/src/controllers/user.ts
new file mode 100644
index 0000000..696960c
--- /dev/null
+++ b/src/controllers/user.ts
@@ -0,0 +1,29 @@
+import { PrismaClient } from '@prisma/client'
+import { UserInputError } from 'apollo-server-express'
+
+import { createDBUser, findDBUserBy } from '../db'
+import { IFindUserParams } from '../db/types'
+import { MutationRegisterArgs, User } from '../typeDefs/typeDefs.gen'
+
+const createUser = async (
+  db: PrismaClient,
+  { email, name }: MutationRegisterArgs
+): Promise<User> => {
+  if (!email || !name)
+    throw new UserInputError(
+      'Provide full user information',
+      [!email ? [email] : [], !name ? [name] : []].flat()
+    )
+
+  return await createDBUser(db, { email, name })
+}
+
+const findUserBy = async (db: PrismaClient, params: IFindUserParams) => {
+  const user = await findDBUserBy(db, params)
+
+  if (!user) throw new UserInputError('No such user')
+
+  return user
+}
+
+export { createUser, findUserBy }
diff --git a/src/db/index.ts b/src/db/index.ts
index 36fd46a..8d4da1a 100644
--- a/src/db/index.ts
+++ b/src/db/index.ts
@@ -1,7 +1,7 @@
 import { PrismaClient } from '@prisma/client'
 import { UserInputError } from 'apollo-server-express'
 import { newForm } from '../controllers/types'
-import { MutationRegisterArgs } from '../typeDefs/typeDefs.gen'
+import { Answer, MutationRegisterArgs } from '../typeDefs/typeDefs.gen'
 import { IFindUserParams } from './types'
 
 const getDBForm = async (db: PrismaClient, id: number) => {
@@ -95,8 +95,6 @@ const createDBForm = async (
   { title, inputQuestions, choisesQuestions }: newForm,
   id: number
 ) => {
-  console.log(title, inputQuestions, choisesQuestions)
-
   return await db.form.create({
     data: {
       author: { connect: { id } },
@@ -107,11 +105,39 @@ const createDBForm = async (
   })
 }
 
+const submitDBAnswer = async (
+  db: PrismaClient,
+  userId: number,
+  formId: number,
+  formAnswers: Answer[]
+) => {
+  const res = await db.formSubmission.create({
+    data: {
+      user: {
+        connect: {
+          id: userId
+        }
+      },
+      Form: {
+        connect: {
+          id: formId
+        }
+      },
+      answers: { create: formAnswers }
+    }
+  })
+
+  if (!res) throw new UserInputError("Can't submit form")
+
+  return { success: true }
+}
+
 export {
   getDBForm,
   getDBFormByUser,
   getDBFormAuthor,
   createDBUser,
   findDBUserBy,
-  createDBForm
+  createDBForm,
+  submitDBAnswer
 }
diff --git a/src/resolvers/Form.ts b/src/resolvers/Form.ts
index f890ed1..56c1b45 100644
--- a/src/resolvers/Form.ts
+++ b/src/resolvers/Form.ts
@@ -5,13 +5,16 @@ import {
   getForms,
   createFormFrom
 } from '../controllers'
+import { submitAnswer } from '../controllers/form'
 import {
   Form,
   QueryFormArgs,
   QuestionResolvers,
   Resolver,
   AnswerResolvers,
-  MutationCreateFormArgs
+  MutationCreateFormArgs,
+  ServerAnswer,
+  MutationFormSubmitArgs
 } from '../typeDefs/typeDefs.gen'
 import { ApolloContextType } from '../types'
 
@@ -67,16 +70,8 @@ const createForm: Resolver<
   {},
   ApolloContextType,
   MutationCreateFormArgs
-> = async (_, { questions, title }, { db, user }) => {
-  const createNewForm = (id: number) =>
-    createFormFrom(
-      db,
-      {
-        title,
-        questions
-      },
-      id
-    )
+> = async (_, params, { db, user }) => {
+  const createNewForm = (id: number) => createFormFrom(db, params, id)
 
   return await checkRightsAndResolve({
     user,
@@ -85,6 +80,21 @@ const createForm: Resolver<
   })
 }
 
+const formSubmit: Resolver<
+  ServerAnswer,
+  {},
+  ApolloContextType,
+  MutationFormSubmitArgs
+> = async (_, params, { db, user }) => {
+  const submitNewAnswer = (userId: number) => submitAnswer(db, params, userId)
+
+  return await checkRightsAndResolve({
+    user,
+    expected: { id: 0, self: true },
+    controller: submitNewAnswer
+  })
+}
+
 const QuestionResolver: QuestionResolvers = {
   __resolveType(obj: any) {
     if (obj.type) {
@@ -103,4 +113,11 @@ const AnswerResolver: AnswerResolvers = {
   }
 }
 
-export { formQuery, formsQuery, QuestionResolver, AnswerResolver, createForm }
+export {
+  formQuery,
+  formsQuery,
+  QuestionResolver,
+  AnswerResolver,
+  createForm,
+  formSubmit
+}
diff --git a/src/resolvers/User.ts b/src/resolvers/User.ts
index faf77e3..ccb5e44 100644
--- a/src/resolvers/User.ts
+++ b/src/resolvers/User.ts
@@ -1,27 +1,27 @@
-import { UserInputError } from 'apollo-server-express'
-
-import { checkRightsAndResolve, sendTokenEmail } from '../controllers'
-import { createDBUser, findDBUserBy } from '../db'
+import {
+  checkRightsAndResolve,
+  findUserBy,
+  sendTokenEmail
+} from '../controllers'
+import { createUser } from '../controllers/user'
 import {
   MutationLoginArgs,
   MutationRegisterArgs,
   Resolver,
-  LoginResult,
+  ServerAnswer,
   User,
   QueryUserArgs
 } from '../typeDefs/typeDefs.gen'
 import { ApolloContextType } from '../types'
 
 const loginResolver: Resolver<
-  LoginResult,
+  ServerAnswer,
   {},
   ApolloContextType,
   MutationLoginArgs
 > = async (_, { email }, { db }) => {
   try {
-    const user = await findDBUserBy(db, { email })
-
-    if (!user) throw new UserInputError('No such user')
+    const user = await findUserBy(db, { email })
 
     await sendTokenEmail(email, user)
 
@@ -32,13 +32,13 @@ const loginResolver: Resolver<
 }
 
 const registerResolver: Resolver<
-  LoginResult,
+  ServerAnswer,
   {},
   ApolloContextType,
   MutationRegisterArgs
 > = async (_, { email, name }, { db }) => {
   try {
-    const user = await createDBUser(db, { email, name })
+    const user = await createUser(db, { email, name })
 
     await sendTokenEmail(email, user)
 
@@ -54,7 +54,7 @@ const userResolver: Resolver<
   ApolloContextType,
   QueryUserArgs
 > = async (_, { id }, { db, user }) => {
-  const findUserById = (id: number) => findDBUserBy(db, { id })
+  const findUserById = (id: number) => findUserBy(db, { id })
 
   try {
     return await checkRightsAndResolve({
diff --git a/src/resolvers/index.ts b/src/resolvers/index.ts
index 6f5fd71..dcdcd57 100644
--- a/src/resolvers/index.ts
+++ b/src/resolvers/index.ts
@@ -5,7 +5,8 @@ import {
   QuestionResolver as Question,
   AnswerResolver as Answer,
   formsQuery as forms,
-  createForm
+  createForm,
+  formSubmit
 } from './Form'
 import {
   loginResolver as login,
@@ -22,7 +23,8 @@ const resolvers: Resolvers<ApolloContextType> = {
   Mutation: {
     login,
     register,
-    createForm
+    createForm,
+    formSubmit
   },
   Question,
   Answer
diff --git a/src/typeDefs/typeDefs.gql b/src/typeDefs/typeDefs.gql
index ab3205b..a52d579 100644
--- a/src/typeDefs/typeDefs.gql
+++ b/src/typeDefs/typeDefs.gql
@@ -5,9 +5,10 @@ type Query {
 }
 
 type Mutation {
-  login(email: String!): LoginResult
-  register(name: String!, email: String!): LoginResult
+  login(email: String!): serverAnswer
+  register(name: String!, email: String!): serverAnswer
   createForm(title: String!, questions: String!): Form!
+  formSubmit(formId: Int!, answers: String!): serverAnswer
 }
 
 type Form {
@@ -47,18 +48,15 @@ type FormSubmission {
 }
 
 interface Answer {
-  id: Int!
   type: AnswerType!
 }
 
 type InputAnswer implements Answer {
-  id: Int!
   type: AnswerType!
   userInput: String
 }
 
 type ChoiseAnswer implements Answer {
-  id: Int!
   type: AnswerType!
   userChoise: Int!
 }
@@ -81,6 +79,6 @@ type User {
   forms: [Form!]
 }
 
-type LoginResult {
+type serverAnswer {
   success: Boolean!
 }