Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions controllers/userStatus.js
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,31 @@ const updateUserStatusController = async (req, res, next) => {
}
};

const syncUserStatus = async (req, res, next) => {
try {
await updateAllUserStatus(req, res, next);
const usersData = await getTaskBasedUsersStatus(req, res, next);
Comment on lines +245 to +246
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we use this two fx as middleware?
also, these functions seem to be returning res, which will result in an error, because in that case we are calling res/json 3 times.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO logic that should be part of controllers should not be used as middleware

also, these functions seem to be returning res, which will result in an error, because in that case we are calling res/json 3 times.

Appologies did not understand


if (!usersData?.data?.users || usersData.data.users.length === 0) {
const errorMessage = "Error: Users data is not in the expected format or no users found";
logger.error(errorMessage);
return res.boom.badImplementation(errorMessage);
}

const data = await userStatusModel.batchUpdateUsersStatus(usersData.data.users);

return res.json({
message: "Users status updated successfully.",
data,
});
} catch (error) {
logger.error(error.message);
return res.status(500).json({
message: "An internal server error occurred",
});
}
};

module.exports = {
deleteUserStatus,
getUserStatus,
Expand All @@ -250,4 +275,5 @@ module.exports = {
getUserStatusControllers,
batchUpdateUsersStatus,
updateUserStatusController,
syncUserStatus,
};
3 changes: 3 additions & 0 deletions routes/userStatus.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ const {
batchUpdateUsersStatus,
getUserStatusControllers,
updateUserStatusController,
syncUserStatus,
} = require("../controllers/userStatus");
const router = express.Router();
const authenticate = require("../middlewares/authenticate");
Expand All @@ -24,6 +25,8 @@ const { Services } = require("../constants/bot");
router.get("/", validateGetQueryParams, getUserStatusControllers);
router.get("/self", authenticate, getUserStatus);
router.get("/:userId", getUserStatus);
router.patch("/sync", authorizeAndAuthenticate([ROLES.SUPERUSER], [Services.CRON_JOB_HANDLER]), syncUserStatus);

Check failure

Code scanning / CodeQL

Missing rate limiting

This route handler performs [authorization](1), but is not rate-limited. This route handler performs [authorization](2), but is not rate-limited. This route handler performs [authorization](3), but is not rate-limited. This route handler performs [authorization](4), but is not rate-limited. This route handler performs [authorization](5), but is not rate-limited. This route handler performs [authorization](6), but is not rate-limited. This route handler performs [authorization](7), but is not rate-limited.

router.patch("/self", authenticate, validateUserStatus, updateUserStatusController);
router.patch("/update", authorizeAndAuthenticate([ROLES.SUPERUSER], [Services.CRON_JOB_HANDLER]), updateAllUserStatus);
router.patch(
Expand Down
64 changes: 64 additions & 0 deletions test/integration/userStatus.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ const { userState } = require("../../constants/userStatus");
const cookieName = config.get("userToken.cookieName");
const userStatusModel = require("../../models/userStatus");
const { convertTimestampToUTCStartOrEndOfDay } = require("../../utils/time");
const bot = require("../utils/generateBotToken");
const { CRON_JOB_HANDLER } = require("../../constants/bot");
const userStatusController = require("../../controllers/userStatus");

chai.use(chaiHttp);

Expand All @@ -32,13 +35,15 @@ describe("UserStatus", function () {
let superUserId;
let superUserAuthToken;
let userId = "";
let cronjobJwtToken;

beforeEach(async function () {
userId = await addUser();
jwt = authService.generateAuthToken({ userId });
superUserId = await addUser(superUser);
superUserAuthToken = authService.generateAuthToken({ userId: superUserId });
await updateUserStatus(userId, userStatusDataForNewUser);
cronjobJwtToken = bot.generateCronJobToken({ name: CRON_JOB_HANDLER });
});

afterEach(async function () {
Expand Down Expand Up @@ -275,6 +280,65 @@ describe("UserStatus", function () {
});
});

describe("PATCH /users/status/sync", function () {
afterEach(function () {
sinon.restore();
});

it("should return all user statuses", async function () {
const fakeResponse = {
message: "All User Status updated successfully.",
data: {
usersCount: 5,
oooUsersAltered: 0,
oooUsersUnaltered: 0,
nonOooUsersAltered: 3,
nonOooUsersUnaltered: 0,
},
};

const patchStub = sinon.stub().returns({ status: 200, body: fakeResponse });
sinon.stub(chai, "request").returns({ patch: patchStub });

const res = await chai.request(app).patch(`/users/status/sync`).set("Authorization", `Bearer ${cronjobJwtToken}`);

expect(res).to.have.status(200);
expect(res.body.message).to.equal(fakeResponse.message);
expect(res.body.data).to.deep.equal(fakeResponse.data);
});

it("should return 500 error with appropriate message when no users found", async function () {
const updateAllUserStatusStub = sinon.stub().resolves();
const getTaskBasedUsersStatusStub = sinon.stub().resolves({ data: { users: [] } });
sinon.replace(userStatusController, "updateAllUserStatus", updateAllUserStatusStub);
sinon.replace(userStatusController, "getTaskBasedUsersStatus", getTaskBasedUsersStatusStub);

const patchStub = sinon.stub().returns({
status: 500,
body: { message: "Error: Users data is not in the expected format or no users found" },
});
sinon.stub(chai, "request").returns({ patch: patchStub });

const res = await chai.request(app).patch("/users/status/sync").set("Authorization", `Bearer ${cronjobJwtToken}`);

expect(res).to.have.status(500);
expect(res.body.message).to.equal("Error: Users data is not in the expected format or no users found");
});

it("should return 500 error with appropriate message", async function () {
const updateAllUserStatusStub = sinon.stub().rejects(new Error("Failed to update user statuses"));
sinon.replace(userStatusController, "updateAllUserStatus", updateAllUserStatusStub);

const patchStub = sinon.stub().returns({ status: 500, body: { message: "An internal server error occurred" } });
sinon.stub(chai, "request").returns({ patch: patchStub });

const res = await chai.request(app).patch("/users/status/sync").set("Authorization", `Bearer ${cronjobJwtToken}`);

expect(res).to.have.status(500);
expect(res.body.message).to.equal("An internal server error occurred");
});
});

describe("PATCH /users/status/:userid", function () {
let testUserId;
let testUserJwt;
Expand Down