Skip to content

Commit aa3086c

Browse files
authored
fix: Can cancel an already cancelled booking through the API (calcom#22128)
* fix: can cancel already cancelled booking * add test * fix test * update * Update bookings.service.ts * add commnet * update * tweak
1 parent b599ce4 commit aa3086c

5 files changed

Lines changed: 82 additions & 0 deletions

File tree

apps/api/v2/src/ee/bookings/2024-04-15/controllers/bookings.controller.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,15 @@ export class BookingsController_2024_04_15 {
232232
}
233233

234234
if (bookingUid) {
235+
const { bookingInfo } = await getBookingInfo(bookingUid);
236+
if (!bookingInfo) {
237+
throw new NotFoundException(`Booking with UID=${bookingUid} does not exist.`);
238+
}
239+
if (bookingInfo.status === "CANCELLED") {
240+
throw new BadRequestException(
241+
`Can't cancel booking with uid=${bookingUid} because it has been cancelled already. Please provide uid of a booking that is not cancelled.`
242+
);
243+
}
235244
try {
236245
req.body.uid = bookingUid;
237246
const bookingRequest = await this.createNextApiBookingRequest(req, oAuthClientId, undefined, isEmbed);

apps/api/v2/src/ee/bookings/2024-08-13/controllers/e2e/user-bookings.e2e-spec.ts

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2553,6 +2553,59 @@ describe("Bookings Endpoints 2024-08-13", () => {
25532553
});
25542554
});
25552555

2556+
describe("cant't cancel already cancelled booking", () => {
2557+
it("should not be able to cancel alraedy cancelled booking", async () => {
2558+
const cancelledBooking = await bookingsRepositoryFixture.create({
2559+
status: "CANCELLED",
2560+
user: {
2561+
connect: {
2562+
id: user.id,
2563+
},
2564+
},
2565+
startTime: new Date(Date.UTC(2050, 0, 8, 13, 0, 0)),
2566+
endTime: new Date(Date.UTC(2050, 0, 8, 14, 0, 0)),
2567+
title: "peer coding lets goo",
2568+
uid: `cancelled-booking-${randomString()}`,
2569+
eventType: {
2570+
connect: {
2571+
id: eventTypeId,
2572+
},
2573+
},
2574+
location: "integrations:daily",
2575+
customInputs: {},
2576+
metadata: {},
2577+
responses: {
2578+
name: "Oldie",
2579+
email: "oldie@gmail.com",
2580+
},
2581+
attendees: {
2582+
create: {
2583+
email: "oldie@gmail.com",
2584+
name: "Oldie",
2585+
locale: "lv",
2586+
timeZone: "Europe/Rome",
2587+
},
2588+
},
2589+
});
2590+
2591+
const body: CancelBookingInput_2024_08_13 = {
2592+
cancellationReason: "Going on a vacation",
2593+
};
2594+
2595+
const response = await request(app.getHttpServer())
2596+
.post(`/v2/bookings/${cancelledBooking.uid}/cancel`)
2597+
.send(body)
2598+
.set(CAL_API_VERSION_HEADER, VERSION_2024_08_13)
2599+
.set(X_CAL_CLIENT_ID, oAuthClient.id)
2600+
.expect(400);
2601+
2602+
expect(response.body.error.message).toEqual(
2603+
`Can't cancel booking with uid=${cancelledBooking.uid} because it has been cancelled already. Please provide uid of a booking that is not cancelled.`
2604+
);
2605+
await bookingsRepositoryFixture.deleteById(cancelledBooking.id);
2606+
});
2607+
});
2608+
25562609
describe("calendar events", () => {
25572610
beforeEach(() => {
25582611
jest.restoreAllMocks();

apps/api/v2/src/ee/bookings/2024-08-13/services/input.service.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -711,6 +711,12 @@ export class InputBookingsService_2024_08_13 {
711711
throw new NotFoundException(`Booking with uid=${bookingUid} not found`);
712712
}
713713

714+
if (booking.status === "CANCELLED") {
715+
throw new BadRequestException(
716+
`Can't cancel booking with uid=${bookingUid} because it has been cancelled already. Please provide uid of a booking that is not cancelled.`
717+
);
718+
}
719+
714720
const oAuthClientParams = booking.eventTypeId
715721
? await this.platformBookingsService.getOAuthClientParams(booking.eventTypeId)
716722
: undefined;

packages/features/bookings/lib/getBookingToDelete.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ export async function getBookingToDelete(id: number | undefined, uid: string | u
100100
responses: true,
101101
iCalUID: true,
102102
iCalSequence: true,
103+
status: true,
103104
},
104105
});
105106
}

packages/features/bookings/lib/handleCancelBooking.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,19 @@ async function handler(input: CancelBookingInput) {
8989
arePlatformEmailsEnabled,
9090
} = input;
9191

92+
/**
93+
* Important: We prevent cancelling an already cancelled booking.
94+
* A booking could have been CANCELLED due to a reschedule,
95+
* in which case we simply update the existing calendar event and meeting.
96+
* We want to avoid deleting them by a subsequent cancellation attempt.
97+
*/
98+
if (bookingToDelete.status === BookingStatus.CANCELLED) {
99+
throw new HttpError({
100+
statusCode: 400,
101+
message: "This booking has already been cancelled.",
102+
});
103+
}
104+
92105
if (!bookingToDelete.userId || !bookingToDelete.user) {
93106
throw new HttpError({ statusCode: 400, message: "User not found" });
94107
}

0 commit comments

Comments
 (0)