Skip to content

Commit c1bcfcf

Browse files
nicktrnzomars
andauthored
fix: replace all async foreach callbacks (#10157)
Co-authored-by: zomars <[email protected]>
1 parent 83aea7d commit c1bcfcf

File tree

8 files changed

+251
-225
lines changed

8 files changed

+251
-225
lines changed

apps/web/playwright/booking-pages.e2e.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -158,9 +158,10 @@ test.describe("pro user", () => {
158158

159159
await expect(page.locator("[data-testid=success-page]")).toBeVisible();
160160

161-
additionalGuests.forEach(async (email) => {
161+
const promises = additionalGuests.map(async (email) => {
162162
await expect(page.locator(`[data-testid="attendee-email-${email}"]`)).toHaveText(email);
163163
});
164+
await Promise.all(promises);
164165
});
165166

166167
test("Time slots should be reserved when selected", async ({ context, page }) => {

packages/app-store/vital/lib/reschedule.ts

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -121,18 +121,21 @@ const Reschedule = async (bookingUid: string, cancellationReason: string) => {
121121
const bookingRefsFiltered: BookingReference[] = bookingToReschedule.references.filter(
122122
(ref) => !!credentialsMap.get(ref.type)
123123
);
124+
125+
const promises = bookingRefsFiltered.map(async (bookingRef) => {
126+
if (!bookingRef.uid) return;
127+
128+
if (bookingRef.type.endsWith("_calendar")) {
129+
const calendar = await getCalendar(credentialsMap.get(bookingRef.type));
130+
return calendar?.deleteEvent(bookingRef.uid, builder.calendarEvent);
131+
} else if (bookingRef.type.endsWith("_video")) {
132+
return deleteMeeting(credentialsMap.get(bookingRef.type), bookingRef.uid);
133+
}
134+
});
124135
try {
125-
bookingRefsFiltered.forEach(async (bookingRef) => {
126-
if (bookingRef.uid) {
127-
if (bookingRef.type.endsWith("_calendar")) {
128-
const calendar = await getCalendar(credentialsMap.get(bookingRef.type));
129-
return calendar?.deleteEvent(bookingRef.uid, builder.calendarEvent);
130-
} else if (bookingRef.type.endsWith("_video")) {
131-
return deleteMeeting(credentialsMap.get(bookingRef.type), bookingRef.uid);
132-
}
133-
}
134-
});
136+
await Promise.all(promises);
135137
} catch (error) {
138+
// FIXME: error logging - non-Error type errors are currently discarded
136139
if (error instanceof Error) {
137140
logger.error(error.message);
138141
}

packages/app-store/wipemycalother/lib/reschedule.ts

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -121,17 +121,19 @@ const Reschedule = async (bookingUid: string, cancellationReason: string) => {
121121
const bookingRefsFiltered: BookingReference[] = bookingToReschedule.references.filter(
122122
(ref) => !!credentialsMap.get(ref.type)
123123
);
124+
125+
const promises = bookingRefsFiltered.map(async (bookingRef) => {
126+
if (!bookingRef.uid) return;
127+
128+
if (bookingRef.type.endsWith("_calendar")) {
129+
const calendar = await getCalendar(credentialsMap.get(bookingRef.type));
130+
return calendar?.deleteEvent(bookingRef.uid, builder.calendarEvent);
131+
} else if (bookingRef.type.endsWith("_video")) {
132+
return deleteMeeting(credentialsMap.get(bookingRef.type), bookingRef.uid);
133+
}
134+
});
124135
try {
125-
bookingRefsFiltered.forEach(async (bookingRef) => {
126-
if (bookingRef.uid) {
127-
if (bookingRef.type.endsWith("_calendar")) {
128-
const calendar = await getCalendar(credentialsMap.get(bookingRef.type));
129-
return calendar?.deleteEvent(bookingRef.uid, builder.calendarEvent);
130-
} else if (bookingRef.type.endsWith("_video")) {
131-
return deleteMeeting(credentialsMap.get(bookingRef.type), bookingRef.uid);
132-
}
133-
}
134-
});
136+
await Promise.all(promises);
135137
} catch (error) {
136138
if (error instanceof Error) {
137139
logger.error(error.message);

packages/app-store/zapier/lib/nodeScheduler.ts

Lines changed: 29 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ export async function scheduleTrigger(
1010
) {
1111
try {
1212
//schedule job to call subscriber url at the end of meeting
13+
// FIXME: in-process scheduling - job will vanish on server crash / restart
1314
const job = schedule.scheduleJob(
1415
`${subscriber.appId}_${subscriber.id}`,
1516
booking.endTime,
@@ -57,38 +58,39 @@ export async function cancelScheduledJobs(
5758
appId?: string | null,
5859
isReschedule?: boolean
5960
) {
60-
try {
61-
let scheduledJobs = booking.scheduledJobs || [];
61+
if (!booking.scheduledJobs) return;
6262

63-
if (booking.scheduledJobs) {
64-
booking.scheduledJobs.forEach(async (scheduledJob) => {
65-
if (appId) {
66-
if (scheduledJob.startsWith(appId)) {
67-
if (schedule.scheduledJobs[scheduledJob]) {
68-
schedule.scheduledJobs[scheduledJob].cancel();
69-
}
70-
scheduledJobs = scheduledJobs?.filter((job) => scheduledJob !== job) || [];
71-
}
72-
} else {
73-
//if no specific appId given, delete all scheduled jobs of booking
74-
if (schedule.scheduledJobs[scheduledJob]) {
75-
schedule.scheduledJobs[scheduledJob].cancel();
76-
}
77-
scheduledJobs = [];
63+
let scheduledJobs = booking.scheduledJobs || [];
64+
const promises = booking.scheduledJobs.map(async (scheduledJob) => {
65+
if (appId) {
66+
if (scheduledJob.startsWith(appId)) {
67+
if (schedule.scheduledJobs[scheduledJob]) {
68+
schedule.scheduledJobs[scheduledJob].cancel();
7869
}
70+
scheduledJobs = scheduledJobs?.filter((job) => scheduledJob !== job) || [];
71+
}
72+
} else {
73+
//if no specific appId given, delete all scheduled jobs of booking
74+
if (schedule.scheduledJobs[scheduledJob]) {
75+
schedule.scheduledJobs[scheduledJob].cancel();
76+
}
77+
scheduledJobs = [];
78+
}
7979

80-
if (!isReschedule) {
81-
await prisma.booking.update({
82-
where: {
83-
uid: booking.uid,
84-
},
85-
data: {
86-
scheduledJobs: scheduledJobs,
87-
},
88-
});
89-
}
80+
if (!isReschedule) {
81+
await prisma.booking.update({
82+
where: {
83+
uid: booking.uid,
84+
},
85+
data: {
86+
scheduledJobs: scheduledJobs,
87+
},
9088
});
9189
}
90+
});
91+
92+
try {
93+
await Promise.all(promises);
9294
} catch (error) {
9395
console.error("Error cancelling scheduled jobs", error);
9496
}

packages/atoms/build.mjs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ const libraries = [
1212
},
1313
];
1414

15-
libraries.forEach(async (lib) => {
15+
const promises = libraries.map(async (lib) => {
1616
await build({
1717
build: {
1818
outDir: `./dist/${lib.fileName}`,
@@ -29,3 +29,4 @@ libraries.forEach(async (lib) => {
2929
},
3030
});
3131
});
32+
await Promise.all(promises);

packages/features/bookings/lib/handleCancelBooking.ts

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -430,9 +430,9 @@ async function handler(req: CustomRequest) {
430430
bookingToDelete.recurringEventId &&
431431
allRemainingBookings
432432
) {
433-
bookingToDelete.user.credentials
433+
const promises = bookingToDelete.user.credentials
434434
.filter((credential) => credential.type.endsWith("_calendar"))
435-
.forEach(async (credential) => {
435+
.map(async (credential) => {
436436
const calendar = await getCalendar(credential);
437437
for (const updBooking of updatedBookings) {
438438
const bookingRef = updBooking.references.find((ref) => ref.type.includes("_calendar"));
@@ -443,6 +443,13 @@ async function handler(req: CustomRequest) {
443443
}
444444
}
445445
});
446+
try {
447+
await Promise.all(promises);
448+
} catch (error) {
449+
if (error instanceof Error) {
450+
logger.error(error.message);
451+
}
452+
}
446453
} else {
447454
apiDeletes.push(calendar?.deleteEvent(uid, evt, externalCalendarId) as Promise<unknown>);
448455
}
@@ -603,11 +610,13 @@ async function handler(req: CustomRequest) {
603610
});
604611

605612
// delete scheduled jobs of cancelled bookings
613+
// FIXME: async calls into ether
606614
updatedBookings.forEach((booking) => {
607615
cancelScheduledJobs(booking);
608616
});
609617

610618
//Workflows - cancel all reminders for cancelled bookings
619+
// FIXME: async calls into ether
611620
updatedBookings.forEach((booking) => {
612621
booking.workflowReminders.forEach((reminder) => {
613622
if (reminder.method === WorkflowMethods.EMAIL) {
@@ -622,11 +631,14 @@ async function handler(req: CustomRequest) {
622631

623632
const prismaPromises: Promise<unknown>[] = [bookingReferenceDeletes];
624633

625-
// @TODO: find a way in the future if a promise fails don't stop the rest of the promises
626-
// Also if emails fails try to requeue them
627634
try {
628-
await Promise.all(prismaPromises.concat(apiDeletes));
635+
const settled = await Promise.allSettled(prismaPromises.concat(apiDeletes));
636+
const rejected = settled.filter(({ status }) => status === "rejected") as PromiseRejectedResult[];
637+
if (rejected.length) {
638+
throw new Error(`Reasons: ${rejected.map(({ reason }) => reason)}`);
639+
}
629640

641+
// TODO: if emails fail try to requeue them
630642
await sendCancelledEmails(evt, { eventName: bookingToDelete?.eventType?.eventName });
631643
} catch (error) {
632644
console.error("Error deleting event", error);

0 commit comments

Comments
 (0)