perf: pre-load booking data for all users (#11243)
* perf: pre-load booking data for all users * Fixing property issues * Fixes * Removed unwanted console.log * Made comment for intention of refactor more clear * fix: types * _count is optional * Assign to const bookings * Added mock for prisma booking * Fixed unit tests * Added lint ignore for prisma field * Update bookingScenario.ts * Fix linting --------- Co-authored-by: Alex van Andel <me@alexvanandel.com>pull/11305/head^2
parent
9fba8c548b
commit
84408025ed
|
@ -8,7 +8,7 @@ import prisma from "@calcom/prisma";
|
|||
import type { BookingStatus } from "@calcom/prisma/enums";
|
||||
import type { Slot } from "@calcom/trpc/server/routers/viewer/slots/types";
|
||||
import { getAvailableSlots as getSchedule } from "@calcom/trpc/server/routers/viewer/slots/util";
|
||||
import { getDate, getGoogleCalendarCredential, createBookingScenario} from "../utils/bookingScenario";
|
||||
import { getDate, getGoogleCalendarCredential, createBookingScenario } from "../utils/bookingScenario";
|
||||
|
||||
// TODO: Mock properly
|
||||
prismaMock.eventType.findUnique.mockResolvedValue(null);
|
||||
|
@ -1139,4 +1139,3 @@ describe("getSchedule", () => {
|
|||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -194,10 +194,17 @@ async function addBookings(bookings: InputBooking[], eventTypes: InputEventType[
|
|||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
const statusIn = where.OR[0].status?.in || [];
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
const userIdIn = where.OR[0].userId?.in || [];
|
||||
const firstConditionMatches =
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
statusIn.includes(booking.status) && booking.userId === where.OR[0].userId;
|
||||
statusIn.includes(booking.status) &&
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
(booking.userId === where.OR[0].userId || userIdIn.includes(booking.userId));
|
||||
|
||||
// We return this booking if either condition is met
|
||||
return firstConditionMatches;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import type { Credential } from "@prisma/client";
|
||||
import type { Booking, Credential, EventType } from "@prisma/client";
|
||||
|
||||
import { getBusyCalendarTimes } from "@calcom/core/CalendarManager";
|
||||
import dayjs from "@calcom/dayjs";
|
||||
|
@ -24,6 +24,17 @@ export async function getBusyTimes(params: {
|
|||
seatedEvent?: boolean;
|
||||
rescheduleUid?: string | null;
|
||||
duration?: number | null;
|
||||
currentBookings?:
|
||||
| (Pick<Booking, "id" | "uid" | "userId" | "startTime" | "endTime" | "title"> & {
|
||||
eventType: Pick<
|
||||
EventType,
|
||||
"id" | "beforeEventBuffer" | "afterEventBuffer" | "seatsPerTimeSlot"
|
||||
> | null;
|
||||
_count?: {
|
||||
seatsReferences: number;
|
||||
};
|
||||
})[]
|
||||
| null;
|
||||
}) {
|
||||
const {
|
||||
credentials,
|
||||
|
@ -40,6 +51,7 @@ export async function getBusyTimes(params: {
|
|||
rescheduleUid,
|
||||
duration,
|
||||
} = params;
|
||||
|
||||
logger.silly(
|
||||
`Checking Busy time from Cal Bookings in range ${startTime} to ${endTime} for input ${JSON.stringify({
|
||||
userId,
|
||||
|
@ -76,8 +88,14 @@ export async function getBusyTimes(params: {
|
|||
in: [BookingStatus.ACCEPTED],
|
||||
},
|
||||
};
|
||||
// Find bookings that block this user from hosting further bookings.
|
||||
const bookings = await prisma.booking.findMany({
|
||||
|
||||
// INFO: Refactored to allow this method to take in a list of current bookings for the user.
|
||||
// Will keep support for retrieving a user's bookings if the caller does not already supply them.
|
||||
// This function is called from multiple places but we aren't refactoring all of them at this moment
|
||||
// to avoid potential side effects.
|
||||
const bookings = params.currentBookings
|
||||
? params.currentBookings
|
||||
: await prisma.booking.findMany({
|
||||
where: {
|
||||
OR: [
|
||||
// User is primary host (individual events, or primary organizer)
|
||||
|
@ -99,6 +117,7 @@ export async function getBusyTimes(params: {
|
|||
select: {
|
||||
id: true,
|
||||
uid: true,
|
||||
userId: true,
|
||||
startTime: true,
|
||||
endTime: true,
|
||||
title: true,
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import type { Prisma } from "@prisma/client";
|
||||
import type { Booking, Prisma, EventType as PrismaEventType } from "@prisma/client";
|
||||
import { z } from "zod";
|
||||
|
||||
import type { Dayjs } from "@calcom/dayjs";
|
||||
|
@ -128,6 +128,15 @@ export const getUserAvailability = async function getUsersWorkingHoursLifeTheUni
|
|||
eventType?: EventType;
|
||||
currentSeats?: CurrentSeats;
|
||||
rescheduleUid?: string | null;
|
||||
currentBookings?: (Pick<Booking, "id" | "uid" | "userId" | "startTime" | "endTime" | "title"> & {
|
||||
eventType: Pick<
|
||||
PrismaEventType,
|
||||
"id" | "beforeEventBuffer" | "afterEventBuffer" | "seatsPerTimeSlot"
|
||||
> | null;
|
||||
_count?: {
|
||||
seatsReferences: number;
|
||||
};
|
||||
})[];
|
||||
}
|
||||
) {
|
||||
const { username, userId, dateFrom, dateTo, eventTypeId, afterEventBuffer, beforeEventBuffer, duration } =
|
||||
|
@ -188,6 +197,7 @@ export const getUserAvailability = async function getUsersWorkingHoursLifeTheUni
|
|||
seatedEvent: !!eventType?.seatsPerTimeSlot,
|
||||
rescheduleUid: initialData?.rescheduleUid || null,
|
||||
duration,
|
||||
currentBookings: initialData?.currentBookings,
|
||||
});
|
||||
|
||||
const detailedBusyTimes: EventBusyDetails[] = [
|
||||
|
|
|
@ -15,6 +15,7 @@ import { performance } from "@calcom/lib/server/perfObserver";
|
|||
import getSlots from "@calcom/lib/slots";
|
||||
import prisma, { availabilityUserSelect } from "@calcom/prisma";
|
||||
import { SchedulingType } from "@calcom/prisma/enums";
|
||||
import { BookingStatus } from "@calcom/prisma/enums";
|
||||
import { EventTypeMetaDataSchema } from "@calcom/prisma/zod-utils";
|
||||
import type { EventBusyDate } from "@calcom/types/Calendar";
|
||||
|
||||
|
@ -291,6 +292,73 @@ export async function getAvailableSlots({ input, ctx }: GetScheduleOptions) {
|
|||
usersWithCredentials = eventType.hosts.map(({ isFixed, user }) => ({ isFixed, ...user }));
|
||||
}
|
||||
|
||||
const durationToUse = input.duration || 0;
|
||||
|
||||
const startTimeDate =
|
||||
input.rescheduleUid && durationToUse
|
||||
? startTime.subtract(durationToUse, "minute").toDate()
|
||||
: startTime.toDate();
|
||||
|
||||
const endTimeDate =
|
||||
input.rescheduleUid && durationToUse ? endTime.add(durationToUse, "minute").toDate() : endTime.toDate();
|
||||
|
||||
const sharedQuery = {
|
||||
startTime: { gte: startTimeDate },
|
||||
endTime: { lte: endTimeDate },
|
||||
status: {
|
||||
in: [BookingStatus.ACCEPTED],
|
||||
},
|
||||
};
|
||||
|
||||
const currentBookingsAllUsers = await prisma.booking.findMany({
|
||||
where: {
|
||||
OR: [
|
||||
// User is primary host (individual events, or primary organizer)
|
||||
{
|
||||
...sharedQuery,
|
||||
userId: {
|
||||
in: usersWithCredentials.map((user) => user.id),
|
||||
},
|
||||
},
|
||||
// The current user has a different booking at this time he/she attends
|
||||
{
|
||||
...sharedQuery,
|
||||
attendees: {
|
||||
some: {
|
||||
email: {
|
||||
in: usersWithCredentials.map((user) => user.email),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
uid: true,
|
||||
userId: true,
|
||||
startTime: true,
|
||||
endTime: true,
|
||||
title: true,
|
||||
attendees: true,
|
||||
eventType: {
|
||||
select: {
|
||||
id: true,
|
||||
afterEventBuffer: true,
|
||||
beforeEventBuffer: true,
|
||||
seatsPerTimeSlot: true,
|
||||
},
|
||||
},
|
||||
...(!!eventType?.seatsPerTimeSlot && {
|
||||
_count: {
|
||||
select: {
|
||||
seatsReferences: true,
|
||||
},
|
||||
},
|
||||
}),
|
||||
},
|
||||
});
|
||||
|
||||
/* We get all users working hours and busy slots */
|
||||
const userAvailability = await Promise.all(
|
||||
usersWithCredentials.map(async (currentUser) => {
|
||||
|
@ -310,7 +378,20 @@ export async function getAvailableSlots({ input, ctx }: GetScheduleOptions) {
|
|||
beforeEventBuffer: eventType.beforeEventBuffer,
|
||||
duration: input.duration || 0,
|
||||
},
|
||||
{ user: currentUser, eventType, currentSeats, rescheduleUid: input.rescheduleUid }
|
||||
{
|
||||
user: currentUser,
|
||||
eventType,
|
||||
currentSeats,
|
||||
rescheduleUid: input.rescheduleUid,
|
||||
currentBookings: currentBookingsAllUsers
|
||||
.filter(
|
||||
(b) => b.userId === currentUser.id || b.attendees?.some((a) => a.email === currentUser.email)
|
||||
)
|
||||
.map((bookings) => {
|
||||
const { attendees: _attendees, ...bookingWithoutAttendees } = bookings;
|
||||
return bookingWithoutAttendees;
|
||||
}),
|
||||
}
|
||||
);
|
||||
if (!currentSeats && _currentSeats) currentSeats = _currentSeats;
|
||||
return {
|
||||
|
|
Loading…
Reference in New Issue