Skip to content

Commit

Permalink
Implment Monthly-Checking API Endpoint (#172)
Browse files Browse the repository at this point in the history
  • Loading branch information
mayura-andrew authored Oct 15, 2024
1 parent 400b8fd commit d88d261
Show file tree
Hide file tree
Showing 11 changed files with 410 additions and 6 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"typeorm:db": "npm run build && npx typeorm -d dist/src/configs/dbConfig.js",
"migration:generate": "npm run typeorm:db -- migration:generate",
"migration:run": "npm run typeorm:db -- migration:run",
"migration:revert": "npm run typeorm:db -- migration:revert",
"sync:db": "npm run typeorm:db schema:sync",
"seed": "npm run build && node dist/src/scripts/seed-db.js"
},
Expand Down
95 changes: 95 additions & 0 deletions src/controllers/monthlyChecking.controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import type { Request, Response } from 'express'
import { type ApiResponse } from '../types'
import type MonthlyCheckIn from '../entities/checkin.entity'

import {
addFeedbackByMentor,
addMonthlyCheckInByMentee,
fetchMonthlyCheckIns
} from '../services/monthlyChecking.service'

export const postMonthlyCheckIn = async (
req: Request,
res: Response
): Promise<Response<ApiResponse<MonthlyCheckIn>>> => {
try {
const {
menteeId,
title,
generalUpdatesAndFeedback,
progressTowardsGoals,
mediaContentLinks
} = req.body

const newCheckIn = await addMonthlyCheckInByMentee(
menteeId,
title,
generalUpdatesAndFeedback,
progressTowardsGoals,
mediaContentLinks
)

return res
.status(201)
.json({ checkIn: newCheckIn, message: 'Check-in added successfully' })
} catch (err) {
if (err instanceof Error) {
console.error('Error executing query', err)
return res
.status(500)
.json({ error: 'Internal server error', message: err.message })
}
throw err
}
}

export const getMonthlyCheckIns = async (
req: Request,
res: Response
): Promise<Response<ApiResponse<MonthlyCheckIn>>> => {
try {
const { menteeId } = req.params

const { statusCode, checkIns, message } = await fetchMonthlyCheckIns(
menteeId
)

return res.status(statusCode).json({ checkIns, message })
} catch (err) {
if (err instanceof Error) {
console.error('Error executing query', err)
return res
.status(500)
.json({ error: 'Internal server error', message: err.message })
}
throw err
}
}

export const addFeedbackMonthlyCheckIn = async (
req: Request,
res: Response
): Promise<void> => {
try {
const { checkInId, menteeId, mentorFeedback, isCheckedByMentor } = req.body

const newMentorFeedbackCheckIn = await addFeedbackByMentor(
menteeId,
checkInId,
mentorFeedback,
isCheckedByMentor
)

res.status(201).json({
feedbackCheckIn: newMentorFeedbackCheckIn
})
} catch (err) {
if (err instanceof Error) {
console.error('Error executing query', err)
res
.status(500)
.json({ error: 'Internal server error', message: err.message })
}
throw err
}
}
63 changes: 63 additions & 0 deletions src/entities/checkin.entity.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { Entity, Column, ManyToOne, JoinColumn } from 'typeorm'
import BaseEntity from './baseEntity'
import Mentee from './mentee.entity'

@Entity('monthly-check-in')
class MonthlyCheckIn extends BaseEntity {
@Column({ type: 'text' })
title: string

@Column({ type: 'text' })
generalUpdatesAndFeedback: string

@Column({ type: 'text' })
progressTowardsGoals: string

@Column({ type: 'json' })
mediaContentLinks: string[]

@Column({ type: 'text', nullable: true })
mentorFeedback: string

@Column({ type: 'boolean', default: false })
isCheckedByMentor: boolean

@Column({ type: 'timestamp', nullable: true })
mentorCheckedDate: Date

@Column({ type: 'timestamp', default: () => 'CURRENT_TIMESTAMP' })
checkInDate: Date

@ManyToOne(() => Mentee, (mentee) => mentee.checkIns)
@JoinColumn({ name: 'menteeId' })
mentee: Mentee

constructor(
title: string,
generalUpdatesAndFeedback: string,
progressTowardsGoals: string,
mediaContentLinks: string[],
mentorFeedback: string,
isCheckedByMentor: boolean,
mentorCheckedDate: Date,
checkInDate: Date,
mentee: Mentee
) {
super()
this.title = title
this.generalUpdatesAndFeedback = generalUpdatesAndFeedback
this.progressTowardsGoals = progressTowardsGoals
this.mediaContentLinks = mediaContentLinks
this.mentorFeedback = mentorFeedback
this.isCheckedByMentor = isCheckedByMentor
this.mentorCheckedDate = mentorCheckedDate
this.checkInDate = checkInDate
this.mentee = mentee
}

validate(): boolean {
return this.mediaContentLinks.length >= 3
}
}

export default MonthlyCheckIn
10 changes: 8 additions & 2 deletions src/entities/mentee.entity.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { Column, Entity, ManyToOne } from 'typeorm'
import { Column, Entity, ManyToOne, OneToMany } from 'typeorm'
import Mentor from './mentor.entity'
import profileEntity from './profile.entity'
import { MenteeApplicationStatus, StatusUpdatedBy } from '../enums'
import BaseEntity from './baseEntity'
import { UUID } from 'typeorm/driver/mongodb/bson.typings'
import MonthlyCheckIn from './checkin.entity'

@Entity('mentee')
class Mentee extends BaseEntity {
Expand Down Expand Up @@ -35,17 +36,22 @@ class Mentee extends BaseEntity {
@ManyToOne(() => Mentor, (mentor) => mentor.mentees)
mentor: Mentor

@OneToMany(() => MonthlyCheckIn, (checkIn) => checkIn.mentee)
checkIns?: MonthlyCheckIn[]

constructor(
state: MenteeApplicationStatus,
application: Record<string, unknown>,
profile: profileEntity,
mentor: Mentor
mentor: Mentor,
checkIns?: MonthlyCheckIn[]
) {
super()
this.state = state || MenteeApplicationStatus.PENDING
this.application = application
this.profile = profile
this.mentor = mentor
this.checkIns = checkIns
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,25 @@ export class RemoveUniqueConstraintFromProfileUuid1722051742722
name = 'RemoveUniqueConstraintFromProfileUuid1722051742722'

public async up(queryRunner: QueryRunner): Promise<void> {
// Check if the constraint exists before attempting to drop it
const constraintExists = await queryRunner.query(`
SELECT 1
FROM information_schema.table_constraints
WHERE constraint_name = 'REL_f671cf2220d1bd0621a1a5e92e'
AND table_name = 'mentee'
`)

if (constraintExists.length > 0) {
await queryRunner.query(
`ALTER TABLE "mentee" DROP CONSTRAINT "REL_f671cf2220d1bd0621a1a5e92e"`
)
}

await queryRunner.query(
`ALTER TABLE "mentee" DROP CONSTRAINT "FK_f671cf2220d1bd0621a1a5e92e7"`
)
await queryRunner.query(
`ALTER TABLE "mentee" DROP CONSTRAINT "REL_f671cf2220d1bd0621a1a5e92e"`
)
await queryRunner.query(
`ALTER TABLE "mentee" ADD CONSTRAINT "FK_f671cf2220d1bd0621a1a5e92e7" FOREIGN KEY ("profileUuid") REFERENCES "profile"("uuid") ON DELETE NO ACTION ON UPDATE NO ACTION`
`ALTER TABLE "mentee" ADD CONSTRAINT "FK_f671cf2220d1bd0621a1a5e92e7" FOREIGN KEY ("profileUuid") REFERENCES "profile"("uuid") ON DELETE CASCADE ON UPDATE NO ACTION`
)
}

Expand Down
14 changes: 14 additions & 0 deletions src/migrations/1727197270336-monthly-checking-tags.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { MigrationInterface, QueryRunner } from "typeorm";

export class MonthlyCheckingTags1727197270336 implements MigrationInterface {
name = 'MonthlyCheckingTags1727197270336'

public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "monthly-check-in" ADD "tags" json`);
}

public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "monthly-check-in" DROP COLUMN "tags"`);
}

}
13 changes: 13 additions & 0 deletions src/migrations/1727636762101-monthlychecking.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { type MigrationInterface, type QueryRunner } from 'typeorm'

export class Monthlychecking1727636762101 implements MigrationInterface {
name = 'Monthlychecking1727636762101'

public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "monthly-check-in" DROP COLUMN "tags"`)
}

public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "monthly-check-in" ADD "tags" json`)
}
}
21 changes: 21 additions & 0 deletions src/routes/mentee/mentee.route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,16 @@ import {
} from '../../controllers/mentee.controller'
import { requestBodyValidator } from '../../middlewares/requestValidator'
import {
addFeedbackMonthlyCheckInSchema,
menteeApplicationSchema,
postMonthlyCheckInSchema,
updateMenteeStatusSchema
} from '../../schemas/mentee-routes.schemas'
import {
addFeedbackMonthlyCheckIn,
getMonthlyCheckIns,
postMonthlyCheckIn
} from '../../controllers/monthlyChecking.controller'

const menteeRouter = express.Router()

Expand All @@ -29,4 +36,18 @@ menteeRouter.put(
)
menteeRouter.put('/revoke-application', requireAuth, revokeApplication)

menteeRouter.post(
'/checkin',
[requireAuth, requestBodyValidator(postMonthlyCheckInSchema)],
postMonthlyCheckIn
)

menteeRouter.get('/checkin/:menteeId', requireAuth, getMonthlyCheckIns)

menteeRouter.put(
'/checking/feedback',
[requireAuth, requestBodyValidator(addFeedbackMonthlyCheckInSchema)],
addFeedbackMonthlyCheckIn
)

export default menteeRouter
34 changes: 34 additions & 0 deletions src/schemas/mentee-routes.schemas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,37 @@ export const menteeApplicationSchema = z.object({
export const updateMenteeStatusSchema = z.object({
state: z.nativeEnum(MenteeApplicationStatus)
})

export const postMonthlyCheckInSchema = z.object({
menteeId: z.string(),
title: z.enum([
'January',
'February',
'March',
'April',
'May',
'June',
'July',
'August',
'September',
'October',
'November',
'December'
]),
generalUpdatesAndFeedback: z
.string()
.min(1, 'Please provide a valid feedback'),
progressTowardsGoals: z
.string()
.min(1, 'Please provide a valid progress report'),
mediaContentLinks: z
.array(z.string())
.min(1, 'Please provide at least 1 media content links')
})

export const addFeedbackMonthlyCheckInSchema = z.object({
menteeId: z.string(),
checkInId: z.string(),
mentorFeedback: z.string().optional(),
isCheckedByMentor: z.boolean()
})
Loading

0 comments on commit d88d261

Please sign in to comment.