Refactor and cleaning up code

This commit is contained in:
Alan 2022-04-13 03:18:24 -06:00
parent ad2b6fbdce
commit 2b07cab25a
6 changed files with 110 additions and 53 deletions

View File

@ -1,12 +1,12 @@
import { BookingStatus, User, SchedulingType, Booking, Attendee } from "@prisma/client";
import { BookingStatus, User, Booking, Attendee } from "@prisma/client";
import dayjs from "dayjs";
import type { NextApiHandler, NextApiRequest, NextApiResponse } from "next";
import type { NextApiRequest, NextApiResponse } from "next";
import { getSession } from "next-auth/react";
import type { TFunction } from "next-i18next";
import { z, ZodError } from "zod";
import { CalendarEventBuilder } from "@calcom/core/builders/CalendarEvent/builder";
import { CalendarEventClass } from "@calcom/core/builders/CalendarEvent/class";
import { CalendarEventDirector } from "@calcom/core/builders/CalendarEvent/director";
import { getTranslation } from "@calcom/lib/server/i18n";
import { Person } from "@calcom/types/Calendar";
@ -16,6 +16,10 @@ import prisma from "@lib/prisma";
export type RescheduleResponse = Booking & {
attendees: Attendee[];
};
export type PersonAttendeeCommonFields = Pick<
User,
"id" | "email" | "name" | "locale" | "timeZone" | "username"
>;
const rescheduleSchema = z.object({
bookingId: z.string(),
@ -27,9 +31,11 @@ const handler = async (
res: NextApiResponse
): Promise<RescheduleResponse | NextApiResponse | void> => {
const session = await getSession({ req });
const { bookingId, rescheduleReason: cancellationReason } = req.body;
type PersonAttendee = Pick<User, "id" | "email" | "name" | "locale" | "timeZone" | "username">;
let userOwner: PersonAttendee | null;
const {
bookingId,
rescheduleReason: cancellationReason,
}: { bookingId: string; rescheduleReason: string; cancellationReason: string } = req.body;
let userOwner: PersonAttendeeCommonFields | null;
try {
if (session?.user?.id) {
userOwner = await prisma.user.findUnique({
@ -51,17 +57,35 @@ const handler = async (
}
const bookingToReschedule = await prisma.booking.findFirst({
select: {
id: true,
uid: true,
title: true,
startTime: true,
endTime: true,
eventTypeId: true,
location: true,
attendees: true,
},
rejectOnNotFound: true,
include: { attendees: true },
where: {
uid: bookingId,
NOT: {
status: BookingStatus.CANCELLED,
status: {
in: [BookingStatus.CANCELLED, BookingStatus.REJECTED],
},
},
},
});
if (bookingToReschedule && bookingToReschedule.eventTypeId && userOwner) {
const event = await prisma.eventType.findFirst({
select: {
title: true,
users: true,
schedulingType: true,
},
rejectOnNotFound: true,
where: {
id: bookingToReschedule.eventTypeId,
},
@ -78,20 +102,15 @@ const handler = async (
},
});
// Soft delete
await prisma.bookingReference.deleteMany({
where: {
bookingId: bookingToReschedule.id,
},
});
// @NOTE: Lets assume all guests are the same language
const [firstAttendee] = bookingToReschedule.attendees;
const tAttendees = await getTranslation(firstAttendee.locale ?? "en", "common");
const usersToPeopleType = (users: PersonAttendee[], selectedLanguage: TFunction): Person[] => {
const [mainAttendee] = bookingToReschedule.attendees;
// @NOTE: Should we assume attendees language?
const tAttendees = await getTranslation(mainAttendee.locale ?? "en", "common");
const usersToPeopleType = (
users: PersonAttendeeCommonFields[],
selectedLanguage: TFunction
): Person[] => {
return users?.map((user) => {
return {
id: user.id || "",
email: user.email || "",
name: user.name || "",
username: user?.username || "",
@ -103,46 +122,37 @@ const handler = async (
const userOwnerTranslation = await getTranslation(userOwner.locale ?? "en", "common");
const [userOwnerAsPeopleType] = usersToPeopleType([userOwner], userOwnerTranslation);
const calendarEventBuilder = new CalendarEventBuilder();
calendarEventBuilder.init({
// Using builder as assembling calEvent can take some time
const builder = new CalendarEventBuilder();
builder.init({
title: bookingToReschedule.title,
type: event?.title || "Nameless Event",
type: event.title,
startTime: bookingToReschedule.startTime.toISOString(),
endTime: bookingToReschedule.endTime.toISOString(),
attendees: usersToPeopleType(
// username field doesn't exists on attendee but could be in the future
bookingToReschedule.attendees as unknown as PersonAttendee[],
bookingToReschedule.attendees as unknown as PersonAttendeeCommonFields[],
tAttendees
),
organizer: userOwnerAsPeopleType,
});
await calendarEventBuilder.buildEventObjectFromInnerClass(bookingToReschedule.eventTypeId);
await calendarEventBuilder.buildUsersFromInnerClass();
if (event?.schedulingType === SchedulingType.ROUND_ROBIN) {
await calendarEventBuilder.buildLuckyUsers();
}
if (event?.schedulingType === SchedulingType.COLLECTIVE) {
await calendarEventBuilder.buildLuckyUsers();
}
await calendarEventBuilder.buildAttendeesList();
calendarEventBuilder.setLocation(bookingToReschedule.location);
calendarEventBuilder.setUId(bookingToReschedule.uid);
calendarEventBuilder.setCancellationReason(cancellationReason);
console.log({ calendarEventBuilder });
const director = new CalendarEventDirector();
director.setBuilder(builder);
director.setExistingBooking(bookingToReschedule as unknown as Booking);
director.setCancellationReason(cancellationReason);
director.buildForRescheduleEmail();
// Send email =================
calendarEventBuilder.buildRescheduleLink(bookingToReschedule.uid);
await sendRequestRescheduleEmail(calendarEventBuilder.calendarEvent, {
rescheduleLink: calendarEventBuilder.rescheduleLink,
await sendRequestRescheduleEmail(builder.calendarEvent, {
rescheduleLink: builder.rescheduleLink,
});
}
return res.status(200).json(bookingToReschedule);
} catch (error) {
console.log(error);
// throw new Error(error?.message);
throw new Error("Error.request.reschedule");
}
return res.status(204);
};
function validate(

View File

@ -1,5 +1,5 @@
import { expect, test } from "@playwright/test";
import { User, BookingStatus } from "@prisma/client";
import { BookingStatus } from "@prisma/client";
import dayjs from "dayjs";
import { TestUtilCreateBookingOnUserId, TestUtilCreatePayment } from "./lib/dbSetup";

View File

@ -25,12 +25,12 @@ const userSelect = Prisma.validator<Prisma.UserArgs>()({
});
type User = Prisma.UserGetPayload<typeof userSelect>;
type PersonAttendeeCommonFields = Pick<User, "id" | "email" | "name" | "locale" | "timeZone" | "username">;
interface ICalendarEventBuilder {
calendarEvent: CalendarEventClass;
eventType: Awaited<ReturnType<CalendarEventBuilder["getEventFromEventId"]>>;
users: Awaited<ReturnType<CalendarEventBuilder["getUserById"]>>[];
attendeesList: any;
attendeesList: PersonAttendeeCommonFields[];
teamMembers: Awaited<ReturnType<CalendarEventBuilder["getTeamMembers"]>>;
rescheduleLink: string;
}
@ -55,6 +55,10 @@ export class CalendarEventBuilder implements ICalendarEventBuilder {
this.calendarEvent = new CalendarEventClass(initProps);
}
public setEventType(eventType: ICalendarEventBuilder["eventType"]) {
this.eventType = eventType;
}
public async buildEventObjectFromInnerClass(eventId: number) {
const resultEvent = await this.getEventFromEventId(eventId);
if (resultEvent) {
@ -80,7 +84,11 @@ export class CalendarEventBuilder implements ICalendarEventBuilder {
}
public buildAttendeesList() {
this.attendeesList = [...this.calendarEvent.attendees, ...this.teamMembers];
// Language Function was set on builder init
this.attendeesList = [
...(this.calendarEvent.attendees as unknown as PersonAttendeeCommonFields[]),
...this.teamMembers,
];
}
private async getUserById(userId: number) {
@ -209,6 +217,8 @@ export class CalendarEventBuilder implements ICalendarEventBuilder {
// Users[0] its organizer so we are omitting with slice(1)
const teamMemberPromises = this.users.slice(1).map(async function (user) {
return {
id: user.id,
username: user.username,
email: user.email || "", // @NOTE: Should we change this "" to teamMemberId?
name: user.name || "",
timeZone: user.timeZone,
@ -216,7 +226,8 @@ export class CalendarEventBuilder implements ICalendarEventBuilder {
translate: await getTranslation(user.locale ?? "en", "common"),
locale: user.locale ?? "en",
},
} as Person;
locale: user.locale,
} as PersonAttendeeCommonFields;
});
return await Promise.all(teamMemberPromises);
}
@ -262,7 +273,7 @@ export class CalendarEventBuilder implements ICalendarEventBuilder {
throw new Error("Run buildEventObjectFromInnerClass before this function");
}
const isTeam = !!this.eventType.teamId;
console.log(process.env);
const queryParams = new URLSearchParams();
queryParams.set("rescheduleUid", `${originalBookingUId}`);
const rescheduleLink = `${process.env.BASE_URL}/${

View File

@ -0,0 +1,35 @@
import { Booking } from "@prisma/client";
import { CalendarEventBuilder } from "./builder";
export class CalendarEventDirector {
private builder!: CalendarEventBuilder;
private existingBooking!: Partial<Booking>;
private cancellationReason!: string;
public setBuilder(builder: CalendarEventBuilder): void {
this.builder = builder;
}
public setExistingBooking(booking: Booking) {
this.existingBooking = booking;
}
public setCancellationReason(reason: string) {
this.cancellationReason = reason;
}
public async buildForRescheduleEmail(): Promise<void> {
if (this.existingBooking && this.existingBooking.eventTypeId && this.existingBooking.uid) {
this.builder.buildEventObjectFromInnerClass(this.existingBooking.eventTypeId);
await this.builder.buildUsersFromInnerClass();
await this.builder.buildAttendeesList();
this.builder.setLocation(this.existingBooking.location);
this.builder.setUId(this.existingBooking.uid);
this.builder.setCancellationReason(this.cancellationReason);
this.builder.buildRescheduleLink(this.existingBooking.uid);
} else {
throw new Error("buildForRescheduleEmail.missing.params.required");
}
}
}

View File

@ -204,7 +204,7 @@ model BookingReference {
meetingId String?
meetingPassword String?
meetingUrl String?
booking Booking? @relation(fields: [bookingId], references: [id])
booking Booking? @relation(fields: [bookingId], references: [id], onDelete: Cascade)
bookingId Int?
deleted Boolean?
}

View File

@ -13,6 +13,7 @@ export type Person = {
timeZone: string;
language: { translate: TFunction; locale: string };
username?: string;
id?: string;
};
export type EventBusyDate = Record<"start" | "end", Date | string>;
@ -77,9 +78,9 @@ export interface CalendarEvent {
title: string;
startTime: string;
endTime: string;
organizer: Person;
attendees: Person[];
additionalNotes?: string | null;
organizer: Person; //@TODO: check if needed for builder
attendees: Person[]; //@TODO: check if needed for builder
description?: string | null;
team?: {
name: string;