Update DefaultAppEventPayloadHandler.java#246
Conversation
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughRenamed internal handler to Changes
Sequence Diagram(s)sequenceDiagram
participant Client
participant Handler as DefaultAppEventPayloadHandler
participant Firestore
Client->>Handler: send AppEventPayload
Handler->>Handler: validate cr_user_id, app_id, collection, timestamp
alt invalid
Handler->>Handler: log error, abort
else valid
Handler->>Handler: inspect collection
alt user_session_data
Handler->>Firestore: add session document (cr_user_id, app_id, collection, timestamp, data)
Firestore-->>Handler: success / failure
Handler->>Handler: log result
else summary_data
Handler->>Firestore: query doc by cr_user_id & app_id (limit 1)
alt document found
Firestore-->>Handler: existing doc
Handler->>Handler: mergeData(existing.data, payload.data, options)
Handler->>Firestore: update document with merged data
Firestore-->>Handler: success / failure
Handler->>Handler: log result
else no document / query error
Firestore-->>Handler: no result / error
Handler->>Firestore: create new summary document
Firestore-->>Handler: success / failure
Handler->>Handler: log result
end
end
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 1 | ❌ 3❌ Failed checks (2 warnings, 1 inconclusive)
✅ Passed checks (1 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ 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.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
app/src/main/java/org/curiouslearning/container/core/subapp/handler/DefaultAppEventPayloadHandler.java (1)
36-44:⚠️ Potential issue | 🟡 MinorInconsistent validation for
timestampfield.The validation checks
trim().isEmpty()forcr_user_id,app_id, andcollection, but only checks!= nullfortimestamp. If blank timestamps should also be rejected, add the same trim check for consistency.🔧 Proposed fix for consistent validation
if (payload.cr_user_id == null || payload.cr_user_id.trim().isEmpty() || payload.app_id == null || payload.app_id.trim().isEmpty() || payload.collection == null || payload.collection.trim().isEmpty() || - payload.timestamp == null) { + payload.timestamp == null || payload.timestamp.trim().isEmpty()) {🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/src/main/java/org/curiouslearning/container/core/subapp/handler/DefaultAppEventPayloadHandler.java` around lines 36 - 44, The payload validation in DefaultAppEventPayloadHandler currently checks payload.cr_user_id, payload.app_id, and payload.collection for null or blank but only checks payload.timestamp for null; update the validation to also reject blank/empty timestamps by adding the same trim().isEmpty() check for payload.timestamp (i.e., ensure payload.timestamp != null && !payload.timestamp.trim().isEmpty()) alongside the other field checks and keep the same Log.e(TAG, ...) return behavior if the validation fails.
🧹 Nitpick comments (1)
app/src/main/java/org/curiouslearning/container/core/subapp/handler/DefaultAppEventPayloadHandler.java (1)
55-58: Unchecked cast ofpayload.datatoMap<String, Object>.The cast
(Map<String, Object>) payload.datais unchecked and could fail at runtime if the Map has incompatible generic types. Consider adding a@SuppressWarnings("unchecked")annotation with a comment explaining why it's safe, or perform more defensive type checking.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/src/main/java/org/curiouslearning/container/core/subapp/handler/DefaultAppEventPayloadHandler.java` around lines 55 - 58, The unchecked cast of payload.data to Map<String, Object> in DefaultAppEventPayloadHandler is unsafe; replace the direct cast with defensive evidence: if payload.data instanceof Map<?,?> then iterate the entries and copy only those with String keys into a new HashMap<String,Object> (checking entry.getKey() instanceof String), otherwise create an empty map; alternatively, if you opt to keep the cast, add a targeted `@SuppressWarnings`("unchecked") on the smallest scope (e.g., a local helper method) with a comment explaining why the payload contract guarantees a Map<String,Object> to silence the warning. Ensure you reference the local variable name data and payload.data when making the change.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Outside diff comments:
In
`@app/src/main/java/org/curiouslearning/container/core/subapp/handler/DefaultAppEventPayloadHandler.java`:
- Around line 36-44: The payload validation in DefaultAppEventPayloadHandler
currently checks payload.cr_user_id, payload.app_id, and payload.collection for
null or blank but only checks payload.timestamp for null; update the validation
to also reject blank/empty timestamps by adding the same trim().isEmpty() check
for payload.timestamp (i.e., ensure payload.timestamp != null &&
!payload.timestamp.trim().isEmpty()) alongside the other field checks and keep
the same Log.e(TAG, ...) return behavior if the validation fails.
---
Nitpick comments:
In
`@app/src/main/java/org/curiouslearning/container/core/subapp/handler/DefaultAppEventPayloadHandler.java`:
- Around line 55-58: The unchecked cast of payload.data to Map<String, Object>
in DefaultAppEventPayloadHandler is unsafe; replace the direct cast with
defensive evidence: if payload.data instanceof Map<?,?> then iterate the entries
and copy only those with String keys into a new HashMap<String,Object> (checking
entry.getKey() instanceof String), otherwise create an empty map; alternatively,
if you opt to keep the cast, add a targeted `@SuppressWarnings`("unchecked") on
the smallest scope (e.g., a local helper method) with a comment explaining why
the payload contract guarantees a Map<String,Object> to silence the warning.
Ensure you reference the local variable name data and payload.data when making
the change.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: b98134f0-b018-4315-afeb-6e73f62efee3
📒 Files selected for processing (1)
app/src/main/java/org/curiouslearning/container/core/subapp/handler/DefaultAppEventPayloadHandler.java
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (2)
app/src/main/java/org/curiouslearning/container/core/subapp/handler/DefaultAppEventPayloadHandler.java (2)
78-81: Unchecked cast warning.The cast
(Map<String, Object>) payload.dataproduces an unchecked cast warning sincepayload.datais typed asObject. This pattern repeats increateNewSummaryDocandmergeData. Consider adding a@SuppressWarnings("unchecked")annotation to acknowledge this is intentional, or extract a helper method for safer conversion.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/src/main/java/org/curiouslearning/container/core/subapp/handler/DefaultAppEventPayloadHandler.java` around lines 78 - 81, The unchecked cast from payload.data to Map<String, Object> in DefaultAppEventPayloadHandler (used in the shown block and also in createNewSummaryDoc and mergeData) should be made explicit: either add `@SuppressWarnings`("unchecked") to the containing method(s) (or the class) around the cast sites, or extract a small helper like safeToMap(Object data) that checks instanceof Map and returns a new HashMap<>((Map<String,Object>) data) after annotating that helper with `@SuppressWarnings`("unchecked"); update createNewSummaryDoc and mergeData to call that helper so the unchecked cast is centralized and the compiler warning is resolved.
55-58: Log message is misleading for non-summary collections.The log says "Handling summary_data payload" but this branch handles all collections that aren't
"user_session_data", not just"summary_data". Consider making the log message more accurate.Suggested fix
} else { - Log.d(TAG, "Handling summary_data payload"); + Log.d(TAG, "Handling payload for collection=" + payload.collection); storeSummaryPayload(db, payload); }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/src/main/java/org/curiouslearning/container/core/subapp/handler/DefaultAppEventPayloadHandler.java` around lines 55 - 58, The log message in DefaultAppEventPayloadHandler's else branch incorrectly states "Handling summary_data payload" even though it handles any collection other than "user_session_data"; update the Log.d(TAG, ...) call to use a generic or dynamic message (e.g., include the actual payload collection name or say "Handling non-user_session_data payload") so it accurately reflects the branch, and ensure storeSummaryPayload(db, payload) remains invoked as before.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In
`@app/src/main/java/org/curiouslearning/container/core/subapp/handler/DefaultAppEventPayloadHandler.java`:
- Around line 142-153: The addOnFailureListener block in
DefaultAppEventPayloadHandler currently creates a new summary document on any
query failure, which can produce duplicates; modify the failure handling in the
query's addOnFailureListener so it does not unconditionally call
createNewSummaryDoc(db, payload, record). Instead, generate a deterministic
document ID (for example derived from payload.cr_user_id + payload.app_id +
payload.collection) or perform a safe existence check (a get() by that
deterministic ID or a transactional read) before calling createNewSummaryDoc,
and/or implement a retry with exponential backoff for transient errors; update
the logic around the query failure handler and the createNewSummaryDoc
invocation to use that deterministic-id or guarded create to prevent duplicates.
---
Nitpick comments:
In
`@app/src/main/java/org/curiouslearning/container/core/subapp/handler/DefaultAppEventPayloadHandler.java`:
- Around line 78-81: The unchecked cast from payload.data to Map<String, Object>
in DefaultAppEventPayloadHandler (used in the shown block and also in
createNewSummaryDoc and mergeData) should be made explicit: either add
`@SuppressWarnings`("unchecked") to the containing method(s) (or the class) around
the cast sites, or extract a small helper like safeToMap(Object data) that
checks instanceof Map and returns a new HashMap<>((Map<String,Object>) data)
after annotating that helper with `@SuppressWarnings`("unchecked"); update
createNewSummaryDoc and mergeData to call that helper so the unchecked cast is
centralized and the compiler warning is resolved.
- Around line 55-58: The log message in DefaultAppEventPayloadHandler's else
branch incorrectly states "Handling summary_data payload" even though it handles
any collection other than "user_session_data"; update the Log.d(TAG, ...) call
to use a generic or dynamic message (e.g., include the actual payload collection
name or say "Handling non-user_session_data payload") so it accurately reflects
the branch, and ensure storeSummaryPayload(db, payload) remains invoked as
before.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 7392c3e7-1425-499b-8c16-3cc98a82ddfe
📒 Files selected for processing (1)
app/src/main/java/org/curiouslearning/container/core/subapp/handler/DefaultAppEventPayloadHandler.java
Updated merge logic to handle add/replace oprations
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
app/src/main/java/org/curiouslearning/container/core/subapp/handler/DefaultAppEventPayloadHandler.java (1)
39-47:⚠️ Potential issue | 🟠 MajorReject unsupported
datashapes before routing.
storeUserSessionPayload()turns non-Mappayloads into{}on Lines 78-83, andmergeData()ignores non-Mapupdates on Lines 189-190. With the current guard, those events are still accepted and logged, but their body is silently dropped. Either validatepayload.data instanceof Maphere for collections that require object data, or preserve the rawpayload.datain theuser_session_datapath instead of replacing it with an empty map.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/src/main/java/org/curiouslearning/container/core/subapp/handler/DefaultAppEventPayloadHandler.java` around lines 39 - 47, DefaultAppEventPayloadHandler currently accepts events whose payload.data is not a Map but downstream storeUserSessionPayload() coerces those to {} and mergeData() ignores non-Map updates, causing silent data loss for user_session_data; update the validation in DefaultAppEventPayloadHandler to explicitly reject events where payload.data is not a Map for collections that require object data (or alternatively preserve the raw payload.data for the user_session_data path), e.g., add a check referencing payload.data instanceof Map and return/log an error when false for the user_session_data collection, or modify storeUserSessionPayload() to preserve non-Map payload.data instead of replacing it with an empty Map so mergeData() can handle it appropriately.
♻️ Duplicate comments (1)
app/src/main/java/org/curiouslearning/container/core/subapp/handler/DefaultAppEventPayloadHandler.java (1)
104-153:⚠️ Potential issue | 🟠 MajorThis query-then-create/update flow can still fork duplicate summary docs.
The read, empty-check, and write are separate operations. Two clients can both observe “no record” and both call
createNewSummaryDoc(...), and the failure branch on Lines 142-152 also creates a new doc for any transient query error. After that,limit(1)only updates one copy. Use a deterministic document ID for the logical summary record, or move this to a Firestore transaction on a single document reference instead of query +add().🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/src/main/java/org/curiouslearning/container/core/subapp/handler/DefaultAppEventPayloadHandler.java` around lines 104 - 153, The current query-then-create/update in DefaultAppEventPayloadHandler can produce duplicate summary docs; replace the query + conditional createNewSummaryDoc flow with a single-document deterministic write or a Firestore transaction on a single document reference (e.g., use a deterministic document ID derived from payload.cr_user_id + payload.app_id + payload.collection) instead of calling db.collection(payload.collection).add(...) in createNewSummaryDoc; if you prefer transactions, use db.runTransaction(...) targeting db.collection(payload.collection).document(deterministicId) and perform read/mergeData(existingDoc, payload) and set(record) atomically, and ensure the failure branch that currently calls createNewSummaryDoc also writes to the same deterministic document reference or runs the same transaction so transient query errors do not create duplicates.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In
`@app/src/main/java/org/curiouslearning/container/core/subapp/handler/DefaultAppEventPayloadHandler.java`:
- Around line 215-223: The add branch in DefaultAppEventPayloadHandler currently
sums numbers via n1.doubleValue() + n2.doubleValue() which forces all numeric
fields to Double and can lose integer precision; change the logic in the "add"
case (the block that computes and does merged.put(key, ...)) to detect when both
operands are integral types (e.g., instances of Long/Integer/Short/Byte) and
perform the addition using longValue() and store a Long (or the widest integral
type present), otherwise fall back to doubleValue() and store a Double;
alternatively (and preferably for Firestore-backed state), replace the in-place
numeric addition with Firestore's FieldValue.increment(...) for atomic numeric
updates; update the code paths around merged.put(key, ...) in
DefaultAppEventPayloadHandler to preserve integral types or use
FieldValue.increment as appropriate.
---
Outside diff comments:
In
`@app/src/main/java/org/curiouslearning/container/core/subapp/handler/DefaultAppEventPayloadHandler.java`:
- Around line 39-47: DefaultAppEventPayloadHandler currently accepts events
whose payload.data is not a Map but downstream storeUserSessionPayload() coerces
those to {} and mergeData() ignores non-Map updates, causing silent data loss
for user_session_data; update the validation in DefaultAppEventPayloadHandler to
explicitly reject events where payload.data is not a Map for collections that
require object data (or alternatively preserve the raw payload.data for the
user_session_data path), e.g., add a check referencing payload.data instanceof
Map and return/log an error when false for the user_session_data collection, or
modify storeUserSessionPayload() to preserve non-Map payload.data instead of
replacing it with an empty Map so mergeData() can handle it appropriately.
---
Duplicate comments:
In
`@app/src/main/java/org/curiouslearning/container/core/subapp/handler/DefaultAppEventPayloadHandler.java`:
- Around line 104-153: The current query-then-create/update in
DefaultAppEventPayloadHandler can produce duplicate summary docs; replace the
query + conditional createNewSummaryDoc flow with a single-document
deterministic write or a Firestore transaction on a single document reference
(e.g., use a deterministic document ID derived from payload.cr_user_id +
payload.app_id + payload.collection) instead of calling
db.collection(payload.collection).add(...) in createNewSummaryDoc; if you prefer
transactions, use db.runTransaction(...) targeting
db.collection(payload.collection).document(deterministicId) and perform
read/mergeData(existingDoc, payload) and set(record) atomically, and ensure the
failure branch that currently calls createNewSummaryDoc also writes to the same
deterministic document reference or runs the same transaction so transient query
errors do not create duplicates.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 0946b3d6-1642-47dc-b467-ff039275d2ac
📒 Files selected for processing (1)
app/src/main/java/org/curiouslearning/container/core/subapp/handler/DefaultAppEventPayloadHandler.java
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In
`@app/src/main/java/org/curiouslearning/container/core/subapp/handler/DefaultAppEventPayloadHandler.java`:
- Around line 76-79: DefaultAppEventPayloadHandler currently discards non-Map
AppEventPayload.data by replacing it with an empty Map; update handling so
unsupported shapes are not silently dropped: either validate and reject non-Map
payloads with an explicit exception (e.g., throw IllegalArgumentException with
clear message) or persist payload.data as-is instead of normalizing to an empty
Map. Modify the code around the Map construction (the snippet using payload.data
instanceof Map) to check the runtime type of AppEventPayload.data and: if your
design requires only maps, validate and return an error; otherwise assign the
original payload.data to the record being stored (or wrap scalars/lists into a
field that preserves their shape). Apply the same change to the analogous blocks
at the other two sites referenced (around lines 160-163 and 187-191) so all
handlers consistently reject or preserve non-Map payloads.
- Around line 102-138: The current read-then-add flow in
DefaultAppEventPayloadHandler (the Query "query" and subsequent query.get()
branch that calls createNewSummaryDoc or
db.collection(...).document(existingDoc.getId()).set(...)) is racy; replace it
with a deterministic document ID (e.g., hash or concatenation of
payload.cr_user_id, payload.app_id, payload.collection) and perform an atomic
upsert against that document instead of querying+adding. Concretely: compute
docId from payload, use DocumentReference docRef =
db.collection(payload.collection).document(docId), then either run a Firestore
transaction that reads and merges via mergeData and writes the merged record, or
call docRef.set(record, SetOptions.merge())/docRef.set(record) as appropriate to
upsert atomically; apply the same change where the other summary-creation code
path (the other occurrence handling payload) is implemented so duplicate docs
cannot be created.
- Around line 48-57: The current handler in DefaultAppEventPayloadHandler treats
any non-empty payload.collection other than "user_session_data" as summary
storage; change this to an explicit allowlist: only accept "user_session_data"
and the exact summary collection name (e.g., "summary_data"), and if
payload.collection is anything else return/throw an error and do not call
storeUserSessionPayload or storeSummaryPayload. Update the conditional to check
equality against the allowed names (referencing payload.collection,
storeUserSessionPayload, storeSummaryPayload) and ensure the handler logs and
returns an error response for unsupported collection names so writes are
rejected rather than routed to an unintended Firestore collection.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 0258adf7-66b0-4473-9669-c7989826bcf8
📒 Files selected for processing (1)
app/src/main/java/org/curiouslearning/container/core/subapp/handler/DefaultAppEventPayloadHandler.java
| Query query = db.collection(payload.collection) | ||
| .whereEqualTo("cr_user_id", payload.cr_user_id) | ||
| .whereEqualTo("app_id", payload.app_id) | ||
| .limit(1); | ||
|
|
||
| query.get() | ||
| .addOnSuccessListener(querySnapshot -> { | ||
|
|
||
| Map<String, Object> record = new HashMap<>(); | ||
| record.put("cr_user_id", payload.cr_user_id); | ||
| record.put("app_id", payload.app_id); | ||
| record.put("collection", payload.collection); | ||
| record.put("timestamp", payload.timestamp); | ||
|
|
||
| Map<String, Object> mergedData = | ||
| mergeData(existingDoc, payload); | ||
| if (!querySnapshot.isEmpty()) { | ||
|
|
||
| record.put("data", mergedData); | ||
| List<DocumentSnapshot> documents = querySnapshot.getDocuments(); | ||
| DocumentSnapshot existingDoc = documents.get(0); | ||
|
|
||
| // ✅ Idempotent write | ||
| documentRef.set(record, SetOptions.merge()) | ||
| .addOnSuccessListener(aVoid -> | ||
| Log.d(TAG, "Summary payload upserted in Firestore")) | ||
| .addOnFailureListener(e -> | ||
| Log.e(TAG, "Failed to upsert summary payload", e)); | ||
| Map<String, Object> mergedData = | ||
| mergeData(existingDoc, payload); | ||
|
|
||
| record.put("data", mergedData); | ||
|
|
||
| db.collection(payload.collection) | ||
| .document(existingDoc.getId()) | ||
| .set(record) | ||
| .addOnSuccessListener(aVoid -> | ||
| Log.d(TAG, "Updated summary payload")) | ||
| .addOnFailureListener(e -> | ||
| Log.e(TAG, "Failed to update summary payload", e)); | ||
|
|
||
| } else { | ||
|
|
||
| Log.d(TAG, "No existing summary record — creating new"); | ||
| createNewSummaryDoc(db, payload, record); | ||
| } |
There was a problem hiding this comment.
Summary creation is still racy.
This is still a non-atomic read-then-create flow: two writers for the same (cr_user_id, app_id, collection) can both observe an empty query result and both call add(record), creating duplicate summary docs even when the query itself succeeds. After that, limit(1) makes future merges nondeterministic. Please use a deterministic document ID for the summary record or move this into a transaction/upsert on a fixed document.
Also applies to: 167-168
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In
`@app/src/main/java/org/curiouslearning/container/core/subapp/handler/DefaultAppEventPayloadHandler.java`
around lines 102 - 138, The current read-then-add flow in
DefaultAppEventPayloadHandler (the Query "query" and subsequent query.get()
branch that calls createNewSummaryDoc or
db.collection(...).document(existingDoc.getId()).set(...)) is racy; replace it
with a deterministic document ID (e.g., hash or concatenation of
payload.cr_user_id, payload.app_id, payload.collection) and perform an atomic
upsert against that document instead of querying+adding. Concretely: compute
docId from payload, use DocumentReference docRef =
db.collection(payload.collection).document(docId), then either run a Firestore
transaction that reads and merges via mergeData and writes the merged record, or
call docRef.set(record, SetOptions.merge())/docRef.set(record) as appropriate to
upsert atomically; apply the same change where the other summary-creation code
path (the other occurrence handling payload) is implemented so duplicate docs
cannot be created.
There was a problem hiding this comment.
♻️ Duplicate comments (2)
app/src/main/java/org/curiouslearning/container/core/subapp/handler/DefaultAppEventPayloadHandler.java (2)
77-80:⚠️ Potential issue | 🟠 MajorReject unsupported
datashapes upstream instead of dropping them here.
AppEventPayloadValidatoronly guaranteesdata != null, so scalar/list payloads still reach these branches. They are then logged and discarded here, which means the event was accepted but its body was not stored. Either validateMappayloads for these collections beforehandle()is called, or persistpayload.dataas-is.Also applies to: 171-175, 202-205
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/src/main/java/org/curiouslearning/container/core/subapp/handler/DefaultAppEventPayloadHandler.java` around lines 77 - 80, The handler currently drops non-Map payload.data in DefaultAppEventPayloadHandler.handle(): locate the checks that log and return when payload.data isn't a Map (the branches at the first check and the similar ones around lines handling collections) and either (A) move/extend validation into AppEventPayloadValidator so it rejects scalar/list shapes before handle() is invoked (ensure AppEventPayloadValidator enforces Map type for data and update its validation method signatures), or (B) persist payload.data unchanged instead of logging-and-returning inside DefaultAppEventPayloadHandler.handle() (update the persistence path to accept Object data and remove the early returns). Pick one approach and apply the same fix to the other identical branches referenced (the checks at ~171-175 and ~202-205).
113-162:⚠️ Potential issue | 🔴 CriticalMake
summary_datawrites atomic.This is still a query → client merge →
add/setflow. Two writers can both create a new summary document, and concurrent"add"updates on an existing document can also lose increments because both are computed from stale snapshots. Once duplicates exist,limit(1)makes future merges nondeterministic. Please switch this path to a deterministic document ID plus a Firestore transaction, or useFieldValue.increment()for additive fields.Also applies to: 182-187, 229-257
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/src/main/java/org/curiouslearning/container/core/subapp/handler/DefaultAppEventPayloadHandler.java` around lines 113 - 162, The current read-then-write flow around the query in DefaultAppEventPayloadHandler (the block that builds Query query = db.collection(payload.collection)... and uses mergeData and createNewSummaryDoc) is racy; replace it with a Firestore transaction (or use a deterministic document ID derived from payload.cr_user_id and payload.app_id instead of query+limit) and perform the read/merge/write inside db.runTransaction so the mergeData logic runs atomically; for any additive counters in mergeData use FieldValue.increment() in the transaction update to avoid lost increments, and update createNewSummaryDoc and the branches at the other noted places (lines ~182-187 and ~229-257) to use the same deterministic doc id + transaction/update pattern rather than query/get → set/add.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Duplicate comments:
In
`@app/src/main/java/org/curiouslearning/container/core/subapp/handler/DefaultAppEventPayloadHandler.java`:
- Around line 77-80: The handler currently drops non-Map payload.data in
DefaultAppEventPayloadHandler.handle(): locate the checks that log and return
when payload.data isn't a Map (the branches at the first check and the similar
ones around lines handling collections) and either (A) move/extend validation
into AppEventPayloadValidator so it rejects scalar/list shapes before handle()
is invoked (ensure AppEventPayloadValidator enforces Map type for data and
update its validation method signatures), or (B) persist payload.data unchanged
instead of logging-and-returning inside DefaultAppEventPayloadHandler.handle()
(update the persistence path to accept Object data and remove the early
returns). Pick one approach and apply the same fix to the other identical
branches referenced (the checks at ~171-175 and ~202-205).
- Around line 113-162: The current read-then-write flow around the query in
DefaultAppEventPayloadHandler (the block that builds Query query =
db.collection(payload.collection)... and uses mergeData and createNewSummaryDoc)
is racy; replace it with a Firestore transaction (or use a deterministic
document ID derived from payload.cr_user_id and payload.app_id instead of
query+limit) and perform the read/merge/write inside db.runTransaction so the
mergeData logic runs atomically; for any additive counters in mergeData use
FieldValue.increment() in the transaction update to avoid lost increments, and
update createNewSummaryDoc and the branches at the other noted places (lines
~182-187 and ~229-257) to use the same deterministic doc id + transaction/update
pattern rather than query/get → set/add.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: c682cc41-140f-422b-8f55-2375e0e5fd1c
📒 Files selected for processing (1)
app/src/main/java/org/curiouslearning/container/core/subapp/handler/DefaultAppEventPayloadHandler.java
Changes
Ref: MR-57
Summary by CodeRabbit