Skip to content

Commit f98acb7

Browse files
refactor: migrate workflows utilities from trpc to features layer (calcom#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>
1 parent 4e3ce77 commit f98acb7

26 files changed

Lines changed: 783 additions & 714 deletions

apps/api/v1/test/lib/bookings/_post.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@ vi.mock("@calcom/features/ee/workflows/lib/getAllWorkflows", () => ({
161161
workflowSelect: {},
162162
}));
163163

164-
vi.mock("@calcom/trpc/server/routers/viewer/workflows/util", () => ({
164+
vi.mock("@calcom/features/ee/workflows/lib/getAllWorkflowsFromEventType", () => ({
165165
getAllWorkflowsFromEventType: vi.fn().mockResolvedValue([]),
166166
}));
167167

packages/features/bookings/lib/handleBookingRequested.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { sendAttendeeRequestEmailAndSMS, sendOrganizerRequestEmail } from "@calcom/emails/email-manager";
22
import { getWebhookPayloadForBooking } from "@calcom/features/bookings/lib/getWebhookPayloadForBooking";
33
import { CreditService } from "@calcom/features/ee/billing/credit-service";
4+
import { getAllWorkflowsFromEventType } from "@calcom/features/ee/workflows/lib/getAllWorkflowsFromEventType";
45
import { WorkflowService } from "@calcom/features/ee/workflows/lib/service/WorkflowService";
56
import type { Workflow } from "@calcom/features/ee/workflows/lib/types";
67
import getWebhooks from "@calcom/features/webhooks/lib/getWebhooks";
@@ -11,7 +12,6 @@ import { safeStringify } from "@calcom/lib/safeStringify";
1112
import type { Prisma } from "@calcom/prisma/client";
1213
import { WebhookTriggerEvents, WorkflowTriggerEvents } from "@calcom/prisma/enums";
1314
import type { EventTypeMetadata } from "@calcom/prisma/zod-utils";
14-
import { getAllWorkflowsFromEventType } from "@calcom/trpc/server/routers/viewer/workflows/util";
1515
import type { CalendarEvent } from "@calcom/types/Calendar";
1616

1717
const log = logger.getSubLogger({ prefix: ["[handleBookingRequested] book:user"] });

packages/features/bookings/lib/handleCancelBooking.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { processNoShowFeeOnCancellation } from "@calcom/features/bookings/lib/pa
1111
import { processPaymentRefund } from "@calcom/features/bookings/lib/payment/processPaymentRefund";
1212
import { CreditService } from "@calcom/features/ee/billing/credit-service";
1313
import { getBookerBaseUrl } from "@calcom/features/ee/organizations/lib/getBookerUrlServer";
14+
import { getAllWorkflowsFromEventType } from "@calcom/features/ee/workflows/lib/getAllWorkflowsFromEventType";
1415
import { sendCancelledReminders } from "@calcom/features/ee/workflows/lib/reminders/reminderScheduler";
1516
import { WorkflowRepository } from "@calcom/features/ee/workflows/repositories/WorkflowRepository";
1617
import type { GetSubscriberOptions } from "@calcom/features/webhooks/lib/getWebhooks";
@@ -38,7 +39,6 @@ import type { WebhookTriggerEvents } from "@calcom/prisma/enums";
3839
import { BookingStatus } from "@calcom/prisma/enums";
3940
import { bookingMetadataSchema, bookingCancelInput } from "@calcom/prisma/zod-utils";
4041
import type { EventTypeMetadata } from "@calcom/prisma/zod-utils";
41-
import { getAllWorkflowsFromEventType } from "@calcom/trpc/server/routers/viewer/workflows/util";
4242
import type { CalendarEvent } from "@calcom/types/Calendar";
4343

4444
import type {

packages/features/bookings/lib/handleConfirmation.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {
99
allowDisablingAttendeeConfirmationEmails,
1010
allowDisablingHostConfirmationEmails,
1111
} from "@calcom/features/ee/workflows/lib/allowDisablingStandardEmails";
12+
import { getAllWorkflowsFromEventType } from "@calcom/features/ee/workflows/lib/getAllWorkflowsFromEventType";
1213
import { WorkflowService } from "@calcom/features/ee/workflows/lib/service/WorkflowService";
1314
import type { Workflow } from "@calcom/features/ee/workflows/lib/types";
1415
import getWebhooks from "@calcom/features/webhooks/lib/getWebhooks";
@@ -28,7 +29,6 @@ import type { SchedulingType } from "@calcom/prisma/enums";
2829
import { BookingStatus, WebhookTriggerEvents, WorkflowTriggerEvents } from "@calcom/prisma/enums";
2930
import type { PlatformClientParams } from "@calcom/prisma/zod-utils";
3031
import { EventTypeMetaDataSchema } from "@calcom/prisma/zod-utils";
31-
import { getAllWorkflowsFromEventType } from "@calcom/trpc/server/routers/viewer/workflows/util";
3232
import type { AdditionalInformation, CalendarEvent } from "@calcom/types/Calendar";
3333

3434
import { getCalEventResponses } from "./getCalEventResponses";

packages/features/bookings/lib/service/RegularBookingService.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ import { getSpamCheckService } from "@calcom/features/di/watchlist/containers/Sp
4141
import { CreditService } from "@calcom/features/ee/billing/credit-service";
4242
import { getBookerBaseUrl } from "@calcom/features/ee/organizations/lib/getBookerUrlServer";
4343
import AssignmentReasonRecorder from "@calcom/features/ee/round-robin/assignmentReason/AssignmentReasonRecorder";
44+
import { getAllWorkflowsFromEventType } from "@calcom/features/ee/workflows/lib/getAllWorkflowsFromEventType";
4445
import { WorkflowService } from "@calcom/features/ee/workflows/lib/service/WorkflowService";
4546
import { WorkflowRepository } from "@calcom/features/ee/workflows/repositories/WorkflowRepository";
4647
import { getUsernameList } from "@calcom/features/eventtypes/lib/defaultEvents";
@@ -87,7 +88,6 @@ import {
8788
CreationSource,
8889
} from "@calcom/prisma/enums";
8990
import { userMetadata as userMetadataSchema } from "@calcom/prisma/zod-utils";
90-
import { getAllWorkflowsFromEventType } from "@calcom/trpc/server/routers/viewer/workflows/util";
9191
import type {
9292
AdditionalInformation,
9393
AppsStatus,

packages/features/bookings/lib/tasker/BookingEmailAndSmsTaskService.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,10 @@ import { CalendarEventBuilder } from "@calcom/features/CalendarEventBuilder";
44
import type { BookingEmailSmsHandler } from "@calcom/features/bookings/lib/BookingEmailSmsHandler";
55
import { getOriginalRescheduledBooking } from "@calcom/features/bookings/lib/handleNewBooking/originalRescheduledBookingUtils";
66
import type { BookingRepository } from "@calcom/features/bookings/repositories/BookingRepository";
7+
import { getAllWorkflowsFromEventType } from "@calcom/features/ee/workflows/lib/getAllWorkflowsFromEventType";
78
import type { EventNameObjectType } from "@calcom/features/eventtypes/lib/eventNaming";
89
import type { ITaskerDependencies } from "@calcom/lib/tasker/types";
910
import { SchedulingType } from "@calcom/prisma/enums";
10-
import { getAllWorkflowsFromEventType } from "@calcom/trpc/server/routers/viewer/workflows/util";
1111
import type { CalendarEvent } from "@calcom/types/Calendar";
1212
import type { JsonObject } from "@calcom/types/Json";
1313

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import { WorkflowRepository } from "@calcom/features/ee/workflows/repositories/WorkflowRepository";
2+
import { prisma } from "@calcom/prisma";
3+
import type { Prisma, WorkflowStep } from "@calcom/prisma/client";
4+
5+
async function getRemindersFromRemovedEventTypes(removedEventTypes: number[], workflowSteps: WorkflowStep[]) {
6+
const remindersToDeletePromise: Prisma.PrismaPromise<
7+
{
8+
id: number;
9+
referenceId: string | null;
10+
method: string;
11+
}[]
12+
>[] = [];
13+
removedEventTypes.forEach((eventTypeId) => {
14+
const remindersToDelete = prisma.workflowReminder.findMany({
15+
where: {
16+
booking: {
17+
eventTypeId,
18+
},
19+
workflowStepId: {
20+
in: workflowSteps.map((step) => {
21+
return step.id;
22+
}),
23+
},
24+
},
25+
select: {
26+
id: true,
27+
referenceId: true,
28+
method: true,
29+
},
30+
});
31+
32+
remindersToDeletePromise.push(remindersToDelete);
33+
});
34+
35+
const remindersToDelete = (await Promise.all(remindersToDeletePromise)).flat();
36+
return remindersToDelete;
37+
}
38+
39+
export async function deleteRemindersOfActiveOnIds({
40+
removedActiveOnIds,
41+
workflowSteps,
42+
isOrg,
43+
activeOnIds,
44+
}: {
45+
removedActiveOnIds: number[];
46+
workflowSteps: WorkflowStep[];
47+
isOrg: boolean;
48+
activeOnIds?: number[];
49+
}) {
50+
const remindersToDelete = !isOrg
51+
? await getRemindersFromRemovedEventTypes(removedActiveOnIds, workflowSteps)
52+
: await WorkflowRepository.getRemindersFromRemovedTeams(removedActiveOnIds, workflowSteps, activeOnIds);
53+
await WorkflowRepository.deleteAllWorkflowReminders(remindersToDelete);
54+
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import { getAllWorkflows } from "@calcom/ee/workflows/lib/getAllWorkflows";
2+
import type { Workflow as WorkflowType } from "@calcom/ee/workflows/lib/types";
3+
import getOrgIdFromMemberOrTeamId from "@calcom/lib/getOrgIdFromMemberOrTeamId";
4+
import { getTeamIdFromEventType } from "@calcom/lib/getTeamIdFromEventType";
5+
import type { Prisma } from "@calcom/prisma/client";
6+
import { WorkflowType as PrismaWorkflowType } from "@calcom/prisma/enums";
7+
import { EventTypeMetaDataSchema } from "@calcom/prisma/zod-utils";
8+
9+
export async function getAllWorkflowsFromEventType(
10+
eventType: {
11+
workflows?: {
12+
workflow: WorkflowType;
13+
}[];
14+
teamId?: number | null;
15+
parentId?: number | null;
16+
parent?: {
17+
id?: number | null;
18+
teamId: number | null;
19+
} | null;
20+
metadata?: Prisma.JsonValue;
21+
} | null,
22+
userId?: number | null
23+
) {
24+
if (!eventType) return [];
25+
26+
const eventTypeWorkflows = eventType?.workflows?.map((workflowRel) => workflowRel.workflow) ?? [];
27+
28+
const teamId = await getTeamIdFromEventType({
29+
eventType: {
30+
team: { id: eventType?.teamId ?? null },
31+
parentId: eventType?.parentId || eventType?.parent?.id || null,
32+
},
33+
});
34+
35+
const orgId = await getOrgIdFromMemberOrTeamId({ memberId: userId, teamId });
36+
37+
const isManagedEventType = !!eventType?.parent;
38+
39+
const eventTypeMetadata = EventTypeMetaDataSchema.parse(eventType?.metadata || {});
40+
41+
const workflowsLockedForUser = isManagedEventType
42+
? !eventTypeMetadata?.managedEventConfig?.unlockedFields?.workflows
43+
: false;
44+
45+
const allWorkflows = await getAllWorkflows({
46+
entityWorkflows: eventTypeWorkflows,
47+
userId,
48+
teamId,
49+
orgId,
50+
workflowsLockedForUser,
51+
type: PrismaWorkflowType.EVENT_TYPE,
52+
});
53+
54+
return allWorkflows;
55+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import type { PermissionString } from "@calcom/features/pbac/domain/types/permission-registry";
2+
import { PermissionCheckService } from "@calcom/features/pbac/services/permission-check.service";
3+
import type { Workflow } from "@calcom/prisma/client";
4+
import { MembershipRole } from "@calcom/prisma/enums";
5+
6+
export async function isAuthorized(
7+
workflow: Pick<Workflow, "id" | "teamId" | "userId"> | null,
8+
currentUserId: number,
9+
permission: PermissionString = "workflow.read"
10+
) {
11+
if (!workflow) {
12+
return false;
13+
}
14+
15+
// For personal workflows (no teamId), check if user owns the workflow
16+
if (!workflow.teamId) {
17+
return workflow.userId === currentUserId;
18+
}
19+
20+
// For team workflows, use PBAC
21+
const permissionService = new PermissionCheckService();
22+
23+
// Determine fallback roles based on permission type
24+
const fallbackRoles =
25+
permission === "workflow.read"
26+
? [MembershipRole.ADMIN, MembershipRole.OWNER, MembershipRole.MEMBER]
27+
: [MembershipRole.ADMIN, MembershipRole.OWNER];
28+
29+
return await permissionService.checkPermission({
30+
userId: currentUserId,
31+
teamId: workflow.teamId,
32+
permission,
33+
fallbackRoles,
34+
});
35+
}

0 commit comments

Comments
 (0)