Files
calendar/packages/features/ee/workflows/lib/scheduleWorkflowNotifications.ts
T
Benny JooGitHubDevin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
f98acb7301 refactor: migrate workflows utilities from trpc to features layer (#25534)
* 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>
2025-12-16 10:17:46 +00:00

194 lines
4.4 KiB
TypeScript

import { prisma } from "@calcom/prisma";
import type { WorkflowStep } from "@calcom/prisma/client";
import { BookingStatus, WorkflowTriggerEvents } from "@calcom/prisma/enums";
import type { TimeUnit } from "@calcom/prisma/enums";
import { scheduleBookingReminders } from "./scheduleBookingReminders";
export const bookingSelect = {
userPrimaryEmail: true,
startTime: true,
endTime: true,
title: true,
uid: true,
metadata: true,
smsReminderNumber: true,
responses: true,
attendees: {
select: {
name: true,
email: true,
timeZone: true,
locale: true,
},
},
eventType: {
select: {
slug: true,
id: true,
schedulingType: true,
hideOrganizerEmail: true,
customReplyToEmail: true,
hosts: {
select: {
user: {
select: {
email: true,
destinationCalendar: {
select: {
primaryEmail: true,
},
},
},
},
},
},
},
},
user: {
select: {
name: true,
timeZone: true,
timeFormat: true,
locale: true,
email: true,
},
},
};
export async function scheduleWorkflowNotifications({
activeOn,
isOrg,
workflowSteps,
time,
timeUnit,
trigger,
userId,
teamId,
alreadyScheduledActiveOnIds,
}: {
activeOn: number[];
isOrg: boolean;
workflowSteps: Partial<WorkflowStep>[];
time: number | null;
timeUnit: TimeUnit | null;
trigger: WorkflowTriggerEvents;
userId: number;
teamId: number | null;
alreadyScheduledActiveOnIds?: number[];
}) {
if (trigger !== WorkflowTriggerEvents.BEFORE_EVENT && trigger !== WorkflowTriggerEvents.AFTER_EVENT) return;
const bookingsToScheduleNotifications = await getBookings(activeOn, isOrg, alreadyScheduledActiveOnIds);
await scheduleBookingReminders(
bookingsToScheduleNotifications,
workflowSteps,
time,
timeUnit,
trigger,
userId,
teamId,
isOrg
);
}
export async function getBookings(
activeOn: number[],
isOrg: boolean,
alreadyScheduledActiveOnIds: number[] = []
) {
if (activeOn.length === 0) return [];
if (isOrg) {
const bookingsForReminders = await prisma.booking.findMany({
where: {
OR: [
{
// bookings from team event types + children managed event types
eventType: {
OR: [
{
teamId: {
in: activeOn,
},
},
{
teamId: null,
parent: {
teamId: {
in: activeOn,
},
},
},
],
},
},
{
// user bookings
user: {
teams: {
some: {
teamId: {
in: activeOn,
},
accepted: true,
},
},
},
eventType: {
teamId: null,
parentId: null, // children managed event types are handled above with team event types
},
// if user is already part of an already scheduled activeOn connecting reminders are already scheduled
NOT: {
user: {
teams: {
some: {
teamId: {
in: alreadyScheduledActiveOnIds,
},
},
},
},
},
},
],
status: BookingStatus.ACCEPTED,
startTime: {
gte: new Date(),
},
},
select: bookingSelect,
orderBy: {
startTime: "asc",
},
});
return bookingsForReminders;
} else {
const bookingsForReminders = await prisma.booking.findMany({
where: {
OR: [
{ eventTypeId: { in: activeOn } },
{
eventType: {
parentId: {
in: activeOn, // child event type can not disable workflows, so this should work
},
},
},
],
status: BookingStatus.ACCEPTED,
startTime: {
gte: new Date(),
},
},
select: bookingSelect,
orderBy: {
startTime: "asc",
},
});
return bookingsForReminders;
}
}