From 1cf84a270c7b6e42d987a9741f421d726246e670 Mon Sep 17 00:00:00 2001 From: Anwar Sadath <37188392+asadath1395@users.noreply.github.com> Date: Fri, 22 Nov 2024 13:50:11 +0530 Subject: [PATCH] feat: Expose booking created at in v2 API response (#17452) * feat: Expose booking created at in booking detailed view and v2 API response * Fix tests * Fix spacing * Add API v2 tests to verify createdAt is present while creating a booking and fetching a booking * Add test to validate createdAt after rescheduling a booking --- .../user-bookings.controller.e2e-spec.ts | 17 +++++++++++++++++ .../2024-08-13/services/output.service.ts | 8 +++++++- apps/api/v2/swagger/documentation.json | 10 ++++++++++ .../2024-08-13/outputs/booking.output.ts | 5 +++++ 4 files changed, 39 insertions(+), 1 deletion(-) diff --git a/apps/api/v2/src/ee/bookings/2024-08-13/controllers/user-bookings.controller.e2e-spec.ts b/apps/api/v2/src/ee/bookings/2024-08-13/controllers/user-bookings.controller.e2e-spec.ts index 9536269889..805c747557 100644 --- a/apps/api/v2/src/ee/bookings/2024-08-13/controllers/user-bookings.controller.e2e-spec.ts +++ b/apps/api/v2/src/ee/bookings/2024-08-13/controllers/user-bookings.controller.e2e-spec.ts @@ -328,12 +328,14 @@ describe("Bookings Endpoints 2024-08-13", () => { }, }; + const beforeCreate = new Date(); return request(app.getHttpServer()) .post("/v2/bookings") .send(body) .set(CAL_API_VERSION_HEADER, VERSION_2024_08_13) .expect(201) .then(async (response) => { + const afterCreate = new Date(); const responseBody: CreateBookingOutput_2024_08_13 = response.body; expect(responseBody.status).toEqual(SUCCESS_STATUS); expect(responseBody.data).toBeDefined(); @@ -367,6 +369,12 @@ describe("Bookings Endpoints 2024-08-13", () => { email: body.attendee.email, ...body.bookingFieldsResponses, }); + + // Check createdAt date is between the time of the request and after the request + const createdAtDate = new Date(data.createdAt); + expect(createdAtDate.getTime()).toBeGreaterThanOrEqual(beforeCreate.getTime()); + expect(createdAtDate.getTime()).toBeLessThanOrEqual(afterCreate.getTime()); + expect(data.metadata).toEqual(body.metadata); createdBooking = data; } else { @@ -503,6 +511,7 @@ describe("Bookings Endpoints 2024-08-13", () => { expect(data.attendees[0]).toEqual(createdBooking.attendees[0]); expect(data.location).toEqual(createdBooking.location); expect(data.absentHost).toEqual(createdBooking.absentHost); + expect(data.createdAt).toEqual(createdBooking.createdAt); } else { throw new Error( "Invalid response data - expected booking but received array of possibily recurring bookings" @@ -976,12 +985,14 @@ describe("Bookings Endpoints 2024-08-13", () => { reschedulingReason: "Flying to mars that day", }; + const beforeCreate = new Date(); return request(app.getHttpServer()) .post(`/v2/bookings/${createdBooking.uid}/reschedule`) .send(body) .set(CAL_API_VERSION_HEADER, VERSION_2024_08_13) .expect(201) .then(async (response) => { + const afterCreate = new Date(); const responseBody: RescheduleBookingOutput_2024_08_13 = response.body; expect(responseBody.status).toEqual(SUCCESS_STATUS); expect(responseBody.data).toBeDefined(); @@ -1003,6 +1014,12 @@ describe("Bookings Endpoints 2024-08-13", () => { expect(data.absentHost).toEqual(createdBooking.absentHost); expect(data.metadata).toEqual(createdBooking.metadata); + // When a booking is rescheduled, a new booking is created and the old booking is cancelled. + // We want to make sure the createdAt date of the new booking is between the beforeCreate and afterCreate dates. + const createdAtDate = new Date(data.createdAt); + expect(createdAtDate.getTime()).toBeGreaterThanOrEqual(beforeCreate.getTime()); + expect(createdAtDate.getTime()).toBeLessThanOrEqual(afterCreate.getTime()); + rescheduledBooking = data; }); }); diff --git a/apps/api/v2/src/ee/bookings/2024-08-13/services/output.service.ts b/apps/api/v2/src/ee/bookings/2024-08-13/services/output.service.ts index d7e85e376a..a7cc4e1bd6 100644 --- a/apps/api/v2/src/ee/bookings/2024-08-13/services/output.service.ts +++ b/apps/api/v2/src/ee/bookings/2024-08-13/services/output.service.ts @@ -50,7 +50,9 @@ type DatabaseBooking = Booking & { noShow: boolean | null; bookingSeat?: BookingSeat | null; }[]; -} & { user: { id: number; name: string | null; email: string } | null }; + user: { id: number; name: string | null; email: string } | null; + createdAt: Date; +}; type BookingWithUser = Booking & { user: { id: number; name: string | null; email: string } | null }; @@ -103,6 +105,7 @@ export class OutputBookingsService_2024_08_13 { // note(Lauris): meetingUrl is deprecated meetingUrl: databaseBooking.location, absentHost: !!databaseBooking.noShowHost, + createdAt: databaseBooking.createdAt, }; const bookingTransformed = plainToClass(BookingOutput_2024_08_13, booking, { strategy: "excludeAll" }); @@ -165,6 +168,7 @@ export class OutputBookingsService_2024_08_13 { recurringBookingUid: databaseBooking.recurringEventId, absentHost: !!databaseBooking.noShowHost, bookingFieldsResponses: databaseBooking.responses, + createdAt: databaseBooking.createdAt, }; const bookingTransformed = plainToClass(RecurringBookingOutput_2024_08_13, booking, { @@ -208,6 +212,7 @@ export class OutputBookingsService_2024_08_13 { // note(Lauris): meetingUrl is deprecated meetingUrl: databaseBooking.location, absentHost: !!databaseBooking.noShowHost, + createdAt: databaseBooking.createdAt, }; const parsed = plainToClass(GetSeatedBookingOutput_2024_08_13, booking, { strategy: "excludeAll" }); @@ -302,6 +307,7 @@ export class OutputBookingsService_2024_08_13 { meetingUrl: databaseBooking.location, recurringBookingUid: databaseBooking.recurringEventId, absentHost: !!databaseBooking.noShowHost, + createdAt: databaseBooking.createdAt, }; const parsed = plainToClass(GetRecurringSeatedBookingOutput_2024_08_13, booking, { diff --git a/apps/api/v2/swagger/documentation.json b/apps/api/v2/swagger/documentation.json index c477127c26..a755682d94 100644 --- a/apps/api/v2/swagger/documentation.json +++ b/apps/api/v2/swagger/documentation.json @@ -13122,6 +13122,10 @@ "type": "boolean", "example": true }, + "createdAt": { + "type": "string", + "example": "2024-08-13T15:30:00Z" + }, "metadata": { "type": "object", "example": { @@ -13165,6 +13169,7 @@ "eventTypeId", "eventType", "absentHost", + "createdAt", "attendees" ] }, @@ -13250,6 +13255,10 @@ "type": "boolean", "example": true }, + "createdAt": { + "type": "string", + "example": "2024-08-13T15:30:00Z" + }, "metadata": { "type": "object", "example": { @@ -13297,6 +13306,7 @@ "eventTypeId", "eventType", "absentHost", + "createdAt", "attendees", "recurringBookingUid" ] diff --git a/packages/platform/types/bookings/2024-08-13/outputs/booking.output.ts b/packages/platform/types/bookings/2024-08-13/outputs/booking.output.ts index 197eb1c057..a274a81b84 100644 --- a/packages/platform/types/bookings/2024-08-13/outputs/booking.output.ts +++ b/packages/platform/types/bookings/2024-08-13/outputs/booking.output.ts @@ -200,6 +200,11 @@ class BaseBookingOutput_2024_08_13 { @Expose() absentHost!: boolean; + @ApiProperty({ type: String, example: "2024-08-13T15:30:00Z" }) + @IsDateString() + @Expose() + createdAt!: string; + @ApiProperty({ type: Object, example: { key: "value" },