Skip to content

Task #259635 chore: Implement audit logs for Center Management Batch …#767

Open
kkbiswal wants to merge 11 commits into
sdbv_rbac_changesfrom
feature/259635-audit-logs
Open

Task #259635 chore: Implement audit logs for Center Management Batch …#767
kkbiswal wants to merge 11 commits into
sdbv_rbac_changesfrom
feature/259635-audit-logs

Conversation

@kkbiswal

@kkbiswal kkbiswal commented Jun 15, 2026

Copy link
Copy Markdown
Collaborator

…Management

Summary by CodeRabbit

Release Notes

  • New Features

    • Added comprehensive audit logging throughout the application to track changes to cohorts, cohort members, users, roles, permissions, and custom fields.
    • Audit logs now capture actor identity, timestamp, and change details for enhanced accountability and system transparency.
  • Chores

    • Added audit logging infrastructure to support system-wide change tracking.

@coderabbitai

coderabbitai Bot commented Jun 15, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

🗂️ Base branches to auto review (7)
  • master
  • main
  • dev
  • feat/*
  • feat-*
  • release-*
  • Shiksha-2.0

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.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: fe26e75b-1f5b-44c6-a0eb-b80cd69554dc

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

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Walkthrough

Adds the @tekdi/audit-logger package and integrates structured audit logging across all major service domains (user, cohort, cohort members, role, assign-role, role-permission, fields, user-tenant mapping). A new AsyncLocalStorage-backed requestContext, RequestContextMiddleware, and getAuditContext utility replace explicit request parameter passing in service methods, and AuditLoggerService is injected into each service to emit CREATED/UPDATED/DELETED events via a shared emitAuditSafely wrapper.

Changes

Audit Logging Integration

Layer / File(s) Summary
Package, module wiring, and request context foundation
package.json, src/common/utils/request-context.ts, src/common/middleware/request-context.middleware.ts, src/common/utils/audit-helper.ts, src/app.module.ts
@tekdi/audit-logger added as a dependency. A shared AsyncLocalStorage<any> instance (requestContext) is created. RequestContextMiddleware wraps each request in requestContext.run(req, next). getAuditContext() reads from the store and returns actor fields with defaults. AppModule registers AuditLoggerModule.forRoot with Kafka topic and activates the middleware for all routes.
UserService and UserController audit wiring
src/user/user.service.ts, src/user/user.controller.ts, src/sso/sso.service.ts
Eleven UserService public methods drop their explicit request parameter and read from requestContext.getStore() instead. AuditLoggerService is injected with emitAuditSafely. createUser, updateUser, and deleteUserById emit CREATED, UPDATED, and DELETED audit events respectively. UserController adjusts all call sites, enables JwtAuthGuard on createUser, and adds @Req() to updateUser. SsoService.handleNewUser removes the mockRequest construction.
Cohort and CohortMembers audit wiring
src/cohort/cohort.service.ts, src/cohort/cohort.controller.ts, src/cohortMembers/cohortMembers.service.ts, src/cohortMembers/cohortMembers.controller.ts
CohortService injects AuditLoggerService and emits COHORT events for CREATED, STATUS_UPDATED, UPDATED, and DELETED actions. updateCohortStatuses gains a leading request parameter. CohortMembersService emits COHORT_MEMBER CREATED and UPDATED events; updateCohortMembers gains a leading request parameter and simplifies validation error handling. Controllers forward request to the revised service signatures.
Role, AssignRole, and RolePermission audit wiring
src/rbac/role/role.service.ts, src/rbac/role/role.controller.ts, src/rbac/assign-role/assign-role.service.ts, src/rbac/assign-role/assign-role.controller.ts, src/permissionRbac/rolePermissionMapping/role-permission-mapping.service.ts, src/permissionRbac/rolePermissionMapping/role-permission-mapping.controller.ts
RoleService drops request from createRole/updateRole, uses auditCtx.actorId for createdBy/updatedBy, and emits ROLE events for all three mutations. AssignRoleService removes request from createAssignRole/getAssignedRole and emits per-item USER_ROLE events in create, delete, and bulk-update flows. RolePermissionService emits ROLE_PERMISSION events in all three mutation methods. Controllers add or remove @Req() to align with revised service signatures.
Fields and UserTenantMapping audit wiring
src/fields/fields.service.ts, src/fields/fields.controller.ts, src/userTenantMapping/user-tenant-mapping.service.ts, src/userTenantMapping/user-tenant-mapping.controller.ts
FieldsService removes request from createFields, updateFields, and createFieldValues, reads from requestContext.getStore(), and emits FIELD_VALUE CREATED and DELETED events. UserTenantMappingService removes request from both public methods, reads from requestContext.getStore(), and emits USER_TENANT CREATED and UPDATED events. Controllers update service call sites to match.

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant RequestContextMiddleware
  participant requestContext
  participant ServiceMethod
  participant getAuditContext
  participant AuditLoggerService
  participant KafkaTopic

  Client->>RequestContextMiddleware: HTTP request (with JWT/user headers)
  RequestContextMiddleware->>requestContext: run(req, next)
  requestContext->>ServiceMethod: executes within async context
  ServiceMethod->>requestContext: getStore() → req
  ServiceMethod->>getAuditContext: getAuditContext()
  getAuditContext->>requestContext: getStore() → actorId, actorName, userRole, ip, userAgent
  getAuditContext-->>ServiceMethod: auditCtx
  ServiceMethod->>AuditLoggerService: emitAuditSafely({ entityType, eventAction, entityId, metadata, ...auditCtx })
  AuditLoggerService->>KafkaTopic: publish to audit.events
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • tekdi/user-microservice#679: Modifies CohortController and cohortService.updateCohortStatuses signature/behavior, overlapping directly with the cohort audit wiring changes in this PR that add a leading request parameter to that same method.
  • tekdi/user-microservice#674: Touches UserController/UserService.updateUser request handling and updatedBy derivation, which is the same code area refactored in this PR for audit context extraction.
  • tekdi/user-microservice#763: Modifies searchUser in user.controller.ts and user.service.ts, the same methods whose signatures are changed in this PR to drop the explicit request parameter.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly identifies the main objective: implementing audit logs for Center Management Batch operations, which aligns with the extensive audit logging changes throughout the codebase.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/259635-audit-logs

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

Comment @coderabbitai help to get the list of available commands.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Code Review

This pull request integrates the @tekdi/audit-logger library and introduces an AsyncLocalStorage-based request context middleware to globally manage request data, allowing the removal of explicitly passed request parameters across various controllers and services. Audit log emissions have been added for key lifecycle events across cohorts, cohort members, fields, roles, permissions, users, and tenant mappings. The review feedback identifies several critical issues, including an undefined res variable in cohortMembers.service.ts that will cause runtime crashes, a missing early return in fields.service.ts leading to a headers-already-sent error, a duplicate and unsafe audit log emission in user.service.ts, a problematic comma-joined entityId in cohort.service.ts, and an unused request variable in user.service.ts.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread src/cohortMembers/cohortMembers.service.ts Outdated
Comment thread src/fields/fields.service.ts
Comment thread src/user/user.service.ts Outdated
Comment thread src/cohort/cohort.service.ts
Comment thread src/user/user.service.ts Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 8

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
src/fields/fields.service.ts (1)

897-926: ⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Missing return causes audit emission on failure path.

When result is falsy (line 897), APIResponse.error() is called but the function continues to execute the success path: calling APIResponse.success() and emitting a CREATED audit event. This results in:

  1. Double response write (corrupted HTTP response or "headers already sent" error)
  2. False audit trail showing a CREATED event for a failed operation
🐛 Proposed fix: add return after error response
       if (!result) {
-        APIResponse.error(
+        return APIResponse.error(
           res,
           apiId,
           `Fields not found or already exist`,
           `Fields not found or already exist`,
           HttpStatus.NOT_FOUND
         );
       }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/fields/fields.service.ts` around lines 897 - 926, The error handling
block for when result is falsy is missing a return statement after calling
APIResponse.error(). This causes the function to continue executing the success
path, resulting in both an error and success response being sent along with a
false audit trail. Add a return statement immediately after the
APIResponse.error() call to prevent execution from reaching the
APIResponse.success() call and the subsequent audit logger emit that follows it.
src/permissionRbac/rolePermissionMapping/role-permission-mapping.service.ts (1)

112-135: ⚠️ Potential issue | 🟠 Major

Gate audit emission on successful row changes to prevent false UPDATED/DELETED events.

Both update() and delete() emit audit logs unconditionally, even when no rows are affected (affected=0). This produces incorrect audit trails for no-op operations. Check the affected field before emitting UPDATED/DELETED events:

Example fix for update()
      let result = await this.rolePermissionRepository.update(
        rolePermissionCreateDto.rolePermissionId,
        {
          roleTitle: rolePermissionCreateDto.roleTitle,
          apiPath: rolePermissionCreateDto.apiPath,
          requestType: rolePermissionCreateDto.requestType,
          module: rolePermissionCreateDto.module,
        }
      );
      const apiRes = APIResponse.success(
        response,
        apiId,
        result,
        HttpStatus.OK,
        "Permission updated succesfully."
      );
-     const auditCtx = getAuditContext();
-     this.auditLoggerService.emit({
-       entityType: "ROLE_PERMISSION",
-       entityId: rolePermissionCreateDto.rolePermissionId || "N/A",
-       eventAction: "UPDATED",
-       ...auditCtx
-     });
+     if ((result.affected ?? 0) > 0) {
+       const auditCtx = getAuditContext();
+       this.auditLoggerService.emit({
+         entityType: "ROLE_PERMISSION",
+         entityId: rolePermissionCreateDto.rolePermissionId || "N/A",
+         eventAction: "UPDATED",
+         ...auditCtx
+       });
+     }

Apply the same pattern to the deletePermission() method.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/permissionRbac/rolePermissionMapping/role-permission-mapping.service.ts`
around lines 112 - 135, The audit log emission in the method handling role
permission updates is occurring unconditionally, even when the repository update
operation does not affect any rows. Check the affected field from the result
returned by rolePermissionRepository.update() and only emit the audit event via
auditLoggerService.emit() when affected is greater than zero to prevent false
UPDATED events for no-op operations. Apply the same conditional logic pattern to
the deletePermission() method to prevent false DELETED events.
🧹 Nitpick comments (8)
src/user/user.controller.ts (1)

164-165: 💤 Low value

Unused parameter: @Req() request is injected but never used.

The request parameter was added to the method signature but is never referenced in the method body. The service now reads the request context internally via requestContext.getStore(). This dead parameter should be removed.

   public async updateUser(
-    `@Req`() request: Request,
     `@Headers`() headers,
     `@Param`("userid") userId: string,
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/user/user.controller.ts` around lines 164 - 165, The updateUser method in
the user controller has an unused `@Req`() request parameter that was added to the
method signature but is never referenced in the method body. Since the service
now reads the request context internally via requestContext.getStore(), this
dead parameter should be removed. Delete the `@Req`() request: Request parameter
from the updateUser method signature entirely to clean up the unused import.
src/user/user.service.ts (2)

3457-3459: 💤 Low value

Unused variable: request is obtained but never used.

The request variable is assigned but never referenced in this method.

  async suggestUsername(response: Response, suggestUserDto: SuggestUserDto) {
-    const request = requestContext.getStore() as any;
-
     const apiId = APIID.USER_LIST;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/user/user.service.ts` around lines 3457 - 3459, Remove the unused
variable assignment in the suggestUsername method in src/user/user.service.ts.
The request variable is assigned from requestContext.getStore() but never
referenced elsewhere in the method, so delete the line that declares and assigns
this unused variable.

139-139: 💤 Low value

Unused request variables across multiple methods after parameter removal refactoring.

Several methods obtain request from requestContext.getStore() but never reference the variable. These are leftover assignments from the migration pattern where request parameters were removed. Consider removing these dead assignments for code clarity.

  • src/user/user.service.ts#L139: Remove unused request in sendPasswordResetLink
  • src/user/user.service.ts#L256: Remove unused request in forgotPassword
  • src/user/user.service.ts#L424: Remove unused request in searchUserMultiTenant
  • src/user/user.service.ts#L1275: Remove unused request in updateUser
  • src/user/user.service.ts#L3458: Remove unused request in suggestUsername
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/user/user.service.ts` at line 139, Remove the unused request variable
assignments that were left over from a prior refactoring where request
parameters were removed from method signatures. In src/user/user.service.ts at
line 139 (sendPasswordResetLink), line 256 (forgotPassword), line 424
(searchUserMultiTenant), line 1275 (updateUser), and line 3458
(suggestUsername), delete the line `const request = requestContext.getStore() as
any;` from each method since the request variable is never referenced in any of
these methods' implementations.
src/rbac/assign-role/assign-role.service.ts (1)

163-163: ⚡ Quick win

Unused variable request in getAssignedRole.

The request variable is fetched from context but never referenced in the method body. Remove the dead code.

Proposed fix
   public async getAssignedRole(
     userId: string,
     response: Response
   ) {
-    const request = requestContext.getStore() as any;
     const apiId = APIID.USERROLE_GET;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/rbac/assign-role/assign-role.service.ts` at line 163, The variable
`request` is assigned from `requestContext.getStore() as any` in the
`getAssignedRole` method but is never used anywhere in the method body. Remove
the unused variable assignment line entirely to clean up the dead code.
src/rbac/assign-role/assign-role.controller.ts (1)

100-100: 💤 Low value

Unused request parameters in deleteRole and bulkUpdateUserRoles.

Both methods inject @Req() request but never pass it to the service—AssignRoleService reads from requestContext internally. These are dead code unless intentionally kept for documentation purposes.

Consider removing them for consistency with create and getRole which no longer inject request, or add a comment explaining the intent if they're kept for future use.

Also applies to: 118-118

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/rbac/assign-role/assign-role.controller.ts` at line 100, Remove the
unused `@Req() request` parameter from both the `deleteRole` method (at line
100) and the `bulkUpdateUserRoles` method (at line 118) in the
assign-role.controller.ts file. These parameters are never used within the
methods and are inconsistent with the `create` and `getRole` methods which do
not inject the request parameter. Since the service uses `requestContext`
internally instead of the injected request, deleting these parameters will clean
up the code and improve consistency across all controller methods.
src/rbac/role/role.controller.ts (1)

127-132: 💤 Low value

Unused @Req() request parameters in role and assign-role controllers. All three endpoints inject the Express request but never pass it to the service layer, which reads from requestContext.getStore() internally. This creates dead code across multiple controllers.

  • src/rbac/role/role.controller.ts#L127-L132: Remove unused request from deleteRole or document why it's retained.
  • src/rbac/assign-role/assign-role.controller.ts#L100-L106: Remove unused request from deleteRole or document why it's retained.
  • src/rbac/assign-role/assign-role.controller.ts#L117-L135: Remove unused request from bulkUpdateUserRoles or document why it's retained.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/rbac/role/role.controller.ts` around lines 127 - 132, The `@Req`() request
parameter is injected but never used in three controller methods across two
files; the service layer instead accesses request context via
requestContext.getStore() internally. Remove the unused `@Req`() request: Request
parameter from the following methods: deleteRole method in
src/rbac/role/role.controller.ts at lines 127-132, deleteRole method in
src/rbac/assign-role/assign-role.controller.ts at lines 100-106, and
bulkUpdateUserRoles method in src/rbac/assign-role/assign-role.controller.ts at
lines 117-135. This eliminates dead code while maintaining the same
functionality since the services do not depend on the request parameter being
passed.
src/rbac/role/role.service.ts (2)

161-161: ⚡ Quick win

Unused variable request in updateRole.

The request variable is fetched from context on line 161 but never referenced—getAuditContext() internally reads from the same store. Remove the unused declaration to avoid dead code and potential reader confusion.

Proposed fix
   public async updateRole(
     roleId: string,
     roleDto: RoleDto,
     response: Response
   ) {
-    const request = requestContext.getStore() as any;
     const apiId = APIID.ROLE_UPDATE;

Also applies to: 167-175

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/rbac/role/role.service.ts` at line 161, The updateRole method declares a
request variable by calling requestContext.getStore() on line 161 but never uses
it, creating dead code. Remove this unused variable declaration since
getAuditContext() internally reads from the same store and makes the explicit
extraction redundant. Apply the same fix to the additional occurrences of this
pattern mentioned at lines 167-175 within the same method.

335-336: ⚡ Quick win

Unused variable request in deleteRole.

Same issue: request is declared but never used because getAuditContext() reads from the store internally. Remove the unused variable.

Proposed fix
   public async deleteRole(roleId: string, res: Response) {
-    const request = requestContext.getStore() as any;
-
     const apiId = APIID.ROLE_DELETE;

Also applies to: 365-372

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/rbac/role/role.service.ts` around lines 335 - 336, The variable request
declared via requestContext.getStore() in the deleteRole method is never used
and should be removed. Since getAuditContext() reads from the store internally,
this variable is redundant. Remove the unused request variable declaration from
the deleteRole method. The same unused variable pattern exists at other
locations in the file (lines 365-372), so remove the unused request variable
from all affected methods where it appears but is not referenced.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/cohortMembers/cohortMembers.service.ts`:
- Around line 813-829: The auditLoggerService.emit call for COHORT_MEMBER update
events is breaking the established audit event contract by nesting request
metadata under a context object and renaming the userAgent field to platform.
Refactor the emit payload to use the shared/established audit context shape that
is consistent with other services in the codebase. Flatten the nested context
structure and use the correct field names (such as userAgent instead of
platform) that align with the standard audit event contract expected by
downstream consumers.

In `@src/permissionRbac/rolePermissionMapping/role-permission-mapping.service.ts`:
- Around line 86-92: The auditLoggerService.emit() call in the ROLE_PERMISSION
creation handler is not awaited and shares the same try/catch block as the
database operation, which means if emit() throws synchronously or returns a
rejected Promise, it will cause an error response to be sent to the client even
after successful database mutations. Decouple the audit emission from the CRUD
response flow by moving the auditLoggerService.emit() call outside the try/catch
block that wraps the database operation, and handle any errors from emit()
separately using a .catch() handler or similar pattern to ensure that audit
logging failures never interfere with returning successful API responses to the
client. Apply this same decoupling pattern to all other locations in the service
where auditLoggerService.emit() is called without awaiting.

In `@src/rbac/assign-role/assign-role.service.ts`:
- Line 42: The code is accessing request["user"].userId without null safety
after calling requestContext.getStore(), which can return undefined and cause a
null-pointer exception. Replace the unsafe access of request["user"].userId with
actorId retrieved from getAuditContext() instead, ensuring consistency with the
audit emit that follows and eliminating the null-safety risk when the request
context is unavailable.
- Around line 269-284: The audit log emit call in the deleteAssignedRole method
uses inconsistent field structure compared to createAssignRole—it manually
extracts user and request details inline and wraps context fields in a nested
object with platform, while createAssignRole uses getAuditContext() which
spreads top-level fields including userAgent. Refactor the audit emit in the
deleteAssignRoleDto roleId loop to call getAuditContext() (passing the request
object) and spread its returned object into the emit payload to match the
consistent schema used by createAssignRole, removing the manual inline field
extraction and the nested context wrapper.
- Around line 338-351: The audit payload in the bulkUpdateUserRoles method uses
inline extraction with a context wrapper instead of the getAuditContext() helper
function, and also places tenantId inside context which is inconsistent with
other methods like createAssignRole that use metadata. Refactor the
auditLoggerService.emit call to use the getAuditContext() method instead of
manually extracting actor and context details, ensuring tenantId placement is
consistent with other audit emits in the file (such as createAssignRole). After
refactoring, remove the now-unused request variable that was previously declared
on line 318.

In `@src/rbac/role/role.service.ts`:
- Line 40: In src/rbac/role/role.service.ts, the code at line 40 assigns the
result of requestContext.getStore() to request without handling the case where
it returns undefined, which causes a null-pointer exception at lines 88-90 when
attempting to access request.user.userId without optional chaining. Replace the
direct assignment and usage pattern at line 40 (the request variable
declaration) and lines 88-90 (where request.user.userId is accessed) by instead
using the existing getAuditContext() helper method which already provides proper
null-safety with optional chaining and fallbacks. After making this change,
remove the now-unused request variable declaration at line 40 since
getAuditContext() will handle the context retrieval safely.

In `@src/user/user.service.ts`:
- Around line 2864-2876: The deleteUserById method is emitting the DELETED audit
event twice, creating duplicate audit records. Remove one of the two audit log
emissions for the DELETED event - either the one using direct request property
access or the one using getAuditContext(). Keep only the emission that provides
complete and consistent audit context information to avoid duplication while
maintaining full audit trail coverage.

In `@src/userTenantMapping/user-tenant-mapping.service.ts`:
- Around line 162-171: The USER_TENANT_MAPPING audit events use inconsistent
entityId formats: the CREATED event (in the auditLoggerService.emit call around
the composite string format) uses a composite string pattern combining userId,
tenantId, and roleId, while the UPDATED event uses the database ID. This
inconsistency prevents reliable lifecycle correlation in audit consumers.
Standardize the entityId scheme across both the CREATED event and the UPDATED
event (also applies to lines 397-406) by selecting one format and applying it
consistently to all USER_TENANT_MAPPING audit events so that the same mapping
can be reliably tracked across all lifecycle actions.

---

Outside diff comments:
In `@src/fields/fields.service.ts`:
- Around line 897-926: The error handling block for when result is falsy is
missing a return statement after calling APIResponse.error(). This causes the
function to continue executing the success path, resulting in both an error and
success response being sent along with a false audit trail. Add a return
statement immediately after the APIResponse.error() call to prevent execution
from reaching the APIResponse.success() call and the subsequent audit logger
emit that follows it.

In `@src/permissionRbac/rolePermissionMapping/role-permission-mapping.service.ts`:
- Around line 112-135: The audit log emission in the method handling role
permission updates is occurring unconditionally, even when the repository update
operation does not affect any rows. Check the affected field from the result
returned by rolePermissionRepository.update() and only emit the audit event via
auditLoggerService.emit() when affected is greater than zero to prevent false
UPDATED events for no-op operations. Apply the same conditional logic pattern to
the deletePermission() method to prevent false DELETED events.

---

Nitpick comments:
In `@src/rbac/assign-role/assign-role.controller.ts`:
- Line 100: Remove the unused `@Req() request` parameter from both the
`deleteRole` method (at line 100) and the `bulkUpdateUserRoles` method (at line
118) in the assign-role.controller.ts file. These parameters are never used
within the methods and are inconsistent with the `create` and `getRole` methods
which do not inject the request parameter. Since the service uses
`requestContext` internally instead of the injected request, deleting these
parameters will clean up the code and improve consistency across all controller
methods.

In `@src/rbac/assign-role/assign-role.service.ts`:
- Line 163: The variable `request` is assigned from `requestContext.getStore()
as any` in the `getAssignedRole` method but is never used anywhere in the method
body. Remove the unused variable assignment line entirely to clean up the dead
code.

In `@src/rbac/role/role.controller.ts`:
- Around line 127-132: The `@Req`() request parameter is injected but never used
in three controller methods across two files; the service layer instead accesses
request context via requestContext.getStore() internally. Remove the unused
`@Req`() request: Request parameter from the following methods: deleteRole method
in src/rbac/role/role.controller.ts at lines 127-132, deleteRole method in
src/rbac/assign-role/assign-role.controller.ts at lines 100-106, and
bulkUpdateUserRoles method in src/rbac/assign-role/assign-role.controller.ts at
lines 117-135. This eliminates dead code while maintaining the same
functionality since the services do not depend on the request parameter being
passed.

In `@src/rbac/role/role.service.ts`:
- Line 161: The updateRole method declares a request variable by calling
requestContext.getStore() on line 161 but never uses it, creating dead code.
Remove this unused variable declaration since getAuditContext() internally reads
from the same store and makes the explicit extraction redundant. Apply the same
fix to the additional occurrences of this pattern mentioned at lines 167-175
within the same method.
- Around line 335-336: The variable request declared via
requestContext.getStore() in the deleteRole method is never used and should be
removed. Since getAuditContext() reads from the store internally, this variable
is redundant. Remove the unused request variable declaration from the deleteRole
method. The same unused variable pattern exists at other locations in the file
(lines 365-372), so remove the unused request variable from all affected methods
where it appears but is not referenced.

In `@src/user/user.controller.ts`:
- Around line 164-165: The updateUser method in the user controller has an
unused `@Req`() request parameter that was added to the method signature but is
never referenced in the method body. Since the service now reads the request
context internally via requestContext.getStore(), this dead parameter should be
removed. Delete the `@Req`() request: Request parameter from the updateUser method
signature entirely to clean up the unused import.

In `@src/user/user.service.ts`:
- Around line 3457-3459: Remove the unused variable assignment in the
suggestUsername method in src/user/user.service.ts. The request variable is
assigned from requestContext.getStore() but never referenced elsewhere in the
method, so delete the line that declares and assigns this unused variable.
- Line 139: Remove the unused request variable assignments that were left over
from a prior refactoring where request parameters were removed from method
signatures. In src/user/user.service.ts at line 139 (sendPasswordResetLink),
line 256 (forgotPassword), line 424 (searchUserMultiTenant), line 1275
(updateUser), and line 3458 (suggestUsername), delete the line `const request =
requestContext.getStore() as any;` from each method since the request variable
is never referenced in any of these methods' implementations.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 1c3dac30-3d32-4440-9893-868a3e8773b8

📥 Commits

Reviewing files that changed from the base of the PR and between 3e64bfd and 53f20f4.

📒 Files selected for processing (23)
  • package.json
  • src/app.module.ts
  • src/cohort/cohort.controller.ts
  • src/cohort/cohort.service.ts
  • src/cohortMembers/cohortMembers.controller.ts
  • src/cohortMembers/cohortMembers.service.ts
  • src/common/middleware/request-context.middleware.ts
  • src/common/utils/audit-helper.ts
  • src/common/utils/request-context.ts
  • src/fields/fields.controller.ts
  • src/fields/fields.service.ts
  • src/permissionRbac/rolePermissionMapping/role-permission-mapping.controller.ts
  • src/permissionRbac/rolePermissionMapping/role-permission-mapping.service.ts
  • src/rbac/assign-role/assign-role.controller.ts
  • src/rbac/assign-role/assign-role.service.ts
  • src/rbac/role/role.controller.ts
  • src/rbac/role/role.service.ts
  • src/sso/sso.service.ts
  • src/user/user.controller.ts
  • src/user/user.service.ts
  • src/userTenantMapping/user-tenant-mapping.controller.ts
  • src/userTenantMapping/user-tenant-mapping.service.ts
  • tsconfig.json
💤 Files with no reviewable changes (1)
  • src/sso/sso.service.ts

Comment thread src/cohortMembers/cohortMembers.service.ts
Comment thread src/permissionRbac/rolePermissionMapping/role-permission-mapping.service.ts Outdated
Comment thread src/rbac/assign-role/assign-role.service.ts Outdated
Comment thread src/rbac/assign-role/assign-role.service.ts
Comment thread src/rbac/assign-role/assign-role.service.ts
Comment thread src/rbac/role/role.service.ts Outdated
Comment thread src/user/user.service.ts Outdated
Comment thread src/userTenantMapping/user-tenant-mapping.service.ts

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
src/cohortMembers/cohortMembers.service.ts (2)

925-931: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Use the method res in the catch path, not the imported Express response prototype.

Line 926 passes response instead of res; this can break error responses during exception handling.

🐛 Minimal fix
-      return APIResponse.error(
-        response,
+      return APIResponse.error(
+        res,
         apiId,
         API_RESPONSES.INTERNAL_SERVER_ERROR,
         `Error : ${error.message}`,
         HttpStatus.INTERNAL_SERVER_ERROR
       );
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/cohortMembers/cohortMembers.service.ts` around lines 925 - 931, In the
catch block of the getCohortMembers or related error handling, the
APIResponse.error call is passing the imported Express response prototype
instead of the actual res parameter that was passed to the function. Replace the
`response` argument with `res` in the APIResponse.error() method call to ensure
error responses are properly sent through the correct response object.

785-797: ⚠️ Potential issue | 🔴 Critical

The validation check must use the object's isValid property, not check the object itself.

The validateCustomFieldByContext method always returns an object ({ isValid: boolean, error?: string }). The condition if (!customFieldValidate) will never be true because objects are truthy in JavaScript—invalid validation results will pass through this check uncaught.

Change line 791 to:

if (!customFieldValidate.isValid) {

This allows invalid custom field payloads to proceed past validation, creating a security and data integrity issue.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/cohortMembers/cohortMembers.service.ts` around lines 785 - 797, The
validation check for customFieldValidate is incorrectly testing the object
itself with if (!customFieldValidate) rather than checking its isValid property.
Since validateCustomFieldByContext returns an object (which is always truthy),
this condition will never properly detect validation failures. Fix this by
changing the condition to check customFieldValidate.isValid instead of the
entire object. This change must be made in the conditional statement immediately
following the validateCustomFieldByContext method call in the
cohortMembers.service.ts file to ensure invalid custom field payloads are
properly rejected.
🧹 Nitpick comments (1)
src/cohort/cohort.service.ts (1)

505-511: ⚡ Quick win

Remove the unused request parameter from updateCohortStatuses.

getAuditContext() is already used for actor/context resolution, so request is dead input and forces unnecessary controller/service coupling.

♻️ Suggested cleanup
-  public async updateCohortStatuses(
-    request: any,
-    cohortIds: string[],
+  public async updateCohortStatuses(
+    cohortIds: string[],
     status: string,
     updatedBy: string,
     res
   ) {

As per coding guidelines, this should adhere to NestJS best practices and maintain similar naming/parameter conventions across services.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/cohort/cohort.service.ts` around lines 505 - 511, The
`updateCohortStatuses` method has an unused `request` parameter that creates
unnecessary coupling between the controller and service layers. Remove the
`request` parameter from the method signature since context and actor
information is already resolved via the `getAuditContext()` call within the
method. Update any callers of this method to pass only the remaining parameters:
cohortIds, status, updatedBy, and res.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@src/cohortMembers/cohortMembers.service.ts`:
- Around line 925-931: In the catch block of the getCohortMembers or related
error handling, the APIResponse.error call is passing the imported Express
response prototype instead of the actual res parameter that was passed to the
function. Replace the `response` argument with `res` in the APIResponse.error()
method call to ensure error responses are properly sent through the correct
response object.
- Around line 785-797: The validation check for customFieldValidate is
incorrectly testing the object itself with if (!customFieldValidate) rather than
checking its isValid property. Since validateCustomFieldByContext returns an
object (which is always truthy), this condition will never properly detect
validation failures. Fix this by changing the condition to check
customFieldValidate.isValid instead of the entire object. This change must be
made in the conditional statement immediately following the
validateCustomFieldByContext method call in the cohortMembers.service.ts file to
ensure invalid custom field payloads are properly rejected.

---

Nitpick comments:
In `@src/cohort/cohort.service.ts`:
- Around line 505-511: The `updateCohortStatuses` method has an unused `request`
parameter that creates unnecessary coupling between the controller and service
layers. Remove the `request` parameter from the method signature since context
and actor information is already resolved via the `getAuditContext()` call
within the method. Update any callers of this method to pass only the remaining
parameters: cohortIds, status, updatedBy, and res.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 902e720f-8fb6-4be1-ad64-f2a8939247d3

📥 Commits

Reviewing files that changed from the base of the PR and between 53f20f4 and 9d5072a.

📒 Files selected for processing (10)
  • src/cohort/cohort.service.ts
  • src/cohortMembers/cohortMembers.service.ts
  • src/common/utils/request-context.ts
  • src/fields/fields.service.ts
  • src/permissionRbac/rolePermissionMapping/role-permission-mapping.service.ts
  • src/rbac/assign-role/assign-role.service.ts
  • src/rbac/role/role.service.ts
  • src/sso/sso.service.ts
  • src/user/user.service.ts
  • src/userTenantMapping/user-tenant-mapping.service.ts
💤 Files with no reviewable changes (1)
  • src/sso/sso.service.ts
🚧 Files skipped from review as they are similar to previous changes (4)
  • src/common/utils/request-context.ts
  • src/userTenantMapping/user-tenant-mapping.service.ts
  • src/user/user.service.ts
  • src/fields/fields.service.ts

@kkbiswal
kkbiswal changed the base branch from main to sdbv_rbac_changes June 16, 2026 09:20
Comment thread src/user/user.controller.ts
Comment thread src/user/user.controller.ts
Comment thread src/user/user.service.ts
Comment thread src/cohort/cohort.service.ts Outdated
@sonarqubecloud

Copy link
Copy Markdown

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