Skip to content

Added api for updating discord group role in firestore and discord #2406

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 5 commits into
base: develop
Choose a base branch
from

Conversation

Akshat187
Copy link
Contributor

@Akshat187 Akshat187 commented Apr 29, 2025

Date: 29-04-2025

Developer Name: Akshat bhatnagar


Issue Ticket Number #2403

Description

Added an API to update the role name and description in discord group.

Documentation Updated?

  • Yes
  • No

Under Feature Flag

  • Yes
  • No

Database Changes

  • Yes
  • No

Breaking Changes

  • Yes
  • No

Development Tested?

  • Yes
  • No

Screenshots

Added screenshot of working proof

{F1950A6C-2AB0-4CB1-81D9-88FACCF5A824}

Post updating the description of discord group
{7B0C29A5-92A7-463D-BA2C-CE1AA9DC062D}

Test Coverage

Additional Notes

Copy link

coderabbitai bot commented Apr 29, 2025

Important

Review skipped

Auto incremental reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Summary by CodeRabbit

  • New Features
    • Added the ability to update group roles, including role name and description, via a PATCH endpoint.
  • Bug Fixes
    • Improved error handling and validation for group role updates.
  • Tests
    • Introduced comprehensive unit tests for group role editing, covering validation, success, and failure scenarios.

Walkthrough

A new feature was introduced to allow editing of Discord group roles via a controller function, editGroupRoles. This function supports updating a group's role name and description, with validation and error handling. Supporting changes include a new service function for updating Discord roles through the Discord API, new PATCH routes for the group role update endpoint (with and without authentication middleware), and comprehensive unit tests for the controller's behavior across various scenarios.

Changes

File(s) Change Summary
controllers/discordactions.js Added the editGroupRoles controller function to handle PATCH requests for updating group role names and descriptions. Implements input validation, existence checks, calls to Discord and Firestore update functions, error handling, and logging. Exported the new function.
routes/discordactions.js Imported editGroupRoles and added two PATCH /groups/:groupId routes: one secured with authentication and SUPERUSER authorization middleware, and one without middleware. Both routes use the new controller function.
services/discordService.js Added updateDiscordGroupRole, an async function that sends PATCH requests to the Discord API to update a role's name and description. Handles API response, error logging, and returns a status message. Exported the new function.
test/unit/models/discordactions.test.js Added a test suite for editGroupRoles, covering validation errors, not-found errors, successful updates, and partial update failures. Used sinon to stub dependencies and verify correct controller behavior. No changes to existing exported entities.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant Router
    participant Controller as editGroupRoles
    participant Model as discordRolesModel
    participant Service as discordService

    Client->>Router: PATCH /groups/:groupId (with body)
    Router->>Controller: editGroupRoles(req, res)
    Controller->>Model: isGroupRoleExists(groupId)
    alt Group role exists
        alt roleName to update
            Controller->>Service: updateDiscordGroupRole(roleId, roleName, description)
            Service-->>Controller: Success/Failure
        end
        alt description to update
            Controller->>Model: updateGroupRole(groupId, {description})
            Model-->>Controller: Success/Failure
        end
        Controller-->>Client: 200 Success or 500 Partial Failure
    else Group role not found
        Controller-->>Client: 404 Not Found
    end
Loading

Poem

In the warren where code roles hop and play,
We patched up the groups in a Discord way.
With names and descriptions, now easy to mend,
The controller and service each lend a hand to send.
Tests keep us safe, bugs at bay—
What a fine, fluffy update for group roles today!
🐇✨


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 12

📜 Review details

Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6a4319c and 3a81f02.

📒 Files selected for processing (4)
  • controllers/discordactions.js (2 hunks)
  • routes/discordactions.js (2 hunks)
  • services/discordService.js (2 hunks)
  • test/unit/models/discordactions.test.js (2 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
services/discordService.js (5)
controllers/discordactions.js (2)
  • authToken (574-577)
  • DISCORD_BASE_URL (22-22)
utils/discord-actions.js (6)
  • authToken (11-14)
  • authToken (18-18)
  • authToken (47-50)
  • generateAuthTokenForCloudflare (8-16)
  • DISCORD_BASE_URL (4-4)
  • error (61-61)
controllers/external-accounts.js (1)
  • logger (9-9)
models/events.js (1)
  • logger (4-4)
controllers/events.js (1)
  • logger (6-6)
🪛 GitHub Check: CodeQL
routes/discordactions.js

[failure] 38-38: Missing rate limiting
This route handler performs authorization, but is not rate-limited.
This route handler performs authorization, but is not rate-limited.
This route handler performs authorization, but is not rate-limited.

🪛 GitHub Actions: Tests
controllers/discordactions.js

[warning] 439-439: ESLint warning: Variable Assigned to Object Injection Sink (security/detect-object-injection)


[warning] 455-455: ESLint warning: Generic Object Injection Sink (security/detect-object-injection)

test/unit/models/discordactions.test.js

[warning] 720-859: ESLint warning: Variable Assigned and Generic Object Injection Sink (security/detect-object-injection)


[error] 191-191: Test failure: AssertionError: expected false to equal true at test/unit/models/discordactions.test.js:191:45 in 'discordactions editGroupRoles should update roleName and description successfully'

🪛 Biome (1.9.4)
test/unit/models/discordactions.test.js

[error] 104-104: Declare variables separately

Unsafe fix: Break out into multiple declarations

(lint/style/useSingleVarDeclarator)

Comment on lines 76 to 88
const response = await (
await fetch(`${DISCORD_BASE_URL}/roles/${roleId})`, {
method: "PATCH",
body: JSON.stringify({
roleName,
description,
}),
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${authToken}`,
},
})
).json();
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix malformed URL & response-handling logic

  1. ${DISCORD_BASE_URL}/roles/${roleId}) has an unmatched right-paren, resulting in a 404 from Cloudflare/Discord.
  2. await (await fetch()).json() will throw on 204 No Content.
  3. response.status is read from the parsed JSON rather than the original Response object, so it is always undefined.

A safe, minimal patch:

-    const response = await (
-      await fetch(`${DISCORD_BASE_URL}/roles/${roleId})`, {
-        method: "PATCH",
-        body: JSON.stringify({
-          roleName,
-          description,
-        }),
-        headers: {
-          "Content-Type": "application/json",
-          Authorization: `Bearer ${authToken}`,
-        },
-      })
-    ).json();
-
-    if (response.status === 204) {
+    const fetchResponse = await fetch(`${DISCORD_BASE_URL}/roles/${roleId}`, {
+      method: "PATCH",
+      body: JSON.stringify({ roleName, description }),
+      headers: {
+        "Content-Type": "application/json",
+        Authorization: `Bearer ${authToken}`,
+      },
+    });
+
+    if (fetchResponse.status === 204) {
       return {
         success: true,
         message: "Role updated  successfully",
       };
     }
-    return {
-      success: false,
-      message: response.message || "Failed to update role in Discord",
-    };
+    const parsed = await fetchResponse.json().catch(() => ({}));
+    return {
+      success: false,
+      message: parsed.message || "Failed to update role in Discord",
+    };

This removes the stray ) and robustly handles the 204 body-less response.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const response = await (
await fetch(`${DISCORD_BASE_URL}/roles/${roleId})`, {
method: "PATCH",
body: JSON.stringify({
roleName,
description,
}),
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${authToken}`,
},
})
).json();
const fetchResponse = await fetch(`${DISCORD_BASE_URL}/roles/${roleId}`, {
method: "PATCH",
body: JSON.stringify({ roleName, description }),
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${authToken}`,
},
});
if (fetchResponse.status === 204) {
return {
success: true,
message: "Role updated successfully",
};
}
const parsed = await fetchResponse.json().catch(() => ({}));
return {
success: false,
message: parsed.message || "Failed to update role in Discord",
};

Comment on lines +101 to +112
if (updateRequest.roleName) {
const discordUpdateResponse = await discordServices.updateDiscordGroupRole(
roleData.roleid,
updateRequest.roleName,
updateRequest.description
);
if (!discordUpdateResponse.success) {
discordUpdateSuccess = false;
logger.error(`Failed to update role name in Discord for groupId: ${groupId}`);
}
}

Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Handle Discord API failures explicitly

discordUpdateSuccess is set to false only when the service returns { success:false }, but the preceding await can throw (network, 5xx).
Wrap the call in its own try…catch to avoid an unhandled exception that would skip Firestore rollback logic.

Comment on lines +93 to +123
if (roleName) {
updateRequest.roleName = roleName;
}
if (description) {
updateRequest.description = description;
}

let discordUpdateSuccess = true;
if (updateRequest.roleName) {
const discordUpdateResponse = await discordServices.updateDiscordGroupRole(
roleData.roleid,
updateRequest.roleName,
updateRequest.description
);
if (!discordUpdateResponse.success) {
discordUpdateSuccess = false;
logger.error(`Failed to update role name in Discord for groupId: ${groupId}`);
}
}

let firestoreUpdateSuccess = true;
if (updateRequest.description) {
try {
await discordRolesModel.updateGroupRole(groupId, {
description: updateRequest.description,
});
} catch (error) {
firestoreUpdateSuccess = false;
logger.error(`Failed to update description in Firestore for groupId: ${groupId}`);
}
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Firestore update arguments swapped & rolename not persisted

discordRolesModel.updateGroupRole accepts (dataToUpdate, docId) (see call at L511-515 in the same file), but here the parameters are reversed and only description is saved.
This leaves Firestore out-of-sync when the role name changes.

-        await discordRolesModel.updateGroupRole(groupId, {
-          description: updateRequest.description,
-        });
+        await discordRolesModel.updateGroupRole(
+          {
+            ...(updateRequest.roleName && { rolename: updateRequest.roleName }),
+            ...(updateRequest.description && { description: updateRequest.description }),
+          },
+          groupId
+        );

Fixing the order and including rolename keeps the DB consistent with Discord.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (roleName) {
updateRequest.roleName = roleName;
}
if (description) {
updateRequest.description = description;
}
let discordUpdateSuccess = true;
if (updateRequest.roleName) {
const discordUpdateResponse = await discordServices.updateDiscordGroupRole(
roleData.roleid,
updateRequest.roleName,
updateRequest.description
);
if (!discordUpdateResponse.success) {
discordUpdateSuccess = false;
logger.error(`Failed to update role name in Discord for groupId: ${groupId}`);
}
}
let firestoreUpdateSuccess = true;
if (updateRequest.description) {
try {
await discordRolesModel.updateGroupRole(groupId, {
description: updateRequest.description,
});
} catch (error) {
firestoreUpdateSuccess = false;
logger.error(`Failed to update description in Firestore for groupId: ${groupId}`);
}
}
let firestoreUpdateSuccess = true;
if (updateRequest.description) {
try {
await discordRolesModel.updateGroupRole(
{
...(updateRequest.roleName && { rolename: updateRequest.roleName }),
...(updateRequest.description && { description: updateRequest.description }),
},
groupId
);
} catch (error) {
firestoreUpdateSuccess = false;
logger.error(`Failed to update description in Firestore for groupId: ${groupId}`);
}
}

Comment on lines 38 to 40
router.patch("/groups/:groupId", authenticate, authorizeRoles([SUPERUSER]), editGroupRoles);
router.patch("/groups/:groupId", editGroupRoles);
router.get("/groups", authenticate, checkIsVerifiedDiscord, validateLazyLoadingParams, getPaginatedAllGroupRoles);
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Unprotected duplicate PATCH route bypasses authentication/authorization

router.patch("/groups/:groupId", editGroupRoles); (L39-L40) is registered after the secured route, but Express will still match it if the first route short-circuits (e.g., authenticate sends 401 without next()).
Attackers can therefore hit the second route and modify group roles without credentials.

-router.patch("/groups/:groupId", editGroupRoles);
+// Duplicate unsecured route removed – all updates must pass auth & SUPERUSER checks

Delete the unsecured definition or add the same middleware chain.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
router.patch("/groups/:groupId", authenticate, authorizeRoles([SUPERUSER]), editGroupRoles);
router.patch("/groups/:groupId", editGroupRoles);
router.get("/groups", authenticate, checkIsVerifiedDiscord, validateLazyLoadingParams, getPaginatedAllGroupRoles);
router.patch("/groups/:groupId", authenticate, authorizeRoles([SUPERUSER]), editGroupRoles);
// Duplicate unsecured route removed – all updates must pass auth & SUPERUSER checks
router.get("/groups", authenticate, checkIsVerifiedDiscord, validateLazyLoadingParams, getPaginatedAllGroupRoles);
🧰 Tools
🪛 GitHub Check: CodeQL

[failure] 38-38: Missing rate limiting
This route handler performs authorization, but is not rate-limited.
This route handler performs authorization, but is not rate-limited.
This route handler performs authorization, but is not rate-limited.

Comment on lines +130 to +136
it("should return 400 if neither roleName nor description is provided", async function () {
await editGroupRoles(req, res);

expect(res.boom.badRequest.calledWith("At least one field (roleName or description) must be provided")).to.equal(
true
);
});
Copy link

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

Add test for combined field validation.

The test cases cover individual validation scenarios, but it would be valuable to test the case where both fields are provided but one is invalid.

Consider adding a test case like:

it("should return 400 if roleName is valid but description exceeds limit", async function () {
  req.body = {
    roleName: "valid-name",
    description: "a".repeat(201)
  };
  
  await editGroupRoles(req, res);
  
  expect(res.boom.badRequest.calledWith("Description must not exceed 200 characters")).to.equal(true);
});

Comment on lines +199 to +216
it("should return 500 if Discord update fails", async function () {
req.body.roleName = "new-role-name";
sinon.stub(discordRolesModel, "isGroupRoleExists").resolves({
roleExists: true,
existingRoles: {
data: () => ({
roleid: "12345",
}),
},
});
sinon.stub(discordServices, "updateDiscordGroupRole").resolves({
success: false,
});

await editGroupRoles(req, res);

expect(res.boom.badImplementation.calledWith("Partial update failed. Check logs for details.")).to.equal(true);
});
Copy link

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

Verify error handling consistency in test.

The test is checking for a 500 response on Discord update failure, but it would be good to ensure the controller doesn't attempt to update Firestore after a Discord failure.

Add an additional assertion to verify that updateGroupRole is not called when the Discord update fails:

  await editGroupRoles(req, res);

  expect(res.boom.badImplementation.calledWith("Partial update failed. Check logs for details.")).to.equal(true);
+ expect(discordRolesModel.updateGroupRole.called).to.equal(false);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
it("should return 500 if Discord update fails", async function () {
req.body.roleName = "new-role-name";
sinon.stub(discordRolesModel, "isGroupRoleExists").resolves({
roleExists: true,
existingRoles: {
data: () => ({
roleid: "12345",
}),
},
});
sinon.stub(discordServices, "updateDiscordGroupRole").resolves({
success: false,
});
await editGroupRoles(req, res);
expect(res.boom.badImplementation.calledWith("Partial update failed. Check logs for details.")).to.equal(true);
});
it("should return 500 if Discord update fails", async function () {
req.body.roleName = "new-role-name";
sinon.stub(discordRolesModel, "isGroupRoleExists").resolves({
roleExists: true,
existingRoles: {
data: () => ({
roleid: "12345",
}),
},
});
sinon.stub(discordServices, "updateDiscordGroupRole").resolves({
success: false,
});
await editGroupRoles(req, res);
expect(res.boom.badImplementation.calledWith("Partial update failed. Check logs for details.")).to.equal(true);
expect(discordRolesModel.updateGroupRole.called).to.equal(false);
});

Comment on lines +58 to +60
const { editGroupRoles } = require("../../../controllers/discordactions");
const discordRolesModel = require("../../../models/discordactions");
const discordServices = require("../../../services/discordService");
Copy link

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

Consider prefixing imported module with original name.

The file imports both discordService and discordServices which could lead to confusion.

  const { editGroupRoles } = require("../../../controllers/discordactions");
  const discordRolesModel = require("../../../models/discordactions");
- const discordServices = require("../../../services/discordService");
+ const discordServices = require("../../../services/discordService"); // Ensure this is consistent with line 16

Consider using the same variable name (either discordService or discordServices) for consistency.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const { editGroupRoles } = require("../../../controllers/discordactions");
const discordRolesModel = require("../../../models/discordactions");
const discordServices = require("../../../services/discordService");
// test/unit/models/discordactions.test.js
const { editGroupRoles } = require("../../../controllers/discordactions");
const discordRolesModel = require("../../../models/discordactions");
const discordServices = require("../../../services/discordService"); // Ensure this is consistent with line 16

Comment on lines +127 to +128
sinon.restore();
});
Copy link

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

Consider using more precise sinon cleanup method.

Using sinon.restore() restores all stubs, spies, and mocks. For better isolation between tests, use the more specific sinon.resetHistory() to only reset call history without removing stubs.

-      sinon.restore();
+      sinon.resetHistory();

Then use afterEach(sinon.restore) at the suite level to fully clean up after all tests in the suite have completed.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
sinon.restore();
});
sinon.resetHistory();
});

Comment on lines 171 to 176
roleExists: true,
existingRoles: {
data: () => ({
roleid: "12345",
}),
},
Copy link

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

Improve test stub structure for better readability.

The nested structure for the stub return value is complex and could be simplified for better readability.

  sinon.stub(discordRolesModel, "isGroupRoleExists").resolves({
    roleExists: true,
-    existingRoles: {
-      data: () => ({
-        roleid: "12345",
-      }),
-    },
+    existingRoles: { 
+      data() { 
+        return { roleid: "12345" }; 
+      } 
+    },
  });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
roleExists: true,
existingRoles: {
data: () => ({
roleid: "12345",
}),
},
sinon.stub(discordRolesModel, "isGroupRoleExists").resolves({
roleExists: true,
existingRoles: {
data() {
return { roleid: "12345" };
}
},
});

Comment on lines 185 to 190
expect(discordServices.updateDiscordGroupRole.calledWith("12345", "new-role-name")).to.equal(true);
expect(
discordRolesModel.updateGroupRole.calledWith("group1", {
description: "Updated description",
})
).to.equal(true);
Copy link

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

Improve test assertion clarity by using sinon.assert.calledWith.

Sinon provides dedicated assertion methods that offer clearer error messages when tests fail.

-  expect(discordServices.updateDiscordGroupRole.calledWith("12345", "new-role-name")).to.equal(true);
-  expect(
-    discordRolesModel.updateGroupRole.calledWith("group1", {
-      description: "Updated description",
-    })
-  ).to.equal(true);
+  sinon.assert.calledWith(discordServices.updateDiscordGroupRole, "12345", "new-role-name");
+  sinon.assert.calledWith(discordRolesModel.updateGroupRole, "group1", {
+    description: "Updated description",
+  });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
expect(discordServices.updateDiscordGroupRole.calledWith("12345", "new-role-name")).to.equal(true);
expect(
discordRolesModel.updateGroupRole.calledWith("group1", {
description: "Updated description",
})
).to.equal(true);
sinon.assert.calledWith(discordServices.updateDiscordGroupRole, "12345", "new-role-name");
sinon.assert.calledWith(discordRolesModel.updateGroupRole, "group1", {
description: "Updated description",
});

@@ -34,6 +35,7 @@
const router = express.Router();

router.post("/groups", authenticate, checkIsVerifiedDiscord, validateGroupRoleBody, createGroupRole);
router.patch("/groups/:groupId", authenticate, authorizeRoles([SUPERUSER]), editGroupRoles);

Check failure

Code scanning / CodeQL

Missing rate limiting High

This route handler performs
authorization
, but is not rate-limited.
This route handler performs
authorization
, but is not rate-limited.
This route handler performs
authorization
, but is not rate-limited.
@vikhyat187 vikhyat187 changed the title added api path Added api for updating discord group role in firestore and discord Apr 30, 2025
Copy link
Member

@AnujChhikara AnujChhikara left a comment

Choose a reason for hiding this comment

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

  • Test coverage is missing — please add relevant tests.

  • A working video demo would be more helpful than just a screenshot.

Copy link
Member

@AnujChhikara AnujChhikara left a comment

Choose a reason for hiding this comment

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

  • Test coverage is missing — please add relevant tests.

  • A working video demo would be more helpful than just a screenshot.


if (roleName && (roleName.length < 3 || roleName.length > 50)) {
return res.boom.badRequest("Role name must be between 3 and 50 characters");
}
Copy link
Member

Choose a reason for hiding this comment

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

  • Why are we performing these validations in the controller?
  • Consider moving them to the validator layer so all incoming data is validated consistently before reaching the controller.

const { dev } = req.query;
const { roleName, description } = req.body;

if (!dev === "true") {
Copy link

Choose a reason for hiding this comment

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

The feature flag check contains a logical error. The expression !dev === "true" will always evaluate to false because !dev produces a boolean value which cannot equal the string "true".

The correct condition should be dev !== "true" to properly check if the feature flag is not enabled.

// Current:
if (!dev === "true") { ... }

// Corrected:
if (dev !== "true") { ... }
Suggested change
if (!dev === "true") {
if (dev !== "true") {

Spotted by Diamond

Is this helpful? React 👍 or 👎 to let us know.

Comment on lines +107 to +111
const discordUpdateResponse = await discordServices.updateDiscordGroupRole(
roleData.roleid,
updateRequest.roleName,
updateRequest.description
);
Copy link

Choose a reason for hiding this comment

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

The dev parameter from the request query is not being passed to the updateDiscordGroupRole function, though the function signature includes it as a fourth parameter. This will result in the dev flag being undefined when making the Discord API call. Consider passing req.query.dev as the fourth argument to ensure the feature flag is properly propagated to the Discord service.

Suggested change
const discordUpdateResponse = await discordServices.updateDiscordGroupRole(
roleData.roleid,
updateRequest.roleName,
updateRequest.description
);
const discordUpdateResponse = await discordServices.updateDiscordGroupRole(
roleData.roleid,
updateRequest.roleName,
updateRequest.description,
req.query.dev
);

Spotted by Diamond

Is this helpful? React 👍 or 👎 to let us know.

Comment on lines +121 to +125
boom: {
badRequest: sinon.stub(),
notFound: sinon.stub(),
badImplementation: sinon.stub(),
},
Copy link

Choose a reason for hiding this comment

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

The test setup is missing a stub for res.boom.success and res.boom.notImplemented, which are used in the controller. The success method is called when the update is successful, and notImplemented is used in the feature flag check. Without these stubs, tests will fail when testing both the happy path and feature flag functionality. Consider adding:

res = {
  boom: {
    badRequest: sinon.stub(),
    notFound: sinon.stub(),
    badImplementation: sinon.stub(),
    success: sinon.stub(),
    notImplemented: sinon.stub()
  },
  // ...
};
Suggested change
boom: {
badRequest: sinon.stub(),
notFound: sinon.stub(),
badImplementation: sinon.stub(),
},
boom: {
badRequest: sinon.stub(),
notFound: sinon.stub(),
badImplementation: sinon.stub(),
success: sinon.stub(),
notImplemented: sinon.stub(),
},

Spotted by Diamond

Is this helpful? React 👍 or 👎 to let us know.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants