refactor: destination calendars when rescheduling (#21873)
* refactor: destination calendars when rescheduling * test: add unit test for collective evnet type * chore: remove log * chore: bump libraries * fix: test * chore: bump libraries * fix: tests * reset lock file * Revert "reset lock file" This reverts commit 4d48b678778b0daa19624338b302478e4655d200. * Revert "Revert "reset lock file"" This reverts commit a7c3120c926a0d37a2b55a6ff8d2a070969ec0f4. * regenerate lock file --------- Co-authored-by: Udit Takkar <53316345+Udit-takkar@users.noreply.github.com> Co-authored-by: Udit Takkar <udit222001@gmail.com>
This commit is contained in:
co-authored by
Udit Takkar
Udit Takkar
parent
21422fe027
commit
2fe71c3695
@@ -38,7 +38,7 @@
|
||||
"@axiomhq/winston": "^1.2.0",
|
||||
"@calcom/platform-constants": "*",
|
||||
"@calcom/platform-enums": "*",
|
||||
"@calcom/platform-libraries": "npm:@calcom/platform-libraries@0.0.231",
|
||||
"@calcom/platform-libraries": "npm:@calcom/platform-libraries@0.0.233",
|
||||
"@calcom/platform-libraries-0.0.2": "npm:@calcom/platform-libraries@0.0.2",
|
||||
"@calcom/platform-types": "*",
|
||||
"@calcom/platform-utils": "*",
|
||||
|
||||
+9
-1
@@ -1721,7 +1721,7 @@ describe("Event types Endpoints", () => {
|
||||
description: "undefined booking fields",
|
||||
length: 40,
|
||||
hidden: false,
|
||||
slug: "undefined-booking-fields",
|
||||
slug: `undefined-booking-fields-${randomString()}`,
|
||||
locations: [],
|
||||
schedulingType: SchedulingType.ROUND_ROBIN,
|
||||
};
|
||||
@@ -1751,6 +1751,14 @@ describe("Event types Endpoints", () => {
|
||||
disableOnPrefill: false,
|
||||
hidden: false,
|
||||
},
|
||||
{
|
||||
disableOnPrefill: false,
|
||||
hidden: true,
|
||||
isDefault: true,
|
||||
required: false,
|
||||
slug: "attendeePhoneNumber",
|
||||
type: "phone",
|
||||
},
|
||||
{
|
||||
isDefault: true,
|
||||
type: "radioInput",
|
||||
|
||||
@@ -1502,15 +1502,6 @@ async function handler(
|
||||
evt.videoCallData = undefined;
|
||||
// To prevent "The requested identifier already exists" error while updating event, we need to remove iCalUID
|
||||
evt.iCalUID = undefined;
|
||||
} else {
|
||||
// In case of rescheduling, we need to keep the previous host destination calendar
|
||||
evt = CalendarEventBuilder.fromEvent(evt)
|
||||
.withDestinationCalendar(
|
||||
originalRescheduledBooking?.destinationCalendar
|
||||
? [originalRescheduledBooking?.destinationCalendar]
|
||||
: evt.destinationCalendar
|
||||
)
|
||||
.build();
|
||||
}
|
||||
|
||||
const updateManager = await eventManager.reschedule(
|
||||
|
||||
+240
@@ -0,0 +1,240 @@
|
||||
import {
|
||||
createBookingScenario,
|
||||
getGoogleCalendarCredential,
|
||||
TestData,
|
||||
getOrganizer,
|
||||
getBooker,
|
||||
getScenarioData,
|
||||
mockSuccessfulVideoMeetingCreation,
|
||||
mockCalendarToHaveNoBusySlots,
|
||||
getDate,
|
||||
BookingLocations,
|
||||
} from "@calcom/web/test/utils/bookingScenario/bookingScenario";
|
||||
import { expectBookingToBeInDatabase } from "@calcom/web/test/utils/bookingScenario/expects";
|
||||
import { getMockRequestDataForBooking } from "@calcom/web/test/utils/bookingScenario/getMockRequestDataForBooking";
|
||||
import { setupAndTeardown } from "@calcom/web/test/utils/bookingScenario/setupAndTeardown";
|
||||
|
||||
import type { Request, Response } from "express";
|
||||
import type { NextApiRequest, NextApiResponse } from "next";
|
||||
import { describe, expect, beforeEach } from "vitest";
|
||||
|
||||
import { appStoreMetadata } from "@calcom/app-store/appStoreMetaData";
|
||||
import { resetTestEmails } from "@calcom/lib/testEmails";
|
||||
import { SchedulingType } from "@calcom/prisma/enums";
|
||||
import { BookingStatus } from "@calcom/prisma/enums";
|
||||
import { test } from "@calcom/web/test/fixtures/fixtures";
|
||||
|
||||
export type CustomNextApiRequest = NextApiRequest & Request;
|
||||
|
||||
export type CustomNextApiResponse = NextApiResponse & Response;
|
||||
|
||||
const timeout = process.env.CI ? 5000 : 20000;
|
||||
|
||||
describe("handleNewBooking", () => {
|
||||
setupAndTeardown();
|
||||
|
||||
beforeEach(() => {
|
||||
resetTestEmails();
|
||||
});
|
||||
|
||||
describe("Collective Event Reschedule with Destination Calendar Bug", () => {
|
||||
test(
|
||||
`should reschedule a collective event booking and ensure both hosts have the booking in their calendars when a second host is added
|
||||
1. Create a collective event with 1 host and book it
|
||||
2. Add a second host to the event type
|
||||
3. Reschedule the booking
|
||||
4. Verify both hosts have the booking in their calendars
|
||||
`,
|
||||
async ({ emails }) => {
|
||||
const handleNewBooking = (await import("@calcom/features/bookings/lib/handleNewBooking")).default;
|
||||
|
||||
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()],
|
||||
selectedCalendars: [TestData.selectedCalendars.google],
|
||||
destinationCalendar: {
|
||||
integration: "google_calendar",
|
||||
externalId: "organizer@google-calendar.com",
|
||||
primaryEmail: "organizer@google-calendar.com",
|
||||
},
|
||||
});
|
||||
|
||||
const { dateString: plus1DateString } = getDate({ dateIncrement: 1 });
|
||||
const { dateString: plus2DateString } = getDate({ dateIncrement: 2 });
|
||||
const uidOfBookingToBeRescheduled = "n5Wv3eHgconAED2j4gcVhP";
|
||||
const iCalUID = `${uidOfBookingToBeRescheduled}@Cal.com`;
|
||||
|
||||
await createBookingScenario(
|
||||
getScenarioData({
|
||||
eventTypes: [
|
||||
{
|
||||
id: 1,
|
||||
slotInterval: 15,
|
||||
length: 30,
|
||||
schedulingType: SchedulingType.COLLECTIVE,
|
||||
users: [
|
||||
{
|
||||
id: 101, // organizer
|
||||
},
|
||||
{
|
||||
id: 102, // second host (will be added to event type)
|
||||
},
|
||||
],
|
||||
destinationCalendar: {
|
||||
integration: "google_calendar",
|
||||
externalId: "event-type-1@google-calendar.com",
|
||||
primaryEmail: "event-type-1@google-calendar.com",
|
||||
},
|
||||
},
|
||||
],
|
||||
users: [
|
||||
{
|
||||
id: 101,
|
||||
name: "Organizer",
|
||||
email: "organizer@example.com",
|
||||
username: "organizer",
|
||||
timeZone: "Asia/Kolkata",
|
||||
defaultScheduleId: null,
|
||||
schedules: [TestData.schedules.IstWorkHours],
|
||||
credentials: [getGoogleCalendarCredential()],
|
||||
selectedCalendars: [TestData.selectedCalendars.google],
|
||||
destinationCalendar: {
|
||||
integration: "google_calendar",
|
||||
externalId: "organizer@google-calendar.com",
|
||||
primaryEmail: "organizer@google-calendar.com",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 102,
|
||||
name: "Second Host",
|
||||
email: "second-host@example.com",
|
||||
username: "second-host",
|
||||
timeZone: "Asia/Kolkata",
|
||||
defaultScheduleId: null,
|
||||
schedules: [TestData.schedules.IstWorkHours],
|
||||
credentials: [getGoogleCalendarCredential()],
|
||||
selectedCalendars: [TestData.selectedCalendars.google],
|
||||
destinationCalendar: {
|
||||
integration: "google_calendar",
|
||||
externalId: "second-host@google-calendar.com",
|
||||
primaryEmail: "second-host@google-calendar.com",
|
||||
},
|
||||
},
|
||||
],
|
||||
bookings: [
|
||||
{
|
||||
uid: uidOfBookingToBeRescheduled,
|
||||
eventTypeId: 1,
|
||||
status: BookingStatus.ACCEPTED,
|
||||
startTime: `${plus1DateString}T05:00:00.000Z`,
|
||||
endTime: `${plus1DateString}T05:30:00.000Z`,
|
||||
users: [
|
||||
{
|
||||
id: 101, // organizer
|
||||
},
|
||||
],
|
||||
references: [
|
||||
{
|
||||
type: appStoreMetadata.dailyvideo.type,
|
||||
uid: "MOCK_ID_1",
|
||||
meetingId: "MOCK_ID_1",
|
||||
meetingPassword: "MOCK_PASS_1",
|
||||
meetingUrl: "http://mock-dailyvideo.example.com/meeting-1",
|
||||
credentialId: null,
|
||||
},
|
||||
{
|
||||
type: appStoreMetadata.googlecalendar.type,
|
||||
uid: "MOCKED_GOOGLE_CALENDAR_EVENT_ID_1",
|
||||
meetingId: "MOCKED_GOOGLE_CALENDAR_EVENT_ID_1",
|
||||
meetingPassword: "MOCK_PASSWORD",
|
||||
meetingUrl: "https://UNUSED_URL",
|
||||
externalCalendarId: "event-type-1@google-calendar.com",
|
||||
credentialId: undefined,
|
||||
},
|
||||
],
|
||||
iCalUID,
|
||||
},
|
||||
],
|
||||
apps: [TestData.apps["google-calendar"], TestData.apps["daily-video"]],
|
||||
})
|
||||
);
|
||||
|
||||
mockSuccessfulVideoMeetingCreation({
|
||||
metadataLookupKey: appStoreMetadata.dailyvideo.dirName,
|
||||
videoMeetingData: {
|
||||
id: "MOCK_ID_1", // Video meeting ID is not updated during reschedule
|
||||
password: "MOCK_PASS_1",
|
||||
url: `http://mock-dailyvideo.example.com/meeting-1`,
|
||||
},
|
||||
});
|
||||
|
||||
const organizerCalendarMock = mockCalendarToHaveNoBusySlots("googlecalendar", {
|
||||
update: {
|
||||
id: "MOCKED_GOOGLE_CALENDAR_EVENT_ID_2",
|
||||
iCalUID: "MOCKED_GOOGLE_CALENDAR_ICS_ID_2",
|
||||
},
|
||||
});
|
||||
|
||||
const rescheduleBookingData = getMockRequestDataForBooking({
|
||||
data: {
|
||||
start: `${plus2DateString}T11:00:00.000Z`,
|
||||
end: `${plus2DateString}T11:30:00.000Z`,
|
||||
eventTypeId: 1,
|
||||
rescheduleUid: uidOfBookingToBeRescheduled,
|
||||
responses: {
|
||||
email: booker.email,
|
||||
name: booker.name,
|
||||
location: { optionValue: "", value: BookingLocations.CalVideo },
|
||||
},
|
||||
rescheduledBy: organizer.email,
|
||||
},
|
||||
});
|
||||
|
||||
const rescheduledBooking = await handleNewBooking({
|
||||
bookingData: rescheduleBookingData,
|
||||
});
|
||||
|
||||
await expectBookingToBeInDatabase({
|
||||
description: "",
|
||||
location: BookingLocations.CalVideo,
|
||||
responses: expect.objectContaining({
|
||||
email: booker.email,
|
||||
name: booker.name,
|
||||
}),
|
||||
uid: rescheduledBooking.uid!,
|
||||
eventTypeId: rescheduleBookingData.eventTypeId,
|
||||
status: BookingStatus.ACCEPTED,
|
||||
references: [
|
||||
{
|
||||
type: appStoreMetadata.dailyvideo.type,
|
||||
uid: "MOCK_ID_1",
|
||||
meetingId: "MOCK_ID_1",
|
||||
meetingPassword: "MOCK_PASS_1",
|
||||
meetingUrl: "http://mock-dailyvideo.example.com/meeting-1",
|
||||
},
|
||||
{
|
||||
type: appStoreMetadata.googlecalendar.type,
|
||||
uid: "MOCKED_GOOGLE_CALENDAR_EVENT_ID_1",
|
||||
externalCalendarId: "event-type-1@google-calendar.com",
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
expect(organizerCalendarMock.updateEventCalls.length).toBe(1);
|
||||
expect(organizerCalendarMock.createEventCalls.length).toBe(0);
|
||||
|
||||
// Check availability calls for both hosts
|
||||
expect(organizerCalendarMock.getAvailabilityCalls.length).toBe(2);
|
||||
},
|
||||
timeout
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -2519,7 +2519,7 @@ __metadata:
|
||||
"@axiomhq/winston": ^1.2.0
|
||||
"@calcom/platform-constants": "*"
|
||||
"@calcom/platform-enums": "*"
|
||||
"@calcom/platform-libraries": "npm:@calcom/platform-libraries@0.0.231"
|
||||
"@calcom/platform-libraries": "npm:@calcom/platform-libraries@0.0.233"
|
||||
"@calcom/platform-libraries-0.0.2": "npm:@calcom/platform-libraries@0.0.2"
|
||||
"@calcom/platform-types": "*"
|
||||
"@calcom/platform-utils": "*"
|
||||
@@ -3576,13 +3576,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@calcom/platform-libraries@npm:@calcom/platform-libraries@0.0.231":
|
||||
version: 0.0.231
|
||||
resolution: "@calcom/platform-libraries@npm:0.0.231"
|
||||
"@calcom/platform-libraries@npm:@calcom/platform-libraries@0.0.233":
|
||||
version: 0.0.233
|
||||
resolution: "@calcom/platform-libraries@npm:0.0.233"
|
||||
dependencies:
|
||||
"@calcom/features": "*"
|
||||
"@calcom/lib": "*"
|
||||
checksum: 0abd36b041cc2ed2068c56d36aa94286f381f425b88a0d0d7cdb14498263bbece4928162d8cea00bbf4fa01f4fb60a018933f3f8e4573331d7be2b22aab2eeaa
|
||||
checksum: 414a908ac982899aa643d125900602210b9066e1f497102af9f8cf10a328c083d037ae6c5c5a2239081dc63a63f8a8eff5228f4115737763a4447838388866a2
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
||||
Reference in New Issue
Block a user