Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,9 @@ describe("Bookings Endpoints 2024-08-13", () => {
...body.bookingFieldsResponses,
},
metadata: body.metadata,
createdAt: expect.any(String),
});
expect(data.attendees[0].createdAt).toBeDefined();
expect(data.location).toBeDefined();
expect(data.absentHost).toEqual(false);
createdSeatedBooking = data;
Expand Down Expand Up @@ -287,7 +289,9 @@ describe("Bookings Endpoints 2024-08-13", () => {
...createdSeatedBooking.attendees[0].bookingFieldsResponses,
},
metadata: createdSeatedBooking.attendees[0].metadata,
createdAt: expect.any(String),
});
expect(firstAttendee?.createdAt).toBeDefined();
const secondAttendee = data.attendees.find((attendee) => attendee.name === nameAttendeeTwo);
expect(secondAttendee).toEqual({
name: body.attendee.name,
Expand All @@ -303,7 +307,9 @@ describe("Bookings Endpoints 2024-08-13", () => {
...body.bookingFieldsResponses,
},
metadata: body.metadata,
createdAt: expect.any(String),
});
expect(secondAttendee?.createdAt).toBeDefined();
expect(data.location).toBeDefined();
expect(data.absentHost).toEqual(false);
createdSeatedBooking = data;
Expand Down Expand Up @@ -609,7 +615,9 @@ describe("Bookings Endpoints 2024-08-13", () => {
...attendee?.bookingFieldsResponses,
},
metadata: attendee?.metadata,
createdAt: expect.any(String),
});
expect(data.attendees[0].createdAt).toBeDefined();
expect(data.location).toBeDefined();
expect(data.absentHost).toEqual(false);
createdSeatedBooking = data;
Expand Down Expand Up @@ -876,6 +884,7 @@ describe("Bookings Endpoints 2024-08-13", () => {
expect(data.attendees[0].name).toEqual(nameAttendeeOne);
expect(data.attendees[0].language).toEqual("it");
expect(data.attendees[0].seatUid).toBeDefined();
expect(data.attendees[0].createdAt).toBeDefined();
expect(data.location).toBeDefined();
expect(data.absentHost).toEqual(false);
booking = data;
Expand Down Expand Up @@ -919,6 +928,7 @@ describe("Bookings Endpoints 2024-08-13", () => {
expect(data.attendees[0].name).toEqual(nameAttendeeOne);
expect(data.attendees[0].language).toEqual("it");
expect(data.attendees[0].seatUid).toBeDefined();
expect(data.attendees[0].createdAt).toBeDefined();
expect(data.location).toBeDefined();
expect(data.absentHost).toEqual(false);
await bookingsRepositoryFixture.deleteById(data.id);
Expand Down Expand Up @@ -1034,7 +1044,9 @@ describe("Bookings Endpoints 2024-08-13", () => {
...body.bookingFieldsResponses,
},
metadata: body.metadata,
createdAt: expect.any(String),
});
expect(data.attendees[0].createdAt).toBeDefined();
expect(data.location).toBeDefined();
expect(data.absentHost).toEqual(false);
createdSeatedBooking2 = data;
Expand Down Expand Up @@ -1103,13 +1115,11 @@ describe("Bookings Endpoints 2024-08-13", () => {
});

function responseDataIsCreateSeatedBooking(data: any): data is CreateSeatedBookingOutput_2024_08_13 {
return Object.prototype.hasOwnProperty.call(data, "seatUid");
return "seatUid" in data;
}

function responseDataIsGetSeatedBooking(data: any): data is GetSeatedBookingOutput_2024_08_13 {
return data?.attendees?.every((attendee: any) =>
Object.prototype.hasOwnProperty.call(attendee, "seatUid")
);
return data?.attendees?.every((attendee: any) => "seatUid" in attendee);
}

afterAll(async () => {
Expand Down
69 changes: 35 additions & 34 deletions apps/api/v2/src/ee/bookings/2024-08-13/services/output.service.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,21 @@
import { bookingMetadataSchema } from "@calcom/platform-libraries";
import type {
CreateRecurringSeatedBookingOutput_2024_08_13,
CreateSeatedBookingOutput_2024_08_13,
ReassignBookingOutput_2024_08_13,
} from "@calcom/platform-types";
import {
BookingOutput_2024_08_13,
GetRecurringSeatedBookingOutput_2024_08_13,
GetSeatedBookingOutput_2024_08_13,
RecurringBookingOutput_2024_08_13,
SeatedAttendee,
} from "@calcom/platform-types";
import type { Booking, BookingSeat } from "@calcom/prisma/client";
import { Injectable } from "@nestjs/common";
import { plainToClass } from "class-transformer";
import { DateTime } from "luxon";
import { z } from "zod";
import { BookingsRepository_2024_08_13 } from "@/ee/bookings/2024-08-13/repositories/bookings.repository";
import {
defaultBookingMetadata,
Expand All @@ -6,25 +24,6 @@ import {
defaultSeatedBookingMetadata,
} from "@/lib/safe-parse/default-responses-booking";
import { safeParse } from "@/lib/safe-parse/safe-parse";
import { Injectable } from "@nestjs/common";
import { plainToClass } from "class-transformer";
import { DateTime } from "luxon";
import { z } from "zod";

import { bookingMetadataSchema } from "@calcom/platform-libraries";
import {
GetRecurringSeatedBookingOutput_2024_08_13,
RecurringBookingOutput_2024_08_13,
SeatedAttendee,
BookingOutput_2024_08_13,
GetSeatedBookingOutput_2024_08_13,
} from "@calcom/platform-types";
import type {
CreateRecurringSeatedBookingOutput_2024_08_13,
CreateSeatedBookingOutput_2024_08_13,
ReassignBookingOutput_2024_08_13,
} from "@calcom/platform-types";
import type { Booking, BookingSeat } from "@calcom/prisma/client";

export const bookingResponsesSchema = z
.object({
Expand Down Expand Up @@ -80,6 +79,7 @@ type DatabaseBooking = Booking & {
phoneNumber?: string | null;
noShow: boolean | null;
bookingSeat?: BookingSeat | null;
createdAt?: Date | null;
}[];
user: DatabaseUser | null;
createdAt: Date;
Expand Down Expand Up @@ -168,9 +168,8 @@ export class OutputBookingsService_2024_08_13 {
bookingTransformed.bookingFieldsResponses?.guests &&
Array.isArray(bookingTransformed.bookingFieldsResponses.guests)
) {
bookingTransformed.bookingFieldsResponses.displayGuests = bookingTransformed.bookingFieldsResponses.guests.map(
(guest: string) => this.getDisplayEmail(guest)
);
bookingTransformed.bookingFieldsResponses.displayGuests =
bookingTransformed.bookingFieldsResponses.guests.map((guest: string) => this.getDisplayEmail(guest));
}

return bookingTransformed;
Expand Down Expand Up @@ -230,10 +229,10 @@ export class OutputBookingsService_2024_08_13 {

async getOutputRecurringBookings(bookingsIds: number[]) {
const databaseBookings = await this.bookingsRepository.getByIdsWithAttendeesAndUserAndEvent(bookingsIds);
const bookingsMap = new Map(databaseBookings.map(booking => [booking.id, booking]));
const transformed = bookingsIds.map(bookingId => {

const bookingsMap = new Map(databaseBookings.map((booking) => [booking.id, booking]));

const transformed = bookingsIds.map((bookingId) => {
const databaseBooking = bookingsMap.get(bookingId);
if (!databaseBooking) {
throw new Error(`Booking with id=${bookingId} was not found in the database`);
Expand Down Expand Up @@ -313,9 +312,8 @@ export class OutputBookingsService_2024_08_13 {
bookingTransformed.bookingFieldsResponses?.guests &&
Array.isArray(bookingTransformed.bookingFieldsResponses.guests)
) {
bookingTransformed.bookingFieldsResponses.displayGuests = bookingTransformed.bookingFieldsResponses.guests.map(
(guest: string) => this.getDisplayEmail(guest)
);
bookingTransformed.bookingFieldsResponses.displayGuests =
bookingTransformed.bookingFieldsResponses.guests.map((guest: string) => this.getDisplayEmail(guest));
}

return bookingTransformed;
Expand Down Expand Up @@ -389,6 +387,7 @@ export class OutputBookingsService_2024_08_13 {
absent: !!attendee.noShow,
seatUid: attendee.bookingSeat?.referenceUid,
bookingFieldsResponses: {},
createdAt: attendee.createdAt,
};
const attendeeParsed = plainToClass(SeatedAttendee, attendeeData, { strategy: "excludeAll" });
attendeeParsed.bookingFieldsResponses = responses || {};
Expand All @@ -409,11 +408,12 @@ export class OutputBookingsService_2024_08_13 {
}

async getOutputRecurringSeatedBookings(bookingsIds: number[], showAttendees: boolean) {
const databaseBookings = await this.bookingsRepository.getByIdsWithAttendeesWithBookingSeatAndUserAndEvent(bookingsIds);

const bookingsMap = new Map(databaseBookings.map(booking => [booking.id, booking]));

const transformed = bookingsIds.map(bookingId => {
const databaseBookings =
await this.bookingsRepository.getByIdsWithAttendeesWithBookingSeatAndUserAndEvent(bookingsIds);

const bookingsMap = new Map(databaseBookings.map((booking) => [booking.id, booking]));

const transformed = bookingsIds.map((bookingId) => {
const databaseBooking = bookingsMap.get(bookingId);
if (!databaseBooking) {
throw new Error(`Booking with id=${bookingId} was not found in the database`);
Expand Down Expand Up @@ -518,6 +518,7 @@ export class OutputBookingsService_2024_08_13 {
absent: !!attendee.noShow,
seatUid: attendee.bookingSeat?.referenceUid,
bookingFieldsResponses: {},
createdAt: attendee.createdAt,
};
const attendeeParsed = plainToClass(SeatedAttendee, attendeeData, { strategy: "excludeAll" });
attendeeParsed.bookingFieldsResponses = responses || {};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1705,6 +1705,7 @@ async function handler(
id: 0,
email: bookerEmail,
name: fullName,
createdAt: new Date(),
timeZone: reqBody.timeZone,
locale: null,
phoneNumber: null,
Expand Down
2 changes: 2 additions & 0 deletions packages/features/bookings/repositories/AttendeeRepository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ const safeSelect = {
phoneNumber: true,
bookingId: true,
noShow: true,
createdAt: true,
};

/**
Expand Down Expand Up @@ -101,6 +102,7 @@ export class AttendeeRepository implements IAttendeeRepository {
phoneNumber: string | null;
bookingId: number | null;
noShow: boolean | null;
createdAt: Date | null;
}[]
> {
return this.prismaClient.attendee.findMany({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ export class BookingSeatRepository {
email: true,
locale: true,
timeZone: true,
createdAt: true,
},
},
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ type UpdatedAttendee = {
phoneNumber: string | null;
bookingId: number | null;
noShow: boolean | null;
createdAt: Date | null;
};

const markGuestAsNoshowInBooking = async ({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,16 @@ import {
IsArray,
IsBoolean,
IsDateString,
IsEmail,
IsEnum,
IsInt,
IsObject,
IsOptional,
IsString,
IsEmail,
IsTimeZone,
IsUrl,
ValidateNested,
} from "class-validator";

import type { BookingLanguageType } from "../inputs/language";
import { BookingLanguage } from "../inputs/language";

Expand Down Expand Up @@ -63,6 +62,16 @@ export class SeatedAttendee extends BookingAttendee {
@Expose()
seatUid!: string;

@ApiPropertyOptional({
type: String,
example: "2024-08-13T15:30:00Z",
description: "The date and time when the attendee joined the seated booking.",
})
@IsDateString()
@IsOptional()
@Expose()
createdAt?: string;

@ApiProperty({
type: Object,
description:
Expand Down Expand Up @@ -100,7 +109,11 @@ class BookingHost {
@Expose()
email!: string;

@ApiProperty({ type: String, example: "jane100@example.com", description: "Clean email for display purposes" })
@ApiProperty({
type: String,
example: "jane100@example.com",
description: "Clean email for display purposes",
})
@IsString()
@Expose()
displayEmail!: string;
Expand Down Expand Up @@ -406,7 +419,11 @@ class ReassignedToDto {
@Expose()
email!: string;

@ApiProperty({ type: String, example: "john.doe@example.com", description: "Clean email for display purposes" })
@ApiProperty({
type: String,
example: "john.doe@example.com",
description: "Clean email for display purposes",
})
@IsString()
@Expose()
displayEmail!: string;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
-- AlterTable
ALTER TABLE "Attendee" ADD COLUMN "createdAt" TIMESTAMP(3) DEFAULT CURRENT_TIMESTAMP;
1 change: 1 addition & 0 deletions packages/prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -842,6 +842,7 @@ model Attendee {
bookingId Int?
bookingSeat BookingSeat?
noShow Boolean? @default(false)
createdAt DateTime? @default(now())

@@index([email])
@@index([bookingId])
Expand Down
4 changes: 3 additions & 1 deletion packages/testing/src/lib/bookingScenario/bookingScenario.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2307,11 +2307,12 @@ export function getMockBookingReference(
}

export function getMockBookingAttendee(
attendee: Omit<Attendee, "bookingId" | "phoneNumber" | "email" | "noShow"> & {
attendee: Omit<Attendee, "bookingId" | "phoneNumber" | "email" | "noShow" | "createdAt"> & {
bookingSeat?: AttendeeBookingSeatInput;
phoneNumber?: string | null;
email: string;
noShow?: boolean;
createdAt?: Date | null;
}
) {
return {
Expand All @@ -2323,6 +2324,7 @@ export function getMockBookingAttendee(
bookingSeat: attendee.bookingSeat || null,
phoneNumber: attendee.phoneNumber ?? undefined,
noShow: attendee.noShow ?? false,
createdAt: attendee.createdAt ?? null,
};
}

Expand Down
Loading