Added forms property fetching to user query and some minor fixes
This commit is contained in:
parent
01676c59e4
commit
673f42fd0f
2
.gitignore
vendored
2
.gitignore
vendored
@ -1,6 +1,6 @@
|
||||
/node_modules
|
||||
|
||||
/build
|
||||
/dist
|
||||
|
||||
*.gen.ts
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
overwrite: true
|
||||
schema: "src/typeDefs/typeDefs.gql"
|
||||
schema: 'src/typeDefs/typeDefs.gql'
|
||||
documents: null
|
||||
generates:
|
||||
src/typeDefs/typeDefs.gen.ts:
|
||||
@ -8,5 +8,5 @@ generates:
|
||||
wrapFieldDefinitions: true
|
||||
enumsAsTypes: true
|
||||
plugins:
|
||||
- "typescript"
|
||||
- "typescript-resolvers"
|
||||
- 'typescript'
|
||||
- 'typescript-resolvers'
|
||||
|
@ -3,4 +3,4 @@
|
||||
"watch": ["src"],
|
||||
"exec": "yarn start",
|
||||
"ext": "ts"
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,7 @@
|
||||
"name": "backend",
|
||||
"version": "1.0.0",
|
||||
"main": "src/index.ts",
|
||||
"private": "true",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@prisma/client": "^2.7.1",
|
||||
@ -17,8 +18,11 @@
|
||||
"scripts": {
|
||||
"dev": "nodemon",
|
||||
"start": "ts-node src/index.ts",
|
||||
"copy-assets": "cp src/typeDefs/typeDefs.gql dist/typeDefs/typeDefs.gql && cp .env.example dist/.env && vi dist/.env",
|
||||
"build": "tsc && npm copy-assets",
|
||||
"codegen": "graphql-codegen --config codegen.yml",
|
||||
"lint": "eslint"
|
||||
"lint": "eslint",
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@graphql-codegen/cli": "1.17.10",
|
||||
|
@ -2,7 +2,7 @@ import jwt from 'jsonwebtoken'
|
||||
import {
|
||||
ApolloError,
|
||||
AuthenticationError,
|
||||
ForbiddenError
|
||||
ForbiddenError,
|
||||
} from 'apollo-server-express'
|
||||
import { PrismaClient } from '@prisma/client'
|
||||
|
||||
@ -37,7 +37,7 @@ const getFormAuthor = async (db: PrismaClient, id: number) => {
|
||||
const tokenGenerate = (email: string, id: number) => {
|
||||
return jwt.sign({ email, id }, '' + process.env.JWT_SECRET, {
|
||||
algorithm: 'HS256',
|
||||
expiresIn: '7 days'
|
||||
expiresIn: '7 days',
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -1,33 +1,35 @@
|
||||
import { Answer as DbAnswer, PrismaClient } from '@prisma/client'
|
||||
import { Answer as DbAnswer, PrismaClient, Form } from '@prisma/client'
|
||||
import { ApolloError, UserInputError } from 'apollo-server-express'
|
||||
|
||||
import {
|
||||
Form,
|
||||
ChoisesQuestion,
|
||||
Form as GraphqlForm,
|
||||
FormSubmission,
|
||||
InputQuestion,
|
||||
MutationCreateFormArgs,
|
||||
MutationFormSubmitArgs,
|
||||
ServerAnswer
|
||||
ServerAnswer,
|
||||
Variant,
|
||||
} from '../typeDefs/typeDefs.gen'
|
||||
import {
|
||||
CreateChoises,
|
||||
FormConstructor,
|
||||
UploadedChoisesQuestion,
|
||||
UploadedInputQuestion,
|
||||
UploadedQuestion
|
||||
UploadedQuestion,
|
||||
} from './types'
|
||||
import {
|
||||
createDBForm,
|
||||
getDBForm,
|
||||
getDBFormsByUser,
|
||||
submitDBAnswer
|
||||
submitDBAnswer,
|
||||
} from '../db'
|
||||
|
||||
const getForm = async (
|
||||
db: PrismaClient,
|
||||
id: number,
|
||||
user: { requesterId: number; userId: number }
|
||||
): Promise<Form> => {
|
||||
): Promise<GraphqlForm> => {
|
||||
try {
|
||||
const dbForm = await getDBForm(db, id, user)
|
||||
|
||||
@ -41,9 +43,9 @@ const getForm = async (
|
||||
submissions: dbForm.submissions.map((submission) => ({
|
||||
answers: submission.answers,
|
||||
date: submission.date.toString(),
|
||||
id: submission.id
|
||||
id: submission.id,
|
||||
})),
|
||||
title: dbForm.title
|
||||
title: dbForm.title,
|
||||
}
|
||||
|
||||
return form
|
||||
@ -52,22 +54,25 @@ const getForm = async (
|
||||
}
|
||||
}
|
||||
|
||||
const getForms = async (db: PrismaClient, userId: number): Promise<Form[]> => {
|
||||
const getForms = async (
|
||||
db: PrismaClient,
|
||||
userId: number
|
||||
): Promise<GraphqlForm[]> => {
|
||||
try {
|
||||
const dbForms = await getDBFormsByUser(db, userId)
|
||||
|
||||
if (!dbForms) throw new ApolloError("Couldn't load forms", 'FETCHINGERROR')
|
||||
|
||||
const forms: Form[] = dbForms.map((form) => ({
|
||||
const forms: GraphqlForm[] = dbForms.map((form) => ({
|
||||
dateCreated: form.dateCreated.toString(),
|
||||
id: form.id,
|
||||
questions: [...form.choisesQuestions, ...form.inputQuestions],
|
||||
submissions: form.submissions.map((submission) => ({
|
||||
answers: submission.answers,
|
||||
date: submission.date.toString(),
|
||||
id: submission.id
|
||||
id: submission.id,
|
||||
})),
|
||||
title: form.title
|
||||
title: form.title,
|
||||
}))
|
||||
|
||||
return forms
|
||||
@ -95,12 +100,12 @@ const createFormFrom = async (
|
||||
title: uQuestion.title,
|
||||
type: uQuestion.type,
|
||||
variants: {
|
||||
create: uQuestion.variants
|
||||
}
|
||||
}
|
||||
create: uQuestion.variants,
|
||||
},
|
||||
},
|
||||
]
|
||||
: []
|
||||
)
|
||||
),
|
||||
},
|
||||
inputQuestions: {
|
||||
create: parsedQuestions.flatMap<InputQuestion>(
|
||||
@ -108,9 +113,9 @@ const createFormFrom = async (
|
||||
!('type' in uQuestion)
|
||||
? [{ number: index, title: uQuestion.title }]
|
||||
: []
|
||||
)
|
||||
),
|
||||
},
|
||||
title: params.title
|
||||
title: params.title,
|
||||
}
|
||||
|
||||
const res = await createDBForm(db, newForm, id)
|
||||
@ -142,4 +147,25 @@ const submitAnswer = async (
|
||||
}
|
||||
}
|
||||
|
||||
export { createFormFrom, getForm, getForms, submitAnswer }
|
||||
const formatForms = (
|
||||
forms: (Form & {
|
||||
choisesQuestions: (ChoisesQuestion & {
|
||||
variants: Variant[]
|
||||
})[]
|
||||
inputQuestions: InputQuestion[]
|
||||
submissions: (Omit<FormSubmission, 'date'> & { date: Date })[]
|
||||
})[]
|
||||
) =>
|
||||
forms.map((form) => ({
|
||||
dateCreated: form.dateCreated.toString(),
|
||||
id: form.id,
|
||||
questions: [...form.choisesQuestions, ...form.inputQuestions],
|
||||
submissions: form.submissions.map((submission) => ({
|
||||
answers: submission.answers,
|
||||
date: submission.date.toString(),
|
||||
id: submission.id,
|
||||
})),
|
||||
title: form.title,
|
||||
}))
|
||||
|
||||
export { createFormFrom, getForm, getForms, submitAnswer, formatForms }
|
||||
|
@ -10,5 +10,5 @@ export {
|
||||
getForm,
|
||||
getFormAuthor,
|
||||
getForms,
|
||||
submitAnswer
|
||||
submitAnswer,
|
||||
}
|
||||
|
@ -9,12 +9,12 @@ const sendToken = (username: string, email: string, token: string) => {
|
||||
dynamicTemplateData: {
|
||||
siteUrl: process.env.SITE_URL,
|
||||
token: token,
|
||||
username: username
|
||||
username: username,
|
||||
},
|
||||
from: 'me@dmitriy.icu',
|
||||
subject: 'Login link',
|
||||
templateId: 'd-a9275a4437bf4dd2b9e858f3a57f85d5',
|
||||
to: email
|
||||
to: email,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -2,7 +2,7 @@ import { ChoiseType } from '@prisma/client'
|
||||
import {
|
||||
ChoisesQuestion,
|
||||
InputQuestion,
|
||||
Variant
|
||||
Variant,
|
||||
} from '../typeDefs/typeDefs.gen'
|
||||
import { JwtPayloadType } from '../types'
|
||||
|
||||
@ -48,5 +48,5 @@ export {
|
||||
FormConstructor,
|
||||
UploadedChoisesQuestion,
|
||||
UploadedInputQuestion,
|
||||
UploadedQuestion
|
||||
UploadedQuestion,
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ import { IFindUserParams } from '../db/types'
|
||||
import { MutationRegisterArgs, User } from '../typeDefs/typeDefs.gen'
|
||||
import { PrismaClient } from '@prisma/client'
|
||||
import { ApolloError, UserInputError } from 'apollo-server-express'
|
||||
import { formatForms } from './form'
|
||||
|
||||
const createUser = async (
|
||||
db: PrismaClient,
|
||||
@ -31,9 +32,14 @@ const findUserBy = async (
|
||||
params: IFindUserParams
|
||||
): Promise<User> => {
|
||||
try {
|
||||
const user = await findDBUserBy(db, params)
|
||||
const dbUser = await findDBUserBy(db, params)
|
||||
|
||||
if (!user) throw new UserInputError('No such user')
|
||||
if (!dbUser) throw new UserInputError('No such user')
|
||||
|
||||
const user = {
|
||||
...dbUser,
|
||||
forms: formatForms(dbUser.forms),
|
||||
}
|
||||
|
||||
return user
|
||||
} catch (err) {
|
||||
|
100
src/db/index.ts
100
src/db/index.ts
@ -19,7 +19,7 @@ const getDBForm = (
|
||||
formId: number,
|
||||
{
|
||||
requesterId,
|
||||
userId
|
||||
userId,
|
||||
}: {
|
||||
requesterId: number
|
||||
userId: number
|
||||
@ -31,32 +31,32 @@ const getDBForm = (
|
||||
select: {
|
||||
email: true,
|
||||
id: true,
|
||||
name: true
|
||||
}
|
||||
name: true,
|
||||
},
|
||||
},
|
||||
choisesQuestions: {
|
||||
include: {
|
||||
variants: true
|
||||
}
|
||||
variants: true,
|
||||
},
|
||||
},
|
||||
inputQuestions: true,
|
||||
submissions: {
|
||||
include: {
|
||||
answers: true
|
||||
answers: true,
|
||||
},
|
||||
where:
|
||||
requesterId != userId
|
||||
? {
|
||||
user: {
|
||||
id: requesterId
|
||||
}
|
||||
id: requesterId,
|
||||
},
|
||||
}
|
||||
: undefined
|
||||
}
|
||||
: undefined,
|
||||
},
|
||||
},
|
||||
where: {
|
||||
id: formId
|
||||
}
|
||||
id: formId,
|
||||
},
|
||||
})
|
||||
|
||||
/**
|
||||
@ -71,21 +71,21 @@ const getDBFormsByUser = (db: PrismaClient, id: number) =>
|
||||
include: {
|
||||
choisesQuestions: {
|
||||
include: {
|
||||
variants: true
|
||||
}
|
||||
variants: true,
|
||||
},
|
||||
},
|
||||
inputQuestions: true,
|
||||
submissions: {
|
||||
include: {
|
||||
answers: true
|
||||
}
|
||||
}
|
||||
answers: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
where: {
|
||||
author: {
|
||||
id
|
||||
}
|
||||
}
|
||||
id,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
const getDBFormAuthor = (db: PrismaClient, id: number) =>
|
||||
@ -93,13 +93,13 @@ const getDBFormAuthor = (db: PrismaClient, id: number) =>
|
||||
select: {
|
||||
author: {
|
||||
select: {
|
||||
id: true
|
||||
}
|
||||
}
|
||||
id: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
where: {
|
||||
id
|
||||
}
|
||||
id,
|
||||
},
|
||||
})
|
||||
|
||||
const createDBUser = (
|
||||
@ -107,24 +107,46 @@ const createDBUser = (
|
||||
{ email, name }: MutationRegisterArgs
|
||||
) =>
|
||||
db.user.create({
|
||||
data: { email, name }
|
||||
data: { email, name },
|
||||
})
|
||||
|
||||
const findDBUserBy = (db: PrismaClient, params: IFindUserParams) =>
|
||||
db.user.findOne({
|
||||
where: {
|
||||
...params
|
||||
}
|
||||
...params,
|
||||
},
|
||||
include: {
|
||||
forms: {
|
||||
include: {
|
||||
choisesQuestions: {
|
||||
include: {
|
||||
variants: true,
|
||||
},
|
||||
},
|
||||
inputQuestions: true,
|
||||
submissions: {
|
||||
include: {
|
||||
answers: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
formsSubmissions: {
|
||||
include: {
|
||||
answers: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
const createDBForm = (db: PrismaClient, form: FormConstructor, id: number) =>
|
||||
db.form.create({
|
||||
data: {
|
||||
author: {
|
||||
connect: { id }
|
||||
connect: { id },
|
||||
},
|
||||
...form
|
||||
}
|
||||
...form,
|
||||
},
|
||||
})
|
||||
|
||||
const submitDBAnswer = (
|
||||
@ -136,19 +158,19 @@ const submitDBAnswer = (
|
||||
db.formSubmission.create({
|
||||
data: {
|
||||
answers: {
|
||||
create: formAnswers
|
||||
create: formAnswers,
|
||||
},
|
||||
Form: {
|
||||
connect: {
|
||||
id: formId
|
||||
}
|
||||
id: formId,
|
||||
},
|
||||
},
|
||||
user: {
|
||||
connect: {
|
||||
id: userId
|
||||
}
|
||||
}
|
||||
}
|
||||
id: userId,
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
export {
|
||||
@ -158,5 +180,5 @@ export {
|
||||
getDBForm,
|
||||
getDBFormAuthor,
|
||||
getDBFormsByUser,
|
||||
submitDBAnswer
|
||||
submitDBAnswer,
|
||||
}
|
||||
|
13
src/index.ts
13
src/index.ts
@ -14,30 +14,31 @@ app.use(
|
||||
expressJwt({
|
||||
algorithms: ['HS256'],
|
||||
credentialsRequired: false,
|
||||
secret: '' + process.env.JWT_SECRET
|
||||
secret: '' + process.env.JWT_SECRET,
|
||||
})
|
||||
)
|
||||
|
||||
const db = new PrismaClient()
|
||||
|
||||
const server = new ApolloServer({
|
||||
context: async ({
|
||||
req
|
||||
req,
|
||||
}: {
|
||||
req: Request & {
|
||||
user: JwtPayloadType
|
||||
}
|
||||
}): Promise<ApolloContextType> => {
|
||||
const db = new PrismaClient()
|
||||
const user = req.user || null
|
||||
return {
|
||||
db,
|
||||
user
|
||||
user,
|
||||
}
|
||||
},
|
||||
debug: false,
|
||||
schema: makeExecutableSchema({
|
||||
resolvers,
|
||||
typeDefs
|
||||
})
|
||||
typeDefs,
|
||||
}),
|
||||
})
|
||||
|
||||
server.applyMiddleware({ app })
|
||||
|
@ -6,7 +6,7 @@ import {
|
||||
QueryFormArgs,
|
||||
QuestionResolvers,
|
||||
Resolver,
|
||||
ServerAnswer
|
||||
ServerAnswer,
|
||||
} from '../typeDefs/typeDefs.gen'
|
||||
import { ApolloContextType } from '../types'
|
||||
import {
|
||||
@ -15,7 +15,7 @@ import {
|
||||
getForm,
|
||||
getFormAuthor,
|
||||
getForms,
|
||||
submitAnswer
|
||||
submitAnswer,
|
||||
} from '../controllers'
|
||||
|
||||
const formQuery: Resolver<Form, {}, ApolloContextType, QueryFormArgs> = async (
|
||||
@ -33,9 +33,9 @@ const formQuery: Resolver<Form, {}, ApolloContextType, QueryFormArgs> = async (
|
||||
controller: getFormById,
|
||||
expected: {
|
||||
id: 0,
|
||||
self: true
|
||||
self: true,
|
||||
},
|
||||
user
|
||||
user,
|
||||
})
|
||||
} catch (err) {
|
||||
return err
|
||||
@ -57,9 +57,9 @@ const formsQuery: Resolver<Form[], {}, ApolloContextType> = async (
|
||||
controller: getFormsByUserId,
|
||||
expected: {
|
||||
id: 0,
|
||||
self: true
|
||||
self: true,
|
||||
},
|
||||
user
|
||||
user,
|
||||
})
|
||||
} catch (err) {
|
||||
return err
|
||||
@ -78,9 +78,9 @@ const createFormMutation: Resolver<
|
||||
controller: createNewForm,
|
||||
expected: {
|
||||
id: 0,
|
||||
self: true
|
||||
self: true,
|
||||
},
|
||||
user
|
||||
user,
|
||||
})
|
||||
}
|
||||
|
||||
@ -96,9 +96,9 @@ const formSubmitMutation: Resolver<
|
||||
controller: submitNewAnswer,
|
||||
expected: {
|
||||
id: 0,
|
||||
self: true
|
||||
self: true,
|
||||
},
|
||||
user
|
||||
user,
|
||||
})
|
||||
}
|
||||
|
||||
@ -108,7 +108,7 @@ const QuestionResolver: QuestionResolvers = {
|
||||
return 'ChoisesQuestion'
|
||||
}
|
||||
return 'InputQuestion'
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
const AnswerResolver: AnswerResolvers = {
|
||||
@ -117,7 +117,7 @@ const AnswerResolver: AnswerResolvers = {
|
||||
if (obj.type == 'INPUT') return 'InputAnswer'
|
||||
|
||||
return null
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
export {
|
||||
@ -126,5 +126,5 @@ export {
|
||||
formQuery,
|
||||
formsQuery,
|
||||
formSubmitMutation,
|
||||
QuestionResolver
|
||||
QuestionResolver,
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
import {
|
||||
checkRightsAndResolve,
|
||||
findUserBy,
|
||||
genAndSendToken
|
||||
genAndSendToken,
|
||||
} from '../controllers'
|
||||
import { createUser } from '../controllers/user'
|
||||
import {
|
||||
@ -10,7 +10,7 @@ import {
|
||||
Resolver,
|
||||
ServerAnswer,
|
||||
User,
|
||||
QueryUserArgs
|
||||
QueryUserArgs,
|
||||
} from '../typeDefs/typeDefs.gen'
|
||||
import { ApolloContextType } from '../types'
|
||||
|
||||
@ -23,6 +23,8 @@ const loginMutation: Resolver<
|
||||
try {
|
||||
const user = await findUserBy(db, { email })
|
||||
|
||||
if (user instanceof Error) throw user // Needed to a strange error
|
||||
|
||||
await genAndSendToken(email, user)
|
||||
|
||||
return { success: true }
|
||||
@ -40,6 +42,8 @@ const registerMutation: Resolver<
|
||||
try {
|
||||
const user = await createUser(db, { email, name })
|
||||
|
||||
if (user instanceof Error) throw user // Needed to a strange error
|
||||
|
||||
await genAndSendToken(email, user)
|
||||
|
||||
return { success: true }
|
||||
@ -60,9 +64,9 @@ const userQuery: Resolver<User, {}, ApolloContextType, QueryUserArgs> = async (
|
||||
controller: findUserById,
|
||||
expected: {
|
||||
id: id || 0,
|
||||
self: true
|
||||
self: true,
|
||||
},
|
||||
user
|
||||
user,
|
||||
})
|
||||
} catch (err) {
|
||||
return err
|
||||
|
@ -5,28 +5,28 @@ import {
|
||||
AnswerResolver as Answer,
|
||||
formsQuery as forms,
|
||||
createFormMutation as createForm,
|
||||
formSubmitMutation as formSubmit
|
||||
formSubmitMutation as formSubmit,
|
||||
} from './Form'
|
||||
import {
|
||||
loginMutation as login,
|
||||
registerMutation as register,
|
||||
userQuery as user
|
||||
userQuery as user,
|
||||
} from './User'
|
||||
|
||||
const resolvers: Resolvers = {
|
||||
Query: {
|
||||
form,
|
||||
forms,
|
||||
user
|
||||
user,
|
||||
},
|
||||
Mutation: {
|
||||
login,
|
||||
register,
|
||||
createForm,
|
||||
formSubmit
|
||||
formSubmit,
|
||||
},
|
||||
Question,
|
||||
Answer
|
||||
Answer,
|
||||
}
|
||||
|
||||
export default resolvers
|
||||
|
@ -4,11 +4,13 @@
|
||||
"module": "CommonJS" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */,
|
||||
"strict": true /* Enable all strict type-checking options. */,
|
||||
"outDir": "dist",
|
||||
"noImplicitAny": true,
|
||||
"moduleResolution": "Node",
|
||||
"baseUrl": "src",
|
||||
"incremental": true,
|
||||
"esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */,
|
||||
"skipLibCheck": true /* Skip type checking of declaration files. */,
|
||||
"forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */
|
||||
},
|
||||
"include": ["src"]
|
||||
"include": ["src/**/*"]
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user