Files
calendar/packages/features/bookings/lib/getCalEventResponses.ts
T
Hariom BalharaGitHubDevin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>Udit Takkar
cf7430d531 fix: convert booking field label error to log.error to prevent booking failures (#25062)
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: Udit Takkar <53316345+Udit-takkar@users.noreply.github.com>
2025-11-19 13:30:46 +00:00

116 lines
4.1 KiB
TypeScript

import type z from "zod";
import type { bookingResponsesDbSchema } from "@calcom/features/bookings/lib/getBookingResponsesSchema";
import { SystemField } from "@calcom/lib/bookings/SystemField";
import { contructEmailFromPhoneNumber } from "@calcom/lib/contructEmailFromPhoneNumber";
import { getBookingWithResponses } from "@calcom/lib/getBooking";
import { HttpError } from "@calcom/lib/http-error";
import logger from "@calcom/lib/logger";
import type { EventType, Prisma } from "@calcom/prisma/client";
import { eventTypeBookingFields } from "@calcom/prisma/zod-utils";
import type { CalendarEvent } from "@calcom/types/Calendar";
const log = logger.getSubLogger({ prefix: ["[getCalEventResponses]"] });
export const getCalEventResponses = ({
bookingFields,
booking,
responses,
seatsEnabled,
}: {
// If the eventType has been deleted and a booking is Accepted later on, then bookingFields will be null and we can't know the label of fields. So, we should store the label as well in the DB
// Also, it is no longer straightforward to identify if a field is system field or not
bookingFields: z.infer<typeof eventTypeBookingFields> | EventType["bookingFields"] | null;
booking?: Prisma.BookingGetPayload<{
select: {
description: true;
customInputs: true;
attendees: {
select: {
email: true;
name: true;
};
};
location: true;
responses: true;
};
}>;
responses?: z.infer<typeof bookingResponsesDbSchema>;
seatsEnabled?: boolean;
}) => {
const calEventUserFieldsResponses = {} as NonNullable<CalendarEvent["userFieldsResponses"]>;
const calEventResponses = {} as NonNullable<CalendarEvent["responses"]>;
const parsedBookingFields = bookingFields ? eventTypeBookingFields.parse(bookingFields) : null;
const backwardCompatibleResponses =
responses ?? (booking ? getBookingWithResponses(booking).responses : null);
if (!backwardCompatibleResponses) throw new Error("Couldn't get responses");
// To set placeholder email for the booking
if (!backwardCompatibleResponses.email) {
if (typeof backwardCompatibleResponses["attendeePhoneNumber"] !== "string") {
log.error(`backwardCompatibleResponses: ${JSON.stringify(backwardCompatibleResponses)}`, {
responses,
bookingResponses: booking?.responses,
});
throw new HttpError({
statusCode: 400,
message: "Both Phone and Email are missing",
});
}
backwardCompatibleResponses.email = contructEmailFromPhoneNumber(
backwardCompatibleResponses["attendeePhoneNumber"]
);
}
if (parsedBookingFields) {
parsedBookingFields.forEach((field) => {
const label = field.label || field.defaultLabel;
if (!label) {
//TODO: This error must be thrown while saving event-type so that such an event-type can't be saved
log.error(`Missing label for booking field "${field.name}"`);
return;
}
if (field.name == "guests" && !!seatsEnabled) {
backwardCompatibleResponses[field.name] = [];
}
if (field.editable === "user" || field.editable === "user-readonly") {
calEventUserFieldsResponses[field.name] = {
label,
value: backwardCompatibleResponses[field.name],
isHidden: !!field.hidden,
};
}
calEventResponses[field.name] = {
label,
value: backwardCompatibleResponses[field.name],
isHidden: !!field.hidden,
};
});
} else {
// Alternative way to generate for a booking of whose eventType has been deleted
for (const [name, value] of Object.entries(backwardCompatibleResponses)) {
const isSystemField = SystemField.safeParse(name);
// Use name for Label because we don't have access to the label. This will not be needed once we start storing the label along with the response
const label = name;
if (!isSystemField.success) {
calEventUserFieldsResponses[name] = {
label,
value,
};
}
calEventResponses[name] = {
label,
value,
};
}
}
return { userFieldsResponses: calEventUserFieldsResponses, responses: calEventResponses };
};