Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Notification Service #19236

Merged
merged 5 commits into from
May 22, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion x-pack/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import { licenseManagement } from './plugins/license_management';
import { cloud } from './plugins/cloud';
import { indexManagement } from './plugins/index_management';
import { consoleExtensions } from './plugins/console_extensions';
import { notifications } from './plugins/notifications';

module.exports = function (kibana) {
return [
Expand All @@ -40,6 +41,7 @@ module.exports = function (kibana) {
licenseManagement(kibana),
cloud(kibana),
indexManagement(kibana),
consoleExtensions(kibana)
consoleExtensions(kibana),
notifications(kibana),
];
};
2 changes: 2 additions & 0 deletions x-pack/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@
"@elastic/numeral": "2.3.2",
"@kbn/datemath": "link:../packages/kbn-datemath",
"@kbn/ui-framework": "link:../packages/kbn-ui-framework",
"@slack/client": "^4.2.2",
"angular-paging": "2.2.1",
"angular-resource": "1.4.9",
"angular-sanitize": "1.4.9",
Expand Down Expand Up @@ -122,6 +123,7 @@
"moment-duration-format": "^1.3.0",
"moment-timezone": "^0.5.14",
"ngreact": "^0.5.1",
"nodemailer": "^4.6.4",
"object-hash": "1.2.0",
"path-match": "1.2.4",
"pdfmake": "0.1.33",
Expand Down
53 changes: 53 additions & 0 deletions x-pack/plugins/notifications/config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

/**
* User-configurable settings for xpack.notifications via configuration schema
*
* @param {Object} Joi - HapiJS Joi module that allows for schema validation
* @return {Object} config schema
*/
export const config = (Joi) => {
const { array, boolean, number, object, string } = Joi;

return object({
enabled: boolean().default(true),
email: object({
enabled: boolean().default(false),
smtp: object({
host: string().default('localhost'),
port: number().default(25),
require_tls: boolean().default(false),
pool: boolean().default(false),
auth: object({
username: string(),
password: string()
}).default(),
}).default(),
defaults: object({
from: string(),
to: array().single().items(string()),
cc: array().single().items(string()),
bcc: array().single().items(string()),
}).default(),
}).default(),
slack: object({
enabled: boolean().default(false),
token: string().required(),
defaults: object({
channel: string(),
as_user: boolean().default(false),
icon_emoji: string(),
icon_url: string(),
link_names: boolean().default(true),
mrkdwn: boolean().default(true),
unfurl_links: boolean().default(true),
unfurl_media: boolean().default(true),
username: string(),
}).default(),
})
}).default();
};
24 changes: 24 additions & 0 deletions x-pack/plugins/notifications/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

import { resolve } from 'path';
import { init } from './init';
import { config } from './config';

/**
* Invokes plugin modules to instantiate the Notification plugin for Kibana
*
* @param kibana {Object} Kibana plugin instance
* @return {Object} Notification Kibana plugin object
*/
export const notifications = (kibana) => new kibana.Plugin({
require: ['kibana', 'xpack_main'],
id: 'notifications',
configPrefix: 'xpack.notifications',
publicDir: resolve(__dirname, 'public'),
init,
config,
});
38 changes: 38 additions & 0 deletions x-pack/plugins/notifications/init.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

import {
notificationService,
createEmailAction,
createSlackAction,
LoggerAction,
} from './server';
import { notificationServiceSendRoute } from './server/routes/api/v1/notifications';

/**
* Initialize the Action Service with various actions provided by X-Pack, when configured.
*
* @param server {Object} HapiJS server instance
*/
export function init(server) {
const config = server.config();

// the logger
notificationService.setAction(new LoggerAction({ server }));

if (config.get('xpack.notifications.email.enabled')) {
notificationService.setAction(createEmailAction(server));
}

if (config.get('xpack.notifications.slack.enabled')) {
notificationService.setAction(createSlackAction(server));
}

notificationServiceSendRoute(server, notificationService);

// expose the notification service for other plugins
server.expose('notificationService', notificationService);
}
58 changes: 58 additions & 0 deletions x-pack/plugins/notifications/server/email/create_email_action.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

import { EmailAction } from './email_action';

/**
* Create a Nodemailer transporter options object from the config.
*
* @param {Object} config The server configuration.
* @return {Object} An object that configures Nodemailer.
*/
export function optionsFromConfig(config) {
return {
host: config.get('xpack.notifications.email.smtp.host'),
port: config.get('xpack.notifications.email.smtp.port'),
requireTLS: config.get('xpack.notifications.email.smtp.require_tls'),
pool: config.get('xpack.notifications.email.smtp.pool'),
auth: {
user: config.get('xpack.notifications.email.smtp.auth.username'),
pass: config.get('xpack.notifications.email.smtp.auth.password'),
},
};
}

/**
* Create a Nodemailer defaults object from the config.
*
* Defaults include things like the default "from" email address.
*
* @param {Object} config The server configuration.
* @return {Object} An object that configures Nodemailer on a per-message basis.
*/
export function defaultsFromConfig(config) {
return {
from: config.get('xpack.notifications.email.defaults.from'),
to: config.get('xpack.notifications.email.defaults.to'),
cc: config.get('xpack.notifications.email.defaults.cc'),
bcc: config.get('xpack.notifications.email.defaults.bcc'),
};
}

/**
* Create a new Email Action based on the configuration.
*
* @param {Object} server The server object.
* @return {EmailAction} A new email action based on the kibana.yml configuration.
*/
export function createEmailAction(server, { _options = optionsFromConfig, _defaults = defaultsFromConfig } = { }) {
const config = server.config();

const options = _options(config);
const defaults = _defaults(config);

return new EmailAction({ server, options, defaults });
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

import { EmailAction } from './email_action';
import {
createEmailAction,
defaultsFromConfig,
optionsFromConfig,
} from './create_email_action';

describe('create_email_action', () => {

test('optionsFromConfig uses config without modification', () => {
const get = key => {
const suffixes = [
'host',
'port',
'require_tls',
'pool',
'auth.username',
'auth.password',
];
const value = suffixes.find(suffix => {
return `xpack.notifications.email.smtp.${suffix}` === key;
});

if (value === undefined) {
throw new Error(`Unknown config key used ${key}`);
}

return value;
};

expect(optionsFromConfig({ get })).toEqual({
host: 'host',
port: 'port',
requireTLS: 'require_tls',
pool: 'pool',
auth: {
user: 'auth.username',
pass: 'auth.password',
},
});
});

test('defaultsFromConfig uses config without modification', () => {
const get = key => {
const suffixes = [
'from',
'to',
'cc',
'bcc',
];
const value = suffixes.find(suffix => {
return `xpack.notifications.email.defaults.${suffix}` === key;
});

if (value === undefined) {
throw new Error(`Unknown config key used ${key}`);
}

return value;
};

expect(defaultsFromConfig({ get })).toEqual({
from: 'from',
to: 'to',
cc: 'cc',
bcc: 'bcc',
});
});

test('createEmailAction', async () => {
const config = { };
const server = { config: jest.fn().mockReturnValue(config) };
const _options = jest.fn().mockReturnValue({ options: true });
const defaults = { defaults: true };
const _defaults = jest.fn().mockReturnValue(defaults);

const action = createEmailAction(server, { _options, _defaults });

expect(action instanceof EmailAction).toBe(true);
expect(action.defaults).toBe(defaults);

expect(server.config).toHaveBeenCalledTimes(1);
expect(server.config).toHaveBeenCalledWith();
expect(_options).toHaveBeenCalledTimes(1);
expect(_options).toHaveBeenCalledWith(config);
expect(_defaults).toHaveBeenCalledTimes(1);
expect(_defaults).toHaveBeenCalledWith(config);
});

});
Loading