Renders email action message text as html from markdown - take 2 (#41187) (#41205)

* Renders email action message text as html from markdown

* fix jest test

* Fix refactor issue with previous PR in actions/email
This commit is contained in:
Patrick Mueller 2019-07-15 23:49:54 -04:00 committed by GitHub
parent fecd03e52f
commit 85dfc42aa8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 48 additions and 13 deletions

View file

@ -188,9 +188,7 @@ describe('execute()', () => {
const executorOptions: ActionTypeExecutorOptions = { config, params, services }; const executorOptions: ActionTypeExecutorOptions = { config, params, services };
sendEmailMock.mockReset(); sendEmailMock.mockReset();
await actionType.executor(executorOptions); await actionType.executor(executorOptions);
expect(sendEmailMock.mock.calls).toMatchInlineSnapshot(` expect(sendEmailMock.mock.calls[0][1]).toMatchInlineSnapshot(`
Array [
Array [
Object { Object {
"content": Object { "content": Object {
"message": "a message to you", "message": "a message to you",
@ -213,9 +211,7 @@ Array [
"service": "__json", "service": "__json",
"user": "bob", "user": "bob",
}, },
}, }
],
]
`); `);
}); });
}); });

View file

@ -110,6 +110,7 @@ export const actionType: ActionType = {
async function executor(execOptions: ActionTypeExecutorOptions): Promise<any> { async function executor(execOptions: ActionTypeExecutorOptions): Promise<any> {
const config = execOptions.config as ActionTypeConfigType; const config = execOptions.config as ActionTypeConfigType;
const params = execOptions.params as ActionParamsType; const params = execOptions.params as ActionParamsType;
const services = execOptions.services;
const transport: any = { const transport: any = {
user: config.user, user: config.user,
@ -138,7 +139,7 @@ async function executor(execOptions: ActionTypeExecutorOptions): Promise<any> {
}, },
}; };
return await sendEmail(sendEmailOptions); return await sendEmail(services, sendEmailOptions);
} }
// utilities // utilities

View file

@ -7,6 +7,10 @@
// info on nodemailer: https://nodemailer.com/about/ // info on nodemailer: https://nodemailer.com/about/
import nodemailer from 'nodemailer'; import nodemailer from 'nodemailer';
import { default as MarkdownIt } from 'markdown-it';
import { Services } from '../../types';
// an email "service" which doesn't actually send, just returns what it would send // an email "service" which doesn't actually send, just returns what it would send
export const JSON_TRANSPORT_SERVICE = '__json'; export const JSON_TRANSPORT_SERVICE = '__json';
@ -39,7 +43,7 @@ interface Content {
} }
// send an email // send an email
export async function sendEmail(options: SendEmailOptions): Promise<any> { export async function sendEmail(services: Services, options: SendEmailOptions): Promise<any> {
const { transport, routing, content } = options; const { transport, routing, content } = options;
const { service, host, port, secure, user, password } = transport; const { service, host, port, secure, user, password } = transport;
const { from, to, cc, bcc } = routing; const { from, to, cc, bcc } = routing;
@ -64,6 +68,7 @@ export async function sendEmail(options: SendEmailOptions): Promise<any> {
} }
const nodemailerTransport = nodemailer.createTransport(transportConfig); const nodemailerTransport = nodemailer.createTransport(transportConfig);
const messageHTML = htmlFromMarkdown(services, message);
const email = { const email = {
// email routing // email routing
@ -73,7 +78,7 @@ export async function sendEmail(options: SendEmailOptions): Promise<any> {
bcc, bcc,
// email content // email content
subject, subject,
html: message, html: messageHTML,
text: message, text: message,
}; };
@ -89,3 +94,18 @@ export async function sendEmail(options: SendEmailOptions): Promise<any> {
return result; return result;
} }
// try rendering markdown to html, return markdown on any kind of error
function htmlFromMarkdown(services: Services, markdown: string) {
try {
const md = MarkdownIt({
linkify: true,
});
return md.render(markdown);
} catch (err) {
services.log(['debug', 'actions'], `error rendering markdown to html: ${err.message}`);
return markdown;
}
}

View file

@ -102,7 +102,7 @@ export default function emailTest({ getService }: KibanaFunctionalTestDefaultPro
cc: null, cc: null,
bcc: null, bcc: null,
subject: 'email-subject', subject: 'email-subject',
html: 'email-message', html: '<p>email-message</p>\n',
text: 'email-message', text: 'email-message',
headers: {}, headers: {},
}, },
@ -110,6 +110,27 @@ export default function emailTest({ getService }: KibanaFunctionalTestDefaultPro
}); });
}); });
it('should render html from markdown', async () => {
await supertest
.post(`/api/action/${createdActionId}/_fire`)
.set('kbn-xsrf', 'foo')
.send({
params: {
to: ['kibana-action-test@elastic.co'],
subject: 'message with markdown',
message: '_italic_ **bold** https://elastic.co link',
},
})
.expect(200)
.then((resp: any) => {
const { text, html } = resp.body.message;
expect(text).to.eql('_italic_ **bold** https://elastic.co link');
expect(html).to.eql(
'<p><em>italic</em> <strong>bold</strong> <a href="https://elastic.co">https://elastic.co</a> link</p>\n'
);
});
});
it('should respond with a 400 Bad Request when creating an email action with an invalid config', async () => { it('should respond with a 400 Bad Request when creating an email action with an invalid config', async () => {
await supertest await supertest
.post('/api/action') .post('/api/action')
@ -132,7 +153,4 @@ export default function emailTest({ getService }: KibanaFunctionalTestDefaultPro
}); });
}); });
}); });
// TODO: once we have the HTTP API fire action, test that with a webhook url pointing
// back to the Kibana server
} }