Skip to content

Comments

fix: guests are not adding when reschedule booking#23620

Merged
anikdhabal merged 6 commits intocalcom:mainfrom
anikdhabal:guests-not-added
Sep 9, 2025
Merged

fix: guests are not adding when reschedule booking#23620
anikdhabal merged 6 commits intocalcom:mainfrom
anikdhabal:guests-not-added

Conversation

@anikdhabal
Copy link
Contributor

@anikdhabal anikdhabal commented Sep 5, 2025

What does this PR do?

After a successful booking when any guest added from the add guests option, those newly added guests are not included during reschedule

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Sep 5, 2025

Walkthrough

The handler at packages/trpc/server/routers/viewer/bookings/addGuests.handler.ts now persists added guests into booking.responses by merging existing booking.responses with a new guests array that concatenates booking.responses?.guests and the computed uniqueGuests. It casts booking.responses to the new BookingResponses type. The attendees createMany logic and function signatures remain unchanged; no exported/public API signatures were modified.

Possibly related PRs

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore or @coderabbit ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@graphite-app graphite-app bot requested a review from a team September 5, 2025 14:07
@keithwillcode keithwillcode added the core area: core, team members only label Sep 5, 2025
@vercel
Copy link

vercel bot commented Sep 5, 2025

@anikdhabal is attempting to deploy a commit to the cal Team on Vercel.

A member of the Team first needs to authorize it.

@dosubot dosubot bot added bookings area: bookings, availability, timezones, double booking 🐛 bug Something isn't working labels Sep 5, 2025
@anikdhabal anikdhabal enabled auto-merge (squash) September 5, 2025 14:10
Copy link
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.

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 (5)
packages/trpc/server/routers/viewer/bookings/addGuests.handler.ts (5)

175-176: Send emails only to actually-added guests

Use uniqueGuests (post-filtered) to avoid emailing blacklisted/duplicate or already-present attendees.

-    await sendAddGuestsEmails(evt, guests);
+    await sendAddGuestsEmails(evt, uniqueGuests);

70-81: Normalize and de-duplicate input guest emails; honor blacklist case-insensitively

Prevents duplicate attendee rows and inconsistent casing.

-  const blacklistedGuestEmails = process.env.BLACKLISTED_GUEST_EMAILS
-    ? process.env.BLACKLISTED_GUEST_EMAILS.split(",").map((email) => email.toLowerCase())
-    : [];
-
-  const uniqueGuests = guests.filter(
-    (guest) =>
-      !booking.attendees.some((attendee) => guest === attendee.email) &&
-      !blacklistedGuestEmails.includes(guest)
-  );
+  const blacklistedGuestEmails = process.env.BLACKLISTED_GUEST_EMAILS
+    ? process.env.BLACKLISTED_GUEST_EMAILS.split(",").map((e) => e.trim().toLowerCase())
+    : [];
+  const attendeeEmails = new Set(booking.attendees.map((a) => a.email.toLowerCase()));
+  const normalizedGuests = guests.map((g) => g.trim().toLowerCase());
+  const uniqueGuests = Array.from(
+    new Set(normalizedGuests.filter((g) => !attendeeEmails.has(g) && !blacklistedGuestEmails.includes(g)))
+  );

101-104: Harden createMany against races/duplicates

Skip duplicates at DB level (assuming unique index on [bookingId,email]); reduces failure surface if the same email slips through.

       attendees: {
         createMany: {
-          data: guestsFullDetails,
+          data: guestsFullDetails,
+          skipDuplicates: true,
         },
       },

26-42: Replace broad Prisma include with minimal select (performance, security guideline)

Avoid fetching unnecessary relations (especially user.credentials). Select only what’s needed.

-  const booking = await prisma.booking.findUnique({
+  const booking = await prisma.booking.findUnique({
     where: {
       id: bookingId,
     },
-    include: {
-      attendees: true,
-      eventType: true,
-      destinationCalendar: true,
-      references: true,
-      user: {
-        include: {
-          destinationCalendar: true,
-          credentials: true,
-        },
-      },
-    },
+    select: {
+      id: true,
+      userId: true,
+      userPrimaryEmail: true,
+      title: true,
+      description: true,
+      startTime: true,
+      endTime: true,
+      uid: true,
+      iCalUID: true,
+      location: true,
+      responses: true,
+      attendees: { select: { email: true } },
+      eventType: {
+        select: {
+          title: true,
+          teamId: true,
+          recurringEvent: true,
+          hideOrganizerEmail: true,
+          seatsPerTimeSlot: true,
+          seatsShowAttendees: true,
+          customReplyToEmail: true,
+        },
+      },
+      destinationCalendar: true,
+      references: { select: { type: true, meetingId: true, meetingPassword: true, meetingUrl: true } },
+      user: { select: { destinationCalendar: true } },
+    },
   });

92-110: Wrap the read and update of booking.responses in a single transaction (or use atomic JSON merging) to avoid lost concurrent writes.
The responses JSON column is also updated in editLocation.handler.ts and confirm.handler.ts, so spreading a stale snapshot (...booking.responses) risks overwriting concurrent edits (e.g., location updates or other response fields). Use prisma.$transaction or a DB‐side JSON patch (e.g. jsonb_set) to atomically append guests without dropping other responses data.

🧹 Nitpick comments (3)
packages/trpc/server/routers/viewer/bookings/addGuests.handler.ts (3)

96-99: Use select instead of include on update result

Return only what you need from bookingAttendees.

-    include: {
-      attendees: true,
-    },
+    select: {
+      attendees: { select: { name: true, email: true, timeZone: true, locale: true } },
+    },

176-178: Log the error object for diagnosis

Keep message, add the error for observability.

-  } catch (err) {
-    console.log("Error sending AddGuestsEmails");
-  }
+  } catch (err) {
+    console.error("Error sending AddGuestsEmails", err);
+  }

52-56: Minor: attendee check should be case-insensitive

If emails differ only by case, the current check can misclassify role.

-  const isAttendee = !!booking.attendees.find((attendee) => attendee.email === user.email);
+  const isAttendee = !!booking.attendees.find((attendee) => attendee.email.toLowerCase() === user.email.toLowerCase());
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 9fea30a and 8bedd14.

📒 Files selected for processing (1)
  • packages/trpc/server/routers/viewer/bookings/addGuests.handler.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.ts

📄 CodeRabbit inference engine (.cursor/rules/review.mdc)

**/*.ts: For Prisma queries, only select data you need; never use include, always use select
Ensure the credential.key field is never returned from tRPC endpoints or APIs

Files:

  • packages/trpc/server/routers/viewer/bookings/addGuests.handler.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/review.mdc)

Flag excessive Day.js use in performance-critical code; prefer native Date or Day.js .utc() in hot paths like loops

Files:

  • packages/trpc/server/routers/viewer/bookings/addGuests.handler.ts
**/*.{ts,tsx,js,jsx}

⚙️ CodeRabbit configuration file

Flag default exports and encourage named exports. Named exports provide better tree-shaking, easier refactoring, and clearer imports. Exempt main components like pages, layouts, and components that serve as the primary export of a module.

Files:

  • packages/trpc/server/routers/viewer/bookings/addGuests.handler.ts
⏰ Context from checks skipped due to timeout of 180000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Tests / Unit
  • GitHub Check: Linters / lint
  • GitHub Check: Type check / check-types

@anikdhabal anikdhabal marked this pull request as draft September 5, 2025 14:24
auto-merge was automatically disabled September 5, 2025 14:24

Pull request was converted to draft

@anikdhabal anikdhabal marked this pull request as ready for review September 5, 2025 19:37
@anikdhabal anikdhabal requested a review from a team as a code owner September 5, 2025 19:37
@anikdhabal anikdhabal enabled auto-merge (squash) September 5, 2025 19:38
Copy link
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.

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
packages/trpc/server/routers/viewer/bookings/addGuests.handler.ts (2)

75-79: Normalize and de-duplicate input emails case-insensitively before filtering

Prevents duplicate attendee rows and aligns comparisons regardless of casing.

Apply:

-  const uniqueGuests = guests.filter(
-    (guest) =>
-      !booking.attendees.some((attendee) => guest === attendee.email) &&
-      !blacklistedGuestEmails.includes(guest)
-  );
+  // Preserve first-seen casing while comparing case-insensitively
+  const normalizedMap = new Map<string, string>();
+  for (const g of guests) {
+    const k = g.toLowerCase().trim();
+    if (k) normalizedMap.set(k, normalizedMap.get(k) ?? g);
+  }
+  const dedupedInputGuests = Array.from(normalizedMap.values());
+  const uniqueGuests = dedupedInputGuests.filter(
+    (guest) =>
+      !booking.attendees.some(
+        (attendee) => (attendee.email ?? "").toLowerCase() === guest.toLowerCase()
+      ) && !blacklistedGuestEmails.includes(guest.toLowerCase())
+  );

178-181: Email only the filtered, deduplicated guests

Avoid emailing blacklisted/duplicate addresses by using the finalized set.

Apply:

-    await sendAddGuestsEmails(evt, guests);
+    await sendAddGuestsEmails(evt, Array.from(new Set(uniqueGuests)));
♻️ Duplicate comments (2)
packages/trpc/server/routers/viewer/bookings/addGuests.handler.ts (2)

93-93: Don’t cast nullable responses; build a safe payload and de-duplicate guests

booking.responses is nullable per schema. Spreading a nullable value later causes TypeError: Cannot convert undefined or null to object. Also ensure we de-duplicate new guests before persisting.

Apply:

-  const bookingResponses = booking.responses as BookingResponses;
+  // Build safe responses payload and dedupe guests
+  const prevResponses = (booking.responses ?? {}) as Record<string, unknown>;
+  const prevGuests = Array.isArray((prevResponses as any).guests)
+    ? ((prevResponses as any).guests as string[])
+    : [];
+  // uniqueGuests is computed below (see separate comment to normalize + dedupe inputs)
+  const dedupedUniqueGuests = Array.from(new Set(uniqueGuests));
+  const responsesPayload = {
+    ...prevResponses,
+    guests: Array.from(new Set([...prevGuests, ...dedupedUniqueGuests])),
+  };

108-111: Use the precomputed safe responsesPayload instead of spreading a nullable object

Prevents runtime crash and ensures guest list uniqueness.

Apply:

-      responses: {
-        ...bookingResponses,
-        guests: [...(bookingResponses?.guests || []), ...uniqueGuests],
-      },
+      responses: responsesPayload,
🧹 Nitpick comments (4)
packages/prisma/zod-utils.ts (1)

180-181: Export a non-nullable alias to reduce downstream null checks and unsafe spreads

The current alias infers BookingResponses as object | null. Many call-sites (like the handler in this PR) tend to spread this value. Exporting a non-nullable alias helps avoid accidental ...null errors.

Apply:

 export type BookingResponses = z.infer<typeof bookingResponses>;
+export type NonNullableBookingResponses = NonNullable<z.infer<typeof bookingResponses>>;
packages/trpc/server/routers/viewer/bookings/addGuests.handler.ts (3)

103-107: Add skipDuplicates safety to createMany

As a guardrail against race conditions or edge-case dupes.

Apply:

       attendees: {
         createMany: {
           data: guestsFullDetails,
+          skipDuplicates: true,
         },
       },

27-43: Prisma: use select (no include) and drop unused credentials per guidelines

Only fetch fields used later and avoid fetching sensitive data unnecessarily.

Apply:

-  const booking = await prisma.booking.findUnique({
+  const booking = await prisma.booking.findUnique({
     where: {
       id: bookingId,
     },
-    include: {
-      attendees: true,
-      eventType: true,
-      destinationCalendar: true,
-      references: true,
-      user: {
-        include: {
-          destinationCalendar: true,
-          credentials: true,
-        },
-      },
-    },
+    select: {
+      id: true,
+      title: true,
+      description: true,
+      startTime: true,
+      endTime: true,
+      uid: true,
+      iCalUID: true,
+      location: true,
+      userPrimaryEmail: true,
+      userId: true,
+      attendees: { select: { name: true, email: true, timeZone: true, locale: true } },
+      eventType: {
+        select: {
+          title: true,
+          teamId: true,
+          hideOrganizerEmail: true,
+          seatsPerTimeSlot: true,
+          seatsShowAttendees: true,
+          customReplyToEmail: true,
+          recurringEvent: true,
+        },
+      },
+      destinationCalendar: true,
+      references: true,
+      user: {
+        select: {
+          name: true,
+          email: true,
+          timeZone: true,
+          locale: true,
+          destinationCalendar: true,
+        },
+      },
+      responses: true,
+    },
   });

95-102: Prisma: prefer select over include for attendees on update

Fetch only what you use to build attendeesList.

Apply:

-    include: {
-      attendees: true,
-    },
+    select: {
+      attendees: { select: { name: true, email: true, timeZone: true, locale: true } },
+    },
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 8bedd14 and 4d7ea1c.

📒 Files selected for processing (2)
  • packages/prisma/zod-utils.ts (1 hunks)
  • packages/trpc/server/routers/viewer/bookings/addGuests.handler.ts (3 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.ts

📄 CodeRabbit inference engine (.cursor/rules/review.mdc)

**/*.ts: For Prisma queries, only select data you need; never use include, always use select
Ensure the credential.key field is never returned from tRPC endpoints or APIs

Files:

  • packages/prisma/zod-utils.ts
  • packages/trpc/server/routers/viewer/bookings/addGuests.handler.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/review.mdc)

Flag excessive Day.js use in performance-critical code; prefer native Date or Day.js .utc() in hot paths like loops

Files:

  • packages/prisma/zod-utils.ts
  • packages/trpc/server/routers/viewer/bookings/addGuests.handler.ts
**/*.{ts,tsx,js,jsx}

⚙️ CodeRabbit configuration file

Flag default exports and encourage named exports. Named exports provide better tree-shaking, easier refactoring, and clearer imports. Exempt main components like pages, layouts, and components that serve as the primary export of a module.

Files:

  • packages/prisma/zod-utils.ts
  • packages/trpc/server/routers/viewer/bookings/addGuests.handler.ts
🧬 Code graph analysis (1)
packages/trpc/server/routers/viewer/bookings/addGuests.handler.ts (1)
packages/prisma/zod-utils.ts (2)
  • bookingResponses (155-178)
  • BookingResponses (180-180)
⏰ Context from checks skipped due to timeout of 180000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Tests / Unit
  • GitHub Check: Type check / check-types
  • GitHub Check: Linters / lint
🔇 Additional comments (1)
packages/trpc/server/routers/viewer/bookings/addGuests.handler.ts (1)

9-9: LGTM: type-only import

No bundle impact; consistent with usage.

@github-actions
Copy link
Contributor

github-actions bot commented Sep 8, 2025

E2E results are ready!

@anikdhabal anikdhabal merged commit 7a6e166 into calcom:main Sep 9, 2025
31 of 35 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bookings area: bookings, availability, timezones, double booking 🐛 bug Something isn't working core area: core, team members only ready-for-e2e size/XS

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants