* refactor: migrate workflows utilities from trpc to features layer Move workflow utility functions from packages/trpc/server/routers/viewer/workflows/util.ts to packages/features/ee/workflows/lib/workflowUtils.ts to break circular dependencies. Functions migrated: - isAuthorized - getAllWorkflowsFromEventType - scheduleWorkflowNotifications - scheduleBookingReminders Changes: - Created new workflowUtils.ts in features layer with migrated functions - Added getBookingsForWorkflowReminders to WorkflowRepository (no prisma outside repositories) - Updated all imports in features layer to use new location - Updated trpc util.ts to re-export from features for backward compatibility - Fixed pre-existing lint warning (any -> unknown) in handleMarkNoShow.ts This is part of the effort to remove circular dependencies between packages/features and packages/trpc. Co-Authored-By: benny@cal.com <sldisek783@gmail.com> * refactor: remove re-exports from trpc util.ts, update imports to use features layer Per user request, removed the backward compatibility re-exports from packages/trpc/server/routers/viewer/workflows/util.ts and updated all imports in the trpc package to import directly from the features layer. Files updated to import from @calcom/features/ee/workflows/lib/workflowUtils: - confirm.handler.ts (getAllWorkflowsFromEventType) - delete.handler.ts (isAuthorized) - update.handler.ts (isAuthorized, scheduleWorkflowNotifications) - getAllActiveWorkflows.handler.ts (getAllWorkflowsFromEventType) - get.handler.ts (isAuthorized) - util.test.ts (isAuthorized) - delete.handler.test.ts (isAuthorized) Co-Authored-By: benny@cal.com <sldisek783@gmail.com> * fix: update test imports to use features layer for workflow utilities Updated test files to import scheduleBookingReminders and scheduleWorkflowNotifications from @calcom/features/ee/workflows/lib/workflowUtils instead of @calcom/trpc/server/routers/viewer/workflows/util. Files updated: - packages/features/ee/workflows/lib/test/workflows.test.ts - packages/features/tasker/tasks/scanWorkflowBody.test.ts Co-Authored-By: benny@cal.com <sldisek783@gmail.com> * refactor: split workflowUtils.ts into individual files Split the monolithic workflowUtils.ts into separate files for each function: - isAuthorized.ts - Authorization check for workflow access - getAllWorkflowsFromEventType.ts - Get workflows for an event type - scheduleWorkflowNotifications.ts - Schedule workflow notifications - scheduleBookingReminders.ts - Schedule booking reminders The workflowUtils.ts now re-exports from these individual files for backward compatibility. Co-Authored-By: benny@cal.com <sldisek783@gmail.com> * fix import * fix more * wip * wip * remove workflowUtils * wip * refactor deleteRemindersOfActiveOnIds * fix --------- Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
154 lines
6.1 KiB
TypeScript
154 lines
6.1 KiB
TypeScript
import { eventTypeMetaDataSchemaWithTypedApps } from "@calcom/app-store/zod-utils";
|
|
import dayjs from "@calcom/dayjs";
|
|
import { CalendarEventBuilder } from "@calcom/features/CalendarEventBuilder";
|
|
import type { BookingEmailSmsHandler } from "@calcom/features/bookings/lib/BookingEmailSmsHandler";
|
|
import { getOriginalRescheduledBooking } from "@calcom/features/bookings/lib/handleNewBooking/originalRescheduledBookingUtils";
|
|
import type { BookingRepository } from "@calcom/features/bookings/repositories/BookingRepository";
|
|
import { getAllWorkflowsFromEventType } from "@calcom/features/ee/workflows/lib/getAllWorkflowsFromEventType";
|
|
import type { EventNameObjectType } from "@calcom/features/eventtypes/lib/eventNaming";
|
|
import type { ITaskerDependencies } from "@calcom/lib/tasker/types";
|
|
import { SchedulingType } from "@calcom/prisma/enums";
|
|
import type { CalendarEvent } from "@calcom/types/Calendar";
|
|
import type { JsonObject } from "@calcom/types/Json";
|
|
|
|
import { BookingEmailAndSmsAsyncTasksPayload, BookingTasks } from "./types";
|
|
|
|
export interface IBookingTaskServiceDependencies {
|
|
emailsAndSmsHandler: BookingEmailSmsHandler;
|
|
bookingRepository: BookingRepository;
|
|
}
|
|
|
|
export class BookingEmailAndSmsTaskService implements BookingTasks {
|
|
constructor(
|
|
public readonly dependencies: { logger: ITaskerDependencies["logger"] } & IBookingTaskServiceDependencies
|
|
) {}
|
|
|
|
private async _getVerifiedBookingData(payload: BookingEmailAndSmsAsyncTasksPayload) {
|
|
const { bookingId, ...bookingMeta } = payload;
|
|
const { bookingRepository } = this.dependencies;
|
|
|
|
const booking = await bookingRepository.getBookingForCalEventBuilder(bookingId);
|
|
if (!booking) {
|
|
throw new Error(`Booking with id '${bookingId}' was not found.`);
|
|
}
|
|
if (!booking.eventType) {
|
|
throw new Error(`EventType of Booking with id '${bookingId}' was not found.`);
|
|
}
|
|
|
|
const calendarEvent = (await CalendarEventBuilder.fromBooking(booking, bookingMeta)).build();
|
|
if (!calendarEvent) {
|
|
throw new Error(`CalendarEvent could not be built from Booking with id '${bookingId}'.`);
|
|
}
|
|
|
|
return {
|
|
booking: booking,
|
|
eventType: booking.eventType,
|
|
calendarEvent: calendarEvent as NonNullable<CalendarEvent>,
|
|
};
|
|
}
|
|
|
|
async request(payload: Parameters<BookingTasks["request"]>[0]) {
|
|
const { eventType, calendarEvent } = await this._getVerifiedBookingData(payload);
|
|
|
|
await this.dependencies.emailsAndSmsHandler.send({
|
|
action: "BOOKING_REQUESTED",
|
|
data: {
|
|
evt: calendarEvent,
|
|
attendees: calendarEvent.attendees,
|
|
eventType: {
|
|
schedulingType: eventType.schedulingType ?? null,
|
|
metadata: eventTypeMetaDataSchemaWithTypedApps.parse(eventType.metadata),
|
|
},
|
|
additionalNotes: calendarEvent.additionalNotes,
|
|
},
|
|
});
|
|
}
|
|
|
|
async confirm(payload: Parameters<BookingTasks["confirm"]>[0]) {
|
|
const { booking, eventType, calendarEvent } = await this._getVerifiedBookingData(payload);
|
|
|
|
const attendeesEmail = booking.attendees.map((attendee) => attendee.email);
|
|
const bookedTeamMembers = eventType.team
|
|
? eventType.team.members.filter((teamUser) => attendeesEmail.includes(teamUser.user.email))
|
|
: [];
|
|
|
|
const eventNameObject = {
|
|
attendeeName: calendarEvent.organizer.name || "Nameless",
|
|
eventType: eventType.title,
|
|
eventName: eventType.eventName,
|
|
// we send on behalf of team if >1 round robin attendee | collective
|
|
teamName:
|
|
eventType.schedulingType === "COLLECTIVE" || bookedTeamMembers.length > 1
|
|
? eventType.team?.name
|
|
: null,
|
|
host: calendarEvent.organizer.name || "Nameless",
|
|
location: booking.location,
|
|
eventDuration: dayjs(booking.endTime).diff(booking.startTime, "minutes"),
|
|
bookingFields: eventType.bookingFields as JsonObject,
|
|
t: calendarEvent.organizer.language.translate,
|
|
} satisfies EventNameObjectType;
|
|
|
|
const workflows = await getAllWorkflowsFromEventType(
|
|
{
|
|
...eventType,
|
|
metadata: eventTypeMetaDataSchemaWithTypedApps.parse(eventType.metadata),
|
|
},
|
|
calendarEvent.organizer.id
|
|
);
|
|
|
|
await this.dependencies.emailsAndSmsHandler.send({
|
|
action: "BOOKING_CONFIRMED",
|
|
data: {
|
|
eventType: {
|
|
schedulingType: booking.eventType?.schedulingType ?? null,
|
|
metadata: eventTypeMetaDataSchemaWithTypedApps.parse(eventType.metadata),
|
|
},
|
|
eventNameObject,
|
|
workflows: workflows,
|
|
evt: calendarEvent,
|
|
additionalInformation: calendarEvent.additionalInformation ?? {},
|
|
additionalNotes: calendarEvent.additionalNotes,
|
|
customInputs: calendarEvent.customInputs,
|
|
},
|
|
});
|
|
}
|
|
|
|
async reschedule(payload: Parameters<BookingTasks["reschedule"]>[0]) {
|
|
const { booking, eventType, calendarEvent } = await this._getVerifiedBookingData(payload);
|
|
|
|
const originalBookingData = await getOriginalRescheduledBooking(
|
|
booking.uid,
|
|
!!booking.eventType?.seatsPerTimeSlot
|
|
);
|
|
|
|
const changedOrganizer =
|
|
!!originalBookingData &&
|
|
(eventType.schedulingType === SchedulingType.ROUND_ROBIN ||
|
|
eventType.schedulingType === SchedulingType.COLLECTIVE) &&
|
|
originalBookingData.userId !== calendarEvent.organizer.id;
|
|
|
|
await this.dependencies.emailsAndSmsHandler.send({
|
|
action: "BOOKING_RESCHEDULED",
|
|
data: {
|
|
evt: calendarEvent,
|
|
eventType: {
|
|
schedulingType: eventType.schedulingType ?? null,
|
|
metadata: eventTypeMetaDataSchemaWithTypedApps.parse(eventType.metadata),
|
|
},
|
|
additionalInformation: calendarEvent.additionalInformation ?? {},
|
|
additionalNotes: calendarEvent.additionalNotes,
|
|
iCalUID: calendarEvent.iCalUID ?? "",
|
|
originalRescheduledBooking: originalBookingData,
|
|
rescheduleReason: (calendarEvent?.responses?.["rescheduleReason"]?.value as string) ?? undefined,
|
|
users: eventType.hosts.map((h) => h.user) ?? [],
|
|
changedOrganizer: changedOrganizer,
|
|
isRescheduledByBooker: payload.isRescheduledByBooker ?? false,
|
|
},
|
|
});
|
|
}
|
|
|
|
async rrReschedule(payload: Parameters<BookingTasks["rrReschedule"]>[0]) {
|
|
return this.reschedule(payload);
|
|
}
|
|
}
|