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 <me@alexvanandel.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 <me@alexvanandel.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 <me@alexvanandel.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 <me@alexvanandel.com>

* feat: add SelectedSlotRepositoryInterface and update DI to use interface

Co-Authored-By: alex@cal.com <me@alexvanandel.com>

* refactor: rename selectedSlots.ts to selectedSlot.ts and update all imports

Co-Authored-By: alex@cal.com <me@alexvanandel.com>

* fix: update test mock import path after file rename

Co-Authored-By: alex@cal.com <me@alexvanandel.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 <somaychauhan98@gmail.com>
This commit is contained in:
Alex van Andel
2025-07-25 16:59:32 -07:00
committed by GitHub
co-authored by Devin AI Somay Chauhan
parent 9ccf5f3723
commit fde1d671fb
26 changed files with 120 additions and 93 deletions
+1 -1
View File
@@ -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": "*",
@@ -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,
@@ -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);
}
@@ -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,
});
@@ -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>(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();
@@ -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();
});
@@ -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>(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();
@@ -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;
}
@@ -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);
+1 -1
View File
@@ -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,
+3 -3
View File
@@ -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]);
+2 -2
View File
@@ -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"),
@@ -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<SelectedSlot | null>;
findManyReservedByOthers(
slots: TimeSlot[],
eventTypeId: number,
uid: string
): Promise<Array<Pick<SelectedSlot, "slotUtcEndDate" | "slotUtcStartDate">>>;
findManyUnexpiredSlots(args: {
userIds: number[];
currentTimeInUtc: string;
}): Promise<Array<Omit<SelectedSlot, "releaseAt">>>;
deleteManyExpiredSlots(args: { eventTypeId: number; currentTimeInUtc: string }): Promise<{ count: number }>;
}
@@ -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,
@@ -0,0 +1,6 @@
import type { SelectedSlots } from "@prisma/client";
export type SelectedSlot = Pick<
SelectedSlots,
"id" | "uid" | "eventTypeId" | "slotUtcStartDate" | "slotUtcEndDate" | "releaseAt" | "isSeat"
>;
@@ -0,0 +1,19 @@
import type { Prisma } from "@prisma/client";
type EnforceTrueOrNested<T> = {
[K in keyof T]: T[K] extends object ? EnforceTrueOrNested<T[K]> : true; // only allow `true` at leaves
};
export type HashedLinkSelect = EnforceTrueOrNested<
Pick<Prisma.HashedLinkSelect, "id"> & {
eventType?: Pick<Prisma.EventTypeSelect, "userId" | "teamId"> & {
hosts?: {
user?: Prisma.UserSelect;
};
profile?: {
user?: Prisma.UserSelect;
};
owner?: Prisma.UserSelect;
};
}
>;
+1 -1
View File
@@ -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";
@@ -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
@@ -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),
})),
}));
@@ -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,
@@ -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({
+5 -5
View File
@@ -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