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:
58 files changed
+433
-285
No files matched your search
@@ -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);
|
||||
}
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -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>);
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -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>);
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -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" });
|
||||
|
||||
|
||||
Reference in new issue
Block a user