Skip to content

Conversation

@jaypanchal-13
Copy link
Contributor

Product Description

Updated model as per /messaging/retrieve_notifications/ api changes

Technical Summary

Feature Flag

Safety Assurance

Safety story

Automated test coverage

QA Plan

Labels and Review

  • Do we need to enhance the manual QA test coverage ? If yes, the "QA Note" label is set correctly
  • Does the PR introduce any major changes worth communicating ? If yes, the "Release Note" label is set and a "Release Note" is specified in PR description.
  • Risk label is set correctly
  • The set of people pinged as reviewers is appropriate for the level of risk of the change

@coderabbitai
Copy link

coderabbitai bot commented Oct 1, 2025

📝 Walkthrough

Walkthrough

The 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
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested labels

cross requested

Suggested reviewers

  • Jignesh-dimagi
  • OrangeAndGreen
  • shubham1g5

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Description Check ⚠️ Warning The description only fills the Product Description section and leaves the Technical Summary, Feature Flag, Safety Assurance (including safety story), Automated test coverage, and QA Plan entirely blank, so it does not adhere to the required template structure. Please populate the Technical Summary with ticket links and design rationale, specify any applicable Feature Flag, detail your Safety story including data impact and testing confidence, add Automated test coverage information, outline the QA Plan, and confirm that labels and reviewer checklist items are addressed.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (1 passed)
Check name Status Explanation
Title Check ✅ Passed The title succinctly identifies the primary change—updating the PushNotificationRecord model to match the notification API response—and does so in a clear, single sentence without extraneous details.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch pn-model-changes

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b6b0609 and d11bf74.

📒 Files selected for processing (1)
  • app/src/org/commcare/android/database/connect/models/PushNotificationRecord.kt (2 hunks)
⏰ 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

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

try {
val obj = jsonArray.getJSONObject(i)
val record = PushNotificationRecord().apply {
notificationId = obj.optString(META_NOTIFICATION_ID,"")
Copy link
Contributor

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

Comment on lines 88 to 89
title = obj.optString(META_TITLE,"")
body = obj.optString(META_BODY,"")
Copy link
Contributor

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,"")
Copy link
Contributor

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

Comment on lines 105 to 106
Logger.exception("Error parsing notification at index $i", e)
throw RuntimeException("Error parsing notification at index $i", e)
Copy link
Contributor

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 {
Copy link
Contributor

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.

Copy link
Contributor

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 = ""
Copy link
Contributor

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?

Copy link
Contributor Author

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

records.add(record)
} catch (e: JSONException) {
Logger.exception("Error parsing push notification", e)
throw RuntimeException("Error parsing push notification history", e)
Copy link
Contributor

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).

Copy link
Contributor

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.

Copy link
Contributor

@Jignesh-dimagi Jignesh-dimagi Oct 3, 2025

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

Copy link
Contributor

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.

Copy link
Contributor

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.

@shubham1g5 shubham1g5 added this to the 2.60 milestone Oct 6, 2025
Comment on lines 113 to 117
} catch (e: JSONException) {
val corruptedJson = obj?.toString() ?: "Unknown JSON"
val errorMessage = "Corrupt pn at index $i: $corruptedJson"
Logger.exception(errorMessage, e)
}
Copy link
Contributor

@shubham1g5 shubham1g5 Oct 6, 2025

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" }
Copy link
Contributor

Choose a reason for hiding this comment

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

this should use optStringSafe ?

shubham1g5
shubham1g5 previously approved these changes Oct 6, 2025
@shubham1g5
Copy link
Contributor

@OrangeAndGreen @Jignesh-dimagi blocked on your reviews and flagging that we want this to get in for 2.60 to avoid a DB migration.

@shubham1g5
Copy link
Contributor

@jaypanchal-13 Looks like code is failing to compile and build is failing because of that.

@jaypanchal-13
Copy link
Contributor Author

@jaypanchal-13 Looks like code is failing to compile and build is failing because of that.

@shubham1g5 yes once approved by both. Will intiate rebuild if still fails

@shubham1g5
Copy link
Contributor

yes once approved by both. Will intiate rebuild if still fails

@jaypanchal-13 Code must compile before a review

@jaypanchal-13
Copy link
Contributor Author

jaypanchal-13 commented Oct 6, 2025

yes once approved by both. Will intiate rebuild if still fails

@jaypanchal-13 Code must compile before a review

@shubham1g5 Compiling successfully locally

@shubham1g5
Copy link
Contributor

@jaypanchal-13 Can you doube check ? I tried building the branch and it's failing straight away -
Screenshot 2025-10-06 at 12 19 21 PM

@jaypanchal-13
Copy link
Contributor Author

@jaypanchal-13 Can you doube check ? I tried building the branch and it's failing straight away - Screenshot 2025-10-06 at 12 19 21 PM

@shubham1g5 yes pushing that file too

Comment on lines 89 to 91
notificationId = obj.getRequiredString(META_NOTIFICATION_ID, i)
title = obj.getRequiredString(META_TITLE, i)
body = obj.getRequiredString(META_BODY, i)
Copy link
Contributor

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

Copy link
Contributor Author

@jaypanchal-13 jaypanchal-13 Oct 6, 2025

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

Copy link
Contributor

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.

Comment on lines 12 to 14
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")
Copy link
Contributor

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) {
Copy link
Contributor

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

Copy link
Contributor Author

@jaypanchal-13 jaypanchal-13 Oct 6, 2025

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

Copy link
Contributor

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.

OrangeAndGreen
OrangeAndGreen previously approved these changes Oct 6, 2025
@shubham1g5 shubham1g5 added the skip-integration-tests Skip android tests. label Oct 6, 2025
@shubham1g5 shubham1g5 merged commit 03da90a into master Oct 6, 2025
4 of 7 checks passed
@shubham1g5 shubham1g5 deleted the pn-model-changes branch October 6, 2025 17:10
@coderabbitai coderabbitai bot mentioned this pull request Oct 21, 2025
@coderabbitai coderabbitai bot mentioned this pull request Nov 6, 2025
4 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

skip-integration-tests Skip android tests.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants