Skip to content
Merged
Show file tree
Hide file tree
Changes from 93 commits
Commits
Show all changes
107 commits
Select commit Hold shift + click to select a range
e753eac
WIP
mikecote May 24, 2019
63efb22
Rename fire function and remove @ts-ignore in all places
mikecote May 24, 2019
600d1c1
Change naming in alerting service
mikecote May 24, 2019
e2c2399
Remove alert instance class for now, support interval configuration
mikecote May 24, 2019
11098fb
Cleanup TS
mikecote May 24, 2019
e98d473
Split alerting between registry and client
mikecote May 27, 2019
b194932
Use saved object alongside task manager instance
mikecote May 27, 2019
dba4cfc
Add remaining alerting APIs
mikecote May 27, 2019
ea72095
Change create structure
mikecote May 27, 2019
f4b631d
Rename some variables, change actionGroups structure
mikecote May 27, 2019
72b461b
Use handlebars for templating strings at fire time
mikecote May 27, 2019
0205783
Fix params given to alert type execute function
mikecote May 27, 2019
b5db24d
Use alert instance class
mikecote May 27, 2019
d0d395d
Alert instances support meta attributes
mikecote May 27, 2019
9927143
Move alert instances deserialization
mikecote May 27, 2019
6442f51
Change interval to be ms
mikecote May 27, 2019
a83c56a
Rename actions es archive
mikecote May 27, 2019
da9c3c5
Fix tests to use encrypted esArchive for action record
mikecote May 27, 2019
1289b50
Add create alert test to demo end to end flow
mikecote May 28, 2019
a33dafe
Merge branch 'feature/alerting' of github.com:elastic/kibana into ale…
mikecote May 28, 2019
36e2776
Fix type check issue
mikecote May 28, 2019
d12d488
Alerts to use references to action objects
mikecote May 28, 2019
515fae4
Only update task manager tasks after saved objects are fully updated
mikecote May 28, 2019
b4e442e
Use scope in task manager
mikecote May 28, 2019
08f3d22
Fix type check
mikecote May 29, 2019
47a3294
Use task manager to execute actions
mikecote May 29, 2019
b9f9c94
Convert ids into references and back
mikecote May 29, 2019
4feb609
Apply PR feedback
mikecote May 30, 2019
96df722
Merge wtih upstream
mikecote May 30, 2019
29c5e11
Fix broken test
mikecote May 30, 2019
a94916e
Fix some bugs
mikecote May 30, 2019
7a414b3
Merge wtih upstream
mikecote May 30, 2019
4abbc73
Fix test errors
mikecote May 30, 2019
1b67166
Alert interval to be previous runAt + interval instead of now + interval
mikecote May 30, 2019
ab7e1ec
Add range support
mikecote May 31, 2019
d3f252f
Remove extra line
mikecote May 31, 2019
c6291af
Cleanup
mikecote May 31, 2019
dd873d5
Add alert_instance.test.ts
mikecote May 31, 2019
40c4b8d
Add alert_type_registry.test.ts
mikecote Jun 3, 2019
0771403
Move tests around
mikecote Jun 3, 2019
acd7522
Create generic task manager mock
mikecote Jun 3, 2019
bdf32ad
Add note about saved objects client mock
mikecote Jun 3, 2019
3b07c5c
Create alert_type_registry.mock.ts
mikecote Jun 3, 2019
64e50e6
Add alerts_client.test.ts
mikecote Jun 3, 2019
81e302e
Add create_alert_instance_factory.test.ts
mikecote Jun 3, 2019
48e4e98
Add create_fire_handler.test.ts
mikecote Jun 3, 2019
3c8ceaf
WIP
mikecote Jun 3, 2019
f535102
Merge with upstream feature/alerting
mikecote Jun 4, 2019
e9c0864
Fix get_create_task_runner_function.test.ts and make test pass
mikecote Jun 4, 2019
59c151b
Make get_create_task_runner_function.test.ts 100% coverage
mikecote Jun 4, 2019
69c9639
Add unit tests for routes
mikecote Jun 4, 2019
900c83d
Move files around
mikecote Jun 4, 2019
9709daf
Created transform_action_params.ts
mikecote Jun 4, 2019
48f1a3f
Add get_next_run_at.ts
mikecote Jun 5, 2019
86e5b3b
Add comment explaining why we copy nextRunAt
mikecote Jun 5, 2019
0dae6bd
Re-use state within alert instance
mikecote Jun 5, 2019
fe2ff93
Finalize code coverage in unit tests
mikecote Jun 5, 2019
5158a00
Create base api integration tests
mikecote Jun 5, 2019
bb8d325
Add a test that ensures end to end functionality of an alert
mikecote Jun 5, 2019
659d655
Fix ui capabilities test
mikecote Jun 5, 2019
a988c5c
Fix broken plugin api integration test
mikecote Jun 5, 2019
423d187
Merge wtih feature/alerting
mikecote Jun 6, 2019
c32c346
Fix jest tests with new saved objects client
mikecote Jun 6, 2019
e8dedbe
Fix broken integration tests
mikecote Jun 6, 2019
a4c85b4
Change api integration test fixture to make more sense, add functions…
mikecote Jun 6, 2019
a32a971
Move alerts integration testing into own file, prep to add more tests
mikecote Jun 6, 2019
8cd4655
Add tests to ensure failed task instances get retried
mikecote Jun 7, 2019
a9760a9
Add get_create_task_runner_function.test.ts for actions, create encry…
mikecote Jun 7, 2019
2049ff0
Add action validation tests
mikecote Jun 7, 2019
3d38e9c
Ensure action type validation occurs on update
mikecote Jun 7, 2019
b64b54d
Test 400 on unregistered alert types
mikecote Jun 7, 2019
1ce009b
Ensure alertTypeId can't be updated
mikecote Jun 7, 2019
0a90879
Add validation test for alert create / update
mikecote Jun 7, 2019
3e704e1
Fix broken checks / tests
mikecote Jun 7, 2019
5d3d84a
Skip failing test for now
mikecote Jun 7, 2019
5eda29c
Cleanup jest tests
mikecote Jun 10, 2019
6cc1991
Ensure action objects can be updated while keeping encrypted attribut…
mikecote Jun 10, 2019
f35c83f
Remove partial update sopport, remove ability to change actionTypeId,…
mikecote Jun 10, 2019
e5e1ed7
Merge branch 'feature/alerting' of github.com:elastic/kibana into ale…
mikecote Jun 10, 2019
c93c408
Ensure actionTypeConfig is validated on create and update
mikecote Jun 10, 2019
d7d2565
Add alertTypeParams validation support
mikecote Jun 10, 2019
1202315
Fix failing tests
mikecote Jun 11, 2019
adcce18
Ensure alert cleanup errors don't replace the original error
mikecote Jun 11, 2019
a5c7a4d
Pass callCluster as a service to alerts and actions
mikecote Jun 11, 2019
d574291
Only pass log to alerts client
mikecote Jun 11, 2019
ccad1e3
Pass savedObjectsClient as a service to alerting and actions
mikecote Jun 11, 2019
f56ee39
Fix failing tests
mikecote Jun 11, 2019
07cb30c
Remove range support, provide when current and previous task got sche…
mikecote Jun 11, 2019
e0a0ffd
Ensure Joi validation happens before every execute
mikecote Jun 11, 2019
0c2ece7
Remove skipped tests, to be done in future PR
mikecote Jun 11, 2019
815568c
Apply self feedback pt1
mikecote Jun 12, 2019
47e3c0d
Apply self feedback pt2
mikecote Jun 12, 2019
3adec20
Fix broken tests
mikecote Jun 12, 2019
f3d4dc4
Apply PR feedback
mikecote Jun 12, 2019
d4a87be
PR feedback pt1
mikecote Jun 14, 2019
bac8286
Apply security team PR feedback
mikecote Jun 14, 2019
bf2ab81
PR feedback pt1
mikecote Jun 17, 2019
39187c0
PR feedback pt2
mikecote Jun 17, 2019
f11a6ae
PR feedback pt3
mikecote Jun 17, 2019
954ffce
Fix broken tests
mikecote Jun 17, 2019
8f8ba23
Fix callCluster to have signature
mikecote Jun 17, 2019
ea4e7a3
Revert f11a6aeb06c65e5ab54bf3cc36694845c812b6a7
mikecote Jun 17, 2019
1776568
PR feedback pt4
mikecote Jun 17, 2019
d03ba90
Remove __jest__ folders
mikecote Jun 17, 2019
f27b5fd
PR feedback pt5
mikecote Jun 17, 2019
950a7c5
Fix Joi from leaking secrets
mikecote Jun 17, 2019
e099294
Fire instance actions in parallel instead of series
mikecote Jun 17, 2019
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
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,14 @@ export interface MigrationVersion {
}

export interface SavedObjectAttributes {
[key: string]: SavedObjectAttributes | string | number | boolean | null;
[key: string]:
| SavedObjectAttributes
| SavedObjectAttributes[]
| string
| number
| boolean
| null
| undefined;
}

export interface VisualizationAttributes extends SavedObjectAttributes {
Expand Down
2 changes: 2 additions & 0 deletions x-pack/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ import { ossTelemetry } from './plugins/oss_telemetry';
import { encryptedSavedObjects } from './plugins/encrypted_saved_objects';
import { snapshotRestore } from './plugins/snapshot_restore';
import { actions } from './plugins/actions';
import { alerting } from './plugins/alerting';

module.exports = function (kibana) {
return [
Expand Down Expand Up @@ -83,5 +84,6 @@ module.exports = function (kibana) {
encryptedSavedObjects(kibana),
snapshotRestore(kibana),
actions(kibana),
alerting(kibana),
];
};
7 changes: 7 additions & 0 deletions x-pack/plugins/actions/common/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/*
* 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.
*/

export const TASK_MANAGER_SCOPE = 'actions';
11 changes: 9 additions & 2 deletions x-pack/plugins/actions/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,20 @@ import { Root } from 'joi';
import mappings from './mappings.json';
import { init } from './server';

export { ActionsPlugin, ActionsClient } from './server';
export { ActionsPlugin, ActionsClient, ActionTypeExecutorOptions } from './server';

export function actions(kibana: any) {
return new kibana.Plugin({
id: 'actions',
configPrefix: 'xpack.actions',
require: ['kibana', 'elasticsearch', 'encrypted_saved_objects'],
require: ['kibana', 'elasticsearch', 'task_manager', 'encrypted_saved_objects'],
isEnabled(config: any) {
return (
config.get('xpack.encrypted_saved_objects.enabled') === true &&
config.get('xpack.actions.enabled') === true &&
config.get('xpack.task_manager.enabled') === true
);
},
config(Joi: Root) {
return Joi.object()
.keys({
Expand Down
249 changes: 41 additions & 208 deletions x-pack/plugins/actions/server/__jest__/action_type_registry.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,26 +4,66 @@
* you may not use this file except in compliance with the Elastic License.
*/

import Joi from 'joi';
jest.mock('../get_create_task_runner_function', () => ({
getCreateTaskRunnerFunction: jest.fn(),
}));

import { taskManagerMock } from '../../../task_manager/task_manager.mock';
import { encryptedSavedObjectsMock } from '../../../encrypted_saved_objects/server/plugin.mock';
import { ActionTypeRegistry } from '../action_type_registry';
import { SavedObjectsClientMock } from '../../../../../src/legacy/server/saved_objects/service/saved_objects_client.mock';

const mockTaskManager = taskManagerMock.create();

const services = {
log: jest.fn(),
callCluster: jest.fn(),
savedObjectsClient: SavedObjectsClientMock.create(),
};
const actionTypeRegistryParams = {
services,
taskManager: mockTaskManager,
encryptedSavedObjectsPlugin: encryptedSavedObjectsMock.create(),
};

beforeEach(() => jest.resetAllMocks());

describe('register()', () => {
test('able to register action types', () => {
const executor = jest.fn();
// eslint-disable-next-line @typescript-eslint/no-var-requires
const { getCreateTaskRunnerFunction } = require('../get_create_task_runner_function');
getCreateTaskRunnerFunction.mockReturnValueOnce(jest.fn());
const actionTypeRegistry = new ActionTypeRegistry(actionTypeRegistryParams);
actionTypeRegistry.register({
id: 'my-action-type',
name: 'My action type',
executor,
});
expect(actionTypeRegistry.has('my-action-type')).toEqual(true);
expect(mockTaskManager.registerTaskDefinitions).toHaveBeenCalledTimes(1);
expect(mockTaskManager.registerTaskDefinitions.mock.calls[0]).toMatchInlineSnapshot(`
Array [
Object {
"actions:my-action-type": Object {
"createTaskRunner": [MockFunction],
"title": "My action type",
"type": "actions:my-action-type",
},
},
]
`);
expect(getCreateTaskRunnerFunction).toHaveBeenCalledTimes(1);
const call = getCreateTaskRunnerFunction.mock.calls[0][0];
expect(call.actionType).toMatchInlineSnapshot(`
Object {
"executor": [MockFunction],
"id": "my-action-type",
"name": "My action type",
}
`);
expect(call.encryptedSavedObjectsPlugin).toBeTruthy();
expect(call.services).toBeTruthy();
});

test('throws error if action type already registered', () => {
Expand Down Expand Up @@ -115,106 +155,6 @@ describe('list()', () => {
});
});

describe('validateParams()', () => {
test('should pass when validation not defined', () => {
const actionTypeRegistry = new ActionTypeRegistry(actionTypeRegistryParams);
actionTypeRegistry.register({
id: 'my-action-type',
name: 'My action type',
async executor() {},
});
actionTypeRegistry.validateParams('my-action-type', {});
});

test('should validate and pass when params is valid', () => {
const actionTypeRegistry = new ActionTypeRegistry(actionTypeRegistryParams);
actionTypeRegistry.register({
id: 'my-action-type',
name: 'My action type',
validate: {
params: Joi.object()
.keys({
param1: Joi.string().required(),
})
.required(),
},
async executor() {},
});
actionTypeRegistry.validateParams('my-action-type', { param1: 'value' });
});

test('should validate and throw error when params is invalid', () => {
const actionTypeRegistry = new ActionTypeRegistry(actionTypeRegistryParams);
actionTypeRegistry.register({
id: 'my-action-type',
name: 'My action type',
validate: {
params: Joi.object()
.keys({
param1: Joi.string().required(),
})
.required(),
},
async executor() {},
});
expect(() =>
actionTypeRegistry.validateParams('my-action-type', {})
).toThrowErrorMatchingInlineSnapshot(
`"child \\"param1\\" fails because [\\"param1\\" is required]"`
);
});
});

describe('validateActionTypeConfig()', () => {
test('should pass when validation not defined', () => {
const actionTypeRegistry = new ActionTypeRegistry(actionTypeRegistryParams);
actionTypeRegistry.register({
id: 'my-action-type',
name: 'My action type',
async executor() {},
});
actionTypeRegistry.validateActionTypeConfig('my-action-type', {});
});

test('should validate and pass when actionTypeConfig is valid', () => {
const actionTypeRegistry = new ActionTypeRegistry(actionTypeRegistryParams);
actionTypeRegistry.register({
id: 'my-action-type',
name: 'My action type',
validate: {
actionTypeConfig: Joi.object()
.keys({
param1: Joi.string().required(),
})
.required(),
},
async executor() {},
});
actionTypeRegistry.validateActionTypeConfig('my-action-type', { param1: 'value' });
});

test('should validate and throw error when actionTypeConfig is invalid', () => {
const actionTypeRegistry = new ActionTypeRegistry(actionTypeRegistryParams);
actionTypeRegistry.register({
id: 'my-action-type',
name: 'My action type',
validate: {
actionTypeConfig: Joi.object()
.keys({
param1: Joi.string().required(),
})
.required(),
},
async executor() {},
});
expect(() =>
actionTypeRegistry.validateActionTypeConfig('my-action-type', {})
).toThrowErrorMatchingInlineSnapshot(
`"child \\"param1\\" fails because [\\"param1\\" is required]"`
);
});
});

describe('has()', () => {
test('returns false for unregistered action types', () => {
const actionTypeRegistry = new ActionTypeRegistry(actionTypeRegistryParams);
Expand All @@ -232,110 +172,3 @@ describe('has()', () => {
expect(actionTypeRegistry.has('my-action-type'));
});
});

describe('execute()', () => {
test('calls the executor with proper params', async () => {
const executor = jest.fn().mockResolvedValueOnce({ success: true });
const actionTypeRegistry = new ActionTypeRegistry(actionTypeRegistryParams);
actionTypeRegistry.register({
id: 'my-action-type',
name: 'My action type',
executor,
});
await actionTypeRegistry.execute({
id: 'my-action-type',
actionTypeConfig: { foo: true },
params: { bar: false },
});
expect(executor).toMatchInlineSnapshot(`
[MockFunction] {
"calls": Array [
Array [
Object {
"actionTypeConfig": Object {
"foo": true,
},
"params": Object {
"bar": false,
},
"services": Object {
"log": [MockFunction],
},
},
],
],
"results": Array [
Object {
"type": "return",
"value": Promise {},
},
],
}
`);
});

test('validates params', async () => {
const executor = jest.fn().mockResolvedValueOnce({ success: true });
const actionTypeRegistry = new ActionTypeRegistry(actionTypeRegistryParams);
actionTypeRegistry.register({
id: 'my-action-type',
name: 'My action type',
executor,
validate: {
params: Joi.object()
.keys({
param1: Joi.string().required(),
})
.required(),
},
});
await expect(
actionTypeRegistry.execute({
id: 'my-action-type',
actionTypeConfig: {},
params: {},
})
).rejects.toThrowErrorMatchingInlineSnapshot(
`"child \\"param1\\" fails because [\\"param1\\" is required]"`
);
});

test('validates actionTypeConfig', async () => {
const executor = jest.fn().mockResolvedValueOnce({ success: true });
const actionTypeRegistry = new ActionTypeRegistry(actionTypeRegistryParams);
actionTypeRegistry.register({
id: 'my-action-type',
name: 'My action type',
executor,
validate: {
actionTypeConfig: Joi.object()
.keys({
param1: Joi.string().required(),
})
.required(),
},
});
await expect(
actionTypeRegistry.execute({
id: 'my-action-type',
actionTypeConfig: {},
params: {},
})
).rejects.toThrowErrorMatchingInlineSnapshot(
`"child \\"param1\\" fails because [\\"param1\\" is required]"`
);
});

test('throws error if action type not registered', async () => {
const actionTypeRegistry = new ActionTypeRegistry(actionTypeRegistryParams);
await expect(
actionTypeRegistry.execute({
id: 'my-action-type',
actionTypeConfig: { foo: true },
params: { bar: false },
})
).rejects.toThrowErrorMatchingInlineSnapshot(
`"Action type \\"my-action-type\\" is not registered."`
);
});
});
Loading