Skip to content

Commit 82063cc

Browse files
refactor: convert checkBookingLimits to class service with dependency injection (calcom#22768)
* refactor: convert checkBookingLimits to class service with dependency injection - Create CheckBookingLimitsService class following AvailableSlotsService pattern - Add countBookingsByEventTypeAndDateRange method to BookingRepository - Move direct prisma calls from service to repository layer - Implement dependency injection with proper DI tokens and modules - Update all usage points to use the new service through DI - Maintain backward compatibility with error-throwing wrapper functions - Update tests to use the new service pattern - Resolve TODO comment in AvailableSlotsService for DI integration Co-Authored-By: morgan@cal.com <morgan@cal.com> * chore: DI CheckBookingLimitsService in v2 slots service * chore: bump libraries * chore: create getCheckBookingLimitsService * refactor: convert checkBookingAndDurationLimits to service class with DI - Create CheckBookingAndDurationLimitsService class following DI pattern - Add DI tokens and module for the new service - Update booking-limits container to provide the new service - Refactor handleNewBooking.ts to use service through DI - Maintain backward compatibility with deprecated function export - Preserve all existing functionality while improving code organization Co-Authored-By: morgan@cal.com <morgan@cal.com> * chore: CheckBookingAndDurationLimitsService * chore: bump platform libs --------- Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-authored-by: morgan@cal.com <morgan@cal.com> Co-authored-by: Morgan <33722304+ThyMinimalDev@users.noreply.github.com>
1 parent 4b2f89d commit 82063cc

23 files changed

Lines changed: 261 additions & 132 deletions

apps/api/v2/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@
3838
"@axiomhq/winston": "^1.2.0",
3939
"@calcom/platform-constants": "*",
4040
"@calcom/platform-enums": "*",
41-
"@calcom/platform-libraries": "npm:@calcom/platform-libraries@0.0.268",
41+
"@calcom/platform-libraries": "npm:@calcom/platform-libraries@0.0.271",
4242
"@calcom/platform-types": "*",
4343
"@calcom/platform-utils": "*",
4444
"@calcom/prisma": "*",

apps/api/v2/src/lib/modules/available-slots.module.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { PrismaSelectedSlotRepository } from "@/lib/repositories/prisma-selected
77
import { PrismaTeamRepository } from "@/lib/repositories/prisma-team.repository";
88
import { PrismaUserRepository } from "@/lib/repositories/prisma-user.repository";
99
import { AvailableSlotsService } from "@/lib/services/available-slots.service";
10+
import { CheckBookingLimitsService } from "@/lib/services/check-booking-limits.service";
1011
import { PrismaModule } from "@/modules/prisma/prisma.module";
1112
import { Module } from "@nestjs/common";
1213

@@ -21,6 +22,7 @@ import { Module } from "@nestjs/common";
2122
PrismaEventTypeRepository,
2223
PrismaRoutingFormResponseRepository,
2324
PrismaTeamRepository,
25+
CheckBookingLimitsService,
2426
AvailableSlotsService,
2527
],
2628
exports: [AvailableSlotsService],

apps/api/v2/src/lib/services/available-slots.service.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { PrismaScheduleRepository } from "@/lib/repositories/prisma-schedule.rep
66
import { PrismaSelectedSlotRepository } from "@/lib/repositories/prisma-selected-slot.repository";
77
import { PrismaTeamRepository } from "@/lib/repositories/prisma-team.repository";
88
import { PrismaUserRepository } from "@/lib/repositories/prisma-user.repository";
9+
import { CheckBookingLimitsService } from "@/lib/services/check-booking-limits.service";
910
import { Injectable } from "@nestjs/common";
1011

1112
import { AvailableSlotsService as BaseAvailableSlotsService } from "@calcom/platform-libraries/slots";
@@ -31,6 +32,7 @@ export class AvailableSlotsService extends BaseAvailableSlotsService {
3132
selectedSlotRepo: selectedSlotRepository,
3233
eventTypeRepo: eventTypeRepository,
3334
userRepo: userRepository,
35+
checkBookingLimitsService: new CheckBookingLimitsService(bookingRepository),
3436
});
3537
}
3638
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { CheckBookingLimitsService } from "@/lib/services/check-booking-limits.service";
2+
import { Injectable } from "@nestjs/common";
3+
4+
import { CheckBookingAndDurationLimitsService as BaseCheckBookingAndDurationLimitsService } from "@calcom/platform-libraries/bookings";
5+
6+
@Injectable()
7+
export class CheckBookingAndDurationLimitsService extends BaseCheckBookingAndDurationLimitsService {
8+
constructor(checkBookingLimitsService: CheckBookingLimitsService) {
9+
super({
10+
checkBookingLimitsService: checkBookingLimitsService,
11+
});
12+
}
13+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { PrismaBookingRepository } from "@/lib/repositories/prisma-booking.repository";
2+
import { Injectable } from "@nestjs/common";
3+
4+
import { CheckBookingLimitsService as BaseCheckBookingLimitsService } from "@calcom/platform-libraries/bookings";
5+
6+
@Injectable()
7+
export class CheckBookingLimitsService extends BaseCheckBookingLimitsService {
8+
constructor(bookingRepository: PrismaBookingRepository) {
9+
super({
10+
bookingRepo: bookingRepository,
11+
});
12+
}
13+
}

apps/api/v2/tsconfig.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@
2626
"@calcom/platform-libraries/app-store": ["../../../packages/platform/libraries/app-store.ts"],
2727
"@calcom/platform-libraries/workflows": ["../../../packages/platform/libraries/workflows.ts"],
2828
"@calcom/platform-libraries/conferencing": ["../../../packages/platform/libraries/conferencing.ts"],
29-
"@calcom/platform-libraries/repositories": ["../../../packages/platform/libraries/repositories.ts"]
29+
"@calcom/platform-libraries/repositories": ["../../../packages/platform/libraries/repositories.ts"],
30+
"@calcom/platform-libraries/bookings": ["../../../packages/platform/libraries/bookings.ts"]
3031
},
3132
"incremental": true,
3233
"skipLibCheck": true,

apps/web/test/lib/checkBookingLimits.test.ts

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ import prismaMock from "../../../../tests/libs/__mocks__/prismaMock";
33
import { describe, expect, it } from "vitest";
44

55
import dayjs from "@calcom/dayjs";
6+
import { getCheckBookingLimitsService } from "@calcom/lib/di/containers/booking-limits";
67
import type { IntervalLimit } from "@calcom/lib/intervalLimits/intervalLimitSchema";
7-
import { checkBookingLimits, checkBookingLimit } from "@calcom/lib/intervalLimits/server/checkBookingLimits";
88
import { validateIntervalLimitOrder } from "@calcom/lib/intervalLimits/validateIntervalLimitOrder";
99

1010
type Mockdata = {
@@ -21,25 +21,27 @@ const MOCK_DATA: Mockdata = {
2121
},
2222
};
2323

24+
const checkBookingLimitsService = getCheckBookingLimitsService();
25+
2426
describe("Check Booking Limits Tests", () => {
2527
it("Should return no errors", async () => {
2628
prismaMock.booking.count.mockResolvedValue(0);
2729
expect(
28-
checkBookingLimits(MOCK_DATA.bookingLimits, MOCK_DATA.startDate, MOCK_DATA.id)
30+
checkBookingLimitsService.checkBookingLimits(MOCK_DATA.bookingLimits, MOCK_DATA.startDate, MOCK_DATA.id)
2931
).resolves.toBeTruthy();
3032
});
3133
it("Should throw an error", async () => {
3234
// Mock there being two a day
3335
prismaMock.booking.count.mockResolvedValue(2);
3436
expect(
35-
checkBookingLimits(MOCK_DATA.bookingLimits, MOCK_DATA.startDate, MOCK_DATA.id)
37+
checkBookingLimitsService.checkBookingLimits(MOCK_DATA.bookingLimits, MOCK_DATA.startDate, MOCK_DATA.id)
3638
).rejects.toThrowError();
3739
});
3840

3941
it("Should pass with multiple booking limits", async () => {
4042
prismaMock.booking.count.mockResolvedValue(0);
4143
expect(
42-
checkBookingLimits(
44+
checkBookingLimitsService.checkBookingLimits(
4345
{
4446
PER_DAY: 1,
4547
PER_WEEK: 2,
@@ -51,8 +53,9 @@ describe("Check Booking Limits Tests", () => {
5153
});
5254
it("Should pass with multiple booking limits with one undefined", async () => {
5355
prismaMock.booking.count.mockResolvedValue(0);
56+
5457
expect(
55-
checkBookingLimits(
58+
checkBookingLimitsService.checkBookingLimits(
5659
{
5760
PER_DAY: 1,
5861
PER_WEEK: undefined,
@@ -65,7 +68,7 @@ describe("Check Booking Limits Tests", () => {
6568
it("Should handle multiple limits correctly", async () => {
6669
prismaMock.booking.count.mockResolvedValue(1);
6770
expect(
68-
checkBookingLimit({
71+
checkBookingLimitsService.checkBookingLimit({
6972
key: "PER_DAY",
7073
limitingNumber: 2,
7174
eventStartDate: MOCK_DATA.startDate,
@@ -74,7 +77,7 @@ describe("Check Booking Limits Tests", () => {
7477
).resolves.not.toThrow();
7578
prismaMock.booking.count.mockResolvedValue(3);
7679
expect(
77-
checkBookingLimit({
80+
checkBookingLimitsService.checkBookingLimit({
7881
key: "PER_WEEK",
7982
limitingNumber: 2,
8083
eventStartDate: MOCK_DATA.startDate,

packages/features/bookings/lib/handleNewBooking.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ import {
5151
enrichHostsWithDelegationCredentials,
5252
getFirstDelegationConferencingCredentialAppLocation,
5353
} from "@calcom/lib/delegationCredential/server";
54+
import { getCheckBookingAndDurationLimitsService } from "@calcom/lib/di/containers/booking-limits";
5455
import { ErrorCode } from "@calcom/lib/errorCodes";
5556
import { getErrorFromUnknown } from "@calcom/lib/errors";
5657
import { getEventName, updateHostInEventName } from "@calcom/lib/event";
@@ -90,7 +91,6 @@ import { refreshCredentials } from "./getAllCredentialsForUsersOnEvent/refreshCr
9091
import getBookingDataSchema from "./getBookingDataSchema";
9192
import { addVideoCallDataToEvent } from "./handleNewBooking/addVideoCallDataToEvent";
9293
import { checkActiveBookingsLimitForBooker } from "./handleNewBooking/checkActiveBookingsLimitForBooker";
93-
import { checkBookingAndDurationLimits } from "./handleNewBooking/checkBookingAndDurationLimits";
9494
import { checkIfBookerEmailIsBlocked } from "./handleNewBooking/checkIfBookerEmailIsBlocked";
9595
import { createBooking } from "./handleNewBooking/createBooking";
9696
import type { Booking } from "./handleNewBooking/createBooking";
@@ -650,7 +650,8 @@ async function handler(
650650
location,
651651
});
652652

653-
await checkBookingAndDurationLimits({
653+
const checkBookingAndDurationLimitsService = getCheckBookingAndDurationLimitsService();
654+
await checkBookingAndDurationLimitsService.checkBookingAndDurationLimits({
654655
eventType,
655656
reqBodyStart: reqBody.start,
656657
reqBodyRescheduleUid: reqBody.rescheduleUid,
Lines changed: 37 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import dayjs from "@calcom/dayjs";
22
import type { IntervalLimit } from "@calcom/lib/intervalLimits/intervalLimitSchema";
3-
import { checkBookingLimits } from "@calcom/lib/intervalLimits/server/checkBookingLimits";
3+
import type { CheckBookingLimitsService } from "@calcom/lib/intervalLimits/server/checkBookingLimits";
44
import { checkDurationLimits } from "@calcom/lib/intervalLimits/server/checkDurationLimits";
55
import { withReporting } from "@calcom/lib/sentryWrapper";
66

@@ -14,37 +14,41 @@ type InputProps = {
1414
reqBodyRescheduleUid?: string;
1515
};
1616

17-
const _checkBookingAndDurationLimits = async ({
18-
eventType,
19-
reqBodyStart,
20-
reqBodyRescheduleUid,
21-
}: InputProps) => {
22-
if (
23-
Object.prototype.hasOwnProperty.call(eventType, "bookingLimits") ||
24-
Object.prototype.hasOwnProperty.call(eventType, "durationLimits")
25-
) {
26-
const startAsDate = dayjs(reqBodyStart).toDate();
27-
if (eventType.bookingLimits && Object.keys(eventType.bookingLimits).length > 0) {
28-
await checkBookingLimits(
29-
eventType.bookingLimits as IntervalLimit,
30-
startAsDate,
31-
eventType.id,
32-
reqBodyRescheduleUid,
33-
eventType.schedule?.timeZone
34-
);
35-
}
36-
if (eventType.durationLimits) {
37-
await checkDurationLimits(
38-
eventType.durationLimits as IntervalLimit,
39-
startAsDate,
40-
eventType.id,
41-
reqBodyRescheduleUid
42-
);
17+
export interface ICheckBookingAndDurationLimitsService {
18+
checkBookingLimitsService: CheckBookingLimitsService;
19+
}
20+
21+
export class CheckBookingAndDurationLimitsService {
22+
constructor(private readonly dependencies: ICheckBookingAndDurationLimitsService) {}
23+
24+
checkBookingAndDurationLimits = withReporting(
25+
this._checkBookingAndDurationLimits.bind(this),
26+
"checkBookingAndDurationLimits"
27+
);
28+
29+
async _checkBookingAndDurationLimits({ eventType, reqBodyStart, reqBodyRescheduleUid }: InputProps) {
30+
if (
31+
Object.prototype.hasOwnProperty.call(eventType, "bookingLimits") ||
32+
Object.prototype.hasOwnProperty.call(eventType, "durationLimits")
33+
) {
34+
const startAsDate = dayjs(reqBodyStart).toDate();
35+
if (eventType.bookingLimits && Object.keys(eventType.bookingLimits).length > 0) {
36+
await this.dependencies.checkBookingLimitsService.checkBookingLimits(
37+
eventType.bookingLimits as IntervalLimit,
38+
startAsDate,
39+
eventType.id,
40+
reqBodyRescheduleUid,
41+
eventType.schedule?.timeZone
42+
);
43+
}
44+
if (eventType.durationLimits) {
45+
await checkDurationLimits(
46+
eventType.durationLimits as IntervalLimit,
47+
startAsDate,
48+
eventType.id,
49+
reqBodyRescheduleUid
50+
);
51+
}
4352
}
4453
}
45-
};
46-
47-
export const checkBookingAndDurationLimits = withReporting(
48-
_checkBookingAndDurationLimits,
49-
"checkBookingAndDurationLimits"
50-
);
54+
}

packages/lib/di/containers/available-slots.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import type { AvailableSlotsService } from "@calcom/trpc/server/routers/viewer/s
66

77
import { availableSlotsModule } from "../modules/available-slots";
88
import { bookingRepositoryModule } from "../modules/booking";
9+
import { checkBookingLimitsModule } from "../modules/check-booking-limits";
910
import { eventTypeRepositoryModule } from "../modules/eventType";
1011
import { oooRepositoryModule } from "../modules/ooo";
1112
import { routingFormResponseRepositoryModule } from "../modules/routingFormResponse";
@@ -24,6 +25,7 @@ container.load(DI_TOKENS.USER_REPOSITORY_MODULE, userRepositoryModule);
2425
container.load(DI_TOKENS.BOOKING_REPOSITORY_MODULE, bookingRepositoryModule);
2526
container.load(DI_TOKENS.EVENT_TYPE_REPOSITORY_MODULE, eventTypeRepositoryModule);
2627
container.load(DI_TOKENS.ROUTING_FORM_RESPONSE_REPOSITORY_MODULE, routingFormResponseRepositoryModule);
28+
container.load(DI_TOKENS.CHECK_BOOKING_LIMITS_SERVICE_MODULE, checkBookingLimitsModule);
2729
container.load(DI_TOKENS.AVAILABLE_SLOTS_SERVICE_MODULE, availableSlotsModule);
2830

2931
export function getAvailableSlotsService() {

0 commit comments

Comments
 (0)