Skip to content

Commit

Permalink
feat(email-plugin): Add support for computed email subject (#2863)
Browse files Browse the repository at this point in the history
  • Loading branch information
rein1410 authored Jun 14, 2024
1 parent 37e6a35 commit e546f24
Show file tree
Hide file tree
Showing 3 changed files with 73 additions and 6 deletions.
16 changes: 13 additions & 3 deletions packages/email-plugin/src/handler/event-handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
LoadDataFn,
SetAttachmentsFn,
SetOptionalAddressFieldsFn,
SetSubjectFn,
SetTemplateVarsFn,
} from '../types';

Expand Down Expand Up @@ -135,6 +136,7 @@ import {
export class EmailEventHandler<T extends string = string, Event extends EventWithContext = EventWithContext> {
private setRecipientFn: (event: Event) => string;
private setLanguageCodeFn: (event: Event) => LanguageCode | undefined;
private setSubjectFn?: SetSubjectFn<Event>;
private setTemplateVarsFn: SetTemplateVarsFn<Event>;
private setAttachmentsFn?: SetAttachmentsFn<Event>;
private setOptionalAddressFieldsFn?: SetOptionalAddressFieldsFn<Event>;
Expand Down Expand Up @@ -214,8 +216,12 @@ export class EmailEventHandler<T extends string = string, Event extends EventWit
* Sets the default subject of the email. The subject string may use Handlebars variables defined by the
* setTemplateVars() method.
*/
setSubject(defaultSubject: string): EmailEventHandler<T, Event> {
this.defaultSubject = defaultSubject;
setSubject(defaultSubject: string | SetSubjectFn<Event>): EmailEventHandler<T, Event> {
if (typeof defaultSubject === 'string') {
this.defaultSubject = defaultSubject;
} else {
this.setSubjectFn = defaultSubject;
}
return this;
}

Expand Down Expand Up @@ -370,7 +376,11 @@ export class EmailEventHandler<T extends string = string, Event extends EventWit
const { ctx } = event;
const languageCode = this.setLanguageCodeFn?.(event) || ctx.languageCode;
const configuration = this.getBestConfiguration(ctx.channel.code, languageCode);
const subject = configuration ? configuration.subject : this.defaultSubject;
const subject = configuration
? configuration.subject
: this.setSubjectFn
? await this.setSubjectFn(event, ctx, injector)
: this.defaultSubject;
if (subject == null) {
throw new Error(
`No subject field has been defined. ` +
Expand Down
53 changes: 51 additions & 2 deletions packages/email-plugin/src/plugin.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,9 @@ import { EmailSender } from './sender/email-sender';
import { EmailEventHandler } from './handler/event-handler';
import { EmailEventListener } from './event-listener';
import { EmailPlugin } from './plugin';
import { EmailDetails, EmailPluginOptions, EmailTransportOptions } from './types';
import { EmailDetails, EmailPluginOptions, EmailTransportOptions, LoadTemplateInput } from './types';
import { TemplateLoader } from './template-loader/template-loader';
import fs from 'fs-extra';

describe('EmailPlugin', () => {
let eventBus: EventBus;
Expand Down Expand Up @@ -913,6 +915,53 @@ describe('EmailPlugin', () => {
expect(transport.type).toBe('testing');
});
});

describe('Dynamic subject handling', () => {
it('With string', async () => {
const ctx = RequestContext.deserialize({
_channel: { code: DEFAULT_CHANNEL_CODE },
_languageCode: LanguageCode.en,
} as any);
const handler = new EmailEventListener('test')
.on(MockEvent)
.setFrom('"test from" <noreply@test.com>')
.setRecipient(() => 'test@test.com')
.setSubject('Hello')
.setTemplateVars(event => ({ subjectVar: 'foo' }));

await initPluginWithHandlers([handler]);

eventBus.publish(new MockEvent(ctx, true));
await pause();
expect(onSend.mock.calls[0][0].subject).toBe('Hello');
expect(onSend.mock.calls[0][0].recipient).toBe('test@test.com');
expect(onSend.mock.calls[0][0].from).toBe('"test from" <noreply@test.com>');
});
it('With callback function', async () => {
const ctx = RequestContext.deserialize({
_channel: { code: DEFAULT_CHANNEL_CODE },
_languageCode: LanguageCode.en,
} as any);
const handler = new EmailEventListener('test')
.on(MockEvent)
.setFrom('"test from" <noreply@test.com>')
.setRecipient(() => 'test@test.com')
.setSubject(async (_e, _ctx, _i) => {
const service = _i.get(MockService)
const mockData = await service.someAsyncMethod()
return `Hello from ${mockData} and {{ subjectVar }}`;
})
.setTemplateVars(event => ({ subjectVar: 'foo' }));

await initPluginWithHandlers([handler]);

eventBus.publish(new MockEvent(ctx, true));
await pause();
expect(onSend.mock.calls[0][0].subject).toBe('Hello from loaded data and foo');
expect(onSend.mock.calls[0][0].recipient).toBe('test@test.com');
expect(onSend.mock.calls[0][0].from).toBe('"test from" <noreply@test.com>');
});
})
});

class FakeCustomSender implements EmailSender {
Expand All @@ -931,4 +980,4 @@ class MockService {
someAsyncMethod() {
return Promise.resolve('loaded data');
}
}
}
10 changes: 9 additions & 1 deletion packages/email-plugin/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ import { Injector, RequestContext, SerializedRequestContext, VendureEvent } from
import { Attachment } from 'nodemailer/lib/mailer';
import SESTransport from 'nodemailer/lib/ses-transport';
import SMTPTransport from 'nodemailer/lib/smtp-transport';
import { EmailEventHandler } from './handler/event-handler';

import { EmailGenerator } from './generator/email-generator';
import { EmailEventHandler } from './handler/event-handler';
import { EmailSender } from './sender/email-sender';
import { TemplateLoader } from './template-loader/template-loader';

Expand Down Expand Up @@ -387,6 +387,14 @@ export type SetTemplateVarsFn<Event> = (
*/
export type SetAttachmentsFn<Event> = (event: Event) => EmailAttachment[] | Promise<EmailAttachment[]>;

/**
* @description
* A function used to define the subject to be sent with the email.
* @docsCategory core plugins/EmailPlugin
* @docsPage Email Plugin Types
*/
export type SetSubjectFn<Event> = (event: Event, ctx: RequestContext, injector: Injector) => string | Promise<string>;

/**
* @description
* Optional address-related fields for sending the email.
Expand Down

0 comments on commit e546f24

Please sign in to comment.