Uh oh!
There was an error while loading.Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork73
CU-868f7hkrj Fixed Novu provider for ios and bugfix for voice.#237
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.
Already on GitHub?Sign in to your account
Uh oh!
There was an error while loading.Please reload this page.
Conversation
Thanks for opening this, but we'd appreciate a little more information. Could you update it with more details? |
coderabbitaibot commentedAug 17, 2025 • edited
Loading Uh oh!
There was an error while loading.Please reload this page.
edited
Uh oh!
There was an error while loading.Please reload this page.
WalkthroughConverted outbound voice provider API from synchronous to asynchronous returning Task, updated CommunicationService to await it, implemented async Twilio calls in the Number provider, reworked NovuProvider notification payloads (added apns overrides and simplified sound naming), and added a NovuResponderApnsProviderId field to ChatConfig. Changes
Sequence Diagram(s)sequenceDiagram participant Caller as CommunicationService.SendCallAsync participant Voice as IOutboundVoiceProvider participant Carrier as Telephony API Caller->>Voice: CommunicateCallAsync(deptNumber, profile, call) activate Voice Voice->>Carrier: CreateCallAsync(normalizedNumber, call details) Carrier-->>Voice: result (success/failure) deactivate Voice Voice-->>Caller: Task<bool> (success/failure)sequenceDiagram participant App as AnyService participant Novu as NovuProvider participant Push as Novu API App->>Novu: SendNotification(eventCode, data) Novu->>Novu: Build payload (channelName, fcm/android/apns overrides, sound .wav) Novu->>Push: POST /events/trigger with payloadString Push-->>Novu: HTTP response Novu-->>App: ResultEstimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Poem
📜 Recent review detailsConfiguration used: CodeRabbit UI 💡 Knowledge Base configuration:
You can enable these sources in your CodeRabbit configuration. 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
⏰ 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)
✨ Finishing Touches
🧪 Generate unit tests
🪧 TipsChatThere are 3 ways to chat withCodeRabbit:
SupportNeed help? Create a ticket on oursupport page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
CodeRabbit Configuration File ( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
Actionable comments posted: 0
🔭 Outside diff range comments (2)
Providers/Resgrid.Providers.Messaging/NovuProvider.cs (2)
198-201:User APNS provider ID appears incorrect (uses Unit provider ID).
UpdateUserSubscriberApnspassesChatConfig.NovuUnitApnsProviderIdfor a user subscriber. GivenUpdateUserSubscriberFcmusesNovuResponderFcmProviderId, the APNS counterpart should likely be the responder/user APNS provider ID, not the unit one. This likely mis-associates user devices to the unit integration in Novu.Apply this diff if
ChatConfig.NovuResponderApnsProviderIdexists:- return await UpdateSubscriberApns($"{code}_User_{userId}", token, ChatConfig.NovuUnitApnsProviderId);+ return await UpdateSubscriberApns($"{code}_User_{userId}", token, ChatConfig.NovuResponderApnsProviderId);If the responder APNS provider ID isn’t defined in ChatConfig, please confirm the intended value and adjust accordingly.
320-349:Restore iOS .caf sound files in NovuProvider and honor enableCustomSoundsThe other providers still emit
.caffor iOS while NovuProvider now hard-codes only.wav, which will break any iOS app bundling.cafassets. Also, theenableCustomSoundsflag is passed into your public API but never used—consider falling back to a generic “beep” when it’s disabled.• File: Providers/Resgrid.Providers.Messaging/NovuProvider.cs
Method: GetSoundFileNameFromType(Platforms platform, string type)Diff suggestion:
private string GetSoundFileNameFromType(Platforms platform, string type) {+ // iOS assets are .caf; others use .wav+ var ext = platform == Platforms.iOS ? ".caf" : ".wav"; if (type == ((int)PushSoundTypes.CallEmergency).ToString()) {- return "callemergency.wav";+ return "callemergency" + ext; } else if (type == ((int)PushSoundTypes.CallHigh).ToString()) {- return "callhigh.wav";+ return "callhigh" + ext; } else if (type == ((int)PushSoundTypes.CallMedium).ToString()) {- return "callmedium.wav";+ return "callmedium" + ext; } else if (type == ((int)PushSoundTypes.CallLow).ToString()) {- return "calllow.wav";+ return "calllow" + ext; } else if (type == ((int)PushSoundTypes.Notifiation).ToString()) {- return "notification.wav";+ return "notification" + ext; } else if (type == ((int)PushSoundTypes.Message).ToString()) {- return "message.wav";+ return "message" + ext; } else {- return $"{type}.wav";+ return $"{type}{ext}"; } }Also, consider updating this method signature to accept the existing
enableCustomSoundsparameter and, when it’sfalse, return a generic"beep"sound (e.g."beep.caf"on iOS,"beep.wav"elsewhere).
🧹 Nitpick comments (6)
Providers/Resgrid.Providers.Messaging/NovuProvider.cs (3)
352-359:Unused helper (FormatForAndroidNativePush) and stale Android sound handling.This helper appears unused after the refactor and the new FCM overrides no longer set a custom sound for Android (only defaultSound). If custom Android sounds are desired when enableCustomSounds is true, this path needs to be wired back in; otherwise, remove the unused helper to reduce dead code.
377-467:Legacy payload builders likely dead code; consider removal or alignment.CreateAndroidNotification and CreateAppleNotification seem to be legacy builders that aren’t used after moving to Novu “overrides”. Keeping both shapes increases drift and confusion.
- Either remove these builders if definitively unused, or
- Reuse them to assemble the override payload to ensure one source of truth for fields, sound, priorities, and category mapping.
21-58:Prefer IHttpClientFactory or a shared HttpClient for resiliency and testability.Multiple methods new-up HttpClient and set common headers. Consider injecting IHttpClientFactory (or an HttpClient) and centralizing common headers to:
- Avoid socket exhaustion from frequent instantiation
- Improve testability (mock handlers)
- Consolidate auth/idempotency header logic
I can draft a minimal refactor using IHttpClientFactory if desired.
Core/Resgrid.Services/CommunicationService.cs (1)
205-213:Awaiting the voice call is correct; consider handling the boolean result.You’re now awaiting the async voice call, but discarding the boolean. If the provider indicates failure (returns false), you could log or take fallback action.
Example:
- var success = await _outboundVoiceProvider.CommunicateCallAsync(...);
- if (!success) Logging.LogWarning("Outbound voice call failed for {UserId}/{CallId}");
Providers/Resgrid.Providers.Number/OutboundVoiceProvider.cs (2)
42-44:Consider basing success on Twilio response and catching exceptions here.Right now the method returns true immediately after CreateAsync. If Twilio returns an error (exception thrown) it will bubble up; if it succeeds but Twilio marks the call failed quickly, you still return true. Optionally:
- Wrap Twilio CreateAsync in try/catch here and return false on exception, logging the error.
- Check phoneCall.Status or ErrorCode to decide success (best-effort).
I can provide a diff adding try/catch and minimal status validation if you want stricter semantics for the boolean.
Also applies to: 56-58
33-59:Minor duplication between mobile/home call paths.The two branches differ only by the target number accessor. Extracting a small local function to create the call reduces duplication and centralizes options assembly.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (4)
Core/Resgrid.Model/Providers/IOutboundVoiceProvider.cs(1 hunks)Core/Resgrid.Services/CommunicationService.cs(1 hunks)Providers/Resgrid.Providers.Messaging/NovuProvider.cs(5 hunks)Providers/Resgrid.Providers.Number/OutboundVoiceProvider.cs(4 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/IOutboundVoiceProvider.csCore/Resgrid.Services/CommunicationService.csProviders/Resgrid.Providers.Number/OutboundVoiceProvider.csProviders/Resgrid.Providers.Messaging/NovuProvider.cs
🧬 Code Graph Analysis (2)
Providers/Resgrid.Providers.Number/OutboundVoiceProvider.cs (2)
Core/Resgrid.Model/Providers/IOutboundVoiceProvider.cs (1)
Task(7-7)Core/Resgrid.Config/NumberProviderConfig.cs (1)
NumberProviderConfig(5-54)
Providers/Resgrid.Providers.Messaging/NovuProvider.cs (2)
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 (6)
Providers/Resgrid.Providers.Messaging/NovuProvider.cs (2)
241-249:Verify FCM override property names and priority values.In the overrides you’re setting:
- android.priority = "high"/"normal" (OK for FCM legacy)
- notification.priority = "max"/"default"
- notification.channelId (camelCase)
- notification.defaultSound
FCM (v1) typically expects:
- notification.priority as an enum (PRIORITY_MAX/DEFAULT) or numeric
notification_priorityin some schemas- notification.channel_id (snake_case)
- sound as "default" or a file name (not defaultSound)
Given this is Novu’s overrides format, please verify the expected schema. If Novu passes these straight to FCM, the current keys/values may be ignored.
If Novu expects snake_case and FCM-aligned fields, consider:
- channelId = type,- defaultSound = true,+ channel_id = type,+ default_sound = true, sticky = channelName == "calls" ? true : false,- priority = channelName == "calls" ? "max" : "default",+ notification_priority = channelName == "calls" ? 5 : 3,Please validate against Novu’s documentation for fcm overrides; if their schema expects the current keys, ignore this suggestion. I can help adjust once we confirm the correct schema.
260-271:APNS override shape: confirm placement of custom fields and sound format.You added an apns block with badge, sound (name/critical/volume), category, type, and eventCode. Two concerns:
- Custom fields like type and eventCode may need to be top-level (outside aps) but still inside apns payload, or under a specific payload node, depending on Novu’s schema.
- Sound naming: you’re using GetSoundFileNameFromType(Platforms.iOS, type) which now returns .wav (see below). If the iOS app bundles .caf files (common), .wav may not exist and sound won’t play.
Please confirm Novu’s APNS override schema and ensure the app bundles the sound files with matching extensions. If the app uses .caf, revert to .caf for iOS (see suggestion below).
Core/Resgrid.Model/Providers/IOutboundVoiceProvider.cs (2)
7-7:Async, result-bearing signature is a good move.Switching to Task enables callers to await and react to success/failure. API surface looks appropriate.
1-9:No lingering CommunicateCall usages found—migration to CommunicateCallAsync is complete
I ran the provided searches and confirmed:
- No calls to the old CommunicateCall(...)
- Only the new CommunicateCallAsync(...) exists in the interface, implementation, and call sites
All synchronous references have been removed.
Providers/Resgrid.Providers.Number/OutboundVoiceProvider.cs (2)
15-16:Async conversion and Task return look good.This aligns with the interface change and enables proper awaiting upstream.
25-33:Verify E.164 normalization and plus-prefixing.Twilio expects E.164; current logic only adds a leading '+' for some 11-digit numbers. If numbers can enter as 10 digits (US local) or lack '+', consider central normalization to a canonical format before this provider (preferred), or enforce here.
If normalization isn’t guaranteed upstream, add a conservative guard, e.g., prepend '+' when already in international format. I can propose a helper if needed.
There was a problem hiding this 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
🔭 Outside diff range comments (1)
Providers/Resgrid.Providers.Messaging/NovuProvider.cs (1)
320-349:Restore platform‐specific sound file extensions in NovuProvider.GetSoundFileNameFromTypeThe current implementation always returns “.wav”, which will break iOS custom sounds (other providers use “.caf” on iOS and “.wav”/“.mp3” on Android). Update as follows:
• File: Providers/Resgrid.Providers.Messaging/NovuProvider.cs
Method: GetSoundFileNameFromType (lines 320–349)Apply this diff to match NotificationProvider/UnitNotificationProvider mapping:
private string GetSoundFileNameFromType(Platforms platform, string type) {- if (type == ((int)PushSoundTypes.CallEmergency).ToString())- {- return "callemergency.wav";- }+ if (type == ((int)PushSoundTypes.CallEmergency).ToString())+ {+ if (platform == Platforms.iOS) return "callemergency.caf";+ return "callemergency.wav";+ } else if (type == ((int)PushSoundTypes.CallHigh).ToString()) {- return "callhigh.wav";+ if (platform == Platforms.iOS) return "callhigh.caf";+ return "callhigh.mp3"; } else if (type == ((int)PushSoundTypes.CallMedium).ToString()) {- return "callmedium.wav";+ if (platform == Platforms.iOS) return "callmedium.caf";+ return "callmedium.mp3"; } else if (type == ((int)PushSoundTypes.CallLow).ToString()) {- return "calllow.wav";+ if (platform == Platforms.iOS) return "calllow.caf";+ return "calllow.mp3"; } else if (type == ((int)PushSoundTypes.Notifiation).ToString()) {- return "notification.wav";+ if (platform == Platforms.iOS) return "notification.caf";+ return "notification.mp3"; } else if (type == ((int)PushSoundTypes.Message).ToString()) {- return "message.wav";+ if (platform == Platforms.iOS) return "message.caf";+ return "message.mp3"; } else {- return $"{type}.wav";+ if (platform == Platforms.iOS) return $"{type}.caf";+ return $"{type}.mp3"; } }If your iOS bundle only contains “.wav” assets, adjust the mapping accordingly to avoid missing sound files.
🧹 Nitpick comments (2)
Providers/Resgrid.Providers.Messaging/NovuProvider.cs (2)
4-4:Remove unused using RabbitMQ.ClientRabbitMQ.Client isn’t referenced in this file. Drop the import to keep things tidy.
Apply this diff:
-using RabbitMQ.Client;
263-267:enableCustomSounds parameter is ignored for APNSThe SendNotification signature includes enableCustomSounds, but APNS sound is always set to a custom file. If you intended to honor the flag (e.g., use system default when false), gate the sound block accordingly.
Apply this diff to conditionally include the sound block:
- sound = new- {- name = GetSoundFileNameFromType(Platforms.iOS, type),- critical = channelName == "calls" ? 1 : 0,- volume = 1.0f- },+ sound = enableCustomSounds ? new+ {+ name = GetSoundFileNameFromType(Platforms.iOS, type),+ critical = channelName == "calls" ? 1 : 0,+ volume = 1.0f+ } : null,Also set NullValueHandling.Ignore to avoid emitting null sound.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (2)
Core/Resgrid.Config/ChatConfig.cs(1 hunks)Providers/Resgrid.Providers.Messaging/NovuProvider.cs(6 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.Config/ChatConfig.csProviders/Resgrid.Providers.Messaging/NovuProvider.cs
🧬 Code Graph Analysis (1)
Providers/Resgrid.Providers.Messaging/NovuProvider.cs (3)
Core/Resgrid.Config/ChatConfig.cs (1)
ChatConfig(3-21)Providers/Resgrid.Providers.Bus/UnitNotificationProvider.cs (1)
GetSoundFileNameFromType(400-460)Providers/Resgrid.Providers.Bus/NotificationProvider.cs (1)
GetSoundFileNameFromType(371-424)
⏰ 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 (4)
Core/Resgrid.Config/ChatConfig.cs (1)
16-16:Config key addition aligns with provider usage; ensure it's populated at runtimeThe new NovuResponderApnsProviderId matches the new usage in NovuProvider. Please ensure this is wired into your configuration (appsettings, env vars, secrets) in all environments—an empty string will cause Novu credential updates to fail.
Would you like me to scan the repo for where ChatConfig values are populated and open a follow-up item if this one isn’t being set?
Providers/Resgrid.Providers.Messaging/NovuProvider.cs (3)
200-201:Correct provider ID for User APNS updatesGood catch switching to NovuResponderApnsProviderId; this aligns user APNS tokens with the responder integration.
Confirm that Core/Resgrid.Config/ChatConfig.NovuResponderApnsProviderId is set for all environments; otherwise APNS token updates will silently fail (HTTP 4xx).
225-225:Rename to channelName improves clarityUsing channelName consistently improves readability across the FCM/iOS branching logic.
280-284:Serialize once and reuse: good optimizationSerializing the payload once into payloadString simplifies the code and avoids repeated serialization work.
Uh oh!
There was an error while loading.Please reload this page.
| { | ||
| badge=count, | ||
| sound=new | ||
| { | ||
| name=GetSoundFileNameFromType(Platforms.iOS,type), | ||
| critical=channelName=="calls"?1:0, | ||
| volume=1.0f | ||
| }, | ||
| type=type, | ||
| category=channelName, | ||
| eventCode=eventCode, | ||
| }, | ||
There was a problem hiding this comment.
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 APNS override shape matches Novu’s schema
The overrides.apns block sets badge/sound/category directly. Novu’s schema for APNS overrides may expect an aps payload wrapper (e.g., overrides.apns.payload.aps.{badge,sound,category}) and custom fields (eventCode/type) outside aps. If this shape is off, APNS may ignore the fields.
Would you verify against Novu’s overrides schema and example for APNS?
🌐 Web query:
What is the expected JSON schema for the "overrides.apns" payload in Novu's v1 events/trigger API? Provide an example including badge, sound with critical/volume, and category.💡 Result:
The expected JSON schema for theoverrides.apns payload inNovu's v1 events/trigger API should closely follow theApnsConfig structure used by Firebase Cloud Messaging (FCM), as Novu internally uses this format for push overrides[1][3]. This allows you to specify detailed APNs (Apple Push Notification Service) payload options, such asbadge,sound (with critical and volume), andcategory.
Example JSON Structure
{"overrides": {"apns": {"payload": {"aps": {"badge":5,"sound": {"critical":1,"name":"default","volume":0.8 },"category":"MY_CATEGORY" } } } }}Key fields explanation:
badge
Integer. Specifies the badge number for the app icon.sound
Object. Includes:"name": String, the sound file name ("default" or custom)."critical": Integer (1 for critical alert, 0 for non-critical)."volume": Float (0.0 to 1.0).
category
String. Used for grouping or to enable specific notification actions.
Schema Reference
The schema should match:
{"overrides": {"apns": {"payload": {"aps": {"badge":<integer>,"sound": {"critical":<integer>, // 0 or 1"name":<string>,"volume":<float> // 0.0 through 1.0 },"category":<string> } } } }}- Wrap these settings in theaps payload object as per APNs and FCM standards[1][3].
- Additional APNs fields (e.g.,
alert,thread-id, etc.) can also be provided in theapsdictionary.
Notes
- Critical sound notifications require the app to have the proper entitlement.
- Novu processes this JSON as-is and relays it to APNs using the FCM or relevant provider integration[1][3].
For further customizations or to validate your payload, refer to theApnsConfig documentation[3].
Citations:
- 1:https://novu-preview.mintlify.app/integrations/providers/push/fcm
- 2:https://github.com/code/app-novu
- 3:https://docs.novu.co/platform/integrations/push/fcm
- 4:https://docs.novu.co/platform/integrations/push/push-webhook
- 5:http://secure.lavasoft.com/mylavasoft/malware-descriptions/blog/SpyToolWin32Ardamaxd24f367079
Adjust APNS override to include thepayload.aps wrapper
The currentapns object is not nested underpayload.aps as required by Novu’s schema. Badge, sound, and category must live insidepayload.aps, with custom fields (type,eventCode) alongsideaps.
Please update in Providers/Resgrid.Providers.Messaging/NovuProvider.cs (lines 259–271):
- apns = new- {- badge = count,- sound = new- {- name = GetSoundFileNameFromType(Platforms.iOS, type),- critical = channelName == "calls" ? 1 : 0,- volume = 1.0f- },- type = type,- category = channelName,- eventCode = eventCode,- },+ apns = new+ {+ payload = new+ {+ aps = new+ {+ badge = count,+ sound = new+ {+ name = GetSoundFileNameFromType(Platforms.iOS, type),+ critical = channelName == "calls" ? 1 : 0,+ volume = 1.0f+ },+ category = channelName+ },+ type = type,+ eventCode = eventCode+ }+ },
This aligns with the expected JSON:
"overrides": {"apns": {"payload": {"aps": {"badge":5,"sound": {"critical":1,"name":"default","volume":0.8 },"category":"MY_CATEGORY" },"type":"<your_type>","eventCode":"<your_eventCode>" } }}
Approve |
There was a problem hiding this 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.
963535b intomasterUh oh!
There was an error while loading.Please reload this page.
Uh oh!
There was an error while loading.Please reload this page.
Summary by CodeRabbit
New Features
Improvements
Refactor