Denormalize actionTypeId into alert actions for easier filtering (#51628)

* Denormalize actionTypeId for easier filtering of alerts

* Add tests

* No longer pass actionTypeId for each alert action in APIs

* Add tests to ensure denormalizeActions works on multiple actions

* Fix ESLint errors
This commit is contained in:
Mike Côté 2019-12-09 19:30:42 -05:00 committed by GitHub
parent de4269f8d4
commit ca5f6d78f1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 655 additions and 81 deletions

View file

@ -25,6 +25,9 @@
"actionRef": {
"type": "keyword"
},
"actionTypeId": {
"type": "keyword"
},
"params": {
"enabled": false,
"type": "object"

View file

@ -74,6 +74,18 @@ describe('create()', () => {
actionGroups: ['default'],
async executor() {},
});
savedObjectsClient.bulkGet.mockResolvedValueOnce({
saved_objects: [
{
id: '1',
type: 'action',
attributes: {
actionTypeId: 'test',
},
references: [],
},
],
});
savedObjectsClient.create.mockResolvedValueOnce({
id: '1',
type: 'alert',
@ -87,6 +99,7 @@ describe('create()', () => {
{
group: 'default',
actionRef: 'action_0',
actionTypeId: 'test',
params: {
foo: true,
},
@ -133,6 +146,7 @@ describe('create()', () => {
Object {
"actions": Array [
Object {
"actionTypeId": "test",
"group": "default",
"id": "1",
"params": Object {
@ -157,6 +171,7 @@ describe('create()', () => {
"actions": Array [
Object {
"actionRef": "action_0",
"actionTypeId": "test",
"group": "default",
"params": Object {
"foo": true,
@ -224,6 +239,184 @@ describe('create()', () => {
`);
});
test('creates an alert with multiple actions', async () => {
const alertsClient = new AlertsClient(alertsClientParams);
const data = getMockData({
actions: [
{
group: 'default',
id: '1',
params: {
foo: true,
},
},
{
group: 'default',
id: '1',
params: {
foo: true,
},
},
{
group: 'default',
id: '2',
params: {
foo: true,
},
},
],
});
alertTypeRegistry.get.mockReturnValueOnce({
id: '123',
name: 'Test',
actionGroups: ['default'],
async executor() {},
});
savedObjectsClient.bulkGet.mockResolvedValueOnce({
saved_objects: [
{
id: '1',
type: 'action',
attributes: {
actionTypeId: 'test',
},
references: [],
},
{
id: '2',
type: 'action',
attributes: {
actionTypeId: 'test2',
},
references: [],
},
],
});
savedObjectsClient.create.mockResolvedValueOnce({
id: '1',
type: 'alert',
attributes: {
alertTypeId: '123',
interval: '10s',
params: {
bar: true,
},
actions: [
{
group: 'default',
actionRef: 'action_0',
actionTypeId: 'test',
params: {
foo: true,
},
},
{
group: 'default',
actionRef: 'action_1',
actionTypeId: 'test',
params: {
foo: true,
},
},
{
group: 'default',
actionRef: 'action_2',
actionTypeId: 'test2',
params: {
foo: true,
},
},
],
},
references: [
{
name: 'action_0',
type: 'action',
id: '1',
},
{
name: 'action_1',
type: 'action',
id: '1',
},
{
name: 'action_2',
type: 'action',
id: '2',
},
],
});
taskManager.schedule.mockResolvedValueOnce({
id: 'task-123',
taskType: 'alerting:123',
scheduledAt: new Date(),
attempts: 1,
status: 'idle',
runAt: new Date(),
startedAt: null,
retryAt: null,
state: {},
params: {},
ownerId: null,
});
savedObjectsClient.update.mockResolvedValueOnce({
id: '1',
type: 'alert',
attributes: {
scheduledTaskId: 'task-123',
},
references: [],
});
const result = await alertsClient.create({ data });
expect(result).toMatchInlineSnapshot(`
Object {
"actions": Array [
Object {
"actionTypeId": "test",
"group": "default",
"id": "1",
"params": Object {
"foo": true,
},
},
Object {
"actionTypeId": "test",
"group": "default",
"id": "1",
"params": Object {
"foo": true,
},
},
Object {
"actionTypeId": "test2",
"group": "default",
"id": "2",
"params": Object {
"foo": true,
},
},
],
"alertTypeId": "123",
"id": "1",
"interval": "10s",
"params": Object {
"bar": true,
},
"scheduledTaskId": "task-123",
}
`);
expect(savedObjectsClient.bulkGet).toHaveBeenCalledWith([
{
id: '1',
type: 'action',
},
{
id: '2',
type: 'action',
},
]);
});
test('creates a disabled alert', async () => {
const alertsClient = new AlertsClient(alertsClientParams);
const data = getMockData({ enabled: false });
@ -233,6 +426,18 @@ describe('create()', () => {
actionGroups: ['default'],
async executor() {},
});
savedObjectsClient.bulkGet.mockResolvedValueOnce({
saved_objects: [
{
id: '1',
type: 'action',
attributes: {
actionTypeId: 'test',
},
references: [],
},
],
});
savedObjectsClient.create.mockResolvedValueOnce({
id: '1',
type: 'alert',
@ -247,6 +452,7 @@ describe('create()', () => {
{
group: 'default',
actionRef: 'action_0',
actionTypeId: 'test',
params: {
foo: true,
},
@ -266,6 +472,7 @@ describe('create()', () => {
Object {
"actions": Array [
Object {
"actionTypeId": "test",
"group": "default",
"id": "1",
"params": Object {
@ -306,6 +513,23 @@ describe('create()', () => {
);
});
test('throws error if loading actions fails', async () => {
const alertsClient = new AlertsClient(alertsClientParams);
const data = getMockData();
alertTypeRegistry.get.mockReturnValueOnce({
id: '123',
name: 'Test',
actionGroups: ['default'],
async executor() {},
});
savedObjectsClient.bulkGet.mockRejectedValueOnce(new Error('Test Error'));
await expect(alertsClient.create({ data })).rejects.toThrowErrorMatchingInlineSnapshot(
`"Test Error"`
);
expect(savedObjectsClient.create).not.toHaveBeenCalled();
expect(taskManager.schedule).not.toHaveBeenCalled();
});
test('throws error if create saved object fails', async () => {
const alertsClient = new AlertsClient(alertsClientParams);
const data = getMockData();
@ -315,6 +539,18 @@ describe('create()', () => {
actionGroups: ['default'],
async executor() {},
});
savedObjectsClient.bulkGet.mockResolvedValueOnce({
saved_objects: [
{
id: '1',
type: 'action',
attributes: {
actionTypeId: 'test',
},
references: [],
},
],
});
savedObjectsClient.create.mockRejectedValueOnce(new Error('Test failure'));
await expect(alertsClient.create({ data })).rejects.toThrowErrorMatchingInlineSnapshot(
`"Test failure"`
@ -331,6 +567,18 @@ describe('create()', () => {
actionGroups: ['default'],
async executor() {},
});
savedObjectsClient.bulkGet.mockResolvedValueOnce({
saved_objects: [
{
id: '1',
type: 'action',
attributes: {
actionTypeId: 'test',
},
references: [],
},
],
});
savedObjectsClient.create.mockResolvedValueOnce({
id: '1',
type: 'alert',
@ -344,6 +592,7 @@ describe('create()', () => {
{
group: 'default',
actionRef: 'action_0',
actionTypeId: 'test',
params: {
foo: true,
},
@ -381,6 +630,18 @@ describe('create()', () => {
actionGroups: ['default'],
async executor() {},
});
savedObjectsClient.bulkGet.mockResolvedValueOnce({
saved_objects: [
{
id: '1',
type: 'action',
attributes: {
actionTypeId: 'test',
},
references: [],
},
],
});
savedObjectsClient.create.mockResolvedValueOnce({
id: '1',
type: 'alert',
@ -394,6 +655,7 @@ describe('create()', () => {
{
group: 'default',
actionRef: 'action_0',
actionTypeId: 'test',
params: {
foo: true,
},
@ -442,6 +704,18 @@ describe('create()', () => {
created: true,
result: { id: '123', api_key: 'abc' },
});
savedObjectsClient.bulkGet.mockResolvedValueOnce({
saved_objects: [
{
id: '1',
type: 'action',
attributes: {
actionTypeId: 'test',
},
references: [],
},
],
});
savedObjectsClient.create.mockResolvedValueOnce({
id: '1',
type: 'alert',
@ -455,6 +729,7 @@ describe('create()', () => {
{
group: 'default',
actionRef: 'action_0',
actionTypeId: 'test',
params: {
foo: true,
},
@ -506,6 +781,7 @@ describe('create()', () => {
{
actionRef: 'action_0',
group: 'default',
actionTypeId: 'test',
params: { foo: true },
},
],
@ -1149,6 +1425,18 @@ describe('update()', () => {
references: [],
version: '123',
});
savedObjectsClient.bulkGet.mockResolvedValueOnce({
saved_objects: [
{
id: '1',
type: 'action',
attributes: {
actionTypeId: 'test',
},
references: [],
},
],
});
savedObjectsClient.update.mockResolvedValueOnce({
id: '1',
type: 'alert',
@ -1162,6 +1450,7 @@ describe('update()', () => {
{
group: 'default',
actionRef: 'action_0',
actionTypeId: 'test',
params: {
foo: true,
},
@ -1201,6 +1490,7 @@ describe('update()', () => {
Object {
"actions": Array [
Object {
"actionTypeId": "test",
"group": "default",
"id": "1",
"params": Object {
@ -1226,6 +1516,7 @@ describe('update()', () => {
"actions": Array [
Object {
"actionRef": "action_0",
"actionTypeId": "test",
"group": "default",
"params": Object {
"foo": true,
@ -1262,6 +1553,183 @@ describe('update()', () => {
`);
});
it('updates with multiple actions', async () => {
const alertsClient = new AlertsClient(alertsClientParams);
alertTypeRegistry.get.mockReturnValueOnce({
id: '123',
name: 'Test',
actionGroups: ['default'],
async executor() {},
});
savedObjectsClient.get.mockResolvedValueOnce({
id: '1',
type: 'alert',
attributes: {
enabled: true,
alertTypeId: '123',
scheduledTaskId: 'task-123',
},
references: [],
version: '123',
});
savedObjectsClient.bulkGet.mockResolvedValueOnce({
saved_objects: [
{
id: '1',
type: 'action',
attributes: {
actionTypeId: 'test',
},
references: [],
},
{
id: '2',
type: 'action',
attributes: {
actionTypeId: 'test2',
},
references: [],
},
],
});
savedObjectsClient.update.mockResolvedValueOnce({
id: '1',
type: 'alert',
attributes: {
enabled: true,
interval: '10s',
params: {
bar: true,
},
actions: [
{
group: 'default',
actionRef: 'action_0',
actionTypeId: 'test',
params: {
foo: true,
},
},
{
group: 'default',
actionRef: 'action_1',
actionTypeId: 'test',
params: {
foo: true,
},
},
{
group: 'default',
actionRef: 'action_2',
actionTypeId: 'test2',
params: {
foo: true,
},
},
],
scheduledTaskId: 'task-123',
},
references: [
{
name: 'action_0',
type: 'action',
id: '1',
},
{
name: 'action_1',
type: 'action',
id: '1',
},
{
name: 'action_2',
type: 'action',
id: '2',
},
],
});
const result = await alertsClient.update({
id: '1',
data: {
interval: '10s',
name: 'abc',
tags: ['foo'],
params: {
bar: true,
},
actions: [
{
group: 'default',
id: '1',
params: {
foo: true,
},
},
{
group: 'default',
id: '1',
params: {
foo: true,
},
},
{
group: 'default',
id: '2',
params: {
foo: true,
},
},
],
},
});
expect(result).toMatchInlineSnapshot(`
Object {
"actions": Array [
Object {
"actionTypeId": "test",
"group": "default",
"id": "1",
"params": Object {
"foo": true,
},
},
Object {
"actionTypeId": "test",
"group": "default",
"id": "1",
"params": Object {
"foo": true,
},
},
Object {
"actionTypeId": "test2",
"group": "default",
"id": "2",
"params": Object {
"foo": true,
},
},
],
"enabled": true,
"id": "1",
"interval": "10s",
"params": Object {
"bar": true,
},
"scheduledTaskId": "task-123",
}
`);
expect(savedObjectsClient.bulkGet).toHaveBeenCalledWith([
{
id: '1',
type: 'action',
},
{
id: '2',
type: 'action',
},
]);
});
it('calls the createApiKey function', async () => {
const alertsClient = new AlertsClient(alertsClientParams);
alertTypeRegistry.get.mockReturnValueOnce({
@ -1281,6 +1749,18 @@ describe('update()', () => {
references: [],
version: '123',
});
savedObjectsClient.bulkGet.mockResolvedValueOnce({
saved_objects: [
{
id: '1',
type: 'action',
attributes: {
actionTypeId: 'test',
},
references: [],
},
],
});
alertsClientParams.createAPIKey.mockResolvedValueOnce({
created: true,
result: { id: '123', api_key: 'abc' },
@ -1298,6 +1778,7 @@ describe('update()', () => {
{
group: 'default',
actionRef: 'action_0',
actionTypeId: 'test',
params: {
foo: true,
},
@ -1338,6 +1819,7 @@ describe('update()', () => {
Object {
"actions": Array [
Object {
"actionTypeId": "test",
"group": "default",
"id": "1",
"params": Object {
@ -1364,6 +1846,7 @@ describe('update()', () => {
"actions": Array [
Object {
"actionRef": "action_0",
"actionTypeId": "test",
"group": "default",
"params": Object {
"foo": true,

View file

@ -21,6 +21,7 @@ interface SuccessCreateAPIKeyResult {
result: SecurityPluginCreateAPIKeyResult;
}
export type CreateAPIKeyResult = FailedCreateAPIKeyResult | SuccessCreateAPIKeyResult;
type NormalizedAlertAction = Omit<AlertAction, 'actionTypeId'>;
interface ConstructorOptions {
logger: Logger;
@ -62,9 +63,15 @@ interface CreateOptions {
Alert,
Exclude<
keyof Alert,
'createdBy' | 'updatedBy' | 'apiKey' | 'apiKeyOwner' | 'muteAll' | 'mutedInstanceIds'
| 'createdBy'
| 'updatedBy'
| 'apiKey'
| 'apiKeyOwner'
| 'muteAll'
| 'mutedInstanceIds'
| 'actions'
>
>;
> & { actions: NormalizedAlertAction[] };
options?: {
migrationVersion?: Record<string, string>;
};
@ -76,7 +83,7 @@ interface UpdateOptions {
name: string;
tags: string[];
interval: string;
actions: AlertAction[];
actions: NormalizedAlertAction[];
params: Record<string, any>;
};
}
@ -117,8 +124,10 @@ export class AlertsClient {
this.validateActions(alertType, data.actions);
const { alert: rawAlert, references } = this.getRawAlert({
const { references, actions } = await this.denormalizeActions(data.actions);
const rawAlert: RawAlert = {
...data,
actions,
createdBy: username,
updatedBy: username,
apiKeyOwner: apiKey.created && username ? username : undefined,
@ -128,7 +137,7 @@ export class AlertsClient {
params: validatedAlertTypeParams,
muteAll: false,
mutedInstanceIds: [],
});
};
const createdAlert = await this.savedObjectsClient.create('alert', rawAlert, {
...options,
references,
@ -202,7 +211,7 @@ export class AlertsClient {
const validatedAlertTypeParams = validateAlertTypeParams(alertType, data.params);
this.validateActions(alertType, data.actions);
const { actions, references } = this.extractReferences(data.actions);
const { actions, references } = await this.denormalizeActions(data.actions);
const username = await this.getUserName();
const updatedObject = await this.savedObjectsClient.update(
'alert',
@ -371,26 +380,6 @@ export class AlertsClient {
});
}
private extractReferences(actions: Alert['actions']) {
const references: SavedObjectReference[] = [];
const rawActions = actions.map((action, i) => {
const actionRef = `action_${i}`;
references.push({
name: actionRef,
type: 'action',
id: action.id,
});
return {
...omit(action, 'id'),
actionRef,
};
}) as RawAlert['actions'];
return {
actions: rawActions,
references,
};
}
private injectReferencesIntoActions(
actions: RawAlert['actions'],
references: SavedObjectReference[]
@ -426,19 +415,7 @@ export class AlertsClient {
};
}
private getRawAlert(alert: Alert): { alert: RawAlert; references: SavedObjectReference[] } {
const { references, actions } = this.extractReferences(alert.actions);
return {
alert: {
...alert,
actions,
},
references,
};
}
private validateActions(alertType: AlertType, actions: Alert['actions']) {
// TODO: Should also ensure user has access to each action
private validateActions(alertType: AlertType, actions: NormalizedAlertAction[]): void {
const { actionGroups: alertTypeActionGroups } = alertType;
const usedAlertActionGroups = actions.map(action => action.group);
const invalidActionGroups = usedAlertActionGroups.filter(
@ -455,4 +432,41 @@ export class AlertsClient {
);
}
}
private async denormalizeActions(
alertActions: NormalizedAlertAction[]
): Promise<{ actions: RawAlert['actions']; references: SavedObjectReference[] }> {
// Fetch action objects in bulk
const actionIds = [...new Set(alertActions.map(alertAction => alertAction.id))];
const bulkGetOpts = actionIds.map(id => ({ id, type: 'action' }));
const bulkGetResult = await this.savedObjectsClient.bulkGet(bulkGetOpts);
const actionMap = new Map<string, any>();
for (const action of bulkGetResult.saved_objects) {
if (action.error) {
throw Boom.badRequest(
`Failed to load action ${action.id} (${action.error.statusCode}): ${action.error.message}`
);
}
actionMap.set(action.id, action);
}
// Extract references and set actionTypeId
const references: SavedObjectReference[] = [];
const actions = alertActions.map(({ id, ...alertAction }, i) => {
const actionRef = `action_${i}`;
references.push({
id,
name: actionRef,
type: 'action',
});
return {
...alertAction,
actionRef,
actionTypeId: actionMap.get(id).attributes.actionTypeId,
};
});
return {
actions,
references,
};
}
}

View file

@ -28,6 +28,7 @@ const createExecutionHandlerParams = {
{
id: '1',
group: 'default',
actionTypeId: 'test',
params: {
foo: true,
contextVal: 'My {{context.value}} goes here',

View file

@ -41,6 +41,12 @@ test('creates an alert with proper parameters', async () => {
alertsClient.create.mockResolvedValueOnce({
...mockedAlert,
id: '123',
actions: [
{
...mockedAlert.actions[0],
actionTypeId: 'test',
},
],
});
const { payload, statusCode } = await server.inject(request);
expect(statusCode).toBe(200);
@ -49,6 +55,7 @@ test('creates an alert with proper parameters', async () => {
Object {
"actions": Array [
Object {
"actionTypeId": "test",
"group": "default",
"id": "2",
"params": Object {
@ -97,33 +104,4 @@ test('creates an alert with proper parameters', async () => {
},
]
`);
expect(alertsClient.create).toHaveBeenCalledTimes(1);
expect(alertsClient.create.mock.calls[0]).toMatchInlineSnapshot(`
Array [
Object {
"data": Object {
"actions": Array [
Object {
"group": "default",
"id": "2",
"params": Object {
"foo": true,
},
},
],
"alertTypeId": "1",
"enabled": true,
"interval": "10s",
"name": "abc",
"params": Object {
"bar": true,
},
"tags": Array [
"foo",
],
"throttle": null,
},
},
]
`);
});

View file

@ -6,7 +6,6 @@
import Hapi from 'hapi';
import Joi from 'joi';
import { AlertAction } from '../types';
import { getDurationSchema } from '../lib';
interface ScheduleRequest extends Hapi.Request {
@ -16,7 +15,11 @@ interface ScheduleRequest extends Hapi.Request {
tags: string[];
alertTypeId: string;
interval: string;
actions: AlertAction[];
actions: Array<{
group: string;
id: string;
params: Record<string, any>;
}>;
params: Record<string, any>;
throttle: string | null;
};

View file

@ -21,6 +21,7 @@ const mockedAlert = {
{
group: 'default',
id: '2',
actionTypeId: 'test',
params: {
foo: true,
},

View file

@ -24,6 +24,7 @@ const mockedResponse = {
{
group: 'default',
id: '2',
actionTypeId: 'test',
params: {
baz: true,
},

View file

@ -6,7 +6,6 @@
import Joi from 'joi';
import Hapi from 'hapi';
import { AlertAction } from '../types';
import { getDurationSchema } from '../lib';
interface UpdateRequest extends Hapi.Request {
@ -18,7 +17,11 @@ interface UpdateRequest extends Hapi.Request {
name: string;
tags: string[];
interval: string;
actions: AlertAction[];
actions: Array<{
group: string;
id: string;
params: Record<string, any>;
}>;
params: Record<string, any>;
throttle: string | null;
};

View file

@ -49,12 +49,14 @@ export type AlertActionParams = SavedObjectAttributes;
export interface AlertAction {
group: string;
id: string;
actionTypeId: string;
params: AlertActionParams;
}
export interface RawAlertAction extends SavedObjectAttributes {
group: string;
actionRef: string;
actionTypeId: string;
params: AlertActionParams;
}

View file

@ -532,6 +532,7 @@ export default function alertTests({ getService }: FtrProviderContext) {
break;
case 'space_1_all at space1':
case 'superuser at space1':
expect(response.statusCode).to.eql(200);
// Wait for actions to execute twice before disabling the alert and waiting for tasks to finish
await esTestIndexTool.waitForDocs('action:test.index-record', reference, 2);
await alertUtils.disable(response.body.id);

View file

@ -31,11 +31,32 @@ export default function createAlertTests({ getService }: FtrProviderContext) {
const { user, space } = scenario;
describe(scenario.id, () => {
it('should handle create alert request appropriately', async () => {
const { body: createdAction } = await supertest
.post(`${getUrlPrefix(space.id)}/api/action`)
.set('kbn-xsrf', 'foo')
.send({
name: 'MY action',
actionTypeId: 'test.noop',
config: {},
secrets: {},
})
.expect(200);
const response = await supertestWithoutAuth
.post(`${getUrlPrefix(space.id)}/api/alert`)
.set('kbn-xsrf', 'foo')
.auth(user.username, user.password)
.send(getTestAlertData());
.send(
getTestAlertData({
actions: [
{
id: createdAction.id,
group: 'default',
params: {},
},
],
})
);
switch (scenario.id) {
case 'no_kibana_privileges at space1':
@ -56,7 +77,14 @@ export default function createAlertTests({ getService }: FtrProviderContext) {
id: response.body.id,
name: 'abc',
tags: ['foo'],
actions: [],
actions: [
{
id: createdAction.id,
actionTypeId: createdAction.actionTypeId,
group: 'default',
params: {},
},
],
enabled: true,
alertTypeId: 'test.noop',
params: {},

View file

@ -78,10 +78,32 @@ export default function createFindTests({ getService }: FtrProviderContext) {
});
it('should handle find alert request with filter appropriately', async () => {
const { body: createdAction } = await supertest
.post(`${getUrlPrefix(space.id)}/api/action`)
.set('kbn-xsrf', 'foo')
.send({
name: 'My action',
actionTypeId: 'test.noop',
config: {},
secrets: {},
})
.expect(200);
const { body: createdAlert } = await supertest
.post(`${getUrlPrefix(space.id)}/api/alert`)
.set('kbn-xsrf', 'foo')
.send(getTestAlertData())
.send(
getTestAlertData({
enabled: false,
actions: [
{
id: createdAction.id,
group: 'default',
params: {},
},
],
})
)
.expect(200);
objectRemover.add(space.id, createdAlert.id, 'alert');
@ -89,7 +111,7 @@ export default function createFindTests({ getService }: FtrProviderContext) {
.get(
`${getUrlPrefix(
space.id
)}/api/alert/_find?filter=alert.attributes.alertTypeId:test.noop`
)}/api/alert/_find?filter=alert.attributes.actions:{ actionTypeId: test.noop }`
)
.auth(user.username, user.password);
@ -117,11 +139,17 @@ export default function createFindTests({ getService }: FtrProviderContext) {
tags: ['foo'],
alertTypeId: 'test.noop',
interval: '1m',
enabled: true,
actions: [],
enabled: false,
actions: [
{
id: createdAction.id,
group: 'default',
actionTypeId: 'test.noop',
params: {},
},
],
params: {},
createdBy: 'elastic',
scheduledTaskId: match.scheduledTaskId,
throttle: '1m',
updatedBy: 'elastic',
apiKeyOwner: 'elastic',

View file

@ -27,10 +27,31 @@ export default function createAlertTests({ getService }: FtrProviderContext) {
}
it('should handle create alert request appropriately', async () => {
const { body: createdAction } = await supertest
.post(`${getUrlPrefix(Spaces.space1.id)}/api/action`)
.set('kbn-xsrf', 'foo')
.send({
name: 'MY action',
actionTypeId: 'test.noop',
config: {},
secrets: {},
})
.expect(200);
const response = await supertest
.post(`${getUrlPrefix(Spaces.space1.id)}/api/alert`)
.set('kbn-xsrf', 'foo')
.send(getTestAlertData());
.send(
getTestAlertData({
actions: [
{
id: createdAction.id,
group: 'default',
params: {},
},
],
})
);
expect(response.statusCode).to.eql(200);
objectRemover.add(Spaces.space1.id, response.body.id, 'alert');
@ -38,7 +59,14 @@ export default function createAlertTests({ getService }: FtrProviderContext) {
id: response.body.id,
name: 'abc',
tags: ['foo'],
actions: [],
actions: [
{
id: createdAction.id,
actionTypeId: createdAction.actionTypeId,
group: 'default',
params: {},
},
],
enabled: true,
alertTypeId: 'test.noop',
params: {},