import type { Prisma } from "@prisma/client"; import type { IncomingMessage } from "http"; import type { Logger } from "tslog"; import { filterHostsByLeadThreshold } from "@calcom/lib/bookings/filterHostsByLeadThreshold"; import { HttpError } from "@calcom/lib/http-error"; import { getPiiFreeUser } from "@calcom/lib/piiFreeData"; import { safeStringify } from "@calcom/lib/safeStringify"; import { userSelect } from "@calcom/prisma"; import prisma from "@calcom/prisma"; import { SchedulingType } from "@calcom/prisma/enums"; import { credentialForCalendarServiceSelect } from "@calcom/prisma/selects/credential"; import { loadUsers } from "./loadUsers"; import type { NewBookingEventType } from "./types"; type Users = (Awaited>[number] & { isFixed?: boolean; metadata?: Prisma.JsonValue; createdAt?: Date; })[]; type EventType = Pick< NewBookingEventType, "hosts" | "users" | "id" | "userId" | "schedulingType" | "maxLeadThreshold" >; type InputProps = { req: IncomingMessage; eventType: EventType; eventTypeId: number; dynamicUserList: string[]; logger: Logger; routedTeamMemberIds: number[] | null; contactOwnerEmail: string | null; }; export async function loadAndValidateUsers({ req, eventType, eventTypeId, dynamicUserList, logger, routedTeamMemberIds, contactOwnerEmail, }: InputProps): Promise { let users: Users = await loadUsers({ eventType, dynamicUserList, req, routedTeamMemberIds, contactOwnerEmail, }); const isDynamicAllowed = !users.some((user) => !user.allowDynamicBooking); if (!isDynamicAllowed && !eventTypeId) { logger.warn({ message: "NewBooking: Some of the users in this group do not allow dynamic booking", }); throw new HttpError({ message: "Some of the users in this group do not allow dynamic booking", statusCode: 400, }); } // If this event was pre-relationship migration // TODO: Establish whether this is dead code. if (!users.length && eventType.userId) { const eventTypeUser = await prisma.user.findUnique({ where: { id: eventType.userId, }, select: { credentials: { select: credentialForCalendarServiceSelect, }, // Don't leak to client ...userSelect.select, }, }); if (!eventTypeUser) { logger.warn({ message: "NewBooking: eventTypeUser.notFound" }); throw new HttpError({ statusCode: 404, message: "eventTypeUser.notFound" }); } users.push(eventTypeUser); } if (!users) throw new HttpError({ statusCode: 404, message: "eventTypeUser.notFound" }); // map fixed users users = users.map((user) => ({ ...user, isFixed: user.isFixed === false ? false : user.isFixed || eventType.schedulingType !== SchedulingType.ROUND_ROBIN, })); const qualifiedHosts = await filterHostsByLeadThreshold({ eventTypeId: eventType.id, hosts: eventType.hosts.map((host) => ({ isFixed: host.isFixed, createdAt: host.createdAt, email: host.user.email, user: host.user, })), maxLeadThreshold: eventType.maxLeadThreshold, }); if (qualifiedHosts.length) { // remove users that are not in the qualified hosts array const qualifiedHostIds = new Set(qualifiedHosts.map((qualifiedHost) => qualifiedHost.user.id)); users = users.filter((user) => qualifiedHostIds.has(user.id)); } logger.debug( "Concerned users", safeStringify({ users: users.map(getPiiFreeUser), }) ); return users; }