hotfix: zoom location on emails (#1153)

* fix: zoom location on emails

* test: fix

Co-authored-by: Peer Richelsen <peeroke@gmail.com>
This commit is contained in:
Mihai C 2021-11-09 18:27:33 +02:00 committed by GitHub
parent 43fa4f6497
commit 559ccb8ca7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 80 additions and 58 deletions

View file

@ -642,6 +642,15 @@ const createEvent = async (
}) })
: undefined; : undefined;
if (!creationResult) {
return {
type: credential.type,
success,
uid,
originalEvent: calEvent,
};
}
const metadata: AdditionInformation = {}; const metadata: AdditionInformation = {};
if (creationResult) { if (creationResult) {
// TODO: Handle created event metadata more elegantly // TODO: Handle created event metadata more elegantly
@ -650,10 +659,10 @@ const createEvent = async (
metadata.entryPoints = creationResult.entryPoints; metadata.entryPoints = creationResult.entryPoints;
} }
const emailEvent = { ...calEvent, additionInformation: metadata }; calEvent.additionInformation = metadata;
if (!noMail) { if (!noMail) {
const organizerMail = new EventOrganizerMail(emailEvent); const organizerMail = new EventOrganizerMail(calEvent);
try { try {
await organizerMail.sendEmail(); await organizerMail.sendEmail();
@ -674,27 +683,37 @@ const createEvent = async (
const updateEvent = async ( const updateEvent = async (
credential: Credential, credential: Credential,
calEvent: CalendarEvent, calEvent: CalendarEvent,
noMail: boolean | null = false noMail: boolean | null = false,
bookingRefUid: string | null
): Promise<EventResult> => { ): Promise<EventResult> => {
const parser: CalEventParser = new CalEventParser(calEvent); const parser: CalEventParser = new CalEventParser(calEvent);
const newUid: string = parser.getUid(); const uid = parser.getUid();
const richEvent: CalendarEvent = parser.asRichEventPlain(); const richEvent: CalendarEvent = parser.asRichEventPlain();
let success = true; let success = true;
const updateResult = const updatedResult =
credential && calEvent.uid credential && bookingRefUid
? await calendars([credential])[0] ? await calendars([credential])[0]
.updateEvent(calEvent.uid, richEvent) .updateEvent(bookingRefUid, richEvent)
.catch((e) => { .catch((e) => {
log.error("updateEvent failed", e, calEvent); log.error("updateEvent failed", e, calEvent);
success = false; success = false;
return undefined;
}) })
: null; : null;
if (!updatedResult) {
return {
type: credential.type,
success,
uid,
originalEvent: calEvent,
};
}
if (!noMail) { if (!noMail) {
const emailEvent = { ...calEvent, uid: newUid }; const organizerMail = new EventOrganizerRescheduledMail(calEvent);
const organizerMail = new EventOrganizerRescheduledMail(emailEvent);
try { try {
await organizerMail.sendEmail(); await organizerMail.sendEmail();
} catch (e) { } catch (e) {
@ -705,8 +724,8 @@ const updateEvent = async (
return { return {
type: credential.type, type: credential.type,
success, success,
uid: newUid, uid,
updatedEvent: updateResult, updatedEvent: updatedResult,
originalEvent: calEvent, originalEvent: calEvent,
}; };
}; };

View file

@ -79,7 +79,7 @@ export default class EventManager {
* @param event * @param event
*/ */
public async create(event: Ensure<CalendarEvent, "language">): Promise<CreateUpdateResult> { public async create(event: Ensure<CalendarEvent, "language">): Promise<CreateUpdateResult> {
let evt = EventManager.processLocation(event); const evt = EventManager.processLocation(event);
const isDedicated = evt.location ? EventManager.isDedicatedIntegration(evt.location) : null; const isDedicated = evt.location ? EventManager.isDedicatedIntegration(evt.location) : null;
// First, create all calendar events. If this is a dedicated integration event, don't send a mail right here. // First, create all calendar events. If this is a dedicated integration event, don't send a mail right here.
@ -88,7 +88,7 @@ export default class EventManager {
if (isDedicated) { if (isDedicated) {
const result = await this.createVideoEvent(evt); const result = await this.createVideoEvent(evt);
if (result.videoCallData) { if (result.videoCallData) {
evt = { ...evt, videoCallData: result.videoCallData }; evt.videoCallData = result.videoCallData;
} }
results.push(result); results.push(result);
} else { } else {
@ -126,17 +126,20 @@ export default class EventManager {
* *
* @param event * @param event
*/ */
public async update(event: Ensure<CalendarEvent, "uid">): Promise<CreateUpdateResult> { public async update(
let evt = EventManager.processLocation(event); event: Ensure<CalendarEvent, "language">,
rescheduleUid: string
): Promise<CreateUpdateResult> {
const evt = EventManager.processLocation(event);
if (!evt.uid) { if (!rescheduleUid) {
throw new Error("You called eventManager.update without an `uid`. This should never happen."); throw new Error("You called eventManager.update without an `rescheduleUid`. This should never happen.");
} }
// Get details of existing booking. // Get details of existing booking.
const booking = await prisma.booking.findFirst({ const booking = await prisma.booking.findFirst({
where: { where: {
uid: evt.uid, uid: rescheduleUid,
}, },
select: { select: {
id: true, id: true,
@ -164,7 +167,7 @@ export default class EventManager {
if (isDedicated) { if (isDedicated) {
const result = await this.updateVideoEvent(evt, booking); const result = await this.updateVideoEvent(evt, booking);
if (result.videoCallData) { if (result.videoCallData) {
evt = { ...evt, videoCallData: result.videoCallData }; evt.videoCallData = result.videoCallData;
} }
results.push(result); results.push(result);
} else { } else {
@ -182,15 +185,11 @@ export default class EventManager {
}, },
}); });
let bookingDeletes = null; const bookingDeletes = prisma.booking.delete({
where: {
if (evt.uid) { id: booking.id,
bookingDeletes = prisma.booking.delete({ },
where: { });
uid: evt.uid,
},
});
}
// Wait for all deletions to be applied. // Wait for all deletions to be applied.
await Promise.all([bookingReferenceDeletes, attendeeDeletes, bookingDeletes]); await Promise.all([bookingReferenceDeletes, attendeeDeletes, bookingDeletes]);
@ -275,8 +274,7 @@ export default class EventManager {
const bookingRefUid = booking const bookingRefUid = booking
? booking.references.filter((ref) => ref.type === credential.type)[0]?.uid ? booking.references.filter((ref) => ref.type === credential.type)[0]?.uid
: null; : null;
const evt = { ...event, uid: bookingRefUid }; return updateEvent(credential, event, noMail, bookingRefUid);
return updateEvent(credential, evt, noMail);
}); });
} }
@ -292,10 +290,10 @@ export default class EventManager {
if (credential) { if (credential) {
const bookingRef = booking ? booking.references.filter((ref) => ref.type === credential.type)[0] : null; const bookingRef = booking ? booking.references.filter((ref) => ref.type === credential.type)[0] : null;
const evt = { ...event, uid: bookingRef?.uid }; const bookingRefUid = bookingRef ? bookingRef.uid : null;
return updateMeeting(credential, evt).then((returnVal: EventResult) => { return updateMeeting(credential, event, bookingRefUid).then((returnVal: EventResult) => {
// Some video integrations, such as Zoom, don't return any data about the booking when updating it. // Some video integrations, such as Zoom, don't return any data about the booking when updating it.
if (returnVal.videoCallData == undefined) { if (returnVal.videoCallData === undefined) {
returnVal.videoCallData = EventManager.bookingReferenceToVideoCallData(bookingRef); returnVal.videoCallData = EventManager.bookingReferenceToVideoCallData(bookingRef);
} }
return returnVal; return returnVal;
@ -441,15 +439,16 @@ export default class EventManager {
metadata.conferenceData = results[0].createdEvent?.conferenceData; metadata.conferenceData = results[0].createdEvent?.conferenceData;
metadata.entryPoints = results[0].createdEvent?.entryPoints; metadata.entryPoints = results[0].createdEvent?.entryPoints;
} }
const emailEvent = { ...event, additionInformation: metadata };
event.additionInformation = metadata;
let attendeeMail; let attendeeMail;
switch (type) { switch (type) {
case "reschedule": case "reschedule":
attendeeMail = new EventAttendeeRescheduledMail(emailEvent); attendeeMail = new EventAttendeeRescheduledMail(event);
break; break;
case "new": case "new":
attendeeMail = new EventAttendeeMail(emailEvent); attendeeMail = new EventAttendeeMail(event);
break; break;
} }
try { try {

View file

@ -116,10 +116,11 @@ const createMeeting = async (
entryPoints: [entryPoint], entryPoints: [entryPoint],
}; };
const emailEvent = { ...calEvent, uid, additionInformation, videoCallData }; calEvent.additionInformation = additionInformation;
calEvent.videoCallData = videoCallData;
try { try {
const organizerMail = new VideoEventOrganizerMail(emailEvent); const organizerMail = new VideoEventOrganizerMail(calEvent);
await organizerMail.sendEmail(); await organizerMail.sendEmail();
} catch (e) { } catch (e) {
console.error("organizerMail.sendEmail failed", e); console.error("organizerMail.sendEmail failed", e);
@ -127,7 +128,7 @@ const createMeeting = async (
if (!createdMeeting || !createdMeeting.disableConfirmationEmail) { if (!createdMeeting || !createdMeeting.disableConfirmationEmail) {
try { try {
const attendeeMail = new VideoEventAttendeeMail(emailEvent); const attendeeMail = new VideoEventAttendeeMail(calEvent);
await attendeeMail.sendEmail(); await attendeeMail.sendEmail();
} catch (e) { } catch (e) {
console.error("attendeeMail.sendEmail failed", e); console.error("attendeeMail.sendEmail failed", e);
@ -144,8 +145,12 @@ const createMeeting = async (
}; };
}; };
const updateMeeting = async (credential: Credential, calEvent: CalendarEvent): Promise<EventResult> => { const updateMeeting = async (
const newUid: string = translator.fromUUID(uuidv5(JSON.stringify(calEvent), uuidv5.URL)); credential: Credential,
calEvent: CalendarEvent,
bookingRefUid: string | null
): Promise<EventResult> => {
const uid: string = translator.fromUUID(uuidv5(JSON.stringify(calEvent), uuidv5.URL));
if (!credential) { if (!credential) {
throw new Error( throw new Error(
@ -153,31 +158,29 @@ const updateMeeting = async (credential: Credential, calEvent: CalendarEvent): P
); );
} }
if (!calEvent.uid) {
throw new Error("You can't update an meeting without it's UID.");
}
let success = true; let success = true;
const [firstVideoAdapter] = getVideoAdapters([credential]); const [firstVideoAdapter] = getVideoAdapters([credential]);
const updatedMeeting = await firstVideoAdapter.updateMeeting(calEvent.uid, calEvent).catch((e) => { const updatedMeeting =
log.error("updateMeeting failed", e, calEvent); credential && bookingRefUid
success = false; ? await firstVideoAdapter.updateMeeting(bookingRefUid, calEvent).catch((e) => {
}); log.error("updateMeeting failed", e, calEvent);
success = false;
return undefined;
})
: undefined;
if (!updatedMeeting) { if (!updatedMeeting) {
return { return {
type: credential.type, type: credential.type,
success, success,
uid: calEvent.uid, uid,
originalEvent: calEvent, originalEvent: calEvent,
}; };
} }
const emailEvent = { ...calEvent, uid: newUid };
try { try {
const organizerMail = new EventOrganizerRescheduledMail(emailEvent); const organizerMail = new EventOrganizerRescheduledMail(calEvent);
await organizerMail.sendEmail(); await organizerMail.sendEmail();
} catch (e) { } catch (e) {
console.error("organizerMail.sendEmail failed", e); console.error("organizerMail.sendEmail failed", e);
@ -185,7 +188,7 @@ const updateMeeting = async (credential: Credential, calEvent: CalendarEvent): P
if (!updatedMeeting.disableConfirmationEmail) { if (!updatedMeeting.disableConfirmationEmail) {
try { try {
const attendeeMail = new EventAttendeeRescheduledMail(emailEvent); const attendeeMail = new EventAttendeeRescheduledMail(calEvent);
await attendeeMail.sendEmail(); await attendeeMail.sendEmail();
} catch (e) { } catch (e) {
console.error("attendeeMail.sendEmail failed", e); console.error("attendeeMail.sendEmail failed", e);
@ -195,7 +198,7 @@ const updateMeeting = async (credential: Credential, calEvent: CalendarEvent): P
return { return {
type: credential.type, type: credential.type,
success, success,
uid: newUid, uid,
updatedEvent: updatedMeeting, updatedEvent: updatedMeeting,
originalEvent: calEvent, originalEvent: calEvent,
}; };

View file

@ -439,8 +439,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
if (rescheduleUid) { if (rescheduleUid) {
// Use EventManager to conditionally use all needed integrations. // Use EventManager to conditionally use all needed integrations.
const eventManagerCalendarEvent = { ...evt, uid: rescheduleUid }; const updateResults = await eventManager.update(evt, rescheduleUid);
const updateResults = await eventManager.update(eventManagerCalendarEvent);
results = updateResults.results; results = updateResults.results;
referencesToCreate = updateResults.referencesToCreate; referencesToCreate = updateResults.referencesToCreate;
@ -473,7 +472,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
} }
if (eventType.requiresConfirmation && !rescheduleUid) { if (eventType.requiresConfirmation && !rescheduleUid) {
await new EventOrganizerRequestMail({ ...evt, uid }).sendEmail(); await new EventOrganizerRequestMail(evt).sendEmail();
} }
if (typeof eventType.price === "number" && eventType.price > 0) { if (typeof eventType.price === "number" && eventType.price > 0) {

View file

@ -67,6 +67,7 @@ describe("webhooks", () => {
} }
body.payload.organizer.timeZone = dynamic; body.payload.organizer.timeZone = dynamic;
body.payload.uid = dynamic; body.payload.uid = dynamic;
body.payload.additionInformation = dynamic;
// if we change the shape of our webhooks, we can simply update this by clicking `u` // if we change the shape of our webhooks, we can simply update this by clicking `u`
// console.log("BODY", body); // console.log("BODY", body);
@ -74,6 +75,7 @@ describe("webhooks", () => {
Object { Object {
"createdAt": "[redacted/dynamic]", "createdAt": "[redacted/dynamic]",
"payload": Object { "payload": Object {
"additionInformation": "[redacted/dynamic]",
"attendees": Array [ "attendees": Array [
Object { Object {
"email": "test@example.com", "email": "test@example.com",