Skip to content

Releases: Resgrid/Core

4.539.0

Choose a tag to compare

@github-actions github-actions released this 14 Jul 23:13
540c023

Here is the analysis of the code changes and the generated Summary:

Summary

This pull request implements RG-T125 (IC bug fixes and weather alert fixes). The work is focused on improving the Incident Command (IC) module (specifically the Command Board and Command Definition Templates) and enhancing weather alert notifications.

Key Changes:

  1. Incident Command (IC) Improvements:

    • Command Definition Board (Templates): Fixed several issues related to the creation and management of command board templates (templates for the default incident command board layout). This includes ensuring that templates, roles, and lanes (assignments on the board) are properly persisted, retrieved, and linked.
    • Lane Requirements: Implemented advisory and forced lane requirements (via ForceRequirements). When enabled, the system can now block assigning a resource (personnel/unit) to a lane on the command board if they do not meet the template-defined lane requirements (e.g., required personnel roles or unit types). This ensures only qualified resources are placed in specific lanes on the default board templates.
    • Lane Assignment: Implemented the logic to correctly assign, move, and remove resources (personnel and units) to lanes on the command board. This includes ensuring proper relationships and preventing assignment when forced lane requirements are not met.
    • Lane Definition Persistence: The system now correctly saves and retrieves the lane requirements (roles, unit types) when a user creates or edits a command board template.
    • Command Board View: Fixed the view of the command board to correctly display the lanes, assignments, and requirements.
  2. Weather Alert Fixes:

    • Fixed the creation and sending of weather alert notifications. Weather alerts are now properly categorized with a new WeatherAlert message type and include a custom PushSubTitle (based on the alert headline) for better user visibility.
    • Added new exception handling (CommandRequirementsNotMetException) to ensure the system gracefully handles and reports errors when a user attempts to assign a resource that does not meet the forced lane requirements.
  3. Database and Infrastructure:

    • Added necessary database migrations to support the new lane requirements and exception handling.
    • Implemented unit tests and integration tests to cover the new command board templates and lane assignment functionality.

4.531.0

Choose a tag to compare

@github-actions github-actions released this 30 Jun 00:18
ec4c6bf

Description

This PR fixes bugs in the voice dispatch TTS (text-to-speech) path where long dispatch text exceeding the TTS chunk limit would cause ArgumentException failures, breaking voice dispatch playback (Sentry: RESGRID-API-78).

Changes

  • Pre-warm path (PreWarmPromptAsync): Previously threw ArgumentException for any input spanning more than one TTS chunk. It now iterates over all chunks and pre-warms each individually, so long dispatch text no longer faults the pre-warm/redirect flow.

  • Playback path (TryAppendDispatchPlaybackAsync in TwilioController): Switched from GetPromptUrlAsync (single-chunk only) to the multi-chunk-aware AppendPromptAsync, which emits one <Play> element per chunk. This prevents failures when dispatch text (e.g., long notes or addresses) exceeds the chunk limit.

  • Tests: Added two regression tests verifying that PreWarmPromptAsync no longer throws for multi-chunk text and that AppendPromptAsync produces a <Play> per chunk for multi-chunk input.

4.529.0

Choose a tag to compare

@github-actions github-actions released this 29 Jun 22:36
8f712f5

Pull Request Description

This PR fixes a SQL Server compatibility issue with the Resgrid Workers that occurs on .NET 8+ (the project now targets .NET 9.0).

Problem

The external Quidjibo.SqlServer NuGet package (v0.6.0) uses the legacy System.Data.SqlClient, which throws a SqlGuidCaster TypeLoadException on .NET 8 and above, breaking the worker's ability to use SQL Server as its background job store.

Changes

  1. Vendored the Quidjibo.SqlServer provider locally: Replaced the NuGet package reference with a new in-repository Quidjibo.SqlServer project that uses Microsoft.Data.SqlClient instead. This project includes:

    • Configuration, factory, and provider implementations for Work, Schedule, and Progress job storage
    • All required SQL scripts (schema setup, work receive/complete/fault/send, schedule create/receive/complete, progress tracking)
    • Utility classes for SQL execution and embedded script loading
  2. Fixed semaphore lock patterns in the Postgres provider factories: Moved SyncLock.WaitAsync() calls outside of their try blocks in the Progress, Schedule, and Work provider factories. This prevents a scenario where a cancellation or exception during WaitAsync would cause the finally block to incorrectly call Release(), leading to a SemaphoreFullException.

4.526.0

Choose a tag to compare

@github-actions github-actions released this 29 Jun 18:50
1447988

Summary

This PR implements the backend infrastructure for the Resgrid IC (Incident Command) app's offline-first shift-start workflow, comprising three major additions: (1) two new aggregate sync endpoints that let a field client pull everything it needs in minimal round-trips, (2) a per-endpoint incident-capability authorization layer, and (3) associated tests and documentation.

1. Shift-start aggregate sync endpoints

GET /api/v4/Sync/Bundle — Returns a render-ready IncidentCommandBoard (lanes, resources, objectives, timers, roles, annotations, and computed accountability/PAR) for every active incident in the caller's department, plus active ad-hoc units and personnel, in a single call. The client uses the returned ServerTimestampMs as the cursor for subsequent incremental /Sync/Changes pulls.

  • IncidentCommandService.GetBundleForDepartmentAsync scans each board table once for the whole department and groups by CallId in memory — O(tables) instead of the previous O(active incidents × department size) per-incident N+1 pattern.
  • The bundle is read-only: unlike the per-call GetCommandBoardAsync, it does not run the write-side PAR sweep (no marker writes or SignalR pushes).
  • ?includeAccountability=false lets very busy departments skip the per-incident PAR computation.

GET /api/v4/Sync/Reference — Returns the slowly-changing department configuration and a safe personnel roster needed to start and run an incident offline (call types, priorities, command templates, units, groups, POIs, protocols, custom statuses, feature flags).

  • A new SyncService aggregates data from 12 existing services into a single SyncReferenceData payload.
  • Personnel and groups are projected to safe DTOs (ReferencePersonnel, ReferenceGroup) that structurally exclude IdentityUser navs, password/security fields, and UserProfile contact-verification secrets.
  • Department-scoped cache-aside (5-minute TTL) via a protobuf-safe JSON envelope (ReferenceCacheEnvelope); ?bypassCache=true forces a fresh read.

Batched ad-hoc resources: IncidentResourcesService.GetActiveAdHocResourcesForDepartmentAsync returns all active (non-released) ad-hoc units and personnel scoped to the department's active incidents in one scan per table, replacing the per-incident N+1 lookups.

2. Incident-scoped capability authorization

A new RequiresIncidentCapabilityAttribute action filter enforces that the calling user holds the required IncidentCapabilities (e.g., ManageStructure, AssignResources, ManageAccountability) for the specific incident targeted by the request — layered on top of the existing broad [Authorize(Policy = Command_*)] claims.

  • Resolves the target Call from the request via three strategies: explicit callId route value, IncidentCommandId (resolved to its Call with department-ownership verification), or CallId on the bound body.
  • Fails open when the Call cannot be determined or belongs to another department — the broad Command_* claim and service-layer ownership guards still apply, so the filter only ever adds protection.
  • Applied to 14 endpoints across IncidentCommandController, IncidentResourcesController, IncidentRolesController, and IncidentVoiceController.
  • Intentionally not applied to EstablishCommand (it creates the command, so no capabilities exist yet) or to entity-id "second action" verbs where the target Call isn't on the request.

3. Tests & documentation

  • Added unit tests for the bundle assembly, tombstone filtering, read-only behavior, ad-hoc batching, the safe personnel projection, cache behavior, and the capability filter's allow/deny/fail-open paths.
  • Updated docs/architecture/offline-first-architecture.md with the authoritative shift-start manifest (§6.1) and marked the delta and aggregate endpoints as done (§9).

4.521.0

Choose a tag to compare

@github-actions github-actions released this 26 Jun 03:25
cc0990c

Pull Request Description

This PR delivers the backend foundation for offline-first Incident Command (IC) plus several SMS deliverability, phone-number validation, and data-truncation fixes.

1. Offline-First Sync Foundation (RIC-T39)

Establishes the server-side primitives the mobile apps need to work fully offline and reconcile on reconnect (documented in the new docs/architecture/offline-first-architecture.md):

  • Change tracking: adds a ModifiedOn cursor (and a DeletedOn soft-delete tombstone for lanes) to all mutable incident-command entities via the new IChangeTracked interface. Every insert/update stamps it through the new Touch() / UpsertOwnedAsync() helpers.
  • Delta sync endpoint: new GET /api/v4/Sync/Changes?since= returns every changed row (including soft-deleted/closed/released) for the caller's department, with a ServerTimestampMs cursor for the next pull.
  • Idempotent creates: create paths now honor a client-supplied GUID PK and upsert by existence (not by id-presence), so an offline-created row replays without duplicating; foreign-department rows are rejected.
  • Idempotency keys for check-ins: CheckInRecord.IdempotencyKey (with a filtered unique index) dedups replayed offline check-ins; a post-violation recovery adopts the winning row instead of erroring.
  • Soft-delete for lanes: DeleteNodeAsync now sets a tombstone instead of hard-deleting, so removals propagate on delta sync.
  • DB migrations M0081–M0083 (SQL Server + PostgreSQL twins) add the columns/indexes.

2. SMS Deliverability & Cost

New SmsContentHelper applied at the single chokepoint (and the workflow SMS executor) before sending:

  • Strips non-allow-listed URLs (A2P 10DLC carrier filtering) while preserving Resgrid/map domains and bit.ly.
  • Normalizes smart punctuation to keep messages in cheaper GSM-7 encoding.
  • Truncates to a configurable max length (avoids Twilio error 21617).
  • Invalid/unreachable "To" numbers from Twilio are now logged quietly instead of surfacing as fatal errors.
  • Adds configurable SmsMaxLength and SmsAllowedUrlDomains settings.

3. Phone-Number Validation & E.164 Normalization

  • New PhoneRegionHelper maps country names to ISO region codes so national-format numbers can be recognized.
  • ContactVerificationService now validates/normalizes to E.164 before sending SMS or voice calls (prevents Twilio "Invalid 'To'" failures).
  • Profile editing validates and normalizes mobile/home numbers server-side, with a new AJAX ValidatePhoneNumber endpoint and a shared client-side resgrid.phonevalidate.js that offers a one-click canonical fix on the profile, contact, and personnel forms.

4. Data-Truncation Fixes

  • Addresses: widens Addresses columns (street to max, others generously) and tightens model-level StringLength validation across all address-entry view models so client + server reject oversized values before save. M0085 (both DBs).
  • Autofills.Data: widened from nvarchar(255) to max to prevent truncation of long call-note templates. M0084 (both DBs).

5. Other Fixes

  • HealthRepository: now uses the configured connection provider and CURRENT_TIMESTAMP so the health check works on PostgreSQL-backed datacenters (was hardcoded to SQL Server + GETDATE()).
  • Contacts Index view: corrected a model-indexer bug (Model.Contacts[i]Model.ContactCategories[i]).
  • Switched append-only timeline/transfer inserts from SaveOrUpdateAsync to explicit InsertAsync (pre-set GUIDs would otherwise cause silent 0-row updates).

Tests

Adds comprehensive coverage for idempotent check-ins (incl. concurrent-replay race), offline-aware IC service behavior (change tracking, idempotent creates, soft-delete, delta pull), ad-hoc resource idempotency/delta, SmsContentHelper, and PhoneRegionHelper.

4.516.0

Choose a tag to compare

@github-actions github-actions released this 24 Jun 15:13
01beb00
<!-- This is an auto-generated comment: release notes by coderabbit.ai -->

4.513.0

Choose a tag to compare

@github-actions github-actions released this 23 Jun 14:50
848f34a
<!-- This is an auto-generated comment: release notes by coderabbit.ai -->

4.509.0

Choose a tag to compare

@github-actions github-actions released this 22 Jun 21:23
27a0c4f
<!-- This is an auto-generated comment: release notes by coderabbit.ai -->

4.507.0

Choose a tag to compare

@github-actions github-actions released this 22 Jun 18:43
e348678
<!-- This is an auto-generated comment: release notes by coderabbit.ai -->

4.504.0

Choose a tag to compare

@github-actions github-actions released this 21 Jun 15:34
0ea0d7e
<!-- This is an auto-generated comment: release notes by coderabbit.ai -->