Skip to content

Commit d9cddd8

Browse files
fix: break circular dependency by passing creditCheckFn in messageDispatcher (calcom#25343)
* fix: break circular dependency in messageDispatcher via dependency injection 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> * feat: wire creditCheckFn from all callers to complete circular dependency 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> * fix: use generic type with type guard in logFailedResults to fix type 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> * wip * wip * wip * revert * revert * feat: wire creditCheckFn from all remaining callers to eliminate fallbacks - 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> * test: update formSubmissionUtils tests to expect creditCheckFn parameter 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> * test: update sms-manager test to expect creditCheckFn parameter 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> * feat: make creditCheckFn required to fully break circular dependency 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> * fix: make creditCheckFn required in WorkflowService.scheduleFormWorkflows 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> * remove * fix * refactor * refactor * refactor * wip * fix * fix * rm --------- Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
1 parent 1ab4b9d commit d9cddd8

24 files changed

Lines changed: 247 additions & 109 deletions

packages/app-store/routing-forms/lib/formSubmissionUtils.test.ts

Lines changed: 63 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -158,25 +158,28 @@ describe("_onFormSubmission", () => {
158158
await _onFormSubmission(mockForm, mockResponse, responseId);
159159

160160
expect(WorkflowService.getAllWorkflowsFromRoutingForm).toHaveBeenCalledWith(mockForm);
161-
expect(WorkflowService.scheduleFormWorkflows).toHaveBeenCalledWith({
162-
workflows: mockWorkflows,
163-
responses: {
164-
email: {
165-
value: "test@response.com",
166-
response: "test@response.com",
161+
expect(WorkflowService.scheduleFormWorkflows).toHaveBeenCalledWith(
162+
expect.objectContaining({
163+
workflows: mockWorkflows,
164+
responses: {
165+
email: {
166+
value: "test@response.com",
167+
response: "test@response.com",
168+
},
169+
name: { value: "Test Name", response: "Test Name" },
167170
},
168-
name: { value: "Test Name", response: "Test Name" },
169-
},
170-
responseId,
171-
routedEventTypeId: null,
172-
form: {
173-
...mockForm,
174-
fields: mockForm.fields.map((field) => ({
175-
type: field.type,
176-
identifier: field.identifier,
177-
})),
178-
},
179-
});
171+
responseId,
172+
routedEventTypeId: null,
173+
form: {
174+
...mockForm,
175+
fields: mockForm.fields.map((field) => ({
176+
type: field.type,
177+
identifier: field.identifier,
178+
})),
179+
},
180+
creditCheckFn: expect.any(Function),
181+
})
182+
);
180183
});
181184

182185
it("should call WorkflowService.scheduleFormWorkflows for FORM_SUBMITTED_NO_EVENT workflows", async () => {
@@ -212,25 +215,28 @@ describe("_onFormSubmission", () => {
212215
await _onFormSubmission(mockForm, mockResponse, responseId);
213216

214217
expect(WorkflowService.getAllWorkflowsFromRoutingForm).toHaveBeenCalledWith(mockForm);
215-
expect(WorkflowService.scheduleFormWorkflows).toHaveBeenCalledWith({
216-
workflows: mockWorkflows,
217-
responses: {
218-
email: {
219-
value: "test@response.com",
220-
response: "test@response.com",
218+
expect(WorkflowService.scheduleFormWorkflows).toHaveBeenCalledWith(
219+
expect.objectContaining({
220+
workflows: mockWorkflows,
221+
responses: {
222+
email: {
223+
value: "test@response.com",
224+
response: "test@response.com",
225+
},
226+
name: { value: "Test Name", response: "Test Name" },
221227
},
222-
name: { value: "Test Name", response: "Test Name" },
223-
},
224-
routedEventTypeId: null,
225-
responseId,
226-
form: {
227-
...mockForm,
228-
fields: mockForm.fields.map((field) => ({
229-
type: field.type,
230-
identifier: field.identifier,
231-
})),
232-
},
233-
});
228+
routedEventTypeId: null,
229+
responseId,
230+
form: {
231+
...mockForm,
232+
fields: mockForm.fields.map((field) => ({
233+
type: field.type,
234+
identifier: field.identifier,
235+
})),
236+
},
237+
creditCheckFn: expect.any(Function),
238+
})
239+
);
234240
});
235241

236242
it("should pass routedEventTypeId when chosenAction is eventTypeRedirectUrl", async () => {
@@ -271,25 +277,28 @@ describe("_onFormSubmission", () => {
271277

272278
await _onFormSubmission(mockForm, mockResponse, responseId, chosenAction);
273279

274-
expect(WorkflowService.scheduleFormWorkflows).toHaveBeenCalledWith({
275-
workflows: mockWorkflows,
276-
responses: {
277-
email: {
278-
value: "test@response.com",
279-
response: "test@response.com",
280+
expect(WorkflowService.scheduleFormWorkflows).toHaveBeenCalledWith(
281+
expect.objectContaining({
282+
workflows: mockWorkflows,
283+
responses: {
284+
email: {
285+
value: "test@response.com",
286+
response: "test@response.com",
287+
},
288+
name: { value: "Test Name", response: "Test Name" },
280289
},
281-
name: { value: "Test Name", response: "Test Name" },
282-
},
283-
routedEventTypeId: 42,
284-
responseId,
285-
form: {
286-
...mockForm,
287-
fields: mockForm.fields.map((field) => ({
288-
type: field.type,
289-
identifier: field.identifier,
290-
})),
291-
},
292-
});
290+
routedEventTypeId: 42,
291+
responseId,
292+
form: {
293+
...mockForm,
294+
fields: mockForm.fields.map((field) => ({
295+
type: field.type,
296+
identifier: field.identifier,
297+
})),
298+
},
299+
creditCheckFn: expect.any(Function),
300+
})
301+
);
293302
});
294303
});
295304

packages/app-store/routing-forms/lib/formSubmissionUtils.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import dayjs from "@calcom/dayjs";
2+
import { CreditService } from "@calcom/features/ee/billing/credit-service";
23
import { WorkflowService } from "@calcom/features/ee/workflows/lib/service/WorkflowService";
34
import type { Tasker } from "@calcom/features/tasker/tasker";
45
import getWebhooks from "@calcom/features/webhooks/lib/getWebhooks";
@@ -224,11 +225,15 @@ export async function _onFormSubmission(
224225
chosenAction && chosenAction.type === "eventTypeRedirectUrl" && chosenAction.eventTypeId
225226
? chosenAction.eventTypeId
226227
: null;
228+
229+
const creditService = new CreditService();
230+
227231
await WorkflowService.scheduleFormWorkflows({
228232
workflows,
229233
responseId,
230234
responses: fieldResponsesByIdentifier,
231235
routedEventTypeId,
236+
creditCheckFn: creditService.hasAvailableCredits.bind(creditService),
232237
form: {
233238
...form,
234239
fields: form.fields.map((field) => ({

packages/features/bookings/lib/handleBookingRequested.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { sendAttendeeRequestEmailAndSMS, sendOrganizerRequestEmail } from "@calcom/emails/email-manager";
22
import { getWebhookPayloadForBooking } from "@calcom/features/bookings/lib/getWebhookPayloadForBooking";
3+
import { CreditService } from "@calcom/features/ee/billing/credit-service";
34
import { WorkflowService } from "@calcom/features/ee/workflows/lib/service/WorkflowService";
45
import type { Workflow } from "@calcom/features/ee/workflows/lib/types";
56
import getWebhooks from "@calcom/features/webhooks/lib/getWebhooks";
@@ -103,6 +104,8 @@ export async function handleBookingRequested(args: {
103104

104105
const workflows = await getAllWorkflowsFromEventType(booking.eventType, booking.userId);
105106
if (workflows.length > 0) {
107+
const creditService = new CreditService();
108+
106109
await WorkflowService.scheduleWorkflowsFilteredByTriggerEvent({
107110
workflows,
108111
smsReminderNumber: booking.smsReminderNumber,
@@ -117,6 +120,7 @@ export async function handleBookingRequested(args: {
117120
},
118121
},
119122
triggers: [WorkflowTriggerEvents.BOOKING_REQUESTED],
123+
creditCheckFn: creditService.hasAvailableCredits.bind(creditService),
120124
});
121125
}
122126
} catch (error) {

packages/features/bookings/lib/handleCancelBooking.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import EventManager from "@calcom/features/bookings/lib/EventManager";
99
import { getCalEventResponses } from "@calcom/features/bookings/lib/getCalEventResponses";
1010
import { processNoShowFeeOnCancellation } from "@calcom/features/bookings/lib/payment/processNoShowFeeOnCancellation";
1111
import { processPaymentRefund } from "@calcom/features/bookings/lib/payment/processPaymentRefund";
12+
import { CreditService } from "@calcom/features/ee/billing/credit-service";
1213
import { getBookerBaseUrl } from "@calcom/features/ee/organizations/lib/getBookerUrlServer";
1314
import { sendCancelledReminders } from "@calcom/features/ee/workflows/lib/reminders/reminderScheduler";
1415
import { WorkflowRepository } from "@calcom/features/ee/workflows/repositories/WorkflowRepository";
@@ -353,6 +354,8 @@ async function handler(input: CancelBookingInput) {
353354
const workflows = await getAllWorkflowsFromEventType(bookingToDelete.eventType, bookingToDelete.userId);
354355
const parsedMetadata = bookingMetadataSchema.safeParse(bookingToDelete.metadata || {});
355356

357+
const creditService = new CreditService();
358+
356359
await sendCancelledReminders({
357360
workflows,
358361
smsReminderNumber: bookingToDelete.smsReminderNumber,
@@ -371,6 +374,7 @@ async function handler(input: CancelBookingInput) {
371374
},
372375
},
373376
hideBranding: !!bookingToDelete.eventType?.owner?.hideBranding,
377+
creditCheckFn: creditService.hasAvailableCredits.bind(creditService),
374378
});
375379

376380
let updatedBookings: {

packages/features/bookings/lib/handleConfirmation.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { scheduleMandatoryReminder } from "@calcom/ee/workflows/lib/reminders/sc
33
import { sendScheduledEmailsAndSMS } from "@calcom/emails/email-manager";
44
import type { EventManagerUser } from "@calcom/features/bookings/lib/EventManager";
55
import EventManager, { placeholderCreatedEvent } from "@calcom/features/bookings/lib/EventManager";
6+
import { CreditService } from "@calcom/features/ee/billing/credit-service";
67
import { getBookerBaseUrl } from "@calcom/features/ee/organizations/lib/getBookerUrlServer";
78
import {
89
allowDisablingAttendeeConfirmationEmails,
@@ -360,6 +361,8 @@ export async function handleConfirmation(args: {
360361
});
361362
}
362363

364+
const creditService = new CreditService();
365+
363366
await WorkflowService.scheduleWorkflowsForNewBooking({
364367
workflows,
365368
smsReminderNumber: updatedBookings[index].smsReminderNumber,
@@ -368,6 +371,7 @@ export async function handleConfirmation(args: {
368371
isConfirmedByDefault: true,
369372
isNormalBookingOrFirstRecurringSlot: isFirstBooking,
370373
isRescheduleEvent: false,
374+
creditCheckFn: creditService.hasAvailableCredits.bind(creditService),
371375
});
372376
}
373377
} catch (error) {
@@ -571,12 +575,15 @@ export async function handleConfirmation(args: {
571575
metadata: { videoCallUrl: meetingUrl },
572576
};
573577

578+
const creditService = new CreditService();
579+
574580
await WorkflowService.scheduleWorkflowsFilteredByTriggerEvent({
575581
workflows,
576582
smsReminderNumber: booking.smsReminderNumber,
577583
calendarEvent: calendarEventForWorkflow,
578584
hideBranding: !!updatedBookings[0].eventType?.owner?.hideBranding,
579585
triggers: [WorkflowTriggerEvents.BOOKING_PAID],
586+
creditCheckFn: creditService.hasAvailableCredits.bind(creditService),
580587
});
581588
} catch (error) {
582589
log.error("Error while scheduling workflow reminders for booking paid", safeStringify(error));

packages/features/bookings/lib/handleSeats/handleSeats.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
21
import dayjs from "@calcom/dayjs";
32
import { handleWebhookTrigger } from "@calcom/features/bookings/lib/handleWebhookTrigger";
3+
import { CreditService } from "@calcom/features/ee/billing/credit-service";
44
import { WorkflowService } from "@calcom/features/ee/workflows/lib/service/WorkflowService";
55
import type { EventPayloadType } from "@calcom/features/webhooks/lib/sendPayload";
66
import { ErrorCode } from "@calcom/lib/errorCodes";
@@ -108,6 +108,8 @@ const handleSeats = async (newSeatedBookingObject: NewSeatedBookingObject) => {
108108
...reqBodyMetadata,
109109
};
110110
try {
111+
const creditService = new CreditService();
112+
111113
await WorkflowService.scheduleWorkflowsForNewBooking({
112114
workflows: workflows,
113115
smsReminderNumber: smsReminderNumber || null,
@@ -130,6 +132,7 @@ const handleSeats = async (newSeatedBookingObject: NewSeatedBookingObject) => {
130132
isConfirmedByDefault: !evt.requiresConfirmation,
131133
isRescheduleEvent: !!rescheduleUid,
132134
isNormalBookingOrFirstRecurringSlot: true,
135+
creditCheckFn: creditService.hasAvailableCredits.bind(creditService),
133136
});
134137
} catch (error) {
135138
loggerWithEventDetails.error("Error while scheduling workflow reminders", JSON.stringify({ error }));

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

Lines changed: 26 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ import { handleWebhookTrigger } from "@calcom/features/bookings/lib/handleWebhoo
3535
import { isEventTypeLoggingEnabled } from "@calcom/features/bookings/lib/isEventTypeLoggingEnabled";
3636
import { BookingEventHandlerService } from "@calcom/features/bookings/lib/onBookingEvents/BookingEventHandlerService";
3737
import { getSpamCheckService } from "@calcom/features/di/watchlist/containers/SpamCheckService.container";
38+
import { CreditService } from "@calcom/features/ee/billing/credit-service";
3839
import { getBookerBaseUrl } from "@calcom/features/ee/organizations/lib/getBookerUrlServer";
3940
import AssignmentReasonRecorder from "@calcom/features/ee/round-robin/assignmentReason/AssignmentReasonRecorder";
4041
import { WorkflowService } from "@calcom/features/ee/workflows/lib/service/WorkflowService";
@@ -1261,9 +1262,9 @@ async function handler(
12611262
// This ensures that createMeeting isn't called for static video apps as bookingLocation becomes just a regular value for them.
12621263
const { bookingLocation, conferenceCredentialId } = organizerOrFirstDynamicGroupMemberDefaultLocationUrl
12631264
? {
1264-
bookingLocation: organizerOrFirstDynamicGroupMemberDefaultLocationUrl,
1265-
conferenceCredentialId: undefined,
1266-
}
1265+
bookingLocation: organizerOrFirstDynamicGroupMemberDefaultLocationUrl,
1266+
conferenceCredentialId: undefined,
1267+
}
12671268
: getLocationValueForDB(locationBodyString, eventType.locations);
12681269

12691270
tracingLogger.info("locationBodyString", locationBodyString);
@@ -1309,8 +1310,8 @@ async function handler(
13091310
const destinationCalendar = eventType.destinationCalendar
13101311
? [eventType.destinationCalendar]
13111312
: organizerUser.destinationCalendar
1312-
? [organizerUser.destinationCalendar]
1313-
: null;
1313+
? [organizerUser.destinationCalendar]
1314+
: null;
13141315

13151316
let organizerEmail = organizerUser.email || "Email-less";
13161317
if (eventType.useEventTypeDestinationCalendarEmail && destinationCalendar?.[0]?.primaryEmail) {
@@ -1931,14 +1932,14 @@ async function handler(
19311932
}
19321933
const updateManager = !skipCalendarSyncTaskCreation
19331934
? await eventManager.reschedule(
1934-
evt,
1935-
originalRescheduledBooking.uid,
1936-
undefined,
1937-
changedOrganizer,
1938-
previousHostDestinationCalendar,
1939-
isBookingRequestedReschedule,
1940-
skipDeleteEventsAndMeetings
1941-
)
1935+
evt,
1936+
originalRescheduledBooking.uid,
1937+
undefined,
1938+
changedOrganizer,
1939+
previousHostDestinationCalendar,
1940+
isBookingRequestedReschedule,
1941+
skipDeleteEventsAndMeetings
1942+
)
19421943
: placeholderCreatedEvent;
19431944
// This gets overridden when updating the event - to check if notes have been hidden or not. We just reset this back
19441945
// to the default description when we are sending the emails.
@@ -2237,8 +2238,8 @@ async function handler(
22372238

22382239
const metadata = videoCallUrl
22392240
? {
2240-
videoCallUrl: getVideoCallUrlFromCalEvent(evt) || videoCallUrl,
2241-
}
2241+
videoCallUrl: getVideoCallUrlFromCalEvent(evt) || videoCallUrl,
2242+
}
22422243
: undefined;
22432244

22442245
const bookingFlowConfig = {
@@ -2331,9 +2332,9 @@ async function handler(
23312332
...eventType,
23322333
metadata: eventType.metadata
23332334
? {
2334-
...eventType.metadata,
2335-
apps: eventType.metadata?.apps as Prisma.JsonValue,
2336-
}
2335+
...eventType.metadata,
2336+
apps: eventType.metadata?.apps as Prisma.JsonValue,
2337+
}
23372338
: {},
23382339
},
23392340
paymentAppCredentials: eventTypePaymentAppCredential as IEventTypePaymentCredentialType,
@@ -2377,6 +2378,8 @@ async function handler(
23772378
};
23782379

23792380
if (isNormalBookingOrFirstRecurringSlot) {
2381+
const creditService = new CreditService();
2382+
23802383
await WorkflowService.scheduleWorkflowsFilteredByTriggerEvent({
23812384
workflows,
23822385
smsReminderNumber: smsReminderNumber || null,
@@ -2385,6 +2388,7 @@ async function handler(
23852388
seatReferenceUid: evt.attendeeSeatId,
23862389
isDryRun,
23872390
triggers: [WorkflowTriggerEvents.BOOKING_PAYMENT_INITIATED],
2391+
creditCheckFn: creditService.hasAvailableCredits.bind(creditService),
23882392
});
23892393
}
23902394
} catch (error) {
@@ -2553,6 +2557,8 @@ async function handler(
25532557
}
25542558

25552559
try {
2560+
const creditService = new CreditService();
2561+
25562562
await WorkflowService.scheduleWorkflowsForNewBooking({
25572563
workflows,
25582564
smsReminderNumber: smsReminderNumber || null,
@@ -2563,6 +2569,7 @@ async function handler(
25632569
isConfirmedByDefault,
25642570
isNormalBookingOrFirstRecurringSlot,
25652571
isRescheduleEvent: !!rescheduleUid,
2572+
creditCheckFn: creditService.hasAvailableCredits.bind(creditService),
25662573
});
25672574
} catch (error) {
25682575
tracingLogger.error("Error while scheduling workflow reminders", JSON.stringify({ error }));
@@ -2630,7 +2637,7 @@ async function handler(
26302637
* We are open to renaming it to something more descriptive.
26312638
*/
26322639
export class RegularBookingService implements IBookingService {
2633-
constructor(private readonly deps: IBookingServiceDependencies) { }
2640+
constructor(private readonly deps: IBookingServiceDependencies) {}
26342641

26352642
async createBooking(input: { bookingData: CreateRegularBookingData; bookingMeta?: CreateBookingMeta }) {
26362643
return handler({ bookingData: input.bookingData, ...input.bookingMeta }, this.deps);

0 commit comments

Comments
 (0)