From fde1d671fb547763eaf1ecfb3dbd7b960439d38a Mon Sep 17 00:00:00 2001 From: Alex van Andel Date: Sat, 26 Jul 2025 00:59:32 +0100 Subject: [PATCH] refactor: rename SelectedSlotsRepository to PrismaSelectedSlotRepository (#22705) * refactor: rename SelectedSlotsRepository to PrismaSelectedSlotRepository - Rename class from SelectedSlotsRepository to PrismaSelectedSlotRepository for consistency - Update all imports and type references throughout the codebase - Update DI module bindings and variable names - Maintain type safety without breaking changes Co-Authored-By: alex@cal.com * refactor: complete PrismaSelectedSlotRepository rename - Update DI module binding property name from selectedSlotsRepo to selectedSlotRepo - Update test mock to use PrismaSelectedSlotRepository class name - Ensure all references are consistently updated Co-Authored-By: alex@cal.com * fix: rename file to prismaSelectedSlotRepository.ts and fix plural variable references - Rename apps/api/v2/src/lib/repositories/prisma-selected-slots.repository.ts to prismaSelectedSlotRepository.ts - Update class name from PrismaSelectedSlotsRepository to PrismaSelectedSlotRepository - Fix plural variable reference selectedSlotsRepository to selectedSlotRepository in service - Update all import paths to reference the new file name - Maintain consistency with camelCase naming convention Co-Authored-By: alex@cal.com * fix: rename SelectedSlotsRepositoryFixture to SelectedSlotRepositoryFixture for consistency - Rename test fixture class from SelectedSlotsRepositoryFixture to SelectedSlotRepositoryFixture (singular) - Update all import statements in test files to use the renamed class - Resolves naming mismatch between fixture class and variable declarations - Fixes TypeScript compilation errors in CI tests Co-Authored-By: alex@cal.com * feat: add SelectedSlotRepositoryInterface and update DI to use interface Co-Authored-By: alex@cal.com * refactor: rename selectedSlots.ts to selectedSlot.ts and update all imports Co-Authored-By: alex@cal.com * fix: update test mock import path after file rename Co-Authored-By: alex@cal.com * Update apps/api/v2/src/lib/modules/available-slots.module.ts * Update apps/api/v2/src/lib/modules/available-slots.module.ts * Implement DTO * dont declare dependencies locally, duplicating * Small DTO/token fix * chore: bump @calcom/platform-libraries from 0.0.266 to 0.0.267 * oops. * Update fixture names also * Omg these vscode actions preventing saves * Final fix, hopefully --------- Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-authored-by: Somay Chauhan --- apps/api/v2/package.json | 2 +- .../src/lib/modules/available-slots.module.ts | 4 +-- ....ts => prisma-selected-slot.repository.ts} | 4 +-- .../lib/services/available-slots.service.ts | 20 +++++------ .../controllers/slots.controller.e2e-spec.ts | 8 ++--- ...am-event-type-slots.controller.e2e-spec.ts | 10 +++--- ...er-event-type-slots.controller.e2e-spec.ts | 32 ++++++++--------- ...ts => selected-slot.repository.fixture.ts} | 4 +-- packages/lib/di/containers/available-slots.ts | 2 +- packages/lib/di/modules/available-slots.ts | 2 +- packages/lib/di/modules/selectedSlots.ts | 6 ++-- packages/lib/di/tokens.ts | 4 +-- .../repository/ISelectedSlotRepository.ts | 24 +++++++++++++ ...ots.ts => PrismaSelectedSlotRepository.ts} | 34 ++++--------------- .../lib/server/repository/dto/SelectedSlot.ts | 6 ++++ .../server/repository/dto/hashedLinkSelect.ts | 19 +++++++++++ ...selectedSlotDeleteManyExpiredSlotsInput.ts | 0 ...lectedSlotFindManyReservedByOthersInput.ts | 0 ...selectedSlotFindManyUnexpiredSlotsInput.ts | 0 .../selectedSlotFindReservedByOthersInput.ts | 0 packages/platform/libraries/repositories.ts | 2 +- .../viewer/slots/isAvailable.handler.ts | 4 +-- .../viewer/slots/reserveSlot.handler.test.ts | 6 ++-- .../viewer/slots/reserveSlot.handler.ts | 4 +-- .../trpc/server/routers/viewer/slots/util.ts | 6 ++-- yarn.lock | 10 +++--- 26 files changed, 120 insertions(+), 93 deletions(-) rename apps/api/v2/src/lib/repositories/{prisma-selected-slots.repository.ts => prisma-selected-slot.repository.ts} (59%) rename apps/api/v2/test/fixtures/repository/{selected-slots.repository.fixture.ts => selected-slot.repository.fixture.ts} (88%) create mode 100644 packages/lib/server/repository/ISelectedSlotRepository.ts rename packages/lib/server/repository/{selectedSlots.ts => PrismaSelectedSlotRepository.ts} (72%) create mode 100644 packages/lib/server/repository/dto/SelectedSlot.ts create mode 100644 packages/lib/server/repository/dto/hashedLinkSelect.ts create mode 100644 packages/lib/server/repository/dto/selectedSlotDeleteManyExpiredSlotsInput.ts create mode 100644 packages/lib/server/repository/dto/selectedSlotFindManyReservedByOthersInput.ts create mode 100644 packages/lib/server/repository/dto/selectedSlotFindManyUnexpiredSlotsInput.ts create mode 100644 packages/lib/server/repository/dto/selectedSlotFindReservedByOthersInput.ts diff --git a/apps/api/v2/package.json b/apps/api/v2/package.json index 35edf235f4..87c2b98682 100644 --- a/apps/api/v2/package.json +++ b/apps/api/v2/package.json @@ -38,7 +38,7 @@ "@axiomhq/winston": "^1.2.0", "@calcom/platform-constants": "*", "@calcom/platform-enums": "*", - "@calcom/platform-libraries": "npm:@calcom/platform-libraries@0.0.266", + "@calcom/platform-libraries": "npm:@calcom/platform-libraries@0.0.267", "@calcom/platform-types": "*", "@calcom/platform-utils": "*", "@calcom/prisma": "*", diff --git a/apps/api/v2/src/lib/modules/available-slots.module.ts b/apps/api/v2/src/lib/modules/available-slots.module.ts index 5be48dcf44..c1f55f55f3 100644 --- a/apps/api/v2/src/lib/modules/available-slots.module.ts +++ b/apps/api/v2/src/lib/modules/available-slots.module.ts @@ -3,7 +3,7 @@ import { PrismaEventTypeRepository } from "@/lib/repositories/prisma-event-type. import { PrismaOOORepository } from "@/lib/repositories/prisma-ooo.repository"; import { PrismaRoutingFormResponseRepository } from "@/lib/repositories/prisma-routing-form-response.repository"; import { PrismaScheduleRepository } from "@/lib/repositories/prisma-schedule.repository"; -import { PrismaSelectedSlotsRepository } from "@/lib/repositories/prisma-selected-slots.repository"; +import { PrismaSelectedSlotRepository } from "@/lib/repositories/prisma-selected-slot.repository"; import { PrismaTeamRepository } from "@/lib/repositories/prisma-team.repository"; import { PrismaUserRepository } from "@/lib/repositories/prisma-user.repository"; import { AvailableSlotsService } from "@/lib/services/available-slots.service"; @@ -16,7 +16,7 @@ import { Module } from "@nestjs/common"; PrismaOOORepository, PrismaScheduleRepository, PrismaBookingRepository, - PrismaSelectedSlotsRepository, + PrismaSelectedSlotRepository, PrismaUserRepository, PrismaEventTypeRepository, PrismaRoutingFormResponseRepository, diff --git a/apps/api/v2/src/lib/repositories/prisma-selected-slots.repository.ts b/apps/api/v2/src/lib/repositories/prisma-selected-slot.repository.ts similarity index 59% rename from apps/api/v2/src/lib/repositories/prisma-selected-slots.repository.ts rename to apps/api/v2/src/lib/repositories/prisma-selected-slot.repository.ts index bbc92a3b2c..09a77f5b6a 100644 --- a/apps/api/v2/src/lib/repositories/prisma-selected-slots.repository.ts +++ b/apps/api/v2/src/lib/repositories/prisma-selected-slot.repository.ts @@ -1,11 +1,11 @@ import { PrismaWriteService } from "@/modules/prisma/prisma-write.service"; import { Injectable } from "@nestjs/common"; -import { PrismaSelectedSlotsRepository as PrismaSelectedSlotsRepositoryLib } from "@calcom/platform-libraries/repositories"; +import { PrismaSelectedSlotRepository as PrismaSelectedSlotRepositoryLib } from "@calcom/platform-libraries/repositories"; import { PrismaClient } from "@calcom/prisma"; @Injectable() -export class PrismaSelectedSlotsRepository extends PrismaSelectedSlotsRepositoryLib { +export class PrismaSelectedSlotRepository extends PrismaSelectedSlotRepositoryLib { constructor(private readonly dbWrite: PrismaWriteService) { super(dbWrite.prisma as unknown as PrismaClient); } diff --git a/apps/api/v2/src/lib/services/available-slots.service.ts b/apps/api/v2/src/lib/services/available-slots.service.ts index e091301001..7f6ce1725c 100644 --- a/apps/api/v2/src/lib/services/available-slots.service.ts +++ b/apps/api/v2/src/lib/services/available-slots.service.ts @@ -3,7 +3,7 @@ import { PrismaEventTypeRepository } from "@/lib/repositories/prisma-event-type. import { PrismaOOORepository } from "@/lib/repositories/prisma-ooo.repository"; import { PrismaRoutingFormResponseRepository } from "@/lib/repositories/prisma-routing-form-response.repository"; import { PrismaScheduleRepository } from "@/lib/repositories/prisma-schedule.repository"; -import { PrismaSelectedSlotsRepository } from "@/lib/repositories/prisma-selected-slots.repository"; +import { PrismaSelectedSlotRepository } from "@/lib/repositories/prisma-selected-slot.repository"; import { PrismaTeamRepository } from "@/lib/repositories/prisma-team.repository"; import { PrismaUserRepository } from "@/lib/repositories/prisma-user.repository"; import { Injectable } from "@nestjs/common"; @@ -13,14 +13,14 @@ import { AvailableSlotsService as BaseAvailableSlotsService } from "@calcom/plat @Injectable() export class AvailableSlotsService extends BaseAvailableSlotsService { constructor( - private readonly oooRepoDependency: PrismaOOORepository, - private readonly scheduleRepoDependency: PrismaScheduleRepository, - private readonly teamRepository: PrismaTeamRepository, - private readonly routingFormResponseRepository: PrismaRoutingFormResponseRepository, - private readonly bookingRepository: PrismaBookingRepository, - private readonly selectedSlotsRepository: PrismaSelectedSlotsRepository, - private readonly eventTypeRepository: PrismaEventTypeRepository, - private readonly userRepository: PrismaUserRepository + oooRepoDependency: PrismaOOORepository, + scheduleRepoDependency: PrismaScheduleRepository, + teamRepository: PrismaTeamRepository, + routingFormResponseRepository: PrismaRoutingFormResponseRepository, + bookingRepository: PrismaBookingRepository, + selectedSlotRepository: PrismaSelectedSlotRepository, + eventTypeRepository: PrismaEventTypeRepository, + userRepository: PrismaUserRepository ) { super({ oooRepo: oooRepoDependency, @@ -28,7 +28,7 @@ export class AvailableSlotsService extends BaseAvailableSlotsService { teamRepo: teamRepository, routingFormResponseRepo: routingFormResponseRepository, bookingRepo: bookingRepository, - selectedSlotsRepo: selectedSlotsRepository, + selectedSlotRepo: selectedSlotRepository, eventTypeRepo: eventTypeRepository, userRepo: userRepository, }); diff --git a/apps/api/v2/src/modules/slots/slots-2024-04-15/controllers/slots.controller.e2e-spec.ts b/apps/api/v2/src/modules/slots/slots-2024-04-15/controllers/slots.controller.e2e-spec.ts index a32c0e9737..028a0461f4 100644 --- a/apps/api/v2/src/modules/slots/slots-2024-04-15/controllers/slots.controller.e2e-spec.ts +++ b/apps/api/v2/src/modules/slots/slots-2024-04-15/controllers/slots.controller.e2e-spec.ts @@ -16,7 +16,7 @@ import { AttendeeRepositoryFixture } from "test/fixtures/repository/attendee.rep import { BookingSeatRepositoryFixture } from "test/fixtures/repository/booking-seat.repository.fixture"; import { BookingsRepositoryFixture } from "test/fixtures/repository/bookings.repository.fixture"; import { EventTypesRepositoryFixture } from "test/fixtures/repository/event-types.repository.fixture"; -import { SelectedSlotsRepositoryFixture } from "test/fixtures/repository/selected-slots.repository.fixture"; +import { SelectedSlotRepositoryFixture } from "test/fixtures/repository/selected-slot.repository.fixture"; import { UserRepositoryFixture } from "test/fixtures/repository/users.repository.fixture"; import { randomString } from "test/utils/randomString"; import { withApiAuth } from "test/utils/withApiAuth"; @@ -250,7 +250,7 @@ describe("Slots 2024-04-15 Endpoints", () => { let userRepositoryFixture: UserRepositoryFixture; let schedulesService: SchedulesService_2024_06_11; let eventTypesRepositoryFixture: EventTypesRepositoryFixture; - let selectedSlotsRepositoryFixture: SelectedSlotsRepositoryFixture; + let selectedSlotRepositoryFixture: SelectedSlotRepositoryFixture; let bookingsRepositoryFixture: BookingsRepositoryFixture; let bookingSeatsRepositoryFixture: BookingSeatRepositoryFixture; let attendeesRepositoryFixture: AttendeeRepositoryFixture; @@ -288,7 +288,7 @@ describe("Slots 2024-04-15 Endpoints", () => { userRepositoryFixture = new UserRepositoryFixture(moduleRef); schedulesService = moduleRef.get(SchedulesService_2024_06_11); eventTypesRepositoryFixture = new EventTypesRepositoryFixture(moduleRef); - selectedSlotsRepositoryFixture = new SelectedSlotsRepositoryFixture(moduleRef); + selectedSlotRepositoryFixture = new SelectedSlotRepositoryFixture(moduleRef); bookingsRepositoryFixture = new BookingsRepositoryFixture(moduleRef); bookingSeatsRepositoryFixture = new BookingSeatRepositoryFixture(moduleRef); attendeesRepositoryFixture = new AttendeeRepositoryFixture(moduleRef); @@ -773,7 +773,7 @@ describe("Slots 2024-04-15 Endpoints", () => { afterAll(async () => { await userRepositoryFixture.deleteByEmail(user.email); - await selectedSlotsRepositoryFixture.deleteByUId(reservedSlotUid); + await selectedSlotRepositoryFixture.deleteByUId(reservedSlotUid); await bookingsRepositoryFixture.deleteAllBookings(user.id, user.email); await app.close(); diff --git a/apps/api/v2/src/modules/slots/slots-2024-09-04/controllers/e2e/team-event-type-slots.controller.e2e-spec.ts b/apps/api/v2/src/modules/slots/slots-2024-09-04/controllers/e2e/team-event-type-slots.controller.e2e-spec.ts index 1b4181f8b1..a2db140ad4 100644 --- a/apps/api/v2/src/modules/slots/slots-2024-09-04/controllers/e2e/team-event-type-slots.controller.e2e-spec.ts +++ b/apps/api/v2/src/modules/slots/slots-2024-09-04/controllers/e2e/team-event-type-slots.controller.e2e-spec.ts @@ -21,7 +21,7 @@ import { ApiKeysRepositoryFixture } from "test/fixtures/repository/api-keys.repo import { BookingsRepositoryFixture } from "test/fixtures/repository/bookings.repository.fixture"; import { EventTypesRepositoryFixture } from "test/fixtures/repository/event-types.repository.fixture"; import { MembershipRepositoryFixture } from "test/fixtures/repository/membership.repository.fixture"; -import { SelectedSlotsRepositoryFixture } from "test/fixtures/repository/selected-slots.repository.fixture"; +import { SelectedSlotRepositoryFixture } from "test/fixtures/repository/selected-slot.repository.fixture"; import { TeamRepositoryFixture } from "test/fixtures/repository/team.repository.fixture"; import { UserRepositoryFixture } from "test/fixtures/repository/users.repository.fixture"; import { randomString } from "test/utils/randomString"; @@ -44,7 +44,7 @@ describe("Slots 2024-09-04 Endpoints", () => { let membershipsRepositoryFixture: MembershipRepositoryFixture; let bookingsRepositoryFixture: BookingsRepositoryFixture; let apiKeysRepositoryFixture: ApiKeysRepositoryFixture; - let selectedSlotsRepositoryFixture: SelectedSlotsRepositoryFixture; + let selectedSlotRepositoryFixture: SelectedSlotRepositoryFixture; const teammateEmailOne = `slots-2024-09-04-user-1-team-slots-${randomString()}`; let teammateApiKeyString: string; @@ -93,7 +93,7 @@ describe("Slots 2024-09-04 Endpoints", () => { membershipsRepositoryFixture = new MembershipRepositoryFixture(moduleRef); bookingsRepositoryFixture = new BookingsRepositoryFixture(moduleRef); apiKeysRepositoryFixture = new ApiKeysRepositoryFixture(moduleRef); - selectedSlotsRepositoryFixture = new SelectedSlotsRepositoryFixture(moduleRef); + selectedSlotRepositoryFixture = new SelectedSlotRepositoryFixture(moduleRef); teammateOne = await userRepositoryFixture.create({ email: teammateEmailOne, @@ -368,14 +368,14 @@ describe("Slots 2024-09-04 Endpoints", () => { ); expect(slots).toEqual({ ...expectedSlotsUTC, "2050-09-05": expectedSlotsUTC2050_09_05 }); - const dbSlot = await selectedSlotsRepositoryFixture.getByUid(reservedSlot.reservationUid); + const dbSlot = await selectedSlotRepositoryFixture.getByUid(reservedSlot.reservationUid); expect(dbSlot).toBeDefined(); if (dbSlot) { const dbReleaseAt = DateTime.fromJSDate(dbSlot.releaseAt, { zone: "UTC" }).toISO(); const expectedReleaseAt = DateTime.fromISO(now, { zone: "UTC" }).plus({ minutes: 10 }).toISO(); expect(dbReleaseAt).toEqual(expectedReleaseAt); } - await selectedSlotsRepositoryFixture.deleteByUId(reservedSlot.reservationUid); + await selectedSlotRepositoryFixture.deleteByUId(reservedSlot.reservationUid); clear(); }); diff --git a/apps/api/v2/src/modules/slots/slots-2024-09-04/controllers/e2e/user-event-type-slots.controller.e2e-spec.ts b/apps/api/v2/src/modules/slots/slots-2024-09-04/controllers/e2e/user-event-type-slots.controller.e2e-spec.ts index 43cae7d6ed..1fe42764d2 100644 --- a/apps/api/v2/src/modules/slots/slots-2024-09-04/controllers/e2e/user-event-type-slots.controller.e2e-spec.ts +++ b/apps/api/v2/src/modules/slots/slots-2024-09-04/controllers/e2e/user-event-type-slots.controller.e2e-spec.ts @@ -30,7 +30,7 @@ import { BookingsRepositoryFixture } from "test/fixtures/repository/bookings.rep import { EventTypesRepositoryFixture } from "test/fixtures/repository/event-types.repository.fixture"; import { MembershipRepositoryFixture } from "test/fixtures/repository/membership.repository.fixture"; import { OOORepositoryFixture } from "test/fixtures/repository/ooo.repository.fixture"; -import { SelectedSlotsRepositoryFixture } from "test/fixtures/repository/selected-slots.repository.fixture"; +import { SelectedSlotRepositoryFixture } from "test/fixtures/repository/selected-slot.repository.fixture"; import { TeamRepositoryFixture } from "test/fixtures/repository/team.repository.fixture"; import { UserRepositoryFixture } from "test/fixtures/repository/users.repository.fixture"; import { randomString } from "test/utils/randomString"; @@ -49,7 +49,7 @@ describe("Slots 2024-09-04 Endpoints", () => { let userRepositoryFixture: UserRepositoryFixture; let schedulesService: SchedulesService_2024_06_11; let eventTypesRepositoryFixture: EventTypesRepositoryFixture; - let selectedSlotsRepositoryFixture: SelectedSlotsRepositoryFixture; + let selectedSlotRepositoryFixture: SelectedSlotRepositoryFixture; let bookingsRepositoryFixture: BookingsRepositoryFixture; let bookingSeatsRepositoryFixture: BookingSeatRepositoryFixture; let attendeesRepositoryFixture: AttendeeRepositoryFixture; @@ -104,7 +104,7 @@ describe("Slots 2024-09-04 Endpoints", () => { userRepositoryFixture = new UserRepositoryFixture(moduleRef); schedulesService = moduleRef.get(SchedulesService_2024_06_11); eventTypesRepositoryFixture = new EventTypesRepositoryFixture(moduleRef); - selectedSlotsRepositoryFixture = new SelectedSlotsRepositoryFixture(moduleRef); + selectedSlotRepositoryFixture = new SelectedSlotRepositoryFixture(moduleRef); bookingsRepositoryFixture = new BookingsRepositoryFixture(moduleRef); bookingSeatsRepositoryFixture = new BookingSeatRepositoryFixture(moduleRef); attendeesRepositoryFixture = new AttendeeRepositoryFixture(moduleRef); @@ -483,7 +483,7 @@ describe("Slots 2024-09-04 Endpoints", () => { ); expect(slots).toEqual({ ...expectedSlotsUTC, "2050-09-05": expectedSlotsUTC2050_09_05 }); - const dbSlot = await selectedSlotsRepositoryFixture.getByUid(reservedSlot.reservationUid); + const dbSlot = await selectedSlotRepositoryFixture.getByUid(reservedSlot.reservationUid); expect(dbSlot).toBeDefined(); if (dbSlot) { const dbReleaseAt = DateTime.fromJSDate(dbSlot.releaseAt, { zone: "UTC" }).toISO(); @@ -617,7 +617,7 @@ describe("Slots 2024-09-04 Endpoints", () => { ); expect(slots).toEqual({ ...expectedSlotsUTC, "2050-09-05": expectedSlotsUTC2050_09_05 }); - const dbSlot = await selectedSlotsRepositoryFixture.getByUid(reservedSlot.reservationUid); + const dbSlot = await selectedSlotRepositoryFixture.getByUid(reservedSlot.reservationUid); expect(dbSlot).toBeDefined(); if (dbSlot) { const dbReleaseAt = DateTime.fromJSDate(dbSlot.releaseAt, { zone: "UTC" }).toISO(); @@ -719,14 +719,14 @@ describe("Slots 2024-09-04 Endpoints", () => { ); expect(slots).toEqual({ ...expectedSlotsUTC, "2050-09-05": expectedSlotsUTC2050_09_05 }); - const dbSlot = await selectedSlotsRepositoryFixture.getByUid(reservedSlot.reservationUid); + const dbSlot = await selectedSlotRepositoryFixture.getByUid(reservedSlot.reservationUid); expect(dbSlot).toBeDefined(); if (dbSlot) { const dbReleaseAt = DateTime.fromJSDate(dbSlot.releaseAt, { zone: "UTC" }).toISO(); const expectedReleaseAt = DateTime.fromISO(now, { zone: "UTC" }).plus({ minutes: 10 }).toISO(); expect(dbReleaseAt).toEqual(expectedReleaseAt); } - await selectedSlotsRepositoryFixture.deleteByUId(reservedSlot.reservationUid); + await selectedSlotRepositoryFixture.deleteByUId(reservedSlot.reservationUid); clear(); }); @@ -775,14 +775,14 @@ describe("Slots 2024-09-04 Endpoints", () => { ); expect(slots).toEqual({ ...expectedSlotsUTC, "2050-09-05": expectedSlotsUTC2050_09_05 }); - const dbSlot = await selectedSlotsRepositoryFixture.getByUid(reservedSlot.reservationUid); + const dbSlot = await selectedSlotRepositoryFixture.getByUid(reservedSlot.reservationUid); expect(dbSlot).toBeDefined(); if (dbSlot) { const dbReleaseAt = DateTime.fromJSDate(dbSlot.releaseAt, { zone: "UTC" }).toISO(); const expectedReleaseAt = DateTime.fromISO(now, { zone: "UTC" }).plus({ minutes: 10 }).toISO(); expect(dbReleaseAt).toEqual(expectedReleaseAt); } - await selectedSlotsRepositoryFixture.deleteByUId(reservedSlot.reservationUid); + await selectedSlotRepositoryFixture.deleteByUId(reservedSlot.reservationUid); clear(); }); @@ -1397,7 +1397,7 @@ describe("Slots 2024-09-04 Endpoints", () => { ); expect(slots).toEqual({ ...expectedSlotsUTC, "2050-09-05": expectedSlotsUTC2050_09_05 }); - const dbSlot = await selectedSlotsRepositoryFixture.getByUid( + const dbSlot = await selectedSlotRepositoryFixture.getByUid( responseReservedVariableSlot.reservationUid ); expect(dbSlot).toBeDefined(); @@ -1674,7 +1674,7 @@ describe("Slots 2024-09-04 Endpoints", () => { ); expect(responseReservedSlot.reservationDuration).toEqual(5); await bookingsRepositoryFixture.deleteById(booking.id); - await selectedSlotsRepositoryFixture.deleteByUId(responseReservedSlot.reservationUid); + await selectedSlotRepositoryFixture.deleteByUId(responseReservedSlot.reservationUid); }); it("should be able to reserve a slot if booking is rejected during that time", async () => { @@ -1872,7 +1872,7 @@ describe("Slots 2024-09-04 Endpoints", () => { expect(reserveResponse.body.data.reservationUid).toBeDefined(); - await selectedSlotsRepositoryFixture.deleteByUId(reserveResponse.body.data.reservationUid); + await selectedSlotRepositoryFixture.deleteByUId(reserveResponse.body.data.reservationUid); await bookingsRepositoryFixture.deleteById(existingBooking.id); }); @@ -1888,7 +1888,7 @@ describe("Slots 2024-09-04 Endpoints", () => { expect(reserveResponse.body.data.reservationUid).toBeDefined(); - await selectedSlotsRepositoryFixture.deleteByUId(reserveResponse.body.data.reservationUid); + await selectedSlotRepositoryFixture.deleteByUId(reserveResponse.body.data.reservationUid); await bookingsRepositoryFixture.deleteById(existingBooking.id); }); @@ -1903,7 +1903,7 @@ describe("Slots 2024-09-04 Endpoints", () => { .expect(201); expect(reserveResponse.body.data.reservationUid).toBeDefined(); - await selectedSlotsRepositoryFixture.deleteByUId(reserveResponse.body.data.reservationUid); + await selectedSlotRepositoryFixture.deleteByUId(reserveResponse.body.data.reservationUid); await bookingsRepositoryFixture.deleteById(existingBooking.id); }); @@ -1918,7 +1918,7 @@ describe("Slots 2024-09-04 Endpoints", () => { .expect(201); expect(reserveResponse.body.data.reservationUid).toBeDefined(); - await selectedSlotsRepositoryFixture.deleteByUId(reserveResponse.body.data.reservationUid); + await selectedSlotRepositoryFixture.deleteByUId(reserveResponse.body.data.reservationUid); await bookingsRepositoryFixture.deleteById(existingBooking.id); }); }); @@ -1928,7 +1928,7 @@ describe("Slots 2024-09-04 Endpoints", () => { await userRepositoryFixture.deleteByEmail(user.email); await userRepositoryFixture.deleteByEmail(oooTestUserEmail); await userRepositoryFixture.deleteByEmail(unrelatedUser.email); - await selectedSlotsRepositoryFixture.deleteByUId(reservedSlot.reservationUid); + await selectedSlotRepositoryFixture.deleteByUId(reservedSlot.reservationUid); await bookingsRepositoryFixture.deleteAllBookings(user.id, user.email); await teamRepositoryFixture.delete(team.id); clear(); diff --git a/apps/api/v2/test/fixtures/repository/selected-slots.repository.fixture.ts b/apps/api/v2/test/fixtures/repository/selected-slot.repository.fixture.ts similarity index 88% rename from apps/api/v2/test/fixtures/repository/selected-slots.repository.fixture.ts rename to apps/api/v2/test/fixtures/repository/selected-slot.repository.fixture.ts index e894935d39..e4f9221e84 100644 --- a/apps/api/v2/test/fixtures/repository/selected-slots.repository.fixture.ts +++ b/apps/api/v2/test/fixtures/repository/selected-slot.repository.fixture.ts @@ -3,11 +3,11 @@ import { PrismaWriteService } from "@/modules/prisma/prisma-write.service"; import { TestingModule } from "@nestjs/testing"; import { SelectedSlots } from "@prisma/client"; -export class SelectedSlotsRepositoryFixture { +export class SelectedSlotRepositoryFixture { private prismaReadClient: PrismaReadService["prisma"]; private prismaWriteClient: PrismaWriteService["prisma"]; - constructor(private readonly module: TestingModule) { + constructor(module: TestingModule) { this.prismaReadClient = module.get(PrismaReadService).prisma; this.prismaWriteClient = module.get(PrismaWriteService).prisma; } diff --git a/packages/lib/di/containers/available-slots.ts b/packages/lib/di/containers/available-slots.ts index 824467363a..927b042d66 100644 --- a/packages/lib/di/containers/available-slots.ts +++ b/packages/lib/di/containers/available-slots.ts @@ -18,7 +18,7 @@ const container = createContainer(); container.load(DI_TOKENS.PRISMA_MODULE, prismaModule); container.load(DI_TOKENS.OOO_REPOSITORY_MODULE, oooRepositoryModule); container.load(DI_TOKENS.SCHEDULE_REPOSITORY_MODULE, scheduleRepositoryModule); -container.load(DI_TOKENS.SELECTED_SLOTS_REPOSITORY_MODULE, selectedSlotsRepositoryModule); +container.load(DI_TOKENS.SELECTED_SLOT_REPOSITORY_MODULE, selectedSlotsRepositoryModule); container.load(DI_TOKENS.TEAM_REPOSITORY_MODULE, teamRepositoryModule); container.load(DI_TOKENS.USER_REPOSITORY_MODULE, userRepositoryModule); container.load(DI_TOKENS.BOOKING_REPOSITORY_MODULE, bookingRepositoryModule); diff --git a/packages/lib/di/modules/available-slots.ts b/packages/lib/di/modules/available-slots.ts index c3f663b865..5c34f94f50 100644 --- a/packages/lib/di/modules/available-slots.ts +++ b/packages/lib/di/modules/available-slots.ts @@ -9,7 +9,7 @@ export const availableSlotsModule = createModule(); availableSlotsModule.bind(DI_TOKENS.AVAILABLE_SLOTS_SERVICE).toClass(AvailableSlotsService, { oooRepo: DI_TOKENS.OOO_REPOSITORY, scheduleRepo: DI_TOKENS.SCHEDULE_REPOSITORY, - selectedSlotsRepo: DI_TOKENS.SELECTED_SLOTS_REPOSITORY, + selectedSlotRepo: DI_TOKENS.SELECTED_SLOT_REPOSITORY, teamRepo: DI_TOKENS.TEAM_REPOSITORY, userRepo: DI_TOKENS.USER_REPOSITORY, bookingRepo: DI_TOKENS.BOOKING_REPOSITORY, diff --git a/packages/lib/di/modules/selectedSlots.ts b/packages/lib/di/modules/selectedSlots.ts index ea88af1876..10d76f18b7 100644 --- a/packages/lib/di/modules/selectedSlots.ts +++ b/packages/lib/di/modules/selectedSlots.ts @@ -1,9 +1,9 @@ import { createModule } from "@evyweb/ioctopus"; import { DI_TOKENS } from "@calcom/lib/di/tokens"; -import { SelectedSlotsRepository } from "@calcom/lib/server/repository/selectedSlots"; +import { PrismaSelectedSlotRepository } from "@calcom/lib/server/repository/PrismaSelectedSlotRepository"; export const selectedSlotsRepositoryModule = createModule(); selectedSlotsRepositoryModule - .bind(DI_TOKENS.SELECTED_SLOTS_REPOSITORY) - .toClass(SelectedSlotsRepository, [DI_TOKENS.PRISMA_CLIENT]); + .bind(DI_TOKENS.SELECTED_SLOT_REPOSITORY) + .toClass(PrismaSelectedSlotRepository, [DI_TOKENS.PRISMA_CLIENT]); diff --git a/packages/lib/di/tokens.ts b/packages/lib/di/tokens.ts index b778389a89..7b59701c47 100644 --- a/packages/lib/di/tokens.ts +++ b/packages/lib/di/tokens.ts @@ -6,8 +6,8 @@ export const DI_TOKENS = { OOO_REPOSITORY_MODULE: Symbol("OOORepositoryModule"), SCHEDULE_REPOSITORY: Symbol("ScheduleRepository"), SCHEDULE_REPOSITORY_MODULE: Symbol("ScheduleRepositoryModule"), - SELECTED_SLOTS_REPOSITORY: Symbol("SelectedSlotsRepository"), - SELECTED_SLOTS_REPOSITORY_MODULE: Symbol("SelectedSlotsRepositoryModule"), + SELECTED_SLOT_REPOSITORY: Symbol("SelectedSlotRepository"), + SELECTED_SLOT_REPOSITORY_MODULE: Symbol("SelectedSlotRepositoryModule"), TEAM_REPOSITORY: Symbol("TeamRepository"), TEAM_REPOSITORY_MODULE: Symbol("TeamRepositoryModule"), USER_REPOSITORY: Symbol("UserRepository"), diff --git a/packages/lib/server/repository/ISelectedSlotRepository.ts b/packages/lib/server/repository/ISelectedSlotRepository.ts new file mode 100644 index 0000000000..7f645e872e --- /dev/null +++ b/packages/lib/server/repository/ISelectedSlotRepository.ts @@ -0,0 +1,24 @@ +import type { SelectedSlot } from "./dto/SelectedSlot"; + +export type TimeSlot = { + utcStartIso: string; + utcEndIso: string; +}; + +export interface ISelectedSlotRepository { + findReservedByOthers(args: { + slot: TimeSlot; + eventTypeId: number; + uid: string; + }): Promise; + findManyReservedByOthers( + slots: TimeSlot[], + eventTypeId: number, + uid: string + ): Promise>>; + findManyUnexpiredSlots(args: { + userIds: number[]; + currentTimeInUtc: string; + }): Promise>>; + deleteManyExpiredSlots(args: { eventTypeId: number; currentTimeInUtc: string }): Promise<{ count: number }>; +} diff --git a/packages/lib/server/repository/selectedSlots.ts b/packages/lib/server/repository/PrismaSelectedSlotRepository.ts similarity index 72% rename from packages/lib/server/repository/selectedSlots.ts rename to packages/lib/server/repository/PrismaSelectedSlotRepository.ts index f86873f48d..da0cff8905 100644 --- a/packages/lib/server/repository/selectedSlots.ts +++ b/packages/lib/server/repository/PrismaSelectedSlotRepository.ts @@ -1,35 +1,13 @@ -import type { Prisma } from "@prisma/client"; - import type { PrismaClient } from "@calcom/prisma"; +import type { Prisma } from "@calcom/prisma/client"; -type WhereCondition = { - slotUtcStartDate?: Date | string; - slotUtcEndDate?: Date | string; - eventTypeId?: number; - uid?: string | { not: string }; - releaseAt?: { gt: Date }; -}; +import type { ISelectedSlotRepository } from "./ISelectedSlotRepository"; +import type { TimeSlot } from "./ISelectedSlotRepository"; -export type FindManyArgs = { - where?: WhereCondition & { - OR?: WhereCondition[]; - }; - select?: Prisma.SelectedSlotsSelect; -}; - -export type TimeSlot = { - utcStartIso: string; - utcEndIso: string; -}; - -export class SelectedSlotsRepository { +export class PrismaSelectedSlotRepository implements ISelectedSlotRepository { constructor(private prismaClient: PrismaClient) {} - async findMany({ where, select }: FindManyArgs) { - return await this.prismaClient.selectedSlots.findMany({ where, select }); - } - - async findFirst({ where }: { where: Prisma.SelectedSlotsWhereInput }) { + private async findFirst({ where }: { where: Prisma.SelectedSlotsWhereInput }) { return await this.prismaClient.selectedSlots.findFirst({ where, }); @@ -56,7 +34,7 @@ export class SelectedSlotsRepository { } async findManyReservedByOthers(slots: TimeSlot[], eventTypeId: number, uid: string) { - return await this.findMany({ + return await this.prismaClient.selectedSlots.findMany({ where: { OR: slots.map((slot) => ({ slotUtcStartDate: slot.utcStartIso, diff --git a/packages/lib/server/repository/dto/SelectedSlot.ts b/packages/lib/server/repository/dto/SelectedSlot.ts new file mode 100644 index 0000000000..68a37af249 --- /dev/null +++ b/packages/lib/server/repository/dto/SelectedSlot.ts @@ -0,0 +1,6 @@ +import type { SelectedSlots } from "@prisma/client"; + +export type SelectedSlot = Pick< + SelectedSlots, + "id" | "uid" | "eventTypeId" | "slotUtcStartDate" | "slotUtcEndDate" | "releaseAt" | "isSeat" +>; diff --git a/packages/lib/server/repository/dto/hashedLinkSelect.ts b/packages/lib/server/repository/dto/hashedLinkSelect.ts new file mode 100644 index 0000000000..04691c2ce2 --- /dev/null +++ b/packages/lib/server/repository/dto/hashedLinkSelect.ts @@ -0,0 +1,19 @@ +import type { Prisma } from "@prisma/client"; + +type EnforceTrueOrNested = { + [K in keyof T]: T[K] extends object ? EnforceTrueOrNested : true; // only allow `true` at leaves +}; + +export type HashedLinkSelect = EnforceTrueOrNested< + Pick & { + eventType?: Pick & { + hosts?: { + user?: Prisma.UserSelect; + }; + profile?: { + user?: Prisma.UserSelect; + }; + owner?: Prisma.UserSelect; + }; + } +>; diff --git a/packages/lib/server/repository/dto/selectedSlotDeleteManyExpiredSlotsInput.ts b/packages/lib/server/repository/dto/selectedSlotDeleteManyExpiredSlotsInput.ts new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/lib/server/repository/dto/selectedSlotFindManyReservedByOthersInput.ts b/packages/lib/server/repository/dto/selectedSlotFindManyReservedByOthersInput.ts new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/lib/server/repository/dto/selectedSlotFindManyUnexpiredSlotsInput.ts b/packages/lib/server/repository/dto/selectedSlotFindManyUnexpiredSlotsInput.ts new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/lib/server/repository/dto/selectedSlotFindReservedByOthersInput.ts b/packages/lib/server/repository/dto/selectedSlotFindReservedByOthersInput.ts new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/platform/libraries/repositories.ts b/packages/platform/libraries/repositories.ts index 6f0d11fe44..536345d29e 100644 --- a/packages/platform/libraries/repositories.ts +++ b/packages/platform/libraries/repositories.ts @@ -3,6 +3,6 @@ export { ScheduleRepository as PrismaScheduleRepository } from "@calcom/lib/serv export { BookingRepository as PrismaBookingRepository } from "@calcom/lib/server/repository/booking"; export { EventTypeRepository as PrismaEventTypeRepository } from "@calcom/lib/server/repository/eventTypeRepository"; export { RoutingFormResponseRepository as PrismaRoutingFormResponseRepository } from "@calcom/lib/server/repository/formResponse"; -export { SelectedSlotsRepository as PrismaSelectedSlotsRepository } from "@calcom/lib/server/repository/selectedSlots"; +export { PrismaSelectedSlotRepository } from "@calcom/lib/server/repository/PrismaSelectedSlotRepository"; export { TeamRepository as PrismaTeamRepository } from "@calcom/lib/server/repository/team"; export { UserRepository as PrismaUserRepository } from "@calcom/lib/server/repository/user"; diff --git a/packages/trpc/server/routers/viewer/slots/isAvailable.handler.ts b/packages/trpc/server/routers/viewer/slots/isAvailable.handler.ts index 0e5ddfb52a..d970dbc2eb 100644 --- a/packages/trpc/server/routers/viewer/slots/isAvailable.handler.ts +++ b/packages/trpc/server/routers/viewer/slots/isAvailable.handler.ts @@ -2,8 +2,8 @@ import type { NextApiRequest } from "next"; import { HttpError } from "@calcom/lib/http-error"; import { getPastTimeAndMinimumBookingNoticeBoundsStatus } from "@calcom/lib/isOutOfBounds"; +import { PrismaSelectedSlotRepository } from "@calcom/lib/server/repository/PrismaSelectedSlotRepository"; import { EventTypeRepository } from "@calcom/lib/server/repository/eventTypeRepository"; -import { SelectedSlotsRepository } from "@calcom/lib/server/repository/selectedSlots"; import type { PrismaClient } from "@calcom/prisma"; import type { TIsAvailableInputSchema, TIsAvailableOutputSchema } from "./isAvailable.schema"; @@ -41,7 +41,7 @@ export const isAvailableHandler = async ({ // Check each slot's availability // Without uid, we must not check for reserved slots because if uuid isn't set in cookie yet, but it is going to be through reserveSlot request soon, we could consider the slot as reserved accidentally. - const slotsRepo = new SelectedSlotsRepository(ctx.prisma); + const slotsRepo = new PrismaSelectedSlotRepository(ctx.prisma); const reservedSlots = uid ? await slotsRepo.findManyReservedByOthers(slots, eventTypeId, uid) : []; // Map all slots to their availability status diff --git a/packages/trpc/server/routers/viewer/slots/reserveSlot.handler.test.ts b/packages/trpc/server/routers/viewer/slots/reserveSlot.handler.test.ts index b2a006f97a..7972d90a2a 100644 --- a/packages/trpc/server/routers/viewer/slots/reserveSlot.handler.test.ts +++ b/packages/trpc/server/routers/viewer/slots/reserveSlot.handler.test.ts @@ -5,15 +5,15 @@ import { vi, describe, it, expect, afterEach } from "vitest"; // reserveSlotHandler relies on WEBAPP_URL being evaluated at import time, so we need to reset modules // between tests after tweaking process.env to simulate different deployment environments. // -// The handler also calls into Prisma and the SelectedSlotsRepository, so we stub those parts out. +// The handler also calls into Prisma and the SelectedSlotRepository, so we stub those parts out. // We alias the module path once we know WEBAPP_URL has been configured. const dynamicImportHandler = async () => await import("./reserveSlot.handler"); // The repository instance method is used to check for an existing reservation by someone else. // To keep this unit test isolated from the database layer, we stub this to always resolve falsey. -vi.mock("@calcom/lib/server/repository/selectedSlots", () => ({ - SelectedSlotsRepository: vi.fn().mockImplementation(() => ({ +vi.mock("@calcom/lib/server/repository/PrismaSelectedSlotRepository", () => ({ + PrismaSelectedSlotRepository: vi.fn().mockImplementation(() => ({ findReservedByOthers: vi.fn().mockResolvedValue(null), })), })); diff --git a/packages/trpc/server/routers/viewer/slots/reserveSlot.handler.ts b/packages/trpc/server/routers/viewer/slots/reserveSlot.handler.ts index 7b9c4d8d4e..ccd4f13539 100644 --- a/packages/trpc/server/routers/viewer/slots/reserveSlot.handler.ts +++ b/packages/trpc/server/routers/viewer/slots/reserveSlot.handler.ts @@ -5,7 +5,7 @@ import { v4 as uuid } from "uuid"; import dayjs from "@calcom/dayjs"; import { WEBAPP_URL } from "@calcom/lib/constants"; import { MINUTES_TO_BOOK } from "@calcom/lib/constants"; -import { SelectedSlotsRepository } from "@calcom/lib/server/repository/selectedSlots"; +import { PrismaSelectedSlotRepository } from "@calcom/lib/server/repository/PrismaSelectedSlotRepository"; import type { PrismaClient } from "@calcom/prisma"; import { BookingStatus } from "@calcom/prisma/enums"; @@ -64,7 +64,7 @@ export const reserveSlotHandler = async ({ ctx, input }: ReserveSlotOptions) => } // Check for existing reservations for the same slot - const slotsRepo = new SelectedSlotsRepository(prisma); + const slotsRepo = new PrismaSelectedSlotRepository(prisma); const reservedBySomeoneElse = await slotsRepo.findReservedByOthers({ slot: { utcStartIso: slotUtcStartDate, diff --git a/packages/trpc/server/routers/viewer/slots/util.ts b/packages/trpc/server/routers/viewer/slots/util.ts index a8d4b3d64b..ed5d4a2354 100644 --- a/packages/trpc/server/routers/viewer/slots/util.ts +++ b/packages/trpc/server/routers/viewer/slots/util.ts @@ -42,12 +42,12 @@ import { isRestrictionScheduleEnabled } from "@calcom/lib/restrictionSchedule"; import { safeStringify } from "@calcom/lib/safeStringify"; import { withReporting } from "@calcom/lib/sentryWrapper"; import { getTotalBookingDuration } from "@calcom/lib/server/queries/booking"; +import type { ISelectedSlotRepository } from "@calcom/lib/server/repository/ISelectedSlotRepository"; import type { BookingRepository } from "@calcom/lib/server/repository/booking"; import type { EventTypeRepository } from "@calcom/lib/server/repository/eventTypeRepository"; import type { RoutingFormResponseRepository } from "@calcom/lib/server/repository/formResponse"; import type { PrismaOOORepository } from "@calcom/lib/server/repository/ooo"; import type { ScheduleRepository } from "@calcom/lib/server/repository/schedule"; -import type { SelectedSlotsRepository } from "@calcom/lib/server/repository/selectedSlots"; import type { TeamRepository } from "@calcom/lib/server/repository/team"; import type { UserRepository } from "@calcom/lib/server/repository/user"; import { withSelectedCalendars } from "@calcom/lib/server/repository/user"; @@ -93,7 +93,7 @@ export type GetAvailableSlotsResponse = Awaited< export interface IAvailableSlotsService { oooRepo: PrismaOOORepository; scheduleRepo: ScheduleRepository; - selectedSlotsRepo: SelectedSlotsRepository; + selectedSlotRepo: ISelectedSlotRepository; teamRepo: TeamRepository; userRepo: UserRepository; bookingRepo: BookingRepository; @@ -114,7 +114,7 @@ export class AvailableSlotsService { eventTypeId: number; }) { const currentTimeInUtc = dayjs.utc().format(); - const slotsRepo = this.dependencies.selectedSlotsRepo; + const slotsRepo = this.dependencies.selectedSlotRepo; const unexpiredSelectedSlots = (await slotsRepo.findManyUnexpiredSlots({ diff --git a/yarn.lock b/yarn.lock index 3e39ce2323..a58fc44fa2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2722,7 +2722,7 @@ __metadata: "@axiomhq/winston": ^1.2.0 "@calcom/platform-constants": "*" "@calcom/platform-enums": "*" - "@calcom/platform-libraries": "npm:@calcom/platform-libraries@0.0.266" + "@calcom/platform-libraries": "npm:@calcom/platform-libraries@0.0.267" "@calcom/platform-types": "*" "@calcom/platform-utils": "*" "@calcom/prisma": "*" @@ -3794,13 +3794,13 @@ __metadata: languageName: unknown linkType: soft -"@calcom/platform-libraries@npm:@calcom/platform-libraries@0.0.266": - version: 0.0.266 - resolution: "@calcom/platform-libraries@npm:0.0.266" +"@calcom/platform-libraries@npm:@calcom/platform-libraries@0.0.267": + version: 0.0.267 + resolution: "@calcom/platform-libraries@npm:0.0.267" dependencies: "@calcom/features": "*" "@calcom/lib": "*" - checksum: 20f140bfb9fc1a08f4f302deb9cf247110ab385974dd0235a3c8ab73df4d2440d3e38cb494ca22d7f58445754e2bf6a6c981933a8e8106eaf55861a61945ce26 + checksum: 0c385514a04ddbd96ab3277774815233801a6eaf9a0d406b473eb7a446602f6f7d98de7f16747893b9fe1d50257db13b050a5726c8e7fe9500750d3ebf7be23a languageName: node linkType: hard