perf: Use improved checkIfAvailable and remove dupe code (#18463)

This commit is contained in:
Alex van Andel
2025-01-06 09:53:39 +00:00
committed by GitHub
parent f9dc788007
commit 2fe4462f62
5 changed files with 223 additions and 255 deletions
@@ -0,0 +1,167 @@
import { describe, expect, it } from "vitest";
import type { CurrentSeats } from "@calcom/core/getUserAvailability";
import dayjs from "@calcom/dayjs";
import type { EventBusyDate } from "@calcom/types/Calendar";
import { checkForConflicts } from "./checkForConflicts";
describe("checkForConflicts", () => {
const createTestData = (time: string) => ({
time: dayjs(time),
eventLength: 30,
busy: [] as EventBusyDate[],
});
describe("currentSeats handling", () => {
it("should return false if slot exists in currentSeats", () => {
const currentSeats: CurrentSeats = [
{
uid: "123",
startTime: dayjs.utc("2023-01-01T09:00:00Z").toDate(),
_count: { attendees: 1 },
},
];
const result = checkForConflicts({
...createTestData("2023-01-01T09:00:00Z"),
currentSeats,
});
expect(result).toBe(false);
});
});
describe("busy time overlap scenarios", () => {
it("should return false when no busy periods", () => {
const result = checkForConflicts(createTestData("2023-01-01T09:00:00Z"));
expect(result).toBe(false);
});
it("should return false when busy period ends before slot starts", () => {
const result = checkForConflicts({
...createTestData("2023-01-01T09:00:00Z"),
busy: [
{
start: dayjs.utc("2023-01-01T08:00:00Z").toDate(),
end: dayjs.utc("2023-01-01T08:30:00Z").toDate(),
},
],
});
expect(result).toBe(false);
});
it("should return false when busy period starts after slot ends", () => {
const result = checkForConflicts({
...createTestData("2023-01-01T09:00:00Z"),
busy: [
{
start: dayjs.utc("2023-01-01T10:00:00Z").toDate(),
end: dayjs.utc("2023-01-01T10:30:00Z").toDate(),
},
],
});
expect(result).toBe(false);
});
it("should return true when slot start falls within busy period", () => {
const result = checkForConflicts({
...createTestData("2023-01-01T09:15:00Z"),
busy: [
{
start: dayjs.utc("2023-01-01T09:00:00Z").toDate(),
end: dayjs.utc("2023-01-01T09:30:00Z").toDate(),
},
],
});
expect(result).toBe(true);
});
it("should return true when slot end falls within busy period", () => {
const result = checkForConflicts({
...createTestData("2023-01-01T08:45:00Z"),
busy: [
{
start: dayjs.utc("2023-01-01T09:00:00Z").toDate(),
end: dayjs.utc("2023-01-01T09:30:00Z").toDate(),
},
],
});
expect(result).toBe(true);
});
it("should return true when busy period is completely contained within slot", () => {
const result = checkForConflicts({
...createTestData("2023-01-01T09:00:00Z"),
eventLength: 60,
busy: [
{
start: dayjs.utc("2023-01-01T09:15:00Z").toDate(),
end: dayjs.utc("2023-01-01T09:45:00Z").toDate(),
},
],
});
expect(result).toBe(true);
});
it("should return true when slot is completely contained within busy period", () => {
const result = checkForConflicts({
...createTestData("2023-01-01T09:15:00Z"),
busy: [
{
start: dayjs.utc("2023-01-01T09:00:00Z").toDate(),
end: dayjs.utc("2023-01-01T10:00:00Z").toDate(),
},
],
});
expect(result).toBe(true);
});
it("should return false when multiple non-overlapping busy periods", () => {
const result = checkForConflicts({
...createTestData("2023-01-01T09:30:00Z"),
busy: [
{
start: dayjs.utc("2023-01-01T08:00:00Z").toDate(),
end: dayjs.utc("2023-01-01T09:00:00Z").toDate(),
},
{
start: dayjs.utc("2023-01-01T10:00:00Z").toDate(),
end: dayjs.utc("2023-01-01T11:00:00Z").toDate(),
},
],
});
expect(result).toBe(false);
});
it("should return true if any busy period overlaps", () => {
const result = checkForConflicts({
...createTestData("2023-01-01T09:30:00Z"),
busy: [
{
start: dayjs.utc("2023-01-01T08:00:00Z").toDate(),
end: dayjs.utc("2023-01-01T09:00:00Z").toDate(),
},
{
start: dayjs.utc("2023-01-01T09:45:00Z").toDate(),
end: dayjs.utc("2023-01-01T10:00:00Z").toDate(),
},
],
});
expect(result).toBe(true);
});
it("should handle exact boundary conditions", () => {
const result = checkForConflicts({
...createTestData("2023-01-01T09:30:00Z"),
busy: [
{
start: dayjs.utc("2023-01-01T09:00:00Z").toDate(),
end: dayjs.utc("2023-01-01T09:30:00Z").toDate(),
},
],
});
expect(result).toBe(false);
});
});
});
@@ -1,45 +1,59 @@
import type { Dayjs } from "dayjs";
import type { CurrentSeats } from "@calcom/core/getUserAvailability";
import dayjs from "@calcom/dayjs";
import logger from "@calcom/lib/logger";
import { safeStringify } from "@calcom/lib/safeStringify";
import type { BufferedBusyTime } from "@calcom/types/BufferedBusyTime";
type BufferedBusyTimes = BufferedBusyTime[];
const log = logger.getSubLogger({ prefix: ["[api] book:user"] });
// if true, there are conflicts.
export function checkForConflicts(busyTimes: BufferedBusyTimes, time: dayjs.ConfigType, length: number) {
export function checkForConflicts({
busy,
time,
eventLength,
currentSeats,
}: {
busy: BufferedBusyTimes;
time: Dayjs;
eventLength: number;
currentSeats?: CurrentSeats;
}) {
// Early return
if (!Array.isArray(busyTimes) || busyTimes.length < 1) {
if (!Array.isArray(busy) || busy.length < 1) {
return false; // guaranteed no conflicts when there is no busy times.
}
for (const busyTime of busyTimes) {
const startTime = dayjs(busyTime.start);
const endTime = dayjs(busyTime.end);
// Check if time is between start and end times
if (dayjs(time).isBetween(startTime, endTime, null, "[)")) {
log.error(
`NAUF: start between a busy time slot ${safeStringify({
...busyTime,
time: dayjs(time).format(),
})}`
);
return true;
}
// Check if slot end time is between start and end time
if (dayjs(time).add(length, "minutes").isBetween(startTime, endTime)) {
log.error(
`NAUF: Ends between a busy time slot ${safeStringify({
...busyTime,
time: dayjs(time).add(length, "minutes").format(),
})}`
);
return true;
}
// Check if startTime is between slot
if (startTime.isBetween(dayjs(time), dayjs(time).add(length, "minutes"))) {
return true;
}
// no conflicts if some seats are found for the current time slot
if (currentSeats?.some((booking) => booking.startTime.toISOString() === time.toISOString())) {
return false;
}
return false;
const slotStartDate = time.utc().toDate();
const slotEndDate = time.add(eventLength, "minutes").utc().toDate();
// handle busy times.
return busy.some((busyTime) => {
const busyStartDate = dayjs.utc(busyTime.start).toDate();
const busyEndDate = dayjs.utc(busyTime.end).toDate();
// First check if there's any overlap at all
// If busy period ends before slot starts or starts after slot ends, there's no overlap
if (busyEndDate <= slotStartDate || busyStartDate >= slotEndDate) {
return false;
}
// Now check all possible overlap scenarios:
// 1. Slot start falls within busy period (inclusive start, exclusive end)
if (slotStartDate >= busyStartDate && slotStartDate < busyEndDate) {
return true;
}
// 2. Slot end falls within busy period (exclusive start, inclusive end)
if (slotEndDate > busyStartDate && slotEndDate <= busyEndDate) {
return true;
}
// 3. Busy period completely contained within slot
if (busyStartDate >= slotStartDate && busyEndDate <= slotEndDate) {
return true;
}
// 4. Slot completely contained within busy period
if (busyStartDate <= slotStartDate && busyEndDate >= slotEndDate) {
return true;
}
return false;
});
}
@@ -4,11 +4,11 @@ import { getBusyTimesForLimitChecks } from "@calcom/core/getBusyTimes";
import { getUsersAvailability } from "@calcom/core/getUserAvailability";
import dayjs from "@calcom/dayjs";
import type { Dayjs } from "@calcom/dayjs";
import { checkForConflicts } from "@calcom/features/bookings/lib/conflictChecker/checkForConflicts";
import { parseBookingLimit, parseDurationLimit } from "@calcom/lib";
import { ErrorCode } from "@calcom/lib/errorCodes";
import { safeStringify } from "@calcom/lib/safeStringify";
import { checkForConflicts } from "../conflictChecker/checkForConflicts";
import type { getEventTypeResponse } from "./getEventTypesFromDB";
import type { IsFixedAwareUser, BookingType } from "./types";
@@ -135,8 +135,11 @@ export async function ensureAvailableUsers(
}
try {
const foundConflict = checkForConflicts(bufferedBusyTimes, startDateTimeUtc, duration);
// no conflicts found, add to available users.
const foundConflict = checkForConflicts({
busy: bufferedBusyTimes,
time: startDateTimeUtc,
eventLength: duration,
});
if (!foundConflict) {
availableUsers.push(user);
}
@@ -1,10 +1,7 @@
import { describe, expect, it } from "vitest";
import type { GetAvailabilityUser } from "@calcom/core/getUserAvailability";
import dayjs from "@calcom/dayjs";
import type { EventBusyDate } from "@calcom/types/Calendar";
import { checkIfIsAvailable } from "./util";
import { getUsersWithCredentialsConsideringContactOwner } from "./util";
describe("getUsersWithCredentialsConsideringContactOwner", () => {
@@ -91,163 +88,3 @@ describe("getUsersWithCredentialsConsideringContactOwner", () => {
]);
});
});
describe("checkIfIsAvailable", () => {
const createTestData = (time: string) => ({
time: dayjs(time),
eventLength: 30,
busy: [] as EventBusyDate[],
});
describe("currentSeats handling", () => {
it("should return true if slot exists in currentSeats", () => {
const currentSeats: CurrentSeats = [
{
uid: "123",
startTime: dayjs.utc("2023-01-01T09:00:00Z").toDate(),
_count: { attendees: 1 },
},
];
const result = checkIfIsAvailable({
...createTestData("2023-01-01T09:00:00Z"),
currentSeats,
});
expect(result).toBe(true);
});
});
describe("busy time overlap scenarios", () => {
it("should return true when no busy periods", () => {
const result = checkIfIsAvailable(createTestData("2023-01-01T09:00:00Z"));
expect(result).toBe(true);
});
it("should return true when busy period ends before slot starts", () => {
const result = checkIfIsAvailable({
...createTestData("2023-01-01T09:00:00Z"),
busy: [
{
start: dayjs.utc("2023-01-01T08:00:00Z").toDate(),
end: dayjs.utc("2023-01-01T08:30:00Z").toDate(),
},
],
});
expect(result).toBe(true);
});
it("should return true when busy period starts after slot ends", () => {
const result = checkIfIsAvailable({
...createTestData("2023-01-01T09:00:00Z"),
busy: [
{
start: dayjs.utc("2023-01-01T10:00:00Z").toDate(),
end: dayjs.utc("2023-01-01T10:30:00Z").toDate(),
},
],
});
expect(result).toBe(true);
});
it("should return false when slot start falls within busy period", () => {
const result = checkIfIsAvailable({
...createTestData("2023-01-01T09:15:00Z"),
busy: [
{
start: dayjs.utc("2023-01-01T09:00:00Z").toDate(),
end: dayjs.utc("2023-01-01T09:30:00Z").toDate(),
},
],
});
expect(result).toBe(false);
});
it("should return false when slot end falls within busy period", () => {
const result = checkIfIsAvailable({
...createTestData("2023-01-01T08:45:00Z"),
busy: [
{
start: dayjs.utc("2023-01-01T09:00:00Z").toDate(),
end: dayjs.utc("2023-01-01T09:30:00Z").toDate(),
},
],
});
expect(result).toBe(false);
});
it("should return false when busy period is completely contained within slot", () => {
const result = checkIfIsAvailable({
...createTestData("2023-01-01T09:00:00Z"),
eventLength: 60,
busy: [
{
start: dayjs.utc("2023-01-01T09:15:00Z").toDate(),
end: dayjs.utc("2023-01-01T09:45:00Z").toDate(),
},
],
});
expect(result).toBe(false);
});
it("should return false when slot is completely contained within busy period", () => {
const result = checkIfIsAvailable({
...createTestData("2023-01-01T09:15:00Z"),
busy: [
{
start: dayjs.utc("2023-01-01T09:00:00Z").toDate(),
end: dayjs.utc("2023-01-01T10:00:00Z").toDate(),
},
],
});
expect(result).toBe(false);
});
it("should return true when multiple non-overlapping busy periods", () => {
const result = checkIfIsAvailable({
...createTestData("2023-01-01T09:30:00Z"),
busy: [
{
start: dayjs.utc("2023-01-01T08:00:00Z").toDate(),
end: dayjs.utc("2023-01-01T09:00:00Z").toDate(),
},
{
start: dayjs.utc("2023-01-01T10:00:00Z").toDate(),
end: dayjs.utc("2023-01-01T11:00:00Z").toDate(),
},
],
});
expect(result).toBe(true);
});
it("should return false if any busy period overlaps", () => {
const result = checkIfIsAvailable({
...createTestData("2023-01-01T09:30:00Z"),
busy: [
{
start: dayjs.utc("2023-01-01T08:00:00Z").toDate(),
end: dayjs.utc("2023-01-01T09:00:00Z").toDate(),
},
{
start: dayjs.utc("2023-01-01T09:45:00Z").toDate(),
end: dayjs.utc("2023-01-01T10:00:00Z").toDate(),
},
],
});
expect(result).toBe(false);
});
it("should handle exact boundary conditions", () => {
const result = checkIfIsAvailable({
...createTestData("2023-01-01T09:30:00Z"),
busy: [
{
start: dayjs.utc("2023-01-01T09:00:00Z").toDate(),
end: dayjs.utc("2023-01-01T09:30:00Z").toDate(),
},
],
});
expect(result).toBe(true);
});
});
});
@@ -11,6 +11,7 @@ import monitorCallbackAsync, { monitorCallbackSync } from "@calcom/core/sentryWr
import type { Dayjs } from "@calcom/dayjs";
import dayjs from "@calcom/dayjs";
import { getSlugOrRequestedSlug, orgDomainConfig } from "@calcom/ee/organizations/lib/orgDomains";
import { checkForConflicts } from "@calcom/features/bookings/lib/conflictChecker/checkForConflicts";
import { isEventTypeLoggingEnabled } from "@calcom/features/bookings/lib/isEventTypeLoggingEnabled";
import { getShouldServeCache } from "@calcom/features/calendar-cache/lib/getShouldServeCache";
import { parseBookingLimit, parseDurationLimit } from "@calcom/lib";
@@ -47,60 +48,6 @@ import { handleNotificationWhenNoSlots } from "./handleNotificationWhenNoSlots";
const log = logger.getSubLogger({ prefix: ["[slots/util]"] });
export const checkIfIsAvailable = ({
time,
busy,
eventLength,
currentSeats,
}: {
time: Dayjs;
busy: EventBusyDate[];
eventLength: number;
currentSeats?: CurrentSeats;
}): boolean => {
if (currentSeats?.some((booking) => booking.startTime.toISOString() === time.toISOString())) {
return true;
}
const slotStartDate = time.utc().toDate();
const slotEndDate = time.add(eventLength, "minutes").utc().toDate();
return busy.every((busyTime) => {
const busyStartDate = dayjs.utc(busyTime.start).toDate();
const busyEndDate = dayjs.utc(busyTime.end).toDate();
// First check if there's any overlap at all
// If busy period ends before slot starts or starts after slot ends, there's no overlap
if (busyEndDate <= slotStartDate || busyStartDate >= slotEndDate) {
return true;
}
// Now check all possible overlap scenarios:
// 1. Slot start falls within busy period (inclusive start, exclusive end)
if (slotStartDate >= busyStartDate && slotStartDate < busyEndDate) {
return false;
}
// 2. Slot end falls within busy period (exclusive start, inclusive end)
if (slotEndDate > busyStartDate && slotEndDate <= busyEndDate) {
return false;
}
// 3. Busy period completely contained within slot
if (busyStartDate >= slotStartDate && busyEndDate <= slotEndDate) {
return false;
}
// 4. Slot completely contained within busy period
if (busyStartDate <= slotStartDate && busyEndDate >= slotEndDate) {
return false;
}
return true;
});
};
async function getEventTypeId({
slug,
eventTypeSlug,
@@ -529,7 +476,7 @@ async function _getAvailableSlots({ input, ctx }: GetScheduleOptions): Promise<I
}, []);
if (
checkIfIsAvailable({
!checkForConflicts({
time: slot.time,
busy,
...availabilityCheckProps,