From fd3ab923cf24c2678208b00f3a55994e385711d3 Mon Sep 17 00:00:00 2001 From: Shrenik Deep Date: Sat, 12 Aug 2023 08:48:04 +0530 Subject: [PATCH 1/6] Get all users endpoint --- src/app.ts | 2 ++ src/controllers/admin.controller.ts | 19 ++++++++++++++++ src/routes/admin/admin.route.test.ts | 34 ++++++++++++++++++++++++++++ src/routes/admin/admin.route.ts | 9 ++++++++ src/services/admin.service.ts | 11 +++++++++ 5 files changed, 75 insertions(+) create mode 100644 src/controllers/admin.controller.ts create mode 100644 src/routes/admin/admin.route.test.ts create mode 100644 src/routes/admin/admin.route.ts create mode 100644 src/services/admin.service.ts diff --git a/src/app.ts b/src/app.ts index 5cc30de9..9bd0639f 100644 --- a/src/app.ts +++ b/src/app.ts @@ -5,6 +5,7 @@ import cors from 'cors' import { dataSource } from './configs/dbConfig' import authRouter from './routes/auth.route' import profileRouter from './routes/profile/profile.route' +import adminRouter from './routes/admin/admin.route' import passport from 'passport' import './configs/passport' import { SERVER_PORT } from './configs/envConfig' @@ -22,6 +23,7 @@ app.get('/', (req, res) => { app.use('/api/auth', authRouter) app.use('/api/me', profileRouter) +app.use('/api/admin', adminRouter) export const startServer = async (): Promise => { try { diff --git a/src/controllers/admin.controller.ts b/src/controllers/admin.controller.ts new file mode 100644 index 00000000..a4e9df15 --- /dev/null +++ b/src/controllers/admin.controller.ts @@ -0,0 +1,19 @@ +import type { Request, Response } from 'express' +import { getAllUsers } from '../services/admin.service' + +export const getAllUsersHandler = async ( + req: Request, + res: Response +): Promise => { + try { + const users = await getAllUsers(req) + if (users && users.length != 0) { + res.status(404).json({ message: 'No users available' }) + } + + res.status(200).json(users) + } catch (err) { + console.error('Error executing query', err) + res.status(500).json({ error: err }) + } +} diff --git a/src/routes/admin/admin.route.test.ts b/src/routes/admin/admin.route.test.ts new file mode 100644 index 00000000..11189535 --- /dev/null +++ b/src/routes/admin/admin.route.test.ts @@ -0,0 +1,34 @@ +import type { Express } from 'express' +import supertest from 'supertest' + +let server: Express +let accessToken: string + +describe('Get all users route', () => { + it('should return a 401 when a valid access token is not provided', async () => { + await supertest(server).get('/api/admin/users').expect(401) + }) + + it('should return a 200 with all users from the DB', async () => { + const response = await supertest(server) + .get('/api/me/profile') + .set('Authorization', `Bearer ${accessToken}`) + .expect(200) + + const userProfiles = response.body + + userProfiles.forEach((userProfile: Object) => { + expect(userProfile).toHaveProperty('created_at') + expect(userProfile).toHaveProperty('updated_at') + expect(userProfile).toHaveProperty('primary_email') + expect(userProfile).toHaveProperty('contact_email') + expect(userProfile).toHaveProperty('first_name') + expect(userProfile).toHaveProperty('last_name') + expect(userProfile).toHaveProperty('image_url') + expect(userProfile).toHaveProperty('linkedin_url') + expect(userProfile).toHaveProperty('type') + expect(userProfile).toHaveProperty('uuid') + expect(userProfile).not.toHaveProperty('password') + }) + }) +}) diff --git a/src/routes/admin/admin.route.ts b/src/routes/admin/admin.route.ts new file mode 100644 index 00000000..0567fbbf --- /dev/null +++ b/src/routes/admin/admin.route.ts @@ -0,0 +1,9 @@ +import express from 'express' +import { getAllUsersHandler } from '../../controllers/admin.controller' +import { requireAuth } from '../../controllers/auth.controller' + +const adminRouter = express.Router() + +adminRouter.get('/users', requireAuth, getAllUsersHandler) + +export default adminRouter diff --git a/src/services/admin.service.ts b/src/services/admin.service.ts new file mode 100644 index 00000000..44f9ae3a --- /dev/null +++ b/src/services/admin.service.ts @@ -0,0 +1,11 @@ +import type { Request } from 'express' +import { dataSource } from '../configs/dbConfig' +import Profile from '../entities/profile.entity' + +export const getAllUsers = async ( + req: Request +): Promise => { + const profileRepository = dataSource.getRepository(Profile) + const allUsers = await profileRepository.find() + return allUsers as Profile[] +} From 7640d98c361ad88f4998b800e64c96bed161950a Mon Sep 17 00:00:00 2001 From: Shrenik Deep Date: Fri, 18 Aug 2023 17:23:17 +0530 Subject: [PATCH 2/6] Folder structure fix --- .../{admin.controller.ts => admin/user.controller.ts} | 3 ++- src/routes/admin/admin.route.test.ts | 3 ++- src/routes/admin/admin.route.ts | 2 +- src/services/admin.service.ts | 2 +- 4 files changed, 6 insertions(+), 4 deletions(-) rename src/controllers/{admin.controller.ts => admin/user.controller.ts} (84%) diff --git a/src/controllers/admin.controller.ts b/src/controllers/admin/user.controller.ts similarity index 84% rename from src/controllers/admin.controller.ts rename to src/controllers/admin/user.controller.ts index a4e9df15..ed7ea579 100644 --- a/src/controllers/admin.controller.ts +++ b/src/controllers/admin/user.controller.ts @@ -1,5 +1,5 @@ import type { Request, Response } from 'express' -import { getAllUsers } from '../services/admin.service' +import { getAllUsers } from '../../services/admin.service' export const getAllUsersHandler = async ( req: Request, @@ -7,6 +7,7 @@ export const getAllUsersHandler = async ( ): Promise => { try { const users = await getAllUsers(req) + console.log(users) if (users && users.length != 0) { res.status(404).json({ message: 'No users available' }) } diff --git a/src/routes/admin/admin.route.test.ts b/src/routes/admin/admin.route.test.ts index 11189535..64a6488a 100644 --- a/src/routes/admin/admin.route.test.ts +++ b/src/routes/admin/admin.route.test.ts @@ -1,5 +1,6 @@ import type { Express } from 'express' import supertest from 'supertest' +import Profile from '../../entities/profile.entity' let server: Express let accessToken: string @@ -17,7 +18,7 @@ describe('Get all users route', () => { const userProfiles = response.body - userProfiles.forEach((userProfile: Object) => { + userProfiles.forEach((userProfile: Partial) => { expect(userProfile).toHaveProperty('created_at') expect(userProfile).toHaveProperty('updated_at') expect(userProfile).toHaveProperty('primary_email') diff --git a/src/routes/admin/admin.route.ts b/src/routes/admin/admin.route.ts index 0567fbbf..4bf7c01a 100644 --- a/src/routes/admin/admin.route.ts +++ b/src/routes/admin/admin.route.ts @@ -1,5 +1,5 @@ import express from 'express' -import { getAllUsersHandler } from '../../controllers/admin.controller' +import { getAllUsersHandler } from '../../controllers/admin/user.controller' import { requireAuth } from '../../controllers/auth.controller' const adminRouter = express.Router() diff --git a/src/services/admin.service.ts b/src/services/admin.service.ts index 44f9ae3a..e352e07d 100644 --- a/src/services/admin.service.ts +++ b/src/services/admin.service.ts @@ -7,5 +7,5 @@ export const getAllUsers = async ( ): Promise => { const profileRepository = dataSource.getRepository(Profile) const allUsers = await profileRepository.find() - return allUsers as Profile[] + return allUsers } From 932e287d2467712507af62f5dc488025515db4d6 Mon Sep 17 00:00:00 2001 From: Shrenik Deep Date: Fri, 18 Aug 2023 17:50:57 +0530 Subject: [PATCH 3/6] Issue fixes --- src/controllers/admin/user.controller.ts | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/controllers/admin/user.controller.ts b/src/controllers/admin/user.controller.ts index ed7ea579..b72d0714 100644 --- a/src/controllers/admin/user.controller.ts +++ b/src/controllers/admin/user.controller.ts @@ -4,15 +4,14 @@ import { getAllUsers } from '../../services/admin.service' export const getAllUsersHandler = async ( req: Request, res: Response -): Promise => { +): Promise => { try { const users = await getAllUsers(req) - console.log(users) - if (users && users.length != 0) { - res.status(404).json({ message: 'No users available' }) + if (users?.length === 0) { + return res.status(404).json({ message: 'No users available' }) + } else { + return res.status(200).json({ profiles: users }) } - - res.status(200).json(users) } catch (err) { console.error('Error executing query', err) res.status(500).json({ error: err }) From 990c846ca9cdbc3e63759327b1d46943675dc149 Mon Sep 17 00:00:00 2001 From: Shrenik Deep Date: Sat, 19 Aug 2023 12:32:02 +0530 Subject: [PATCH 4/6] Unit Test updates --- src/routes/admin/admin.route.test.ts | 30 ++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/src/routes/admin/admin.route.test.ts b/src/routes/admin/admin.route.test.ts index 64a6488a..f03bf14b 100644 --- a/src/routes/admin/admin.route.test.ts +++ b/src/routes/admin/admin.route.test.ts @@ -1,22 +1,40 @@ +import { startServer } from '../../app' import type { Express } from 'express' import supertest from 'supertest' import Profile from '../../entities/profile.entity' +const port = Math.floor(Math.random() * (9999 - 3000 + 1)) + 3000 + +const randomString = Math.random().toString(36) let server: Express -let accessToken: string +let agent: supertest.SuperAgentTest describe('Get all users route', () => { + beforeAll(async () => { + server = await startServer(port) + agent = supertest.agent(server) + + const testUser = { + email: `test${randomString}@gmail.com`, + password: '123' + } + + await supertest(server) + .post('/api/auth/register') + .send(testUser) + .expect(201) + + await agent.post('/api/auth/login').send(testUser).expect(200) + }, 5000) + it('should return a 401 when a valid access token is not provided', async () => { await supertest(server).get('/api/admin/users').expect(401) }) it('should return a 200 with all users from the DB', async () => { - const response = await supertest(server) - .get('/api/me/profile') - .set('Authorization', `Bearer ${accessToken}`) - .expect(200) + const response = await agent.get('/api/admin/users').expect(200) - const userProfiles = response.body + const userProfiles = response.body.profiles userProfiles.forEach((userProfile: Partial) => { expect(userProfile).toHaveProperty('created_at') From 17bf386171b5f43cadad17a0a30570c33f74c94a Mon Sep 17 00:00:00 2001 From: Shrenik Deep Date: Sun, 20 Aug 2023 11:47:11 +0530 Subject: [PATCH 5/6] Testing for Admin role --- src/controllers/admin/user.controller.ts | 17 +++++++++---- src/controllers/auth.controller.ts | 8 ++++-- src/routes/admin/admin.route.test.ts | 32 +++++++++++++++++++----- src/services/admin.service.ts | 5 +--- src/services/auth.service.ts | 5 +++- 5 files changed, 49 insertions(+), 18 deletions(-) diff --git a/src/controllers/admin/user.controller.ts b/src/controllers/admin/user.controller.ts index b72d0714..aeb23c57 100644 --- a/src/controllers/admin/user.controller.ts +++ b/src/controllers/admin/user.controller.ts @@ -1,16 +1,23 @@ import type { Request, Response } from 'express' import { getAllUsers } from '../../services/admin.service' +import type Profile from '../../entities/profile.entity' +import { ProfileTypes } from '../../enums' export const getAllUsersHandler = async ( req: Request, res: Response -): Promise => { +): Promise => { try { - const users = await getAllUsers(req) - if (users?.length === 0) { - return res.status(404).json({ message: 'No users available' }) + const user = req.user as Profile + if (user.type !== ProfileTypes.ADMIN) { + res.status(403).json({ message: 'Only Admins are allowed' }) } else { - return res.status(200).json({ profiles: users }) + const users = await getAllUsers() + if (users?.length === 0) { + res.status(404).json({ message: 'No users available' }) + } else { + res.status(200).json({ profiles: users }) + } } } catch (err) { console.error('Error executing query', err) diff --git a/src/controllers/auth.controller.ts b/src/controllers/auth.controller.ts index 553eeac4..d6a09284 100644 --- a/src/controllers/auth.controller.ts +++ b/src/controllers/auth.controller.ts @@ -7,13 +7,17 @@ import { JWT_SECRET } from '../configs/envConfig' export const register = async (req: Request, res: Response): Promise => { try { - const { email, password } = req.body + const { email, password, type } = req.body if (!email || !password) { res.status(400).json({ error: 'Email and password are required fields' }) } - const { statusCode, message, profile } = await registerUser(email, password) + const { statusCode, message, profile } = await registerUser( + email, + password, + type + ) res.status(statusCode).json({ message, profile }) } catch (err) { diff --git a/src/routes/admin/admin.route.test.ts b/src/routes/admin/admin.route.test.ts index f03bf14b..bfe8ef3d 100644 --- a/src/routes/admin/admin.route.test.ts +++ b/src/routes/admin/admin.route.test.ts @@ -1,38 +1,58 @@ import { startServer } from '../../app' import type { Express } from 'express' import supertest from 'supertest' -import Profile from '../../entities/profile.entity' +import type Profile from '../../entities/profile.entity' const port = Math.floor(Math.random() * (9999 - 3000 + 1)) + 3000 const randomString = Math.random().toString(36) +const randomStringAdmin = Math.random().toString(36) let server: Express let agent: supertest.SuperAgentTest +let adminAgent: supertest.SuperAgentTest describe('Get all users route', () => { beforeAll(async () => { server = await startServer(port) agent = supertest.agent(server) + adminAgent = supertest.agent(server) - const testUser = { + const defaultUser = { email: `test${randomString}@gmail.com`, password: '123' } await supertest(server) .post('/api/auth/register') - .send(testUser) + .send(defaultUser) .expect(201) - await agent.post('/api/auth/login').send(testUser).expect(200) + await agent.post('/api/auth/login').send(defaultUser).expect(200) + + const adminUser = { + email: `test${randomStringAdmin}@gmail.com`, + password: 'admin123', + type: 'admin' + } + + await supertest(server) + .post('/api/auth/register') + .send(adminUser) + .expect(201) + + await adminAgent.post('/api/auth/login').send(adminUser).expect(200) }, 5000) it('should return a 401 when a valid access token is not provided', async () => { await supertest(server).get('/api/admin/users').expect(401) }) - it('should return a 200 with all users from the DB', async () => { - const response = await agent.get('/api/admin/users').expect(200) + it('should return a 403 if user is not admin', async () => { + await agent.get('/api/admin/users').expect(403) + }) + + it('should return a 200 with all users if user is admin', async () => { + const response = await adminAgent.get('/api/admin/users').expect(200) const userProfiles = response.body.profiles diff --git a/src/services/admin.service.ts b/src/services/admin.service.ts index e352e07d..e4a9fa3d 100644 --- a/src/services/admin.service.ts +++ b/src/services/admin.service.ts @@ -1,10 +1,7 @@ -import type { Request } from 'express' import { dataSource } from '../configs/dbConfig' import Profile from '../entities/profile.entity' -export const getAllUsers = async ( - req: Request -): Promise => { +export const getAllUsers = async (): Promise => { const profileRepository = dataSource.getRepository(Profile) const allUsers = await profileRepository.find() return allUsers diff --git a/src/services/auth.service.ts b/src/services/auth.service.ts index 179f832c..c0aae3c2 100644 --- a/src/services/auth.service.ts +++ b/src/services/auth.service.ts @@ -3,10 +3,12 @@ import bcrypt from 'bcrypt' import jwt from 'jsonwebtoken' import Profile from '../entities/profile.entity' import { JWT_SECRET } from '../configs/envConfig' +import type { ProfileTypes } from '../enums' export const registerUser = async ( email: string, - password: string + password: string, + userType: ProfileTypes ): Promise<{ statusCode: number message: string @@ -27,6 +29,7 @@ export const registerUser = async ( const newProfile = profileRepository.create({ primary_email: email, password: hashedPassword, + type: userType, contact_email: '', first_name: '', last_name: '', From 833f5650fab6953c42a279bee14ad2bd75fcf5cf Mon Sep 17 00:00:00 2001 From: Shrenik Deep Date: Sun, 20 Aug 2023 12:33:23 +0530 Subject: [PATCH 6/6] Issue fixes for Admin role --- src/controllers/auth.controller.ts | 8 ++------ src/routes/admin/admin.route.test.ts | 27 ++++++++++++++++++++------- src/services/auth.service.ts | 5 +---- 3 files changed, 23 insertions(+), 17 deletions(-) diff --git a/src/controllers/auth.controller.ts b/src/controllers/auth.controller.ts index d6a09284..553eeac4 100644 --- a/src/controllers/auth.controller.ts +++ b/src/controllers/auth.controller.ts @@ -7,17 +7,13 @@ import { JWT_SECRET } from '../configs/envConfig' export const register = async (req: Request, res: Response): Promise => { try { - const { email, password, type } = req.body + const { email, password } = req.body if (!email || !password) { res.status(400).json({ error: 'Email and password are required fields' }) } - const { statusCode, message, profile } = await registerUser( - email, - password, - type - ) + const { statusCode, message, profile } = await registerUser(email, password) res.status(statusCode).json({ message, profile }) } catch (err) { diff --git a/src/routes/admin/admin.route.test.ts b/src/routes/admin/admin.route.test.ts index bfe8ef3d..d88ef533 100644 --- a/src/routes/admin/admin.route.test.ts +++ b/src/routes/admin/admin.route.test.ts @@ -1,7 +1,10 @@ import { startServer } from '../../app' import type { Express } from 'express' import supertest from 'supertest' -import type Profile from '../../entities/profile.entity' +import Profile from '../../entities/profile.entity' +import { ProfileTypes } from '../../enums' +import { dataSource } from '../../configs/dbConfig' +import bcrypt from 'bcrypt' const port = Math.floor(Math.random() * (9999 - 3000 + 1)) + 3000 @@ -31,14 +34,24 @@ describe('Get all users route', () => { const adminUser = { email: `test${randomStringAdmin}@gmail.com`, - password: 'admin123', - type: 'admin' + password: 'admin123' } - await supertest(server) - .post('/api/auth/register') - .send(adminUser) - .expect(201) + const profileRepository = dataSource.getRepository(Profile) + + const hashedPassword = await bcrypt.hash(adminUser.password, 10) + const newProfile = profileRepository.create({ + primary_email: adminUser.email, + password: hashedPassword, + contact_email: '', + first_name: '', + last_name: '', + image_url: '', + linkedin_url: '', + type: ProfileTypes.ADMIN + }) + + await profileRepository.save(newProfile) await adminAgent.post('/api/auth/login').send(adminUser).expect(200) }, 5000) diff --git a/src/services/auth.service.ts b/src/services/auth.service.ts index c0aae3c2..179f832c 100644 --- a/src/services/auth.service.ts +++ b/src/services/auth.service.ts @@ -3,12 +3,10 @@ import bcrypt from 'bcrypt' import jwt from 'jsonwebtoken' import Profile from '../entities/profile.entity' import { JWT_SECRET } from '../configs/envConfig' -import type { ProfileTypes } from '../enums' export const registerUser = async ( email: string, - password: string, - userType: ProfileTypes + password: string ): Promise<{ statusCode: number message: string @@ -29,7 +27,6 @@ export const registerUser = async ( const newProfile = profileRepository.create({ primary_email: email, password: hashedPassword, - type: userType, contact_email: '', first_name: '', last_name: '',