Skip to content

RE1-T117 Fixing SMS disclosure logic#410

Merged
ucswift merged 2 commits into
masterfrom
develop
Jun 18, 2026
Merged

RE1-T117 Fixing SMS disclosure logic#410
ucswift merged 2 commits into
masterfrom
develop

Conversation

@ucswift

@ucswift ucswift commented Jun 18, 2026

Copy link
Copy Markdown
Member

Summary by CodeRabbit

  • New Features
    • SMS messages now include STOP/HELP opt-out footer text when required for compliance.
    • Opt-out disclosure is shown on a recurring schedule (every ~30 days) using tracking to limit repetition.
    • A brand marker is appended to outbound text messages.
  • Bug Fixes
    • Ensures the updated opt-out footer and formatting are applied consistently across all SMS sending and related alert/call message flows.

@request-info

request-info Bot commented Jun 18, 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 18, 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: 59966374-621a-4d3e-a12d-ba889881b3be

📥 Commits

Reviewing files that changed from the base of the PR and between 4cd45c7 and 8ee6a27.

📒 Files selected for processing (1)
  • Core/Resgrid.Services/SmsService.cs
🚧 Files skipped from review as they are similar to previous changes (1)
  • Core/Resgrid.Services/SmsService.cs

📝 Walkthrough

Walkthrough

SmsService adds ICacheProvider as a constructor dependency and introduces a ShouldDiscloseOptOut helper that tracks per-user opt-out footer disclosure on a 30-day rolling cache cadence. All six outbound SMS send methods (SendMessageAsync, SendCallAsync, SendCancelCallAsync, SendTroubleAlert, SendTextAsync, SendNotificationAsync) are updated to pass the resulting flag to new FormatTextForMessage and FormatNotificationForMessage overloads that conditionally append a STOP/HELP footer.

Changes

SMS Opt-Out Compliance

Layer / File(s) Summary
Compliance infrastructure: DI, constants, and helpers
Core/Resgrid.Services/SmsService.cs
Constructor gains ICacheProvider cacheProvider parameter and stores it as _cacheProvider. New constants define the STOP/HELP footer text, (Resgrid) brand marker, 30-day disclosure interval, and cache key/marker. FormatTextForMessage gains an includeOptOut parameter; FormatNotificationForMessage, AppendOptOutFooter, and ShouldDiscloseOptOut are added. ShouldDiscloseOptOut calls _cacheProvider.Retrieve, stores a marker on first disclosure, and defaults to true on cache-disabled, invalid userId, or exception.
Send-method call-site updates
Core/Resgrid.Services/SmsService.cs
SendMessageAsync, SendCallAsync (gateway and direct-send paths), SendCancelCallAsync, SendTroubleAlert, SendTextAsync, and SendNotificationAsync each replace the previous two-parameter FormatTextForMessage or raw-message send with the new includeOptOut overloads, passing ShouldDiscloseOptOut(profile.UserId) at each call site.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Suggested reviewers

  • github-actions
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 16.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: adding SMS opt-out/compliance footer logic with a 30-day disclosure cadence to the SmsService class.
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.

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

172-174: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Same double-processing issue in SendCallAsync.

The text is already HTML-converted and stripped at lines 172-174 (plus address/protocols appended), then FormatTextForMessage will process it again. Since additional content is appended here, consider updating FormatTextForMessage to accept already-processed text instead.

Also applies to: 203-203

🤖 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/SmsService.cs` around lines 172 - 174, In the
SendCallAsync method, the text processing from call.NatureOfCall is being done
twice: first at the HtmlToTextHelper.ConvertHtml and
StringHelpers.StripHtmlTagsCharArray calls where the address is also appended,
and then again when the result is passed to FormatTextForMessage. Since the text
has already been converted from HTML and stripped before being sent to
FormatTextForMessage, either update FormatTextForMessage to have a parameter or
overload that accepts already-processed text and skips the HTML conversion and
stripping steps, or refactor the code flow to avoid calling both the initial
HTML processing steps and the processing inside FormatTextForMessage on the same
text content.
🧹 Nitpick comments (2)
Core/Resgrid.Services/SmsService.cs (2)

22-32: 💤 Low value

Constructor injection should use Service Locator pattern.

The coding guidelines specify using Bootstrapper.GetKernel().Resolve<T>() in constructors rather than constructor injection. Consider resolving ICacheProvider explicitly.

♻️ Suggested refactor to align with Service Locator pattern
-		public SmsService(IUserProfileService userProfileService, IGeoLocationProvider geoLocationProvider,
-			ITextMessageProvider textMessageProvider, IDepartmentSettingsService departmentSettingsService,
-			IEmailSender emailSender, ISubscriptionsService subscriptionsService, ICacheProvider cacheProvider)
+		public SmsService(IUserProfileService userProfileService, IGeoLocationProvider geoLocationProvider,
+			ITextMessageProvider textMessageProvider, IDepartmentSettingsService departmentSettingsService,
+			IEmailSender emailSender, ISubscriptionsService subscriptionsService)
 		{
 			_userProfileService = userProfileService;
 			_geoLocationProvider = geoLocationProvider;
 			_textMessageProvider = textMessageProvider;
 			_departmentSettingsService = departmentSettingsService;
 			_emailSender = emailSender;
 			_subscriptionsService = subscriptionsService;
-			_cacheProvider = cacheProvider;
+			_cacheProvider = Bootstrapper.GetKernel().Resolve<ICacheProvider>();
 		}
🤖 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/SmsService.cs` around lines 22 - 32, The SmsService
constructor is using constructor injection for dependencies, but the coding
guidelines require using the Service Locator pattern instead. Remove the
ICacheProvider parameter from the SmsService constructor signature and the
corresponding assignment statement for _cacheProvider. Instead, resolve the
ICacheProvider explicitly within the constructor body using
Bootstrapper.GetKernel().Resolve<ICacheProvider>() and assign it to the
_cacheProvider field to align with the Service Locator pattern guidelines.

Source: Coding guidelines


501-522: ⚡ Quick win

Consider a separate overload for pre-processed text.

FormatTextForMessage always runs HTML conversion, but many call sites already process the text before calling. Consider either:

  1. Adding an overload that skips HTML processing for pre-processed inputs, or
  2. Renaming this method to clarify it expects raw HTML input

Current implementation works but has the redundant processing issue noted earlier.

♻️ Suggested: Add overload for pre-processed text
// For raw HTML input - processes and formats
private string FormatTextForMessage(string title, string body, bool includeOptOut)
{
    string text = HtmlToTextHelper.ConvertHtml(body);
    text = StringHelpers.StripHtmlTagsCharArray(text);
    return FormatTextForMessageInternal(title, text, includeOptOut);
}

// For already-processed text - just formats
private string FormatTextForMessageInternal(string title, string processedBody, bool includeOptOut)
{
    return AppendOptOutFooter(String.Format("{0} {1} : {2}", title, SmsViaResgrid, processedBody), includeOptOut);
}
🤖 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/SmsService.cs` around lines 501 - 522, The
FormatTextForMessage method performs redundant HTML processing when call sites
have already processed the text. Refactor by creating a new private method
called FormatTextForMessageInternal that accepts a pre-processed text parameter
and handles only the formatting logic by calling AppendOptOutFooter with the
String.Format call. Then modify the existing FormatTextForMessage method to keep
its current signature and HTML processing steps using
HtmlToTextHelper.ConvertHtml and StringHelpers.StripHtmlTagsCharArray, but have
it delegate to FormatTextForMessageInternal with the processed result instead of
directly calling AppendOptOutFooter. This allows pre-processed call sites to use
the new internal method while maintaining backward compatibility for raw HTML
inputs.
🤖 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/SmsService.cs`:
- Around line 57-60: The message body is being processed for HTML conversion and
stripping twice: once inline with HtmlToTextHelper.ConvertHtml() and
StringHelpers.StripHtmlTagsCharArray() before passing to FormatTextForMessage(),
and again inside FormatTextForMessage() itself. Remove the inline HTML
processing calls (HtmlToTextHelper.ConvertHtml and
StringHelpers.StripHtmlTagsCharArray) and pass the raw message.Body directly to
FormatTextForMessage(), allowing that method to handle all HTML processing in
one place. This consolidation should be applied to both occurrences of this
pattern in the file.

---

Outside diff comments:
In `@Core/Resgrid.Services/SmsService.cs`:
- Around line 172-174: In the SendCallAsync method, the text processing from
call.NatureOfCall is being done twice: first at the HtmlToTextHelper.ConvertHtml
and StringHelpers.StripHtmlTagsCharArray calls where the address is also
appended, and then again when the result is passed to FormatTextForMessage.
Since the text has already been converted from HTML and stripped before being
sent to FormatTextForMessage, either update FormatTextForMessage to have a
parameter or overload that accepts already-processed text and skips the HTML
conversion and stripping steps, or refactor the code flow to avoid calling both
the initial HTML processing steps and the processing inside FormatTextForMessage
on the same text content.

---

Nitpick comments:
In `@Core/Resgrid.Services/SmsService.cs`:
- Around line 22-32: The SmsService constructor is using constructor injection
for dependencies, but the coding guidelines require using the Service Locator
pattern instead. Remove the ICacheProvider parameter from the SmsService
constructor signature and the corresponding assignment statement for
_cacheProvider. Instead, resolve the ICacheProvider explicitly within the
constructor body using Bootstrapper.GetKernel().Resolve<ICacheProvider>() and
assign it to the _cacheProvider field to align with the Service Locator pattern
guidelines.
- Around line 501-522: The FormatTextForMessage method performs redundant HTML
processing when call sites have already processed the text. Refactor by creating
a new private method called FormatTextForMessageInternal that accepts a
pre-processed text parameter and handles only the formatting logic by calling
AppendOptOutFooter with the String.Format call. Then modify the existing
FormatTextForMessage method to keep its current signature and HTML processing
steps using HtmlToTextHelper.ConvertHtml and
StringHelpers.StripHtmlTagsCharArray, but have it delegate to
FormatTextForMessageInternal with the processed result instead of directly
calling AppendOptOutFooter. This allows pre-processed call sites to use the new
internal method while maintaining backward compatibility for raw HTML inputs.
🪄 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: 00e9da25-e346-46f3-81b0-9fbe75b3c0b4

📥 Commits

Reviewing files that changed from the base of the PR and between 0221b30 and 4cd45c7.

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

Comment thread Core/Resgrid.Services/SmsService.cs Outdated
@ucswift

ucswift commented Jun 18, 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 2baed90 into master Jun 18, 2026
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