Skip to content

Develop - #460

Merged
ucswift merged 3 commits into
masterfrom
develop
Aug 12, 2026
Merged

Develop#460
ucswift merged 3 commits into
masterfrom
develop

Conversation

@ucswift

@ucswift ucswift commented Aug 12, 2026

Copy link
Copy Markdown
Member

Pull Request Description

This PR introduces several improvements to the chat system, department group performance, and error handling:

Chat Enhancements

  • Active-channel push suppression: Replaces broad "online = no push" logic with precise tracking of which channel a user is actively viewing. Push notifications are now only suppressed for users (and acting units) currently viewing that specific conversation, ensuring background channels still alert online users.
  • Unit-participant channel visibility: Unit operators can now see and interact with channels where their unit is a participant (e.g., Dispatch↔Unit DMs, unit-invited groups) without needing a personal member row.
  • DM channel name resolution: DM channels without stored names are dynamically labeled with the counterpart participant's display name so multiple DMs are distinguishable.
  • Auto-naming for group channels: When creating an ad-hoc group without a name, the server auto-generates one from the invited members' names (Slack-style).
  • Unit sender attribution: Messages sent as a unit now include the operator's name (e.g., "Engine 6 (Alice Smith)") so dispatchers know who is operating the rig.
  • Immediate channel visibility: Cache invalidation on channel provisioning ensures both participants see new DM/group channels without waiting for the per-user list cache to expire.

Department Groups Performance & Correctness

  • Eliminated N+1 query patterns in GetAllGroupsForDepartmentUnlimitedAsync by resolving parent/child relationships and addresses in-memory from a single query.
  • Fixed group member queries in both PostgreSQL and SQL Server to filter by DepartmentId, preventing cross-department data leakage when the same user belongs to multiple departments.

Error Handling

  • Filtered BadHttpRequestException (client-aborted requests) from Sentry to reduce noise.
  • Filtered Safari extension-originated script errors (webkit-masked-url://) from both client and server Sentry reporting.
  • Added null-safety guards in the recipients grid to gracefully degrade on transient database failures.

Summary by CodeRabbit

  • New Features

    • Chat now tracks the channel and optional acting unit you’re actively viewing.
    • Ad-hoc group names are optional and can be generated automatically from members.
    • Newly created conversations appear immediately in the chat list.
    • Unit-based channel access and membership handling has been expanded.
  • Improvements

    • Notifications are suppressed when users or units are already active in the channel.
    • Direct-message and unit display names are more informative.
  • Bug Fixes

    • Improved channel access validation, group filtering, and resilience when data is unavailable.
    • Reduced monitoring noise from expected client connection errors.

@request-info

request-info Bot commented Aug 12, 2026

Copy link
Copy Markdown

Thanks for opening this, but we'd appreciate a little more information. Could you update it with more details?

@Resgrid-Bot

This comment has been minimized.

@coderabbitai

coderabbitai Bot commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 0a383d5d-9c1d-4ed7-aac7-0d8bd322eec7

📥 Commits

Reviewing files that changed from the base of the PR and between 280fb22 and 7b2ae6a.

⛔ Files ignored due to path filters (3)
  • Tests/Resgrid.Tests/Services/ChatChannelServiceTests.cs is excluded by !**/Tests/**
  • Tests/Resgrid.Tests/Services/ChatPermissionServiceTests.cs is excluded by !**/Tests/**
  • Tests/Resgrid.Tests/Services/ChatPresenceServiceTests.cs is excluded by !**/Tests/**
📒 Files selected for processing (5)
  • Core/Resgrid.Services/ChatChannelService.cs
  • Core/Resgrid.Services/ChatPermissionService.cs
  • Core/Resgrid.Services/ChatPresenceService.cs
  • Web/Resgrid.Web.Services/Controllers/v4/ChatController.cs
  • Web/Resgrid.Web/Areas/User/Apps/src/components/chat/chatHub.ts
🚧 Files skipped from review as they are similar to previous changes (4)
  • Web/Resgrid.Web/Areas/User/Apps/src/components/chat/chatHub.ts
  • Web/Resgrid.Web.Services/Controllers/v4/ChatController.cs
  • Core/Resgrid.Services/ChatChannelService.cs
  • Core/Resgrid.Services/ChatPresenceService.cs

📝 Walkthrough

Walkthrough

The PR adds active-channel presence tracking with unit context, integrates unit memberships into channel retrieval, scopes notification suppression to channels, supports server-generated group names, and updates related group, recipient, and Sentry handling.

Changes

Chat activity and channel behavior

Layer / File(s) Summary
Presence contracts and persistence
Core/Resgrid.Model/Repositories/IChatRepositories.cs, Core/Resgrid.Model/Services/IChatServices.cs, Core/Resgrid.Services/ChatPresenceService.cs, Repositories/.../ChatRepositories.cs
Adds active membership queries and cache-backed user and unit channel activity tracking.
Unit-aware channel and notification flow
Core/Resgrid.Services/ChatChannelService.cs, Core/Resgrid.Services/ChatPermissionService.cs, Core/Resgrid.Services/ChatNotificationService.cs, Web/Resgrid.Web.Services/Controllers/v4/ChatController.cs
Merges validated unit memberships into channel results, invalidates channel caches after creation, and suppresses notifications for users or units active in the channel.
Client activity synchronization
Web/Resgrid.Web.Eventing/Hubs/ChatHub.cs, Web/Resgrid.Web/Areas/User/Apps/src/components/chat/*
Reports active channels after connection and reconnection, clears inactive state, validates hub updates, and upserts new channels before opening them.
Channel naming and display resolution
Web/Resgrid.Web.Services/Controllers/v4/ChatController.cs, Web/Resgrid.Web.Services/Models/v4/Chat/ChatApiModels.cs, Web/Resgrid.Web/Areas/User/Apps/src/components/chat/NewConversationDialog.tsx, Core/Resgrid.Services/ChatMessageService.cs, Web/Resgrid.Web.Services/Resgrid.Web.Services.xml
Allows blank group names, generates names from member profiles, resolves direct-message names, and adds profile names to unit sender labels.
Related service and error handling corrections
Core/Resgrid.Services/DepartmentGroupsService.cs, Repositories/.../PostgreSqlConfiguration.cs, Repositories/.../SqlServerConfiguration.cs, Web/Resgrid.Web.Services/Program.cs, Web/Resgrid.Web/Areas/User/Controllers/DepartmentController.cs, Web/Resgrid.Web/Areas/User/Views/Shared/_UserLayout.cshtml
Updates group relationship loading, department filtering, null handling, and Sentry filters.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant ChatPanelElement
  participant chatHub
  participant ChatHub
  participant ChatPresenceService
  participant ChatNotificationService
  ChatPanelElement->>chatHub: report viewed channel
  chatHub->>ChatHub: SetActiveChannel(channelId)
  ChatHub->>ChatPresenceService: store active channel
  ChatNotificationService->>ChatPresenceService: query channel activity
  ChatPresenceService-->>ChatNotificationService: active users and units
Loading

Possibly related PRs

  • Resgrid/Core#450: Both PRs modify ChatChannelService.GetChannelsForUserAsync channel selection logic.

Suggested reviewers: github-actions

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 34.43% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title “Develop” is too vague to identify the chat, presence, group, and error-handling changes in this pull request. Replace “Develop” with a concise title that states the primary change, such as “Improve chat presence, channel visibility, and group naming”.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch develop

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

❤️ Share

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (1)
Core/Resgrid.Services/ChatPresenceService.cs (1)

39-48: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚖️ Poor tradeoff

Use the required cache-aside API.

These new cache reads use GetStringAsync, SetStringAsync, and RemoveAsync directly. Route cache retrieval through ICacheProvider.RetrieveAsync<T>() with a local fallback, or document and approve a presence-state exception to this rule.

As per coding guidelines, “All caching operations must go through ICacheProvider.Retrieve<T>() or ICacheProvider.RetrieveAsync<T>() using the cache-aside pattern with fallback functions.”

Also applies to: 92-162

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

In `@Core/Resgrid.Services/ChatPresenceService.cs` around lines 39 - 48, The
heartbeat logic in ChatPresenceService must use the required cache-aside API
instead of direct GetStringAsync, SetStringAsync, or RemoveAsync calls. Update
the active-channel and unit-marker handling across the affected methods to
retrieve state through ICacheProvider.RetrieveAsync<T>() with local fallback
functions, while preserving the existing TTL and presence behavior; do not add a
presence exception unless explicitly approved.

Source: Coding guidelines

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

Inline comments:
In `@Core/Resgrid.Services/ChatChannelService.cs`:
- Around line 159-176: Validate activeUnitId with CanSendAsUnitAsync(UserId,
activeUnitId.Value, departmentId) before querying or returning unit memberships
in ChatChannelService; skip or reject unauthorized IDs. In ChatController, carry
only the validated active unit through channel/message reads,
CanAccessChannelAsync checks, and member-state operations so unit-only channels
remain accessible only to authorized crew members. Apply the controller change
at Web/Resgrid.Web.Services/Controllers/v4/ChatController.cs lines 125-145 and
the service authorization change at Core/Resgrid.Services/ChatChannelService.cs
lines 159-176.

In `@Core/Resgrid.Services/ChatPresenceService.cs`:
- Around line 39-48: Update TouchAsync and the related active-channel set/remove
paths to store unit activity per user or connection rather than in the shared
GetUnitActiveKey marker. Use atomic owner-aware updates and deletes so one
viewer cannot overwrite, remove, or restore another viewer’s activity, and
derive unit push suppression only after resolving the current owners.

In `@Web/Resgrid.Web/Areas/User/Apps/src/components/chat/chatHub.ts`:
- Around line 371-387: Update setActiveChannel and reportActiveChannel to accept
and retain the acting unit ID alongside the channel ID, then pass that stored
unit ID as the asUnitId argument to CHAT_HUB_METHODS.SetActiveChannel instead of
null. Preserve the existing reconnect reporting behavior and allow the unit ID
to be cleared when no unit is active.

---

Nitpick comments:
In `@Core/Resgrid.Services/ChatPresenceService.cs`:
- Around line 39-48: The heartbeat logic in ChatPresenceService must use the
required cache-aside API instead of direct GetStringAsync, SetStringAsync, or
RemoveAsync calls. Update the active-channel and unit-marker handling across the
affected methods to retrieve state through ICacheProvider.RetrieveAsync<T>()
with local fallback functions, while preserving the existing TTL and presence
behavior; do not add a presence exception unless explicitly approved.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

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

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 4707efa0-c94c-40d0-88b4-098a25b441fd

📥 Commits

Reviewing files that changed from the base of the PR and between 41affdc and 280fb22.

⛔ Files ignored due to path filters (2)
  • Tests/Resgrid.Tests/Services/ChatChannelServiceTests.cs is excluded by !**/Tests/**
  • Tests/Resgrid.Tests/Services/ChatPresenceServiceTests.cs is excluded by !**/Tests/**
📒 Files selected for processing (22)
  • Core/Resgrid.Model/Repositories/IChatRepositories.cs
  • Core/Resgrid.Model/Services/IChatServices.cs
  • Core/Resgrid.Services/ChatChannelService.cs
  • Core/Resgrid.Services/ChatMessageService.cs
  • Core/Resgrid.Services/ChatNotificationService.cs
  • Core/Resgrid.Services/ChatPresenceService.cs
  • Core/Resgrid.Services/DepartmentGroupsService.cs
  • Repositories/Resgrid.Repositories.DataRepository/ChatRepositories.cs
  • Repositories/Resgrid.Repositories.DataRepository/Servers/PostgreSql/PostgreSqlConfiguration.cs
  • Repositories/Resgrid.Repositories.DataRepository/Servers/SqlServer/SqlServerConfiguration.cs
  • Web/Resgrid.Web.Eventing/Hubs/ChatHub.cs
  • Web/Resgrid.Web.Services/Controllers/v4/ChatController.cs
  • Web/Resgrid.Web.Services/Models/v4/Chat/ChatApiModels.cs
  • Web/Resgrid.Web.Services/Program.cs
  • Web/Resgrid.Web.Services/Resgrid.Web.Services.xml
  • Web/Resgrid.Web/Areas/User/Apps/src/components/chat/ChatPageElement.tsx
  • Web/Resgrid.Web/Areas/User/Apps/src/components/chat/ChatPanelElement.tsx
  • Web/Resgrid.Web/Areas/User/Apps/src/components/chat/NewConversationDialog.tsx
  • Web/Resgrid.Web/Areas/User/Apps/src/components/chat/chatHub.ts
  • Web/Resgrid.Web/Areas/User/Apps/src/components/chat/types.ts
  • Web/Resgrid.Web/Areas/User/Controllers/DepartmentController.cs
  • Web/Resgrid.Web/Areas/User/Views/Shared/_UserLayout.cshtml

Comment thread Core/Resgrid.Services/ChatChannelService.cs
Comment thread Core/Resgrid.Services/ChatPresenceService.cs Outdated
Comment thread Web/Resgrid.Web/Areas/User/Apps/src/components/chat/chatHub.ts
// invited to groups) — the unit's operator must see them without a personal member row.
if (activeUnitId.HasValue)
{
var unitMemberships = await _chatChannelMemberRepository.GetActiveByUnitIdAsync(departmentId, activeUnitId.Value);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

kody code-review Kody Rules high

Unhandled repository call: GetActiveByUnitIdAsync can throw transient or permanent errors that should be caught, augmented with context (departmentId, activeUnitId), and mapped to application-level errors. Wrap the call in try/catch, distinguish transient vs non-transient errors, log the operation name and identifiers, and apply retry or fallback as appropriate.

Kody rule violation: Add try-catch blocks for external calls

Prompt for LLM

File Core/Resgrid.Services/ChatChannelService.cs:

Line 163:

Unhandled repository call: GetActiveByUnitIdAsync can throw transient or permanent errors that should be caught, augmented with context (departmentId, activeUnitId), and mapped to application-level errors. Wrap the call in try/catch, distinguish transient vs non-transient errors, log the operation name and identifiers, and apply retry or fallback as appropriate.

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.

Comment on lines +124 to +125
if (await _chatPresenceService.IsUnitActiveInChannelAsync(channel.DepartmentId, unitMember.UnitId.Value, channel.ChatChannelId))
continue;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

kody code-review Kody Rules high

N+1 query pattern in the notification loop: the per-unit await of IsUnitActiveInChannelAsync makes one round-trip per unit member, breaking the batch pattern established by the activeUsers HashSet (lines 78–80) and degrading latency as audience size grows. Fetch all active unit IDs for the channel once into a HashSet (via GetUnitsActiveInChannelAsync) alongside the activeUsers fetch, then replace the await with a local set lookup (activeUnits.Contains(unitMember.UnitId.Value)).

Kody rule violation: Detect N+1 style queries and suggest batching

Prompt for LLM

File Core/Resgrid.Services/ChatNotificationService.cs:

Line 124 to 125:

N+1 query pattern in the notification loop: the per-unit await of IsUnitActiveInChannelAsync makes one round-trip per unit member, breaking the batch pattern established by the activeUsers HashSet (lines 78–80) and degrading latency as audience size grows. Fetch all active unit IDs for the channel once into a HashSet (via GetUnitsActiveInChannelAsync) alongside the activeUsers fetch, then replace the await with a local set lookup (activeUnits.Contains(unitMember.UnitId.Value)).

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.

Comment on lines +46 to +48
var unitId = ParseUnitId(active);
if (unitId.HasValue)
await _cacheProvider.SetStringAsync(GetUnitActiveKey(departmentId, unitId.Value), ParseChannelId(active), GetTtl());

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

kody code-review Bug high

Race condition in TouchAsync (line 48): the shared chatactiveunit:{dept}:{unitId} key is overwritten by each operator's heartbeat, causing the marker to oscillate between channels when multiple operators of the same unit view different channels simultaneously — during the mismatch window IsUnitActiveInChannelAsync returns false and triggers duplicate pushes to the unit device. Key the unit active marker per (unitId, userId) pair instead of per unitId alone, or have GetUsersActiveInChannelAsync check whether any operator of the unit is active in the channel.

Prompt for LLM

File Core/Resgrid.Services/ChatPresenceService.cs:

Line 46 to 48:

Race condition in TouchAsync (line 48): the shared chatactiveunit:{dept}:{unitId} key is overwritten by each operator's heartbeat, causing the marker to oscillate between channels when multiple operators of the same unit view different channels simultaneously — during the mismatch window IsUnitActiveInChannelAsync returns false and triggers duplicate pushes to the unit device. Key the unit active marker per (unitId, userId) pair instead of per unitId alone, or have GetUsersActiveInChannelAsync check whether any operator of the unit is active in the channel.

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.

}
catch (Exception ex)
{
Logging.LogException(ex);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

kody code-review Kody Rules high

Insufficient error context: Logging.LogException(ex) omits the operation name and identifiers (departmentId, unitId) required for tracing failures per Rule [3]. Pass structured context, e.g., Logging.LogException(ex, new { op = nameof(GetActiveByUnitIdAsync), departmentId, unitId }).

Kody rule violation: Include error context in structured logs

Prompt for LLM

File Repositories/Resgrid.Repositories.DataRepository/ChatRepositories.cs:

Line 898:

Insufficient error context: Logging.LogException(ex) omits the operation name and identifiers (departmentId, unitId) required for tracing failures per Rule [3]. Pass structured context, e.g., Logging.LogException(ex, new { op = nameof(GetActiveByUnitIdAsync), departmentId, unitId }).

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.


if (string.IsNullOrWhiteSpace(channelId))
{
await _chatPresenceService.ClearActiveChannelAsync(departmentId, userId);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

kody code-review Kody Rules high

Unguarded async operation: the awaited ClearActiveChannelAsync will propagate an opaque error to the SignalR client if it throws. Wrap the call in try/catch, log the error with context (departmentId, userId), and either rethrow or return an appropriate error.

Kody rule violation: Handle async operations with proper error handling

Prompt for LLM

File Web/Resgrid.Web.Eventing/Hubs/ChatHub.cs:

Line 297:

Unguarded async operation: the awaited ClearActiveChannelAsync will propagate an opaque error to the SignalR client if it throws. Wrap the call in try/catch, log the error with context (departmentId, userId), and either rethrow or return an appropriate error.

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.

Comment on lines +1877 to +1881
var userIds = counterparts.Values
.Where(m => String.IsNullOrWhiteSpace(m.DisplayNameOverride) && !String.IsNullOrWhiteSpace(m.UserId))
.Select(m => m.UserId)
.Distinct()
.ToList();

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

kody code-review Kody Rules high

Long LINQ chain (Where → Select → Distinct → ToList) in a single expression reduces readability and obscures intermediate results. Break into named intermediate steps — for example, extract counterparts.Values.Where(...) into a membersNeedingLookup variable before projecting and deduplicating.

Kody rule violation: Limit Lengthy LINQ Chains

Prompt for LLM

File Web/Resgrid.Web.Services/Controllers/v4/ChatController.cs:

Line 1877 to 1881:

Long LINQ chain (Where → Select → Distinct → ToList) in a single expression reduces readability and obscures intermediate results. Break into named intermediate steps — for example, extract counterparts.Values.Where(...) into a membersNeedingLookup variable before projecting and deduplicating.

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.

Comment on lines 184 to 186
result.Data = ConvertChannelResultData(channel, member);
await ResolveDirectMessageNamesAsync(new List<ChatChannelResultData> { result.Data });
result.PageSize = 1;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

kody code-review Bug medium

ResolveDirectMessageNamesAsync is called from GetChannel (line 185) and CreateDirectMessage (line 227) without activeUnitId, so the unit's own member row (UserId=null, UnitId=7) bypasses both the UserId check (line 1867) and the activeUnitId check (line 1870, which defaults to null), potentially selecting the unit's DisplayNameOverride (e.g. "Engine 6") as the DM counterpart name instead of the dispatcher's. Thread the activeUnitId parameter through to ResolveDirectMessageNamesAsync so the unit's own row is excluded from counterpart selection.

// Pass the viewer's acting unit so the unit's own row is never selected as the DM counterpart.
result.Data = ConvertChannelResultData(channel, member);
				await ResolveDirectMessageNamesAsync(new List<ChatChannelResultData> { result.Data }, activeUnitId);
Prompt for LLM

File Web/Resgrid.Web.Services/Controllers/v4/ChatController.cs:

Line 184 to 186:

ResolveDirectMessageNamesAsync is called from GetChannel (line 185) and CreateDirectMessage (line 227) without activeUnitId, so the unit's own member row (UserId=null, UnitId=7) bypasses both the UserId check (line 1867) and the activeUnitId check (line 1870, which defaults to null), potentially selecting the unit's DisplayNameOverride (e.g. "Engine 6") as the DM counterpart name instead of the dispatcher's. Thread the activeUnitId parameter through to ResolveDirectMessageNamesAsync so the unit's own row is excluded from counterpart selection.

Suggested Code:

// Pass the viewer's acting unit so the unit's own row is never selected as the DM counterpart.
result.Data = ConvertChannelResultData(channel, member);
				await ResolveDirectMessageNamesAsync(new List<ChatChannelResultData> { result.Data }, activeUnitId);

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.

if (this.connection && this.connection.state === HubConnectionState.Connected) {
this.connection
.invoke(CHAT_HUB_METHODS.SetActiveChannel, this.activeChannelReported, null)
.catch(() => undefined);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

kody code-review Kody Rules high

Silent error swallowing: the .catch(() => undefined) on the SignalR invoke promise discards all exceptions, making failed SetActiveChannel calls invisible during debugging. Replace with a catch that logs structured context including the operation name and channelId (e.g., logger.error('Failed to report active channel', { channelId: this.activeChannelReported, err })).

Kody rule violation: Avoid empty catch blocks

Prompt for LLM

File Web/Resgrid.Web/Areas/User/Apps/src/components/chat/chatHub.ts:

Line 385:

Silent error swallowing: the .catch(() => undefined) on the SignalR invoke promise discards all exceptions, making failed SetActiveChannel calls invisible during debugging. Replace with a catch that logs structured context including the operation name and channelId (e.g., logger.error('Failed to report active channel', { channelId: this.activeChannelReported, err })).

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.

@Resgrid-Bot

Resgrid-Bot commented Aug 12, 2026

Copy link
Copy Markdown

Code Review Completed! 🔥

The code review was successfully completed based on your current configurations.

Kody Guide: Usage and Configuration
Interacting with Kody
  • Request a Review: Ask Kody to review your PR manually by adding a comment with the @kody start-review command at the root of your PR.

  • Validate Business Logic: Ask Kody to validate your code against business rules by adding a comment with the @kody -v business-logic command.

  • Provide Feedback: Help Kody learn and improve by reacting to its comments with a 👍 for helpful suggestions or a 👎 if improvements are needed.

Current Kody Configuration
Review Options

The following review options are enabled or disabled:

Options Enabled
Bug
Performance
Security
Business Logic

Access your configuration settings here.

// invited to groups) — the unit's operator must see them without a personal member row.
// The caller-supplied unit only counts when the user actually crews it; otherwise any
// department member could list another unit's private channels.
if (activeUnitId.HasValue && await _chatPermissionService.CanSendAsUnitAsync(userId, activeUnitId.Value, departmentId))

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

kody code-review Kody Rules high

Unhandled exception in await _chatPermissionService.CanSendAsUnitAsync(...) propagates raw when the permission service throws, causing the channel list fetch to fail without context or safe fallback (also at Web/Resgrid.Web.Services/Controllers/v4/ChatController.cs:110). Wrap the call in try/catch, log structured context (userId, activeUnitId, departmentId), and default to denying access on failure.

Kody rule violation: Handle async operations with proper error handling

Prompt for LLM

File Core/Resgrid.Services/ChatChannelService.cs:

Line 163:

Unhandled exception in `await _chatPermissionService.CanSendAsUnitAsync(...)` propagates raw when the permission service throws, causing the channel list fetch to fail without context or safe fallback (also at `Web/Resgrid.Web.Services/Controllers/v4/ChatController.cs:110`). Wrap the call in try/catch, log structured context (`userId`, `activeUnitId`, `departmentId`), and default to denying access on failure.

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.

if (unitId <= 0 || string.IsNullOrWhiteSpace(channelId))
return false;

var marker = await _cacheProvider.GetStringAsync(GetUnitActiveKey(departmentId, unitId));

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

kody code-review Kody Rules high

Unwrapped cache read in await _cacheProvider.GetStringAsync(GetUnitActiveKey(departmentId, unitId)) violates Rule [27] — a cache failure throws raw and loses operation context (also at Core/Resgrid.Services/ChatPresenceService.cs:169, 185, 190, 196, 202). Wrap the call in try/catch, log {op:'IsUnitActiveInChannelAsync', departmentId, unitId, channelId}, and return false on failure.

Kody rule violation: Add try-catch blocks for external calls

Prompt for LLM

File Core/Resgrid.Services/ChatPresenceService.cs:

Line 161:

Unwrapped cache read in `await _cacheProvider.GetStringAsync(GetUnitActiveKey(departmentId, unitId))` violates Rule [27] — a cache failure throws raw and loses operation context (also at `Core/Resgrid.Services/ChatPresenceService.cs:169, 185, 190, 196, 202`). Wrap the call in try/catch, log `{op:'IsUnitActiveInChannelAsync', departmentId, unitId, channelId}`, and return `false` on failure.

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.

@ucswift

ucswift commented Aug 12, 2026

Copy link
Copy Markdown
Member Author

Approve

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This PR is approved.

@ucswift
ucswift merged commit 5c8fd36 into master Aug 12, 2026
18 of 19 checks passed
@coderabbitai coderabbitai Bot mentioned this pull request Aug 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants