refactor: convert checkBookingLimits to class service with dependency injection (#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>
This commit is contained in:
authored and GitHub committed 2025-07-31 02:03:18 +01:00
1 parent 4b2f89d6e8
commit 82063cc9a1
23 files changed
+260 -131

No files matched your search

+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.268",
"@calcom/platform-libraries": "npm:@calcom/platform-libraries@0.0.271",
"@calcom/platform-types": "*",
"@calcom/platform-utils": "*",
"@calcom/prisma": "*",
@@ -7,6 +7,7 @@ import { PrismaSelectedSlotRepository } from "@/lib/repositories/prisma-selected
import { PrismaTeamRepository } from "@/lib/repositories/prisma-team.repository";
import { PrismaUserRepository } from "@/lib/repositories/prisma-user.repository";
import { AvailableSlotsService } from "@/lib/services/available-slots.service";
import { CheckBookingLimitsService } from "@/lib/services/check-booking-limits.service";
import { PrismaModule } from "@/modules/prisma/prisma.module";
import { Module } from "@nestjs/common";
@@ -21,6 +22,7 @@ import { Module } from "@nestjs/common";
PrismaEventTypeRepository,
PrismaRoutingFormResponseRepository,
PrismaTeamRepository,
CheckBookingLimitsService,
AvailableSlotsService,
],
exports: [AvailableSlotsService],
@@ -6,6 +6,7 @@ import { PrismaScheduleRepository } from "@/lib/repositories/prisma-schedule.rep
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 { CheckBookingLimitsService } from "@/lib/services/check-booking-limits.service";
import { Injectable } from "@nestjs/common";
import { AvailableSlotsService as BaseAvailableSlotsService } from "@calcom/platform-libraries/slots";
@@ -31,6 +32,7 @@ export class AvailableSlotsService extends BaseAvailableSlotsService {
selectedSlotRepo: selectedSlotRepository,
eventTypeRepo: eventTypeRepository,
userRepo: userRepository,
checkBookingLimitsService: new CheckBookingLimitsService(bookingRepository),
});
}
}
@@ -0,0 +1,13 @@
import { CheckBookingLimitsService } from "@/lib/services/check-booking-limits.service";
import { Injectable } from "@nestjs/common";
import { CheckBookingAndDurationLimitsService as BaseCheckBookingAndDurationLimitsService } from "@calcom/platform-libraries/bookings";
@Injectable()
export class CheckBookingAndDurationLimitsService extends BaseCheckBookingAndDurationLimitsService {
constructor(checkBookingLimitsService: CheckBookingLimitsService) {
super({
checkBookingLimitsService: checkBookingLimitsService,
});
}
}
@@ -0,0 +1,13 @@
import { PrismaBookingRepository } from "@/lib/repositories/prisma-booking.repository";
import { Injectable } from "@nestjs/common";
import { CheckBookingLimitsService as BaseCheckBookingLimitsService } from "@calcom/platform-libraries/bookings";
@Injectable()
export class CheckBookingLimitsService extends BaseCheckBookingLimitsService {
constructor(bookingRepository: PrismaBookingRepository) {
super({
bookingRepo: bookingRepository,
});
}
}
+2 -1
View File
@@ -26,7 +26,8 @@
"@calcom/platform-libraries/app-store": ["../../../packages/platform/libraries/app-store.ts"],
"@calcom/platform-libraries/workflows": ["../../../packages/platform/libraries/workflows.ts"],
"@calcom/platform-libraries/conferencing": ["../../../packages/platform/libraries/conferencing.ts"],
"@calcom/platform-libraries/repositories": ["../../../packages/platform/libraries/repositories.ts"]
"@calcom/platform-libraries/repositories": ["../../../packages/platform/libraries/repositories.ts"],
"@calcom/platform-libraries/bookings": ["../../../packages/platform/libraries/bookings.ts"]
},
"incremental": true,
"skipLibCheck": true,
+10 -7
View File
@@ -3,8 +3,8 @@ import prismaMock from "../../../../tests/libs/__mocks__/prismaMock";
import { describe, expect, it } from "vitest";
import dayjs from "@calcom/dayjs";
import { getCheckBookingLimitsService } from "@calcom/lib/di/containers/booking-limits";
import type { IntervalLimit } from "@calcom/lib/intervalLimits/intervalLimitSchema";
import { checkBookingLimits, checkBookingLimit } from "@calcom/lib/intervalLimits/server/checkBookingLimits";
import { validateIntervalLimitOrder } from "@calcom/lib/intervalLimits/validateIntervalLimitOrder";
type Mockdata = {
@@ -21,25 +21,27 @@ const MOCK_DATA: Mockdata = {
},
};
const checkBookingLimitsService = getCheckBookingLimitsService();
describe("Check Booking Limits Tests", () => {
it("Should return no errors", async () => {
prismaMock.booking.count.mockResolvedValue(0);
expect(
checkBookingLimits(MOCK_DATA.bookingLimits, MOCK_DATA.startDate, MOCK_DATA.id)
checkBookingLimitsService.checkBookingLimits(MOCK_DATA.bookingLimits, MOCK_DATA.startDate, MOCK_DATA.id)
).resolves.toBeTruthy();
});
it("Should throw an error", async () => {
// Mock there being two a day
prismaMock.booking.count.mockResolvedValue(2);
expect(
checkBookingLimits(MOCK_DATA.bookingLimits, MOCK_DATA.startDate, MOCK_DATA.id)
checkBookingLimitsService.checkBookingLimits(MOCK_DATA.bookingLimits, MOCK_DATA.startDate, MOCK_DATA.id)
).rejects.toThrowError();
});
it("Should pass with multiple booking limits", async () => {
prismaMock.booking.count.mockResolvedValue(0);
expect(
checkBookingLimits(
checkBookingLimitsService.checkBookingLimits(
{
PER_DAY: 1,
PER_WEEK: 2,
@@ -51,8 +53,9 @@ describe("Check Booking Limits Tests", () => {
});
it("Should pass with multiple booking limits with one undefined", async () => {
prismaMock.booking.count.mockResolvedValue(0);
expect(
checkBookingLimits(
checkBookingLimitsService.checkBookingLimits(
{
PER_DAY: 1,
PER_WEEK: undefined,
@@ -65,7 +68,7 @@ describe("Check Booking Limits Tests", () => {
it("Should handle multiple limits correctly", async () => {
prismaMock.booking.count.mockResolvedValue(1);
expect(
checkBookingLimit({
checkBookingLimitsService.checkBookingLimit({
key: "PER_DAY",
limitingNumber: 2,
eventStartDate: MOCK_DATA.startDate,
@@ -74,7 +77,7 @@ describe("Check Booking Limits Tests", () => {
).resolves.not.toThrow();
prismaMock.booking.count.mockResolvedValue(3);
expect(
checkBookingLimit({
checkBookingLimitsService.checkBookingLimit({
key: "PER_WEEK",
limitingNumber: 2,
eventStartDate: MOCK_DATA.startDate,
@@ -51,6 +51,7 @@ import {
enrichHostsWithDelegationCredentials,
getFirstDelegationConferencingCredentialAppLocation,
} from "@calcom/lib/delegationCredential/server";
import { getCheckBookingAndDurationLimitsService } from "@calcom/lib/di/containers/booking-limits";
import { ErrorCode } from "@calcom/lib/errorCodes";
import { getErrorFromUnknown } from "@calcom/lib/errors";
import { getEventName, updateHostInEventName } from "@calcom/lib/event";
@@ -90,7 +91,6 @@ import { refreshCredentials } from "./getAllCredentialsForUsersOnEvent/refreshCr
import getBookingDataSchema from "./getBookingDataSchema";
import { addVideoCallDataToEvent } from "./handleNewBooking/addVideoCallDataToEvent";
import { checkActiveBookingsLimitForBooker } from "./handleNewBooking/checkActiveBookingsLimitForBooker";
import { checkBookingAndDurationLimits } from "./handleNewBooking/checkBookingAndDurationLimits";
import { checkIfBookerEmailIsBlocked } from "./handleNewBooking/checkIfBookerEmailIsBlocked";
import { createBooking } from "./handleNewBooking/createBooking";
import type { Booking } from "./handleNewBooking/createBooking";
@@ -650,7 +650,8 @@ async function handler(
location,
});
await checkBookingAndDurationLimits({
const checkBookingAndDurationLimitsService = getCheckBookingAndDurationLimitsService();
await checkBookingAndDurationLimitsService.checkBookingAndDurationLimits({
eventType,
reqBodyStart: reqBody.start,
reqBodyRescheduleUid: reqBody.rescheduleUid,
@@ -1,6 +1,6 @@
import dayjs from "@calcom/dayjs";
import type { IntervalLimit } from "@calcom/lib/intervalLimits/intervalLimitSchema";
import { checkBookingLimits } from "@calcom/lib/intervalLimits/server/checkBookingLimits";
import type { CheckBookingLimitsService } from "@calcom/lib/intervalLimits/server/checkBookingLimits";
import { checkDurationLimits } from "@calcom/lib/intervalLimits/server/checkDurationLimits";
import { withReporting } from "@calcom/lib/sentryWrapper";
@@ -14,37 +14,41 @@ type InputProps = {
reqBodyRescheduleUid?: string;
};
const _checkBookingAndDurationLimits = async ({
eventType,
reqBodyStart,
reqBodyRescheduleUid,
}: InputProps) => {
if (
Object.prototype.hasOwnProperty.call(eventType, "bookingLimits") ||
Object.prototype.hasOwnProperty.call(eventType, "durationLimits")
) {
const startAsDate = dayjs(reqBodyStart).toDate();
if (eventType.bookingLimits && Object.keys(eventType.bookingLimits).length > 0) {
await checkBookingLimits(
eventType.bookingLimits as IntervalLimit,
startAsDate,
eventType.id,
reqBodyRescheduleUid,
eventType.schedule?.timeZone
);
}
if (eventType.durationLimits) {
await checkDurationLimits(
eventType.durationLimits as IntervalLimit,
startAsDate,
eventType.id,
reqBodyRescheduleUid
);
export interface ICheckBookingAndDurationLimitsService {
checkBookingLimitsService: CheckBookingLimitsService;
}
export class CheckBookingAndDurationLimitsService {
constructor(private readonly dependencies: ICheckBookingAndDurationLimitsService) {}
checkBookingAndDurationLimits = withReporting(
this._checkBookingAndDurationLimits.bind(this),
"checkBookingAndDurationLimits"
);
async _checkBookingAndDurationLimits({ eventType, reqBodyStart, reqBodyRescheduleUid }: InputProps) {
if (
Object.prototype.hasOwnProperty.call(eventType, "bookingLimits") ||
Object.prototype.hasOwnProperty.call(eventType, "durationLimits")
) {
const startAsDate = dayjs(reqBodyStart).toDate();
if (eventType.bookingLimits && Object.keys(eventType.bookingLimits).length > 0) {
await this.dependencies.checkBookingLimitsService.checkBookingLimits(
eventType.bookingLimits as IntervalLimit,
startAsDate,
eventType.id,
reqBodyRescheduleUid,
eventType.schedule?.timeZone
);
}
if (eventType.durationLimits) {
await checkDurationLimits(
eventType.durationLimits as IntervalLimit,
startAsDate,
eventType.id,
reqBodyRescheduleUid
);
}
}
}
};
export const checkBookingAndDurationLimits = withReporting(
_checkBookingAndDurationLimits,
"checkBookingAndDurationLimits"
);
}
@@ -6,6 +6,7 @@ import type { AvailableSlotsService } from "@calcom/trpc/server/routers/viewer/s
import { availableSlotsModule } from "../modules/available-slots";
import { bookingRepositoryModule } from "../modules/booking";
import { checkBookingLimitsModule } from "../modules/check-booking-limits";
import { eventTypeRepositoryModule } from "../modules/eventType";
import { oooRepositoryModule } from "../modules/ooo";
import { routingFormResponseRepositoryModule } from "../modules/routingFormResponse";
@@ -24,6 +25,7 @@ container.load(DI_TOKENS.USER_REPOSITORY_MODULE, userRepositoryModule);
container.load(DI_TOKENS.BOOKING_REPOSITORY_MODULE, bookingRepositoryModule);
container.load(DI_TOKENS.EVENT_TYPE_REPOSITORY_MODULE, eventTypeRepositoryModule);
container.load(DI_TOKENS.ROUTING_FORM_RESPONSE_REPOSITORY_MODULE, routingFormResponseRepositoryModule);
container.load(DI_TOKENS.CHECK_BOOKING_LIMITS_SERVICE_MODULE, checkBookingLimitsModule);
container.load(DI_TOKENS.AVAILABLE_SLOTS_SERVICE_MODULE, availableSlotsModule);
export function getAvailableSlotsService() {
@@ -0,0 +1,29 @@
import { createContainer } from "@evyweb/ioctopus";
import { DI_TOKENS } from "@calcom/lib/di/tokens";
import { prismaModule } from "@calcom/prisma/prisma.module";
import type { CheckBookingAndDurationLimitsService } from "../../../features/bookings/lib/handleNewBooking/checkBookingAndDurationLimits";
import type { CheckBookingLimitsService } from "../../intervalLimits/server/checkBookingLimits";
import { bookingRepositoryModule } from "../modules/booking";
import { checkBookingAndDurationLimitsModule } from "../modules/check-booking-and-duration-limits";
import { checkBookingLimitsModule } from "../modules/check-booking-limits";
const container = createContainer();
container.load(DI_TOKENS.PRISMA_MODULE, prismaModule);
container.load(DI_TOKENS.BOOKING_REPOSITORY_MODULE, bookingRepositoryModule);
container.load(DI_TOKENS.CHECK_BOOKING_LIMITS_SERVICE_MODULE, checkBookingLimitsModule);
container.load(
DI_TOKENS.CHECK_BOOKING_AND_DURATION_LIMITS_SERVICE_MODULE,
checkBookingAndDurationLimitsModule
);
export function getCheckBookingLimitsService() {
return container.get<CheckBookingLimitsService>(DI_TOKENS.CHECK_BOOKING_LIMITS_SERVICE);
}
export function getCheckBookingAndDurationLimitsService() {
return container.get<CheckBookingAndDurationLimitsService>(
DI_TOKENS.CHECK_BOOKING_AND_DURATION_LIMITS_SERVICE
);
}
@@ -15,4 +15,5 @@ availableSlotsModule.bind(DI_TOKENS.AVAILABLE_SLOTS_SERVICE).toClass(AvailableSl
bookingRepo: DI_TOKENS.BOOKING_REPOSITORY,
eventTypeRepo: DI_TOKENS.EVENT_TYPE_REPOSITORY,
routingFormResponseRepo: DI_TOKENS.ROUTING_FORM_RESPONSE_REPOSITORY,
checkBookingLimitsService: DI_TOKENS.CHECK_BOOKING_LIMITS_SERVICE,
} satisfies Record<keyof IAvailableSlotsService, symbol>);
@@ -0,0 +1,13 @@
import { createModule } from "@evyweb/ioctopus";
import type { ICheckBookingAndDurationLimitsService } from "@calcom/features/bookings/lib/handleNewBooking/checkBookingAndDurationLimits";
import { CheckBookingAndDurationLimitsService } from "@calcom/features/bookings/lib/handleNewBooking/checkBookingAndDurationLimits";
import { DI_TOKENS } from "../tokens";
export const checkBookingAndDurationLimitsModule = createModule();
checkBookingAndDurationLimitsModule
.bind(DI_TOKENS.CHECK_BOOKING_AND_DURATION_LIMITS_SERVICE)
.toClass(CheckBookingAndDurationLimitsService, {
checkBookingLimitsService: DI_TOKENS.CHECK_BOOKING_LIMITS_SERVICE,
} satisfies Record<keyof ICheckBookingAndDurationLimitsService, symbol>);
@@ -0,0 +1,9 @@
import { createModule } from "@evyweb/ioctopus";
import { DI_TOKENS } from "@calcom/lib/di/tokens";
import { CheckBookingLimitsService } from "@calcom/lib/intervalLimits/server/checkBookingLimits";
export const checkBookingLimitsModule = createModule();
checkBookingLimitsModule
.bind(DI_TOKENS.CHECK_BOOKING_LIMITS_SERVICE)
.toClass(CheckBookingLimitsService, { bookingRepo: DI_TOKENS.BOOKING_REPOSITORY });
+4
View File
@@ -22,4 +22,8 @@ export const DI_TOKENS = {
AVAILABLE_SLOTS_SERVICE_MODULE: Symbol("AvailableSlotsModule"),
INSIGHTS_ROUTING_SERVICE: Symbol("InsightsRoutingService"),
INSIGHTS_ROUTING_SERVICE_MODULE: Symbol("InsightsRoutingServiceModule"),
CHECK_BOOKING_LIMITS_SERVICE: Symbol("CheckBookingLimitsService"),
CHECK_BOOKING_LIMITS_SERVICE_MODULE: Symbol("CheckBookingLimitsServiceModule"),
CHECK_BOOKING_AND_DURATION_LIMITS_SERVICE: Symbol("CheckBookingAndDurationLimitsService"),
CHECK_BOOKING_AND_DURATION_LIMITS_SERVICE_MODULE: Symbol("CheckBookingAndDurationLimitsServiceModule"),
};
@@ -2,69 +2,73 @@ import dayjs from "@calcom/dayjs";
import { getErrorFromUnknown } from "@calcom/lib/errors";
import { HttpError } from "@calcom/lib/http-error";
import { withReporting } from "@calcom/lib/sentryWrapper";
import { BookingRepository } from "@calcom/lib/server/repository/booking";
import prisma from "@calcom/prisma";
import { BookingStatus } from "@calcom/prisma/enums";
import type { BookingRepository } from "@calcom/lib/server/repository/booking";
import { ascendingLimitKeys, intervalLimitKeyToUnit } from "../intervalLimit";
import type { IntervalLimit, IntervalLimitKey } from "../intervalLimitSchema";
import { parseBookingLimit } from "../isBookingLimits";
async function _checkBookingLimits(
bookingLimits: IntervalLimit,
eventStartDate: Date,
eventId: number,
rescheduleUid?: string | undefined,
timeZone?: string | null,
includeManagedEvents?: boolean
) {
const parsedBookingLimits = parseBookingLimit(bookingLimits);
if (!parsedBookingLimits) return false;
// not iterating entries to preserve types
const limitCalculations = ascendingLimitKeys.map((key) =>
checkBookingLimit({
key,
limitingNumber: parsedBookingLimits[key],
eventStartDate,
eventId,
timeZone,
rescheduleUid,
includeManagedEvents,
})
);
try {
return !!(await Promise.all(limitCalculations));
} catch (error) {
throw new HttpError({ message: getErrorFromUnknown(error).message, statusCode: 401 });
}
export interface ICheckBookingLimitsService {
bookingRepo: BookingRepository;
}
export const checkBookingLimits = withReporting(_checkBookingLimits, "checkBookingLimits");
export class CheckBookingLimitsService {
constructor(private readonly dependencies: ICheckBookingLimitsService) {}
async function _checkBookingLimit({
eventStartDate,
eventId,
key,
limitingNumber,
rescheduleUid,
timeZone,
teamId,
user,
includeManagedEvents = false,
}: {
eventStartDate: Date;
eventId?: number;
key: IntervalLimitKey;
limitingNumber: number | undefined;
rescheduleUid?: string | undefined;
timeZone?: string | null;
teamId?: number;
user?: { id: number; email: string };
includeManagedEvents?: boolean;
}) {
{
private async _checkBookingLimits(
bookingLimits: IntervalLimit,
eventStartDate: Date,
eventId: number,
rescheduleUid?: string | undefined,
timeZone?: string | null,
includeManagedEvents?: boolean
) {
const parsedBookingLimits = parseBookingLimit(bookingLimits);
if (!parsedBookingLimits) return false;
// not iterating entries to preserve types
const limitCalculations = ascendingLimitKeys.map((key) =>
this.checkBookingLimit({
key,
limitingNumber: parsedBookingLimits[key],
eventStartDate,
eventId,
timeZone,
rescheduleUid,
includeManagedEvents,
})
);
try {
return !!(await Promise.all(limitCalculations));
} catch (error) {
throw new HttpError({ message: getErrorFromUnknown(error).message, statusCode: 401 });
}
}
checkBookingLimits = withReporting(this._checkBookingLimits.bind(this), "checkBookingLimits");
private async _checkBookingLimit({
eventStartDate,
eventId,
key,
limitingNumber,
rescheduleUid,
timeZone,
teamId,
user,
includeManagedEvents = false,
}: {
eventStartDate: Date;
eventId?: number;
key: IntervalLimitKey;
limitingNumber: number | undefined;
rescheduleUid?: string | undefined;
timeZone?: string | null;
teamId?: number;
user?: { id: number; email: string };
includeManagedEvents?: boolean;
}) {
const eventDateInOrganizerTz = timeZone ? dayjs(eventStartDate).tz(timeZone) : dayjs(eventStartDate);
if (!limitingNumber) return;
@@ -77,8 +81,7 @@ async function _checkBookingLimit({
let bookingsInPeriod;
if (teamId && user) {
const bookingRepo = new BookingRepository(prisma);
bookingsInPeriod = await bookingRepo.getAllAcceptedTeamBookingsOfUser({
bookingsInPeriod = await this.dependencies.bookingRepo.getAllAcceptedTeamBookingsOfUser({
user: { id: user.id, email: user.email },
teamId,
startDate: startDate,
@@ -88,21 +91,11 @@ async function _checkBookingLimit({
includeManagedEvents,
});
} else {
bookingsInPeriod = await prisma.booking.count({
where: {
status: BookingStatus.ACCEPTED,
eventTypeId: eventId,
// FIXME: bookings that overlap on one side will never be counted
startTime: {
gte: startDate,
},
endTime: {
lte: endDate,
},
uid: {
not: rescheduleUid,
},
},
bookingsInPeriod = await this.dependencies.bookingRepo.countBookingsByEventTypeAndDateRange({
eventTypeId: eventId!,
startDate,
endDate,
excludedUid: rescheduleUid,
});
}
@@ -113,6 +106,6 @@ async function _checkBookingLimit({
statusCode: 403,
});
}
}
export const checkBookingLimit = withReporting(_checkBookingLimit, "checkBookingLimit");
checkBookingLimit = withReporting(this._checkBookingLimit.bind(this), "checkBookingLimit");
}
@@ -1,5 +1,6 @@
import type { Dayjs } from "@calcom/dayjs";
import dayjs from "@calcom/dayjs";
import { getCheckBookingLimitsService } from "@calcom/lib/di/containers/booking-limits";
import { getStartEndDateforLimitCheck } from "@calcom/lib/getBusyTimes";
import type { EventType } from "@calcom/lib/getUserAvailability";
import { getPeriodStartDatesBetween } from "@calcom/lib/getUserAvailability";
@@ -14,7 +15,6 @@ import { descendingLimitKeys, intervalLimitKeyToUnit } from "../intervalLimit";
import type { IntervalLimit } from "../intervalLimitSchema";
import LimitManager from "../limitManager";
import { isBookingWithinPeriod } from "../utils";
import { checkBookingLimit } from "./checkBookingLimits";
const _getBusyTimesFromLimits = async (
bookingLimits: IntervalLimit | null,
@@ -113,7 +113,8 @@ const _getBusyTimesFromBookingLimits = async (params: {
// special handling of yearly limits to improve performance
if (unit === "year") {
try {
await checkBookingLimit({
const checkBookingLimitsService = getCheckBookingLimitsService();
await checkBookingLimitsService.checkBookingLimit({
eventStartDate: periodStart.toDate(),
limitingNumber: limit,
eventId: eventTypeId,
+28
View File
@@ -837,4 +837,32 @@ export class BookingRepository {
},
});
}
async countBookingsByEventTypeAndDateRange({
eventTypeId,
startDate,
endDate,
excludedUid,
}: {
eventTypeId: number;
startDate: Date;
endDate: Date;
excludedUid?: string;
}) {
return await this.prismaClient.booking.count({
where: {
status: BookingStatus.ACCEPTED,
eventTypeId,
startTime: {
gte: startDate,
},
endTime: {
lte: endDate,
},
uid: {
not: excludedUid,
},
},
});
}
}
+2
View File
@@ -0,0 +1,2 @@
export { CheckBookingLimitsService } from "@calcom/lib/intervalLimits/server/checkBookingLimits";
export { CheckBookingAndDurationLimitsService } from "@calcom/features/bookings/lib/handleNewBooking/checkBookingAndDurationLimits";
+8
View File
@@ -73,6 +73,11 @@
"import": "./dist/repositories.js",
"require": "./dist/repositories.cjs",
"types": "./dist/repositories.d.ts"
},
"./bookings": {
"import": "./dist/bookings.js",
"require": "./dist/bookings.cjs",
"types": "./dist/bookings.d.ts"
}
},
"typesVersions": {
@@ -100,6 +105,9 @@
],
"repositories": [
"dist/repositories.d.ts"
],
"bookings": [
"dist/bookings.d.ts"
]
}
}
@@ -26,6 +26,7 @@ export default defineConfig({
slots: resolve(__dirname, "./slots.ts"),
conferencing: resolve(__dirname, "./conferencing.ts"),
repositories: resolve(__dirname, "./repositories.ts"),
bookings: resolve(__dirname, "./bookings.ts"),
},
name: "calcom-lib",
fileName: "calcom-lib",
@@ -29,7 +29,7 @@ import type { IntervalLimit } from "@calcom/lib/intervalLimits/intervalLimitSche
import { parseBookingLimit } from "@calcom/lib/intervalLimits/isBookingLimits";
import { parseDurationLimit } from "@calcom/lib/intervalLimits/isDurationLimits";
import LimitManager from "@calcom/lib/intervalLimits/limitManager";
import { checkBookingLimit } from "@calcom/lib/intervalLimits/server/checkBookingLimits";
import type { CheckBookingLimitsService } from "@calcom/lib/intervalLimits/server/checkBookingLimits";
import { isBookingWithinPeriod } from "@calcom/lib/intervalLimits/utils";
import {
calculatePeriodLimits,
@@ -99,10 +99,11 @@ export interface IAvailableSlotsService {
bookingRepo: BookingRepository;
eventTypeRepo: EventTypeRepository;
routingFormResponseRepo: RoutingFormResponseRepository;
checkBookingLimitsService: CheckBookingLimitsService;
}
export class AvailableSlotsService {
constructor(private readonly dependencies: IAvailableSlotsService) {}
constructor(public readonly dependencies: IAvailableSlotsService) {}
private async _getReservedSlotsAndCleanupExpired({
bookerClientUid,
@@ -397,8 +398,7 @@ export class AvailableSlotsService {
if (unit === "year") {
try {
// TODO: DI checkBookingLimit
await checkBookingLimit({
await this.dependencies.checkBookingLimitsService.checkBookingLimit({
eventStartDate: periodStart.toDate(),
limitingNumber: limit,
eventId: eventType.id,
@@ -597,8 +597,7 @@ export class AvailableSlotsService {
if (unit === "year") {
try {
// TODO: DI checkBookingLimit
await checkBookingLimit({
await this.dependencies.checkBookingLimitsService.checkBookingLimit({
eventStartDate: periodStart.toDate(),
limitingNumber: limit,
key,
+5 -5
View File
@@ -2519,7 +2519,7 @@ __metadata:
"@axiomhq/winston": ^1.2.0
"@calcom/platform-constants": "*"
"@calcom/platform-enums": "*"
"@calcom/platform-libraries": "npm:@calcom/platform-libraries@0.0.268"
"@calcom/platform-libraries": "npm:@calcom/platform-libraries@0.0.271"
"@calcom/platform-types": "*"
"@calcom/platform-utils": "*"
"@calcom/prisma": "*"
@@ -3575,13 +3575,13 @@ __metadata:
languageName: unknown
linkType: soft
"@calcom/platform-libraries@npm:@calcom/platform-libraries@0.0.268":
version: 0.0.268
resolution: "@calcom/platform-libraries@npm:0.0.268"
"@calcom/platform-libraries@npm:@calcom/platform-libraries@0.0.271":
version: 0.0.271
resolution: "@calcom/platform-libraries@npm:0.0.271"
dependencies:
"@calcom/features": "*"
"@calcom/lib": "*"
checksum: d361c067dd33e3807c3ba965b3582eadaeec2ef1e2a3114c68e0c2ab1d7149fcca2c72ce00f8f3f341bdf3caeca578898d3345d8615c58da3a66e541e62d2ca5
checksum: 5b55a05c5cd1d471fe36c90b8b2f16e70f6155065b3b13e12c06bde73f3ceffde74de3921c3a9fe05d04eed7fb587836ac79a46a521d35fe83812d66b13d8733
languageName: node
linkType: hard