Fixing count and redundant checks for recurring (#5426)

Co-authored-by: Alex van Andel <me@alexvanandel.com>
possible-fix/sentry^2
Leo Giovanetti 2022-11-08 17:59:44 -03:00 committed by GitHub
parent 97b99deb5d
commit 7d1fb7c659
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 43 additions and 22 deletions

View File

@ -3,10 +3,18 @@ import { AppsStatus } from "@calcom/types/Calendar";
import * as fetch from "@lib/core/http/fetch-wrapper"; import * as fetch from "@lib/core/http/fetch-wrapper";
import { BookingCreateBody, BookingResponse } from "@lib/types/booking"; import { BookingCreateBody, BookingResponse } from "@lib/types/booking";
type ExtendedBookingCreateBody = BookingCreateBody & { noEmail?: boolean; recurringCount?: number }; type ExtendedBookingCreateBody = BookingCreateBody & {
noEmail?: boolean;
recurringCount?: number;
appsStatus?: AppsStatus[] | undefined;
allRecurringDates?: string[];
currentRecurringIndex?: number;
};
const createRecurringBooking = async (data: ExtendedBookingCreateBody[]) => { const createRecurringBooking = async (data: ExtendedBookingCreateBody[]) => {
const createdBookings: BookingResponse[] = []; const createdBookings: BookingResponse[] = [];
const allRecurringDates: string[] = data.map((booking) => booking.start);
let appsStatus: AppsStatus[] | undefined = undefined;
// Reversing to accumulate results for noEmail instances first, to then lastly, create the // Reversing to accumulate results for noEmail instances first, to then lastly, create the
// emailed booking taking into account accumulated results to send app status accurately // emailed booking taking into account accumulated results to send app status accurately
for (let key = 0; key < data.length; key++) { for (let key = 0; key < data.length; key++) {
@ -23,22 +31,16 @@ const createRecurringBooking = async (data: ExtendedBookingCreateBody[]) => {
} }
return prev; return prev;
}, {} as { [key: string]: AppsStatus }); }, {} as { [key: string]: AppsStatus });
const appsStatus = Object.values(calcAppsStatus); appsStatus = Object.values(calcAppsStatus);
const response = await fetch.post<
ExtendedBookingCreateBody & { appsStatus: AppsStatus[] },
BookingResponse
>("/api/book/event", {
...booking,
appsStatus,
});
createdBookings.push(response);
} else {
const response = await fetch.post<ExtendedBookingCreateBody, BookingResponse>("/api/book/event", {
...booking,
noEmail: true,
});
createdBookings.push(response);
} }
const response = await fetch.post<ExtendedBookingCreateBody, BookingResponse>("/api/book/event", {
...booking,
appsStatus,
allRecurringDates,
currentRecurringIndex: key,
});
createdBookings.push(response);
} }
return createdBookings; return createdBookings;
}; };

View File

@ -9,7 +9,6 @@ import {
import async from "async"; import async from "async";
import { cloneDeep } from "lodash"; import { cloneDeep } from "lodash";
import type { NextApiRequest } from "next"; import type { NextApiRequest } from "next";
import { RRule } from "rrule";
import short from "short-uuid"; import short from "short-uuid";
import { v5 as uuidv5 } from "uuid"; import { v5 as uuidv5 } from "uuid";
import z from "zod"; import z from "zod";
@ -212,7 +211,11 @@ async function ensureAvailableUsers(
eventType: Awaited<ReturnType<typeof getEventTypesFromDB>> & { eventType: Awaited<ReturnType<typeof getEventTypesFromDB>> & {
users: User[]; users: User[];
}, },
input: { dateFrom: string; dateTo: string } input: { dateFrom: string; dateTo: string },
recurringDatesInfo?: {
allRecurringDates: string[] | undefined;
currentRecurringIndex: number | undefined;
}
) { ) {
const availableUsers: typeof eventType.users = []; const availableUsers: typeof eventType.users = [];
/** Let's start checking for availability */ /** Let's start checking for availability */
@ -243,9 +246,12 @@ async function ensureAvailableUsers(
let foundConflict = false; let foundConflict = false;
try { try {
if (eventType.recurringEvent) { if (
const recurringEvent = parseRecurringEvent(eventType.recurringEvent); eventType.recurringEvent &&
const allBookingDates = new RRule({ dtstart: new Date(input.dateFrom), ...recurringEvent }).all(); recurringDatesInfo?.currentRecurringIndex === 0 &&
recurringDatesInfo.allRecurringDates
) {
const allBookingDates = recurringDatesInfo.allRecurringDates.map((strDate) => new Date(strDate));
// Go through each date for the recurring event and check if each one's availability // Go through each date for the recurring event and check if each one's availability
// DONE: Decreased computational complexity from O(2^n) to O(n) by refactoring this loop to stop // DONE: Decreased computational complexity from O(2^n) to O(n) by refactoring this loop to stop
// running at the first unavailable time. // running at the first unavailable time.
@ -277,6 +283,8 @@ async function handler(req: NextApiRequest & { userId?: number | undefined }) {
const { const {
recurringCount, recurringCount,
allRecurringDates,
currentRecurringIndex,
noEmail, noEmail,
eventTypeSlug, eventTypeSlug,
eventTypeId, eventTypeId,
@ -376,10 +384,20 @@ async function handler(req: NextApiRequest & { userId?: number | undefined }) {
{ {
...eventType, ...eventType,
users, users,
...(eventType.recurringEvent && {
recurringEvent: {
...eventType.recurringEvent,
count: recurringCount || eventType.recurringEvent.count,
},
}),
}, },
{ {
dateFrom: reqBody.start, dateFrom: reqBody.start,
dateTo: reqBody.end, dateTo: reqBody.end,
},
{
allRecurringDates,
currentRecurringIndex,
} }
); );

View File

@ -513,7 +513,6 @@ export default class CloseCom {
description: "Generic Lead for Contacts created by Cal.com", description: "Generic Lead for Contacts created by Cal.com",
} }
): Promise<string> { ): Promise<string> {
debugger;
const closeComLeadNames = await this.lead.list({ query: { _fields: ["name", "id"] } }); const closeComLeadNames = await this.lead.list({ query: { _fields: ["name", "id"] } });
const searchLeadFromCalCom = closeComLeadNames.data.filter((lead) => lead.name === leadInfo.companyName); const searchLeadFromCalCom = closeComLeadNames.data.filter((lead) => lead.name === leadInfo.companyName);
if (searchLeadFromCalCom.length === 0) { if (searchLeadFromCalCom.length === 0) {

View File

@ -150,6 +150,8 @@ export const extendedBookingCreateBody = bookingCreateBodySchema.merge(
z.object({ z.object({
noEmail: z.boolean().optional(), noEmail: z.boolean().optional(),
recurringCount: z.number().optional(), recurringCount: z.number().optional(),
allRecurringDates: z.string().array().optional(),
currentRecurringIndex: z.number().optional(),
rescheduleReason: z.string().optional(), rescheduleReason: z.string().optional(),
smsReminderNumber: z.string().optional(), smsReminderNumber: z.string().optional(),
appsStatus: z appsStatus: z