From 7a7aae32155f6f1ee7c6742e985addf1debdb72f Mon Sep 17 00:00:00 2001 From: Anjula Shanaka Date: Sun, 4 Aug 2024 21:04:25 +0530 Subject: [PATCH] Update mentee application status with revoke status (#148) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Well Done 🚀 --- .github/workflows/CI.yml | 10 ++++ .gitignore | 1 + src/app.ts | 6 +++ src/controllers/admin/mentee.controller.ts | 22 +++++---- src/controllers/admin/mentor.controller.ts | 16 +++--- src/controllers/mentee.controller.ts | 4 +- src/controllers/mentor.controller.ts | 8 +-- src/entities/mentee.entity.ts | 12 ++--- src/entities/mentor.entity.ts | 10 ++-- src/enums/index.ts | 11 ++++- .../1722749907154-AddNewApplicationStates.ts | 49 +++++++++++++++++++ src/scripts/seed-db.ts | 10 ++-- src/services/admin/mentee.service.test.ts | 16 +++--- src/services/admin/mentee.service.ts | 21 +++++--- src/services/admin/mentor.service.ts | 8 +-- .../admin/mentor_admin.service.test.ts | 18 +++---- src/services/mentee.service.ts | 17 +++++-- src/services/mentor.service.ts | 14 +++--- src/types.ts | 19 ------- src/utils.ts | 26 ++++------ 20 files changed, 184 insertions(+), 114 deletions(-) create mode 100644 src/migrations/1722749907154-AddNewApplicationStates.ts diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 15db856e..baa8b224 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -47,6 +47,16 @@ jobs: echo "DB_PORT=5432" >> .env echo "SERVER_PORT=4000" >> .env echo "JWT_SECRET=your_jwt_secret_key" >> .env + echo "GOOGLE_CLIENT_ID=your_google_client_id" >> .env + echo "GOOGLE_CLIENT_SECRET=your_google_client_secret" >> .env + echo "GOOGLE_REDIRECT_URL=http://localhost:${SERVER_PORT}/api/auth/google/callback" >> .env + echo "CLIENT_URL=http://localhost:5173" >> .env + echo "IMG_HOST=http://localhost:${SERVER_PORT}" >> .env + echo "SMTP_MAIL=your_smtp_mail" >> .env + echo "SMTP_PASSWORD=your_smtp_password" >> .env + echo "LINKEDIN_CLIENT_ID=your_linkedin_client_id" >> .env + echo "LINKEDIN_CLIENT_SECRET=your_linkedin_client_secret" >> .env + echo "LINKEDIN_REDIRECT_URL=http://localhost:${SERVER_PORT}/api/auth/linkedin/callback" >> .env - name: Run tests run: npm run test diff --git a/.gitignore b/.gitignore index 762e799b..ab65a7f9 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ node_modules/ dist/ .idea uploads/ +certificates/ diff --git a/src/app.ts b/src/app.ts index 8499359b..a144bf43 100644 --- a/src/app.ts +++ b/src/app.ts @@ -16,9 +16,11 @@ import emailRouter from './routes/emails/emails.route' import menteeRouter from './routes/mentee/mentee.route' import mentorRouter from './routes/mentor/mentor.route' import profileRouter from './routes/profile/profile.route' +import path from 'path' const app = express() const staticFolder = 'uploads' +export const certificatesDir = path.join(__dirname, 'certificates') app.use(cookieParser()) app.use(bodyParser.json()) @@ -51,6 +53,10 @@ if (!fs.existsSync(staticFolder)) { console.log('Directory already exists.') } +if (!fs.existsSync(certificatesDir)) { + fs.mkdirSync(certificatesDir) +} + export const startServer = async (port: number): Promise => { try { await dataSource.initialize() diff --git a/src/controllers/admin/mentee.controller.ts b/src/controllers/admin/mentee.controller.ts index d73df916..e97c70bd 100644 --- a/src/controllers/admin/mentee.controller.ts +++ b/src/controllers/admin/mentee.controller.ts @@ -1,5 +1,9 @@ import type { Request, Response } from 'express' -import { ApplicationStatus, ProfileTypes, StatusUpdatedBy } from '../../enums' +import { + MenteeApplicationStatus, + ProfileTypes, + StatusUpdatedBy +} from '../../enums' import type Profile from '../../entities/profile.entity' import type Mentee from '../../entities/mentee.entity' import type { ApiResponse } from '../../types' @@ -16,15 +20,15 @@ export const getMentees = async ( ): Promise> => { try { const user = req.user as Profile - const status: ApplicationStatus | undefined = req.query.status as - | ApplicationStatus + const status: MenteeApplicationStatus | undefined = req.query.status as + | MenteeApplicationStatus | undefined if (user.type !== ProfileTypes.ADMIN) { return res.status(403).json({ message: 'Only Admins are allowed' }) } - if (status && !(status.toUpperCase() in ApplicationStatus)) { + if (status && !(status.toUpperCase() in MenteeApplicationStatus)) { return res.status(400).json({ message: 'Please provide a valid status' }) } @@ -101,12 +105,10 @@ export const getAllMenteeEmails = async ( res: Response ): Promise> => { try { - const status = req.query.status - if ( - status === ApplicationStatus.APPROVED || - status === ApplicationStatus.REJECTED || - status === ApplicationStatus.PENDING - ) { + const status: MenteeApplicationStatus | undefined = req.query.status as + | MenteeApplicationStatus + | undefined + if (status && status.toUpperCase() in MenteeApplicationStatus) { const { emails, statusCode, message } = await getAllMenteeEmailsService( status ) diff --git a/src/controllers/admin/mentor.controller.ts b/src/controllers/admin/mentor.controller.ts index 0ada121b..90663239 100644 --- a/src/controllers/admin/mentor.controller.ts +++ b/src/controllers/admin/mentor.controller.ts @@ -5,7 +5,7 @@ import { updateMentorStatus, getMentor } from '../../services/admin/mentor.service' -import { ApplicationStatus, ProfileTypes } from '../../enums' +import { MentorApplicationStatus, ProfileTypes } from '../../enums' import type Profile from '../../entities/profile.entity' import type Mentor from '../../entities/mentor.entity' import type { ApiResponse } from '../../types' @@ -27,7 +27,7 @@ export const mentorStatusHandler = async ( return res.status(403).json({ message: 'Only Admins are allowed' }) } - if (!(state.toUpperCase() in ApplicationStatus)) { + if (!(state.toUpperCase() in MentorApplicationStatus)) { return res.status(400).json({ message: 'Please provide a valid status' }) } @@ -54,15 +54,15 @@ export const getAllMentorsByStatus = async ( ): Promise> => { try { const user = req.user as Profile - const status: ApplicationStatus | undefined = req.query.status as - | ApplicationStatus + const status: MentorApplicationStatus | undefined = req.query.status as + | MentorApplicationStatus | undefined if (user.type !== ProfileTypes.ADMIN) { return res.status(403).json({ message: 'Only Admins are allowed' }) } - if (status && !(status.toUpperCase() in ApplicationStatus)) { + if (status && !(status.toUpperCase() in MentorApplicationStatus)) { return res.status(400).json({ message: 'Please provide a valid status' }) } @@ -86,15 +86,15 @@ export const getAllMentorEmails = async ( ): Promise> => { try { const user = req.user as Profile - const status: ApplicationStatus | undefined = req.query.status as - | ApplicationStatus + const status: MentorApplicationStatus | undefined = req.query.status as + | MentorApplicationStatus | undefined if (user.type !== ProfileTypes.ADMIN) { return res.status(403).json({ message: 'Only Admins are allowed' }) } - if (status && !(status.toUpperCase() in ApplicationStatus)) { + if (status && !(status.toUpperCase() in MentorApplicationStatus)) { return res.status(400).json({ message: 'Please provide a valid status' }) } diff --git a/src/controllers/mentee.controller.ts b/src/controllers/mentee.controller.ts index d6bd9d1a..eeb41207 100644 --- a/src/controllers/mentee.controller.ts +++ b/src/controllers/mentee.controller.ts @@ -3,7 +3,7 @@ import { type ApiResponse } from '../types' import type Mentee from '../entities/mentee.entity' import type Profile from '../entities/profile.entity' import { getMentee, updateStatus } from '../services/admin/mentee.service' -import { ApplicationStatus, StatusUpdatedBy } from '../enums' +import { MentorApplicationStatus, StatusUpdatedBy } from '../enums' import { addMentee } from '../services/mentee.service' export const menteeApplicationHandler = async ( @@ -42,7 +42,7 @@ export const updateMenteeStatus = async ( if ( !user.mentor?.filter( - (mentor) => mentor.state === ApplicationStatus.APPROVED + (mentor) => mentor.state === MentorApplicationStatus.APPROVED ) ) { return res.status(403).json({ message: 'Only mentors are allowed' }) diff --git a/src/controllers/mentor.controller.ts b/src/controllers/mentor.controller.ts index 4ff298ac..65db9017 100644 --- a/src/controllers/mentor.controller.ts +++ b/src/controllers/mentor.controller.ts @@ -9,7 +9,7 @@ import type Profile from '../entities/profile.entity' import type Mentor from '../entities/mentor.entity' import type { ApiResponse } from '../types' import type Mentee from '../entities/mentee.entity' -import { ApplicationStatus } from '../enums' +import { MenteeApplicationStatus } from '../enums' import { getAllMenteesByMentor } from '../services/admin/mentee.service' export const mentorApplicationHandler = async ( @@ -117,11 +117,11 @@ export const getMenteesByMentor = async ( ): Promise> => { try { const user = req.user as Profile - const status: ApplicationStatus | undefined = req.query.status as - | ApplicationStatus + const status: MenteeApplicationStatus | undefined = req.query.status as + | MenteeApplicationStatus | undefined - if (status && !(status.toUpperCase() in ApplicationStatus)) { + if (status && !(status.toUpperCase() in MenteeApplicationStatus)) { return res.status(400).json({ message: 'Please provide a valid status' }) } diff --git a/src/entities/mentee.entity.ts b/src/entities/mentee.entity.ts index a6b6ca15..d2d4813d 100644 --- a/src/entities/mentee.entity.ts +++ b/src/entities/mentee.entity.ts @@ -1,7 +1,7 @@ import { Column, Entity, ManyToOne } from 'typeorm' import Mentor from './mentor.entity' import profileEntity from './profile.entity' -import { ApplicationStatus, StatusUpdatedBy } from '../enums' +import { MenteeApplicationStatus, StatusUpdatedBy } from '../enums' import BaseEntity from './baseEntity' import { UUID } from 'typeorm/driver/mongodb/bson.typings' @@ -9,10 +9,10 @@ import { UUID } from 'typeorm/driver/mongodb/bson.typings' class Mentee extends BaseEntity { @Column({ type: 'enum', - enum: ApplicationStatus, - default: ApplicationStatus.PENDING + enum: MenteeApplicationStatus, + default: MenteeApplicationStatus.PENDING }) - state: ApplicationStatus + state: MenteeApplicationStatus @Column({ type: 'enum', enum: StatusUpdatedBy, nullable: true }) status_updated_by!: StatusUpdatedBy @@ -36,13 +36,13 @@ class Mentee extends BaseEntity { mentor: Mentor constructor( - state: ApplicationStatus, + state: MenteeApplicationStatus, application: Record, profile: profileEntity, mentor: Mentor ) { super() - this.state = state || ApplicationStatus.PENDING + this.state = state || MenteeApplicationStatus.PENDING this.application = application this.profile = profile this.mentor = mentor diff --git a/src/entities/mentor.entity.ts b/src/entities/mentor.entity.ts index c9566952..cd6ddfa2 100644 --- a/src/entities/mentor.entity.ts +++ b/src/entities/mentor.entity.ts @@ -2,17 +2,17 @@ import { Column, Entity, JoinColumn, ManyToOne, OneToMany } from 'typeorm' import Profile from './profile.entity' import Mentee from './mentee.entity' import Category from './category.entity' -import { ApplicationStatus } from '../enums' +import { MentorApplicationStatus } from '../enums' import BaseEntity from './baseEntity' @Entity('mentor') class Mentor extends BaseEntity { @Column({ type: 'enum', - enum: ApplicationStatus, - default: ApplicationStatus.PENDING + enum: MentorApplicationStatus, + default: MentorApplicationStatus.PENDING }) - state: ApplicationStatus + state: MentorApplicationStatus @ManyToOne(() => Category, (category) => category.mentors) @JoinColumn() @@ -32,7 +32,7 @@ class Mentor extends BaseEntity { mentees?: Mentee[] constructor( - state: ApplicationStatus, + state: MentorApplicationStatus, category: Category, application: Record, availability: boolean, diff --git a/src/enums/index.ts b/src/enums/index.ts index d6095836..6a84e187 100644 --- a/src/enums/index.ts +++ b/src/enums/index.ts @@ -9,11 +9,18 @@ export enum EmailStatusTypes { FAILED = 'failed' } -export enum ApplicationStatus { +export enum MentorApplicationStatus { + PENDING = 'pending', + REJECTED = 'rejected', + APPROVED = 'approved' +} + +export enum MenteeApplicationStatus { PENDING = 'pending', REJECTED = 'rejected', APPROVED = 'approved', - COMPLETED = 'completed' + COMPLETED = 'completed', + REVOKED = 'revoked' } export enum StatusUpdatedBy { diff --git a/src/migrations/1722749907154-AddNewApplicationStates.ts b/src/migrations/1722749907154-AddNewApplicationStates.ts new file mode 100644 index 00000000..08ba8d9f --- /dev/null +++ b/src/migrations/1722749907154-AddNewApplicationStates.ts @@ -0,0 +1,49 @@ +import { type MigrationInterface, type QueryRunner } from 'typeorm' + +export class AddNewApplicationStates1722749907154 + implements MigrationInterface +{ + name = 'AddNewApplicationStates1722749907154' + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `ALTER TYPE "public"."mentee_state_enum" RENAME TO "mentee_state_enum_old"` + ) + await queryRunner.query( + `CREATE TYPE "public"."mentee_state_enum" AS ENUM('pending', 'rejected', 'approved', 'completed', 'revoked')` + ) + await queryRunner.query( + `ALTER TABLE "mentee" ALTER COLUMN "state" DROP DEFAULT` + ) + await queryRunner.query( + `ALTER TABLE "mentee" ALTER COLUMN "state" TYPE "public"."mentee_state_enum" USING "state"::"text"::"public"."mentee_state_enum"` + ) + await queryRunner.query( + `ALTER TABLE "mentee" ALTER COLUMN "state" SET DEFAULT 'pending'` + ) + await queryRunner.query(`DROP TYPE "public"."mentee_state_enum_old"`) + await queryRunner.query(`ALTER TABLE "mentee" DROP COLUMN "certificate_id"`) + await queryRunner.query(`ALTER TABLE "mentee" ADD "certificate_id" uuid`) + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query(`ALTER TABLE "mentee" DROP COLUMN "certificate_id"`) + await queryRunner.query(`ALTER TABLE "mentee" ADD "certificate_id" bigint`) + await queryRunner.query( + `CREATE TYPE "public"."mentee_state_enum_old" AS ENUM('pending', 'rejected', 'approved')` + ) + await queryRunner.query( + `ALTER TABLE "mentee" ALTER COLUMN "state" DROP DEFAULT` + ) + await queryRunner.query( + `ALTER TABLE "mentee" ALTER COLUMN "state" TYPE "public"."mentee_state_enum_old" USING "state"::"text"::"public"."mentee_state_enum_old"` + ) + await queryRunner.query( + `ALTER TABLE "mentee" ALTER COLUMN "state" SET DEFAULT 'pending'` + ) + await queryRunner.query(`DROP TYPE "public"."mentee_state_enum"`) + await queryRunner.query( + `ALTER TYPE "public"."mentee_state_enum_old" RENAME TO "mentee_state_enum"` + ) + } +} diff --git a/src/scripts/seed-db.ts b/src/scripts/seed-db.ts index b97a8f70..6a18a696 100644 --- a/src/scripts/seed-db.ts +++ b/src/scripts/seed-db.ts @@ -5,7 +5,11 @@ import Email from '../entities/email.entity' import Mentee from '../entities/mentee.entity' import Mentor from '../entities/mentor.entity' import Profile from '../entities/profile.entity' -import { ApplicationStatus, EmailStatusTypes, ProfileTypes } from '../enums' +import { + EmailStatusTypes, + MenteeApplicationStatus, + ProfileTypes +} from '../enums' export const seedDatabaseService = async (): Promise => { try { @@ -74,7 +78,7 @@ export const seedDatabaseService = async (): Promise => { const mentor = mentors[faker.number.int({ min: 0, max: mentors.length - 1 })] return new Mentee( - faker.helpers.enumValue(ApplicationStatus), + faker.helpers.enumValue(MenteeApplicationStatus), { firstName: faker.person.firstName(), lastName: faker.person.lastName(), @@ -125,7 +129,7 @@ const createRandomProfile = (): Partial => { const createMentor = (category: Category, profile: Profile): Mentor => { return { - state: faker.helpers.enumValue(ApplicationStatus), + state: faker.helpers.enumValue(MenteeApplicationStatus), category, application: { firstName: faker.person.firstName(), diff --git a/src/services/admin/mentee.service.test.ts b/src/services/admin/mentee.service.test.ts index cee158e3..886462cc 100644 --- a/src/services/admin/mentee.service.test.ts +++ b/src/services/admin/mentee.service.test.ts @@ -1,6 +1,6 @@ import { getAllMentees, getAllMenteeEmailsService } from './mentee.service' import { dataSource } from '../../configs/dbConfig' -import { ApplicationStatus } from '../../enums' +import { MenteeApplicationStatus } from '../../enums' import type Mentee from '../../entities/mentee.entity' jest.mock('../../configs/dbConfig', () => ({ @@ -11,7 +11,7 @@ jest.mock('../../configs/dbConfig', () => ({ describe('Mentee Service - getAllMenteeEmailsService', () => { it('should get all mentee emails with a specific status successfully', async () => { - const status: ApplicationStatus = ApplicationStatus.APPROVED + const status: MenteeApplicationStatus = MenteeApplicationStatus.APPROVED const mockMentees = [ { @@ -87,7 +87,9 @@ describe('Mentee Service - getAllMenteeEmailsService', () => { mockMenteeRepository ) - const result = await getAllMenteeEmailsService(ApplicationStatus.PENDING) + const result = await getAllMenteeEmailsService( + MenteeApplicationStatus.PENDING + ) expect(result.emails?.length).toBe(0) }) @@ -102,7 +104,7 @@ describe('Mentee Service - getAllMenteeEmailsService', () => { ) await expect( - getAllMenteeEmailsService(ApplicationStatus.APPROVED) + getAllMenteeEmailsService(MenteeApplicationStatus.APPROVED) ).rejects.toThrowError('Error getting mentee emails') }) }) @@ -110,7 +112,7 @@ describe('Mentee Service - getAllMenteeEmailsService', () => { describe('Mentee Service', () => { describe('getAllMentees', () => { it('should get all mentees successfully', async () => { - const status: ApplicationStatus = ApplicationStatus.APPROVED + const status: MenteeApplicationStatus = MenteeApplicationStatus.APPROVED const mockMentees = [ { @@ -145,7 +147,7 @@ describe('Mentee Service', () => { }) it('should handle no mentees found', async () => { - const status: ApplicationStatus = ApplicationStatus.APPROVED + const status: MenteeApplicationStatus = MenteeApplicationStatus.APPROVED const mockMenteeRepository = { find: jest.fn().mockResolvedValue(null) @@ -162,7 +164,7 @@ describe('Mentee Service', () => { }) it('should handle error during mentees retrieval', async () => { - const status: ApplicationStatus = ApplicationStatus.APPROVED + const status: MenteeApplicationStatus = MenteeApplicationStatus.APPROVED const mockMenteeRepository = { find: jest.fn().mockRejectedValue(new Error('Test repository error')) diff --git a/src/services/admin/mentee.service.ts b/src/services/admin/mentee.service.ts index 4f19d82c..82fcb96c 100644 --- a/src/services/admin/mentee.service.ts +++ b/src/services/admin/mentee.service.ts @@ -1,12 +1,16 @@ import { dataSource } from '../../configs/dbConfig' import Mentee from '../../entities/mentee.entity' import Mentor from '../../entities/mentor.entity' -import { ApplicationStatus, type StatusUpdatedBy } from '../../enums' +import { + MenteeApplicationStatus, + MentorApplicationStatus, + type StatusUpdatedBy +} from '../../enums' import { getEmailContent } from '../../utils' import { sendEmail } from './email.service' export const getAllMentees = async ( - status: ApplicationStatus | undefined + status: MenteeApplicationStatus | undefined ): Promise<{ statusCode: number mentees?: Mentee[] @@ -38,7 +42,7 @@ export const getAllMentees = async ( } export const getAllMenteesByMentor = async ( - status: ApplicationStatus | undefined, + status: MenteeApplicationStatus | undefined, userId: string ): Promise<{ statusCode: number @@ -50,7 +54,10 @@ export const getAllMenteesByMentor = async ( const mentorRepository = dataSource.getRepository(Mentor) const mentor: Mentor | null = await mentorRepository.findOne({ - where: { profile: { uuid: userId }, state: ApplicationStatus.APPROVED }, + where: { + profile: { uuid: userId }, + state: MentorApplicationStatus.APPROVED + }, relations: ['profile'] }) @@ -80,7 +87,7 @@ export const getAllMenteesByMentor = async ( export const updateStatus = async ( menteeId: string, - state: ApplicationStatus, + state: MenteeApplicationStatus, statusUpdatedBy: StatusUpdatedBy ): Promise<{ statusCode: number @@ -107,7 +114,7 @@ export const updateStatus = async ( const approvedApplications = await menteeRepository.findOne({ where: { - state: ApplicationStatus.APPROVED, + state: MenteeApplicationStatus.APPROVED, profile: { uuid: profileUuid } @@ -156,7 +163,7 @@ export const updateStatus = async ( } export const getAllMenteeEmailsService = async ( - status: ApplicationStatus | undefined + status: MenteeApplicationStatus | undefined ): Promise<{ statusCode: number emails?: string[] diff --git a/src/services/admin/mentor.service.ts b/src/services/admin/mentor.service.ts index 7db826cb..6ba242e5 100644 --- a/src/services/admin/mentor.service.ts +++ b/src/services/admin/mentor.service.ts @@ -1,12 +1,12 @@ import { dataSource } from '../../configs/dbConfig' import Mentor from '../../entities/mentor.entity' -import type { ApplicationStatus } from '../../enums' +import type { MentorApplicationStatus } from '../../enums' import { getEmailContent } from '../../utils' import { sendEmail } from './email.service' export const updateMentorStatus = async ( mentorId: string, - status: ApplicationStatus + status: MentorApplicationStatus ): Promise<{ statusCode: number mentor?: Mentor | null @@ -54,7 +54,7 @@ export const updateMentorStatus = async ( } export const getAllMentors = async ( - status: ApplicationStatus | undefined + status: MentorApplicationStatus | undefined ): Promise<{ statusCode: number mentors?: Mentor[] @@ -86,7 +86,7 @@ export const getAllMentors = async ( } export const findAllMentorEmails = async ( - status: ApplicationStatus | undefined + status: MentorApplicationStatus | undefined ): Promise<{ statusCode: number emails?: string[] diff --git a/src/services/admin/mentor_admin.service.test.ts b/src/services/admin/mentor_admin.service.test.ts index f675ab53..32749d54 100644 --- a/src/services/admin/mentor_admin.service.test.ts +++ b/src/services/admin/mentor_admin.service.test.ts @@ -5,7 +5,7 @@ import { } from './mentor.service' import { dataSource } from '../../configs/dbConfig' import type Mentor from '../../entities/mentor.entity' -import { ApplicationStatus } from '../../enums' +import { MentorApplicationStatus } from '../../enums' jest.mock('../../configs/dbConfig', () => ({ dataSource: { @@ -17,7 +17,7 @@ describe('Mentor Service', () => { describe('updateMentorStatus', () => { it('should handle mentor not found during update', async () => { const mentorId = 'nonexistent-uuid' - const status: ApplicationStatus = ApplicationStatus.APPROVED + const status: MentorApplicationStatus = MentorApplicationStatus.APPROVED const mockMentorRepository = { findOne: jest.fn().mockResolvedValue(null) @@ -35,7 +35,7 @@ describe('Mentor Service', () => { it('should handle error during mentor status update', async () => { const mentorId = 'mock-uuid' - const status: ApplicationStatus = ApplicationStatus.APPROVED + const status: MentorApplicationStatus = MentorApplicationStatus.APPROVED const mockMentorRepository = { findOne: jest.fn().mockResolvedValue({}), @@ -54,7 +54,7 @@ describe('Mentor Service', () => { describe('getAllMentors', () => { it('should get all mentors successfully', async () => { - const status: ApplicationStatus = ApplicationStatus.APPROVED + const status: MentorApplicationStatus = MentorApplicationStatus.APPROVED const mockMentors = [ { @@ -91,7 +91,7 @@ describe('Mentor Service', () => { }) it('should handle no mentors found', async () => { - const status: ApplicationStatus = ApplicationStatus.APPROVED + const status: MentorApplicationStatus = MentorApplicationStatus.APPROVED const mockMentorRepository = { find: jest.fn().mockResolvedValue(null) @@ -108,7 +108,7 @@ describe('Mentor Service', () => { }) it('should handle error during mentors retrieval', async () => { - const status: ApplicationStatus = ApplicationStatus.APPROVED + const status: MentorApplicationStatus = MentorApplicationStatus.APPROVED const mockMentorRepository = { find: jest.fn().mockRejectedValue(new Error('Test repository error')) @@ -126,7 +126,7 @@ describe('Mentor Service', () => { describe('findAllMentorEmails', () => { it('should get all mentor emails successfully', async () => { - const status: ApplicationStatus = ApplicationStatus.APPROVED + const status: MentorApplicationStatus = MentorApplicationStatus.APPROVED const mockMentors = [ { @@ -164,7 +164,7 @@ describe('Mentor Service', () => { }) it('should handle no mentor emails found', async () => { - const status: ApplicationStatus = ApplicationStatus.APPROVED + const status: MentorApplicationStatus = MentorApplicationStatus.APPROVED const mockMentorRepository = { find: jest.fn().mockResolvedValue(null) @@ -183,7 +183,7 @@ describe('Mentor Service', () => { }) it('should handle error during mentor emails retrieval', async () => { - const status: ApplicationStatus = ApplicationStatus.APPROVED + const status: MentorApplicationStatus = MentorApplicationStatus.APPROVED const mockMentorRepository = { find: jest.fn().mockRejectedValue(new Error('Test repository error')) diff --git a/src/services/mentee.service.ts b/src/services/mentee.service.ts index 7a8f2e5e..430ee405 100644 --- a/src/services/mentee.service.ts +++ b/src/services/mentee.service.ts @@ -2,7 +2,7 @@ import { dataSource } from '../configs/dbConfig' import Mentee from '../entities/mentee.entity' import Mentor from '../entities/mentor.entity' import type Profile from '../entities/profile.entity' -import { ApplicationStatus } from '../enums' +import { MenteeApplicationStatus } from '../enums' import { getEmailContent, getMentorNotifyEmailContent } from '../utils' import { sendEmail } from './admin/email.service' @@ -30,6 +30,13 @@ export const addMentee = async ( } } + if (!mentor.availability) { + return { + statusCode: 403, + message: 'Mentor is not currently available' + } + } + const userMentorProfile = await mentorRepository.findOne({ where: { profile: { @@ -52,12 +59,12 @@ export const addMentee = async ( for (const mentee of existingMentees) { switch (mentee.state) { - case ApplicationStatus.PENDING: + case MenteeApplicationStatus.PENDING: return { statusCode: 409, message: 'The mentee application is pending' } - case ApplicationStatus.APPROVED: + case MenteeApplicationStatus.APPROVED: return { statusCode: 409, message: 'The user is already a mentee' @@ -68,7 +75,7 @@ export const addMentee = async ( } const newMentee = new Mentee( - ApplicationStatus.PENDING, + MenteeApplicationStatus.PENDING, application, user, mentor @@ -78,7 +85,7 @@ export const addMentee = async ( const content = await getEmailContent( 'mentee', - ApplicationStatus.PENDING, + MenteeApplicationStatus.PENDING, application.firstName as string ) diff --git a/src/services/mentor.service.ts b/src/services/mentor.service.ts index de568790..0a587dd9 100644 --- a/src/services/mentor.service.ts +++ b/src/services/mentor.service.ts @@ -1,7 +1,7 @@ import { dataSource } from '../configs/dbConfig' import Mentor from '../entities/mentor.entity' import type Profile from '../entities/profile.entity' -import { ApplicationStatus } from '../enums' +import { MentorApplicationStatus } from '../enums' import Category from '../entities/category.entity' import { getEmailContent, getMentorPublicData } from '../utils' import { sendEmail } from './admin/email.service' @@ -54,13 +54,13 @@ export const createMentor = async ( for (const mentor of existingMentorApplications) { switch (mentor.state) { - case ApplicationStatus.PENDING: + case MentorApplicationStatus.PENDING: return { mentor, statusCode: 409, message: 'You have already applied' } - case ApplicationStatus.APPROVED: + case MentorApplicationStatus.APPROVED: return { mentor, statusCode: 409, @@ -72,7 +72,7 @@ export const createMentor = async ( } const newMentor = new Mentor( - ApplicationStatus.PENDING, + MentorApplicationStatus.PENDING, category, application, true, @@ -83,7 +83,7 @@ export const createMentor = async ( const content = await getEmailContent( 'mentor', - ApplicationStatus.PENDING, + MentorApplicationStatus.PENDING, application.firstName as string ) @@ -229,9 +229,9 @@ export const getAllMentors = async ( where: categoryId ? { category: { uuid: categoryId }, - state: ApplicationStatus.APPROVED + state: MentorApplicationStatus.APPROVED } - : { state: ApplicationStatus.APPROVED }, + : { state: MentorApplicationStatus.APPROVED }, relations: ['profile', 'category'], select: ['application', 'uuid', 'availability'], order: { diff --git a/src/types.ts b/src/types.ts index 6773ba40..ac36d009 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,22 +1,3 @@ -interface Option { - // todo: To be determined (Not final) - answer: string -} - -interface Question { - question: string - answer: string - // todo: Types should be declared here (Not final) - type: 'TYPES' - options: Option -} - -export interface MenteeApplication { - answers: Question[] - state: string - mentor_id: bigint -} - export interface ApiResponse { statusCode: number message?: string diff --git a/src/utils.ts b/src/utils.ts index 28423407..9ff30558 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -3,12 +3,12 @@ import jwt from 'jsonwebtoken' import type { Response } from 'express' import type Mentor from './entities/mentor.entity' import path from 'path' -import fs from 'fs' import multer from 'multer' import ejs from 'ejs' -import { ApplicationStatus } from './enums' +import { MenteeApplicationStatus, MentorApplicationStatus } from './enums' import { generateCertificate } from './services/admin/generateCertificate' import { randomUUID } from 'crypto' +import { certificatesDir } from './app' export const signAndSetCookie = (res: Response, uuid: string): void => { const token = jwt.sign({ userId: uuid }, JWT_SECRET ?? '') @@ -84,7 +84,7 @@ export const loadTemplate = ( export const getEmailContent = async ( type: 'mentor' | 'mentee', - status: ApplicationStatus, + status: MenteeApplicationStatus | MentorApplicationStatus, name: string ): Promise< | { @@ -97,14 +97,14 @@ export const getEmailContent = async ( > => { if (type === 'mentor') { switch (status) { - case ApplicationStatus.PENDING: + case MentorApplicationStatus.PENDING: return { subject: 'Thank you very much for applying to ScholarX', message: `Dear ${name},

Thank you very much for applying to ScholarX. Your application has been received. Our team will soon review your application, and we will keep you posted on the progress via email. Please reach out to us via sustainableedufoundation@gmail.com for any clarifications.` } - case ApplicationStatus.APPROVED: + case MentorApplicationStatus.APPROVED: return { subject: 'Congratulations! You have been selected as a ScholarX Mentor', @@ -126,7 +126,7 @@ export const getEmailContent = async ( Please ensure you review this guide thoroughly to understand the next steps and to prepare for your journey with us.` } - case ApplicationStatus.REJECTED: + case MentorApplicationStatus.REJECTED: return { subject: 'Thank You for Your Interest in the ScholarX Program', message: `Dear ${name},

@@ -141,13 +141,13 @@ export const getEmailContent = async ( } } else { switch (status) { - case ApplicationStatus.PENDING: + case MenteeApplicationStatus.PENDING: return { subject: 'Thank you very much for applying to ScholarX', message: `Dear ${name},

Thank you very much for applying to ScholarX. Your application has been received. Mentor will soon review your application and we will keep you posted on the progress via email. Until then, read more about student experience here and reach out to us via sustainableedufoundation@gmail.com for any clarifications. ` } - case ApplicationStatus.APPROVED: + case MenteeApplicationStatus.APPROVED: return { subject: 'Congratulations! You have been selected for ScholarX', message: `Dear ${name},

@@ -167,7 +167,7 @@ export const getEmailContent = async (

Please ensure you review this guide thoroughly to understand the next steps and to prepare for your journey with us.` } - case ApplicationStatus.REJECTED: + case MenteeApplicationStatus.REJECTED: return { subject: 'Thank You for Your Interest in the ScholarX Program', message: `Dear ${name},

@@ -177,13 +177,7 @@ export const getEmailContent = async ( We do offer the possibility for you to apply again next time if you meet the eligibility criteria. We invite you to stay engaged with us by attending our events, reaching out to our admissions team, and taking advantage of any opportunities to connect with our current students and alumni.

Thank you again for considering our program and for the time you invested in your application. We wish you all the best in your future endeavours.` } - case ApplicationStatus.COMPLETED: { - const certificatesDir = path.join(__dirname, 'certificates') - - if (!fs.existsSync(certificatesDir)) { - fs.mkdirSync(certificatesDir) - } - + case MenteeApplicationStatus.COMPLETED: { const templatePath = path.join( __dirname, 'templates',