From 301281b7fc65f6d800256585797aaea6cabda690 Mon Sep 17 00:00:00 2001 From: Dm1tr1y147 Date: Sat, 10 Oct 2020 00:19:07 +0500 Subject: [PATCH] Added user query, some code refactors --- src/controllers/auth.ts | 21 +++++++++--- src/controllers/types.ts | 9 ++--- src/db/index.ts | 69 ++++++++++++++++++++++++++------------- src/db/types.ts | 11 +++++-- src/index.ts | 28 ++++++++-------- src/resolvers/Form.ts | 40 ++++++++++------------- src/resolvers/User.ts | 52 ++++++++++++++++------------- src/resolvers/index.ts | 17 ++++++---- src/typeDefs/typeDefs.gql | 2 +- src/types.ts | 3 +- 10 files changed, 147 insertions(+), 105 deletions(-) diff --git a/src/controllers/auth.ts b/src/controllers/auth.ts index 0f88e8a..49f8dcb 100644 --- a/src/controllers/auth.ts +++ b/src/controllers/auth.ts @@ -9,15 +9,17 @@ import jwt from 'jsonwebtoken' require('dotenv').config() import { getDBFormAuthor } from '../db' +import { sendToken } from '../mailer' import { CheckRightsAndResolve } from './types' const checkRightsAndResolve: CheckRightsAndResolve = async (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.n || user.id == expected.id.n) return controller() + if (expected.id && expected.self) return controller(expected.id || user.id) + if (expected.self) return controller(user.id) + if (!expected.id || user.id == expected.id) return controller() 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 } diff --git a/src/controllers/types.ts b/src/controllers/types.ts index 27fe9f9..b92478f 100644 --- a/src/controllers/types.ts +++ b/src/controllers/types.ts @@ -1,11 +1,8 @@ -import { JwtPayloadType } from "../types" +import { JwtPayloadType } from '../types' type expectedType = { - id: { - n: number - self: boolean - } - admin: boolean + id: number + self: boolean } interface ICheckRightsAndResolve { diff --git a/src/db/index.ts b/src/db/index.ts index b383520..e0ec7a3 100644 --- a/src/db/index.ts +++ b/src/db/index.ts @@ -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) => { return await db.form.findOne({ where: { - id, + id }, include: { author: { @@ -11,20 +14,20 @@ const getDBForm = async (db: PrismaClient, id: number) => { id: true, name: true, email: true - }, + } }, choisesQuestions: { include: { - variants: true, - }, + variants: true + } }, inputQuestions: true, submissions: { include: { - answers: true, - }, - }, - }, + answers: true + } + } + } }) } @@ -32,38 +35,58 @@ const getDBFormByUser = async (db: PrismaClient, id: number) => { return await db.form.findMany({ where: { author: { - id, - }, + id + } }, include: { choisesQuestions: { include: { - variants: true, - }, + variants: true + } }, inputQuestions: true, submissions: { include: { - answers: true, - }, - }, - }, + answers: true + } + } + } }) } const getDBFormAuthor = async (db: PrismaClient, id: number) => { return await db.form.findOne({ where: { - id, + id }, select: { author: { 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 } diff --git a/src/db/types.ts b/src/db/types.ts index ae8d8e0..24527ae 100644 --- a/src/db/types.ts +++ b/src/db/types.ts @@ -1,6 +1,11 @@ -import { PromiseReturnType } from "@prisma/client" -import { getDBForm } from "../db" +import { PromiseReturnType } from '@prisma/client' +import { getDBForm } from '../db' type FullForm = PromiseReturnType -export { FullForm } +interface IFindUserParams { + email?: string + id?: number +} + +export { FullForm, IFindUserParams } diff --git a/src/index.ts b/src/index.ts index af6393b..12dff63 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,31 +1,31 @@ -import { ApolloServer, makeExecutableSchema } from "apollo-server-express" -import express from "express" -import expressJwt from "express-jwt" -import { PrismaClient } from "@prisma/client" +import { ApolloServer, makeExecutableSchema } from 'apollo-server-express' +import express from 'express' +import expressJwt from 'express-jwt' +import { PrismaClient } from '@prisma/client' -require("dotenv").config() +require('dotenv').config() -import typeDefs from "./typeDefs" -import resolvers from "./resolvers" -import { ApolloContextType, JwtPayloadType } from "./types" +import typeDefs from './typeDefs' +import resolvers from './resolvers' +import { ApolloContextType, JwtPayloadType } from './types' const app = express() app.use( expressJwt({ - secret: "" + process.env.JWT_SECRET, + secret: '' + process.env.JWT_SECRET, credentialsRequired: false, - algorithms: ["HS256"], + algorithms: ['HS256'] }) ) const server = new ApolloServer({ schema: makeExecutableSchema({ typeDefs, - resolvers, + resolvers }), context: async ({ - req, + req }: { req: Request & { user: JwtPayloadType } }): Promise => { @@ -34,11 +34,11 @@ const server = new ApolloServer({ return { db, user } }, - debug: false, + debug: false }) server.applyMiddleware({ app }) app.listen(4000, () => { - console.log("Server ready at http://localhost:4000") + console.log('Server ready at http://localhost:4000') }) diff --git a/src/resolvers/Form.ts b/src/resolvers/Form.ts index 0a43189..56e9b23 100644 --- a/src/resolvers/Form.ts +++ b/src/resolvers/Form.ts @@ -2,16 +2,16 @@ import { checkRightsAndResolve, getForm, getFormAuthor, - getForms, -} from "../controllers" + getForms +} from '../controllers' import { Form, QueryFormArgs, QuestionResolvers, Resolver, - AnswerResolvers, -} from "../typeDefs/typeDefs.gen" -import { ApolloContextType } from "../types" + AnswerResolvers +} from '../typeDefs/typeDefs.gen' +import { ApolloContextType } from '../types' const formQuery: Resolver = async ( _, @@ -26,13 +26,10 @@ const formQuery: Resolver = async ( return await checkRightsAndResolve({ user, expected: { - id: { - n: 0, - self: false, - }, - admin: false, + id: 0, + self: false }, - controller: getFormById, + controller: getFormById }) } catch (err) { return err @@ -53,13 +50,10 @@ const formsQuery: Resolver = async ( >({ user, expected: { - id: { - n: 0, - self: true, - }, - admin: false, + id: 0, + self: true }, - controller: getFormsByUserId, + controller: getFormsByUserId }) } catch (err) { return err @@ -69,19 +63,19 @@ const formsQuery: Resolver = async ( const QuestionResolver: QuestionResolvers = { __resolveType(obj: any) { if (obj.type) { - return "ChoisesQuestion" + return 'ChoisesQuestion' } - return "InputQuestion" - }, + return 'InputQuestion' + } } const AnswerResolver: AnswerResolvers = { __resolveType(obj) { - if (obj.type == "CHOISE") return "ChoiseAnswer" - if (obj.type == "INPUT") return "InputAnswer" + if (obj.type == 'CHOISE') return 'ChoiseAnswer' + if (obj.type == 'INPUT') return 'InputAnswer' return null - }, + } } export { formQuery, formsQuery, QuestionResolver, AnswerResolver } diff --git a/src/resolvers/User.ts b/src/resolvers/User.ts index 566880e..5219e04 100644 --- a/src/resolvers/User.ts +++ b/src/resolvers/User.ts @@ -1,13 +1,14 @@ -import { ApolloError, UserInputError } from 'apollo-server-express' +import { UserInputError } from 'apollo-server-express' -import { tokenGenerate } from '../controllers/auth' -import { sendToken } from '../mailer' +import { checkRightsAndResolve, sendTokenEmail } from '../controllers/auth' +import { createUser, findUserBy } from '../db' import { MutationLoginArgs, MutationRegisterArgs, Resolver, + LoginResult, User, - LoginResult + QueryUserArgs } from '../typeDefs/typeDefs.gen' import { ApolloContextType } from '../types' @@ -18,19 +19,11 @@ const loginResolver: Resolver< MutationLoginArgs > = async (_, { email }, { db }) => { try { - const user = await db.user.findOne({ - where: { - email - } - }) + const user = await findUserBy(db, { email }) if (!user) throw new UserInputError('No such user') - 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") + await sendTokenEmail(email, user) return { success: true } } catch (err) { @@ -45,15 +38,9 @@ const registerResolver: Resolver< MutationRegisterArgs > = async (_, { email, name }, { db }) => { try { - const user = await db.user.create({ - data: { email, name } - }) + const user = await createUser(db, { email, name }) - 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") + await sendTokenEmail(email, user) return { success: true } } 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 } diff --git a/src/resolvers/index.ts b/src/resolvers/index.ts index 8fc3525..a2c4e54 100644 --- a/src/resolvers/index.ts +++ b/src/resolvers/index.ts @@ -1,24 +1,29 @@ -import { ApolloContextType } from "../types" -import { Resolvers } from "../typeDefs/typeDefs.gen" +import { ApolloContextType } from '../types' +import { Resolvers } from '../typeDefs/typeDefs.gen' import { formQuery as form, QuestionResolver as Question, AnswerResolver as Answer, - formsQuery as forms, -} from "./Form" -import { loginResolver as login, registerResolver as register } from "./User" + formsQuery as forms +} from './Form' +import { + loginResolver as login, + registerResolver as register, + userResolver as user +} from './User' const resolvers: Resolvers = { Query: { form, forms, + user }, Mutation: { login, register }, Question, - Answer, + Answer } export default resolvers diff --git a/src/typeDefs/typeDefs.gql b/src/typeDefs/typeDefs.gql index c429c07..0750c66 100644 --- a/src/typeDefs/typeDefs.gql +++ b/src/typeDefs/typeDefs.gql @@ -1,6 +1,7 @@ type Query { forms: [Form!]! form(id: Int!): Form + user(id: Int): User } type Mutation { @@ -77,7 +78,6 @@ type User { email: String! id: Int! forms: [Form!] - token: String } type LoginResult { diff --git a/src/types.ts b/src/types.ts index df7fbf4..116bf40 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,5 +1,4 @@ -import { PrismaClient } from "@prisma/client" -import {} from "express-jwt" +import { PrismaClient } from '@prisma/client' export type ApolloContextType = { db: PrismaClient