Skeleton for Booking flow refactor using Factory and services (#23277)
This commit is contained in:
@@ -4,10 +4,10 @@ import { getServerSession } from "@calcom/features/auth/lib/getServerSession";
|
||||
import handleNewBooking from "@calcom/features/bookings/lib/handleNewBooking";
|
||||
import { checkRateLimitAndThrowError } from "@calcom/lib/checkRateLimitAndThrowError";
|
||||
import getIP from "@calcom/lib/getIP";
|
||||
import { piiHasher } from "@calcom/lib/server/PiiHasher";
|
||||
import { checkCfTurnstileToken } from "@calcom/lib/server/checkCfTurnstileToken";
|
||||
import { defaultResponder } from "@calcom/lib/server/defaultResponder";
|
||||
import { CreationSource } from "@calcom/prisma/enums";
|
||||
import { piiHasher } from "@calcom/lib/server/PiiHasher";
|
||||
|
||||
async function handler(req: NextApiRequest & { userId?: number }) {
|
||||
const userIp = getIP(req);
|
||||
@@ -37,7 +37,24 @@ async function handler(req: NextApiRequest & { userId?: number }) {
|
||||
hostname: req.headers.host || "",
|
||||
forcedSlug: req.headers["x-cal-force-slug"] as string | undefined,
|
||||
});
|
||||
// const booking = await createBookingThroughFactory();
|
||||
return booking;
|
||||
|
||||
// To be added in the follow-up PR
|
||||
// async function createBookingThroughFactory() {
|
||||
// console.log("Creating booking through factory");
|
||||
// const regularBookingService = getRegularBookingService();
|
||||
|
||||
// const booking = await regularBookingService.createBooking({
|
||||
// bookingData: req.body,
|
||||
// bookingMeta: {
|
||||
// userId: session?.user?.id || -1,
|
||||
// hostname: req.headers.host || "",
|
||||
// forcedSlug: req.headers["x-cal-force-slug"] as string | undefined,
|
||||
// },
|
||||
// });
|
||||
// return booking;
|
||||
// }
|
||||
}
|
||||
|
||||
export default defaultResponder(handler, "/api/book/event");
|
||||
|
||||
@@ -4,9 +4,9 @@ import { getServerSession } from "@calcom/features/auth/lib/getServerSession";
|
||||
import handleInstantMeeting from "@calcom/features/instant-meeting/handleInstantMeeting";
|
||||
import { checkRateLimitAndThrowError } from "@calcom/lib/checkRateLimitAndThrowError";
|
||||
import getIP from "@calcom/lib/getIP";
|
||||
import { piiHasher } from "@calcom/lib/server/PiiHasher";
|
||||
import { defaultResponder } from "@calcom/lib/server/defaultResponder";
|
||||
import { CreationSource } from "@calcom/prisma/enums";
|
||||
import { piiHasher } from "@calcom/lib/server/PiiHasher";
|
||||
|
||||
async function handler(req: NextApiRequest & { userId?: number }) {
|
||||
const userIp = getIP(req);
|
||||
|
||||
@@ -5,9 +5,9 @@ import { handleNewRecurringBooking } from "@calcom/features/bookings/lib/handleN
|
||||
import type { BookingResponse } from "@calcom/features/bookings/types";
|
||||
import { checkRateLimitAndThrowError } from "@calcom/lib/checkRateLimitAndThrowError";
|
||||
import getIP from "@calcom/lib/getIP";
|
||||
import { piiHasher } from "@calcom/lib/server/PiiHasher";
|
||||
import { checkCfTurnstileToken } from "@calcom/lib/server/checkCfTurnstileToken";
|
||||
import { defaultResponder } from "@calcom/lib/server/defaultResponder";
|
||||
import { piiHasher } from "@calcom/lib/server/PiiHasher";
|
||||
|
||||
// @TODO: Didn't look at the contents of this function in order to not break old booking page.
|
||||
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
import { createContainer } from "@evyweb/ioctopus";
|
||||
|
||||
import { DI_TOKENS } from "@calcom/lib/di/tokens";
|
||||
|
||||
import type { InstantBookingCreateService } from "../modules/InstantBookingCreateServiceModule";
|
||||
import { instantBookingCreateServiceModule } from "../modules/InstantBookingCreateServiceModule";
|
||||
|
||||
const container = createContainer();
|
||||
container.load(DI_TOKENS.INSTANT_BOOKING_CREATE_SERVICE_MODULE, instantBookingCreateServiceModule);
|
||||
|
||||
export function getInstantBookingCreateService(): InstantBookingCreateService {
|
||||
return container.get<InstantBookingCreateService>(DI_TOKENS.INSTANT_BOOKING_CREATE_SERVICE);
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
import { createContainer } from "@evyweb/ioctopus";
|
||||
|
||||
import { bookingRepositoryModule } from "@calcom/lib/di/modules/Booking";
|
||||
import { cacheModule } from "@calcom/lib/di/modules/Cache";
|
||||
import { checkBookingAndDurationLimitsModule } from "@calcom/lib/di/modules/CheckBookingAndDurationLimits";
|
||||
import { checkBookingLimitsModule } from "@calcom/lib/di/modules/CheckBookingLimits";
|
||||
import { featuresRepositoryModule } from "@calcom/lib/di/modules/Features";
|
||||
import { DI_TOKENS } from "@calcom/lib/di/tokens";
|
||||
import { prismaModule } from "@calcom/prisma/prisma.module";
|
||||
|
||||
import type { RecurringBookingService } from "../modules/RecurringBookingServiceModule";
|
||||
import { recurringBookingServiceModule } from "../modules/RecurringBookingServiceModule";
|
||||
|
||||
const container = createContainer();
|
||||
container.load(DI_TOKENS.PRISMA_MODULE, prismaModule);
|
||||
container.load(DI_TOKENS.BOOKING_REPOSITORY_MODULE, bookingRepositoryModule);
|
||||
container.load(DI_TOKENS.CACHE_SERVICE_MODULE, cacheModule);
|
||||
container.load(DI_TOKENS.CHECK_BOOKING_LIMITS_SERVICE_MODULE, checkBookingLimitsModule);
|
||||
container.load(DI_TOKENS.FEATURES_REPOSITORY_MODULE, featuresRepositoryModule);
|
||||
container.load(
|
||||
DI_TOKENS.CHECK_BOOKING_AND_DURATION_LIMITS_SERVICE_MODULE,
|
||||
checkBookingAndDurationLimitsModule
|
||||
);
|
||||
container.load(DI_TOKENS.RECURRING_BOOKING_SERVICE_MODULE, recurringBookingServiceModule);
|
||||
|
||||
export function getRecurringBookingService(): RecurringBookingService {
|
||||
return container.get<RecurringBookingService>(DI_TOKENS.RECURRING_BOOKING_SERVICE);
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
import { createContainer } from "@evyweb/ioctopus";
|
||||
|
||||
import { DI_TOKENS } from "@calcom/lib/di/tokens";
|
||||
|
||||
import type { RegularBookingService } from "../modules/RegularBookingServiceModule";
|
||||
import {
|
||||
regularBookingServiceModule,
|
||||
loadModuleDeps,
|
||||
moduleToken,
|
||||
} from "../modules/RegularBookingServiceModule";
|
||||
|
||||
const regularBookingServiceContainer = createContainer();
|
||||
|
||||
regularBookingServiceContainer.load(DI_TOKENS.REGULAR_BOOKING_SERVICE_MODULE, regularBookingServiceModule);
|
||||
|
||||
export function getRegularBookingService(): RegularBookingService {
|
||||
loadModuleDeps(regularBookingServiceContainer);
|
||||
|
||||
return regularBookingServiceContainer.get<RegularBookingService>(moduleToken);
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import { createModule } from "@evyweb/ioctopus";
|
||||
|
||||
import { InstantBookingCreateService } from "@calcom/features/instant-meeting/handleInstantMeeting";
|
||||
import { DI_TOKENS } from "@calcom/lib/di/tokens";
|
||||
|
||||
export const instantBookingCreateServiceModule = createModule();
|
||||
|
||||
instantBookingCreateServiceModule
|
||||
.bind(DI_TOKENS.INSTANT_BOOKING_CREATE_SERVICE)
|
||||
.toClass(InstantBookingCreateService);
|
||||
export type { InstantBookingCreateService };
|
||||
@@ -0,0 +1,13 @@
|
||||
import { createModule } from "@evyweb/ioctopus";
|
||||
|
||||
import { DI_TOKENS } from "@calcom/lib/di/tokens";
|
||||
|
||||
import { RecurringBookingService } from "../../handleNewRecurringBooking";
|
||||
|
||||
export const recurringBookingServiceModule = createModule();
|
||||
|
||||
recurringBookingServiceModule.bind(DI_TOKENS.RECURRING_BOOKING_SERVICE).toClass(RecurringBookingService, {
|
||||
regularBookingService: DI_TOKENS.REGULAR_BOOKING_SERVICE,
|
||||
});
|
||||
|
||||
export type { RecurringBookingService };
|
||||
@@ -0,0 +1,39 @@
|
||||
import type { Container } from "@evyweb/ioctopus";
|
||||
import { createModule } from "@evyweb/ioctopus";
|
||||
|
||||
import { bookingRepositoryModule } from "@calcom/lib/di/modules/Booking";
|
||||
import { cacheModule } from "@calcom/lib/di/modules/Cache";
|
||||
import { checkBookingAndDurationLimitsModule } from "@calcom/lib/di/modules/CheckBookingAndDurationLimits";
|
||||
import { checkBookingLimitsModule } from "@calcom/lib/di/modules/CheckBookingLimits";
|
||||
import { featuresRepositoryModule } from "@calcom/lib/di/modules/Features";
|
||||
import { DI_TOKENS } from "@calcom/lib/di/tokens";
|
||||
import { prismaModule } from "@calcom/prisma/prisma.module";
|
||||
|
||||
import { RegularBookingService } from "../../handleNewBooking";
|
||||
|
||||
export const regularBookingServiceModule = createModule();
|
||||
const moduleToken = DI_TOKENS.REGULAR_BOOKING_SERVICE;
|
||||
regularBookingServiceModule.bind(moduleToken).toClass(RegularBookingService, {
|
||||
cacheService: DI_TOKENS.CACHE_SERVICE,
|
||||
checkBookingAndDurationLimitsService: DI_TOKENS.CHECK_BOOKING_AND_DURATION_LIMITS_SERVICE,
|
||||
prismaClient: DI_TOKENS.PRISMA_CLIENT,
|
||||
bookingRepository: DI_TOKENS.BOOKING_REPOSITORY,
|
||||
featuresRepository: DI_TOKENS.FEATURES_REPOSITORY,
|
||||
checkBookingLimitsService: DI_TOKENS.CHECK_BOOKING_LIMITS_SERVICE,
|
||||
});
|
||||
|
||||
// Load the dependencies defined for the module above
|
||||
function loadModuleDeps(container: Container) {
|
||||
container.load(DI_TOKENS.CACHE_SERVICE_MODULE, cacheModule);
|
||||
container.load(
|
||||
DI_TOKENS.CHECK_BOOKING_AND_DURATION_LIMITS_SERVICE_MODULE,
|
||||
checkBookingAndDurationLimitsModule
|
||||
);
|
||||
container.load(DI_TOKENS.PRISMA_MODULE, prismaModule);
|
||||
container.load(DI_TOKENS.BOOKING_REPOSITORY_MODULE, bookingRepositoryModule);
|
||||
container.load(DI_TOKENS.FEATURES_REPOSITORY_MODULE, featuresRepositoryModule);
|
||||
container.load(DI_TOKENS.CHECK_BOOKING_LIMITS_SERVICE_MODULE, checkBookingLimitsModule);
|
||||
}
|
||||
|
||||
export { loadModuleDeps, moduleToken };
|
||||
export type { RegularBookingService };
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
/**
|
||||
* Domain types for BookingCreateService
|
||||
* These types are framework-agnostic and contain only the data required for booking operations
|
||||
*/
|
||||
import type { z } from "zod";
|
||||
|
||||
import type getBookingDataSchema from "@calcom/features/bookings/lib/getBookingDataSchema";
|
||||
import type getBookingDataSchemaForApi from "@calcom/features/bookings/lib/getBookingDataSchemaForApi";
|
||||
import type { BookingCreateBody as BaseCreateBookingData } from "@calcom/prisma/zod/custom/booking";
|
||||
import type { extendedBookingCreateBody } from "@calcom/prisma/zod/custom/booking";
|
||||
|
||||
export type ExtendedBookingCreateData = z.input<typeof extendedBookingCreateBody>;
|
||||
export type BookingDataSchemaGetter = typeof getBookingDataSchema | typeof getBookingDataSchemaForApi;
|
||||
|
||||
export type CreateRegularBookingData = BaseCreateBookingData;
|
||||
|
||||
export type CreateInstantBookingData = BaseCreateBookingData;
|
||||
|
||||
export type CreateRecurringBookingData = (BaseCreateBookingData & {
|
||||
schedulingType?: SchedulingType;
|
||||
})[];
|
||||
|
||||
export type PlatformParams = {
|
||||
platformClientId?: string;
|
||||
platformCancelUrl?: string;
|
||||
platformBookingUrl?: string;
|
||||
platformRescheduleUrl?: string;
|
||||
platformBookingLocation?: string;
|
||||
areCalendarEventsEnabled?: boolean;
|
||||
};
|
||||
|
||||
export type CreateBookingMeta = {
|
||||
userId?: number;
|
||||
// These used to come from headers but now we're passing them as params
|
||||
hostname?: string;
|
||||
forcedSlug?: string;
|
||||
noEmail?: boolean;
|
||||
} & PlatformParams;
|
||||
|
||||
export type BookingHandlerInput = {
|
||||
bookingData: CreateRegularBookingData;
|
||||
} & CreateBookingMeta;
|
||||
|
||||
export type CreateInstantBookingResponse = {
|
||||
message: string;
|
||||
meetingTokenId: number;
|
||||
bookingId: number;
|
||||
bookingUid: string;
|
||||
expires: Date;
|
||||
userId: number | null;
|
||||
};
|
||||
@@ -25,14 +25,19 @@ import {
|
||||
} from "@calcom/emails";
|
||||
import getICalUID from "@calcom/emails/lib/getICalUID";
|
||||
import { CalendarEventBuilder } from "@calcom/features/CalendarEventBuilder";
|
||||
import type { BookingDataSchemaGetter } from "@calcom/features/bookings/lib/dto/types";
|
||||
import type { CreateRegularBookingData, CreateBookingMeta } from "@calcom/features/bookings/lib/dto/types";
|
||||
import type { CheckBookingAndDurationLimitsService } from "@calcom/features/bookings/lib/handleNewBooking/checkBookingAndDurationLimits";
|
||||
import { handleWebhookTrigger } from "@calcom/features/bookings/lib/handleWebhookTrigger";
|
||||
import { isEventTypeLoggingEnabled } from "@calcom/features/bookings/lib/isEventTypeLoggingEnabled";
|
||||
import type { CacheService } from "@calcom/features/calendar-cache/lib/getShouldServeCache";
|
||||
import AssignmentReasonRecorder from "@calcom/features/ee/round-robin/assignmentReason/AssignmentReasonRecorder";
|
||||
import {
|
||||
allowDisablingAttendeeConfirmationEmails,
|
||||
allowDisablingHostConfirmationEmails,
|
||||
} from "@calcom/features/ee/workflows/lib/allowDisablingStandardEmails";
|
||||
import { scheduleWorkflowReminders } from "@calcom/features/ee/workflows/lib/reminders/reminderScheduler";
|
||||
import type { FeaturesRepository } from "@calcom/features/flags/features.repository";
|
||||
import { getFullName } from "@calcom/features/form-builder/utils";
|
||||
import { UsersRepository } from "@calcom/features/users/users.repository";
|
||||
import type { GetSubscriberOptions } from "@calcom/features/webhooks/lib/getWebhooks";
|
||||
@@ -64,6 +69,7 @@ import getOrgIdFromMemberOrTeamId from "@calcom/lib/getOrgIdFromMemberOrTeamId";
|
||||
import { getPaymentAppData } from "@calcom/lib/getPaymentAppData";
|
||||
import { getTeamIdFromEventType } from "@calcom/lib/getTeamIdFromEventType";
|
||||
import { HttpError } from "@calcom/lib/http-error";
|
||||
import type { CheckBookingLimitsService } from "@calcom/lib/intervalLimits/server/checkBookingLimits";
|
||||
import logger from "@calcom/lib/logger";
|
||||
import { handlePayment } from "@calcom/lib/payment/handlePayment";
|
||||
import { getPiiFreeCalendarEvent, getPiiFreeEventType } from "@calcom/lib/piiFreeData";
|
||||
@@ -73,6 +79,7 @@ import { BookingRepository } from "@calcom/lib/server/repository/booking";
|
||||
import { WorkflowRepository } from "@calcom/lib/server/repository/workflow";
|
||||
import { HashedLinkService } from "@calcom/lib/server/service/hashedLinkService";
|
||||
import { getTimeFormatStringFromUserTimeFormat } from "@calcom/lib/timeFormat";
|
||||
import type { PrismaClient } from "@calcom/prisma";
|
||||
import prisma from "@calcom/prisma";
|
||||
import type { AssignmentReasonEnum } from "@calcom/prisma/enums";
|
||||
import { BookingStatus, SchedulingType, WebhookTriggerEvents } from "@calcom/prisma/enums";
|
||||
@@ -121,6 +128,7 @@ import type { IEventTypePaymentCredentialType, Invitee, IsFixedAwareUser } from
|
||||
import { validateBookingTimeIsNotOutOfBounds } from "./handleNewBooking/validateBookingTimeIsNotOutOfBounds";
|
||||
import { validateEventLength } from "./handleNewBooking/validateEventLength";
|
||||
import handleSeats from "./handleSeats/handleSeats";
|
||||
import type { IBookingService } from "./interfaces/IBookingService";
|
||||
|
||||
const translator = short();
|
||||
const log = logger.getSubLogger({ prefix: ["[api] book:user"] });
|
||||
@@ -150,10 +158,6 @@ function getICalSequence(originalRescheduledBooking: BookingType | null) {
|
||||
return originalRescheduledBooking.iCalSequence + 1;
|
||||
}
|
||||
|
||||
type BookingDataSchemaGetter =
|
||||
| typeof getBookingDataSchema
|
||||
| typeof import("@calcom/features/bookings/lib/getBookingDataSchemaForApi").default;
|
||||
|
||||
type CreatedBooking = Booking & { appsStatus?: AppsStatus[]; paymentUid?: string; paymentId?: number };
|
||||
type ReturnTypeCreateBooking = Awaited<ReturnType<typeof createBooking>>;
|
||||
export const buildDryRunBooking = ({
|
||||
@@ -2455,3 +2459,38 @@ async function handler(
|
||||
}
|
||||
|
||||
export default handler;
|
||||
|
||||
export interface IBookingServiceDependencies {
|
||||
cacheService: CacheService;
|
||||
checkBookingAndDurationLimitsService: CheckBookingAndDurationLimitsService;
|
||||
prismaClient: PrismaClient;
|
||||
bookingRepository: BookingRepository;
|
||||
featuresRepository: FeaturesRepository;
|
||||
checkBookingLimitsService: CheckBookingLimitsService;
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes care of creating/rescheduling non-recurring, non-instant bookings. Such bookings could be TeamBooking, UserBooking, SeatedUserBooking, SeatedTeamBooking, etc.
|
||||
* We can't name it CoreBookingService because non-instant booking also creates a booking but it is entirely different from the regular booking.
|
||||
* We are open to renaming it to something more descriptive.
|
||||
*/
|
||||
export class RegularBookingService implements IBookingService {
|
||||
constructor(private readonly deps: IBookingServiceDependencies) {}
|
||||
|
||||
async createBooking(input: { bookingData: CreateRegularBookingData; bookingMeta?: CreateBookingMeta }) {
|
||||
// deps to be passed to handler in follow-up PR
|
||||
return handler({ bookingData: input.bookingData, ...input.bookingMeta });
|
||||
}
|
||||
|
||||
async rescheduleBooking(input: { bookingData: CreateRegularBookingData; bookingMeta?: CreateBookingMeta }) {
|
||||
return handler({ bookingData: input.bookingData, ...input.bookingMeta });
|
||||
}
|
||||
|
||||
async rescheduleBookingForApiV1(input: {
|
||||
bookingData: CreateRegularBookingData;
|
||||
bookingMeta?: CreateBookingMeta;
|
||||
bookingDataSchemaGetter: BookingDataSchemaGetter;
|
||||
}) {
|
||||
return handler({ bookingData: input.bookingData, ...input.bookingMeta }, input.bookingDataSchemaGetter);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,25 +1,15 @@
|
||||
import type { CreateBookingMeta, CreateRecurringBookingData } from "@calcom/features/bookings/lib/dto/types";
|
||||
import handleNewBooking from "@calcom/features/bookings/lib/handleNewBooking";
|
||||
import type { BookingResponse } from "@calcom/features/bookings/types";
|
||||
import { SchedulingType } from "@calcom/prisma/client";
|
||||
import type { AppsStatus } from "@calcom/types/Calendar";
|
||||
|
||||
export type PlatformParams = {
|
||||
platformClientId?: string;
|
||||
platformCancelUrl?: string;
|
||||
platformBookingUrl?: string;
|
||||
platformRescheduleUrl?: string;
|
||||
platformBookingLocation?: string;
|
||||
areCalendarEventsEnabled?: boolean;
|
||||
};
|
||||
import type { RegularBookingService } from "./handleNewBooking";
|
||||
import type { IBookingService } from "./interfaces/IBookingService";
|
||||
|
||||
export type BookingHandlerInput = {
|
||||
bookingData: Record<string, any>[];
|
||||
userId?: number;
|
||||
// These used to come from headers but now we're passing them as params
|
||||
hostname?: string;
|
||||
forcedSlug?: string;
|
||||
noEmail?: boolean;
|
||||
} & PlatformParams;
|
||||
bookingData: CreateRecurringBookingData;
|
||||
} & CreateBookingMeta;
|
||||
|
||||
export const handleNewRecurringBooking = async (input: BookingHandlerInput): Promise<BookingResponse[]> => {
|
||||
const data = input.bookingData;
|
||||
@@ -115,7 +105,7 @@ export const handleNewRecurringBooking = async (input: BookingHandlerInput): Pro
|
||||
|
||||
if (!thirdPartyRecurringEventId) {
|
||||
if (eachRecurringBooking.references && eachRecurringBooking.references.length > 0) {
|
||||
for (const reference of eachRecurringBooking.references!) {
|
||||
for (const reference of eachRecurringBooking.references) {
|
||||
if (reference.thirdPartyRecurringEventId) {
|
||||
thirdPartyRecurringEventId = reference.thirdPartyRecurringEventId;
|
||||
break;
|
||||
@@ -126,3 +116,32 @@ export const handleNewRecurringBooking = async (input: BookingHandlerInput): Pro
|
||||
}
|
||||
return createdBookings;
|
||||
};
|
||||
|
||||
export interface IRecurringBookingServiceDependencies {
|
||||
regularBookingService: RegularBookingService;
|
||||
}
|
||||
|
||||
/**
|
||||
* Recurring Booking Service takes care of creating/rescheduling recurring bookings.
|
||||
*/
|
||||
export class RecurringBookingService implements IBookingService {
|
||||
constructor(private readonly deps: IRecurringBookingServiceDependencies) {}
|
||||
|
||||
async createBooking(input: {
|
||||
bookingData: CreateRecurringBookingData;
|
||||
bookingMeta?: CreateBookingMeta;
|
||||
}): Promise<BookingResponse[]> {
|
||||
const handlerInput = { bookingData: input.bookingData, ...(input.bookingMeta || {}) };
|
||||
// FOLLOW-UP: Pass on dependencies to the handler
|
||||
return handleNewRecurringBooking(handlerInput);
|
||||
}
|
||||
|
||||
async rescheduleBooking(input: {
|
||||
bookingData: CreateRecurringBookingData;
|
||||
bookingMeta?: CreateBookingMeta;
|
||||
}): Promise<BookingResponse[]> {
|
||||
const handlerInput = { bookingData: input.bookingData, ...(input.bookingMeta || {}) };
|
||||
// FOLLOW-UP: Pass on dependencies to the handler
|
||||
return handleNewRecurringBooking(handlerInput);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
// Defines an interface that could be used by any type of Booking Service
|
||||
// "Any" types are used because this inteface is used by RecurringBookingService which accepts an array of bookingData and RegularBookingService which accepts a single bookingData
|
||||
// So, this interface enforces just the methods that must be present but not their parameters and return types
|
||||
export interface IBookingCreateService {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
createBooking: (...args: any[]) => Promise<any>;
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
// Defines an interface that could be used by any type of Booking Service
|
||||
// "Any" types are used because this inteface is used by RecurringBookingService which accepts an array of bookingData and RegularBookingService which accepts a single bookingData
|
||||
// So, this interface enforces just the methods that must be present but not their parameters and return types
|
||||
export interface IBookingService {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
createBooking: (...args: any[]) => Promise<any>;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
rescheduleBooking: (...args: any[]) => Promise<any>;
|
||||
}
|
||||
@@ -5,11 +5,16 @@ import short from "short-uuid";
|
||||
import { v5 as uuidv5 } from "uuid";
|
||||
|
||||
import dayjs from "@calcom/dayjs";
|
||||
import type {
|
||||
CreateInstantBookingData,
|
||||
CreateInstantBookingResponse,
|
||||
} from "@calcom/features/bookings/lib/dto/types";
|
||||
import getBookingDataSchema from "@calcom/features/bookings/lib/getBookingDataSchema";
|
||||
import { getBookingFieldsWithSystemFields } from "@calcom/features/bookings/lib/getBookingFields";
|
||||
import { getBookingData } from "@calcom/features/bookings/lib/handleNewBooking/getBookingData";
|
||||
import { getCustomInputsResponses } from "@calcom/features/bookings/lib/handleNewBooking/getCustomInputsResponses";
|
||||
import { getEventTypesFromDB } from "@calcom/features/bookings/lib/handleNewBooking/getEventTypesFromDB";
|
||||
import type { IBookingCreateService } from "@calcom/features/bookings/lib/interfaces/IBookingCreateService";
|
||||
import { getFullName } from "@calcom/features/form-builder/utils";
|
||||
import { sendNotification } from "@calcom/features/notifications/sendNotification";
|
||||
import { sendGenericWebhookPayload } from "@calcom/features/webhooks/lib/sendPayload";
|
||||
@@ -151,17 +156,8 @@ const triggerBrowserNotifications = async (args: {
|
||||
await Promise.allSettled(promises);
|
||||
};
|
||||
|
||||
export type HandleInstantMeetingResponse = {
|
||||
message: string;
|
||||
meetingTokenId: number;
|
||||
bookingId: number;
|
||||
bookingUid: string;
|
||||
expires: Date;
|
||||
userId: number | null;
|
||||
};
|
||||
|
||||
async function handler(req: NextApiRequest) {
|
||||
let eventType = await getEventTypesFromDB(req.body.eventTypeId);
|
||||
async function _handler(bookingData: CreateInstantBookingData) {
|
||||
let eventType = await getEventTypesFromDB(bookingData.eventTypeId);
|
||||
const isOrgTeamEvent = !!eventType?.team && !!eventType?.team?.parentId;
|
||||
eventType = {
|
||||
...eventType,
|
||||
@@ -173,11 +169,11 @@ async function handler(req: NextApiRequest) {
|
||||
}
|
||||
|
||||
const schema = getBookingDataSchema({
|
||||
view: req.body?.rescheduleUid ? "reschedule" : "booking",
|
||||
view: bookingData?.rescheduleUid ? "reschedule" : "booking",
|
||||
bookingFields: eventType.bookingFields,
|
||||
});
|
||||
const reqBody = await getBookingData({
|
||||
reqBody: req.body,
|
||||
reqBody: bookingData,
|
||||
eventType,
|
||||
schema,
|
||||
});
|
||||
@@ -257,7 +253,7 @@ async function handler(req: NextApiRequest) {
|
||||
data: attendeesList,
|
||||
},
|
||||
},
|
||||
creationSource: req.body.creationSource,
|
||||
creationSource: bookingData.creationSource,
|
||||
};
|
||||
|
||||
const createBookingObj = {
|
||||
@@ -275,7 +271,7 @@ async function handler(req: NextApiRequest) {
|
||||
|
||||
const eventTypeWithExpiryTimeOffset = await prisma.eventType.findUniqueOrThrow({
|
||||
where: {
|
||||
id: req.body.eventTypeId,
|
||||
id: bookingData.eventTypeId,
|
||||
},
|
||||
select: {
|
||||
instantMeetingExpiryTimeOffsetInSeconds: true,
|
||||
@@ -334,7 +330,21 @@ async function handler(req: NextApiRequest) {
|
||||
bookingUid: newBooking.uid,
|
||||
expires: instantMeetingToken.expires,
|
||||
userId: newBooking.userId,
|
||||
} satisfies HandleInstantMeetingResponse;
|
||||
} satisfies CreateInstantBookingResponse;
|
||||
}
|
||||
|
||||
export default handler;
|
||||
/**
|
||||
* Instant booking service that handles instant/immediate bookings
|
||||
*/
|
||||
export class InstantBookingCreateService implements IBookingCreateService {
|
||||
async createBooking(input: {
|
||||
bookingData: CreateInstantBookingData;
|
||||
}): Promise<CreateInstantBookingResponse> {
|
||||
return _handler(input.bookingData);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Remove it in a follow-up PR
|
||||
export default async function handler(req: NextApiRequest) {
|
||||
return _handler(req.body);
|
||||
}
|
||||
|
||||
@@ -51,4 +51,10 @@ export const DI_TOKENS = {
|
||||
HOST_REPOSITORY_MODULE: Symbol("HostRepositoryModule"),
|
||||
ATTRIBUTE_REPOSITORY: Symbol("AttributeRepository"),
|
||||
ATTRIBUTE_REPOSITORY_MODULE: Symbol("AttributeRepositoryModule"),
|
||||
REGULAR_BOOKING_SERVICE: Symbol("RegularBookingService"),
|
||||
REGULAR_BOOKING_SERVICE_MODULE: Symbol("RegularBookingServiceModule"),
|
||||
RECURRING_BOOKING_SERVICE: Symbol("RecurringBookingService"),
|
||||
RECURRING_BOOKING_SERVICE_MODULE: Symbol("RecurringBookingServiceModule"),
|
||||
INSTANT_BOOKING_CREATE_SERVICE: Symbol("InstantBookingCreateService"),
|
||||
INSTANT_BOOKING_CREATE_SERVICE_MODULE: Symbol("InstantBookingCreateServiceModule"),
|
||||
};
|
||||
|
||||
@@ -3,6 +3,7 @@ import z, { ZodNullable, ZodObject, ZodOptional } from "zod";
|
||||
import { timeZoneSchema } from "@calcom/lib/dayjs/timeZone.schema";
|
||||
// TODO: Move this out of here. Importing from app-store is a circular package dependency.
|
||||
import { routingFormResponseInDbSchema } from "@calcom/app-store/routing-forms/zod";
|
||||
import { CreationSource } from "@calcom/prisma/enums";
|
||||
|
||||
export const bookingCreateBodySchema = z.object({
|
||||
end: z.string().optional(),
|
||||
@@ -47,7 +48,8 @@ export const bookingCreateBodySchema = z.object({
|
||||
utm_term: z.string().optional(),
|
||||
utm_content: z.string().optional(),
|
||||
}).optional(),
|
||||
dub_id: z.string().nullish()
|
||||
dub_id: z.string().nullish(),
|
||||
creationSource: z.nativeEnum(CreationSource).optional(),
|
||||
});
|
||||
|
||||
export type BookingCreateBody = z.input<typeof bookingCreateBodySchema>;
|
||||
|
||||
Reference in New Issue
Block a user