diff --git a/apps/api/v2/package.json b/apps/api/v2/package.json index 023091970c..c55b6a2443 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.298", + "@calcom/platform-libraries": "npm:@calcom/platform-libraries@0.0.300", "@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 ea2284b5f6..c6a0f26f67 100644 --- a/apps/api/v2/src/lib/modules/available-slots.module.ts +++ b/apps/api/v2/src/lib/modules/available-slots.module.ts @@ -11,6 +11,8 @@ import { AvailableSlotsService } from "@/lib/services/available-slots.service"; import { BusyTimesService } from "@/lib/services/busy-times.service"; import { CacheService } from "@/lib/services/cache.service"; import { CheckBookingLimitsService } from "@/lib/services/check-booking-limits.service"; +import { FilterHostsService } from "@/lib/services/filter-hosts.service"; +import { QualifiedHostsService } from "@/lib/services/qualified-hosts.service"; import { UserAvailabilityService } from "@/lib/services/user-availability.service"; import { PrismaModule } from "@/modules/prisma/prisma.module"; import { RedisService } from "@/modules/redis/redis.service"; @@ -34,6 +36,8 @@ import { Module } from "@nestjs/common"; AvailableSlotsService, UserAvailabilityService, BusyTimesService, + FilterHostsService, + QualifiedHostsService, ], exports: [AvailableSlotsService], }) 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 474a9acfc9..6f2e33cf4e 100644 --- a/apps/api/v2/src/lib/services/available-slots.service.ts +++ b/apps/api/v2/src/lib/services/available-slots.service.ts @@ -10,6 +10,7 @@ import { PrismaUserRepository } from "@/lib/repositories/prisma-user.repository" import { BusyTimesService } from "@/lib/services/busy-times.service"; import { CacheService } from "@/lib/services/cache.service"; import { CheckBookingLimitsService } from "@/lib/services/check-booking-limits.service"; +import { QualifiedHostsService } from "@/lib/services/qualified-hosts.service"; import { RedisService } from "@/modules/redis/redis.service"; import { Injectable } from "@nestjs/common"; @@ -29,7 +30,12 @@ export class AvailableSlotsService extends BaseAvailableSlotsService { eventTypeRepository: PrismaEventTypeRepository, userRepository: PrismaUserRepository, redisService: RedisService, - featuresRepository: PrismaFeaturesRepository + featuresRepository: PrismaFeaturesRepository, + qualifiedHostsService: QualifiedHostsService, + checkBookingLimitsService: CheckBookingLimitsService, + cacheService: CacheService, + userAvailabilityService: UserAvailabilityService, + busyTimesService: BusyTimesService ) { super({ oooRepo: oooRepoDependency, @@ -41,15 +47,12 @@ export class AvailableSlotsService extends BaseAvailableSlotsService { eventTypeRepo: eventTypeRepository, userRepo: userRepository, redisClient: redisService, - checkBookingLimitsService: new CheckBookingLimitsService(bookingRepository), - cacheService: new CacheService(featuresRepository), - userAvailabilityService: new UserAvailabilityService( - oooRepoDependency, - bookingRepository, - eventTypeRepository, - redisService - ), - busyTimesService: new BusyTimesService(bookingRepository), + checkBookingLimitsService, + cacheService, + userAvailabilityService, + busyTimesService, + qualifiedHostsService, + featuresRepo: featuresRepository, }); } } diff --git a/apps/api/v2/src/lib/services/filter-hosts.service.ts b/apps/api/v2/src/lib/services/filter-hosts.service.ts new file mode 100644 index 0000000000..edb6a081de --- /dev/null +++ b/apps/api/v2/src/lib/services/filter-hosts.service.ts @@ -0,0 +1,13 @@ +import { PrismaBookingRepository } from "@/lib/repositories/prisma-booking.repository"; +import { Injectable } from "@nestjs/common"; + +import { FilterHostsService as BaseFilterHostsService } from "@calcom/platform-libraries/slots"; + +@Injectable() +export class FilterHostsService extends BaseFilterHostsService { + constructor(bookingRepository: PrismaBookingRepository) { + super({ + bookingRepo: bookingRepository, + }); + } +} diff --git a/apps/api/v2/src/lib/services/qualified-hosts.service.ts b/apps/api/v2/src/lib/services/qualified-hosts.service.ts new file mode 100644 index 0000000000..55906d6e51 --- /dev/null +++ b/apps/api/v2/src/lib/services/qualified-hosts.service.ts @@ -0,0 +1,15 @@ +import { PrismaBookingRepository } from "@/lib/repositories/prisma-booking.repository"; +import { FilterHostsService } from "@/lib/services/filter-hosts.service"; +import { Injectable } from "@nestjs/common"; + +import { QualifiedHostsService as BaseQualifiedHostsService } from "@calcom/platform-libraries/slots"; + +@Injectable() +export class QualifiedHostsService extends BaseQualifiedHostsService { + constructor(bookingRepository: PrismaBookingRepository, filterHostsService: FilterHostsService) { + super({ + bookingRepo: bookingRepository, + filterHostsService, + }); + } +} diff --git a/packages/lib/bookings/findQualifiedHostsWithDelegationCredentials.test.ts b/packages/lib/bookings/findQualifiedHostsWithDelegationCredentials.test.ts index 90b534df4f..ed44c67d96 100644 --- a/packages/lib/bookings/findQualifiedHostsWithDelegationCredentials.test.ts +++ b/packages/lib/bookings/findQualifiedHostsWithDelegationCredentials.test.ts @@ -5,8 +5,8 @@ import type { Mock } from "vitest"; import { RRResetInterval, SchedulingType } from "@calcom/prisma/enums"; +import { getQualifiedHostsService } from "../di/containers/QualifiedHosts"; import { filterHostsByLeadThreshold } from "./filterHostsByLeadThreshold"; -import { findQualifiedHostsWithDelegationCredentials } from "./findQualifiedHostsWithDelegationCredentials"; import * as getRoutedUsers from "./getRoutedUsers"; // Mock the filterHostsByLeadThreshold function @@ -21,6 +21,7 @@ afterEach(() => { (filterHostsByLeadThreshold as Mock).mockClear(); }); +const qualifiedHostsService = getQualifiedHostsService(); describe("findQualifiedHostsWithDelegationCredentials", async () => { it("should return qualified hosts based on mock of filterHostsByLeadThreshold", async () => { const hosts = [ @@ -92,7 +93,7 @@ describe("findQualifiedHostsWithDelegationCredentials", async () => { }; // Call the function under test - const result = await findQualifiedHostsWithDelegationCredentials({ + const result = await qualifiedHostsService.findQualifiedHostsWithDelegationCredentials({ eventType, routedTeamMemberIds: [], rescheduleUid: null, @@ -144,7 +145,7 @@ describe("findQualifiedHostsWithDelegationCredentials", async () => { }; // Call the function under test - const result = await findQualifiedHostsWithDelegationCredentials({ + const result = await qualifiedHostsService.findQualifiedHostsWithDelegationCredentials({ eventType, routedTeamMemberIds: [], rescheduleUid: null, @@ -231,7 +232,7 @@ describe("findQualifiedHostsWithDelegationCredentials", async () => { }; // Call the function under test - const result = await findQualifiedHostsWithDelegationCredentials({ + const result = await qualifiedHostsService.findQualifiedHostsWithDelegationCredentials({ eventType, routedTeamMemberIds: [], rescheduleUid: null, @@ -320,7 +321,7 @@ describe("findQualifiedHostsWithDelegationCredentials", async () => { }; // Call the function under test - const result = await findQualifiedHostsWithDelegationCredentials({ + const result = await qualifiedHostsService.findQualifiedHostsWithDelegationCredentials({ eventType, routedTeamMemberIds: [1], rescheduleUid: null, @@ -398,7 +399,7 @@ describe("findQualifiedHostsWithDelegationCredentials", async () => { }; prismaMock.booking.findFirst.mockResolvedValue({ userId: 2 }); - const result = await findQualifiedHostsWithDelegationCredentials({ + const result = await qualifiedHostsService.findQualifiedHostsWithDelegationCredentials({ eventType, routedTeamMemberIds: [], rescheduleUid: "recheduleUid", @@ -478,7 +479,7 @@ describe("findQualifiedHostsWithDelegationCredentials", async () => { .mockImplementation(async () => [hosts[0]]); // Call the function under test - const result = await findQualifiedHostsWithDelegationCredentials({ + const result = await qualifiedHostsService.findQualifiedHostsWithDelegationCredentials({ eventType, routedTeamMemberIds: [0, 1, 2], rescheduleUid: null, @@ -572,7 +573,7 @@ describe("findQualifiedHostsWithDelegationCredentials", async () => { ]); // Call the function under test - const result = await findQualifiedHostsWithDelegationCredentials({ + const result = await qualifiedHostsService.findQualifiedHostsWithDelegationCredentials({ eventType, routedTeamMemberIds: [2, 3], rescheduleUid: null, @@ -672,7 +673,7 @@ describe("findQualifiedHostsWithDelegationCredentials", async () => { (filterHostsByLeadThreshold as Mock).mockResolvedValue(rrHostsAfterFairness); // Call the function under test - const result = await findQualifiedHostsWithDelegationCredentials({ + const result = await qualifiedHostsService.findQualifiedHostsWithDelegationCredentials({ eventType, routedTeamMemberIds: [2, 3], rescheduleUid: null, diff --git a/packages/lib/bookings/findQualifiedHostsWithDelegationCredentials.ts b/packages/lib/bookings/findQualifiedHostsWithDelegationCredentials.ts index 8341d71549..02273ea97d 100644 --- a/packages/lib/bookings/findQualifiedHostsWithDelegationCredentials.ts +++ b/packages/lib/bookings/findQualifiedHostsWithDelegationCredentials.ts @@ -6,7 +6,6 @@ import { import type { EventType } from "@calcom/lib/bookings/getRoutedUsers"; import { withReporting } from "@calcom/lib/sentryWrapper"; import type { BookingRepository } from "@calcom/lib/server/repository/booking"; -import type { PrismaClient } from "@calcom/prisma"; import type { SelectedCalendar } from "@calcom/prisma/client"; import type { SchedulingType } from "@calcom/prisma/enums"; import type { CredentialForCalendarService, CredentialPayload } from "@calcom/types/Credential"; @@ -15,7 +14,6 @@ import type { RoutingFormResponse } from "../server/getLuckyUser"; import { filterHostsByLeadThreshold } from "./filterHostsByLeadThreshold"; export interface IQualifiedHostsService { - prisma: PrismaClient; bookingRepo: BookingRepository; filterHostsService: FilterHostsService; } @@ -227,65 +225,3 @@ export class QualifiedHostsService { "findQualifiedHostsWithDelegationCredentials" ); } - -const _findQualifiedHostsWithDelegationCredentials = async < - T extends { - email: string; - id: number; - credentials: CredentialPayload[]; - userLevelSelectedCalendars: SelectedCalendar[]; - } & Record ->({ - eventType, - rescheduleUid, - routedTeamMemberIds, - contactOwnerEmail, - routingFormResponse, -}: { - eventType: { - id: number; - schedulingType: SchedulingType | null; - hosts?: Host[]; - users: T[]; - maxLeadThreshold?: number | null; - team?: { - parentId?: number | null; - rrResetInterval: string | null; - rrTimestampBasis: string | null; - } | null; - isRRWeightsEnabled: boolean; - rescheduleWithSameRoundRobinHost: boolean; - teamId?: number; - includeNoShowInRRCalculation: boolean; - } & EventType; - rescheduleUid: string | null; - routedTeamMemberIds: number[]; - contactOwnerEmail: string | null; - routingFormResponse: RoutingFormResponse | null; -}) => { - const { prisma } = await import("@calcom/prisma"); - const { BookingRepository } = await import("@calcom/lib/server/repository/booking"); - const { FilterHostsService } = await import("@calcom/lib/bookings/filterHostsBySameRoundRobinHost"); - - const bookingRepo = new BookingRepository(prisma); - const filterHostsService = new FilterHostsService({ bookingRepo }); - - const service = new QualifiedHostsService({ - prisma, - bookingRepo, - filterHostsService, - }); - - return service.findQualifiedHostsWithDelegationCredentials({ - eventType, - rescheduleUid, - routedTeamMemberIds, - contactOwnerEmail, - routingFormResponse, - }); -}; - -export const findQualifiedHostsWithDelegationCredentials = withReporting( - _findQualifiedHostsWithDelegationCredentials, - "findQualifiedHostsWithDelegationCredentials" -); diff --git a/packages/lib/di/containers/AvailableSlots.ts b/packages/lib/di/containers/AvailableSlots.ts index e414cded5a..7def9ba7fd 100644 --- a/packages/lib/di/containers/AvailableSlots.ts +++ b/packages/lib/di/containers/AvailableSlots.ts @@ -12,8 +12,10 @@ import { cacheModule } from "../modules/Cache"; import { checkBookingLimitsModule } from "../modules/CheckBookingLimits"; import { eventTypeRepositoryModule } from "../modules/EventType"; import { featuresRepositoryModule } from "../modules/Features"; +import { filterHostsModule } from "../modules/FilterHosts"; import { getUserAvailabilityModule } from "../modules/GetUserAvailability"; import { oooRepositoryModule } from "../modules/Ooo"; +import { qualifiedHostsModule } from "../modules/QualifiedHosts"; import { routingFormResponseRepositoryModule } from "../modules/RoutingFormResponse"; import { scheduleRepositoryModule } from "../modules/Schedule"; import { selectedSlotsRepositoryModule } from "../modules/SelectedSlots"; @@ -37,6 +39,9 @@ container.load(DI_TOKENS.CHECK_BOOKING_LIMITS_SERVICE_MODULE, checkBookingLimits container.load(DI_TOKENS.AVAILABLE_SLOTS_SERVICE_MODULE, availableSlotsModule); container.load(DI_TOKENS.GET_USER_AVAILABILITY_SERVICE_MODULE, getUserAvailabilityModule); container.load(DI_TOKENS.BUSY_TIMES_SERVICE_MODULE, busyTimesModule); +container.load(DI_TOKENS.BUSY_TIMES_SERVICE_MODULE, busyTimesModule); +container.load(DI_TOKENS.FILTER_HOSTS_SERVICE_MODULE, filterHostsModule); +container.load(DI_TOKENS.QUALIFIED_HOSTS_SERVICE_MODULE, qualifiedHostsModule); export function getAvailableSlotsService() { return container.get(DI_TOKENS.AVAILABLE_SLOTS_SERVICE); diff --git a/packages/lib/di/modules/AvailableSlots.ts b/packages/lib/di/modules/AvailableSlots.ts index d7ffcb60a4..727c7b614a 100644 --- a/packages/lib/di/modules/AvailableSlots.ts +++ b/packages/lib/di/modules/AvailableSlots.ts @@ -20,4 +20,6 @@ availableSlotsModule.bind(DI_TOKENS.AVAILABLE_SLOTS_SERVICE).toClass(AvailableSl checkBookingLimitsService: DI_TOKENS.CHECK_BOOKING_LIMITS_SERVICE, userAvailabilityService: DI_TOKENS.GET_USER_AVAILABILITY_SERVICE, busyTimesService: DI_TOKENS.BUSY_TIMES_SERVICE, + featuresRepo: DI_TOKENS.FEATURES_REPOSITORY, + qualifiedHostsService: DI_TOKENS.QUALIFIED_HOSTS_SERVICE, } satisfies Record); diff --git a/packages/lib/di/modules/QualifiedHosts.ts b/packages/lib/di/modules/QualifiedHosts.ts index fcd703c60f..5aa37f37f4 100644 --- a/packages/lib/di/modules/QualifiedHosts.ts +++ b/packages/lib/di/modules/QualifiedHosts.ts @@ -6,7 +6,6 @@ import { DI_TOKENS } from "../tokens"; export const qualifiedHostsModule = createModule(); qualifiedHostsModule.bind(DI_TOKENS.QUALIFIED_HOSTS_SERVICE).toClass(QualifiedHostsService, { - prisma: DI_TOKENS.PRISMA_CLIENT, bookingRepo: DI_TOKENS.BOOKING_REPOSITORY, filterHostsService: DI_TOKENS.FILTER_HOSTS_SERVICE, } satisfies Record); diff --git a/packages/platform/libraries/slots.ts b/packages/platform/libraries/slots.ts index 782927228e..35bf208e2a 100644 --- a/packages/platform/libraries/slots.ts +++ b/packages/platform/libraries/slots.ts @@ -1,6 +1,12 @@ +import { FilterHostsService } from "@calcom/lib/bookings/filterHostsBySameRoundRobinHost"; +import { QualifiedHostsService } from "@calcom/lib/bookings/findQualifiedHostsWithDelegationCredentials"; import { BusyTimesService } from "@calcom/lib/getBusyTimes"; import { AvailableSlotsService } from "@calcom/trpc/server/routers/viewer/slots/util"; export { AvailableSlotsService }; export { BusyTimesService }; + +export { QualifiedHostsService }; + +export { FilterHostsService }; diff --git a/packages/trpc/server/routers/viewer/slots/util.ts b/packages/trpc/server/routers/viewer/slots/util.ts index 8fa2cc0fa0..1931a058f4 100644 --- a/packages/trpc/server/routers/viewer/slots/util.ts +++ b/packages/trpc/server/routers/viewer/slots/util.ts @@ -8,14 +8,15 @@ import { orgDomainConfig } from "@calcom/ee/organizations/lib/orgDomains"; import { checkForConflicts } from "@calcom/features/bookings/lib/conflictChecker/checkForConflicts"; import { isEventTypeLoggingEnabled } from "@calcom/features/bookings/lib/isEventTypeLoggingEnabled"; import type { CacheService } from "@calcom/features/calendar-cache/lib/getShouldServeCache"; +import type { FeaturesRepository } from "@calcom/features/flags/features.repository"; import type { IRedisService } from "@calcom/features/redis/IRedisService"; +import type { QualifiedHostsService } from "@calcom/lib/bookings/findQualifiedHostsWithDelegationCredentials"; import { shouldIgnoreContactOwner } from "@calcom/lib/bookings/routing/utils"; import { RESERVED_SUBDOMAINS } from "@calcom/lib/constants"; import { buildDateRanges } from "@calcom/lib/date-ranges"; import { getUTCOffsetByTimezone } from "@calcom/lib/dayjs"; import { getDefaultEvent } from "@calcom/lib/defaultEvents"; import type { getBusyTimesService } from "@calcom/lib/di/containers/BusyTimes"; -import { getQualifiedHostsService } from "@calcom/lib/di/containers/QualifiedHosts"; import { getAggregatedAvailability } from "@calcom/lib/getAggregatedAvailability"; import type { BusyTimesService } from "@calcom/lib/getBusyTimes"; import type { @@ -40,7 +41,6 @@ import { BookingDateInPastError, } from "@calcom/lib/isOutOfBounds"; import logger from "@calcom/lib/logger"; -import { isRestrictionScheduleEnabled } from "@calcom/lib/restrictionSchedule"; import { safeStringify } from "@calcom/lib/safeStringify"; import { withReporting } from "@calcom/lib/sentryWrapper"; import type { ISelectedSlotRepository } from "@calcom/lib/server/repository/ISelectedSlotRepository"; @@ -106,6 +106,8 @@ export interface IAvailableSlotsService { userAvailabilityService: UserAvailabilityService; busyTimesService: BusyTimesService; redisClient: IRedisService; + featuresRepo: FeaturesRepository; + qualifiedHostsService: QualifiedHostsService; } function withSlotsCache( @@ -924,6 +926,14 @@ export class AvailableSlotsService { }; } + private async checkRestrictionScheduleEnabled(teamId?: number): Promise { + if (!teamId) { + return false; + } + + return await this.dependencies.featuresRepo.checkIfTeamHasFeature(teamId, "restriction-schedule"); + } + private async _getRegularOrDynamicEventType( input: TGetScheduleInputSchema, organizationDetails: { currentOrgDomain: string | null; isValidOrgDomain: boolean } @@ -1027,9 +1037,8 @@ export class AvailableSlotsService { }); } - const qualifiedHostsService = getQualifiedHostsService(); const { qualifiedRRHosts, allFallbackRRHosts, fixedHosts } = - await qualifiedHostsService.findQualifiedHostsWithDelegationCredentials({ + await this.dependencies.qualifiedHostsService.findQualifiedHostsWithDelegationCredentials({ eventType, rescheduleUid: input.rescheduleUid ?? null, routedTeamMemberIds, @@ -1143,8 +1152,9 @@ export class AvailableSlotsService { let availableTimeSlots: typeof timeSlots = []; const bookerClientUid = ctx?.req?.cookies?.uid; - // TODO: DI isRestrictionScheduleEnabled - const isRestrictionScheduleFeatureEnabled = await isRestrictionScheduleEnabled(eventType.team?.id); + const isRestrictionScheduleFeatureEnabled = await this.checkRestrictionScheduleEnabled( + eventType.team?.id + ); if (eventType.restrictionScheduleId && isRestrictionScheduleFeatureEnabled) { const restrictionSchedule = await this.dependencies.scheduleRepo.findScheduleByIdForBuildDateRanges({ scheduleId: eventType.restrictionScheduleId, diff --git a/yarn.lock b/yarn.lock index a066768505..b6292e45c7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2500,7 +2500,7 @@ __metadata: "@axiomhq/winston": ^1.2.0 "@calcom/platform-constants": "*" "@calcom/platform-enums": "*" - "@calcom/platform-libraries": "npm:@calcom/platform-libraries@0.0.298" + "@calcom/platform-libraries": "npm:@calcom/platform-libraries@0.0.300" "@calcom/platform-types": "*" "@calcom/platform-utils": "*" "@calcom/prisma": "*" @@ -3559,13 +3559,13 @@ __metadata: languageName: unknown linkType: soft -"@calcom/platform-libraries@npm:@calcom/platform-libraries@0.0.298": - version: 0.0.298 - resolution: "@calcom/platform-libraries@npm:0.0.298" +"@calcom/platform-libraries@npm:@calcom/platform-libraries@0.0.300": + version: 0.0.300 + resolution: "@calcom/platform-libraries@npm:0.0.300" dependencies: "@calcom/features": "*" "@calcom/lib": "*" - checksum: 63401c90d15ce05a17905f68eb36e655c6368b430ee8c28df371c2b91d5033cab846e96406e28d2c716e8e361eefaf96caf2c3a26fa63bc239ec97632b0b7613 + checksum: bff9f08a0586169f2d5a85167f0a0e0e57ca93b637ca858a72b5d3d943ceef37d652efa3d2e6f7561548e4dfe3aabb5c0c6e0a8d38f68d3d21d5e6993e56ad7d languageName: node linkType: hard