Skip to content

Commit

Permalink
feat(email-plugin): Added from field to the email config (#168)
Browse files Browse the repository at this point in the history
* feat(email-plugin): Added `from` field to the email config

* test(email-plugin): Fix tests and raise error for missing  field
  • Loading branch information
lsimone authored and michaelbromley committed Sep 25, 2019
1 parent 7568e20 commit 09eb34e
Show file tree
Hide file tree
Showing 8 changed files with 79 additions and 19 deletions.
18 changes: 16 additions & 2 deletions packages/email-plugin/src/default-email-handlers.ts
Original file line number Diff line number Diff line change
@@ -1,34 +1,48 @@
/* tslint:disable:no-non-null-assertion */
import { AccountRegistrationEvent, IdentifierChangeRequestEvent, OrderStateTransitionEvent, PasswordResetEvent } from '@vendure/core';
import {
AccountRegistrationEvent,
IdentifierChangeRequestEvent,
OrderStateTransitionEvent,
PasswordResetEvent,
} from '@vendure/core';

import { EmailEventListener } from './event-listener';
import { mockAccountRegistrationEvent, mockEmailAddressChangeEvent, mockOrderStateTransitionEvent, mockPasswordResetEvent } from './mock-events';
import {
mockAccountRegistrationEvent,
mockEmailAddressChangeEvent,
mockOrderStateTransitionEvent,
mockPasswordResetEvent,
} from './mock-events';

export const orderConfirmationHandler = new EmailEventListener('order-confirmation')
.on(OrderStateTransitionEvent)
.filter(event => event.toState === 'PaymentSettled' && !!event.order.customer)
.setRecipient(event => event.order.customer!.emailAddress)
.setFrom(`{{ fromAddress }}`)
.setSubject(`Order confirmation for #{{ order.code }}`)
.setTemplateVars(event => ({ order: event.order }))
.setMockEvent(mockOrderStateTransitionEvent);

export const emailVerificationHandler = new EmailEventListener('email-verification')
.on(AccountRegistrationEvent)
.setRecipient(event => event.user.identifier)
.setFrom(`{{ fromAddress }}`)
.setSubject(`Please verify your email address`)
.setTemplateVars(event => ({ user: event.user }))
.setMockEvent(mockAccountRegistrationEvent);

export const passwordResetHandler = new EmailEventListener('password-reset')
.on(PasswordResetEvent)
.setRecipient(event => event.user.identifier)
.setFrom(`{{ fromAddress }}`)
.setSubject(`Forgotten password reset`)
.setTemplateVars(event => ({ user: event.user }))
.setMockEvent(mockPasswordResetEvent);

export const emailAddressChangeHandler = new EmailEventListener('email-address-change')
.on(IdentifierChangeRequestEvent)
.setRecipient(event => event.user.pendingIdentifier!)
.setFrom(`{{ fromAddress }}`)
.setSubject(`Please verify your change of email address`)
.setTemplateVars(event => ({ user: event.user }))
.setMockEvent(mockEmailAddressChangeEvent);
Expand Down
2 changes: 2 additions & 0 deletions packages/email-plugin/src/email-sender.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ export class EmailSender {

private async sendMail(email: EmailDetails, transporter: Mail): Promise<any> {
return transporter.sendMail({
from: email.from,
to: email.recipient,
subject: email.subject,
html: email.body,
Expand All @@ -75,6 +76,7 @@ export class EmailSender {
private async sendFileJson(email: EmailDetails, pathWithoutExt: string) {
const output = {
date: new Date().toLocaleString(),
from: email.from,
recipient: email.recipient,
subject: email.subject,
body: email.body,
Expand Down
26 changes: 22 additions & 4 deletions packages/email-plugin/src/event-listener.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ export class EmailEventHandler<T extends string = string, Event extends EventWit
private filterFns: Array<(event: Event) => boolean> = [];
private configurations: EmailTemplateConfig[] = [];
private defaultSubject: string;
private from: string;
private _mockEvent: Omit<Event, 'ctx'> | undefined;

constructor(public listener: EmailEventListener<T>, public event: Type<Event>) {}
Expand Down Expand Up @@ -170,6 +171,16 @@ export class EmailEventHandler<T extends string = string, Event extends EventWit
return this;
}

/**
* @description
* Sets the default from field of the email. The from string may use Handlebars variables defined by the
* setTemplateVars() method.
*/
setFrom(from: string): EmailEventHandler<T, Event> {
this.from = from;
return this;
}

/**
* @description
* Add configuration for another template other than the default `"body.hbs"`. Use this method to define specific
Expand All @@ -189,7 +200,9 @@ export class EmailEventHandler<T extends string = string, Event extends EventWit
handle(
event: Event,
globals: { [key: string]: any } = {},
): { recipient: string; templateVars: any; subject: string; templateFile: string } | undefined {
):
| { from: string; recipient: string; templateVars: any; subject: string; templateFile: string }
| undefined {
for (const filterFn of this.filterFns) {
if (!filterFn(event)) {
return;
Expand All @@ -198,9 +211,13 @@ export class EmailEventHandler<T extends string = string, Event extends EventWit
if (!this.setRecipientFn) {
throw new Error(
`No setRecipientFn has been defined. ` +
`Remember to call ".setRecipient()" when setting up the EmailEventHandler for ${
this.type
}`,
`Remember to call ".setRecipient()" when setting up the EmailEventHandler for ${this.type}`,
);
}
if (this.from === undefined) {
throw new Error(
`No from field has been defined. ` +
`Remember to call ".setFrom()" when setting up the EmailEventHandler for ${this.type}`,
);
}
const { ctx } = event;
Expand All @@ -209,6 +226,7 @@ export class EmailEventHandler<T extends string = string, Event extends EventWit
const templateVars = this.setTemplateVarsFn ? this.setTemplateVarsFn(event, globals) : {};
return {
recipient,
from: this.from,
templateVars: { ...globals, ...templateVars },
subject: configuration ? configuration.subject : this.defaultSubject,
templateFile: configuration ? configuration.templateFile : 'body.hbs',
Expand Down
10 changes: 4 additions & 6 deletions packages/email-plugin/src/handlebars-mjml-generator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,15 @@ export class HandlebarsMjmlGenerator implements EmailGenerator {
this.registerHelpers();
}

generate(
subject: string,
template: string,
templateVars: any,
) {
generate(from: string, subject: string, template: string, templateVars: any) {
const compiledFrom = Handlebars.compile(from);
const compiledSubject = Handlebars.compile(subject);
const compiledTemplate = Handlebars.compile(template);
const fromResult = compiledFrom(templateVars);
const subjectResult = compiledSubject(templateVars);
const mjml = compiledTemplate(templateVars);
const body = mjml2html(mjml).html;
return { subject: subjectResult, body };
return { from: fromResult, subject: subjectResult, body };
}

private registerPartials(partialsPath: string) {
Expand Down
8 changes: 2 additions & 6 deletions packages/email-plugin/src/noop-email-generator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,7 @@ import { EmailGenerator } from './types';
* Simply passes through the subject and template content without modification.
*/
export class NoopEmailGenerator implements EmailGenerator {
generate(
subject: string,
body: string,
templateVars: any,
) {
return { subject, body };
generate(from: string, subject: string, body: string, templateVars: any) {
return { from, subject, body };
}
}
25 changes: 25 additions & 0 deletions packages/email-plugin/src/plugin.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ describe('EmailPlugin', () => {
.on(MockEvent)
.filter(event => event.shouldSend === true)
.setRecipient(() => '[email protected]')
.setFrom('"test from" <[email protected]>')
.setSubject('test subject');

const module = await initPluginWithHandlers([handler]);
Expand All @@ -74,6 +75,7 @@ describe('EmailPlugin', () => {
.on(MockEvent)
.filter(event => event.shouldSend === true)
.filter(event => !!event.ctx.user)
.setFrom('"test from" <[email protected]>')
.setRecipient(() => '[email protected]')
.setSubject('test subject');

Expand All @@ -99,6 +101,7 @@ describe('EmailPlugin', () => {
it('interpolates subject', async () => {
const handler = new EmailEventListener('test')
.on(MockEvent)
.setFrom('"test from" <[email protected]>')
.setRecipient(() => '[email protected]')
.setSubject('Hello {{ subjectVar }}')
.setTemplateVars(event => ({ subjectVar: 'foo' }));
Expand All @@ -114,6 +117,7 @@ describe('EmailPlugin', () => {
it('interpolates body', async () => {
const handler = new EmailEventListener('test')
.on(MockEvent)
.setFrom('"test from" <[email protected]>')
.setRecipient(() => '[email protected]')
.setSubject('Hello')
.setTemplateVars(event => ({ testVar: 'this is the test var' }));
Expand All @@ -129,6 +133,7 @@ describe('EmailPlugin', () => {
it('interpolates globalTemplateVars', async () => {
const handler = new EmailEventListener('test')
.on(MockEvent)
.setFrom('"test from" <[email protected]>')
.setRecipient(() => '[email protected]')
.setSubject('Hello {{ globalVar }}');

Expand All @@ -142,9 +147,27 @@ describe('EmailPlugin', () => {
await module.close();
});

it('interpolates from', async () => {
const handler = new EmailEventListener('test')
.on(MockEvent)
.setFrom('"test from {{ globalVar }}" <[email protected]>')
.setRecipient(() => '[email protected]')
.setSubject('Hello');

const module = await initPluginWithHandlers([handler], {
globalTemplateVars: { globalVar: 'baz' },
});

eventBus.publish(new MockEvent(ctx, true));
await pause();
expect(onSend.mock.calls[0][0].from).toBe('"test from baz" <[email protected]>');
await module.close();
});

it('globalTemplateVars available in setTemplateVars method', async () => {
const handler = new EmailEventListener('test')
.on(MockEvent)
.setFrom('"test from" <[email protected]>')
.setRecipient(() => '[email protected]')
.setSubject('Hello {{ testVar }}')
.setTemplateVars((event, globals) => ({ testVar: globals.globalVar + ' quux' }));
Expand All @@ -162,6 +185,7 @@ describe('EmailPlugin', () => {
it('setTemplateVars overrides globals', async () => {
const handler = new EmailEventListener('test')
.on(MockEvent)
.setFrom('"test from" <[email protected]>')
.setRecipient(() => '[email protected]')
.setSubject('Hello {{ name }}')
.setTemplateVars((event, globals) => ({ name: 'quux' }));
Expand All @@ -184,6 +208,7 @@ describe('EmailPlugin', () => {
it('additional LanguageCode', async () => {
const handler = new EmailEventListener('test')
.on(MockEvent)
.setFrom('"test from" <[email protected]>')
.setSubject('Hello, {{ name }}!')
.setRecipient(() => '[email protected]')
.setTemplateVars(() => ({ name: 'Test' }))
Expand Down
7 changes: 6 additions & 1 deletion packages/email-plugin/src/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,12 @@ export class EmailPlugin implements OnVendureBootstrap, OnVendureClose {
return;
}
const bodySource = await this.templateLoader.loadTemplate(type, result.templateFile);
const generated = await this.generator.generate(result.subject, bodySource, result.templateVars);
const generated = await this.generator.generate(
result.from,
result.subject,
bodySource,
result.templateVars,
);
const emailDetails = { ...generated, recipient: result.recipient };
await this.emailSender.send(emailDetails, this.transport);
}
Expand Down
2 changes: 2 additions & 0 deletions packages/email-plugin/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,7 @@ export interface NoopTransportOptions {
* @docsPage Email Plugin Types
*/
export interface EmailDetails {
from: string;
recipient: string;
subject: string;
body: string;
Expand Down Expand Up @@ -244,6 +245,7 @@ export interface EmailGenerator<T extends string = any, E extends VendureEvent =
* interpolated email text.
*/
generate(
from: string,
subject: string,
body: string,
templateVars: { [key: string]: any },
Expand Down

0 comments on commit 09eb34e

Please sign in to comment.