[Alerting UI] Changed alerting UIs use new rule APIs. (#96018)
* [Alerting UI] Changed alerting UIs use new rule APIs. * added unit tests * fixed types * fixed types * fixed types * fixed due to comments
This commit is contained in:
parent
bb109b533c
commit
76ed8dbeab
|
@ -59,8 +59,13 @@ describe('health check', () => {
|
|||
|
||||
it('renders children if keys are enabled', async () => {
|
||||
useKibanaMock().services.http.get = jest.fn().mockResolvedValue({
|
||||
isSufficientlySecure: true,
|
||||
hasPermanentEncryptionKey: true,
|
||||
is_sufficiently_secure: true,
|
||||
has_permanent_encryption_key: true,
|
||||
alerting_framework_heath: {
|
||||
decryption_health: { status: 'ok', timestamp: '2021-04-01T21:29:22.991Z' },
|
||||
execution_health: { status: 'ok', timestamp: '2021-04-01T21:29:22.991Z' },
|
||||
read_health: { status: 'ok', timestamp: '2021-04-01T21:29:22.991Z' },
|
||||
},
|
||||
isAlertsAvailable: true,
|
||||
});
|
||||
const { queryByText } = render(
|
||||
|
@ -78,8 +83,13 @@ describe('health check', () => {
|
|||
|
||||
test('renders warning if TLS is required', async () => {
|
||||
useKibanaMock().services.http.get = jest.fn().mockImplementation(async () => ({
|
||||
isSufficientlySecure: false,
|
||||
hasPermanentEncryptionKey: true,
|
||||
is_sufficiently_secure: false,
|
||||
has_permanent_encryption_key: true,
|
||||
alerting_framework_heath: {
|
||||
decryption_health: { status: 'ok', timestamp: '2021-04-01T21:29:22.991Z' },
|
||||
execution_health: { status: 'ok', timestamp: '2021-04-01T21:29:22.991Z' },
|
||||
read_health: { status: 'ok', timestamp: '2021-04-01T21:29:22.991Z' },
|
||||
},
|
||||
isAlertsAvailable: true,
|
||||
}));
|
||||
const { queryAllByText } = render(
|
||||
|
@ -110,8 +120,13 @@ describe('health check', () => {
|
|||
|
||||
test('renders warning if encryption key is ephemeral', async () => {
|
||||
useKibanaMock().services.http.get = jest.fn().mockImplementation(async () => ({
|
||||
isSufficientlySecure: true,
|
||||
hasPermanentEncryptionKey: false,
|
||||
is_sufficiently_secure: true,
|
||||
has_permanent_encryption_key: false,
|
||||
alerting_framework_heath: {
|
||||
decryption_health: { status: 'ok', timestamp: '2021-04-01T21:29:22.991Z' },
|
||||
execution_health: { status: 'ok', timestamp: '2021-04-01T21:29:22.991Z' },
|
||||
read_health: { status: 'ok', timestamp: '2021-04-01T21:29:22.991Z' },
|
||||
},
|
||||
isAlertsAvailable: true,
|
||||
}));
|
||||
const { queryByText, queryByRole } = render(
|
||||
|
@ -139,8 +154,13 @@ describe('health check', () => {
|
|||
|
||||
test('renders warning if encryption key is ephemeral and keys are disabled', async () => {
|
||||
useKibanaMock().services.http.get = jest.fn().mockImplementation(async () => ({
|
||||
isSufficientlySecure: false,
|
||||
hasPermanentEncryptionKey: false,
|
||||
is_sufficiently_secure: false,
|
||||
has_permanent_encryption_key: false,
|
||||
alerting_framework_heath: {
|
||||
decryption_health: { status: 'ok', timestamp: '2021-04-01T21:29:22.991Z' },
|
||||
execution_health: { status: 'ok', timestamp: '2021-04-01T21:29:22.991Z' },
|
||||
read_health: { status: 'ok', timestamp: '2021-04-01T21:29:22.991Z' },
|
||||
},
|
||||
isAlertsAvailable: true,
|
||||
}));
|
||||
|
||||
|
|
|
@ -15,12 +15,12 @@ import { i18n } from '@kbn/i18n';
|
|||
|
||||
import { EuiEmptyPrompt, EuiCode } from '@elastic/eui';
|
||||
import { DocLinksStart } from 'kibana/public';
|
||||
import { alertingFrameworkHealth } from '../lib/alert_api';
|
||||
import './health_check.scss';
|
||||
import { useHealthContext } from '../context/health_context';
|
||||
import { useKibana } from '../../common/lib/kibana';
|
||||
import { CenterJustifiedSpinner } from './center_justified_spinner';
|
||||
import { triggersActionsUiHealth } from '../../common/lib/health_api';
|
||||
import { alertingFrameworkHealth } from '../lib/alert_api';
|
||||
|
||||
interface Props {
|
||||
inFlyout?: boolean;
|
||||
|
|
|
@ -7,7 +7,10 @@
|
|||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
export { LEGACY_BASE_ALERT_API_PATH } from '../../../../alerting/common';
|
||||
export {
|
||||
BASE_ALERTING_API_PATH,
|
||||
INTERNAL_BASE_ALERTING_API_PATH,
|
||||
} from '../../../../alerting/common';
|
||||
export { BASE_ACTION_API_PATH } from '../../../../actions/common';
|
||||
|
||||
export type Section = 'connectors' | 'rules';
|
||||
|
|
|
@ -1,875 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { Alert, AlertType, AlertUpdates } from '../../types';
|
||||
import { httpServiceMock } from '../../../../../../src/core/public/mocks';
|
||||
import {
|
||||
createAlert,
|
||||
deleteAlerts,
|
||||
disableAlerts,
|
||||
enableAlerts,
|
||||
disableAlert,
|
||||
enableAlert,
|
||||
loadAlert,
|
||||
loadAlertAggregations,
|
||||
loadAlerts,
|
||||
loadAlertState,
|
||||
loadAlertTypes,
|
||||
muteAlerts,
|
||||
unmuteAlerts,
|
||||
muteAlert,
|
||||
unmuteAlert,
|
||||
updateAlert,
|
||||
muteAlertInstance,
|
||||
unmuteAlertInstance,
|
||||
alertingFrameworkHealth,
|
||||
mapFiltersToKql,
|
||||
} from './alert_api';
|
||||
import uuid from 'uuid';
|
||||
import { AlertNotifyWhenType, ALERTS_FEATURE_ID } from '../../../../alerting/common';
|
||||
|
||||
const http = httpServiceMock.createStartContract();
|
||||
|
||||
beforeEach(() => jest.resetAllMocks());
|
||||
|
||||
describe('loadAlertTypes', () => {
|
||||
test('should call get alert types API', async () => {
|
||||
const resolvedValue: AlertType[] = [
|
||||
{
|
||||
id: 'test',
|
||||
name: 'Test',
|
||||
actionVariables: {
|
||||
context: [{ name: 'var1', description: 'val1' }],
|
||||
state: [{ name: 'var2', description: 'val2' }],
|
||||
params: [{ name: 'var3', description: 'val3' }],
|
||||
},
|
||||
producer: ALERTS_FEATURE_ID,
|
||||
actionGroups: [{ id: 'default', name: 'Default' }],
|
||||
recoveryActionGroup: { id: 'recovered', name: 'Recovered' },
|
||||
defaultActionGroupId: 'default',
|
||||
authorizedConsumers: {},
|
||||
minimumLicenseRequired: 'basic',
|
||||
enabledInLicense: true,
|
||||
},
|
||||
];
|
||||
http.get.mockResolvedValueOnce(resolvedValue);
|
||||
|
||||
const result = await loadAlertTypes({ http });
|
||||
expect(result).toEqual(resolvedValue);
|
||||
expect(http.get.mock.calls[0]).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"/api/alerts/list_alert_types",
|
||||
]
|
||||
`);
|
||||
});
|
||||
});
|
||||
|
||||
describe('loadAlert', () => {
|
||||
test('should call get API with base parameters', async () => {
|
||||
const alertId = uuid.v4();
|
||||
const resolvedValue = {
|
||||
id: alertId,
|
||||
name: 'name',
|
||||
tags: [],
|
||||
enabled: true,
|
||||
alertTypeId: '.noop',
|
||||
schedule: { interval: '1s' },
|
||||
actions: [],
|
||||
params: {},
|
||||
createdBy: null,
|
||||
updatedBy: null,
|
||||
throttle: null,
|
||||
muteAll: false,
|
||||
mutedInstanceIds: [],
|
||||
};
|
||||
http.get.mockResolvedValueOnce(resolvedValue);
|
||||
|
||||
expect(await loadAlert({ http, alertId })).toEqual(resolvedValue);
|
||||
expect(http.get).toHaveBeenCalledWith(`/api/alerts/alert/${alertId}`);
|
||||
});
|
||||
});
|
||||
|
||||
describe('loadAlertState', () => {
|
||||
test('should call get API with base parameters', async () => {
|
||||
const alertId = uuid.v4();
|
||||
const resolvedValue = {
|
||||
alertTypeState: {
|
||||
some: 'value',
|
||||
},
|
||||
alertInstances: {
|
||||
first_instance: {},
|
||||
second_instance: {},
|
||||
},
|
||||
};
|
||||
http.get.mockResolvedValueOnce(resolvedValue);
|
||||
|
||||
expect(await loadAlertState({ http, alertId })).toEqual(resolvedValue);
|
||||
expect(http.get).toHaveBeenCalledWith(`/api/alerts/alert/${alertId}/state`);
|
||||
});
|
||||
|
||||
test('should parse AlertInstances', async () => {
|
||||
const alertId = uuid.v4();
|
||||
const resolvedValue = {
|
||||
alertTypeState: {
|
||||
some: 'value',
|
||||
},
|
||||
alertInstances: {
|
||||
first_instance: {
|
||||
state: {},
|
||||
meta: {
|
||||
lastScheduledActions: {
|
||||
group: 'first_group',
|
||||
date: '2020-02-09T23:15:41.941Z',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
http.get.mockResolvedValueOnce(resolvedValue);
|
||||
|
||||
expect(await loadAlertState({ http, alertId })).toEqual({
|
||||
...resolvedValue,
|
||||
alertInstances: {
|
||||
first_instance: {
|
||||
state: {},
|
||||
meta: {
|
||||
lastScheduledActions: {
|
||||
group: 'first_group',
|
||||
date: new Date('2020-02-09T23:15:41.941Z'),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
expect(http.get).toHaveBeenCalledWith(`/api/alerts/alert/${alertId}/state`);
|
||||
});
|
||||
|
||||
test('should handle empty response from api', async () => {
|
||||
const alertId = uuid.v4();
|
||||
http.get.mockResolvedValueOnce('');
|
||||
|
||||
expect(await loadAlertState({ http, alertId })).toEqual({});
|
||||
expect(http.get).toHaveBeenCalledWith(`/api/alerts/alert/${alertId}/state`);
|
||||
});
|
||||
});
|
||||
|
||||
describe('loadAlerts', () => {
|
||||
test('should call find API with base parameters', async () => {
|
||||
const resolvedValue = {
|
||||
page: 1,
|
||||
perPage: 10,
|
||||
total: 0,
|
||||
data: [],
|
||||
};
|
||||
http.get.mockResolvedValueOnce(resolvedValue);
|
||||
|
||||
const result = await loadAlerts({ http, page: { index: 0, size: 10 } });
|
||||
expect(result).toEqual(resolvedValue);
|
||||
expect(http.get.mock.calls[0]).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"/api/alerts/_find",
|
||||
Object {
|
||||
"query": Object {
|
||||
"default_search_operator": "AND",
|
||||
"filter": undefined,
|
||||
"page": 1,
|
||||
"per_page": 10,
|
||||
"search": undefined,
|
||||
"search_fields": undefined,
|
||||
"sort_field": "name",
|
||||
"sort_order": "asc",
|
||||
},
|
||||
},
|
||||
]
|
||||
`);
|
||||
});
|
||||
|
||||
test('should call find API with searchText', async () => {
|
||||
const resolvedValue = {
|
||||
page: 1,
|
||||
perPage: 10,
|
||||
total: 0,
|
||||
data: [],
|
||||
};
|
||||
http.get.mockResolvedValueOnce(resolvedValue);
|
||||
|
||||
const result = await loadAlerts({ http, searchText: 'apples', page: { index: 0, size: 10 } });
|
||||
expect(result).toEqual(resolvedValue);
|
||||
expect(http.get.mock.calls[0]).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"/api/alerts/_find",
|
||||
Object {
|
||||
"query": Object {
|
||||
"default_search_operator": "AND",
|
||||
"filter": undefined,
|
||||
"page": 1,
|
||||
"per_page": 10,
|
||||
"search": "apples",
|
||||
"search_fields": "[\\"name\\",\\"tags\\"]",
|
||||
"sort_field": "name",
|
||||
"sort_order": "asc",
|
||||
},
|
||||
},
|
||||
]
|
||||
`);
|
||||
});
|
||||
|
||||
test('should call find API with actionTypesFilter', async () => {
|
||||
const resolvedValue = {
|
||||
page: 1,
|
||||
perPage: 10,
|
||||
total: 0,
|
||||
data: [],
|
||||
};
|
||||
http.get.mockResolvedValueOnce(resolvedValue);
|
||||
|
||||
const result = await loadAlerts({
|
||||
http,
|
||||
searchText: 'foo',
|
||||
page: { index: 0, size: 10 },
|
||||
});
|
||||
expect(result).toEqual(resolvedValue);
|
||||
expect(http.get.mock.calls[0]).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"/api/alerts/_find",
|
||||
Object {
|
||||
"query": Object {
|
||||
"default_search_operator": "AND",
|
||||
"filter": undefined,
|
||||
"page": 1,
|
||||
"per_page": 10,
|
||||
"search": "foo",
|
||||
"search_fields": "[\\"name\\",\\"tags\\"]",
|
||||
"sort_field": "name",
|
||||
"sort_order": "asc",
|
||||
},
|
||||
},
|
||||
]
|
||||
`);
|
||||
});
|
||||
|
||||
test('should call find API with typesFilter', async () => {
|
||||
const resolvedValue = {
|
||||
page: 1,
|
||||
perPage: 10,
|
||||
total: 0,
|
||||
data: [],
|
||||
};
|
||||
http.get.mockResolvedValueOnce(resolvedValue);
|
||||
|
||||
const result = await loadAlerts({
|
||||
http,
|
||||
typesFilter: ['foo', 'bar'],
|
||||
page: { index: 0, size: 10 },
|
||||
});
|
||||
expect(result).toEqual(resolvedValue);
|
||||
expect(http.get.mock.calls[0]).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"/api/alerts/_find",
|
||||
Object {
|
||||
"query": Object {
|
||||
"default_search_operator": "AND",
|
||||
"filter": "alert.attributes.alertTypeId:(foo or bar)",
|
||||
"page": 1,
|
||||
"per_page": 10,
|
||||
"search": undefined,
|
||||
"search_fields": undefined,
|
||||
"sort_field": "name",
|
||||
"sort_order": "asc",
|
||||
},
|
||||
},
|
||||
]
|
||||
`);
|
||||
});
|
||||
|
||||
test('should call find API with actionTypesFilter and typesFilter', async () => {
|
||||
const resolvedValue = {
|
||||
page: 1,
|
||||
perPage: 10,
|
||||
total: 0,
|
||||
data: [],
|
||||
};
|
||||
http.get.mockResolvedValueOnce(resolvedValue);
|
||||
|
||||
const result = await loadAlerts({
|
||||
http,
|
||||
searchText: 'baz',
|
||||
typesFilter: ['foo', 'bar'],
|
||||
page: { index: 0, size: 10 },
|
||||
});
|
||||
expect(result).toEqual(resolvedValue);
|
||||
expect(http.get.mock.calls[0]).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"/api/alerts/_find",
|
||||
Object {
|
||||
"query": Object {
|
||||
"default_search_operator": "AND",
|
||||
"filter": "alert.attributes.alertTypeId:(foo or bar)",
|
||||
"page": 1,
|
||||
"per_page": 10,
|
||||
"search": "baz",
|
||||
"search_fields": "[\\"name\\",\\"tags\\"]",
|
||||
"sort_field": "name",
|
||||
"sort_order": "asc",
|
||||
},
|
||||
},
|
||||
]
|
||||
`);
|
||||
});
|
||||
|
||||
test('should call find API with searchText and tagsFilter and typesFilter', async () => {
|
||||
const resolvedValue = {
|
||||
page: 1,
|
||||
perPage: 10,
|
||||
total: 0,
|
||||
data: [],
|
||||
};
|
||||
http.get.mockResolvedValueOnce(resolvedValue);
|
||||
|
||||
const result = await loadAlerts({
|
||||
http,
|
||||
searchText: 'apples, foo, baz',
|
||||
typesFilter: ['foo', 'bar'],
|
||||
page: { index: 0, size: 10 },
|
||||
});
|
||||
expect(result).toEqual(resolvedValue);
|
||||
expect(http.get.mock.calls[0]).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"/api/alerts/_find",
|
||||
Object {
|
||||
"query": Object {
|
||||
"default_search_operator": "AND",
|
||||
"filter": "alert.attributes.alertTypeId:(foo or bar)",
|
||||
"page": 1,
|
||||
"per_page": 10,
|
||||
"search": "apples, foo, baz",
|
||||
"search_fields": "[\\"name\\",\\"tags\\"]",
|
||||
"sort_field": "name",
|
||||
"sort_order": "asc",
|
||||
},
|
||||
},
|
||||
]
|
||||
`);
|
||||
});
|
||||
});
|
||||
|
||||
describe('loadAlertAggregations', () => {
|
||||
test('should call aggregate API with base parameters', async () => {
|
||||
const resolvedValue = {
|
||||
alertExecutionStatus: {
|
||||
ok: 4,
|
||||
active: 2,
|
||||
error: 1,
|
||||
pending: 1,
|
||||
unknown: 0,
|
||||
},
|
||||
};
|
||||
http.get.mockResolvedValueOnce(resolvedValue);
|
||||
|
||||
const result = await loadAlertAggregations({ http });
|
||||
expect(result).toEqual(resolvedValue);
|
||||
expect(http.get.mock.calls[0]).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"/api/alerts/_aggregate",
|
||||
Object {
|
||||
"query": Object {
|
||||
"default_search_operator": "AND",
|
||||
"filter": undefined,
|
||||
"search": undefined,
|
||||
"search_fields": undefined,
|
||||
},
|
||||
},
|
||||
]
|
||||
`);
|
||||
});
|
||||
|
||||
test('should call aggregate API with searchText', async () => {
|
||||
const resolvedValue = {
|
||||
alertExecutionStatus: {
|
||||
ok: 4,
|
||||
active: 2,
|
||||
error: 1,
|
||||
pending: 1,
|
||||
unknown: 0,
|
||||
},
|
||||
};
|
||||
http.get.mockResolvedValueOnce(resolvedValue);
|
||||
|
||||
const result = await loadAlertAggregations({ http, searchText: 'apples' });
|
||||
expect(result).toEqual(resolvedValue);
|
||||
expect(http.get.mock.calls[0]).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"/api/alerts/_aggregate",
|
||||
Object {
|
||||
"query": Object {
|
||||
"default_search_operator": "AND",
|
||||
"filter": undefined,
|
||||
"search": "apples",
|
||||
"search_fields": "[\\"name\\",\\"tags\\"]",
|
||||
},
|
||||
},
|
||||
]
|
||||
`);
|
||||
});
|
||||
|
||||
test('should call aggregate API with actionTypesFilter', async () => {
|
||||
const resolvedValue = {
|
||||
alertExecutionStatus: {
|
||||
ok: 4,
|
||||
active: 2,
|
||||
error: 1,
|
||||
pending: 1,
|
||||
unknown: 0,
|
||||
},
|
||||
};
|
||||
http.get.mockResolvedValueOnce(resolvedValue);
|
||||
|
||||
const result = await loadAlertAggregations({
|
||||
http,
|
||||
searchText: 'foo',
|
||||
actionTypesFilter: ['action', 'type'],
|
||||
});
|
||||
expect(result).toEqual(resolvedValue);
|
||||
expect(http.get.mock.calls[0]).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"/api/alerts/_aggregate",
|
||||
Object {
|
||||
"query": Object {
|
||||
"default_search_operator": "AND",
|
||||
"filter": "(alert.attributes.actions:{ actionTypeId:action } OR alert.attributes.actions:{ actionTypeId:type })",
|
||||
"search": "foo",
|
||||
"search_fields": "[\\"name\\",\\"tags\\"]",
|
||||
},
|
||||
},
|
||||
]
|
||||
`);
|
||||
});
|
||||
|
||||
test('should call aggregate API with typesFilter', async () => {
|
||||
const resolvedValue = {
|
||||
alertExecutionStatus: {
|
||||
ok: 4,
|
||||
active: 2,
|
||||
error: 1,
|
||||
pending: 1,
|
||||
unknown: 0,
|
||||
},
|
||||
};
|
||||
http.get.mockResolvedValueOnce(resolvedValue);
|
||||
|
||||
const result = await loadAlertAggregations({
|
||||
http,
|
||||
typesFilter: ['foo', 'bar'],
|
||||
});
|
||||
expect(result).toEqual(resolvedValue);
|
||||
expect(http.get.mock.calls[0]).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"/api/alerts/_aggregate",
|
||||
Object {
|
||||
"query": Object {
|
||||
"default_search_operator": "AND",
|
||||
"filter": "alert.attributes.alertTypeId:(foo or bar)",
|
||||
"search": undefined,
|
||||
"search_fields": undefined,
|
||||
},
|
||||
},
|
||||
]
|
||||
`);
|
||||
});
|
||||
|
||||
test('should call aggregate API with actionTypesFilter and typesFilter', async () => {
|
||||
const resolvedValue = {
|
||||
alertExecutionStatus: {
|
||||
ok: 4,
|
||||
active: 2,
|
||||
error: 1,
|
||||
pending: 1,
|
||||
unknown: 0,
|
||||
},
|
||||
};
|
||||
http.get.mockResolvedValueOnce(resolvedValue);
|
||||
|
||||
const result = await loadAlertAggregations({
|
||||
http,
|
||||
searchText: 'baz',
|
||||
actionTypesFilter: ['action', 'type'],
|
||||
typesFilter: ['foo', 'bar'],
|
||||
});
|
||||
expect(result).toEqual(resolvedValue);
|
||||
expect(http.get.mock.calls[0]).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"/api/alerts/_aggregate",
|
||||
Object {
|
||||
"query": Object {
|
||||
"default_search_operator": "AND",
|
||||
"filter": "alert.attributes.alertTypeId:(foo or bar) and (alert.attributes.actions:{ actionTypeId:action } OR alert.attributes.actions:{ actionTypeId:type })",
|
||||
"search": "baz",
|
||||
"search_fields": "[\\"name\\",\\"tags\\"]",
|
||||
},
|
||||
},
|
||||
]
|
||||
`);
|
||||
});
|
||||
});
|
||||
|
||||
describe('deleteAlerts', () => {
|
||||
test('should call delete API for each alert', async () => {
|
||||
const ids = ['1', '2', '3'];
|
||||
const result = await deleteAlerts({ http, ids });
|
||||
expect(result).toEqual({ errors: [], successes: [undefined, undefined, undefined] });
|
||||
expect(http.delete.mock.calls).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
Array [
|
||||
"/api/alerts/alert/1",
|
||||
],
|
||||
Array [
|
||||
"/api/alerts/alert/2",
|
||||
],
|
||||
Array [
|
||||
"/api/alerts/alert/3",
|
||||
],
|
||||
]
|
||||
`);
|
||||
});
|
||||
});
|
||||
|
||||
describe('createAlert', () => {
|
||||
test('should call create alert API', async () => {
|
||||
const alertToCreate: AlertUpdates = {
|
||||
name: 'test',
|
||||
consumer: 'alerts',
|
||||
tags: ['foo'],
|
||||
enabled: true,
|
||||
alertTypeId: 'test',
|
||||
schedule: {
|
||||
interval: '1m',
|
||||
},
|
||||
actions: [],
|
||||
params: {},
|
||||
throttle: null,
|
||||
notifyWhen: 'onActionGroupChange' as AlertNotifyWhenType,
|
||||
createdAt: new Date('1970-01-01T00:00:00.000Z'),
|
||||
updatedAt: new Date('1970-01-01T00:00:00.000Z'),
|
||||
apiKeyOwner: null,
|
||||
createdBy: null,
|
||||
updatedBy: null,
|
||||
muteAll: false,
|
||||
mutedInstanceIds: [],
|
||||
};
|
||||
const resolvedValue = {
|
||||
...alertToCreate,
|
||||
id: '123',
|
||||
createdBy: null,
|
||||
updatedBy: null,
|
||||
muteAll: false,
|
||||
mutedInstanceIds: [],
|
||||
executionStatus: {
|
||||
status: 'unknown',
|
||||
lastExecutionDate: new Date('2020-08-20T19:23:38Z'),
|
||||
},
|
||||
};
|
||||
http.post.mockResolvedValueOnce(resolvedValue);
|
||||
|
||||
const result = await createAlert({ http, alert: alertToCreate });
|
||||
expect(result).toEqual(resolvedValue);
|
||||
expect(http.post.mock.calls[0]).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"/api/alerts/alert",
|
||||
Object {
|
||||
"body": "{\\"name\\":\\"test\\",\\"consumer\\":\\"alerts\\",\\"tags\\":[\\"foo\\"],\\"enabled\\":true,\\"alertTypeId\\":\\"test\\",\\"schedule\\":{\\"interval\\":\\"1m\\"},\\"actions\\":[],\\"params\\":{},\\"throttle\\":null,\\"notifyWhen\\":\\"onActionGroupChange\\",\\"createdAt\\":\\"1970-01-01T00:00:00.000Z\\",\\"updatedAt\\":\\"1970-01-01T00:00:00.000Z\\",\\"apiKeyOwner\\":null,\\"createdBy\\":null,\\"updatedBy\\":null,\\"muteAll\\":false,\\"mutedInstanceIds\\":[]}",
|
||||
},
|
||||
]
|
||||
`);
|
||||
});
|
||||
});
|
||||
|
||||
describe('updateAlert', () => {
|
||||
test('should call alert update API', async () => {
|
||||
const alertToUpdate = {
|
||||
throttle: '1m',
|
||||
consumer: 'alerts',
|
||||
name: 'test',
|
||||
tags: ['foo'],
|
||||
schedule: {
|
||||
interval: '1m',
|
||||
},
|
||||
params: {},
|
||||
actions: [],
|
||||
createdAt: new Date('1970-01-01T00:00:00.000Z'),
|
||||
updatedAt: new Date('1970-01-01T00:00:00.000Z'),
|
||||
apiKey: null,
|
||||
apiKeyOwner: null,
|
||||
notifyWhen: 'onThrottleInterval' as AlertNotifyWhenType,
|
||||
};
|
||||
const resolvedValue: Alert = {
|
||||
...alertToUpdate,
|
||||
id: '123',
|
||||
enabled: true,
|
||||
alertTypeId: 'test',
|
||||
createdBy: null,
|
||||
updatedBy: null,
|
||||
muteAll: false,
|
||||
mutedInstanceIds: [],
|
||||
executionStatus: {
|
||||
status: 'unknown',
|
||||
lastExecutionDate: new Date('2020-08-20T19:23:38Z'),
|
||||
},
|
||||
};
|
||||
http.put.mockResolvedValueOnce(resolvedValue);
|
||||
|
||||
const result = await updateAlert({ http, id: '123', alert: alertToUpdate });
|
||||
expect(result).toEqual(resolvedValue);
|
||||
expect(http.put.mock.calls[0]).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"/api/alerts/alert/123",
|
||||
Object {
|
||||
"body": "{\\"throttle\\":\\"1m\\",\\"name\\":\\"test\\",\\"tags\\":[\\"foo\\"],\\"schedule\\":{\\"interval\\":\\"1m\\"},\\"params\\":{},\\"actions\\":[],\\"notifyWhen\\":\\"onThrottleInterval\\"}",
|
||||
},
|
||||
]
|
||||
`);
|
||||
});
|
||||
});
|
||||
|
||||
describe('enableAlert', () => {
|
||||
test('should call enable alert API', async () => {
|
||||
const result = await enableAlert({ http, id: '1' });
|
||||
expect(result).toEqual(undefined);
|
||||
expect(http.post.mock.calls).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
Array [
|
||||
"/api/alerts/alert/1/_enable",
|
||||
],
|
||||
]
|
||||
`);
|
||||
});
|
||||
});
|
||||
|
||||
describe('disableAlert', () => {
|
||||
test('should call disable alert API', async () => {
|
||||
const result = await disableAlert({ http, id: '1' });
|
||||
expect(result).toEqual(undefined);
|
||||
expect(http.post.mock.calls).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
Array [
|
||||
"/api/alerts/alert/1/_disable",
|
||||
],
|
||||
]
|
||||
`);
|
||||
});
|
||||
});
|
||||
|
||||
describe('muteAlertInstance', () => {
|
||||
test('should call mute instance alert API', async () => {
|
||||
const result = await muteAlertInstance({ http, id: '1', instanceId: '123' });
|
||||
expect(result).toEqual(undefined);
|
||||
expect(http.post.mock.calls).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
Array [
|
||||
"/api/alerts/alert/1/alert_instance/123/_mute",
|
||||
],
|
||||
]
|
||||
`);
|
||||
});
|
||||
});
|
||||
|
||||
describe('unmuteAlertInstance', () => {
|
||||
test('should call mute instance alert API', async () => {
|
||||
const result = await unmuteAlertInstance({ http, id: '1', instanceId: '123' });
|
||||
expect(result).toEqual(undefined);
|
||||
expect(http.post.mock.calls).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
Array [
|
||||
"/api/alerts/alert/1/alert_instance/123/_unmute",
|
||||
],
|
||||
]
|
||||
`);
|
||||
});
|
||||
});
|
||||
|
||||
describe('muteAlert', () => {
|
||||
test('should call mute alert API', async () => {
|
||||
const result = await muteAlert({ http, id: '1' });
|
||||
expect(result).toEqual(undefined);
|
||||
expect(http.post.mock.calls).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
Array [
|
||||
"/api/alerts/alert/1/_mute_all",
|
||||
],
|
||||
]
|
||||
`);
|
||||
});
|
||||
});
|
||||
|
||||
describe('unmuteAlert', () => {
|
||||
test('should call unmute alert API', async () => {
|
||||
const result = await unmuteAlert({ http, id: '1' });
|
||||
expect(result).toEqual(undefined);
|
||||
expect(http.post.mock.calls).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
Array [
|
||||
"/api/alerts/alert/1/_unmute_all",
|
||||
],
|
||||
]
|
||||
`);
|
||||
});
|
||||
});
|
||||
|
||||
describe('enableAlerts', () => {
|
||||
test('should call enable alert API per alert', async () => {
|
||||
const ids = ['1', '2', '3'];
|
||||
const result = await enableAlerts({ http, ids });
|
||||
expect(result).toEqual(undefined);
|
||||
expect(http.post.mock.calls).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
Array [
|
||||
"/api/alerts/alert/1/_enable",
|
||||
],
|
||||
Array [
|
||||
"/api/alerts/alert/2/_enable",
|
||||
],
|
||||
Array [
|
||||
"/api/alerts/alert/3/_enable",
|
||||
],
|
||||
]
|
||||
`);
|
||||
});
|
||||
});
|
||||
|
||||
describe('disableAlerts', () => {
|
||||
test('should call disable alert API per alert', async () => {
|
||||
const ids = ['1', '2', '3'];
|
||||
const result = await disableAlerts({ http, ids });
|
||||
expect(result).toEqual(undefined);
|
||||
expect(http.post.mock.calls).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
Array [
|
||||
"/api/alerts/alert/1/_disable",
|
||||
],
|
||||
Array [
|
||||
"/api/alerts/alert/2/_disable",
|
||||
],
|
||||
Array [
|
||||
"/api/alerts/alert/3/_disable",
|
||||
],
|
||||
]
|
||||
`);
|
||||
});
|
||||
});
|
||||
|
||||
describe('muteAlerts', () => {
|
||||
test('should call mute alert API per alert', async () => {
|
||||
const ids = ['1', '2', '3'];
|
||||
const result = await muteAlerts({ http, ids });
|
||||
expect(result).toEqual(undefined);
|
||||
expect(http.post.mock.calls).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
Array [
|
||||
"/api/alerts/alert/1/_mute_all",
|
||||
],
|
||||
Array [
|
||||
"/api/alerts/alert/2/_mute_all",
|
||||
],
|
||||
Array [
|
||||
"/api/alerts/alert/3/_mute_all",
|
||||
],
|
||||
]
|
||||
`);
|
||||
});
|
||||
});
|
||||
|
||||
describe('unmuteAlerts', () => {
|
||||
test('should call unmute alert API per alert', async () => {
|
||||
const ids = ['1', '2', '3'];
|
||||
const result = await unmuteAlerts({ http, ids });
|
||||
expect(result).toEqual(undefined);
|
||||
expect(http.post.mock.calls).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
Array [
|
||||
"/api/alerts/alert/1/_unmute_all",
|
||||
],
|
||||
Array [
|
||||
"/api/alerts/alert/2/_unmute_all",
|
||||
],
|
||||
Array [
|
||||
"/api/alerts/alert/3/_unmute_all",
|
||||
],
|
||||
]
|
||||
`);
|
||||
});
|
||||
});
|
||||
|
||||
describe('alertingFrameworkHealth', () => {
|
||||
test('should call alertingFrameworkHealth API', async () => {
|
||||
const result = await alertingFrameworkHealth({ http });
|
||||
expect(result).toEqual(undefined);
|
||||
expect(http.get.mock.calls).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
Array [
|
||||
"/api/alerts/_health",
|
||||
],
|
||||
]
|
||||
`);
|
||||
});
|
||||
});
|
||||
|
||||
describe('mapFiltersToKql', () => {
|
||||
test('should handle no filters', () => {
|
||||
expect(mapFiltersToKql({})).toEqual([]);
|
||||
});
|
||||
|
||||
test('should handle typesFilter', () => {
|
||||
expect(
|
||||
mapFiltersToKql({
|
||||
typesFilter: ['type', 'filter'],
|
||||
})
|
||||
).toEqual(['alert.attributes.alertTypeId:(type or filter)']);
|
||||
});
|
||||
|
||||
test('should handle actionTypesFilter', () => {
|
||||
expect(
|
||||
mapFiltersToKql({
|
||||
actionTypesFilter: ['action', 'types', 'filter'],
|
||||
})
|
||||
).toEqual([
|
||||
'(alert.attributes.actions:{ actionTypeId:action } OR alert.attributes.actions:{ actionTypeId:types } OR alert.attributes.actions:{ actionTypeId:filter })',
|
||||
]);
|
||||
});
|
||||
|
||||
test('should handle alertStatusesFilter', () => {
|
||||
expect(
|
||||
mapFiltersToKql({
|
||||
alertStatusesFilter: ['alert', 'statuses', 'filter'],
|
||||
})
|
||||
).toEqual(['alert.attributes.executionStatus.status:(alert or statuses or filter)']);
|
||||
});
|
||||
|
||||
test('should handle typesFilter and actionTypesFilter', () => {
|
||||
expect(
|
||||
mapFiltersToKql({
|
||||
typesFilter: ['type', 'filter'],
|
||||
actionTypesFilter: ['action', 'types', 'filter'],
|
||||
})
|
||||
).toEqual([
|
||||
'alert.attributes.alertTypeId:(type or filter)',
|
||||
'(alert.attributes.actions:{ actionTypeId:action } OR alert.attributes.actions:{ actionTypeId:types } OR alert.attributes.actions:{ actionTypeId:filter })',
|
||||
]);
|
||||
});
|
||||
|
||||
test('should handle typesFilter, actionTypesFilter and alertStatusesFilter', () => {
|
||||
expect(
|
||||
mapFiltersToKql({
|
||||
typesFilter: ['type', 'filter'],
|
||||
actionTypesFilter: ['action', 'types', 'filter'],
|
||||
alertStatusesFilter: ['alert', 'statuses', 'filter'],
|
||||
})
|
||||
).toEqual([
|
||||
'alert.attributes.alertTypeId:(type or filter)',
|
||||
'(alert.attributes.actions:{ actionTypeId:action } OR alert.attributes.actions:{ actionTypeId:types } OR alert.attributes.actions:{ actionTypeId:filter })',
|
||||
'alert.attributes.executionStatus.status:(alert or statuses or filter)',
|
||||
]);
|
||||
});
|
||||
});
|
|
@ -1,296 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { HttpSetup } from 'kibana/public';
|
||||
import { Errors, identity } from 'io-ts';
|
||||
import { pipe } from 'fp-ts/lib/pipeable';
|
||||
import { fold } from 'fp-ts/lib/Either';
|
||||
import { pick } from 'lodash';
|
||||
import { alertStateSchema, AlertingFrameworkHealth } from '../../../../alerting/common';
|
||||
import { LEGACY_BASE_ALERT_API_PATH } from '../constants';
|
||||
import {
|
||||
Alert,
|
||||
AlertAggregations,
|
||||
AlertType,
|
||||
AlertUpdates,
|
||||
AlertTaskState,
|
||||
AlertInstanceSummary,
|
||||
Pagination,
|
||||
Sorting,
|
||||
} from '../../types';
|
||||
|
||||
export async function loadAlertTypes({ http }: { http: HttpSetup }): Promise<AlertType[]> {
|
||||
return await http.get(`${LEGACY_BASE_ALERT_API_PATH}/list_alert_types`);
|
||||
}
|
||||
|
||||
export async function loadAlert({
|
||||
http,
|
||||
alertId,
|
||||
}: {
|
||||
http: HttpSetup;
|
||||
alertId: string;
|
||||
}): Promise<Alert> {
|
||||
return await http.get(`${LEGACY_BASE_ALERT_API_PATH}/alert/${alertId}`);
|
||||
}
|
||||
|
||||
type EmptyHttpResponse = '';
|
||||
export async function loadAlertState({
|
||||
http,
|
||||
alertId,
|
||||
}: {
|
||||
http: HttpSetup;
|
||||
alertId: string;
|
||||
}): Promise<AlertTaskState> {
|
||||
return await http
|
||||
.get(`${LEGACY_BASE_ALERT_API_PATH}/alert/${alertId}/state`)
|
||||
.then((state: AlertTaskState | EmptyHttpResponse) => (state ? state : {}))
|
||||
.then((state: AlertTaskState) => {
|
||||
return pipe(
|
||||
alertStateSchema.decode(state),
|
||||
fold((e: Errors) => {
|
||||
throw new Error(`Alert "${alertId}" has invalid state`);
|
||||
}, identity)
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
export async function loadAlertInstanceSummary({
|
||||
http,
|
||||
alertId,
|
||||
}: {
|
||||
http: HttpSetup;
|
||||
alertId: string;
|
||||
}): Promise<AlertInstanceSummary> {
|
||||
return await http.get(`${LEGACY_BASE_ALERT_API_PATH}/alert/${alertId}/_instance_summary`);
|
||||
}
|
||||
|
||||
export const mapFiltersToKql = ({
|
||||
typesFilter,
|
||||
actionTypesFilter,
|
||||
alertStatusesFilter,
|
||||
}: {
|
||||
typesFilter?: string[];
|
||||
actionTypesFilter?: string[];
|
||||
alertStatusesFilter?: string[];
|
||||
}): string[] => {
|
||||
const filters = [];
|
||||
if (typesFilter && typesFilter.length) {
|
||||
filters.push(`alert.attributes.alertTypeId:(${typesFilter.join(' or ')})`);
|
||||
}
|
||||
if (actionTypesFilter && actionTypesFilter.length) {
|
||||
filters.push(
|
||||
[
|
||||
'(',
|
||||
actionTypesFilter
|
||||
.map((id) => `alert.attributes.actions:{ actionTypeId:${id} }`)
|
||||
.join(' OR '),
|
||||
')',
|
||||
].join('')
|
||||
);
|
||||
}
|
||||
if (alertStatusesFilter && alertStatusesFilter.length) {
|
||||
filters.push(`alert.attributes.executionStatus.status:(${alertStatusesFilter.join(' or ')})`);
|
||||
}
|
||||
return filters;
|
||||
};
|
||||
|
||||
export async function loadAlerts({
|
||||
http,
|
||||
page,
|
||||
searchText,
|
||||
typesFilter,
|
||||
actionTypesFilter,
|
||||
alertStatusesFilter,
|
||||
sort = { field: 'name', direction: 'asc' },
|
||||
}: {
|
||||
http: HttpSetup;
|
||||
page: Pagination;
|
||||
searchText?: string;
|
||||
typesFilter?: string[];
|
||||
actionTypesFilter?: string[];
|
||||
alertStatusesFilter?: string[];
|
||||
sort?: Sorting;
|
||||
}): Promise<{
|
||||
page: number;
|
||||
perPage: number;
|
||||
total: number;
|
||||
data: Alert[];
|
||||
}> {
|
||||
const filters = mapFiltersToKql({ typesFilter, actionTypesFilter, alertStatusesFilter });
|
||||
return await http.get(`${LEGACY_BASE_ALERT_API_PATH}/_find`, {
|
||||
query: {
|
||||
page: page.index + 1,
|
||||
per_page: page.size,
|
||||
search_fields: searchText ? JSON.stringify(['name', 'tags']) : undefined,
|
||||
search: searchText,
|
||||
filter: filters.length ? filters.join(' and ') : undefined,
|
||||
default_search_operator: 'AND',
|
||||
sort_field: sort.field,
|
||||
sort_order: sort.direction,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export async function loadAlertAggregations({
|
||||
http,
|
||||
searchText,
|
||||
typesFilter,
|
||||
actionTypesFilter,
|
||||
alertStatusesFilter,
|
||||
}: {
|
||||
http: HttpSetup;
|
||||
searchText?: string;
|
||||
typesFilter?: string[];
|
||||
actionTypesFilter?: string[];
|
||||
alertStatusesFilter?: string[];
|
||||
}): Promise<AlertAggregations> {
|
||||
const filters = mapFiltersToKql({ typesFilter, actionTypesFilter, alertStatusesFilter });
|
||||
return await http.get(`${LEGACY_BASE_ALERT_API_PATH}/_aggregate`, {
|
||||
query: {
|
||||
search_fields: searchText ? JSON.stringify(['name', 'tags']) : undefined,
|
||||
search: searchText,
|
||||
filter: filters.length ? filters.join(' and ') : undefined,
|
||||
default_search_operator: 'AND',
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export async function deleteAlerts({
|
||||
ids,
|
||||
http,
|
||||
}: {
|
||||
ids: string[];
|
||||
http: HttpSetup;
|
||||
}): Promise<{ successes: string[]; errors: string[] }> {
|
||||
const successes: string[] = [];
|
||||
const errors: string[] = [];
|
||||
await Promise.all(ids.map((id) => http.delete(`${LEGACY_BASE_ALERT_API_PATH}/alert/${id}`))).then(
|
||||
function (fulfilled) {
|
||||
successes.push(...fulfilled);
|
||||
},
|
||||
function (rejected) {
|
||||
errors.push(...rejected);
|
||||
}
|
||||
);
|
||||
return { successes, errors };
|
||||
}
|
||||
|
||||
export async function createAlert({
|
||||
http,
|
||||
alert,
|
||||
}: {
|
||||
http: HttpSetup;
|
||||
alert: Omit<
|
||||
AlertUpdates,
|
||||
'createdBy' | 'updatedBy' | 'muteAll' | 'mutedInstanceIds' | 'executionStatus'
|
||||
>;
|
||||
}): Promise<Alert> {
|
||||
return await http.post(`${LEGACY_BASE_ALERT_API_PATH}/alert`, {
|
||||
body: JSON.stringify(alert),
|
||||
});
|
||||
}
|
||||
|
||||
export async function updateAlert({
|
||||
http,
|
||||
alert,
|
||||
id,
|
||||
}: {
|
||||
http: HttpSetup;
|
||||
alert: Pick<
|
||||
AlertUpdates,
|
||||
'throttle' | 'name' | 'tags' | 'schedule' | 'params' | 'actions' | 'notifyWhen'
|
||||
>;
|
||||
id: string;
|
||||
}): Promise<Alert> {
|
||||
return await http.put(`${LEGACY_BASE_ALERT_API_PATH}/alert/${id}`, {
|
||||
body: JSON.stringify(
|
||||
pick(alert, ['throttle', 'name', 'tags', 'schedule', 'params', 'actions', 'notifyWhen'])
|
||||
),
|
||||
});
|
||||
}
|
||||
|
||||
export async function enableAlert({ id, http }: { id: string; http: HttpSetup }): Promise<void> {
|
||||
await http.post(`${LEGACY_BASE_ALERT_API_PATH}/alert/${id}/_enable`);
|
||||
}
|
||||
|
||||
export async function enableAlerts({
|
||||
ids,
|
||||
http,
|
||||
}: {
|
||||
ids: string[];
|
||||
http: HttpSetup;
|
||||
}): Promise<void> {
|
||||
await Promise.all(ids.map((id) => enableAlert({ id, http })));
|
||||
}
|
||||
|
||||
export async function disableAlert({ id, http }: { id: string; http: HttpSetup }): Promise<void> {
|
||||
await http.post(`${LEGACY_BASE_ALERT_API_PATH}/alert/${id}/_disable`);
|
||||
}
|
||||
|
||||
export async function disableAlerts({
|
||||
ids,
|
||||
http,
|
||||
}: {
|
||||
ids: string[];
|
||||
http: HttpSetup;
|
||||
}): Promise<void> {
|
||||
await Promise.all(ids.map((id) => disableAlert({ id, http })));
|
||||
}
|
||||
|
||||
export async function muteAlertInstance({
|
||||
id,
|
||||
instanceId,
|
||||
http,
|
||||
}: {
|
||||
id: string;
|
||||
instanceId: string;
|
||||
http: HttpSetup;
|
||||
}): Promise<void> {
|
||||
await http.post(`${LEGACY_BASE_ALERT_API_PATH}/alert/${id}/alert_instance/${instanceId}/_mute`);
|
||||
}
|
||||
|
||||
export async function unmuteAlertInstance({
|
||||
id,
|
||||
instanceId,
|
||||
http,
|
||||
}: {
|
||||
id: string;
|
||||
instanceId: string;
|
||||
http: HttpSetup;
|
||||
}): Promise<void> {
|
||||
await http.post(`${LEGACY_BASE_ALERT_API_PATH}/alert/${id}/alert_instance/${instanceId}/_unmute`);
|
||||
}
|
||||
|
||||
export async function muteAlert({ id, http }: { id: string; http: HttpSetup }): Promise<void> {
|
||||
await http.post(`${LEGACY_BASE_ALERT_API_PATH}/alert/${id}/_mute_all`);
|
||||
}
|
||||
|
||||
export async function muteAlerts({ ids, http }: { ids: string[]; http: HttpSetup }): Promise<void> {
|
||||
await Promise.all(ids.map((id) => muteAlert({ http, id })));
|
||||
}
|
||||
|
||||
export async function unmuteAlert({ id, http }: { id: string; http: HttpSetup }): Promise<void> {
|
||||
await http.post(`${LEGACY_BASE_ALERT_API_PATH}/alert/${id}/_unmute_all`);
|
||||
}
|
||||
|
||||
export async function unmuteAlerts({
|
||||
ids,
|
||||
http,
|
||||
}: {
|
||||
ids: string[];
|
||||
http: HttpSetup;
|
||||
}): Promise<void> {
|
||||
await Promise.all(ids.map((id) => unmuteAlert({ id, http })));
|
||||
}
|
||||
|
||||
export async function alertingFrameworkHealth({
|
||||
http,
|
||||
}: {
|
||||
http: HttpSetup;
|
||||
}): Promise<AlertingFrameworkHealth> {
|
||||
return await http.get(`${LEGACY_BASE_ALERT_API_PATH}/_health`);
|
||||
}
|
|
@ -0,0 +1,212 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { httpServiceMock } from '../../../../../../../src/core/public/mocks';
|
||||
import { loadAlertAggregations } from './aggregate';
|
||||
|
||||
const http = httpServiceMock.createStartContract();
|
||||
|
||||
describe('loadAlertAggregations', () => {
|
||||
beforeEach(() => jest.resetAllMocks());
|
||||
|
||||
test('should call aggregate API with base parameters', async () => {
|
||||
const resolvedValue = {
|
||||
rule_execution_status: {
|
||||
ok: 4,
|
||||
active: 2,
|
||||
error: 1,
|
||||
pending: 1,
|
||||
unknown: 0,
|
||||
},
|
||||
};
|
||||
http.get.mockResolvedValueOnce(resolvedValue);
|
||||
|
||||
const result = await loadAlertAggregations({ http });
|
||||
expect(result).toEqual({
|
||||
alertExecutionStatus: {
|
||||
ok: 4,
|
||||
active: 2,
|
||||
error: 1,
|
||||
pending: 1,
|
||||
unknown: 0,
|
||||
},
|
||||
});
|
||||
expect(http.get.mock.calls[0]).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"/internal/alerting/rules/_aggregate",
|
||||
Object {
|
||||
"query": Object {
|
||||
"default_search_operator": "AND",
|
||||
"filter": undefined,
|
||||
"search": undefined,
|
||||
"search_fields": undefined,
|
||||
},
|
||||
},
|
||||
]
|
||||
`);
|
||||
});
|
||||
|
||||
test('should call aggregate API with searchText', async () => {
|
||||
const resolvedValue = {
|
||||
rule_execution_status: {
|
||||
ok: 4,
|
||||
active: 2,
|
||||
error: 1,
|
||||
pending: 1,
|
||||
unknown: 0,
|
||||
},
|
||||
};
|
||||
http.get.mockResolvedValueOnce(resolvedValue);
|
||||
|
||||
const result = await loadAlertAggregations({ http, searchText: 'apples' });
|
||||
expect(result).toEqual({
|
||||
alertExecutionStatus: {
|
||||
ok: 4,
|
||||
active: 2,
|
||||
error: 1,
|
||||
pending: 1,
|
||||
unknown: 0,
|
||||
},
|
||||
});
|
||||
expect(http.get.mock.calls[0]).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"/internal/alerting/rules/_aggregate",
|
||||
Object {
|
||||
"query": Object {
|
||||
"default_search_operator": "AND",
|
||||
"filter": undefined,
|
||||
"search": "apples",
|
||||
"search_fields": "[\\"name\\",\\"tags\\"]",
|
||||
},
|
||||
},
|
||||
]
|
||||
`);
|
||||
});
|
||||
|
||||
test('should call aggregate API with actionTypesFilter', async () => {
|
||||
const resolvedValue = {
|
||||
rule_execution_status: {
|
||||
ok: 4,
|
||||
active: 2,
|
||||
error: 1,
|
||||
pending: 1,
|
||||
unknown: 0,
|
||||
},
|
||||
};
|
||||
http.get.mockResolvedValueOnce(resolvedValue);
|
||||
|
||||
const result = await loadAlertAggregations({
|
||||
http,
|
||||
searchText: 'foo',
|
||||
actionTypesFilter: ['action', 'type'],
|
||||
});
|
||||
expect(result).toEqual({
|
||||
alertExecutionStatus: {
|
||||
ok: 4,
|
||||
active: 2,
|
||||
error: 1,
|
||||
pending: 1,
|
||||
unknown: 0,
|
||||
},
|
||||
});
|
||||
expect(http.get.mock.calls[0]).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"/internal/alerting/rules/_aggregate",
|
||||
Object {
|
||||
"query": Object {
|
||||
"default_search_operator": "AND",
|
||||
"filter": "(alert.attributes.actions:{ actionTypeId:action } OR alert.attributes.actions:{ actionTypeId:type })",
|
||||
"search": "foo",
|
||||
"search_fields": "[\\"name\\",\\"tags\\"]",
|
||||
},
|
||||
},
|
||||
]
|
||||
`);
|
||||
});
|
||||
|
||||
test('should call aggregate API with typesFilter', async () => {
|
||||
const resolvedValue = {
|
||||
rule_execution_status: {
|
||||
ok: 4,
|
||||
active: 2,
|
||||
error: 1,
|
||||
pending: 1,
|
||||
unknown: 0,
|
||||
},
|
||||
};
|
||||
http.get.mockResolvedValueOnce(resolvedValue);
|
||||
|
||||
const result = await loadAlertAggregations({
|
||||
http,
|
||||
typesFilter: ['foo', 'bar'],
|
||||
});
|
||||
expect(result).toEqual({
|
||||
alertExecutionStatus: {
|
||||
ok: 4,
|
||||
active: 2,
|
||||
error: 1,
|
||||
pending: 1,
|
||||
unknown: 0,
|
||||
},
|
||||
});
|
||||
expect(http.get.mock.calls[0]).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"/internal/alerting/rules/_aggregate",
|
||||
Object {
|
||||
"query": Object {
|
||||
"default_search_operator": "AND",
|
||||
"filter": "alert.attributes.alertTypeId:(foo or bar)",
|
||||
"search": undefined,
|
||||
"search_fields": undefined,
|
||||
},
|
||||
},
|
||||
]
|
||||
`);
|
||||
});
|
||||
|
||||
test('should call aggregate API with actionTypesFilter and typesFilter', async () => {
|
||||
const resolvedValue = {
|
||||
rule_execution_status: {
|
||||
ok: 4,
|
||||
active: 2,
|
||||
error: 1,
|
||||
pending: 1,
|
||||
unknown: 0,
|
||||
},
|
||||
};
|
||||
http.get.mockResolvedValueOnce(resolvedValue);
|
||||
|
||||
const result = await loadAlertAggregations({
|
||||
http,
|
||||
searchText: 'baz',
|
||||
actionTypesFilter: ['action', 'type'],
|
||||
typesFilter: ['foo', 'bar'],
|
||||
});
|
||||
expect(result).toEqual({
|
||||
alertExecutionStatus: {
|
||||
ok: 4,
|
||||
active: 2,
|
||||
error: 1,
|
||||
pending: 1,
|
||||
unknown: 0,
|
||||
},
|
||||
});
|
||||
expect(http.get.mock.calls[0]).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"/internal/alerting/rules/_aggregate",
|
||||
Object {
|
||||
"query": Object {
|
||||
"default_search_operator": "AND",
|
||||
"filter": "alert.attributes.alertTypeId:(foo or bar) and (alert.attributes.actions:{ actionTypeId:action } OR alert.attributes.actions:{ actionTypeId:type })",
|
||||
"search": "baz",
|
||||
"search_fields": "[\\"name\\",\\"tags\\"]",
|
||||
},
|
||||
},
|
||||
]
|
||||
`);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
import { HttpSetup } from 'kibana/public';
|
||||
import { AlertAggregations } from '../../../types';
|
||||
import { INTERNAL_BASE_ALERTING_API_PATH } from '../../constants';
|
||||
import { mapFiltersToKql } from './map_filters_to_kql';
|
||||
import { RewriteRequestCase } from '../../../../../actions/common';
|
||||
|
||||
const rewriteBodyRes: RewriteRequestCase<AlertAggregations> = ({
|
||||
rule_execution_status: alertExecutionStatus,
|
||||
...rest
|
||||
}: any) => ({
|
||||
...rest,
|
||||
alertExecutionStatus,
|
||||
});
|
||||
|
||||
export async function loadAlertAggregations({
|
||||
http,
|
||||
searchText,
|
||||
typesFilter,
|
||||
actionTypesFilter,
|
||||
alertStatusesFilter,
|
||||
}: {
|
||||
http: HttpSetup;
|
||||
searchText?: string;
|
||||
typesFilter?: string[];
|
||||
actionTypesFilter?: string[];
|
||||
alertStatusesFilter?: string[];
|
||||
}): Promise<AlertAggregations> {
|
||||
const filters = mapFiltersToKql({ typesFilter, actionTypesFilter, alertStatusesFilter });
|
||||
const res = await http.get(`${INTERNAL_BASE_ALERTING_API_PATH}/rules/_aggregate`, {
|
||||
query: {
|
||||
search_fields: searchText ? JSON.stringify(['name', 'tags']) : undefined,
|
||||
search: searchText,
|
||||
filter: filters.length ? filters.join(' and ') : undefined,
|
||||
default_search_operator: 'AND',
|
||||
},
|
||||
});
|
||||
return rewriteBodyRes(res);
|
||||
}
|
|
@ -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
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { httpServiceMock } from '../../../../../../../src/core/public/mocks';
|
||||
import { AlertInstanceSummary } from '../../../../../alerting/common';
|
||||
import { loadAlertInstanceSummary } from './alert_summary';
|
||||
|
||||
const http = httpServiceMock.createStartContract();
|
||||
|
||||
describe('loadAlertInstanceSummary', () => {
|
||||
test('should call get alert types API', async () => {
|
||||
const resolvedValue: AlertInstanceSummary = {
|
||||
instances: {},
|
||||
consumer: 'alerts',
|
||||
enabled: true,
|
||||
errorMessages: [],
|
||||
id: 'test',
|
||||
lastRun: '2021-04-01T22:18:27.609Z',
|
||||
muteAll: false,
|
||||
name: 'test',
|
||||
alertTypeId: '.index-threshold',
|
||||
status: 'OK',
|
||||
statusEndDate: '2021-04-01T22:19:25.174Z',
|
||||
statusStartDate: '2021-04-01T21:19:25.174Z',
|
||||
tags: [],
|
||||
throttle: null,
|
||||
};
|
||||
|
||||
http.get.mockResolvedValueOnce({
|
||||
alerts: {},
|
||||
consumer: 'alerts',
|
||||
enabled: true,
|
||||
error_messages: [],
|
||||
id: 'test',
|
||||
last_run: '2021-04-01T22:18:27.609Z',
|
||||
mute_all: false,
|
||||
name: 'test',
|
||||
rule_type_id: '.index-threshold',
|
||||
status: 'OK',
|
||||
status_end_date: '2021-04-01T22:19:25.174Z',
|
||||
status_start_date: '2021-04-01T21:19:25.174Z',
|
||||
tags: [],
|
||||
throttle: null,
|
||||
});
|
||||
|
||||
const result = await loadAlertInstanceSummary({ http, alertId: 'test' });
|
||||
expect(result).toEqual(resolvedValue);
|
||||
expect(http.get.mock.calls[0]).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"/internal/alerting/rule/test/_alert_summary",
|
||||
]
|
||||
`);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
import { HttpSetup } from 'kibana/public';
|
||||
import { INTERNAL_BASE_ALERTING_API_PATH } from '../../constants';
|
||||
import { AlertInstanceSummary } from '../../../types';
|
||||
import { RewriteRequestCase } from '../../../../../actions/common';
|
||||
|
||||
const rewriteBodyRes: RewriteRequestCase<AlertInstanceSummary> = ({
|
||||
alerts,
|
||||
rule_type_id: alertTypeId,
|
||||
mute_all: muteAll,
|
||||
status_start_date: statusStartDate,
|
||||
status_end_date: statusEndDate,
|
||||
error_messages: errorMessages,
|
||||
last_run: lastRun,
|
||||
...rest
|
||||
}: any) => ({
|
||||
...rest,
|
||||
alertTypeId,
|
||||
muteAll,
|
||||
statusStartDate,
|
||||
statusEndDate,
|
||||
errorMessages,
|
||||
lastRun,
|
||||
instances: alerts,
|
||||
});
|
||||
|
||||
export async function loadAlertInstanceSummary({
|
||||
http,
|
||||
alertId,
|
||||
}: {
|
||||
http: HttpSetup;
|
||||
alertId: string;
|
||||
}): Promise<AlertInstanceSummary> {
|
||||
const res = await http.get(`${INTERNAL_BASE_ALERTING_API_PATH}/rule/${alertId}/_alert_summary`);
|
||||
return rewriteBodyRes(res);
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
import { AlertExecutionStatus } from '../../../../../alerting/common';
|
||||
import { AsApiContract, RewriteRequestCase } from '../../../../../actions/common';
|
||||
import { Alert, AlertAction } from '../../../types';
|
||||
|
||||
const transformAction: RewriteRequestCase<AlertAction> = ({
|
||||
group,
|
||||
id,
|
||||
connector_type_id: actionTypeId,
|
||||
params,
|
||||
}) => ({
|
||||
group,
|
||||
id,
|
||||
params,
|
||||
actionTypeId,
|
||||
});
|
||||
|
||||
const transformExecutionStatus: RewriteRequestCase<AlertExecutionStatus> = ({
|
||||
last_execution_date: lastExecutionDate,
|
||||
...rest
|
||||
}) => ({
|
||||
lastExecutionDate,
|
||||
...rest,
|
||||
});
|
||||
|
||||
export const transformAlert: RewriteRequestCase<Alert> = ({
|
||||
rule_type_id: alertTypeId,
|
||||
created_by: createdBy,
|
||||
updated_by: updatedBy,
|
||||
created_at: createdAt,
|
||||
updated_at: updatedAt,
|
||||
api_key_owner: apiKeyOwner,
|
||||
notify_when: notifyWhen,
|
||||
mute_all: muteAll,
|
||||
muted_alert_ids: mutedInstanceIds,
|
||||
scheduled_task_id: scheduledTaskId,
|
||||
execution_status: executionStatus,
|
||||
actions: actions,
|
||||
...rest
|
||||
}: any) => ({
|
||||
alertTypeId,
|
||||
createdBy,
|
||||
updatedBy,
|
||||
createdAt,
|
||||
updatedAt,
|
||||
apiKeyOwner,
|
||||
notifyWhen,
|
||||
muteAll,
|
||||
mutedInstanceIds,
|
||||
executionStatus: executionStatus ? transformExecutionStatus(executionStatus) : undefined,
|
||||
actions: actions
|
||||
? actions.map((action: AsApiContract<AlertAction>) => transformAction(action))
|
||||
: [],
|
||||
scheduledTaskId,
|
||||
...rest,
|
||||
});
|
|
@ -0,0 +1,140 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { httpServiceMock } from '../../../../../../../src/core/public/mocks';
|
||||
import { AlertUpdates } from '../../../types';
|
||||
import { createAlert } from './create';
|
||||
|
||||
const http = httpServiceMock.createStartContract();
|
||||
|
||||
describe('createAlert', () => {
|
||||
beforeEach(() => jest.resetAllMocks());
|
||||
|
||||
test('should call create alert API', async () => {
|
||||
const resolvedValue = {
|
||||
params: {
|
||||
aggType: 'count',
|
||||
termSize: 5,
|
||||
thresholdComparator: '>',
|
||||
timeWindowSize: 5,
|
||||
timeWindowUnit: 'm',
|
||||
groupBy: 'all',
|
||||
threshold: [1000],
|
||||
index: ['.kibana'],
|
||||
timeField: 'alert.executionStatus.lastExecutionDate',
|
||||
},
|
||||
consumer: 'alerts',
|
||||
schedule: { interval: '1m' },
|
||||
tags: [],
|
||||
name: 'test',
|
||||
rule_type_id: '.index-threshold',
|
||||
notify_when: 'onActionGroupChange',
|
||||
actions: [
|
||||
{
|
||||
group: 'threshold met',
|
||||
id: '1',
|
||||
params: {
|
||||
level: 'info',
|
||||
message: 'alert ',
|
||||
},
|
||||
connector_type_id: '.server-log',
|
||||
},
|
||||
],
|
||||
scheduled_task_id: '1',
|
||||
execution_status: { status: 'pending', last_execution_date: '2021-04-01T21:33:13.250Z' },
|
||||
create_at: '2021-04-01T21:33:13.247Z',
|
||||
updated_at: '2021-04-01T21:33:13.247Z',
|
||||
};
|
||||
const alertToCreate: Omit<
|
||||
AlertUpdates,
|
||||
'createdBy' | 'updatedBy' | 'muteAll' | 'mutedInstanceIds' | 'executionStatus'
|
||||
> = {
|
||||
params: {
|
||||
aggType: 'count',
|
||||
termSize: 5,
|
||||
thresholdComparator: '>',
|
||||
timeWindowSize: 5,
|
||||
timeWindowUnit: 'm',
|
||||
groupBy: 'all',
|
||||
threshold: [1000],
|
||||
index: ['.kibana'],
|
||||
timeField: 'alert.executionStatus.lastExecutionDate',
|
||||
},
|
||||
consumer: 'alerts',
|
||||
schedule: { interval: '1m' },
|
||||
tags: [],
|
||||
name: 'test',
|
||||
enabled: true,
|
||||
throttle: null,
|
||||
alertTypeId: '.index-threshold',
|
||||
notifyWhen: 'onActionGroupChange',
|
||||
actions: [
|
||||
{
|
||||
group: 'threshold met',
|
||||
id: '83d4d860-9316-11eb-a145-93ab369a4461',
|
||||
params: {
|
||||
level: 'info',
|
||||
message:
|
||||
"alert '{{alertName}}' is active for group '{{context.group}}':\n\n- Value: {{context.value}}\n- Conditions Met: {{context.conditions}} over {{params.timeWindowSize}}{{params.timeWindowUnit}}\n- Timestamp: {{context.date}}",
|
||||
},
|
||||
actionTypeId: '.server-log',
|
||||
},
|
||||
],
|
||||
createdAt: new Date('2021-04-01T21:33:13.247Z'),
|
||||
updatedAt: new Date('2021-04-01T21:33:13.247Z'),
|
||||
apiKeyOwner: '',
|
||||
};
|
||||
http.post.mockResolvedValueOnce(resolvedValue);
|
||||
|
||||
const result = await createAlert({ http, alert: alertToCreate });
|
||||
expect(result).toEqual({
|
||||
actions: [
|
||||
{
|
||||
actionTypeId: '.server-log',
|
||||
group: 'threshold met',
|
||||
id: '1',
|
||||
params: {
|
||||
level: 'info',
|
||||
message: 'alert ',
|
||||
},
|
||||
},
|
||||
],
|
||||
alertTypeId: '.index-threshold',
|
||||
apiKeyOwner: undefined,
|
||||
consumer: 'alerts',
|
||||
create_at: '2021-04-01T21:33:13.247Z',
|
||||
createdAt: undefined,
|
||||
createdBy: undefined,
|
||||
executionStatus: {
|
||||
lastExecutionDate: '2021-04-01T21:33:13.250Z',
|
||||
status: 'pending',
|
||||
},
|
||||
muteAll: undefined,
|
||||
mutedInstanceIds: undefined,
|
||||
name: 'test',
|
||||
notifyWhen: 'onActionGroupChange',
|
||||
params: {
|
||||
aggType: 'count',
|
||||
groupBy: 'all',
|
||||
index: ['.kibana'],
|
||||
termSize: 5,
|
||||
threshold: [1000],
|
||||
thresholdComparator: '>',
|
||||
timeField: 'alert.executionStatus.lastExecutionDate',
|
||||
timeWindowSize: 5,
|
||||
timeWindowUnit: 'm',
|
||||
},
|
||||
schedule: {
|
||||
interval: '1m',
|
||||
},
|
||||
scheduledTaskId: '1',
|
||||
tags: [],
|
||||
updatedAt: '2021-04-01T21:33:13.247Z',
|
||||
updatedBy: undefined,
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
import { HttpSetup } from 'kibana/public';
|
||||
import { RewriteResponseCase } from '../../../../../actions/common';
|
||||
import { Alert, AlertUpdates } from '../../../types';
|
||||
import { BASE_ALERTING_API_PATH } from '../../constants';
|
||||
import { transformAlert } from './common_transformations';
|
||||
|
||||
type AlertCreateBody = Omit<
|
||||
AlertUpdates,
|
||||
'createdBy' | 'updatedBy' | 'muteAll' | 'mutedInstanceIds' | 'executionStatus'
|
||||
>;
|
||||
const rewriteBodyRequest: RewriteResponseCase<AlertCreateBody> = ({
|
||||
alertTypeId,
|
||||
notifyWhen,
|
||||
actions,
|
||||
...res
|
||||
}): any => ({
|
||||
...res,
|
||||
rule_type_id: alertTypeId,
|
||||
notify_when: notifyWhen,
|
||||
actions: actions.map(({ group, id, params }) => ({
|
||||
group,
|
||||
id,
|
||||
params,
|
||||
})),
|
||||
});
|
||||
|
||||
export async function createAlert({
|
||||
http,
|
||||
alert,
|
||||
}: {
|
||||
http: HttpSetup;
|
||||
alert: AlertCreateBody;
|
||||
}): Promise<Alert> {
|
||||
const res = await http.post(`${BASE_ALERTING_API_PATH}/rule`, {
|
||||
body: JSON.stringify(rewriteBodyRequest(alert)),
|
||||
});
|
||||
return transformAlert(res);
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { httpServiceMock } from '../../../../../../../src/core/public/mocks';
|
||||
import { deleteAlerts } from './delete';
|
||||
|
||||
const http = httpServiceMock.createStartContract();
|
||||
|
||||
describe('deleteAlerts', () => {
|
||||
test('should call delete API for each alert', async () => {
|
||||
const ids = ['1', '2', '3'];
|
||||
const result = await deleteAlerts({ http, ids });
|
||||
expect(result).toEqual({ errors: [], successes: [undefined, undefined, undefined] });
|
||||
expect(http.delete.mock.calls).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
Array [
|
||||
"/api/alerting/rule/1",
|
||||
],
|
||||
Array [
|
||||
"/api/alerting/rule/2",
|
||||
],
|
||||
Array [
|
||||
"/api/alerting/rule/3",
|
||||
],
|
||||
]
|
||||
`);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
import { HttpSetup } from 'kibana/public';
|
||||
import { BASE_ALERTING_API_PATH } from '../../constants';
|
||||
|
||||
export async function deleteAlerts({
|
||||
ids,
|
||||
http,
|
||||
}: {
|
||||
ids: string[];
|
||||
http: HttpSetup;
|
||||
}): Promise<{ successes: string[]; errors: string[] }> {
|
||||
const successes: string[] = [];
|
||||
const errors: string[] = [];
|
||||
await Promise.all(ids.map((id) => http.delete(`${BASE_ALERTING_API_PATH}/rule/${id}`))).then(
|
||||
function (fulfilled) {
|
||||
successes.push(...fulfilled);
|
||||
},
|
||||
function (rejected) {
|
||||
errors.push(...rejected);
|
||||
}
|
||||
);
|
||||
return { successes, errors };
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { httpServiceMock } from '../../../../../../../src/core/public/mocks';
|
||||
import { disableAlert, disableAlerts } from './disable';
|
||||
|
||||
const http = httpServiceMock.createStartContract();
|
||||
beforeEach(() => jest.resetAllMocks());
|
||||
|
||||
describe('disableAlert', () => {
|
||||
test('should call disable alert API', async () => {
|
||||
const result = await disableAlert({ http, id: '1' });
|
||||
expect(result).toEqual(undefined);
|
||||
expect(http.post.mock.calls).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
Array [
|
||||
"/api/alerting/rule/1/_disable",
|
||||
],
|
||||
]
|
||||
`);
|
||||
});
|
||||
});
|
||||
|
||||
describe('disableAlerts', () => {
|
||||
test('should call disable alert API per alert', async () => {
|
||||
const ids = ['1', '2', '3'];
|
||||
const result = await disableAlerts({ http, ids });
|
||||
expect(result).toEqual(undefined);
|
||||
expect(http.post.mock.calls).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
Array [
|
||||
"/api/alerting/rule/1/_disable",
|
||||
],
|
||||
Array [
|
||||
"/api/alerting/rule/2/_disable",
|
||||
],
|
||||
Array [
|
||||
"/api/alerting/rule/3/_disable",
|
||||
],
|
||||
]
|
||||
`);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
import { HttpSetup } from 'kibana/public';
|
||||
import { BASE_ALERTING_API_PATH } from '../../constants';
|
||||
|
||||
export async function disableAlert({ id, http }: { id: string; http: HttpSetup }): Promise<void> {
|
||||
await http.post(`${BASE_ALERTING_API_PATH}/rule/${id}/_disable`);
|
||||
}
|
||||
|
||||
export async function disableAlerts({
|
||||
ids,
|
||||
http,
|
||||
}: {
|
||||
ids: string[];
|
||||
http: HttpSetup;
|
||||
}): Promise<void> {
|
||||
await Promise.all(ids.map((id) => disableAlert({ id, http })));
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { httpServiceMock } from '../../../../../../../src/core/public/mocks';
|
||||
import { enableAlert, enableAlerts } from './enable';
|
||||
|
||||
const http = httpServiceMock.createStartContract();
|
||||
beforeEach(() => jest.resetAllMocks());
|
||||
|
||||
describe('enableAlert', () => {
|
||||
test('should call enable alert API', async () => {
|
||||
const result = await enableAlert({ http, id: '1' });
|
||||
expect(result).toEqual(undefined);
|
||||
expect(http.post.mock.calls).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
Array [
|
||||
"/api/alerting/rule/1/_enable",
|
||||
],
|
||||
]
|
||||
`);
|
||||
});
|
||||
});
|
||||
|
||||
describe('enableAlerts', () => {
|
||||
test('should call enable alert API per alert', async () => {
|
||||
const ids = ['1', '2', '3'];
|
||||
const result = await enableAlerts({ http, ids });
|
||||
expect(result).toEqual(undefined);
|
||||
expect(http.post.mock.calls).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
Array [
|
||||
"/api/alerting/rule/1/_enable",
|
||||
],
|
||||
Array [
|
||||
"/api/alerting/rule/2/_enable",
|
||||
],
|
||||
Array [
|
||||
"/api/alerting/rule/3/_enable",
|
||||
],
|
||||
]
|
||||
`);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
import { HttpSetup } from 'kibana/public';
|
||||
import { BASE_ALERTING_API_PATH } from '../../constants';
|
||||
|
||||
export async function enableAlert({ id, http }: { id: string; http: HttpSetup }): Promise<void> {
|
||||
await http.post(`${BASE_ALERTING_API_PATH}/rule/${id}/_enable`);
|
||||
}
|
||||
|
||||
export async function enableAlerts({
|
||||
ids,
|
||||
http,
|
||||
}: {
|
||||
ids: string[];
|
||||
http: HttpSetup;
|
||||
}): Promise<void> {
|
||||
await Promise.all(ids.map((id) => enableAlert({ id, http })));
|
||||
}
|
|
@ -0,0 +1,99 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { httpServiceMock } from '../../../../../../../src/core/public/mocks';
|
||||
import { loadAlert } from './get_rule';
|
||||
import uuid from 'uuid';
|
||||
|
||||
const http = httpServiceMock.createStartContract();
|
||||
|
||||
describe('loadAlert', () => {
|
||||
test('should call get API with base parameters', async () => {
|
||||
const alertId = uuid.v4();
|
||||
const resolvedValue = {
|
||||
id: '1',
|
||||
params: {
|
||||
aggType: 'count',
|
||||
termSize: 5,
|
||||
thresholdComparator: '>',
|
||||
timeWindowSize: 5,
|
||||
timeWindowUnit: 'm',
|
||||
groupBy: 'all',
|
||||
threshold: [1000],
|
||||
index: ['.kibana'],
|
||||
timeField: 'canvas-element.@created',
|
||||
},
|
||||
consumer: 'alerts',
|
||||
schedule: { interval: '1m' },
|
||||
tags: ['sdfsdf'],
|
||||
name: 'dfsdfdsf',
|
||||
enabled: true,
|
||||
throttle: '1h',
|
||||
rule_type_id: '.index-threshold',
|
||||
created_by: 'elastic',
|
||||
updated_by: 'elastic',
|
||||
created_at: '2021-04-01T20:29:18.652Z',
|
||||
updated_at: '2021-04-01T20:33:38.260Z',
|
||||
api_key_owner: 'elastic',
|
||||
notify_when: 'onThrottleInterval',
|
||||
mute_all: false,
|
||||
muted_alert_ids: [],
|
||||
scheduled_task_id: '1',
|
||||
execution_status: { status: 'ok', last_execution_date: '2021-04-01T21:16:46.709Z' },
|
||||
actions: [
|
||||
{
|
||||
group: 'threshold met',
|
||||
id: '1',
|
||||
params: { documents: [{ dsfsdf: 1212 }] },
|
||||
connector_type_id: '.index',
|
||||
},
|
||||
],
|
||||
};
|
||||
http.get.mockResolvedValueOnce(resolvedValue);
|
||||
|
||||
expect(await loadAlert({ http, alertId })).toEqual({
|
||||
id: '1',
|
||||
params: {
|
||||
aggType: 'count',
|
||||
termSize: 5,
|
||||
thresholdComparator: '>',
|
||||
timeWindowSize: 5,
|
||||
timeWindowUnit: 'm',
|
||||
groupBy: 'all',
|
||||
threshold: [1000],
|
||||
index: ['.kibana'],
|
||||
timeField: 'canvas-element.@created',
|
||||
},
|
||||
consumer: 'alerts',
|
||||
schedule: { interval: '1m' },
|
||||
tags: ['sdfsdf'],
|
||||
name: 'dfsdfdsf',
|
||||
enabled: true,
|
||||
throttle: '1h',
|
||||
alertTypeId: '.index-threshold',
|
||||
createdBy: 'elastic',
|
||||
updatedBy: 'elastic',
|
||||
createdAt: '2021-04-01T20:29:18.652Z',
|
||||
updatedAt: '2021-04-01T20:33:38.260Z',
|
||||
apiKeyOwner: 'elastic',
|
||||
notifyWhen: 'onThrottleInterval',
|
||||
muteAll: false,
|
||||
mutedInstanceIds: [],
|
||||
scheduledTaskId: '1',
|
||||
executionStatus: { status: 'ok', lastExecutionDate: '2021-04-01T21:16:46.709Z' },
|
||||
actions: [
|
||||
{
|
||||
group: 'threshold met',
|
||||
id: '1',
|
||||
params: { documents: [{ dsfsdf: 1212 }] },
|
||||
actionTypeId: '.index',
|
||||
},
|
||||
],
|
||||
});
|
||||
expect(http.get).toHaveBeenCalledWith(`/api/alerting/rule/${alertId}`);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
import { HttpSetup } from 'kibana/public';
|
||||
import { Alert } from '../../../types';
|
||||
import { BASE_ALERTING_API_PATH } from '../../constants';
|
||||
import { transformAlert } from './common_transformations';
|
||||
|
||||
export async function loadAlert({
|
||||
http,
|
||||
alertId,
|
||||
}: {
|
||||
http: HttpSetup;
|
||||
alertId: string;
|
||||
}): Promise<Alert> {
|
||||
const res = await http.get(`${BASE_ALERTING_API_PATH}/rule/${alertId}`);
|
||||
return transformAlert(res);
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { httpServiceMock } from '../../../../../../../src/core/public/mocks';
|
||||
import { alertingFrameworkHealth } from './health';
|
||||
|
||||
describe('alertingFrameworkHealth', () => {
|
||||
const http = httpServiceMock.createStartContract();
|
||||
test('should call alertingFrameworkHealth API', async () => {
|
||||
http.get.mockResolvedValueOnce({
|
||||
is_sufficiently_secure: true,
|
||||
has_permanent_encryption_key: true,
|
||||
alerting_framework_heath: {
|
||||
decryption_health: { status: 'ok', timestamp: '2021-04-01T21:29:22.991Z' },
|
||||
execution_health: { status: 'ok', timestamp: '2021-04-01T21:29:22.991Z' },
|
||||
read_health: { status: 'ok', timestamp: '2021-04-01T21:29:22.991Z' },
|
||||
},
|
||||
});
|
||||
const result = await alertingFrameworkHealth({ http });
|
||||
expect(result).toEqual({
|
||||
alertingFrameworkHeath: {
|
||||
decryptionHealth: { status: 'ok', timestamp: '2021-04-01T21:29:22.991Z' },
|
||||
executionHealth: { status: 'ok', timestamp: '2021-04-01T21:29:22.991Z' },
|
||||
readHealth: { status: 'ok', timestamp: '2021-04-01T21:29:22.991Z' },
|
||||
},
|
||||
hasPermanentEncryptionKey: true,
|
||||
isSufficientlySecure: true,
|
||||
});
|
||||
expect(http.get.mock.calls).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
Array [
|
||||
"/api/alerting/_health",
|
||||
],
|
||||
]
|
||||
`);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
import { HttpSetup } from 'kibana/public';
|
||||
import { AsApiContract, RewriteRequestCase } from '../../../../../actions/common';
|
||||
import { AlertingFrameworkHealth, AlertsHealth } from '../../../../../alerting/common';
|
||||
import { BASE_ALERTING_API_PATH } from '../../constants';
|
||||
|
||||
const rewriteAlertingFrameworkHeath: RewriteRequestCase<AlertsHealth> = ({
|
||||
decryption_health: decryptionHealth,
|
||||
execution_health: executionHealth,
|
||||
read_health: readHealth,
|
||||
...res
|
||||
}: AsApiContract<AlertsHealth>) => ({
|
||||
decryptionHealth,
|
||||
executionHealth,
|
||||
readHealth,
|
||||
...res,
|
||||
});
|
||||
|
||||
const rewriteBodyRes: RewriteRequestCase<AlertingFrameworkHealth> = ({
|
||||
is_sufficiently_secure: isSufficientlySecure,
|
||||
has_permanent_encryption_key: hasPermanentEncryptionKey,
|
||||
alerting_framework_heath: alertingFrameworkHeath,
|
||||
...res
|
||||
}: AsApiContract<AlertingFrameworkHealth>) => ({
|
||||
isSufficientlySecure,
|
||||
hasPermanentEncryptionKey,
|
||||
alertingFrameworkHeath,
|
||||
...res,
|
||||
});
|
||||
|
||||
export async function alertingFrameworkHealth({
|
||||
http,
|
||||
}: {
|
||||
http: HttpSetup;
|
||||
}): Promise<AlertingFrameworkHealth> {
|
||||
const res = await http.get(`${BASE_ALERTING_API_PATH}/_health`);
|
||||
const alertingFrameworkHeath = rewriteAlertingFrameworkHeath(res.alerting_framework_heath);
|
||||
return { ...rewriteBodyRes(res), alertingFrameworkHeath };
|
||||
}
|
|
@ -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
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
export { alertingFrameworkHealth } from './health';
|
||||
export { mapFiltersToKql } from './map_filters_to_kql';
|
||||
export { loadAlertAggregations } from './aggregate';
|
||||
export { createAlert } from './create';
|
||||
export { deleteAlerts } from './delete';
|
||||
export { disableAlert, disableAlerts } from './disable';
|
||||
export { enableAlert, enableAlerts } from './enable';
|
||||
export { loadAlert } from './get_rule';
|
||||
export { loadAlertInstanceSummary } from './alert_summary';
|
||||
export { muteAlertInstance } from './mute_alert';
|
||||
export { muteAlert, muteAlerts } from './mute';
|
||||
export { loadAlertTypes } from './rule_types';
|
||||
export { loadAlerts } from './rules';
|
||||
export { loadAlertState } from './state';
|
||||
export { unmuteAlertInstance } from './unmute_alert';
|
||||
export { unmuteAlert, unmuteAlerts } from './unmute';
|
||||
export { updateAlert } from './update';
|
|
@ -0,0 +1,68 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { mapFiltersToKql } from './map_filters_to_kql';
|
||||
|
||||
describe('mapFiltersToKql', () => {
|
||||
beforeEach(() => jest.resetAllMocks());
|
||||
|
||||
test('should handle no filters', () => {
|
||||
expect(mapFiltersToKql({})).toEqual([]);
|
||||
});
|
||||
|
||||
test('should handle typesFilter', () => {
|
||||
expect(
|
||||
mapFiltersToKql({
|
||||
typesFilter: ['type', 'filter'],
|
||||
})
|
||||
).toEqual(['alert.attributes.alertTypeId:(type or filter)']);
|
||||
});
|
||||
|
||||
test('should handle actionTypesFilter', () => {
|
||||
expect(
|
||||
mapFiltersToKql({
|
||||
actionTypesFilter: ['action', 'types', 'filter'],
|
||||
})
|
||||
).toEqual([
|
||||
'(alert.attributes.actions:{ actionTypeId:action } OR alert.attributes.actions:{ actionTypeId:types } OR alert.attributes.actions:{ actionTypeId:filter })',
|
||||
]);
|
||||
});
|
||||
|
||||
test('should handle alertStatusesFilter', () => {
|
||||
expect(
|
||||
mapFiltersToKql({
|
||||
alertStatusesFilter: ['alert', 'statuses', 'filter'],
|
||||
})
|
||||
).toEqual(['alert.attributes.executionStatus.status:(alert or statuses or filter)']);
|
||||
});
|
||||
|
||||
test('should handle typesFilter and actionTypesFilter', () => {
|
||||
expect(
|
||||
mapFiltersToKql({
|
||||
typesFilter: ['type', 'filter'],
|
||||
actionTypesFilter: ['action', 'types', 'filter'],
|
||||
})
|
||||
).toEqual([
|
||||
'alert.attributes.alertTypeId:(type or filter)',
|
||||
'(alert.attributes.actions:{ actionTypeId:action } OR alert.attributes.actions:{ actionTypeId:types } OR alert.attributes.actions:{ actionTypeId:filter })',
|
||||
]);
|
||||
});
|
||||
|
||||
test('should handle typesFilter, actionTypesFilter and alertStatusesFilter', () => {
|
||||
expect(
|
||||
mapFiltersToKql({
|
||||
typesFilter: ['type', 'filter'],
|
||||
actionTypesFilter: ['action', 'types', 'filter'],
|
||||
alertStatusesFilter: ['alert', 'statuses', 'filter'],
|
||||
})
|
||||
).toEqual([
|
||||
'alert.attributes.alertTypeId:(type or filter)',
|
||||
'(alert.attributes.actions:{ actionTypeId:action } OR alert.attributes.actions:{ actionTypeId:types } OR alert.attributes.actions:{ actionTypeId:filter })',
|
||||
'alert.attributes.executionStatus.status:(alert or statuses or filter)',
|
||||
]);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
export const mapFiltersToKql = ({
|
||||
typesFilter,
|
||||
actionTypesFilter,
|
||||
alertStatusesFilter,
|
||||
}: {
|
||||
typesFilter?: string[];
|
||||
actionTypesFilter?: string[];
|
||||
alertStatusesFilter?: string[];
|
||||
}): string[] => {
|
||||
const filters = [];
|
||||
if (typesFilter && typesFilter.length) {
|
||||
filters.push(`alert.attributes.alertTypeId:(${typesFilter.join(' or ')})`);
|
||||
}
|
||||
if (actionTypesFilter && actionTypesFilter.length) {
|
||||
filters.push(
|
||||
[
|
||||
'(',
|
||||
actionTypesFilter
|
||||
.map((id) => `alert.attributes.actions:{ actionTypeId:${id} }`)
|
||||
.join(' OR '),
|
||||
')',
|
||||
].join('')
|
||||
);
|
||||
}
|
||||
if (alertStatusesFilter && alertStatusesFilter.length) {
|
||||
filters.push(`alert.attributes.executionStatus.status:(${alertStatusesFilter.join(' or ')})`);
|
||||
}
|
||||
return filters;
|
||||
};
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { httpServiceMock } from '../../../../../../../src/core/public/mocks';
|
||||
import { muteAlert, muteAlerts } from './mute';
|
||||
|
||||
const http = httpServiceMock.createStartContract();
|
||||
beforeEach(() => jest.resetAllMocks());
|
||||
|
||||
describe('muteAlert', () => {
|
||||
test('should call mute alert API', async () => {
|
||||
const result = await muteAlert({ http, id: '1' });
|
||||
expect(result).toEqual(undefined);
|
||||
expect(http.post.mock.calls).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
Array [
|
||||
"/api/alerting/rule/1/_mute_all",
|
||||
],
|
||||
]
|
||||
`);
|
||||
});
|
||||
});
|
||||
|
||||
describe('muteAlerts', () => {
|
||||
test('should call mute alert API per alert', async () => {
|
||||
const ids = ['1', '2', '3'];
|
||||
const result = await muteAlerts({ http, ids });
|
||||
expect(result).toEqual(undefined);
|
||||
expect(http.post.mock.calls).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
Array [
|
||||
"/api/alerting/rule/1/_mute_all",
|
||||
],
|
||||
Array [
|
||||
"/api/alerting/rule/2/_mute_all",
|
||||
],
|
||||
Array [
|
||||
"/api/alerting/rule/3/_mute_all",
|
||||
],
|
||||
]
|
||||
`);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,16 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
import { HttpSetup } from 'kibana/public';
|
||||
import { BASE_ALERTING_API_PATH } from '../../constants';
|
||||
|
||||
export async function muteAlert({ id, http }: { id: string; http: HttpSetup }): Promise<void> {
|
||||
await http.post(`${BASE_ALERTING_API_PATH}/rule/${id}/_mute_all`);
|
||||
}
|
||||
|
||||
export async function muteAlerts({ ids, http }: { ids: string[]; http: HttpSetup }): Promise<void> {
|
||||
await Promise.all(ids.map((id) => muteAlert({ http, id })));
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { httpServiceMock } from '../../../../../../../src/core/public/mocks';
|
||||
import { muteAlertInstance } from './mute_alert';
|
||||
|
||||
const http = httpServiceMock.createStartContract();
|
||||
|
||||
describe('muteAlertInstance', () => {
|
||||
test('should call mute instance alert API', async () => {
|
||||
const result = await muteAlertInstance({ http, id: '1', instanceId: '123' });
|
||||
expect(result).toEqual(undefined);
|
||||
expect(http.post.mock.calls).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
Array [
|
||||
"/api/alerting/rule/1/alert/123/_mute",
|
||||
],
|
||||
]
|
||||
`);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,20 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
import { HttpSetup } from 'kibana/public';
|
||||
import { BASE_ALERTING_API_PATH } from '../../constants';
|
||||
|
||||
export async function muteAlertInstance({
|
||||
id,
|
||||
instanceId,
|
||||
http,
|
||||
}: {
|
||||
id: string;
|
||||
instanceId: string;
|
||||
http: HttpSetup;
|
||||
}): Promise<void> {
|
||||
await http.post(`${BASE_ALERTING_API_PATH}/rule/${id}/alert/${instanceId}/_mute`);
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { AlertType } from '../../../types';
|
||||
import { httpServiceMock } from '../../../../../../../src/core/public/mocks';
|
||||
import { loadAlertTypes } from './rule_types';
|
||||
import { ALERTS_FEATURE_ID } from '../../../../../alerting/common';
|
||||
|
||||
const http = httpServiceMock.createStartContract();
|
||||
|
||||
describe('loadAlertTypes', () => {
|
||||
test('should call get alert types API', async () => {
|
||||
const resolvedValue: AlertType[] = [
|
||||
{
|
||||
id: 'test',
|
||||
name: 'Test',
|
||||
actionVariables: {
|
||||
context: [{ name: 'var1', description: 'val1' }],
|
||||
state: [{ name: 'var2', description: 'val2' }],
|
||||
params: [{ name: 'var3', description: 'val3' }],
|
||||
},
|
||||
producer: ALERTS_FEATURE_ID,
|
||||
actionGroups: [{ id: 'default', name: 'Default' }],
|
||||
recoveryActionGroup: { id: 'recovered', name: 'Recovered' },
|
||||
defaultActionGroupId: 'default',
|
||||
authorizedConsumers: {},
|
||||
minimumLicenseRequired: 'basic',
|
||||
enabledInLicense: true,
|
||||
},
|
||||
];
|
||||
http.get.mockResolvedValueOnce(resolvedValue);
|
||||
|
||||
const result = await loadAlertTypes({ http });
|
||||
expect(result).toEqual(resolvedValue);
|
||||
expect(http.get.mock.calls[0]).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"/api/alerting/rule_types",
|
||||
]
|
||||
`);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
import { HttpSetup } from 'kibana/public';
|
||||
import { AlertType } from '../../../types';
|
||||
import { BASE_ALERTING_API_PATH } from '../../constants';
|
||||
import { AsApiContract, RewriteRequestCase } from '../../../../../actions/common';
|
||||
|
||||
const rewriteResponseRes = (results: Array<AsApiContract<AlertType>>): AlertType[] => {
|
||||
return results.map((item) => rewriteBodyReq(item));
|
||||
};
|
||||
|
||||
const rewriteBodyReq: RewriteRequestCase<AlertType> = ({
|
||||
enabled_in_license: enabledInLicense,
|
||||
recovery_action_group: recoveryActionGroup,
|
||||
action_groups: actionGroups,
|
||||
default_action_group_id: defaultActionGroupId,
|
||||
minimum_license_required: minimumLicenseRequired,
|
||||
action_variables: actionVariables,
|
||||
authorized_consumers: authorizedConsumers,
|
||||
...rest
|
||||
}: AsApiContract<AlertType>) => ({
|
||||
enabledInLicense,
|
||||
recoveryActionGroup,
|
||||
actionGroups,
|
||||
defaultActionGroupId,
|
||||
minimumLicenseRequired,
|
||||
actionVariables,
|
||||
authorizedConsumers,
|
||||
...rest,
|
||||
});
|
||||
|
||||
export async function loadAlertTypes({ http }: { http: HttpSetup }): Promise<AlertType[]> {
|
||||
const res = await http.get(`${BASE_ALERTING_API_PATH}/rule_types`);
|
||||
return rewriteResponseRes(res);
|
||||
}
|
|
@ -0,0 +1,242 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
import { httpServiceMock } from '../../../../../../../src/core/public/mocks';
|
||||
import { loadAlerts } from './rules';
|
||||
|
||||
const http = httpServiceMock.createStartContract();
|
||||
|
||||
describe('loadAlerts', () => {
|
||||
beforeEach(() => jest.resetAllMocks());
|
||||
|
||||
test('should call find API with base parameters', async () => {
|
||||
const resolvedValue = {
|
||||
page: 1,
|
||||
per_page: 10,
|
||||
total: 0,
|
||||
data: [],
|
||||
};
|
||||
http.get.mockResolvedValueOnce(resolvedValue);
|
||||
|
||||
const result = await loadAlerts({ http, page: { index: 0, size: 10 } });
|
||||
expect(result).toEqual({
|
||||
page: 1,
|
||||
perPage: 10,
|
||||
total: 0,
|
||||
data: [],
|
||||
});
|
||||
expect(http.get.mock.calls[0]).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"/api/alerting/rules/_find",
|
||||
Object {
|
||||
"query": Object {
|
||||
"default_search_operator": "AND",
|
||||
"filter": undefined,
|
||||
"page": 1,
|
||||
"per_page": 10,
|
||||
"search": undefined,
|
||||
"search_fields": undefined,
|
||||
"sort_field": "name",
|
||||
"sort_order": "asc",
|
||||
},
|
||||
},
|
||||
]
|
||||
`);
|
||||
});
|
||||
|
||||
test('should call find API with searchText', async () => {
|
||||
const resolvedValue = {
|
||||
page: 1,
|
||||
per_page: 10,
|
||||
total: 0,
|
||||
data: [],
|
||||
};
|
||||
http.get.mockResolvedValueOnce(resolvedValue);
|
||||
|
||||
const result = await loadAlerts({ http, searchText: 'apples', page: { index: 0, size: 10 } });
|
||||
expect(result).toEqual({
|
||||
page: 1,
|
||||
perPage: 10,
|
||||
total: 0,
|
||||
data: [],
|
||||
});
|
||||
expect(http.get.mock.calls[0]).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"/api/alerting/rules/_find",
|
||||
Object {
|
||||
"query": Object {
|
||||
"default_search_operator": "AND",
|
||||
"filter": undefined,
|
||||
"page": 1,
|
||||
"per_page": 10,
|
||||
"search": "apples",
|
||||
"search_fields": "[\\"name\\",\\"tags\\"]",
|
||||
"sort_field": "name",
|
||||
"sort_order": "asc",
|
||||
},
|
||||
},
|
||||
]
|
||||
`);
|
||||
});
|
||||
|
||||
test('should call find API with actionTypesFilter', async () => {
|
||||
const resolvedValue = {
|
||||
page: 1,
|
||||
per_page: 10,
|
||||
total: 0,
|
||||
data: [],
|
||||
};
|
||||
http.get.mockResolvedValueOnce(resolvedValue);
|
||||
|
||||
const result = await loadAlerts({
|
||||
http,
|
||||
searchText: 'foo',
|
||||
page: { index: 0, size: 10 },
|
||||
});
|
||||
expect(result).toEqual({
|
||||
page: 1,
|
||||
perPage: 10,
|
||||
total: 0,
|
||||
data: [],
|
||||
});
|
||||
expect(http.get.mock.calls[0]).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"/api/alerting/rules/_find",
|
||||
Object {
|
||||
"query": Object {
|
||||
"default_search_operator": "AND",
|
||||
"filter": undefined,
|
||||
"page": 1,
|
||||
"per_page": 10,
|
||||
"search": "foo",
|
||||
"search_fields": "[\\"name\\",\\"tags\\"]",
|
||||
"sort_field": "name",
|
||||
"sort_order": "asc",
|
||||
},
|
||||
},
|
||||
]
|
||||
`);
|
||||
});
|
||||
|
||||
test('should call find API with typesFilter', async () => {
|
||||
const resolvedValue = {
|
||||
page: 1,
|
||||
per_page: 10,
|
||||
total: 0,
|
||||
data: [],
|
||||
};
|
||||
http.get.mockResolvedValueOnce(resolvedValue);
|
||||
|
||||
const result = await loadAlerts({
|
||||
http,
|
||||
typesFilter: ['foo', 'bar'],
|
||||
page: { index: 0, size: 10 },
|
||||
});
|
||||
expect(result).toEqual({
|
||||
page: 1,
|
||||
perPage: 10,
|
||||
total: 0,
|
||||
data: [],
|
||||
});
|
||||
expect(http.get.mock.calls[0]).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"/api/alerting/rules/_find",
|
||||
Object {
|
||||
"query": Object {
|
||||
"default_search_operator": "AND",
|
||||
"filter": "alert.attributes.alertTypeId:(foo or bar)",
|
||||
"page": 1,
|
||||
"per_page": 10,
|
||||
"search": undefined,
|
||||
"search_fields": undefined,
|
||||
"sort_field": "name",
|
||||
"sort_order": "asc",
|
||||
},
|
||||
},
|
||||
]
|
||||
`);
|
||||
});
|
||||
|
||||
test('should call find API with actionTypesFilter and typesFilter', async () => {
|
||||
const resolvedValue = {
|
||||
page: 1,
|
||||
per_page: 10,
|
||||
total: 0,
|
||||
data: [],
|
||||
};
|
||||
http.get.mockResolvedValueOnce(resolvedValue);
|
||||
|
||||
const result = await loadAlerts({
|
||||
http,
|
||||
searchText: 'baz',
|
||||
typesFilter: ['foo', 'bar'],
|
||||
page: { index: 0, size: 10 },
|
||||
});
|
||||
expect(result).toEqual({
|
||||
page: 1,
|
||||
perPage: 10,
|
||||
total: 0,
|
||||
data: [],
|
||||
});
|
||||
expect(http.get.mock.calls[0]).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"/api/alerting/rules/_find",
|
||||
Object {
|
||||
"query": Object {
|
||||
"default_search_operator": "AND",
|
||||
"filter": "alert.attributes.alertTypeId:(foo or bar)",
|
||||
"page": 1,
|
||||
"per_page": 10,
|
||||
"search": "baz",
|
||||
"search_fields": "[\\"name\\",\\"tags\\"]",
|
||||
"sort_field": "name",
|
||||
"sort_order": "asc",
|
||||
},
|
||||
},
|
||||
]
|
||||
`);
|
||||
});
|
||||
|
||||
test('should call find API with searchText and tagsFilter and typesFilter', async () => {
|
||||
const resolvedValue = {
|
||||
page: 1,
|
||||
per_page: 10,
|
||||
total: 0,
|
||||
data: [],
|
||||
};
|
||||
http.get.mockResolvedValueOnce(resolvedValue);
|
||||
|
||||
const result = await loadAlerts({
|
||||
http,
|
||||
searchText: 'apples, foo, baz',
|
||||
typesFilter: ['foo', 'bar'],
|
||||
page: { index: 0, size: 10 },
|
||||
});
|
||||
expect(result).toEqual({
|
||||
page: 1,
|
||||
perPage: 10,
|
||||
total: 0,
|
||||
data: [],
|
||||
});
|
||||
expect(http.get.mock.calls[0]).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"/api/alerting/rules/_find",
|
||||
Object {
|
||||
"query": Object {
|
||||
"default_search_operator": "AND",
|
||||
"filter": "alert.attributes.alertTypeId:(foo or bar)",
|
||||
"page": 1,
|
||||
"per_page": 10,
|
||||
"search": "apples, foo, baz",
|
||||
"search_fields": "[\\"name\\",\\"tags\\"]",
|
||||
"sort_field": "name",
|
||||
"sort_order": "asc",
|
||||
},
|
||||
},
|
||||
]
|
||||
`);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
import { HttpSetup } from 'kibana/public';
|
||||
import { BASE_ALERTING_API_PATH } from '../../constants';
|
||||
import { Alert, Pagination, Sorting } from '../../../types';
|
||||
import { AsApiContract } from '../../../../../actions/common';
|
||||
import { mapFiltersToKql } from './map_filters_to_kql';
|
||||
import { transformAlert } from './common_transformations';
|
||||
|
||||
const rewriteResponseRes = (results: Array<AsApiContract<Alert>>): Alert[] => {
|
||||
return results.map((item) => transformAlert(item));
|
||||
};
|
||||
|
||||
export async function loadAlerts({
|
||||
http,
|
||||
page,
|
||||
searchText,
|
||||
typesFilter,
|
||||
actionTypesFilter,
|
||||
alertStatusesFilter,
|
||||
sort = { field: 'name', direction: 'asc' },
|
||||
}: {
|
||||
http: HttpSetup;
|
||||
page: Pagination;
|
||||
searchText?: string;
|
||||
typesFilter?: string[];
|
||||
actionTypesFilter?: string[];
|
||||
alertStatusesFilter?: string[];
|
||||
sort?: Sorting;
|
||||
}): Promise<{
|
||||
page: number;
|
||||
perPage: number;
|
||||
total: number;
|
||||
data: Alert[];
|
||||
}> {
|
||||
const filters = mapFiltersToKql({ typesFilter, actionTypesFilter, alertStatusesFilter });
|
||||
const res = await http.get(`${BASE_ALERTING_API_PATH}/rules/_find`, {
|
||||
query: {
|
||||
page: page.index + 1,
|
||||
per_page: page.size,
|
||||
search_fields: searchText ? JSON.stringify(['name', 'tags']) : undefined,
|
||||
search: searchText,
|
||||
filter: filters.length ? filters.join(' and ') : undefined,
|
||||
default_search_operator: 'AND',
|
||||
sort_field: sort.field,
|
||||
sort_order: sort.direction,
|
||||
},
|
||||
});
|
||||
return {
|
||||
page: res.page,
|
||||
perPage: res.per_page,
|
||||
total: res.total,
|
||||
data: rewriteResponseRes(res.data),
|
||||
};
|
||||
}
|
|
@ -0,0 +1,101 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { httpServiceMock } from '../../../../../../../src/core/public/mocks';
|
||||
import { loadAlertState } from './state';
|
||||
import uuid from 'uuid';
|
||||
|
||||
const http = httpServiceMock.createStartContract();
|
||||
|
||||
describe('loadAlertState', () => {
|
||||
beforeEach(() => jest.resetAllMocks());
|
||||
|
||||
test('should call get API with base parameters', async () => {
|
||||
const alertId = uuid.v4();
|
||||
const resolvedValue = {
|
||||
alertTypeState: {
|
||||
some: 'value',
|
||||
},
|
||||
alertInstances: {
|
||||
first_instance: {},
|
||||
second_instance: {},
|
||||
},
|
||||
};
|
||||
http.get.mockResolvedValueOnce({
|
||||
rule_type_state: {
|
||||
some: 'value',
|
||||
},
|
||||
alerts: {
|
||||
first_instance: {},
|
||||
second_instance: {},
|
||||
},
|
||||
});
|
||||
|
||||
expect(await loadAlertState({ http, alertId })).toEqual(resolvedValue);
|
||||
expect(http.get).toHaveBeenCalledWith(`/internal/alerting/rule/${alertId}/state`);
|
||||
});
|
||||
|
||||
test('should parse AlertInstances', async () => {
|
||||
const alertId = uuid.v4();
|
||||
const resolvedValue = {
|
||||
alertTypeState: {
|
||||
some: 'value',
|
||||
},
|
||||
alertInstances: {
|
||||
first_instance: {
|
||||
state: {},
|
||||
meta: {
|
||||
lastScheduledActions: {
|
||||
group: 'first_group',
|
||||
date: '2020-02-09T23:15:41.941Z',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
http.get.mockResolvedValueOnce({
|
||||
rule_type_state: {
|
||||
some: 'value',
|
||||
},
|
||||
alerts: {
|
||||
first_instance: {
|
||||
state: {},
|
||||
meta: {
|
||||
lastScheduledActions: {
|
||||
group: 'first_group',
|
||||
date: '2020-02-09T23:15:41.941Z',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(await loadAlertState({ http, alertId })).toEqual({
|
||||
...resolvedValue,
|
||||
alertInstances: {
|
||||
first_instance: {
|
||||
state: {},
|
||||
meta: {
|
||||
lastScheduledActions: {
|
||||
group: 'first_group',
|
||||
date: new Date('2020-02-09T23:15:41.941Z'),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
expect(http.get).toHaveBeenCalledWith(`/internal/alerting/rule/${alertId}/state`);
|
||||
});
|
||||
|
||||
test('should handle empty response from api', async () => {
|
||||
const alertId = uuid.v4();
|
||||
http.get.mockResolvedValueOnce('');
|
||||
|
||||
expect(await loadAlertState({ http, alertId })).toEqual({});
|
||||
expect(http.get).toHaveBeenCalledWith(`/internal/alerting/rule/${alertId}/state`);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
import { HttpSetup } from 'kibana/public';
|
||||
import { pipe } from 'fp-ts/lib/pipeable';
|
||||
import { fold } from 'fp-ts/lib/Either';
|
||||
import { Errors, identity } from 'io-ts';
|
||||
import { AlertTaskState } from '../../../types';
|
||||
import { INTERNAL_BASE_ALERTING_API_PATH } from '../../constants';
|
||||
import { alertStateSchema } from '../../../../../alerting/common';
|
||||
import { AsApiContract, RewriteRequestCase } from '../../../../../actions/common';
|
||||
|
||||
const rewriteBodyRes: RewriteRequestCase<AlertTaskState> = ({
|
||||
rule_type_state: alertTypeState,
|
||||
alerts: alertInstances,
|
||||
previous_started_at: previousStartedAt,
|
||||
...rest
|
||||
}: any) => ({
|
||||
...rest,
|
||||
alertTypeState,
|
||||
alertInstances,
|
||||
previousStartedAt,
|
||||
});
|
||||
|
||||
type EmptyHttpResponse = '';
|
||||
export async function loadAlertState({
|
||||
http,
|
||||
alertId,
|
||||
}: {
|
||||
http: HttpSetup;
|
||||
alertId: string;
|
||||
}): Promise<AlertTaskState> {
|
||||
return await http
|
||||
.get(`${INTERNAL_BASE_ALERTING_API_PATH}/rule/${alertId}/state`)
|
||||
.then((state: AsApiContract<AlertTaskState> | EmptyHttpResponse) =>
|
||||
state ? rewriteBodyRes(state) : {}
|
||||
)
|
||||
.then((state: AlertTaskState) => {
|
||||
return pipe(
|
||||
alertStateSchema.decode(state),
|
||||
fold((e: Errors) => {
|
||||
throw new Error(`Alert "${alertId}" has invalid state`);
|
||||
}, identity)
|
||||
);
|
||||
});
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { httpServiceMock } from '../../../../../../../src/core/public/mocks';
|
||||
import { unmuteAlert, unmuteAlerts } from './unmute';
|
||||
|
||||
const http = httpServiceMock.createStartContract();
|
||||
beforeEach(() => jest.resetAllMocks());
|
||||
|
||||
describe('unmuteAlerts', () => {
|
||||
test('should call unmute alert API per alert', async () => {
|
||||
const ids = ['1', '2', '3'];
|
||||
const result = await unmuteAlerts({ http, ids });
|
||||
expect(result).toEqual(undefined);
|
||||
expect(http.post.mock.calls).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
Array [
|
||||
"/api/alerting/rule/1/_unmute_all",
|
||||
],
|
||||
Array [
|
||||
"/api/alerting/rule/2/_unmute_all",
|
||||
],
|
||||
Array [
|
||||
"/api/alerting/rule/3/_unmute_all",
|
||||
],
|
||||
]
|
||||
`);
|
||||
});
|
||||
});
|
||||
|
||||
describe('unmuteAlert', () => {
|
||||
test('should call unmute alert API', async () => {
|
||||
const result = await unmuteAlert({ http, id: '1' });
|
||||
expect(result).toEqual(undefined);
|
||||
expect(http.post.mock.calls).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
Array [
|
||||
"/api/alerting/rule/1/_unmute_all",
|
||||
],
|
||||
]
|
||||
`);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
import { HttpSetup } from 'kibana/public';
|
||||
import { BASE_ALERTING_API_PATH } from '../../constants';
|
||||
|
||||
export async function unmuteAlert({ id, http }: { id: string; http: HttpSetup }): Promise<void> {
|
||||
await http.post(`${BASE_ALERTING_API_PATH}/rule/${id}/_unmute_all`);
|
||||
}
|
||||
|
||||
export async function unmuteAlerts({
|
||||
ids,
|
||||
http,
|
||||
}: {
|
||||
ids: string[];
|
||||
http: HttpSetup;
|
||||
}): Promise<void> {
|
||||
await Promise.all(ids.map((id) => unmuteAlert({ id, http })));
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { httpServiceMock } from '../../../../../../../src/core/public/mocks';
|
||||
import { unmuteAlertInstance } from './unmute_alert';
|
||||
|
||||
const http = httpServiceMock.createStartContract();
|
||||
|
||||
describe('unmuteAlertInstance', () => {
|
||||
test('should call mute instance alert API', async () => {
|
||||
const result = await unmuteAlertInstance({ http, id: '1', instanceId: '123' });
|
||||
expect(result).toEqual(undefined);
|
||||
expect(http.post.mock.calls).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
Array [
|
||||
"/api/alerting/rule/1/alert/123/_unmute",
|
||||
],
|
||||
]
|
||||
`);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,20 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
import { HttpSetup } from 'kibana/public';
|
||||
import { BASE_ALERTING_API_PATH } from '../../constants';
|
||||
|
||||
export async function unmuteAlertInstance({
|
||||
id,
|
||||
instanceId,
|
||||
http,
|
||||
}: {
|
||||
id: string;
|
||||
instanceId: string;
|
||||
http: HttpSetup;
|
||||
}): Promise<void> {
|
||||
await http.post(`${BASE_ALERTING_API_PATH}/rule/${id}/alert/${instanceId}/_unmute`);
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { Alert } from '../../../types';
|
||||
import { httpServiceMock } from '../../../../../../../src/core/public/mocks';
|
||||
import { updateAlert } from './update';
|
||||
import { AlertNotifyWhenType } from '../../../../../alerting/common';
|
||||
|
||||
const http = httpServiceMock.createStartContract();
|
||||
|
||||
describe('updateAlert', () => {
|
||||
test('should call alert update API', async () => {
|
||||
const alertToUpdate = {
|
||||
throttle: '1m',
|
||||
consumer: 'alerts',
|
||||
name: 'test',
|
||||
tags: ['foo'],
|
||||
schedule: {
|
||||
interval: '1m',
|
||||
},
|
||||
params: {},
|
||||
actions: [],
|
||||
createdAt: new Date('1970-01-01T00:00:00.000Z'),
|
||||
updatedAt: new Date('1970-01-01T00:00:00.000Z'),
|
||||
apiKey: null,
|
||||
apiKeyOwner: null,
|
||||
notifyWhen: 'onThrottleInterval' as AlertNotifyWhenType,
|
||||
};
|
||||
const resolvedValue: Alert = {
|
||||
...alertToUpdate,
|
||||
id: '123',
|
||||
enabled: true,
|
||||
alertTypeId: 'test',
|
||||
createdBy: null,
|
||||
updatedBy: null,
|
||||
muteAll: false,
|
||||
mutedInstanceIds: [],
|
||||
executionStatus: {
|
||||
status: 'unknown',
|
||||
lastExecutionDate: new Date('2020-08-20T19:23:38Z'),
|
||||
},
|
||||
};
|
||||
http.put.mockResolvedValueOnce(resolvedValue);
|
||||
|
||||
const result = await updateAlert({ http, id: '123', alert: alertToUpdate });
|
||||
expect(result).toEqual(resolvedValue);
|
||||
expect(http.put.mock.calls[0]).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"/api/alerting/rule/123",
|
||||
Object {
|
||||
"body": "{\\"throttle\\":\\"1m\\",\\"name\\":\\"test\\",\\"tags\\":[\\"foo\\"],\\"schedule\\":{\\"interval\\":\\"1m\\"},\\"params\\":{},\\"notify_when\\":\\"onThrottleInterval\\",\\"actions\\":[]}",
|
||||
},
|
||||
]
|
||||
`);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
import { HttpSetup } from 'kibana/public';
|
||||
import { pick } from 'lodash';
|
||||
import { BASE_ALERTING_API_PATH } from '../../constants';
|
||||
import { Alert, AlertUpdates } from '../../../types';
|
||||
import { RewriteResponseCase } from '../../../../../actions/common';
|
||||
import { transformAlert } from './common_transformations';
|
||||
|
||||
type AlertUpdatesBody = Pick<
|
||||
AlertUpdates,
|
||||
'name' | 'tags' | 'schedule' | 'actions' | 'params' | 'throttle' | 'notifyWhen'
|
||||
>;
|
||||
const rewriteBodyRequest: RewriteResponseCase<AlertUpdatesBody> = ({
|
||||
notifyWhen,
|
||||
actions,
|
||||
...res
|
||||
}): any => ({
|
||||
...res,
|
||||
notify_when: notifyWhen,
|
||||
actions: actions.map(({ group, id, params }) => ({
|
||||
group,
|
||||
id,
|
||||
params,
|
||||
})),
|
||||
});
|
||||
|
||||
export async function updateAlert({
|
||||
http,
|
||||
alert,
|
||||
id,
|
||||
}: {
|
||||
http: HttpSetup;
|
||||
alert: Pick<
|
||||
AlertUpdates,
|
||||
'throttle' | 'name' | 'tags' | 'schedule' | 'params' | 'actions' | 'notifyWhen'
|
||||
>;
|
||||
id: string;
|
||||
}): Promise<Alert> {
|
||||
const res = await http.put(`${BASE_ALERTING_API_PATH}/rule/${id}`, {
|
||||
body: JSON.stringify(
|
||||
rewriteBodyRequest(
|
||||
pick(alert, ['throttle', 'name', 'tags', 'schedule', 'params', 'actions', 'notifyWhen'])
|
||||
)
|
||||
),
|
||||
});
|
||||
return transformAlert(res);
|
||||
}
|
Loading…
Reference in a new issue