Skip to content

Commit

Permalink
Add footer to all emails sent by Kibana email connector with a link t…
Browse files Browse the repository at this point in the history
…o open Kibana or to the alert details page (#84371) (#86003)

* Initial work

* Change messaging from copy

* Fix jest tests for email connector

* Fix jest tests for alerts plugin

* Update copy

* Use server.publicBaseUrl

* Fix jest tests

* Update tests

* Cleanup jest test

* Code cleanup

* Improve email parameter names for kibana footer url

* Cleanup

* Add test for kibana footer link

* Fix type check

* Fix jest test

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
  • Loading branch information
mikecote and kibanamachine authored Dec 16, 2020
1 parent 9816da3 commit ac6584d
Show file tree
Hide file tree
Showing 10 changed files with 367 additions and 123 deletions.
270 changes: 159 additions & 111 deletions x-pack/plugins/actions/server/builtin_action_types/email.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,10 @@ describe('params validation', () => {
Object {
"bcc": Array [],
"cc": Array [],
"kibanaFooterLink": Object {
"path": "/",
"text": "Go to Kibana",
},
"message": "this is the message",
"subject": "this is a test",
"to": Array [
Expand All @@ -228,35 +232,40 @@ describe('params validation', () => {
});

describe('execute()', () => {
test('ensure parameters are as expected', async () => {
const config: ActionTypeConfigType = {
service: '__json',
host: 'a host',
port: 42,
secure: true,
from: 'bob@example.com',
hasAuth: true,
};
const secrets: ActionTypeSecretsType = {
user: 'bob',
password: 'supersecret',
};
const params: ActionParamsType = {
to: ['jim@example.com'],
cc: ['james@example.com'],
bcc: ['jimmy@example.com'],
subject: 'the subject',
message: 'a message to you',
};
const config: ActionTypeConfigType = {
service: '__json',
host: 'a host',
port: 42,
secure: true,
from: 'bob@example.com',
hasAuth: true,
};
const secrets: ActionTypeSecretsType = {
user: 'bob',
password: 'supersecret',
};
const params: ActionParamsType = {
to: ['jim@example.com'],
cc: ['james@example.com'],
bcc: ['jimmy@example.com'],
subject: 'the subject',
message: 'a message to you',
kibanaFooterLink: {
path: '/',
text: 'Go to Kibana',
},
};

const actionId = 'some-id';
const executorOptions: EmailActionTypeExecutorOptions = {
actionId,
config,
params,
secrets,
services,
};

const actionId = 'some-id';
const executorOptions: EmailActionTypeExecutorOptions = {
actionId,
config,
params,
secrets,
services,
};
test('ensure parameters are as expected', async () => {
sendEmailMock.mockReset();
const result = await actionType.executor(executorOptions);
expect(result).toMatchInlineSnapshot(`
Expand All @@ -267,69 +276,63 @@ describe('execute()', () => {
}
`);
expect(sendEmailMock.mock.calls[0][1]).toMatchInlineSnapshot(`
Object {
"content": Object {
"message": "a message to you",
"subject": "the subject",
},
"hasAuth": true,
"proxySettings": undefined,
"routing": Object {
"bcc": Array [
"jimmy@example.com",
],
"cc": Array [
"james@example.com",
],
"from": "bob@example.com",
"to": Array [
"jim@example.com",
],
},
"transport": Object {
"password": "supersecret",
"service": "__json",
"user": "bob",
},
}
Object {
"content": Object {
"message": "a message to you
--
This message was sent by Kibana.",
"subject": "the subject",
},
"hasAuth": true,
"proxySettings": undefined,
"routing": Object {
"bcc": Array [
"jimmy@example.com",
],
"cc": Array [
"james@example.com",
],
"from": "bob@example.com",
"to": Array [
"jim@example.com",
],
},
"transport": Object {
"password": "supersecret",
"service": "__json",
"user": "bob",
},
}
`);
});

test('parameters are as expected with no auth', async () => {
const config: ActionTypeConfigType = {
service: null,
host: 'a host',
port: 42,
secure: true,
from: 'bob@example.com',
hasAuth: false,
};
const secrets: ActionTypeSecretsType = {
user: null,
password: null,
};
const params: ActionParamsType = {
to: ['jim@example.com'],
cc: ['james@example.com'],
bcc: ['jimmy@example.com'],
subject: 'the subject',
message: 'a message to you',
const customExecutorOptions: EmailActionTypeExecutorOptions = {
...executorOptions,
config: {
...config,
service: null,
hasAuth: false,
},
secrets: {
...secrets,
user: null,
password: null,
},
};

const actionId = 'some-id';
const executorOptions: EmailActionTypeExecutorOptions = {
actionId,
config,
params,
secrets,
services,
};
sendEmailMock.mockReset();
await actionType.executor(executorOptions);
await actionType.executor(customExecutorOptions);
expect(sendEmailMock.mock.calls[0][1]).toMatchInlineSnapshot(`
Object {
"content": Object {
"message": "a message to you",
"message": "a message to you
--
This message was sent by Kibana.",
"subject": "the subject",
},
"hasAuth": false,
Expand All @@ -356,37 +359,23 @@ describe('execute()', () => {
});

test('returns expected result when an error is thrown', async () => {
const config: ActionTypeConfigType = {
service: null,
host: 'a host',
port: 42,
secure: true,
from: 'bob@example.com',
hasAuth: false,
};
const secrets: ActionTypeSecretsType = {
user: null,
password: null,
};
const params: ActionParamsType = {
to: ['jim@example.com'],
cc: ['james@example.com'],
bcc: ['jimmy@example.com'],
subject: 'the subject',
message: 'a message to you',
const customExecutorOptions: EmailActionTypeExecutorOptions = {
...executorOptions,
config: {
...config,
service: null,
hasAuth: false,
},
secrets: {
...secrets,
user: null,
password: null,
},
};

const actionId = 'some-id';
const executorOptions: EmailActionTypeExecutorOptions = {
actionId,
config,
params,
secrets,
services,
};
sendEmailMock.mockReset();
sendEmailMock.mockRejectedValue(new Error('wops'));
const result = await actionType.executor(executorOptions);
const result = await actionType.executor(customExecutorOptions);
expect(result).toMatchInlineSnapshot(`
Object {
"actionId": "some-id",
Expand All @@ -405,15 +394,19 @@ describe('execute()', () => {
bcc: ['jim', '{{rogue}}', 'bob'],
subject: '{{rogue}}',
message: '{{rogue}}',
kibanaFooterLink: {
path: '/',
text: 'Go to Kibana',
},
};
const variables = {
rogue: '*bold*',
};
const params = actionType.renderParameterTemplates!(paramsWithTemplates, variables);
const renderedParams = actionType.renderParameterTemplates!(paramsWithTemplates, variables);
// Yes, this is tested in the snapshot below, but it's double-escaped there,
// so easier to see here that the escaping is correct.
expect(params.message).toBe('\\*bold\\*');
expect(params).toMatchInlineSnapshot(`
expect(renderedParams.message).toBe('\\*bold\\*');
expect(renderedParams).toMatchInlineSnapshot(`
Object {
"bcc": Array [
"jim",
Expand All @@ -423,10 +416,65 @@ describe('execute()', () => {
"cc": Array [
"*bold*",
],
"kibanaFooterLink": Object {
"path": "/",
"text": "Go to Kibana",
},
"message": "\\\\*bold\\\\*",
"subject": "*bold*",
"to": Array [],
}
`);
});

test('provides a footer link to Kibana when publicBaseUrl is defined', async () => {
const actionTypeWithPublicUrl = getActionType({
logger: mockedLogger,
configurationUtilities: actionsConfigMock.create(),
publicBaseUrl: 'https://localhost:1234/foo/bar',
});

await actionTypeWithPublicUrl.executor(executorOptions);

expect(sendEmailMock).toHaveBeenCalledTimes(1);
const sendMailCall = sendEmailMock.mock.calls[0][1];
expect(sendMailCall.content.message).toMatchInlineSnapshot(`
"a message to you
--
This message was sent by Kibana. [Go to Kibana](https://localhost:1234/foo/bar)."
`);
});

test('allows to generate a deep link into Kibana when publicBaseUrl is defined', async () => {
const actionTypeWithPublicUrl = getActionType({
logger: mockedLogger,
configurationUtilities: actionsConfigMock.create(),
publicBaseUrl: 'https://localhost:1234/foo/bar',
});

const customExecutorOptions: EmailActionTypeExecutorOptions = {
...executorOptions,
params: {
...params,
kibanaFooterLink: {
path: '/my/app',
text: 'View this in Kibana',
},
},
};

await actionTypeWithPublicUrl.executor(customExecutorOptions);

expect(sendEmailMock).toHaveBeenCalledTimes(1);
const sendMailCall = sendEmailMock.mock.calls[0][1];
expect(sendMailCall.content.message).toMatchInlineSnapshot(`
"a message to you
--
This message was sent by Kibana. [View this in Kibana](https://localhost:1234/foo/bar/my/app)."
`);
});
});
Loading

0 comments on commit ac6584d

Please sign in to comment.