-
-
Notifications
You must be signed in to change notification settings - Fork 45
Separated the logic for channel keys in worker #3397
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 our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
📝 WalkthroughWalkthroughThis pull request refactors the missing encryption channel key handling workflow. Previously, the RetrieveNotificationsResponseParser would detect missing channel keys and trigger background retrieval directly. The changes introduce a dedicated WorkManager-based worker, MessagingChannelsKeySyncWorker, that runs after successful notification retrieval to scan for consented channels with empty keys and retrieve their encryption keys. When key retrieval succeeds, the worker schedules an immediate push notification retrieval with a 3-second delay. Supporting infrastructure updates include adding a delay parameter to the NotificationsSyncWorkerManager scheduling API and new scheduling functions in PushNotificationApiHelper. Sequence DiagramsequenceDiagram
participant NR as Notification<br/>Retrieval
participant Helper as PushNotificationApi<br/>Helper
participant Manager as NotificationsSyncWorker<br/>Manager
participant Sync as MessagingChannelsKey<br/>SyncWorker
participant MM as MessageManager
NR->>Helper: Push notification retrieval succeeds
Helper->>Manager: scheduleMessagingChannelsKeySync()
Manager->>Sync: Enqueue WorkManager task
Sync->>Sync: Fetch consented channels<br/>with empty keys
Sync->>MM: getChannelEncryptionKey()<br/>for each channel
alt Keys retrieved
Sync->>Manager: schedulePushNotificationRetrievalWith<br/>(delay: 3000ms)
Manager->>NR: Schedule next retrieval
else No keys needed
Sync->>Sync: Complete successfully
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes
Possibly related PRs
Suggested reviewers
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (1 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
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
🧹 Nitpick comments (2)
app/src/org/commcare/pn/workers/MessagingChannelsKeySyncWorker.kt (2)
17-22: Refactor to useforEachand simplify flag logic.The current implementation uses
mapfor side effects and sets the flag inside the lambda. This can be simplified for better readability:Apply this diff:
- var needReloadDueToMissingChannelKey = false existingChannels?.let { - existingChannels.filter { it.consented && it.key.isEmpty() }.map { - needReloadDueToMissingChannelKey = true + val channelsNeedingKeys = existingChannels.filter { it.consented && it.key.isEmpty() } + channelsNeedingKeys.forEach { MessageManager.getChannelEncryptionKey(context, it, null) } + if (channelsNeedingKeys.isNotEmpty()) { + schedulePushNotificationRetrievalWith(CommCareApplication.instance(), 3000) + } } - if (needReloadDueToMissingChannelKey) { - schedulePushNotificationRetrievalWith(CommCareApplication.instance(), 3000) - }
24-24: Consider extracting the hardcoded delay as a constant.The 3000ms (3 second) delay is hardcoded. Consider extracting this as a named constant at the class or companion object level for maintainability and clarity.
Apply this diff:
+ companion object { + private const val NOTIFICATION_RETRIEVAL_DELAY_MS = 3000L + } + override suspend fun doWork(): Result { val existingChannels = ConnectMessagingDatabaseHelper.getMessagingChannels(context) var needReloadDueToMissingChannelKey = false existingChannels?.let { existingChannels.filter { it.consented && it.key.isEmpty() }.map { needReloadDueToMissingChannelKey = true MessageManager.getChannelEncryptionKey(context, it, null) } } if (needReloadDueToMissingChannelKey) { - schedulePushNotificationRetrievalWith(CommCareApplication.instance(), 3000) + schedulePushNotificationRetrievalWith(CommCareApplication.instance(), NOTIFICATION_RETRIEVAL_DELAY_MS) } return Result.success() }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
app/src/org/commcare/connect/network/connectId/parser/RetrieveNotificationsResponseParser.kt(0 hunks)app/src/org/commcare/pn/workermanager/NotificationsSyncWorkerManager.kt(4 hunks)app/src/org/commcare/pn/workers/MessagingChannelsKeySyncWorker.kt(1 hunks)app/src/org/commcare/utils/PushNotificationApiHelper.kt(4 hunks)
💤 Files with no reviewable changes (1)
- app/src/org/commcare/connect/network/connectId/parser/RetrieveNotificationsResponseParser.kt
🧰 Additional context used
🧠 Learnings (5)
📓 Common learnings
Learnt from: shubham1g5
Repo: dimagi/commcare-android PR: 0
File: :0-0
Timestamp: 2025-05-08T11:08:18.530Z
Learning: PR #3048 "Phase 4 Connect PR" introduces a substantial feature called "Connect" to the CommCare Android app, which includes messaging, job management, delivery tracking, payment processing, authentication flows, and learning modules. It follows a modern architecture using Navigation Components with three navigation graphs, segregated business logic in Manager classes, and proper database persistence.
Learnt from: OrangeAndGreen
Repo: dimagi/commcare-android PR: 3248
File: app/src/org/commcare/connect/MessageManager.java:0-0
Timestamp: 2025-07-29T14:54:47.940Z
Learning: User OrangeAndGreen organizes messaging-related changes into separate PRs rather than mixing them with other Connect work, which aligns with their preference for handling code issues in separate PRs when they are not directly related to the main changes.
Learnt from: OrangeAndGreen
Repo: dimagi/commcare-android PR: 3248
File: app/src/org/commcare/connect/MessageManager.java:0-0
Timestamp: 2025-07-29T14:45:13.470Z
Learning: In the CommCare Android Connect messaging system (MessageManager.java), the error handling logic in updateChannelConsent's processFailure callback intentionally attempts to retrieve the channel encryption key even after consent update failure. This behavior was recommended by the server team and the worst case scenario is silently failing to retrieve the encryption key, which is considered acceptable.
Learnt from: Jignesh-dimagi
Repo: dimagi/commcare-android PR: 0
File: :0-0
Timestamp: 2025-05-08T13:40:19.645Z
Learning: PR #3048 introduces a comprehensive messaging system in the Connect feature, implementing secure encryption using AES-GCM for message content, proper channel management with consent flows, and a well-designed UI separation between sent and received messages with real-time notification integration.
📚 Learning: 2025-02-19T15:15:01.935Z
Learnt from: pm-dimagi
Repo: dimagi/commcare-android PR: 2956
File: app/src/org/commcare/fragments/connectMessaging/ConnectMessageChannelListFragment.java:58-60
Timestamp: 2025-02-19T15:15:01.935Z
Learning: Error handling for message retrieval in ConnectMessageChannelListFragment's retrieveMessages callback is not required as per user preference.
Applied to files:
app/src/org/commcare/utils/PushNotificationApiHelper.kt
📚 Learning: 2025-07-29T14:45:13.470Z
Learnt from: OrangeAndGreen
Repo: dimagi/commcare-android PR: 3248
File: app/src/org/commcare/connect/MessageManager.java:0-0
Timestamp: 2025-07-29T14:45:13.470Z
Learning: In the CommCare Android Connect messaging system (MessageManager.java), the error handling logic in updateChannelConsent's processFailure callback intentionally attempts to retrieve the channel encryption key even after consent update failure. This behavior was recommended by the server team and the worst case scenario is silently failing to retrieve the encryption key, which is considered acceptable.
Applied to files:
app/src/org/commcare/utils/PushNotificationApiHelper.ktapp/src/org/commcare/pn/workers/MessagingChannelsKeySyncWorker.kt
📚 Learning: 2025-05-08T11:08:18.530Z
Learnt from: shubham1g5
Repo: dimagi/commcare-android PR: 0
File: :0-0
Timestamp: 2025-05-08T11:08:18.530Z
Learning: PR #3048 "Phase 4 Connect PR" introduces a substantial feature called "Connect" to the CommCare Android app, which includes messaging, job management, delivery tracking, payment processing, authentication flows, and learning modules. It follows a modern architecture using Navigation Components with three navigation graphs, segregated business logic in Manager classes, and proper database persistence.
Applied to files:
app/src/org/commcare/pn/workers/MessagingChannelsKeySyncWorker.kt
📚 Learning: 2025-05-08T13:40:19.645Z
Learnt from: Jignesh-dimagi
Repo: dimagi/commcare-android PR: 0
File: :0-0
Timestamp: 2025-05-08T13:40:19.645Z
Learning: PR #3048 introduces a comprehensive messaging system in the Connect feature, implementing secure encryption using AES-GCM for message content, proper channel management with consent flows, and a well-designed UI separation between sent and received messages with real-time notification integration.
Applied to files:
app/src/org/commcare/pn/workers/MessagingChannelsKeySyncWorker.kt
🧬 Code graph analysis (1)
app/src/org/commcare/pn/workers/MessagingChannelsKeySyncWorker.kt (1)
app/src/org/commcare/pn/workermanager/NotificationsSyncWorkerManager.kt (1)
schedulePushNotificationRetrievalWith(105-113)
⏰ 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: Lint Code Base
🔇 Additional comments (10)
app/src/org/commcare/utils/PushNotificationApiHelper.kt (4)
4-8: LGTM!The new imports are necessary for the WorkManager-based channel key sync functionality.
Also applies to: 30-30, 32-32
38-39: LGTM!The constants are well-named and the 3-minute backoff delay is appropriate for network-based retry operations.
68-68: Verify the scheduling frequency is appropriate.The channel key sync is scheduled after every successful push notification retrieval. While the
ExistingWorkPolicy.KEEPprevents duplicate work, ensure this scheduling pattern aligns with your expected key synchronization frequency.
153-170: LGTM!The WorkManager configuration is appropriate:
- Network constraint ensures connectivity for key retrieval
KEEPpolicy prevents duplicate work when notifications are retrieved frequently- Exponential backoff handles transient failures gracefully
app/src/org/commcare/pn/workermanager/NotificationsSyncWorkerManager.kt (3)
196-210: LGTM!The delay parameter is properly added with a sensible default of 0, maintaining backward compatibility while enabling delayed execution.
244-276: LGTM!The delay parameter is correctly applied to the work request using
setInitialDelay, and the default value of 0 preserves existing behavior.
105-113: ****The concern about an uninitialized
notificationsPayloadfield is unfounded. The call on line 107 invokes the secondary constructor (lines 137–145) which acceptsList<PushNotificationRecord>?as the second parameter. This constructor properly initializesnotificationsPayloadon line 143 viaconvertPNRecordsToPayload(pnsRecords), even whenpnsRecordsisnull. Additionally,startPersonalIdNotificationsWorker(lines 196–210) accepts the notification map as a direct parameter and never accesses the instance field. No initialization issue exists.Likely an incorrect or invalid review comment.
app/src/org/commcare/pn/workers/MessagingChannelsKeySyncWorker.kt (3)
1-10: LGTM!The imports are appropriate for the worker implementation.
20-20: Verify error handling for key retrieval.The result of
MessageManager.getChannelEncryptionKeyis not captured or checked. If key retrieval fails, the worker won't be aware. Confirm this is the intended behavior or consider logging failures for observability.Based on learnings
24-24: Verify use ofCommCareApplication.instance()instead of worker context.Line 24 uses
CommCareApplication.instance()instead of the worker'scontextparameter. While both might work, using the worker's context is generally preferred for testability and consistency. Confirm this choice is intentional.
| existingChannels?.let { | ||
| existingChannels.filter { it.consented && it.key.isEmpty() }.map { | ||
| needReloadDueToMissingChannelKey = true | ||
| MessageManager.getChannelEncryptionKey(context, it, null) |
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.
think needReloadDueToMissingChannelKey should only be set when we are able to fetch the key successfully.
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.
Done 6f420fa
|
@damagatchi retest this please |
| } | ||
|
|
||
| fun scheduleImmediatePushNotificationRetrieval(context: Context) { | ||
| fun schedulePushNotificationRetrievalWith(context: Context, delay: Long = 0) { |
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.
Nit: Should we call this delayMs so the units are clear? Seems like an easy place for a caller to make a mistake and assume seconds
| notificationPayload: Map<String, String>, | ||
| showNotification: Boolean = this.showNotification | ||
| showNotification: Boolean = this.showNotification, | ||
| delay:Long=0 |
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.
Same nit
| uniqueWorkName: String, | ||
| showNotification: Boolean = this.showNotification | ||
| showNotification: Boolean = this.showNotification, | ||
| delay: Long = 0 |
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.
Same
| MessageManager.getChannelEncryptionKey(context, it, object : | ||
| ConnectActivityCompleteListener{ | ||
| override fun connectActivityComplete(success: Boolean) { | ||
| if(success)schedulePushNotificationRetrievalWith(context,3000) |
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.
Nit: This feels overly collapsed but I'm still getting used to Kotlin formatting so maybe it's okay?
My inclination would be to have the if(success) on its own line, and { } enclosing the inner code
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.
I believe the best practice is what you described @OrangeAndGreen
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.
Oooh I almost forgot that this is a thing:
https://kotlinlang.org/docs/coding-conventions.html
I'm not sure if this particular case is described in there, but this is a good reference for Kotlin in general
838f4fa
Technical Summary
https://dimagi.atlassian.net/browse/CCCT-1887
Labels and Review