diff --git a/apps/api/v1/pages/api/bookings/[id]/_delete.ts b/apps/api/v1/pages/api/bookings/[id]/_delete.ts index 451c8d05d4f1dd..f3406317d7fef5 100644 --- a/apps/api/v1/pages/api/bookings/[id]/_delete.ts +++ b/apps/api/v1/pages/api/bookings/[id]/_delete.ts @@ -2,7 +2,7 @@ import type { NextApiRequest } from "next"; import handleCancelBooking from "@calcom/features/bookings/lib/handleCancelBooking"; import { defaultResponder } from "@calcom/lib/server"; -import { schemaBookingCancelParams } from "@calcom/prisma/zod-utils"; +import { bookingCancelSchema } from "@calcom/prisma/zod-utils"; import { schemaQueryIdParseInt } from "~/lib/validations/shared/queryIdTransformParseInt"; @@ -65,7 +65,7 @@ import { schemaQueryIdParseInt } from "~/lib/validations/shared/queryIdTransform */ async function handler(req: NextApiRequest) { const { id, allRemainingBookings, cancellationReason } = schemaQueryIdParseInt - .merge(schemaBookingCancelParams.pick({ allRemainingBookings: true, cancellationReason: true })) + .merge(bookingCancelSchema.pick({ allRemainingBookings: true, cancellationReason: true })) .parse({ ...req.query, allRemainingBookings: req.query.allRemainingBookings === "true", diff --git a/apps/web/test/utils/bookingScenario/getMockRequestDataForCancelBooking.ts b/apps/web/test/utils/bookingScenario/getMockRequestDataForCancelBooking.ts index fe7424a8f48f57..15741fd934de3e 100644 --- a/apps/web/test/utils/bookingScenario/getMockRequestDataForCancelBooking.ts +++ b/apps/web/test/utils/bookingScenario/getMockRequestDataForCancelBooking.ts @@ -1,7 +1,7 @@ import type z from "zod"; -import type { schemaBookingCancelParams } from "@calcom/prisma/zod-utils"; +import type { bookingCancelSchema } from "@calcom/prisma/zod-utils"; -export function getMockRequestDataForCancelBooking(data: z.infer) { +export function getMockRequestDataForCancelBooking(data: z.infer) { return data; } diff --git a/packages/features/bookings/lib/handleCancelBooking.ts b/packages/features/bookings/lib/handleCancelBooking.ts index ee88e04e0014a3..a8bfe4530f1efe 100644 --- a/packages/features/bookings/lib/handleCancelBooking.ts +++ b/packages/features/bookings/lib/handleCancelBooking.ts @@ -28,11 +28,7 @@ import prisma, { bookingMinimalSelect } from "@calcom/prisma"; import type { WebhookTriggerEvents } from "@calcom/prisma/enums"; import { BookingStatus } from "@calcom/prisma/enums"; import { credentialForCalendarServiceSelect } from "@calcom/prisma/selects/credential"; -import { - bookingMetadataSchema, - EventTypeMetaDataSchema, - schemaBookingCancelParams, -} from "@calcom/prisma/zod-utils"; +import { bookingMetadataSchema, EventTypeMetaDataSchema, bookingCancelInput } from "@calcom/prisma/zod-utils"; import type { EventTypeMetadata } from "@calcom/prisma/zod-utils"; import { getAllWorkflowsFromEventType } from "@calcom/trpc/server/routers/viewer/workflows/util"; import type { CalendarEvent } from "@calcom/types/Calendar"; @@ -43,7 +39,7 @@ import cancelAttendeeSeat from "./handleSeats/cancel/cancelAttendeeSeat"; const log = logger.getSubLogger({ prefix: ["handleCancelBooking"] }); async function getBookingToDelete(id: number | undefined, uid: string | undefined) { - return await prisma.booking.findUnique({ + return await prisma.booking.findUniqueOrThrow({ where: { id, uid, @@ -160,7 +156,7 @@ export type HandleCancelBookingResponse = { async function handler(req: CustomRequest) { const { id, uid, allRemainingBookings, cancellationReason, seatReferenceUid, cancelledBy } = - schemaBookingCancelParams.parse(req.body); + bookingCancelInput.parse(req.body); req.bookingToDelete = await getBookingToDelete(id, uid); const { bookingToDelete, @@ -172,11 +168,7 @@ async function handler(req: CustomRequest) { arePlatformEmailsEnabled, } = req; - if (!bookingToDelete || !bookingToDelete.user) { - throw new HttpError({ statusCode: 400, message: "Booking not found" }); - } - - if (!bookingToDelete.userId) { + if (!bookingToDelete.userId || !bookingToDelete.user) { throw new HttpError({ statusCode: 400, message: "User not found" }); } diff --git a/packages/features/bookings/lib/handleSeats/cancel/cancelAttendeeSeat.ts b/packages/features/bookings/lib/handleSeats/cancel/cancelAttendeeSeat.ts index 1fb10abb73c5b8..c12e49cff6f828 100644 --- a/packages/features/bookings/lib/handleSeats/cancel/cancelAttendeeSeat.ts +++ b/packages/features/bookings/lib/handleSeats/cancel/cancelAttendeeSeat.ts @@ -11,7 +11,7 @@ import { WorkflowRepository } from "@calcom/lib/server/repository/workflow"; import prisma from "@calcom/prisma"; import { WebhookTriggerEvents } from "@calcom/prisma/enums"; import { credentialForCalendarServiceSelect } from "@calcom/prisma/selects/credential"; -import { schemaBookingCancelParams } from "@calcom/prisma/zod-utils"; +import { bookingCancelAttendeeSeatSchema } from "@calcom/prisma/zod-utils"; import type { EventTypeMetadata } from "@calcom/prisma/zod-utils"; import type { CalendarEvent } from "@calcom/types/Calendar"; @@ -32,9 +32,10 @@ async function cancelAttendeeSeat( }, eventTypeMetadata: EventTypeMetadata ) { - const { seatReferenceUid } = schemaBookingCancelParams.parse(req.body); + const input = bookingCancelAttendeeSeatSchema.safeParse(req.body); const { webhooks, evt, eventTypeInfo } = dataForWebhooks; - if (!seatReferenceUid) return; + if (!input.success) return; + const { seatReferenceUid } = input.data; const bookingToDelete = req.bookingToDelete; if (!bookingToDelete?.attendees.length || bookingToDelete.attendees.length < 2) return; diff --git a/packages/features/bookings/lib/handleSeats/test/handleSeats.test.ts b/packages/features/bookings/lib/handleSeats/test/handleSeats.test.ts index 74fecd8a167d32..45324d62efb55b 100644 --- a/packages/features/bookings/lib/handleSeats/test/handleSeats.test.ts +++ b/packages/features/bookings/lib/handleSeats/test/handleSeats.test.ts @@ -2664,6 +2664,7 @@ describe("handleSeats", () => { const mockBookingData = getMockRequestDataForBooking({ data: { + id: firstBookingId, eventTypeId: 1, responses: { email: booker.email, diff --git a/packages/lib/server/defaultResponder.ts b/packages/lib/server/defaultResponder.ts index 86f283ac923771..24fafe6c684d39 100644 --- a/packages/lib/server/defaultResponder.ts +++ b/packages/lib/server/defaultResponder.ts @@ -17,12 +17,13 @@ export function defaultResponder(f: Handle) { return res.json(result); } } catch (err) { - console.error(err); const error = getServerErrorFromUnknown(err); - // dynamic import of Sentry so it's only loaded when something goes wrong. - const captureException = (await import("@sentry/nextjs")).captureException; - captureException(err); - // return API route response + // we don't want to report Bad Request errors to Sentry / console + if (!(error.statusCode >= 400 && error.statusCode < 500)) { + console.error(err); + const captureException = (await import("@sentry/nextjs")).captureException; + captureException(err); + } return res .status(error.statusCode) .json({ message: error.message, url: error.url, method: error.method }); diff --git a/packages/lib/server/getServerErrorFromUnknown.ts b/packages/lib/server/getServerErrorFromUnknown.ts index a1118be888e37b..c4d7819660fb3b 100644 --- a/packages/lib/server/getServerErrorFromUnknown.ts +++ b/packages/lib/server/getServerErrorFromUnknown.ts @@ -28,7 +28,6 @@ function parseZodErrorIssues(issues: ZodIssue[]): string { export function getServerErrorFromUnknown(cause: unknown): HttpError { if (isZodError(cause)) { - console.log("cause", cause); return new HttpError({ statusCode: 400, message: parseZodErrorIssues(cause.issues), diff --git a/packages/platform/atoms/hooks/useCancelBooking.ts b/packages/platform/atoms/hooks/useCancelBooking.ts index d1502a04489b26..b9e669cc072a3a 100644 --- a/packages/platform/atoms/hooks/useCancelBooking.ts +++ b/packages/platform/atoms/hooks/useCancelBooking.ts @@ -3,7 +3,7 @@ import type { z } from "zod"; import { SUCCESS_STATUS } from "@calcom/platform-constants"; import type { ApiResponse, ApiErrorResponse } from "@calcom/platform-types"; -import type { schemaBookingCancelParams } from "@calcom/prisma/zod-utils"; +import type { bookingCancelSchema } from "@calcom/prisma/zod-utils"; import http from "../lib/http"; @@ -12,7 +12,7 @@ interface IUseCancelBooking { onError?: (err: ApiErrorResponse | Error) => void; } -type inputParams = z.infer; +type inputParams = z.infer; export const useCancelBooking = ( { onSuccess, onError }: IUseCancelBooking = { diff --git a/packages/prisma/zod-utils.ts b/packages/prisma/zod-utils.ts index 65a5c0b5260c67..32f1ac88efa691 100644 --- a/packages/prisma/zod-utils.ts +++ b/packages/prisma/zod-utils.ts @@ -314,7 +314,7 @@ export const bookingCreateBodySchemaForApi = extendedBookingCreateBody.merge( bookingCreateSchemaLegacyPropsForApi.partial() ); -export const schemaBookingCancelParams = z.object({ +export const bookingCancelSchema = z.object({ id: z.number().optional(), uid: z.string().optional(), allRemainingBookings: z.boolean().optional(), @@ -323,6 +323,15 @@ export const schemaBookingCancelParams = z.object({ cancelledBy: z.string().email({ message: "Invalid email" }).optional(), }); +export const bookingCancelAttendeeSeatSchema = z.object({ + seatReferenceUid: z.string(), +}); + +export const bookingCancelInput = bookingCancelSchema.refine( + (data) => !!data.id || !!data.uid, + "At least one of the following required: 'id', 'uid'." +); + export const vitalSettingsUpdateSchema = z.object({ connected: z.boolean().optional(), selectedParam: z.string().optional(),