diff --git a/apps/api/v2/src/ee/bookings/2024-04-15/controllers/bookings.controller.e2e-spec.ts b/apps/api/v2/src/ee/bookings/2024-04-15/controllers/bookings.controller.e2e-spec.ts index 099d5bb6c8..757a8171dc 100644 --- a/apps/api/v2/src/ee/bookings/2024-04-15/controllers/bookings.controller.e2e-spec.ts +++ b/apps/api/v2/src/ee/bookings/2024-04-15/controllers/bookings.controller.e2e-spec.ts @@ -351,6 +351,64 @@ describe("Bookings Endpoints 2024-04-15", () => { }); }); + it("should create a booking with split name", async () => { + const bookingStart = "2040-05-21T11:30:00.000Z"; + const bookingEnd = "2040-05-21T12:30:00.000Z"; + const bookingEventTypeId = eventTypeId; + const bookingTimeZone = "Europe/London"; + const bookingLanguage = "en"; + const bookingHashedLink = ""; + const bookingMetadata = { + timeFormat: "12", + meetingType: "organizer-phone", + }; + const bookingResponses = { + name: { firstName: "John", lastName: "Doe" }, + email: "tester@example.com", + location: { + value: "link", + optionValue: "", + }, + notes: "test", + }; + + const body: CreateBookingInput_2024_04_15 = { + start: bookingStart, + end: bookingEnd, + eventTypeId: bookingEventTypeId, + timeZone: bookingTimeZone, + language: bookingLanguage, + metadata: bookingMetadata, + hashedLink: bookingHashedLink, + responses: bookingResponses, + }; + + return request(app.getHttpServer()) + .post("/v2/bookings") + .send(body) + .expect(201) + .then(async (response) => { + const responseBody: ApiSuccessResponse>> = + response.body; + expect(responseBody.status).toEqual(SUCCESS_STATUS); + expect(responseBody.data).toBeDefined(); + expect(responseBody.data.userPrimaryEmail).toBeDefined(); + expect(responseBody.data.userPrimaryEmail).toEqual(userEmail); + expect(responseBody.data.id).toBeDefined(); + expect(responseBody.data.uid).toBeDefined(); + expect(responseBody.data.startTime).toEqual(bookingStart); + expect(responseBody.data.eventTypeId).toEqual(bookingEventTypeId); + expect(responseBody.data.user.timeZone).toEqual(bookingTimeZone); + expect(responseBody.data.metadata).toEqual(bookingMetadata); + expect(responseBody.data.responses).toEqual({ + ...bookingResponses, + name: `${bookingResponses.name.firstName} ${bookingResponses.name.lastName}`, + }); + + createdBooking = responseBody.data; + }); + }); + afterAll(async () => { await userRepositoryFixture.deleteByEmail(user.email); await bookingsRepositoryFixture.deleteAllBookings(user.id, user.email); diff --git a/apps/api/v2/src/ee/bookings/2024-04-15/inputs/create-booking.input.ts b/apps/api/v2/src/ee/bookings/2024-04-15/inputs/create-booking.input.ts index a45f1ce3b5..2325ce36cb 100644 --- a/apps/api/v2/src/ee/bookings/2024-04-15/inputs/create-booking.input.ts +++ b/apps/api/v2/src/ee/bookings/2024-04-15/inputs/create-booking.input.ts @@ -12,9 +12,42 @@ import { isEmail, Validate, } from "class-validator"; +import { ValidationOptions, registerDecorator } from "class-validator"; import { RESCHEDULED_BY_DOCS } from "@calcom/platform-types"; +type BookingName = { firstName: string; lastName: string }; + +function ValidateBookingName(validationOptions?: ValidationOptions) { + return function (target: object, propertyName: string) { + registerDecorator({ + name: "validateBookingName", + target: target.constructor, + propertyName: propertyName, + options: validationOptions, + validator: { + validate(value: any) { + if (typeof value === "string") { + return value.trim().length > 0; + } + if (typeof value === "object" && value !== null) { + return ( + typeof value.firstName === "string" && + typeof value.lastName === "string" && + value.firstName.trim().length > 0 && + value.lastName.trim().length > 0 + ); + } + return false; + }, + defaultMessage(): string { + return "Name must be either a non-empty string or an object with non-empty firstName and lastName"; + }, + }, + }); + }; +} + class Location { @IsString() @ApiProperty() @@ -26,9 +59,14 @@ class Location { } class Response { - @IsString() - @ApiProperty() - name!: string; + @ApiProperty({ + oneOf: [ + { type: "string" }, + { type: "object", properties: { firstName: { type: "string" }, lastName: { type: "string" } } }, + ], + }) + @ValidateBookingName() + name!: string | BookingName; @Validate((value: string) => !value || isEmail(value), { message: "Invalid response email",