fix: invalid Cancelled / Rescheduled downstream calendar block (#14700)
* fix: invalid Cancelled / Rescheduled downstream calendar block * Adjust test so no calendar is connected * Remove the googleCalendar from multiple tests
This commit is contained in:
1 parent
20dfc34e70
commit
0306ad737f
4 files changed
+54
-114
No files matched your search
@@ -630,8 +630,7 @@ describe("getSchedule", () => {
|
||||
...TestData.users.example,
|
||||
id: 101,
|
||||
schedules: [TestData.schedules.IstWorkHours],
|
||||
credentials: [getGoogleCalendarCredential()],
|
||||
selectedCalendars: [TestData.selectedCalendars.google],
|
||||
credentials: [],
|
||||
},
|
||||
],
|
||||
bookings: [
|
||||
@@ -643,7 +642,7 @@ describe("getSchedule", () => {
|
||||
status: "ACCEPTED" as BookingStatus,
|
||||
},
|
||||
],
|
||||
apps: [TestData.apps.googleCalendar],
|
||||
// apps: [TestData.apps.googleCalendar],
|
||||
};
|
||||
|
||||
await createBookingScenario(scenarioData);
|
||||
|
||||
@@ -144,43 +144,50 @@ export async function getBusyTimes(params: {
|
||||
});
|
||||
|
||||
const bookingSeatCountMap: { [x: string]: number } = {};
|
||||
const busyTimes = bookings.reduce(
|
||||
(aggregate: EventBusyDetails[], { id, startTime, endTime, eventType, title, ...rest }) => {
|
||||
if (rest._count?.seatsReferences) {
|
||||
const bookedAt = `${dayjs(startTime).utc().format()}<>${dayjs(endTime).utc().format()}`;
|
||||
bookingSeatCountMap[bookedAt] = bookingSeatCountMap[bookedAt] || 0;
|
||||
bookingSeatCountMap[bookedAt]++;
|
||||
// Seat references on the current event are non-blocking until the event is fully booked.
|
||||
if (
|
||||
// there are still seats available.
|
||||
bookingSeatCountMap[bookedAt] < (eventType?.seatsPerTimeSlot || 1) &&
|
||||
// and this is the seated event, other event types should be blocked.
|
||||
eventTypeId === eventType?.id
|
||||
) {
|
||||
// then we do not add the booking to the busyTimes.
|
||||
return aggregate;
|
||||
}
|
||||
// if it does get blocked at this point; we remove the bookingSeatCountMap entry
|
||||
// doing this allows using the map later to remove the ranges from calendar busy times.
|
||||
delete bookingSeatCountMap[bookedAt];
|
||||
}
|
||||
if (rest.uid === rescheduleUid) {
|
||||
return aggregate;
|
||||
}
|
||||
aggregate.push({
|
||||
start: dayjs(startTime)
|
||||
.subtract((eventType?.beforeEventBuffer || 0) + (afterEventBuffer || 0), "minute")
|
||||
.toDate(),
|
||||
end: dayjs(endTime)
|
||||
.add((eventType?.afterEventBuffer || 0) + (beforeEventBuffer || 0), "minute")
|
||||
.toDate(),
|
||||
title,
|
||||
source: `eventType-${eventType?.id}-booking-${id}`,
|
||||
});
|
||||
return aggregate;
|
||||
},
|
||||
[]
|
||||
);
|
||||
// Only take bookings as source of truth when there are no connected calendars
|
||||
// this is because cancellations downstream aren't reflected in our Booking database.
|
||||
// TODO: Reflect downstream cancellations in our Booking database and use Booking as
|
||||
// source of truth.
|
||||
const busyTimes =
|
||||
credentials?.length > 0
|
||||
? []
|
||||
: bookings.reduce(
|
||||
(aggregate: EventBusyDetails[], { id, startTime, endTime, eventType, title, ...rest }) => {
|
||||
if (rest._count?.seatsReferences) {
|
||||
const bookedAt = `${dayjs(startTime).utc().format()}<>${dayjs(endTime).utc().format()}`;
|
||||
bookingSeatCountMap[bookedAt] = bookingSeatCountMap[bookedAt] || 0;
|
||||
bookingSeatCountMap[bookedAt]++;
|
||||
// Seat references on the current event are non-blocking until the event is fully booked.
|
||||
if (
|
||||
// there are still seats available.
|
||||
bookingSeatCountMap[bookedAt] < (eventType?.seatsPerTimeSlot || 1) &&
|
||||
// and this is the seated event, other event types should be blocked.
|
||||
eventTypeId === eventType?.id
|
||||
) {
|
||||
// then we do not add the booking to the busyTimes.
|
||||
return aggregate;
|
||||
}
|
||||
// if it does get blocked at this point; we remove the bookingSeatCountMap entry
|
||||
// doing this allows using the map later to remove the ranges from calendar busy times.
|
||||
delete bookingSeatCountMap[bookedAt];
|
||||
}
|
||||
if (rest.uid === rescheduleUid) {
|
||||
return aggregate;
|
||||
}
|
||||
aggregate.push({
|
||||
start: dayjs(startTime)
|
||||
.subtract((eventType?.beforeEventBuffer || 0) + (afterEventBuffer || 0), "minute")
|
||||
.toDate(),
|
||||
end: dayjs(endTime)
|
||||
.add((eventType?.afterEventBuffer || 0) + (beforeEventBuffer || 0), "minute")
|
||||
.toDate(),
|
||||
title,
|
||||
source: `eventType-${eventType?.id}-booking-${id}`,
|
||||
});
|
||||
return aggregate;
|
||||
},
|
||||
[]
|
||||
);
|
||||
|
||||
logger.debug(
|
||||
`Busy Time from Cal Bookings ${JSON.stringify({
|
||||
|
||||
@@ -2433,13 +2433,7 @@ describe("handleNewBooking", () => {
|
||||
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: organizerOtherEmail,
|
||||
},
|
||||
credentials: [],
|
||||
});
|
||||
|
||||
await createBookingScenario(
|
||||
@@ -2455,15 +2449,10 @@ describe("handleNewBooking", () => {
|
||||
id: 101,
|
||||
},
|
||||
],
|
||||
destinationCalendar: {
|
||||
integration: "google_calendar",
|
||||
externalId: "event-type-1@google-calendar.com",
|
||||
primaryEmail: organizerDestinationCalendarEmailOnEventType,
|
||||
},
|
||||
},
|
||||
],
|
||||
organizer,
|
||||
apps: [TestData.apps["google-calendar"], TestData.apps["daily-video"]],
|
||||
apps: [TestData.apps["daily-video"]],
|
||||
})
|
||||
);
|
||||
|
||||
@@ -2524,36 +2513,10 @@ describe("handleNewBooking", () => {
|
||||
meetingPassword: "MOCK_PASS",
|
||||
meetingUrl: "http://mock-dailyvideo.example.com/meeting-1",
|
||||
},
|
||||
{
|
||||
type: appStoreMetadata.googlecalendar.type,
|
||||
uid: "MOCKED_GOOGLE_CALENDAR_EVENT_ID",
|
||||
meetingId: "MOCKED_GOOGLE_CALENDAR_EVENT_ID",
|
||||
meetingPassword: "MOCK_PASSWORD",
|
||||
meetingUrl: "https://UNUSED_URL",
|
||||
},
|
||||
],
|
||||
iCalUID: createdBooking.iCalUID,
|
||||
});
|
||||
|
||||
expectSuccessfulCalendarEventCreationInCalendar(calendarMock, {
|
||||
calendarId: "event-type-1@google-calendar.com",
|
||||
videoCallUrl: "http://mock-dailyvideo.example.com/meeting-1",
|
||||
});
|
||||
|
||||
const iCalUID = expectICalUIDAsString(createdBooking.iCalUID);
|
||||
|
||||
expectSuccessfulBookingCreationEmails({
|
||||
booking: {
|
||||
uid: createdBooking.uid!,
|
||||
urlOrigin: WEBSITE_URL,
|
||||
},
|
||||
booker,
|
||||
organizer,
|
||||
emails,
|
||||
iCalUID,
|
||||
destinationEmail: organizerDestinationCalendarEmailOnEventType,
|
||||
});
|
||||
|
||||
await expect(async () => await handleNewBooking(req)).rejects.toThrowError(
|
||||
ErrorCode.NoAvailableUsersFound
|
||||
);
|
||||
|
||||
@@ -266,12 +266,8 @@ describe("handleNewBooking", () => {
|
||||
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",
|
||||
},
|
||||
credentials: [],
|
||||
selectedCalendars: [],
|
||||
});
|
||||
|
||||
const recurrence = getRecurrence({
|
||||
@@ -303,10 +299,6 @@ describe("handleNewBooking", () => {
|
||||
id: 101,
|
||||
},
|
||||
],
|
||||
destinationCalendar: {
|
||||
integration: "google_calendar",
|
||||
externalId: "event-type-1@google-calendar.com",
|
||||
},
|
||||
},
|
||||
],
|
||||
bookings: [
|
||||
@@ -320,7 +312,7 @@ describe("handleNewBooking", () => {
|
||||
},
|
||||
],
|
||||
organizer,
|
||||
apps: [TestData.apps["google-calendar"], TestData.apps["daily-video"]],
|
||||
apps: [TestData.apps["daily-video"]],
|
||||
})
|
||||
);
|
||||
|
||||
@@ -837,12 +829,7 @@ describe("handleNewBooking", () => {
|
||||
],
|
||||
// Has morning shift with some overlap with morning shift
|
||||
schedules: [TestData.schedules.IstMorningShift],
|
||||
credentials: [getGoogleCalendarCredential()],
|
||||
selectedCalendars: [TestData.selectedCalendars.google],
|
||||
destinationCalendar: {
|
||||
integration: TestData.apps["google-calendar"].type,
|
||||
externalId: "organizer@google-calendar.com",
|
||||
},
|
||||
credentials: [],
|
||||
});
|
||||
|
||||
const otherTeamMembers = [
|
||||
@@ -856,12 +843,7 @@ describe("handleNewBooking", () => {
|
||||
id: 102,
|
||||
// Has Evening shift
|
||||
schedules: [TestData.schedules.IstMorningShift],
|
||||
credentials: [getGoogleCalendarCredential()],
|
||||
selectedCalendars: [TestData.selectedCalendars.google],
|
||||
destinationCalendar: {
|
||||
integration: TestData.apps["google-calendar"].type,
|
||||
externalId: "other-team-member-1@google-calendar.com",
|
||||
},
|
||||
credentials: [],
|
||||
},
|
||||
];
|
||||
|
||||
@@ -901,10 +883,6 @@ describe("handleNewBooking", () => {
|
||||
isFixed: true,
|
||||
},
|
||||
],
|
||||
destinationCalendar: {
|
||||
integration: "google_calendar",
|
||||
externalId: "event-type-1@google-calendar.com",
|
||||
},
|
||||
},
|
||||
],
|
||||
bookings: [
|
||||
@@ -923,7 +901,7 @@ describe("handleNewBooking", () => {
|
||||
],
|
||||
organizer,
|
||||
usersApartFromOrganizer: otherTeamMembers,
|
||||
apps: [TestData.apps["google-calendar"], TestData.apps["daily-video"]],
|
||||
apps: [TestData.apps["daily-video"]],
|
||||
})
|
||||
);
|
||||
|
||||
@@ -936,13 +914,6 @@ describe("handleNewBooking", () => {
|
||||
},
|
||||
});
|
||||
|
||||
const calendarMock = mockCalendarToHaveNoBusySlots("googlecalendar", {
|
||||
create: {
|
||||
id: "MOCKED_GOOGLE_CALENDAR_EVENT_ID",
|
||||
iCalUID: "MOCKED_GOOGLE_CALENDAR_ICS_ID",
|
||||
},
|
||||
});
|
||||
|
||||
const recurringCountInRequest = 4;
|
||||
const mockBookingData1 = getMockRequestDataForBooking({
|
||||
data: {
|
||||
|
||||
Reference in new issue
Block a user