From ee7c77b8945f65f75cf30eff2127fb5ce4f1bbbc Mon Sep 17 00:00:00 2001 From: Krishnadeva Date: Wed, 4 Oct 2023 23:29:53 +0530 Subject: [PATCH 1/4] Implement Get all mentors endpoint (Admin) #24 --- src/controllers/admin/mentor.controller.ts | 33 +++++++- src/routes/admin/mentor/mentor.route.ts | 6 +- src/services/admin/mentor.service.test.ts | 78 ++++++++++++++++++ src/services/admin/mentor.service.ts | 95 ++++++++++++++++++++++ src/types.ts | 22 +++++ 5 files changed, 232 insertions(+), 2 deletions(-) create mode 100644 src/services/admin/mentor.service.test.ts diff --git a/src/controllers/admin/mentor.controller.ts b/src/controllers/admin/mentor.controller.ts index 4d0ee0d9..cbc462a1 100644 --- a/src/controllers/admin/mentor.controller.ts +++ b/src/controllers/admin/mentor.controller.ts @@ -1,5 +1,8 @@ import type { Request, Response } from 'express' -import { updateMentorStatus } from '../../services/admin/mentor.service' +import { + getAllMentors, + updateMentorStatus +} from '../../services/admin/mentor.service' import { ApplicationStatus, ProfileTypes } from '../../enums' import type Profile from '../../entities/profile.entity' @@ -34,3 +37,31 @@ export const mentorStatusHandler = async ( } } } + +export const getAllMentorsByStatus = async ( + req: Request, + res: Response +): Promise => { + try { + const user = req.user as Profile + const status: ApplicationStatus = req.query.status as ApplicationStatus + + if (user.type !== ProfileTypes.ADMIN) { + res.status(403).json({ message: 'Only Admins are allowed' }) + } else { + if (!(status.toUpperCase() in ApplicationStatus)) { + res.status(400).json({ message: 'Please provide a valid status' }) + return + } + const { mentors, statusCode, message } = await getAllMentors(status) + res.status(statusCode).json({ mentors, message }) + } + } 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/mentor/mentor.route.ts b/src/routes/admin/mentor/mentor.route.ts index c9775994..f8a81015 100644 --- a/src/routes/admin/mentor/mentor.route.ts +++ b/src/routes/admin/mentor/mentor.route.ts @@ -1,9 +1,13 @@ import express from 'express' import { requireAuth } from '../../../controllers/auth.controller' -import { mentorStatusHandler } from '../../../controllers/admin/mentor.controller' +import { + getAllMentorsByStatus, + mentorStatusHandler +} from '../../../controllers/admin/mentor.controller' const mentorRouter = express.Router() mentorRouter.put('/:mentorId/status', requireAuth, mentorStatusHandler) +mentorRouter.get('/', requireAuth, getAllMentorsByStatus) export default mentorRouter diff --git a/src/services/admin/mentor.service.test.ts b/src/services/admin/mentor.service.test.ts new file mode 100644 index 00000000..4f4cfd8a --- /dev/null +++ b/src/services/admin/mentor.service.test.ts @@ -0,0 +1,78 @@ +import { startServer } from '../../app' +import type { Express } from 'express' +import supertest from 'supertest' +import { dataSource } from '../../configs/dbConfig' +import { mockAdmin, mockMentor, mentorApplicationInfo } from '../../../mocks' +import bcrypt from 'bcrypt' +import Profile from '../../entities/profile.entity' +import { ApplicationStatus, ProfileTypes } from '../../enums' +import { getAllMentors } from './mentor.service' +import type { AllMentorsArray } from '../../types' + +const port = Math.floor(Math.random() * (9999 - 3000 + 1)) + 3000 + +let server: Express +let mentorAgent: supertest.SuperAgentTest +let adminAgent: supertest.SuperAgentTest +let mentorId: string + +describe('getAllMentors function', () => { + beforeAll(async () => { + server = await startServer(port) + mentorAgent = supertest.agent(server) + adminAgent = supertest.agent(server) + + await mentorAgent.post('/api/auth/register').send(mockMentor).expect(201) + await mentorAgent.post('/api/auth/login').send(mockMentor).expect(200) + + const profileRepository = dataSource.getRepository(Profile) + const hashedPassword = await bcrypt.hash(mockAdmin.password, 10) + const newProfile = profileRepository.create({ + primary_email: mockAdmin.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(mockAdmin).expect(200) + const categoryResponse = await adminAgent + .post('/api/admin/categories') + .send({ categoryName: 'Computer Science' }) + .expect(201) + + const response = await mentorAgent + .post('/api/mentors') + .send({ + ...mentorApplicationInfo, + categoryId: categoryResponse.body.category.uuid + }) + .expect(201) + + mentorId = response.body.mentor.uuid + }, 5000) + + it('should return mentorsArray and a success message when mentors are found', async () => { + const { mentors, statusCode, message } = await getAllMentors( + ApplicationStatus.APPROVED + ) + + expect(statusCode).toBe(200) + mentors?.forEach((mentor: AllMentorsArray) => { + expect(mentor).toHaveProperty('created_at') + expect(mentor).toHaveProperty('updated_at') + expect(mentor).toHaveProperty('category') + expect(mentor).toHaveProperty('application') + expect(mentor).toHaveProperty('profile') + expect(mentor).toHaveProperty('state') + expect(mentor).toHaveProperty('availability') + expect(mentor).toHaveProperty('mentor_id') + }) + expect(message).toBe('All Mentors found') + }) +}) diff --git a/src/services/admin/mentor.service.ts b/src/services/admin/mentor.service.ts index 99a41bc2..75981607 100644 --- a/src/services/admin/mentor.service.ts +++ b/src/services/admin/mentor.service.ts @@ -1,6 +1,19 @@ import { dataSource } from '../../configs/dbConfig' import Mentor from '../../entities/mentor.entity' import type { ApplicationStatus } from '../../enums' +import type { AllMentorsArray, MentorInfo } from '../../types' + +const applicationFormat: MentorInfo = { + designation: '', + country: '', + areas_of_expertise: '', + expectations_from_mentees: '', + mentoring_philosophy: '', + commitment_to_program: false, + previous_experience_as_mentor: false, + reason_for_being_mentor: '', + cv_link: '' +} export const updateMentorStatus = async ( mentorId: string, @@ -36,3 +49,85 @@ export const updateMentorStatus = async ( throw new Error('Error updating the mentor status') } } + +export const getAllMentors = async ( + status: ApplicationStatus +): Promise<{ + statusCode: number + mentors?: AllMentorsArray[] + message: string +}> => { + try { + const mentorRepository = dataSource.getRepository(Mentor) + + const mentorsList: Mentor[] = await mentorRepository.find({ + where: { state: status }, + select: [ + 'application', + 'availability', + 'state', + 'created_at', + 'updated_at' + ], + relations: ['profile', 'category'] + }) + + const mentors: AllMentorsArray[] = mentorsList.map((mentor, i) => { + Object.entries(mentor.application).map((item) => { + switchQuestion(item, applicationFormat) + }) + return { + ...mentor, + mentor_id: i + 1, + application: applicationFormat, + category: mentor.category.category, + state: mentor.state.toUpperCase() + } + }) + + if (!mentors) { + return { + statusCode: 404, + message: 'Mentors not found' + } + } + + return { + statusCode: 200, + mentors, + message: 'All Mentors found' + } + } catch (err) { + console.error('Error updating the mentor status', err) + throw new Error('Error updating the mentor status') + } +} + +function switchQuestion(item: any, format: MentorInfo): void { + switch (item[1].question.toLowerCase()) { + case 'what is your country?': + format.country = item[1].answers + break + case 'what is your expertise?': + format.areas_of_expertise = item[1].answers + break + case 'what is your mentoring startegy?': + format.mentoring_philosophy = item[1].answers + break + case 'what is your designation?': + format.designation = item[1].answers + break + case 'what is your reason for being mentor?': + format.reason_for_being_mentor = item[1].answers + break + case 'what is your expectations from mentees?': + format.expectations_from_mentees = item[1].answers + break + case 'do you have revious experience as mentor?': + format.previous_experience_as_mentor = item[1].answers + break + default: + // Handle other cases if needed + break + } +} diff --git a/src/types.ts b/src/types.ts index 5596d525..386438dd 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,3 +1,4 @@ +import type Profile from './entities/profile.entity' interface Option { // todo: To be determined (Not final) answer: string @@ -16,3 +17,24 @@ export interface MenteeApplication { state: string mentor_id: bigint } + +export interface MentorInfo { + designation: string + country: string + areas_of_expertise: string + expectations_from_mentees: string + mentoring_philosophy: string + commitment_to_program: boolean + previous_experience_as_mentor: boolean + reason_for_being_mentor: string + cv_link: string +} + +export interface AllMentorsArray { + mentor_id: number + state: string + category: string + application: MentorInfo + availability: boolean + profile: Profile +} From ffe92b73992b241462bec4acf5d60b9ec0a57718 Mon Sep 17 00:00:00 2001 From: Krishnadeva Date: Sat, 7 Oct 2023 17:01:17 +0530 Subject: [PATCH 2/4] Resolved mentioned issues --- src/controllers/admin/mentor.controller.ts | 4 - src/routes/admin/mentor/mentor.route.test.ts | 16 +++- src/services/admin/mentor.service.test.ts | 78 -------------------- src/services/admin/mentor.service.ts | 66 ++--------------- src/types.ts | 10 --- 5 files changed, 20 insertions(+), 154 deletions(-) delete mode 100644 src/services/admin/mentor.service.test.ts diff --git a/src/controllers/admin/mentor.controller.ts b/src/controllers/admin/mentor.controller.ts index cbc462a1..25dd559a 100644 --- a/src/controllers/admin/mentor.controller.ts +++ b/src/controllers/admin/mentor.controller.ts @@ -49,10 +49,6 @@ export const getAllMentorsByStatus = async ( if (user.type !== ProfileTypes.ADMIN) { res.status(403).json({ message: 'Only Admins are allowed' }) } else { - if (!(status.toUpperCase() in ApplicationStatus)) { - res.status(400).json({ message: 'Please provide a valid status' }) - return - } const { mentors, statusCode, message } = await getAllMentors(status) res.status(statusCode).json({ mentors, message }) } diff --git a/src/routes/admin/mentor/mentor.route.test.ts b/src/routes/admin/mentor/mentor.route.test.ts index 4b78e3a6..b72e40c8 100644 --- a/src/routes/admin/mentor/mentor.route.test.ts +++ b/src/routes/admin/mentor/mentor.route.test.ts @@ -2,7 +2,7 @@ import { startServer } from '../../../app' import type { Express } from 'express' import supertest from 'supertest' import Profile from '../../../entities/profile.entity' -import { ProfileTypes } from '../../../enums' +import { ApplicationStatus, ProfileTypes } from '../../../enums' import { dataSource } from '../../../configs/dbConfig' import bcrypt from 'bcrypt' import { mentorApplicationInfo, mockAdmin, mockMentor } from '../../../../mocks' @@ -75,4 +75,18 @@ describe('Admin mentor routes', () => { .send({ status: 'approved' }) .expect(403) }) + + it('should return mentorsArray and a success message when mentors are found', async () => { + const response = await adminAgent + .get(`/api/admin/mentors?status=${ApplicationStatus.APPROVED}`) + .expect(200) + + expect(response.body).toHaveProperty('mentors') + }) + + it('should only allow admins to get the mentors', async () => { + await mentorAgent + .get(`/api/admin/mentors?status=${ApplicationStatus.APPROVED}`) + .expect(403) + }) }) diff --git a/src/services/admin/mentor.service.test.ts b/src/services/admin/mentor.service.test.ts deleted file mode 100644 index 4f4cfd8a..00000000 --- a/src/services/admin/mentor.service.test.ts +++ /dev/null @@ -1,78 +0,0 @@ -import { startServer } from '../../app' -import type { Express } from 'express' -import supertest from 'supertest' -import { dataSource } from '../../configs/dbConfig' -import { mockAdmin, mockMentor, mentorApplicationInfo } from '../../../mocks' -import bcrypt from 'bcrypt' -import Profile from '../../entities/profile.entity' -import { ApplicationStatus, ProfileTypes } from '../../enums' -import { getAllMentors } from './mentor.service' -import type { AllMentorsArray } from '../../types' - -const port = Math.floor(Math.random() * (9999 - 3000 + 1)) + 3000 - -let server: Express -let mentorAgent: supertest.SuperAgentTest -let adminAgent: supertest.SuperAgentTest -let mentorId: string - -describe('getAllMentors function', () => { - beforeAll(async () => { - server = await startServer(port) - mentorAgent = supertest.agent(server) - adminAgent = supertest.agent(server) - - await mentorAgent.post('/api/auth/register').send(mockMentor).expect(201) - await mentorAgent.post('/api/auth/login').send(mockMentor).expect(200) - - const profileRepository = dataSource.getRepository(Profile) - const hashedPassword = await bcrypt.hash(mockAdmin.password, 10) - const newProfile = profileRepository.create({ - primary_email: mockAdmin.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(mockAdmin).expect(200) - const categoryResponse = await adminAgent - .post('/api/admin/categories') - .send({ categoryName: 'Computer Science' }) - .expect(201) - - const response = await mentorAgent - .post('/api/mentors') - .send({ - ...mentorApplicationInfo, - categoryId: categoryResponse.body.category.uuid - }) - .expect(201) - - mentorId = response.body.mentor.uuid - }, 5000) - - it('should return mentorsArray and a success message when mentors are found', async () => { - const { mentors, statusCode, message } = await getAllMentors( - ApplicationStatus.APPROVED - ) - - expect(statusCode).toBe(200) - mentors?.forEach((mentor: AllMentorsArray) => { - expect(mentor).toHaveProperty('created_at') - expect(mentor).toHaveProperty('updated_at') - expect(mentor).toHaveProperty('category') - expect(mentor).toHaveProperty('application') - expect(mentor).toHaveProperty('profile') - expect(mentor).toHaveProperty('state') - expect(mentor).toHaveProperty('availability') - expect(mentor).toHaveProperty('mentor_id') - }) - expect(message).toBe('All Mentors found') - }) -}) diff --git a/src/services/admin/mentor.service.ts b/src/services/admin/mentor.service.ts index 75981607..a832067e 100644 --- a/src/services/admin/mentor.service.ts +++ b/src/services/admin/mentor.service.ts @@ -1,19 +1,6 @@ import { dataSource } from '../../configs/dbConfig' import Mentor from '../../entities/mentor.entity' import type { ApplicationStatus } from '../../enums' -import type { AllMentorsArray, MentorInfo } from '../../types' - -const applicationFormat: MentorInfo = { - designation: '', - country: '', - areas_of_expertise: '', - expectations_from_mentees: '', - mentoring_philosophy: '', - commitment_to_program: false, - previous_experience_as_mentor: false, - reason_for_being_mentor: '', - cv_link: '' -} export const updateMentorStatus = async ( mentorId: string, @@ -51,17 +38,17 @@ export const updateMentorStatus = async ( } export const getAllMentors = async ( - status: ApplicationStatus + status: ApplicationStatus | null ): Promise<{ statusCode: number - mentors?: AllMentorsArray[] + mentors?: Mentor[] message: string }> => { try { const mentorRepository = dataSource.getRepository(Mentor) - const mentorsList: Mentor[] = await mentorRepository.find({ - where: { state: status }, + const mentors: Mentor[] = await mentorRepository.find({ + where: status ? { state: status } : {}, select: [ 'application', 'availability', @@ -72,19 +59,6 @@ export const getAllMentors = async ( relations: ['profile', 'category'] }) - const mentors: AllMentorsArray[] = mentorsList.map((mentor, i) => { - Object.entries(mentor.application).map((item) => { - switchQuestion(item, applicationFormat) - }) - return { - ...mentor, - mentor_id: i + 1, - application: applicationFormat, - category: mentor.category.category, - state: mentor.state.toUpperCase() - } - }) - if (!mentors) { return { statusCode: 404, @@ -98,36 +72,6 @@ export const getAllMentors = async ( message: 'All Mentors found' } } catch (err) { - console.error('Error updating the mentor status', err) - throw new Error('Error updating the mentor status') - } -} - -function switchQuestion(item: any, format: MentorInfo): void { - switch (item[1].question.toLowerCase()) { - case 'what is your country?': - format.country = item[1].answers - break - case 'what is your expertise?': - format.areas_of_expertise = item[1].answers - break - case 'what is your mentoring startegy?': - format.mentoring_philosophy = item[1].answers - break - case 'what is your designation?': - format.designation = item[1].answers - break - case 'what is your reason for being mentor?': - format.reason_for_being_mentor = item[1].answers - break - case 'what is your expectations from mentees?': - format.expectations_from_mentees = item[1].answers - break - case 'do you have revious experience as mentor?': - format.previous_experience_as_mentor = item[1].answers - break - default: - // Handle other cases if needed - break + throw new Error('Error getting mentors') } } diff --git a/src/types.ts b/src/types.ts index 386438dd..85aa6e55 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,4 +1,3 @@ -import type Profile from './entities/profile.entity' interface Option { // todo: To be determined (Not final) answer: string @@ -29,12 +28,3 @@ export interface MentorInfo { reason_for_being_mentor: string cv_link: string } - -export interface AllMentorsArray { - mentor_id: number - state: string - category: string - application: MentorInfo - availability: boolean - profile: Profile -} From 26a9b7d2391290cdfa5c9dad50f8602d0c30d389 Mon Sep 17 00:00:00 2001 From: Krishnadeva Date: Sat, 7 Oct 2023 19:05:45 +0530 Subject: [PATCH 3/4] Removed mentoInfo type and status check added --- src/controllers/admin/mentor.controller.ts | 4 ++++ src/routes/admin/mentor/mentor.route.test.ts | 2 +- src/types.ts | 12 ------------ 3 files changed, 5 insertions(+), 13 deletions(-) diff --git a/src/controllers/admin/mentor.controller.ts b/src/controllers/admin/mentor.controller.ts index 25dd559a..cbc462a1 100644 --- a/src/controllers/admin/mentor.controller.ts +++ b/src/controllers/admin/mentor.controller.ts @@ -49,6 +49,10 @@ export const getAllMentorsByStatus = async ( if (user.type !== ProfileTypes.ADMIN) { res.status(403).json({ message: 'Only Admins are allowed' }) } else { + if (!(status.toUpperCase() in ApplicationStatus)) { + res.status(400).json({ message: 'Please provide a valid status' }) + return + } const { mentors, statusCode, message } = await getAllMentors(status) res.status(statusCode).json({ mentors, message }) } diff --git a/src/routes/admin/mentor/mentor.route.test.ts b/src/routes/admin/mentor/mentor.route.test.ts index b72e40c8..d5a272e9 100644 --- a/src/routes/admin/mentor/mentor.route.test.ts +++ b/src/routes/admin/mentor/mentor.route.test.ts @@ -76,7 +76,7 @@ describe('Admin mentor routes', () => { .expect(403) }) - it('should return mentorsArray and a success message when mentors are found', async () => { + it('should return mentors and a success message when mentors are found', async () => { const response = await adminAgent .get(`/api/admin/mentors?status=${ApplicationStatus.APPROVED}`) .expect(200) diff --git a/src/types.ts b/src/types.ts index 85aa6e55..5596d525 100644 --- a/src/types.ts +++ b/src/types.ts @@ -16,15 +16,3 @@ export interface MenteeApplication { state: string mentor_id: bigint } - -export interface MentorInfo { - designation: string - country: string - areas_of_expertise: string - expectations_from_mentees: string - mentoring_philosophy: string - commitment_to_program: boolean - previous_experience_as_mentor: boolean - reason_for_being_mentor: string - cv_link: string -} From 8801c20245f915bb8f800963f5d9f6780bd8e56d Mon Sep 17 00:00:00 2001 From: Krishnadeva Date: Sun, 8 Oct 2023 22:16:34 +0530 Subject: [PATCH 4/4] Get all mentors without status test implemented --- src/controllers/admin/mentor.controller.ts | 6 ++++-- src/routes/admin/mentor/mentor.route.test.ts | 6 ++++++ src/services/admin/mentor.service.ts | 2 +- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/controllers/admin/mentor.controller.ts b/src/controllers/admin/mentor.controller.ts index cbc462a1..299c6f17 100644 --- a/src/controllers/admin/mentor.controller.ts +++ b/src/controllers/admin/mentor.controller.ts @@ -44,12 +44,14 @@ export const getAllMentorsByStatus = async ( ): Promise => { try { const user = req.user as Profile - const status: ApplicationStatus = req.query.status as ApplicationStatus + const status: ApplicationStatus | undefined = req.query.status as + | ApplicationStatus + | undefined if (user.type !== ProfileTypes.ADMIN) { res.status(403).json({ message: 'Only Admins are allowed' }) } else { - if (!(status.toUpperCase() in ApplicationStatus)) { + if (status && !(status?.toUpperCase() in ApplicationStatus)) { res.status(400).json({ message: 'Please provide a valid status' }) return } diff --git a/src/routes/admin/mentor/mentor.route.test.ts b/src/routes/admin/mentor/mentor.route.test.ts index d5a272e9..52702567 100644 --- a/src/routes/admin/mentor/mentor.route.test.ts +++ b/src/routes/admin/mentor/mentor.route.test.ts @@ -89,4 +89,10 @@ describe('Admin mentor routes', () => { .get(`/api/admin/mentors?status=${ApplicationStatus.APPROVED}`) .expect(403) }) + + it('should return mentors and a success message when mentors are found', async () => { + const response = await adminAgent.get(`/api/admin/mentors`).expect(200) + + expect(response.body).toHaveProperty('mentors') + }) }) diff --git a/src/services/admin/mentor.service.ts b/src/services/admin/mentor.service.ts index a832067e..b5bd9749 100644 --- a/src/services/admin/mentor.service.ts +++ b/src/services/admin/mentor.service.ts @@ -38,7 +38,7 @@ export const updateMentorStatus = async ( } export const getAllMentors = async ( - status: ApplicationStatus | null + status: ApplicationStatus | undefined ): Promise<{ statusCode: number mentors?: Mentor[]