Skip to content

Commit c862b3d

Browse files
committed
fix: resolve SonarQube issues in optAnalyticsController and optanalyticsRoutes
1 parent 1ad6500 commit c862b3d

2 files changed

Lines changed: 88 additions & 80 deletions

File tree

src/controllers/optAnalyticsController.js

Lines changed: 87 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -7,94 +7,103 @@ const isValidDate = (date) => {
77
return !Number.isNaN(parsedDate.getTime());
88
};
99

10-
module.exports = () => ({
11-
getOPTStatusBreakdown: async (req, res, next) => {
12-
try {
13-
const { startDate, endDate, role } = req.query;
14-
const query = {};
10+
const parseDateParam = (dateStr, fieldName, res) => {
11+
if (!isValidDate(dateStr)) {
12+
res.status(400).json({ message: `Invalid ${fieldName}. Use ISO format (YYYY-MM-DD).` });
13+
return null;
14+
}
15+
const parsed = new Date(dateStr);
16+
if (Number.isNaN(parsed.getTime())) {
17+
res.status(400).json({ message: `Invalid ${fieldName}. Use ISO format (YYYY-MM-DD).` });
18+
return null;
19+
}
20+
return parsed;
21+
};
1522

16-
let start;
17-
let end;
23+
const validateDateRange = (start, end, startDate, endDate, res) => {
24+
if (start && end && start > end) {
25+
res.status(400).json({ message: 'startDate cannot be greater than endDate.' });
26+
return false;
27+
}
28+
if (startDate && endDate && start.toDateString() === end.toDateString()) {
29+
res.status(400).json({ message: 'startDate and endDate cannot be the same.' });
30+
return false;
31+
}
32+
return true;
33+
};
1834

19-
if (startDate) {
20-
if (!isValidDate(startDate)) {
21-
return res.status(400).json({
22-
message: 'Invalid startDate. Use ISO format (YYYY-MM-DD).',
23-
});
24-
}
25-
start = new Date(startDate);
26-
if (Number.isNaN(start.getTime())) {
27-
return res.status(400).json({
28-
message: 'Invalid startDate. Use ISO format (YYYY-MM-DD).',
29-
});
30-
}
31-
}
35+
const sanitizeRole = (role) => {
36+
if (!role) return null;
37+
const str = String(role);
38+
return str.replace(/[^a-zA-Z0-9 _-]/g, '');
39+
};
3240

33-
if (endDate) {
34-
if (!isValidDate(endDate)) {
35-
return res.status(400).json({
36-
message: 'Invalid endDate. Use ISO format (YYYY-MM-DD).',
37-
});
38-
}
39-
end = new Date(endDate);
40-
if (Number.isNaN(end.getTime())) {
41-
return res.status(400).json({
42-
message: 'Invalid endDate. Use ISO format (YYYY-MM-DD).',
43-
});
44-
}
45-
}
41+
const buildQuery = (start, end, role) => {
42+
const query = {};
43+
if (start || end) {
44+
query.applicationDate = {};
45+
if (start) query.applicationDate.$gte = start;
46+
if (end) query.applicationDate.$lte = end;
47+
}
48+
const safeRole = sanitizeRole(role);
49+
if (safeRole) {
50+
query.role = safeRole;
51+
}
52+
return query;
53+
};
4654

47-
if (start && end && start > end) {
48-
return res.status(400).json({
49-
message: 'startDate cannot be greater than endDate.',
50-
});
51-
}
55+
const computeBreakdown = (result) => {
56+
const totalCandidates = result.length;
57+
const breakDownMap = {};
58+
result.forEach(({ optStatus }) => {
59+
breakDownMap[optStatus] = (breakDownMap[optStatus] || 0) + 1;
60+
});
61+
const breakDown = Object.entries(breakDownMap).map(([optStatus, count]) => ({
62+
optStatus,
63+
count,
64+
percentage: Number(((count / totalCandidates) * 100).toFixed(2)),
65+
}));
66+
return { totalCandidates, breakDown };
67+
};
5268

53-
if (startDate && endDate && start.toDateString() === end.toDateString()) {
54-
return res.status(400).json({
55-
message: 'startDate and endDate cannot be the same.',
56-
});
57-
}
69+
module.exports = function optAnalyticsController() {
70+
return {
71+
getOPTStatusBreakdown: async function getOPTStatusBreakdown(req, res, next) {
72+
try {
73+
const { startDate, endDate, role } = req.query;
5874

59-
if (start || end) {
60-
query.applicationDate = {};
61-
if (start) query.applicationDate.$gte = start;
62-
if (end) query.applicationDate.$lte = end;
63-
}
75+
let start;
76+
let end;
6477

65-
if (role) {
66-
query.role = role;
67-
}
78+
if (startDate) {
79+
start = parseDateParam(startDate, 'startDate', res);
80+
if (start === null) return null;
81+
}
6882

69-
const result = await CandidateOPTStatus.find(query);
83+
if (endDate) {
84+
end = parseDateParam(endDate, 'endDate', res);
85+
if (end === null) return null;
86+
}
7087

71-
if (!result.length) {
72-
return res.json({
73-
totalCandidates: 0,
74-
breakDown: [],
75-
message: 'No records found for the given filters.',
76-
});
77-
}
88+
if (!validateDateRange(start, end, startDate, endDate, res)) return null;
7889

79-
const totalCandidates = result.length;
80-
const breakDownMap = {};
90+
const query = buildQuery(start, end, role);
8191

82-
result.forEach(({ optStatus }) => {
83-
breakDownMap[optStatus] = (breakDownMap[optStatus] || 0) + 1;
84-
});
92+
const result = await CandidateOPTStatus.find(query);
8593

86-
const breakDown = Object.entries(breakDownMap).map(([optStatus, count]) => ({
87-
optStatus,
88-
count,
89-
percentage: Number(((count / totalCandidates) * 100).toFixed(2)),
90-
}));
94+
if (!result.length) {
95+
return res.json({
96+
totalCandidates: 0,
97+
breakDown: [],
98+
message: 'No records found for the given filters.',
99+
});
100+
}
91101

92-
return res.json({
93-
totalCandidates,
94-
breakDown,
95-
});
96-
} catch (err) {
97-
next(err);
98-
}
99-
},
100-
});
102+
return res.json(computeBreakdown(result));
103+
} catch (err) {
104+
next(err);
105+
}
106+
return null;
107+
},
108+
};
109+
};

src/routes/optanalyticsRoutes.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
const express = require('express');
22

3-
module.exports = function () {
3+
module.exports = function optAnalyticsRoutes() {
44
const router = express.Router();
55
const optAnalyticsController = require('../controllers/optAnalyticsController')();
66
router.get('/opt-status', optAnalyticsController.getOPTStatusBreakdown);
7-
87
return router;
98
};

0 commit comments

Comments
 (0)