Skip to content

Commit 3c57960

Browse files
Ryukemeisterdevin-ai-integration[bot]bot_apk
authored
feat: api v2 DELETE booking attendees endpoint (calcom#27781)
* feat: remove attendee endpoint * fix: remove attendee email from error logs to avoid logging PII Co-Authored-By: unknown <> * fix: add isBookingAuditEnabled to removeAttendee handler Align removeAttendee.handler.ts with the new onAttendeeRemoved interface that requires isBookingAuditEnabled, following the same pattern used in addGuests.handler.ts. Co-Authored-By: bot_apk <apk@cognition.ai> * style: apply biome formatting to conflict-resolved files Co-Authored-By: bot_apk <apk@cognition.ai> * chore: implement PR feedback * fixup * revert: biome formatting changes * chore: implement feedback part 1 * chore: implement feedback part 2 * fix: await cancellation email flow to prevent uncaught promise rejections The fire-and-forget .then() chain on prepareAttendeePerson() left rejections from that promise uncaught. Await both prepareAttendeePerson() and sendCancelledEmailToAttendee() so errors are properly handled. sendCancelledEmailToAttendee() already has an internal try/catch, so awaiting it will not cause the overall removeAttendee flow to fail on email errors. Addresses Cubic AI review (confidence 9/10). Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> * chore: implement feedback part 3 * chore: implement devin feedback --------- Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-authored-by: bot_apk <apk@cognition.ai>
1 parent 72acf09 commit 3c57960

23 files changed

Lines changed: 1469 additions & 94 deletions

apps/api/v2/src/ee/bookings/2024-08-13/bookings.module.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import { EventTypesModule_2024_06_14 } from "@/ee/event-types/event-types_2024_0
3030
import { EventTypesRepository_2024_06_14 } from "@/ee/event-types/event-types_2024_06_14/event-types.repository";
3131
import { OutputEventTypesService_2024_06_14 } from "@/ee/event-types/event-types_2024_06_14/services/output-event-types.service";
3232
import { SchedulesModule_2024_04_15 } from "@/ee/schedules/schedules_2024_04_15/schedules.module";
33+
import { BookingAttendeesModule } from "@/lib/modules/booking-attendees.module";
3334
import { BookingEventHandlerModule } from "@/lib/modules/booking-event-handler.module";
3435
import { InstantBookingModule } from "@/lib/modules/instant-booking.module";
3536
import { RecurringBookingModule } from "@/lib/modules/recurring-booking.module";
@@ -80,6 +81,7 @@ import { UsersModule } from "@/modules/users/users.module";
8081
RecurringBookingModule,
8182
InstantBookingModule,
8283
BookingEventHandlerModule,
84+
BookingAttendeesModule,
8385
],
8486
providers: [
8587
TokensRepository,

apps/api/v2/src/ee/bookings/2024-08-13/controllers/booking-attendees.controller.ts

Lines changed: 42 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,28 @@
1-
import {
2-
BOOKING_WRITE,
3-
BOOKING_READ,
4-
SUCCESS_STATUS,
5-
} from "@calcom/platform-constants";
1+
import { BOOKING_READ, BOOKING_WRITE, SUCCESS_STATUS } from "@calcom/platform-constants";
62
import { AddAttendeeInput_2024_08_13 } from "@calcom/platform-types";
73
import {
84
Body,
95
Controller,
6+
Delete,
7+
Get,
108
HttpCode,
119
HttpStatus,
1210
Param,
11+
ParseIntPipe,
1312
Post,
1413
UseGuards,
15-
Get,
16-
ParseIntPipe,
1714
} from "@nestjs/common";
1815
import { ApiHeader, ApiOperation, ApiTags as DocsTags } from "@nestjs/swagger";
1916
import { BookingPbacGuard } from "@/ee/bookings/2024-08-13/guards/booking-pbac.guard";
2017
import { BookingUidGuard } from "@/ee/bookings/2024-08-13/guards/booking-uid.guard";
2118
import { AddAttendeeOutput_2024_08_13 } from "@/ee/bookings/2024-08-13/outputs/add-attendee.output";
2219
import {
23-
GetBookingAttendeesOutput_2024_08_13,
2420
GetBookingAttendeeOutput_2024_08_13,
21+
GetBookingAttendeesOutput_2024_08_13,
2522
} from "@/ee/bookings/2024-08-13/outputs/get-booking-attendees.output";
23+
import { RemoveAttendeeOutput_2024_08_13 } from "@/ee/bookings/2024-08-13/outputs/remove-attendee.output";
2624
import { BookingAttendeesService_2024_08_13 } from "@/ee/bookings/2024-08-13/services/booking-attendees.service";
27-
import {
28-
VERSION_2024_08_13,
29-
VERSION_2024_08_13_VALUE,
30-
} from "@/lib/api-versions";
25+
import { VERSION_2024_08_13, VERSION_2024_08_13_VALUE } from "@/lib/api-versions";
3126
import { API_KEY_OR_ACCESS_TOKEN_HEADER } from "@/lib/docs/headers";
3227
import { Throttle } from "@/lib/endpoint-throttler-decorator";
3328
import { GetUser } from "@/modules/auth/decorators/get-user/get-user.decorator";
@@ -49,9 +44,7 @@ import { ApiAuthGuardUser } from "@/modules/auth/strategies/api-auth/api-auth.st
4944
required: true,
5045
})
5146
export class BookingAttendeesController_2024_08_13 {
52-
constructor(
53-
private readonly bookingAttendeesService: BookingAttendeesService_2024_08_13
54-
) {}
47+
constructor(private readonly bookingAttendeesService: BookingAttendeesService_2024_08_13) {}
5548

5649
@Get("/")
5750
@Permissions([BOOKING_READ])
@@ -68,9 +61,7 @@ export class BookingAttendeesController_2024_08_13 {
6861
@Param("bookingUid") bookingUid: string,
6962
@GetUser() user: ApiAuthGuardUser
7063
): Promise<GetBookingAttendeesOutput_2024_08_13> {
71-
const attendees = await this.bookingAttendeesService.getBookingAttendees(
72-
bookingUid
73-
);
64+
const attendees = await this.bookingAttendeesService.getBookingAttendees(bookingUid);
7465

7566
return {
7667
status: SUCCESS_STATUS,
@@ -93,10 +84,7 @@ export class BookingAttendeesController_2024_08_13 {
9384
@Param("bookingUid") bookingUid: string,
9485
@Param("attendeeId", ParseIntPipe) attendeeId: number
9586
): Promise<GetBookingAttendeeOutput_2024_08_13> {
96-
const attendee = await this.bookingAttendeesService.getBookingAttendee(
97-
bookingUid,
98-
attendeeId
99-
);
87+
const attendee = await this.bookingAttendeesService.getBookingAttendee(bookingUid, attendeeId);
10088

10189
return {
10290
status: SUCCESS_STATUS,
@@ -135,15 +123,42 @@ export class BookingAttendeesController_2024_08_13 {
135123
@Body() body: AddAttendeeInput_2024_08_13,
136124
@GetUser() user: ApiAuthGuardUser
137125
): Promise<AddAttendeeOutput_2024_08_13> {
138-
const attendee = await this.bookingAttendeesService.addAttendee(
139-
bookingUid,
140-
body,
141-
user
142-
);
126+
const attendee = await this.bookingAttendeesService.addAttendee(bookingUid, body, user);
143127

144128
return {
145129
status: SUCCESS_STATUS,
146130
data: attendee,
147131
};
148132
}
133+
134+
@Delete("/:attendeeId")
135+
@HttpCode(HttpStatus.OK)
136+
@Permissions([BOOKING_WRITE])
137+
@UseGuards(ApiAuthGuard, BookingUidGuard)
138+
@Throttle({
139+
limit: 5,
140+
ttl: 60000,
141+
blockDuration: 60000,
142+
name: "booking_attendees_remove",
143+
})
144+
@ApiHeader(API_KEY_OR_ACCESS_TOKEN_HEADER)
145+
@ApiOperation({
146+
summary: "Remove an attendee from a booking",
147+
description: `Remove an attendee from an existing booking by their attendee ID. The primary attendee (first attendee) cannot be removed — to remove them, cancel the booking instead. The removed attendee will receive a cancellation email notification.
148+
149+
<Note>The cal-api-version header is required for this endpoint. Without it, the request will fail with a 404 error.</Note>
150+
`,
151+
})
152+
async removeAttendee(
153+
@Param("bookingUid") bookingUid: string,
154+
@Param("attendeeId", ParseIntPipe) attendeeId: number,
155+
@GetUser() user: ApiAuthGuardUser
156+
): Promise<RemoveAttendeeOutput_2024_08_13> {
157+
const removedAttendee = await this.bookingAttendeesService.removeAttendee(bookingUid, attendeeId, user);
158+
159+
return {
160+
status: SUCCESS_STATUS,
161+
data: removedAttendee,
162+
};
163+
}
149164
}

0 commit comments

Comments
 (0)