Skip to content
87 changes: 83 additions & 4 deletions src/controllers/currentWarningsController.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,53 @@
const mongoose = require('mongoose');
const userProfile = require('../models/userProfile');
const helper = require('../utilities/permissions');
const { setOutdatedWarningsFlag } = require('../utilities/warningsCache');
const currentWarningsController = function (currentWarnings) {
const normalizeWarningTitle = (warningTitle /*: string */) => warningTitle.toLowerCase().trim();

const checkIfSpecialCharacter = (warning) => {
return !/^[a-zA-Z][a-zA-Z0-9,+-]*(?: [a-zA-Z0-9,+-]+)*$/.test(warning);
};

const addMissingOrderValues = async (warnings) => {
const updates = [];
warnings.forEach((warning, index) => {
if (warning.order === null || warning.order === undefined || warning.order === -1) {
updates.push({
updateOne: {
filter: { _id: warning._id },
update: { $set: { order: index } },
},
});
}
});

if (updates.length > 0) {
await currentWarnings.bulkWrite(updates);
}
// only updates warnings missing an order value
return warnings.map((warning, index) => ({
...warning,
order: warning.order ?? index,
}));
};

const getCurrentWarnings = async (req, res) => {
try {
const response = await currentWarnings.find({});
const response = await currentWarnings.find({}).sort({ order: 1 });

if (response.length === 0) {
return res.status(400).send({ message: 'No records', response: response });
}
return res.status(200).send({ currentWarningDescriptions: response });

const missingOrder = response.some(
(warning) => warning.order === null || warning.order === undefined || warning.order === -1,
);

const updatedWarnings = missingOrder ? await addMissingOrderValues(response) : response;
return res.status(200).send({ currentWarningDescriptions: updatedWarnings });
} catch (error) {
console.log('Entered error of fetch warnings');
res.status(401).send({ message: error.message || error });
}
};
Expand Down Expand Up @@ -55,6 +86,7 @@ const currentWarningsController = function (currentWarnings) {
isPermanent,
}).save();

setOutdatedWarningsFlag();
const updatedWarnings = await currentWarnings.find({});
return res.status(201).send({ newWarnings: updatedWarnings });
} catch (error) {
Expand All @@ -63,6 +95,14 @@ const currentWarningsController = function (currentWarnings) {
};

const editWarningDescription = async (req, res) => {
if (
!(await helper.hasPermission(req.body.requestor, 'addWarningTracker')) &&
!(await helper.hasPermission(req.body.requestor, 'deleteWarningTracker'))
) {
res.status(403).send('You are not authorized to edit a WarningTracker.');
return;
}

try {
const { editedWarning } = req.body;
const normalizedWarningTitle = normalizeWarningTitle(editedWarning.warningTitle);
Expand Down Expand Up @@ -91,11 +131,49 @@ const currentWarningsController = function (currentWarnings) {
warning.warningTitle = trimmedWarning;

await warning.save();
setOutdatedWarningsFlag();
res.status(201).send({ message: 'warning description was updated' });
} catch (error) {
res.status(401).send({ message: error.message || error });
}
};

const reorderWarningDescriptions = async (req, res) => {
if (
!(await helper.hasPermission(req.body.requestor, 'addWarningTracker')) &&
!(await helper.hasPermission(req.body.requestor, 'deleteWarningTracker'))
) {
res.status(403).send('You are not authorized to edit the order of the WarningTrackers.');
return;
}
try {
const reorderedWarningDescriptions = req.body.warningDescriptions;
const response = await currentWarnings.find({}).sort({ order: 1 });

reorderedWarningDescriptions.map((warning, index) => (warning.order = index));
await currentWarnings.bulkWrite(
response.map((warning, index) => ({
updateOne: {
filter: { _id: warning._id },
update: {
$set: {
order: reorderedWarningDescriptions.findIndex(
(warn) => warn.warningTitle === warning.warningTitle,
),
},
},
},
})),
);

setOutdatedWarningsFlag();
res.status(201).send({
reorderedWarningDescriptions: reorderedWarningDescriptions,
});
} catch (error) {
res.status(401).send({ message: error.message || error });
}
};
const updateWarningDescription = async (req, res) => {
if (
!(await helper.hasPermission(req.body.requestor, 'reactivateWarningTracker')) &&
Expand All @@ -116,7 +194,7 @@ const currentWarningsController = function (currentWarnings) {
[{ $set: { activeWarning: { $not: '$activeWarning' } } }],
{ new: true },
);

setOutdatedWarningsFlag();
res.status(201).send({ message: 'warning description was updated' });
} catch (error) {
res.status(401).send({ message: error.message || error });
Expand Down Expand Up @@ -148,7 +226,7 @@ const currentWarningsController = function (currentWarnings) {
},
},
);

setOutdatedWarningsFlag();
return res.status(200);
} catch (error) {
res.status(401).send({ message: error.message || error });
Expand All @@ -161,6 +239,7 @@ const currentWarningsController = function (currentWarnings) {
updateWarningDescription,
deleteWarningDescription,
editWarningDescription,
reorderWarningDescriptions,
};
};
module.exports = currentWarningsController;
129 changes: 129 additions & 0 deletions src/controllers/currentWarningsController.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
const helper = require('../utilities/permissions');
const UserProfile = require('../models/userProfile');
const currentWarnings = require('../models/currentWarnings');
const { mockReq, mockRes } = require('../test');
const currentWarningsController = require('./currentWarningsController');

const makeSut = () => {
const {
getCurrentWarnings,
postNewWarningDescription,
updateWarningDescription,
deleteWarningDescription,
editWarningDescription,
reorderWarningDescriptions,
} = currentWarningsController(currentWarnings);

return {
getCurrentWarnings,
postNewWarningDescription,
updateWarningDescription,
deleteWarningDescription,
editWarningDescription,
reorderWarningDescriptions,
};
};

// meant for failed response
const assertResMock = (statusCode, message, response) => {
expect(mockRes.status).toHaveBeenCalledWith(statusCode);
expect(mockRes.send).toHaveBeenCalledWith(message);
expect(response).toBeUndefined();
};

const mockHasPermission = (value) =>
jest.spyOn(helper, 'hasPermission').mockImplementation(() => Promise.resolve(value));

describe('current warnings controller module', () => {
afterEach(() => {
jest.clearAllMocks();
});

describe('get current warnings method', () => {
test('Ensure getCurrentWarnings returns error 401 if retrieving warnings fail', async () => {
const { getCurrentWarnings } = makeSut();
const errorMessage = 'Database Error';
jest.spyOn(currentWarnings, 'find').mockReturnValue({
sort: jest.fn().mockRejectedValue(new Error('Database Error')),
});
const res = await getCurrentWarnings(mockReq, mockRes);
assertResMock(401, { message: errorMessage }, res);
});

test('Ensure getCurrentWarnings returns error 400 if no warnings are found', async () => {
const { getCurrentWarnings } = makeSut();
jest.spyOn(currentWarnings, 'find').mockReturnValue({
sort: jest.fn().mockReturnValueOnce([]),
});
const res = await getCurrentWarnings(mockReq, mockRes);
assertResMock(400, { message: 'No records', response: [] }, res);
});

test('Ensure getCurrentWarnings returns warning list', async () => {
const { getCurrentWarnings } = makeSut();
const testWarnings = [{ order: 1 }, { order: 2 }];
jest.spyOn(currentWarnings, 'find').mockReturnValue({
sort: jest.fn().mockReturnValueOnce([{ order: 1 }, { order: 2 }]),
});
const res = await getCurrentWarnings(mockReq, mockRes);
assertResMock(200, { currentWarningDescriptions: testWarnings }, res);
});
});

describe('post new warning description method', () => {
test('Ensure postNewWarningDescription returns error 403 if user does not have required permissions', async () => {
const { postNewWarningDescription } = makeSut();
const hasPermissionSpy = mockHasPermission(false);
const res = await postNewWarningDescription(mockReq, mockRes);
expect(hasPermissionSpy).toHaveBeenCalledWith(mockReq.body.requestor, 'addWarningTracker');
assertResMock(403, 'You are not authorized to add a new WarningTracker.', res);
});

test('Ensure postNewWarningDescription returns error 400 if warning title provided has special character as their first letter', async () => {
mockReq.body.newWarning = '#new';
mockReq.body.isPermanent = true;
mockReq.body.activeWarning = true;
const { postNewWarningDescription } = makeSut();
const hasPermissionSpy = mockHasPermission(true);
const res = await postNewWarningDescription(mockReq, mockRes);
expect(hasPermissionSpy).toHaveBeenCalledWith(mockReq.body.requestor, 'addWarningTracker');
assertResMock(
400,
{ error: 'Warnings cannot have special characters as the first letter' },
res,
);
});
});

describe('update warning description method', () => {
test('Ensure updateWarningDescription returns error 403 if user does not have required permissions', async () => {
const { updateWarningDescription } = makeSut();
const hasPermissionSpy = mockHasPermission(false);
const res = await updateWarningDescription(mockReq, mockRes);
expect(hasPermissionSpy).toHaveBeenCalledWith(
mockReq.body.requestor,
'reactivateWarningTracker',
);
expect(hasPermissionSpy).toHaveBeenCalledWith(
mockReq.body.requestor,
'deactivateWarningTracker',
);
assertResMock(
403,
'You are not authorized to reactivate a WarningTracker or deactivate warning tracker.',
res,
);
});
});
describe('reorder new warning description method', () => {
test('Ensure reorderWarningDescriptions returns error 403 if user does not have required permissions', async () => {
const { reorderWarningDescriptions } = makeSut();
const hasPermissionSpy = mockHasPermission(false);
const res = await reorderWarningDescriptions(mockReq, mockRes);
expect(hasPermissionSpy).toHaveBeenCalledWith(mockReq.body.requestor, 'addWarningTracker');
expect(hasPermissionSpy).toHaveBeenCalledWith(mockReq.body.requestor, 'deleteWarningTracker');
console.log('mockReq: ', mockReq);
assertResMock(403, 'You are not authorized to edit the order of the WarningTrackers.', res);
});
});
});
Loading
Loading