Skip to content

Feature/group role edit feature for superusers #299

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

mdansarijaved
Copy link

@mdansarijaved mdansarijaved commented Jan 26, 2025

Date: 29th Aprill

Developer Name:
akshat

Issue Ticket Number

Real-Dev-Squad/website-dashboard#898

Description

Documentation Updated?

  • Yes
  • No

Under Feature Flag

  • Yes
  • No

Database Changes

  • Yes
  • No

Breaking Changes

  • Yes
  • No

Development Tested?

  • Yes
  • No

Screenshots

Screenshot 1

image

Test Coverage

Screenshot 1

image

Additional Notes

Copy link

coderabbitai bot commented Apr 30, 2025

Summary by CodeRabbit

  • New Features
    • Added the ability to update Discord guild roles via a new PATCH API endpoint.
  • Bug Fixes
    • Improved response messages for role update operations.
  • Tests
    • Introduced comprehensive unit tests for the new role update handler and utility function.
  • Documentation
    • Updated type definitions to include the new role update structure.

Walkthrough

A new feature for editing Discord guild roles was implemented. This includes the introduction of a handler to process PATCH requests for updating roles, a utility function to perform the update via the Discord API, and a new response constant for successful updates. Type definitions were expanded to include the update payload structure. The API's main router was updated to support the new PATCH endpoint. Comprehensive unit tests were added for both the handler and the utility function to ensure correct behavior across various scenarios.

Changes

File(s) Change Summary
src/constants/responses.ts Added new response constant ROLE_UPDATED for successful role updates.
src/controllers/editGuildRolesHandler.ts Introduced editGuildRoleHandler function to handle PATCH requests for updating guild roles.
src/index.ts Imported the new handler and registered a PATCH route at /roles/:roleId for editing roles.
src/typeDefinitions/discordMessage.types.d.ts Added updateRole interface defining the structure for role update requests.
src/utils/editGroupRole.ts Added editGuildRole async function to perform PATCH requests to Discord API for role updates.
tests/unit/handlers/editGuildRoleHandler.test.ts New test suite for editGuildRoleHandler, covering error cases and successful updates.
tests/unit/utils/editGuildRole.test.ts New test suite for editGuildRole, verifying API interaction and error handling.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant API (editGuildRoleHandler)
    participant AuthVerifier
    participant DiscordAPI

    Client->>API (editGuildRoleHandler): PATCH /roles/:roleId (with headers, body)
    API->>API: Validate headers and parameters
    API->>AuthVerifier: Verify authorization token
    AuthVerifier-->>API: Token valid/invalid
    API->>DiscordAPI: PATCH /guilds/:guildId/roles/:roleId (with new name, mentionable, reason)
    DiscordAPI-->>API: Success or failure response
    API-->>Client: 204 No Content (on success) or error response
Loading

Poem

🐇
A patch for roles, a handler new,
Updates with care, as rabbits do!
With tests to check each twist and turn,
And constants clear for all to learn.
Discord roles now change with grace—
CodeRabbit hops to every place!

✨ Finishing Touches
  • 📝 Generate Docstrings

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: 11

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between f96ec15 and 4c51164.

📒 Files selected for processing (7)
  • src/constants/responses.ts (1 hunks)
  • src/controllers/editGuildRolesHandler.ts (1 hunks)
  • src/index.ts (2 hunks)
  • src/typeDefinitions/discordMessage.types.d.ts (1 hunks)
  • src/utils/editGroupRole.ts (1 hunks)
  • tests/unit/handlers/editGuildRoleHandler.test.ts (1 hunks)
  • tests/unit/utils/editGuildRole.test.ts (1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (4)
src/utils/editGroupRole.ts (2)
src/constants/urls.ts (1)
  • DISCORD_BASE_URL (5-5)
src/constants/responses.ts (2)
  • ROLE_UPDATED (37-37)
  • INTERNAL_SERVER_ERROR (23-24)
tests/unit/utils/editGuildRole.test.ts (3)
src/constants/urls.ts (1)
  • DISCORD_BASE_URL (5-5)
tests/fixtures/fixture.ts (1)
  • guildEnv (59-63)
src/utils/editGroupRole.ts (1)
  • editGuildRole (7-37)
tests/unit/handlers/editGuildRoleHandler.test.ts (2)
tests/fixtures/fixture.ts (2)
  • generateDummyRequestObject (125-141)
  • guildEnv (59-63)
src/controllers/editGuildRolesHandler.ts (1)
  • editGuildRoleHandler (10-45)
src/controllers/editGuildRolesHandler.ts (3)
src/utils/verifyAuthToken.ts (1)
  • verifyNodejsBackendAuthToken (31-40)
src/typeDefinitions/discordMessage.types.d.ts (1)
  • updateRole (93-97)
src/utils/editGroupRole.ts (1)
  • editGuildRole (7-37)
🔇 Additional comments (14)
src/constants/responses.ts (1)

37-37: Looks good - added ROLE_UPDATED constant

The addition of the ROLE_UPDATED constant follows the same pattern as other response messages in this file and will be used to indicate successful role updates.

src/index.ts (2)

27-27: Import statement looks good

The import for the new handler function follows the same pattern as other imports in this file.


69-69:

✅ Verification successful

Route definition is appropriate

The new PATCH endpoint for /roles/:roleId correctly maps to the editGuildRoleHandler function. This follows RESTful API conventions where PATCH is used for partial updates.

Note that this path parameter should match the expected param name in the handler. Make sure the handler uses roleId as the parameter name to extract from the request.


🏁 Script executed:

#!/bin/bash
# Check how the roleId is used in the handler
rg -t ts "editGuildRoleHandler" -A 10 -B 10

Length of output: 14278


Route /roles/:roleId parameter matches handler
The handler in src/controllers/editGuildRolesHandler.ts uses

const roleId = decodeURI(request.params?.roleId ?? "");

so the :roleId path parameter aligns correctly. Ready to merge.

tests/unit/utils/editGuildRole.test.ts (3)

11-20: LGTM: Mock request parameters are properly set up.

The test correctly constructs the expected URL and request parameters, including headers and body, which will be used to verify that the utility makes the correct API call to Discord.


27-43: Test properly verifies that the reason is passed as a header.

This test effectively verifies that when a reason is provided to the editGuildRole function, it is correctly passed as the X-Audit-Log-Reason header in the PATCH request to Discord's API.


45-53: Test for successful response is comprehensive.

This test correctly verifies that when the Discord API responds with a 204 status code (success), the function returns the ROLE_UPDATED response constant.

tests/unit/handlers/editGuildRoleHandler.test.ts (6)

1-14: Test setup is properly structured.

The test imports all necessary dependencies, sets up proper Jest hooks to clear and restore mocks between tests, and defines a constant roleId to be used across tests.


15-26: Authorization test is properly implemented.

This test correctly verifies that the handler returns a BAD_SIGNATURE response when the authorization header is missing.


27-42: Feature flag test is properly implemented.

This test effectively verifies that the handler returns a NOT_IMPLEMENTED response when the dev flag is set to "false", confirming that the feature can be toggled off as intended.


43-58: Request validation test is properly implemented.

This test correctly verifies that the handler returns a BAD_REQUEST response when the roleId parameter is empty or invalid.


59-78: Token verification failure test is properly implemented.

This test correctly mocks the token verification function to reject and verifies that the handler returns an INTERNAL_SERVER_ERROR response.


100-128: Success path test is comprehensively implemented.

This test correctly mocks both the token verification and the role update function to succeed, and verifies that the handler returns a 204 No Content response, along with checking that the mocked functions were called as expected.

src/controllers/editGuildRolesHandler.ts (2)

10-16: Handler correctly extracts and sets up parameters.

The handler properly extracts the authorization header, reason header, roleId parameter, and dev flag from the request.


17-26: Request validation is well implemented.

The handler properly validates the authorization header, dev flag, and roleId parameter, returning appropriate error responses when validation fails.

Comment on lines +93 to +97
export interface updateRole {
roleid: string;
rolename: string;
mentionable: boolean;
}
Copy link

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

Interface definition looks good

The updateRole interface correctly defines the structure needed for role updates with appropriate types for each property.

One nitpick: Consider using camelCase for property names (roleId instead of roleid) to maintain consistency with TypeScript naming conventions.

export interface updateRole {
-  roleid: string;
+  roleId: string;
  rolename: string;
  mentionable: boolean;
}
📝 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
export interface updateRole {
roleid: string;
rolename: string;
mentionable: boolean;
}
export interface updateRole {
roleId: string;
rolename: string;
mentionable: boolean;
}

Comment on lines +7 to +37
export async function editGuildRole(
rolename: string,
roleid: string,
env: env,
reason?: string
) {
const editGuildRoleUrl = `${DISCORD_BASE_URL}/guilds/${env.DISCORD_GUILD_ID}/roles/${roleid}`;

const headers: HeadersInit = createDiscordHeaders({
reason,
token: env.DISCORD_TOKEN,
});
const data = {
name: rolename,
mentionable: true,
};
try {
const response = await fetch(editGuildRoleUrl, {
method: "PATCH",
headers,
body: JSON.stringify(data),
});
if (response.ok) {
return ROLE_UPDATED;
} else {
return INTERNAL_SERVER_ERROR;
}
} catch (err) {
return INTERNAL_SERVER_ERROR;
}
}
Copy link

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Function implementation needs improvement

The function correctly implements the Discord API call to update a role, but has a few issues:

  1. The function parameter list doesn't match the updateRole interface - it doesn't accept the mentionable parameter but hardcodes it to true
  2. There's no detailed error logging, which will make debugging difficult

Consider these improvements:

export async function editGuildRole(
  rolename: string,
  roleid: string,
  env: env,
+  mentionable: boolean = true,
  reason?: string
) {
  const editGuildRoleUrl = `${DISCORD_BASE_URL}/guilds/${env.DISCORD_GUILD_ID}/roles/${roleid}`;

  const headers: HeadersInit = createDiscordHeaders({
    reason,
    token: env.DISCORD_TOKEN,
  });
  const data = {
    name: rolename,
-    mentionable: true,
+    mentionable,
  };
  try {
    const response = await fetch(editGuildRoleUrl, {
      method: "PATCH",
      headers,
      body: JSON.stringify(data),
    });
    if (response.ok) {
      return ROLE_UPDATED;
    } else {
+     const errorData = await response.json().catch(() => null);
+     console.error("Failed to update role:", response.status, errorData);
      return INTERNAL_SERVER_ERROR;
    }
  } catch (err) {
+   console.error("Exception when updating role:", err);
    return INTERNAL_SERVER_ERROR;
  }
}

Let's verify if the mentionable property is used elsewhere:


🏁 Script executed:

#!/bin/bash
# Search for references to mentionable in the codebase to see if the property should be configurable
rg "mentionable" --type ts -A 3 -B 3

Length of output: 3413


Align function signature with updateRole interface and improve error logging

To ensure consistency with the updateRole type and make debugging easier:

• File needing changes:

  • src/utils/editGroupRole.ts

• What to update:

  1. Add a mentionable: boolean = true parameter.
  2. Use mentionable instead of hard-coding true in the request body.
  3. Log response errors and caught exceptions for better visibility.
 export async function editGuildRole(
   rolename: string,
   roleid: string,
   env: env,
+  mentionable: boolean = true,
   reason?: string
 ) {
   const editGuildRoleUrl = `${DISCORD_BASE_URL}/guilds/${env.DISCORD_GUILD_ID}/roles/${roleid}`;

   const headers: HeadersInit = createDiscordHeaders({
     reason,
     token: env.DISCORD_TOKEN,
   });
   const data = {
     name: rolename,
-    mentionable: true,
+    mentionable,
   };
   try {
     const response = await fetch(editGuildRoleUrl, {
       method: "PATCH",
       headers,
       body: JSON.stringify(data),
     });
     if (response.ok) {
       return ROLE_UPDATED;
     } else {
+      const errorData = await response.json().catch(() => null);
+      console.error("Failed to update role:", response.status, errorData);
       return INTERNAL_SERVER_ERROR;
     }
   } catch (err) {
+    console.error("Exception when updating role:", err);
     return INTERNAL_SERVER_ERROR;
   }
 }
📝 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
export async function editGuildRole(
rolename: string,
roleid: string,
env: env,
reason?: string
) {
const editGuildRoleUrl = `${DISCORD_BASE_URL}/guilds/${env.DISCORD_GUILD_ID}/roles/${roleid}`;
const headers: HeadersInit = createDiscordHeaders({
reason,
token: env.DISCORD_TOKEN,
});
const data = {
name: rolename,
mentionable: true,
};
try {
const response = await fetch(editGuildRoleUrl, {
method: "PATCH",
headers,
body: JSON.stringify(data),
});
if (response.ok) {
return ROLE_UPDATED;
} else {
return INTERNAL_SERVER_ERROR;
}
} catch (err) {
return INTERNAL_SERVER_ERROR;
}
}
export async function editGuildRole(
rolename: string,
roleid: string,
env: env,
mentionable: boolean = true,
reason?: string
) {
const editGuildRoleUrl = `${DISCORD_BASE_URL}/guilds/${env.DISCORD_GUILD_ID}/roles/${roleid}`;
const headers: HeadersInit = createDiscordHeaders({
reason,
token: env.DISCORD_TOKEN,
});
const data = {
name: rolename,
mentionable,
};
try {
const response = await fetch(editGuildRoleUrl, {
method: "PATCH",
headers,
body: JSON.stringify(data),
});
if (response.ok) {
return ROLE_UPDATED;
} else {
const errorData = await response.json().catch(() => null);
console.error("Failed to update role:", response.status, errorData);
return INTERNAL_SERVER_ERROR;
}
} catch (err) {
console.error("Exception when updating role:", err);
return INTERNAL_SERVER_ERROR;
}
}

Comment on lines +1 to +5
import { DISCORD_BASE_URL } from "../../../src/constants/urls";
import { editGuildRole } from "../../../src/utils/editGroupRole";
import JSONResponse from "../../../src/utils/JsonResponse";
import { guildEnv } from "../../fixtures/fixture";
import * as response from "../../../src/constants/responses";
Copy link

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

Inconsistent naming between file and import path.

There's a naming inconsistency between the test file name (editGuildRole.test.ts) and the imported function path (src/utils/editGroupRole.ts). This could lead to confusion - one uses "Guild" and the other uses "Group".

Consider standardizing the naming convention across the project for consistency.

Comment on lines +54 to +60
it("should return INTERNAL_SERVER_ERROR when response is not ok", async () => {
const expectedErrorResponse = new Response(response.INTERNAL_SERVER_ERROR);
jest.spyOn(global, "fetch").mockRejectedValue(expectedErrorResponse);
const result = await editGuildRole(rolename, roleid, guildEnv);
expect(result).toEqual(response.INTERNAL_SERVER_ERROR);
expect(global.fetch).toHaveBeenCalledTimes(1);
});
Copy link

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

Missing test for non-204 successful responses.

While the test covers the rejected Promise case, it doesn't test the scenario where the API responds with a status other than 204 but doesn't throw an error (the else branch in the utility function).

Consider adding a test for when the API responds with a non-204 status code:

it("should return INTERNAL_SERVER_ERROR when response status is not 204", async () => {
  const expectedErrorResponse = new Response(null, {
    status: 400,
  });
  jest.spyOn(global, "fetch").mockResolvedValue(expectedErrorResponse);
  const result = await editGuildRole(rolename, roleid, guildEnv);
  expect(result).toEqual(response.INTERNAL_SERVER_ERROR);
  expect(global.fetch).toHaveBeenCalledTimes(1);
});

Comment on lines +1 to +129
params: {
roleId: roleId,
},
query: {
dev: "false",
},
method: "PATCH",
headers: { Authorization: "Bearer testtoken" },
});
const response = await editGuildRoleHandler(mockRequest, guildEnv);
const jsonResponse = await response.json();
expect(jsonResponse).toEqual(responseConstants.NOT_IMPLEMENTED);
});
it("should return BAD_REQUEST when roleId is not valid", async () => {
const mockRequest = generateDummyRequestObject({
url: "/roles",
params: {
roleId: "",
},
query: {
dev: "true",
},
method: "PATCH",
headers: { Authorization: "Bearer testtoken" },
});
const response = await editGuildRoleHandler(mockRequest, guildEnv);
const jsonResponse = await response.json();
expect(jsonResponse).toEqual(responseConstants.BAD_REQUEST);
});
it("should return INTERNAL_SERVER_ERROR when token is not verified", async () => {
const expectedResponse = responseConstants.INTERNAL_SERVER_ERROR;
const mockRequest = generateDummyRequestObject({
url: "/roles",
params: {
roleId: roleId,
},
query: {
dev: "true",
},
method: "PATCH",
headers: { Authorization: "Bearer testtoken" },
});
jest
.spyOn(verifyTokenUtils, "verifyNodejsBackendAuthToken")
.mockRejectedValue(expectedResponse);
const response = await editGuildRoleHandler(mockRequest, guildEnv);
const jsonResponse = await response.json();
expect(jsonResponse).toEqual(expectedResponse);
});
it("should return INTERNAL_SERVER_ERROR when update fails", async () => {
const expectedResponse = responseConstants.INTERNAL_SERVER_ERROR;
const mockRequest = generateDummyRequestObject({
url: "/roles",
params: {
roleId: roleId,
},
query: {
dev: "true",
},
method: "PATCH",
headers: { Authorization: "Bearer testtoken" },
body: JSON.stringify({ rolename: "New Role Name" }),
});
jest
.spyOn(verifyTokenUtils, "verifyNodejsBackendAuthToken")
.mockResolvedValueOnce();
const response = await editGuildRoleHandler(mockRequest, guildEnv);
const jsonResponse = await response.json();
expect(jsonResponse).toEqual(expectedResponse);
});
it("should return ok response when role is updated", async () => {
const expectedResponse = new Response(null, {
status: 204,
});
const mockRequest = generateDummyRequestObject({
url: "/roles",
method: "PATCH",
params: {
roleId: roleId,
},
query: {
dev: "true",
},
headers: { Authorization: "Bearer testtoken" },
json: () => Promise.resolve({ rolename: "something" }),
});
const verifyTokenSpy = jest
.spyOn(verifyTokenUtils, "verifyNodejsBackendAuthToken")
.mockResolvedValueOnce();
const editGuildRoleSpy = jest
.spyOn(editGuildRoleUtils, "editGuildRole")
.mockResolvedValueOnce(responseConstants.ROLE_UPDATED);
const response = await editGuildRoleHandler(mockRequest, guildEnv);

expect(verifyTokenSpy).toHaveBeenCalledTimes(1);
expect(editGuildRoleSpy).toHaveBeenCalledTimes(1);
expect(response).toEqual(expectedResponse);
expect(response.status).toEqual(expectedResponse.status);
});
});
Copy link

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

Missing test for malformed request body.

The test suite doesn't cover the scenario where the request body is malformed or doesn't contain the required fields.

Consider adding a test for handling malformed JSON:

it("should return INTERNAL_SERVER_ERROR when the request body is malformed", async () => {
  const mockRequest = generateDummyRequestObject({
    url: "/roles",
    params: {
      roleId: roleId,
    },
    query: {
      dev: "true",
    },
    method: "PATCH",
    headers: { Authorization: "Bearer testtoken" },
    json: () => Promise.reject(new Error("Invalid JSON")),
  });
  jest
    .spyOn(verifyTokenUtils, "verifyNodejsBackendAuthToken")
    .mockResolvedValueOnce();
  
  const response = await editGuildRoleHandler(mockRequest, guildEnv);
  const jsonResponse = await response.json();
  expect(jsonResponse).toEqual(responseConstants.INTERNAL_SERVER_ERROR);
  expect(response.status).toBe(500);
});

Comment on lines +1 to +9
// This function updates group-role in discord.
import * as response from "../constants/responses";
import { IRequest } from "itty-router";
import { env } from "../typeDefinitions/default.types";
import JSONResponse from "../utils/JsonResponse";
import { verifyNodejsBackendAuthToken } from "../utils/verifyAuthToken";
import { updateRole } from "../typeDefinitions/discordMessage.types";
import { editGuildRole } from "../utils/editGroupRole";

Copy link

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

Inconsistent naming between file and import path.

Similar to the test file, there's a naming inconsistency between the handler file name (editGuildRolesHandler.ts) and the imported utility function path (editGroupRole.ts). One uses "Guild" and the other uses "Group".

Standardize the naming convention across the project for consistency.

Comment on lines +27 to +38
try {
await verifyNodejsBackendAuthToken(authHeader, env);
const body: updateRole = await request.json();
const result = await editGuildRole(body.rolename, roleId, env, reason);

if (result === response.ROLE_UPDATED) {
return new Response(null, { status: 204 });
} else {
return new JSONResponse(response.INTERNAL_SERVER_ERROR, {
status: 500,
});
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Missing validation for request body.

The handler parses the request body as an updateRole object but doesn't validate that the required rolename field is present.

Add validation for the required fields in the request body:

   try {
     await verifyNodejsBackendAuthToken(authHeader, env);
     const body: updateRole = await request.json();
+    if (!body.rolename) {
+      return new JSONResponse(response.BAD_REQUEST, { status: 400 });
+    }
     const result = await editGuildRole(body.rolename, roleId, env, reason);
📝 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
try {
await verifyNodejsBackendAuthToken(authHeader, env);
const body: updateRole = await request.json();
const result = await editGuildRole(body.rolename, roleId, env, reason);
if (result === response.ROLE_UPDATED) {
return new Response(null, { status: 204 });
} else {
return new JSONResponse(response.INTERNAL_SERVER_ERROR, {
status: 500,
});
}
try {
await verifyNodejsBackendAuthToken(authHeader, env);
const body: updateRole = await request.json();
if (!body.rolename) {
return new JSONResponse(response.BAD_REQUEST, { status: 400 });
}
const result = await editGuildRole(body.rolename, roleId, env, reason);
if (result === response.ROLE_UPDATED) {
return new Response(null, { status: 204 });
} else {
return new JSONResponse(response.INTERNAL_SERVER_ERROR, {
status: 500,
});
}

Comment on lines +39 to +45
} catch (err) {
console.log("Error updating guild role: ", err);
return new JSONResponse(response.INTERNAL_SERVER_ERROR, {
status: 500,
});
}
}
Copy link

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

Error handling is too generic.

The catch block logs the error but doesn't distinguish between different types of errors, such as unauthorized access, rate limiting, or server errors. This makes debugging more difficult.

Consider enhancing error logging to provide more context:

   } catch (err) {
-    console.log("Error updating guild role: ", err);
+    console.error("Error updating guild role: ", {
+      roleId,
+      error: err instanceof Error ? err.message : String(err),
+      stack: err instanceof Error ? err.stack : undefined
+    });
     return new JSONResponse(response.INTERNAL_SERVER_ERROR, {
       status: 500,
     });
   }
📝 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
} catch (err) {
console.log("Error updating guild role: ", err);
return new JSONResponse(response.INTERNAL_SERVER_ERROR, {
status: 500,
});
}
}
} catch (err) {
console.error("Error updating guild role: ", {
roleId,
error: err instanceof Error ? err.message : String(err),
stack: err instanceof Error ? err.stack : undefined
});
return new JSONResponse(response.INTERNAL_SERVER_ERROR, {
status: 500,
});
}
}

Comment on lines +1 to +45
// This function updates group-role in discord.
import * as response from "../constants/responses";
import { IRequest } from "itty-router";
import { env } from "../typeDefinitions/default.types";
import JSONResponse from "../utils/JsonResponse";
import { verifyNodejsBackendAuthToken } from "../utils/verifyAuthToken";
import { updateRole } from "../typeDefinitions/discordMessage.types";
import { editGuildRole } from "../utils/editGroupRole";

export async function editGuildRoleHandler(request: IRequest, env: env) {
const authHeader = request.headers.get("Authorization");
const reason = request.headers.get("X-Audit-Log-Reason");
const roleId = decodeURI(request.params?.roleId ?? "");
const { dev } = request.query;
const devFlag = dev === "true";

if (!authHeader) {
return new JSONResponse(response.BAD_SIGNATURE, { status: 401 });
}

if (!devFlag) {
return new JSONResponse(response.NOT_IMPLEMENTED, { status: 501 });
}
if (!roleId) {
return new JSONResponse(response.BAD_REQUEST, { status: 400 });
}
try {
await verifyNodejsBackendAuthToken(authHeader, env);
const body: updateRole = await request.json();
const result = await editGuildRole(body.rolename, roleId, env, reason);

if (result === response.ROLE_UPDATED) {
return new Response(null, { status: 204 });
} else {
return new JSONResponse(response.INTERNAL_SERVER_ERROR, {
status: 500,
});
}
} catch (err) {
console.log("Error updating guild role: ", err);
return new JSONResponse(response.INTERNAL_SERVER_ERROR, {
status: 500,
});
}
}
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 documentation for feature flag.

The handler implements a feature flag (dev=true) but doesn't document this in the file comments. Adding documentation would help other developers understand when and how the feature can be enabled.

Enhance the file comment to explain the feature flag:

-// This function updates group-role in discord.
+// This function updates group-role in discord.
+// Feature is behind a feature flag: set query parameter dev=true to enable it.
+// Example: PATCH /roles/:roleId?dev=true
📝 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
// This function updates group-role in discord.
import * as response from "../constants/responses";
import { IRequest } from "itty-router";
import { env } from "../typeDefinitions/default.types";
import JSONResponse from "../utils/JsonResponse";
import { verifyNodejsBackendAuthToken } from "../utils/verifyAuthToken";
import { updateRole } from "../typeDefinitions/discordMessage.types";
import { editGuildRole } from "../utils/editGroupRole";
export async function editGuildRoleHandler(request: IRequest, env: env) {
const authHeader = request.headers.get("Authorization");
const reason = request.headers.get("X-Audit-Log-Reason");
const roleId = decodeURI(request.params?.roleId ?? "");
const { dev } = request.query;
const devFlag = dev === "true";
if (!authHeader) {
return new JSONResponse(response.BAD_SIGNATURE, { status: 401 });
}
if (!devFlag) {
return new JSONResponse(response.NOT_IMPLEMENTED, { status: 501 });
}
if (!roleId) {
return new JSONResponse(response.BAD_REQUEST, { status: 400 });
}
try {
await verifyNodejsBackendAuthToken(authHeader, env);
const body: updateRole = await request.json();
const result = await editGuildRole(body.rolename, roleId, env, reason);
if (result === response.ROLE_UPDATED) {
return new Response(null, { status: 204 });
} else {
return new JSONResponse(response.INTERNAL_SERVER_ERROR, {
status: 500,
});
}
} catch (err) {
console.log("Error updating guild role: ", err);
return new JSONResponse(response.INTERNAL_SERVER_ERROR, {
status: 500,
});
}
}
// This function updates group-role in discord.
// Feature is behind a feature flag: set query parameter dev=true to enable it.
// Example: PATCH /roles/:roleId?dev=true
import * as response from "../constants/responses";
import { IRequest } from "itty-router";
import { env } from "../typeDefinitions/default.types";
import JSONResponse from "../utils/JsonResponse";
import { verifyNodejsBackendAuthToken } from "../utils/verifyAuthToken";
import { updateRole } from "../typeDefinitions/discordMessage.types";
import { editGuildRole } from "../utils/editGroupRole";
export async function editGuildRoleHandler(request: IRequest, env: env) {
const authHeader = request.headers.get("Authorization");
const reason = request.headers.get("X-Audit-Log-Reason");
const roleId = decodeURI(request.params?.roleId ?? "");
const { dev } = request.query;
const devFlag = dev === "true";
if (!authHeader) {
return new JSONResponse(response.BAD_SIGNATURE, { status: 401 });
}
if (!devFlag) {
return new JSONResponse(response.NOT_IMPLEMENTED, { status: 501 });
}
if (!roleId) {
return new JSONResponse(response.BAD_REQUEST, { status: 400 });
}
try {
await verifyNodejsBackendAuthToken(authHeader, env);
const body: updateRole = await request.json();
const result = await editGuildRole(body.rolename, roleId, env, reason);
if (result === response.ROLE_UPDATED) {
return new Response(null, { status: 204 });
} else {
return new JSONResponse(response.INTERNAL_SERVER_ERROR, {
status: 500,
});
}
} catch (err) {
console.log("Error updating guild role: ", err);
return new JSONResponse(response.INTERNAL_SERVER_ERROR, {
status: 500,
});
}
}

import { env } from "../typeDefinitions/default.types";
import JSONResponse from "../utils/JsonResponse";
import { verifyNodejsBackendAuthToken } from "../utils/verifyAuthToken";
import { updateRole } from "../typeDefinitions/discordMessage.types";
Copy link

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Mismatch between interface and implementation.

The updateRole interface imported from discordMessage.types likely has fields roleid, rolename, and mentionable based on context, but only rolename is used in the implementation.


🏁 Script executed:

#!/bin/bash
# Check the updateRole interface definition
grep -A 10 "interface updateRole" src/typeDefinitions/discordMessage.types.ts || echo "Interface not found"

Length of output: 205


🏁 Script executed:

#!/bin/bash
# Locate the updateRole interface definition
grep -R "interface updateRole" -n -A5 .

# Display the handler to see how updateRole is used
echo
echo "----- editGuildRolesHandler.ts (first 30 lines) -----"
sed -n '1,30p' src/controllers/editGuildRolesHandler.ts

Length of output: 1893


🏁 Script executed:

#!/bin/bash
# Locate the editGuildRole implementation and display its signature
grep -R "export async function editGuildRole" -n src/utils/editGroupRole.ts || grep -R "function editGuildRole" -n src/utils/editGroupRole.ts
echo
echo "----- src/utils/editGroupRole.ts -----"
sed -n '1,50p' src/utils/editGroupRole.ts

Length of output: 1180


Align the updateRole interface with its usage

The updateRole type includes roleid and mentionable, but the handler only uses rolename (and a path‐derived roleId), and the helper always sets mentionable: true. We should either remove unused fields from the interface or wire them into the implementation:

• In src/controllers/editGuildRolesHandler.ts
– Drop roleid/mentionable from the type or pull body.mentionable if you intend to honor the client’s flag.
– Pass body.roleid instead of the path param if that’s the source of truth.

• In src/utils/editGroupRole.ts
– Update the data payload to use a mentionable parameter.
– Adjust the function signature to accept (rolename: string, roleid: string, env: env, reason?: string, mentionable?: boolean).

Example diff for using mentionable from the request:

--- a/src/controllers/editGuildRolesHandler.ts
+++ b/src/controllers/editGuildRolesHandler.ts
@@ -26,7 +26,8 @@ export async function editGuildRoleHandler(request: IRequest, env: env) {
     await verifyNodejsBackendAuthToken(authHeader, env);
     const body: updateRole = await request.json();
-    const result = await editGuildRole(body.rolename, roleId, env, reason);
+    const result = await editGuildRole(
+      body.rolename, body.roleid, env, reason, body.mentionable
+    );

     return new JSONResponse(result);
--- a/src/utils/editGroupRole.ts
+++ b/src/utils/editGroupRole.ts
@@ export async function editGuildRole(
-  rolename: string,
-  roleid: string,
-  env: env,
-  reason?: string
+) rolename: string,
+) roleid: string,
+) env: env,
+) reason?: string,
+) mentionable: boolean = true
 ) {
   const editGuildRoleUrl = `${DISCORD_BASE_URL}/guilds/${env.DISCORD_GUILD_ID}/roles/${roleid}`;
@@
-  const data = {
-    name: rolename,
-    mentionable: true,
-  };
+  const data = { name: rolename, mentionable };

   try {
     const response = await fetch(editGuildRoleUrl, {

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.

2 participants