Improving Email DRYness (#2486)

* Email DRY

* WIP

* Improve email DRYness

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
This commit is contained in:
Omar López
2022-05-13 06:34:54 -06:00
committed by GitHub
parent 498759890f
commit 68e275ab07
5 changed files with 66 additions and 135 deletions

View File

@@ -0,0 +1,41 @@
import nodemailer from "nodemailer";
import { getErrorFromUnknown } from "@calcom/lib/errors";
import { serverConfig } from "@calcom/lib/serverConfig";
export default class BaseEmail {
name: string = "";
protected getNodeMailerPayload(): Record<string, unknown> {
return {};
}
public sendEmail() {
new Promise((resolve, reject) =>
nodemailer
.createTransport(this.getMailerOptions().transport)
.sendMail(this.getNodeMailerPayload(), (_err, info) => {
if (_err) {
const err = getErrorFromUnknown(_err);
this.printNodeMailerError(err);
reject(err);
} else {
resolve(info);
}
})
).catch((e) => console.error("sendEmail", e));
return new Promise((resolve) => resolve("send mail async"));
}
protected getMailerOptions() {
return {
transport: serverConfig.transport,
from: serverConfig.from,
};
}
protected printNodeMailerError(error: Error): void {
/** Don't clog the logs with unsent emails in E2E */
if (process.env.NEXT_PUBLIC_IS_E2E) return;
console.error(`${this.name}_ERROR`, error);
}
}

View File

@@ -4,22 +4,20 @@ import timezone from "dayjs/plugin/timezone";
import toArray from "dayjs/plugin/toArray";
import utc from "dayjs/plugin/utc";
import { createEvent, DateArray } from "ics";
import { DatasetJsonLdProps } from "next-seo";
import nodemailer from "nodemailer";
import rrule from "rrule";
import { getAppName } from "@calcom/app-store/utils";
import { getCancelLink, getRichDescription } from "@calcom/lib/CalEventParser";
import { getErrorFromUnknown } from "@calcom/lib/errors";
import { serverConfig } from "@calcom/lib/serverConfig";
import type { Person, CalendarEvent, RecurringEvent } from "@calcom/types/Calendar";
import type { CalendarEvent, Person, RecurringEvent } from "@calcom/types/Calendar";
import BaseEmail from "@lib/emails/templates/_base-email";
import {
emailHead,
emailSchedulingBodyHeader,
emailBodyLogo,
emailHead,
emailScheduledBodyHeaderContent,
emailSchedulingBodyDivider,
emailSchedulingBodyHeader,
linkIcon,
} from "./common";
@@ -28,34 +26,19 @@ dayjs.extend(timezone);
dayjs.extend(localizedFormat);
dayjs.extend(toArray);
export default class AttendeeScheduledEmail {
export default class AttendeeScheduledEmail extends BaseEmail {
calEvent: CalendarEvent;
attendee: Person;
recurringEvent: RecurringEvent;
constructor(calEvent: CalendarEvent, attendee: Person, recurringEvent: RecurringEvent) {
super();
this.name = "SEND_BOOKING_CONFIRMATION";
this.calEvent = calEvent;
this.attendee = attendee;
this.recurringEvent = recurringEvent;
}
public sendEmail() {
new Promise((resolve, reject) =>
nodemailer
.createTransport(this.getMailerOptions().transport)
.sendMail(this.getNodeMailerPayload(), (_err, info) => {
if (_err) {
const err = getErrorFromUnknown(_err);
this.printNodeMailerError(err);
reject(err);
} else {
resolve(info);
}
})
).catch((e) => console.error("sendEmail", e));
return new Promise((resolve) => resolve("send mail async"));
}
protected getiCalEventAsString(): string | undefined {
// Taking care of recurrence rule beforehand
let recurrenceRule: string | undefined = undefined;
@@ -115,12 +98,6 @@ export default class AttendeeScheduledEmail {
};
}
protected getMailerOptions() {
return {
transport: serverConfig.transport,
from: serverConfig.from,
};
}
protected getDescription(): string {
if (!this.calEvent.description) return "";
return `
@@ -144,10 +121,6 @@ ${getRichDescription(this.calEvent)}
`.trim();
}
protected printNodeMailerError(error: Error): void {
console.error("SEND_BOOKING_CONFIRMATION_ERROR", this.attendee.email, error);
}
protected getHtmlBody(): string {
const headerContent = this.calEvent.attendees[0].language.translate("confirmed_event_type_subject", {
eventType: this.calEvent.type,

View File

@@ -1,10 +1,8 @@
import { TFunction } from "next-i18next";
import nodemailer from "nodemailer";
import { getErrorFromUnknown } from "@calcom/lib/errors";
import { serverConfig } from "@calcom/lib/serverConfig";
import BaseEmail from "@lib/emails/templates/_base-email";
import { emailHead, linkIcon, emailBodyLogo } from "./common";
import { emailBodyLogo, emailHead, linkIcon } from "./common";
export type PasswordReset = {
language: TFunction;
@@ -17,37 +15,15 @@ export type PasswordReset = {
export const PASSWORD_RESET_EXPIRY_HOURS = 6;
export default class ForgotPasswordEmail {
export default class ForgotPasswordEmail extends BaseEmail {
passwordEvent: PasswordReset;
constructor(passwordEvent: PasswordReset) {
super();
this.name = "SEND_PASSWORD_RESET_EMAIL";
this.passwordEvent = passwordEvent;
}
public sendEmail() {
new Promise((resolve, reject) =>
nodemailer
.createTransport(this.getMailerOptions().transport)
.sendMail(this.getNodeMailerPayload(), (_err, info) => {
if (_err) {
const err = getErrorFromUnknown(_err);
this.printNodeMailerError(err);
reject(err);
} else {
resolve(info);
}
})
).catch((e) => console.error("sendEmail", e));
return new Promise((resolve) => resolve("send mail async"));
}
protected getMailerOptions() {
return {
transport: serverConfig.transport,
from: serverConfig.from,
};
}
protected getNodeMailerPayload(): Record<string, unknown> {
return {
to: `${this.passwordEvent.user.name} <${this.passwordEvent.user.email}>`,
@@ -58,10 +34,6 @@ export default class ForgotPasswordEmail {
};
}
protected printNodeMailerError(error: Error): void {
console.error("SEND_PASSWORD_RESET_EMAIL_ERROR", this.passwordEvent.user.email, error);
}
protected getTextBody(): string {
return `
${this.passwordEvent.language("reset_password_subject")}

View File

@@ -4,21 +4,20 @@ import timezone from "dayjs/plugin/timezone";
import toArray from "dayjs/plugin/toArray";
import utc from "dayjs/plugin/utc";
import { createEvent, DateArray, Person } from "ics";
import nodemailer from "nodemailer";
import rrule from "rrule";
import { getAppName } from "@calcom/app-store/utils";
import { getCancelLink, getRichDescription } from "@calcom/lib/CalEventParser";
import { getErrorFromUnknown } from "@calcom/lib/errors";
import { serverConfig } from "@calcom/lib/serverConfig";
import type { CalendarEvent, RecurringEvent } from "@calcom/types/Calendar";
import BaseEmail from "@lib/emails/templates/_base-email";
import {
emailHead,
emailSchedulingBodyHeader,
emailBodyLogo,
emailHead,
emailScheduledBodyHeaderContent,
emailSchedulingBodyDivider,
emailSchedulingBodyHeader,
linkIcon,
} from "./common";
@@ -27,32 +26,17 @@ dayjs.extend(timezone);
dayjs.extend(localizedFormat);
dayjs.extend(toArray);
export default class OrganizerScheduledEmail {
export default class OrganizerScheduledEmail extends BaseEmail {
calEvent: CalendarEvent;
recurringEvent: RecurringEvent;
constructor(calEvent: CalendarEvent, recurringEvent: RecurringEvent) {
super();
this.name = "SEND_BOOKING_CONFIRMATION";
this.calEvent = calEvent;
this.recurringEvent = recurringEvent;
}
public sendEmail() {
new Promise((resolve, reject) =>
nodemailer
.createTransport(this.getMailerOptions().transport)
.sendMail(this.getNodeMailerPayload(), (_err, info) => {
if (_err) {
const err = getErrorFromUnknown(_err);
this.printNodeMailerError(err);
reject(err);
} else {
resolve(info);
}
})
).catch((e) => console.error("sendEmail", e));
return new Promise((resolve) => resolve("send mail async"));
}
protected getiCalEventAsString(): string | undefined {
// Taking care of recurrence rule beforehand
let recurrenceRule: string | undefined = undefined;
@@ -121,13 +105,6 @@ export default class OrganizerScheduledEmail {
};
}
protected getMailerOptions() {
return {
transport: serverConfig.transport,
from: serverConfig.from,
};
}
protected getTextBody(): string {
return `
${this.calEvent.organizer.language.translate(
@@ -139,10 +116,6 @@ ${getRichDescription(this.calEvent)}
`.trim();
}
protected printNodeMailerError(error: Error): void {
console.error("SEND_BOOKING_CONFIRMATION_ERROR", this.calEvent.organizer.email, error);
}
protected getHtmlBody(): string {
const headerContent = this.calEvent.organizer.language.translate("confirmed_event_type_subject", {
eventType: this.calEvent.type,

View File

@@ -1,10 +1,8 @@
import { TFunction } from "next-i18next";
import nodemailer from "nodemailer";
import { getErrorFromUnknown } from "@calcom/lib/errors";
import { serverConfig } from "@calcom/lib/serverConfig";
import BaseEmail from "@lib/emails/templates/_base-email";
import { emailHead, linkIcon, emailBodyLogo } from "./common";
import { emailBodyLogo, emailHead, linkIcon } from "./common";
export type TeamInvite = {
language: TFunction;
@@ -14,37 +12,15 @@ export type TeamInvite = {
joinLink: string;
};
export default class TeamInviteEmail {
export default class TeamInviteEmail extends BaseEmail {
teamInviteEvent: TeamInvite;
constructor(teamInviteEvent: TeamInvite) {
super();
this.name = "SEND_TEAM_INVITE_EMAIL";
this.teamInviteEvent = teamInviteEvent;
}
public sendEmail() {
new Promise((resolve, reject) =>
nodemailer
.createTransport(this.getMailerOptions().transport)
.sendMail(this.getNodeMailerPayload(), (_err, info) => {
if (_err) {
const err = getErrorFromUnknown(_err);
this.printNodeMailerError(err);
reject(err);
} else {
resolve(info);
}
})
).catch((e) => console.error("sendEmail", e));
return new Promise((resolve) => resolve("send mail async"));
}
protected getMailerOptions() {
return {
transport: serverConfig.transport,
from: serverConfig.from,
};
}
protected getNodeMailerPayload(): Record<string, unknown> {
return {
to: this.teamInviteEvent.to,
@@ -58,10 +34,6 @@ export default class TeamInviteEmail {
};
}
protected printNodeMailerError(error: Error): void {
console.error("SEND_TEAM_INVITE_EMAIL_ERROR", this.teamInviteEvent.to, error);
}
protected getTextBody(): string {
return "";
}