Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Implement Create email template endpoint (Admin) #40 #75

Merged
merged 14 commits into from
Oct 24, 2023
5 changes: 5 additions & 0 deletions mocks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,3 +66,8 @@ export const platformInfo = {
},
title: 'Sample Mentoring Program'
}

export const emailTemplateInfo = {
subject: 'Follow-up on your mentoring session',
content: 'Sample content'
}
59 changes: 59 additions & 0 deletions src/controllers/admin/emailTemplate.controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import type { Request, Response } from 'express'
import type { ApiResponse } from '../../types'
import type Profile from '../../entities/profile.entity'
import { ProfileTypes } from '../../enums'
import {
createEmailTemplate,
getEmailTemplateById
} from '../../services/admin/emailTemplate.service'
import type EmailTemplate from '../../entities/emailTemplate.entity'

export const addEmailTemplate = async (
req: Request,
res: Response
): Promise<ApiResponse<EmailTemplate>> => {
try {
const user = req.user as Profile
const { subject, content } = req.body

if (user.type !== ProfileTypes.ADMIN) {
return res.status(403).json({ message: 'Only Admins are allowed' })
}

if (!subject || !content) {
return res.status(400).json({
error: 'Subject, content and state are required fields'
})
}
const { emailTemplate, statusCode, message } = await createEmailTemplate(
subject,
content
)
return res.status(statusCode).json({ emailTemplate, message })
} catch (err) {
console.error('Error executing query', err)
return res.status(500).json({ error: err })
}
}

export const getEmailTemplate = async (
req: Request,
res: Response
): Promise<ApiResponse<EmailTemplate>> => {
try {
const user = req.user as Profile
const templateId = req.params.mentorId

if (user.type !== ProfileTypes.ADMIN) {
return res.status(403).json({ message: 'Only Admins are allowed' })
}

const { emailTemplate, statusCode, message } = await getEmailTemplateById(
templateId
)
return res.status(statusCode).json({ emailTemplate, message })
} catch (err) {
console.error('Error executing query', err)
return res.status(500).json({ error: err })
}
}
31 changes: 30 additions & 1 deletion src/controllers/admin/mentor.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@ import { ApplicationStatus, ProfileTypes } from '../../enums'
import type Profile from '../../entities/profile.entity'
import type Mentor from '../../entities/mentor.entity'
import type { ApiResponse } from '../../types'
import { updateAvailability } from '../../services/mentor.service'
import {
searchMentorsByQuery,
updateAvailability
} from '../../services/mentor.service'

export const mentorStatusHandler = async (
req: Request,
Expand Down Expand Up @@ -134,3 +137,29 @@ export const updateMentorAvailability = async (
throw err
}
}

export const searchMentors = async (
req: Request,
res: Response
): Promise<ApiResponse<Mentor>> => {
try {
const user = req.user as Profile
const q: string | undefined = req.query.q as string | undefined

if (user.type !== ProfileTypes.ADMIN) {
return res.status(403).json({ message: 'Only Admins are allowed' })
}

const { statusCode, mentors, message } = await searchMentorsByQuery(q)
return res.status(statusCode).json({ mentors, 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
}
}
19 changes: 19 additions & 0 deletions src/entities/emailTemplate.entity.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { Column, Entity } from 'typeorm'
import BaseEntity from './baseEntity'

@Entity('emailTemplate')
class EmailTemplate extends BaseEntity {
@Column({ type: 'varchar', length: 255 })
subject: string

@Column({ type: 'varchar', length: 655 })
content: string

constructor(subject: string, content: string) {
super()
this.subject = subject
this.content = content
}
}

export default EmailTemplate
2 changes: 2 additions & 0 deletions src/routes/admin/admin.route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,14 @@ import userRouter from './user/user.route'
import mentorRouter from './mentor/mentor.route'
import categoryRouter from './category/category.route'
import platformRouter from './platform/platform.route'
import emailTemplateRouter from './email/emailTemplate.route'

const adminRouter = express()

adminRouter.use('/users', userRouter)
adminRouter.use('/mentors', mentorRouter)
adminRouter.use('/categories', categoryRouter)
adminRouter.use('/platform', platformRouter)
adminRouter.use('/emailTemplate', emailTemplateRouter)

export default adminRouter
74 changes: 74 additions & 0 deletions src/routes/admin/email/emailTemplate.route.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import { startServer } from '../../../app'
import type { Express } from 'express'
import supertest from 'supertest'
import bcrypt from 'bcrypt'
import { emailTemplateInfo, mockAdmin, mockUser } from '../../../../mocks'
import { dataSource } from '../../../configs/dbConfig'
import Profile from '../../../entities/profile.entity'
import { ProfileTypes } from '../../../enums'
import type EmailTemplate from '../../../entities/emailTemplate.entity'

const port = Math.floor(Math.random() * (9999 - 3000 + 1)) + 3000

let server: Express
let agent: supertest.SuperAgentTest
let adminAgent: supertest.SuperAgentTest
let savedEmailTemplate: EmailTemplate

describe('Admin email template routes', () => {
beforeAll(async () => {
server = await startServer(port)
agent = supertest.agent(server)
adminAgent = supertest.agent(server)

await supertest(server)
.post('/api/auth/register')
.send(mockUser)
.expect(201)
await agent.post('/api/auth/#').send(mockUser).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/#').send(mockAdmin).expect(200)
}, 5000)

it('should add a email template', async () => {
const response = await adminAgent
.post('/api/admin/emailTemplate')
.send({ ...emailTemplateInfo })
.expect(201)

savedEmailTemplate = response.body.emailTemplate
})

it('should only allow admins to add a email template', async () => {
await agent
.post('/api/admin/emailTemplate')
.send({ ...emailTemplateInfo })
.expect(403)
})

it('should get a email template by passing template id', async () => {
const response = await adminAgent
.get(`/api/admin/emailTemplate/${savedEmailTemplate.uuid}`)
.expect(200)

const emailTemplate = response.body.emailTemplate

expect(emailTemplate).toHaveProperty('content')
})
})
13 changes: 13 additions & 0 deletions src/routes/admin/email/emailTemplate.route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import express from 'express'
import { requireAuth } from '../../../controllers/auth.controller'
import {
addEmailTemplate,
getEmailTemplate
} from '../../../controllers/admin/emailTemplate.controller'

const emailTemplateRouter = express.Router()

emailTemplateRouter.post('/', requireAuth, addEmailTemplate)
emailTemplateRouter.get('/:templateId', requireAuth, getEmailTemplate)

export default emailTemplateRouter
22 changes: 22 additions & 0 deletions src/routes/admin/mentor/mentor.route.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,28 @@ describe('Admin mentor routes', () => {
}
)

it('should return mentors first name starts with john and a success message when mentors are found', async () => {
const updatedProfile = {
contact_email: 'test_contact@example.com',
first_name: 'John',
last_name: 'Doe',
image_url: 'https://example.com/test_profile_image.jpg',
linkedin_url: 'https://www.linkedin.com/in/johndoe'
}

await mentorAgent.put('/api/me/profile').send(updatedProfile).expect(200)

const response = await adminAgent
.get(`/api/admin/mentors/search?q=john`)
.expect(200)

expect(response.body).toHaveProperty('mentors')
})

it('should only allow admins to search mentors', async () => {
await mentorAgent.get(`/api/admin/mentors/search?q=john`).expect(403)
})

afterAll(async () => {
await dataSource.destroy()
})
Expand Down
2 changes: 2 additions & 0 deletions src/routes/admin/mentor/mentor.route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
getAllMentorEmails,
getAllMentorsByStatus,
mentorStatusHandler,
searchMentors,
updateMentorAvailability
} from '../../../controllers/admin/mentor.controller'

Expand All @@ -17,5 +18,6 @@ mentorRouter.put(
requireAuth,
updateMentorAvailability
)
mentorRouter.get('/search', requireAuth, searchMentors)

export default mentorRouter
3 changes: 2 additions & 1 deletion src/routes/admin/platform/platform.route.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ describe('Admin platform routes', () => {
it('should add a platform', async () => {
await adminAgent.post('/api/admin/platform').send(platformInfo).expect(201)
})

it('should only allow admins to add a platform', async () => {
await agent.post('/api/admin/categories').send(platformInfo).expect(403)
})
Expand All @@ -67,7 +68,7 @@ describe('Admin platform routes', () => {
})
})

it('should only allow admins to add a platform', async () => {
it('should only allow admins to get a platform', async () => {
await agent.get('/api/admin/platform').expect(403)
})
})
63 changes: 63 additions & 0 deletions src/services/admin/emailTemplate.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { dataSource } from '../../configs/dbConfig'
import EmailTemplate from '../../entities/emailTemplate.entity'

export const createEmailTemplate = async (
subject: string,
content: string
): Promise<{
statusCode: number
emailTemplate?: EmailTemplate | null
message: string
}> => {
try {
const emailTemplateRepositroy = dataSource.getRepository(EmailTemplate)

const newEmailTemplate = new EmailTemplate(subject, content)

const savedEmailTemplate = await emailTemplateRepositroy.save(
newEmailTemplate
)

return {
statusCode: 201,
emailTemplate: savedEmailTemplate,
message: 'Email Template created successfully'
}
} catch (err) {
console.error('Error creating Email Template', err)
throw new Error('Error creating Email Template')
}
}

export const getEmailTemplateById = async (
templateId: string
): Promise<{
statusCode: number
emailTemplate?: EmailTemplate | null
message: string
}> => {
try {
const emailRepositroy = dataSource.getRepository(EmailTemplate)

const emailTemplate = await emailRepositroy.findOne({
where: { uuid: templateId },
select: ['uuid', 'content', 'subject']
})

if (!emailTemplate) {
return {
statusCode: 404,
message: 'Email template not found'
}
}

return {
statusCode: 200,
emailTemplate,
message: 'Email template found'
}
} catch (err) {
console.error('Error getting email template', err)
throw new Error('Error getting email template')
}
}
Loading