From 2b29b5607dc5d07f75b1d564a1e8d3d4b3be755b Mon Sep 17 00:00:00 2001 From: Hariom Balhara Date: Fri, 5 Sep 2025 18:55:57 +0530 Subject: [PATCH] Add logs to cancellation flow (#23585) --- .../handleNewBooking/test/reschedule.test.ts | 149 ++++++++++++++++++ packages/lib/EventManager.ts | 30 +++- 2 files changed, 175 insertions(+), 4 deletions(-) diff --git a/packages/features/bookings/lib/handleNewBooking/test/reschedule.test.ts b/packages/features/bookings/lib/handleNewBooking/test/reschedule.test.ts index eb3afff940..dc39387ab9 100644 --- a/packages/features/bookings/lib/handleNewBooking/test/reschedule.test.ts +++ b/packages/features/bookings/lib/handleNewBooking/test/reschedule.test.ts @@ -2956,5 +2956,154 @@ describe("handleNewBooking", () => { }, timeout ); + + test( + "should set correct booking reference when rescheduling with phone location change", + async () => { + const handleNewBooking = (await import("@calcom/features/bookings/lib/handleNewBooking")).default; + const booker = getBooker({ + email: "test@example.com", + name: "Booker", + }); + + const organizer = getOrganizer({ + name: "Organizer", + email: "organizer@example.com", + id: 101, + schedules: [TestData.schedules.IstWorkHours], + credentials: [getGoogleCalendarCredential()], + selectedCalendars: [TestData.selectedCalendars.google], + }); + + const { dateString: plus1DateString } = getDate({ dateIncrement: 1 }); + const uidOfBookingToBeRescheduled = "n5Wv3eHgconAED2j4gcVhP"; + + await createBookingScenario( + getScenarioData({ + webhooks: [ + { + userId: organizer.id, + eventTriggers: ["BOOKING_RESCHEDULED"], + subscriberUrl: "http://my-webhook.example.com", + active: true, + eventTypeId: 1, + appId: null, + }, + ], + eventTypes: [ + { + id: 1, + slotInterval: 15, + length: 15, + users: [ + { + id: 101, + }, + ], + locations: [ + { + type: "phone", // ← Original generic phone location + }, + ], + }, + ], + bookings: [ + { + uid: uidOfBookingToBeRescheduled, + eventTypeId: 1, + status: BookingStatus.ACCEPTED, + startTime: `${plus1DateString}T05:00:00.000Z`, + endTime: `${plus1DateString}T05:15:00.000Z`, + references: [ + getMockBookingReference({ + type: "google_calendar", + uid: "MOCK_ID", + meetingId: "MOCK_ID", + meetingPassword: "MOCK_PASS", + meetingUrl: "http://mock-google-meet.example.com", + externalCalendarId: "MOCK_EXTERNAL_CALENDAR_ID", + credentialId: 1, + }), + ], + attendees: [ + getMockBookingAttendee({ + id: 1, + name: booker.name, + email: booker.email, + }), + ], + // Different location from the one in the new booking + location: "+15552234567", + }, + ], + organizer, + usersApartFromOrganizer: [], + apps: [TestData.apps["google-calendar"]], + }) + ); + + const calendarMock = await mockCalendarToHaveNoBusySlots("googlecalendar", { + create: { uid: "NEW_EVENT_ID" }, + update: { uid: "UPDATED_EVENT_ID" }, + }); + + const mockBookingData = getMockRequestDataForBooking({ + data: { + eventTypeId: 1, + rescheduleUid: uidOfBookingToBeRescheduled, + start: `${plus1DateString}T06:00:00.000Z`, + end: `${plus1DateString}T06:15:00.000Z`, + responses: { + email: booker.email, + name: booker.name, + location: { optionValue: "+15551234567", value: "+15551234567" }, + }, + }, + }); + + const createdBooking = await handleNewBooking({ + bookingData: mockBookingData, + }); + + logger.silly("Created booking", { createdBooking }); + + await expectBookingInDBToBeRescheduledFromTo({ + from: { + uid: uidOfBookingToBeRescheduled, + }, + to: { + description: "", + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + uid: createdBooking.uid!, + eventTypeId: 1, + // Only recurring event can have recurringEventId + recurringEventId: null, + location: "+15551234567", + status: BookingStatus.ACCEPTED, + responses: expect.objectContaining({ + email: booker.email, + name: booker.name, + }), + }, + }); + + // ✅ THE CRITICAL TEST: Ensure BookingReference has proper values + const newBooking = await prismaMock.booking.findFirst({ + where: { + uid: createdBooking.uid, + }, + include: { + references: true, + }, + }); + + expect(newBooking?.references).toHaveLength(1); + const reference = newBooking!.references[0]; + + // Valid reference should have uid + expect(reference.uid).toBe("UPDATED_EVENT_ID"); + }, + timeout + ); }); }); diff --git a/packages/lib/EventManager.ts b/packages/lib/EventManager.ts index a244eee2dc..f7b4396ca8 100644 --- a/packages/lib/EventManager.ts +++ b/packages/lib/EventManager.ts @@ -447,12 +447,34 @@ export default class EventManager { } const referencesToCreate = results.map((result) => { + // For update operations, check updatedEvent first, then fall back to createdEvent + const updatedEvent = Array.isArray(result.updatedEvent) ? result.updatedEvent[0] : result.updatedEvent; + const createdEvent = result.createdEvent; + let event = updatedEvent; + if (!event) { + log.warn( + "updateLocation: No updatedEvent when doing updateLocation. Falling back to createdEvent but this is probably not what we want", + safeStringify({ bookingId: booking.id }) + ); + event = createdEvent; + } + + const uid = event?.id?.toString() ?? ""; + const meetingId = event?.id?.toString(); + + if (!uid) { + log.error( + "updateLocation: No uid for booking reference. The corresponding record in third party if created is orphan now", + safeStringify({ result }) + ); + } + return { type: result.type, - uid: result.createdEvent?.id?.toString() ?? "", - meetingId: result.createdEvent?.id?.toString(), - meetingPassword: result.createdEvent?.password, - meetingUrl: result.createdEvent?.url, + uid, + meetingId, + meetingPassword: event?.password, + meetingUrl: event?.url, externalCalendarId: result.externalId, ...(result.credentialId && result.credentialId > 0 ? { credentialId: result.credentialId } : {}), };