Skip to content

Conversation

@ucswift
Copy link
Member

@ucswift ucswift commented Sep 28, 2025

Summary by CodeRabbit

  • New Features

    • Automatic geocoding for addresses in call email templates.
  • Bug Fixes

    • Return false when departments cannot be retrieved to avoid null-reference in authorization.
    • Prevent duplicate shift group keys.
    • Require Title and Body when sending messages.
    • Stricter validation for unit role assignments and unit status roles.
    • Ignore empty/very-short active filters in personnel and units lists.
    • Contacts edit loads stored addresses correctly.
    • Geofence page guards against null group and corrects label text.
  • Chores

    • Removed legacy v3 API controllers and endpoints; continue using v4.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Sep 28, 2025

Walkthrough

Removes most v3 API controllers and their base; adds null/validation guards in core services; adds conditional geolocation with error logging in email-parsing; fixes shift-group aggregation upsert; tightens several v4 input/role validations; fixes contact address loading and a Geofence null-guard and text.

Changes

Cohort / File(s) Summary of changes
Core service guards & parsing
Core/Resgrid.Services/AuthorizationService.cs, Core/Resgrid.Services/CallEmailTemplates/ResgridEmailTemplate.cs, Core/Resgrid.Services/ShiftsService.cs
Authorization: early-return false if either department lookup is null. Email template parsing: set address only when present, call geolocation provider, assign geo data on success, catch/log exceptions with email body context. Shifts: use conditional upsert when aggregating shiftGroups to avoid duplicate-key adds.
Removed v3 API controllers & base
Web/Resgrid.Web.Services/Controllers/v3/*
Deleted the v3 controllers and their endpoints (Auth, Avatars, BigBoard, Calendar, CallPriorities, Calls, Chat, CommandApp, Commands, Connect, CoreData, Department, DepartmentStatus, Devices, DispatchApp, Dispatch, Feeds, Geo, Health, Links, Messages, Notes, Personnel, Profile, Protocols, Security, Shifts, Staffing, StaffingSchedules, Stations, Status, UnitApp, UnitLocation, UnitState, Units, Voice) and the V3AuthenticatedApiControllerbase class.
v4 controllers validation tweaks
Web/Resgrid.Web.Services/Controllers/v4/MessagesController.cs, .../v4/PersonnelController.cs, .../v4/UnitRolesController.cs, .../v4/UnitStatusController.cs, .../v4/UnitsController.cs
Messages: require non-empty Title and Body. Personnel/Units: skip active filters that are null/whitespace or ≤2 chars. UnitRoles/UnitStatus: require both UserId and RoleId when creating/assigning role entries.
User area MVC and view fixes
Web/Resgrid.Web/Areas/User/Controllers/ContactsController.cs, Web/Resgrid.Web/Areas/User/Views/Groups/Geofence.cshtml
Contacts Edit: load physical/mailing addresses using persisted contact IDs rather than submitted model IDs. Geofence view: corrected "GeoFence" → "Geofence" and added null-safe guard when initializing geofence script.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor Client
  participant API as v4 MessagesController
  participant Svc as MessageService

  Client->>API: POST /SendMessage { Title, Body, Recipients }
  API->>API: Validate Title && Body (non-empty, non-whitespace)
  alt invalid
    API-->>Client: 400 BadRequest
  else valid
    API->>Svc: SaveMessage(...)
    Svc-->>API: MessageId
    API-->>Client: 201 Created
  end
Loading
sequenceDiagram
  autonumber
  participant Parser as EmailParser
  participant Tmpl as ResgridEmailTemplate
  participant Geo as GeoLocationProvider

  Parser->>Tmpl: Parse(data[])
  alt data[3] (address) present
    Tmpl->>Tmpl: c.Address = data[3]
    Tmpl->>Geo: GetLatLonFromAddress(address)
    alt geo success
      Geo-->>Tmpl: Lat/Lon
      Tmpl->>Tmpl: c.GeoLocationData = result
    else exception/failure
      Tmpl->>Tmpl: Log exception with email body context
    end
  else no address
    Tmpl->>Tmpl: Skip geolocation
  end
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Possibly related PRs

  • Develop #234 — Modifies unit status handling and related services; overlaps with unit role/state and validation changes present here.

Poem

In a burrow I nibbled lines of code,
Pruned v3 branches from the old road.
I checked for nulls with twitching nose,
Geocoded letters where the address goes.
Now v4 stands tidy — hop, deploy! 🐇✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title clearly outlines the primary objective by stating the removal of the v3 API and concisely mentions the additional minor fixes, matching the changeset’s focus. Including the issue key at the beginning follows project conventions and does not detract from clarity.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch develop

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e2853a5 and 34ff56b.

📒 Files selected for processing (2)
  • Core/Resgrid.Services/CallEmailTemplates/ResgridEmailTemplate.cs (2 hunks)
  • Core/Resgrid.Services/ShiftsService.cs (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • Core/Resgrid.Services/ShiftsService.cs
  • Core/Resgrid.Services/CallEmailTemplates/ResgridEmailTemplate.cs
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: build
  • GitHub Check: Analyze (javascript-typescript)
  • GitHub Check: Analyze (csharp)
  • GitHub Check: Analyze (csharp)
  • GitHub Check: Analyze (javascript-typescript)

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.

@request-info
Copy link

request-info bot commented Sep 28, 2025

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

Copy link
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 (4)
Web/Resgrid.Web/Areas/User/Views/Groups/Geofence.cshtml (1)

74-84: Guard for Model.Group is incomplete

Adding Model.Group == null here doesn’t actually prevent the null-reference crash you’re aiming to avoid. Earlier in the view (Line 42’s HiddenFor, Line 47’s @Model.Group.Name, etc.) we still dereference Model.Group without a safety check, so if the controller hands us a null Group we’ll explode long before we reach this script block. Please either guarantee Model.Group is non-null before rendering the view, or wrap the form/UI in an @if (Model.Group != null)/else so every access is guarded. Otherwise this change only masks the real problem.

Web/Resgrid.Web/Areas/User/Controllers/ContactsController.cs (2)

498-505: Handle missing physical address records before reuse

Line 498 rehydrates an existing physical address, but _addressService.GetAddressByIdAsync can legitimately return null (e.g., the address row was deleted while the FK still exists). The next line immediately dereferences physicalAddress, so we now crash with a NullReferenceException on edit. Please keep the existing new Address() fallback when no record is found.

-				if (contact.PhysicalAddressId.HasValue)
-					physicalAddress = await _addressService.GetAddressByIdAsync(contact.PhysicalAddressId.Value);
+				if (contact.PhysicalAddressId.HasValue)
+				{
+					var existingPhysical = await _addressService.GetAddressByIdAsync(contact.PhysicalAddressId.Value);
+					if (existingPhysical != null)
+						physicalAddress = existingPhysical;
+				}

518-525: Guard against null when loading the mailing address

Same issue as the physical address block: if the stored mailing address ID points to a record that no longer exists, mailingAddress becomes null and the subsequent property assignments throw. Preserve the freshly constructed Address when no row is returned.

-				if (contact.MailingAddressId.HasValue)
-					mailingAddress = await _addressService.GetAddressByIdAsync(contact.MailingAddressId.Value);
+				if (contact.MailingAddressId.HasValue)
+				{
+					var existingMailing = await _addressService.GetAddressByIdAsync(contact.MailingAddressId.Value);
+					if (existingMailing != null)
+						mailingAddress = existingMailing;
+				}
Web/Resgrid.Web.Services/Controllers/v4/PersonnelController.cs (1)

188-194: Guard against null activeFilters before iterating.

We still enter the loop when activeFilter contains only whitespace (it’s not null, .Any() returns true), but in that branch activeFilters never gets initialized and the foreach will throw a NullReferenceException. Please switch the condition to check the array instead, e.g.:

-			if (activeFilter != null && activeFilter.Any())
+			if (activeFilters != null && activeFilters.Any())

That way we only iterate when the split actually produced entries.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4e54b00 and e2853a5.

📒 Files selected for processing (47)
  • Core/Resgrid.Services/AuthorizationService.cs (1 hunks)
  • Core/Resgrid.Services/CallEmailTemplates/ResgridEmailTemplate.cs (2 hunks)
  • Core/Resgrid.Services/ShiftsService.cs (1 hunks)
  • Web/Resgrid.Web.Services/Controllers/v3/AuthController.cs (0 hunks)
  • Web/Resgrid.Web.Services/Controllers/v3/AvatarsController.cs (0 hunks)
  • Web/Resgrid.Web.Services/Controllers/v3/BigBoardController.cs (0 hunks)
  • Web/Resgrid.Web.Services/Controllers/v3/CalendarController.cs (0 hunks)
  • Web/Resgrid.Web.Services/Controllers/v3/CallPrioritiesController.cs (0 hunks)
  • Web/Resgrid.Web.Services/Controllers/v3/CallsController.cs (0 hunks)
  • Web/Resgrid.Web.Services/Controllers/v3/ChatController.cs (0 hunks)
  • Web/Resgrid.Web.Services/Controllers/v3/CommandAppController.cs (0 hunks)
  • Web/Resgrid.Web.Services/Controllers/v3/CommandsController.cs (0 hunks)
  • Web/Resgrid.Web.Services/Controllers/v3/ConnectController.cs (0 hunks)
  • Web/Resgrid.Web.Services/Controllers/v3/CoreDataController.cs (0 hunks)
  • Web/Resgrid.Web.Services/Controllers/v3/DepartmentController.cs (0 hunks)
  • Web/Resgrid.Web.Services/Controllers/v3/DepartmentStatusController.cs (0 hunks)
  • Web/Resgrid.Web.Services/Controllers/v3/DevicesController.cs (0 hunks)
  • Web/Resgrid.Web.Services/Controllers/v3/DispatchAppController.cs (0 hunks)
  • Web/Resgrid.Web.Services/Controllers/v3/DispatchController.cs (0 hunks)
  • Web/Resgrid.Web.Services/Controllers/v3/FeedsController.cs (0 hunks)
  • Web/Resgrid.Web.Services/Controllers/v3/GeoController.cs (0 hunks)
  • Web/Resgrid.Web.Services/Controllers/v3/HealthController.cs (0 hunks)
  • Web/Resgrid.Web.Services/Controllers/v3/LinksController.cs (0 hunks)
  • Web/Resgrid.Web.Services/Controllers/v3/MessagesController.cs (0 hunks)
  • Web/Resgrid.Web.Services/Controllers/v3/NotesController.cs (0 hunks)
  • Web/Resgrid.Web.Services/Controllers/v3/PersonnelController.cs (0 hunks)
  • Web/Resgrid.Web.Services/Controllers/v3/ProfileController.cs (0 hunks)
  • Web/Resgrid.Web.Services/Controllers/v3/ProtocolsController.cs (0 hunks)
  • Web/Resgrid.Web.Services/Controllers/v3/SecurityController.cs (0 hunks)
  • Web/Resgrid.Web.Services/Controllers/v3/ShiftsController.cs (0 hunks)
  • Web/Resgrid.Web.Services/Controllers/v3/StaffingController.cs (0 hunks)
  • Web/Resgrid.Web.Services/Controllers/v3/StaffingSchedulesController.cs (0 hunks)
  • Web/Resgrid.Web.Services/Controllers/v3/StationsController.cs (0 hunks)
  • Web/Resgrid.Web.Services/Controllers/v3/StatusController.cs (0 hunks)
  • Web/Resgrid.Web.Services/Controllers/v3/UnitAppController.cs (0 hunks)
  • Web/Resgrid.Web.Services/Controllers/v3/UnitLocationController.cs (0 hunks)
  • Web/Resgrid.Web.Services/Controllers/v3/UnitStateController.cs (0 hunks)
  • Web/Resgrid.Web.Services/Controllers/v3/UnitsController.cs (0 hunks)
  • Web/Resgrid.Web.Services/Controllers/v3/V3AuthenticatedApiControllerBase.cs (0 hunks)
  • Web/Resgrid.Web.Services/Controllers/v3/VoiceController.cs (0 hunks)
  • Web/Resgrid.Web.Services/Controllers/v4/MessagesController.cs (1 hunks)
  • Web/Resgrid.Web.Services/Controllers/v4/PersonnelController.cs (8 hunks)
  • Web/Resgrid.Web.Services/Controllers/v4/UnitRolesController.cs (1 hunks)
  • Web/Resgrid.Web.Services/Controllers/v4/UnitStatusController.cs (1 hunks)
  • Web/Resgrid.Web.Services/Controllers/v4/UnitsController.cs (1 hunks)
  • Web/Resgrid.Web/Areas/User/Controllers/ContactsController.cs (2 hunks)
  • Web/Resgrid.Web/Areas/User/Views/Groups/Geofence.cshtml (2 hunks)
💤 Files with no reviewable changes (37)
  • Web/Resgrid.Web.Services/Controllers/v3/DepartmentStatusController.cs
  • Web/Resgrid.Web.Services/Controllers/v3/UnitLocationController.cs
  • Web/Resgrid.Web.Services/Controllers/v3/BigBoardController.cs
  • Web/Resgrid.Web.Services/Controllers/v3/MessagesController.cs
  • Web/Resgrid.Web.Services/Controllers/v3/UnitAppController.cs
  • Web/Resgrid.Web.Services/Controllers/v3/UnitStateController.cs
  • Web/Resgrid.Web.Services/Controllers/v3/DevicesController.cs
  • Web/Resgrid.Web.Services/Controllers/v3/StaffingController.cs
  • Web/Resgrid.Web.Services/Controllers/v3/DispatchAppController.cs
  • Web/Resgrid.Web.Services/Controllers/v3/DepartmentController.cs
  • Web/Resgrid.Web.Services/Controllers/v3/PersonnelController.cs
  • Web/Resgrid.Web.Services/Controllers/v3/AuthController.cs
  • Web/Resgrid.Web.Services/Controllers/v3/CommandAppController.cs
  • Web/Resgrid.Web.Services/Controllers/v3/HealthController.cs
  • Web/Resgrid.Web.Services/Controllers/v3/ProfileController.cs
  • Web/Resgrid.Web.Services/Controllers/v3/StaffingSchedulesController.cs
  • Web/Resgrid.Web.Services/Controllers/v3/VoiceController.cs
  • Web/Resgrid.Web.Services/Controllers/v3/UnitsController.cs
  • Web/Resgrid.Web.Services/Controllers/v3/AvatarsController.cs
  • Web/Resgrid.Web.Services/Controllers/v3/LinksController.cs
  • Web/Resgrid.Web.Services/Controllers/v3/DispatchController.cs
  • Web/Resgrid.Web.Services/Controllers/v3/CallPrioritiesController.cs
  • Web/Resgrid.Web.Services/Controllers/v3/ChatController.cs
  • Web/Resgrid.Web.Services/Controllers/v3/ProtocolsController.cs
  • Web/Resgrid.Web.Services/Controllers/v3/CalendarController.cs
  • Web/Resgrid.Web.Services/Controllers/v3/StatusController.cs
  • Web/Resgrid.Web.Services/Controllers/v3/NotesController.cs
  • Web/Resgrid.Web.Services/Controllers/v3/CoreDataController.cs
  • Web/Resgrid.Web.Services/Controllers/v3/CommandsController.cs
  • Web/Resgrid.Web.Services/Controllers/v3/SecurityController.cs
  • Web/Resgrid.Web.Services/Controllers/v3/CallsController.cs
  • Web/Resgrid.Web.Services/Controllers/v3/FeedsController.cs
  • Web/Resgrid.Web.Services/Controllers/v3/GeoController.cs
  • Web/Resgrid.Web.Services/Controllers/v3/StationsController.cs
  • Web/Resgrid.Web.Services/Controllers/v3/ShiftsController.cs
  • Web/Resgrid.Web.Services/Controllers/v3/V3AuthenticatedApiControllerBase.cs
  • Web/Resgrid.Web.Services/Controllers/v3/ConnectController.cs
🧰 Additional context used
📓 Path-based instructions (1)
**/*.cs

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.cs: Prefer functional patterns and immutable data where appropriate in C#
Use modern C# features appropriately
Use meaningful, descriptive names for types, methods, and parameters; avoid unclear abbreviations
Separate state from behavior (e.g., use records for state and place operations in separate static classes)
Prefer pure methods over methods with side effects
Use extension methods appropriately for domain-specific operations
Design for testability; avoid hidden dependencies inside methods and prefer explicit, pure functions
Minimize constructor injection; keep the number of injected dependencies small
Prefer composition with interfaces to extend behavior

Files:

  • Web/Resgrid.Web.Services/Controllers/v4/UnitsController.cs
  • Web/Resgrid.Web.Services/Controllers/v4/UnitRolesController.cs
  • Core/Resgrid.Services/AuthorizationService.cs
  • Core/Resgrid.Services/CallEmailTemplates/ResgridEmailTemplate.cs
  • Web/Resgrid.Web.Services/Controllers/v4/UnitStatusController.cs
  • Core/Resgrid.Services/ShiftsService.cs
  • Web/Resgrid.Web.Services/Controllers/v4/MessagesController.cs
  • Web/Resgrid.Web/Areas/User/Controllers/ContactsController.cs
  • Web/Resgrid.Web.Services/Controllers/v4/PersonnelController.cs
🧬 Code graph analysis (1)
Core/Resgrid.Services/CallEmailTemplates/ResgridEmailTemplate.cs (1)
Core/Resgrid.Framework/Logging.cs (2)
  • Logging (15-178)
  • LogException (65-77)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: build
  • GitHub Check: Analyze (javascript-typescript)
  • GitHub Check: Analyze (csharp)
  • GitHub Check: Analyze (csharp)
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (2)
Web/Resgrid.Web/Areas/User/Views/Groups/Geofence.cshtml (1)

24-24: Breadcrumb casing fix acknowledged

The copy edit from “GeoFence” to “Geofence” keeps the UI consistent with the rest of the product terminology. Nice catch.

Web/Resgrid.Web.Services/Controllers/v4/UnitsController.cs (1)

137-138: Guard prevents short filters from triggering substring errors

Line 137’s length/whitespace check cleanly skips empty segments, so the subsequent Substring(0, 2) on Line 142 remains safe. Thanks for tightening this up.

@ucswift
Copy link
Member Author

ucswift commented Sep 28, 2025

Approve

Copy link

@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 298b09b into master Sep 28, 2025
16 of 17 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