diff --git a/packages/features/bookings/lib/handleNewBooking/test/fresh-booking.test.ts b/packages/features/bookings/lib/handleNewBooking/test/fresh-booking.test.ts index e59736f670..7b71cd8e50 100644 --- a/packages/features/bookings/lib/handleNewBooking/test/fresh-booking.test.ts +++ b/packages/features/bookings/lib/handleNewBooking/test/fresh-booking.test.ts @@ -13,6 +13,7 @@ import { createBookingScenario, getDate, getGoogleCalendarCredential, + getGoogleMeetCredential, getAppleCalendarCredential, TestData, getOrganizer, @@ -1339,6 +1340,191 @@ describe("handleNewBooking", () => { }, timeout ); + + test( + `should fallback to Cal Video when organizer's default conferencing app is Google Meet but destination calendar is NOT Google Calendar`, + async ({ emails }) => { + const handleNewBooking = getNewBookingHandler(); + const booker = getBooker({ + email: "booker@example.com", + name: "Booker", + }); + + const organizer = getOrganizer({ + name: "Organizer", + email: "organizer@example.com", + id: 101, + schedules: [TestData.schedules.IstWorkHours], + credentials: [getGoogleMeetCredential()], + selectedCalendars: [TestData.selectedCalendars.office365], + destinationCalendar: { + integration: "office365_calendar", + externalId: "organizer@outlook.com", + }, + metadata: { + defaultConferencingApp: { + appSlug: "google-meet", + }, + }, + }); + + const scenarioData = getScenarioData({ + webhooks: [ + { + userId: organizer.id, + eventTriggers: ["BOOKING_CREATED"], + subscriberUrl: "http://my-webhook.example.com", + active: true, + eventTypeId: 1, + appId: null, + }, + ], + eventTypes: [ + { + id: 1, + slotInterval: 30, + length: 30, + users: [ + { + id: 101, + }, + ], + }, + ], + organizer, + apps: [TestData.apps["google-meet"], TestData.apps["daily-video"], TestData.apps["office365-calendar"]], + }); + + mockSuccessfulVideoMeetingCreation({ + metadataLookupKey: "dailyvideo", + videoMeetingData: { + id: "MOCK_ID", + password: "MOCK_PASS", + url: "http://mock-dailyvideo.example.com/meeting-1", + }, + }); + + await createBookingScenario(scenarioData); + + const mockedBookingData = getMockRequestDataForBooking({ + data: { + eventTypeId: 1, + responses: { + email: booker.email, + name: booker.name, + }, + }, + }); + + const createdBooking = await handleNewBooking({ + bookingData: mockedBookingData, + }); + + // Should fallback to Cal Video instead of Google Meet + expect(createdBooking).toEqual( + expect.objectContaining({ + location: BookingLocations.CalVideo, + }) + ); + }, + timeout + ); + + test( + `should use Google Meet when organizer's default conferencing app is Google Meet and destination calendar IS Google Calendar`, + async ({ emails }) => { + const handleNewBooking = getNewBookingHandler(); + const booker = getBooker({ + email: "booker@example.com", + name: "Booker", + }); + + const organizer = getOrganizer({ + name: "Organizer", + email: "organizer@example.com", + id: 101, + schedules: [TestData.schedules.IstWorkHours], + credentials: [getGoogleCalendarCredential(), getGoogleMeetCredential()], + selectedCalendars: [TestData.selectedCalendars.google], + destinationCalendar: { + integration: "google_calendar", + externalId: "organizer@google-calendar.com", + }, + metadata: { + defaultConferencingApp: { + appSlug: "google-meet", + }, + }, + }); + + const scenarioData = getScenarioData({ + webhooks: [ + { + userId: organizer.id, + eventTriggers: ["BOOKING_CREATED"], + subscriberUrl: "http://my-webhook.example.com", + active: true, + eventTypeId: 1, + appId: null, + }, + ], + eventTypes: [ + { + id: 1, + slotInterval: 30, + length: 30, + users: [ + { + id: 101, + }, + ], + }, + ], + organizer, + apps: [TestData.apps["google-calendar"], TestData.apps["google-meet"], TestData.apps["daily-video"]], + }); + + mockSuccessfulVideoMeetingCreation({ + metadataLookupKey: "googlevideo", + }); + + await mockCalendarToHaveNoBusySlots("googlecalendar", { + create: { + id: "GOOGLE_CALENDAR_EVENT_ID", + uid: "MOCK_ID", + appSpecificData: { + googleCalendar: { + hangoutLink: "https://meet.google.com/test-meeting", + }, + }, + }, + }); + + await createBookingScenario(scenarioData); + + const mockedBookingData = getMockRequestDataForBooking({ + data: { + eventTypeId: 1, + responses: { + email: booker.email, + name: booker.name, + }, + }, + }); + + const createdBooking = await handleNewBooking({ + bookingData: mockedBookingData, + }); + + // Should use Google Meet since Google Calendar is the destination calendar + expect(createdBooking).toEqual( + expect.objectContaining({ + location: BookingLocations.GoogleMeet, + }) + ); + }, + timeout + ); }); describe("Video Meeting Creation", () => { diff --git a/packages/features/bookings/lib/service/RegularBookingService.ts b/packages/features/bookings/lib/service/RegularBookingService.ts index 074412c9a0..f65c43873a 100644 --- a/packages/features/bookings/lib/service/RegularBookingService.ts +++ b/packages/features/bookings/lib/service/RegularBookingService.ts @@ -1350,22 +1350,28 @@ async function handler( }); // use host default - if (locationBodyString == OrganizerDefaultConferencingAppType) { + if (locationBodyString === OrganizerDefaultConferencingAppType) { const metadataParseResult = userMetadataSchema.safeParse(organizerUser.metadata); const organizerMetadata = metadataParseResult.success ? metadataParseResult.data : undefined; - if (organizerMetadata?.defaultConferencingApp?.appSlug) { - const app = getAppFromSlug(organizerMetadata?.defaultConferencingApp?.appSlug); + const defaultApp = organizerMetadata?.defaultConferencingApp; + + if (defaultApp?.appSlug) { + const app = getAppFromSlug(defaultApp.appSlug); locationBodyString = app?.appData?.location?.type || locationBodyString; - if (isManagedEventType || isTeamEventType) { - organizerOrFirstDynamicGroupMemberDefaultLocationUrl = - organizerMetadata?.defaultConferencingApp?.appLink; + + const mainHostCalendar = eventType.destinationCalendar || organizerUser.destinationCalendar; + + if (locationBodyString === MeetLocationType && mainHostCalendar?.integration !== "google_calendar") { + locationBodyString = "integrations:daily"; + organizerOrFirstDynamicGroupMemberDefaultLocationUrl = undefined; + } else if (isManagedEventType || isTeamEventType) { + organizerOrFirstDynamicGroupMemberDefaultLocationUrl = defaultApp?.appLink; } - } else if (organizationDefaultLocation) { - locationBodyString = organizationDefaultLocation; } else { - locationBodyString = "integrations:daily"; + locationBodyString = organizationDefaultLocation || "integrations:daily"; } } + const invitee: Invitee = [ {