Skip to content

Conversation

@ucswift
Copy link
Member

@ucswift ucswift commented Aug 13, 2025

Summary by CodeRabbit

  • New Features

    • Added user-targeted iOS (APNS) and Android (FCM) push send/dispatch capabilities and subscriber updates.
    • Device registrations now use department codes for routing.
  • Bug Fixes

    • Push delivery made more resilient — provider failures are logged but won’t block other notification paths.
    • Streamlined token/registration updates for improved reliability.
  • Chores

    • Added APNS provider configuration and expanded push platform handling (focus on iOS/Android).

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Aug 13, 2025

Note

Currently processing new changes in this PR. This may take a few minutes, please wait...

📥 Commits

Reviewing files that changed from the base of the PR and between c376755 and 95ecd5d.

📒 Files selected for processing (10)
  • Core/Resgrid.Model/Services/ICommunicationService.cs (1 hunks)
  • Core/Resgrid.Services/CalendarService.cs (1 hunks)
  • Core/Resgrid.Services/CommunicationService.cs (2 hunks)
  • Core/Resgrid.Services/TrainingService.cs (1 hunks)
  • Providers/Resgrid.Providers.Messaging/NovuProvider.cs (11 hunks)
  • Workers/Resgrid.Workers.Framework/Logic/CalendarNotifierLogic.cs (3 hunks)
  • Workers/Resgrid.Workers.Framework/Logic/NotificationBroadcastLogic.cs (1 hunks)
  • Workers/Resgrid.Workers.Framework/Logic/ShiftNotificationLogic.cs (8 hunks)
  • Workers/Resgrid.Workers.Framework/Logic/ShiftNotifierLogic.cs (4 hunks)
  • Workers/Resgrid.Workers.Framework/Logic/TrainingNotifierLogic.cs (3 hunks)
 ____________________________
< The OG: Original Grepster. >
 ----------------------------
  \
   \   (\__/)
       (•ㅅ•)
       /   づ

Walkthrough

Adds Novu APNS support and broader Novu user/unit notification APIs, updates platform enum and push handling to favor iOS/Android (FCM/APNS), routes device push location to department code, and updates PushService and providers to call new Novu methods; also adds DepartmentCode to push message model.

Changes

Cohort / File(s) Summary
Config
Core/Resgrid.Config/ChatConfig.cs
Adds new public static string fields: NovuUnitApnsProviderId, NovuDispatchUserWorkflowId, NovuMessageUserWorkflowId, NovuNotificationUserWorkflowId.
Provider Interface
Core/Resgrid.Model/Providers/Models/INovuProvider.cs
Adds multiple new public methods for subscriber management and notifications: CreateUserSubscriber, CreateUnitSubscriber, UpdateUserSubscriberFcm, UpdateUnitSubscriberFcm, UpdateUnitSubscriberApns, UpdateUserSubscriberApns, SendUnitDispatch, DeleteMessage, SendUserDispatch, SendUserMessage, SendUserNotification.
Messaging Provider
Providers/Resgrid.Providers.Messaging/NovuProvider.cs
Implements APNS credential update helper and public APNS update methods (UpdateUserSubscriberApns, UpdateUnitSubscriberApns), expands CreateUnitSubscriber to accept deviceId, adds user-targeted send methods (SendUserDispatch, SendUserMessage, SendUserNotification), refactors notification send base URL/payload and sound handling (iOS).
Services — Push handling
Core/Resgrid.Services/PushService.cs
Reworked Register/RegisterUnit to use pushUri.PushLocation as code and call Novu provider update methods per platform (APNS for iOS, FCM for Android); wrapped notification send calls with try/catch and added calls to Novu send methods for user/unit messages/dispatches.
Bus Providers
Providers/Resgrid.Providers.Bus/NotificationProvider.cs, Providers/Resgrid.Providers.Bus/UnitNotificationProvider.cs
Removed Windows/MPNS paths, consolidated to Android (FCM V1) and iOS flows, migrated platform checks to numeric codes, updated sound naming to use Platforms.iOS, and added optional hubClient parameter to SendAndroidNotification.
Model — Platforms & PushUri & Message
Core/Resgrid.Model/Platforms.cs, Core/Resgrid.Model/PushUri.cs, Core/Resgrid.Model/Messages/StandardPushMessage.cs
Reworked Platforms enum to: None=0, iOS=1, Android=2, Web=3, Windows8=4; commented-out older values. Commented out Windows-specific ChannelUri assignment in PushLocation setter. Added DepartmentCode property to StandardPushMessage.
Web API Controllers
Web/Resgrid.Web.Services/Controllers/v3/DevicesController.cs, Web/Resgrid.Web.Services/Controllers/v4/DevicesController.cs
v3: replaced enum checks with literal ints for Apple platforms. v4: injected IDepartmentsService, changed RegisterDevice to set push.PushLocation to department.Code retrieved from departments service.
Web UI
Web/Resgrid.Web/Areas/User/Views/Profile/Devices.cshtml
Changed device Type label logic to map only Platforms.iOS to "iOS" (iPhone/iPad enum values no longer special-cased); minor formatting whitespace change.

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant DevicesAPI
  participant PushService
  participant DepartmentsSvc
  participant NovuProvider
  Client->>DevicesAPI: RegisterDevice(input with DeptId)
  DevicesAPI->>DepartmentsSvc: GetDepartmentById(DeptId)
  DepartmentsSvc-->>DevicesAPI: department (code)
  DevicesAPI->>PushService: Register(pushLocation=department.Code,...)
  PushService->>NovuProvider: if iOS -> UpdateUserSubscriberApns(userId, code, deviceId)
  PushService->>NovuProvider: if Android -> UpdateUserSubscriberFcm(userId, code, deviceId)
  NovuProvider-->>PushService: success/failure
  PushService-->>Client: registration result
Loading
sequenceDiagram
  participant PushService
  participant NovuProvider
  participant NovuAPI
  PushService->>NovuProvider: SendUserMessage(title, body, userId, depCode,...)
  NovuProvider->>NovuAPI: POST v1/events/trigger (payload)
  NovuAPI-->>NovuProvider: 2xx/4xx
  NovuProvider-->>PushService: success/failure
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~35 minutes

Poem

I nibble keys and hop in tune,
APNS carrots under the moon.
FCM trails for Android friends,
Department codes guide where push sends.
A rabbit's patch of notifications—🥕✨

Tip

You can get early access to new features in CodeRabbit.

Enable the early_access setting in your project's settings in CodeRabbit to enable early access features such as new models, tools, and more.

✨ 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

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@request-info
Copy link

request-info bot commented Aug 13, 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: 4

🧹 Nitpick comments (1)
Core/Resgrid.Config/ChatConfig.cs (1)

14-14: Use APNS spelling for consistency and clarity.

Field name uses "Aps" while everywhere else (providerId and PR title) use APNS. Recommend renaming to avoid confusion and align with providerId = "apns".

Apply this diff:

-        public static string NovuUnitApsProviderId = "";
+        public static string NovuUnitApnsProviderId = "";
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3fac2b4 and de413fd.

📒 Files selected for processing (4)
  • Core/Resgrid.Config/ChatConfig.cs (1 hunks)
  • Core/Resgrid.Model/Providers/Models/INovuProvider.cs (1 hunks)
  • Core/Resgrid.Services/PushService.cs (1 hunks)
  • Providers/Resgrid.Providers.Messaging/NovuProvider.cs (3 hunks)
🧰 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:

  • Core/Resgrid.Model/Providers/Models/INovuProvider.cs
  • Core/Resgrid.Config/ChatConfig.cs
  • Core/Resgrid.Services/PushService.cs
  • Providers/Resgrid.Providers.Messaging/NovuProvider.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). (1)
  • GitHub Check: build

Task<bool> CreateUnitSubscriber(int unitId, string code, int departmentId, string unitName, string deviceId);
Task<bool> UpdateUserSubscriberFcm(string userId, string code, string token);
Task<bool> UpdateUnitSubscriberFcm(int unitId, string code, string token);
Task<bool> UpdateUnitSubscriberAps(int unitId, string code, string token);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Rename method to Apns to match industry terminology and the providerId.

"Aps" is ambiguous; APNS is the widely used term and matches providerId = "apns". Rename for clarity and consistency across the API and implementation.

Apply this diff:

-    Task<bool> UpdateUnitSubscriberAps(int unitId, string code, string token);
+    Task<bool> UpdateUnitSubscriberApns(int unitId, string code, string token);

To help update all references after the rename, run:


🏁 Script executed:

#!/bin/bash
# Find all Aps occurrences to update to Apns
rg -n --no-heading $'Update(Unit|User)?SubscriberAps|UpdateSubscriberAps|NovuUnitApsProviderId'

Length of output: 921


Rename Aps to Apns across the Novu push API

The method name, its helper, calls, and the provider-ID constant must all be updated for consistency and clarity:

• Core/Resgrid.Model/Providers/Models/INovuProvider.cs
– Rename
diff - Task<bool> UpdateUnitSubscriberAps(int unitId, string code, string token); + Task<bool> UpdateUnitSubscriberApns(int unitId, string code, string token);

• Providers/Resgrid.Providers.Messaging/NovuProvider.cs
– Rename the private helper:
diff - private async Task<bool> UpdateSubscriberAps(string id, string token, string apnsId) + private async Task<bool> UpdateSubscriberApns(string id, string token, string apnsId)
– Rename the public method and its internal call:
diff - public async Task<bool> UpdateUnitSubscriberAps(int unitId, string code, string token) + public async Task<bool> UpdateUnitSubscriberApns(int unitId, string code, string token) … - return await UpdateSubscriberAps($"{code}_Unit_{unitId}", token, ChatConfig.NovuUnitApsProviderId); + return await UpdateSubscriberApns($"{code}_Unit_{unitId}", token, ChatConfig.NovuUnitApnsProviderId);

• Core/Resgrid.Services/PushService.cs
– Update the call site:
diff - await _novuProvider.UpdateUnitSubscriberAps(pushUri.UnitId.Value, pushUri.PushLocation, pushUri.DeviceId); + await _novuProvider.UpdateUnitSubscriberApns(pushUri.UnitId.Value, pushUri.PushLocation, pushUri.DeviceId);

• Core/Resgrid.Config/ChatConfig.cs
– Rename the constant:
diff - public static string NovuUnitApsProviderId = ""; + public static string NovuUnitApnsProviderId = "";

Run a final check to catch any stray “Aps” identifiers:

rg -n 'Aps\b'
🤖 Prompt for AI Agents
In Core/Resgrid.Model/Providers/Models/INovuProvider.cs around line 11, the
method UpdateUnitSubscriberAps should be renamed to UpdateUnitSubscriberApns and
all related identifiers updated for consistency; rename the method signature in
this interface and then update
Providers/Resgrid.Providers.Messaging/NovuProvider.cs by renaming the public
method and its internal helper from *Aps to *Apns, updating any calls and
references, change the provider-ID constant name in
Core/Resgrid.Config/ChatConfig.cs from Aps to Apns, and update the call site in
Core/Resgrid.Services/PushService.cs to the new method name; after making these
changes, run a project-wide search (e.g., rg -n 'Aps\b') to find and replace any
remaining occurrences and rebuild to ensure no references are broken.

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: 10

🔭 Outside diff range comments (5)
Web/Resgrid.Web/Areas/User/Views/Profile/Devices.cshtml (1)

45-52: Platform label will misreport Android devices as Web due to enum value shifts

With Platforms.Android moved to 2 and Web to 3 in the new enum, existing persisted devices that still have 3 for Android will render as Web. Avoid relying on enum ToString here and map legacy values explicitly.

Apply this diff to render correct, backward-compatible labels:

-                                            @if ((Platforms)p.PlatformType == Platforms.iOS)
-                                            {
-                                                <span>iOS</span>
-                                            }
-                                            else
-                                            {
-                                                @Html.Raw(((Platforms)p.PlatformType).ToString())
-                                            }
+                                            @if (p.PlatformType == 1 || p.PlatformType == 2)
+                                            {
+                                                <span>iOS</span>
+                                            }
+                                            else if (p.PlatformType == 3)
+                                            {
+                                                <span>Android</span>
+                                            }
+                                            else
+                                            {
+                                                @(((Platforms)p.PlatformType).ToString())
+                                            }
Providers/Resgrid.Providers.Bus/NotificationProvider.cs (2)

60-96: Registration logic never runs because of misplaced else-if; platform branches must not hang off the null-check

The platform-specific registration is tied to the else of “if (registrations != null)”, so it will skip registration whenever GetRegistrationsByTagAsync returns a non-null result (which is most cases, even when empty). Remove the else and always attempt registration after cleanup.

Apply this diff:

-            // Loop through all Azure registrations for this Hashed DeviceId and remove them
-            if (registrations != null)
-            {
-                foreach (var registration in registrations)
-                {
-                    try
-                    {
-                        await hubClient.DeleteRegistrationAsync(registration);
-                    }
-                    catch (Exception ex)
-                    {
-                        //Framework.Logging.LogException(ex);
-                    }
-                }
-            }
-
-            
-            else if (pushUri.PlatformType == 3) //(int)Platforms.Android)
+            // Loop through all Azure registrations for this Hashed DeviceId and remove them
+            if (registrations != null)
+            {
+                foreach (var registration in registrations)
+                {
+                    try
+                    {
+                        await hubClient.DeleteRegistrationAsync(registration);
+                    }
+                    catch (Exception ex)
+                    {
+                        //Framework.Logging.LogException(ex);
+                    }
+                }
+            }
+
+            // Register platform-specific
+            if (pushUri.PlatformType == 3) //(legacy Android)
             {
                 try
                 {
                     //var result = await hubClient.CreateFcmNativeRegistrationAsync(pushUri.DeviceId, tagsWithHashedDeviceId.ToArray());
                     var result = await hubClient.CreateFcmV1NativeRegistrationAsync(pushUri.DeviceId, tagsWithHashedDeviceId.ToArray());
                 }
                 catch (Exception ex)
                 {
                     //Framework.Logging.LogException(ex);
                 }
             }
-            else if (pushUri.PlatformType == 1 || pushUri.PlatformType == 2) //(int)Platforms.iPad || pushUri.PlatformType == (int)Platforms.iPhone)
+            else if (pushUri.PlatformType == 1 || pushUri.PlatformType == 2) //(legacy Apple: iPad/iPhone)
             {
                 try
                 {
                     var result = await hubClient.CreateAppleNativeRegistrationAsync(pushUri.DeviceId, tagsWithHashedDeviceId.ToArray());
                 }
                 catch (Exception ex)
                 {
                     //Framework.Logging.LogException(ex);
                 }
             }

Follow-up: Replace magic numbers with a centralized resolver to handle both legacy and new enum values consistently (see next comment).


107-118: Replace magic platform numbers with centralized, backward-compatible checks

Hard-coded ints (1, 2, 3) are brittle and now contradict the updated enum. Introduce helpers to resolve legacy vs new values and use them here.

Apply this diff:

-                if (pushUri.PlatformType == 3)// (int)Platforms.Android)
+                if (PlatformResolver.IsAndroid(pushUri.PlatformType))
                 {
                     var androidReg = registration as GcmRegistrationDescription;
                     if (androidReg != null && androidReg.GcmRegistrationId == pushUri.DeviceId)
                         await hubClient.DeleteRegistrationAsync(registration);
                 }
-                else if (pushUri.PlatformType == 1 || pushUri.PlatformType == 2) //(int)Platforms.iPad || pushUri.PlatformType == (int)Platforms.iPhone)
+                else if (PlatformResolver.IsApple(pushUri.PlatformType))
                 {
                     var iosReg = registration as AppleRegistrationDescription;
                     if (iosReg != null && iosReg.DeviceToken == pushUri.DeviceId)
                         await hubClient.DeleteRegistrationAsync(registration);
                 }

Add this utility (one-time) to a shared location (e.g., Core/Resgrid.Model/PlatformResolver.cs):

namespace Resgrid.Model
{
    public static class PlatformResolver
    {
        // Legacy values: 1=iPad, 2=iPhone, 3=Android, 4=Windows8
        public static bool IsApple(int platformType) =>
            platformType == 1 || platformType == 2 || (Platforms)platformType == Platforms.iOS;

        public static bool IsAndroid(int platformType) =>
            platformType == 3 || (Platforms)platformType == Platforms.Android;
    }
}
Providers/Resgrid.Providers.Messaging/NovuProvider.cs (2)

24-24: Fix Authorization header prefix and namespace in CreateSubscriber (will 401).

Header must include the "ApiKey " prefix, and the current reference uses a non-existent Config alias. Use ChatConfig directly.

Apply this diff:

-					httpClient.DefaultRequestHeaders.Add("Authorization", Config.ChatConfig.NovuSecretKey);
+					httpClient.DefaultRequestHeaders.Add("Authorization", $"ApiKey {ChatConfig.NovuSecretKey}");

274-277: Avoid serializing null properties in the payload (e.g., channelId when type is null).

type can be null in some calls (e.g., SendUserMessage), which leads to "channelId": null and similar fields. Instruct the serializer to ignore nulls to reduce FCM/Novu rejection risk.

Apply this diff:

-					var content = new StringContent(
-						JsonConvert.SerializeObject(payload),
+					var content = new StringContent(
+						JsonConvert.SerializeObject(payload, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore }),
 						Encoding.UTF8,
 						"application/json");
♻️ Duplicate comments (2)
Core/Resgrid.Model/Providers/Models/INovuProvider.cs (1)

58-67: Method rename to Apns is correct and consistent with providerId naming

The APNS naming resolves prior ambiguity (“Aps”). Thanks for aligning with industry terminology.

Providers/Resgrid.Providers.Messaging/NovuProvider.cs (1)

20-21: Reuse HttpClient via IHttpClientFactory to avoid socket exhaustion and consolidate headers.

Multiple new HttpClient instances per request can exhaust sockets and degrade performance. Prefer a single named client configured with base address and default headers.

Example approach (outside these hunks):

// Field + ctor
private readonly IHttpClientFactory _httpClientFactory;
public NovuProvider(IHttpClientFactory httpClientFactory) => _httpClientFactory = httpClientFactory;

// Usage
var client = _httpClientFactory.CreateClient("Novu");
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("ApiKey", ChatConfig.NovuSecretKey);
client.DefaultRequestHeaders.Add("idempotency-key", Guid.NewGuid().ToString());
// For absolute URLs: client.BaseAddress = new Uri(ChatConfig.NovuBackendUrl);

Register the named client in DI with BaseAddress = ChatConfig.NovuBackendUrl and default headers set via HttpClientFactory configuration.

Also applies to: 101-102, 137-138, 173-174, 216-217

🧹 Nitpick comments (9)
Core/Resgrid.Model/Messages/StandardPushMessage.cs (1)

9-9: Consider documenting and validating the new DepartmentCode field

Add XML docs for discoverability and clarify whether null/empty is allowed. If this is optional, consider marking it nullable (string?) when NRT is enabled; otherwise ensure upstream always sets it.

Web/Resgrid.Web/Areas/User/Views/Profile/Devices.cshtml (1)

61-61: Avoid Html.Raw on enum strings in confirmation text

Enums are safe, but Raw is unnecessary and risks future XSS if inputs change. Let Razor encode by default.

Apply this diff:

-                                            <a asp-controller="Home" asp-action="DeletePushUri" asp-route-area="User" asp-route-pushUriId="@p.PushUriId" class="btn btn-xs btn-danger" data-confirm="WARNING: This will remove the ability to push messages to your @Html.Raw(((Platforms)p.PlatformType).ToString()) device. Are you sure you want to delete your @Html.Raw(((Platforms)p.PlatformType).ToString()) device?" rel="nofollow">Delete</a>
+                                            <a asp-controller="Home" asp-action="DeletePushUri" asp-route-area="User" asp-route-pushUriId="@p.PushUriId" class="btn btn-xs btn-danger" data-confirm="WARNING: This will remove the ability to push messages to your @(((Platforms)p.PlatformType).ToString()) device. Are you sure you want to delete your @(((Platforms)p.PlatformType).ToString()) device?" rel="nofollow">Delete</a>
Providers/Resgrid.Providers.Bus/NotificationProvider.cs (1)

249-272: Optional: Reuse hubClient across calls and reduce string allocations

You already allow passing a hubClient; good. Consider caching channel derivation and avoiding repeated ToLower calls by computing eventType once. Not urgent.

Core/Resgrid.Model/Providers/Models/INovuProvider.cs (5)

92-96: Add XML docs for the new SendUser methods*

Other members are documented; document these for parity (parameters, behavior, return semantics).


20-31: Consider adding CancellationToken parameters to async methods

Improve resilience and testability by allowing callers to cancel outbound calls to Novu and hub operations.

Example signature change:

Task<bool> CreateUserSubscriber(string userId, string code, int departmentId, string email, string firstName, string lastName, System.Threading.CancellationToken ct = default);

Apply similarly to other methods that perform I/O.

Also applies to: 40-50, 58-68, 82-84, 90-90, 92-96


20-20: Parameter naming: clarify “code” semantic

Across the interface, “code” sometimes refers to a Novu integration key and elsewhere to a department code (depCode). Consider renaming to integrationCode and departmentCode respectively to avoid ambiguity.


5-7: Interface summary is helpful; consider referencing workflows/providers explicitly

Add references to ChatConfig workflow/provider IDs this interface expects (e.g., NovuDispatchUserWorkflowId, NovuUnitApnsProviderId) to make implementers aware of required configuration.


1-4: Namespace/file-scoped style (optional)

If the repo targets modern C#, consider file-scoped namespace to reduce indentation noise.

Providers/Resgrid.Providers.Messaging/NovuProvider.cs (1)

259-266: Remove commented-out data block for cleanliness.

Dead commented code reduces readability. It’s already represented in the structured payload above.

Apply this diff:

-								 //data = new
-								 //{
-								 //	title = title,
-								 //	message = body,
-								 //	eventCode = eventCode,
-								 //	type = type
-								 //}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between de413fd and c376755.

📒 Files selected for processing (12)
  • Core/Resgrid.Config/ChatConfig.cs (1 hunks)
  • Core/Resgrid.Model/Messages/StandardPushMessage.cs (1 hunks)
  • Core/Resgrid.Model/Platforms.cs (1 hunks)
  • Core/Resgrid.Model/Providers/Models/INovuProvider.cs (1 hunks)
  • Core/Resgrid.Model/PushUri.cs (1 hunks)
  • Core/Resgrid.Services/PushService.cs (6 hunks)
  • Providers/Resgrid.Providers.Bus/NotificationProvider.cs (5 hunks)
  • Providers/Resgrid.Providers.Bus/UnitNotificationProvider.cs (5 hunks)
  • Providers/Resgrid.Providers.Messaging/NovuProvider.cs (8 hunks)
  • Web/Resgrid.Web.Services/Controllers/v3/DevicesController.cs (1 hunks)
  • Web/Resgrid.Web.Services/Controllers/v4/DevicesController.cs (2 hunks)
  • Web/Resgrid.Web/Areas/User/Views/Profile/Devices.cshtml (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • Core/Resgrid.Config/ChatConfig.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:

  • Core/Resgrid.Model/Messages/StandardPushMessage.cs
  • Web/Resgrid.Web.Services/Controllers/v3/DevicesController.cs
  • Core/Resgrid.Model/PushUri.cs
  • Web/Resgrid.Web.Services/Controllers/v4/DevicesController.cs
  • Providers/Resgrid.Providers.Bus/UnitNotificationProvider.cs
  • Core/Resgrid.Services/PushService.cs
  • Providers/Resgrid.Providers.Bus/NotificationProvider.cs
  • Core/Resgrid.Model/Platforms.cs
  • Core/Resgrid.Model/Providers/Models/INovuProvider.cs
  • Providers/Resgrid.Providers.Messaging/NovuProvider.cs
🧬 Code Graph Analysis (5)
Web/Resgrid.Web.Services/Controllers/v4/DevicesController.cs (1)
Web/Resgrid.Web.Services/Controllers/v3/DevicesController.cs (1)
  • DevicesController (39-52)
Providers/Resgrid.Providers.Bus/UnitNotificationProvider.cs (2)
Providers/Resgrid.Providers.Bus/NotificationProvider.cs (1)
  • GetSoundFileNameFromType (371-424)
Providers/Resgrid.Providers.Messaging/NovuProvider.cs (1)
  • GetSoundFileNameFromType (313-365)
Core/Resgrid.Services/PushService.cs (1)
Core/Resgrid.Framework/Logging.cs (2)
  • Logging (15-178)
  • LogException (65-77)
Providers/Resgrid.Providers.Bus/NotificationProvider.cs (2)
Providers/Resgrid.Providers.Bus/UnitNotificationProvider.cs (1)
  • GetSoundFileNameFromType (400-460)
Providers/Resgrid.Providers.Messaging/NovuProvider.cs (1)
  • GetSoundFileNameFromType (313-365)
Providers/Resgrid.Providers.Messaging/NovuProvider.cs (5)
Core/Resgrid.Model/Providers/Models/INovuProvider.cs (11)
  • Task (20-20)
  • Task (31-31)
  • Task (40-40)
  • Task (49-49)
  • Task (58-58)
  • Task (67-67)
  • Task (82-83)
  • Task (90-90)
  • Task (92-92)
  • Task (94-94)
  • Task (96-96)
Core/Resgrid.Services/PushService.cs (5)
  • Task (36-53)
  • Task (55-60)
  • Task (62-80)
  • Task (82-87)
  • Task (94-124)
Core/Resgrid.Config/ChatConfig.cs (1)
  • ChatConfig (3-20)
Providers/Resgrid.Providers.Bus/NotificationProvider.cs (1)
  • GetSoundFileNameFromType (371-424)
Providers/Resgrid.Providers.Bus/UnitNotificationProvider.cs (1)
  • GetSoundFileNameFromType (400-460)
⏰ 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). (1)
  • GitHub Check: build
🔇 Additional comments (11)
Providers/Resgrid.Providers.Bus/NotificationProvider.cs (2)

319-319: LGTM: APNS payload sound selection uses iOS consistently

Correctly switches to iOS-based sound naming, aligning with the enum rename and APNS expectations.


371-424: LGTM: Unified iOS sound mapping across all branches

Consistent use of Platforms.iOS for CAF sound variants looks correct and matches related provider changes.

Core/Resgrid.Services/PushService.cs (2)

71-72: Method name inconsistency: UpdateUnitSubscriberApns vs UpdateUnitSubscriberFcm

The iOS update method is called UpdateUnitSubscriberApns while the Android method is UpdateUnitSubscriberFcm. For consistency with the user registration methods that use UpdateUserSubscriberApns, this is correct naming.


115-115: No NullReference risk for DepartmentCode in SendUserMessage
The SendUserMessage implementation in NovuProvider.cs (around line 301) uses

$"{depCode}_User_{userId}"

In C#, interpolating a null string yields an empty string rather than throwing, so passing a null departmentCode won’t cause a NullReferenceException. You can safely ignore the original null-reference concern.

Likely an incorrect or invalid review comment.

Providers/Resgrid.Providers.Messaging/NovuProvider.cs (7)

133-167: APNS subscriber update helper looks correct.

Good use of providerId = "apns" with deviceTokens and integrationIdentifier; correct headers and idempotency key.


206-210: Unit APNS wrapper correctly delegates to UpdateSubscriberApns.

This aligns with the providerId = "apns" path and uses the correct Unit APNS provider id.


111-111: LGTM: providerId comment for FCM.

The inline reference to Novu enums helps future maintainers.


317-317: Platform rename to iOS is consistent and correct.

Consistently using Platforms.iOS improves clarity across sound mapping and APNS payload.

Also applies to: 325-325, 333-333, 339-339, 346-346, 353-353, 360-360, 470-470


85-92: Including DeviceId in unit subscriber data is a solid improvement.

This metadata will help correlate devices to unit subscribers downstream.


296-309: New user notification wrappers are clear and cohesive.

The SendUserDispatch/Message/Notification helpers align with the INovuProvider contract and routing via depCode/userId.


240-251: Incorrect FCM priority recommendations – no changes needed

The existing override already follows Novu’s mapping to Firebase Admin (Node) types:

  • Top-level android.priority must be lowercase "high"/"normal" (not uppercase).
  • android.notification.priority (display priority) is a valid field and accepts "min" | "low" | "default" | "high" | "max".
  • Other fields (channelId, defaultSound, sticky) are correctly camelCased.

You can leave the code as-is:

android = new
{
    priority = androidChannelName == "calls" ? "high" : "normal",
    notification = new
    {
        channelId = type,
        defaultSound = true,
        sticky = androidChannelName == "calls",
        priority = androidChannelName == "calls" ? "max" : "default",
    },
    data = new
    {
        // …
    }
}

Likely an incorrect or invalid review comment.

Comment on lines +5 to +15
None = 0,
iOS = 1,
Android = 2,
Web = 3,
Windows8 = 4,
WindowsPhone8 = 5,
Blackberry = 6,
UnitIOS = 7,
UnitAndroid = 8,
UnitWin = 9
//Windows8 = 4,
//WindowsPhone8 = 5,
//Blackberry = 6,
//UnitIOS = 7,
//UnitAndroid = 8,
//UnitWin = 9
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Breaking enum value changes will corrupt persisted platform mappings and conflict with hard-coded checks

Previous code (and likely persisted DB values) use 1=iPad, 2=iPhone, 3=Android, 4=Windows8. This change sets iOS=1, Android=2, Web=3, which:

  • Mislabels existing devices in the UI (3 now renders as Web).
  • Breaks provider logic using numeric checks (e.g., NotificationProvider treats 3 as Android).

Preserve legacy numeric values and introduce new names without reassigning existing numbers. Optionally re-add legacy members marked [Obsolete] for compatibility.

Apply this diff to restore stable values and keep compatibility:

-        None            = 0,
-        iOS             = 1,
-        Android         = 2,
-        Web             = 3,
-        Windows8        = 4,
-        //Windows8       = 4,
+        None            = 0,
+        // Legacy Apple values retained for compatibility
+        [System.Obsolete("Use iOS (value 2) where possible. This remains for legacy records.")]
+        iPad            = 1,
+        // iOS aligns with former iPhone value (2)
+        iOS             = 2,
+        // Android retains legacy value (3)
+        Android         = 3,
+        Windows8        = 4,
+        // New platform should use an unused value to avoid collisions
+        Web             = 5,
         //WindowsPhone8  = 5,
         //Blackberry     = 6,
         //UnitIOS                = 7,
         //UnitAndroid    = 8,
         //UnitWin                = 9

If changing enum assignments is not feasible now, introduce centralized helpers to interpret legacy ints consistently and update UI/provider code to use them.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
None = 0,
iOS = 1,
Android = 2,
Web = 3,
Windows8 = 4,
WindowsPhone8 = 5,
Blackberry = 6,
UnitIOS = 7,
UnitAndroid = 8,
UnitWin = 9
//Windows8 = 4,
//WindowsPhone8 = 5,
//Blackberry = 6,
//UnitIOS = 7,
//UnitAndroid = 8,
//UnitWin = 9
None = 0,
// Legacy Apple values retained for compatibility
[System.Obsolete("Use iOS (value 2) where possible. This remains for legacy records.")]
iPad = 1,
// iOS aligns with former iPhone value (2)
iOS = 2,
// Android retains legacy value (3)
Android = 3,
Windows8 = 4,
// New platform should use an unused value to avoid collisions
Web = 5,
//WindowsPhone8 = 5,
//Blackberry = 6,
//UnitIOS = 7,
//UnitAndroid = 8,
//UnitWin = 9
🤖 Prompt for AI Agents
In Core/Resgrid.Model/Platforms.cs around lines 5 to 15, the enum numeric
assignments were changed which will corrupt persisted platform mappings and
break any numeric checks; restore the original numeric values for legacy members
(e.g., ensure 1=iPad/iOS legacy mapping, 2=iPhone/Android legacy mapping,
3=Android or previous legacy meaning, 4=Windows8) and reintroduce any removed
legacy members as distinct enum entries annotated with [Obsolete] pointing to
the new names so existing DB values and hard-coded numeric checks continue to
work; alternatively, if you cannot change enum values now, add a central
conversion helper that maps persisted ints to the new enum names and update
UI/provider code to call that helper everywhere instead of relying on raw
numeric comparisons.

Comment on lines +48 to +49
//if (((Platforms)PlatformType) == Platforms.Windows8 || ((Platforms)PlatformType) == Platforms.WindowsPhone7 || ((Platforms)PlatformType) == Platforms.WindowsPhone8 || ((Platforms)PlatformType) == Platforms.UnitWin)
// ChannelUri = new Uri(_pushLocation, UriKind.Absolute);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Remove commented-out code to maintain code cleanliness

Since Windows platform support is being phased out as part of this PR, the commented code should be removed rather than left as comments. This improves code maintainability and clarity.

-//if (((Platforms)PlatformType) == Platforms.Windows8 || ((Platforms)PlatformType) == Platforms.WindowsPhone7 || ((Platforms)PlatformType) == Platforms.WindowsPhone8 || ((Platforms)PlatformType) == Platforms.UnitWin)
-//	ChannelUri = new Uri(_pushLocation, UriKind.Absolute);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
//if (((Platforms)PlatformType) == Platforms.Windows8 || ((Platforms)PlatformType) == Platforms.WindowsPhone7 || ((Platforms)PlatformType) == Platforms.WindowsPhone8 || ((Platforms)PlatformType) == Platforms.UnitWin)
// ChannelUri = new Uri(_pushLocation, UriKind.Absolute);
🤖 Prompt for AI Agents
In Core/Resgrid.Model/PushUri.cs around lines 48-49, there is commented-out
legacy code handling Windows platform ChannelUri creation; remove these two
commented lines entirely to clean up dead code now that Windows platform support
is phased out, and ensure no remaining references depend on this commented
block.


try
{
await _novuProvider.SendUserDispatch(call.Title, call.SubTitle, userId, call.DepartmentCode, string.Format("C{0}", call.CallId), ConvertCallPriorityToSound((int)call.Priority, priority), true, call.ActiveCallCount, color);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Verify DepartmentCode is populated in StandardPushCall

The code passes call.DepartmentCode to SendUserDispatch, but StandardPushCall may not always have this property populated. Ensure it's properly set before this method is called.


🏁 Script executed:

#!/bin/bash
# Check StandardPushCall definition and usage to verify DepartmentCode is always set
ast-grep --pattern 'class StandardPushCall {
  $$$
  public string DepartmentCode { $$$; set; }
  $$$
}'

# Check where StandardPushCall is instantiated
rg -A 5 "new StandardPushCall"

Length of output: 1586


StandardPushCall.DepartmentCode is never set

In PushService.cs you pass call.DepartmentCode to SendUserDispatch, but all instantiations of StandardPushCall in CommunicationService.cs only assign DepartmentId. This means DepartmentCode remains null and downstream notifications won’t include the correct department code.

Please update each instantiation in
Core/Resgrid.Services/CommunicationService.cs
to also set DepartmentCode. For example:

     // existing assignment
     spc.DepartmentId = departmentId;
+    spc.DepartmentCode = departmentCode;   // ensure DepartmentCode is populated

Affected locations (3 occurrences of new StandardPushCall() → setting spc.DepartmentId):

  • Core/Resgrid.Services/CommunicationService.cs: around lines where spc.DepartmentId = … is called.
🤖 Prompt for AI Agents
In Core/Resgrid.Services/CommunicationService.cs around the three locations
where new StandardPushCall() is created and spc.DepartmentId is assigned, the
DepartmentCode property is never set causing null department codes downstream;
update each instantiation to also set spc.DepartmentCode to the correct
department code (e.g., the department object's Code/DepartmentCode or the source
call’s DepartmentCode value depending on available variables) so that
SendUserDispatch receives a valid DepartmentCode.

}
}
else if (pushUri.PlatformType == (int)Platforms.UnitAndroid)
if (pushUri.PlatformType == 8)//(int)Platforms.UnitAndroid)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Use enum values instead of hard-coded platform type numbers

The code uses hard-coded value 8 with a comment indicating it's for UnitAndroid. This should use the enum constant for better maintainability and type safety.

Based on the PR context, it appears platform enums are being simplified. If UnitAndroid is being removed, consider using the new Android enum value:

-if (pushUri.PlatformType == 8)//(int)Platforms.UnitAndroid)
+if (pushUri.PlatformType == (int)Platforms.Android)
🤖 Prompt for AI Agents
In Providers/Resgrid.Providers.Bus/UnitNotificationProvider.cs around line 69,
replace the hard-coded numeric literal `8` with the corresponding platform enum
constant (do not leave the commented hint). Update the condition to compare
against the enum member introduced by the refactor (for example
Platforms.UnitAndroid or the new Android enum value provided by the
Platform/Platforms enum), and ensure the correct namespace or using is present
so the code compiles; remove the inline comment and any now-stale numeric
references.

}
}
else if (pushUri.PlatformType == (int)Platforms.UnitIOS)
else if (pushUri.PlatformType == 7)//(int)Platforms.UnitIOS)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Use enum values instead of hard-coded platform type numbers

Similar to the Android check above, use the enum constant instead of hard-coded value 7.

-else if (pushUri.PlatformType == 7)//(int)Platforms.UnitIOS)
+else if (pushUri.PlatformType == (int)Platforms.iOS)
🤖 Prompt for AI Agents
In Providers/Resgrid.Providers.Bus/UnitNotificationProvider.cs around line 83,
the platform check uses a hard-coded literal `7`; replace this with the
appropriate enum constant (e.g., Platforms.UnitIOS) instead of the magic number.
Update the comparison to use the enum value (or cast if necessary to match the
pushUri.PlatformType type) so it reads the enum reference rather than `7`,
keeping consistency with the Android check above.

foreach (var registration in registrations)
{
if (pushUri.PlatformType == (int)Platforms.Android)
if (pushUri.PlatformType == 3 || pushUri.PlatformType == 8)//(int)Platforms.Android)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Use enum values for platform type comparisons

Multiple hard-coded platform type values (3, 8) are used. Replace with appropriate enum constants.

-if (pushUri.PlatformType == 3 || pushUri.PlatformType == 8)//(int)Platforms.Android)
+if (pushUri.PlatformType == (int)Platforms.Android)

Note: If value 3 represents a different platform that should still be supported, please verify and update accordingly.

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In Providers/Resgrid.Providers.Bus/UnitNotificationProvider.cs around line 107,
the code compares pushUri.PlatformType to hard-coded integers (3 and 8); replace
these numeric literals with the corresponding enum members (e.g.,
Platforms.Android for 8 if that is the intended mapping, and the correct enum
value for 3) so the comparison reads pushUri.PlatformType == (int)Platforms.X
or, better, cast pushUri.PlatformType to the enum and compare directly
(pushUri.PlatformTypeEnum == Platforms.X); verify the actual meaning of value 3
and update the enum member used accordingly to preserve intended behavior.

await hubClient.DeleteRegistrationAsync(registration);
}
else if (pushUri.PlatformType == (int)Platforms.iPad || pushUri.PlatformType == (int)Platforms.iPhone)
else if (pushUri.PlatformType == 1 || pushUri.PlatformType == 2 || pushUri.PlatformType == 7)//(int)Platforms.iPad || pushUri.PlatformType == (int)Platforms.iPhone)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Use enum values for platform type comparisons

Multiple hard-coded platform type values (1, 2, 7) are used. Replace with appropriate enum constants.

-else if (pushUri.PlatformType == 1 || pushUri.PlatformType == 2 || pushUri.PlatformType == 7)//(int)Platforms.iPad || pushUri.PlatformType == (int)Platforms.iPhone)
+else if (pushUri.PlatformType == (int)Platforms.iOS)

Note: Based on the PR context, platforms 1, 2, and 7 all represent iOS devices that have been consolidated.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
else if (pushUri.PlatformType == 1 || pushUri.PlatformType == 2 || pushUri.PlatformType == 7)//(int)Platforms.iPad || pushUri.PlatformType == (int)Platforms.iPhone)
@@ Providers/Resgrid.Providers.Bus/UnitNotificationProvider.cs (line 113)
- else if (pushUri.PlatformType == 1 || pushUri.PlatformType == 2 || pushUri.PlatformType == 7)//(int)Platforms.iPad || pushUri.PlatformType == (int)Platforms.iPhone)
+ else if (pushUri.PlatformType == (int)Platforms.iOS)
🤖 Prompt for AI Agents
In Providers/Resgrid.Providers.Bus/UnitNotificationProvider.cs around line 113,
replace the hard-coded numeric platform comparisons (1, 2, 7) with the
appropriate Platforms enum constants (e.g., Platforms.iPad, Platforms.iPhone and
the enum member representing the third iOS device value or the consolidated
Platforms.iOS if present) so the code reads comparisons against enum members
instead of literals; update the comment to reflect enum usage and prefer a
single consolidated enum check for iOS devices if your enum defines one.

Comment on lines 196 to 199
public async Task<bool> UpdateUserSubscriberApns(string userId, string code, string token)
{
return await UpdateSubscriberFcm($"{code}_User_{userId}", token, ChatConfig.NovuUnitApnsProviderId);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

UpdateUserSubscriberApns calls the FCM path and uses the Unit APNS provider id.

This should call UpdateSubscriberApns and use the responder/user APNS integration identifier (not the unit one). As-is, APNS tokens will be stored under the wrong provider, and the FCM updater won’t attach APNS tokens.

Proposed fix:

-		public async Task<bool> UpdateUserSubscriberApns(string userId, string code, string token)
-		{
-			return await UpdateSubscriberFcm($"{code}_User_{userId}", token, ChatConfig.NovuUnitApnsProviderId);
-		}
+		public async Task<bool> UpdateUserSubscriberApns(string userId, string code, string token)
+		{
+			return await UpdateSubscriberApns($"{code}_User_{userId}", token, ChatConfig.NovuResponderApnsProviderId);
+		}

Verify the ChatConfig field exists or add it:


🏁 Script executed:

#!/bin/bash
# Check if the responder APNS provider id is defined anywhere
rg -n "NovuResponderApnsProviderId" -S

# If not found, open Core/Resgrid.Config/ChatConfig.cs and add:
# public static string NovuResponderApnsProviderId = "";

Length of output: 38


🏁 Script executed:

#!/bin/bash
set -e

# Locate ChatConfig.cs
echo "Finding ChatConfig.cs..."
CONFIG_FILE=$(fd --ignore-case "ChatConfig\.cs$" || true)
if [[ -z "$CONFIG_FILE" ]]; then
  echo "ChatConfig.cs not found"
  exit 0
fi
echo "Found: $CONFIG_FILE"

# Show the first 200 lines of ChatConfig.cs
echo "----- ChatConfig.cs (1–200) -----"
sed -n '1,200p' "$CONFIG_FILE"

# List all Novu*ProviderId constants
echo "----- Novu*ProviderId constants -----"
rg -n "Novu.*ProviderId" -S

# Search for UpdateSubscriberApns and UpdateSubscriberFcm definitions
echo "----- UpdateSubscriberApns occurrences -----"
rg -n "UpdateSubscriberApns" -S || echo "None found"
echo "----- UpdateSubscriberFcm occurrences -----"
rg -n "UpdateSubscriberFcm" -S || echo "None found"

Length of output: 3499


Correct the APNS path and provider ID for user subscribers

The UpdateUserSubscriberApns method is currently invoking the FCM updater with the unit APNS provider ID. It must call the APNS updater with a responder-specific APNS provider. Additionally, we need to introduce the missing NovuResponderApnsProviderId constant in ChatConfig.cs.

Changes required:

  • In Providers/Resgrid.Providers.Messaging/NovuProvider.cs, update UpdateUserSubscriberApns to call UpdateSubscriberApns and switch to ChatConfig.NovuResponderApnsProviderId.
  • In Core/Resgrid.Config/ChatConfig.cs, add the NovuResponderApnsProviderId field.

Diff for NovuProvider.cs:

--- a/Providers/Resgrid.Providers.Messaging/NovuProvider.cs
+++ b/Providers/Resgrid.Providers.Messaging/NovuProvider.cs
@@ -196,7 +196,7 @@
 		public async Task<bool> UpdateUserSubscriberApns(string userId, string code, string token)
 		{
-			return await UpdateSubscriberFcm($"{code}_User_{userId}", token, ChatConfig.NovuUnitApnsProviderId);
+			return await UpdateSubscriberApns($"{code}_User_{userId}", token, ChatConfig.NovuResponderApnsProviderId);
 		}

Snippet to add in ChatConfig.cs (inside the ChatConfig class):

public static string NovuResponderApnsProviderId = "";

Please implement these updates to ensure APNS tokens are stored under the correct provider.

🤖 Prompt for AI Agents
In Providers/Resgrid.Providers.Messaging/NovuProvider.cs around lines 196-199,
change the method UpdateUserSubscriberApns to call UpdateSubscriberApns (not
UpdateSubscriberFcm) and pass ChatConfig.NovuResponderApnsProviderId as the
provider id so APNS tokens use the responder APNS provider; and in
Core/Resgrid.Config/ChatConfig.cs add a public static string
NovuResponderApnsProviderId = "" inside the ChatConfig class to introduce the
missing constant.

else if (registrationInput.Plt == (int)Platforms.Android)
push.PushLocation = "Android";
else if (registrationInput.Plt == (int)Platforms.iPhone || registrationInput.Plt == (int)Platforms.iPad)
else if (registrationInput.Plt == 1 || registrationInput.Plt == 2)//(int)Platforms.iPhone || registrationInput.Plt == (int)Platforms.iPad)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Use platform enum values consistently instead of hard-coded integers

The code uses hard-coded numeric values (1 and 2) for platform comparison, while the rest of the codebase uses the Platforms enum. This makes the code less maintainable and harder to understand.

-else if (registrationInput.Plt == 1 || registrationInput.Plt == 2)//(int)Platforms.iPhone || registrationInput.Plt == (int)Platforms.iPad)
+else if (registrationInput.Plt == (int)Platforms.iOS)

Note: Based on the PR context, the platform enum has been simplified to iOS=1, Android=2. The old iPhone and iPad values have been consolidated into iOS.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
else if (registrationInput.Plt == 1 || registrationInput.Plt == 2)//(int)Platforms.iPhone || registrationInput.Plt == (int)Platforms.iPad)
else if (registrationInput.Plt == (int)Platforms.iOS)
🤖 Prompt for AI Agents
In Web/Resgrid.Web.Services/Controllers/v3/DevicesController.cs around line 81,
replace the hard-coded numeric platform checks (registrationInput.Plt == 1 ||
registrationInput.Plt == 2) with comparisons that use the Platforms enum (e.g.,
check registrationInput.Plt against (int)Platforms.iOS and/or
(int)Platforms.Android or cast registrationInput.Plt to Platforms and compare to
Platforms.iOS/Platforms.Android); update the condition to use the enum values so
the intent is clear and future changes to the enum remain consistent.

Comment on lines +157 to +158
var department = await _departmentsService.GetDepartmentByIdAsync(DepartmentId, false);
push.PushLocation = department.Code;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Add null check for department retrieval

The code assumes that GetDepartmentByIdAsync will always return a valid department object. If it returns null, accessing department.Code will throw a NullReferenceException.

 var department = await _departmentsService.GetDepartmentByIdAsync(DepartmentId, false);
+if (department == null)
+{
+    result.Status = ResponseHelper.Failure;
+    return result;
+}
 push.PushLocation = department.Code;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
var department = await _departmentsService.GetDepartmentByIdAsync(DepartmentId, false);
push.PushLocation = department.Code;
var department = await _departmentsService.GetDepartmentByIdAsync(DepartmentId, false);
if (department == null)
{
result.Status = ResponseHelper.Failure;
return result;
}
push.PushLocation = department.Code;
🤖 Prompt for AI Agents
In Web/Resgrid.Web.Services/Controllers/v4/DevicesController.cs around lines
157-158, the code uses department.Code without checking if department is null;
add a null check after awaiting
_departmentsService.GetDepartmentByIdAsync(DepartmentId, false) and handle the
null case (e.g., return an appropriate IActionResult like NotFound/BadRequest,
or set push.PushLocation to a safe default/null and log a warning) to avoid a
NullReferenceException.

@ucswift
Copy link
Member Author

ucswift commented Aug 14, 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 3edc042 into master Aug 14, 2025
4 of 6 checks passed
@coderabbitai coderabbitai bot mentioned this pull request Aug 27, 2025
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