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:
@@ -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,
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user