From bf0fbe488845fa74656b85d2524a66d41fc9939b Mon Sep 17 00:00:00 2001 From: Morgan <33722304+ThyMinimalDev@users.noreply.github.com> Date: Wed, 5 Nov 2025 13:07:43 +0200 Subject: [PATCH] feat: skip authentication check for reschedule bookings with validation (#24903) * feat: skip authentication check for reschedule bookings with validation - Skip checkBookingRequiresAuthentication when rescheduleUid is present - Add validation to ensure rescheduleUid points to a real booking - Verify booking status is ACCEPTED (upcoming) - Verify booking uses the same event-type - Throw appropriate errors for invalid reschedule attempts Co-Authored-By: morgan@cal.com * refactor: move reschedule validation logic into checkBookingRequiresAuthentication - Refactor checkBookingRequiresAuthentication to accept optional rescheduleUid parameter - Move reschedule booking validation logic inside the method - Simplify createBooking method by removing duplicate validation code - Maintain same validation logic: check booking exists, is ACCEPTED, and uses same event-type Co-Authored-By: morgan@cal.com * fix: allow PENDING bookings to be rescheduled - Update status validation to allow both ACCEPTED and PENDING bookings - Change error message to reflect both allowed statuses - PENDING bookings can now be rescheduled without authentication check Co-Authored-By: morgan@cal.com * refactor: separate reschedule validation from auth check - Extract validateRescheduleBooking method to handle reschedule-specific validation - Keep checkBookingRequiresAuthentication strictly for auth checks - Use conditional logic in createBooking: validate reschedule OR check auth - Improves code clarity by separating concerns per lauris's feedback Co-Authored-By: morgan@cal.com --------- Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- .../controllers/bookings.controller.ts | 25 ++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/apps/api/v2/src/ee/bookings/2024-04-15/controllers/bookings.controller.ts b/apps/api/v2/src/ee/bookings/2024-04-15/controllers/bookings.controller.ts index d88ecce3d4..6b55717f5c 100644 --- a/apps/api/v2/src/ee/bookings/2024-04-15/controllers/bookings.controller.ts +++ b/apps/api/v2/src/ee/bookings/2024-04-15/controllers/bookings.controller.ts @@ -205,7 +205,11 @@ export class BookingsController_2024_04_15 { clientId?.toString() || (await this.getOAuthClientIdFromEventType(body.eventTypeId)); const { orgSlug, locationUrl } = body; try { - await this.checkBookingRequiresAuthentication(req, body.eventTypeId); + if (body.rescheduleUid) { + await this.validateRescheduleBooking(body.rescheduleUid, body.eventTypeId); + } else { + await this.checkBookingRequiresAuthentication(req, body.eventTypeId); + } const bookingRequest = await this.createNextApiBookingRequest(req, oAuthClientId, locationUrl, isEmbed); const booking = await this.regularBookingService.createBooking({ bookingData: bookingRequest.body, @@ -459,6 +463,25 @@ export class BookingsController_2024_04_15 { return oAuthClientParams.platformClientId; } + private async validateRescheduleBooking(rescheduleUid: string, eventTypeId: number): Promise { + const { bookingInfo } = await getBookingInfo(rescheduleUid); + if (!bookingInfo) { + throw new NotFoundException( + `Booking with UID=${rescheduleUid} does not exist. Cannot reschedule a non-existent booking.` + ); + } + if (bookingInfo.status !== "ACCEPTED" && bookingInfo.status !== "PENDING") { + throw new BadRequestException( + `Booking with UID=${rescheduleUid} has invalid status (status: ${bookingInfo.status}). Only ACCEPTED or PENDING bookings can be rescheduled.` + ); + } + if (bookingInfo.eventTypeId !== eventTypeId) { + throw new BadRequestException( + `Booking with UID=${rescheduleUid} is for a different event type (eventTypeId: ${bookingInfo.eventTypeId}). Cannot reschedule to a different event type (eventTypeId: ${eventTypeId}).` + ); + } + } + private async checkBookingRequiresAuthentication(req: Request, eventTypeId: number): Promise { const eventType = await this.eventTypeRepository.findByIdIncludeHostsAndTeamMembers({ id: eventTypeId,