From 675c54b01f6beef5bfd44e4d1b904a6a808670d1 Mon Sep 17 00:00:00 2001 From: Alex van Andel Date: Tue, 1 Oct 2024 11:42:35 +0100 Subject: [PATCH 1/6] chore: Couple fixes to improve error reporting --- packages/lib/server/defaultResponder.ts | 11 ++++++----- .../lib/server/getServerErrorFromUnknown.ts | 1 - packages/prisma/zod-utils.ts | 18 ++++++++++-------- 3 files changed, 16 insertions(+), 14 deletions(-) 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/prisma/zod-utils.ts b/packages/prisma/zod-utils.ts index 65a5c0b5260c67..4a185efaae5f5b 100644 --- a/packages/prisma/zod-utils.ts +++ b/packages/prisma/zod-utils.ts @@ -314,14 +314,16 @@ export const bookingCreateBodySchemaForApi = extendedBookingCreateBody.merge( bookingCreateSchemaLegacyPropsForApi.partial() ); -export const schemaBookingCancelParams = z.object({ - id: z.number().optional(), - uid: z.string().optional(), - allRemainingBookings: z.boolean().optional(), - cancellationReason: z.string().optional(), - seatReferenceUid: z.string().optional(), - cancelledBy: z.string().email({ message: "Invalid email" }).optional(), -}); +export const schemaBookingCancelParams = z + .object({ + id: z.number().optional(), + uid: z.string().optional(), + allRemainingBookings: z.boolean().optional(), + cancellationReason: z.string().optional(), + seatReferenceUid: z.string().optional(), + cancelledBy: z.string().email({ message: "Invalid email" }).optional(), + }) + .refine((data) => !!data.id || !!data.uid, "Required at least one of the following: 'id', 'uid'."); export const vitalSettingsUpdateSchema = z.object({ connected: z.boolean().optional(), From eba8fc7bb7ab966d49e51bc55e0e12bf1e9bf363 Mon Sep 17 00:00:00 2001 From: Alex van Andel Date: Tue, 1 Oct 2024 11:48:37 +0100 Subject: [PATCH 2/6] tweak: Fix error message --- packages/prisma/zod-utils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/prisma/zod-utils.ts b/packages/prisma/zod-utils.ts index 4a185efaae5f5b..9f014f689e8a9f 100644 --- a/packages/prisma/zod-utils.ts +++ b/packages/prisma/zod-utils.ts @@ -323,7 +323,7 @@ export const schemaBookingCancelParams = z seatReferenceUid: z.string().optional(), cancelledBy: z.string().email({ message: "Invalid email" }).optional(), }) - .refine((data) => !!data.id || !!data.uid, "Required at least one of the following: 'id', 'uid'."); + .refine((data) => !!data.id || !!data.uid, "At least one of the following required: 'id', 'uid'."); export const vitalSettingsUpdateSchema = z.object({ connected: z.boolean().optional(), From 6f7ebeb439eaa498cdf2eb7094a43a00ee3e78c6 Mon Sep 17 00:00:00 2001 From: Alex van Andel Date: Tue, 1 Oct 2024 12:07:37 +0100 Subject: [PATCH 3/6] refactor: Use findUniqueOrThrow instead of guard --- packages/features/bookings/lib/handleCancelBooking.ts | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/packages/features/bookings/lib/handleCancelBooking.ts b/packages/features/bookings/lib/handleCancelBooking.ts index ee88e04e0014a3..a1b841999089b5 100644 --- a/packages/features/bookings/lib/handleCancelBooking.ts +++ b/packages/features/bookings/lib/handleCancelBooking.ts @@ -43,7 +43,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, @@ -172,10 +172,6 @@ async function handler(req: CustomRequest) { arePlatformEmailsEnabled, } = req; - if (!bookingToDelete || !bookingToDelete.user) { - throw new HttpError({ statusCode: 400, message: "Booking not found" }); - } - if (!bookingToDelete.userId) { throw new HttpError({ statusCode: 400, message: "User not found" }); } From dad85eb5e325a29193ce5c2aeb1c75088f8e05dd Mon Sep 17 00:00:00 2001 From: Alex van Andel Date: Tue, 1 Oct 2024 12:25:36 +0100 Subject: [PATCH 4/6] fix: Broken test, missing ID - suspect it passes because only one record exists --- .../features/bookings/lib/handleSeats/test/handleSeats.test.ts | 1 + 1 file changed, 1 insertion(+) 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, From 8459d9a3c9d1b0a63119f406e84a6f10270f88c5 Mon Sep 17 00:00:00 2001 From: Alex van Andel Date: Tue, 1 Oct 2024 12:44:00 +0100 Subject: [PATCH 5/6] chore: Minor refactor to split ZodEffect from ZodType --- .../api/v1/pages/api/bookings/[id]/_delete.ts | 4 +-- .../getMockRequestDataForCancelBooking.ts | 4 +-- .../bookings/lib/handleCancelBooking.ts | 8 ++---- .../handleSeats/cancel/cancelAttendeeSeat.ts | 7 ++--- .../platform/atoms/hooks/useCancelBooking.ts | 4 +-- packages/prisma/zod-utils.ts | 27 ++++++++++++------- 6 files changed, 29 insertions(+), 25 deletions(-) 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 a1b841999089b5..913e573c1328e0 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"; @@ -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, 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/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 9f014f689e8a9f..32f1ac88efa691 100644 --- a/packages/prisma/zod-utils.ts +++ b/packages/prisma/zod-utils.ts @@ -314,16 +314,23 @@ export const bookingCreateBodySchemaForApi = extendedBookingCreateBody.merge( bookingCreateSchemaLegacyPropsForApi.partial() ); -export const schemaBookingCancelParams = z - .object({ - id: z.number().optional(), - uid: z.string().optional(), - allRemainingBookings: z.boolean().optional(), - cancellationReason: z.string().optional(), - seatReferenceUid: z.string().optional(), - cancelledBy: z.string().email({ message: "Invalid email" }).optional(), - }) - .refine((data) => !!data.id || !!data.uid, "At least one of the following required: 'id', 'uid'."); +export const bookingCancelSchema = z.object({ + id: z.number().optional(), + uid: z.string().optional(), + allRemainingBookings: z.boolean().optional(), + cancellationReason: z.string().optional(), + seatReferenceUid: z.string().optional(), + 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(), From 1751b548293f24c40134e6cdcdf97ff55f9bb314 Mon Sep 17 00:00:00 2001 From: Alex van Andel Date: Tue, 1 Oct 2024 12:55:17 +0100 Subject: [PATCH 6/6] fix: Satify typescript --- packages/features/bookings/lib/handleCancelBooking.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/features/bookings/lib/handleCancelBooking.ts b/packages/features/bookings/lib/handleCancelBooking.ts index 913e573c1328e0..a8bfe4530f1efe 100644 --- a/packages/features/bookings/lib/handleCancelBooking.ts +++ b/packages/features/bookings/lib/handleCancelBooking.ts @@ -168,7 +168,7 @@ async function handler(req: CustomRequest) { arePlatformEmailsEnabled, } = req; - if (!bookingToDelete.userId) { + if (!bookingToDelete.userId || !bookingToDelete.user) { throw new HttpError({ statusCode: 400, message: "User not found" }); }