Skip to content

feat: implement multi-transaction support for event creation and update in Firestore (#333)#426

Merged
roshankumar0036singh merged 6 commits into
roshankumar0036singh:mainfrom
vansh-09:feat/multi-transaction-support
May 31, 2026
Merged

feat: implement multi-transaction support for event creation and update in Firestore (#333)#426
roshankumar0036singh merged 6 commits into
roshankumar0036singh:mainfrom
vansh-09:feat/multi-transaction-support

Conversation

@vansh-09
Copy link
Copy Markdown
Contributor

@vansh-09 vansh-09 commented May 30, 2026

Description

  • Event creation now uses a single Firestore transaction so multi-collection writes commit together or roll back together.
  • The transaction in CreateEvent.js now performs:
    • event document creation
    • attendance bootstrap placeholder creation
    • organizer event counter increment
  • Firestore rules were updated in firestore.rules to allow attendance writes that occur in the same atomic commit as event creation (using post-commit ownership checks).
  • Test coverage was expanded:
    • rules-level atomic create coverage in firestore.rules.test.ts
    • screen/integration-level create flow coverage in CreateEvent.test.js

Fixes #333

Type of change

  • New feature (non-breaking change which adds functionality)

How Has This Been Tested?

Ran:
npx jest src/screens/tests/CreateEvent.test.js --runInBand --coverage=false

Result:

  • 1 test suite passed
  • 1 test passed

Checklist:

  • My code follows the style guidelines of this project
  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation
  • My changes generate no new warnings
  • I have added tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally with my changes
  • Any dependent changes have been merged and published in downstream modules

Summary by CodeRabbit

  • Bug Fixes

    • Event creation is now atomic: creating an event also creates its attendance placeholder and updates organizer counters together to prevent partial records.
  • Security Updates

    • Attendance write rules now require the parent event to exist after writes and permit post-write owner validation.
  • Tests

    • Added end-to-end tests for atomic event creation and expanded/rewritten security-rule tests to cover batched/atomic writes with a shared test scaffold.

Review Change Stack

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 30, 2026

Warning

Review limit reached

@vansh-09, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 55 minutes and 46 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 9402184e-6b5b-44bb-9e13-4c65ee53ab11

📥 Commits

Reviewing files that changed from the base of the PR and between b87640e and 2ad1bfe.

📒 Files selected for processing (1)
  • tests/firestore.rules.test.ts
📝 Walkthrough

Walkthrough

CreateEvent now uses a Firestore transaction to atomically write an event, an attendance/bootstrap document, and update the creator's organizer stats; an app Jest test verifies the transaction; Firestore rules gained post-write ownership checks; rules tests were refactored to use batched, timestamped setups.

Changes

Atomic Event Creation Transaction

Layer / File(s) Summary
Event creation transaction implementation
app/src/screens/CreateEvent.js
Imports updated to include runTransaction, increment, and serverTimestamp. Event creation refactored from direct addDoc to a Firestore transaction that atomically writes the event document, an attendance/bootstrap placeholder, and updates or initializes organizer stats with increment and serverTimestamp.
CreateEvent transaction test coverage
app/src/screens/__tests__/CreateEvent.test.js
New Jest test suite with mocked Firebase transaction primitives and UI deps verifies atomic transaction behavior by simulating UI interactions and asserting transaction set/update calls, rate limiting, and success alert/navigation side effects.
Firestore rules for post-write event ownership
firestore.rules
Adds isEventOwnerAfter(database, eventId) using getAfter/existsAfter and updates events/{eventId}/attendance/{attendanceId} rules to require the event to exist after the write and permit writes by admins, clubs, pre-write owners, or post-write owners.
Firestore rules test coverage updates
tests/firestore.rules.test.ts
Imports extended with writeBatch and serverTimestamp. Adds shared test scaffolding and rewrites club-admin event creation and other tests to use batched commits and atomic setups; adds an atomic event+attendance batch test and updates reminder/feedback/message/access-control suites.

Sequence Diagram

sequenceDiagram
  participant App as CreateEvent App
  participant Txn as Firestore Transaction
  participant Events as events collection
  participant Attendance as events/{eventId}/attendance
  participant Users as users collection

  App->>Txn: runTransaction(callback)
  Txn->>Events: set(eventRef, eventData)
  Txn->>Attendance: set(bootstrapRef, placeholderData)
  Txn->>Users: update(userRef, {organizerStats.eventsCreated: increment(1), lastEventCreatedAt: serverTimestamp()})
  Txn-->>App: commit success (all or nothing)
  App->>App: Alert.alert('Success', 'Event Created!')
  App->>App: navigation.goBack()
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested labels

level:intermediate, type:refactor, type:testing, quality:clean, gssoc:approved

Suggested reviewers

  • roshankumar0036singh

Poem

🐰 I hopped through code to bind each write,
Events and attendance committed tight.
Counters tick and timestamps land,
Alert rings true — the app's in hand.
Hop, go back, and celebrate tonight.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed Title clearly describes the main feature: implementing Firestore transactions for multi-document writes in event creation, directly aligned with the changeset.
Linked Issues check ✅ Passed PR implements core objectives from #333: uses Firestore transactions for atomic event creation, attendance creation, and organizer counter updates with all-or-nothing semantics.
Out of Scope Changes check ✅ Passed All changes align with implementing transaction support: CreateEvent.js implements transactions, firestore.rules enables post-write ownership checks, and tests verify atomic behaviors.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (3)
app/src/screens/__tests__/CreateEvent.test.js (2)

141-152: 💤 Low value

Minor style improvements per static analysis.

The logic is correct, but optional chaining and .at() would be cleaner:

♻️ Optional style improvements
 const mockDoc = jest.fn((firstArg, ...segments) => {
-    if (firstArg && firstArg.__isCollectionRef && segments.length === 0) {
+    if (firstArg?.__isCollectionRef && segments.length === 0) {
         return { id: 'event_tx_1', path: `${firstArg.path}/event_tx_1` };
     }

     const parts = [...(typeof firstArg === 'string' ? [firstArg] : []), ...segments];

     return {
-        id: parts[parts.length - 1],
+        id: parts.at(-1),
         path: parts.join('/'),
     };
 });
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/src/screens/__tests__/CreateEvent.test.js` around lines 141 - 152, The
mockDoc implementation can be simplified for clarity: in the jest mock function
named mockDoc, replace the explicit firstArg and property check with optional
chaining (firstArg?.__isCollectionRef) and when returning the last segment use
.at(-1) instead of parts[parts.length - 1]; keep the existing logic that
constructs parts from a string firstArg and ...segments and preserve the
special-case return for collection refs (id 'event_tx_1' and path
`${firstArg.path}/event_tx_1`), but rewrite those checks using optional chaining
and .at(-1) for cleaner style.

126-126: ⚖️ Poor tradeoff

Consider adding test coverage for new organizer documents.

The mock always returns { exists: () => true }, so the fallback path in CreateEvent.js (lines 358-366) where transaction.set with merge: true is used for new organizers is not exercised. Consider adding a second test case where mockTransactionGet returns { exists: () => false } to verify the initialization logic.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/src/screens/__tests__/CreateEvent.test.js` at line 126, The test
currently only covers the existing-organizer path because mockTransactionGet
always returns exists: true; add a second test in CreateEvent.test.js that sets
mockTransactionGet to return { exists: () => false } (e.g. via
mockImplementationOnce) so the fallback branch in CreateEvent.js is exercised,
then run the transaction and assert that transaction.set was called for the
organizer document with merge: true and the expected initial organizer fields;
reference mockTransactionGet, transaction.set and the new-organizer
initialization branch in CreateEvent.js.
tests/firestore.rules.test.ts (1)

84-111: ⚡ Quick win

This happy-path test does not exercise the new isEventOwnerAfter() branch.

Because this request is authenticated with { club: true }, the attendance write is already allowed by the existing isClub() check in firestore.rules. This test will still pass even if isEventOwnerAfter() is broken or removed, so it does not actually cover the new authorization path introduced by this PR. Please add a case that would fail without the post-write helper.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/firestore.rules.test.ts` around lines 84 - 111, The current happy-path
test 'Club admin atomically creates event + attendance placeholder + organizer
stats -> allowed' never hits the new isEventOwnerAfter() branch because it
authenticates with { club: true }; add a new test case that authenticates as a
normal user (no club role) using getFirestoreContext without the club flag, then
perform an atomic batch that creates the event document (ownerId set to the test
user) plus the attendance placeholder and author updates so the authorization
must rely on isEventOwnerAfter() to allow the attendance write; ensure the test
asserts success with assertSucceeds(batch.commit()) and name it to indicate it
covers the isEventOwnerAfter() path so failures will catch regressions in
isEventOwnerAfter().
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@app/src/screens/__tests__/CreateEvent.test.js`:
- Around line 141-152: The mockDoc implementation can be simplified for clarity:
in the jest mock function named mockDoc, replace the explicit firstArg and
property check with optional chaining (firstArg?.__isCollectionRef) and when
returning the last segment use .at(-1) instead of parts[parts.length - 1]; keep
the existing logic that constructs parts from a string firstArg and ...segments
and preserve the special-case return for collection refs (id 'event_tx_1' and
path `${firstArg.path}/event_tx_1`), but rewrite those checks using optional
chaining and .at(-1) for cleaner style.
- Line 126: The test currently only covers the existing-organizer path because
mockTransactionGet always returns exists: true; add a second test in
CreateEvent.test.js that sets mockTransactionGet to return { exists: () => false
} (e.g. via mockImplementationOnce) so the fallback branch in CreateEvent.js is
exercised, then run the transaction and assert that transaction.set was called
for the organizer document with merge: true and the expected initial organizer
fields; reference mockTransactionGet, transaction.set and the new-organizer
initialization branch in CreateEvent.js.

In `@tests/firestore.rules.test.ts`:
- Around line 84-111: The current happy-path test 'Club admin atomically creates
event + attendance placeholder + organizer stats -> allowed' never hits the new
isEventOwnerAfter() branch because it authenticates with { club: true }; add a
new test case that authenticates as a normal user (no club role) using
getFirestoreContext without the club flag, then perform an atomic batch that
creates the event document (ownerId set to the test user) plus the attendance
placeholder and author updates so the authorization must rely on
isEventOwnerAfter() to allow the attendance write; ensure the test asserts
success with assertSucceeds(batch.commit()) and name it to indicate it covers
the isEventOwnerAfter() path so failures will catch regressions in
isEventOwnerAfter().

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 1ef59c1f-5fd5-4d9d-90ee-2a142596fc3e

📥 Commits

Reviewing files that changed from the base of the PR and between 8a535b2 and 8f0e2a6.

📒 Files selected for processing (4)
  • app/src/screens/CreateEvent.js
  • app/src/screens/__tests__/CreateEvent.test.js
  • firestore.rules
  • tests/firestore.rules.test.ts

@vansh-09
Copy link
Copy Markdown
Contributor Author

@roshankumar0036singh please review

@roshankumar0036singh
Copy link
Copy Markdown
Owner

@vansh-09 createdAt uses ISO string, instead use serverTimestamp()

@roshankumar0036singh
Copy link
Copy Markdown
Owner

@vansh-09 Missing newline at end of firestore.rules

@roshankumar0036singh
Copy link
Copy Markdown
Owner

isEventOwnerAfter calls getAfter() twice in the same function, which counts against Firestore rules read limits

@roshankumar0036singh
Copy link
Copy Markdown
Owner

why is the mockTransactionGet is hardcoded to exists: () => true

@roshankumar0036singh
Copy link
Copy Markdown
Owner

If the transaction retries due to , nowIso will carry the timestamp from the first attempt fix this

@roshankumar0036singh
Copy link
Copy Markdown
Owner

@vansh-09 fix the issue flagged by osnar mean there are duplicate code blocks in pr remoev them

@sonarqubecloud
Copy link
Copy Markdown

@vansh-09
Copy link
Copy Markdown
Contributor Author

@roshankumar0036singh please check

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add transaction support for multi-document writes

2 participants