From 3cf3f633aad39c4a94dfd4caf2829eec8765a2e0 Mon Sep 17 00:00:00 2001 From: "Fabien \"egg\" O'Carroll" Date: Sun, 12 Mar 2023 23:11:45 +0700 Subject: [PATCH] Refactored staff-service This moves the shared data between all templates into a method so that it's calculated in a single place, as well as exposing separate render methods for HTML and Text. We've also added some new custom helpers for use in the handlebars templates --- ghost/staff-service/lib/emails.js | 63 ++++++++++++++++++- .../staff-service/test/staff-service.test.js | 62 ++++++++++++++++++ 2 files changed, 122 insertions(+), 3 deletions(-) diff --git a/ghost/staff-service/lib/emails.js b/ghost/staff-service/lib/emails.js index 2aaefc6148..a7109ac57a 100644 --- a/ghost/staff-service/lib/emails.js +++ b/ghost/staff-service/lib/emails.js @@ -162,6 +162,23 @@ class StaffServiceEmails { } } + /** + * @param {object} recipient + * @param {string} recipient.email + * @param {string} recipient.slug + */ + async getSharedData(recipient) { + return { + siteTitle: this.settingsCache.get('title'), + siteUrl: this.urlUtils.getSiteUrl(), + siteDomain: this.siteDomain, + accentColor: this.settingsCache.get('accent_color'), + fromEmail: this.fromEmailAddress, + toEmail: recipient.email, + staffUrl: this.urlUtils.urlJoin(this.urlUtils.urlFor('admin', true), '#', `/settings/staff/${recipient.slug}`) + }; + } + /** * * @param {object} eventData @@ -322,13 +339,53 @@ class StaffServiceEmails { }); } - async renderEmailTemplate(templateName, data) { + async renderHTML(templateName, data) { const htmlTemplateSource = await fs.readFile(path.join(__dirname, './email-templates/', `${templateName}.hbs`), 'utf8'); const htmlTemplate = this.Handlebars.compile(Buffer.from(htmlTemplateSource).toString()); + + this.Handlebars.registerHelper('eq', function (arg, value, options) { + if (arg === value) { + return options.fn(this); + } else { + return options.inverse(this); + } + }); + + this.Handlebars.registerHelper('limit', function (array, limit) { + if (!Array.isArray(array)) { + return []; + } + return array.slice(0,limit); + }); + + let sharedData = {}; + if (data.recipient) { + sharedData = await this.getSharedData(data.recipient); + } + + return htmlTemplate({ + ...data, + ...sharedData + }); + } + + async renderText(templateName, data) { const textTemplate = require(`./email-templates/${templateName}.txt.js`); - const html = htmlTemplate(data); - const text = textTemplate(data); + let sharedData = {}; + if (data.recipient) { + sharedData = await this.getSharedData(data.recipient); + } + + return textTemplate({ + ...data, + ...sharedData + }); + } + + async renderEmailTemplate(templateName, data) { + const html = await this.renderHTML(templateName, data); + const text = await this.renderText(templateName, data); return {html, text}; } diff --git a/ghost/staff-service/test/staff-service.test.js b/ghost/staff-service/test/staff-service.test.js index 4b61a44b21..d76eb608a9 100644 --- a/ghost/staff-service/test/staff-service.test.js +++ b/ghost/staff-service/test/staff-service.test.js @@ -4,6 +4,9 @@ const sinon = require('sinon'); const {MemberCreatedEvent, SubscriptionCancelledEvent, SubscriptionActivatedEvent} = require('@tryghost/member-events'); const {MilestoneCreatedEvent} = require('@tryghost/milestones'); +// Stuff we are testing +const DomainEvents = require('@tryghost/domain-events'); + require('./utils'); const StaffService = require('../index'); @@ -191,6 +194,65 @@ describe('StaffService', function () { subscribeStub.calledWith(MemberCreatedEvent).should.be.true(); subscribeStub.calledWith(MilestoneCreatedEvent).should.be.true(); }); + + it('listens to events', async function () { + service = new StaffService({ + logging: { + info: loggingInfoStub, + warn: () => {}, + error: () => {} + }, + models: { + User: { + getEmailAlertUsers: getEmailAlertUsersStub + } + }, + mailer: { + send: mailStub + }, + DomainEvents, + settingsCache, + urlUtils, + settingsHelpers + }); + service.subscribeEvents(); + sinon.spy(service, 'handleEvent'); + DomainEvents.dispatch(MemberCreatedEvent.create({ + source: 'member', + memberId: 'member-2' + })); + await DomainEvents.allSettled(); + service.handleEvent.calledWith(MemberCreatedEvent).should.be.true(); + + DomainEvents.dispatch(SubscriptionActivatedEvent.create({ + source: 'member', + memberId: 'member-1', + subscriptionId: 'sub-1', + offerId: 'offer-1', + tierId: 'tier-1' + })); + await DomainEvents.allSettled(); + service.handleEvent.calledWith(SubscriptionActivatedEvent).should.be.true(); + + DomainEvents.dispatch(SubscriptionCancelledEvent.create({ + source: 'member', + memberId: 'member-1', + subscriptionId: 'sub-1', + tierId: 'tier-1' + })); + await DomainEvents.allSettled(); + service.handleEvent.calledWith(SubscriptionCancelledEvent).should.be.true(); + + DomainEvents.dispatch(MilestoneCreatedEvent.create({ + milestone: { + type: 'arr', + value: '100', + currency: 'usd' + } + })); + await DomainEvents.allSettled(); + service.handleEvent.calledWith(MilestoneCreatedEvent).should.be.true(); + }); }); describe('handleEvent', function () {