-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
implemented warning in frontend for invalid confirmation(activity) Id…
…s and a new endpoint in backend to perform said check
- Loading branch information
Showing
11 changed files
with
227 additions
and
7 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
import { activityService } from '../services'; | ||
|
||
import { ACTIVITY_ID_LENGTH } from '../utils/constants/application'; | ||
|
||
import type { NextFunction, Request, Response } from '../interfaces/IExpress'; | ||
|
||
const controller = { | ||
validateActivityId: async (req: Request<{ activityId: string }>, res: Response, next: NextFunction) => { | ||
try { | ||
const { activityId } = req.params; | ||
const hexidecimal = parseInt(activityId, 16); | ||
|
||
if (activityId.length !== ACTIVITY_ID_LENGTH || !hexidecimal) { | ||
return res.status(400).json({ message: 'Invalid activity Id format' }); | ||
} | ||
const activity = await activityService.getActivity(activityId); | ||
|
||
res.status(200).json({ valid: !!activity && !activity.isDeleted }); | ||
} catch (e: unknown) { | ||
next(e); | ||
} | ||
} | ||
}; | ||
|
||
export default controller; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
import express from 'express'; | ||
import { activityController } from '../../controllers'; | ||
import { requireSomeAuth } from '../../middleware/requireSomeAuth'; | ||
|
||
import type { NextFunction, Request, Response } from '../../interfaces/IExpress'; | ||
|
||
const router = express.Router(); | ||
router.use(requireSomeAuth); | ||
|
||
//** Validates an Activity Id */ | ||
router.get('/validate/:activityId', (req: Request, res: Response, next: NextFunction): void => { | ||
activityController.validateActivityId(req, res, next); | ||
}); | ||
|
||
export default router; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,146 @@ | ||
import { activityController } from '../../../src/controllers'; | ||
import { activityService } from '../../../src/services'; | ||
|
||
// Mock config library - @see {@link https://stackoverflow.com/a/64819698} | ||
jest.mock('config'); | ||
|
||
const mockResponse = () => { | ||
const res: { status?: jest.Mock; json?: jest.Mock; end?: jest.Mock } = {}; | ||
res.status = jest.fn().mockReturnValue(res); | ||
res.json = jest.fn().mockReturnValue(res); | ||
|
||
return res; | ||
}; | ||
|
||
let res: { status?: jest.Mock; json?: jest.Mock; end?: jest.Mock }; | ||
beforeEach(() => { | ||
res = mockResponse(); | ||
}); | ||
|
||
afterEach(() => { | ||
/* | ||
* Must use clearAllMocks when using the mocked config | ||
* resetAllMocks seems to cause strange issues such as | ||
* functions not calling as expected | ||
*/ | ||
jest.clearAllMocks(); | ||
}); | ||
|
||
const CURRENT_USER = { authType: 'BEARER', tokenPayload: null }; | ||
|
||
const ACTIVITY = { | ||
activityId: '12345678', | ||
initiativeId: '59cd9e86-7cce-4791-b071-69002c731315', | ||
isDeleted: false | ||
}; | ||
|
||
const DELETED_ACTIVITY = { | ||
activityId: '87654321', | ||
initiativeId: '59cd9e86-7cce-4791-b071-69002c731315', | ||
isDeleted: true | ||
}; | ||
|
||
describe('validateActivityId', () => { | ||
const next = jest.fn(); | ||
|
||
// Mock service calls | ||
const activityServiceSpy = jest.spyOn(activityService, 'getActivity'); | ||
|
||
it('shoulld return status 200 and valid true if activityId exists and isDeleted is false', async () => { | ||
const req = { | ||
params: { activityId: '12345678' }, | ||
currentUser: CURRENT_USER | ||
}; | ||
|
||
activityServiceSpy.mockResolvedValue(ACTIVITY); | ||
|
||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
await activityController.validateActivityId(req as any, res as any, next); | ||
|
||
expect(activityServiceSpy).toHaveBeenCalledTimes(1); | ||
expect(activityServiceSpy).toHaveBeenCalledWith(req.params.activityId); | ||
expect(res.status).toHaveBeenCalledWith(200); | ||
expect(res.json).toHaveBeenCalledWith({ valid: true }); | ||
}); | ||
|
||
it('shoulld return status 200 and valid false if activityId exists but isDeleted is true', async () => { | ||
const req = { | ||
params: { activityId: '87654321' }, | ||
currentUser: CURRENT_USER | ||
}; | ||
|
||
activityServiceSpy.mockResolvedValue(DELETED_ACTIVITY); | ||
|
||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
await activityController.validateActivityId(req as any, res as any, next); | ||
|
||
expect(activityServiceSpy).toHaveBeenCalledTimes(1); | ||
expect(activityServiceSpy).toHaveBeenCalledWith(req.params.activityId); | ||
expect(res.status).toHaveBeenCalledWith(200); | ||
expect(res.json).toHaveBeenCalledWith({ valid: false }); | ||
}); | ||
|
||
it('shoulld return status 200 and valid false if activityId does not exist', async () => { | ||
const req = { | ||
params: { activityId: 'FFFFFFFF' }, | ||
currentUser: CURRENT_USER | ||
}; | ||
|
||
activityServiceSpy.mockResolvedValue(null); | ||
|
||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
await activityController.validateActivityId(req as any, res as any, next); | ||
|
||
expect(activityServiceSpy).toHaveBeenCalledTimes(1); | ||
expect(activityServiceSpy).toHaveBeenCalledWith(req.params.activityId); | ||
expect(res.status).toHaveBeenCalledWith(200); | ||
expect(res.json).toHaveBeenCalledWith({ valid: false }); | ||
}); | ||
|
||
it('Should return 400 and error message if activityId does not have correct length', async () => { | ||
const req = { | ||
params: { activityId: '12345' }, | ||
currentUser: CURRENT_USER | ||
}; | ||
|
||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
await activityController.validateActivityId(req as any, res as any, next); | ||
|
||
expect(activityServiceSpy).toHaveBeenCalledTimes(0); | ||
expect(res.status).toHaveBeenCalledWith(400); | ||
expect(res.json).toHaveBeenCalledWith({ message: 'Invalid activity Id format' }); | ||
}); | ||
|
||
it('Should return 400 and error message if activityId is not hexidecimal value', async () => { | ||
const req = { | ||
params: { activityId: 'GGGGGGGG' }, | ||
currentUser: CURRENT_USER | ||
}; | ||
|
||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
await activityController.validateActivityId(req as any, res as any, next); | ||
|
||
expect(activityServiceSpy).toHaveBeenCalledTimes(0); | ||
expect(res.status).toHaveBeenCalledWith(400); | ||
expect(res.json).toHaveBeenCalledWith({ message: 'Invalid activity Id format' }); | ||
}); | ||
|
||
it('Should call next with error if getActivity fails', async () => { | ||
const req = { | ||
params: { activityId: '12345678' }, | ||
currentUser: CURRENT_USER | ||
}; | ||
|
||
const error = new Error(); | ||
|
||
activityServiceSpy.mockRejectedValue(error); | ||
|
||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
await activityController.validateActivityId(req as any, res as any, next); | ||
|
||
expect(activityServiceSpy).toHaveBeenCalledTimes(1); | ||
expect(activityServiceSpy).toHaveBeenCalledWith(req.params.activityId); | ||
expect(next).toHaveBeenCalledTimes(1); | ||
expect(next).toHaveBeenCalledWith(error); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
import { appAxios } from './interceptors'; | ||
|
||
export default { | ||
/** | ||
* @function checkActivityIdValidity | ||
* Checks if an activity ID is valid | ||
* @returns {Promise} An axios response | ||
*/ | ||
checkActivityIdValidity(activityId: string) { | ||
return appAxios().get(`activity/validate/${activityId}`); | ||
} | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters