Skip to content

Conversation

@praveenKDass
Copy link
Collaborator

@praveenKDass praveenKDass commented Sep 9, 2025

Summary by CodeRabbit

  • New Features

    • Tenant, organization, and user create/update now emit routed events with richer structured payloads, per-field change details, related-organization info, and domain update events when enabled.
    • Events can be routed to Kafka or HTTP listeners via configurable paths.
  • Chores

    • New environment toggles, topics, and endpoint settings to enable/configure tenant, user, and organization events; some settings required when enabled.
  • Utilities

    • Added helper to compute per-field change deltas for event payloads.

@coderabbitai
Copy link

coderabbitai bot commented Sep 9, 2025

Walkthrough

Adds environment toggles and topics/listener keys for tenant, organization, and user events; adds tenant/org Kafka publishers; introduces DTO event payload builders and a change-detection util; emits structured create/update/domain-change events and renames broadcaster export to broadcastEvent.

Changes

Cohort / File(s) Summary
Env sample & validation
src/.env.sample, src/envVariables.js
Surface new env toggles and listener/topic keys for tenant/org/user events (e.g., ENABLE_TENANT_KAFKA_EVENTS, ENABLE_USER_KAFKA_EVENTS, EVENT_TENANT_KAFKA_TOPIC, EVENT_TENANT_LISTENER_API, EVENT_ORGANIZATION_KAFKA_TOPIC) and wire requiredIf dependencies (user topic now depends on EVENT_ENABLE_USER_KAFKA_EVENTS).
Kafka publishers
src/generics/kafka-communication.js
Add pushTenantEventsToKafka(message) and pushOrganizationEventsToKafka(message) mirroring existing user push function and export them.
Event broadcaster & routing
src/helpers/eventBroadcasterMain.js
Add tenantEvents handling to getEndpoints/isEventEnabled, switch Kafka dispatch to route by group (organization/user/tenant), update kafka enable checks to per-entity env flags, and rename/export broadcastUserEventbroadcastEvent.
DTO helpers
src/dtos/tenantDTO.js, src/dtos/organizationDTO.js
Add static eventBodyDTO({ entity, eventType, entityId, changedValues = [], args = {} }) to build sanitized, structured event payloads with per-field changes.
Change-detection util
src/generics/utils.js
Add extractUpdatedValues(oldData = {}, newData = {}, updateData = {}) to compute [{ fieldName, oldValue, newValue }] for keys present in updateData (uses deep equality) and export it.
Tenant service
src/services/tenant.js
Attach default org id/code to tenant responses; emit tenant create events using TenantDTO.eventBodyDTO and broadcastEvent; centralize update emissions via tenantEventEmitter; emit domain add/remove update events.
Organization service
src/services/organization.js
Introduce orgEventEmitter for create/update/related-org flows that computes deltas via utils.extractUpdatedValues, may fetch related_org_details, builds payloads with organizationDTO.eventBodyDTO, and broadcasts via broadcastEvent (keeps old/deprecated path alongside).
Admin / Account / User / Invite flows
src/services/admin.js, src/services/account.js, src/services/user.js, src/helpers/userInvite.js
Replace broadcastUserEvent with broadcastEvent; admin.deactivateOrg now uses returning update results and emits an organization update event via organizationDTO.eventBodyDTO.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant Service as Service (tenant/org/admin/user)
  participant Broadcaster as EventBroadcaster (broadcastEvent)
  participant KafkaComm as KafkaComm
  participant Kafka as Kafka
  participant Listener as External Listener

  Service->>Service: build event payload (DTO.eventBodyDTO)
  Service->>Broadcaster: broadcastEvent(group, payload, isInternal?)
  Broadcaster->>Broadcaster: isEventEnabled(group)?
  alt enabled
    Broadcaster->>KafkaComm: push<Group>EventsToKafka(payload)
    KafkaComm->>Kafka: produce to configured topic
    Broadcaster->>Listener: POST to configured listener API (if set)
  else disabled
    Broadcaster-->>Service: skip kafka/listener
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Poem

I hop with bright code in my tiny paws,
New topics bloom and listeners pause,
Tenants, orgs, and users sing their part,
I package changes and send them with heart,
From burrow to topic — dispatched with glee! 🥕🐇

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Title Check ⚠️ Warning The pull request title "kafka event for tenant" is partially related to the changeset but does not capture the main scope of changes. While the PR does add Kafka event support for tenant creation and updates, the changeset also introduces similar event broadcasting capabilities for organizations (EVENT_ENABLE_ORG_KAFKA_EVENTS, pushOrganizationEventsToKafka, organization event payloads). Additionally, the PR refactors the existing user event broadcasting system by renaming broadcastUserEvent to broadcastEvent and updating event emission across multiple services (admin, organization, user, account). The title focuses only on tenant events and misses the broader event system refactoring and organization event support that constitute significant portions of the changeset. Consider updating the title to reflect the broader scope of changes. A more accurate title would be something like "Add Kafka event broadcasting for tenant and organization entities" or "Extend event system to support tenant and organization events". This would better communicate that the PR introduces event capabilities for multiple entity types and includes refactoring of the event broadcasting system, not just tenant-specific changes.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch kafkaEvent_tenant

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
🧪 Early access (Sonnet 4.5): enabled

We are currently testing the Sonnet 4.5 model, which is expected to improve code review quality. However, this model may lead to increased noise levels in the review comments. Please disable the early access features if the noise level causes any inconvenience.

Note:

  • Public repositories are always opted into early access features.
  • You can enable or disable early access features from the CodeRabbit UI or by updating the CodeRabbit configuration file.

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

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

🧹 Nitpick comments (3)
src/.env.sample (1)

183-189: Clean up trailing whitespace, quoting, and ensure EOF newline.

  • Remove trailing spaces on Lines 183 and 185.
  • Drop quotes around values on Lines 187 and 189 for consistency with the rest of the file and to satisfy the linter.
  • Add a newline at EOF.

Apply this diff:

-# kafka topic for health check
-KAFKA_HEALTH_CHECK_TOPIC='user-health-check-topic-check'   
+# kafka topic for health check
+KAFKA_HEALTH_CHECK_TOPIC=user-health-check-topic-check
 #Enable / disable tenant create event 
-EVENT_ENABLE_TENANT_EVENTS=true 
+EVENT_ENABLE_TENANT_EVENTS=true
 #Event Kafka topic for tenant create
-EVENT_TENANT_KAFKA_TOPIC='dev.tenantCreate'
+EVENT_TENANT_KAFKA_TOPIC=dev.tenantCreate
 #Event API for tenant create , can be comma separated
-EVENT_TENANT_LISTENER_API='http://interface:3567/scp/v1/tenant/add'
+EVENT_TENANT_LISTENER_API=http://interface:3567/scp/v1/tenant/add
+

Additionally, consider reordering keys to satisfy the UnorderedKey hints, but that’s optional for functionality.

src/services/tenant.js (2)

375-376: Avoid mutating the DB response object (minor).

Prefer local consts over adding ad-hoc fields to tenantCreateResponse to keep shapes predictable.

Apply this diff:

-tenantCreateResponse.orgId = defaultOrgCreateResponse?.result?.id
-tenantCreateResponse.orgCode = defaultOrgCreateResponse?.result?.code
+const orgId = defaultOrgCreateResponse?.result?.id
+const orgCode = defaultOrgCreateResponse?.result?.code

And use orgId/orgCode below when building the event.


408-409: Optionally await broadcast to catch failures.

If event delivery matters for auditing/reliability, await broadcastUserEvent(...) so failures are observable within this flow.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 11b1cf2 and cc751ff.

📒 Files selected for processing (5)
  • src/.env.sample (1 hunks)
  • src/envVariables.js (1 hunks)
  • src/generics/kafka-communication.js (2 hunks)
  • src/helpers/eventBroadcasterMain.js (3 hunks)
  • src/services/tenant.js (3 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
src/services/**

⚙️ CodeRabbit configuration file

This is core business logic. Please check for correctness, efficiency, and potential edge cases.

Files:

  • src/services/tenant.js
🧬 Code graph analysis (2)
src/helpers/eventBroadcasterMain.js (5)
src/services/org-admin.js (1)
  • kafkaCommunication (15-15)
src/services/organization.js (1)
  • kafkaCommunication (11-11)
src/services/account.js (1)
  • kafkaCommunication (20-20)
src/helpers/userInvite.js (2)
  • kafkaCommunication (17-17)
  • requestBody (941-945)
src/services/userInvite.js (2)
  • kafkaCommunication (24-24)
  • requestBody (379-383)
src/services/tenant.js (2)
src/services/account.js (8)
  • require (26-26)
  • require (27-27)
  • require (37-37)
  • require (41-41)
  • UserTransformDTO (38-38)
  • eventBody (604-623)
  • eventBody (1754-1769)
  • common (16-16)
src/helpers/userInvite.js (8)
  • require (18-18)
  • require (23-23)
  • require (27-27)
  • require (28-28)
  • require (29-29)
  • eventBody (882-896)
  • eventBody (1125-1130)
  • common (8-8)
🪛 dotenv-linter (3.3.0)
src/.env.sample

[warning] 183-183: [TrailingWhitespace] Trailing whitespace detected

(TrailingWhitespace)


[warning] 183-183: [UnorderedKey] The KAFKA_HEALTH_CHECK_TOPIC key should go before the NOTIFICATION_API_URL key

(UnorderedKey)


[warning] 185-185: [TrailingWhitespace] Trailing whitespace detected

(TrailingWhitespace)


[warning] 185-185: [UnorderedKey] The EVENT_ENABLE_TENANT_EVENTS key should go before the EVENT_ENABLE_USER_EVENTS key

(UnorderedKey)


[warning] 187-187: [QuoteCharacter] The value has quote characters (', ")

(QuoteCharacter)


[warning] 187-187: [UnorderedKey] The EVENT_TENANT_KAFKA_TOPIC key should go before the EVENT_USER_KAFKA_TOPIC key

(UnorderedKey)


[warning] 189-189: [EndingBlankLine] No blank line at the end of the file

(EndingBlankLine)


[warning] 189-189: [QuoteCharacter] The value has quote characters (', ")

(QuoteCharacter)


[warning] 189-189: [UnorderedKey] The EVENT_TENANT_LISTENER_API key should go before the EVENT_USER_KAFKA_TOPIC key

(UnorderedKey)

🪛 Biome (2.1.2)
src/helpers/eventBroadcasterMain.js

[error] 14-16: This case is falling through to the next case.

Add a break or return statement to the end of this case to prevent fallthrough.

(lint/suspicious/noFallthroughSwitchClause)

🔇 Additional comments (5)
src/generics/kafka-communication.js (2)

28-36: New tenant Kafka producer mirrors existing patterns — OK.

Payload shape and error handling are consistent with user-events. Ensure EVENT_TENANT_KAFKA_TOPIC is set (see env validation comment).


60-65: Export added — OK.

Exporting pushTenantEventsToKafka is correct and matches the new broadcaster routing.

src/envVariables.js (1)

431-439: EVENT_TENANT_LISTENER_API requirement — OK.

Tying the HTTP listener requirement to EVENT_ENABLE_TENANT_EVENTS is appropriate.

src/services/tenant.js (1)

33-34: Imports — OK.

Correct modules imported for broadcasting and DTO creation.

src/helpers/eventBroadcasterMain.js (1)

32-34: Enablement flag for tenant API events — OK.

Matches the pattern used for other groups.

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

Caution

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

⚠️ Outside diff range comments (1)
src/services/organization.js (1)

137-149: Prevent duplicate organization event emission — remove or gate legacy call

The create flow calls eventBroadcasterMain('organizationEvents', eventBody) and then later calls broadcastUserEvent('organizationEvents', eventBodyData) (which itself invokes eventBroadcasterMain + Kafka), resulting in two organizationEvents being emitted. Remove the early legacy event or gate it behind an ENV flag (suggest: EVENT_ENABLE_LEGACY_ORG_EVENTS) so only one emission occurs.

Location: src/services/organization.js — legacy call at ~lines 137–149; new broadcastUserEvent call at ~line 171.

🧹 Nitpick comments (3)
src/.env.sample (1)

183-193: Fix dotenv nits: trailing whitespace, quotes, EOF newline.

Removes trailing spaces, unquotes simple values/URLs, and ensures a final newline. Reordering keys can be done later to satisfy UnorderedKey warnings.

-# kafka topic for health check
-KAFKA_HEALTH_CHECK_TOPIC='user-health-check-topic-check'   
+# kafka topic for health check
+KAFKA_HEALTH_CHECK_TOPIC=user-health-check-topic-check
 #Enable / disable tenant create event 
-EVENT_ENABLE_TENANT_EVENTS=true 
+EVENT_ENABLE_TENANT_EVENTS=true
 #Event Kafka topic for tenant create
-EVENT_TENANT_KAFKA_TOPIC='dev.tenantCreate'
+EVENT_TENANT_KAFKA_TOPIC=dev.tenantCreate
 #Event API for tenant create , can be comma separated
-EVENT_TENANT_LISTENER_API='http://interface:3567/scp/v1/tenant/add'
+EVENT_TENANT_LISTENER_API=http://interface:3567/scp/v1/tenant/add
 #Enable / disable organization  event 
-EVENT_ENABLE_ORGANIZATION_EVENTS=true 
+EVENT_ENABLE_ORGANIZATION_EVENTS=true
 #Event Kafka topic for organization create/update
-EVENT_ORGANIZATION_KAFKA_TOPIC='dev.organizationEvent'
+EVENT_ORGANIZATION_KAFKA_TOPIC=dev.organizationEvent
+
src/services/organization.js (2)

559-586: Potential double-publish on related_orgs add.

You broadcast legacy updateRelatedOrgs and also emit the new DTO-based update via orgEventEmitter. Confirm consumers won’t receive duplicates; otherwise gate/remove one path.


637-666: Potential double-publish on related_orgs remove.

Same duplication risk as addRelatedOrg.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between cc751ff and b8c16cc.

📒 Files selected for processing (9)
  • src/.env.sample (1 hunks)
  • src/dtos/organizationDTO.js (1 hunks)
  • src/dtos/tenantDTO.js (1 hunks)
  • src/envVariables.js (1 hunks)
  • src/generics/kafka-communication.js (2 hunks)
  • src/generics/utils.js (2 hunks)
  • src/services/admin.js (3 hunks)
  • src/services/organization.js (9 hunks)
  • src/services/tenant.js (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/envVariables.js
  • src/services/tenant.js
🧰 Additional context used
📓 Path-based instructions (1)
src/services/**

⚙️ CodeRabbit configuration file

This is core business logic. Please check for correctness, efficiency, and potential edge cases.

Files:

  • src/services/admin.js
  • src/services/organization.js
🧬 Code graph analysis (5)
src/generics/utils.js (2)
src/database/migrations/20240502174600-add_email_templates.js (1)
  • updateData (177-177)
src/services/organization.js (2)
  • changes (833-833)
  • _ (13-13)
src/services/admin.js (1)
src/services/organization.js (9)
  • organizationDTO (19-19)
  • require (12-12)
  • require (14-14)
  • require (15-15)
  • require (18-18)
  • require (22-22)
  • eventBodyData (151-169)
  • eventBodyData (845-865)
  • common (2-2)
src/dtos/tenantDTO.js (3)
src/dtos/eventBody.js (1)
  • disallowedArgs (8-8)
src/helpers/userInvite.js (1)
  • args (1105-1119)
src/generics/utils.js (4)
  • changes (1096-1096)
  • fieldName (1101-1101)
  • oldValue (1099-1099)
  • newValue (1100-1100)
src/dtos/organizationDTO.js (3)
src/dtos/eventBody.js (1)
  • disallowedArgs (8-8)
src/helpers/userInvite.js (1)
  • args (1105-1119)
src/generics/utils.js (5)
  • changes (1096-1096)
  • result (402-402)
  • fieldName (1101-1101)
  • oldValue (1099-1099)
  • newValue (1100-1100)
src/services/organization.js (4)
src/services/admin.js (9)
  • require (11-11)
  • require (27-27)
  • require (34-34)
  • require (36-36)
  • require (37-37)
  • eventBodyData (705-720)
  • organizationDTO (43-43)
  • common (15-15)
  • utils (17-17)
src/services/account.js (6)
  • require (26-26)
  • require (27-27)
  • require (37-37)
  • require (41-41)
  • common (16-16)
  • utils (25-25)
src/database/queries/organization.js (3)
  • require (2-2)
  • require (3-3)
  • common (4-4)
src/generics/utils.js (2)
  • common (20-20)
  • changes (1096-1096)
🪛 dotenv-linter (3.3.0)
src/.env.sample

[warning] 183-183: [TrailingWhitespace] Trailing whitespace detected

(TrailingWhitespace)


[warning] 183-183: [UnorderedKey] The KAFKA_HEALTH_CHECK_TOPIC key should go before the NOTIFICATION_API_URL key

(UnorderedKey)


[warning] 185-185: [TrailingWhitespace] Trailing whitespace detected

(TrailingWhitespace)


[warning] 185-185: [UnorderedKey] The EVENT_ENABLE_TENANT_EVENTS key should go before the EVENT_ENABLE_USER_EVENTS key

(UnorderedKey)


[warning] 187-187: [QuoteCharacter] The value has quote characters (', ")

(QuoteCharacter)


[warning] 187-187: [UnorderedKey] The EVENT_TENANT_KAFKA_TOPIC key should go before the EVENT_USER_KAFKA_TOPIC key

(UnorderedKey)


[warning] 189-189: [QuoteCharacter] The value has quote characters (', ")

(QuoteCharacter)


[warning] 189-189: [UnorderedKey] The EVENT_TENANT_LISTENER_API key should go before the EVENT_USER_KAFKA_TOPIC key

(UnorderedKey)


[warning] 191-191: [TrailingWhitespace] Trailing whitespace detected

(TrailingWhitespace)


[warning] 191-191: [UnorderedKey] The EVENT_ENABLE_ORGANIZATION_EVENTS key should go before the EVENT_ENABLE_TENANT_EVENTS key

(UnorderedKey)


[warning] 193-193: [EndingBlankLine] No blank line at the end of the file

(EndingBlankLine)


[warning] 193-193: [QuoteCharacter] The value has quote characters (', ")

(QuoteCharacter)


[warning] 193-193: [UnorderedKey] The EVENT_ORGANIZATION_KAFKA_TOPIC key should go before the EVENT_TENANT_KAFKA_TOPIC key

(UnorderedKey)

🔇 Additional comments (1)
src/services/admin.js (1)

703-723: Clarify: 'delete' event vs soft-deactivate & ensure deleted_at is delivered

src/services/admin.js (lines 703–723)

  • Emits eventType: 'delete' but the DB change in this block only sets status = INACTIVE (no persisted deleted_at). Confirm contract: if this is a soft-deactivate, change eventType → 'update' and remove deleted/deleted_at from the payload; if it must be a delete, persist deleted_at and set status = DELETED before publishing.
  • organizationDTO currently strips deleted_at (per DTO fix). Either have the DTO include deleted_at or stop adding deleted_at here so consumers get a consistent payload.
  • Null-safety: guard updatedRows?.[0] (deletedOrgDetails) before dereferencing .id/.created_by.

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

♻️ Duplicate comments (3)
src/dtos/tenantDTO.js (2)

23-52: Duplicate code - same issue as organizationDTO

This method has the same implementation as organizationDTO.eventBodyDTO, creating code duplication.

Please refer to the comment in organizationDTO.js about extracting common event body DTO logic.


37-38: Consider preserving undefined values

Same issue as in organizationDTO - the current logic won't preserve legitimate falsy values.

src/envVariables.js (1)

418-430: Clarify toggle description and Kafka-topic requirement coupling.

  • The message says “kafka event” but the flag gates tenant events generally. Update message to avoid confusion.
  • Re: tying EVENT_TENANT_KAFKA_TOPIC to EVENT_ENABLE_TENANT_EVENTS (vs EVENT_ENABLE_KAFKA_PUSH) was discussed earlier; risk remains that Kafka is enabled without enforcing the topic.
-  message: 'Key to toggle tenant creation kafka event',
+  message: 'Key to toggle tenant events (HTTP/Kafka)',

Confirm broadcaster behavior when EVENT_ENABLE_KAFKA_PUSH=true but EVENT_ENABLE_TENANT_EVENTS=false to ensure no runtime missing-topic errors.

🧹 Nitpick comments (12)
src/.env.sample (5)

183-183: Remove trailing whitespace

Line 183 has trailing whitespace that should be removed.

-KAFKA_HEALTH_CHECK_TOPIC='user-health-check-topic-check'   
+KAFKA_HEALTH_CHECK_TOPIC='user-health-check-topic-check'

185-185: Remove trailing whitespace

Line 185 has trailing whitespace that should be removed.

-EVENT_ENABLE_TENANT_EVENTS=true 
+EVENT_ENABLE_TENANT_EVENTS=true

187-187: Inconsistent quote usage in Kafka topic values

The Kafka topic values use single quotes while other string values in the file don't use quotes at all. Consider removing the quotes for consistency.

-EVENT_TENANT_KAFKA_TOPIC='dev.tenantCreate'
+EVENT_TENANT_KAFKA_TOPIC=dev.tenantCreate
-EVENT_TENANT_LISTENER_API='http://interface:3567/scp/v1/tenant/add'
+EVENT_TENANT_LISTENER_API=http://interface:3567/scp/v1/tenant/add
-EVENT_ORGANIZATION_KAFKA_TOPIC='dev.organizationEvent'
+EVENT_ORGANIZATION_KAFKA_TOPIC=dev.organizationEvent

Also applies to: 189-189, 193-193


191-191: Remove trailing whitespace

Line 191 has trailing whitespace that should be removed.

-EVENT_ENABLE_ORGANIZATION_EVENTS=true 
+EVENT_ENABLE_ORGANIZATION_EVENTS=true

193-193: Add blank line at end of file

The file should end with a blank line for better compatibility with various tools.

 EVENT_ORGANIZATION_KAFKA_TOPIC='dev.organizationEvent'
+
src/dtos/organizationDTO.js (2)

14-43: Consider code reuse to avoid duplication

The eventBodyDTO method is nearly identical to the one in tenantDTO.js. This duplication could lead to maintenance issues if the logic needs to change in the future.

Consider extracting the common event body DTO logic into a shared utility function that both organizationDTO and tenantDTO can use:

// In a new file: src/dtos/eventUtils.js
function createEventBodyDTO({ entity, eventType, entityId, changedValues = [], args = {} }) {
    try {
        if (!entity || !eventType || !entityId)
            throw new Error('Entity, EventType & EntityId values are mandatory for an Event')

        const disallowedArgs = ['deleted_at']

        disallowedArgs.forEach((key) => {
            delete args[key]
        })

        const changes = changedValues.reduce((result, obj) => {
            const { fieldName, oldValue, newValue } = obj
            if (!result[fieldName]) result[fieldName] = {}
            if (oldValue) result[fieldName].oldValue = oldValue
            if (newValue) result[fieldName].newValue = newValue
            return result
        }, {})
        return {
            entity,
            eventType,
            entityId,
            changes,
            ...args,
        }
    } catch (error) {
        console.error(error)
        return false
    }
}

module.exports = { createEventBodyDTO }

Then use it in both DTOs:

const { createEventBodyDTO } = require('./eventUtils')

static eventBodyDTO(params) {
    return createEventBodyDTO(params)
}

28-29: Consider preserving undefined values

The current logic only adds oldValue and newValue if they're truthy, which means it won't preserve legitimate falsy values like 0, false, or empty strings.

-if (oldValue) result[fieldName].oldValue = oldValue
-if (newValue) result[fieldName].newValue = newValue
+if (oldValue !== undefined) result[fieldName].oldValue = oldValue
+if (newValue !== undefined) result[fieldName].newValue = newValue
src/generics/utils.js (2)

1099-1101: Redundant variable assignment

The fieldName variable is assigned the same value as key and could be eliminated.

 for (let key of Object.keys(updateData)) {
     let oldValue = oldData[key]
     let newValue = newData[key]
-    let fieldName = key
     // Compare only if key exists in updateData
     if (!_.isEqual(oldValue, newValue)) {
-        changes.push({ fieldName, oldValue, newValue })
+        changes.push({ fieldName: key, oldValue, newValue })
     }
 }

1095-1109: Consider handling null/undefined inputs

The function assumes oldData, newData, and updateData are always objects. Consider adding defensive checks to prevent runtime errors.

 function getChanges(oldData, newData, updateData) {
     let changes = []
+    
+    // Guard against null/undefined inputs
+    if (!updateData || typeof updateData !== 'object') {
+        return changes
+    }
+    
+    oldData = oldData || {}
+    newData = newData || {}
 
     for (let key of Object.keys(updateData)) {
src/services/admin.js (1)

705-720: Consider adding event emission error handling

The event emission for organization deletion should be wrapped in a try-catch to prevent failures from affecting the main deactivation flow.

 //event Body for org create
 let deletedOrgDetails = orgRowsAffected.updatedRows?.[0]
+if (deletedOrgDetails) {
+    try {
         const eventBodyData = organizationDTO.eventBodyDTO({
             entity: 'organization',
             eventType: 'delete',
             entityId: deletedOrgDetails.id,
             args: {
                 created_by: deletedOrgDetails.created_by,
                 name: deletedOrgDetails.name,
                 code: deletedOrgDetails.code,
                 deleted_at: deletedOrgDetails?.deleted_at || new Date(),
                 updated_at: deletedOrgDetails?.updated_at || new Date(),
                 status: deletedOrgDetails?.status || common.INACTIVE_STATUS,
                 deleted: true,
                 id: deletedOrgDetails.id,
                 tenant_code: tenantCode,
             },
         })
 
         broadcastUserEvent('organizationEvents', { requestBody: eventBodyData, isInternal: true })
+    } catch (eventError) {
+        console.error('Failed to emit organization deletion event:', eventError)
+        // Don't throw - continue with the successful deactivation response
+    }
+}
src/services/tenant.js (1)

376-378: Minor: avoid mutating the DB response object.
Prefer local consts (orgId/orgCode) over augmenting tenantCreateResponse to reduce accidental coupling.

-tenantCreateResponse.orgId = defaultOrgCreateResponse?.result?.id
-tenantCreateResponse.orgCode = defaultOrgCreateResponse?.result?.code
+const orgId = defaultOrgCreateResponse?.result?.id
+const orgCode = defaultOrgCreateResponse?.result?.code

…and use orgId/orgCode below.

src/envVariables.js (1)

431-439: Revisit requirement of EVENT_TENANT_LISTENER_API.
If Kafka-only deployments are allowed, making the HTTP listener required when enabling tenant events may overconstrain env setup.

Consider a separate HTTP toggle (parity with users), or require the API URL only when HTTP is enabled.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between cc751ff and bea1528.

📒 Files selected for processing (10)
  • src/.env.sample (1 hunks)
  • src/dtos/organizationDTO.js (1 hunks)
  • src/dtos/tenantDTO.js (1 hunks)
  • src/envVariables.js (1 hunks)
  • src/generics/kafka-communication.js (2 hunks)
  • src/generics/utils.js (2 hunks)
  • src/helpers/eventBroadcasterMain.js (3 hunks)
  • src/services/admin.js (3 hunks)
  • src/services/organization.js (9 hunks)
  • src/services/tenant.js (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/helpers/eventBroadcasterMain.js
  • src/generics/kafka-communication.js
🧰 Additional context used
📓 Path-based instructions (1)
src/services/**

⚙️ CodeRabbit configuration file

This is core business logic. Please check for correctness, efficiency, and potential edge cases.

Files:

  • src/services/tenant.js
  • src/services/admin.js
  • src/services/organization.js
🧬 Code graph analysis (5)
src/dtos/organizationDTO.js (2)
src/dtos/eventBody.js (1)
  • disallowedArgs (8-8)
src/generics/utils.js (5)
  • changes (1096-1096)
  • result (402-402)
  • fieldName (1101-1101)
  • oldValue (1099-1099)
  • newValue (1100-1100)
src/dtos/tenantDTO.js (2)
src/dtos/eventBody.js (1)
  • disallowedArgs (8-8)
src/generics/utils.js (4)
  • changes (1096-1096)
  • fieldName (1101-1101)
  • oldValue (1099-1099)
  • newValue (1100-1100)
src/generics/utils.js (2)
src/database/migrations/20240502174600-add_email_templates.js (1)
  • updateData (177-177)
src/services/organization.js (2)
  • changes (833-833)
  • _ (13-13)
src/services/admin.js (1)
src/services/organization.js (9)
  • organizationDTO (19-19)
  • require (12-12)
  • require (14-14)
  • require (15-15)
  • require (18-18)
  • require (22-22)
  • eventBodyData (151-169)
  • eventBodyData (845-865)
  • common (2-2)
src/services/organization.js (4)
src/services/admin.js (9)
  • require (11-11)
  • require (27-27)
  • require (34-34)
  • require (36-36)
  • require (37-37)
  • eventBodyData (705-720)
  • organizationDTO (43-43)
  • common (15-15)
  • utils (17-17)
src/database/queries/organization.js (3)
  • require (2-2)
  • require (3-3)
  • common (4-4)
src/generics/utils.js (2)
  • common (20-20)
  • changes (1096-1096)
src/dtos/eventBody.js (1)
  • changes (11-17)
🪛 dotenv-linter (3.3.0)
src/.env.sample

[warning] 183-183: [TrailingWhitespace] Trailing whitespace detected

(TrailingWhitespace)


[warning] 183-183: [UnorderedKey] The KAFKA_HEALTH_CHECK_TOPIC key should go before the NOTIFICATION_API_URL key

(UnorderedKey)


[warning] 185-185: [TrailingWhitespace] Trailing whitespace detected

(TrailingWhitespace)


[warning] 185-185: [UnorderedKey] The EVENT_ENABLE_TENANT_EVENTS key should go before the EVENT_ENABLE_USER_EVENTS key

(UnorderedKey)


[warning] 187-187: [QuoteCharacter] The value has quote characters (', ")

(QuoteCharacter)


[warning] 187-187: [UnorderedKey] The EVENT_TENANT_KAFKA_TOPIC key should go before the EVENT_USER_KAFKA_TOPIC key

(UnorderedKey)


[warning] 189-189: [QuoteCharacter] The value has quote characters (', ")

(QuoteCharacter)


[warning] 189-189: [UnorderedKey] The EVENT_TENANT_LISTENER_API key should go before the EVENT_USER_KAFKA_TOPIC key

(UnorderedKey)


[warning] 191-191: [TrailingWhitespace] Trailing whitespace detected

(TrailingWhitespace)


[warning] 191-191: [UnorderedKey] The EVENT_ENABLE_ORGANIZATION_EVENTS key should go before the EVENT_ENABLE_TENANT_EVENTS key

(UnorderedKey)


[warning] 193-193: [EndingBlankLine] No blank line at the end of the file

(EndingBlankLine)


[warning] 193-193: [QuoteCharacter] The value has quote characters (', ")

(QuoteCharacter)


[warning] 193-193: [UnorderedKey] The EVENT_ORGANIZATION_KAFKA_TOPIC key should go before the EVENT_TENANT_KAFKA_TOPIC key

(UnorderedKey)

🔇 Additional comments (10)
src/generics/utils.js (1)

1095-1109: LGTM! Clean implementation for change tracking

The getChanges function is well-implemented with proper deep comparison using lodash's isEqual method. It correctly iterates only over the keys in updateData to track changes.

src/services/admin.js (1)

656-666: Good implementation of event emission for organization deletion

The code correctly captures the updated organization details using the returning: true option and emits the deletion event with appropriate metadata.

src/services/tenant.js (2)

33-35: LGTM: imports align with new event flow.


388-407: Align payload shape and await broadcaster.

  • Nest meta (avoid spreading) to prevent key collisions and match org event shape.
-					...(tenantCreateResponse.meta || {}),
+					meta: tenantCreateResponse?.meta || {},
-					org_id: tenantCreateResponse.orgId,
-					org_code: tenantCreateResponse.orgCode,
+					org_id: orgId ?? tenantCreateResponse.orgId,
+					org_code: orgCode ?? tenantCreateResponse.orgCode,
  • Await the broadcaster (or explicitly catch) to avoid unhandled rejections / dropped events under load.
-broadcastUserEvent('tenantEvents', { requestBody: eventBody, isInternal: true })
+await broadcastUserEvent('tenantEvents', { requestBody: eventBody, isInternal: true })

Confirm TenantDTO.eventBodyDTO expects args.meta (nested) rather than flattened meta keys.

Location: src/services/tenant.js lines 388-407 (also applies to 409-410).

src/services/organization.js (6)

22-22: LGTM: shared broadcaster import matches usage elsewhere.


253-254: LGTM: centralizing update emissions via orgEventEmitter is clean.


559-571: LGTM: related_orgs update with returning:true/RAW for delta calc.


586-589: LGTM: emit after related_orgs append for accurate “after” state.


637-648: LGTM: removal path mirrors add path with returning:true for emitter.


664-667: LGTM: emits removal changes with provided related_orgs delta.

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

♻️ Duplicate comments (2)
src/generics/utils.js (1)

1095-1109: Null-safe keys iteration and consts (optional; aligns with prior discussion).

Using default params doesn’t guard against null or non-objects. If service-level validation always guarantees an object (as noted), consider this a low-priority hardening; also prefer const here.

-function getChanges(oldData = {}, newData = {}, updateData = {}) {
+function getChanges(oldData = {}, newData = {}, updateData = {}) {
   const changes = []
 
-  for (const key of Object.keys(updateData)) {
-    let oldValue = oldData[key]
-    let newValue = newData[key]
-    let fieldName = key
+  for (const key of Object.keys(updateData || {})) {
+    const oldValue = oldData[key]
+    const newValue = newData[key]
+    const fieldName = key
     // Compare only if key exists in updateData
     if (!_.isEqual(oldValue, newValue)) {
       changes.push({ fieldName, oldValue, newValue })
     }
   }
 
   return changes
 }
src/services/admin.js (1)

668-668: Fix confirmed: correct rowsAffected check

if (!orgUpdateResult || orgUpdateResult.rowsAffected === 0) resolves the prior type/shape bug from earlier revisions.

🧹 Nitpick comments (4)
src/generics/utils.js (2)

1085-1093: Tighten JSDoc wording and param description.

Clarify intent and what updateData represents.

 /**
- * Compare object and get the updated value with old value
+ * Compute per-field changes between oldData and newData,
+ * restricted to keys present in updateData.
  * @method
  * @name getChanges
  * @param {Object} oldData - Data before update
  * @param {Object} newData - Data after update
- * @param {Object} updateData - reqBody data
+ * @param {Object} updateData - reqBody subset (keys to inspect)
  * @returns {Array<{fieldName:string, oldValue:any, newValue:any}>}
  */

1099-1105: Confirm array ordering semantics; optionally ignore order in equality.

If any inspected fields are arrays where order is not meaningful, _.isEqual will flag reordering as a change. If order should be ignored, consider a custom comparator.

-    if (!_.isEqual(oldValue, newValue)) {
+    const equal = _.isEqualWith(oldValue, newValue, (a, b) => {
+      if (Array.isArray(a) && Array.isArray(b)) {
+        return _.isEqual([...a].sort(), [...b].sort())
+      }
+      return undefined // fall back to default comparison
+    })
+    if (!equal) {
       changes.push({ fieldName, oldValue, newValue })
     }
src/services/admin.js (2)

703-704: Nit: misleading comment

This block emits a deactivation event, not “org create.”

-//event Body for org create
+// Event body for organization deactivation/update

705-721: Emit 'update' (not 'delete') for org soft-deactivate

organization.js emits org changes as eventType: 'update' and organizationDTO accepts empty changedValues; admin.js currently emits eventType: 'delete' when soft-deactivating — inconsistent and risks downstream consumers treating this as a hard delete. Change to an 'update' event, include changedValues ['status','deleted'] and updated_by.

Location: src/services/admin.js (around lines 704–719)

- let deletedOrgDetails = orgUpdateResult.updatedRows?.[0]
- const eventBodyData = organizationDTO.eventBodyDTO({
-   entity: 'organization',
-   eventType: 'delete',
-   entityId: deletedOrgDetails.id,
-   args: {
-     created_by: deletedOrgDetails.created_by,
-     name: deletedOrgDetails.name,
-     code: deletedOrgDetails.code,
-     deleted_at: deletedOrgDetails?.deleted_at || new Date(),
-     updated_at: deletedOrgDetails?.updated_at || new Date(),
-     status: deletedOrgDetails?.status || common.INACTIVE_STATUS,
-     deleted: true,
-     id: deletedOrgDetails.id,
-     tenant_code: tenantCode,
-   },
- })
+ const updatedOrgDetails = orgUpdateResult.updatedRows?.[0]
+ const eventBodyData = organizationDTO.eventBodyDTO({
+   entity: 'organization',
+   eventType: 'update',
+   changedValues: ['status', 'deleted'],
+   entityId: updatedOrgDetails.id,
+   args: {
+     created_by: updatedOrgDetails.created_by,
+     updated_by: loggedInUserId,
+     name: updatedOrgDetails.name,
+     code: updatedOrgDetails.code,
+     deleted_at: updatedOrgDetails?.deleted_at || new Date(),
+     updated_at: updatedOrgDetails?.updated_at || new Date(),
+     status: updatedOrgDetails?.status || common.INACTIVE_STATUS,
+     deleted: true,
+     id: updatedOrgDetails.id,
+     tenant_code: tenantCode,
+   },
+ })
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bea1528 and fd8d901.

📒 Files selected for processing (3)
  • src/generics/utils.js (2 hunks)
  • src/services/admin.js (3 hunks)
  • src/services/organization.js (9 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/services/organization.js
🧰 Additional context used
📓 Path-based instructions (1)
src/services/**

⚙️ CodeRabbit configuration file

This is core business logic. Please check for correctness, efficiency, and potential edge cases.

Files:

  • src/services/admin.js
🧠 Learnings (9)
📓 Common learnings
Learnt from: praveenKDass
PR: ELEVATE-Project/user#816
File: src/generics/kafka-communication.js:28-0
Timestamp: 2025-09-12T10:02:11.442Z
Learning: In the ELEVATE-Project/user codebase, EVENT_TENANT_KAFKA_TOPIC uses conditional validation (requiredIf) in src/envVariables.js, not default values. When EVENT_ENABLE_TENANT_EVENTS equals 'true', the topic becomes required and application startup validation ensures it's present, preventing runtime issues with undefined topics.
Learnt from: praveenKDass
PR: ELEVATE-Project/user#816
File: src/generics/kafka-communication.js:28-0
Timestamp: 2025-09-12T10:02:11.442Z
Learning: In the ELEVATE-Project/user codebase, environment variables use conditional validation via requiredIf logic in src/envVariables.js rather than default values. When EVENT_ENABLE_TENANT_EVENTS equals 'true', EVENT_TENANT_KAFKA_TOPIC becomes required and startup validation ensures it's present.
Learnt from: praveenKDass
PR: ELEVATE-Project/user#816
File: src/services/organization.js:831-0
Timestamp: 2025-09-12T10:40:34.795Z
Learning: In the ELEVATE-Project/user codebase, the organizationDTO.eventBodyDTO is designed to handle empty changes arrays properly, so organization update events should always be emitted even when changedValues is empty. The DTO handles this scenario correctly and there may be other reasons to emit events beyond just field changes (like metadata updates).
📚 Learning: 2025-09-12T10:35:28.237Z
Learnt from: praveenKDass
PR: ELEVATE-Project/user#816
File: src/services/admin.js:704-704
Timestamp: 2025-09-12T10:35:28.237Z
Learning: In ELEVATE-Project/user admin.js, there's an inconsistency in organizationQueries.update usage: addOrgAdmin calls it without options (returns number), while deactivateOrg calls it with { returning: true, raw: true } (returns object). The validation logic 'if (orgRowsAffected === 0)' only works for the first case - the second case needs 'if (orgRowsAffected.rowsAffected === 0)' to properly validate updates.

Applied to files:

  • src/services/admin.js
📚 Learning: 2025-09-12T10:35:28.237Z
Learnt from: praveenKDass
PR: ELEVATE-Project/user#816
File: src/services/admin.js:704-704
Timestamp: 2025-09-12T10:35:28.237Z
Learning: In the ELEVATE-Project/user codebase, when organizationQueries.update is called with { returning: true, raw: true }, it returns an object { rowsAffected, updatedRows }, not just a number. The validation logic in admin.js deactivateOrg method checking 'if (orgRowsAffected === 0)' is incorrect and should be 'if (orgRowsAffected.rowsAffected === 0)' to properly validate successful updates.

Applied to files:

  • src/services/admin.js
📚 Learning: 2025-09-12T10:40:34.795Z
Learnt from: praveenKDass
PR: ELEVATE-Project/user#816
File: src/services/organization.js:831-0
Timestamp: 2025-09-12T10:40:34.795Z
Learning: In the ELEVATE-Project/user codebase, the organizationDTO.eventBodyDTO is designed to handle empty changes arrays properly, so organization update events should always be emitted even when changedValues is empty. The DTO handles this scenario correctly and there may be other reasons to emit events beyond just field changes (like metadata updates).

Applied to files:

  • src/services/admin.js
📚 Learning: 2025-09-12T10:35:28.237Z
Learnt from: praveenKDass
PR: ELEVATE-Project/user#816
File: src/services/admin.js:704-704
Timestamp: 2025-09-12T10:35:28.237Z
Learning: In the ELEVATE-Project/user codebase, when using organizationQueries.update with { returning: true, raw: true } options, the method consistently returns a structured result with updatedRows property when successful. The initial check for orgRowsAffected === 0 already handles failed updates, so additional null checking for updatedRows is redundant when the code execution reaches the event emission logic.

Applied to files:

  • src/services/admin.js
📚 Learning: 2025-09-12T10:01:49.544Z
Learnt from: praveenKDass
PR: ELEVATE-Project/user#816
File: src/generics/kafka-communication.js:38-0
Timestamp: 2025-09-12T10:01:49.544Z
Learning: In the ELEVATE-Project/user codebase, environment variables use a requiredIf validation pattern where EVENT_ORGANIZATION_KAFKA_TOPIC and EVENT_TENANT_KAFKA_TOPIC are optional by default but become required when their respective enable flags (EVENT_ENABLE_ORGANIZATION_EVENTS and EVENT_ENABLE_TENANT_EVENTS) equal 'true'. This prevents undefined topic issues through environment validation rather than runtime guards.

Applied to files:

  • src/services/admin.js
📚 Learning: 2025-09-12T10:02:11.442Z
Learnt from: praveenKDass
PR: ELEVATE-Project/user#816
File: src/generics/kafka-communication.js:28-0
Timestamp: 2025-09-12T10:02:11.442Z
Learning: In the ELEVATE-Project/user codebase, EVENT_TENANT_KAFKA_TOPIC uses conditional validation (requiredIf) in src/envVariables.js, not default values. When EVENT_ENABLE_TENANT_EVENTS equals 'true', the topic becomes required and application startup validation ensures it's present, preventing runtime issues with undefined topics.

Applied to files:

  • src/services/admin.js
📚 Learning: 2025-09-12T10:02:11.442Z
Learnt from: praveenKDass
PR: ELEVATE-Project/user#816
File: src/generics/kafka-communication.js:28-0
Timestamp: 2025-09-12T10:02:11.442Z
Learning: In the ELEVATE-Project/user codebase, environment variables use conditional validation via requiredIf logic in src/envVariables.js rather than default values. When EVENT_ENABLE_TENANT_EVENTS equals 'true', EVENT_TENANT_KAFKA_TOPIC becomes required and startup validation ensures it's present.

Applied to files:

  • src/services/admin.js
📚 Learning: 2025-09-12T11:03:00.511Z
Learnt from: praveenKDass
PR: ELEVATE-Project/user#816
File: src/dtos/tenantDTO.js:23-0
Timestamp: 2025-09-12T11:03:00.511Z
Learning: In the ELEVATE-Project/user codebase, the oldValue and newValue properties in changedValues objects passed to tenantDTO.eventBodyDTO are guaranteed to be defined when present, so undefined checks are not necessary according to the data contracts.

Applied to files:

  • src/generics/utils.js
🧬 Code graph analysis (2)
src/services/admin.js (1)
src/services/organization.js (10)
  • organizationDTO (19-19)
  • require (12-12)
  • require (14-14)
  • require (15-15)
  • require (18-18)
  • require (22-22)
  • organizationQueries (3-3)
  • common (2-2)
  • eventBodyData (151-169)
  • eventBodyData (845-865)
src/generics/utils.js (1)
src/services/organization.js (2)
  • changes (833-833)
  • _ (13-13)
🔇 Additional comments (4)
src/generics/utils.js (1)

1157-1158: Export addition looks good.

getChanges is correctly added to module.exports.

src/services/admin.js (3)

43-43: LGTM: organization DTO import

Import aligns with usage below.


656-666: Good: normalized update() usage with returning/raw

Switch to orgUpdateResult + { returning: true, raw: true } is correct for fetching the updated row.


722-723: LGTM: broadcast route

Publishing to organizationEvents with isInternal: true matches the pattern used in organization service.

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

Caution

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

⚠️ Outside diff range comments (1)
src/services/tenant.js (1)

725-731: Fix success message for domain removal.

Message says “ADDED” after removal. Update to “REMOVED”.

-				message: 'TENANT_DOMAINS_ADDED_SUCCESSFULLY',
+				message: 'TENANT_DOMAINS_REMOVED_SUCCESSFULLY',
🧹 Nitpick comments (3)
src/.env.sample (2)

183-193: Trim trailing whitespace and add final newline.

Fix dotenv-linter warnings by removing trailing spaces on Lines 183, 185, 191 and ensuring the file ends with a newline.

Apply this diff:

-KAFKA_HEALTH_CHECK_TOPIC='user-health-check-topic-check'   
+KAFKA_HEALTH_CHECK_TOPIC='user-health-check-topic-check'
-#Enable / disable tenant create event 
-EVENT_ENABLE_TENANT_EVENTS=true 
+#Enable / disable tenant create event
+EVENT_ENABLE_TENANT_EVENTS=true
 #Event Kafka topic for tenant create
 EVENT_TENANT_KAFKA_TOPIC='dev.tenantEvent'
 #Event API for tenant create , can be comma separated
 EVENT_TENANT_LISTENER_API='http://interface:3567/scp/v1/tenant/add'
-#Enable / disable organization  event 
-EVENT_ENABLE_ORGANIZATION_EVENTS=true 
+#Enable / disable organization  event
+EVENT_ENABLE_ORGANIZATION_EVENTS=true
 #Event Kafka topic for organization create/update
 EVENT_ORGANIZATION_KAFKA_TOPIC='dev.organizationEvent'
+ 

191-193: Clarify org event toggles to avoid ambiguity.

Both EVENT_ENABLE_ORG_EVENTS (Line 118) and EVENT_ENABLE_ORGANIZATION_EVENTS (Line 191) exist. This is confusing for operators. Pick one canonical variable (prefer the newer EVENT_ENABLE_ORGANIZATION_EVENTS) and deprecate the other with a compatibility fallback in src/envVariables.js; update .env.sample and docs accordingly.

src/services/tenant.js (1)

409-409: Consider awaiting event broadcast (or explicitly fire-and-forget).

If broadcastUserEvent returns a Promise, not awaiting risks unhandled rejections and lost errors. Either await it or explicitly mark as intentional fire-and-forget.

-			broadcastUserEvent('tenantEvents', { requestBody: eventBody, isInternal: true })
+			await broadcastUserEvent('tenantEvents', { requestBody: eventBody, isInternal: true })
+			// If you prefer non-blocking:
+			// void broadcastUserEvent('tenantEvents', { requestBody: eventBody, isInternal: true })
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between fd8d901 and f10b7e4.

📒 Files selected for processing (3)
  • src/.env.sample (1 hunks)
  • src/services/admin.js (3 hunks)
  • src/services/tenant.js (7 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/services/admin.js
🧰 Additional context used
📓 Path-based instructions (1)
src/services/**

⚙️ CodeRabbit configuration file

This is core business logic. Please check for correctness, efficiency, and potential edge cases.

Files:

  • src/services/tenant.js
🧠 Learnings (5)
📓 Common learnings
Learnt from: praveenKDass
PR: ELEVATE-Project/user#816
File: src/generics/kafka-communication.js:28-0
Timestamp: 2025-09-12T10:02:11.455Z
Learning: In the ELEVATE-Project/user codebase, EVENT_TENANT_KAFKA_TOPIC uses conditional validation (requiredIf) in src/envVariables.js, not default values. When EVENT_ENABLE_TENANT_EVENTS equals 'true', the topic becomes required and application startup validation ensures it's present, preventing runtime issues with undefined topics.
Learnt from: praveenKDass
PR: ELEVATE-Project/user#816
File: src/generics/kafka-communication.js:28-0
Timestamp: 2025-09-12T10:02:11.455Z
Learning: In the ELEVATE-Project/user codebase, environment variables use conditional validation via requiredIf logic in src/envVariables.js rather than default values. When EVENT_ENABLE_TENANT_EVENTS equals 'true', EVENT_TENANT_KAFKA_TOPIC becomes required and startup validation ensures it's present.
Learnt from: praveenKDass
PR: ELEVATE-Project/user#816
File: src/services/organization.js:831-0
Timestamp: 2025-09-12T10:40:34.860Z
Learning: In the ELEVATE-Project/user codebase, the organizationDTO.eventBodyDTO is designed to handle empty changes arrays properly, so organization update events should always be emitted even when changedValues is empty. The DTO handles this scenario correctly and there may be other reasons to emit events beyond just field changes (like metadata updates).
📚 Learning: 2025-09-12T10:40:34.860Z
Learnt from: praveenKDass
PR: ELEVATE-Project/user#816
File: src/services/organization.js:831-0
Timestamp: 2025-09-12T10:40:34.860Z
Learning: In the ELEVATE-Project/user codebase, the organizationDTO.eventBodyDTO is designed to handle empty changes arrays properly, so organization update events should always be emitted even when changedValues is empty. The DTO handles this scenario correctly and there may be other reasons to emit events beyond just field changes (like metadata updates).

Applied to files:

  • src/services/tenant.js
📚 Learning: 2025-09-12T10:02:11.455Z
Learnt from: praveenKDass
PR: ELEVATE-Project/user#816
File: src/generics/kafka-communication.js:28-0
Timestamp: 2025-09-12T10:02:11.455Z
Learning: In the ELEVATE-Project/user codebase, environment variables use conditional validation via requiredIf logic in src/envVariables.js rather than default values. When EVENT_ENABLE_TENANT_EVENTS equals 'true', EVENT_TENANT_KAFKA_TOPIC becomes required and startup validation ensures it's present.

Applied to files:

  • src/.env.sample
📚 Learning: 2025-09-12T10:02:11.455Z
Learnt from: praveenKDass
PR: ELEVATE-Project/user#816
File: src/generics/kafka-communication.js:28-0
Timestamp: 2025-09-12T10:02:11.455Z
Learning: In the ELEVATE-Project/user codebase, EVENT_TENANT_KAFKA_TOPIC uses conditional validation (requiredIf) in src/envVariables.js, not default values. When EVENT_ENABLE_TENANT_EVENTS equals 'true', the topic becomes required and application startup validation ensures it's present, preventing runtime issues with undefined topics.

Applied to files:

  • src/.env.sample
📚 Learning: 2025-09-12T10:01:49.571Z
Learnt from: praveenKDass
PR: ELEVATE-Project/user#816
File: src/generics/kafka-communication.js:38-0
Timestamp: 2025-09-12T10:01:49.571Z
Learning: In the ELEVATE-Project/user codebase, environment variables use a requiredIf validation pattern where EVENT_ORGANIZATION_KAFKA_TOPIC and EVENT_TENANT_KAFKA_TOPIC are optional by default but become required when their respective enable flags (EVENT_ENABLE_ORGANIZATION_EVENTS and EVENT_ENABLE_TENANT_EVENTS) equal 'true'. This prevents undefined topic issues through environment validation rather than runtime guards.

Applied to files:

  • src/.env.sample
🧬 Code graph analysis (1)
src/services/tenant.js (5)
src/services/admin.js (7)
  • require (11-11)
  • eventBody (64-81)
  • eventBody (208-219)
  • common (15-15)
  • rowsAffected (772-775)
  • eventBodyData (705-720)
  • utils (17-17)
src/services/account.js (8)
  • require (26-26)
  • require (27-27)
  • require (37-37)
  • require (41-41)
  • eventBody (604-623)
  • eventBody (1754-1769)
  • common (16-16)
  • utils (25-25)
src/services/organization.js (6)
  • eventBody (138-146)
  • common (2-2)
  • changes (833-833)
  • eventBodyData (151-169)
  • eventBodyData (845-865)
  • utils (5-5)
src/generics/utils.js (2)
  • common (20-20)
  • changes (1096-1096)
src/database/queries/organization.js (3)
  • rowsAffected (104-104)
  • rowsAffected (143-143)
  • rowsAffected (164-164)
🪛 dotenv-linter (3.3.0)
src/.env.sample

[warning] 183-183: [TrailingWhitespace] Trailing whitespace detected

(TrailingWhitespace)


[warning] 183-183: [UnorderedKey] The KAFKA_HEALTH_CHECK_TOPIC key should go before the NOTIFICATION_API_URL key

(UnorderedKey)


[warning] 185-185: [TrailingWhitespace] Trailing whitespace detected

(TrailingWhitespace)


[warning] 185-185: [UnorderedKey] The EVENT_ENABLE_TENANT_EVENTS key should go before the EVENT_ENABLE_USER_EVENTS key

(UnorderedKey)


[warning] 187-187: [QuoteCharacter] The value has quote characters (', ")

(QuoteCharacter)


[warning] 187-187: [UnorderedKey] The EVENT_TENANT_KAFKA_TOPIC key should go before the EVENT_USER_KAFKA_TOPIC key

(UnorderedKey)


[warning] 189-189: [QuoteCharacter] The value has quote characters (', ")

(QuoteCharacter)


[warning] 189-189: [UnorderedKey] The EVENT_TENANT_LISTENER_API key should go before the EVENT_USER_KAFKA_TOPIC key

(UnorderedKey)


[warning] 191-191: [TrailingWhitespace] Trailing whitespace detected

(TrailingWhitespace)


[warning] 191-191: [UnorderedKey] The EVENT_ENABLE_ORGANIZATION_EVENTS key should go before the EVENT_ENABLE_TENANT_EVENTS key

(UnorderedKey)


[warning] 193-193: [EndingBlankLine] No blank line at the end of the file

(EndingBlankLine)


[warning] 193-193: [QuoteCharacter] The value has quote characters (', ")

(QuoteCharacter)


[warning] 193-193: [UnorderedKey] The EVENT_ORGANIZATION_KAFKA_TOPIC key should go before the EVENT_TENANT_KAFKA_TOPIC key

(UnorderedKey)

🔇 Additional comments (5)
src/services/tenant.js (5)

33-35: LGTM: imports for event broadcasting and DTO.


376-377: LGTM: propagate default org identifiers to tenant response.

This enables including org_id/org_code in the create event payload.


558-584: LGTM: domain-add emits precise update event with changes diff.

The changes array correctly captures domains old/new values and payload fields look consistent.


698-724: LGTM: domain-remove emits update event with correct diff.


878-901: LGTM: centralized tenantEventEmitter and empty-change handling.

getChanges + DTO usage mirrors org flow and will handle empty changedValues.

@nevil-mathew
Copy link
Collaborator

@coderabbitai full review

@coderabbitai
Copy link

coderabbitai bot commented Sep 17, 2025

✅ Actions performed

Full review triggered.

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

♻️ Duplicate comments (3)
src/services/tenant.js (2)

388-407: Don’t flatten meta; await the emitter.
Flattening meta into args risks key collisions and schema drift. Also await the broadcaster to surface failures.

   const eventBody = TenantDTO.eventBodyDTO({
     entity: 'tenant',
     eventType: 'create',
     entityId: tenantCreateResponse.code,
     args: {
       created_by: tenantCreateResponse.created_by,
       name: tenantCreateResponse.name,
       code: tenantCreateResponse.code,
       created_at: tenantCreateResponse?.created_at || new Date(),
       updated_at: tenantCreateResponse?.updated_at || new Date(),
-      ...(tenantCreateResponse.meta || {}),
+      meta: tenantCreateResponse?.meta || {},
       status: tenantCreateResponse?.status || common.ACTIVE_STATUS,
       deleted: false,
       org_id: tenantCreateResponse.orgId,
       org_code: tenantCreateResponse.orgCode,
       description: tenantCreateResponse.description,
       logo: tenantCreateResponse.logo,
     },
   })
-
- broadcastUserEvent('tenantEvents', { requestBody: eventBody, isInternal: true })
+ await broadcastUserEvent('tenantEvents', { requestBody: eventBody, isInternal: true })

Also applies to: 409-409


458-476: Fix crash when no fields change (destructuring undefined).
If nothing changes, updatedTenantDetails is undefined and destructuring throws. Emit the event with the original state and a null/empty updated row.

-      let updatedTenantDetails
+      let updatedFirstRow
       // update only if the data is changed
       if (Object.keys(tenantUpdateBody).length > 0) {
         tenantUpdateBody.updated_by = userId
-        updatedTenantDetails = await tenantQueries.update(
+        const [rowsAffected, updatedRows] = await tenantQueries.update(
           {
             code: tenantCode,
           },
           tenantUpdateBody,
           {
             returning: true,
             raw: true,
           }
         )
+        updatedFirstRow = updatedRows?.[0]
       }
-
-      const [rowsAffected, updatedRows] = updatedTenantDetails
-
-      await tenantEventEmitter(tenantDetails, updatedRows?.[0], bodyData)
+      await tenantEventEmitter(tenantDetails, updatedFirstRow, bodyData)
src/services/organization.js (1)

151-172: Prevent duplicate create emissions; keep only the new path (awaited).
You still invoke the legacy eventBroadcasterMain('organizationEvents', ...) above while also calling broadcastUserEvent(...) here. This double-sends create events.

  • Remove or feature‑flag the earlier eventBroadcasterMain call and drop its now-unused import.
  • Keep await broadcastUserEvent(...) (good).
🧹 Nitpick comments (12)
src/dtos/organizationDTO.js (1)

19-23: Avoid mutating args; prevent reserved-key overrides in the response.

Delete from a cloned copy to avoid side effects, and place args before reserved fields so args cannot override entity/eventType/entityId/changes.

-      const disallowedArgs = ['deleted_at']
-
-      disallowedArgs.forEach((key) => {
-        delete args[key]
-      })
+      const disallowedArgs = ['deleted_at']
+      const sanitizedArgs = { ...args }
+      disallowedArgs.forEach((key) => {
+        delete sanitizedArgs[key]
+      })
@@
-      return {
-        entity,
-        eventType,
-        entityId,
-        changes,
-        ...args,
-      }
+      return {
+        ...sanitizedArgs,
+        entity,
+        eventType,
+        entityId,
+        changes,
+      }

Also applies to: 32-38

src/.env.sample (1)

183-193: Fix trailing whitespace and add newline at EOF; optional: satisfy dotenv-linter ordering.

Removes lint noise; no behavior change.

-# kafka topic for health check
-KAFKA_HEALTH_CHECK_TOPIC='user-health-check-topic-check'   
+# kafka topic for health check
+KAFKA_HEALTH_CHECK_TOPIC='user-health-check-topic-check'
@@
-EVENT_ENABLE_TENANT_EVENTS=true 
+EVENT_ENABLE_TENANT_EVENTS=true
@@
-EVENT_ENABLE_ORGANIZATION_EVENTS=true 
+EVENT_ENABLE_ORGANIZATION_EVENTS=true
@@
-EVENT_ORGANIZATION_KAFKA_TOPIC='dev.organizationEvent'
+EVENT_ORGANIZATION_KAFKA_TOPIC='dev.organizationEvent'
+

Optional (no diff): consider reordering keys to satisfy UnorderedKey warnings.

src/dtos/tenantDTO.js (1)

28-32: Mirror DTO safety: don’t mutate args; ensure reserved fields can’t be overridden.

Keeps behavior identical to org DTO and avoids side effects.

-      const disallowedArgs = ['deleted_at']
-
-      disallowedArgs.forEach((key) => {
-        delete args[key]
-      })
+      const disallowedArgs = ['deleted_at']
+      const sanitizedArgs = { ...args }
+      disallowedArgs.forEach((key) => {
+        delete sanitizedArgs[key]
+      })
@@
-      return {
-        entity,
-        eventType,
-        entityId,
-        changes,
-        ...args,
-      }
+      return {
+        ...sanitizedArgs,
+        entity,
+        eventType,
+        entityId,
+        changes,
+      }

Also applies to: 41-47

src/generics/utils.js (1)

1099-1102: Use const for loop-scoped bindings.

Minor readability/immutability win.

-    let oldValue = oldData[key]
-    let newValue = newData[key]
-    let fieldName = key
+    const oldValue = oldData[key]
+    const newValue = newData[key]
+    const fieldName = key
src/envVariables.js (3)

418-421: Clarify toggle scope in message.

This flag is used to gate both API and Kafka tenant events in code; message says “kafka” only. Update to avoid ops confusion.

-  message: 'Key to toggle tenant creation kafka event',
+  message: 'Key to toggle tenant events (API and Kafka)',

440-443: Clarify org toggle scope in message.

Align message with usage intent (Kafka and/or future API).

-  message: 'Key to toggle organization creation kafka event',
+  message: 'Key to toggle organization events (Kafka)',

418-452: Normalize event gating toggles or document intentional asymmetry.

Found mixed toggles — EVENT_ENABLE_USER_EVENTS, EVENT_ENABLE_KAFKA_PUSH, and EVENT_ENABLE_TENANT_EVENTS (src/helpers/eventBroadcasterMain.js:30–34; src/envVariables.js:395,418). Either consolidate to a consistent naming strategy (e.g., per-entity EVENT_ENABLE__KAFKA_PUSH or a single global EVENT_ENABLE_KAFKA_PUSH) or add inline/docs explaining why the asymmetry is intentional.

src/helpers/eventBroadcasterMain.js (1)

65-79: Await Kafka sends to surface failures to callers.

Without await, Promise.allSettled in broadcastUserEvent may report success even if send fails later.

   switch (eventGroup) {
     case 'organizationEvents':
-      kafkaCommunication.pushOrganizationEventsToKafka(requestBody)
+      await kafkaCommunication.pushOrganizationEventsToKafka(requestBody)
       break
     case 'userEvents':
-      kafkaCommunication.pushUserEventsToKafka(requestBody)
+      await kafkaCommunication.pushUserEventsToKafka(requestBody)
       break
     case 'tenantEvents':
-      kafkaCommunication.pushTenantEventsToKafka(requestBody)
+      await kafkaCommunication.pushTenantEventsToKafka(requestBody)
       break
src/services/tenant.js (3)

558-584: Await tenantEvents broadcast (addDomain).
Ensure failures surface and maintain ordering with DB changes.

- broadcastUserEvent('tenantEvents', { requestBody: eventBodyData, isInternal: true })
+ await broadcastUserEvent('tenantEvents', { requestBody: eventBodyData, isInternal: true })

698-724: Await emitter and fix success message (removeDomain).

  • Await the broadcast.
  • Response message says “ADDED” for a removal.
- broadcastUserEvent('tenantEvents', { requestBody: eventBodyData, isInternal: true })
+ await broadcastUserEvent('tenantEvents', { requestBody: eventBodyData, isInternal: true })
@@
-  message: 'TENANT_DOMAINS_ADDED_SUCCESSFULLY',
+  message: 'TENANT_DOMAINS_REMOVED_SUCCESSFULLY',

Also applies to: 725-731


878-901: Await tenantEvents broadcast in tenantEventEmitter.
Keep parity with org flows; avoid silent failures.

- broadcastUserEvent('tenantEvents', { requestBody: eventBodyData, isInternal: true })
+ await broadcastUserEvent('tenantEvents', { requestBody: eventBodyData, isInternal: true })
src/services/organization.js (1)

831-867: Await orgEvents broadcast in orgEventEmitter.
Match create flow; avoid silent failures.

- broadcastUserEvent('organizationEvents', { requestBody: eventBodyData, isInternal: true })
+ await broadcastUserEvent('organizationEvents', { requestBody: eventBodyData, isInternal: true })

Given prior agreement to fail fast in org event args, consider removing fallback defaults (e.g., || {} / || new Date()) in this args block to surface missing data early.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 41e992e and f10b7e4.

📒 Files selected for processing (10)
  • src/.env.sample (1 hunks)
  • src/dtos/organizationDTO.js (1 hunks)
  • src/dtos/tenantDTO.js (1 hunks)
  • src/envVariables.js (1 hunks)
  • src/generics/kafka-communication.js (2 hunks)
  • src/generics/utils.js (2 hunks)
  • src/helpers/eventBroadcasterMain.js (3 hunks)
  • src/services/admin.js (3 hunks)
  • src/services/organization.js (9 hunks)
  • src/services/tenant.js (7 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
src/services/**

⚙️ CodeRabbit configuration file

This is core business logic. Please check for correctness, efficiency, and potential edge cases.

Files:

  • src/services/organization.js
  • src/services/tenant.js
  • src/services/admin.js
🧠 Learnings (19)
📓 Common learnings
Learnt from: praveenKDass
PR: ELEVATE-Project/user#816
File: src/generics/kafka-communication.js:28-0
Timestamp: 2025-09-12T10:02:11.455Z
Learning: In the ELEVATE-Project/user codebase, environment variables use conditional validation via requiredIf logic in src/envVariables.js rather than default values. When EVENT_ENABLE_TENANT_EVENTS equals 'true', EVENT_TENANT_KAFKA_TOPIC becomes required and startup validation ensures it's present.
Learnt from: praveenKDass
PR: ELEVATE-Project/user#816
File: src/services/organization.js:831-0
Timestamp: 2025-09-12T10:40:34.860Z
Learning: In the ELEVATE-Project/user codebase, the organizationDTO.eventBodyDTO is designed to handle empty changes arrays properly, so organization update events should always be emitted even when changedValues is empty. The DTO handles this scenario correctly and there may be other reasons to emit events beyond just field changes (like metadata updates).
📚 Learning: 2025-09-12T11:03:00.543Z
Learnt from: praveenKDass
PR: ELEVATE-Project/user#816
File: src/dtos/tenantDTO.js:23-0
Timestamp: 2025-09-12T11:03:00.543Z
Learning: In the ELEVATE-Project/user codebase, the changedValues parameter passed to tenantDTO.eventBodyDTO is guaranteed to be an array by the calling code, so array validation is not needed.

Applied to files:

  • src/dtos/tenantDTO.js
  • src/dtos/organizationDTO.js
📚 Learning: 2025-09-12T10:40:34.860Z
Learnt from: praveenKDass
PR: ELEVATE-Project/user#816
File: src/services/organization.js:831-0
Timestamp: 2025-09-12T10:40:34.860Z
Learning: In the ELEVATE-Project/user codebase, the organizationDTO.eventBodyDTO is designed to handle empty changes arrays properly, so organization update events should always be emitted even when changedValues is empty. The DTO handles this scenario correctly and there may be other reasons to emit events beyond just field changes (like metadata updates).

Applied to files:

  • src/dtos/tenantDTO.js
  • src/dtos/organizationDTO.js
  • src/services/organization.js
  • src/services/tenant.js
  • src/services/admin.js
📚 Learning: 2025-09-12T10:36:43.529Z
Learnt from: praveenKDass
PR: ELEVATE-Project/user#816
File: src/dtos/organizationDTO.js:26-29
Timestamp: 2025-09-12T10:36:43.529Z
Learning: In the ELEVATE-Project/user codebase, the changedValues array passed to organizationDTO.eventBodyDTO and tenantDTO.eventBodyDTO always contains a fieldName property when oldValue or newValue are present, so additional fieldName validation is not needed according to the business logic.

Applied to files:

  • src/dtos/tenantDTO.js
  • src/dtos/organizationDTO.js
  • src/services/organization.js
📚 Learning: 2025-09-12T11:03:00.543Z
Learnt from: praveenKDass
PR: ELEVATE-Project/user#816
File: src/dtos/tenantDTO.js:23-0
Timestamp: 2025-09-12T11:03:00.543Z
Learning: In the ELEVATE-Project/user codebase, the tenantDTO.eventBodyDTO method should always filter out 'deleted_at' from args regardless of event type, as this field should not be allowed in any event payload according to the business rules.

Applied to files:

  • src/dtos/tenantDTO.js
  • src/dtos/organizationDTO.js
📚 Learning: 2025-09-12T11:02:51.008Z
Learnt from: praveenKDass
PR: ELEVATE-Project/user#816
File: src/dtos/organizationDTO.js:14-43
Timestamp: 2025-09-12T11:02:51.008Z
Learning: In the ELEVATE-Project/user codebase, oldValue and newValue in the changedValues array for organizationDTO.eventBodyDTO are guaranteed to not be undefined by the business logic, and filtering out falsy values (0, false, '') from changes is the intended behavior.

Applied to files:

  • src/dtos/tenantDTO.js
  • src/dtos/organizationDTO.js
  • src/services/organization.js
📚 Learning: 2025-09-12T11:03:00.543Z
Learnt from: praveenKDass
PR: ELEVATE-Project/user#816
File: src/dtos/tenantDTO.js:23-0
Timestamp: 2025-09-12T11:03:00.543Z
Learning: In the ELEVATE-Project/user codebase, the oldValue and newValue properties in changedValues objects passed to tenantDTO.eventBodyDTO are guaranteed to be defined when present, so undefined checks are not necessary according to the data contracts.

Applied to files:

  • src/dtos/tenantDTO.js
  • src/generics/utils.js
  • src/dtos/organizationDTO.js
  • src/services/organization.js
  • src/services/tenant.js
📚 Learning: 2025-09-12T11:02:51.008Z
Learnt from: praveenKDass
PR: ELEVATE-Project/user#816
File: src/dtos/organizationDTO.js:14-43
Timestamp: 2025-09-12T11:02:51.008Z
Learning: In the ELEVATE-Project/user codebase, the organizationDTO.eventBodyDTO method intentionally filters out deleted_at from args for all event types (create, update, delete), not just non-delete events. This is by design according to the business requirements.

Applied to files:

  • src/dtos/tenantDTO.js
  • src/dtos/organizationDTO.js
  • src/services/organization.js
📚 Learning: 2025-09-12T11:02:51.008Z
Learnt from: praveenKDass
PR: ELEVATE-Project/user#816
File: src/dtos/organizationDTO.js:14-43
Timestamp: 2025-09-12T11:02:51.008Z
Learning: In the ELEVATE-Project/user codebase, the changedValues parameter in organizationDTO.eventBodyDTO is guaranteed to always be an array by the calling code, so additional Array.isArray validation is not needed.

Applied to files:

  • src/dtos/tenantDTO.js
  • src/dtos/organizationDTO.js
  • src/services/organization.js
📚 Learning: 2025-07-31T08:44:36.982Z
Learnt from: nevil-mathew
PR: ELEVATE-Project/user#776
File: src/services/entities.js:18-23
Timestamp: 2025-07-31T08:44:36.982Z
Learning: In the ELEVATE-Project/user codebase, organizationCode and tenantCode parameters passed to service methods always come from req.decodedToken.organization_code and req.decodedToken.tenant_code, which are guaranteed to be present after token validation. Additional validation for these parameters in service methods is unnecessary as the token validation process ensures they are always available.

Applied to files:

  • src/dtos/tenantDTO.js
📚 Learning: 2025-09-12T10:02:11.455Z
Learnt from: praveenKDass
PR: ELEVATE-Project/user#816
File: src/generics/kafka-communication.js:28-0
Timestamp: 2025-09-12T10:02:11.455Z
Learning: In the ELEVATE-Project/user codebase, environment variables use conditional validation via requiredIf logic in src/envVariables.js rather than default values. When EVENT_ENABLE_TENANT_EVENTS equals 'true', EVENT_TENANT_KAFKA_TOPIC becomes required and startup validation ensures it's present.

Applied to files:

  • src/dtos/tenantDTO.js
  • src/.env.sample
  • src/helpers/eventBroadcasterMain.js
  • src/generics/kafka-communication.js
  • src/services/admin.js
  • src/envVariables.js
📚 Learning: 2025-09-12T10:02:11.455Z
Learnt from: praveenKDass
PR: ELEVATE-Project/user#816
File: src/generics/kafka-communication.js:28-0
Timestamp: 2025-09-12T10:02:11.455Z
Learning: In the ELEVATE-Project/user codebase, EVENT_TENANT_KAFKA_TOPIC uses conditional validation (requiredIf) in src/envVariables.js, not default values. When EVENT_ENABLE_TENANT_EVENTS equals 'true', the topic becomes required and application startup validation ensures it's present, preventing runtime issues with undefined topics.

Applied to files:

  • src/.env.sample
  • src/helpers/eventBroadcasterMain.js
  • src/generics/kafka-communication.js
  • src/services/admin.js
  • src/envVariables.js
📚 Learning: 2025-09-12T10:01:49.571Z
Learnt from: praveenKDass
PR: ELEVATE-Project/user#816
File: src/generics/kafka-communication.js:38-0
Timestamp: 2025-09-12T10:01:49.571Z
Learning: In the ELEVATE-Project/user codebase, environment variables use a requiredIf validation pattern where EVENT_ORGANIZATION_KAFKA_TOPIC and EVENT_TENANT_KAFKA_TOPIC are optional by default but become required when their respective enable flags (EVENT_ENABLE_ORGANIZATION_EVENTS and EVENT_ENABLE_TENANT_EVENTS) equal 'true'. This prevents undefined topic issues through environment validation rather than runtime guards.

Applied to files:

  • src/.env.sample
  • src/helpers/eventBroadcasterMain.js
  • src/generics/kafka-communication.js
  • src/services/admin.js
  • src/envVariables.js
📚 Learning: 2025-09-11T23:15:19.523Z
Learnt from: praveenKDass
PR: ELEVATE-Project/user#816
File: src/helpers/eventBroadcasterMain.js:14-17
Timestamp: 2025-09-11T23:15:19.523Z
Learning: In the ELEVATE-Project/user codebase, switch cases typically use return statements without break statements when each case returns a value directly. However, when a case has conditional returns, it should include a fallback return statement to prevent unintended fallthrough behavior.

Applied to files:

  • src/helpers/eventBroadcasterMain.js
📚 Learning: 2025-09-12T10:14:22.455Z
Learnt from: praveenKDass
PR: ELEVATE-Project/user#816
File: src/services/organization.js:831-867
Timestamp: 2025-09-12T10:14:22.455Z
Learning: In the ELEVATE-Project/user codebase, the related_orgs field is stored as an array in the database by default, so additional array validation checks are unnecessary when processing related organization data.

Applied to files:

  • src/services/organization.js
📚 Learning: 2025-09-12T10:35:28.285Z
Learnt from: praveenKDass
PR: ELEVATE-Project/user#816
File: src/services/admin.js:704-704
Timestamp: 2025-09-12T10:35:28.285Z
Learning: In ELEVATE-Project/user admin.js, there's an inconsistency in organizationQueries.update usage: addOrgAdmin calls it without options (returns number), while deactivateOrg calls it with { returning: true, raw: true } (returns object). The validation logic 'if (orgRowsAffected === 0)' only works for the first case - the second case needs 'if (orgRowsAffected.rowsAffected === 0)' to properly validate updates.

Applied to files:

  • src/services/organization.js
  • src/services/admin.js
📚 Learning: 2025-09-12T10:35:28.285Z
Learnt from: praveenKDass
PR: ELEVATE-Project/user#816
File: src/services/admin.js:704-704
Timestamp: 2025-09-12T10:35:28.285Z
Learning: In the ELEVATE-Project/user codebase, when using organizationQueries.update with { returning: true, raw: true } options, the method consistently returns a structured result with updatedRows property when successful. The initial check for orgRowsAffected === 0 already handles failed updates, so additional null checking for updatedRows is redundant when the code execution reaches the event emission logic.

Applied to files:

  • src/services/organization.js
  • src/services/tenant.js
  • src/services/admin.js
📚 Learning: 2025-09-12T10:35:28.285Z
Learnt from: praveenKDass
PR: ELEVATE-Project/user#816
File: src/services/admin.js:704-704
Timestamp: 2025-09-12T10:35:28.285Z
Learning: In the ELEVATE-Project/user codebase, when organizationQueries.update is called with { returning: true, raw: true }, it returns an object { rowsAffected, updatedRows }, not just a number. The validation logic in admin.js deactivateOrg method checking 'if (orgRowsAffected === 0)' is incorrect and should be 'if (orgRowsAffected.rowsAffected === 0)' to properly validate successful updates.

Applied to files:

  • src/services/organization.js
  • src/services/admin.js
📚 Learning: 2025-09-12T10:38:57.594Z
Learnt from: praveenKDass
PR: ELEVATE-Project/user#816
File: src/services/organization.js:831-0
Timestamp: 2025-09-12T10:38:57.594Z
Learning: In the ELEVATE-Project/user codebase, when building event payloads in orgEventEmitter function, the preference is to use direct property access without nullish coalescing operators (??) for args object properties. This ensures fail-fast behavior - if expected document properties are missing, the system should throw an error rather than silently falling back to default values, helping to catch data integrity issues early.

Applied to files:

  • src/services/organization.js
🪛 dotenv-linter (3.3.0)
src/.env.sample

[warning] 183-183: [TrailingWhitespace] Trailing whitespace detected

(TrailingWhitespace)


[warning] 183-183: [UnorderedKey] The KAFKA_HEALTH_CHECK_TOPIC key should go before the NOTIFICATION_API_URL key

(UnorderedKey)


[warning] 185-185: [TrailingWhitespace] Trailing whitespace detected

(TrailingWhitespace)


[warning] 185-185: [UnorderedKey] The EVENT_ENABLE_TENANT_EVENTS key should go before the EVENT_ENABLE_USER_EVENTS key

(UnorderedKey)


[warning] 187-187: [QuoteCharacter] The value has quote characters (', ")

(QuoteCharacter)


[warning] 187-187: [UnorderedKey] The EVENT_TENANT_KAFKA_TOPIC key should go before the EVENT_USER_KAFKA_TOPIC key

(UnorderedKey)


[warning] 189-189: [QuoteCharacter] The value has quote characters (', ")

(QuoteCharacter)


[warning] 189-189: [UnorderedKey] The EVENT_TENANT_LISTENER_API key should go before the EVENT_USER_KAFKA_TOPIC key

(UnorderedKey)


[warning] 191-191: [TrailingWhitespace] Trailing whitespace detected

(TrailingWhitespace)


[warning] 191-191: [UnorderedKey] The EVENT_ENABLE_ORGANIZATION_EVENTS key should go before the EVENT_ENABLE_TENANT_EVENTS key

(UnorderedKey)


[warning] 193-193: [EndingBlankLine] No blank line at the end of the file

(EndingBlankLine)


[warning] 193-193: [QuoteCharacter] The value has quote characters (', ")

(QuoteCharacter)


[warning] 193-193: [UnorderedKey] The EVENT_ORGANIZATION_KAFKA_TOPIC key should go before the EVENT_TENANT_KAFKA_TOPIC key

(UnorderedKey)

🔇 Additional comments (11)
src/generics/kafka-communication.js (2)

28-36: LGTM: tenant Kafka publisher mirrors existing pattern.


38-49: LGTM: org Kafka publisher mirrors existing pattern.

src/helpers/eventBroadcasterMain.js (1)

14-18: Endpoints switch: good fallback handling.

Returning [] prevents fallthrough; matches project pattern.

src/services/admin.js (2)

656-666: LGTM: use returning+raw and capture update result.

Sets status to INACTIVE and records updater; consistent with deactivation.


668-674: Guard fix is correct.

rowsAffected check now matches returned shape.

src/services/tenant.js (2)

33-35: LGTM: new imports are appropriate.


376-377: LGTM: carry forward orgId/orgCode from default org.

src/services/organization.js (4)

22-22: LGTM: import for unified broadcaster.


252-254: LGTM: unified org update emission via orgEventEmitter.


559-571: LGTM: related_orgs add emits via updatedRows[0] and orgEventEmitter.

Also applies to: 586-589


637-648: LGTM: related_orgs remove emits with proper state.

Also applies to: 664-666

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

🧹 Nitpick comments (3)
src/services/admin.js (1)

703-721: Include updated_by (and consider minimal parity fields) in org update event.
Add updated_by: loggedInUserId for traceability. Optionally add deleted: false and created_at for parity with other org update events.

Apply:

- const eventBodyData = organizationDTO.eventBodyDTO({
+ const eventBodyData = organizationDTO.eventBodyDTO({
    entity: 'organization',
    eventType: 'update',
    entityId: updatedOrgDetails.id,
    args: {
-     created_by: updatedOrgDetails.created_by,
+     created_by: updatedOrgDetails.created_by,
+     updated_by: loggedInUserId,
      name: updatedOrgDetails.name,
      code: updatedOrgDetails.code,
-     updated_at: updatedOrgDetails?.updated_at || new Date(),
+     updated_at: updatedOrgDetails?.updated_at || new Date(),
+     // optional parity with other update events:
+     // created_at: updatedOrgDetails?.created_at || null,
+     // deleted: false,
      status: updatedOrgDetails?.status || common.INACTIVE_STATUS,
      id: updatedOrgDetails.id,
      tenant_code: tenantCode,
    },
 })

Note: per prior learning, changedValues is optional; DTO handles empty changes arrays.

src/helpers/eventBroadcasterMain.js (2)

68-82: Await Kafka dispatch to surface failures to caller.
Without await, errors won’t be caught by eventBroadcasterKafka and won’t appear in broadcastUserEvent’s allSettled.

 case 'organizationEvents':
-  kafkaCommunication.pushOrganizationEventsToKafka(requestBody)
+  await kafkaCommunication.pushOrganizationEventsToKafka(requestBody)
   break
 case 'userEvents':
-  kafkaCommunication.pushUserEventsToKafka(requestBody)
+  await kafkaCommunication.pushUserEventsToKafka(requestBody)
   break
 case 'tenantEvents':
-  kafkaCommunication.pushTenantEventsToKafka(requestBody)
+  await kafkaCommunication.pushTenantEventsToKafka(requestBody)
   break

55-58: Bug: logging endpoint URL from string array.
endPoints is an array of strings; .url is undefined. Log the string directly.

- console.error(`Error for endpoint ${endPoints[index].url}:`, result.reason)
+ console.error(`Error for endpoint ${endPoints[index]}:`, result.reason)
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f10b7e4 and 0411c50.

📒 Files selected for processing (3)
  • src/helpers/eventBroadcasterMain.js (3 hunks)
  • src/services/admin.js (3 hunks)
  • src/services/tenant.js (7 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/services/tenant.js
🧰 Additional context used
📓 Path-based instructions (1)
src/services/**

⚙️ CodeRabbit configuration file

This is core business logic. Please check for correctness, efficiency, and potential edge cases.

Files:

  • src/services/admin.js
🧠 Learnings (11)
📓 Common learnings
Learnt from: praveenKDass
PR: ELEVATE-Project/user#816
File: src/services/organization.js:831-0
Timestamp: 2025-09-12T10:40:34.860Z
Learning: In the ELEVATE-Project/user codebase, the organizationDTO.eventBodyDTO is designed to handle empty changes arrays properly, so organization update events should always be emitted even when changedValues is empty. The DTO handles this scenario correctly and there may be other reasons to emit events beyond just field changes (like metadata updates).
📚 Learning: 2025-09-12T10:35:28.285Z
Learnt from: praveenKDass
PR: ELEVATE-Project/user#816
File: src/services/admin.js:704-704
Timestamp: 2025-09-12T10:35:28.285Z
Learning: In ELEVATE-Project/user admin.js, there's an inconsistency in organizationQueries.update usage: addOrgAdmin calls it without options (returns number), while deactivateOrg calls it with { returning: true, raw: true } (returns object). The validation logic 'if (orgRowsAffected === 0)' only works for the first case - the second case needs 'if (orgRowsAffected.rowsAffected === 0)' to properly validate updates.

Applied to files:

  • src/services/admin.js
📚 Learning: 2025-09-12T10:35:28.285Z
Learnt from: praveenKDass
PR: ELEVATE-Project/user#816
File: src/services/admin.js:704-704
Timestamp: 2025-09-12T10:35:28.285Z
Learning: In the ELEVATE-Project/user codebase, when organizationQueries.update is called with { returning: true, raw: true }, it returns an object { rowsAffected, updatedRows }, not just a number. The validation logic in admin.js deactivateOrg method checking 'if (orgRowsAffected === 0)' is incorrect and should be 'if (orgRowsAffected.rowsAffected === 0)' to properly validate successful updates.

Applied to files:

  • src/services/admin.js
📚 Learning: 2025-09-12T10:35:28.285Z
Learnt from: praveenKDass
PR: ELEVATE-Project/user#816
File: src/services/admin.js:704-704
Timestamp: 2025-09-12T10:35:28.285Z
Learning: In the ELEVATE-Project/user codebase, when using organizationQueries.update with { returning: true, raw: true } options, the method consistently returns a structured result with updatedRows property when successful. The initial check for orgRowsAffected === 0 already handles failed updates, so additional null checking for updatedRows is redundant when the code execution reaches the event emission logic.

Applied to files:

  • src/services/admin.js
📚 Learning: 2025-09-12T10:40:34.860Z
Learnt from: praveenKDass
PR: ELEVATE-Project/user#816
File: src/services/organization.js:831-0
Timestamp: 2025-09-12T10:40:34.860Z
Learning: In the ELEVATE-Project/user codebase, the organizationDTO.eventBodyDTO is designed to handle empty changes arrays properly, so organization update events should always be emitted even when changedValues is empty. The DTO handles this scenario correctly and there may be other reasons to emit events beyond just field changes (like metadata updates).

Applied to files:

  • src/services/admin.js
📚 Learning: 2025-09-12T10:01:49.571Z
Learnt from: praveenKDass
PR: ELEVATE-Project/user#816
File: src/generics/kafka-communication.js:38-0
Timestamp: 2025-09-12T10:01:49.571Z
Learning: In the ELEVATE-Project/user codebase, environment variables use a requiredIf validation pattern where EVENT_ORGANIZATION_KAFKA_TOPIC and EVENT_TENANT_KAFKA_TOPIC are optional by default but become required when their respective enable flags (EVENT_ENABLE_ORGANIZATION_EVENTS and EVENT_ENABLE_TENANT_EVENTS) equal 'true'. This prevents undefined topic issues through environment validation rather than runtime guards.

Applied to files:

  • src/services/admin.js
  • src/helpers/eventBroadcasterMain.js
📚 Learning: 2025-09-12T10:02:11.455Z
Learnt from: praveenKDass
PR: ELEVATE-Project/user#816
File: src/generics/kafka-communication.js:28-0
Timestamp: 2025-09-12T10:02:11.455Z
Learning: In the ELEVATE-Project/user codebase, EVENT_TENANT_KAFKA_TOPIC uses conditional validation (requiredIf) in src/envVariables.js, not default values. When EVENT_ENABLE_TENANT_EVENTS equals 'true', the topic becomes required and application startup validation ensures it's present, preventing runtime issues with undefined topics.

Applied to files:

  • src/services/admin.js
  • src/helpers/eventBroadcasterMain.js
📚 Learning: 2025-09-12T10:02:11.455Z
Learnt from: praveenKDass
PR: ELEVATE-Project/user#816
File: src/generics/kafka-communication.js:28-0
Timestamp: 2025-09-12T10:02:11.455Z
Learning: In the ELEVATE-Project/user codebase, environment variables use conditional validation via requiredIf logic in src/envVariables.js rather than default values. When EVENT_ENABLE_TENANT_EVENTS equals 'true', EVENT_TENANT_KAFKA_TOPIC becomes required and startup validation ensures it's present.

Applied to files:

  • src/services/admin.js
  • src/helpers/eventBroadcasterMain.js
📚 Learning: 2025-09-12T11:02:51.008Z
Learnt from: praveenKDass
PR: ELEVATE-Project/user#816
File: src/dtos/organizationDTO.js:14-43
Timestamp: 2025-09-12T11:02:51.008Z
Learning: In the ELEVATE-Project/user codebase, oldValue and newValue in the changedValues array for organizationDTO.eventBodyDTO are guaranteed to not be undefined by the business logic, and filtering out falsy values (0, false, '') from changes is the intended behavior.

Applied to files:

  • src/services/admin.js
📚 Learning: 2025-09-12T11:02:51.008Z
Learnt from: praveenKDass
PR: ELEVATE-Project/user#816
File: src/dtos/organizationDTO.js:14-43
Timestamp: 2025-09-12T11:02:51.008Z
Learning: In the ELEVATE-Project/user codebase, the organizationDTO.eventBodyDTO method intentionally filters out deleted_at from args for all event types (create, update, delete), not just non-delete events. This is by design according to the business requirements.

Applied to files:

  • src/services/admin.js
📚 Learning: 2025-09-11T23:15:19.523Z
Learnt from: praveenKDass
PR: ELEVATE-Project/user#816
File: src/helpers/eventBroadcasterMain.js:14-17
Timestamp: 2025-09-11T23:15:19.523Z
Learning: In the ELEVATE-Project/user codebase, switch cases typically use return statements without break statements when each case returns a value directly. However, when a case has conditional returns, it should include a fallback return statement to prevent unintended fallthrough behavior.

Applied to files:

  • src/helpers/eventBroadcasterMain.js
🧬 Code graph analysis (2)
src/services/admin.js (1)
src/services/organization.js (10)
  • organizationDTO (19-19)
  • require (12-12)
  • require (14-14)
  • require (15-15)
  • require (18-18)
  • require (22-22)
  • organizationQueries (3-3)
  • common (2-2)
  • eventBodyData (151-169)
  • eventBodyData (845-865)
src/helpers/eventBroadcasterMain.js (5)
src/services/organization.js (1)
  • kafkaCommunication (11-11)
src/services/org-admin.js (1)
  • kafkaCommunication (15-15)
src/services/account.js (1)
  • kafkaCommunication (20-20)
src/helpers/userInvite.js (1)
  • kafkaCommunication (17-17)
src/services/userInvite.js (1)
  • kafkaCommunication (24-24)
🔇 Additional comments (6)
src/services/admin.js (3)

43-43: Good: DTO import aligns with new event flow.
Consistent with org events usage elsewhere.


656-666: Update result handling: correct use of { returning: true, raw: true }.
Switch to orgUpdateResult with return metadata is appropriate.


668-673: Guard fixed: validate rowsAffected.
The failure path now triggers correctly when no rows updated.

src/helpers/eventBroadcasterMain.js (3)

14-18: Good: fallback return [] prevents switch fallthrough.
Matches the pattern used for organizationEvents.


30-31: Enablement gate for tenant events looks good.
Consistent with env-driven toggles.


34-37: Incorrect: env var mismatch claim — both ORG and ORGANIZATION flags exist

envVariables.js defines EVENT_ENABLE_ORG_EVENTS (line 188) and EVENT_ENABLE_ORGANIZATION_EVENTS (line 440); src/helpers/eventBroadcasterMain.js uses EVENT_ENABLE_ORG_EVENTS (lines ~27, ~37). The original comment claiming a missing/canonical ORGANIZATION flag is incorrect.

  • Decide whether EVENT_ENABLE_ORG_EVENTS is an intended alias; if not, standardize usages to EVENT_ENABLE_ORGANIZATION_EVENTS (e.g., update src/helpers/eventBroadcasterMain.js).
  • Confirm whether Kafka gating should be per-group or use the global EVENT_ENABLE_KAFKA_PUSH (userEvents-kafka currently uses the global flag).

Likely an incorrect or invalid review comment.

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

Caution

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

⚠️ Outside diff range comments (4)
src/helpers/userInvite.js (1)

1196-1200: Fix undefined variable newUserCred on failure path.

This throws a ReferenceError at runtime.

-            isErrorOccured = true
-            await userInviteQueries.deleteOne(newInvitee.id)
-            invitee.statusOrUserId = newUserCred
+            isErrorOccured = true
+            await userInviteQueries.deleteOne(newInvitee.id)
+            invitee.statusOrUserId = 'USER_CREATION_FAILED'
src/helpers/eventBroadcasterMain.js (1)

56-58: Logging bug: endpoint is a string, not an object.

endPoints[index].url is undefined. Log the string.

- console.error(`Error for endpoint ${endPoints[index].url}:`, result.reason)
+ console.error(`Error for endpoint ${endPoints[index]}:`, result.reason)
src/services/tenant.js (1)

724-731: Wrong success message for removeDomain.

Returns “TENANT_DOMAINS_ADDED_SUCCESSFULLY” on removal.

- message: 'TENANT_DOMAINS_ADDED_SUCCESSFULLY',
+ message: 'TENANT_DOMAINS_REMOVED_SUCCESSFULLY',
src/envVariables.js (1)

407-411: Use ENABLE_USER_KAFKA_EVENTS in requiredIf
The key ENABLE_USER_KAFKA_PUSH doesn’t exist; update it to ENABLE_USER_KAFKA_EVENTS.

 requiredIf: {
-  key: 'ENABLE_USER_KAFKA_PUSH',
+  key: 'ENABLE_USER_KAFKA_EVENTS',
   operator: 'EQUALS',
   value: 'true',
 },
🧹 Nitpick comments (9)
src/.env.sample (2)

183-191: Fix dotenv lint warnings: trim trailing spaces and add final newline.

Remove trailing whitespace on lines 183, 185, 187, 189, 191, 197 and add a newline at EOF to satisfy dotenv-linter.

- KAFKA_HEALTH_CHECK_TOPIC='user-health-check-topic-check'   
+KAFKA_HEALTH_CHECK_TOPIC='user-health-check-topic-check'
@@
-EVENT_ENABLE_TENANT_EVENTS=true 
+EVENT_ENABLE_TENANT_EVENTS=true
@@
-ENABLE_TENANT_KAFKA_EVENTS=true 
+ENABLE_TENANT_KAFKA_EVENTS=true
@@
-ENABLE_USER_KAFKA_EVENTS=true 
+ENABLE_USER_KAFKA_EVENTS=true
@@
-ENABLE_ORG_KAFKA_EVENTS=true 
+ENABLE_ORG_KAFKA_EVENTS=true
@@
-EVENT_ENABLE_ORGANIZATION_EVENTS=true 
+EVENT_ENABLE_ORGANIZATION_EVENTS=true
@@
-EVENT_ORGANIZATION_KAFKA_TOPIC='dev.organizationEvent'
+EVENT_ORGANIZATION_KAFKA_TOPIC='dev.organizationEvent'
+

Also applies to: 197-199


185-199: Consolidate event and Kafka toggle flags

  • Rename EVENT_ENABLE_ORGANIZATION_EVENTS in .env.sample to match code’s EVENT_ENABLE_ORG_EVENTS.
  • Remove redundant Kafka switches: rely on the single EVENT_ENABLE_KAFKA_PUSH plus per-domain ENABLE_<DOMAIN>_KAFKA_EVENTS.
src/services/admin.js (1)

703-721: Include updated_by in org deactivation event for auditability.

Emit who performed the change.

- let updatedOrgDetails = orgUpdateResult.updatedRows?.[0]
+ let updatedOrgDetails = orgUpdateResult.updatedRows?.[0]
  const eventBodyData = organizationDTO.eventBodyDTO({
    entity: 'organization',
    eventType: 'update',
    entityId: updatedOrgDetails.id,
    args: {
-     created_by: updatedOrgDetails.created_by,
+     created_by: updatedOrgDetails.created_by,
+     updated_by: loggedInUserId,
      name: updatedOrgDetails.name,
      code: updatedOrgDetails.code,
      updated_at: updatedOrgDetails?.updated_at || new Date(),
      status: updatedOrgDetails?.status || common.INACTIVE_STATUS,
      id: updatedOrgDetails.id,
      tenant_code: tenantCode,
    },
  })
src/helpers/eventBroadcasterMain.js (1)

8-10: Fix trimming: filter doesn't trim the values.

You’re filtering by truthiness of url.trim() but not trimming the returned strings. Map to trim, then filter.

- if (process.env.EVENT_ORG_LISTENER_URLS)
-   return process.env.EVENT_ORG_LISTENER_URLS.split(',').filter((url) => url.trim())
+ if (process.env.EVENT_ORG_LISTENER_URLS)
+   return process.env.EVENT_ORG_LISTENER_URLS.split(',').map((url) => url.trim()).filter(Boolean)

- if (process.env.EVENT_USER_LISTENER_API)
-   return process.env.EVENT_USER_LISTENER_API.split(',').filter((url) => url.trim())
+ if (process.env.EVENT_USER_LISTENER_API)
+   return process.env.EVENT_USER_LISTENER_API.split(',').map((url) => url.trim()).filter(Boolean)

- if (process.env.EVENT_TENANT_LISTENER_API)
-   return process.env.EVENT_TENANT_LISTENER_API.split(',').filter((url) => url.trim())
+ if (process.env.EVENT_TENANT_LISTENER_API)
+   return process.env.EVENT_TENANT_LISTENER_API.split(',').map((url) => url.trim()).filter(Boolean)

Also applies to: 12-14, 16-18

src/services/tenant.js (4)

409-410: Await the broadcaster for consistency and error surfacing.

Aligns with org flow and ensures failures are visible.

- broadcastUserServiceEvent('tenantEvents', { requestBody: eventBody, isInternal: true })
+ await broadcastUserServiceEvent('tenantEvents', { requestBody: eventBody, isInternal: true })

565-583: Await tenant update event (domain add).

Ensure event sending completes and errors are handled.

- broadcastUserServiceEvent('tenantEvents', { requestBody: eventBodyData, isInternal: true })
+ await broadcastUserServiceEvent('tenantEvents', { requestBody: eventBodyData, isInternal: true })

705-723: Await tenant update event (domain remove).

- broadcastUserServiceEvent('tenantEvents', { requestBody: eventBodyData, isInternal: true })
+ await broadcastUserServiceEvent('tenantEvents', { requestBody: eventBodyData, isInternal: true })

877-900: Await broadcaster inside tenantEventEmitter.

Calling sites await the emitter; without awaiting the internal broadcast, they still return before emit completes.

- broadcastUserServiceEvent('tenantEvents', { requestBody: eventBodyData, isInternal: true })
+ await broadcastUserServiceEvent('tenantEvents', { requestBody: eventBodyData, isInternal: true })
src/services/organization.js (1)

831-867: Await broadcaster in orgEventEmitter.

Prevents returning before the event is sent.

- broadcastUserServiceEvent('organizationEvents', { requestBody: eventBodyData, isInternal: true })
+ await broadcastUserServiceEvent('organizationEvents', { requestBody: eventBodyData, isInternal: true })
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0411c50 and ffbb4ed.

📒 Files selected for processing (10)
  • src/.env.sample (1 hunks)
  • src/envVariables.js (2 hunks)
  • src/generics/utils.js (2 hunks)
  • src/helpers/eventBroadcasterMain.js (4 hunks)
  • src/helpers/userInvite.js (3 hunks)
  • src/services/account.js (3 hunks)
  • src/services/admin.js (6 hunks)
  • src/services/organization.js (9 hunks)
  • src/services/tenant.js (7 hunks)
  • src/services/user.js (2 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
src/services/**

⚙️ CodeRabbit configuration file

This is core business logic. Please check for correctness, efficiency, and potential edge cases.

Files:

  • src/services/user.js
  • src/services/account.js
  • src/services/organization.js
  • src/services/tenant.js
  • src/services/admin.js
🧠 Learnings (18)
📓 Common learnings
Learnt from: praveenKDass
PR: ELEVATE-Project/user#816
File: src/services/organization.js:831-0
Timestamp: 2025-09-12T10:40:34.860Z
Learning: In the ELEVATE-Project/user codebase, the organizationDTO.eventBodyDTO is designed to handle empty changes arrays properly, so organization update events should always be emitted even when changedValues is empty. The DTO handles this scenario correctly and there may be other reasons to emit events beyond just field changes (like metadata updates).
📚 Learning: 2025-09-12T11:03:00.543Z
Learnt from: praveenKDass
PR: ELEVATE-Project/user#816
File: src/dtos/tenantDTO.js:23-0
Timestamp: 2025-09-12T11:03:00.543Z
Learning: In the ELEVATE-Project/user codebase, the oldValue and newValue properties in changedValues objects passed to tenantDTO.eventBodyDTO are guaranteed to be defined when present, so undefined checks are not necessary according to the data contracts.

Applied to files:

  • src/generics/utils.js
  • src/services/organization.js
  • src/services/tenant.js
📚 Learning: 2025-09-12T10:40:34.860Z
Learnt from: praveenKDass
PR: ELEVATE-Project/user#816
File: src/services/organization.js:831-0
Timestamp: 2025-09-12T10:40:34.860Z
Learning: In the ELEVATE-Project/user codebase, the organizationDTO.eventBodyDTO is designed to handle empty changes arrays properly, so organization update events should always be emitted even when changedValues is empty. The DTO handles this scenario correctly and there may be other reasons to emit events beyond just field changes (like metadata updates).

Applied to files:

  • src/services/organization.js
  • src/services/tenant.js
  • src/services/admin.js
📚 Learning: 2025-09-12T10:14:22.455Z
Learnt from: praveenKDass
PR: ELEVATE-Project/user#816
File: src/services/organization.js:831-867
Timestamp: 2025-09-12T10:14:22.455Z
Learning: In the ELEVATE-Project/user codebase, the related_orgs field is stored as an array in the database by default, so additional array validation checks are unnecessary when processing related organization data.

Applied to files:

  • src/services/organization.js
📚 Learning: 2025-09-12T10:35:28.285Z
Learnt from: praveenKDass
PR: ELEVATE-Project/user#816
File: src/services/admin.js:704-704
Timestamp: 2025-09-12T10:35:28.285Z
Learning: In ELEVATE-Project/user admin.js, there's an inconsistency in organizationQueries.update usage: addOrgAdmin calls it without options (returns number), while deactivateOrg calls it with { returning: true, raw: true } (returns object). The validation logic 'if (orgRowsAffected === 0)' only works for the first case - the second case needs 'if (orgRowsAffected.rowsAffected === 0)' to properly validate updates.

Applied to files:

  • src/services/organization.js
  • src/services/tenant.js
  • src/services/admin.js
📚 Learning: 2025-09-12T10:35:28.285Z
Learnt from: praveenKDass
PR: ELEVATE-Project/user#816
File: src/services/admin.js:704-704
Timestamp: 2025-09-12T10:35:28.285Z
Learning: In the ELEVATE-Project/user codebase, when using organizationQueries.update with { returning: true, raw: true } options, the method consistently returns a structured result with updatedRows property when successful. The initial check for orgRowsAffected === 0 already handles failed updates, so additional null checking for updatedRows is redundant when the code execution reaches the event emission logic.

Applied to files:

  • src/services/organization.js
  • src/services/tenant.js
  • src/services/admin.js
📚 Learning: 2025-09-12T11:02:51.008Z
Learnt from: praveenKDass
PR: ELEVATE-Project/user#816
File: src/dtos/organizationDTO.js:14-43
Timestamp: 2025-09-12T11:02:51.008Z
Learning: In the ELEVATE-Project/user codebase, oldValue and newValue in the changedValues array for organizationDTO.eventBodyDTO are guaranteed to not be undefined by the business logic, and filtering out falsy values (0, false, '') from changes is the intended behavior.

Applied to files:

  • src/services/organization.js
  • src/services/admin.js
📚 Learning: 2025-09-12T10:35:28.285Z
Learnt from: praveenKDass
PR: ELEVATE-Project/user#816
File: src/services/admin.js:704-704
Timestamp: 2025-09-12T10:35:28.285Z
Learning: In the ELEVATE-Project/user codebase, when organizationQueries.update is called with { returning: true, raw: true }, it returns an object { rowsAffected, updatedRows }, not just a number. The validation logic in admin.js deactivateOrg method checking 'if (orgRowsAffected === 0)' is incorrect and should be 'if (orgRowsAffected.rowsAffected === 0)' to properly validate successful updates.

Applied to files:

  • src/services/organization.js
  • src/services/tenant.js
  • src/services/admin.js
📚 Learning: 2025-09-12T11:02:51.008Z
Learnt from: praveenKDass
PR: ELEVATE-Project/user#816
File: src/dtos/organizationDTO.js:14-43
Timestamp: 2025-09-12T11:02:51.008Z
Learning: In the ELEVATE-Project/user codebase, the changedValues parameter in organizationDTO.eventBodyDTO is guaranteed to always be an array by the calling code, so additional Array.isArray validation is not needed.

Applied to files:

  • src/services/organization.js
  • src/services/admin.js
📚 Learning: 2025-09-12T10:36:43.529Z
Learnt from: praveenKDass
PR: ELEVATE-Project/user#816
File: src/dtos/organizationDTO.js:26-29
Timestamp: 2025-09-12T10:36:43.529Z
Learning: In the ELEVATE-Project/user codebase, the changedValues array passed to organizationDTO.eventBodyDTO and tenantDTO.eventBodyDTO always contains a fieldName property when oldValue or newValue are present, so additional fieldName validation is not needed according to the business logic.

Applied to files:

  • src/services/organization.js
📚 Learning: 2025-09-12T10:38:57.594Z
Learnt from: praveenKDass
PR: ELEVATE-Project/user#816
File: src/services/organization.js:831-0
Timestamp: 2025-09-12T10:38:57.594Z
Learning: In the ELEVATE-Project/user codebase, when building event payloads in orgEventEmitter function, the preference is to use direct property access without nullish coalescing operators (??) for args object properties. This ensures fail-fast behavior - if expected document properties are missing, the system should throw an error rather than silently falling back to default values, helping to catch data integrity issues early.

Applied to files:

  • src/services/organization.js
📚 Learning: 2025-09-12T11:02:51.008Z
Learnt from: praveenKDass
PR: ELEVATE-Project/user#816
File: src/dtos/organizationDTO.js:14-43
Timestamp: 2025-09-12T11:02:51.008Z
Learning: In the ELEVATE-Project/user codebase, the organizationDTO.eventBodyDTO method intentionally filters out deleted_at from args for all event types (create, update, delete), not just non-delete events. This is by design according to the business requirements.

Applied to files:

  • src/services/organization.js
  • src/services/admin.js
📚 Learning: 2025-09-12T10:02:11.455Z
Learnt from: praveenKDass
PR: ELEVATE-Project/user#816
File: src/generics/kafka-communication.js:28-0
Timestamp: 2025-09-12T10:02:11.455Z
Learning: In the ELEVATE-Project/user codebase, EVENT_TENANT_KAFKA_TOPIC uses conditional validation (requiredIf) in src/envVariables.js, not default values. When EVENT_ENABLE_TENANT_EVENTS equals 'true', the topic becomes required and application startup validation ensures it's present, preventing runtime issues with undefined topics.

Applied to files:

  • src/helpers/eventBroadcasterMain.js
  • src/envVariables.js
  • src/services/admin.js
  • src/.env.sample
📚 Learning: 2025-09-12T10:02:11.455Z
Learnt from: praveenKDass
PR: ELEVATE-Project/user#816
File: src/generics/kafka-communication.js:28-0
Timestamp: 2025-09-12T10:02:11.455Z
Learning: In the ELEVATE-Project/user codebase, environment variables use conditional validation via requiredIf logic in src/envVariables.js rather than default values. When EVENT_ENABLE_TENANT_EVENTS equals 'true', EVENT_TENANT_KAFKA_TOPIC becomes required and startup validation ensures it's present.

Applied to files:

  • src/helpers/eventBroadcasterMain.js
  • src/envVariables.js
  • src/services/admin.js
  • src/.env.sample
📚 Learning: 2025-09-12T10:01:49.571Z
Learnt from: praveenKDass
PR: ELEVATE-Project/user#816
File: src/generics/kafka-communication.js:38-0
Timestamp: 2025-09-12T10:01:49.571Z
Learning: In the ELEVATE-Project/user codebase, environment variables use a requiredIf validation pattern where EVENT_ORGANIZATION_KAFKA_TOPIC and EVENT_TENANT_KAFKA_TOPIC are optional by default but become required when their respective enable flags (EVENT_ENABLE_ORGANIZATION_EVENTS and EVENT_ENABLE_TENANT_EVENTS) equal 'true'. This prevents undefined topic issues through environment validation rather than runtime guards.

Applied to files:

  • src/helpers/eventBroadcasterMain.js
  • src/envVariables.js
  • src/services/admin.js
  • src/.env.sample
📚 Learning: 2025-09-11T23:15:19.523Z
Learnt from: praveenKDass
PR: ELEVATE-Project/user#816
File: src/helpers/eventBroadcasterMain.js:14-17
Timestamp: 2025-09-11T23:15:19.523Z
Learning: In the ELEVATE-Project/user codebase, switch cases typically use return statements without break statements when each case returns a value directly. However, when a case has conditional returns, it should include a fallback return statement to prevent unintended fallthrough behavior.

Applied to files:

  • src/helpers/eventBroadcasterMain.js
📚 Learning: 2025-08-08T13:06:32.911Z
Learnt from: nevil-mathew
PR: ELEVATE-Project/user#790
File: src/helpers/userHelper.js:10-10
Timestamp: 2025-08-08T13:06:32.911Z
Learning: In ELEVATE-Project/user, avoid potential circular dependencies between src/helpers/userHelper.js and src/services/user-sessions by extracting shared session cleanup logic (finding active sessions, deleting Redis keys, ending sessions) into a dedicated utility (e.g., helpers/sessionCleanup.js) that both layers can consume.

Applied to files:

  • src/services/admin.js
📚 Learning: 2025-08-08T15:12:44.423Z
Learnt from: nevil-mathew
PR: ELEVATE-Project/user#790
File: src/helpers/userHelper.js:117-156
Timestamp: 2025-08-08T15:12:44.423Z
Learning: In ELEVATE-Project/user, prefer extracting shared session teardown (find active sessions, delete Redis keys, mark ended_at) into helpers/sessionCleanup.js so both helpers and services use it, preventing upward (helper→service) dependencies and avoiding circular imports.

Applied to files:

  • src/services/admin.js
🧬 Code graph analysis (8)
src/services/user.js (3)
src/helpers/userInvite.js (7)
  • require (18-18)
  • require (23-23)
  • require (27-27)
  • require (28-28)
  • require (29-29)
  • eventBody (882-896)
  • eventBody (1125-1130)
src/services/account.js (6)
  • require (26-26)
  • require (27-27)
  • require (37-37)
  • require (41-41)
  • eventBody (604-623)
  • eventBody (1754-1769)
src/services/admin.js (6)
  • require (11-11)
  • require (27-27)
  • require (34-34)
  • require (36-36)
  • eventBody (64-81)
  • eventBody (208-219)
src/services/account.js (3)
src/helpers/userInvite.js (5)
  • require (18-18)
  • require (23-23)
  • require (27-27)
  • require (28-28)
  • require (29-29)
src/services/admin.js (4)
  • require (11-11)
  • require (27-27)
  • require (34-34)
  • require (36-36)
src/services/user.js (5)
  • require (17-17)
  • require (19-19)
  • require (20-20)
  • require (24-24)
  • require (26-26)
src/helpers/userInvite.js (2)
src/services/account.js (6)
  • require (26-26)
  • require (27-27)
  • require (37-37)
  • require (41-41)
  • eventBody (604-623)
  • eventBody (1754-1769)
src/services/user.js (6)
  • require (17-17)
  • require (19-19)
  • require (20-20)
  • require (24-24)
  • require (26-26)
  • eventBody (191-201)
src/generics/utils.js (2)
src/services/organization.js (2)
  • changes (833-833)
  • _ (13-13)
src/services/tenant.js (4)
  • changes (557-563)
  • changes (697-703)
  • changes (879-879)
  • _ (30-30)
src/services/organization.js (4)
src/services/account.js (5)
  • require (26-26)
  • require (27-27)
  • require (37-37)
  • require (41-41)
  • common (16-16)
src/services/admin.js (9)
  • require (11-11)
  • require (27-27)
  • require (34-34)
  • require (36-36)
  • require (37-37)
  • eventBodyData (705-718)
  • organizationDTO (43-43)
  • common (15-15)
  • updatedOrgDetails (704-704)
src/database/queries/organization.js (3)
  • require (2-2)
  • require (3-3)
  • common (4-4)
src/dtos/eventBody.js (1)
  • changes (11-17)
src/helpers/eventBroadcasterMain.js (5)
src/helpers/userInvite.js (2)
  • kafkaCommunication (17-17)
  • requestBody (941-945)
src/services/account.js (1)
  • kafkaCommunication (20-20)
src/services/organization.js (1)
  • kafkaCommunication (11-11)
src/services/org-admin.js (1)
  • kafkaCommunication (15-15)
src/services/userInvite.js (2)
  • kafkaCommunication (24-24)
  • requestBody (379-383)
src/services/tenant.js (4)
src/helpers/userInvite.js (9)
  • require (18-18)
  • require (23-23)
  • require (27-27)
  • require (28-28)
  • require (29-29)
  • eventBody (882-896)
  • eventBody (1125-1130)
  • common (8-8)
  • utils (3-3)
src/services/account.js (8)
  • require (26-26)
  • require (27-27)
  • require (37-37)
  • require (41-41)
  • eventBody (604-623)
  • eventBody (1754-1769)
  • common (16-16)
  • utils (25-25)
src/services/admin.js (10)
  • require (11-11)
  • require (27-27)
  • require (34-34)
  • require (36-36)
  • require (37-37)
  • eventBody (64-81)
  • eventBody (208-219)
  • common (15-15)
  • eventBodyData (705-718)
  • utils (17-17)
src/services/organization.js (7)
  • eventBody (138-146)
  • common (2-2)
  • tenantCode (281-281)
  • changes (833-833)
  • eventBodyData (151-169)
  • eventBodyData (845-865)
  • utils (5-5)
src/services/admin.js (2)
src/services/account.js (7)
  • require (26-26)
  • require (27-27)
  • require (37-37)
  • require (41-41)
  • eventBody (604-623)
  • eventBody (1754-1769)
  • common (16-16)
src/services/organization.js (10)
  • require (12-12)
  • require (14-14)
  • require (15-15)
  • require (18-18)
  • require (22-22)
  • organizationDTO (19-19)
  • eventBody (138-146)
  • common (2-2)
  • eventBodyData (151-169)
  • eventBodyData (845-865)
🪛 dotenv-linter (3.3.0)
src/.env.sample

[warning] 183-183: [TrailingWhitespace] Trailing whitespace detected

(TrailingWhitespace)


[warning] 183-183: [UnorderedKey] The KAFKA_HEALTH_CHECK_TOPIC key should go before the NOTIFICATION_API_URL key

(UnorderedKey)


[warning] 185-185: [TrailingWhitespace] Trailing whitespace detected

(TrailingWhitespace)


[warning] 185-185: [UnorderedKey] The EVENT_ENABLE_TENANT_EVENTS key should go before the EVENT_ENABLE_USER_EVENTS key

(UnorderedKey)


[warning] 187-187: [TrailingWhitespace] Trailing whitespace detected

(TrailingWhitespace)


[warning] 187-187: [UnorderedKey] The ENABLE_TENANT_KAFKA_EVENTS key should go before the ENTITY_MANAGEMENT_SERVICE_BASE_URL key

(UnorderedKey)


[warning] 189-189: [TrailingWhitespace] Trailing whitespace detected

(TrailingWhitespace)


[warning] 189-189: [UnorderedKey] The ENABLE_USER_KAFKA_EVENTS key should go before the ENTITY_MANAGEMENT_SERVICE_BASE_URL key

(UnorderedKey)


[warning] 191-191: [TrailingWhitespace] Trailing whitespace detected

(TrailingWhitespace)


[warning] 191-191: [UnorderedKey] The ENABLE_ORG_KAFKA_EVENTS key should go before the ENABLE_TENANT_KAFKA_EVENTS key

(UnorderedKey)


[warning] 193-193: [QuoteCharacter] The value has quote characters (', ")

(QuoteCharacter)


[warning] 193-193: [UnorderedKey] The EVENT_TENANT_KAFKA_TOPIC key should go before the EVENT_USER_KAFKA_TOPIC key

(UnorderedKey)


[warning] 195-195: [QuoteCharacter] The value has quote characters (', ")

(QuoteCharacter)


[warning] 195-195: [UnorderedKey] The EVENT_TENANT_LISTENER_API key should go before the EVENT_USER_KAFKA_TOPIC key

(UnorderedKey)


[warning] 197-197: [TrailingWhitespace] Trailing whitespace detected

(TrailingWhitespace)


[warning] 197-197: [UnorderedKey] The EVENT_ENABLE_ORGANIZATION_EVENTS key should go before the EVENT_ENABLE_TENANT_EVENTS key

(UnorderedKey)


[warning] 199-199: [EndingBlankLine] No blank line at the end of the file

(EndingBlankLine)


[warning] 199-199: [QuoteCharacter] The value has quote characters (', ")

(QuoteCharacter)


[warning] 199-199: [UnorderedKey] The EVENT_ORGANIZATION_KAFKA_TOPIC key should go before the EVENT_TENANT_KAFKA_TOPIC key

(UnorderedKey)

🔇 Additional comments (32)
src/services/user.js (2)

26-26: Switch to service-based broadcaster looks good.

Import updated to broadcastUserServiceEvent as intended.


203-203: Approve: legacy broadcaster fully removed

No occurrences of broadcastUserEvent remain; event dispatch updated correctly.

src/services/account.js (3)

41-41: Updated import aligns with new event routing.

Looks correct.


625-626: Create event routed via service broadcaster.

Emission matches the new pattern and topic.


1771-1771: Delete event routed via service broadcaster.

Consistent with other modules.

src/helpers/userInvite.js (3)

28-28: Import migration to service broadcaster is correct.

No issues.


898-899: Bulk-update event emission path updated correctly.

Good use of broadcastUserServiceEvent.


1132-1133: Bulk-create event emission path updated correctly.

Good.

src/generics/utils.js (1)

1085-1109: Solid rename and JSDoc; utility is clear and minimal.

Defaults and deep-equality usage look good.

src/services/admin.js (5)

34-34: Import updated to centralized broadcaster.

Good.


43-43: organizationDTO import to build event body is appropriate.

Good addition.


83-84: Delete-user event broadcast path migrated.

Looks correct.


221-221: Admin create event routed via service broadcaster.

No issues.


656-666: Verify organizationQueries.update return structure. Unable to locate its implementation; confirm that with { returning: true, raw: true } it returns an object { rowsAffected, updatedRows } so the zero-row check uses rowsAffected.

src/services/tenant.js (7)

33-35: Imports look good.

Consistent with new broadcaster/DTO usage.


376-378: Good: persist default org identifiers onto tenant response.

Used immediately for the create event payload.


388-407: Tenant create event payload looks correct.

Schema consistent (meta kept as object; org_id/org_code present).


462-472: Update + safe extraction of first updated row: LGTM.

Avoids destructuring undefined and still emits update events when no row changes.


475-476: Good: centralized emitter invocation.

Keeps change-detection and emission logic consistent.


557-563: Changes payload for domain add: LGTM.


697-703: Changes payload for domain remove: LGTM.

src/services/organization.js (8)

22-23: Import of new broadcaster: LGTM.


137-149: Duplicate create emissions (legacy + new). Remove or gate legacy call.

This still fires eventBroadcasterMain('organizationEvents', ...) and then broadcastUserServiceEvent, which internally calls the same broadcaster again, producing duplicates.

- // This event will deprecated in future
- const eventBody = eventBodyDTO({
-   entity: 'organization',
-   eventType: 'create',
-   entityId: createdOrganization.id,
-   args: {
-     name: bodyData.name,
-     created_by: loggedInUserId,
-   },
- })
- eventBroadcasterMain('organizationEvents', { requestBody: eventBody, isInternal: true })
- //ends here
+ // (legacy emission removed to avoid duplicate events)

150-172: New org create payload + awaited broadcast: LGTM.


252-254: Good: await orgEventEmitter on update.


559-585: Related orgs add: using returning+raw and delta emission: LGTM.


586-589: Good: emit with bodyData.related_orgs context.


637-648: Related orgs remove path: LGTM.


664-666: Good: emit with removal context.

src/envVariables.js (3)

399-403: New user Kafka flag: LGTM.


422-447: Tenant event flags and topic gating: LGTM.


448-460: Org Kafka flag + topic requiredIf: LGTM.

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

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ffbb4ed and 07b7409.

📒 Files selected for processing (1)
  • src/helpers/eventBroadcasterMain.js (4 hunks)
🧰 Additional context used
🧠 Learnings (5)
📓 Common learnings
Learnt from: praveenKDass
PR: ELEVATE-Project/user#816
File: src/services/organization.js:831-0
Timestamp: 2025-09-12T10:40:34.860Z
Learning: In the ELEVATE-Project/user codebase, the organizationDTO.eventBodyDTO is designed to handle empty changes arrays properly, so organization update events should always be emitted even when changedValues is empty. The DTO handles this scenario correctly and there may be other reasons to emit events beyond just field changes (like metadata updates).
📚 Learning: 2025-09-12T10:02:11.455Z
Learnt from: praveenKDass
PR: ELEVATE-Project/user#816
File: src/generics/kafka-communication.js:28-0
Timestamp: 2025-09-12T10:02:11.455Z
Learning: In the ELEVATE-Project/user codebase, EVENT_TENANT_KAFKA_TOPIC uses conditional validation (requiredIf) in src/envVariables.js, not default values. When EVENT_ENABLE_TENANT_EVENTS equals 'true', the topic becomes required and application startup validation ensures it's present, preventing runtime issues with undefined topics.

Applied to files:

  • src/helpers/eventBroadcasterMain.js
📚 Learning: 2025-09-12T10:02:11.455Z
Learnt from: praveenKDass
PR: ELEVATE-Project/user#816
File: src/generics/kafka-communication.js:28-0
Timestamp: 2025-09-12T10:02:11.455Z
Learning: In the ELEVATE-Project/user codebase, environment variables use conditional validation via requiredIf logic in src/envVariables.js rather than default values. When EVENT_ENABLE_TENANT_EVENTS equals 'true', EVENT_TENANT_KAFKA_TOPIC becomes required and startup validation ensures it's present.

Applied to files:

  • src/helpers/eventBroadcasterMain.js
📚 Learning: 2025-09-12T10:01:49.571Z
Learnt from: praveenKDass
PR: ELEVATE-Project/user#816
File: src/generics/kafka-communication.js:38-0
Timestamp: 2025-09-12T10:01:49.571Z
Learning: In the ELEVATE-Project/user codebase, environment variables use a requiredIf validation pattern where EVENT_ORGANIZATION_KAFKA_TOPIC and EVENT_TENANT_KAFKA_TOPIC are optional by default but become required when their respective enable flags (EVENT_ENABLE_ORGANIZATION_EVENTS and EVENT_ENABLE_TENANT_EVENTS) equal 'true'. This prevents undefined topic issues through environment validation rather than runtime guards.

Applied to files:

  • src/helpers/eventBroadcasterMain.js
📚 Learning: 2025-09-11T23:15:19.523Z
Learnt from: praveenKDass
PR: ELEVATE-Project/user#816
File: src/helpers/eventBroadcasterMain.js:14-17
Timestamp: 2025-09-11T23:15:19.523Z
Learning: In the ELEVATE-Project/user codebase, switch cases typically use return statements without break statements when each case returns a value directly. However, when a case has conditional returns, it should include a fallback return statement to prevent unintended fallthrough behavior.

Applied to files:

  • src/helpers/eventBroadcasterMain.js
🧬 Code graph analysis (1)
src/helpers/eventBroadcasterMain.js (5)
src/helpers/userInvite.js (2)
  • kafkaCommunication (17-17)
  • requestBody (941-945)
src/services/organization.js (1)
  • kafkaCommunication (11-11)
src/services/org-admin.js (1)
  • kafkaCommunication (15-15)
src/services/userInvite.js (2)
  • kafkaCommunication (24-24)
  • requestBody (379-383)
src/services/account.js (1)
  • kafkaCommunication (20-20)

@nevil-mathew
Copy link
Collaborator

@coderabbitai summary

@coderabbitai
Copy link

coderabbitai bot commented Sep 29, 2025

✅ Actions performed

Summary regeneration triggered.

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

Caution

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

⚠️ Outside diff range comments (1)
src/generics/utils.js (1)

914-926: Fix minutes-to-seconds conversion and preserve unit context

In src/generics/utils.js, convertExpiryTimeToSeconds currently divides by 60 for “Xm” and the call‐site’s parseInt strips the “m” (e.g. “20m” → 20 seconds), resulting in incorrect TTLs. Replace with:

 function convertExpiryTimeToSeconds(expiryTime) {
-  expiryTime = String(expiryTime)
-  const match = expiryTime.match(/^(\d+)([m]?)$/)
-  if (match) {
-    const value = parseInt(match[1], 10)
-    const unit = match[2]
-    if (unit === 'm') {
-      return Math.floor(value / 60)
-    } else {
-      return value
-    }
-  }
+  const str = String(expiryTime).trim()
+  const match = str.match(/^(\d+)(m)?$/i)
+  if (!match) return NaN
+  const value = parseInt(match[1], 10)
+  return match[2] ? value * 60 : value
 }

Also update the call-site (e.g. in src/generics/utils.js or cloud-services.js) to pass the raw env string instead of parsing it first:

- let expiryInSeconds = parseInt(process.env.SIGNED_URL_EXPIRY_DURATION) || 300
- let updatedExpiryTime = convertExpiryTimeToSeconds(expiryInSeconds)
+ const rawExpiry = process.env.SIGNED_URL_EXPIRY_DURATION ?? '300'
+ const updatedExpiryTime = convertExpiryTimeToSeconds(rawExpiry)
🧹 Nitpick comments (3)
src/services/admin.js (2)

83-84: Make event emission explicitly non-blocking (or confirm helper behavior)

If broadcastUserServiceEvent returns a Promise and throws on failure, consider deferring to avoid request-path latency/risks, or confirm the helper already handles this internally.

Suggested change:

- broadcastUserServiceEvent('userEvents', { requestBody: eventBody, isInternal: true })
+ setImmediate(() =>
+   broadcastUserServiceEvent('userEvents', { requestBody: eventBody, isInternal: true })
+ )

If the helper already enqueues asynchronously, keep as-is and ignore this. As per coding guidelines.

Also applies to: 221-222


676-686: Good fix on update() result handling; minor clarity refactor

Using { returning: true, raw: true } and checking rowsAffected is correct. For readability and to prevent shape mix-ups elsewhere, destructure the result and use consistent names.

-const orgUpdateResult = await organizationQueries.update(
+const orgUpdateResult = await organizationQueries.update(
   { code: organizationCode, tenant_code: tenantCode },
   { status: common.INACTIVE_STATUS, updated_by: loggedInUserId },
   { returning: true, raw: true }
 )
-
-if (!orgUpdateResult || orgUpdateResult.rowsAffected === 0) {
+const { rowsAffected, updatedRows } = orgUpdateResult || {}
+if (!rowsAffected) {
   return responses.failureResponse({
     message: 'ORG_DEACTIVATION_FAILED',
     statusCode: httpStatusCode.bad_request,
     responseCode: 'CLIENT_ERROR',
   })
 }

This also lets you reuse updatedRows below without repeating the long path. As per coding guidelines.

Also applies to: 688-694

src/generics/utils.js (1)

1090-1097: Nit: tighten JSDoc and use consts

Clarify param meaning and prefer const; also inline fieldName to avoid a temp.

-/**
- * Compare object and get the updated value with old value
+/**
+ * Compare objects and return changed fields (with old and new values).
  * @method
  * @name extractUpdatedValues
  * @param {Object} oldData - Data before update
  * @param {Object} newData - Data after update
- * @param {Object} updateData - reqBody data
+ * @param {Object} updateData - reqBody keys to inspect (fields intended to be updated)
  * @returns {Array<{fieldName:string, oldValue:any, newValue:any}>}
  */
 
 function extractUpdatedValues(oldData = {}, newData = {}, updateData = {}) {
   const changes = []
 
   for (const key of Object.keys(updateData)) {
-    let oldValue = oldData[key]
-    let newValue = newData[key]
-    let fieldName = key
+    const oldValue = oldData[key]
+    const newValue = newData[key]
     // Compare only if key exists in updateData
     if (!_.isEqual(oldValue, newValue)) {
-      changes.push({ fieldName, oldValue, newValue })
+      changes.push({ fieldName: key, oldValue, newValue })
     }
   }
 
   return changes
 }

Also applies to: 1103-1109

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 07b7409 and fc7c2b5.

📒 Files selected for processing (3)
  • src/generics/utils.js (2 hunks)
  • src/helpers/userInvite.js (3 hunks)
  • src/services/admin.js (6 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/helpers/userInvite.js
🧰 Additional context used
📓 Path-based instructions (1)
src/services/**

⚙️ CodeRabbit configuration file

This is core business logic. Please check for correctness, efficiency, and potential edge cases.

Files:

  • src/services/admin.js
🧠 Learnings (12)
📓 Common learnings
Learnt from: praveenKDass
PR: ELEVATE-Project/user#816
File: src/helpers/eventBroadcasterMain.js:32-37
Timestamp: 2025-09-29T10:51:54.009Z
Learning: In the ELEVATE-Project/user codebase, the environment variables ENABLE_USER_KAFKA_EVENTS, ENABLE_TENANT_KAFKA_EVENTS, and ENABLE_ORG_KAFKA_EVENTS are properly defined in src/envVariables.js with optional: true validation. These variables control Kafka event publishing for their respective entity types and are correctly referenced in src/helpers/eventBroadcasterMain.js isEventEnabled function.
Learnt from: praveenKDass
PR: ELEVATE-Project/user#816
File: src/helpers/eventBroadcasterMain.js:32-37
Timestamp: 2025-09-29T10:51:54.009Z
Learning: In the ELEVATE-Project/user codebase, the environment variables ENABLE_USER_KAFKA_EVENTS, ENABLE_TENANT_KAFKA_EVENTS, and ENABLE_ORG_KAFKA_EVENTS are properly defined in src/envVariables.js with optional: true validation. These variables control Kafka event publishing for their respective entity types and are correctly referenced in src/helpers/eventBroadcasterMain.js isEventEnabled function.
Learnt from: praveenKDass
PR: ELEVATE-Project/user#816
File: src/services/organization.js:831-0
Timestamp: 2025-09-12T10:40:34.860Z
Learning: In the ELEVATE-Project/user codebase, the organizationDTO.eventBodyDTO is designed to handle empty changes arrays properly, so organization update events should always be emitted even when changedValues is empty. The DTO handles this scenario correctly and there may be other reasons to emit events beyond just field changes (like metadata updates).
📚 Learning: 2025-09-12T10:35:28.285Z
Learnt from: praveenKDass
PR: ELEVATE-Project/user#816
File: src/services/admin.js:704-704
Timestamp: 2025-09-12T10:35:28.285Z
Learning: In ELEVATE-Project/user admin.js, there's an inconsistency in organizationQueries.update usage: addOrgAdmin calls it without options (returns number), while deactivateOrg calls it with { returning: true, raw: true } (returns object). The validation logic 'if (orgRowsAffected === 0)' only works for the first case - the second case needs 'if (orgRowsAffected.rowsAffected === 0)' to properly validate updates.

Applied to files:

  • src/services/admin.js
📚 Learning: 2025-09-12T10:35:28.285Z
Learnt from: praveenKDass
PR: ELEVATE-Project/user#816
File: src/services/admin.js:704-704
Timestamp: 2025-09-12T10:35:28.285Z
Learning: In the ELEVATE-Project/user codebase, when organizationQueries.update is called with { returning: true, raw: true }, it returns an object { rowsAffected, updatedRows }, not just a number. The validation logic in admin.js deactivateOrg method checking 'if (orgRowsAffected === 0)' is incorrect and should be 'if (orgRowsAffected.rowsAffected === 0)' to properly validate successful updates.

Applied to files:

  • src/services/admin.js
📚 Learning: 2025-09-12T10:40:34.860Z
Learnt from: praveenKDass
PR: ELEVATE-Project/user#816
File: src/services/organization.js:831-0
Timestamp: 2025-09-12T10:40:34.860Z
Learning: In the ELEVATE-Project/user codebase, the organizationDTO.eventBodyDTO is designed to handle empty changes arrays properly, so organization update events should always be emitted even when changedValues is empty. The DTO handles this scenario correctly and there may be other reasons to emit events beyond just field changes (like metadata updates).

Applied to files:

  • src/services/admin.js
📚 Learning: 2025-09-12T11:02:51.008Z
Learnt from: praveenKDass
PR: ELEVATE-Project/user#816
File: src/dtos/organizationDTO.js:14-43
Timestamp: 2025-09-12T11:02:51.008Z
Learning: In the ELEVATE-Project/user codebase, oldValue and newValue in the changedValues array for organizationDTO.eventBodyDTO are guaranteed to not be undefined by the business logic, and filtering out falsy values (0, false, '') from changes is the intended behavior.

Applied to files:

  • src/services/admin.js
📚 Learning: 2025-09-12T10:35:28.285Z
Learnt from: praveenKDass
PR: ELEVATE-Project/user#816
File: src/services/admin.js:704-704
Timestamp: 2025-09-12T10:35:28.285Z
Learning: In the ELEVATE-Project/user codebase, when using organizationQueries.update with { returning: true, raw: true } options, the method consistently returns a structured result with updatedRows property when successful. The initial check for orgRowsAffected === 0 already handles failed updates, so additional null checking for updatedRows is redundant when the code execution reaches the event emission logic.

Applied to files:

  • src/services/admin.js
📚 Learning: 2025-09-29T10:51:54.009Z
Learnt from: praveenKDass
PR: ELEVATE-Project/user#816
File: src/helpers/eventBroadcasterMain.js:32-37
Timestamp: 2025-09-29T10:51:54.009Z
Learning: In the ELEVATE-Project/user codebase, the environment variables ENABLE_USER_KAFKA_EVENTS, ENABLE_TENANT_KAFKA_EVENTS, and ENABLE_ORG_KAFKA_EVENTS are properly defined in src/envVariables.js with optional: true validation. These variables control Kafka event publishing for their respective entity types and are correctly referenced in src/helpers/eventBroadcasterMain.js isEventEnabled function.

Applied to files:

  • src/services/admin.js
📚 Learning: 2025-09-12T11:02:51.008Z
Learnt from: praveenKDass
PR: ELEVATE-Project/user#816
File: src/dtos/organizationDTO.js:14-43
Timestamp: 2025-09-12T11:02:51.008Z
Learning: In the ELEVATE-Project/user codebase, the organizationDTO.eventBodyDTO method intentionally filters out deleted_at from args for all event types (create, update, delete), not just non-delete events. This is by design according to the business requirements.

Applied to files:

  • src/services/admin.js
📚 Learning: 2025-09-12T11:02:51.008Z
Learnt from: praveenKDass
PR: ELEVATE-Project/user#816
File: src/dtos/organizationDTO.js:14-43
Timestamp: 2025-09-12T11:02:51.008Z
Learning: In the ELEVATE-Project/user codebase, the changedValues parameter in organizationDTO.eventBodyDTO is guaranteed to always be an array by the calling code, so additional Array.isArray validation is not needed.

Applied to files:

  • src/services/admin.js
📚 Learning: 2025-08-08T13:06:32.911Z
Learnt from: nevil-mathew
PR: ELEVATE-Project/user#790
File: src/helpers/userHelper.js:10-10
Timestamp: 2025-08-08T13:06:32.911Z
Learning: In ELEVATE-Project/user, avoid potential circular dependencies between src/helpers/userHelper.js and src/services/user-sessions by extracting shared session cleanup logic (finding active sessions, deleting Redis keys, ending sessions) into a dedicated utility (e.g., helpers/sessionCleanup.js) that both layers can consume.

Applied to files:

  • src/services/admin.js
📚 Learning: 2025-08-08T15:12:44.423Z
Learnt from: nevil-mathew
PR: ELEVATE-Project/user#790
File: src/helpers/userHelper.js:117-156
Timestamp: 2025-08-08T15:12:44.423Z
Learning: In ELEVATE-Project/user, prefer extracting shared session teardown (find active sessions, delete Redis keys, mark ended_at) into helpers/sessionCleanup.js so both helpers and services use it, preventing upward (helper→service) dependencies and avoiding circular imports.

Applied to files:

  • src/services/admin.js
📚 Learning: 2025-09-12T11:03:00.543Z
Learnt from: praveenKDass
PR: ELEVATE-Project/user#816
File: src/dtos/tenantDTO.js:23-0
Timestamp: 2025-09-12T11:03:00.543Z
Learning: In the ELEVATE-Project/user codebase, the oldValue and newValue properties in changedValues objects passed to tenantDTO.eventBodyDTO are guaranteed to be defined when present, so undefined checks are not necessary according to the data contracts.

Applied to files:

  • src/generics/utils.js
🧬 Code graph analysis (1)
src/services/admin.js (2)
src/services/organization.js (10)
  • require (12-12)
  • require (14-14)
  • require (15-15)
  • require (18-18)
  • require (22-22)
  • organizationDTO (19-19)
  • eventBody (138-146)
  • common (2-2)
  • eventBodyData (151-169)
  • eventBodyData (845-865)
src/services/account.js (7)
  • require (26-26)
  • require (27-27)
  • require (37-37)
  • require (41-41)
  • eventBody (604-623)
  • eventBody (1754-1769)
  • common (16-16)
🔇 Additional comments (5)
src/services/admin.js (3)

34-34: Unified broadcaster import looks good

Switch to broadcastUserServiceEvent aligns with the new event pipeline. No issues spotted. Based on learnings.


43-43: DTO import added correctly

organizationDTO is used later for the org update event payload; import placement is fine.


723-741: Include updated_by and changedValues in deactivation event payload

For auditability and consumer diffs, add updated_by and emit changedValues (at least the status transition). This also matches how org updates are modeled elsewhere.

-//Event body for org update (deactivation)
-let updatedOrgDetails = orgUpdateResult.updatedRows?.[0]
-const eventBodyData = organizationDTO.eventBodyDTO({
+// Event body for org update (deactivation)
+const updatedOrgDetails = (orgUpdateResult.updatedRows || updatedRows || [])[0]
+const changes = [
+  { field: 'status', oldValue: org.status, newValue: updatedOrgDetails?.status }
+]
+const eventBodyData = organizationDTO.eventBodyDTO({
   entity: 'organization',
   eventType: 'update',
   entityId: updatedOrgDetails.id,
   args: {
     created_by: updatedOrgDetails.created_by,
+    updated_by: loggedInUserId,
     name: updatedOrgDetails.name,
     code: updatedOrgDetails.code,
     updated_at: updatedOrgDetails?.updated_at || new Date(),
     status: updatedOrgDetails?.status || common.INACTIVE_STATUS,
     id: updatedOrgDetails.id,
     tenant_code: tenantCode,
   },
+  changedValues: changes,
 })

If organizationDTO.eventBodyDTO already derives changedValues internally when omitted, feel free to skip that part—but please still include updated_by. Based on learnings.

src/generics/utils.js (2)

1161-1161: Export wiring added — OK

New util is correctly exported; no conflicts found.


1089-1113: extractUpdatedValues: implementation is correct and usage is consistent

  • Compares only updated keys and returns {fieldName, oldValue, newValue} with safe defaults; no extra guards required.
  • Rename from getChanges to extractUpdatedValues is applied project-wide; no residual references found.

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

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between fc7c2b5 and db52e4c.

📒 Files selected for processing (8)
  • src/envVariables.js (2 hunks)
  • src/helpers/eventBroadcasterMain.js (4 hunks)
  • src/helpers/userInvite.js (3 hunks)
  • src/services/account.js (3 hunks)
  • src/services/admin.js (6 hunks)
  • src/services/organization.js (9 hunks)
  • src/services/tenant.js (7 hunks)
  • src/services/user.js (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/services/user.js
  • src/helpers/userInvite.js
🧰 Additional context used
📓 Path-based instructions (1)
src/services/**

⚙️ CodeRabbit configuration file

This is core business logic. Please check for correctness, efficiency, and potential edge cases.

Files:

  • src/services/tenant.js
  • src/services/admin.js
  • src/services/organization.js
  • src/services/account.js
🧠 Learnings (16)
📓 Common learnings
Learnt from: praveenKDass
PR: ELEVATE-Project/user#816
File: src/helpers/eventBroadcasterMain.js:32-37
Timestamp: 2025-09-29T10:51:54.028Z
Learning: In the ELEVATE-Project/user codebase, the environment variables ENABLE_USER_KAFKA_EVENTS, ENABLE_TENANT_KAFKA_EVENTS, and ENABLE_ORG_KAFKA_EVENTS are properly defined in src/envVariables.js with optional: true validation. These variables control Kafka event publishing for their respective entity types and are correctly referenced in src/helpers/eventBroadcasterMain.js isEventEnabled function.
Learnt from: praveenKDass
PR: ELEVATE-Project/user#816
File: src/helpers/eventBroadcasterMain.js:32-37
Timestamp: 2025-09-29T10:51:54.028Z
Learning: In the ELEVATE-Project/user codebase, the environment variables ENABLE_USER_KAFKA_EVENTS, ENABLE_TENANT_KAFKA_EVENTS, and ENABLE_ORG_KAFKA_EVENTS are properly defined in src/envVariables.js with optional: true validation. These variables control Kafka event publishing for their respective entity types and are correctly referenced in src/helpers/eventBroadcasterMain.js isEventEnabled function.
Learnt from: praveenKDass
PR: ELEVATE-Project/user#816
File: src/services/organization.js:831-0
Timestamp: 2025-09-12T10:40:34.860Z
Learning: In the ELEVATE-Project/user codebase, the organizationDTO.eventBodyDTO is designed to handle empty changes arrays properly, so organization update events should always be emitted even when changedValues is empty. The DTO handles this scenario correctly and there may be other reasons to emit events beyond just field changes (like metadata updates).
📚 Learning: 2025-09-12T10:35:28.285Z
Learnt from: praveenKDass
PR: ELEVATE-Project/user#816
File: src/services/admin.js:704-704
Timestamp: 2025-09-12T10:35:28.285Z
Learning: In the ELEVATE-Project/user codebase, when using organizationQueries.update with { returning: true, raw: true } options, the method consistently returns a structured result with updatedRows property when successful. The initial check for orgRowsAffected === 0 already handles failed updates, so additional null checking for updatedRows is redundant when the code execution reaches the event emission logic.

Applied to files:

  • src/services/tenant.js
  • src/services/admin.js
  • src/services/organization.js
📚 Learning: 2025-09-12T11:03:00.543Z
Learnt from: praveenKDass
PR: ELEVATE-Project/user#816
File: src/dtos/tenantDTO.js:23-0
Timestamp: 2025-09-12T11:03:00.543Z
Learning: In the ELEVATE-Project/user codebase, the oldValue and newValue properties in changedValues objects passed to tenantDTO.eventBodyDTO are guaranteed to be defined when present, so undefined checks are not necessary according to the data contracts.

Applied to files:

  • src/services/tenant.js
  • src/services/organization.js
📚 Learning: 2025-09-12T10:35:28.285Z
Learnt from: praveenKDass
PR: ELEVATE-Project/user#816
File: src/services/admin.js:704-704
Timestamp: 2025-09-12T10:35:28.285Z
Learning: In the ELEVATE-Project/user codebase, when organizationQueries.update is called with { returning: true, raw: true }, it returns an object { rowsAffected, updatedRows }, not just a number. The validation logic in admin.js deactivateOrg method checking 'if (orgRowsAffected === 0)' is incorrect and should be 'if (orgRowsAffected.rowsAffected === 0)' to properly validate successful updates.

Applied to files:

  • src/services/tenant.js
  • src/services/admin.js
  • src/services/organization.js
📚 Learning: 2025-09-12T10:35:28.285Z
Learnt from: praveenKDass
PR: ELEVATE-Project/user#816
File: src/services/admin.js:704-704
Timestamp: 2025-09-12T10:35:28.285Z
Learning: In ELEVATE-Project/user admin.js, there's an inconsistency in organizationQueries.update usage: addOrgAdmin calls it without options (returns number), while deactivateOrg calls it with { returning: true, raw: true } (returns object). The validation logic 'if (orgRowsAffected === 0)' only works for the first case - the second case needs 'if (orgRowsAffected.rowsAffected === 0)' to properly validate updates.

Applied to files:

  • src/services/tenant.js
  • src/services/admin.js
  • src/services/organization.js
📚 Learning: 2025-09-12T10:40:34.860Z
Learnt from: praveenKDass
PR: ELEVATE-Project/user#816
File: src/services/organization.js:831-0
Timestamp: 2025-09-12T10:40:34.860Z
Learning: In the ELEVATE-Project/user codebase, the organizationDTO.eventBodyDTO is designed to handle empty changes arrays properly, so organization update events should always be emitted even when changedValues is empty. The DTO handles this scenario correctly and there may be other reasons to emit events beyond just field changes (like metadata updates).

Applied to files:

  • src/services/tenant.js
  • src/services/admin.js
  • src/services/organization.js
📚 Learning: 2025-09-29T10:51:54.028Z
Learnt from: praveenKDass
PR: ELEVATE-Project/user#816
File: src/helpers/eventBroadcasterMain.js:32-37
Timestamp: 2025-09-29T10:51:54.028Z
Learning: In the ELEVATE-Project/user codebase, the environment variables ENABLE_USER_KAFKA_EVENTS, ENABLE_TENANT_KAFKA_EVENTS, and ENABLE_ORG_KAFKA_EVENTS are properly defined in src/envVariables.js with optional: true validation. These variables control Kafka event publishing for their respective entity types and are correctly referenced in src/helpers/eventBroadcasterMain.js isEventEnabled function.

Applied to files:

  • src/helpers/eventBroadcasterMain.js
  • src/services/admin.js
  • src/envVariables.js
📚 Learning: 2025-09-11T23:15:19.523Z
Learnt from: praveenKDass
PR: ELEVATE-Project/user#816
File: src/helpers/eventBroadcasterMain.js:14-17
Timestamp: 2025-09-11T23:15:19.523Z
Learning: In the ELEVATE-Project/user codebase, switch cases typically use return statements without break statements when each case returns a value directly. However, when a case has conditional returns, it should include a fallback return statement to prevent unintended fallthrough behavior.

Applied to files:

  • src/helpers/eventBroadcasterMain.js
📚 Learning: 2025-09-12T11:02:51.008Z
Learnt from: praveenKDass
PR: ELEVATE-Project/user#816
File: src/dtos/organizationDTO.js:14-43
Timestamp: 2025-09-12T11:02:51.008Z
Learning: In the ELEVATE-Project/user codebase, the organizationDTO.eventBodyDTO method intentionally filters out deleted_at from args for all event types (create, update, delete), not just non-delete events. This is by design according to the business requirements.

Applied to files:

  • src/services/admin.js
  • src/services/organization.js
📚 Learning: 2025-09-12T11:02:51.008Z
Learnt from: praveenKDass
PR: ELEVATE-Project/user#816
File: src/dtos/organizationDTO.js:14-43
Timestamp: 2025-09-12T11:02:51.008Z
Learning: In the ELEVATE-Project/user codebase, oldValue and newValue in the changedValues array for organizationDTO.eventBodyDTO are guaranteed to not be undefined by the business logic, and filtering out falsy values (0, false, '') from changes is the intended behavior.

Applied to files:

  • src/services/admin.js
  • src/services/organization.js
📚 Learning: 2025-09-12T11:02:51.008Z
Learnt from: praveenKDass
PR: ELEVATE-Project/user#816
File: src/dtos/organizationDTO.js:14-43
Timestamp: 2025-09-12T11:02:51.008Z
Learning: In the ELEVATE-Project/user codebase, the changedValues parameter in organizationDTO.eventBodyDTO is guaranteed to always be an array by the calling code, so additional Array.isArray validation is not needed.

Applied to files:

  • src/services/admin.js
  • src/services/organization.js
📚 Learning: 2025-08-08T13:06:32.911Z
Learnt from: nevil-mathew
PR: ELEVATE-Project/user#790
File: src/helpers/userHelper.js:10-10
Timestamp: 2025-08-08T13:06:32.911Z
Learning: In ELEVATE-Project/user, avoid potential circular dependencies between src/helpers/userHelper.js and src/services/user-sessions by extracting shared session cleanup logic (finding active sessions, deleting Redis keys, ending sessions) into a dedicated utility (e.g., helpers/sessionCleanup.js) that both layers can consume.

Applied to files:

  • src/services/admin.js
📚 Learning: 2025-08-08T15:12:44.423Z
Learnt from: nevil-mathew
PR: ELEVATE-Project/user#790
File: src/helpers/userHelper.js:117-156
Timestamp: 2025-08-08T15:12:44.423Z
Learning: In ELEVATE-Project/user, prefer extracting shared session teardown (find active sessions, delete Redis keys, mark ended_at) into helpers/sessionCleanup.js so both helpers and services use it, preventing upward (helper→service) dependencies and avoiding circular imports.

Applied to files:

  • src/services/admin.js
📚 Learning: 2025-09-12T10:14:22.455Z
Learnt from: praveenKDass
PR: ELEVATE-Project/user#816
File: src/services/organization.js:831-867
Timestamp: 2025-09-12T10:14:22.455Z
Learning: In the ELEVATE-Project/user codebase, the related_orgs field is stored as an array in the database by default, so additional array validation checks are unnecessary when processing related organization data.

Applied to files:

  • src/services/organization.js
📚 Learning: 2025-09-12T10:36:43.529Z
Learnt from: praveenKDass
PR: ELEVATE-Project/user#816
File: src/dtos/organizationDTO.js:26-29
Timestamp: 2025-09-12T10:36:43.529Z
Learning: In the ELEVATE-Project/user codebase, the changedValues array passed to organizationDTO.eventBodyDTO and tenantDTO.eventBodyDTO always contains a fieldName property when oldValue or newValue are present, so additional fieldName validation is not needed according to the business logic.

Applied to files:

  • src/services/organization.js
📚 Learning: 2025-09-12T10:38:57.594Z
Learnt from: praveenKDass
PR: ELEVATE-Project/user#816
File: src/services/organization.js:831-0
Timestamp: 2025-09-12T10:38:57.594Z
Learning: In the ELEVATE-Project/user codebase, when building event payloads in orgEventEmitter function, the preference is to use direct property access without nullish coalescing operators (??) for args object properties. This ensures fail-fast behavior - if expected document properties are missing, the system should throw an error rather than silently falling back to default values, helping to catch data integrity issues early.

Applied to files:

  • src/services/organization.js
🧬 Code graph analysis (5)
src/services/tenant.js (2)
src/helpers/userInvite.js (8)
  • require (18-18)
  • require (23-23)
  • require (27-27)
  • require (28-28)
  • require (29-29)
  • eventBody (910-924)
  • eventBody (1153-1158)
  • utils (3-3)
src/database/queries/organization.js (3)
  • rowsAffected (104-104)
  • rowsAffected (143-143)
  • rowsAffected (164-164)
src/helpers/eventBroadcasterMain.js (5)
src/helpers/userInvite.js (1)
  • kafkaCommunication (17-17)
src/services/account.js (1)
  • kafkaCommunication (20-20)
src/services/organization.js (1)
  • kafkaCommunication (11-11)
src/services/org-admin.js (1)
  • kafkaCommunication (15-15)
src/services/userInvite.js (1)
  • kafkaCommunication (24-24)
src/services/admin.js (2)
src/services/account.js (7)
  • require (26-26)
  • require (27-27)
  • require (37-37)
  • require (41-41)
  • eventBody (604-623)
  • eventBody (1754-1769)
  • common (16-16)
src/services/organization.js (10)
  • require (12-12)
  • require (14-14)
  • require (15-15)
  • require (18-18)
  • require (22-22)
  • organizationDTO (19-19)
  • eventBody (138-146)
  • common (2-2)
  • eventBodyData (151-169)
  • eventBodyData (845-865)
src/services/organization.js (5)
src/services/account.js (6)
  • require (26-26)
  • require (27-27)
  • require (37-37)
  • require (41-41)
  • common (16-16)
  • utils (25-25)
src/services/admin.js (8)
  • require (11-11)
  • require (27-27)
  • require (34-34)
  • require (36-36)
  • require (37-37)
  • organizationDTO (43-43)
  • common (15-15)
  • utils (17-17)
src/database/queries/organization.js (3)
  • require (2-2)
  • require (3-3)
  • common (4-4)
src/services/tenant.js (5)
  • common (10-10)
  • changes (557-563)
  • changes (697-703)
  • changes (879-879)
  • utils (29-29)
src/dtos/eventBody.js (1)
  • changes (11-17)
src/services/account.js (3)
src/helpers/userInvite.js (5)
  • require (18-18)
  • require (23-23)
  • require (27-27)
  • require (28-28)
  • require (29-29)
src/services/admin.js (4)
  • require (11-11)
  • require (27-27)
  • require (34-34)
  • require (36-36)
src/services/user.js (5)
  • require (17-17)
  • require (19-19)
  • require (20-20)
  • require (24-24)
  • require (26-26)
🔇 Additional comments (13)
src/services/account.js (2)

41-41: LGTM! Import updated correctly.

The import change from broadcastUserEvent to broadcastEvent aligns with the API rename in the event broadcaster module, maintaining consistency across the codebase.


625-625: LGTM! Function calls updated consistently.

Both calls to broadcastEvent('userEvents', ...) correctly use the renamed function. The event payloads and parameters remain unchanged, ensuring backward compatibility.

Also applies to: 1771-1771

src/services/admin.js (2)

34-34: LGTM! Imports updated correctly.

The addition of organizationDTO import and the change from broadcastUserEvent to broadcastEvent properly support the enhanced event broadcasting capabilities for both user and organization events.

Also applies to: 43-43


83-83: LGTM! User event broadcasts updated consistently.

Both user event emissions correctly use the renamed broadcastEvent function with the 'userEvents' event group. The event payloads remain properly structured.

Also applies to: 221-221

src/helpers/eventBroadcasterMain.js (4)

14-18: LGTM! Fallback returns prevent fallthrough.

The addition of return [] statements for both userEvents and tenantEvents cases ensures no unintended fallthrough occurs when the environment variables are not set, consistent with the organizationEvents pattern.


30-37: LGTM! Event enablement gates properly configured.

All event groups now have proper enablement checks:

  • tenantEvents case added for API events
  • Entity-specific Kafka flags (EVENT_ENABLE_USER_KAFKA_EVENTS, EVENT_ENABLE_TENANT_KAFKA_EVENTS, EVENT_ENABLE_ORG_KAFKA_EVENTS) correctly gate Kafka publishing for each event type

Based on learnings


68-82: LGTM! Kafka routing properly implemented with await.

The switch statement correctly routes events to the appropriate Kafka push functions:

  • All three entity types (organization, user, tenant) are handled
  • Each Kafka push is properly awaited to prevent silent failures
  • Default case logs unknown event groups appropriately

87-109: LGTM! API rename completed successfully.

The rename from broadcastUserEvent to broadcastEvent accurately reflects the function's broader scope, now handling user, organization, and tenant events uniformly. Error logging has been updated consistently.

src/services/organization.js (3)

150-172: LGTM: Organization create event properly structured and awaited.

The event payload includes all required fields with appropriate fallbacks, and the broadcast is awaited to surface any failures.


253-253: LGTM: Update event emission properly awaited.

Correctly passes before/after state and bodyData to the event emitter, with await to catch failures.


586-589: LGTM: Related organization event emissions properly awaited.

Both addRelatedOrg and removeRelatedOrg correctly await the event emitter to ensure errors surface.

Also applies to: 664-667

src/services/tenant.js (1)

458-475: LGTM: Tenant update event emission properly awaited.

The update flow correctly handles conditional updates and awaits the event emitter to surface failures.

src/envVariables.js (1)

399-460: LGTM: Environment variables follow consistent patterns.

The new event control flags and topic/URL variables follow the established requiredIf validation pattern, ensuring topics are required only when their respective event types are enabled. Based on learnings.

@nevil-mathew nevil-mathew merged commit dd08237 into develop Sep 30, 2025
1 of 2 checks passed
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