Skip to content

RG-T117 Fixes - #445

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

RG-T117 Fixes#445
ucswift merged 2 commits into
masterfrom
develop

Conversation

@ucswift

@ucswift ucswift commented Aug 3, 2026

Copy link
Copy Markdown
Member

Pull Request Description

RG-T117 Fixes: Improve contact verification error reporting with detailed status codes

This PR replaces the previous boolean (true/false) return type from contact verification send methods with a new ContactVerificationSendStatus enum that provides granular failure reasons. This allows the system to communicate specific error conditions to users instead of a generic failure message.

Changes:

  • New ContactVerificationSendStatus enum with five states: Sent, ContactNotConfigured, InvalidContact, RateLimited, and DeliveryFailed

  • Updated IContactVerificationService and ContactVerificationService so that SendEmailVerificationCodeAsync, SendMobileVerificationCodeAsync, and SendHomeVerificationCodeAsync now return the detailed status enum instead of a boolean

  • Updated controllers (Web API ContactVerificationController and MVC HomeController) to propagate the error code in API responses, enabling clients to display appropriate messages

  • Added ErrorCode property to the SendVerificationCodeResult API model

  • Added localized error messages for the three new failure conditions (VerificationContactNotConfigured, VerificationInvalidContact, VerificationDeliveryFailed) across all supported languages (Arabic, German, English, Spanish, French, Italian, Polish, Swedish, Ukrainian)

  • Updated frontend (EditUserProfile) to map error codes to user-friendly localized messages, replacing the previous behavior where all failures showed a rate-limit message

  • Updated and expanded unit tests to validate the new status-based return values and cover additional failure scenarios (invalid contact, rate limiting, delivery failure)

Summary by CodeRabbit

  • New Features

    • Contact verification now reports specific outcomes, including invalid, unconfigured, rate-limited, and delivery-failure statuses.
    • Profile verification messages display clearer, status-specific error messages.
    • Verification send limits are tracked separately for email, mobile, and home contacts.
  • Bug Fixes

    • Contact changes reset verification send limits.
    • Verification failures no longer appear incorrectly as rate-limit errors.

@request-info

request-info Bot commented Aug 3, 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 Aug 3, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Contact verification sends now return explicit statuses for configuration, validation, rate limiting, delivery, and success. Per-channel send windows are persisted. API responses expose error codes and messages. The profile UI displays localized messages for each failure status.

Changes

Contact verification status flow

Layer / File(s) Summary
Send-window persistence and rate limiting
Core/Resgrid.Model/UserProfile.cs, Core/Resgrid.Services/ContactVerificationService.cs, Providers/Resgrid.Providers.Migrations/..., Providers/Resgrid.Providers.MigrationsPg/...
User profiles store separate send counts and one-hour window timestamps for email, mobile, and home verification. Migrations add the fields, contact changes reset them, and the service enforces the send limit.
Status contract and service mapping
Core/Resgrid.Model/ContactVerificationSendStatus.cs, Core/Resgrid.Model/Services/IContactVerificationService.cs, Core/Resgrid.Services/ContactVerificationService.cs
Verification methods now return ContactVerificationSendStatus and map contact validation, rate limiting, and provider delivery results to explicit statuses.
API status propagation
Web/Resgrid.Web.Services/Models/v4/ContactVerification/ContactVerificationModels.cs, Web/Resgrid.Web.Services/Controllers/v4/ContactVerificationController.cs, Web/Resgrid.Web/Areas/User/Controllers/HomeController.cs
Controllers now return status-specific error codes and messages while preserving successful responses.
Profile verification error display
Web/Resgrid.Web/Areas/User/Views/Home/EditUserProfile.cshtml, Web/Resgrid.Web/wwwroot/js/app/internal/home/resgrid.home.edituserprofile.js, Web/Resgrid.Web/wwwroot/js/ng/react-elements.js, .gitignore
The profile UI maps verification error codes to localized labels. The generated Angular elements path is ignored and stale references are removed.

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

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant ContactVerificationController
  participant ContactVerificationService
  participant UserProfile
  participant ContactProvider
  Client->>ContactVerificationController: Request verification code
  ContactVerificationController->>ContactVerificationService: Send verification code
  ContactVerificationService->>UserProfile: Check and record send window
  UserProfile-->>ContactVerificationService: Allow or rate-limit result
  ContactVerificationService->>ContactProvider: Deliver verification code
  ContactProvider-->>ContactVerificationService: Delivery result
  ContactVerificationService-->>ContactVerificationController: ContactVerificationSendStatus
  ContactVerificationController-->>Client: Success or status-specific error
Loading
🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 25.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 identifies the ticket but does not describe the contact-verification status and error-handling changes. Use a specific title such as "Return detailed contact-verification send statuses".
✅ 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

🤖 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/ContactVerificationService.cs`:
- Around line 91-92: Remove userId from the informational invalid-contact log
messages in ContactVerificationService.cs at lines 91-92 and 123-124, logging
only the contact type and failure reason. Keep userId unchanged in the
corresponding WriteAuditAsync calls.
- Around line 64-65: Update the email, mobile, and home-number send flows in
ContactVerificationService.cs at lines 64-65, 96-97, and 128-129 to use
dedicated send-window state rather than confirmation-attempt fields. Record each
send atomically before delivery, while preserving the existing hourly rate-limit
behavior and leaving ConfirmVerificationCodeAsync attempt tracking separate.

In `@Web/Resgrid.Web/wwwroot/js/ng/react-elements.js`:
- Line 1: Add the generated elements-DHTbn7Xd.min.js chunk under the ng/chunks
bundle directory so the import in react-elements.js resolves in committed
deployments; preserve the existing filename and relative import path exactly.
🪄 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 Plus

Run ID: a54ee81f-9943-443c-a787-ebf31c406b91

📥 Commits

Reviewing files that changed from the base of the PR and between b4e1eea and e984686.

⛔ Files ignored due to path filters (10)
  • Core/Resgrid.Localization/Areas/User/Home/EditProfile.ar.resx is excluded by !**/*.resx
  • Core/Resgrid.Localization/Areas/User/Home/EditProfile.de.resx is excluded by !**/*.resx
  • Core/Resgrid.Localization/Areas/User/Home/EditProfile.en.resx is excluded by !**/*.resx
  • Core/Resgrid.Localization/Areas/User/Home/EditProfile.es.resx is excluded by !**/*.resx
  • Core/Resgrid.Localization/Areas/User/Home/EditProfile.fr.resx is excluded by !**/*.resx
  • Core/Resgrid.Localization/Areas/User/Home/EditProfile.it.resx is excluded by !**/*.resx
  • Core/Resgrid.Localization/Areas/User/Home/EditProfile.pl.resx is excluded by !**/*.resx
  • Core/Resgrid.Localization/Areas/User/Home/EditProfile.sv.resx is excluded by !**/*.resx
  • Core/Resgrid.Localization/Areas/User/Home/EditProfile.uk.resx is excluded by !**/*.resx
  • Tests/Resgrid.Tests/Services/ContactVerificationServiceTests.cs is excluded by !**/Tests/**
📒 Files selected for processing (9)
  • Core/Resgrid.Model/ContactVerificationSendStatus.cs
  • Core/Resgrid.Model/Services/IContactVerificationService.cs
  • Core/Resgrid.Services/ContactVerificationService.cs
  • Web/Resgrid.Web.Services/Controllers/v4/ContactVerificationController.cs
  • Web/Resgrid.Web.Services/Models/v4/ContactVerification/ContactVerificationModels.cs
  • Web/Resgrid.Web/Areas/User/Controllers/HomeController.cs
  • Web/Resgrid.Web/Areas/User/Views/Home/EditUserProfile.cshtml
  • Web/Resgrid.Web/wwwroot/js/app/internal/home/resgrid.home.edituserprofile.js
  • Web/Resgrid.Web/wwwroot/js/ng/react-elements.js

Comment thread Core/Resgrid.Services/ContactVerificationService.cs Outdated
Comment on lines 91 to 92
Logging.LogInfo($"Mobile verification SMS skipped for user {userId}: phone number is not a valid sendable number (needs international format, e.g. +<country code><number>).");
await WriteAuditAsync(userId, departmentId, ContactVerificationType.MobileNumber, false, "Send-InvalidNumber", null, cancellationToken);

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.

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Remove raw user identifiers from informational logs.

Both invalid-contact paths write userId to general application logs. Keep the identifier in the audit record, but log only the contact type and failure reason at informational level.

  • Core/Resgrid.Services/ContactVerificationService.cs#L91-L92: Remove userId from the mobile invalid-contact log message.
  • Core/Resgrid.Services/ContactVerificationService.cs#L123-L124: Remove userId from the home-number invalid-contact log message.
📍 Affects 1 file
  • Core/Resgrid.Services/ContactVerificationService.cs#L91-L92 (this comment)
  • Core/Resgrid.Services/ContactVerificationService.cs#L123-L124
🤖 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/ContactVerificationService.cs` around lines 91 - 92,
Remove userId from the informational invalid-contact log messages in
ContactVerificationService.cs at lines 91-92 and 123-124, logging only the
contact type and failure reason. Keep userId unchanged in the corresponding
WriteAuditAsync calls.

Comment thread Web/Resgrid.Web/wwwroot/js/ng/react-elements.js Outdated
@Resgrid-Bot

This comment has been minimized.

public class SendVerificationCodeResult
{
public bool Successful { get; set; }
public string ErrorCode { get; set; }

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 low

Uninitialized property: The ErrorCode string property defaults to null, violating Rule [31] and risking NullReferenceException downstream. Initialize it with = string.Empty (or = null only if explicitly intended).

Kody rule violation: Initialize properties with default values

Prompt for LLM

File Web/Resgrid.Web.Services/Models/v4/ContactVerification/ContactVerificationModels.cs:

Line 22:

Uninitialized property: The `ErrorCode` string property defaults to null, violating Rule [31] and risking `NullReferenceException` downstream. Initialize it with `= string.Empty` (or `= null` only if explicitly intended).

Talk to Kody by mentioning @kody

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

{
case ContactVerificationType.Email:
sent = await _contactVerificationService.SendEmailVerificationCodeAsync(UserId, DepartmentId, cancellationToken);
sendStatus = await _contactVerificationService.SendEmailVerificationCodeAsync(UserId, DepartmentId, cancellationToken);

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 risk: The awaited call to SendEmailVerificationCodeAsync lacks a try/catch, causing failures like SMTP errors or service timeouts to surface as unstructured 500 responses. Wrap the await (or the entire switch block) in try/catch, log with context (UserId, DepartmentId, operation name), and return a meaningful error response.

Also found in:

  • Web/Resgrid.Web/Areas/User/Controllers/HomeController.cs:1150-1150
  • Web/Resgrid.Web/Areas/User/Controllers/HomeController.cs:1153-1153
  • Web/Resgrid.Web.Services/Controllers/v4/ContactVerificationController.cs:53-53
  • Web/Resgrid.Web.Services/Controllers/v4/ContactVerificationController.cs:59-59
  • Web/Resgrid.Web.Services/Controllers/v4/ContactVerificationController.cs:65-65

Kody rule violation: Handle async operations with proper error handling

Prompt for LLM

File Web/Resgrid.Web/Areas/User/Controllers/HomeController.cs:

Line 1147:

Unhandled exception risk: The awaited call to `SendEmailVerificationCodeAsync` lacks a try/catch, causing failures like SMTP errors or service timeouts to surface as unstructured 500 responses. Wrap the await (or the entire switch block) in try/catch, log with context (UserId, DepartmentId, operation name), and return a meaningful error response.

**Also found in:**
- `Web/Resgrid.Web/Areas/User/Controllers/HomeController.cs:1150-1150`
- `Web/Resgrid.Web/Areas/User/Controllers/HomeController.cs:1153-1153`
- `Web/Resgrid.Web.Services/Controllers/v4/ContactVerificationController.cs:53-53`
- `Web/Resgrid.Web.Services/Controllers/v4/ContactVerificationController.cs:59-59`
- `Web/Resgrid.Web.Services/Controllers/v4/ContactVerificationController.cs:65-65`

Talk to Kody by mentioning @kody

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

{
case ContactVerificationType.Email:
sent = await _contactVerificationService.SendEmailVerificationCodeAsync(UserId, DepartmentId, cancellationToken);
sendStatus = await _contactVerificationService.SendEmailVerificationCodeAsync(UserId, DepartmentId, cancellationToken);

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

Missing error handling: The external call to SendEmailVerificationCodeAsync lacks try/catch, so transient or connectivity errors from email/SMS gateways propagate without context. Enclose the call in try/catch, log with operation name and identifiers, and return an appropriate error code or ContactVerificationSendStatus.Failed.

Also found in:

  • Web/Resgrid.Web/Areas/User/Controllers/HomeController.cs:1153-1153
  • Web/Resgrid.Web.Services/Controllers/v4/ContactVerificationController.cs:59-59
  • Web/Resgrid.Web.Services/Controllers/v4/ContactVerificationController.cs:65-65
  • Web/Resgrid.Web/Areas/User/Controllers/HomeController.cs:1150-1150
  • Web/Resgrid.Web.Services/Controllers/v4/ContactVerificationController.cs:53-53

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

Prompt for LLM

File Web/Resgrid.Web/Areas/User/Controllers/HomeController.cs:

Line 1147:

Missing error handling: The external call to `SendEmailVerificationCodeAsync` lacks try/catch, so transient or connectivity errors from email/SMS gateways propagate without context. Enclose the call in try/catch, log with operation name and identifiers, and return an appropriate error code or `ContactVerificationSendStatus.Failed`.

**Also found in:**
- `Web/Resgrid.Web/Areas/User/Controllers/HomeController.cs:1153-1153`
- `Web/Resgrid.Web.Services/Controllers/v4/ContactVerificationController.cs:59-59`
- `Web/Resgrid.Web.Services/Controllers/v4/ContactVerificationController.cs:65-65`
- `Web/Resgrid.Web/Areas/User/Controllers/HomeController.cs:1150-1150`
- `Web/Resgrid.Web.Services/Controllers/v4/ContactVerificationController.cs:53-53`

Talk to Kody by mentioning @kody

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

Comment on lines +1159 to +1163
return Json(new
{
success = sendStatus == ContactVerificationSendStatus.Sent,
errorCode = sendStatus == ContactVerificationSendStatus.Sent ? null : sendStatus.ToString()
});

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

Incorrect HTTP status code: The endpoint returns HTTP 200 via Json() even when sendStatus != Sent, violating Rule [60] and obscuring failures from clients and monitoring tools that rely on status codes. Return StatusCode(400/500) or BadRequest/ObjectResult with the error payload when sendStatus != Sent, reserving 200 for the genuinely successful case.

Also found in:

  • Web/Resgrid.Web.Services/Controllers/v4/ContactVerificationController.cs:74-80

Kody rule violation: Use appropriate HTTP status codes

Prompt for LLM

File Web/Resgrid.Web/Areas/User/Controllers/HomeController.cs:

Line 1159 to 1163:

Incorrect HTTP status code: The endpoint returns HTTP 200 via `Json()` even when `sendStatus != Sent`, violating Rule [60] and obscuring failures from clients and monitoring tools that rely on status codes. Return `StatusCode(400/500)` or `BadRequest/ObjectResult` with the error payload when `sendStatus != Sent`, reserving 200 for the genuinely successful case.

**Also found in:**
- `Web/Resgrid.Web.Services/Controllers/v4/ContactVerificationController.cs:74-80`

Talk to Kody by mentioning @kody

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


function getSendFailureLabel(errorCode) {
switch (errorCode) {
case 'ContactNotConfigured':

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 low

Magic string in switch case: The literal 'ContactNotConfigured' risks typos and drift with the server contract. Define a constants object (e.g., SendFailureCodes = { ContactNotConfigured: 'ContactNotConfigured', ... }) and reference SendFailureCodes.ContactNotConfigured in the switch.

Also found in:

  • Web/Resgrid.Web/wwwroot/js/app/internal/home/resgrid.home.edituserprofile.js:76-76
  • Web/Resgrid.Web/wwwroot/js/app/internal/home/resgrid.home.edituserprofile.js:74-74

Kody rule violation: Use enums instead of magic strings

Prompt for LLM

File Web/Resgrid.Web/wwwroot/js/app/internal/home/resgrid.home.edituserprofile.js:

Line 72:

Magic string in switch case: The literal `'ContactNotConfigured'` risks typos and drift with the server contract. Define a constants object (e.g., `SendFailureCodes = { ContactNotConfigured: 'ContactNotConfigured', ... }`) and reference `SendFailureCodes.ContactNotConfigured` in the switch.

**Also found in:**
- `Web/Resgrid.Web/wwwroot/js/app/internal/home/resgrid.home.edituserprofile.js:76-76`
- `Web/Resgrid.Web/wwwroot/js/app/internal/home/resgrid.home.edituserprofile.js:74-74`

Talk to Kody by mentioning @kody

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

$btn.hide();
} else {
$(w.msgSpan).text(rgVerifyLabels.rateLimited).css('color', '#a94442').show();
$(w.msgSpan).text(getSendFailureLabel(result && result.errorCode)).css('color', '#a94442').show();

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 low

Magic color value: The hex '#a94442' is inlined directly in the .css() call, reducing readability and complicating theming changes. Extract it into a named constant (e.g., ErrorTextColor) or reference a shared styles/theme constant.

Kody rule violation: Replace magic numbers with named constants

Prompt for LLM

File Web/Resgrid.Web/wwwroot/js/app/internal/home/resgrid.home.edituserprofile.js:

Line 106:

Magic color value: The hex `'#a94442'` is inlined directly in the `.css()` call, reducing readability and complicating theming changes. Extract it into a named constant (e.g., `ErrorTextColor`) or reference a shared styles/theme constant.

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 3, 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.

@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

🤖 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/ContactVerificationService.cs`:
- Around line 64-68: The email reservation at
Core/Resgrid.Services/ContactVerificationService.cs:64-68, mobile reservation at
Core/Resgrid.Services/ContactVerificationService.cs:99-103, and home reservation
at Core/Resgrid.Services/ContactVerificationService.cs:134-138 must reserve
sends atomically before delivery. Replace the current
TryRecordSend/read-then-later-save flow in ContactVerificationService with a
store-level conditional update, transaction lock, or optimistic-concurrency
retry that increments the persisted count only while it remains below
MaxVerificationSendsPerHour, returning RateLimited when reservation fails.
🪄 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 Plus

Run ID: 6122765f-4671-4ecf-8157-f40591343bc7

📥 Commits

Reviewing files that changed from the base of the PR and between e984686 and 2c1c76a.

⛔ Files ignored due to path filters (1)
  • Tests/Resgrid.Tests/Services/ContactVerificationServiceTests.cs is excluded by !**/Tests/**
📒 Files selected for processing (7)
  • .gitignore
  • Core/Resgrid.Model/UserProfile.cs
  • Core/Resgrid.Services/ContactVerificationService.cs
  • Providers/Resgrid.Providers.Migrations/Migrations/M0110_AddingVerificationSendWindows.cs
  • Providers/Resgrid.Providers.MigrationsPg/Migrations/M0110_AddingVerificationSendWindowsPg.cs
  • Web/Resgrid.Web/wwwroot/js/ng/react-elements.css
  • Web/Resgrid.Web/wwwroot/js/ng/react-elements.js
💤 Files with no reviewable changes (1)
  • Web/Resgrid.Web/wwwroot/js/ng/react-elements.js

Comment on lines +64 to +68
if (!TryRecordSend(profile.EmailVerificationSendWindowStart, profile.EmailVerificationSendCount, out DateTime emailWindowStart, out int emailSendCount))
return ContactVerificationSendStatus.RateLimited;

profile.EmailVerificationSendWindowStart = emailWindowStart;
profile.EmailVerificationSendCount = emailSendCount;

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.

🩺 Stability & Availability | 🟠 Major | 🏗️ Heavy lift

Make send-window reservation atomic.

Each flow reads a profile, calculates a count, and saves later. Concurrent requests can read the same count and all pass the cap. This can issue more verification messages than MaxVerificationSendsPerHour permits.

Use a store-level conditional update, transaction lock, or optimistic-concurrency retry that reserves one send only when the persisted count remains below the cap.

  • Core/Resgrid.Services/ContactVerificationService.cs#L64-L68: Atomically reserve the email send before delivery.
  • Core/Resgrid.Services/ContactVerificationService.cs#L99-L103: Atomically reserve the mobile send before delivery.
  • Core/Resgrid.Services/ContactVerificationService.cs#L134-L138: Atomically reserve the home send before delivery.
#!/bin/bash
set -euo pipefail

ast-grep outline Core/Resgrid.Services/ContactVerificationService.cs --items all --type method

# Inspect the persistence implementation for transactional or optimistic-concurrency protection.
rg -n -C 10 --glob '*.cs' \
  '\bSaveProfileAsync\s*\(|class\s+\w*UserProfileService\b|RowVersion|ConcurrencyCheck|FOR UPDATE|Serializable' .
📍 Affects 1 file
  • Core/Resgrid.Services/ContactVerificationService.cs#L64-L68 (this comment)
  • Core/Resgrid.Services/ContactVerificationService.cs#L99-L103
  • Core/Resgrid.Services/ContactVerificationService.cs#L134-L138
🤖 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/ContactVerificationService.cs` around lines 64 - 68,
The email reservation at
Core/Resgrid.Services/ContactVerificationService.cs:64-68, mobile reservation at
Core/Resgrid.Services/ContactVerificationService.cs:99-103, and home reservation
at Core/Resgrid.Services/ContactVerificationService.cs:134-138 must reserve
sends atomically before delivery. Replace the current
TryRecordSend/read-then-later-save flow in ContactVerificationService with a
store-level conditional update, transaction lock, or optimistic-concurrency
retry that increments the persisted count only while it remains below
MaxVerificationSendsPerHour, returning RateLimited when reservation fails.

public async Task should_allow_send_and_reset_count_after_window_elapses()
{
var profile = BuildProfile();
profile.EmailVerificationSendWindowStart = DateTime.UtcNow.AddMinutes(-61); // window elapsed

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 low

The literal -61 is a magic number that implicitly couples the test to the configured window duration, causing silent test failures without a compiler signal if the window changes. Derive the offset from the configuration constant using DateTime.UtcNow.AddMinutes(-(VerificationConfig.SendWindowMinutes + 1)) or declare a named local const such as const int ElapsedWindowOffsetMinutes = 61.

Kody rule violation: Replace magic numbers with named constants

Prompt for LLM

File Tests/Resgrid.Tests/Services/ContactVerificationServiceTests.cs:

Line 131:

The literal -61 is a magic number that implicitly couples the test to the configured window duration, causing silent test failures without a compiler signal if the window changes. Derive the offset from the configuration constant using DateTime.UtcNow.AddMinutes(-(VerificationConfig.SendWindowMinutes + 1)) or declare a named local const such as const int ElapsedWindowOffsetMinutes = 61.

Talk to Kody by mentioning @kody

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

@ucswift

ucswift commented Aug 3, 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 6b0f0e3 into master Aug 3, 2026
17 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.

2 participants