Skip to content

Commit 25c4b96

Browse files
committed
feat(auth): Restore write-emails-to-disk script
Because: * This script is sometimes useful for debugging emails and seeing true link output with UTM params etc. This commit: * Restores the script from the commit prior to a3c090, with one tweak on config import
1 parent 4e7c414 commit 25c4b96

2 files changed

Lines changed: 225 additions & 0 deletions

File tree

packages/fxa-auth-server/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
"install-ejs": "./scripts/install-ejs.sh",
3535
"gen-keys": "node -r esbuild-register ./scripts/gen_keys.js; node -r esbuild-register ./scripts/oauth_gen_keys.js; node -r esbuild-register ./scripts/gen_vapid_keys.js",
3636
"emails-scss": "node -r esbuild-register ./lib/senders/emails/sass-compile-files.ts",
37+
"write-emails": "yarn emails-scss && node -r esbuild-register ./scripts/write-emails-to-disk.js",
3738
"format": "prettier --write --config ../../_dev/.prettierrc '**'",
3839
"start": "yarn check:mysql && pm2 start pm2.config.js && yarn check:url localhost:9000/__heartbeat__",
3940
"restart": "pm2 restart pm2.config.js",
Lines changed: 224 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,224 @@
1+
#!/usr/bin/env node -r esbuild-register
2+
3+
/* This Source Code Form is subject to the terms of the Mozilla Public
4+
* License, v. 2.0. If a copy of the MPL was not distributed with this
5+
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6+
7+
/**
8+
* Write all emails to disk. Output is written to ./.mail_output/<email type>.html
9+
* and ./.mail_output/<email type>.txt
10+
*
11+
* Usage:
12+
* ./scripts/write-to-disk.js
13+
*
14+
* Emails that are written to disk can be previewed in Firefox
15+
* to give a rough idea of how they would render in real life.
16+
*/
17+
18+
'use strict';
19+
20+
// HACK: Prevent config falling over due to missing secrets
21+
process.env.NODE_ENV = 'dev';
22+
23+
const config = require('../config').default.getProperties();
24+
const createSenders = require('../lib/senders');
25+
const fs = require('fs');
26+
const log = require('../lib/log')({});
27+
const path = require('path');
28+
29+
const OUTPUT_DIRECTORY = path.join(__dirname, '..', '.mail_output');
30+
31+
// Subscription emails are behind a feature flag, enable it
32+
config.subscriptions.transactionalEmails.enabled = true;
33+
34+
const mailSender = {
35+
sendMail: function (emailConfig, done) {
36+
const templateName = emailConfig.headers['X-Template-Name'];
37+
const htmlOutputPath = getEmailOutputPath(templateName, 'html');
38+
fs.writeFileSync(htmlOutputPath, emailConfig.html);
39+
40+
const textOutputPath = getEmailOutputPath(templateName, 'txt');
41+
fs.writeFileSync(textOutputPath, emailConfig.text);
42+
43+
done(null);
44+
},
45+
46+
close: function () {},
47+
};
48+
49+
if (require.main === module) {
50+
Promise.resolve(
51+
createSenders(
52+
log,
53+
config,
54+
{ check: () => Promise.resolve() },
55+
{},
56+
mailSender
57+
)
58+
)
59+
.then((senders) => {
60+
const mailer = senders.email._ungatedMailer;
61+
checkMessageType(mailer);
62+
63+
ensureTargetDirectoryExists();
64+
65+
return sendMails(mailer, getMessageTypesToWrite(mailer));
66+
})
67+
.then(() => {
68+
console.info('done');
69+
process.exit(0);
70+
})
71+
.catch((err) => {
72+
console.error(err.stack);
73+
process.exit(1);
74+
});
75+
}
76+
77+
function getEmailOutputPath(subject, extension) {
78+
const outputFilename = `${subject.replace(/\s+/g, '_')}.${extension}`;
79+
return path.join(OUTPUT_DIRECTORY, outputFilename);
80+
}
81+
82+
function sendMails(mailer, messagesToSend) {
83+
return Promise.all(messagesToSend.map(sendMail.bind(null, mailer)));
84+
}
85+
86+
function sendMail(mailer, messageToSend) {
87+
const parts = messageToSend.split(':');
88+
const messageType = parts[0];
89+
const messageSubType = parts[1];
90+
91+
const planConfig = {
92+
urls: {
93+
termsOfServiceDownload:
94+
'https://example.com/subscription-product/terms/download',
95+
privacyNoticeDownload:
96+
'https://example.com/subscription-product/privacy/download',
97+
download: 'http://getfirefox.com/',
98+
emailIcon: 'http://placekitten.com/512/512',
99+
},
100+
};
101+
102+
const productMetadata = {
103+
'product:termsOfServiceDownloadURL': planConfig.urls.termsOfServiceDownload,
104+
'product:privacyNoticeDownloadURL': planConfig.urls.privacyNoticeDownload,
105+
};
106+
107+
const message = {
108+
acceptLanguage: 'en;q=0.8,en-US;q=0.5,en;q=0.3"',
109+
appStoreLink: 'https://example.com/app-store',
110+
code: '123123',
111+
112+
ip: '10.246.67.38',
113+
location: {
114+
city: 'Madrid',
115+
country: 'Spain',
116+
},
117+
locations: [],
118+
mozillaSupportUrl: 'https://support.mozilla.org',
119+
numberRemaining: 2,
120+
productId: '0123456789abcdef',
121+
planId: 'plan-example',
122+
productName: 'Firefox Fortress',
123+
planEmailIconURL: planConfig.urls.emailIcon,
124+
planSuccessActionButtonURL: planConfig.urls.download,
125+
planInterval: 'week',
126+
planIntervalCount: 4,
127+
playStoreLink: 'https://example.com/play-store',
128+
invoiceNumber: '8675309',
129+
cardType: 'MasterCard',
130+
lastFour: '5309',
131+
invoiceDate: new Date(),
132+
nextInvoiceDate: new Date(Date.now() + 1000 * 3600 * 24 * 30),
133+
serviceLastActiveDate: new Date(Date.now() + 1000 * 3600 * 24 * 60),
134+
productIconURLNew: 'http://placekitten.com/512/512?image=2',
135+
productIconURLOld: 'http://placekitten.com/512/512?image=1',
136+
productNameOld: 'Product A',
137+
productNameNew: 'Product B',
138+
invoiceLink:
139+
'https://pay.stripe.com/invoice/acct_1GCAr3BVqmGyQTMa/invst_GyHjTyIXBg8jj5yjt7Z0T4CCG3hfGtp',
140+
invoiceTotalInCents: 999999.9,
141+
invoiceTotalCurrency: 'eur',
142+
paymentAmountOldInCents: 9999099.9,
143+
paymentAmountOldCurrency: 'jpy',
144+
paymentAmountNewInCents: 12312099.9,
145+
paymentAmountNewCurrency: 'gbp',
146+
paymentProratedInCents: 523099.9,
147+
paymentProratedCurrency: 'usd',
148+
productPaymentCycleNew: 'month',
149+
productPaymentCycleOld: 'year',
150+
redirectTo: 'https://redirect.com/',
151+
reminderLength: 14,
152+
resume:
153+
'eyJjYW1wYWlnbiI6bnVsbCwiZW50cnlwb2ludCI6bnVsbCwiZmxvd0lkIjoiM2Q1ODZiNzY4Mzc2NGJhOWFiNzhkMzMxMTdjZDU4Y2RmYjk3Mzk5MWU5NTk0NjgxODBlMDUyMmY2MThhNmEyMSIsInJlc2V0UGFzc3dvcmRDb25maXJtIjp0cnVlLCJ1bmlxdWVVc2VySWQiOiI1ODNkOGFlYS00NzU3LTRiZTQtYWJlNC0wZWQ2NWZhY2Y2YWQiLCJ1dG1DYW1wYWlnbiI6bnVsbCwidXRtQ29udGVudCI6bnVsbCwidXRtTWVkaXVtIjpudWxsLCJ1dG1Tb3VyY2UiOm51bGwsInV0bVRlcm0iOm51bGx9',
154+
secondaryEmail: 'secondary@email',
155+
style: 'trailhead',
156+
service: 'sync',
157+
token: '47b22cd271963448cf36da95cccfcfb342b5693d66f58aa635f9a95579431002',
158+
timeZone: 'Europe/Madrid',
159+
type: messageSubType,
160+
uaBrowser: 'Firefox',
161+
uaBrowserVersion: '57',
162+
uaOS: 'Mac OSX',
163+
uaOSVersion: '10.11',
164+
unblockCode: '1ILO0Z5P',
165+
tokenCode: 'LIT12345',
166+
uid: '6510cb04abd742c6b3e4abefc7e39c9f',
167+
metricsEnabled: true,
168+
productMetadata,
169+
providerName: 'Google',
170+
subscription: {
171+
planSuccessActionButtonURL: 'http://getfirefox.com/',
172+
planEmailIconURL: 'http://placekitten.com/512/512',
173+
planId: 'plan-example',
174+
productId: '0123456789abcdef',
175+
productMetadata,
176+
productName: 'Firefox Fortress',
177+
},
178+
subscriptions: [
179+
{
180+
planSuccessActionButtonURL: 'http://getfirefox.com/',
181+
planEmailIconURL: 'http://placekitten.com/512/512',
182+
planId: 'plan-example',
183+
productId: '0123456789abcdef',
184+
productMetadata,
185+
productName: 'Firefox Fortress',
186+
},
187+
],
188+
planConfig,
189+
};
190+
191+
return mailer[messageType](message);
192+
}
193+
194+
function checkMessageType(mailer, messageToSend) {
195+
const messageTypes = getMailerMessageTypes(mailer);
196+
messageTypes.push('all');
197+
}
198+
199+
function getMailerMessageTypes(mailer) {
200+
const messageTypes = [];
201+
202+
for (const key in mailer) {
203+
if (
204+
typeof mailer[key] === 'function' &&
205+
!/^_/.test(key) &&
206+
!/^send/.test(key) &&
207+
/Email$/.test(key)
208+
) {
209+
messageTypes.push(key);
210+
}
211+
}
212+
213+
return messageTypes.sort();
214+
}
215+
216+
function getMessageTypesToWrite(mailer) {
217+
return getMailerMessageTypes(mailer);
218+
}
219+
220+
function ensureTargetDirectoryExists() {
221+
fs.mkdirSync(OUTPUT_DIRECTORY, { recursive: true });
222+
}
223+
224+
module.exports.OUTPUT_DIRECTORY = OUTPUT_DIRECTORY;

0 commit comments

Comments
 (0)