-
Notifications
You must be signed in to change notification settings - Fork 15
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
058 Dec 19 Calculate Assessment and Compliance Tracker progress #367
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -4,8 +4,16 @@ import { | |||||||||||||||||||||||||||||
createNewUserQuery, | ||||||||||||||||||||||||||||||
deleteUserByIdQuery, | ||||||||||||||||||||||||||||||
getAllUsersQuery, | ||||||||||||||||||||||||||||||
getAssessmentsForProject, | ||||||||||||||||||||||||||||||
getControlCategoriesForProject, | ||||||||||||||||||||||||||||||
getControlForControlCategory, | ||||||||||||||||||||||||||||||
getQuestionsForSubTopic, | ||||||||||||||||||||||||||||||
getSubControlForControl, | ||||||||||||||||||||||||||||||
getSubTopicsForTopic, | ||||||||||||||||||||||||||||||
getTopicsForAssessment, | ||||||||||||||||||||||||||||||
getUserByEmailQuery, | ||||||||||||||||||||||||||||||
getUserByIdQuery, | ||||||||||||||||||||||||||||||
getUserProjects, | ||||||||||||||||||||||||||||||
resetPasswordQuery, | ||||||||||||||||||||||||||||||
updateUserByIdQuery, | ||||||||||||||||||||||||||||||
} from "../utils/user.utils"; | ||||||||||||||||||||||||||||||
|
@@ -331,6 +339,86 @@ async function checkUserExists( | |||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
async function calculateProgress( | ||||||||||||||||||||||||||||||
req: Request, | ||||||||||||||||||||||||||||||
res: Response | ||||||||||||||||||||||||||||||
): Promise<Response> { | ||||||||||||||||||||||||||||||
try { | ||||||||||||||||||||||||||||||
const id = parseInt(req.params.id) | ||||||||||||||||||||||||||||||
const userProjects = await getUserProjects(id) | ||||||||||||||||||||||||||||||
Comment on lines
+347
to
+348
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Add input validation for user ID The function should validate that the user ID is positive and exists before proceeding with calculations. const id = parseInt(req.params.id)
+if (isNaN(id) || id <= 0) {
+ return res.status(400).json({ message: "Invalid user ID" });
+}
const userProjects = await getUserProjects(id)
+if (!userProjects?.length) {
+ return res.status(404).json({ message: "No projects found for user" });
+} 📝 Committable suggestion
Suggested change
|
||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
let assessmentsMetadata = [] | ||||||||||||||||||||||||||||||
let allTotalAssessments = 0 | ||||||||||||||||||||||||||||||
let allDoneAssessments = 0 | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
let controlsMetadata = [] | ||||||||||||||||||||||||||||||
let allTotalSubControls = 0 | ||||||||||||||||||||||||||||||
let allDoneSubControls = 0 | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
for (const userProject of userProjects) { | ||||||||||||||||||||||||||||||
let totalSubControls = 0 | ||||||||||||||||||||||||||||||
let doneSubControls = 0 | ||||||||||||||||||||||||||||||
const controlcategories = await getControlCategoriesForProject(userProject.id) | ||||||||||||||||||||||||||||||
for (const controlcategory of controlcategories) { | ||||||||||||||||||||||||||||||
const controls = await getControlForControlCategory(controlcategory.id) | ||||||||||||||||||||||||||||||
for (const control of controls) { | ||||||||||||||||||||||||||||||
const subControls = await getSubControlForControl(control.id) | ||||||||||||||||||||||||||||||
for (const subControl of subControls) { | ||||||||||||||||||||||||||||||
totalSubControls++; | ||||||||||||||||||||||||||||||
if (subControl.status === "Done") { | ||||||||||||||||||||||||||||||
doneSubControls++; | ||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||
allTotalSubControls += totalSubControls | ||||||||||||||||||||||||||||||
allDoneSubControls += doneSubControls | ||||||||||||||||||||||||||||||
controlsMetadata.push({ projectId: userProject.id, totalSubControls, doneSubControls }) | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
let totalAssessments = 0 | ||||||||||||||||||||||||||||||
let doneAssessments = 0 | ||||||||||||||||||||||||||||||
const assessments = await getAssessmentsForProject(userProject.id) | ||||||||||||||||||||||||||||||
for (const assessment of assessments) { | ||||||||||||||||||||||||||||||
const topics = await getTopicsForAssessment(assessment.id) | ||||||||||||||||||||||||||||||
for (const topic of topics) { | ||||||||||||||||||||||||||||||
const subTopics = await getSubTopicsForTopic(topic.id) | ||||||||||||||||||||||||||||||
for (const subTopic of subTopics) { | ||||||||||||||||||||||||||||||
const questions = await getQuestionsForSubTopic(subTopic.id) | ||||||||||||||||||||||||||||||
for (const question of questions) { | ||||||||||||||||||||||||||||||
totalAssessments++; | ||||||||||||||||||||||||||||||
if (question.answer) { | ||||||||||||||||||||||||||||||
doneAssessments++ | ||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||
allTotalAssessments += totalAssessments | ||||||||||||||||||||||||||||||
allDoneAssessments += doneAssessments | ||||||||||||||||||||||||||||||
assessmentsMetadata.push({ projectId: userProject.id, totalAssessments, doneAssessments }) | ||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
const response = { | ||||||||||||||||||||||||||||||
controls: { | ||||||||||||||||||||||||||||||
projects: controlsMetadata, | ||||||||||||||||||||||||||||||
totalSubControls: allTotalSubControls, | ||||||||||||||||||||||||||||||
doneSubControls: allDoneSubControls, | ||||||||||||||||||||||||||||||
percentageComplete: Number(((allDoneSubControls / allTotalSubControls) * 100).toFixed(2)) | ||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add safety check for division by zero The percentage calculations could throw errors if there are no controls or assessments. -percentageComplete: Number(((allDoneSubControls / allTotalSubControls) * 100).toFixed(2))
+percentageComplete: allTotalSubControls > 0 ? Number(((allDoneSubControls / allTotalSubControls) * 100).toFixed(2)) : 0
-percentageComplete: Number(((allDoneAssessments / allTotalAssessments) * 100).toFixed(2))
+percentageComplete: allTotalAssessments > 0 ? Number(((allDoneAssessments / allTotalAssessments) * 100).toFixed(2)) : 0 Also applies to: 412-412 |
||||||||||||||||||||||||||||||
}, | ||||||||||||||||||||||||||||||
assessments: { | ||||||||||||||||||||||||||||||
projects: assessmentsMetadata, | ||||||||||||||||||||||||||||||
totalAssessments: allTotalAssessments, | ||||||||||||||||||||||||||||||
doneAssessments: allDoneAssessments, | ||||||||||||||||||||||||||||||
percentageComplete: Number(((allDoneAssessments / allTotalAssessments) * 100).toFixed(2)) | ||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||
return res.status(200).json(response) | ||||||||||||||||||||||||||||||
} catch (error) { | ||||||||||||||||||||||||||||||
console.log(error); | ||||||||||||||||||||||||||||||
return res.status(500).json({ message: "Internal server error" }); | ||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||
Comment on lines
+416
to
+419
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Improve error handling The catch block should distinguish between different types of errors and return appropriate status codes. } catch (error) {
console.log(error);
- return res.status(500).json({ message: "Internal server error" });
+ if (error instanceof TypeError) {
+ return res.status(400).json({ message: "Invalid data structure" });
+ }
+ return res.status(500).json({
+ message: "Internal server error",
+ error: error instanceof Error ? error.message : "Unknown error"
+ });
} 📝 Committable suggestion
Suggested change
|
||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
export { | ||||||||||||||||||||||||||||||
getAllUsers, | ||||||||||||||||||||||||||||||
getUserByEmail, | ||||||||||||||||||||||||||||||
|
@@ -341,4 +429,5 @@ export { | |||||||||||||||||||||||||||||
updateUserById, | ||||||||||||||||||||||||||||||
deleteUserById, | ||||||||||||||||||||||||||||||
checkUserExists, | ||||||||||||||||||||||||||||||
calculateProgress | ||||||||||||||||||||||||||||||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -19,6 +19,7 @@ import { | |
loginUser, | ||
resetPassword, | ||
updateUserById, | ||
calculateProgress | ||
} from "../controllers/user.ctrl"; | ||
import authenticateJWT from "../middleware/auth.middleware"; | ||
|
||
|
@@ -148,4 +149,6 @@ router.delete("/:id", authenticateJWT, deleteUserById); | |
*/ | ||
router.get("/check/exists", checkUserExists); | ||
|
||
router.get("/:id/calculate-progress", calculateProgress) | ||
|
||
Comment on lines
+152
to
+153
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 💡 Codebase verification Authentication middleware is missing for sensitive user progress data Looking at the route patterns in user.route.ts, it's clear that several sensitive endpoints are protected with
router.get("/:id/calculate-progress", authenticateJWT, calculateProgress) 🔗 Analysis chainConsider authentication for progress route. 🏁 Scripts executedThe following scripts were executed for the analysis: Script: #!/bin/bash
# Searching the route usage and verifying if we have an associated test that expects 401 or 403 for unauthenticated requests
rg -A 5 '"\/:id\/calculate-progress"'
Length of output: 134 Script: #!/bin/bash
# Search for the route pattern with proper escaping
rg -A 5 "/:id/calculate-progress"
# Check if calculateProgress function has any authentication checks
ast-grep --pattern 'function calculateProgress($_) { $$$ }'
# Look for other routes in the same file to compare authentication patterns
rg "router\.(get|post|put|delete)" "Servers/routes/user.route.ts"
Length of output: 781 |
||
export default router; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Codebase verification
Missing test coverage for critical utility functions
The utility functions imported from
Servers/utils/user.utils.ts
lack test coverage. These functions are used in critical progress calculation logic but:Action items:
Servers/utils/user.utils.ts
, particularly:🔗 Analysis chain
Ensure all newly added utility functions are tested and consistently reused.
You have introduced several function imports at lines 7-13 and 16 (e.g. getControlCategoriesForProject, getUserProjects, etc.). Verify these are tested thoroughly, as they appear critical to the progress calculation in the new controller method.
Also applies to: 16-16
Let me gather more information about the test coverage for these utility functions.
Let me check for test files specifically in the Servers directory since we found the utility functions in Servers/utils/user.utils.ts.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
Length of output: 310
Script:
Length of output: 1274
Script:
Length of output: 151