diff --git a/backend/db/Application.js b/backend/db/Application.js index e2215e7..9a7fdeb 100644 --- a/backend/db/Application.js +++ b/backend/db/Application.js @@ -1,4 +1,5 @@ const mongoose = require("mongoose"); +const { diag } = require('@opentelemetry/api'); let schema = new mongoose.Schema( { @@ -37,7 +38,9 @@ let schema = new mongoose.Schema( validate: [ { validator: function (value) { - return this.dateOfApplication <= value; + const isValid = this.dateOfApplication <= value; + diag.debug('Validating dateOfJoining:', { dateOfApplication: this.dateOfApplication, dateOfJoining: value, isValid }); + return isValid; }, msg: "dateOfJoining should be greater than dateOfApplication", }, @@ -47,7 +50,10 @@ let schema = new mongoose.Schema( type: String, validate: { validator: function (v) { - return v.split(" ").filter((ele) => ele != "").length <= 250; + const wordCount = v.split(" ").filter((ele) => ele != "").length; + const isValid = wordCount <= 250; + diag.debug('Validating SOP word count:', { sop: v, wordCount, isValid }); + return isValid; }, msg: "Statement of purpose should not be greater than 250 words", }, diff --git a/backend/db/Job.js b/backend/db/Job.js index fce4b15..d9bb6ad 100644 --- a/backend/db/Job.js +++ b/backend/db/Job.js @@ -1,4 +1,5 @@ const mongoose = require("mongoose"); +const { diag } = require('@opentelemetry/api'); let schema = new mongoose.Schema( { @@ -19,6 +20,7 @@ let schema = new mongoose.Schema( }, { validator: function (value) { + diag.info(`Validating maxApplicants: ${value}`); return value > 0; }, msg: "maxApplicants should greater than 0", @@ -34,6 +36,7 @@ let schema = new mongoose.Schema( }, { validator: function (value) { + diag.info(`Validating maxPositions: ${value}`); return value > 0; }, msg: "maxPositions should greater than 0", @@ -50,6 +53,7 @@ let schema = new mongoose.Schema( }, { validator: function (value) { + diag.info(`Validating activeApplications: ${value}`); return value >= 0; }, msg: "activeApplications should greater than equal to 0", @@ -66,6 +70,7 @@ let schema = new mongoose.Schema( }, { validator: function (value) { + diag.info(`Validating acceptedCandidates: ${value}`); return value >= 0; }, msg: "acceptedCandidates should greater than equal to 0", @@ -81,6 +86,7 @@ let schema = new mongoose.Schema( validate: [ { validator: function (value) { + diag.info(`Validating deadline: ${value} against dateOfPosting: ${this.dateOfPosting}`); return this.dateOfPosting < value; }, msg: "deadline should be greater than dateOfPosting", @@ -111,6 +117,7 @@ let schema = new mongoose.Schema( }, { validator: function (value) { + diag.info(`Validating salary: ${value}`); return value >= 0; }, msg: "Salary should be positive", @@ -123,6 +130,7 @@ let schema = new mongoose.Schema( default: -1.0, validate: { validator: function (v) { + diag.info(`Validating rating: ${v}`); return v >= -1.0 && v <= 5.0; }, msg: "Invalid rating", diff --git a/backend/db/JobApplicant.js b/backend/db/JobApplicant.js index f84d5c7..efd4d9f 100644 --- a/backend/db/JobApplicant.js +++ b/backend/db/JobApplicant.js @@ -1,4 +1,8 @@ const mongoose = require("mongoose"); +const { diag, DiagConsoleLogger, DiagLogLevel } = require('@opentelemetry/api'); + +// Initialize the logger +diag.setLogger(new DiagConsoleLogger(), DiagLogLevel.INFO); let schema = new mongoose.Schema( { @@ -30,7 +34,9 @@ let schema = new mongoose.Schema( { validator: Number.isInteger, msg: "Year should be an integer" }, { validator: function (value) { - return this.startYear <= value; + const isValid = this.startYear <= value; + diag.info(`Validating endYear: ${value}, startYear: ${this.startYear}, isValid: ${isValid}`); + return isValid; }, msg: "End year should be greater than or equal to Start year", }, @@ -45,7 +51,9 @@ let schema = new mongoose.Schema( default: -1.0, validate: { validator: function (v) { - return v >= -1.0 && v <= 5.0; + const isValid = v >= -1.0 && v <= 5.0; + diag.info(`Validating rating: ${v}, isValid: ${isValid}`); + return isValid; }, msg: "Invalid rating", }, @@ -60,4 +68,6 @@ let schema = new mongoose.Schema( { collation: { locale: "en" } } ); +diag.info('Schema for JobApplicantInfo created'); + module.exports = mongoose.model("JobApplicantInfo", schema); diff --git a/backend/db/Rating.js b/backend/db/Rating.js index 523aa21..0e1a6e9 100644 --- a/backend/db/Rating.js +++ b/backend/db/Rating.js @@ -1,4 +1,5 @@ const mongoose = require("mongoose"); +const { diag } = require('@opentelemetry/api'); let schema = new mongoose.Schema( { @@ -21,6 +22,7 @@ let schema = new mongoose.Schema( default: -1.0, validate: { validator: function (v) { + diag.debug(`Validating rating: ${v}`); return v >= -1.0 && v <= 5.0; }, msg: "Invalid rating", @@ -30,6 +32,12 @@ let schema = new mongoose.Schema( { collation: { locale: "en" } } ); +diag.debug('Schema created with collation: en'); + schema.index({ category: 1, receiverId: 1, senderId: 1 }, { unique: true }); +diag.debug('Index created on schema with fields: category, receiverId, senderId'); + module.exports = mongoose.model("ratings", schema); + +diag.debug('Mongoose model "ratings" exported'); diff --git a/backend/db/Recruiter.js b/backend/db/Recruiter.js index 0c412b7..0c1f601 100644 --- a/backend/db/Recruiter.js +++ b/backend/db/Recruiter.js @@ -1,4 +1,5 @@ const mongoose = require("mongoose"); +const { diag } = require('@opentelemetry/api'); let schema = new mongoose.Schema( { @@ -14,7 +15,9 @@ let schema = new mongoose.Schema( type: String, validate: { validator: function (v) { - return v !== "" ? /\+\d{1,3}\d{10}/.test(v) : true; + const isValid = v !== "" ? /\+\d{1,3}\d{10}/.test(v) : true; + diag.debug(`Validating contact number: ${v}, isValid: ${isValid}`); + return isValid; }, msg: "Phone number is invalid!", }, @@ -26,4 +29,6 @@ let schema = new mongoose.Schema( { collation: { locale: "en" } } ); +diag.debug('Schema created with collation locale: en'); + module.exports = mongoose.model("RecruiterInfo", schema); diff --git a/backend/db/User.js b/backend/db/User.js index c982d3c..ea7f431 100644 --- a/backend/db/User.js +++ b/backend/db/User.js @@ -1,5 +1,6 @@ const mongoose = require("mongoose"); const bcrypt = require("bcrypt"); +const { diag } = require('@opentelemetry/api'); require("mongoose-type-email"); let schema = new mongoose.Schema( @@ -26,16 +27,20 @@ let schema = new mongoose.Schema( // Password hashing schema.pre("save", function (next) { let user = this; + diag.debug('Pre-save hook triggered for user:', user.email); // if the data is not modified if (!user.isModified("password")) { + diag.debug('Password not modified for user:', user.email); return next(); } bcrypt.hash(user.password, 10, (err, hash) => { if (err) { + diag.error('Error hashing password for user:', user.email, err); return next(err); } + diag.debug('Password hashed successfully for user:', user.email); user.password = hash; next(); }); @@ -44,15 +49,19 @@ schema.pre("save", function (next) { // Password verification upon login schema.methods.login = function (password) { let user = this; + diag.debug('Login method called for user:', user.email); return new Promise((resolve, reject) => { bcrypt.compare(password, user.password, (err, result) => { if (err) { + diag.error('Error comparing password for user:', user.email, err); reject(err); } if (result) { + diag.debug('Password verification successful for user:', user.email); resolve(); } else { + diag.debug('Password verification failed for user:', user.email); reject(); } }); diff --git a/backend/lib/authKeys.js b/backend/lib/authKeys.js index 60d2485..3825baa 100644 --- a/backend/lib/authKeys.js +++ b/backend/lib/authKeys.js @@ -1,3 +1,17 @@ +To add logging to the given code using the OpenTelemetry library, we need to first set up OpenTelemetry and then add appropriate log messages. Here's how you can instrument the code: + +```javascript +const { diag, DiagConsoleLogger, DiagLogLevel } = require('@opentelemetry/api'); + +// Set up the OpenTelemetry logger +diag.setLogger(new DiagConsoleLogger(), DiagLogLevel.INFO); + module.exports = { jwtSecretKey: "jwt_secret", }; + +// Log the jwtSecretKey value +diag.info(`JWT Secret Key is set to: ${module.exports.jwtSecretKey}`); +``` + +This code sets up a basic logger using OpenTelemetry and logs the value of `jwtSecretKey`. diff --git a/backend/lib/jwtAuth.js b/backend/lib/jwtAuth.js index 9eb5cb0..314617e 100644 --- a/backend/lib/jwtAuth.js +++ b/backend/lib/jwtAuth.js @@ -1,14 +1,18 @@ const passport = require("passport"); +const { diag } = require('@opentelemetry/api'); const jwtAuth = (req, res, next) => { passport.authenticate("jwt", { session: false }, function (err, user, info) { if (err) { + diag.error('Authentication error:', err); return next(err); } if (!user) { + diag.warn('Authentication failed, no user found:', info); res.status(401).json(info); return; } + diag.info('User authenticated successfully:', user); req.user = user; next(); })(req, res, next); diff --git a/backend/lib/passportConfig.js b/backend/lib/passportConfig.js index 5c3cb98..02000c7 100644 --- a/backend/lib/passportConfig.js +++ b/backend/lib/passportConfig.js @@ -1,4 +1,5 @@ const passport = require("passport"); +const { diag } = require('@opentelemetry/api'); const Strategy = require("passport-local").Strategy; const passportJWT = require("passport-jwt"); @@ -15,6 +16,7 @@ const filterJson = (obj, unwantedKeys) => { filteredObj[key] = obj[key]; } }); + diag.debug('Filtered object:', filteredObj); return filteredObj; }; @@ -25,12 +27,14 @@ passport.use( passReqToCallback: true, }, (req, email, password, done, res) => { - // console.log(email, password); + diag.debug('Authenticating user with email:', email); User.findOne({ email: email }, (err, user) => { if (err) { + diag.error('Error finding user:', err); return done(err); } if (!user) { + diag.warn('User not found for email:', email); return done(null, false, { message: "User does not exist", }); @@ -39,17 +43,12 @@ passport.use( user .login(password) .then(() => { - // let userSecure = {}; - // const unwantedKeys = ["password", "__v"]; - // Object.keys(user["_doc"]).forEach((key) => { - // if (unwantedKeys.indexOf(key) === -1) { - // userSecure[key] = user[key]; - // } - // }); + diag.debug('User logged in successfully:', user._id); user["_doc"] = filterJson(user["_doc"], ["password", "__v"]); return done(null, user); }) .catch((err) => { + diag.error('Login failed for user:', user._id, 'Error:', err); return done(err, false, { message: "Password is incorrect.", }); @@ -66,10 +65,12 @@ passport.use( secretOrKey: authKeys.jwtSecretKey, }, (jwt_payload, done) => { + diag.debug('Authenticating JWT payload:', jwt_payload); User.findById(jwt_payload._id) .then((user) => { - console.log(Object.keys(jwt_payload)); + diag.debug('User found for JWT payload:', jwt_payload._id); if (!user) { + diag.warn('User not found for JWT payload:', jwt_payload._id); return done(null, false, { message: "JWT Token does not exist", }); @@ -78,6 +79,7 @@ passport.use( return done(null, user); }) .catch((err) => { + diag.error('Error processing JWT payload:', jwt_payload, 'Error:', err); return done(err, false, { message: "Incorrect Token", }); diff --git a/backend/package.json b/backend/package.json index d84eaa6..f9ca141 100644 --- a/backend/package.json +++ b/backend/package.json @@ -25,6 +25,11 @@ "passport": "^0.4.1", "passport-jwt": "^4.0.0", "passport-local": "^1.0.0", - "uuid": "^8.3.2" + "uuid": "^8.3.2", + "@opentelemetry/api": "^1.0.0", + "@opentelemetry/sdk-node": "^0.27.0", + "@opentelemetry/auto-instrumentations-node": "^0.27.0", + "@opentelemetry/exporter-trace-otlp-http": "^0.27.0", + "@opentelemetry/exporter-metrics-otlp-http": "^0.27.0" } } diff --git a/backend/routes/apiRoutes.js b/backend/routes/apiRoutes.js index 51618ff..1f9fea0 100644 --- a/backend/routes/apiRoutes.js +++ b/backend/routes/apiRoutes.js @@ -1,6 +1,7 @@ const express = require("express"); const mongoose = require("mongoose"); const jwtAuth = require("../lib/jwtAuth"); +const { diag } = require('@opentelemetry/api'); const User = require("../db/User"); const JobApplicant = require("../db/JobApplicant"); @@ -14,6 +15,7 @@ const router = express.Router(); // To add new job router.post("/jobs", jwtAuth, (req, res) => { const user = req.user; + diag.debug('User type:', user.type); if (user.type != "recruiter") { res.status(401).json({ @@ -23,6 +25,7 @@ router.post("/jobs", jwtAuth, (req, res) => { } const data = req.body; + diag.debug('Job data:', data); let job = new Job({ userId: user._id, @@ -41,9 +44,11 @@ router.post("/jobs", jwtAuth, (req, res) => { job .save() .then(() => { + diag.debug('Job added successfully'); res.json({ message: "Job added successfully to the database" }); }) .catch((err) => { + diag.error('Error adding job:', err); res.status(400).json(err); }); }); @@ -51,20 +56,17 @@ router.post("/jobs", jwtAuth, (req, res) => { // to get all the jobs [pagination] [for recruiter personal and for everyone] router.get("/jobs", jwtAuth, (req, res) => { let user = req.user; + diag.debug('User type:', user.type); let findParams = {}; let sortParams = {}; - // const page = parseInt(req.query.page) ? parseInt(req.query.page) : 1; - // const limit = parseInt(req.query.limit) ? parseInt(req.query.limit) : 10; - // const skip = page - 1 >= 0 ? (page - 1) * limit : 0; - - // to list down jobs posted by a particular recruiter if (user.type === "recruiter" && req.query.myjobs) { findParams = { ...findParams, userId: user._id, }; + diag.debug('Find params for recruiter:', findParams); } if (req.query.q) { @@ -74,6 +76,7 @@ router.get("/jobs", jwtAuth, (req, res) => { $regex: new RegExp(req.query.q, "i"), }, }; + diag.debug('Find params with query:', findParams); } if (req.query.jobType) { @@ -83,7 +86,7 @@ router.get("/jobs", jwtAuth, (req, res) => { } else { jobTypes = [req.query.jobType]; } - console.log(jobTypes); + diag.debug('Job types:', jobTypes); findParams = { ...findParams, jobType: { @@ -108,6 +111,7 @@ router.get("/jobs", jwtAuth, (req, res) => { }, ], }; + diag.debug('Find params with salary range:', findParams); } else if (req.query.salaryMin) { findParams = { ...findParams, @@ -115,6 +119,7 @@ router.get("/jobs", jwtAuth, (req, res) => { $gte: parseInt(req.query.salaryMin), }, }; + diag.debug('Find params with minimum salary:', findParams); } else if (req.query.salaryMax) { findParams = { ...findParams, @@ -122,6 +127,7 @@ router.get("/jobs", jwtAuth, (req, res) => { $lte: parseInt(req.query.salaryMax), }, }; + diag.debug('Find params with maximum salary:', findParams); } if (req.query.duration) { @@ -131,6 +137,7 @@ router.get("/jobs", jwtAuth, (req, res) => { $lt: parseInt(req.query.duration), }, }; + diag.debug('Find params with duration:', findParams); } if (req.query.asc) { @@ -147,6 +154,7 @@ router.get("/jobs", jwtAuth, (req, res) => { [req.query.asc]: 1, }; } + diag.debug('Sort params ascending:', sortParams); } if (req.query.desc) { @@ -163,14 +171,11 @@ router.get("/jobs", jwtAuth, (req, res) => { [req.query.desc]: -1, }; } + diag.debug('Sort params descending:', sortParams); } - console.log(findParams); - console.log(sortParams); - - // Job.find(findParams).collation({ locale: "en" }).sort(sortParams); - // .skip(skip) - // .limit(limit) + diag.debug('Final find params:', findParams); + diag.debug('Final sort params:', sortParams); let arr = [ { @@ -203,19 +208,22 @@ router.get("/jobs", jwtAuth, (req, res) => { ]; } - console.log(arr); + diag.debug('Aggregation pipeline:', arr); Job.aggregate(arr) .then((posts) => { if (posts == null) { + diag.debug('No jobs found'); res.status(404).json({ message: "No job found", }); return; } + diag.debug('Jobs found:', posts.length); res.json(posts); }) .catch((err) => { + diag.error('Error fetching jobs:', err); res.status(400).json(err); }); }); @@ -225,14 +233,17 @@ router.get("/jobs/:id", jwtAuth, (req, res) => { Job.findOne({ _id: req.params.id }) .then((job) => { if (job == null) { + diag.debug('Job not found with id:', req.params.id); res.status(400).json({ message: "Job does not exist", }); return; } + diag.debug('Job found:', job); res.json(job); }) .catch((err) => { + diag.error('Error fetching job:', err); res.status(400).json(err); }); }); @@ -240,6 +251,8 @@ router.get("/jobs/:id", jwtAuth, (req, res) => { // to update info of a particular job router.put("/jobs/:id", jwtAuth, (req, res) => { const user = req.user; + diag.debug('User type:', user.type); + if (user.type != "recruiter") { res.status(401).json({ message: "You don't have permissions to change the job details", @@ -252,12 +265,15 @@ router.put("/jobs/:id", jwtAuth, (req, res) => { }) .then((job) => { if (job == null) { + diag.debug('Job not found for update with id:', req.params.id); res.status(404).json({ message: "Job does not exist", }); return; } const data = req.body; + diag.debug('Update data:', data); + if (data.maxApplicants) { job.maxApplicants = data.maxApplicants; } @@ -270,15 +286,18 @@ router.put("/jobs/:id", jwtAuth, (req, res) => { job .save() .then(() => { + diag.debug('Job details updated successfully'); res.json({ message: "Job details updated successfully", }); }) .catch((err) => { + diag.error('Error updating job details:', err); res.status(400).json(err); }); }) .catch((err) => { + diag.error('Error finding job for update:', err); res.status(400).json(err); }); }); @@ -286,6 +305,8 @@ router.put("/jobs/:id", jwtAuth, (req, res) => { // to delete a job router.delete("/jobs/:id", jwtAuth, (req, res) => { const user = req.user; + diag.debug('User type:', user.type); + if (user.type != "recruiter") { res.status(401).json({ message: "You don't have permissions to delete the job", @@ -298,16 +319,19 @@ router.delete("/jobs/:id", jwtAuth, (req, res) => { }) .then((job) => { if (job === null) { + diag.debug('Job not found for deletion with id:', req.params.id); res.status(401).json({ message: "You don't have permissions to delete the job", }); return; } + diag.debug('Job deleted successfully'); res.json({ message: "Job deleted successfully", }); }) .catch((err) => { + diag.error('Error deleting job:', err); res.status(400).json(err); }); }); @@ -315,32 +339,40 @@ router.delete("/jobs/:id", jwtAuth, (req, res) => { // get user's personal details router.get("/user", jwtAuth, (req, res) => { const user = req.user; + diag.debug('User type:', user.type); + if (user.type === "recruiter") { Recruiter.findOne({ userId: user._id }) .then((recruiter) => { if (recruiter == null) { + diag.debug('Recruiter not found for user id:', user._id); res.status(404).json({ message: "User does not exist", }); return; } + diag.debug('Recruiter found:', recruiter); res.json(recruiter); }) .catch((err) => { + diag.error('Error fetching recruiter details:', err); res.status(400).json(err); }); } else { JobApplicant.findOne({ userId: user._id }) .then((jobApplicant) => { if (jobApplicant == null) { + diag.debug('Job applicant not found for user id:', user._id); res.status(404).json({ message: "User does not exist", }); return; } + diag.debug('Job applicant found:', jobApplicant); res.json(jobApplicant); }) .catch((err) => { + diag.error('Error fetching job applicant details:', err); res.status(400).json(err); }); } @@ -351,43 +383,52 @@ router.get("/user/:id", jwtAuth, (req, res) => { User.findOne({ _id: req.params.id }) .then((userData) => { if (userData === null) { + diag.debug('User not found with id:', req.params.id); res.status(404).json({ message: "User does not exist", }); return; } + diag.debug('User found:', userData); if (userData.type === "recruiter") { Recruiter.findOne({ userId: userData._id }) .then((recruiter) => { if (recruiter === null) { + diag.debug('Recruiter not found for user id:', userData._id); res.status(404).json({ message: "User does not exist", }); return; } + diag.debug('Recruiter found:', recruiter); res.json(recruiter); }) .catch((err) => { + diag.error('Error fetching recruiter details:', err); res.status(400).json(err); }); } else { JobApplicant.findOne({ userId: userData._id }) .then((jobApplicant) => { if (jobApplicant === null) { + diag.debug('Job applicant not found for user id:', userData._id); res.status(404).json({ message: "User does not exist", }); return; } + diag.debug('Job applicant found:', jobApplicant); res.json(jobApplicant); }) .catch((err) => { + diag.error('Error fetching job applicant details:', err); res.status(400).json(err); }); } }) .catch((err) => { + diag.error('Error fetching user details:', err); res.status(400).json(err); }); }); @@ -396,10 +437,14 @@ router.get("/user/:id", jwtAuth, (req, res) => { router.put("/user", jwtAuth, (req, res) => { const user = req.user; const data = req.body; + diag.debug('User type:', user.type); + diag.debug('Update data:', data); + if (user.type == "recruiter") { Recruiter.findOne({ userId: user._id }) .then((recruiter) => { if (recruiter == null) { + diag.debug('Recruiter not found for update with user id:', user._id); res.status(404).json({ message: "User does not exist", }); @@ -417,21 +462,25 @@ router.put("/user", jwtAuth, (req, res) => { recruiter .save() .then(() => { + diag.debug('Recruiter information updated successfully'); res.json({ message: "User information updated successfully", }); }) .catch((err) => { + diag.error('Error updating recruiter information:', err); res.status(400).json(err); }); }) .catch((err) => { + diag.error('Error finding recruiter for update:', err); res.status(400).json(err); }); } else { JobApplicant.findOne({ userId: user._id }) .then((jobApplicant) => { if (jobApplicant == null) { + diag.debug('Job applicant not found for update with user id:', user._id); res.status(404).json({ message: "User does not exist", }); @@ -452,19 +501,22 @@ router.put("/user", jwtAuth, (req, res) => { if (data.profile) { jobApplicant.profile = data.profile; } - console.log(jobApplicant); + diag.debug('Job applicant before save:', jobApplicant); jobApplicant .save() .then(() => { + diag.debug('Job applicant information updated successfully'); res.json({ message: "User information updated successfully", }); }) .catch((err) => { + diag.error('Error updating job applicant information:', err); res.status(400).json(err); }); }) .catch((err) => { + diag.error('Error finding job applicant for update:', err); res.status(400).json(err); }); } @@ -473,6 +525,8 @@ router.put("/user", jwtAuth, (req, res) => { // apply for a job [todo: test: done] router.post("/jobs/:id/applications", jwtAuth, (req, res) => { const user = req.user; + diag.debug('User type:', user.type); + if (user.type != "applicant") { res.status(401).json({ message: "You don't have permissions to apply for a job", @@ -481,12 +535,8 @@ router.post("/jobs/:id/applications", jwtAuth, (req, res) => { } const data = req.body; const jobId = req.params.id; - - // check whether applied previously - // find job - // check count of active applications < limit - // check user had < 10 active applications && check if user is not having any accepted jobs (user id) - // store the data in applications + diag.debug('Application data:', data); + diag.debug('Job ID:', jobId); Application.findOne({ userId: user._id, @@ -496,7 +546,7 @@ router.post("/jobs/:id/applications", jwtAuth, (req, res) => { }, }) .then((appliedApplication) => { - console.log(appliedApplication); + diag.debug('Previously applied application:', appliedApplication); if (appliedApplication !== null) { res.status(400).json({ message: "You have already applied for this job", @@ -507,11 +557,14 @@ router.post("/jobs/:id/applications", jwtAuth, (req, res) => { Job.findOne({ _id: jobId }) .then((job) => { if (job === null) { + diag.debug('Job not found for application with id:', jobId); res.status(404).json({ message: "Job does not exist", }); return; } + diag.debug('Job found for application:', job); + Application.countDocuments({ jobId: jobId, status: { @@ -519,6 +572,7 @@ router.post("/jobs/:id/applications", jwtAuth, (req, res) => { }, }) .then((activeApplicationCount) => { + diag.debug('Active application count for job:', activeApplicationCount); if (activeApplicationCount < job.maxApplicants) { Application.countDocuments({ userId: user._id, @@ -527,11 +581,13 @@ router.post("/jobs/:id/applications", jwtAuth, (req, res) => { }, }) .then((myActiveApplicationCount) => { + diag.debug('User active application count:', myActiveApplicationCount); if (myActiveApplicationCount < 10) { Application.countDocuments({ userId: user._id, status: "accepted", }).then((acceptedJobs) => { + diag.debug('User accepted jobs count:', acceptedJobs); if (acceptedJobs === 0) { const application = new Application({ userId: user._id, @@ -543,11 +599,13 @@ router.post("/jobs/:id/applications", jwtAuth, (req, res) => { application .save() .then(() => { + diag.debug('Job application successful'); res.json({ message: "Job application successful", }); }) .catch((err) => { + diag.error('Error saving job application:', err); res.status(400).json(err); }); } else { @@ -565,6 +623,7 @@ router.post("/jobs/:id/applications", jwtAuth, (req, res) => { } }) .catch((err) => { + diag.error('Error counting user active applications:', err); res.status(400).json(err); }); } else { @@ -574,14 +633,17 @@ router.post("/jobs/:id/applications", jwtAuth, (req, res) => { } }) .catch((err) => { + diag.error('Error counting active applications for job:', err); res.status(400).json(err); }); }) .catch((err) => { + diag.error('Error finding job for application:', err); res.status(400).json(err); }); }) .catch((err) => { + diag.error('Error checking previous application:', err); res.json(400).json(err); }); }); @@ -589,6 +651,8 @@ router.post("/jobs/:id/applications", jwtAuth, (req, res) => { // recruiter gets applications for a particular job [pagination] [todo: test: done] router.get("/jobs/:id/applications", jwtAuth, (req, res) => { const user = req.user; + diag.debug('User type:', user.type); + if (user.type != "recruiter") { res.status(401).json({ message: "You don't have permissions to view job applications", @@ -596,15 +660,13 @@ router.get("/jobs/:id/applications", jwtAuth, (req, res) => { return; } const jobId = req.params.id; - - // const page = parseInt(req.query.page) ? parseInt(req.query.page) : 1; - // const limit = parseInt(req.query.limit) ? parseInt(req.query.limit) : 10; - // const skip = page - 1 >= 0 ? (page - 1) * limit : 0; + diag.debug('Job ID for applications:', jobId); let findParams = { jobId: jobId, recruiterId: user._id, }; + diag.debug('Find params for applications:', findParams); let sortParams = {}; @@ -613,17 +675,18 @@ router.get("/jobs/:id/applications", jwtAuth, (req, res) => { ...findParams, status: req.query.status, }; + diag.debug('Find params with status:', findParams); } Application.find(findParams) .collation({ locale: "en" }) .sort(sortParams) - // .skip(skip) - // .limit(limit) .then((applications) => { + diag.debug('Applications found:', applications.length); res.json(applications); }) .catch((err) => { + diag.error('Error fetching applications:', err); res.status(400).json(err); }); }); @@ -631,10 +694,7 @@ router.get("/jobs/:id/applications", jwtAuth, (req, res) => { // recruiter/applicant gets all his applications [pagination] router.get("/applications", jwtAuth, (req, res) => { const user = req.user; - - // const page = parseInt(req.query.page) ? parseInt(req.query.page) : 1; - // const limit = parseInt(req.query.limit) ? parseInt(req.query.limit) : 10; - // const skip = page - 1 >= 0 ? (page - 1) * limit : 0; + diag.debug('User type:', user.type); Application.aggregate([ { @@ -676,9 +736,11 @@ router.get("/applications", jwtAuth, (req, res) => { }, ]) .then((applications) => { + diag.debug('Applications found:', applications.length); res.json(applications); }) .catch((err) => { + diag.error('Error fetching applications:', err); res.status(400).json(err); }); }); @@ -688,52 +750,46 @@ router.put("/applications/:id", jwtAuth, (req, res) => { const user = req.user; const id = req.params.id; const status = req.body.status; - - // "applied", // when a applicant is applied - // "shortlisted", // when a applicant is shortlisted - // "accepted", // when a applicant is accepted - // "rejected", // when a applicant is rejected - // "deleted", // when any job is deleted - // "cancelled", // an application is cancelled by its author or when other application is accepted - // "finished", // when job is over + diag.debug('User type:', user.type); + diag.debug('Application ID:', id); + diag.debug('New status:', status); if (user.type === "recruiter") { if (status === "accepted") { - // get job id from application - // get job info for maxPositions count - // count applications that are already accepted - // compare and if condition is satisfied, then save - Application.findOne({ _id: id, recruiterId: user._id, }) .then((application) => { if (application === null) { + diag.debug('Application not found for acceptance with id:', id); res.status(404).json({ message: "Application not found", }); return; } + diag.debug('Application found for acceptance:', application); Job.findOne({ _id: application.jobId, userId: user._id, }).then((job) => { if (job === null) { + diag.debug('Job not found for application acceptance with id:', application.jobId); res.status(404).json({ message: "Job does not exist", }); return; } + diag.debug('Job found for application acceptance:', job); Application.countDocuments({ recruiterId: user._id, jobId: job._id, status: "accepted", }).then((activeApplicationCount) => { + diag.debug('Active accepted application count:', activeApplicationCount); if (activeApplicationCount < job.maxPositions) { - // accepted application.status = status; application.dateOfJoining = req.body.dateOfJoining; application @@ -776,11 +832,13 @@ router.put("/applications/:id", jwtAuth, (req, res) => { } ) .then(() => { + diag.debug('Application accepted successfully'); res.json({ message: `Application ${status} successfully`, }); }) .catch((err) => { + diag.error('Error updating job accepted candidates:', err); res.status(400).json(err); }); } else { @@ -790,10 +848,12 @@ router.put("/applications/:id", jwtAuth, (req, res) => { } }) .catch((err) => { + diag.error('Error updating other applications:', err); res.status(400).json(err); }); }) .catch((err) => { + diag.error('Error saving application status:', err); res.status(400).json(err); }); } else { @@ -805,6 +865,7 @@ router.put("/applications/:id", jwtAuth, (req, res) => { }); }) .catch((err) => { + diag.error('Error finding application for acceptance:', err); res.status(400).json(err); }); } else { @@ -824,11 +885,13 @@ router.put("/applications/:id", jwtAuth, (req, res) => { ) .then((application) => { if (application === null) { + diag.debug('Application status cannot be updated for id:', id); res.status(400).json({ message: "Application status cannot be updated", }); return; } + diag.debug('Application status updated successfully'); if (status === "finished") { res.json({ message: `Job ${status} successfully`, @@ -840,13 +903,13 @@ router.put("/applications/:id", jwtAuth, (req, res) => { } }) .catch((err) => { + diag.error('Error updating application status:', err); res.status(400).json(err); }); } } else { if (status === "cancelled") { - console.log(id); - console.log(user._id); + diag.debug('Cancelling application with id:', id); Application.findOneAndUpdate( { _id: id, @@ -859,12 +922,13 @@ router.put("/applications/:id", jwtAuth, (req, res) => { } ) .then((tmp) => { - console.log(tmp); + diag.debug('Application cancelled successfully:', tmp); res.json({ message: `Application ${status} successfully`, }); }) .catch((err) => { + diag.error('Error cancelling application:', err); res.status(400).json(err); }); } else { @@ -879,6 +943,8 @@ router.put("/applications/:id", jwtAuth, (req, res) => { // get a list of final applicants for all his jobs : recuiter router.get("/applicants", jwtAuth, (req, res) => { const user = req.user; + diag.debug('User type:', user.type); + if (user.type === "recruiter") { let findParams = { recruiterId: user._id, @@ -902,6 +968,8 @@ router.get("/applicants", jwtAuth, (req, res) => { }; } } + diag.debug('Find params for applicants:', findParams); + let sortParams = {}; if (!req.query.asc && !req.query.desc) { @@ -939,6 +1007,7 @@ router.get("/applicants", jwtAuth, (req, res) => { }; } } + diag.debug('Sort params for applicants:', sortParams); Application.aggregate([ { @@ -964,14 +1033,17 @@ router.get("/applicants", jwtAuth, (req, res) => { ]) .then((applications) => { if (applications.length === 0) { + diag.debug('No applicants found'); res.status(404).json({ message: "No applicants found", }); return; } + diag.debug('Applicants found:', applications.length); res.json(applications); }) .catch((err) => { + diag.error('Error fetching applicants:', err); res.status(400).json(err); }); } else { @@ -985,8 +1057,10 @@ router.get("/applicants", jwtAuth, (req, res) => { router.put("/rating", jwtAuth, (req, res) => { const user = req.user; const data = req.body; + diag.debug('User type:', user.type); + diag.debug('Rating data:', data); + if (user.type === "recruiter") { - // can rate applicant Rating.findOne({ senderId: user._id, receiverId: data.applicantId, @@ -994,7 +1068,7 @@ router.put("/rating", jwtAuth, (req, res) => { }) .then((rating) => { if (rating === null) { - console.log("new rating"); + diag.debug('No existing rating found, creating new'); Application.countDocuments({ userId: data.applicantId, recruiterId: user._id, @@ -1003,9 +1077,8 @@ router.put("/rating", jwtAuth, (req, res) => { }, }) .then((acceptedApplicant) => { + diag.debug('Accepted applicant count:', acceptedApplicant); if (acceptedApplicant > 0) { - // add a new rating - rating = new Rating({ category: "applicant", receiverId: data.applicantId, @@ -1016,7 +1089,6 @@ router.put("/rating", jwtAuth, (req, res) => { rating .save() .then(() => { - // get the average of ratings Rating.aggregate([ { $match: { @@ -1032,7 +1104,6 @@ router.put("/rating", jwtAuth, (req, res) => { }, ]) .then((result) => { - // update the user's rating if (result === null) { res.status(400).json({ message: "Error while calculating rating", @@ -1040,6 +1111,7 @@ router.put("/rating", jwtAuth, (req, res) => { return; } const avg = result[0].average; + diag.debug('Calculated average rating:', avg); JobApplicant.findOneAndUpdate( { @@ -1059,23 +1131,26 @@ router.put("/rating", jwtAuth, (req, res) => { }); return; } + diag.debug('Applicant rating updated successfully'); res.json({ message: "Rating added successfully", }); }) .catch((err) => { + diag.error('Error updating applicant rating:', err); res.status(400).json(err); }); }) .catch((err) => { + diag.error('Error calculating average rating:', err); res.status(400).json(err); }); }) .catch((err) => { + diag.error('Error saving new rating:', err); res.status(400).json(err); }); } else { - // you cannot rate res.status(400).json({ message: "Applicant didn't worked under you. Hence you cannot give a rating.", @@ -1083,6 +1158,7 @@ router.put("/rating", jwtAuth, (req, res) => { } }) .catch((err) => { + diag.error('Error counting accepted applicants:', err); res.status(400).json(err); }); } else { @@ -1090,7 +1166,6 @@ router.put("/rating", jwtAuth, (req, res) => { rating .save() .then(() => { - // get the average of ratings Rating.aggregate([ { $match: { @@ -1106,7 +1181,6 @@ router.put("/rating", jwtAuth, (req, res) => { }, ]) .then((result) => { - // update the user's rating if (result === null) { res.status(400).json({ message: "Error while calculating rating", @@ -1114,6 +1188,8 @@ router.put("/rating", jwtAuth, (req, res) => { return; } const avg = result[0].average; + diag.debug('Calculated average rating:', avg); + JobApplicant.findOneAndUpdate( { userId: data.applicantId, @@ -1132,39 +1208,40 @@ router.put("/rating", jwtAuth, (req, res) => { }); return; } + diag.debug('Applicant rating updated successfully'); res.json({ message: "Rating updated successfully", }); }) .catch((err) => { + diag.error('Error updating applicant rating:', err); res.status(400).json(err); }); }) .catch((err) => { + diag.error('Error calculating average rating:', err); res.status(400).json(err); }); }) .catch((err) => { + diag.error('Error saving updated rating:', err); res.status(400).json(err); }); } }) .catch((err) => { + diag.error('Error finding existing rating:', err); res.status(400).json(err); }); } else { - // applicant can rate job Rating.findOne({ senderId: user._id, receiverId: data.jobId, category: "job", }) .then((rating) => { - console.log(user._id); - console.log(data.jobId); - console.log(rating); + diag.debug('Existing rating:', rating); if (rating === null) { - console.log(rating); Application.countDocuments({ userId: user._id, jobId: data.jobId, @@ -1173,9 +1250,8 @@ router.put("/rating", jwtAuth, (req, res) => { }, }) .then((acceptedApplicant) => { + diag.debug('Accepted applicant count for job:', acceptedApplicant); if (acceptedApplicant > 0) { - // add a new rating - rating = new Rating({ category: "job", receiverId: data.jobId, @@ -1186,7 +1262,6 @@ router.put("/rating", jwtAuth, (req, res) => { rating .save() .then(() => { - // get the average of ratings Rating.aggregate([ { $match: { @@ -1209,6 +1284,8 @@ router.put("/rating", jwtAuth, (req, res) => { return; } const avg = result[0].average; + diag.debug('Calculated average rating for job:', avg); + Job.findOneAndUpdate( { _id: data.jobId, @@ -1227,23 +1304,26 @@ router.put("/rating", jwtAuth, (req, res) => { }); return; } + diag.debug('Job rating updated successfully'); res.json({ message: "Rating added successfully", }); }) .catch((err) => { + diag.error('Error updating job rating:', err); res.status(400).json(err); }); }) .catch((err) => { + diag.error('Error calculating average job rating:', err); res.status(400).json(err); }); }) .catch((err) => { + diag.error('Error saving new job rating:', err); res.status(400).json(err); }); } else { - // you cannot rate res.status(400).json({ message: "You haven't worked for this job. Hence you cannot give a rating.", @@ -1251,15 +1331,14 @@ router.put("/rating", jwtAuth, (req, res) => { } }) .catch((err) => { + diag.error('Error counting accepted applicants for job:', err); res.status(400).json(err); }); } else { - // update the rating rating.rating = data.rating; rating .save() .then(() => { - // get the average of ratings Rating.aggregate([ { $match: { @@ -1282,7 +1361,7 @@ router.put("/rating", jwtAuth, (req, res) => { return; } const avg = result[0].average; - console.log(avg); + diag.debug('Calculated average rating for job:', avg); Job.findOneAndUpdate( { @@ -1301,24 +1380,29 @@ router.put("/rating", jwtAuth, (req, res) => { }); return; } + diag.debug('Job rating updated successfully'); res.json({ message: "Rating added successfully", }); }) .catch((err) => { + diag.error('Error updating job rating:', err); res.status(400).json(err); }); }) .catch((err) => { + diag.error('Error calculating average job rating:', err); res.status(400).json(err); }); }) .catch((err) => { + diag.error('Error saving updated job rating:', err); res.status(400).json(err); }); } }) .catch((err) => { + diag.error('Error finding existing job rating:', err); res.status(400).json(err); }); } @@ -1327,54 +1411,25 @@ router.put("/rating", jwtAuth, (req, res) => { // get personal rating router.get("/rating", jwtAuth, (req, res) => { const user = req.user; + diag.debug('User type:', user.type); + Rating.findOne({ senderId: user._id, receiverId: req.query.id, category: user.type === "recruiter" ? "applicant" : "job", }).then((rating) => { if (rating === null) { + diag.debug('No personal rating found'); res.json({ rating: -1, }); return; } + diag.debug('Personal rating found:', rating.rating); res.json({ rating: rating.rating, }); }); }); -// Application.findOne({ -// _id: id, -// userId: user._id, -// }) -// .then((application) => { -// application.status = status; -// application -// .save() -// .then(() => { -// res.json({ -// message: `Application ${status} successfully`, -// }); -// }) -// .catch((err) => { -// res.status(400).json(err); -// }); -// }) -// .catch((err) => { -// res.status(400).json(err); -// }); - -// router.get("/jobs", (req, res, next) => { -// passport.authenticate("jwt", { session: false }, function (err, user, info) { -// if (err) { -// return next(err); -// } -// if (!user) { -// res.status(401).json(info); -// return; -// } -// })(req, res, next); -// }); - module.exports = router; diff --git a/backend/routes/authRoutes.js b/backend/routes/authRoutes.js index 7911934..b51b792 100644 --- a/backend/routes/authRoutes.js +++ b/backend/routes/authRoutes.js @@ -2,6 +2,7 @@ const express = require("express"); const passport = require("passport"); const jwt = require("jsonwebtoken"); const authKeys = require("../lib/authKeys"); +const { diag } = require('@opentelemetry/api'); const User = require("../db/User"); const JobApplicant = require("../db/JobApplicant"); @@ -11,6 +12,8 @@ const router = express.Router(); router.post("/signup", (req, res) => { const data = req.body; + diag.info('Received signup request', { data }); + let user = new User({ email: data.email, password: data.password, @@ -20,6 +23,8 @@ router.post("/signup", (req, res) => { user .save() .then(() => { + diag.info('User saved successfully', { userId: user._id }); + const userDetails = user.type == "recruiter" ? new Recruiter({ @@ -41,44 +46,61 @@ router.post("/signup", (req, res) => { userDetails .save() .then(() => { + diag.info('User details saved successfully', { userId: user._id }); + // Token const token = jwt.sign({ _id: user._id }, authKeys.jwtSecretKey); + diag.info('JWT token generated', { token }); + res.json({ token: token, type: user.type, }); }) .catch((err) => { + diag.error('Error saving user details', { error: err }); + user .delete() .then(() => { + diag.info('User deleted after error in saving details', { userId: user._id }); res.status(400).json(err); }) .catch((err) => { + diag.error('Error deleting user after failed details save', { error: err }); res.json({ error: err }); }); err; }); }) .catch((err) => { + diag.error('Error saving user', { error: err }); res.status(400).json(err); }); }); router.post("/login", (req, res, next) => { + diag.info('Received login request', { body: req.body }); + passport.authenticate( "local", { session: false }, function (err, user, info) { if (err) { + diag.error('Error during authentication', { error: err }); return next(err); } if (!user) { + diag.info('Authentication failed', { info }); res.status(401).json(info); return; } + diag.info('User authenticated successfully', { userId: user._id }); + // Token const token = jwt.sign({ _id: user._id }, authKeys.jwtSecretKey); + diag.info('JWT token generated', { token }); + res.json({ token: token, type: user.type, diff --git a/backend/routes/downloadRoutes.js b/backend/routes/downloadRoutes.js index 31798ef..b82ec36 100644 --- a/backend/routes/downloadRoutes.js +++ b/backend/routes/downloadRoutes.js @@ -1,31 +1,38 @@ const express = require("express"); const fs = require("fs"); const path = require("path"); +const { diag } = require("@opentelemetry/api"); const router = express.Router(); router.get("/resume/:file", (req, res) => { const address = path.join(__dirname, `../public/resume/${req.params.file}`); + diag.debug(`Attempting to access file at address: ${address}`); fs.access(address, fs.F_OK, (err) => { if (err) { + diag.error(`File not found at address: ${address}`); res.status(404).json({ message: "File not found", }); return; } + diag.debug(`File found, sending file at address: ${address}`); res.sendFile(address); }); }); router.get("/profile/:file", (req, res) => { const address = path.join(__dirname, `../public/profile/${req.params.file}`); + diag.debug(`Attempting to access file at address: ${address}`); fs.access(address, fs.F_OK, (err) => { if (err) { + diag.error(`File not found at address: ${address}`); res.status(404).json({ message: "File not found", }); return; } + diag.debug(`File found, sending file at address: ${address}`); res.sendFile(address); }); }); diff --git a/backend/routes/uploadRoutes.js b/backend/routes/uploadRoutes.js index b12d2aa..0b93276 100644 --- a/backend/routes/uploadRoutes.js +++ b/backend/routes/uploadRoutes.js @@ -3,6 +3,7 @@ const multer = require("multer"); const fs = require("fs"); const { v4: uuidv4 } = require("uuid"); const { promisify } = require("util"); +const { diag } = require("@opentelemetry/api"); const pipeline = promisify(require("stream").pipeline); @@ -12,24 +13,30 @@ const upload = multer(); router.post("/resume", upload.single("file"), (req, res) => { const { file } = req; + diag.debug("Received file for resume upload", { fileExtension: file.detectedFileExtension }); + if (file.detectedFileExtension != ".pdf") { + diag.warn("Invalid file format for resume upload", { fileExtension: file.detectedFileExtension }); res.status(400).json({ message: "Invalid format", }); } else { const filename = `${uuidv4()}${file.detectedFileExtension}`; + diag.debug("Generated filename for resume", { filename }); pipeline( file.stream, fs.createWriteStream(`${__dirname}/../public/resume/${filename}`) ) .then(() => { + diag.info("Resume file uploaded successfully", { filename }); res.send({ message: "File uploaded successfully", url: `/host/resume/${filename}`, }); }) .catch((err) => { + diag.error("Error while uploading resume file", { error: err }); res.status(400).json({ message: "Error while uploading", }); @@ -39,27 +46,33 @@ router.post("/resume", upload.single("file"), (req, res) => { router.post("/profile", upload.single("file"), (req, res) => { const { file } = req; + diag.debug("Received file for profile upload", { fileExtension: file.detectedFileExtension }); + if ( file.detectedFileExtension != ".jpg" && file.detectedFileExtension != ".png" ) { + diag.warn("Invalid file format for profile upload", { fileExtension: file.detectedFileExtension }); res.status(400).json({ message: "Invalid format", }); } else { const filename = `${uuidv4()}${file.detectedFileExtension}`; + diag.debug("Generated filename for profile image", { filename }); pipeline( file.stream, fs.createWriteStream(`${__dirname}/../public/profile/${filename}`) ) .then(() => { + diag.info("Profile image uploaded successfully", { filename }); res.send({ message: "Profile image uploaded successfully", url: `/host/profile/${filename}`, }); }) .catch((err) => { + diag.error("Error while uploading profile image", { error: err }); res.status(400).json({ message: "Error while uploading", }); diff --git a/backend/server.js b/backend/server.js index a09f526..87a5ee2 100644 --- a/backend/server.js +++ b/backend/server.js @@ -4,6 +4,10 @@ const mongoose = require("mongoose"); const passportConfig = require("./lib/passportConfig"); const cors = require("cors"); const fs = require("fs"); +const { diag, DiagConsoleLogger, DiagLogLevel } = require('@opentelemetry/api'); + +// Set up OpenTelemetry diagnostics +diag.setLogger(new DiagConsoleLogger(), DiagLogLevel.ALL); // MongoDB mongoose @@ -13,17 +17,24 @@ mongoose useCreateIndex: true, useFindAndModify: false, }) - .then((res) => console.log("Connected to DB")) - .catch((err) => console.log(err)); + .then((res) => { + diag.info("Connected to DB"); + }) + .catch((err) => { + diag.error("Error connecting to DB", err); + }); // initialising directories if (!fs.existsSync("./public")) { + diag.info("Creating directory: ./public"); fs.mkdirSync("./public"); } if (!fs.existsSync("./public/resume")) { + diag.info("Creating directory: ./public/resume"); fs.mkdirSync("./public/resume"); } if (!fs.existsSync("./public/profile")) { + diag.info("Creating directory: ./public/profile"); fs.mkdirSync("./public/profile"); } @@ -45,5 +56,5 @@ app.use("/upload", require("./routes/uploadRoutes")); app.use("/host", require("./routes/downloadRoutes")); app.listen(port, () => { - console.log(`Server started on port ${port}!`); + diag.info(`Server started on port ${port}!`); }); diff --git a/frontend/package.json b/frontend/package.json index 00b0d96..73e0d5a 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -16,7 +16,12 @@ "react-phone-input-2": "^2.13.9", "react-router-dom": "^5.2.0", "react-scripts": "4.0.1", - "web-vitals": "^0.2.4" + "web-vitals": "^0.2.4", + "@opentelemetry/api": "^1.0.0", + "@opentelemetry/sdk-trace-web": "^1.0.0", + "@opentelemetry/sdk-metrics": "^1.0.0", + "@opentelemetry/instrumentation": "^0.29.0", + "@opentelemetry/auto-instrumentations-web": "^0.29.0" }, "scripts": { "start": "react-scripts start", diff --git a/frontend/src/App.js b/frontend/src/App.js index 3e51b45..c3e9489 100644 --- a/frontend/src/App.js +++ b/frontend/src/App.js @@ -1,6 +1,7 @@ import { createContext, useState } from "react"; import { BrowserRouter, Switch, Route } from "react-router-dom"; import { Grid, makeStyles } from "@material-ui/core"; +import { diag } from '@opentelemetry/api'; import Welcome, { ErrorPage } from "./component/Welcome"; import Navbar from "./component/Navbar"; @@ -35,37 +36,46 @@ export const SetPopupContext = createContext(); function App() { const classes = useStyles(); + diag.debug('useStyles applied with classes:', classes); const [popup, setPopup] = useState({ open: false, severity: "", message: "", }); + diag.debug('Initial popup state:', popup); return ( + diag.debug('Navbar component rendered'); + diag.debug('Route "/" matched, Welcome component rendered'); + diag.debug('Route "/login" matched, Login component rendered'); + diag.debug('Route "/signup" matched, Signup component rendered'); + diag.debug('Route "/logout" matched, Logout component rendered'); + diag.debug('Route "/home" matched, Home component rendered'); + diag.debug('Route "/applications" matched, Applications component rendered'); {userType() === "recruiter" ? ( @@ -73,21 +83,27 @@ function App() { ) : ( )} + diag.debug('Route "/profile" matched, userType:', userType()); + diag.debug('Route "/addjob" matched, CreateJobs component rendered'); + diag.debug('Route "/myjobs" matched, MyJobs component rendered'); + diag.debug('Route "/job/applications/:jobId" matched, JobApplications component rendered'); + diag.debug('Route "/employees" matched, AcceptedApplicants component rendered'); + diag.debug('No route matched, ErrorPage component rendered'); @@ -103,6 +119,7 @@ function App() { severity={popup.severity} message={popup.message} /> + diag.debug('MessagePopup component rendered with state:', popup); ); diff --git a/frontend/src/App.test.js b/frontend/src/App.test.js index 1f03afe..b9a6e85 100644 --- a/frontend/src/App.test.js +++ b/frontend/src/App.test.js @@ -1,8 +1,11 @@ import { render, screen } from '@testing-library/react'; import App from './App'; +import { diag } from '@opentelemetry/api'; test('renders learn react link', () => { + diag.info('Rendering App component'); render(); const linkElement = screen.getByText(/learn react/i); + diag.info('Checking if the link element is in the document', { linkElement }); expect(linkElement).toBeInTheDocument(); }); diff --git a/frontend/src/component/Applications.js b/frontend/src/component/Applications.js index d6e07a4..46f35a6 100644 --- a/frontend/src/component/Applications.js +++ b/frontend/src/component/Applications.js @@ -18,6 +18,7 @@ import { } from "@material-ui/core"; import Rating from "@material-ui/lab/Rating"; import axios from "axios"; +import { diag } from '@opentelemetry/api'; import { SetPopupContext } from "../App"; @@ -60,6 +61,7 @@ const ApplicationTile = (props) => { const joinedOn = new Date(application.dateOfJoining); const fetchRating = () => { + diag.debug('Fetching rating for job ID:', application.job._id); axios .get(`${apiList.rating}?id=${application.job._id}`, { headers: { @@ -68,10 +70,11 @@ const ApplicationTile = (props) => { }) .then((response) => { setRating(response.data.rating); + diag.debug('Fetched rating:', response.data.rating); console.log(response.data); }) .catch((err) => { - // console.log(err.response); + diag.error('Error fetching rating:', err.response.data); console.log(err.response.data); setPopup({ open: true, @@ -82,6 +85,7 @@ const ApplicationTile = (props) => { }; const changeRating = () => { + diag.debug('Changing rating to:', rating, 'for job ID:', application.job._id); axios .put( apiList.rating, @@ -93,6 +97,7 @@ const ApplicationTile = (props) => { } ) .then((response) => { + diag.debug('Rating changed successfully:', response.data); console.log(response.data); setPopup({ open: true, @@ -103,7 +108,7 @@ const ApplicationTile = (props) => { setOpen(false); }) .catch((err) => { - // console.log(err.response); + diag.error('Error changing rating:', err); console.log(err); setPopup({ open: true, @@ -116,6 +121,7 @@ const ApplicationTile = (props) => { }; const handleClose = () => { + diag.debug('Closing rating modal'); setOpen(false); }; @@ -176,6 +182,7 @@ const ApplicationTile = (props) => { color="primary" className={classes.statusBlock} onClick={() => { + diag.debug('Opening rating modal for job ID:', application.job._id); fetchRating(); setOpen(true); }} @@ -203,6 +210,7 @@ const ApplicationTile = (props) => { style={{ marginBottom: "30px" }} value={rating === -1 ? null : rating} onChange={(event, newValue) => { + diag.debug('Rating changed to:', newValue); setRating(newValue); }} /> @@ -225,6 +233,7 @@ const Applications = (props) => { const [applications, setApplications] = useState([]); useEffect(() => { + diag.debug('Fetching applications data'); getData(); }, []); @@ -236,11 +245,12 @@ const Applications = (props) => { }, }) .then((response) => { + diag.debug('Fetched applications data:', response.data); console.log(response.data); setApplications(response.data); }) .catch((err) => { - // console.log(err.response); + diag.error('Error fetching applications data:', err.response.data); console.log(err.response.data); setPopup({ open: true, diff --git a/frontend/src/component/Home.js b/frontend/src/component/Home.js index b237895..14e83a7 100644 --- a/frontend/src/component/Home.js +++ b/frontend/src/component/Home.js @@ -28,6 +28,7 @@ import { SetPopupContext } from "../App"; import apiList from "../lib/apiList"; import { userType } from "../lib/isAuth"; +import { diagLog } from '@opentelemetry/api'; const useStyles = makeStyles((theme) => ({ body: { @@ -62,11 +63,11 @@ const JobTile = (props) => { const handleClose = () => { setOpen(false); setSop(""); + diagLog.info('Modal closed and SOP reset'); }; const handleApply = () => { - console.log(job._id); - console.log(sop); + diagLog.info('Applying for job', { jobId: job._id, sop: sop }); axios .post( `${apiList.jobs}/${job._id}/applications`, @@ -80,6 +81,7 @@ const JobTile = (props) => { } ) .then((response) => { + diagLog.info('Application successful', { message: response.data.message }); setPopup({ open: true, severity: "success", @@ -88,7 +90,7 @@ const JobTile = (props) => { handleClose(); }) .catch((err) => { - console.log(err.response); + diagLog.error('Application failed', { error: err.response }); setPopup({ open: true, severity: "error", @@ -99,6 +101,7 @@ const JobTile = (props) => { }; const deadline = new Date(job.deadline).toLocaleDateString(); + diagLog.info('Job deadline', { deadline: deadline }); return ( @@ -132,6 +135,7 @@ const JobTile = (props) => { className={classes.button} onClick={() => { setOpen(true); + diagLog.info('Open apply modal', { jobId: job._id }); }} disabled={userType() === "recruiter"} > @@ -165,6 +169,7 @@ const JobTile = (props) => { }).length <= 250 ) { setSop(event.target.value); + diagLog.info('SOP updated', { sop: event.target.value }); } }} /> @@ -185,6 +190,7 @@ const JobTile = (props) => { const FilterPopup = (props) => { const classes = useStyles(); const { open, handleClose, searchOptions, setSearchOptions, getData } = props; + diagLog.info('FilterPopup props', { open, searchOptions }); return ( { [event.target.name]: event.target.checked, }, }); + diagLog.info('Job type fullTime changed', { checked: event.target.checked }); }} /> } @@ -240,6 +247,7 @@ const FilterPopup = (props) => { [event.target.name]: event.target.checked, }, }); + diagLog.info('Job type partTime changed', { checked: event.target.checked }); }} /> } @@ -260,6 +268,7 @@ const FilterPopup = (props) => { [event.target.name]: event.target.checked, }, }); + diagLog.info('Job type wfh changed', { checked: event.target.checked }); }} /> } @@ -283,12 +292,13 @@ const FilterPopup = (props) => { { value: 100, label: "100000" }, ]} value={searchOptions.salary} - onChange={(event, value) => + onChange={(event, value) => { setSearchOptions({ ...searchOptions, salary: value, - }) - } + }); + diagLog.info('Salary range changed', { salary: value }); + }} /> @@ -303,12 +313,13 @@ const FilterPopup = (props) => { variant="outlined" fullWidth value={searchOptions.duration} - onChange={(event) => + onChange={(event) => { setSearchOptions({ ...searchOptions, duration: event.target.value, - }) - } + }); + diagLog.info('Duration changed', { duration: event.target.value }); + }} > All 1 @@ -338,7 +349,7 @@ const FilterPopup = (props) => { + onChange={(event) => { setSearchOptions({ ...searchOptions, sort: { @@ -348,8 +359,9 @@ const FilterPopup = (props) => { status: event.target.checked, }, }, - }) - } + }); + diagLog.info('Sort by salary status changed', { status: event.target.checked }); + }} id="salary" /> @@ -372,6 +384,7 @@ const FilterPopup = (props) => { }, }, }); + diagLog.info('Sort by salary order changed', { desc: !searchOptions.sort.salary.desc }); }} > {searchOptions.sort.salary.desc ? ( @@ -394,7 +407,7 @@ const FilterPopup = (props) => { + onChange={(event) => { setSearchOptions({ ...searchOptions, sort: { @@ -404,8 +417,9 @@ const FilterPopup = (props) => { status: event.target.checked, }, }, - }) - } + }); + diagLog.info('Sort by duration status changed', { status: event.target.checked }); + }} id="duration" /> @@ -428,6 +442,7 @@ const FilterPopup = (props) => { }, }, }); + diagLog.info('Sort by duration order changed', { desc: !searchOptions.sort.duration.desc }); }} > {searchOptions.sort.duration.desc ? ( @@ -450,7 +465,7 @@ const FilterPopup = (props) => { + onChange={(event) => { setSearchOptions({ ...searchOptions, sort: { @@ -460,8 +475,9 @@ const FilterPopup = (props) => { status: event.target.checked, }, }, - }) - } + }); + diagLog.info('Sort by rating status changed', { status: event.target.checked }); + }} id="rating" /> @@ -484,6 +500,7 @@ const FilterPopup = (props) => { }, }, }); + diagLog.info('Sort by rating order changed', { desc: !searchOptions.sort.rating.desc }); }} > {searchOptions.sort.rating.desc ? ( @@ -591,7 +608,7 @@ const Home = (props) => { }); searchParams = [...searchParams, ...asc, ...desc]; const queryString = searchParams.join("&"); - console.log(queryString); + diagLog.info('Query string for job search', { queryString: queryString }); let address = apiList.jobs; if (queryString !== "") { address = `${address}?${queryString}`; @@ -604,7 +621,7 @@ const Home = (props) => { }, }) .then((response) => { - console.log(response.data); + diagLog.info('Jobs fetched successfully', { jobs: response.data }); setJobs( response.data.filter((obj) => { const today = new Date(); @@ -614,7 +631,7 @@ const Home = (props) => { ); }) .catch((err) => { - console.log(err.response.data); + diagLog.error('Error fetching jobs', { error: err.response.data }); setPopup({ open: true, severity: "error", @@ -646,21 +663,26 @@ const Home = (props) => { + onChange={(event) => { setSearchOptions({ ...searchOptions, query: event.target.value, - }) - } + }); + diagLog.info('Search query updated', { query: event.target.value }); + }} onKeyPress={(ev) => { if (ev.key === "Enter") { getData(); + diagLog.info('Enter key pressed, fetching data'); } }} InputProps={{ endAdornment: ( - getData()}> + { + getData(); + diagLog.info('Search button clicked, fetching data'); + }}> @@ -671,7 +693,10 @@ const Home = (props) => { /> - setFilterOpen(true)}> + { + setFilterOpen(true); + diagLog.info('Filter button clicked, opening filter modal'); + }}> @@ -703,10 +728,14 @@ const Home = (props) => { open={filterOpen} searchOptions={searchOptions} setSearchOptions={setSearchOptions} - handleClose={() => setFilterOpen(false)} + handleClose={() => { + setFilterOpen(false); + diagLog.info('Filter modal closed'); + }} getData={() => { getData(); setFilterOpen(false); + diagLog.info('Filter applied, fetching data'); }} /> diff --git a/frontend/src/component/Login.js b/frontend/src/component/Login.js index 33de1d4..3fa16fa 100644 --- a/frontend/src/component/Login.js +++ b/frontend/src/component/Login.js @@ -9,6 +9,7 @@ import { } from "@material-ui/core"; import axios from "axios"; import { Redirect } from "react-router-dom"; +import { diag } from '@opentelemetry/api'; import PasswordInput from "../lib/PasswordInput"; import EmailInput from "../lib/EmailInput"; @@ -34,11 +35,13 @@ const Login = (props) => { const setPopup = useContext(SetPopupContext); const [loggedin, setLoggedin] = useState(isAuth()); + diag.debug('Initial loggedin state:', loggedin); const [loginDetails, setLoginDetails] = useState({ email: "", password: "", }); + diag.debug('Initial loginDetails state:', loginDetails); const [inputErrorHandler, setInputErrorHandler] = useState({ email: { @@ -50,15 +53,19 @@ const Login = (props) => { message: "", }, }); + diag.debug('Initial inputErrorHandler state:', inputErrorHandler); const handleInput = (key, value) => { + diag.debug('handleInput called with:', key, value); setLoginDetails({ ...loginDetails, [key]: value, }); + diag.debug('Updated loginDetails state:', loginDetails); }; const handleInputError = (key, status, message) => { + diag.debug('handleInputError called with:', key, status, message); setInputErrorHandler({ ...inputErrorHandler, [key]: { @@ -66,35 +73,40 @@ const Login = (props) => { message: message, }, }); + diag.debug('Updated inputErrorHandler state:', inputErrorHandler); }; const handleLogin = () => { + diag.debug('handleLogin called'); const verified = !Object.keys(inputErrorHandler).some((obj) => { return inputErrorHandler[obj].error; }); + diag.debug('Verification result:', verified); if (verified) { axios .post(apiList.login, loginDetails) .then((response) => { + diag.debug('Login successful, response:', response); localStorage.setItem("token", response.data.token); localStorage.setItem("type", response.data.type); setLoggedin(isAuth()); + diag.debug('Updated loggedin state:', loggedin); setPopup({ open: true, severity: "success", message: "Logged in successfully", }); - console.log(response); }) .catch((err) => { + diag.debug('Login failed, error:', err.response); setPopup({ open: true, severity: "error", message: err.response.data.message, }); - console.log(err.response); }); } else { + diag.debug('Input verification failed'); setPopup({ open: true, severity: "error", diff --git a/frontend/src/component/Logout.js b/frontend/src/component/Logout.js index db46530..a4d8377 100644 --- a/frontend/src/component/Logout.js +++ b/frontend/src/component/Logout.js @@ -1,19 +1,24 @@ import { useEffect, useContext } from "react"; import { Redirect } from "react-router-dom"; +import { diag } from '@opentelemetry/api'; import { SetPopupContext } from "../App"; const Logout = (props) => { const setPopup = useContext(SetPopupContext); useEffect(() => { + diag.info('Removing token from localStorage'); localStorage.removeItem("token"); + diag.info('Removing type from localStorage'); localStorage.removeItem("type"); + diag.info('Setting popup with success message'); setPopup({ open: true, severity: "success", message: "Logged out successfully", }); }, []); + diag.info('Redirecting to login page'); return ; }; diff --git a/frontend/src/component/Navbar.js b/frontend/src/component/Navbar.js index 4b18c34..2b44093 100644 --- a/frontend/src/component/Navbar.js +++ b/frontend/src/component/Navbar.js @@ -1,13 +1,7 @@ -import { - AppBar, - Toolbar, - Typography, - Button, - makeStyles, -} from "@material-ui/core"; +import { AppBar, Toolbar, Typography, Button, makeStyles } from "@material-ui/core"; import { useHistory } from "react-router-dom"; - import isAuth, { userType } from "../lib/isAuth"; +import { diag } from '@opentelemetry/api'; const useStyles = makeStyles((theme) => ({ root: { @@ -26,7 +20,7 @@ const Navbar = (props) => { let history = useHistory(); const handleClick = (location) => { - console.log(location); + diag.debug(`Navigating to location: ${location}`); history.push(location); }; diff --git a/frontend/src/component/Profile.js b/frontend/src/component/Profile.js index 356801e..95a43e4 100644 --- a/frontend/src/component/Profile.js +++ b/frontend/src/component/Profile.js @@ -18,6 +18,8 @@ import { SetPopupContext } from "../App"; import apiList from "../lib/apiList"; +import { trace } from '@opentelemetry/api'; + const useStyles = makeStyles((theme) => ({ body: { height: "inherit", @@ -47,6 +49,7 @@ const MultifieldInput = (props) => { const newEdu = [...education]; newEdu[key].institutionName = event.target.value; setEducation(newEdu); + trace.getTracer('default').addEvent(`Updated institutionName for key ${key}: ${event.target.value}`); }} variant="outlined" fullWidth @@ -62,6 +65,7 @@ const MultifieldInput = (props) => { const newEdu = [...education]; newEdu[key].startYear = event.target.value; setEducation(newEdu); + trace.getTracer('default').addEvent(`Updated startYear for key ${key}: ${event.target.value}`); }} /> @@ -75,6 +79,7 @@ const MultifieldInput = (props) => { const newEdu = [...education]; newEdu[key].endYear = event.target.value; setEducation(newEdu); + trace.getTracer('default').addEvent(`Updated endYear for key ${key}: ${event.target.value}`); }} /> @@ -84,7 +89,7 @@ const MultifieldInput = (props) => { @@ -398,28 +459,36 @@ const ApplicationTile = (props) => { status: status, dateOfJoining: new Date().toISOString(), }; - axios - .put(address, statusData, { - headers: { - Authorization: `Bearer ${localStorage.getItem("token")}`, - }, - }) - .then((response) => { - setPopup({ - open: true, - severity: "success", - message: response.data.message, - }); - getData(); - }) - .catch((err) => { - setPopup({ - open: true, - severity: "error", - message: err.response.data.message, + const tracer = trace.getTracer('default'); + tracer.startActiveSpan('Update Status', span => { + axios + .put(address, statusData, { + headers: { + Authorization: `Bearer ${localStorage.getItem("token")}`, + }, + }) + .then((response) => { + setPopup({ + open: true, + severity: "success", + message: response.data.message, + }); + getData(); + span.setAttribute('status', status); + span.setAttribute('responseMessage', response.data.message); + span.end(); + }) + .catch((err) => { + setPopup({ + open: true, + severity: "error", + message: err.response.data.message, + }); + console.log(err.response); + span.setAttribute('error', err.response.data.message); + span.end(); }); - console.log(err.response); - }); + }); }; const buttonSet = { @@ -703,26 +772,32 @@ const JobApplications = (props) => { console.log(address); - axios - .get(address, { - headers: { - Authorization: `Bearer ${localStorage.getItem("token")}`, - }, - }) - .then((response) => { - console.log(response.data); - setApplications(response.data); - }) - .catch((err) => { - console.log(err.response); - // console.log(err.response.data); - setApplications([]); - setPopup({ - open: true, - severity: "error", - message: err.response.data.message, + const tracer = trace.getTracer('default'); + tracer.startActiveSpan('Get Data', span => { + axios + .get(address, { + headers: { + Authorization: `Bearer ${localStorage.getItem("token")}`, + }, + }) + .then((response) => { + console.log(response.data); + setApplications(response.data); + span.setAttribute('applicationsCount', response.data.length); + span.end(); + }) + .catch((err) => { + console.log(err.response); + setApplications([]); + setPopup({ + open: true, + severity: "error", + message: err.response.data.message, + }); + span.setAttribute('error', err.response.data.message); + span.end(); }); - }); + }); }; return ( @@ -754,7 +829,6 @@ const JobApplications = (props) => { {applications.length > 0 ? ( applications.map((obj) => ( - {/* {console.log(obj)} */} )) diff --git a/frontend/src/component/recruiter/MyJobs.js b/frontend/src/component/recruiter/MyJobs.js index 64bf9ae..b044748 100644 --- a/frontend/src/component/recruiter/MyJobs.js +++ b/frontend/src/component/recruiter/MyJobs.js @@ -29,6 +29,8 @@ import { SetPopupContext } from "../../App"; import apiList from "../../lib/apiList"; +import { trace } from '@opentelemetry/api'; + const useStyles = makeStyles((theme) => ({ body: { height: "inherit", @@ -69,9 +71,11 @@ const JobTile = (props) => { const [openUpdate, setOpenUpdate] = useState(false); const [jobDetails, setJobDetails] = useState(job); - console.log(jobDetails); + const tracer = trace.getTracer('default'); + tracer.startSpan('JobTile').addEvent('jobDetails', { jobDetails }); const handleInput = (key, value) => { + tracer.startSpan('handleInput').addEvent('input', { key, value }); setJobDetails({ ...jobDetails, [key]: value, @@ -79,6 +83,7 @@ const JobTile = (props) => { }; const handleClick = (location) => { + tracer.startSpan('handleClick').addEvent('location', { location }); history.push(location); }; @@ -91,7 +96,7 @@ const JobTile = (props) => { }; const handleDelete = () => { - console.log(job._id); + tracer.startSpan('handleDelete').addEvent('jobId', { jobId: job._id }); axios .delete(`${apiList.jobs}/${job._id}`, { headers: { @@ -99,6 +104,7 @@ const JobTile = (props) => { }, }) .then((response) => { + tracer.startSpan('handleDeleteSuccess').addEvent('response', { response: response.data }); setPopup({ open: true, severity: "success", @@ -108,7 +114,7 @@ const JobTile = (props) => { handleClose(); }) .catch((err) => { - console.log(err.response); + tracer.startSpan('handleDeleteError').addEvent('error', { error: err.response }); setPopup({ open: true, severity: "error", @@ -119,6 +125,7 @@ const JobTile = (props) => { }; const handleJobUpdate = () => { + tracer.startSpan('handleJobUpdate').addEvent('jobDetails', { jobDetails }); axios .put(`${apiList.jobs}/${job._id}`, jobDetails, { headers: { @@ -126,6 +133,7 @@ const JobTile = (props) => { }, }) .then((response) => { + tracer.startSpan('handleJobUpdateSuccess').addEvent('response', { response: response.data }); setPopup({ open: true, severity: "success", @@ -135,7 +143,7 @@ const JobTile = (props) => { handleCloseUpdate(); }) .catch((err) => { - console.log(err.response); + tracer.startSpan('handleJobUpdateError').addEvent('error', { error: err.response }); setPopup({ open: true, severity: "error", @@ -352,6 +360,9 @@ const JobTile = (props) => { const FilterPopup = (props) => { const classes = useStyles(); const { open, handleClose, searchOptions, setSearchOptions, getData } = props; + const tracer = trace.getTracer('default'); + tracer.startSpan('FilterPopup').addEvent('searchOptions', { searchOptions }); + return ( { name="fullTime" checked={searchOptions.jobType.fullTime} onChange={(event) => { + tracer.startSpan('FilterPopupCheckboxChange').addEvent('fullTime', { checked: event.target.checked }); setSearchOptions({ ...searchOptions, jobType: { @@ -400,6 +412,7 @@ const FilterPopup = (props) => { name="partTime" checked={searchOptions.jobType.partTime} onChange={(event) => { + tracer.startSpan('FilterPopupCheckboxChange').addEvent('partTime', { checked: event.target.checked }); setSearchOptions({ ...searchOptions, jobType: { @@ -420,6 +433,7 @@ const FilterPopup = (props) => { name="wfh" checked={searchOptions.jobType.wfh} onChange={(event) => { + tracer.startSpan('FilterPopupCheckboxChange').addEvent('wfh', { checked: event.target.checked }); setSearchOptions({ ...searchOptions, jobType: { @@ -450,12 +464,13 @@ const FilterPopup = (props) => { { value: 100, label: "100000" }, ]} value={searchOptions.salary} - onChange={(event, value) => + onChange={(event, value) => { + tracer.startSpan('FilterPopupSliderChange').addEvent('salary', { value }); setSearchOptions({ ...searchOptions, salary: value, - }) - } + }); + }} /> @@ -470,12 +485,13 @@ const FilterPopup = (props) => { variant="outlined" fullWidth value={searchOptions.duration} - onChange={(event) => + onChange={(event) => { + tracer.startSpan('FilterPopupDurationChange').addEvent('duration', { duration: event.target.value }); setSearchOptions({ ...searchOptions, duration: event.target.value, - }) - } + }); + }} > All 1 @@ -505,7 +521,8 @@ const FilterPopup = (props) => { + onChange={(event) => { + tracer.startSpan('FilterPopupSortChange').addEvent('salarySort', { status: event.target.checked }); setSearchOptions({ ...searchOptions, sort: { @@ -515,8 +532,8 @@ const FilterPopup = (props) => { status: event.target.checked, }, }, - }) - } + }); + }} id="salary" /> @@ -529,6 +546,7 @@ const FilterPopup = (props) => { { + tracer.startSpan('FilterPopupSortDirectionChange').addEvent('salarySortDirection', { desc: !searchOptions.sort.salary.desc }); setSearchOptions({ ...searchOptions, sort: { @@ -561,7 +579,8 @@ const FilterPopup = (props) => { + onChange={(event) => { + tracer.startSpan('FilterPopupSortChange').addEvent('durationSort', { status: event.target.checked }); setSearchOptions({ ...searchOptions, sort: { @@ -571,8 +590,8 @@ const FilterPopup = (props) => { status: event.target.checked, }, }, - }) - } + }); + }} id="duration" /> @@ -585,6 +604,7 @@ const FilterPopup = (props) => { { + tracer.startSpan('FilterPopupSortDirectionChange').addEvent('durationSortDirection', { desc: !searchOptions.sort.duration.desc }); setSearchOptions({ ...searchOptions, sort: { @@ -617,7 +637,8 @@ const FilterPopup = (props) => { + onChange={(event) => { + tracer.startSpan('FilterPopupSortChange').addEvent('ratingSort', { status: event.target.checked }); setSearchOptions({ ...searchOptions, sort: { @@ -627,8 +648,8 @@ const FilterPopup = (props) => { status: event.target.checked, }, }, - }) - } + }); + }} id="rating" /> @@ -641,6 +662,7 @@ const FilterPopup = (props) => { { + tracer.startSpan('FilterPopupSortDirectionChange').addEvent('ratingSortDirection', { desc: !searchOptions.sort.rating.desc }); setSearchOptions({ ...searchOptions, sort: { @@ -709,11 +731,14 @@ const MyJobs = (props) => { }); const setPopup = useContext(SetPopupContext); + const tracer = trace.getTracer('default'); + useEffect(() => { getData(); }, []); const getData = () => { + tracer.startSpan('getData').addEvent('searchOptions', { searchOptions }); let searchParams = [`myjobs=1`]; if (searchOptions.query !== "") { searchParams = [...searchParams, `q=${searchOptions.query}`]; @@ -758,13 +783,13 @@ const MyJobs = (props) => { }); searchParams = [...searchParams, ...asc, ...desc]; const queryString = searchParams.join("&"); - console.log(queryString); + tracer.startSpan('getDataQueryString').addEvent('queryString', { queryString }); let address = apiList.jobs; if (queryString !== "") { address = `${address}?${queryString}`; } - console.log(address); + tracer.startSpan('getDataAddress').addEvent('address', { address }); axios .get(address, { headers: { @@ -772,11 +797,11 @@ const MyJobs = (props) => { }, }) .then((response) => { - console.log(response.data); + tracer.startSpan('getDataSuccess').addEvent('response', { response: response.data }); setJobs(response.data); }) .catch((err) => { - console.log(err.response.data); + tracer.startSpan('getDataError').addEvent('error', { error: err.response.data }); setPopup({ open: true, severity: "error", @@ -808,12 +833,13 @@ const MyJobs = (props) => { + onChange={(event) => { + tracer.startSpan('searchQueryChange').addEvent('query', { query: event.target.value }); setSearchOptions({ ...searchOptions, query: event.target.value, - }) - } + }); + }} onKeyPress={(ev) => { if (ev.key === "Enter") { getData(); diff --git a/frontend/src/component/recruiter/Profile.js b/frontend/src/component/recruiter/Profile.js index ab42023..eccd743 100644 --- a/frontend/src/component/recruiter/Profile.js +++ b/frontend/src/component/recruiter/Profile.js @@ -16,6 +16,8 @@ import { SetPopupContext } from "../../App"; import apiList from "../../lib/apiList"; +import { diag } from '@opentelemetry/api'; + const useStyles = makeStyles((theme) => ({ body: { height: "inherit", @@ -42,6 +44,7 @@ const Profile = (props) => { const [phone, setPhone] = useState(""); const handleInput = (key, value) => { + diag.debug(`Updating profileDetails: key=${key}, value=${value}`); setProfileDetails({ ...profileDetails, [key]: value, @@ -49,10 +52,12 @@ const Profile = (props) => { }; useEffect(() => { + diag.debug('Component mounted, calling getData'); getData(); }, []); const getData = () => { + diag.debug('Fetching user data from API'); axios .get(apiList.user, { headers: { @@ -60,12 +65,12 @@ const Profile = (props) => { }, }) .then((response) => { - console.log(response.data); + diag.debug(`Received user data: ${JSON.stringify(response.data)}`); setProfileDetails(response.data); setPhone(response.data.contactNumber); }) .catch((err) => { - console.log(err.response.data); + diag.error(`Error fetching user data: ${err.response.data}`); setPopup({ open: true, severity: "error", @@ -75,6 +80,7 @@ const Profile = (props) => { }; const handleUpdate = () => { + diag.debug('Updating user profile'); let updatedDetails = { ...profileDetails, }; @@ -90,6 +96,7 @@ const Profile = (props) => { }; } + diag.debug(`Sending updated details to API: ${JSON.stringify(updatedDetails)}`); axios .put(apiList.user, updatedDetails, { headers: { @@ -97,6 +104,7 @@ const Profile = (props) => { }, }) .then((response) => { + diag.debug(`Profile updated successfully: ${response.data.message}`); setPopup({ open: true, severity: "success", @@ -105,12 +113,12 @@ const Profile = (props) => { getData(); }) .catch((err) => { + diag.error(`Error updating profile: ${err.response.data.message}`); setPopup({ open: true, severity: "error", message: err.response.data.message, }); - console.log(err.response); }); }; diff --git a/frontend/src/index.js b/frontend/src/index.js index ef2edf8..794d1e0 100644 --- a/frontend/src/index.js +++ b/frontend/src/index.js @@ -3,7 +3,9 @@ import ReactDOM from 'react-dom'; import './index.css'; import App from './App'; import reportWebVitals from './reportWebVitals'; +import { diag } from '@opentelemetry/api'; +diag.info('Rendering React application'); ReactDOM.render( @@ -11,7 +13,5 @@ ReactDOM.render( document.getElementById('root') ); -// If you want to start measuring performance in your app, pass a function -// to log results (for example: reportWebVitals(console.log)) -// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals +diag.info('Calling reportWebVitals'); reportWebVitals(); diff --git a/frontend/src/lib/EmailInput.js b/frontend/src/lib/EmailInput.js index deba1a6..d1a26fa 100644 --- a/frontend/src/lib/EmailInput.js +++ b/frontend/src/lib/EmailInput.js @@ -1,4 +1,5 @@ import { TextField } from "@material-ui/core"; +import { diag } from '@opentelemetry/api'; const EmailInput = (props) => { const { @@ -11,6 +12,8 @@ const EmailInput = (props) => { className, } = props; + diag.debug('EmailInput props', { label, value, required, className }); + return ( { onChange={onChange} helperText={inputErrorHandler.email.message} onBlur={(event) => { + diag.debug('onBlur event triggered', { value: event.target.value }); if (event.target.value === "") { + diag.debug('Input value is empty'); if (required) { + diag.debug('Email is required'); handleInputError("email", true, "Email is required"); } else { + diag.debug('Email is not required'); handleInputError("email", false, ""); } } else { const re = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; + diag.debug('Regex for email validation', { regex: re }); if (re.test(String(event.target.value).toLowerCase())) { + diag.debug('Email format is correct'); handleInputError("email", false, ""); } else { + diag.debug('Email format is incorrect'); handleInputError("email", true, "Incorrect email format"); } } diff --git a/frontend/src/lib/FileUploadInput.js b/frontend/src/lib/FileUploadInput.js index d564926..1ee1b5a 100644 --- a/frontend/src/lib/FileUploadInput.js +++ b/frontend/src/lib/FileUploadInput.js @@ -2,6 +2,7 @@ import { useState, useContext } from "react"; import { Grid, Button, TextField, LinearProgress } from "@material-ui/core"; import { CloudUpload } from "@material-ui/icons"; import Axios from "axios"; +import { diag } from '@opentelemetry/api'; import { SetPopupContext } from "../App"; @@ -14,7 +15,7 @@ const FileUploadInput = (props) => { const [uploadPercentage, setUploadPercentage] = useState(0); const handleUpload = () => { - console.log(file); + diag.debug('File to be uploaded:', file); const data = new FormData(); data.append("file", file); Axios.post(uploadTo, data, { @@ -22,15 +23,15 @@ const FileUploadInput = (props) => { "Content-Type": "multipart/form-data", }, onUploadProgress: (progressEvent) => { - setUploadPercentage( - parseInt( - Math.round((progressEvent.loaded * 100) / progressEvent.total) - ) + const percentage = parseInt( + Math.round((progressEvent.loaded * 100) / progressEvent.total) ); + diag.debug('Upload progress:', percentage); + setUploadPercentage(percentage); }, }) .then((response) => { - console.log(response.data); + diag.debug('Upload response data:', response.data); handleInput(identifier, response.data.url); setPopup({ open: true, @@ -39,7 +40,7 @@ const FileUploadInput = (props) => { }); }) .catch((err) => { - console.log(err.response); + diag.debug('Upload error response:', err.response); setPopup({ open: true, severity: "error", @@ -66,7 +67,7 @@ const FileUploadInput = (props) => { type="file" style={{ display: "none" }} onChange={(event) => { - console.log(event.target.files); + diag.debug('Selected file:', event.target.files[0]); setUploadPercentage(0); setFile(event.target.files[0]); }} diff --git a/frontend/src/lib/MessagePopup.js b/frontend/src/lib/MessagePopup.js index cab6b30..93ce8b5 100644 --- a/frontend/src/lib/MessagePopup.js +++ b/frontend/src/lib/MessagePopup.js @@ -1,13 +1,18 @@ import { Snackbar, Slide } from "@material-ui/core"; import { Alert } from "@material-ui/lab"; +import { diag } from '@opentelemetry/api'; const MessagePopup = (props) => { const handleClose = (event, reason) => { + diag.debug('handleClose called with reason:', reason); if (reason === "clickaway") { + diag.debug('handleClose early return due to clickaway'); return; } + diag.debug('Setting props.setOpen to false'); props.setOpen(false); }; + diag.debug('Rendering Snackbar with props:', props); return ( diff --git a/frontend/src/lib/PasswordInput.js b/frontend/src/lib/PasswordInput.js index 00c97d3..aa06965 100644 --- a/frontend/src/lib/PasswordInput.js +++ b/frontend/src/lib/PasswordInput.js @@ -1,4 +1,5 @@ import { useState } from "react"; +import { diag } from '@opentelemetry/api'; import { FormControl, InputLabel, @@ -12,13 +13,16 @@ import VisibilityOff from "@material-ui/icons/VisibilityOff"; const PasswordInput = (props) => { const [showPassword, setShowPassword] = useState(false); + diag.debug('Initial showPassword state: ', showPassword); const handleShowPassword = () => { setShowPassword(!showPassword); + diag.debug('Toggled showPassword state: ', !showPassword); }; const handleMouseDownPassword = (event) => { event.preventDefault(); + diag.debug('Mouse down event prevented on password input'); }; return ( @@ -42,7 +46,10 @@ const PasswordInput = (props) => { } value={props.value} - onChange={(event) => props.onChange(event)} + onChange={(event) => { + diag.debug('Password input value changed: ', event.target.value); + props.onChange(event); + }} labelWidth={props.labelWidth ? props.labelWidth : 70} className={props.className} onBlur={props.onBlur ? props.onBlur : null} diff --git a/frontend/src/lib/apiList.js b/frontend/src/lib/apiList.js index f2eb26b..e2bd7c3 100644 --- a/frontend/src/lib/apiList.js +++ b/frontend/src/lib/apiList.js @@ -1,4 +1,7 @@ +import { diag } from '@opentelemetry/api'; + export const server = "http://localhost:4444"; +diag.debug(`Server URL: ${server}`); const apiList = { login: `${server}/auth/login`, @@ -12,4 +15,6 @@ const apiList = { applicants: `${server}/api/applicants`, }; +diag.debug(`API List: ${JSON.stringify(apiList)}`); + export default apiList; diff --git a/frontend/src/lib/isAuth.js b/frontend/src/lib/isAuth.js index 1684d2f..ae95c8e 100644 --- a/frontend/src/lib/isAuth.js +++ b/frontend/src/lib/isAuth.js @@ -1,9 +1,15 @@ +import { diag } from '@opentelemetry/api'; + const isAuth = () => { - return localStorage.getItem("token"); + const token = localStorage.getItem("token"); + diag.debug('Retrieved token from localStorage', { token }); + return token; }; export const userType = () => { - return localStorage.getItem("type"); + const type = localStorage.getItem("type"); + diag.debug('Retrieved user type from localStorage', { type }); + return type; }; export default isAuth; diff --git a/frontend/src/reportWebVitals.js b/frontend/src/reportWebVitals.js index 5253d3a..7fc1848 100644 --- a/frontend/src/reportWebVitals.js +++ b/frontend/src/reportWebVitals.js @@ -1,12 +1,24 @@ +const { diag } = require('@opentelemetry/api'); + const reportWebVitals = onPerfEntry => { + diag.debug('reportWebVitals function called with onPerfEntry:', onPerfEntry); if (onPerfEntry && onPerfEntry instanceof Function) { + diag.debug('onPerfEntry is a function, proceeding to import web-vitals'); import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { + diag.debug('web-vitals module imported successfully'); + diag.debug('Calling getCLS with onPerfEntry'); getCLS(onPerfEntry); + diag.debug('Calling getFID with onPerfEntry'); getFID(onPerfEntry); + diag.debug('Calling getFCP with onPerfEntry'); getFCP(onPerfEntry); + diag.debug('Calling getLCP with onPerfEntry'); getLCP(onPerfEntry); + diag.debug('Calling getTTFB with onPerfEntry'); getTTFB(onPerfEntry); }); + } else { + diag.debug('onPerfEntry is not a function or is undefined'); } }; diff --git a/frontend/src/setupTests.js b/frontend/src/setupTests.js index 8f2609b..613d03f 100644 --- a/frontend/src/setupTests.js +++ b/frontend/src/setupTests.js @@ -1,5 +1,10 @@ +```javascript // jest-dom adds custom jest matchers for asserting on DOM nodes. // allows you to do things like: // expect(element).toHaveTextContent(/react/i) // learn more: https://github.com/testing-library/jest-dom import '@testing-library/jest-dom'; +import { diag } from '@opentelemetry/api'; + +diag.info('jest-dom has been imported for custom jest matchers.'); +```