Added user query, some code refactors

This commit is contained in:
Dmitriy Shishkov 2020-10-10 00:19:07 +05:00
parent ec2c5feb69
commit 301281b7fc
10 changed files with 147 additions and 105 deletions

View File

@ -9,15 +9,17 @@ import jwt from 'jsonwebtoken'
require('dotenv').config() require('dotenv').config()
import { getDBFormAuthor } from '../db' import { getDBFormAuthor } from '../db'
import { sendToken } from '../mailer'
import { CheckRightsAndResolve } from './types' import { CheckRightsAndResolve } from './types'
const checkRightsAndResolve: CheckRightsAndResolve = async (params) => { const checkRightsAndResolve: CheckRightsAndResolve = async (params) => {
const { user, expected, controller } = params const { user, expected, controller } = params
if (!user) throw new AuthenticationError('Authentication required') if (!user) throw new AuthenticationError('Authorization required')
if (expected.id.self) return controller(user.id) if (expected.id && expected.self) return controller(expected.id || user.id)
if (!expected.id.n || user.id == expected.id.n) return controller() if (expected.self) return controller(user.id)
if (!expected.id || user.id == expected.id) return controller()
throw new ForbiddenError('Access denied') throw new ForbiddenError('Access denied')
} }
@ -39,4 +41,15 @@ const tokenGenerate = (email: string, id: number) => {
}) })
} }
export { checkRightsAndResolve, getFormAuthor, tokenGenerate } const sendTokenEmail = async (
email: string,
user: { id: number; name: string }
) => {
const token = tokenGenerate(email, user.id)
const res = await sendToken(user.name, email, token)
if (res[0].statusCode != 202) return new ApolloError("Couldn't send email")
}
export { checkRightsAndResolve, getFormAuthor, tokenGenerate, sendTokenEmail }

View File

@ -1,11 +1,8 @@
import { JwtPayloadType } from "../types" import { JwtPayloadType } from '../types'
type expectedType = { type expectedType = {
id: { id: number
n: number self: boolean
self: boolean
}
admin: boolean
} }
interface ICheckRightsAndResolve<T> { interface ICheckRightsAndResolve<T> {

View File

@ -1,9 +1,12 @@
import { PrismaClient } from "@prisma/client" import { PrismaClient } from '@prisma/client'
import { UserInputError } from 'apollo-server-express'
import { MutationRegisterArgs } from '../typeDefs/typeDefs.gen'
import { IFindUserParams } from './types'
const getDBForm = async (db: PrismaClient, id: number) => { const getDBForm = async (db: PrismaClient, id: number) => {
return await db.form.findOne({ return await db.form.findOne({
where: { where: {
id, id
}, },
include: { include: {
author: { author: {
@ -11,20 +14,20 @@ const getDBForm = async (db: PrismaClient, id: number) => {
id: true, id: true,
name: true, name: true,
email: true email: true
}, }
}, },
choisesQuestions: { choisesQuestions: {
include: { include: {
variants: true, variants: true
}, }
}, },
inputQuestions: true, inputQuestions: true,
submissions: { submissions: {
include: { include: {
answers: true, answers: true
}, }
}, }
}, }
}) })
} }
@ -32,38 +35,58 @@ const getDBFormByUser = async (db: PrismaClient, id: number) => {
return await db.form.findMany({ return await db.form.findMany({
where: { where: {
author: { author: {
id, id
}, }
}, },
include: { include: {
choisesQuestions: { choisesQuestions: {
include: { include: {
variants: true, variants: true
}, }
}, },
inputQuestions: true, inputQuestions: true,
submissions: { submissions: {
include: { include: {
answers: true, answers: true
}, }
}, }
}, }
}) })
} }
const getDBFormAuthor = async (db: PrismaClient, id: number) => { const getDBFormAuthor = async (db: PrismaClient, id: number) => {
return await db.form.findOne({ return await db.form.findOne({
where: { where: {
id, id
}, },
select: { select: {
author: { author: {
select: { select: {
id: true, id: true
}, }
}, }
}, }
}) })
} }
export { getDBForm, getDBFormByUser, getDBFormAuthor } const createUser = async (
db: PrismaClient,
{ email, name }: MutationRegisterArgs
) => {
return await db.user.create({
data: { email, name }
})
}
const findUserBy = async (db: PrismaClient, params: IFindUserParams) => {
const user = await db.user.findOne({
where: {
...params
}
})
if (!user) throw new UserInputError('Not found')
return user
}
export { getDBForm, getDBFormByUser, getDBFormAuthor, createUser, findUserBy }

View File

@ -1,6 +1,11 @@
import { PromiseReturnType } from "@prisma/client" import { PromiseReturnType } from '@prisma/client'
import { getDBForm } from "../db" import { getDBForm } from '../db'
type FullForm = PromiseReturnType<typeof getDBForm> type FullForm = PromiseReturnType<typeof getDBForm>
export { FullForm } interface IFindUserParams {
email?: string
id?: number
}
export { FullForm, IFindUserParams }

View File

@ -1,31 +1,31 @@
import { ApolloServer, makeExecutableSchema } from "apollo-server-express" import { ApolloServer, makeExecutableSchema } from 'apollo-server-express'
import express from "express" import express from 'express'
import expressJwt from "express-jwt" import expressJwt from 'express-jwt'
import { PrismaClient } from "@prisma/client" import { PrismaClient } from '@prisma/client'
require("dotenv").config() require('dotenv').config()
import typeDefs from "./typeDefs" import typeDefs from './typeDefs'
import resolvers from "./resolvers" import resolvers from './resolvers'
import { ApolloContextType, JwtPayloadType } from "./types" import { ApolloContextType, JwtPayloadType } from './types'
const app = express() const app = express()
app.use( app.use(
expressJwt({ expressJwt({
secret: "" + process.env.JWT_SECRET, secret: '' + process.env.JWT_SECRET,
credentialsRequired: false, credentialsRequired: false,
algorithms: ["HS256"], algorithms: ['HS256']
}) })
) )
const server = new ApolloServer({ const server = new ApolloServer({
schema: makeExecutableSchema({ schema: makeExecutableSchema({
typeDefs, typeDefs,
resolvers, resolvers
}), }),
context: async ({ context: async ({
req, req
}: { }: {
req: Request & { user: JwtPayloadType } req: Request & { user: JwtPayloadType }
}): Promise<ApolloContextType> => { }): Promise<ApolloContextType> => {
@ -34,11 +34,11 @@ const server = new ApolloServer({
return { db, user } return { db, user }
}, },
debug: false, debug: false
}) })
server.applyMiddleware({ app }) server.applyMiddleware({ app })
app.listen(4000, () => { app.listen(4000, () => {
console.log("Server ready at http://localhost:4000") console.log('Server ready at http://localhost:4000')
}) })

View File

@ -2,16 +2,16 @@ import {
checkRightsAndResolve, checkRightsAndResolve,
getForm, getForm,
getFormAuthor, getFormAuthor,
getForms, getForms
} from "../controllers" } from '../controllers'
import { import {
Form, Form,
QueryFormArgs, QueryFormArgs,
QuestionResolvers, QuestionResolvers,
Resolver, Resolver,
AnswerResolvers, AnswerResolvers
} from "../typeDefs/typeDefs.gen" } from '../typeDefs/typeDefs.gen'
import { ApolloContextType } from "../types" import { ApolloContextType } from '../types'
const formQuery: Resolver<Form, {}, ApolloContextType, QueryFormArgs> = async ( const formQuery: Resolver<Form, {}, ApolloContextType, QueryFormArgs> = async (
_, _,
@ -26,13 +26,10 @@ const formQuery: Resolver<Form, {}, ApolloContextType, QueryFormArgs> = async (
return await checkRightsAndResolve({ return await checkRightsAndResolve({
user, user,
expected: { expected: {
id: { id: 0,
n: 0, self: false
self: false,
},
admin: false,
}, },
controller: getFormById, controller: getFormById
}) })
} catch (err) { } catch (err) {
return err return err
@ -53,13 +50,10 @@ const formsQuery: Resolver<Form[], {}, ApolloContextType> = async (
>({ >({
user, user,
expected: { expected: {
id: { id: 0,
n: 0, self: true
self: true,
},
admin: false,
}, },
controller: getFormsByUserId, controller: getFormsByUserId
}) })
} catch (err) { } catch (err) {
return err return err
@ -69,19 +63,19 @@ const formsQuery: Resolver<Form[], {}, ApolloContextType> = async (
const QuestionResolver: QuestionResolvers = { const QuestionResolver: QuestionResolvers = {
__resolveType(obj: any) { __resolveType(obj: any) {
if (obj.type) { if (obj.type) {
return "ChoisesQuestion" return 'ChoisesQuestion'
} }
return "InputQuestion" return 'InputQuestion'
}, }
} }
const AnswerResolver: AnswerResolvers = { const AnswerResolver: AnswerResolvers = {
__resolveType(obj) { __resolveType(obj) {
if (obj.type == "CHOISE") return "ChoiseAnswer" if (obj.type == 'CHOISE') return 'ChoiseAnswer'
if (obj.type == "INPUT") return "InputAnswer" if (obj.type == 'INPUT') return 'InputAnswer'
return null return null
}, }
} }
export { formQuery, formsQuery, QuestionResolver, AnswerResolver } export { formQuery, formsQuery, QuestionResolver, AnswerResolver }

View File

@ -1,13 +1,14 @@
import { ApolloError, UserInputError } from 'apollo-server-express' import { UserInputError } from 'apollo-server-express'
import { tokenGenerate } from '../controllers/auth' import { checkRightsAndResolve, sendTokenEmail } from '../controllers/auth'
import { sendToken } from '../mailer' import { createUser, findUserBy } from '../db'
import { import {
MutationLoginArgs, MutationLoginArgs,
MutationRegisterArgs, MutationRegisterArgs,
Resolver, Resolver,
LoginResult,
User, User,
LoginResult QueryUserArgs
} from '../typeDefs/typeDefs.gen' } from '../typeDefs/typeDefs.gen'
import { ApolloContextType } from '../types' import { ApolloContextType } from '../types'
@ -18,19 +19,11 @@ const loginResolver: Resolver<
MutationLoginArgs MutationLoginArgs
> = async (_, { email }, { db }) => { > = async (_, { email }, { db }) => {
try { try {
const user = await db.user.findOne({ const user = await findUserBy(db, { email })
where: {
email
}
})
if (!user) throw new UserInputError('No such user') if (!user) throw new UserInputError('No such user')
const token = tokenGenerate(email, user.id) await sendTokenEmail(email, user)
const res = await sendToken(user.name, email, token)
if (res[0].statusCode != 202) return new ApolloError("Couldn't send email")
return { success: true } return { success: true }
} catch (err) { } catch (err) {
@ -45,15 +38,9 @@ const registerResolver: Resolver<
MutationRegisterArgs MutationRegisterArgs
> = async (_, { email, name }, { db }) => { > = async (_, { email, name }, { db }) => {
try { try {
const user = await db.user.create({ const user = await createUser(db, { email, name })
data: { email, name }
})
const token = tokenGenerate(email, user.id) await sendTokenEmail(email, user)
const res = await sendToken(user.name, email, token)
if (res[0].statusCode != 202) return new ApolloError("Couldn't send email")
return { success: true } return { success: true }
} catch (err) { } catch (err) {
@ -61,4 +48,23 @@ const registerResolver: Resolver<
} }
} }
export { loginResolver, registerResolver } const userResolver: Resolver<
User,
{},
ApolloContextType,
QueryUserArgs
> = async (_, { id }, { db, user }) => {
const findUserById = (id: number) => findUserBy(db, { id })
try {
return await checkRightsAndResolve({
user,
expected: { id: id || 0, self: true },
controller: findUserById
})
} catch (err) {
return err
}
}
export { loginResolver, registerResolver, userResolver }

View File

@ -1,24 +1,29 @@
import { ApolloContextType } from "../types" import { ApolloContextType } from '../types'
import { Resolvers } from "../typeDefs/typeDefs.gen" import { Resolvers } from '../typeDefs/typeDefs.gen'
import { import {
formQuery as form, formQuery as form,
QuestionResolver as Question, QuestionResolver as Question,
AnswerResolver as Answer, AnswerResolver as Answer,
formsQuery as forms, formsQuery as forms
} from "./Form" } from './Form'
import { loginResolver as login, registerResolver as register } from "./User" import {
loginResolver as login,
registerResolver as register,
userResolver as user
} from './User'
const resolvers: Resolvers<ApolloContextType> = { const resolvers: Resolvers<ApolloContextType> = {
Query: { Query: {
form, form,
forms, forms,
user
}, },
Mutation: { Mutation: {
login, login,
register register
}, },
Question, Question,
Answer, Answer
} }
export default resolvers export default resolvers

View File

@ -1,6 +1,7 @@
type Query { type Query {
forms: [Form!]! forms: [Form!]!
form(id: Int!): Form form(id: Int!): Form
user(id: Int): User
} }
type Mutation { type Mutation {
@ -77,7 +78,6 @@ type User {
email: String! email: String!
id: Int! id: Int!
forms: [Form!] forms: [Form!]
token: String
} }
type LoginResult { type LoginResult {

View File

@ -1,5 +1,4 @@
import { PrismaClient } from "@prisma/client" import { PrismaClient } from '@prisma/client'
import {} from "express-jwt"
export type ApolloContextType = { export type ApolloContextType = {
db: PrismaClient db: PrismaClient