refactor: convert findQualifiedHostsWithDelegationCredentials to service class with DI (#22974)

* refactor: convert findQualifiedHostsWithDelegationCredentials to service class with DI

- Create QualifiedHostsService class following UserAvailabilityService pattern
- Add IQualifiedHostsService interface with prisma and bookingRepo dependencies
- Create DI module and container for qualified hosts service
- Update filterHostsBySameRoundRobinHost to accept prisma as parameter
- Update all usage sites to use the new service:
  - loadAndValidateUsers.ts
  - slots/util.ts
  - test mocks in _post.test.ts
- Maintain backward compatibility with original function export
- Fix type issues in team properties (rrResetInterval, rrTimestampBasis)

Co-Authored-By: morgan@cal.com <morgan@cal.com>

* fix: update filterHostsBySameRoundRobinHost test to include prisma parameter

- Add missing prisma parameter to all test function calls
- Resolves unit test failure caused by function signature change

Co-Authored-By: morgan@cal.com <morgan@cal.com>

* fix: resolve type issues in FilterHostsService

- Import PrismaClient type instead of using unknown
- Fix type compatibility for BookingRepository constructor
- Update test mocks to use proper BookingRepository type
- Ensure all DI dependencies are properly typed

Co-Authored-By: morgan@cal.com <morgan@cal.com>

* refactor: rename DI files to CamelCase and update imports

- Rename all files in packages/lib/di from kebab-case to CamelCase
- Update 22 external files with import statements to use new file names
- Update internal DI module files with corrected imports
- Maintain consistency with TypeScript naming conventions

Co-Authored-By: morgan@cal.com <morgan@cal.com>

* chore: bump platform libs

* chore: bump platform libs

* fix: remove obsolete vitest mock after service class refactoring

- Remove obsolete mock for old function module
- Keep correct mock for new DI container
- Resolves CI unit test failures

Co-Authored-By: morgan@cal.com <morgan@cal.com>

* fix: correct import path for calAIPhone zod-utils module

Co-Authored-By: morgan@cal.com <morgan@cal.com>

* fix: Booker active booking limit can't be switched off (#23005)

* refactor: Get rid of `getServerSideProps` for /getting-started pages (#23003)

* refactor

* fix type check

* fix: Remove Reporting page within Routing Forms (#22990)

* fix error in handleNewBooking (#23011)

Co-authored-by: CarinaWolli <wollencarina@gmail.com>

* Documentation edits made through Mintlify web editor (#23007)

Co-authored-by: mintlify[bot] <109931778+mintlify[bot]@users.noreply.github.com>

* fix: Contact support button position changed from absolute to fixed (#23002)

* feat: Add private links to API (#22943)

* --init

* address change requests

* adding further changes

* address feedback

* further changes

* further clean-up

* clean up

* fix module import and others

* add guards

* remove unnecessary comments

* remove unnecessary comments

* cleanup

* sort coderabbig suggestions

* improve check

* chore: bump platform libraries

---------

Co-authored-by: Lauris Skraucis <lauris.skraucis@gmail.com>
Co-authored-by: supalarry <laurisskraucis@gmail.com>

* chore: release v5.5.15

* chore: bump platform libs

* 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: Anik Dhabal Babu <81948346+anikdhabal@users.noreply.github.com>
Co-authored-by: Benny Joo <sldisek783@gmail.com>
Co-authored-by: Sahitya Chandra <sahityajb@gmail.com>
Co-authored-by: Carina Wollendorfer <30310907+CarinaWolli@users.noreply.github.com>
Co-authored-by: CarinaWolli <wollencarina@gmail.com>
Co-authored-by: mintlify[bot] <109931778+mintlify[bot]@users.noreply.github.com>
Co-authored-by: Ayush Kumar <kumarayushkumar@protonmail.com>
Co-authored-by: Syed Ali Shahbaz <52925846+alishaz-polymath@users.noreply.github.com>
Co-authored-by: Lauris Skraucis <lauris.skraucis@gmail.com>
Co-authored-by: supalarry <laurisskraucis@gmail.com>
Co-authored-by: emrysal <me@alexvanandel.com>
This commit is contained in:
devin-ai-integration[bot]
2025-08-11 12:24:15 +00:00
committed by GitHub
co-authored by morgan@cal.com CarinaWolli mintlify[bot] Lauris Skraucis supalarry Anik Dhabal Babu Benny Joo Sahitya Chandra Carina Wollendorfer Ayush Kumar Syed Ali Shahbaz emrysal
parent a90d765fca
commit ebeb008f9b
58 changed files with 433 additions and 285 deletions
+1 -1
View File
@@ -1,7 +1,7 @@
import type { NextApiRequest } from "next";
import { z } from "zod";
import { getUserAvailabilityService } from "@calcom/lib/di/containers/get-user-availability";
import { getUserAvailabilityService } from "@calcom/lib/di/containers/GetUserAvailability";
import { HttpError } from "@calcom/lib/http-error";
import { defaultResponder } from "@calcom/lib/server/defaultResponder";
import prisma from "@calcom/prisma";
+1 -1
View File
@@ -4,7 +4,7 @@ import type { NextApiRequest, NextApiResponse } from "next";
import dayjs from "@calcom/dayjs";
import { isSupportedTimeZone } from "@calcom/lib/dayjs";
import { getAvailableSlotsService } from "@calcom/lib/di/containers/available-slots";
import { getAvailableSlotsService } from "@calcom/lib/di/containers/AvailableSlots";
import { HttpError } from "@calcom/lib/http-error";
import { defaultResponder } from "@calcom/lib/server/defaultResponder";
import { createContext } from "@calcom/trpc/server/createContext";
+7 -5
View File
@@ -76,11 +76,13 @@ vi.mock("@calcom/features/watchlist/operations/check-if-users-are-blocked.contro
checkIfUsersAreBlocked: vi.fn().mockResolvedValue(false),
}));
vi.mock("@calcom/lib/bookings/findQualifiedHostsWithDelegationCredentials", () => ({
findQualifiedHostsWithDelegationCredentials: vi.fn().mockResolvedValue({
qualifiedRRHosts: [],
allFallbackRRHosts: [],
fixedHosts: [],
vi.mock("@calcom/lib/di/containers/QualifiedHosts", () => ({
getQualifiedHostsService: vi.fn().mockReturnValue({
findQualifiedHostsWithDelegationCredentials: vi.fn().mockResolvedValue({
qualifiedRRHosts: [],
allFallbackRRHosts: [],
fixedHosts: [],
}),
}),
}));
+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.294",
"@calcom/platform-libraries": "npm:@calcom/platform-libraries@0.0.298",
"@calcom/platform-types": "*",
"@calcom/platform-utils": "*",
"@calcom/prisma": "*",
@@ -5,7 +5,7 @@ import { z } from "zod";
import dayjs from "@calcom/dayjs";
import { ZGetRetellLLMSchema } from "@calcom/features/calAIPhone/zod-utils";
import type { TGetRetellLLMSchema } from "@calcom/features/calAIPhone/zod-utils";
import { getAvailableSlotsService } from "@calcom/lib/di/containers/available-slots";
import { getAvailableSlotsService } from "@calcom/lib/di/containers/AvailableSlots";
import { fetcher } from "@calcom/lib/retellAIFetcher";
import { defaultHandler } from "@calcom/lib/server/defaultHandler";
import prisma from "@calcom/prisma";
+1 -1
View File
@@ -3,7 +3,7 @@ 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 { getCheckBookingLimitsService } from "@calcom/lib/di/containers/BookingLimits";
import type { IntervalLimit } from "@calcom/lib/intervalLimits/intervalLimitSchema";
import { validateIntervalLimitOrder } from "@calcom/lib/intervalLimits/validateIntervalLimitOrder";
+1 -1
View File
@@ -17,7 +17,7 @@ import {
import { describe, vi, test } from "vitest";
import dayjs from "@calcom/dayjs";
import { getAvailableSlotsService } from "@calcom/lib/di/containers/available-slots";
import { getAvailableSlotsService } from "@calcom/lib/di/containers/AvailableSlots";
import { SchedulingType, type BookingStatus } from "@calcom/prisma/enums";
import { expect, expectedSlotsForSchedule } from "./getSchedule/expects";
@@ -9,7 +9,7 @@ import {
import { describe, test } from "vitest";
import { getAvailableSlotsService } from "@calcom/lib/di/containers/available-slots";
import { getAvailableSlotsService } from "@calcom/lib/di/containers/AvailableSlots";
import { expect, expectedSlotsForSchedule } from "./expects";
import { setupAndTeardown } from "./setupAndTeardown";
@@ -12,7 +12,7 @@ import { expectNoAttemptToGetAvailability } from "../../utils/bookingScenario/ex
import { describe, test } from "vitest";
import { getAvailableSlotsService } from "@calcom/lib/di/containers/available-slots";
import { getAvailableSlotsService } from "@calcom/lib/di/containers/AvailableSlots";
import { MembershipRole } from "@calcom/prisma/enums";
import { expect, expectedSlotsForSchedule } from "./expects";
@@ -8,7 +8,7 @@ import type { ScenarioData } from "../../utils/bookingScenario/bookingScenario";
import { describe, expect, vi, test } from "vitest";
import { getAvailableSlotsService } from "@calcom/lib/di/containers/available-slots";
import { getAvailableSlotsService } from "@calcom/lib/di/containers/AvailableSlots";
import { PeriodType } from "@calcom/prisma/enums";
import { expectedSlotsForSchedule } from "./expects";
@@ -10,7 +10,7 @@ import {
import { describe, test, vi } from "vitest";
import type { z } from "zod";
import { getAvailableSlotsService } from "@calcom/lib/di/containers/available-slots";
import { getAvailableSlotsService } from "@calcom/lib/di/containers/AvailableSlots";
import type { getScheduleSchema } from "@calcom/trpc/server/routers/viewer/slots/types";
import { expect } from "./expects";
@@ -10,7 +10,7 @@ import type { IncomingMessage } from "http";
import { describe, test, beforeEach, vi } from "vitest";
import type { z } from "zod";
import { getAvailableSlotsService } from "@calcom/lib/di/containers/available-slots";
import { getAvailableSlotsService } from "@calcom/lib/di/containers/AvailableSlots";
import { prisma } from "@calcom/prisma";
import type { getScheduleSchema, GetScheduleOptions } from "@calcom/trpc/server/routers/viewer/slots/types";
@@ -52,8 +52,8 @@ import {
enrichHostsWithDelegationCredentials,
getFirstDelegationConferencingCredentialAppLocation,
} from "@calcom/lib/delegationCredential/server";
import { getCheckBookingAndDurationLimitsService } from "@calcom/lib/di/containers/booking-limits";
import { getCacheService } from "@calcom/lib/di/containers/cache";
import { getCheckBookingAndDurationLimitsService } from "@calcom/lib/di/containers/BookingLimits";
import { getCacheService } from "@calcom/lib/di/containers/Cache";
import { ErrorCode } from "@calcom/lib/errorCodes";
import { getErrorFromUnknown } from "@calcom/lib/errors";
import { getEventName, updateHostInEventName } from "@calcom/lib/event";
@@ -4,8 +4,8 @@ import dayjs from "@calcom/dayjs";
import type { Dayjs } from "@calcom/dayjs";
import { checkForConflicts } from "@calcom/features/bookings/lib/conflictChecker/checkForConflicts";
import { buildDateRanges } from "@calcom/lib/date-ranges";
import { getBusyTimesService } from "@calcom/lib/di/containers/busy-times";
import { getUserAvailabilityService } from "@calcom/lib/di/containers/get-user-availability";
import { getBusyTimesService } from "@calcom/lib/di/containers/BusyTimes";
import { getUserAvailabilityService } from "@calcom/lib/di/containers/GetUserAvailability";
import { ErrorCode } from "@calcom/lib/errorCodes";
import { parseBookingLimit } from "@calcom/lib/intervalLimits/isBookingLimits";
import { parseDurationLimit } from "@calcom/lib/intervalLimits/isDurationLimits";
@@ -2,8 +2,8 @@ import type { Prisma } from "@prisma/client";
import type { Logger } from "tslog";
import { checkIfUsersAreBlocked } from "@calcom/features/watchlist/operations/check-if-users-are-blocked.controller";
import { findQualifiedHostsWithDelegationCredentials } from "@calcom/lib/bookings/findQualifiedHostsWithDelegationCredentials";
import { enrichUsersWithDelegationCredentials } from "@calcom/lib/delegationCredential/server";
import { getQualifiedHostsService } from "@calcom/lib/di/containers/QualifiedHosts";
import getOrgIdFromMemberOrTeamId from "@calcom/lib/getOrgIdFromMemberOrTeamId";
import { HttpError } from "@calcom/lib/http-error";
import { getPiiFreeUser } from "@calcom/lib/piiFreeData";
@@ -142,8 +142,9 @@ const _loadAndValidateUsers = async ({
? false
: user.isFixed || eventType.schedulingType !== SchedulingType.ROUND_ROBIN,
}));
const qualifiedHostsService = getQualifiedHostsService();
const { qualifiedRRHosts, allFallbackRRHosts, fixedHosts } =
await findQualifiedHostsWithDelegationCredentials({
await qualifiedHostsService.findQualifiedHostsWithDelegationCredentials({
eventType,
routedTeamMemberIds: routedTeamMemberIds || [],
rescheduleUid,
@@ -159,7 +160,7 @@ const _loadAndValidateUsers = async ({
},
{} as {
[key: number]: Awaited<
ReturnType<typeof findQualifiedHostsWithDelegationCredentials>
ReturnType<ReturnType<typeof getQualifiedHostsService>["findQualifiedHostsWithDelegationCredentials"]>
>["qualifiedRRHosts"][number];
}
);
@@ -9,8 +9,8 @@ import {
routingRepositoryBaseInputSchema,
bookingRepositoryBaseInputSchema,
} from "@calcom/features/insights/server/raw-data.schema";
import { getInsightsBookingService } from "@calcom/lib/di/containers/insights-booking";
import { getInsightsRoutingService } from "@calcom/lib/di/containers/insights-routing";
import { getInsightsBookingService } from "@calcom/lib/di/containers/InsightsBooking";
import { getInsightsRoutingService } from "@calcom/lib/di/containers/InsightsRouting";
import type { readonlyPrisma } from "@calcom/prisma";
import { BookingStatus } from "@calcom/prisma/enums";
import authedProcedure from "@calcom/trpc/server/procedures/authedProcedure";
@@ -1,31 +1,29 @@
import type { Mock } from "vitest";
import { describe, expect, it, vi, afterEach } from "vitest";
import prisma from "@calcom/prisma";
import type { BookingRepository } from "@calcom/lib/server/repository/booking";
import { filterHostsBySameRoundRobinHost } from "./filterHostsBySameRoundRobinHost";
import { FilterHostsService } from "./filterHostsBySameRoundRobinHost";
// Mocking setup
const prismaMock = {
booking: {
findFirst: vi.fn(), // Mock the findFirst method
},
};
const mockBookingRepo = {
findOriginalRescheduledBookingUserId: vi.fn(),
} as unknown as BookingRepository;
// Use `vi.spyOn` to make `prisma.booking.groupBy` call the mock instead
vi.spyOn(prisma.booking, "findFirst").mockImplementation(prismaMock.booking.findFirst);
afterEach(() => {
// Clear call history before each test to avoid cross-test interference
prismaMock.booking.findFirst.mockClear();
const filterHostsService = new FilterHostsService({
bookingRepo: mockBookingRepo,
});
describe("filterHostsBySameRoundRobinHost", () => {
afterEach(() => {
(mockBookingRepo.findOriginalRescheduledBookingUserId as Mock).mockClear();
});
describe("FilterHostsService", () => {
it("skips filter if rescheduleWithSameRoundRobinHost set to false", async () => {
const hosts = [
{ isFixed: false as const, createdAt: new Date(), user: { id: 1, email: "example1@acme.com" } },
];
expect(
filterHostsBySameRoundRobinHost({
filterHostsService.filterHostsBySameRoundRobinHost({
hosts,
rescheduleUid: "some-uid",
rescheduleWithSameRoundRobinHost: false,
@@ -35,10 +33,10 @@ describe("filterHostsBySameRoundRobinHost", () => {
});
it("skips filter if rerouting", async () => {
const hosts = [
{ isFixed: true as const, createdAt: new Date(), user: { id: 1, email: "example1@acme.com" } },
{ isFixed: false as const, createdAt: new Date(), user: { id: 1, email: "example1@acme.com" } },
];
expect(
filterHostsBySameRoundRobinHost({
filterHostsService.filterHostsBySameRoundRobinHost({
hosts,
rescheduleUid: "some-uid",
rescheduleWithSameRoundRobinHost: true,
@@ -48,15 +46,14 @@ describe("filterHostsBySameRoundRobinHost", () => {
});
it("correctly selects the same host if the filter applies and the host is in the RR users", async () => {
prismaMock.booking.findFirst.mockResolvedValue({ userId: 1 });
(mockBookingRepo.findOriginalRescheduledBookingUserId as Mock).mockResolvedValue({ userId: 1 });
const hosts = [
{ isFixed: false as const, createdAt: new Date(), user: { id: 1, email: "example1@acme.com" } },
{ isFixed: false as const, createdAt: new Date(), user: { id: 2, email: "example2@acme.com" } },
];
// Same host should be selected
expect(
filterHostsBySameRoundRobinHost({
filterHostsService.filterHostsBySameRoundRobinHost({
hosts,
rescheduleUid: "some-uid",
rescheduleWithSameRoundRobinHost: true,
@@ -68,7 +65,7 @@ describe("filterHostsBySameRoundRobinHost", () => {
// Tests for bookings that have more than one host
describe("Fixed hosts and round robin groups support", () => {
it("should return organizer and attendee hosts", async () => {
prismaMock.booking.findFirst.mockResolvedValue({
(mockBookingRepo.findOriginalRescheduledBookingUserId as Mock).mockResolvedValue({
userId: 1,
attendees: [
{ email: "host2@acme.com" },
@@ -84,7 +81,7 @@ describe("filterHostsBySameRoundRobinHost", () => {
{ isFixed: false as const, createdAt: new Date(), user: { id: 4, email: "host4@acme.com" } },
];
const result = await filterHostsBySameRoundRobinHost({
const result = await filterHostsService.filterHostsBySameRoundRobinHost({
hosts,
rescheduleUid: "some-uid",
rescheduleWithSameRoundRobinHost: true,
@@ -101,7 +98,7 @@ describe("filterHostsBySameRoundRobinHost", () => {
});
it("should return only organizer host when no attendees match current hosts", async () => {
prismaMock.booking.findFirst.mockResolvedValue({
(mockBookingRepo.findOriginalRescheduledBookingUserId as Mock).mockResolvedValue({
userId: 1,
attendees: [
{ email: "attendee1@example.com" }, // Non-host attendee
@@ -114,7 +111,7 @@ describe("filterHostsBySameRoundRobinHost", () => {
{ isFixed: false as const, createdAt: new Date(), user: { id: 2, email: "host2@acme.com" } },
];
const result = await filterHostsBySameRoundRobinHost({
const result = await filterHostsService.filterHostsBySameRoundRobinHost({
hosts,
rescheduleUid: "some-uid",
rescheduleWithSameRoundRobinHost: true,
@@ -1,57 +1,53 @@
import { prisma } from "@calcom/prisma";
import { BookingStatus } from "@calcom/prisma/enums";
import type { BookingRepository } from "@calcom/lib/server/repository/booking";
import { isRerouting } from "./routing/utils";
export const filterHostsBySameRoundRobinHost = async <
T extends {
isFixed: false; // ensure no fixed hosts are passed.
user: { id: number; email: string };
}
>({
hosts,
rescheduleUid,
rescheduleWithSameRoundRobinHost,
routedTeamMemberIds,
}: {
hosts: T[];
rescheduleUid: string | null;
rescheduleWithSameRoundRobinHost: boolean;
routedTeamMemberIds: number[] | null;
}) => {
if (
!rescheduleUid ||
!rescheduleWithSameRoundRobinHost ||
isRerouting({ rescheduleUid, routedTeamMemberIds })
) {
return hosts;
}
const originalRescheduledBooking = await prisma.booking.findFirst({
where: {
uid: rescheduleUid,
status: {
in: [BookingStatus.ACCEPTED, BookingStatus.CANCELLED, BookingStatus.PENDING],
},
},
select: {
userId: true,
attendees: {
select: {
email: true,
},
},
},
});
export interface IFilterHostsService {
bookingRepo: BookingRepository;
}
if (!originalRescheduledBooking) {
return hosts;
export class FilterHostsService {
constructor(public readonly dependencies: IFilterHostsService) {}
async filterHostsBySameRoundRobinHost<
T extends {
isFixed: false; // ensure no fixed hosts are passed.
user: { id: number; email: string };
}
>({
hosts,
rescheduleUid,
rescheduleWithSameRoundRobinHost,
routedTeamMemberIds,
}: {
hosts: T[];
rescheduleUid: string | null;
rescheduleWithSameRoundRobinHost: boolean;
routedTeamMemberIds: number[] | null;
}) {
if (
!rescheduleUid ||
!rescheduleWithSameRoundRobinHost ||
isRerouting({ rescheduleUid, routedTeamMemberIds })
) {
return hosts;
}
const originalRescheduledBooking =
await this.dependencies.bookingRepo.findOriginalRescheduledBookingUserId({
rescheduleUid,
});
if (!originalRescheduledBooking) {
return hosts;
}
const attendeeEmails = originalRescheduledBooking.attendees?.map((attendee) => attendee.email) || [];
return hosts.filter((host) => {
const isOrganizer = host.user.id === originalRescheduledBooking.userId;
const isAttendee = attendeeEmails.includes(host.user.email);
return isOrganizer || isAttendee;
});
}
const attendeeEmails = originalRescheduledBooking.attendees?.map((attendee) => attendee.email) || [];
return hosts.filter((host) => {
const isOrganizer = host.user.id === originalRescheduledBooking.userId;
const isAttendee = attendeeEmails.includes(host.user.email);
return isOrganizer || isAttendee;
});
};
}
@@ -1,16 +1,24 @@
import type { FilterHostsService } from "@calcom/lib/bookings/filterHostsBySameRoundRobinHost";
import {
findMatchingHostsWithEventSegment,
getNormalizedHostsWithDelegationCredentials,
} from "@calcom/lib/bookings/getRoutedUsers";
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";
import type { RoutingFormResponse } from "../server/getLuckyUser";
import { filterHostsByLeadThreshold } from "./filterHostsByLeadThreshold";
import { filterHostsBySameRoundRobinHost } from "./filterHostsBySameRoundRobinHost";
export interface IQualifiedHostsService {
prisma: PrismaClient;
bookingRepo: BookingRepository;
filterHostsService: FilterHostsService;
}
type Host<T> = {
isFixed: boolean;
@@ -48,6 +56,178 @@ const isFixedHost = <T extends { isFixed: boolean }>(host: T): host is T & { isF
return host.isFixed;
};
export class QualifiedHostsService {
constructor(public readonly dependencies: IQualifiedHostsService) {}
async _findQualifiedHostsWithDelegationCredentials<
T extends {
email: string;
id: number;
credentials: CredentialPayload[];
userLevelSelectedCalendars: SelectedCalendar[];
} & Record<string, unknown>
>({
eventType,
rescheduleUid,
routedTeamMemberIds,
contactOwnerEmail,
routingFormResponse,
}: {
eventType: {
id: number;
maxLeadThreshold?: number | null;
hosts?: Host<T>[];
users: T[];
schedulingType: SchedulingType | null;
isRRWeightsEnabled: boolean;
rescheduleWithSameRoundRobinHost: boolean;
includeNoShowInRRCalculation: boolean;
} & EventType;
rescheduleUid: string | null;
routedTeamMemberIds: number[];
contactOwnerEmail: string | null;
routingFormResponse: RoutingFormResponse | null;
}): Promise<{
qualifiedRRHosts: {
isFixed: boolean;
createdAt: Date | null;
priority?: number | null;
weight?: number | null;
user: Omit<T, "credentials"> & { credentials: CredentialForCalendarService[] };
}[];
fixedHosts: {
isFixed: boolean;
createdAt: Date | null;
priority?: number | null;
weight?: number | null;
user: Omit<T, "credentials"> & { credentials: CredentialForCalendarService[] };
}[];
// all hosts we want to fallback to including the qualifiedRRHosts (fairness + crm contact owner)
allFallbackRRHosts?: {
isFixed: boolean;
createdAt: Date | null;
priority?: number | null;
weight?: number | null;
user: Omit<T, "credentials"> & { credentials: CredentialForCalendarService[] };
}[];
}> {
const { hosts: normalizedHosts, fallbackHosts: fallbackUsers } =
await getNormalizedHostsWithDelegationCredentials({
eventType,
});
// not a team event type, or some other reason - segment matching isn't necessary.
if (!normalizedHosts) {
const fixedHosts = fallbackUsers.filter(isFixedHost);
const roundRobinHosts = fallbackUsers.filter(isRoundRobinHost);
return { qualifiedRRHosts: roundRobinHosts, fixedHosts };
}
const fixedHosts = normalizedHosts.filter(isFixedHost);
const roundRobinHosts = normalizedHosts.filter(isRoundRobinHost);
// If it is rerouting, we should not force reschedule with same host.
const hostsAfterRescheduleWithSameRoundRobinHost = applyFilterWithFallback(
roundRobinHosts,
await this.dependencies.filterHostsService.filterHostsBySameRoundRobinHost({
hosts: roundRobinHosts,
rescheduleUid,
rescheduleWithSameRoundRobinHost: eventType.rescheduleWithSameRoundRobinHost,
routedTeamMemberIds,
})
);
if (hostsAfterRescheduleWithSameRoundRobinHost.length === 1) {
return {
qualifiedRRHosts: hostsAfterRescheduleWithSameRoundRobinHost,
fixedHosts,
};
}
const hostsAfterSegmentMatching = applyFilterWithFallback(
hostsAfterRescheduleWithSameRoundRobinHost,
(await findMatchingHostsWithEventSegment({
eventType,
hosts: hostsAfterRescheduleWithSameRoundRobinHost,
})) as typeof hostsAfterRescheduleWithSameRoundRobinHost
);
if (hostsAfterSegmentMatching.length === 1) {
return {
qualifiedRRHosts: hostsAfterSegmentMatching,
fixedHosts,
};
}
//if segment matching doesn't return any hosts we fall back to all round robin hosts
const officalRRHosts = hostsAfterSegmentMatching.length
? hostsAfterSegmentMatching
: hostsAfterRescheduleWithSameRoundRobinHost;
const hostsAfterContactOwnerMatching = applyFilterWithFallback(
officalRRHosts,
officalRRHosts.filter((host) => host.user.email === contactOwnerEmail)
);
const hostsAfterRoutedTeamMemberIdsMatching = applyFilterWithFallback(
officalRRHosts,
officalRRHosts.filter((host) => routedTeamMemberIds.includes(host.user.id))
);
if (hostsAfterRoutedTeamMemberIdsMatching.length === 1) {
if (hostsAfterContactOwnerMatching.length === 1) {
return {
qualifiedRRHosts: hostsAfterContactOwnerMatching,
allFallbackRRHosts: getFallBackWithContactOwner(
hostsAfterRoutedTeamMemberIdsMatching,
hostsAfterContactOwnerMatching[0]
),
fixedHosts,
};
}
return {
qualifiedRRHosts: hostsAfterRoutedTeamMemberIdsMatching,
fixedHosts,
};
}
const hostsAfterFairnessMatching = applyFilterWithFallback(
hostsAfterRoutedTeamMemberIdsMatching,
await filterHostsByLeadThreshold({
eventType,
hosts: hostsAfterRoutedTeamMemberIdsMatching,
maxLeadThreshold: eventType.maxLeadThreshold ?? null,
routingFormResponse,
})
);
if (hostsAfterContactOwnerMatching.length === 1) {
return {
qualifiedRRHosts: hostsAfterContactOwnerMatching,
allFallbackRRHosts: getFallBackWithContactOwner(
hostsAfterFairnessMatching,
hostsAfterContactOwnerMatching[0]
),
fixedHosts,
};
}
return {
qualifiedRRHosts: hostsAfterFairnessMatching,
// only if fairness filtering is active
allFallbackRRHosts:
hostsAfterFairnessMatching.length !== hostsAfterRoutedTeamMemberIdsMatching.length
? hostsAfterRoutedTeamMemberIdsMatching
: undefined,
fixedHosts,
};
}
findQualifiedHostsWithDelegationCredentials = withReporting(
this._findQualifiedHostsWithDelegationCredentials.bind(this),
"findQualifiedHostsWithDelegationCredentials"
);
}
const _findQualifiedHostsWithDelegationCredentials = async <
T extends {
email: string;
@@ -64,154 +244,45 @@ const _findQualifiedHostsWithDelegationCredentials = async <
}: {
eventType: {
id: number;
maxLeadThreshold: number | null;
schedulingType: SchedulingType | null;
hosts?: Host<T>[];
users: T[];
schedulingType: SchedulingType | null;
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;
}): Promise<{
qualifiedRRHosts: {
isFixed: boolean;
createdAt: Date | null;
priority?: number | null;
weight?: number | null;
groupId?: string | null;
user: Omit<T, "credentials"> & { credentials: CredentialForCalendarService[] };
}[];
fixedHosts: {
isFixed: boolean;
createdAt: Date | null;
priority?: number | null;
weight?: number | null;
groupId?: string | null;
user: Omit<T, "credentials"> & { credentials: CredentialForCalendarService[] };
}[];
// all hosts we want to fallback to including the qualifiedRRHosts (fairness + crm contact owner)
allFallbackRRHosts?: {
isFixed: boolean;
createdAt: Date | null;
priority?: number | null;
weight?: number | null;
groupId?: string | null;
user: Omit<T, "credentials"> & { credentials: CredentialForCalendarService[] };
}[];
}> => {
const { hosts: normalizedHosts, fallbackHosts: fallbackUsers } =
await getNormalizedHostsWithDelegationCredentials({
eventType,
});
// not a team event type, or some other reason - segment matching isn't necessary.
if (!normalizedHosts) {
const fixedHosts = fallbackUsers.filter(isFixedHost);
const roundRobinHosts = fallbackUsers.filter(isRoundRobinHost);
return { qualifiedRRHosts: roundRobinHosts, fixedHosts };
}
}) => {
const { prisma } = await import("@calcom/prisma");
const { BookingRepository } = await import("@calcom/lib/server/repository/booking");
const { FilterHostsService } = await import("@calcom/lib/bookings/filterHostsBySameRoundRobinHost");
const fixedHosts = normalizedHosts.filter(isFixedHost);
const roundRobinHosts = normalizedHosts.filter(isRoundRobinHost);
const bookingRepo = new BookingRepository(prisma);
const filterHostsService = new FilterHostsService({ bookingRepo });
// If it is rerouting, we should not force reschedule with same host.
const hostsAfterRescheduleWithSameRoundRobinHost = applyFilterWithFallback(
roundRobinHosts,
await filterHostsBySameRoundRobinHost({
hosts: roundRobinHosts,
rescheduleUid,
rescheduleWithSameRoundRobinHost: eventType.rescheduleWithSameRoundRobinHost,
routedTeamMemberIds,
})
);
const service = new QualifiedHostsService({
prisma,
bookingRepo,
filterHostsService,
});
if (hostsAfterRescheduleWithSameRoundRobinHost.length === 1) {
return {
qualifiedRRHosts: hostsAfterRescheduleWithSameRoundRobinHost,
fixedHosts,
};
}
const hostsAfterSegmentMatching = applyFilterWithFallback(
hostsAfterRescheduleWithSameRoundRobinHost,
(await findMatchingHostsWithEventSegment({
eventType,
hosts: hostsAfterRescheduleWithSameRoundRobinHost,
})) as typeof hostsAfterRescheduleWithSameRoundRobinHost
);
if (hostsAfterSegmentMatching.length === 1) {
return {
qualifiedRRHosts: hostsAfterSegmentMatching,
fixedHosts,
};
}
//if segment matching doesn't return any hosts we fall back to all round robin hosts
const officalRRHosts = hostsAfterSegmentMatching.length
? hostsAfterSegmentMatching
: hostsAfterRescheduleWithSameRoundRobinHost;
const hostsAfterContactOwnerMatching = applyFilterWithFallback(
officalRRHosts,
officalRRHosts.filter((host) => host.user.email === contactOwnerEmail)
);
const hostsAfterRoutedTeamMemberIdsMatching = applyFilterWithFallback(
officalRRHosts,
officalRRHosts.filter((host) => routedTeamMemberIds.includes(host.user.id))
);
if (hostsAfterRoutedTeamMemberIdsMatching.length === 1) {
if (hostsAfterContactOwnerMatching.length === 1) {
return {
qualifiedRRHosts: hostsAfterContactOwnerMatching,
allFallbackRRHosts: getFallBackWithContactOwner(
hostsAfterRoutedTeamMemberIdsMatching,
hostsAfterContactOwnerMatching[0]
),
fixedHosts,
};
}
return {
qualifiedRRHosts: hostsAfterRoutedTeamMemberIdsMatching,
fixedHosts,
};
}
const hostsAfterFairnessMatching = applyFilterWithFallback(
hostsAfterRoutedTeamMemberIdsMatching,
await filterHostsByLeadThreshold({
eventType,
hosts: hostsAfterRoutedTeamMemberIdsMatching,
maxLeadThreshold: eventType.maxLeadThreshold,
routingFormResponse,
})
);
if (hostsAfterContactOwnerMatching.length === 1) {
return {
qualifiedRRHosts: hostsAfterContactOwnerMatching,
allFallbackRRHosts: getFallBackWithContactOwner(
hostsAfterFairnessMatching,
hostsAfterContactOwnerMatching[0]
),
fixedHosts,
};
}
return {
qualifiedRRHosts: hostsAfterFairnessMatching,
// only if fairness filtering is active
allFallbackRRHosts:
hostsAfterFairnessMatching.length !== hostsAfterRoutedTeamMemberIdsMatching.length
? hostsAfterRoutedTeamMemberIdsMatching
: undefined,
fixedHosts,
};
return service.findQualifiedHostsWithDelegationCredentials({
eventType,
rescheduleUid,
routedTeamMemberIds,
contactOwnerEmail,
routingFormResponse,
});
};
export const findQualifiedHostsWithDelegationCredentials = withReporting(
@@ -5,20 +5,20 @@ import { DI_TOKENS } from "@calcom/lib/di/tokens";
import { prismaModule } from "@calcom/prisma/prisma.module";
import type { AvailableSlotsService } from "@calcom/trpc/server/routers/viewer/slots/util";
import { availableSlotsModule } from "../modules/available-slots";
import { bookingRepositoryModule } from "../modules/booking";
import { busyTimesModule } from "../modules/busy-times";
import { cacheModule } from "../modules/cache";
import { checkBookingLimitsModule } from "../modules/check-booking-limits";
import { eventTypeRepositoryModule } from "../modules/eventType";
import { featuresRepositoryModule } from "../modules/features";
import { getUserAvailabilityModule } from "../modules/get-user-availability";
import { oooRepositoryModule } from "../modules/ooo";
import { routingFormResponseRepositoryModule } from "../modules/routingFormResponse";
import { scheduleRepositoryModule } from "../modules/schedule";
import { selectedSlotsRepositoryModule } from "../modules/selectedSlots";
import { teamRepositoryModule } from "../modules/team";
import { userRepositoryModule } from "../modules/user";
import { availableSlotsModule } from "../modules/AvailableSlots";
import { bookingRepositoryModule } from "../modules/Booking";
import { busyTimesModule } from "../modules/BusyTimes";
import { cacheModule } from "../modules/Cache";
import { checkBookingLimitsModule } from "../modules/CheckBookingLimits";
import { eventTypeRepositoryModule } from "../modules/EventType";
import { featuresRepositoryModule } from "../modules/Features";
import { getUserAvailabilityModule } from "../modules/GetUserAvailability";
import { oooRepositoryModule } from "../modules/Ooo";
import { routingFormResponseRepositoryModule } from "../modules/RoutingFormResponse";
import { scheduleRepositoryModule } from "../modules/Schedule";
import { selectedSlotsRepositoryModule } from "../modules/SelectedSlots";
import { teamRepositoryModule } from "../modules/Team";
import { userRepositoryModule } from "../modules/User";
const container = createContainer();
container.load(DI_TOKENS.REDIS_CLIENT, redisModule);
@@ -5,9 +5,9 @@ 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";
import { bookingRepositoryModule } from "../modules/Booking";
import { checkBookingAndDurationLimitsModule } from "../modules/CheckBookingAndDurationLimits";
import { checkBookingLimitsModule } from "../modules/CheckBookingLimits";
const container = createContainer();
container.load(DI_TOKENS.PRISMA_MODULE, prismaModule);
@@ -4,8 +4,8 @@ import { DI_TOKENS } from "@calcom/lib/di/tokens";
import { prismaModule } from "@calcom/prisma/prisma.module";
import type { BusyTimesService } from "../../getBusyTimes";
import { bookingRepositoryModule } from "../modules/booking";
import { busyTimesModule } from "../modules/busy-times";
import { bookingRepositoryModule } from "../modules/Booking";
import { busyTimesModule } from "../modules/BusyTimes";
const container = createContainer();
container.load(DI_TOKENS.PRISMA_MODULE, prismaModule);
@@ -4,8 +4,8 @@ import { DI_TOKENS } from "@calcom/lib/di/tokens";
import { prismaModule } from "@calcom/prisma/prisma.module";
import type { CacheService } from "../../../features/calendar-cache/lib/getShouldServeCache";
import { cacheModule } from "../modules/cache";
import { featuresRepositoryModule } from "../modules/features";
import { cacheModule } from "../modules/Cache";
import { featuresRepositoryModule } from "../modules/Features";
const container = createContainer();
container.load(DI_TOKENS.PRISMA_MODULE, prismaModule);
+17
View File
@@ -0,0 +1,17 @@
import { createContainer } from "@evyweb/ioctopus";
import { DI_TOKENS } from "@calcom/lib/di/tokens";
import { prismaModule } from "@calcom/prisma/prisma.module";
import type { FilterHostsService } from "../../bookings/filterHostsBySameRoundRobinHost";
import { bookingRepositoryModule } from "../modules/Booking";
import { filterHostsModule } from "../modules/FilterHosts";
const container = createContainer();
container.load(DI_TOKENS.PRISMA_MODULE, prismaModule);
container.load(DI_TOKENS.BOOKING_REPOSITORY_MODULE, bookingRepositoryModule);
container.load(DI_TOKENS.FILTER_HOSTS_SERVICE_MODULE, filterHostsModule);
export function getFilterHostsService() {
return container.get<FilterHostsService>(DI_TOKENS.FILTER_HOSTS_SERVICE);
}
@@ -5,11 +5,11 @@ import { DI_TOKENS } from "@calcom/lib/di/tokens";
import { prismaModule } from "@calcom/prisma/prisma.module";
import type { UserAvailabilityService } from "../../getUserAvailability";
import { bookingRepositoryModule } from "../modules/booking";
import { busyTimesModule } from "../modules/busy-times";
import { eventTypeRepositoryModule } from "../modules/eventType";
import { getUserAvailabilityModule } from "../modules/get-user-availability";
import { oooRepositoryModule } from "../modules/ooo";
import { bookingRepositoryModule } from "../modules/Booking";
import { busyTimesModule } from "../modules/BusyTimes";
import { eventTypeRepositoryModule } from "../modules/EventType";
import { getUserAvailabilityModule } from "../modules/GetUserAvailability";
import { oooRepositoryModule } from "../modules/Ooo";
const container = createContainer();
container.load(DI_TOKENS.PRISMA_MODULE, prismaModule);
@@ -9,7 +9,7 @@ import type {
import type { InsightsBookingService } from "@calcom/lib/server/service/InsightsBookingDIService";
import { prismaModule } from "@calcom/prisma/prisma.module";
import { insightsBookingModule } from "../modules/insights-booking";
import { insightsBookingModule } from "../modules/InsightsBooking";
export function getInsightsBookingService({
options,
@@ -9,7 +9,7 @@ import type {
import type { InsightsRoutingService } from "@calcom/lib/server/service/insightsRoutingDI";
import { prismaModule } from "@calcom/prisma/prisma.module";
import { insightsRoutingModule } from "../modules/insights-routing";
import { insightsRoutingModule } from "../modules/InsightsRouting";
export function getInsightsRoutingService({
options,
@@ -0,0 +1,19 @@
import { createContainer } from "@evyweb/ioctopus";
import { DI_TOKENS } from "@calcom/lib/di/tokens";
import { prismaModule } from "@calcom/prisma/prisma.module";
import type { QualifiedHostsService } from "../../bookings/findQualifiedHostsWithDelegationCredentials";
import { bookingRepositoryModule } from "../modules/Booking";
import { filterHostsModule } from "../modules/FilterHosts";
import { qualifiedHostsModule } from "../modules/QualifiedHosts";
const container = createContainer();
container.load(DI_TOKENS.PRISMA_MODULE, prismaModule);
container.load(DI_TOKENS.BOOKING_REPOSITORY_MODULE, bookingRepositoryModule);
container.load(DI_TOKENS.FILTER_HOSTS_SERVICE_MODULE, filterHostsModule);
container.load(DI_TOKENS.QUALIFIED_HOSTS_SERVICE_MODULE, qualifiedHostsModule);
export function getQualifiedHostsService() {
return container.get<QualifiedHostsService>(DI_TOKENS.QUALIFIED_HOSTS_SERVICE);
}
+10
View File
@@ -0,0 +1,10 @@
import { createModule } from "@evyweb/ioctopus";
import type { IFilterHostsService } from "../../bookings/filterHostsBySameRoundRobinHost";
import { FilterHostsService } from "../../bookings/filterHostsBySameRoundRobinHost";
import { DI_TOKENS } from "../tokens";
export const filterHostsModule = createModule();
filterHostsModule.bind(DI_TOKENS.FILTER_HOSTS_SERVICE).toClass(FilterHostsService, {
bookingRepo: DI_TOKENS.BOOKING_REPOSITORY,
} satisfies Record<keyof IFilterHostsService, symbol>);
+12
View File
@@ -0,0 +1,12 @@
import { createModule } from "@evyweb/ioctopus";
import type { IQualifiedHostsService } from "../../bookings/findQualifiedHostsWithDelegationCredentials";
import { QualifiedHostsService } from "../../bookings/findQualifiedHostsWithDelegationCredentials";
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<keyof IQualifiedHostsService, symbol>);
+4
View File
@@ -37,4 +37,8 @@ export const DI_TOKENS = {
GET_USER_AVAILABILITY_SERVICE_MODULE: Symbol("GetUserAvailabilityModule"),
BUSY_TIMES_SERVICE: Symbol("BusyTimesService"),
BUSY_TIMES_SERVICE_MODULE: Symbol("BusyTimesServiceModule"),
QUALIFIED_HOSTS_SERVICE: Symbol("QualifiedHostsService"),
QUALIFIED_HOSTS_SERVICE_MODULE: Symbol("QualifiedHostsServiceModule"),
FILTER_HOSTS_SERVICE: Symbol("FilterHostsService"),
FILTER_HOSTS_SERVICE_MODULE: Symbol("FilterHostsServiceModule"),
};
+1 -1
View File
@@ -2,7 +2,7 @@ import { describe, expect, it } from "vitest";
import dayjs from "@calcom/dayjs";
import { getBusyTimesService } from "./di/containers/busy-times";
import { getBusyTimesService } from "./di/containers/BusyTimes";
const startOfTomorrow = dayjs().add(1, "day").startOf("day");
const tomorrowDate = startOfTomorrow.format("YYYY-MM-DD");
+1 -1
View File
@@ -36,7 +36,7 @@ import { EventTypeMetaDataSchema } from "@calcom/prisma/zod-utils";
import type { EventBusyDetails, IntervalLimitUnit } from "@calcom/types/Calendar";
import type { TimeRange } from "@calcom/types/schedule";
import { getBusyTimesService } from "./di/containers/busy-times";
import { getBusyTimesService } from "./di/containers/BusyTimes";
import { getPeriodStartDatesBetween as getPeriodStartDatesBetweenUtil } from "./intervalLimits/utils/getPeriodStartDatesBetween";
import { withReporting } from "./sentryWrapper";
@@ -1,7 +1,7 @@
import type { Dayjs } from "@calcom/dayjs";
import dayjs from "@calcom/dayjs";
import { getCheckBookingLimitsService } from "@calcom/lib/di/containers/booking-limits";
import { getBusyTimesService } from "@calcom/lib/di/containers/busy-times";
import { getCheckBookingLimitsService } from "@calcom/lib/di/containers/BookingLimits";
import { getBusyTimesService } from "@calcom/lib/di/containers/BusyTimes";
import type { EventType } from "@calcom/lib/getUserAvailability";
import { getPeriodStartDatesBetween } from "@calcom/lib/intervalLimits/utils/getPeriodStartDatesBetween";
import { withReporting } from "@calcom/lib/sentryWrapper";
+19
View File
@@ -935,4 +935,23 @@ export class BookingRepository {
}
return totalBookingTime.totalMinutes ?? 0;
}
async findOriginalRescheduledBookingUserId({ rescheduleUid }: { rescheduleUid: string }) {
return await this.prismaClient.booking.findFirst({
where: {
uid: rescheduleUid,
status: {
in: [BookingStatus.ACCEPTED, BookingStatus.CANCELLED, BookingStatus.PENDING],
},
},
select: {
userId: true,
attendees: {
select: {
email: true,
},
},
},
});
}
}
@@ -1,4 +1,4 @@
import { getUserAvailabilityService } from "@calcom/lib/di/containers/get-user-availability";
import { getUserAvailabilityService } from "@calcom/lib/di/containers/GetUserAvailability";
import type { TrpcSessionUser } from "../../../types";
import type { TUserInputSchema } from "./user.schema";
@@ -11,7 +11,7 @@ type UserOptions = {
};
export const userHandler = async ({ input }: UserOptions) => {
const userAvailabilityService = getUserAvailabilityService()
const userAvailabilityService = getUserAvailabilityService();
return userAvailabilityService.getUserAvailability(
{ returnDateOverrides: true, bypassBusyCalendarTimes: false, ...input },
undefined
@@ -1,6 +1,6 @@
import type { IncomingMessage } from "http";
import { getAvailableSlotsService } from "@calcom/lib/di/containers/available-slots";
import { getAvailableSlotsService } from "@calcom/lib/di/containers/AvailableSlots";
import type { TGetTeamScheduleInputSchema } from "./getTeamSchedule.schema";
@@ -1,4 +1,4 @@
import { getAvailableSlotsService } from "@calcom/lib/di/containers/available-slots";
import { getAvailableSlotsService } from "@calcom/lib/di/containers/AvailableSlots";
import type { GetScheduleOptions } from "./types";
@@ -9,13 +9,13 @@ import { checkForConflicts } from "@calcom/features/bookings/lib/conflictChecker
import { isEventTypeLoggingEnabled } from "@calcom/features/bookings/lib/isEventTypeLoggingEnabled";
import type { CacheService } from "@calcom/features/calendar-cache/lib/getShouldServeCache";
import type { IRedisService } from "@calcom/features/redis/IRedisService";
import { findQualifiedHostsWithDelegationCredentials } 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/busy-times";
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 {
@@ -1027,9 +1027,9 @@ export class AvailableSlotsService {
});
}
// TODO: DI findQualifiedHostsWithDelegationCredentials
const qualifiedHostsService = getQualifiedHostsService();
const { qualifiedRRHosts, allFallbackRRHosts, fixedHosts } =
await findQualifiedHostsWithDelegationCredentials({
await qualifiedHostsService.findQualifiedHostsWithDelegationCredentials({
eventType,
rescheduleUid: input.rescheduleUid ?? null,
routedTeamMemberIds,
@@ -1,5 +1,5 @@
import { enrichUserWithDelegationCredentialsIncludeServiceAccountKey } from "@calcom/lib/delegationCredential/server";
import { getUserAvailabilityService } from "@calcom/lib/di/containers/get-user-availability";
import { getUserAvailabilityService } from "@calcom/lib/di/containers/GetUserAvailability";
import { isTeamMember } from "@calcom/lib/server/queries/teams";
import { MembershipRepository } from "@calcom/lib/server/repository/membership";
import type { TrpcSessionUser } from "@calcom/trpc/server/types";
@@ -16,7 +16,7 @@ type GetMemberAvailabilityOptions = {
};
export const getMemberAvailabilityHandler = async ({ ctx, input }: GetMemberAvailabilityOptions) => {
const userAvailabilityService = getUserAvailabilityService()
const userAvailabilityService = getUserAvailabilityService();
const team = await isTeamMember(ctx.user?.id, input.teamId);
if (!team) throw new TRPCError({ code: "UNAUTHORIZED" });
+5 -5
View File
@@ -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.294"
"@calcom/platform-libraries": "npm:@calcom/platform-libraries@0.0.298"
"@calcom/platform-types": "*"
"@calcom/platform-utils": "*"
"@calcom/prisma": "*"
@@ -3558,13 +3558,13 @@ __metadata:
languageName: unknown
linkType: soft
"@calcom/platform-libraries@npm:@calcom/platform-libraries@0.0.294":
version: 0.0.294
resolution: "@calcom/platform-libraries@npm:0.0.294"
"@calcom/platform-libraries@npm:@calcom/platform-libraries@0.0.298":
version: 0.0.298
resolution: "@calcom/platform-libraries@npm:0.0.298"
dependencies:
"@calcom/features": "*"
"@calcom/lib": "*"
checksum: ce7beda30e629e87d560386bf1f117280ec3466ba7dfa2089de79c2d03c46d24ee3a5388a63974ba084b6624e863c2feb5e02f42ef08c72daefc7c51e771d5f8
checksum: 63401c90d15ce05a17905f68eb36e655c6368b430ee8c28df371c2b91d5033cab846e96406e28d2c716e8e361eefaf96caf2c3a26fa63bc239ec97632b0b7613
languageName: node
linkType: hard