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 Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> morgan@cal.com <morgan@cal.com> Anik Dhabal Babu Benny Joo Sahitya Chandra Carina Wollendorfer CarinaWolli mintlify[bot] <109931778+mintlify[bot]@users.noreply.github.com> Ayush Kumar Syed Ali Shahbaz Lauris Skraucis supalarry 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