Skip to content

Commit 0125e2d

Browse files
committed
feat: more types (#33)
1 parent c929752 commit 0125e2d

File tree

3 files changed

+112
-39
lines changed

3 files changed

+112
-39
lines changed

index.d.ts

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,15 @@
1-
declare module '@autotelic/fastify-mail' {
2-
import { FastifyInstance, FastifyPluginCallback } from 'fastify';
3-
import { Transporter } from 'nodemailer';
1+
import { FastifyPluginCallback } from 'fastify';
2+
import { Transporter } from 'nodemailer';
43

4+
type FastifyMail = FastifyPluginCallback<fastifyMail.FastifyMailOptions>;
5+
6+
declare module 'fastify' {
7+
interface FastifyInstance {
8+
mail: fastifyMail.FastifyMailDecorator;
9+
}
10+
}
11+
12+
declare namespace fastifyMail {
513
// Define the options for the plugin
614
export interface FastifyMailOptions {
715
pov?: {
@@ -13,7 +21,7 @@ declare module '@autotelic/fastify-mail' {
1321
}
1422

1523
// Define the shape of the mail decorator
16-
interface FastifyMailDecorator {
24+
export interface FastifyMailDecorator {
1725
sendMail: (message: MailMessage, opts?: SendMailOptions) => Promise<any>;
1826
createMessage: (message: MailMessage, templatePath: string, context: any) => Promise<MailMessage>;
1927
validateMessage: (message: MailMessage) => string[];
@@ -46,5 +54,11 @@ declare module '@autotelic/fastify-mail' {
4654
// The exported plugin function
4755
const fastifyMail: FastifyPluginCallback<FastifyMailOptions>;
4856

49-
export default fastifyMail;
57+
export { fastifyMail as default };
5058
}
59+
60+
declare function fastifyMail(
61+
...params: Parameters<FastifyMail>
62+
): ReturnType<FastifyMail>;
63+
64+
export = fastifyMail;

index.js

Lines changed: 42 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -56,41 +56,51 @@ const fastifyMail = async (fastify, opts) => {
5656
// Creates the message object that will be sent to nodemailer.
5757
// It will either render the templates or use the data in the message object as is
5858
createMessage: async function (message, templatePath, context) {
59-
const from = message.from
60-
const to = message.to
61-
const subject = message.subject
62-
const replyTo = message.replyTo
63-
const cc = message.cc
64-
const bcc = message.bcc
65-
const attachments = message.attachments
66-
const html = message.html
67-
const text = message.text
68-
69-
const [
70-
renderedHtml,
71-
renderedText
72-
] = await Promise.all([
73-
await renderTemplate('html'),
74-
await renderTemplate('text')
75-
])
76-
77-
return {
78-
from,
79-
to,
80-
cc,
81-
bcc,
82-
attachments,
83-
replyTo,
84-
subject,
85-
html: renderedHtml || html,
86-
text: renderedText || text
59+
const formattedMessage = {
60+
from: message.from,
61+
to: message.to,
62+
subject: message.subject,
63+
replyTo: message.replyTo,
64+
cc: message.cc,
65+
bcc: message.bcc,
66+
html: message.html,
67+
text: message.text
8768
}
8869

89-
// renders a template with the given context based on the templateName which
90-
// should be found in the path provided by templates. Returns "" if the promise is rejected.
70+
if (templatePath) {
71+
const [
72+
{ template: renderedHtml, error: htmlError },
73+
{ template: renderedText, error: textError }
74+
] = await Promise.all([
75+
await renderTemplate('html'),
76+
await renderTemplate('text')
77+
])
78+
79+
if (!renderedHtml && !renderedText) {
80+
fastify.log.error(`fastify-mail: ${htmlError}`)
81+
fastify.log.error(`fastify-mail: ${textError}`)
82+
}
83+
84+
if (renderedHtml) {
85+
formattedMessage.html = renderedHtml
86+
}
87+
88+
if (renderedText) {
89+
formattedMessage.text = renderedText
90+
}
91+
}
92+
93+
return formattedMessage
94+
95+
// renders a template with the given context based on the templateName & templatePath,
96+
// if it fails to render the template, it returns an error message instead.
9197
async function renderTemplate (templateName) {
92-
return await fastify[propertyName](join(templatePath, templateName), context)
93-
.catch(() => { return null })
98+
try {
99+
const template = await fastify[propertyName](join(templatePath, templateName), context)
100+
return { template }
101+
} catch (error) {
102+
return { error: error.message }
103+
}
94104
}
95105
},
96106
// validates the message object includes to, from, subject and returns an error message if it does not

index.test.js

Lines changed: 51 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ test('view decorator does not exist if the engine is not provided', async ({ tea
8080
notOk(fastify.hasDecorator('view'))
8181
})
8282

83-
test('throws an error if point-of-view is not registered', async ({ teardown, notOk, rejects, ok, equal }) => {
83+
test('throws an error if point-of-view is not registered', async ({ teardown, notOk, rejects }) => {
8484
teardown(() => fastify.close())
8585
const fastify = Fastify()
8686
fastify.register(fastifyMail, { transporter: { jsonTransport: true } })
@@ -89,7 +89,7 @@ test('throws an error if point-of-view is not registered', async ({ teardown, no
8989
notOk(fastify.hasDecorator('view'))
9090
})
9191

92-
test('throws an error if an invalid transporter is given', async ({ teardown, rejects, ok, equal }) => {
92+
test('throws an error if an invalid transporter is given', async ({ teardown, rejects }) => {
9393
teardown(() => fastify.close())
9494
const fastify = Fastify()
9595
fastify.register(fastifyMail, { pov: { engine: { nunjucks } }, transporter: 'error' })
@@ -145,6 +145,10 @@ test('fastify-mail uses string variables (for text and html) when a template is
145145

146146
const fastify = Fastify()
147147
fastify.register(require('@fastify/view'), povConfig)
148+
149+
const loggedErrors = []
150+
fastify.log.error = (msg) => { loggedErrors.push(msg) }
151+
148152
fastify.after(() => {
149153
fastify.register(fastifyMail, { pov: { propertyName: 'foo' }, transporter: { jsonTransport: true } })
150154
})
@@ -156,6 +160,7 @@ test('fastify-mail uses string variables (for text and html) when a template is
156160

157161
ok(fastify.hasDecorator('foo'))
158162
same(sendMailStub.args[0], [testMessage])
163+
equal(loggedErrors.length, 0)
159164
equal(sendMailStub.args.length, 1)
160165
})
161166

@@ -178,6 +183,10 @@ test('fastify-mail uses text template when available but defaults to provided ht
178183

179184
const fastify = Fastify()
180185
fastify.register(require('@fastify/view'), povConfig)
186+
187+
const loggedErrors = []
188+
fastify.log.error = (msg) => { loggedErrors.push(msg) }
189+
181190
fastify.after(() => {
182191
fastify.register(fastifyMail, { pov: { propertyName: 'foo' }, transporter: { jsonTransport: true } })
183192
})
@@ -189,6 +198,7 @@ test('fastify-mail uses text template when available but defaults to provided ht
189198

190199
ok(fastify.hasDecorator('foo'))
191200
same(sendMailStub.args[0][0].html, testHtml)
201+
equal(loggedErrors.length, 0)
192202
equal(sendMailStub.args.length, 1)
193203
})
194204

@@ -211,6 +221,10 @@ test('fastify-mail uses html template when available but defaults to provided te
211221

212222
const fastify = Fastify()
213223
fastify.register(require('@fastify/view'), povConfig)
224+
225+
const loggedErrors = []
226+
fastify.log.error = (msg) => { loggedErrors.push(msg) }
227+
214228
fastify.after(() => {
215229
fastify.register(fastifyMail, { pov: { propertyName: 'foo' }, transporter: { jsonTransport: true } })
216230
})
@@ -223,9 +237,44 @@ test('fastify-mail uses html template when available but defaults to provided te
223237
ok(fastify.hasDecorator('foo'))
224238
same(sendMailStub.args[0][0].html, testHtml)
225239
same(sendMailStub.args[0][0].text, 'This is a plain text email message.')
240+
equal(loggedErrors.length, 0)
226241
equal(sendMailStub.args.length, 1)
227242
})
228243

244+
test('fastify-mail will throw errors if templatePath is defined, but does not exist', async ({ teardown, testdir, equal }) => {
245+
teardown(() => {
246+
fastify.close()
247+
sendMailStub.restore()
248+
})
249+
250+
const testTemplates = testdir({})
251+
252+
const povConfig = {
253+
propertyName: 'foo',
254+
engine: { nunjucks },
255+
includeViewExtension: true,
256+
options: { filename: resolve('templates') }
257+
}
258+
259+
const fastify = Fastify()
260+
fastify.register(require('@fastify/view'), povConfig)
261+
262+
const loggedErrors = []
263+
fastify.log.error = (msg) => { loggedErrors.push(msg) }
264+
265+
fastify.after(() => {
266+
fastify.register(fastifyMail, { pov: { propertyName: 'foo' }, transporter: { jsonTransport: true } })
267+
})
268+
await fastify.ready()
269+
270+
const sendMailStub = sinon.stub(fastify.nodemailer, 'sendMail')
271+
272+
await fastify.mail.sendMail(testMessage, { templatePath: relative(__dirname, testTemplates), context: testContext })
273+
274+
equal(loggedErrors[0], 'fastify-mail: template not found: .tap/fixtures/.-index.test.js-fastify-mail-will-throw-errors-if-templatePath-is-defined-but-does-not-exist/html.njk')
275+
equal(loggedErrors[1], 'fastify-mail: template not found: .tap/fixtures/.-index.test.js-fastify-mail-will-throw-errors-if-templatePath-is-defined-but-does-not-exist/text.njk')
276+
})
277+
229278
test('fastify.mail.sendMail calls nodemailer.sendMail with correct arguments', async ({ teardown, testdir, fixture, same, equal }) => {
230279
teardown(() => {
231280
fastify.close()

0 commit comments

Comments
 (0)