Skip to content

Commit 5a7e783

Browse files
Ryukemeisterdevin-ai-integration[bot]bot_apk
authored
feat: api v2 GET booking attendees endpoint (calcom#27664)
* feat: add booking attendees endpoint to API v2 Co-Authored-By: rajiv@cal.com <sahalrajiv6900@gmail.com> * feat: add rate limiting to booking attendees endpoint Co-Authored-By: rajiv@cal.com <sahalrajiv6900@gmail.com> * refactor: simplify attendees output to id, bookingId, name, email, timeZone Co-Authored-By: rajiv@cal.com <sahalrajiv6900@gmail.com> * test: add E2E tests for booking attendees endpoint Co-Authored-By: rajiv@cal.com <sahalrajiv6900@gmail.com> * chore: update bookings repository * fixup: add pbac guards and update service logic * chore: update openapi spec * test: add rate limiting E2E test for booking attendees endpoint Co-Authored-By: rajiv@cal.com <sahalrajiv6900@gmail.com> * fix: tests * fix: return 404 instead of 403 for non-existent booking in BookingPbacGuard The BookingPbacGuard was returning 403 (Forbidden) for non-existent bookings because doesUserIdHaveAccessToBooking returns false when a booking doesn't exist, which the guard treated as an access denial. Added an explicit booking existence check in the guard before the access check, so non-existent bookings now correctly return 404 (Not Found) as documented in the PR description. Updated the E2E test to expect 404 for non-existent booking UIDs. Issue identified by cubic. Co-Authored-By: unknown <> * fixup * fix: return 404 instead of 403 for non-existent booking in attendees endpoint BookingPbacGuard now checks booking existence before the access check, returning 404 (Not Found) instead of 403 (Forbidden) for non-existent booking UIDs. Updated the E2E test assertion and description to match. Issue identified by cubic (confidence 9/10). Co-Authored-By: unknown <> * chore: implement PR feedback * chore: update tests * fixup * chore: update endpoint decsription * feat: endpoint to retrieve specific attendee * chore: update e2e tests * chore: implement cubic feedback * fix: update test to expect 403 for non-existent booking UID (BookingPbacGuard behavior) Co-Authored-By: rajiv@cal.com <sahalrajiv6900@gmail.com> * fix: merge conflicts * feat: endpoint to get attendees * chore: update findByUidIncludeEventTypeAttendeesAndUser method * chore: implement PR feedback * fix: e2e tests * chore: update e2e tests * fixup fixup * fix: remove phoneNumber assertion since it's optional and not provided in test * chore: implement PR feedback * fix: keep the same output shape for get attendees and get attendee endpoint * chore: update openapi spec --------- Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-authored-by: bot_apk <apk@cognition.ai>
1 parent 658e65b commit 5a7e783

11 files changed

Lines changed: 906 additions & 29 deletions

File tree

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

Lines changed: 83 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,33 @@
1-
import { BOOKING_WRITE, SUCCESS_STATUS } from "@calcom/platform-constants";
1+
import {
2+
BOOKING_WRITE,
3+
BOOKING_READ,
4+
SUCCESS_STATUS,
5+
} from "@calcom/platform-constants";
26
import { AddAttendeeInput_2024_08_13 } from "@calcom/platform-types";
3-
import { Body, Controller, HttpCode, HttpStatus, Param, Post, UseGuards } from "@nestjs/common";
7+
import {
8+
Body,
9+
Controller,
10+
HttpCode,
11+
HttpStatus,
12+
Param,
13+
Post,
14+
UseGuards,
15+
Get,
16+
ParseIntPipe,
17+
} from "@nestjs/common";
418
import { ApiHeader, ApiOperation, ApiTags as DocsTags } from "@nestjs/swagger";
519
import { BookingPbacGuard } from "@/ee/bookings/2024-08-13/guards/booking-pbac.guard";
620
import { BookingUidGuard } from "@/ee/bookings/2024-08-13/guards/booking-uid.guard";
721
import { AddAttendeeOutput_2024_08_13 } from "@/ee/bookings/2024-08-13/outputs/add-attendee.output";
22+
import {
23+
GetBookingAttendeesOutput_2024_08_13,
24+
GetBookingAttendeeOutput_2024_08_13,
25+
} from "@/ee/bookings/2024-08-13/outputs/get-booking-attendees.output";
826
import { BookingAttendeesService_2024_08_13 } from "@/ee/bookings/2024-08-13/services/booking-attendees.service";
9-
import { VERSION_2024_08_13, VERSION_2024_08_13_VALUE } from "@/lib/api-versions";
27+
import {
28+
VERSION_2024_08_13,
29+
VERSION_2024_08_13_VALUE,
30+
} from "@/lib/api-versions";
1031
import { API_KEY_OR_ACCESS_TOKEN_HEADER } from "@/lib/docs/headers";
1132
import { Throttle } from "@/lib/endpoint-throttler-decorator";
1233
import { GetUser } from "@/modules/auth/decorators/get-user/get-user.decorator";
@@ -28,7 +49,60 @@ import { ApiAuthGuardUser } from "@/modules/auth/strategies/api-auth/api-auth.st
2849
required: true,
2950
})
3051
export class BookingAttendeesController_2024_08_13 {
31-
constructor(private readonly bookingAttendeesService: BookingAttendeesService_2024_08_13) {}
52+
constructor(
53+
private readonly bookingAttendeesService: BookingAttendeesService_2024_08_13
54+
) {}
55+
56+
@Get("/")
57+
@Permissions([BOOKING_READ])
58+
@UseGuards(ApiAuthGuard, BookingUidGuard, BookingPbacGuard)
59+
@ApiHeader(API_KEY_OR_ACCESS_TOKEN_HEADER)
60+
@ApiOperation({
61+
summary: "Get all attendees for a booking",
62+
description: `Retrieve all attendees for a specific booking by its UID.
63+
64+
<Note>The cal-api-version header is required for this endpoint. Without it, the request will fail with a 404 error.</Note>
65+
`,
66+
})
67+
async getBookingAttendees(
68+
@Param("bookingUid") bookingUid: string,
69+
@GetUser() user: ApiAuthGuardUser
70+
): Promise<GetBookingAttendeesOutput_2024_08_13> {
71+
const attendees = await this.bookingAttendeesService.getBookingAttendees(
72+
bookingUid
73+
);
74+
75+
return {
76+
status: SUCCESS_STATUS,
77+
data: attendees,
78+
};
79+
}
80+
81+
@Get("/:attendeeId")
82+
@Permissions([BOOKING_READ])
83+
@UseGuards(ApiAuthGuard, BookingUidGuard, BookingPbacGuard)
84+
@ApiHeader(API_KEY_OR_ACCESS_TOKEN_HEADER)
85+
@ApiOperation({
86+
summary: "Get a specific attendee for a booking",
87+
description: `Retrieve a specific attendee by their ID for a booking identified by its UID.
88+
89+
<Note>The cal-api-version header is required for this endpoint. Without it, the request will fail with a 404 error.</Note>
90+
`,
91+
})
92+
async getBookingAttendee(
93+
@Param("bookingUid") bookingUid: string,
94+
@Param("attendeeId", ParseIntPipe) attendeeId: number
95+
): Promise<GetBookingAttendeeOutput_2024_08_13> {
96+
const attendee = await this.bookingAttendeesService.getBookingAttendee(
97+
bookingUid,
98+
attendeeId
99+
);
100+
101+
return {
102+
status: SUCCESS_STATUS,
103+
data: attendee,
104+
};
105+
}
32106

33107
@Post("/")
34108
@HttpCode(HttpStatus.CREATED)
@@ -61,7 +135,11 @@ export class BookingAttendeesController_2024_08_13 {
61135
@Body() body: AddAttendeeInput_2024_08_13,
62136
@GetUser() user: ApiAuthGuardUser
63137
): Promise<AddAttendeeOutput_2024_08_13> {
64-
const attendee = await this.bookingAttendeesService.addAttendee(bookingUid, body, user);
138+
const attendee = await this.bookingAttendeesService.addAttendee(
139+
bookingUid,
140+
body,
141+
user
142+
);
65143

66144
return {
67145
status: SUCCESS_STATUS,

0 commit comments

Comments
 (0)