diff --git a/packages/features/bookings/lib/handleNewBooking.ts b/packages/features/bookings/lib/handleNewBooking.ts index f476a00cd5..6acca0a5a5 100644 --- a/packages/features/bookings/lib/handleNewBooking.ts +++ b/packages/features/bookings/lib/handleNewBooking.ts @@ -53,7 +53,7 @@ import { updateWebUser as syncServicesUpdateWebUser } from "@calcom/lib/sync/Syn import { TimeFormat } from "@calcom/lib/timeFormat"; import prisma, { userSelect } from "@calcom/prisma"; import type { BookingReference } from "@calcom/prisma/client"; -import type { bookingCreateSchemaLegacyPropsForApi } from "@calcom/prisma/zod-utils"; +import { bookingCreateSchemaLegacyPropsForApi } from "@calcom/prisma/zod-utils"; import { bookingCreateBodySchemaForApi, customInputSchema, @@ -394,21 +394,81 @@ function getBookingData({ isNotAnApiCall: boolean; eventType: Awaited>; }) { + const responsesSchema = getBookingResponsesSchema({ + eventType: { + bookingFields: eventType.bookingFields, + }, + view: req.body.rescheduleUid ? "reschedule" : "booking", + }); const bookingDataSchema = isNotAnApiCall ? extendedBookingCreateBody.merge( z.object({ - responses: getBookingResponsesSchema({ - eventType: { - bookingFields: eventType.bookingFields, - }, - view: req.body.rescheduleUid ? "reschedule" : "booking", - }), + responses: responsesSchema, }) ) - : bookingCreateBodySchemaForApi; + : bookingCreateBodySchemaForApi + .merge( + z.object({ + responses: responsesSchema.optional(), + }) + ) + .superRefine((val, ctx) => { + if (val.responses && val.customInputs) { + ctx.addIssue({ + code: "custom", + message: + "Don't use both customInputs and responses. `customInputs` is only there for legacy support.", + }); + return; + } + const legacyProps = Object.keys(bookingCreateSchemaLegacyPropsForApi.shape); + + if (val.responses) { + const unwantedProps: string[] = []; + legacyProps.forEach((legacyProp) => { + if (val[legacyProp as keyof typeof val]) { + unwantedProps.push(legacyProp); + } + }); + if (unwantedProps.length) { + ctx.addIssue({ + code: "custom", + message: `Legacy Props: ${unwantedProps.join(",")}. They can't be used with \`responses\``, + }); + return; + } + } else if (val.customInputs) { + const { success } = bookingCreateSchemaLegacyPropsForApi.safeParse(val); + if (!success) { + ctx.addIssue({ + code: "custom", + message: `With \`customInputs\` you must specify legacy props ${legacyProps.join(",")}`, + }); + } + } + }); const reqBody = bookingDataSchema.parse(req.body); - if ("responses" in reqBody) { + if ("customInputs" in reqBody) { + if (reqBody.customInputs) { + // Check if required custom inputs exist + handleCustomInputs(eventType.customInputs as EventTypeCustomInput[], reqBody.customInputs); + } + const reqBodyWithLegacyProps = bookingCreateSchemaLegacyPropsForApi.parse(reqBody); + return { + ...reqBody, + name: reqBodyWithLegacyProps.name, + email: reqBodyWithLegacyProps.email, + guests: reqBodyWithLegacyProps.guests, + location: reqBodyWithLegacyProps.location || "", + smsReminderNumber: reqBodyWithLegacyProps.smsReminderNumber, + notes: reqBodyWithLegacyProps.notes, + rescheduleReason: reqBodyWithLegacyProps.rescheduleReason, + }; + } else { + if (!reqBody.responses) { + throw new Error("`responses` must not be nullish"); + } const responses = reqBody.responses; const { userFieldsResponses: calEventUserFieldsResponses, responses: calEventResponses } = getCalEventResponses({ @@ -427,20 +487,6 @@ function getBookingData({ rescheduleReason: responses.rescheduleReason, calEventResponses, }; - } else { - // Check if required custom inputs exist - handleCustomInputs(eventType.customInputs as EventTypeCustomInput[], reqBody.customInputs); - - return { - ...reqBody, - name: reqBody.name, - email: reqBody.email, - guests: reqBody.guests, - location: reqBody.location || "", - smsReminderNumber: reqBody.smsReminderNumber, - notes: reqBody.notes, - rescheduleReason: reqBody.rescheduleReason, - }; } } diff --git a/packages/prisma/zod-utils.ts b/packages/prisma/zod-utils.ts index fb6734f615..7bb69aca47 100644 --- a/packages/prisma/zod-utils.ts +++ b/packages/prisma/zod-utils.ts @@ -220,7 +220,7 @@ export const bookingCreateSchemaLegacyPropsForApi = z.object({ // This is the schema that is used for the API. It has all the legacy props that are part of `responses` now. export const bookingCreateBodySchemaForApi = extendedBookingCreateBody.merge( - bookingCreateSchemaLegacyPropsForApi + bookingCreateSchemaLegacyPropsForApi.partial() ); export const schemaBookingCancelParams = z.object({