Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Add the approve mentor and create category endpoints #63

Merged
merged 5 commits into from
Sep 11, 2023
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 0 additions & 19 deletions .github/workflows/sonarQube.yml

This file was deleted.

17 changes: 17 additions & 0 deletions mocks.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,20 @@
const randomString = Math.random().toString(36)

export const mockMentor = {
email: `mentor${randomString}@gmail.com`,
password: '123'
}

export const mockAdmin = {
email: `admin${randomString}@gmail.com`,
password: 'admin123'
}

export const mockUser = {
email: `user${randomString}@gmail.com`,
password: '123'
}

export const mentorApplicationInfo = {
application: [
{
Expand Down
27 changes: 27 additions & 0 deletions src/controllers/admin/category.controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import type { Request, Response } from 'express'
import type Profile from '../../entities/profile.entity'
import { ProfileTypes } from '../../enums'
import { createCategory } from '../../services/admin/category.service'

export const addCategory = async (
req: Request,
res: Response
): Promise<void> => {
try {
const user = req.user as Profile
const { categoryName } = req.body

if (user.type !== ProfileTypes.ADMIN) {
res.status(403).json({ message: 'Only Admins are allowed' })
} else {
const { category, statusCode, message } = await createCategory(
categoryName
)

res.status(statusCode).json({ category, message })
}
} catch (err) {
console.error('Error executing query', err)
res.status(500).json({ error: err })
}
}
37 changes: 37 additions & 0 deletions src/controllers/admin/mentor.controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import type { Request, Response } from 'express'
import { updateMentorStatus } from '../../services/admin/mentor.service'
import { ApplicationStatus, ProfileTypes } from '../../enums'
import type Profile from '../../entities/profile.entity'

export const mentorStatusHandler = async (
req: Request,
res: Response
): Promise<void> => {
try {
const user = req.user as Profile
const { status } = req.body
const { mentorId } = req.params

if (!(status.toUpperCase() in ApplicationStatus)) {
res.status(400).json({ message: 'Please provide a valid status' })
return
}

if (user.type !== ProfileTypes.ADMIN) {
anjula-sack marked this conversation as resolved.
Show resolved Hide resolved
res.status(403).json({ message: 'Only Admins are allowed' })
} else {
const { mentor, statusCode, message } = await updateMentorStatus(
mentorId,
status
)
res.status(statusCode).json({ mentor, message })
}
} catch (err) {
if (err instanceof Error) {
console.error('Error executing query', err)
res
.status(500)
.json({ error: 'Internal server error', message: err.message })
}
}
}
3 changes: 2 additions & 1 deletion src/controllers/admin/user.controller.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { Request, Response } from 'express'
import { getAllUsers } from '../../services/admin.service'
import { getAllUsers } from '../../services/admin/user.service'
import type Profile from '../../entities/profile.entity'
import { ProfileTypes } from '../../enums'

Expand All @@ -9,6 +9,7 @@ export const getAllUsersHandler = async (
): Promise<void> => {
try {
const user = req.user as Profile

if (user.type !== ProfileTypes.ADMIN) {
res.status(403).json({ message: 'Only Admins are allowed' })
} else {
Expand Down
4 changes: 1 addition & 3 deletions src/controllers/mentor.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,12 @@ export const mentorApplicationHandler = async (
try {
const user = req.user as Profile
const { application, categoryId } = req.body

const { mentor, statusCode, message } = await createMentor(
user,
application,
categoryId
)
if (!mentor) {
res.status(404).json({ message: 'Mentor not created' })
}

res.status(statusCode).json({ mentor, message })
} catch (err) {
Expand Down
2 changes: 1 addition & 1 deletion src/enums/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,5 @@ export enum EmailStatusTypes {
export enum ApplicationStatus {
PENDING = 'pending',
REJECTED = 'rejected',
ACCEPTED = 'accepted'
APPROVED = 'approved'
}
11 changes: 7 additions & 4 deletions src/routes/admin/admin.route.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import express from 'express'
import { getAllUsersHandler } from '../../controllers/admin/user.controller'
import { requireAuth } from '../../controllers/auth.controller'
import userRouter from './user/user.route'
import mentorRouter from './mentor/mentor.route'
import categoryRouter from './category/category.route'

const adminRouter = express.Router()
const adminRouter = express()

adminRouter.get('/users', requireAuth, getAllUsersHandler)
adminRouter.use('/users', userRouter)
adminRouter.use('/mentors', mentorRouter)
adminRouter.use('/categories', categoryRouter)

export default adminRouter
60 changes: 60 additions & 0 deletions src/routes/admin/category/category.route.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
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 { mockUser, mockAdmin } from '../../../../mocks'

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

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

describe('Admin category 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/login').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/login').send(mockAdmin).expect(200)
}, 5000)

it('should add a category', async () => {
await adminAgent
.post('/api/admin/categories')
.send({ categoryName: 'Computer Science' })
.expect(201)
})

it('should only allow admins to add a category', async () => {
await agent
.post('/api/admin/categories')
.send({ categoryName: 'Computer Science' })
.expect(403)
})
})
9 changes: 9 additions & 0 deletions src/routes/admin/category/category.route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import express from 'express'
import { requireAuth } from '../../../controllers/auth.controller'
import { addCategory } from '../../../controllers/admin/category.controller'

const categoryRouter = express.Router()

categoryRouter.post('/', requireAuth, addCategory)

export default categoryRouter
78 changes: 78 additions & 0 deletions src/routes/admin/mentor/mentor.route.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
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 { mentorApplicationInfo, mockAdmin, mockMentor } from '../../../../mocks'

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('Admin mentor routes', () => {
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 update the mentor application state', async () => {
await adminAgent
.put(`/api/admin/mentors/${mentorId}/status`)
.send({ status: 'approved' })
.expect(200)
})

it('should return 400 when an invalid status was provided', async () => {
await adminAgent
.put(`/api/admin/mentors/${mentorId}/status`)
.send({ status: 'invalid status' })
.expect(400)
})

it('should only allow admins to update the status', async () => {
await mentorAgent
.put(`/api/admin/mentors/${mentorId}/status`)
.send({ status: 'approved' })
.expect(403)
})
})
9 changes: 9 additions & 0 deletions src/routes/admin/mentor/mentor.route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import express from 'express'
import { requireAuth } from '../../../controllers/auth.controller'
import { mentorStatusHandler } from '../../../controllers/admin/mentor.controller'

const mentorRouter = express.Router()

mentorRouter.put('/:mentorId/status', requireAuth, mentorStatusHandler)

export default mentorRouter
Original file line number Diff line number Diff line change
@@ -1,47 +1,35 @@
import { startServer } from '../../app'
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 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

const randomString = Math.random().toString(36)
const randomStringAdmin = Math.random().toString(36)
let server: Express
let agent: supertest.SuperAgentTest
let adminAgent: supertest.SuperAgentTest

describe('Get all users route', () => {
describe('Admin user routes', () => {
beforeAll(async () => {
server = await startServer(port)
agent = supertest.agent(server)
adminAgent = supertest.agent(server)

const defaultUser = {
email: `test${randomString}@gmail.com`,
password: '123'
}

await supertest(server)
.post('/api/auth/register')
.send(defaultUser)
.send(mockUser)
.expect(201)

await agent.post('/api/auth/login').send(defaultUser).expect(200)

const adminUser = {
email: `test${randomStringAdmin}@gmail.com`,
password: 'admin123'
}
await agent.post('/api/auth/login').send(mockUser).expect(200)

const profileRepository = dataSource.getRepository(Profile)

const hashedPassword = await bcrypt.hash(adminUser.password, 10)
const hashedPassword = await bcrypt.hash(mockAdmin.password, 10)
const newProfile = profileRepository.create({
primary_email: adminUser.email,
primary_email: mockAdmin.email,
password: hashedPassword,
contact_email: '',
first_name: '',
Expand All @@ -53,7 +41,7 @@ describe('Get all users route', () => {

await profileRepository.save(newProfile)

await adminAgent.post('/api/auth/login').send(adminUser).expect(200)
await adminAgent.post('/api/auth/login').send(mockAdmin).expect(200)
}, 5000)

it('should return a 401 when a valid access token is not provided', async () => {
Expand Down
9 changes: 9 additions & 0 deletions src/routes/admin/user/user.route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import express from 'express'
import { getAllUsersHandler } from '../../../controllers/admin/user.controller'
import { requireAuth } from '../../../controllers/auth.controller'

const userRouter = express.Router()

userRouter.get('/', requireAuth, getAllUsersHandler)

export default userRouter
Loading