Skip to content

RE1-T112 fixes#326

Merged
ucswift merged 1 commit intomasterfrom
develop
Apr 11, 2026
Merged

RE1-T112 fixes#326
ucswift merged 1 commit intomasterfrom
develop

Conversation

@ucswift
Copy link
Copy Markdown
Member

@ucswift ucswift commented Apr 11, 2026

Summary by CodeRabbit

  • Bug Fixes

    • Improved weather alert notification delivery and persistence mechanism.
  • Chores

    • Updated feature access requirements based on subscription plan status.

@request-info
Copy link
Copy Markdown

request-info bot commented Apr 11, 2026

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

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 11, 2026

📝 Walkthrough

Walkthrough

The PR modifies two files: (1) WeatherAlertService.cs refactors the message delivery workflow by separating save and send operations into distinct calls, building messages with per-member recipients and setting SystemMessageId from the persisted message; (2) RequireActivePlanFilter.cs updates controller and action exemption lists for plan enforcement checks.

Changes

Cohort / File(s) Summary
Message Delivery Workflow
Core/Resgrid.Services/WeatherAlertService.cs
Refactored SendPendingNotificationsAsync to separate message persistence and delivery: replaced broadcast recipients assignment with per-member recipient building, split single save-and-send call into separate SaveMessageAsync and SendMessageAsync calls with enqueueDelivery=false, and updated SystemMessageId assignment to use the saved message's ID.
Plan Filter Exemptions
Web/Resgrid.Web/Filters/RequireActivePlanFilter.cs
Updated exemption lists: added "subscription" to always-reachable exempt controllers; replaced prior exempt actions (plan selection, payment, billing callbacks) with narrower set containing only "topiconsarea" and "getsearchresults".

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • RE1-T112 fixes #323 — Both PRs modify RequireActivePlanFilter.cs exemption lists with overlapping changes to controller and action exemptions.
🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% 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 'RE1-T112 fixes' is vague and does not clearly describe what changes are being made; it only references a ticket number without conveying the actual modifications. Replace the ticket reference with a descriptive summary of the main changes, such as 'Refactor weather alert message delivery workflow and update plan filter exemptions' to make the purpose clear to reviewers.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

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

✨ Finishing Touches
📝 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 and usage tips.

Copy link
Copy Markdown
Contributor

@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)
Core/Resgrid.Services/WeatherAlertService.cs (1)

375-389: ⚠️ Potential issue | 🟠 Major

Avoid marking the alert notified when enqueue fails.

This split introduces a partial-success window: SaveMessageAsync can succeed, SendMessageAsync can throw, and the catch only logs. Execution then reaches Lines 387-389 and marks the alert as notified anyway, so it will not be retried even though delivery failed. Move the NotificationSent update into the success path, or skip the post-send update after the catch.

Possible fix
-						try
+						var notificationSent = false;
+						try
 						{
 							var members = await _departmentsService.GetAllMembersForDepartmentAsync(departmentId);
 							if (members != null && members.Any())
 							{
 								// Use department managing user as sender for system messages
@@
 								var savedMessage = await _messageService.SaveMessageAsync(message, ct);
 								await _messageService.SendMessageAsync(savedMessage, "Weather Alert System", departmentId, false, ct);
 
 								alert.SystemMessageId = savedMessage.MessageId;
+								notificationSent = true;
 							}
 						}
 						catch (Exception ex)
 						{
 							Logging.LogException(ex);
+							continue;
 						}
 					}
 
-					alert.NotificationSent = true;
+					alert.NotificationSent = notificationSent;
 					alert.LastUpdatedUtc = DateTime.UtcNow;
 					await _weatherAlertRepository.UpdateAsync(alert, ct, true);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Core/Resgrid.Services/WeatherAlertService.cs` around lines 375 - 389, The
code marks alert.NotificationSent = true and calls
_weatherAlertRepository.UpdateAsync(alert...) regardless of whether
SendMessageAsync succeeded, causing failed deliveries to be treated as sent;
modify the flow in the block that calls _messageService.SaveMessageAsync and
_messageService.SendMessageAsync so that alert.SystemMessageId is set and then,
only after SendMessageAsync completes without exception, set
alert.NotificationSent = true and alert.LastUpdatedUtc = DateTime.UtcNow and
call _weatherAlertRepository.UpdateAsync(alert, ct, true); keep the catch to
LogException(ex) but ensure the UpdateAsync that marks NotificationSent is only
executed on the successful path (or alternatively add a boolean success flag
around SaveMessageAsync/SendMessageAsync and only update when true).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@Core/Resgrid.Services/WeatherAlertService.cs`:
- Around line 369-376: The code currently reuses a single Message instance
(variable message) for all recipients, then calls
_messageService.SaveMessageAsync and _messageService.SendMessageAsync, which
incorrectly shares recipient-scoped state (ReceivingUserId, ReadOn, IsDeleted)
across users; instead, iterate the members and for each member (in the loop
where you call message.AddRecipient(member.UserId)) create a new Message
instance or clone the original, set the recipient-specific fields
(ReceivingUserId etc.) for that user, then call
_messageService.SaveMessageAsync(newMessage, ct) and
_messageService.SendMessageAsync(savedMessage, "Weather Alert System",
departmentId, false, ct) per recipient so each user gets their own Message row
as the previous broadcastSingle:true path did.

In `@Web/Resgrid.Web/Filters/RequireActivePlanFilter.cs`:
- Around line 22-23: The exemption list in RequireActivePlanFilter currently
whitelists the entire "subscription" controller which is too broad and bypasses
plan enforcement; remove "subscription" from the controller-level exemptions in
the ExemptControllers/whitelist array inside RequireActivePlanFilter and instead
add explicit route/action-level checks: keep controller-level "account" if
needed but implement granular exemptions by matching controller+action names
(e.g., only allow specific read-only or webhook actions) in the filter's
OnActionExecuting (or equivalent) logic so only those explicit actions are
skipped while all other SubscriptionController endpoints still enforce an active
plan.

---

Outside diff comments:
In `@Core/Resgrid.Services/WeatherAlertService.cs`:
- Around line 375-389: The code marks alert.NotificationSent = true and calls
_weatherAlertRepository.UpdateAsync(alert...) regardless of whether
SendMessageAsync succeeded, causing failed deliveries to be treated as sent;
modify the flow in the block that calls _messageService.SaveMessageAsync and
_messageService.SendMessageAsync so that alert.SystemMessageId is set and then,
only after SendMessageAsync completes without exception, set
alert.NotificationSent = true and alert.LastUpdatedUtc = DateTime.UtcNow and
call _weatherAlertRepository.UpdateAsync(alert, ct, true); keep the catch to
LogException(ex) but ensure the UpdateAsync that marks NotificationSent is only
executed on the successful path (or alternatively add a boolean success flag
around SaveMessageAsync/SendMessageAsync and only update when true).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

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

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 1364680f-6b30-4927-8932-0c2ff50384a5

📥 Commits

Reviewing files that changed from the base of the PR and between a5e17a2 and 392d2d9.

📒 Files selected for processing (2)
  • Core/Resgrid.Services/WeatherAlertService.cs
  • Web/Resgrid.Web/Filters/RequireActivePlanFilter.cs

Comment on lines +369 to +376
foreach (var member in members)
{
alert.SystemMessageId = message.MessageId;
if (member.UserId != senderId)
message.AddRecipient(member.UserId);
}

var savedMessage = await _messageService.SaveMessageAsync(message, ct);
await _messageService.SendMessageAsync(savedMessage, "Weather Alert System", departmentId, false, ct);
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.

⚠️ Potential issue | 🟠 Major

Don't persist one shared Message for all recipients.

The previous broadcastSingle: true path created one Message row per recipient. This version saves a single row with multiple MessageRecipients, but ReceivingUserId, ReadOn, and IsDeleted still live on Message itself. That changes inbox semantics for weather alerts from recipient-scoped state to shared state.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Core/Resgrid.Services/WeatherAlertService.cs` around lines 369 - 376, The
code currently reuses a single Message instance (variable message) for all
recipients, then calls _messageService.SaveMessageAsync and
_messageService.SendMessageAsync, which incorrectly shares recipient-scoped
state (ReceivingUserId, ReadOn, IsDeleted) across users; instead, iterate the
members and for each member (in the loop where you call
message.AddRecipient(member.UserId)) create a new Message instance or clone the
original, set the recipient-specific fields (ReceivingUserId etc.) for that
user, then call _messageService.SaveMessageAsync(newMessage, ct) and
_messageService.SendMessageAsync(savedMessage, "Weather Alert System",
departmentId, false, ct) per recipient so each user gets their own Message row
as the previous broadcastSingle:true path did.

Comment on lines +22 to +23
"account",
"subscription"
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.

⚠️ Potential issue | 🟠 Major

Controller-level subscription exemption is too broad and bypasses plan enforcement.

On Line 23, exempting the entire Subscription controller skips this filter for all subscription endpoints, including sensitive mutation flows (e.g., cancel/addon management). That weakens the intended “active plan required” gate and can allow no-plan departments into operations that should stay restricted.

Suggested fix (narrow exemption to explicit routes)
-		private static readonly string[] ExemptControllers = new[]
-		{
-			"account",
-			"subscription"
-		};
+		private static readonly string[] ExemptControllers = new[]
+		{
+			"account"
+		};
+
+		private static readonly (string Controller, string Action)[] ExemptRoutes = new[]
+		{
+			("subscription", "selectregistrationplan"),
+			("subscription", "logstriperesponse"),
+			("subscription", "validatecoupon")
+		};
@@
-			// Skip exempt actions (payment flow, plan selection itself)
+			// Skip explicitly exempt controller/action routes
+			foreach (var route in ExemptRoutes)
+			{
+				if (string.Equals(controller, route.Controller, StringComparison.OrdinalIgnoreCase) &&
+					string.Equals(action, route.Action, StringComparison.OrdinalIgnoreCase))
+				{
+					await next();
+					return;
+				}
+			}
+
+			// Skip exempt actions
 			var action = routeData.Values["action"]?.ToString() ?? string.Empty;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Web/Resgrid.Web/Filters/RequireActivePlanFilter.cs` around lines 22 - 23, The
exemption list in RequireActivePlanFilter currently whitelists the entire
"subscription" controller which is too broad and bypasses plan enforcement;
remove "subscription" from the controller-level exemptions in the
ExemptControllers/whitelist array inside RequireActivePlanFilter and instead add
explicit route/action-level checks: keep controller-level "account" if needed
but implement granular exemptions by matching controller+action names (e.g.,
only allow specific read-only or webhook actions) in the filter's
OnActionExecuting (or equivalent) logic so only those explicit actions are
skipped while all other SubscriptionController endpoints still enforce an active
plan.

@ucswift
Copy link
Copy Markdown
Member Author

ucswift commented Apr 11, 2026

Approve

Copy link
Copy Markdown

@github-actions github-actions bot left a comment

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 2699f08 into master Apr 11, 2026
18 of 19 checks passed
@coderabbitai coderabbitai bot mentioned this pull request Apr 11, 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.

1 participant