Преглед изворни кода

refactor(email-plugin): Refactor to new Plugin architecture

Relates to #123
Michael Bromley пре 6 година
родитељ
комит
5cb789fb4c
2 измењених фајлова са 110 додато и 74 уклоњено
  1. 53 34
      packages/email-plugin/src/plugin.spec.ts
  2. 57 40
      packages/email-plugin/src/plugin.ts

+ 53 - 34
packages/email-plugin/src/plugin.spec.ts

@@ -1,4 +1,5 @@
 /* tslint:disable:no-non-null-assertion */
+import { Test, TestingModule } from '@nestjs/testing';
 import path from 'path';
 
 import { LanguageCode } from '../../common/lib/generated-types';
@@ -18,32 +19,32 @@ describe('EmailPlugin', () => {
     let eventBus: EventBus;
     let onSend: jest.Mock;
 
-    async function initPluginWithHandlers(handlers: Array<EmailEventHandler<string, any>>, options?: Partial<EmailPluginOptions>) {
-        eventBus = new EventBus();
+    async function initPluginWithHandlers(
+        handlers: Array<EmailEventHandler<string, any>>,
+        options?: Partial<EmailPluginOptions>,
+    ) {
         onSend = jest.fn();
-        plugin = new EmailPlugin({
-            templatePath: path.join(__dirname, '../test-templates'),
-            transport: {
-                type: 'testing',
-                onSend,
-            },
-            handlers,
-            ...options,
-        });
-
-        const inject = (token: any): any => {
-            if (token === EventBus) {
-                return eventBus;
-            } else {
-                throw new Error(`Was not expecting to inject the token ${token}`);
-            }
-        };
-
-        await plugin.onBootstrap(inject);
+        const module = await Test.createTestingModule({
+            imports: [
+                EmailPlugin.init({
+                    templatePath: path.join(__dirname, '../test-templates'),
+                    transport: {
+                        type: 'testing',
+                        onSend,
+                    },
+                    handlers,
+                    ...options,
+                }),
+            ],
+        }).compile();
+
+        plugin = module.get(EmailPlugin);
+        eventBus = module.get(EventBus);
+        await plugin.onVendureBootstrap();
+        return module;
     }
 
     describe('event filtering', () => {
-
         const ctx = {
             channel: { code: DEFAULT_CHANNEL_CODE },
             languageCode: LanguageCode.en,
@@ -56,7 +57,7 @@ describe('EmailPlugin', () => {
                 .setRecipient(() => 'test@test.com')
                 .setSubject('test subject');
 
-            await initPluginWithHandlers([handler]);
+            const module = await initPluginWithHandlers([handler]);
 
             eventBus.publish(new MockEvent(ctx, false));
             await pause();
@@ -65,6 +66,7 @@ describe('EmailPlugin', () => {
             eventBus.publish(new MockEvent(ctx, true));
             await pause();
             expect(onSend).toHaveBeenCalledTimes(1);
+            await module.close();
         });
 
         it('multiple filters', async () => {
@@ -75,7 +77,7 @@ describe('EmailPlugin', () => {
                 .setRecipient(() => 'test@test.com')
                 .setSubject('test subject');
 
-            await initPluginWithHandlers([handler]);
+            const module = await initPluginWithHandlers([handler]);
 
             eventBus.publish(new MockEvent(ctx, true));
             await pause();
@@ -84,6 +86,7 @@ describe('EmailPlugin', () => {
             eventBus.publish(new MockEvent({ ...ctx, user: 'joe' }, true));
             await pause();
             expect(onSend).toHaveBeenCalledTimes(1);
+            await module.close();
         });
     });
 
@@ -100,11 +103,12 @@ describe('EmailPlugin', () => {
                 .setSubject('Hello {{ subjectVar }}')
                 .setTemplateVars(event => ({ subjectVar: 'foo' }));
 
-            await initPluginWithHandlers([handler]);
+            const module = await initPluginWithHandlers([handler]);
 
             eventBus.publish(new MockEvent(ctx, true));
             await pause();
             expect(onSend.mock.calls[0][0].subject).toBe('Hello foo');
+            await module.close();
         });
 
         it('interpolates body', async () => {
@@ -114,11 +118,12 @@ describe('EmailPlugin', () => {
                 .setSubject('Hello')
                 .setTemplateVars(event => ({ testVar: 'this is the test var' }));
 
-            await initPluginWithHandlers([handler]);
+            const module = await initPluginWithHandlers([handler]);
 
             eventBus.publish(new MockEvent(ctx, true));
             await pause();
             expect(onSend.mock.calls[0][0].body).toContain('this is the test var');
+            await module.close();
         });
 
         it('interpolates globalTemplateVars', async () => {
@@ -127,11 +132,14 @@ describe('EmailPlugin', () => {
                 .setRecipient(() => 'test@test.com')
                 .setSubject('Hello {{ globalVar }}');
 
-            await initPluginWithHandlers([handler], { globalTemplateVars: { globalVar: 'baz' } });
+            const module = await initPluginWithHandlers([handler], {
+                globalTemplateVars: { globalVar: 'baz' },
+            });
 
             eventBus.publish(new MockEvent(ctx, true));
             await pause();
             expect(onSend.mock.calls[0][0].subject).toBe('Hello baz');
+            await module.close();
         });
 
         it('globalTemplateVars available in setTemplateVars method', async () => {
@@ -141,11 +149,14 @@ describe('EmailPlugin', () => {
                 .setSubject('Hello {{ testVar }}')
                 .setTemplateVars((event, globals) => ({ testVar: globals.globalVar + ' quux' }));
 
-            await initPluginWithHandlers([handler], { globalTemplateVars: { globalVar: 'baz' } });
+            const module = await initPluginWithHandlers([handler], {
+                globalTemplateVars: { globalVar: 'baz' },
+            });
 
             eventBus.publish(new MockEvent(ctx, true));
             await pause();
             expect(onSend.mock.calls[0][0].subject).toBe('Hello baz quux');
+            await module.close();
         });
 
         it('setTemplateVars overrides globals', async () => {
@@ -155,11 +166,12 @@ describe('EmailPlugin', () => {
                 .setSubject('Hello {{ name }}')
                 .setTemplateVars((event, globals) => ({ name: 'quux' }));
 
-            await initPluginWithHandlers([handler], { globalTemplateVars: { name: 'baz' } });
+            const module = await initPluginWithHandlers([handler], { globalTemplateVars: { name: 'baz' } });
 
             eventBus.publish(new MockEvent(ctx, true));
             await pause();
             expect(onSend.mock.calls[0][0].subject).toBe('Hello quux');
+            await module.close();
         });
     });
 
@@ -182,7 +194,7 @@ describe('EmailPlugin', () => {
                     subject: 'Servus, {{ name }}!',
                 });
 
-            await initPluginWithHandlers([handler]);
+            const module = await initPluginWithHandlers([handler]);
 
             eventBus.publish(new MockEvent({ ...ctx, languageCode: LanguageCode.ta }, true));
             await pause();
@@ -193,13 +205,20 @@ describe('EmailPlugin', () => {
             await pause();
             expect(onSend.mock.calls[1][0].subject).toBe('Servus, Test!');
             expect(onSend.mock.calls[1][0].body).toContain('German body.');
+            await module.close();
         });
     });
 
     describe('orderConfirmationHandler', () => {
-
+        let module: TestingModule;
         beforeEach(async () => {
-            await initPluginWithHandlers([orderConfirmationHandler], { templatePath: path.join(__dirname, '../templates') });
+            module = await initPluginWithHandlers([orderConfirmationHandler], {
+                templatePath: path.join(__dirname, '../templates'),
+            });
+        });
+
+        afterEach(async () => {
+            await module.close();
         });
 
         const ctx = {
@@ -207,12 +226,12 @@ describe('EmailPlugin', () => {
             languageCode: LanguageCode.en,
         } as any;
 
-        const order = {
+        const order = ({
             code: 'ABCDE',
             customer: {
                 emailAddress: 'test@test.com',
             },
-        } as Partial<Order> as any;
+        } as Partial<Order>) as any;
 
         it('filters events with wrong order state', async () => {
             eventBus.publish(new OrderStateTransitionEvent('AddingItems', 'ArrangingPayment', ctx, order));

+ 57 - 40
packages/email-plugin/src/plugin.ts

@@ -1,4 +1,14 @@
-import { createProxyHandler, EventBus, InternalServerError, Logger, Type, VendureConfig, VendurePlugin } from '@vendure/core';
+import {
+    createProxyHandler,
+    EventBus,
+    EventBusModule,
+    InternalServerError,
+    Logger,
+    OnVendureBootstrap,
+    OnVendureClose,
+    VendureConfig,
+    VendurePlugin,
+} from '@vendure/core';
 import fs from 'fs-extra';
 
 import { DevMailbox } from './dev-mailbox';
@@ -6,7 +16,12 @@ import { EmailSender } from './email-sender';
 import { EmailEventHandler } from './event-listener';
 import { HandlebarsMjmlGenerator } from './handlebars-mjml-generator';
 import { TemplateLoader } from './template-loader';
-import { EmailPluginDevModeOptions, EmailPluginOptions, EmailTransportOptions, EventWithContext } from './types';
+import {
+    EmailPluginDevModeOptions,
+    EmailPluginOptions,
+    EmailTransportOptions,
+    EventWithContext,
+} from './types';
 
 /**
  * @description
@@ -100,7 +115,7 @@ import { EmailPluginDevModeOptions, EmailPluginOptions, EmailTransportOptions, E
  * `outputPath` property.
  *
  * ```ts
- * new EmailPlugin({
+ * EmailPlugin.init({
  *   devMode: true,
  *   handlers: defaultEmailHandlers,
  *   templatePath: path.join(__dirname, 'vendure/email/templates'),
@@ -116,17 +131,42 @@ import { EmailPluginDevModeOptions, EmailPluginOptions, EmailTransportOptions, E
  *
  * @docsCategory EmailPlugin
  */
-export class EmailPlugin implements VendurePlugin {
-    private readonly transport: EmailTransportOptions;
-    private readonly options: EmailPluginOptions | EmailPluginDevModeOptions;
-    private eventBus: EventBus;
+@VendurePlugin({
+    imports: [EventBusModule],
+    configuration: (config: Required<VendureConfig>) => EmailPlugin.configure(config),
+})
+export class EmailPlugin implements OnVendureBootstrap, OnVendureClose {
+    private static options: EmailPluginOptions | EmailPluginDevModeOptions;
+    private transport: EmailTransportOptions;
     private templateLoader: TemplateLoader;
     private emailSender: EmailSender;
     private generator: HandlebarsMjmlGenerator;
     private devMailbox: DevMailbox | undefined;
 
-    constructor(options: EmailPluginOptions | EmailPluginDevModeOptions) {
+    constructor(private eventBus: EventBus) {}
+
+    static init(options: EmailPluginOptions | EmailPluginDevModeOptions) {
         this.options = options;
+        return EmailPlugin;
+    }
+
+    /** @internal */
+    static configure(
+        config: Required<VendureConfig>,
+    ): Required<VendureConfig> | Promise<Required<VendureConfig>> {
+        if (isDevModeOptions(this.options) && this.options.mailboxPort !== undefined) {
+            const route = 'mailbox';
+            config.middleware.push({
+                handler: createProxyHandler({ port: this.options.mailboxPort, route, label: 'Dev Mailbox' }),
+                route,
+            });
+        }
+        return config;
+    }
+
+    /** @internal */
+    async onVendureBootstrap(): Promise<void> {
+        const options = EmailPlugin.options;
         if (isDevModeOptions(options)) {
             this.transport = {
                 type: 'file',
@@ -141,48 +181,32 @@ export class EmailPlugin implements VendurePlugin {
             }
             this.transport = options.transport;
         }
-    }
-
-    /** @internal */
-    configure(config: Required<VendureConfig>): Required<VendureConfig> | Promise<Required<VendureConfig>> {
-        if (isDevModeOptions(this.options) && this.options.mailboxPort !== undefined) {
-            const route = 'mailbox';
-            config.middleware.push({
-                handler: createProxyHandler({ port: this.options.mailboxPort, route, label: 'Dev Mailbox' }),
-                route,
-            });
-        }
-        return config;
-    }
 
-    /** @internal */
-    async onBootstrap(inject: <T>(type: Type<T>) => T): Promise<void> {
-        this.eventBus = inject(EventBus);
-        this.templateLoader = new TemplateLoader(this.options.templatePath);
+        this.templateLoader = new TemplateLoader(options.templatePath);
         this.emailSender = new EmailSender();
         this.generator = new HandlebarsMjmlGenerator();
 
-        if (isDevModeOptions(this.options) && this.options.mailboxPort !== undefined) {
+        if (isDevModeOptions(options) && options.mailboxPort !== undefined) {
             this.devMailbox = new DevMailbox();
-            this.devMailbox.serve(this.options);
+            this.devMailbox.serve(options);
             this.devMailbox.handleMockEvent((handler, event) => this.handleEvent(handler, event));
         }
 
         await this.setupEventSubscribers();
         if (this.generator.onInit) {
-            await this.generator.onInit.call(this.generator, this.options);
+            await this.generator.onInit.call(this.generator, options);
         }
     }
 
     /** @internal */
-    async onClose() {
+    async onVendureClose() {
         if (this.devMailbox) {
             this.devMailbox.destroy();
         }
     }
 
     private async setupEventSubscribers() {
-        for (const handler of this.options.handlers) {
+        for (const handler of EmailPlugin.options.handlers) {
             this.eventBus.subscribe(handler.event, event => {
                 return this.handleEvent(handler, event);
             });
@@ -198,19 +222,12 @@ export class EmailPlugin implements VendurePlugin {
     private async handleEvent(handler: EmailEventHandler, event: EventWithContext) {
         Logger.debug(`Handling event "${handler.type}"`, 'EmailPlugin');
         const { type } = handler;
-        const result = handler.handle(event, this.options.globalTemplateVars);
+        const result = handler.handle(event, EmailPlugin.options.globalTemplateVars);
         if (!result) {
             return;
         }
-        const bodySource = await this.templateLoader.loadTemplate(
-            type,
-            result.templateFile,
-        );
-        const generated = await this.generator.generate(
-            result.subject,
-            bodySource,
-            result.templateVars,
-        );
+        const bodySource = await this.templateLoader.loadTemplate(type, result.templateFile);
+        const generated = await this.generator.generate(result.subject, bodySource, result.templateVars);
         const emailDetails = { ...generated, recipient: result.recipient };
         await this.emailSender.send(emailDetails, this.transport);
     }