Introduced EventOwnerMail and VideoEventOwnerMail as class based implementations
This commit is contained in:
		
							parent
							
								
									51a8bafaa7
								
							
						
					
					
						commit
						e37dd017c8
					
				
					 5 changed files with 231 additions and 81 deletions
				
			
		| 
						 | 
				
			
			@ -1,5 +1,6 @@
 | 
			
		|||
import EventOwnerMail from "./emails/EventOwnerMail";
 | 
			
		||||
 | 
			
		||||
const {google} = require('googleapis');
 | 
			
		||||
import createNewEventEmail from "./emails/new-event";
 | 
			
		||||
 | 
			
		||||
const googleAuth = () => {
 | 
			
		||||
    const {client_secret, client_id, redirect_uris} = JSON.parse(process.env.GOOGLE_API_CREDENTIALS).web;
 | 
			
		||||
| 
						 | 
				
			
			@ -323,17 +324,16 @@ const getBusyTimes = (withCredentials, dateFrom, dateTo) => Promise.all(
 | 
			
		|||
    (results) => results.reduce((acc, availability) => acc.concat(availability), [])
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
const createEvent = (credential, calEvent: CalendarEvent): Promise<any> => {
 | 
			
		||||
const createEvent = async (credential, calEvent: CalendarEvent): Promise<any> => {
 | 
			
		||||
    const mail = new EventOwnerMail(calEvent);
 | 
			
		||||
    const sentMail = await mail.sendEmail();
 | 
			
		||||
 | 
			
		||||
    createNewEventEmail(
 | 
			
		||||
        calEvent,
 | 
			
		||||
    );
 | 
			
		||||
    const creationResult = credential ? await calendars([credential])[0].createEvent(calEvent) : null;
 | 
			
		||||
 | 
			
		||||
    if (credential) {
 | 
			
		||||
        return calendars([credential])[0].createEvent(calEvent);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return Promise.resolve({});
 | 
			
		||||
    return {
 | 
			
		||||
        createdEvent: creationResult,
 | 
			
		||||
        sentMail: sentMail
 | 
			
		||||
    };
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const updateEvent = (credential, uid: String, calEvent: CalendarEvent): Promise<any> => {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										150
									
								
								lib/emails/EventOwnerMail.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										150
									
								
								lib/emails/EventOwnerMail.ts
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,150 @@
 | 
			
		|||
import {CalendarEvent} from "../calendarClient";
 | 
			
		||||
import {createEvent} from "ics";
 | 
			
		||||
import dayjs, {Dayjs} from "dayjs";
 | 
			
		||||
import {serverConfig} from "../serverConfig";
 | 
			
		||||
import nodemailer from 'nodemailer';
 | 
			
		||||
 | 
			
		||||
export default class EventOwnerMail {
 | 
			
		||||
  calEvent: CalendarEvent;
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * An EventOwnerMail always consists of a CalendarEvent
 | 
			
		||||
   * that stores the very basic data of the event (like date, title etc).
 | 
			
		||||
   *
 | 
			
		||||
   * @param calEvent
 | 
			
		||||
   */
 | 
			
		||||
  constructor(calEvent: CalendarEvent) {
 | 
			
		||||
    this.calEvent = calEvent;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Returns the instance's event as an iCal event in string representation.
 | 
			
		||||
   * @protected
 | 
			
		||||
   */
 | 
			
		||||
  protected getiCalEventAsString(): string {
 | 
			
		||||
    const icsEvent = createEvent({
 | 
			
		||||
      start: dayjs(this.calEvent.startTime).utc().toArray().slice(0, 6),
 | 
			
		||||
      startInputType: 'utc',
 | 
			
		||||
      productId: 'calendso/ics',
 | 
			
		||||
      title: `${this.calEvent.type} with ${this.calEvent.attendees[0].name}`,
 | 
			
		||||
      description: this.calEvent.description + this.stripHtml(this.getAdditionalBody()),
 | 
			
		||||
      duration: {minutes: dayjs(this.calEvent.endTime).diff(dayjs(this.calEvent.startTime), 'minute')},
 | 
			
		||||
      organizer: {name: this.calEvent.organizer.name, email: this.calEvent.organizer.email},
 | 
			
		||||
      attendees: this.calEvent.attendees.map((attendee: any) => ({name: attendee.name, email: attendee.email})),
 | 
			
		||||
      status: "CONFIRMED",
 | 
			
		||||
    });
 | 
			
		||||
    if (icsEvent.error) {
 | 
			
		||||
      throw icsEvent.error;
 | 
			
		||||
    }
 | 
			
		||||
    return icsEvent.value;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Returns the email text as HTML representation.
 | 
			
		||||
   *
 | 
			
		||||
   * @protected
 | 
			
		||||
   */
 | 
			
		||||
  protected getHtmlRepresentation(): string {
 | 
			
		||||
    return `
 | 
			
		||||
      <div>
 | 
			
		||||
        Hi ${this.calEvent.organizer.name},<br />
 | 
			
		||||
        <br />
 | 
			
		||||
        A new event has been scheduled.<br />
 | 
			
		||||
        <br />
 | 
			
		||||
        <strong>Event Type:</strong><br />
 | 
			
		||||
        ${this.calEvent.type}<br />
 | 
			
		||||
        <br />
 | 
			
		||||
        <strong>Invitee Email:</strong><br />
 | 
			
		||||
        <a href="mailto:${this.calEvent.attendees[0].email}">${this.calEvent.attendees[0].email}</a><br />
 | 
			
		||||
        <br />` + this.getAdditionalBody() +
 | 
			
		||||
      (
 | 
			
		||||
        this.calEvent.location ? `
 | 
			
		||||
            <strong>Location:</strong><br />
 | 
			
		||||
            ${this.calEvent.location}<br />
 | 
			
		||||
            <br />
 | 
			
		||||
          ` : ''
 | 
			
		||||
      ) +
 | 
			
		||||
      `<strong>Invitee Time Zone:</strong><br />
 | 
			
		||||
        ${this.calEvent.attendees[0].timeZone}<br />
 | 
			
		||||
        <br />
 | 
			
		||||
        <strong>Additional notes:</strong><br />
 | 
			
		||||
        ${this.calEvent.description}
 | 
			
		||||
      </div>
 | 
			
		||||
    `;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Returns the email text in a plain text representation
 | 
			
		||||
   * by stripping off the HTML tags.
 | 
			
		||||
   *
 | 
			
		||||
   * @protected
 | 
			
		||||
   */
 | 
			
		||||
  protected getPlainTextRepresentation(): string {
 | 
			
		||||
    return this.stripHtml(this.getHtmlRepresentation());
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Strips off all HTML tags and leaves plain text.
 | 
			
		||||
   *
 | 
			
		||||
   * @param html
 | 
			
		||||
   * @protected
 | 
			
		||||
   */
 | 
			
		||||
  protected stripHtml(html: string): string {
 | 
			
		||||
    return html
 | 
			
		||||
      .replace('<br />', "\n")
 | 
			
		||||
      .replace(/<[^>]+>/g, '');
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Sends the email to the event attendant and returns a Promise.
 | 
			
		||||
   */
 | 
			
		||||
  public sendEmail(): Promise<any> {
 | 
			
		||||
    const options = this.getMailerOptions();
 | 
			
		||||
    const {transport, from} = options;
 | 
			
		||||
    const organizerStart: Dayjs = <Dayjs>dayjs(this.calEvent.startTime).tz(this.calEvent.organizer.timeZone);
 | 
			
		||||
 | 
			
		||||
    return new Promise((resolve, reject) => nodemailer.createTransport(transport).sendMail(
 | 
			
		||||
      {
 | 
			
		||||
        icalEvent: {
 | 
			
		||||
          filename: 'event.ics',
 | 
			
		||||
          content: this.getiCalEventAsString(),
 | 
			
		||||
        },
 | 
			
		||||
        from: `Calendso <${from}>`,
 | 
			
		||||
        to: this.calEvent.organizer.email,
 | 
			
		||||
        subject: `New event: ${this.calEvent.attendees[0].name} - ${organizerStart.format('LT dddd, LL')} - ${this.calEvent.type}`,
 | 
			
		||||
        html: this.getHtmlRepresentation(),
 | 
			
		||||
        text: this.getPlainTextRepresentation(),
 | 
			
		||||
      },
 | 
			
		||||
      (error, info) => {
 | 
			
		||||
        if (error) {
 | 
			
		||||
          console.error("SEND_NEW_EVENT_NOTIFICATION_ERROR", this.calEvent.organizer.email, error);
 | 
			
		||||
          reject(new Error(error));
 | 
			
		||||
        } else {
 | 
			
		||||
          resolve(info);
 | 
			
		||||
        }
 | 
			
		||||
      }));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Gathers the required provider information from the config.
 | 
			
		||||
   *
 | 
			
		||||
   * @protected
 | 
			
		||||
   */
 | 
			
		||||
  protected getMailerOptions(): any {
 | 
			
		||||
    return {
 | 
			
		||||
      transport: serverConfig.transport,
 | 
			
		||||
      from: serverConfig.from,
 | 
			
		||||
    };
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Can be used to include additional HTML or plain text
 | 
			
		||||
   * content into the mail body and calendar event description.
 | 
			
		||||
   * Leave it to an empty string if not desired.
 | 
			
		||||
   *
 | 
			
		||||
   * @protected
 | 
			
		||||
   */
 | 
			
		||||
  protected getAdditionalBody(): string {
 | 
			
		||||
    return "";
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										27
									
								
								lib/emails/VideoEventOwnerMail.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								lib/emails/VideoEventOwnerMail.ts
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,27 @@
 | 
			
		|||
import {CalendarEvent} from "../calendarClient";
 | 
			
		||||
import EventOwnerMail from "./EventOwnerMail";
 | 
			
		||||
import {formattedId, integrationTypeToName, VideoCallData} from "./confirm-booked";
 | 
			
		||||
 | 
			
		||||
export default class VideoEventOwnerMail extends EventOwnerMail {
 | 
			
		||||
  videoCallData: VideoCallData;
 | 
			
		||||
 | 
			
		||||
  constructor(calEvent: CalendarEvent, videoCallData: VideoCallData) {
 | 
			
		||||
    super(calEvent);
 | 
			
		||||
    this.videoCallData = videoCallData;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Adds the video call information to the mail body
 | 
			
		||||
   * and calendar event description.
 | 
			
		||||
   *
 | 
			
		||||
   * @protected
 | 
			
		||||
   */
 | 
			
		||||
  protected getAdditionalBody(): string {
 | 
			
		||||
    return `
 | 
			
		||||
      <strong>Video call provider:</strong> ${integrationTypeToName(this.videoCallData.type)}<br />
 | 
			
		||||
      <strong>Meeting ID:</strong> ${formattedId(this.videoCallData)}<br />
 | 
			
		||||
      <strong>Meeting Password:</strong> ${this.videoCallData.password}<br />
 | 
			
		||||
      <strong>Meeting URL:</strong> <a href="${this.videoCallData.url}">${this.videoCallData.url}</a><br />
 | 
			
		||||
    `;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,4 +1,7 @@
 | 
			
		|||
import prisma from "./prisma";
 | 
			
		||||
import {VideoCallData} from "./emails/confirm-booked";
 | 
			
		||||
import {CalendarEvent} from "./calendarClient";
 | 
			
		||||
import VideoEventOwnerMail from "./emails/VideoEventOwnerMail";
 | 
			
		||||
 | 
			
		||||
function handleErrorsJson(response) {
 | 
			
		||||
    if (!response.ok) {
 | 
			
		||||
| 
						 | 
				
			
			@ -53,26 +56,10 @@ const zoomAuth = (credential) => {
 | 
			
		|||
    };
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
interface Person {
 | 
			
		||||
    name?: string,
 | 
			
		||||
    email: string,
 | 
			
		||||
    timeZone: string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
interface VideoMeeting {
 | 
			
		||||
    title: string;
 | 
			
		||||
    startTime: string;
 | 
			
		||||
    endTime: string;
 | 
			
		||||
    description?: string;
 | 
			
		||||
    timezone: string;
 | 
			
		||||
    organizer: Person;
 | 
			
		||||
    attendees: Person[];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
interface VideoApiAdapter {
 | 
			
		||||
    createMeeting(meeting: VideoMeeting): Promise<any>;
 | 
			
		||||
    createMeeting(event: CalendarEvent): Promise<any>;
 | 
			
		||||
 | 
			
		||||
    updateMeeting(uid: String, meeting: VideoMeeting);
 | 
			
		||||
    updateMeeting(uid: String, event: CalendarEvent);
 | 
			
		||||
 | 
			
		||||
    deleteMeeting(uid: String);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -83,17 +70,17 @@ const ZoomVideo = (credential): VideoApiAdapter => {
 | 
			
		|||
 | 
			
		||||
    const auth = zoomAuth(credential);
 | 
			
		||||
 | 
			
		||||
    const translateMeeting = (meeting: VideoMeeting) => {
 | 
			
		||||
    const translateEvent = (event: CalendarEvent) => {
 | 
			
		||||
        // Documentation at: https://marketplace.zoom.us/docs/api-reference/zoom-api/meetings/meetingcreate
 | 
			
		||||
        const meet = {
 | 
			
		||||
            topic: meeting.title,
 | 
			
		||||
        return {
 | 
			
		||||
            topic: event.title,
 | 
			
		||||
            type: 2,    // Means that this is a scheduled meeting
 | 
			
		||||
            start_time: meeting.startTime,
 | 
			
		||||
            duration: ((new Date(meeting.endTime)).getTime() - (new Date(meeting.startTime)).getTime()) / 60000,
 | 
			
		||||
            start_time: event.startTime,
 | 
			
		||||
            duration: ((new Date(event.endTime)).getTime() - (new Date(event.startTime)).getTime()) / 60000,
 | 
			
		||||
            //schedule_for: "string",   TODO: Used when scheduling the meeting for someone else (needed?)
 | 
			
		||||
            timezone: meeting.timezone,
 | 
			
		||||
            timezone: event.attendees[0].timeZone,
 | 
			
		||||
            //password: "string",       TODO: Should we use a password? Maybe generate a random one?
 | 
			
		||||
            agenda: meeting.description,
 | 
			
		||||
            agenda: event.description,
 | 
			
		||||
            settings: {
 | 
			
		||||
                host_video: true,
 | 
			
		||||
                participant_video: true,
 | 
			
		||||
| 
						 | 
				
			
			@ -110,8 +97,6 @@ const ZoomVideo = (credential): VideoApiAdapter => {
 | 
			
		|||
                registrants_email_notification: true
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        return meet;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    return {
 | 
			
		||||
| 
						 | 
				
			
			@ -149,13 +134,13 @@ const ZoomVideo = (credential): VideoApiAdapter => {
 | 
			
		|||
                console.log(err);
 | 
			
		||||
            });*/
 | 
			
		||||
        },
 | 
			
		||||
        createMeeting: (meeting: VideoMeeting) => auth.getToken().then(accessToken => fetch('https://api.zoom.us/v2/users/me/meetings', {
 | 
			
		||||
        createMeeting: (event: CalendarEvent) => auth.getToken().then(accessToken => fetch('https://api.zoom.us/v2/users/me/meetings', {
 | 
			
		||||
            method: 'POST',
 | 
			
		||||
            headers: {
 | 
			
		||||
                'Authorization': 'Bearer ' + accessToken,
 | 
			
		||||
                'Content-Type': 'application/json',
 | 
			
		||||
            },
 | 
			
		||||
            body: JSON.stringify(translateMeeting(meeting))
 | 
			
		||||
            body: JSON.stringify(translateEvent(event))
 | 
			
		||||
        }).then(handleErrorsJson)),
 | 
			
		||||
        deleteMeeting: (uid: String) => auth.getToken().then(accessToken => fetch('https://api.zoom.us/v2/meetings/' + uid, {
 | 
			
		||||
            method: 'DELETE',
 | 
			
		||||
| 
						 | 
				
			
			@ -163,13 +148,13 @@ const ZoomVideo = (credential): VideoApiAdapter => {
 | 
			
		|||
                'Authorization': 'Bearer ' + accessToken
 | 
			
		||||
            }
 | 
			
		||||
        }).then(handleErrorsRaw)),
 | 
			
		||||
        updateMeeting: (uid: String, meeting: VideoMeeting) => auth.getToken().then(accessToken => fetch('https://api.zoom.us/v2/meetings/' + uid, {
 | 
			
		||||
        updateMeeting: (uid: String, event: CalendarEvent) => auth.getToken().then(accessToken => fetch('https://api.zoom.us/v2/meetings/' + uid, {
 | 
			
		||||
            method: 'PATCH',
 | 
			
		||||
            headers: {
 | 
			
		||||
                'Authorization': 'Bearer ' + accessToken,
 | 
			
		||||
                'Content-Type': 'application/json'
 | 
			
		||||
            },
 | 
			
		||||
            body: JSON.stringify(translateMeeting(meeting))
 | 
			
		||||
            body: JSON.stringify(translateEvent(event))
 | 
			
		||||
        }).then(handleErrorsRaw)),
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
| 
						 | 
				
			
			@ -191,23 +176,32 @@ const getBusyTimes = (withCredentials, dateFrom, dateTo) => Promise.all(
 | 
			
		|||
    (results) => results.reduce((acc, availability) => acc.concat(availability), [])
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
const createMeeting = (credential, meeting: VideoMeeting): Promise<any> => {
 | 
			
		||||
 | 
			
		||||
    //TODO Send email to event host
 | 
			
		||||
    /*createNewMeetingEmail(
 | 
			
		||||
      meeting,
 | 
			
		||||
    );*/
 | 
			
		||||
 | 
			
		||||
    if (credential) {
 | 
			
		||||
        return videoIntegrations([credential])[0].createMeeting(meeting);
 | 
			
		||||
const createMeeting = async (credential, calEvent: CalendarEvent): Promise<any> => {
 | 
			
		||||
    if(!credential) {
 | 
			
		||||
        throw new Error("Credentials must be set! Video platforms are optional, so this method shouldn't even be called.");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return Promise.resolve({});
 | 
			
		||||
    const creationResult = await videoIntegrations([credential])[0].createMeeting(calEvent);
 | 
			
		||||
 | 
			
		||||
    const videoCallData: VideoCallData = {
 | 
			
		||||
        type: credential.type,
 | 
			
		||||
        id: creationResult.id,
 | 
			
		||||
        password: creationResult.password,
 | 
			
		||||
        url: creationResult.join_url,
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    const mail = new VideoEventOwnerMail(calEvent, videoCallData);
 | 
			
		||||
    const sentMail = await mail.sendEmail();
 | 
			
		||||
 | 
			
		||||
    return {
 | 
			
		||||
        createdEvent: creationResult,
 | 
			
		||||
        sentMail: sentMail
 | 
			
		||||
    };
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const updateMeeting = (credential, uid: String, meeting: VideoMeeting): Promise<any> => {
 | 
			
		||||
const updateMeeting = (credential, uid: String, event: CalendarEvent): Promise<any> => {
 | 
			
		||||
    if (credential) {
 | 
			
		||||
        return videoIntegrations([credential])[0].updateMeeting(uid, meeting);
 | 
			
		||||
        return videoIntegrations([credential])[0].updateMeeting(uid, event);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return Promise.resolve({});
 | 
			
		||||
| 
						 | 
				
			
			@ -221,4 +215,4 @@ const deleteMeeting = (credential, uid: String): Promise<any> => {
 | 
			
		|||
    return Promise.resolve({});
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export {getBusyTimes, createMeeting, updateMeeting, deleteMeeting, VideoMeeting};
 | 
			
		||||
export {getBusyTimes, createMeeting, updateMeeting, deleteMeeting};
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,11 +1,10 @@
 | 
			
		|||
import type {NextApiRequest, NextApiResponse} from 'next';
 | 
			
		||||
import prisma from '../../../lib/prisma';
 | 
			
		||||
import {CalendarEvent, createEvent, updateEvent} from '../../../lib/calendarClient';
 | 
			
		||||
import createConfirmBookedEmail, {VideoCallData} from "../../../lib/emails/confirm-booked";
 | 
			
		||||
import async from 'async';
 | 
			
		||||
import {v5 as uuidv5} from 'uuid';
 | 
			
		||||
import short from 'short-uuid';
 | 
			
		||||
import {createMeeting, updateMeeting, VideoMeeting} from "../../../lib/videoClient";
 | 
			
		||||
import {createMeeting, updateMeeting} from "../../../lib/videoClient";
 | 
			
		||||
 | 
			
		||||
const translator = short();
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -44,18 +43,6 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
 | 
			
		|||
    ]
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  //TODO Only create meeting if integration exists.
 | 
			
		||||
  const meeting: VideoMeeting = {
 | 
			
		||||
    attendees: [
 | 
			
		||||
      {email: req.body.email, name: req.body.name, timeZone: req.body.timeZone}
 | 
			
		||||
    ],
 | 
			
		||||
    endTime: req.body.end,
 | 
			
		||||
    organizer: {email: currentUser.email, name: currentUser.name, timeZone: currentUser.timeZone},
 | 
			
		||||
    startTime: req.body.start,
 | 
			
		||||
    timezone: currentUser.timeZone,
 | 
			
		||||
    title: req.body.eventName + ' with ' + req.body.name,
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  const hashUID: string = translator.fromUUID(uuidv5(JSON.stringify(evt), uuidv5.URL));
 | 
			
		||||
  const cancelLink: string = process.env.BASE_URL + '/cancel/' + hashUID;
 | 
			
		||||
  const rescheduleLink:string = process.env.BASE_URL + '/reschedule/' + hashUID;
 | 
			
		||||
| 
						 | 
				
			
			@ -108,7 +95,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
 | 
			
		|||
 | 
			
		||||
    results = results.concat(await async.mapLimit(videoCredentials, 5, async (credential) => {
 | 
			
		||||
      const bookingRefUid = booking.references.filter((ref) => ref.type === credential.type)[0].uid;
 | 
			
		||||
      return await updateMeeting(credential, bookingRefUid, meeting)  // TODO Maybe append links?
 | 
			
		||||
      return await updateMeeting(credential, bookingRefUid, evt)  // TODO Maybe append links?
 | 
			
		||||
    }));
 | 
			
		||||
 | 
			
		||||
    // Clone elements
 | 
			
		||||
| 
						 | 
				
			
			@ -147,7 +134,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
 | 
			
		|||
    }));
 | 
			
		||||
 | 
			
		||||
    results = results.concat(await async.mapLimit(videoCredentials, 5, async (credential) => {
 | 
			
		||||
      const response = await createMeeting(credential, meeting);
 | 
			
		||||
      const response = await createMeeting(credential, evt);
 | 
			
		||||
      return {
 | 
			
		||||
        type: credential.type,
 | 
			
		||||
        response
 | 
			
		||||
| 
						 | 
				
			
			@ -157,7 +144,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
 | 
			
		|||
    referencesToCreate = results.map((result => {
 | 
			
		||||
      return {
 | 
			
		||||
        type: result.type,
 | 
			
		||||
        uid: result.response.id.toString()
 | 
			
		||||
        uid: result.response.createdEvent.id.toString()
 | 
			
		||||
      };
 | 
			
		||||
    }));
 | 
			
		||||
  }
 | 
			
		||||
| 
						 | 
				
			
			@ -182,20 +169,12 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
 | 
			
		|||
    }
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  const videoResults = results.filter((res) => res.type.endsWith('_video'));
 | 
			
		||||
  const videoCallData: VideoCallData = videoResults.length === 0 ? undefined : {
 | 
			
		||||
    type: videoResults[0].type,
 | 
			
		||||
    id: videoResults[0].response.id,
 | 
			
		||||
    password: videoResults[0].response.password,
 | 
			
		||||
    url: videoResults[0].response.join_url,
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  // If one of the integrations allows email confirmations or no integrations are added, send it.
 | 
			
		||||
  if (currentUser.credentials.length === 0 || !results.every((result) => result.disableConfirmationEmail)) {
 | 
			
		||||
  /*if (currentUser.credentials.length === 0 || !results.every((result) => result.disableConfirmationEmail)) {
 | 
			
		||||
    await createConfirmBookedEmail(
 | 
			
		||||
      evt, cancelLink, rescheduleLink, {}, videoCallData
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
  }*/
 | 
			
		||||
 | 
			
		||||
  res.status(200).json(results);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in a new issue