Add DI for BookingAudit (#25123)

This commit is contained in:
Hariom Balhara
2025-11-28 09:14:48 +00:00
committed by GitHub
parent e6ba89961c
commit cbc8d3d7a8
26 changed files with 394 additions and 33 deletions
@@ -4,6 +4,8 @@ import { PrismaFeaturesRepository } from "@/lib/repositories/prisma-features.rep
import { PrismaHostRepository } from "@/lib/repositories/prisma-host.repository";
import { PrismaOOORepository } from "@/lib/repositories/prisma-ooo.repository";
import { PrismaUserRepository } from "@/lib/repositories/prisma-user.repository";
import { Logger } from "@/lib/logger.bridge";
import { BookingEventHandlerService } from "@/lib/services/booking-event-handler.service";
import { CheckBookingAndDurationLimitsService } from "@/lib/services/check-booking-and-duration-limits.service";
import { CheckBookingLimitsService } from "@/lib/services/check-booking-limits.service";
import { HashedLinkService } from "@/lib/services/hashed-link.service";
@@ -21,6 +23,13 @@ import { Module } from "@nestjs/common";
PrismaHostRepository,
PrismaOOORepository,
PrismaUserRepository,
{
provide: Logger,
useFactory: () => {
return new Logger();
},
},
BookingEventHandlerService,
CheckBookingAndDurationLimitsService,
CheckBookingLimitsService,
HashedLinkService,
@@ -1,7 +1,7 @@
import { PrismaWriteService } from "@/modules/prisma/prisma-write.service";
import { Injectable } from "@nestjs/common";
import { BookingCancelService as BaseBookingCancelService } from "@calcom/features/bookings/lib/handleCancelBooking";
import { BookingCancelService as BaseBookingCancelService } from "@calcom/platform-libraries/bookings";
@Injectable()
export class BookingCancelService extends BaseBookingCancelService {
@@ -0,0 +1,18 @@
import { Injectable } from "@nestjs/common";
import { BookingEventHandlerService as BaseBookingEventHandlerService } from "@calcom/platform-libraries/bookings";
import { Logger } from "@/lib/logger.bridge";
import { HashedLinkService } from "./hashed-link.service";
@Injectable()
export class BookingEventHandlerService extends BaseBookingEventHandlerService {
constructor(hashedLinkService: HashedLinkService, bridgeLogger: Logger) {
super({
log: bridgeLogger,
hashedLinkService,
});
}
}
@@ -1,5 +1,6 @@
import { PrismaBookingRepository } from "@/lib/repositories/prisma-booking.repository";
import { PrismaUserRepository } from "@/lib/repositories/prisma-user.repository";
import { BookingEventHandlerService } from "@/lib/services/booking-event-handler.service";
import { CheckBookingAndDurationLimitsService } from "@/lib/services/check-booking-and-duration-limits.service";
import { HashedLinkService } from "@/lib/services/hashed-link.service";
import { LuckyUserService } from "@/lib/services/lucky-user.service";
@@ -17,7 +18,8 @@ export class RegularBookingService extends BaseRegularBookingService {
bookingRepository: PrismaBookingRepository,
hashedLinkService: HashedLinkService,
luckyUserService: LuckyUserService,
userRepository: PrismaUserRepository
userRepository: PrismaUserRepository,
bookingEventHandler: BookingEventHandlerService
) {
super({
checkBookingAndDurationLimitsService,
@@ -26,6 +28,7 @@ export class RegularBookingService extends BaseRegularBookingService {
hashedLinkService,
luckyUserService,
userRepository,
bookingEventHandler,
});
}
}
@@ -0,0 +1,23 @@
import { PrismaAuditActorRepository } from "@calcom/features/booking-audit/lib/repository/PrismaAuditActorRepository";
import { BOOKING_AUDIT_DI_TOKENS } from "@calcom/features/booking-audit/di/tokens";
import { bindModuleToClassOnToken } from "@calcom/features/di/di";
import { moduleLoader as prismaModuleLoader } from "@calcom/features/di/modules/Prisma";
import { createModule } from "../../di/di";
export const actorRepositoryModule = createModule();
const token = BOOKING_AUDIT_DI_TOKENS.ACTOR_REPOSITORY;
const moduleToken = BOOKING_AUDIT_DI_TOKENS.ACTOR_REPOSITORY_MODULE;
const loadModule = bindModuleToClassOnToken({
module: actorRepositoryModule,
moduleToken,
token,
classs: PrismaAuditActorRepository,
depsMap: {
prismaClient: prismaModuleLoader,
},
});
export const moduleLoader = {
token,
loadModule,
};
@@ -0,0 +1,23 @@
import { PrismaBookingAuditRepository } from "@calcom/features/booking-audit/lib/repository/PrismaBookingAuditRepository";
import { BOOKING_AUDIT_DI_TOKENS } from "@calcom/features/booking-audit/di/tokens";
import { bindModuleToClassOnToken } from "@calcom/features/di/di";
import { moduleLoader as prismaModuleLoader } from "@calcom/features/di/modules/Prisma";
import { createModule } from "../../di/di";
export const bookingAuditRepositoryModule = createModule();
const token = BOOKING_AUDIT_DI_TOKENS.BOOKING_AUDIT_REPOSITORY;
const moduleToken = BOOKING_AUDIT_DI_TOKENS.BOOKING_AUDIT_REPOSITORY_MODULE;
const loadModule = bindModuleToClassOnToken({
module: bookingAuditRepositoryModule,
moduleToken,
token,
classs: PrismaBookingAuditRepository,
depsMap: {
prismaClient: prismaModuleLoader,
},
});
export const moduleLoader = {
token,
loadModule,
};
@@ -0,0 +1,26 @@
import { BookingAuditService } from "@calcom/features/booking-audit/lib/service/BookingAuditService";
import { BOOKING_AUDIT_DI_TOKENS } from "@calcom/features/booking-audit/di/tokens";
import { moduleLoader as bookingAuditRepositoryModuleLoader } from "@calcom/features/booking-audit/di/BookingAuditRepository.module";
import { moduleLoader as actorRepositoryModuleLoader } from "@calcom/features/booking-audit/di/ActorRepository.module";
import { createModule, bindModuleToClassOnToken } from "../../di/di";
export const bookingAuditServiceModule = createModule();
const token = BOOKING_AUDIT_DI_TOKENS.BOOKING_AUDIT_SERVICE;
const moduleToken = BOOKING_AUDIT_DI_TOKENS.BOOKING_AUDIT_SERVICE_MODULE;
const loadModule = bindModuleToClassOnToken({
module: bookingAuditServiceModule,
moduleToken,
token,
classs: BookingAuditService,
depsMap: {
bookingAuditRepository: bookingAuditRepositoryModuleLoader,
actorRepository: actorRepositoryModuleLoader,
},
});
export const moduleLoader = {
token,
loadModule
};
@@ -0,0 +1,8 @@
export const BOOKING_AUDIT_DI_TOKENS = {
BOOKING_AUDIT_SERVICE: Symbol("BookingAuditService"),
BOOKING_AUDIT_SERVICE_MODULE: Symbol("BookingAuditServiceModule"),
BOOKING_AUDIT_REPOSITORY: Symbol("BookingAuditRepository"),
BOOKING_AUDIT_REPOSITORY_MODULE: Symbol("BookingAuditRepositoryModule"),
ACTOR_REPOSITORY: Symbol("ActorRepository"),
ACTOR_REPOSITORY_MODULE: Symbol("ActorRepositoryModule"),
};
@@ -13,5 +13,9 @@ type AuditActor = {
export interface IAuditActorRepository {
findByUserUuid(userUuid: string): Promise<AuditActor | null>;
findSystemActorOrThrow(): Promise<AuditActor>;
// TODO: To be implemented in followup PR
// upsertUserActor(userUuid: string): Promise<AuditActor>;
// upsertGuestActor(email: string, name?: string, phone?: string): Promise<AuditActor>;
// findByAttendeeId(attendeeId: number): Promise<AuditActor | null>;
}
@@ -1,11 +1,12 @@
import type { JsonValue } from "@calcom/types/Json";
export type BookingAuditType = "RECORD_CREATED" | "RECORD_UPDATED" | "RECORD_DELETED"
export type BookingAuditAction = "CREATED" | "CANCELLED" | "ACCEPTED" | "REJECTED" | "PENDING" | "AWAITING_HOST" | "RESCHEDULED" | "ATTENDEE_ADDED" | "ATTENDEE_REMOVED" | "CANCELLATION_REASON_UPDATED" | "REJECTION_REASON_UPDATED" | "ASSIGNMENT_REASON_UPDATED" | "REASSIGNMENT_REASON_UPDATED" | "LOCATION_CHANGED" | "HOST_NO_SHOW_UPDATED" | "ATTENDEE_NO_SHOW_UPDATED" | "RESCHEDULE_REQUESTED"
export type BookingAuditAction = "CREATED" | "CANCELLED" | "ACCEPTED" | "REJECTED" | "PENDING" | "AWAITING_HOST" | "RESCHEDULED" | "ATTENDEE_ADDED" | "ATTENDEE_REMOVED" | "REASSIGNMENT" | "LOCATION_CHANGED" | "HOST_NO_SHOW_UPDATED" | "ATTENDEE_NO_SHOW_UPDATED" | "RESCHEDULE_REQUESTED"
export type BookingAuditCreateInput = {
bookingUid: string;
actorId: string;
action: BookingAuditAction;
data: unknown;
createdAt: Date;
data: JsonValue;
type: BookingAuditType;
timestamp: Date;
}
@@ -14,9 +15,12 @@ type BookingAudit = {
id: string;
bookingUid: string;
actorId: string;
action: string;
data: unknown;
action: BookingAuditAction;
type: BookingAuditType;
timestamp: Date;
createdAt: Date;
updatedAt: Date;
data: JsonValue;
}
export interface IBookingAuditRepository {
@@ -10,7 +10,14 @@ export class PrismaBookingAuditRepository implements IBookingAuditRepository {
async create(bookingAudit: BookingAuditCreateInput) {
return this.deps.prismaClient.bookingAudit.create({
data: bookingAudit,
data: {
bookingUid: bookingAudit.bookingUid,
actorId: bookingAudit.actorId,
action: bookingAudit.action,
type: bookingAudit.type,
timestamp: bookingAudit.timestamp,
data: bookingAudit.data === null ? undefined : bookingAudit.data,
},
});
}
}
@@ -0,0 +1,67 @@
import type { JsonValue } from "@calcom/types/Json";
import logger from "@calcom/lib/logger";
import type { IBookingAuditRepository, BookingAuditType, BookingAuditAction } from "../repository/IBookingAuditRepository";
import type { IAuditActorRepository } from "../repository/IAuditActorRepository";
import { safeStringify } from "@calcom/lib/safeStringify";
interface BookingAuditServiceDeps {
bookingAuditRepository: IBookingAuditRepository;
actorRepository: IAuditActorRepository;
}
type CreateBookingAuditInput = {
bookingUid: string;
actorId: string;
type: BookingAuditType;
action: BookingAuditAction;
data: JsonValue;
timestamp: Date; // Required: actual time of the booking change (business event)
};
type BookingAudit = {
id: string;
bookingUid: string;
actorId: string;
type: BookingAuditType;
action: BookingAuditAction;
timestamp: Date;
createdAt: Date;
updatedAt: Date;
data: JsonValue;
};
/**
* BookingAuditService - Central service for all booking audit operations
* Handles both write (audit creation) and read (display) operations
* Each action service manages its own schema versioning
*/
export class BookingAuditService {
private readonly bookingAuditRepository: IBookingAuditRepository;
constructor(private readonly deps: BookingAuditServiceDeps) {
this.bookingAuditRepository = deps.bookingAuditRepository;
}
/**
* TODO: TO be integrated with public methods in followup PR
*/
async createAuditRecord(input: CreateBookingAuditInput): Promise<BookingAudit> {
// Log only non-sensitive metadata, excluding the data field which may contain PII
logger.info("Creating audit record", safeStringify({
bookingUid: input.bookingUid,
actorId: input.actorId,
type: input.type,
action: input.action,
timestamp: input.timestamp,
}));
return this.bookingAuditRepository.create({
bookingUid: input.bookingUid,
actorId: input.actorId,
type: input.type,
action: input.action,
timestamp: input.timestamp,
data: input.data,
});
}
}
@@ -0,0 +1,15 @@
import { createContainer } from "@calcom/features/di/di";
import {
type BookingEventHandlerService,
moduleLoader as bookingEventHandlerServiceModule,
} from "./BookingEventHandlerService.module";
const container = createContainer();
export function getBookingEventHandlerService() {
bookingEventHandlerServiceModule.loadModule(container);
return container.get<BookingEventHandlerService>(bookingEventHandlerServiceModule.token);
}
@@ -0,0 +1,30 @@
import { BookingEventHandlerService } from "@calcom/features/bookings/lib/onBookingEvents/BookingEventHandlerService";
import { bindModuleToClassOnToken, createModule } from "@calcom/features/di/di";
import { DI_TOKENS } from "@calcom/features/di/tokens";
import { moduleLoader as hashedLinkServiceModuleLoader } from "@calcom/features/hashedLink/di/HashedLinkService.module";
import { moduleLoader as bookingAuditServiceModuleLoader } from "@calcom/features/booking-audit/di/BookingAuditService.module";
import { moduleLoader as loggerModuleLoader } from "@calcom/features/di/shared/services/logger.service";
const thisModule = createModule();
const token = DI_TOKENS.BOOKING_EVENT_HANDLER_SERVICE;
const moduleToken = DI_TOKENS.BOOKING_EVENT_HANDLER_SERVICE_MODULE;
const loadModule = bindModuleToClassOnToken({
module: thisModule,
moduleToken,
token,
classs: BookingEventHandlerService,
depsMap: {
hashedLinkService: hashedLinkServiceModuleLoader,
bookingAuditService: bookingAuditServiceModuleLoader,
log: loggerModuleLoader,
},
});
export const moduleLoader = {
token,
loadModule,
};
export type { BookingEventHandlerService };
@@ -1,3 +1,4 @@
import { moduleLoader as bookingAuditServiceModuleLoader } from "@calcom/features/booking-audit/di/BookingAuditService.module";
import { RegularBookingService } from "@calcom/features/bookings/lib/service/RegularBookingService";
import { bindModuleToClassOnToken, createModule } from "@calcom/features/di/di";
import { moduleLoader as bookingRepositoryModuleLoader } from "@calcom/features/di/modules/Booking";
@@ -7,6 +8,7 @@ import { moduleLoader as userRepositoryModuleLoader } from "@calcom/features/di/
import { DI_TOKENS } from "@calcom/features/di/tokens";
import { moduleLoader as hashedLinkServiceModuleLoader } from "@calcom/features/hashedLink/di/HashedLinkService.module";
import { moduleLoader as prismaModuleLoader } from "@calcom/features/di/modules/Prisma";
import { moduleLoader as bookingEventHandlerModuleLoader } from "@calcom/features/bookings/di/BookingEventHandlerService.module";
const thisModule = createModule();
const token = DI_TOKENS.REGULAR_BOOKING_SERVICE;
@@ -24,6 +26,7 @@ const loadModule = bindModuleToClassOnToken({
luckyUserService: luckyUserServiceModuleLoader,
userRepository: userRepositoryModuleLoader,
hashedLinkService: hashedLinkServiceModuleLoader,
bookingEventHandler: bookingEventHandlerModuleLoader,
},
});
+2
View File
@@ -7,4 +7,6 @@ export const BOOKING_DI_TOKENS = {
INSTANT_BOOKING_CREATE_SERVICE_MODULE: Symbol("InstantBookingCreateServiceModule"),
BOOKING_CANCEL_SERVICE: Symbol("BookingCancelService"),
BOOKING_CANCEL_SERVICE_MODULE: Symbol("BookingCancelServiceModule"),
BOOKING_EVENT_HANDLER_SERVICE: Symbol("BookingEventHandlerService"),
BOOKING_EVENT_HANDLER_SERVICE_MODULE: Symbol("BookingEventHandlerServiceModule"),
};
@@ -1,13 +1,15 @@
import type { Logger } from "tslog";
import type { BookingAuditService } from "@calcom/features/booking-audit/lib/service/BookingAuditService";
import type { HashedLinkService } from "@calcom/features/hashedLink/lib/service/HashedLinkService";
import type { ISimpleLogger } from "@calcom/features/di/shared/services/logger.service";
import { safeStringify } from "@calcom/lib/safeStringify";
import type { BookingCreatedPayload, BookingRescheduledPayload } from "./types";
interface BookingEventHandlerDeps {
log: Logger<unknown>;
log: ISimpleLogger;
hashedLinkService: HashedLinkService;
//TODO: To be made required in followup PR
bookingAuditService?: BookingAuditService;
}
export class BookingEventHandlerService {
@@ -417,6 +417,7 @@ export interface IBookingServiceDependencies {
luckyUserService: LuckyUserService;
userRepository: UserRepository;
hashedLinkService: HashedLinkService;
bookingEventHandler: BookingEventHandlerService;
}
/**
@@ -2259,10 +2260,7 @@ async function handler(
// Add more fields here when needed
const bookingRescheduledPayload = bookingCreatedPayload;
const bookingEventHandler = new BookingEventHandlerService({
log: tracingLogger,
hashedLinkService: deps.hashedLinkService,
});
const bookingEventHandler = deps.bookingEventHandler;
// TODO: Incrementally move all stuff that happens after a booking is created to these handlers
if (originalRescheduledBooking) {
@@ -0,0 +1,89 @@
/**
* Represents the entity that performed a booking action
* Uses discriminating union for type-safe actor identification
*/
export type Actor = ActorById | UserActor | AttendeeActor | GuestActor;
/**
* Actor referenced by existing ID in AuditActor table
* Use this for System actors or when you already have an actor ID
*/
type ActorById = {
identifiedBy: "id";
id: string; // UUID of existing actor (including System actor)
};
/**
* Actor identified by user UUID
* Will be upserted in AuditActor table
*/
type UserActor = {
identifiedBy: "user";
userUuid: string;
};
/**
* Actor identified by attendee ID
* Must already exist in AuditActor table
*/
type AttendeeActor = {
identifiedBy: "attendee";
attendeeId: number;
};
/**
* Actor identified by guest data (email, name, phone)
* Will be upserted in AuditActor table
*/
type GuestActor = {
identifiedBy: "guest";
email?: string;
name?: string;
phone?: string;
};
// System actor ID constant
export const SYSTEM_ACTOR_ID = "00000000-0000-0000-0000-000000000000";
/**
* Creates an Actor representing a User by UUID
*/
export function makeUserActor(userUuid: string): UserActor {
return {
identifiedBy: "user",
userUuid,
};
}
/**
* Creates an Actor representing the System
* System actors must be referenced by ID (requires migration)
*/
export function makeSystemActor(): ActorById {
return {
identifiedBy: "id",
id: SYSTEM_ACTOR_ID,
};
}
/**
* Creates an Actor representing a Guest (non-registered attendee)
*/
export function makeGuestActor(email: string, name?: string, phone?: string): GuestActor {
return {
identifiedBy: "guest",
email,
name,
phone,
};
}
/**
* Creates an Actor by existing actor ID
*/
export function makeActorById(id: string): ActorById {
return {
identifiedBy: "id",
id,
};
}
@@ -1,13 +1,38 @@
import { createModule } from "@evyweb/ioctopus";
import type { ILogger } from "@calcom/features/webhooks/lib/interface/infrastructure";
import { bindModuleToClassOnToken, createModule } from "@calcom/features/di/di";
import { Logger } from "tslog";
import { SHARED_TOKENS } from "../shared.tokens";
import { loggerConfig } from "@calcom/lib/logger";
export const loggerServiceModule = createModule();
/**
* Minimal logger interface that supports common logging methods.
* This allows both tslog Logger and bridge Logger implementations to be used interchangeably.
* Can be used by any module that needs a simple logger interface.
*/
export interface ISimpleLogger {
debug(...args: unknown[]): void;
error(...args: unknown[]): void;
info(...args: unknown[]): void;
warn(...args: unknown[]): void;
}
// Bind logger with proper factory that respects IoC
loggerServiceModule.bind(SHARED_TOKENS.LOGGER).toFactory(async (): Promise<ILogger> => {
const loggerModule = await import("@calcom/lib/logger");
return loggerModule.default;
const thisModule = createModule();
const token = SHARED_TOKENS.LOGGER;
const moduleToken = SHARED_TOKENS.LOGGER_MODULE;
class LoggerService extends Logger<unknown> {
constructor() {
super(loggerConfig);
}
}
const loadModule = bindModuleToClassOnToken({
module: thisModule,
moduleToken,
token,
classs: LoggerService,
depsMap: {},
});
export const moduleLoader = {
token,
loadModule,
};
@@ -2,4 +2,5 @@ export const SHARED_TOKENS = {
// Infrastructure services
TASKER: Symbol("ITasker"),
LOGGER: Symbol("ILogger"),
LOGGER_MODULE: Symbol("ILoggerModule"),
} as const;
+2 -2
View File
@@ -1,4 +1,5 @@
import { BOOKING_DI_TOKENS } from "@calcom/features/bookings/di/tokens";
import { BOOKING_AUDIT_DI_TOKENS } from "@calcom/features/booking-audit/di/tokens";
import { HASHED_LINK_DI_TOKENS } from "@calcom/features/hashedLink/di/tokens";
import { ORGANIZATION_DI_TOKENS } from "@calcom/features/ee/organizations/di/tokens";
import { WATCHLIST_DI_TOKENS } from "./watchlist/Watchlist.tokens";
@@ -56,10 +57,9 @@ export const DI_TOKENS = {
ATTRIBUTE_REPOSITORY_MODULE: Symbol("AttributeRepositoryModule"),
MEMBERSHIP_SERVICE: Symbol("MembershipService"),
MEMBERSHIP_SERVICE_MODULE: Symbol("MembershipServiceModule"),
// Booking service tokens
...BOOKING_AUDIT_DI_TOKENS,
...BOOKING_DI_TOKENS,
...HASHED_LINK_DI_TOKENS,
// Watchlist service tokens
...WATCHLIST_DI_TOKENS,
...ORGANIZATION_DI_TOKENS,
};
@@ -1,6 +1,6 @@
import { createContainer } from "@evyweb/ioctopus";
import { loggerServiceModule } from "@calcom/features/di/shared/services/logger.service";
import { moduleLoader as loggerModuleLoader } from "@calcom/features/di/shared/services/logger.service";
import { taskerServiceModule } from "@calcom/features/di/shared/services/tasker.service";
import { SHARED_TOKENS } from "@calcom/features/di/shared/shared.tokens";
import {
@@ -21,8 +21,8 @@ import { watchlistModule } from "../modules/Watchlist.module";
export const watchlistContainer = createContainer();
prismaModuleLoader.loadModule(watchlistContainer);
loggerModuleLoader.loadModule(watchlistContainer);
watchlistContainer.load(SHARED_TOKENS.LOGGER, loggerServiceModule);
watchlistContainer.load(SHARED_TOKENS.TASKER, taskerServiceModule);
watchlistContainer.load(WATCHLIST_DI_TOKENS.GLOBAL_WATCHLIST_REPOSITORY, watchlistModule);
@@ -1,6 +1,6 @@
import { createContainer } from "@evyweb/ioctopus";
import { loggerServiceModule } from "../../shared/services/logger.service";
import { moduleLoader as loggerModuleLoader } from "../../shared/services/logger.service";
import { taskerServiceModule } from "../../shared/services/tasker.service";
import { SHARED_TOKENS } from "../../shared/shared.tokens";
import { WEBHOOK_TOKENS } from "../Webhooks.tokens";
@@ -9,7 +9,7 @@ import { webhookModule } from "../modules/Webhook.module";
export const webhookContainer = createContainer();
// Load shared infrastructure
webhookContainer.load(SHARED_TOKENS.LOGGER, loggerServiceModule);
loggerModuleLoader.loadModule(webhookContainer);
webhookContainer.load(SHARED_TOKENS.TASKER, taskerServiceModule);
// Load webhook module
+5 -3
View File
@@ -1,8 +1,8 @@
import { Logger } from "tslog";
import { Logger, ISettingsParam } from "tslog";
import { IS_PRODUCTION } from "./constants";
const logger = new Logger({
export const loggerConfig: ISettingsParam<unknown> = {
minLevel: parseInt(process.env.NEXT_PUBLIC_LOGGER_LEVEL || "4"),
maskValuesOfKeys: ["password", "passwordConfirmation", "credentials", "credential"],
prettyLogTimeZone: IS_PRODUCTION ? "UTC" : "local",
@@ -15,6 +15,8 @@ const logger = new Logger({
dateIsoStr: "blue",
},
type: IS_PRODUCTION ? "json" : "pretty",
});
};
const logger = new Logger(loggerConfig);
export default logger;
+2
View File
@@ -4,6 +4,8 @@ export { CheckBookingAndDurationLimitsService } from "@calcom/features/bookings/
export { RegularBookingService } from "@calcom/features/bookings/lib/service/RegularBookingService";
export { RecurringBookingService } from "@calcom/features/bookings/lib/service/RecurringBookingService";
export { InstantBookingCreateService } from "@calcom/features/bookings/lib/service/InstantBookingCreateService";
export { BookingEventHandlerService } from "@calcom/features/bookings/lib/onBookingEvents/BookingEventHandlerService";
export { BookingCancelService } from "@calcom/features/bookings/lib/handleCancelBooking";
export type {
InstantBookingCreateResult,
RegularBookingCreateResult,