Skip to content

RE1-T121 Minor bugfixes#411

Merged
ucswift merged 3 commits into
masterfrom
develop
Jun 20, 2026
Merged

RE1-T121 Minor bugfixes#411
ucswift merged 3 commits into
masterfrom
develop

Conversation

@ucswift

@ucswift ucswift commented Jun 20, 2026

Copy link
Copy Markdown
Member

Summary by CodeRabbit

  • Bug Fixes

    • Fixed a crash when viewing subscription pages for departments without an active payment.
    • Hardened billing/plan limit checks to safely deny actions when plan/count data is missing, avoiding null-related failures.
    • Added safe free-plan fallback behavior when department limit data can’t be resolved.
  • Improvements

    • Enhanced diagnostics for inbound SMS handling and legacy text-command processing.
    • Improved text-to-speech reliability by deferring REST client creation until audio generation.

@request-info

request-info Bot commented Jun 20, 2026

Copy link
Copy Markdown

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

@coderabbitai

coderabbitai Bot commented Jun 20, 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

Run ID: 580d6b86-5216-4273-a340-67d9858ced8a

📥 Commits

Reviewing files that changed from the base of the PR and between 223456e and dac5767.

📒 Files selected for processing (1)
  • Core/Resgrid.Services/LimitsService.cs

📝 Walkthrough

Walkthrough

The PR refactors TtsAudioService to defer RestClient resolution by accepting a Func<RestClient> factory instead of an eagerly-constructed instance, updates DI wiring accordingly, adds eight null guards across LimitsService methods to safely handle missing billing/plan data, introduces detailed diagnostic logging throughout the Twilio SMS inbound handler and legacy text-command path, and adds a fallback Payment guard in SubscriptionController.Index to prevent view-layer null dereferences.

Changes

TtsAudioService Lazy RestClient Factory

Layer / File(s) Summary
TtsAudioService Lazy Factory Implementation
Core/Resgrid.Services/TtsAudioService.cs
Constructor replaced to accept Func<RestClient> with null-check; GenerateSpeechUrlAsync and RegenerateStaticPromptsAsync each invoke the factory to obtain a local RestClient before executing TTS requests.
ServicesModule DI Registration
Core/Resgrid.Services/ServicesModule.cs
DI parameter for TtsAudioService changed from an eagerly-resolved named RestClient to a lambda capturing ILifetimeScope that resolves the named RestClient only when invoked.

Defensive Null Handling and Diagnostics

Layer / File(s) Summary
LimitsService Null Guard and Plan Availability Checks
Core/Resgrid.Services/LimitsService.cs
Eight methods add null guards for missing department counts or plan data: ValidateDepartmentIsWithinLimitsAsync, CanDepartmentAddNewUnitAsync, GetEntityLimitForDepartmentAsync (with free-plan fallback), CanDepartmentProvisionNumberAsync (with logging), CanDepartmentUseVoiceAsync, CanDepartmentUseLinksAsync, CanDepartmentCreateOrdersAsync, and GetLimitsForEntityPlanWithFallbackAsync all return false or default free-plan limits instead of dereferencing null objects.
TwilioController SMS Inbound Diagnostics
Web/Resgrid.Web.Services/Controllers/TwilioController.cs
IncomingMessage logs department resolution, chatbot vs. legacy text-command path selection, chatbot processing results, and TwiML response payload size. ProcessTextCommandsAsync logs missing department, authorization result, missing user profile, and resolved text-command type.
SubscriptionController Payment Null Guard
Web/Resgrid.Web/Areas/User/Controllers/SubscriptionController.cs
Index assigns a default fallback Payment object when model.Payment is null, preventing NullReferenceException in the subscription view.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Possibly related PRs

  • Resgrid/Core#362: Directly related null-check change in LimitsService.GetLimitsForEntityPlanWithFallbackAsync aligns with the main PR's broader defensive programming enhancements in the same method.

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 13.33% 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-T121 Minor bugfixes" is vague and generic, using the term "bugfixes" without specifying which bugs or what systems are being fixed across multiple services. Provide a more specific title that describes the primary change, such as "Add null guards to LimitsService and fix null reference handling in billing data" or focus on the most significant fix.
✅ 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.

✏️ 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.

@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.

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/LimitsService.cs (1)

127-140: ⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Critical: Add null-check for plan before accessing plan.PlanId on line 131.

Per coding guidelines, SubscriptionsService.GetCurrentPlanForDepartmentAsync() can return null when the Billing API response is unavailable. Accessing plan.PlanId without a null-check will cause a NullReferenceException.

The added diagnostic log (line 138) is valuable, but it cannot execute if the method crashes on line 131. The null-check must come before the plan.PlanId comparison.

🛡️ Proposed fix: Add null-check before accessing plan.PlanId
 public async Task<bool> CanDepartmentProvisionNumberAsync(int departmentId)
 {
     var plan = await _subscriptionsService.GetCurrentPlanForDepartmentAsync(departmentId);
+
+    if (plan == null)
+    {
+        Resgrid.Framework.Logging.LogInfo($"[Twilio SMS] CanDepartmentProvisionNumber=false for DepartmentId={departmentId} — plan is null (Billing API unavailable), so no SMS reply will be generated.");
+        return false;
+    }

     if (plan.PlanId == 4 || plan.PlanId == 5 || plan.PlanId == 10 || plan.PlanId == 14 || plan.PlanId == 15 || plan.PlanId == 16 || plan.PlanId == 17 ||
           plan.PlanId == 18 || plan.PlanId == 19 || plan.PlanId == 20 || plan.PlanId == 21 || plan.PlanId == 26 || plan.PlanId == 27 || plan.PlanId == 28 ||
             plan.PlanId == 29 || plan.PlanId == 30 || plan.PlanId == 31 || plan.PlanId == 32 || plan.PlanId == 33 || plan.PlanId == 36 || plan.PlanId == 37)
         return true;

-    // Diagnostic: this gate silently blocks the inbound-SMS text-command reply path. Log the
-    // resolved plan so it is clear *why* a department gets no reply (plan not in the allow-list).
-    Resgrid.Framework.Logging.LogInfo($"[Twilio SMS] CanDepartmentProvisionNumber=false for DepartmentId={departmentId} PlanId={plan.PlanId} — plan is not in the number/text-feature allow-list, so no SMS reply will be generated.");
+    // Diagnostic: this gate silently blocks the inbound-SMS text-command reply path. Log the
+    // resolved plan so it is clear *why* a department gets no reply (plan not in the allow-list).
+    Resgrid.Framework.Logging.LogInfo($"[Twilio SMS] CanDepartmentProvisionNumber=false for DepartmentId={departmentId} PlanId={plan.PlanId} — plan is not in the number/text-feature allow-list, so no SMS reply will be generated.");
     return false;
 }
🤖 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/LimitsService.cs` around lines 127 - 140, The
CanDepartmentProvisionNumberAsync method accesses plan.PlanId without first
checking if the plan object returned from GetCurrentPlanForDepartmentAsync is
null, which will cause a NullReferenceException since the subscription service
can return null when the Billing API is unavailable. Add a null-check
immediately after the await call to GetCurrentPlanForDepartmentAsync and return
false if the plan is null, before attempting to access any properties on the
plan object.

Source: Coding guidelines

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

Outside diff comments:
In `@Core/Resgrid.Services/LimitsService.cs`:
- Around line 127-140: The CanDepartmentProvisionNumberAsync method accesses
plan.PlanId without first checking if the plan object returned from
GetCurrentPlanForDepartmentAsync is null, which will cause a
NullReferenceException since the subscription service can return null when the
Billing API is unavailable. Add a null-check immediately after the await call to
GetCurrentPlanForDepartmentAsync and return false if the plan is null, before
attempting to access any properties on the plan object.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: f32bc24c-eeab-4908-ad3b-89f22bf610b9

📥 Commits

Reviewing files that changed from the base of the PR and between 2baed90 and 45a5dfc.

⛔ Files ignored due to path filters (1)
  • Tests/Resgrid.Tests/Web/Services/TtsAudioServiceTests.cs is excluded by !**/Tests/**
📒 Files selected for processing (5)
  • Core/Resgrid.Services/LimitsService.cs
  • Core/Resgrid.Services/ServicesModule.cs
  • Core/Resgrid.Services/TtsAudioService.cs
  • Web/Resgrid.Web.Services/Controllers/TwilioController.cs
  • Web/Resgrid.Web/Areas/User/Controllers/SubscriptionController.cs

@ucswift

ucswift commented Jun 20, 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 aacc1c4 into master Jun 20, 2026
18 of 19 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.

1 participant