perf: Better perf by going event type first w/o booking (#21369)

* perf: Better perf by going event type first w/o booking

* minor fixup

* Use an IN with children instead of an OR with parent

* Turns out we already had a variable with the right values

* Make sure the right event type is used even for children

* few fixes

* Full select is needed to keep typings working

* Fix last remaining issue
This commit is contained in:
Alex van Andel
2025-05-16 17:55:57 -07:00
committed by GitHub
parent 4e02fe4251
commit badd9a5698
@@ -5,7 +5,7 @@ import { getBookerBaseUrl } from "@calcom/lib/getBookerUrl/server";
import { WorkflowRepository } from "@calcom/lib/server/repository/workflow";
import { getTimeFormatStringFromUserTimeFormat } from "@calcom/lib/timeFormat";
import { prisma } from "@calcom/prisma";
import { BookingStatus } from "@calcom/prisma/client";
import { BookingStatus } from "@calcom/prisma/enums";
import { MembershipRole, SchedulingType, WorkflowActions, WorkflowTriggerEvents } from "@calcom/prisma/enums";
import { EventTypeMetaDataSchema } from "@calcom/prisma/zod-utils";
import type { TrpcSessionUser } from "@calcom/trpc/server/types";
@@ -26,7 +26,7 @@ export const activateEventTypeHandler = async ({ ctx, input }: ActivateEventType
const { eventTypeId, workflowId } = input;
// Check that event type belong to the user or team
const userEventType = await prisma.eventType.findFirst({
const eventType = await prisma.eventType.findFirst({
where: {
id: eventTypeId,
OR: [
@@ -47,30 +47,65 @@ export const activateEventTypeHandler = async ({ ctx, input }: ActivateEventType
],
},
select: {
id: true,
teamId: true,
hideOrganizerEmail: true,
customReplyToEmail: true,
schedulingType: true,
slug: true,
hosts: {
select: {
user: {
select: {
email: true,
destinationCalendar: {
select: {
primaryEmail: true,
},
},
},
},
},
},
children: {
select: {
id: true,
teamId: true,
hideOrganizerEmail: true,
customReplyToEmail: true,
schedulingType: true,
slug: true,
hosts: {
select: {
user: {
select: {
email: true,
destinationCalendar: {
select: {
primaryEmail: true,
},
},
},
},
},
},
},
},
},
});
if (!userEventType)
if (!eventType)
throw new TRPCError({ code: "UNAUTHORIZED", message: "Not authorized to edit this event type" });
// at this point we know that the event type belongs to the user or team
// so we don't use OR, we use logic.
const whereClause = eventType.teamId ? { teamId: eventType.teamId } : { userId: ctx.user.id };
// Check that the workflow belongs to the user or team
const eventTypeWorkflow = await prisma.workflow.findFirst({
where: {
id: workflowId,
OR: [
{
userId: ctx.user.id,
},
{
teamId: userEventType.teamId || undefined,
},
],
...whereClause,
},
select: {
steps: {
@@ -117,7 +152,12 @@ export const activateEventTypeHandler = async ({ ctx, input }: ActivateEventType
const isOrg = eventTypeWorkflow.team?.isOrganization ?? false;
const activeOn = [eventTypeId].concat(userEventType.children.map((ch) => ch.id));
const activeOnEventTypes = new Map<number, typeof eventType>([
[eventType.id, eventType],
...(eventType.children
? eventType.children.map((child) => [child.id, child] as [number, typeof eventType])
: []),
]);
if (isActive) {
// disable workflow for this event type & delete all reminders
@@ -146,7 +186,7 @@ export const activateEventTypeHandler = async ({ ctx, input }: ActivateEventType
await prisma.workflowsOnEventTypes.deleteMany({
where: {
workflowId,
eventTypeId: { in: [eventTypeId].concat(userEventType.children.map((ch) => ch.id)) },
eventTypeId: { in: Array.from(activeOnEventTypes.keys()) },
},
});
@@ -207,7 +247,11 @@ export const activateEventTypeHandler = async ({ ctx, input }: ActivateEventType
});
}
}
await removeSmsReminderFieldForEventTypes({ activeOnToRemove: activeOn, workflowId, isOrg });
await removeSmsReminderFieldForEventTypes({
activeOnToRemove: Array.from(activeOnEventTypes.keys()),
workflowId,
isOrg,
});
} else {
if (
eventTypeWorkflow.trigger == WorkflowTriggerEvents.BEFORE_EVENT ||
@@ -216,20 +260,16 @@ export const activateEventTypeHandler = async ({ ctx, input }: ActivateEventType
// activate workflow and schedule reminders for existing bookings
const bookingsForReminders = await prisma.booking.findMany({
where: {
OR: [
{ eventTypeId },
{
eventType: {
parentId: eventTypeId,
},
},
],
eventTypeId: {
in: Array.from(activeOnEventTypes.keys()),
},
status: BookingStatus.ACCEPTED,
startTime: {
gte: new Date(),
},
},
select: {
eventTypeId: true,
metadata: true,
userId: true,
smsReminderNumber: true,
@@ -246,28 +286,6 @@ export const activateEventTypeHandler = async ({ ctx, input }: ActivateEventType
locale: true,
},
},
eventType: {
select: {
schedulingType: true,
slug: true,
customReplyToEmail: true,
hosts: {
select: {
user: {
select: {
email: true,
destinationCalendar: {
select: {
primaryEmail: true,
},
},
},
},
},
},
hideOrganizerEmail: true,
},
},
user: {
select: {
name: true,
@@ -283,6 +301,8 @@ export const activateEventTypeHandler = async ({ ctx, input }: ActivateEventType
const bookerUrl = await getBookerBaseUrl(ctx.user.organizationId ?? null);
for (const booking of bookingsForReminders) {
// eventTypeId is technically nullable but we know it will be there
const bookingEventType = activeOnEventTypes.get(booking.eventTypeId!);
const defaultLocale = "en";
const bookingInfo = {
uid: booking.uid,
@@ -308,14 +328,14 @@ export const activateEventTypeHandler = async ({ ctx, input }: ActivateEventType
endTime: booking.endTime.toISOString(),
title: booking.title,
language: { locale: booking?.user?.locale || defaultLocale },
hideOrganizerEmail: booking.eventType?.hideOrganizerEmail,
hideOrganizerEmail: bookingEventType?.hideOrganizerEmail,
eventType: {
slug: booking.eventType?.slug || "",
schedulingType: booking.eventType?.schedulingType,
hosts: booking.eventType?.hosts,
slug: bookingEventType?.slug || "",
schedulingType: bookingEventType?.schedulingType,
hosts: bookingEventType?.hosts,
},
metadata: booking.metadata,
customReplyToEmail: booking.eventType?.customReplyToEmail,
customReplyToEmail: bookingEventType?.customReplyToEmail,
};
for (const step of eventTypeWorkflow.steps) {
if (
@@ -449,12 +469,10 @@ export const activateEventTypeHandler = async ({ ctx, input }: ActivateEventType
}
await prisma.workflowsOnEventTypes.createMany({
data: [
{
workflowId,
eventTypeId,
},
].concat(userEventType.children.map((ch) => ({ workflowId, eventTypeId: ch.id }))),
data: Array.from(activeOnEventTypes).map(([eventTypeId]) => ({
workflowId,
eventTypeId,
})),
});
const requiresAttendeeNumber = (action: WorkflowActions) =>
action === WorkflowActions.SMS_ATTENDEE || action === WorkflowActions.WHATSAPP_ATTENDEE;
@@ -464,7 +482,12 @@ export const activateEventTypeHandler = async ({ ctx, input }: ActivateEventType
return requiresAttendeeNumber(step.action) && step.numberRequired;
});
await upsertSmsReminderFieldForEventTypes({ activeOn, workflowId, isSmsReminderNumberRequired, isOrg });
await upsertSmsReminderFieldForEventTypes({
activeOn: Array.from(activeOnEventTypes.keys()),
workflowId,
isSmsReminderNumberRequired,
isOrg,
});
}
}
};