fix: break circular dependency by passing creditCheckFn in messageDispatcher#25343
Merged
fix: break circular dependency by passing creditCheckFn in messageDispatcher#25343
Conversation
…jection Break the 4-file circular dependency chain: credit-service → reminderScheduler → smsReminderManager → messageDispatcher → credit-service Solution: - Add optional creditCheckFn parameter to messageDispatcher functions - Thread creditCheckFn through the call chain: scheduleWorkflowReminders → scheduleSMSReminder/scheduleWhatsappReminder → messageDispatcher - When creditCheckFn is provided, use it; otherwise fall back to dynamic CreditService import for backward compatibility - This breaks the workflows → billing import while preserving immediate fallback behavior Changes: - messageDispatcher: Accept optional creditCheckFn parameter, use it if provided - smsReminderManager: Thread creditCheckFn through scheduleSMSReminder - whatsappReminderManager: Thread creditCheckFn through scheduleWhatsappReminder - reminderScheduler: Add creditCheckFn to ScheduleWorkflowRemindersArgs and pass through processWorkflowStep All type checks, lint checks, and unit tests pass. Co-Authored-By: benny@cal.com <sldisek783@gmail.com>
Contributor
🤖 Devin AI EngineerI'll be helping with this pull request! Here's what you should know: ✅ I will automatically:
Note: I can only respond to comments from users who have write access to this repository. ⚙️ Control Options:
|
10 tasks
…ency fix - Add creditCheckFn parameter to WorkflowService.scheduleFormWorkflows - Wire creditCheckFn from all 10 entry points that call workflow scheduling: * formSubmissionUtils.ts (form submissions) * roundRobinManualReassignment.ts (round-robin reassignment) * triggerFormSubmittedNoEventWorkflow.ts (form workflow trigger) * handleBookingRequested.ts (booking requests) * RegularBookingService.ts (2 calls - payment initiated & new bookings) * handleSeats.ts (seated bookings) * handleConfirmation.ts (2 calls - confirmation & payment) * handleMarkNoShow.ts (no-show updates) * confirm.handler.ts (booking rejection) - Update test expectations to use expect.objectContaining() - Fix pre-existing lint warning in handleMarkNoShow.ts (any type) - This completes the messageDispatcher circular dependency fix by ensuring creditCheckFn is actually passed through the call chain, breaking the 4-file circular dependency at runtime Co-Authored-By: benny@cal.com <sldisek783@gmail.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
… check error - Replace constrained type with generic type parameter - Add proper type guard for rejected promises - Fixes CI type check failure in handleMarkNoShow.ts:385 - Avoids 'any' type while accepting any fulfilled value shape Co-Authored-By: benny@cal.com <sldisek783@gmail.com>
Contributor
E2E results are ready! |
2821381 to
4f4cf37
Compare
…backs - Wire creditCheckFn in packages/sms/sms-manager.ts (can safely import CreditService) - Create makeHandler factory pattern for CRON endpoints (scheduleSMSReminders.ts, scheduleWhatsappReminders.ts) - Wire creditCheckFn from apps/web CRON routes to factories - Add warning log in messageDispatcher when fallback is used - Complete creditCheckFn wiring from all direct callers (activateEventType.handler.ts, util.ts) This eliminates all fallbacks to dynamic import except as a safety net for unforeseen call sites. The circular dependency (workflows ↔ billing) remains acceptable as discussed with user (Option C). Co-Authored-By: benny@cal.com <sldisek783@gmail.com>
The scheduleFormWorkflows function now receives creditCheckFn as a parameter. Updated test assertions to use expect.objectContaining() with creditCheckFn: expect.any(Function) to account for the new dependency injection parameter. Co-Authored-By: benny@cal.com <sldisek783@gmail.com>
The sendSmsOrFallbackEmail function now receives creditCheckFn as a parameter. Updated test assertion to use expect.objectContaining() with creditCheckFn: expect.any(Function) to account for the new dependency injection parameter. Also removed teamId: undefined assertion as the key may be omitted entirely from the actual call. Co-Authored-By: benny@cal.com <sldisek783@gmail.com>
This commit completes the circular dependency fix by making creditCheckFn required throughout the call chain, eliminating the dynamic import fallback entirely. Changes: - Make creditCheckFn required in messageDispatcher functions (sendSmsOrFallbackEmail, scheduleSmsOrFallbackEmail) - Remove dynamic import fallback and warning log from messageDispatcher - Make creditCheckFn required in ScheduleTextReminderArgs (smsReminderManager) - Make creditCheckFn required in processWorkflowStep and ScheduleWorkflowRemindersArgs (reminderScheduler) - Add creditCheckFn to SendCancelledRemindersArgs and wire from handleCancelBooking The circular dependency is now fully broken - no more dynamic imports of CreditService from within the workflows package. All callers must explicitly provide creditCheckFn via dependency injection. Co-Authored-By: benny@cal.com <sldisek783@gmail.com>
…lows This commit fixes the CI type check error by making creditCheckFn required in WorkflowService.scheduleFormWorkflows. Previously, creditCheckFn was optional in scheduleFormWorkflows but required in scheduleWorkflowReminders, causing a type mismatch. Changes: - Make creditCheckFn required in scheduleFormWorkflows signature - Update WorkflowService.test.ts to pass mock creditCheckFn in all test cases - Add responseId and routedEventTypeId to test calls for completeness All callers of scheduleFormWorkflows already pass creditCheckFn, so this change is safe and completes the circular dependency fix. Co-Authored-By: benny@cal.com <sldisek783@gmail.com>
Contributor
There was a problem hiding this comment.
13 issues found across 24 files
Prompt for AI agents (all 13 issues)
Understand the root cause of the following 13 issues and fix them.
<file name="packages/features/handleMarkNoShow.ts">
<violation number="1" location="packages/features/handleMarkNoShow.ts:307">
Bind CreditService.hasAvailableCredits before passing it as creditCheckFn; the current unbound method loses its this context and will throw at runtime, preventing reminders from being sent.</violation>
</file>
<file name="packages/features/ee/workflows/api/scheduleWhatsappReminders.ts">
<violation number="1" location="packages/features/ee/workflows/api/scheduleWhatsappReminders.ts:130">
Bind CreditService.hasAvailableCredits before passing it to scheduleSmsOrFallbackEmail; otherwise the callback runs with the wrong `this` and crashes when it calls private helpers.</violation>
</file>
<file name="packages/sms/sms-manager.ts">
<violation number="1" location="packages/sms/sms-manager.ts:51">
Passing `creditService.hasAvailableCredits` unbound drops its `this` context, so the credit check throws at runtime. Bind or wrap the method before injecting it.</violation>
</file>
<file name="packages/features/bookings/lib/handleSeats/handleSeats.ts">
<violation number="1" location="packages/features/bookings/lib/handleSeats/handleSeats.ts:135">
Bind the CreditService instance when wiring creditCheckFn; passing the method unbound causes runtime failures when messageDispatcher calls it without a `this` context.</violation>
</file>
<file name="packages/app-store/routing-forms/lib/formSubmissionUtils.ts">
<violation number="1" location="packages/app-store/routing-forms/lib/formSubmissionUtils.ts:236">
Passing the instance method without binding drops its `this` context, so any workflow reminder credit check will throw. Bind the method before passing it.</violation>
</file>
<file name="packages/features/bookings/lib/handleBookingRequested.ts">
<violation number="1" location="packages/features/bookings/lib/handleBookingRequested.ts:123">
Bind the CreditService instance when passing `creditCheckFn`; otherwise `this` becomes undefined and credit checks throw once the limit-reached path executes.</violation>
</file>
<file name="packages/features/ee/workflows/api/scheduleSMSReminders.ts">
<violation number="1" location="packages/features/ee/workflows/api/scheduleSMSReminders.ts:199">
creditCheckFn is passed an unbound instance method, so inside messageDispatcher `this` becomes undefined and credit checks throw before reminders can be scheduled. Bind the method to the CreditService instance.</violation>
</file>
<file name="packages/trpc/server/routers/viewer/workflows/activateEventType.handler.ts">
<violation number="1" location="packages/trpc/server/routers/viewer/workflows/activateEventType.handler.ts:425">
`creditService.hasAvailableCredits` is passed unbound, so `this` is undefined when the reminder invokes `creditCheckFn`, crashing credit checks.</violation>
</file>
<file name="packages/features/ee/round-robin/roundRobinManualReassignment.ts">
<violation number="1" location="packages/features/ee/round-robin/roundRobinManualReassignment.ts:618">
`creditCheckFn` is passed an unbound instance method, so `hasAvailableCredits` will throw when accessed without its `this` context.</violation>
</file>
<file name="packages/features/bookings/lib/handleCancelBooking.ts">
<violation number="1" location="packages/features/bookings/lib/handleCancelBooking.ts:377">
Bind CreditService.hasAvailableCredits before passing it as creditCheckFn; otherwise the callback throws because it relies on this.</violation>
</file>
<file name="packages/features/bookings/lib/handleConfirmation.ts">
<violation number="1" location="packages/features/bookings/lib/handleConfirmation.ts:374">
Passing `creditService.hasAvailableCredits` directly loses the `this` binding, so workflow reminders will crash when `creditCheckFn` runs. Bind the method (e.g., `.bind(creditService)` or wrap in a lambda) before passing it down.</violation>
</file>
<file name="packages/features/bookings/lib/service/RegularBookingService.ts">
<violation number="1" location="packages/features/bookings/lib/service/RegularBookingService.ts:2391">
Bind the hasAvailableCredits method before passing it into scheduleWorkflowsForNewBooking so the callback keeps the CreditService instance context.</violation>
</file>
<file name="packages/trpc/server/routers/viewer/workflows/util.ts">
<violation number="1" location="packages/trpc/server/routers/viewer/workflows/util.ts:847">
`creditService.hasAvailableCredits` is passed without binding, so the credit check throws when messageDispatcher calls it. Bind the method to the instance before injecting.</violation>
</file>
Reply to cubic to teach it or ask questions. Re-run a review with @cubic-dev-ai review this PR
packages/features/ee/workflows/api/scheduleWhatsappReminders.ts
Outdated
Show resolved
Hide resolved
packages/features/ee/round-robin/roundRobinManualReassignment.ts
Outdated
Show resolved
Hide resolved
packages/features/bookings/lib/service/RegularBookingService.ts
Outdated
Show resolved
Hide resolved
hbjORbj
commented
Nov 26, 2025
| t: TFunction; | ||
| replyTo: string; | ||
| }; | ||
| creditCheckFn: CreditCheckFn; |
hbjORbj
commented
Nov 26, 2025
| replyTo: string; | ||
| workflowStepId?: number; | ||
| }; | ||
| creditCheckFn: CreditCheckFn; |
Comment on lines
48
to
49
| const isRejected = <T>(x: PromiseSettledResult<T>): x is PromiseRejectedResult => x.status === "rejected"; | ||
|
|
Contributor
There was a problem hiding this comment.
is this used somewhere ?
Contributor
Author
There was a problem hiding this comment.
ah no, will revert now
ThyMinimalDev
approved these changes
Nov 27, 2025
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What does this PR do?
This PR completes the circular dependency fix between the workflows and billing packages by making
creditCheckFna required parameter throughout the workflow reminder system, eliminating the dynamic import fallback entirely.Context: This builds on PR #25312 which fixed the
reminderSchedulercircular dependency. This PR addresses the remaining circular dependency inmessageDispatcherand related functions.The circular dependency:
The solution: Use dependency injection by making
creditCheckFnrequired and wiring it from all callers (which live outside the workflows package and can safely importCreditService).Mandatory Tasks