-
-
Notifications
You must be signed in to change notification settings - Fork 45
PushNotificationRecord model changes as per notification api response #3353
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
📝 WalkthroughWalkthroughThe Pull Request updates PushNotificationRecord by converting multiple numeric fields to String, adjusting nullability and defaults, and renaming date_time to timeStamp with a new META_TIME_STAMP constant. It adds a Boolean readStatus. A companion object method fromJsonArray(JSONArray) is introduced to parse a JSON array into PushNotificationRecord instances, extracting nested “data” fields (connectMessageId, channel, action, opportunityId) and handling errors via Logger and RuntimeException. Necessary imports for Logger, JSONArray, and JSONException are added. Sequence Diagram(s)sequenceDiagram
autonumber
actor Caller
participant Model as PushNotificationRecord
participant JSON as JSONArray
participant Item as JSONObject
participant Data as data JSONObject
Caller->>Model: fromJsonArray(JSONArray)
activate Model
Model->>JSON: iterate items
loop For each item
JSON-->>Model: JSONObject
Model->>Item: read fields (notificationId, type, title, body,\nMETA_TIME_STAMP, confirmationStatus, paymentId, readStatus)
alt Has nested "data"
Item-->>Model: data JSONObject
Model->>Data: read connectMessageId, channel, action, opportunityId
else No "data"
Model-->>Model: use defaults
end
Model-->>Caller: accumulate PushNotificationRecord
end
deactivate Model
Note over Model: On parse error: log via Logger and throw RuntimeException
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
Suggested labels
Suggested reviewers
Pre-merge checks and finishing touches❌ Failed checks (2 warnings)
✅ Passed checks (1 passed)
✨ Finishing touches
🧪 Generate unit tests
📜 Recent review detailsConfiguration used: CodeRabbit UI Review profile: CHILL Plan: Pro 📒 Files selected for processing (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)
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 |
| try { | ||
| val obj = jsonArray.getJSONObject(i) | ||
| val record = PushNotificationRecord().apply { | ||
| notificationId = obj.optString(META_NOTIFICATION_ID,"") |
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.
@jaypanchal-13 Notification id is mandatory field and should raise exception if not available
| title = obj.optString(META_TITLE,"") | ||
| body = obj.optString(META_BODY,"") |
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.
@jaypanchal-13 title, body is mandatory field and should raise exception if not available
| dataObj?.let { | ||
| connectMessageId = it.optString(META_MESSAGE_ID,"") | ||
| channel = it.optString(META_CHANNEL,"") | ||
| action = it.optString(META_ACTION,"") |
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.
@jaypanchal-13 action is mandatory field and should raise exception if not available
| Logger.exception("Error parsing notification at index $i", e) | ||
| throw RuntimeException("Error parsing notification at index $i", e) |
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.
@jaypanchal-13 Only log Error parsing push notification history
| return records | ||
| } | ||
|
|
||
| private fun getRequiredString(obj: JSONObject, key: String, index: Int): String { |
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 think this should be defined in JsonExtensions.kt instead so it can also be used by other parsers as we make more things officially required.
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.
+1
| @MetaField(META_DATE_TIME) | ||
| var dateTime: String? = null | ||
| @MetaField(META_TIME_STAMP) | ||
| var timeStamp: String = "" |
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 think the name for this should be more descriptive. Thinking something like createdDate, sentDate, receivedDate, expirationDate. A name like "dateTime" or "timestamp" tells us the data type, but not it's function.
On that note though, shouldn't this field be a Date object instead of a String?
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.
Yes changing it to Date
app/src/org/commcare/android/database/connect/models/PushNotificationRecord.kt
Show resolved
Hide resolved
| records.add(record) | ||
| } catch (e: JSONException) { | ||
| Logger.exception("Error parsing push notification", e) | ||
| throw RuntimeException("Error parsing push notification history", e) |
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.
For @shubham1g5 and @Jignesh-dimagi to confirm:
I don't think we should throw a RuntimeException here, as we've discussed that one failed item in a list of received items should not cause the app to completely crash and block the user.
I think we should either ignore the corrupt item (aside from logging it), or have a "corrupt item" placeholder to show to the user (as we've implemented in other places).
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 I would be fine either way on this given it's a non core workflow and users can arrive at the feature screens from other pathways.
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 guess we should keep it as it will give us confirmation that nothing is wrong in newly build functionality and user are able to use it without any issues. Open to the suggestions
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.
Maybe I'm mistaken, but won't the RuntimeException crash the app anytime we receive a corrupt notification item? It seems this call could be made somewhat frequently and without direct intention by the user (like to auto-refresh when the app starts or a page loads), and crashing due to one bad item could prevent the user from performing other work.
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.
@OrangeAndGreen That's a good point. @jaypanchal-13 I think in that case, please handle it gracefully and report error to user.
| } catch (e: JSONException) { | ||
| val corruptedJson = obj?.toString() ?: "Unknown JSON" | ||
| val errorMessage = "Corrupt pn at index $i: $corruptedJson" | ||
| Logger.exception(errorMessage, e) | ||
| } |
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.
can we have this method throw JsonException as it is instead (no catch) and have callers decide whether they want to swallow vs re-throw. I don't think it's alright for us to swallow this mehtod always for example on notification UI screen where we should be just crashing the app instead.
| } | ||
|
|
||
| fun JSONObject.getRequiredString(key: String, index: Int): String { | ||
| return optString(key, "").takeIf { it.isNotBlank() && 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.
this should use optStringSafe ?
|
@OrangeAndGreen @Jignesh-dimagi blocked on your reviews and flagging that we want this to get in for 2.60 to avoid a DB migration. |
|
@jaypanchal-13 Looks like code is failing to compile and build is failing because of that. |
|
@jaypanchal-13 Code must compile before a review |
|
|
@jaypanchal-13 Can you doube check ? I tried building the branch and it's failing straight away - |
|
| notificationId = obj.getRequiredString(META_NOTIFICATION_ID, i) | ||
| title = obj.getRequiredString(META_TITLE, i) | ||
| body = obj.getRequiredString(META_BODY, i) |
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.
@jaypanchal-13 We don't want to crash the application if strings are not present as pointed out by @OrangeAndGreen. Let this method throw JSONException and calling function handle it depending upon the screen as @shubham1g5 mentioned earlier
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.
@Jignesh-dimagi I think @OrangeAndGreen was referring for fields which are not mandadory for pn and notificationId title body are mandadory fields. So did not removed crash part for it cc: @shubham1g5
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.
@jaypanchal-13 Let the decision for crashing / not crashing or handling the error be taken by calling screen. This is due to the fact that this API might be called at multiple places and if crashed, it will not allow user to work on any important task like opportunity. Yeah but when user on PN activity, app should crash exclusively.
| fun JSONObject.getRequiredString(key: String, index: Int): String { | ||
| return this.optStringSafe(key)?.takeIf { it.isNotBlank() && it != "null" } | ||
| ?: throw RuntimeException("$key is missing at index $index") |
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.
@jaypanchal-13 We can remove it completely
| incoming.id = existing.id | ||
| } | ||
| storage.write(incoming) | ||
| } catch (e: SQLException) { |
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.
we should not be catching this as well
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.
@shubham1g5 catching here with purpose of getting boolean true/false. Not throwing exception. What do you suggest
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 will still opt for not catching it, app should crash in situations like this.


Product Description
Updated model as per
/messaging/retrieve_notifications/api changesTechnical Summary
Feature Flag
Safety Assurance
Safety story
Automated test coverage
QA Plan
Labels and Review