From c95e3bc2860e362eebf265fa160965d7ee95c5d3 Mon Sep 17 00:00:00 2001 From: Shrenik Deep <92735170+Shrenik0321@users.noreply.github.com> Date: Sat, 23 Sep 2023 21:31:07 +0530 Subject: [PATCH] Implement mentor availability endpoint (#64) Co-authored-by: Yoshitha Rathnayake --- src/controllers/mentor.controller.ts | 22 ++++++- src/routes/admin/admin.route.test.ts | 82 ++++++++++++++++++++++++++ src/routes/mentor/mentor.route.test.ts | 14 +++++ src/routes/mentor/mentor.route.ts | 6 +- src/services/admin.service.ts | 8 +++ src/services/mentor.service.ts | 30 ++++++++++ 6 files changed, 159 insertions(+), 3 deletions(-) create mode 100644 src/routes/admin/admin.route.test.ts create mode 100644 src/services/admin.service.ts diff --git a/src/controllers/mentor.controller.ts b/src/controllers/mentor.controller.ts index d823938d..fb8ba9fa 100644 --- a/src/controllers/mentor.controller.ts +++ b/src/controllers/mentor.controller.ts @@ -1,5 +1,5 @@ import type { Request, Response } from 'express' -import { createMentor } from '../services/mentor.service' +import { createMentor, updateAvailability } from '../services/mentor.service' import type Profile from '../entities/profile.entity' export const mentorApplicationHandler = async ( @@ -9,7 +9,6 @@ export const mentorApplicationHandler = async ( try { const user = req.user as Profile const { application, categoryId } = req.body - const { mentor, statusCode, message } = await createMentor( user, application, @@ -26,3 +25,22 @@ export const mentorApplicationHandler = async ( } } } + +export const mentorAvailabilityHandler = async ( + req: Request, + res: Response +): Promise => { + try { + const user = req.user as Profile + const { availability } = req.body + const result = await updateAvailability(user, availability) + res.status(result.statusCode).json(result.updatedMentorApplication) + } catch (err) { + if (err instanceof Error) { + console.error('Error executing query', err) + res + .status(500) + .json({ error: 'Internal server error', message: err.message }) + } + } +} diff --git a/src/routes/admin/admin.route.test.ts b/src/routes/admin/admin.route.test.ts new file mode 100644 index 00000000..22b5c2f7 --- /dev/null +++ b/src/routes/admin/admin.route.test.ts @@ -0,0 +1,82 @@ +import { startServer } from '../../app' +import type { Express } from 'express' +import supertest from 'supertest' +import Profile from '../../entities/profile.entity' +import { ProfileTypes } from '../../enums' +import { dataSource } from '../../configs/dbConfig' +import bcrypt from 'bcrypt' +import { mockAdmin, mockUser } from '../../../mocks' + +const port = Math.floor(Math.random() * (9999 - 3000 + 1)) + 3000 +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 defaultUser = { + ...mockUser + } + + await supertest(server) + .post('/api/auth/register') + .send(defaultUser) + .expect(201) + + await agent.post('/api/auth/login').send(defaultUser).expect(200) + + const adminUser = { + ...mockAdmin + } + + 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) + + 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 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 + + userProfiles.forEach((userProfile: Partial) => { + 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/mentor/mentor.route.test.ts b/src/routes/mentor/mentor.route.test.ts index 2f7f2714..428c5b0c 100644 --- a/src/routes/mentor/mentor.route.test.ts +++ b/src/routes/mentor/mentor.route.test.ts @@ -61,6 +61,20 @@ describe('Mentor application', () => { await supertest(server).post('/api/mentors').send({}).expect(401) }) + describe('Update Mentor Availability', () => { + it.each([true, false])( + 'should update mentor availability and return a 201 with the updated availability', + async (availability) => { + const response = await agent + .put('/api/mentors/me/availability') + .send({ availability }) + .expect(200) + + expect(response.body).toHaveProperty('availability', availability) + } + ) + }) + afterAll(async () => { await dataSource.destroy() }) diff --git a/src/routes/mentor/mentor.route.ts b/src/routes/mentor/mentor.route.ts index 17b233d5..fdf58263 100644 --- a/src/routes/mentor/mentor.route.ts +++ b/src/routes/mentor/mentor.route.ts @@ -1,9 +1,13 @@ import express from 'express' import { requireAuth } from './../../controllers/auth.controller' -import { mentorApplicationHandler } from './../../controllers/mentor.controller' +import { + mentorApplicationHandler, + mentorAvailabilityHandler +} from './../../controllers/mentor.controller' const mentorRouter = express.Router() mentorRouter.post('/', requireAuth, mentorApplicationHandler) +mentorRouter.put('/me/availability', requireAuth, mentorAvailabilityHandler) export default mentorRouter diff --git a/src/services/admin.service.ts b/src/services/admin.service.ts new file mode 100644 index 00000000..e4a9fa3d --- /dev/null +++ b/src/services/admin.service.ts @@ -0,0 +1,8 @@ +import { dataSource } from '../configs/dbConfig' +import Profile from '../entities/profile.entity' + +export const getAllUsers = async (): Promise => { + const profileRepository = dataSource.getRepository(Profile) + const allUsers = await profileRepository.find() + return allUsers +} diff --git a/src/services/mentor.service.ts b/src/services/mentor.service.ts index e8193e6a..68b51960 100644 --- a/src/services/mentor.service.ts +++ b/src/services/mentor.service.ts @@ -72,3 +72,33 @@ export const createMentor = async ( throw new Error('Error creating mentor') } } + +export const updateAvailability = async ( + user: Profile, + availability: boolean +): Promise<{ statusCode: number; updatedMentorApplication: Mentor }> => { + try { + const mentorRepository = dataSource.getRepository(Mentor) + const existingMentorApplications = await mentorRepository.find({ + where: { profile: { uuid: user.uuid } } + }) + + const mentorApplication = existingMentorApplications[0] + + if (mentorApplication) { + mentorApplication.availability = availability + const updatedMentorApplication = await mentorRepository.save( + mentorApplication + ) + return { + statusCode: 200, + updatedMentorApplication + } + } else { + throw new Error('Mentor application not found') + } + } catch (err) { + console.error('Error creating mentor', err) + throw new Error('Error creating mentor') + } +}