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:
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
@@ -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";
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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: [],
|
||||
}),
|
||||
}),
|
||||
}));
|
||||
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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";
|
||||
|
||||
|
||||
@@ -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(
|
||||
|
||||
+14
-14
@@ -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);
|
||||
+3
-3
@@ -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);
|
||||
@@ -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
-5
@@ -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);
|
||||
+1
-1
@@ -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,
|
||||
+1
-1
@@ -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);
|
||||
}
|
||||
@@ -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>);
|
||||
@@ -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>);
|
||||
@@ -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"),
|
||||
};
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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" });
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user