diff --git a/x-pack/legacy/plugins/actions/README.md b/x-pack/legacy/plugins/actions/README.md index 3ac4741b6f061..f17456d6ea393 100644 --- a/x-pack/legacy/plugins/actions/README.md +++ b/x-pack/legacy/plugins/actions/README.md @@ -145,8 +145,7 @@ The following table describes the properties of the `options` object. |---|---|---| |id|The id of the action you want to fire.|string| |params|The `params` value to give the action type executor.|object| -|namespace|The saved object namespace the action exists within.|string| -|basePath|This is a temporary parameter, but we need to capture and track the value of `request.getBasePath()` until future changes are made.

In most cases this can be `undefined` unless you need cross spaces support.|string| +|spaceId|The space id the action is within.|string| ### Example @@ -155,14 +154,13 @@ This example makes action `3c5b2bd4-5424-4e4b-8cf5-c0a58c762cc5` fire an email. ``` server.plugins.actions.fire({ id: '3c5b2bd4-5424-4e4b-8cf5-c0a58c762cc5', + spaceId: 'default', // The spaceId of the action params: { from: 'example@elastic.co', to: ['destination@elastic.co'], subject: 'My email subject', body: 'My email body', }, - namespace: undefined, // The namespace the action exists within - basePath: undefined, // Usually `request.getBasePath();` or `undefined` }); ``` diff --git a/x-pack/legacy/plugins/actions/server/action_type_registry.test.ts b/x-pack/legacy/plugins/actions/server/action_type_registry.test.ts index 7e181ff1ca4ef..dcb8b8b299d1a 100644 --- a/x-pack/legacy/plugins/actions/server/action_type_registry.test.ts +++ b/x-pack/legacy/plugins/actions/server/action_type_registry.test.ts @@ -28,6 +28,8 @@ const actionTypeRegistryParams = { getServices, taskManager: mockTaskManager, encryptedSavedObjectsPlugin: encryptedSavedObjectsMock.create(), + spaceIdToNamespace: jest.fn().mockReturnValue(undefined), + getBasePath: jest.fn().mockReturnValue(undefined), }; beforeEach(() => jest.resetAllMocks()); diff --git a/x-pack/legacy/plugins/actions/server/action_type_registry.ts b/x-pack/legacy/plugins/actions/server/action_type_registry.ts index 72ba36c402bf9..635d618878260 100644 --- a/x-pack/legacy/plugins/actions/server/action_type_registry.ts +++ b/x-pack/legacy/plugins/actions/server/action_type_registry.ts @@ -10,28 +10,35 @@ import { ActionType, GetServicesFunction } from './types'; import { TaskManager, TaskRunCreatorFunction } from '../../task_manager'; import { getCreateTaskRunnerFunction, ExecutorError } from './lib'; import { EncryptedSavedObjectsPlugin } from '../../encrypted_saved_objects'; +import { SpacesPlugin } from '../../spaces'; interface ConstructorOptions { taskManager: TaskManager; getServices: GetServicesFunction; encryptedSavedObjectsPlugin: EncryptedSavedObjectsPlugin; + spaceIdToNamespace: SpacesPlugin['spaceIdToNamespace']; + getBasePath: SpacesPlugin['getBasePath']; } export class ActionTypeRegistry { private readonly taskRunCreatorFunction: TaskRunCreatorFunction; - private readonly getServices: GetServicesFunction; private readonly taskManager: TaskManager; private readonly actionTypes: Map = new Map(); - private readonly encryptedSavedObjectsPlugin: EncryptedSavedObjectsPlugin; - constructor({ getServices, taskManager, encryptedSavedObjectsPlugin }: ConstructorOptions) { - this.getServices = getServices; + constructor({ + getServices, + taskManager, + encryptedSavedObjectsPlugin, + spaceIdToNamespace, + getBasePath, + }: ConstructorOptions) { this.taskManager = taskManager; - this.encryptedSavedObjectsPlugin = encryptedSavedObjectsPlugin; this.taskRunCreatorFunction = getCreateTaskRunnerFunction({ + getServices, actionTypeRegistry: this, - getServices: this.getServices, - encryptedSavedObjectsPlugin: this.encryptedSavedObjectsPlugin, + encryptedSavedObjectsPlugin, + spaceIdToNamespace, + getBasePath, }); } diff --git a/x-pack/legacy/plugins/actions/server/actions_client.test.ts b/x-pack/legacy/plugins/actions/server/actions_client.test.ts index 25a26f2093267..d724c28d72ea5 100644 --- a/x-pack/legacy/plugins/actions/server/actions_client.test.ts +++ b/x-pack/legacy/plugins/actions/server/actions_client.test.ts @@ -10,16 +10,14 @@ import { ActionTypeRegistry } from './action_type_registry'; import { ActionsClient } from './actions_client'; import { ExecutorType } from './types'; import { taskManagerMock } from '../../task_manager/task_manager.mock'; -import { EncryptedSavedObjectsPlugin } from '../../encrypted_saved_objects'; import { SavedObjectsClientMock } from '../../../../../src/core/server/mocks'; +import { encryptedSavedObjectsMock } from '../../encrypted_saved_objects/server/plugin.mock'; const savedObjectsClient = SavedObjectsClientMock.create(); const mockTaskManager = taskManagerMock.create(); -const mockEncryptedSavedObjectsPlugin = { - getDecryptedAsInternalUser: jest.fn() as EncryptedSavedObjectsPlugin['getDecryptedAsInternalUser'], -} as EncryptedSavedObjectsPlugin; +const mockEncryptedSavedObjectsPlugin = encryptedSavedObjectsMock.create(); function getServices() { return { @@ -33,6 +31,8 @@ const actionTypeRegistryParams = { getServices, taskManager: mockTaskManager, encryptedSavedObjectsPlugin: mockEncryptedSavedObjectsPlugin, + spaceIdToNamespace: jest.fn().mockReturnValue(undefined), + getBasePath: jest.fn().mockReturnValue(undefined), }; const executor: ExecutorType = async options => { diff --git a/x-pack/legacy/plugins/actions/server/builtin_action_types/email.test.ts b/x-pack/legacy/plugins/actions/server/builtin_action_types/email.test.ts index a8416f469ce1c..7ebb1ff943750 100644 --- a/x-pack/legacy/plugins/actions/server/builtin_action_types/email.test.ts +++ b/x-pack/legacy/plugins/actions/server/builtin_action_types/email.test.ts @@ -43,6 +43,8 @@ beforeAll(() => { getServices, taskManager: taskManagerMock.create(), encryptedSavedObjectsPlugin: mockEncryptedSavedObjectsPlugin, + spaceIdToNamespace: jest.fn().mockReturnValue(undefined), + getBasePath: jest.fn().mockReturnValue(undefined), }); registerBuiltInActionTypes(actionTypeRegistry); diff --git a/x-pack/legacy/plugins/actions/server/builtin_action_types/es_index.test.ts b/x-pack/legacy/plugins/actions/server/builtin_action_types/es_index.test.ts index 03829815c0bdf..301f6613bcfa6 100644 --- a/x-pack/legacy/plugins/actions/server/builtin_action_types/es_index.test.ts +++ b/x-pack/legacy/plugins/actions/server/builtin_action_types/es_index.test.ts @@ -40,6 +40,8 @@ beforeAll(() => { getServices, taskManager: taskManagerMock.create(), encryptedSavedObjectsPlugin: mockEncryptedSavedObjectsPlugin, + spaceIdToNamespace: jest.fn().mockReturnValue(undefined), + getBasePath: jest.fn().mockReturnValue(undefined), }); registerBuiltInActionTypes(actionTypeRegistry); diff --git a/x-pack/legacy/plugins/actions/server/builtin_action_types/server_log.test.ts b/x-pack/legacy/plugins/actions/server/builtin_action_types/server_log.test.ts index a852752784a3c..ef38c5b910627 100644 --- a/x-pack/legacy/plugins/actions/server/builtin_action_types/server_log.test.ts +++ b/x-pack/legacy/plugins/actions/server/builtin_action_types/server_log.test.ts @@ -35,6 +35,8 @@ beforeAll(() => { getServices, taskManager: taskManagerMock.create(), encryptedSavedObjectsPlugin: mockEncryptedSavedObjectsPlugin, + spaceIdToNamespace: jest.fn().mockReturnValue(undefined), + getBasePath: jest.fn().mockReturnValue(undefined), }); registerBuiltInActionTypes(actionTypeRegistry); }); diff --git a/x-pack/legacy/plugins/actions/server/builtin_action_types/slack.test.ts b/x-pack/legacy/plugins/actions/server/builtin_action_types/slack.test.ts index 71e307721e51a..c2cc2d04d4f37 100644 --- a/x-pack/legacy/plugins/actions/server/builtin_action_types/slack.test.ts +++ b/x-pack/legacy/plugins/actions/server/builtin_action_types/slack.test.ts @@ -52,6 +52,8 @@ beforeAll(() => { getServices, taskManager: taskManagerMock.create(), encryptedSavedObjectsPlugin: mockEncryptedSavedObjectsPlugin, + spaceIdToNamespace: jest.fn().mockReturnValue(undefined), + getBasePath: jest.fn().mockReturnValue(undefined), }); actionTypeRegistry.register(getActionType({ executor: mockSlackExecutor })); actionType = actionTypeRegistry.get(ACTION_TYPE_ID); diff --git a/x-pack/legacy/plugins/actions/server/create_fire_function.test.ts b/x-pack/legacy/plugins/actions/server/create_fire_function.test.ts index 2f56a7f7a943e..1f286d371e633 100644 --- a/x-pack/legacy/plugins/actions/server/create_fire_function.test.ts +++ b/x-pack/legacy/plugins/actions/server/create_fire_function.test.ts @@ -10,6 +10,7 @@ import { SavedObjectsClientMock } from '../../../../../src/core/server/mocks'; const mockTaskManager = taskManagerMock.create(); const savedObjectsClient = SavedObjectsClientMock.create(); +const spaceIdToNamespace = jest.fn(); beforeEach(() => jest.resetAllMocks()); @@ -18,6 +19,7 @@ describe('fire()', () => { const fireFn = createFireFunction({ taskManager: mockTaskManager, internalSavedObjectsRepository: savedObjectsClient, + spaceIdToNamespace, }); savedObjectsClient.get.mockResolvedValueOnce({ id: '123', @@ -27,41 +29,41 @@ describe('fire()', () => { }, references: [], }); + spaceIdToNamespace.mockReturnValueOnce('namespace1'); await fireFn({ id: '123', params: { baz: false }, - namespace: 'abc', - basePath: '/s/default', + spaceId: 'default', }); expect(mockTaskManager.schedule).toHaveBeenCalledTimes(1); expect(mockTaskManager.schedule.mock.calls[0]).toMatchInlineSnapshot(` -Array [ - Object { - "params": Object { - "basePath": "/s/default", - "id": "123", - "namespace": "abc", - "params": Object { - "baz": false, - }, - }, - "scope": Array [ - "actions", - ], - "state": Object {}, - "taskType": "actions:mock-action", - }, -] -`); + Array [ + Object { + "params": Object { + "id": "123", + "params": Object { + "baz": false, + }, + "spaceId": "default", + }, + "scope": Array [ + "actions", + ], + "state": Object {}, + "taskType": "actions:mock-action", + }, + ] + `); expect(savedObjectsClient.get).toHaveBeenCalledTimes(1); expect(savedObjectsClient.get.mock.calls[0]).toMatchInlineSnapshot(` -Array [ - "action", - "123", - Object { - "namespace": "abc", - }, -] -`); + Array [ + "action", + "123", + Object { + "namespace": "namespace1", + }, + ] + `); + expect(spaceIdToNamespace).toHaveBeenCalledWith('default'); }); }); diff --git a/x-pack/legacy/plugins/actions/server/create_fire_function.ts b/x-pack/legacy/plugins/actions/server/create_fire_function.ts index a00744aa329c3..0bf11cbf5dd40 100644 --- a/x-pack/legacy/plugins/actions/server/create_fire_function.ts +++ b/x-pack/legacy/plugins/actions/server/create_fire_function.ts @@ -6,31 +6,33 @@ import { SavedObjectsClientContract } from 'src/core/server'; import { TaskManager } from '../../task_manager'; +import { SpacesPlugin } from '../../spaces'; interface CreateFireFunctionOptions { taskManager: TaskManager; internalSavedObjectsRepository: SavedObjectsClientContract; + spaceIdToNamespace: SpacesPlugin['spaceIdToNamespace']; } -interface FireOptions { +export interface FireOptions { id: string; params: Record; - namespace?: string; - basePath: string; + spaceId: string; } export function createFireFunction({ taskManager, internalSavedObjectsRepository, + spaceIdToNamespace, }: CreateFireFunctionOptions) { - return async function fire({ id, params, namespace, basePath }: FireOptions) { + return async function fire({ id, params, spaceId }: FireOptions) { + const namespace = spaceIdToNamespace(spaceId); const actionSavedObject = await internalSavedObjectsRepository.get('action', id, { namespace }); await taskManager.schedule({ taskType: `actions:${actionSavedObject.attributes.actionTypeId}`, params: { id, - basePath, - namespace, + spaceId, params, }, state: {}, diff --git a/x-pack/legacy/plugins/actions/server/init.ts b/x-pack/legacy/plugins/actions/server/init.ts index c046979aca0a9..27cff53bb97d1 100644 --- a/x-pack/legacy/plugins/actions/server/init.ts +++ b/x-pack/legacy/plugins/actions/server/init.ts @@ -18,10 +18,19 @@ import { listActionTypesRoute, fireRoute, } from './routes'; - import { registerBuiltInActionTypes } from './builtin_action_types'; +import { SpacesPlugin } from '../../spaces'; +import { createOptionalPlugin } from '../../../server/lib/optional_plugin'; export function init(server: Legacy.Server) { + const config = server.config(); + const spaces = createOptionalPlugin( + config, + 'xpack.spaces', + server.plugins, + 'spaces' + ); + const { callWithInternalUser } = server.plugins.elasticsearch.getCluster('admin'); const savedObjectsRepositoryWithInternalUser = server.savedObjects.getSavedObjectsRepository( callWithInternalUser @@ -58,6 +67,14 @@ export function init(server: Legacy.Server) { getServices, taskManager: taskManager!, encryptedSavedObjectsPlugin: server.plugins.encrypted_saved_objects!, + getBasePath(...args) { + return spaces.isEnabled + ? spaces.getBasePath(...args) + : server.config().get('server.basePath'); + }, + spaceIdToNamespace(...args) { + return spaces.isEnabled ? spaces.spaceIdToNamespace(...args) : undefined; + }, }); registerBuiltInActionTypes(actionTypeRegistry); @@ -78,6 +95,9 @@ export function init(server: Legacy.Server) { const fireFn = createFireFunction({ taskManager: taskManager!, internalSavedObjectsRepository: savedObjectsRepositoryWithInternalUser, + spaceIdToNamespace(...args) { + return spaces.isEnabled ? spaces.spaceIdToNamespace(...args) : undefined; + }, }); // Expose functions to server diff --git a/x-pack/legacy/plugins/actions/server/lib/execute.ts b/x-pack/legacy/plugins/actions/server/lib/execute.ts index 04e4ae156d337..95c4f17dd922e 100644 --- a/x-pack/legacy/plugins/actions/server/lib/execute.ts +++ b/x-pack/legacy/plugins/actions/server/lib/execute.ts @@ -10,7 +10,7 @@ import { EncryptedSavedObjectsPlugin } from '../../../encrypted_saved_objects'; interface ExecuteOptions { actionId: string; - namespace: string; + namespace?: string; services: Services; params: Record; encryptedSavedObjectsPlugin: EncryptedSavedObjectsPlugin; diff --git a/x-pack/legacy/plugins/actions/server/lib/get_create_task_runner_function.test.ts b/x-pack/legacy/plugins/actions/server/lib/get_create_task_runner_function.test.ts index e870330e95e2f..4f732afc3ec7f 100644 --- a/x-pack/legacy/plugins/actions/server/lib/get_create_task_runner_function.test.ts +++ b/x-pack/legacy/plugins/actions/server/lib/get_create_task_runner_function.test.ts @@ -14,6 +14,7 @@ import { SavedObjectsClientMock } from '../../../../../../src/core/server/mocks' import { actionTypeRegistryMock } from '../action_type_registry.mock'; import { ExecutorError } from './executor_error'; +const spaceIdToNamespace = jest.fn(); const actionTypeRegistry = actionTypeRegistryMock.create(); const mockedEncryptedSavedObjectsPlugin = encryptedSavedObjectsMock.create(); @@ -34,7 +35,9 @@ const getCreateTaskRunnerFunctionParams = { }; }, actionTypeRegistry, + spaceIdToNamespace, encryptedSavedObjectsPlugin: mockedEncryptedSavedObjectsPlugin, + getBasePath: jest.fn().mockReturnValue(undefined), }; const taskInstanceMock = { @@ -43,7 +46,7 @@ const taskInstanceMock = { params: { id: '2', params: { baz: true }, - namespace: 'test', + spaceId: 'test', }, taskType: 'actions:1', }; @@ -56,12 +59,14 @@ test('executes the task by calling the executor with proper parameters', async ( const runner = createTaskRunner({ taskInstance: taskInstanceMock }); mockExecute.mockResolvedValueOnce({ status: 'ok' }); + spaceIdToNamespace.mockReturnValueOnce('namespace-test'); const runnerResult = await runner.run(); expect(runnerResult).toBeUndefined(); + expect(spaceIdToNamespace).toHaveBeenCalledWith('test'); expect(mockExecute).toHaveBeenCalledWith({ - namespace: 'test', + namespace: 'namespace-test', actionId: '2', actionTypeRegistry, encryptedSavedObjectsPlugin: mockedEncryptedSavedObjectsPlugin, diff --git a/x-pack/legacy/plugins/actions/server/lib/get_create_task_runner_function.ts b/x-pack/legacy/plugins/actions/server/lib/get_create_task_runner_function.ts index 366bf057c846b..f9398f25ff7ff 100644 --- a/x-pack/legacy/plugins/actions/server/lib/get_create_task_runner_function.ts +++ b/x-pack/legacy/plugins/actions/server/lib/get_create_task_runner_function.ts @@ -9,11 +9,14 @@ import { ExecutorError } from './executor_error'; import { ActionTypeRegistryContract, GetServicesFunction } from '../types'; import { TaskInstance } from '../../../task_manager'; import { EncryptedSavedObjectsPlugin } from '../../../encrypted_saved_objects'; +import { SpacesPlugin } from '../../../spaces'; interface CreateTaskRunnerFunctionOptions { getServices: GetServicesFunction; actionTypeRegistry: ActionTypeRegistryContract; encryptedSavedObjectsPlugin: EncryptedSavedObjectsPlugin; + spaceIdToNamespace: SpacesPlugin['spaceIdToNamespace']; + getBasePath: SpacesPlugin['getBasePath']; } interface TaskRunnerOptions { @@ -24,17 +27,21 @@ export function getCreateTaskRunnerFunction({ getServices, actionTypeRegistry, encryptedSavedObjectsPlugin, + spaceIdToNamespace, + getBasePath, }: CreateTaskRunnerFunctionOptions) { return ({ taskInstance }: TaskRunnerOptions) => { return { run: async () => { - const { namespace, id, params } = taskInstance.params; + const { spaceId, id, params } = taskInstance.params; + const namespace = spaceIdToNamespace(spaceId); + const basePath = getBasePath(spaceId); const executorResult = await execute({ namespace, actionTypeRegistry, encryptedSavedObjectsPlugin, actionId: id, - services: getServices(taskInstance.params.basePath), + services: getServices(basePath), params, }); if (executorResult.status === 'error') { diff --git a/x-pack/legacy/plugins/actions/server/types.ts b/x-pack/legacy/plugins/actions/server/types.ts index 56f895bbaa778..a3134b4cd4d1f 100644 --- a/x-pack/legacy/plugins/actions/server/types.ts +++ b/x-pack/legacy/plugins/actions/server/types.ts @@ -6,6 +6,7 @@ import { SavedObjectsClientContract } from 'src/core/server'; import { ActionTypeRegistry } from './action_type_registry'; +import { FireOptions } from './create_fire_function'; export type WithoutQueryAndParams = Pick>; export type GetServicesFunction = (basePath: string, overwrites?: Partial) => Services; @@ -20,7 +21,7 @@ export interface Services { export interface ActionsPlugin { registerType: ActionTypeRegistry['register']; listTypes: ActionTypeRegistry['list']; - fire(options: { id: string; params: Record; basePath: string }): Promise; + fire(options: FireOptions): Promise; } // the parameters passed to an action type executor function diff --git a/x-pack/legacy/plugins/alerting/server/alert_type_registry.test.ts b/x-pack/legacy/plugins/alerting/server/alert_type_registry.test.ts index 442023951714e..f14da9e9ed83d 100644 --- a/x-pack/legacy/plugins/alerting/server/alert_type_registry.test.ts +++ b/x-pack/legacy/plugins/alerting/server/alert_type_registry.test.ts @@ -25,6 +25,8 @@ const alertTypeRegistryParams = { taskManager, fireAction: jest.fn(), internalSavedObjectsRepository: SavedObjectsClientMock.create(), + spaceIdToNamespace: jest.fn().mockReturnValue(undefined), + getBasePath: jest.fn().mockReturnValue(undefined), }; beforeEach(() => jest.resetAllMocks()); @@ -46,7 +48,7 @@ describe('has()', () => { }); }); -describe('registry()', () => { +describe('register()', () => { test('registers the executor with the task manager', () => { // eslint-disable-next-line @typescript-eslint/no-var-requires const { getCreateTaskRunnerFunction } = require('./lib/get_create_task_runner_function'); @@ -79,6 +81,8 @@ Object { } `); expect(firstCall.internalSavedObjectsRepository).toBeTruthy(); + expect(firstCall.getBasePath).toBeTruthy(); + expect(firstCall.spaceIdToNamespace).toBeTruthy(); expect(firstCall.fireAction).toMatchInlineSnapshot(`[MockFunction]`); }); diff --git a/x-pack/legacy/plugins/alerting/server/alert_type_registry.ts b/x-pack/legacy/plugins/alerting/server/alert_type_registry.ts index 61a358473ef46..7e763858fd20e 100644 --- a/x-pack/legacy/plugins/alerting/server/alert_type_registry.ts +++ b/x-pack/legacy/plugins/alerting/server/alert_type_registry.ts @@ -11,12 +11,15 @@ import { AlertType, Services } from './types'; import { TaskManager } from '../../task_manager'; import { getCreateTaskRunnerFunction } from './lib'; import { ActionsPlugin } from '../../actions'; +import { SpacesPlugin } from '../../spaces'; interface ConstructorOptions { getServices: (basePath: string) => Services; taskManager: TaskManager; fireAction: ActionsPlugin['fire']; internalSavedObjectsRepository: SavedObjectsClientContract; + spaceIdToNamespace: SpacesPlugin['spaceIdToNamespace']; + getBasePath: SpacesPlugin['getBasePath']; } export class AlertTypeRegistry { @@ -25,17 +28,23 @@ export class AlertTypeRegistry { private readonly fireAction: ActionsPlugin['fire']; private readonly alertTypes: Map = new Map(); private readonly internalSavedObjectsRepository: SavedObjectsClientContract; + private readonly spaceIdToNamespace: SpacesPlugin['spaceIdToNamespace']; + private readonly getBasePath: SpacesPlugin['getBasePath']; constructor({ internalSavedObjectsRepository, fireAction, taskManager, getServices, + spaceIdToNamespace, + getBasePath, }: ConstructorOptions) { this.taskManager = taskManager; this.fireAction = fireAction; this.internalSavedObjectsRepository = internalSavedObjectsRepository; this.getServices = getServices; + this.getBasePath = getBasePath; + this.spaceIdToNamespace = spaceIdToNamespace; } public has(id: string) { @@ -63,6 +72,8 @@ export class AlertTypeRegistry { getServices: this.getServices, fireAction: this.fireAction, internalSavedObjectsRepository: this.internalSavedObjectsRepository, + getBasePath: this.getBasePath, + spaceIdToNamespace: this.spaceIdToNamespace, }), }, }); diff --git a/x-pack/legacy/plugins/alerting/server/alerts_client.test.ts b/x-pack/legacy/plugins/alerting/server/alerts_client.test.ts index aba51e82c6677..c6eae114e725a 100644 --- a/x-pack/legacy/plugins/alerting/server/alerts_client.test.ts +++ b/x-pack/legacy/plugins/alerting/server/alerts_client.test.ts @@ -19,7 +19,7 @@ const alertsClientParams = { taskManager, alertTypeRegistry, savedObjectsClient, - basePath: '/s/default', + spaceId: 'default', }; beforeEach(() => jest.resetAllMocks()); @@ -119,98 +119,98 @@ describe('create()', () => { }); const result = await alertsClient.create({ data }); expect(result).toMatchInlineSnapshot(` - Object { - "actions": Array [ - Object { - "group": "default", - "id": "1", - "params": Object { - "foo": true, - }, - }, - ], - "alertTypeId": "123", - "alertTypeParams": Object { - "bar": true, - }, - "id": "1", - "interval": "10s", - "scheduledTaskId": "task-123", - } - `); + Object { + "actions": Array [ + Object { + "group": "default", + "id": "1", + "params": Object { + "foo": true, + }, + }, + ], + "alertTypeId": "123", + "alertTypeParams": Object { + "bar": true, + }, + "id": "1", + "interval": "10s", + "scheduledTaskId": "task-123", + } + `); expect(savedObjectsClient.create).toHaveBeenCalledTimes(1); expect(savedObjectsClient.create.mock.calls[0]).toHaveLength(3); expect(savedObjectsClient.create.mock.calls[0][0]).toEqual('alert'); expect(savedObjectsClient.create.mock.calls[0][1]).toMatchInlineSnapshot(` - Object { - "actions": Array [ - Object { - "actionRef": "action_0", - "group": "default", - "params": Object { - "foo": true, - }, - }, - ], - "alertTypeId": "123", - "alertTypeParams": Object { - "bar": true, - }, - "enabled": true, - "interval": "10s", - } - `); + Object { + "actions": Array [ + Object { + "actionRef": "action_0", + "group": "default", + "params": Object { + "foo": true, + }, + }, + ], + "alertTypeId": "123", + "alertTypeParams": Object { + "bar": true, + }, + "enabled": true, + "interval": "10s", + } + `); expect(savedObjectsClient.create.mock.calls[0][2]).toMatchInlineSnapshot(` - Object { - "references": Array [ - Object { - "id": "1", - "name": "action_0", - "type": "action", - }, - ], - } - `); + Object { + "references": Array [ + Object { + "id": "1", + "name": "action_0", + "type": "action", + }, + ], + } + `); expect(taskManager.schedule).toHaveBeenCalledTimes(1); expect(taskManager.schedule.mock.calls[0]).toMatchInlineSnapshot(` - Array [ - Object { - "params": Object { - "alertId": "1", - "basePath": "/s/default", - }, - "scope": Array [ - "alerting", - ], - "state": Object { - "alertInstances": Object {}, - "alertTypeState": Object {}, - "previousStartedAt": null, - }, - "taskType": "alerting:123", - }, - ] - `); + Array [ + Object { + "params": Object { + "alertId": "1", + "spaceId": "default", + }, + "scope": Array [ + "alerting", + ], + "state": Object { + "alertInstances": Object {}, + "alertTypeState": Object {}, + "previousStartedAt": null, + }, + "taskType": "alerting:123", + }, + ] + `); expect(savedObjectsClient.update).toHaveBeenCalledTimes(1); expect(savedObjectsClient.update.mock.calls[0]).toHaveLength(4); expect(savedObjectsClient.update.mock.calls[0][0]).toEqual('alert'); expect(savedObjectsClient.update.mock.calls[0][1]).toEqual('1'); expect(savedObjectsClient.update.mock.calls[0][2]).toMatchInlineSnapshot(` - Object { - "scheduledTaskId": "task-123", - } - `); + Object { + "scheduledTaskId": "task-123", + } + `); expect(savedObjectsClient.update.mock.calls[0][3]).toMatchInlineSnapshot(` - Object { - "references": Array [ - Object { - "id": "1", - "name": "action_0", - "type": "action", - }, - ], - } - `); + Object { + "references": Array [ + Object { + "id": "1", + "name": "action_0", + "type": "action", + }, + ], + } + `); }); test('creates a disabled alert', async () => { @@ -251,25 +251,25 @@ describe('create()', () => { }); const result = await alertsClient.create({ data }); expect(result).toMatchInlineSnapshot(` - Object { - "actions": Array [ - Object { - "group": "default", - "id": "1", - "params": Object { - "foo": true, - }, - }, - ], - "alertTypeId": "123", - "alertTypeParams": Object { - "bar": true, - }, - "enabled": false, - "id": "1", - "interval": 10000, - } - `); + Object { + "actions": Array [ + Object { + "group": "default", + "id": "1", + "params": Object { + "foo": true, + }, + }, + ], + "alertTypeId": "123", + "alertTypeParams": Object { + "bar": true, + }, + "enabled": false, + "id": "1", + "interval": 10000, + } + `); expect(savedObjectsClient.create).toHaveBeenCalledTimes(1); expect(taskManager.schedule).toHaveBeenCalledTimes(0); }); @@ -350,11 +350,11 @@ describe('create()', () => { ); expect(savedObjectsClient.delete).toHaveBeenCalledTimes(1); expect(savedObjectsClient.delete.mock.calls[0]).toMatchInlineSnapshot(` - Array [ - "alert", - "1", - ] - `); + Array [ + "alert", + "1", + ] + `); }); test('returns task manager error if cleanup fails, logs to console', async () => { @@ -399,14 +399,14 @@ describe('create()', () => { ); expect(alertsClientParams.log).toHaveBeenCalledTimes(1); expect(alertsClientParams.log.mock.calls[0]).toMatchInlineSnapshot(` - Array [ - Array [ - "alerting", - "error", - ], - "Failed to cleanup alert \\"1\\" after scheduling task failed. Error: Saved object delete error", - ] - `); + Array [ + Array [ + "alerting", + "error", + ], + "Failed to cleanup alert \\"1\\" after scheduling task failed. Error: Saved object delete error", + ] + `); }); test('throws an error if alert type not registerd', async () => { @@ -463,7 +463,7 @@ describe('enable()', () => { taskType: `alerting:2`, params: { alertId: '1', - basePath: '/s/default', + spaceId: 'default', }, state: { alertInstances: {}, @@ -575,31 +575,31 @@ describe('get()', () => { }); const result = await alertsClient.get({ id: '1' }); expect(result).toMatchInlineSnapshot(` - Object { - "actions": Array [ - Object { - "group": "default", - "id": "1", - "params": Object { - "foo": true, - }, - }, - ], - "alertTypeId": "123", - "alertTypeParams": Object { - "bar": true, - }, - "id": "1", - "interval": "10s", - } - `); + Object { + "actions": Array [ + Object { + "group": "default", + "id": "1", + "params": Object { + "foo": true, + }, + }, + ], + "alertTypeId": "123", + "alertTypeParams": Object { + "bar": true, + }, + "id": "1", + "interval": "10s", + } + `); expect(savedObjectsClient.get).toHaveBeenCalledTimes(1); expect(savedObjectsClient.get.mock.calls[0]).toMatchInlineSnapshot(` - Array [ - "alert", - "1", - ] - `); + Array [ + "alert", + "1", + ] + `); }); test(`throws an error when references aren't found`, async () => { @@ -670,39 +670,39 @@ describe('find()', () => { }); const result = await alertsClient.find(); expect(result).toMatchInlineSnapshot(` - Object { - "data": Array [ - Object { - "actions": Array [ - Object { - "group": "default", - "id": "1", - "params": Object { - "foo": true, + Object { + "data": Array [ + Object { + "actions": Array [ + Object { + "group": "default", + "id": "1", + "params": Object { + "foo": true, + }, + }, + ], + "alertTypeId": "123", + "alertTypeParams": Object { + "bar": true, + }, + "id": "1", + "interval": "10s", }, - }, - ], - "alertTypeId": "123", - "alertTypeParams": Object { - "bar": true, - }, - "id": "1", - "interval": "10s", - }, - ], - "page": 1, - "perPage": 10, - "total": 1, - } - `); + ], + "page": 1, + "perPage": 10, + "total": 1, + } + `); expect(savedObjectsClient.find).toHaveBeenCalledTimes(1); expect(savedObjectsClient.find.mock.calls[0]).toMatchInlineSnapshot(` - Array [ - Object { - "type": "alert", - }, - ] - `); + Array [ + Object { + "type": "alert", + }, + ] + `); }); }); @@ -744,17 +744,17 @@ describe('delete()', () => { expect(result).toEqual({ success: true }); expect(savedObjectsClient.delete).toHaveBeenCalledTimes(1); expect(savedObjectsClient.delete.mock.calls[0]).toMatchInlineSnapshot(` - Array [ - "alert", - "1", - ] - `); + Array [ + "alert", + "1", + ] + `); expect(taskManager.remove).toHaveBeenCalledTimes(1); expect(taskManager.remove.mock.calls[0]).toMatchInlineSnapshot(` - Array [ - "task-123", - ] - `); + Array [ + "task-123", + ] + `); }); }); @@ -826,58 +826,58 @@ describe('update()', () => { }, }); expect(result).toMatchInlineSnapshot(` - Object { - "actions": Array [ - Object { - "group": "default", - "id": "1", - "params": Object { - "foo": true, - }, - }, - ], - "alertTypeParams": Object { - "bar": true, - }, - "enabled": true, - "id": "1", - "interval": "10s", - "scheduledTaskId": "task-123", - } - `); + Object { + "actions": Array [ + Object { + "group": "default", + "id": "1", + "params": Object { + "foo": true, + }, + }, + ], + "alertTypeParams": Object { + "bar": true, + }, + "enabled": true, + "id": "1", + "interval": "10s", + "scheduledTaskId": "task-123", + } + `); expect(savedObjectsClient.update).toHaveBeenCalledTimes(1); expect(savedObjectsClient.update.mock.calls[0]).toHaveLength(4); expect(savedObjectsClient.update.mock.calls[0][0]).toEqual('alert'); expect(savedObjectsClient.update.mock.calls[0][1]).toEqual('1'); expect(savedObjectsClient.update.mock.calls[0][2]).toMatchInlineSnapshot(` - Object { - "actions": Array [ - Object { - "actionRef": "action_0", - "group": "default", - "params": Object { - "foo": true, - }, - }, - ], - "alertTypeParams": Object { - "bar": true, - }, - "interval": "10s", - } - `); + Object { + "actions": Array [ + Object { + "actionRef": "action_0", + "group": "default", + "params": Object { + "foo": true, + }, + }, + ], + "alertTypeParams": Object { + "bar": true, + }, + "interval": "10s", + } + `); expect(savedObjectsClient.update.mock.calls[0][3]).toMatchInlineSnapshot(` - Object { - "references": Array [ - Object { - "id": "1", - "name": "action_0", - "type": "action", - }, - ], - "version": "123", - } - `); + Object { + "references": Array [ + Object { + "id": "1", + "name": "action_0", + "type": "action", + }, + ], + "version": "123", + } + `); }); it('should validate alertTypeParams', async () => { diff --git a/x-pack/legacy/plugins/alerting/server/alerts_client.ts b/x-pack/legacy/plugins/alerting/server/alerts_client.ts index 2c5dd49a9e2a7..cbd1ba3eab53e 100644 --- a/x-pack/legacy/plugins/alerting/server/alerts_client.ts +++ b/x-pack/legacy/plugins/alerting/server/alerts_client.ts @@ -15,7 +15,7 @@ interface ConstructorOptions { taskManager: TaskManager; savedObjectsClient: SavedObjectsClientContract; alertTypeRegistry: AlertTypeRegistry; - basePath: string; + spaceId?: string; } interface FindOptions { @@ -60,7 +60,7 @@ interface UpdateOptions { export class AlertsClient { private readonly log: Log; - private readonly basePath: string; + private readonly spaceId?: string; private readonly taskManager: TaskManager; private readonly savedObjectsClient: SavedObjectsClientContract; private readonly alertTypeRegistry: AlertTypeRegistry; @@ -70,10 +70,10 @@ export class AlertsClient { savedObjectsClient, taskManager, log, - basePath, + spaceId, }: ConstructorOptions) { this.log = log; - this.basePath = basePath; + this.spaceId = spaceId; this.taskManager = taskManager; this.alertTypeRegistry = alertTypeRegistry; this.savedObjectsClient = savedObjectsClient; @@ -97,8 +97,7 @@ export class AlertsClient { scheduledTask = await this.scheduleAlert( createdAlert.id, rawAlert.alertTypeId, - rawAlert.interval, - this.basePath + rawAlert.interval ); } catch (e) { // Cleanup data, something went wrong scheduling the task @@ -189,8 +188,7 @@ export class AlertsClient { const scheduledTask = await this.scheduleAlert( id, existingObject.attributes.alertTypeId, - existingObject.attributes.interval, - this.basePath + existingObject.attributes.interval ); await this.savedObjectsClient.update( 'alert', @@ -220,12 +218,12 @@ export class AlertsClient { } } - private async scheduleAlert(id: string, alertTypeId: string, interval: string, basePath: string) { + private async scheduleAlert(id: string, alertTypeId: string, interval: string) { return await this.taskManager.schedule({ taskType: `alerting:${alertTypeId}`, params: { alertId: id, - basePath, + spaceId: this.spaceId, }, state: { previousStartedAt: null, diff --git a/x-pack/legacy/plugins/alerting/server/init.ts b/x-pack/legacy/plugins/alerting/server/init.ts index ec1255cc5ac20..bf7b4b8009b97 100644 --- a/x-pack/legacy/plugins/alerting/server/init.ts +++ b/x-pack/legacy/plugins/alerting/server/init.ts @@ -18,8 +18,18 @@ import { import { AlertingPlugin, Services } from './types'; import { AlertTypeRegistry } from './alert_type_registry'; import { AlertsClient } from './alerts_client'; +import { SpacesPlugin } from '../../spaces'; +import { createOptionalPlugin } from '../../../server/lib/optional_plugin'; export function init(server: Legacy.Server) { + const config = server.config(); + const spaces = createOptionalPlugin( + config, + 'xpack.spaces', + server.plugins, + 'spaces' + ); + const { callWithInternalUser } = server.plugins.elasticsearch.getCluster('admin'); const savedObjectsRepositoryWithInternalUser = server.savedObjects.getSavedObjectsRepository( callWithInternalUser @@ -43,6 +53,14 @@ export function init(server: Legacy.Server) { taskManager: taskManager!, fireAction: server.plugins.actions!.fire, internalSavedObjectsRepository: savedObjectsRepositoryWithInternalUser, + getBasePath(...args) { + return spaces.isEnabled + ? spaces.getBasePath(...args) + : server.config().get('server.basePath'); + }, + spaceIdToNamespace(...args) { + return spaces.isEnabled ? spaces.spaceIdToNamespace(...args) : undefined; + }, }); // Register routes @@ -64,7 +82,7 @@ export function init(server: Legacy.Server) { savedObjectsClient, alertTypeRegistry, taskManager: taskManager!, - basePath: request.getBasePath(), + spaceId: spaces.isEnabled ? spaces.getSpaceId(request) : undefined, }); return alertsClient; }); diff --git a/x-pack/legacy/plugins/alerting/server/lib/create_fire_handler.test.ts b/x-pack/legacy/plugins/alerting/server/lib/create_fire_handler.test.ts index ba8e35dbd7ba9..bbafd60cdbc64 100644 --- a/x-pack/legacy/plugins/alerting/server/lib/create_fire_handler.test.ts +++ b/x-pack/legacy/plugins/alerting/server/lib/create_fire_handler.test.ts @@ -7,8 +7,10 @@ import { createFireHandler } from './create_fire_handler'; const createFireHandlerParams = { - basePath: '/s/default', fireAction: jest.fn(), + spaceId: 'default', + spaceIdToNamespace: jest.fn().mockReturnValue(undefined), + getBasePath: jest.fn().mockReturnValue(undefined), alertSavedObject: { id: '1', type: 'alert', @@ -47,18 +49,18 @@ test('calls fireAction per selected action', async () => { await fireHandler('default', {}, {}); expect(createFireHandlerParams.fireAction).toHaveBeenCalledTimes(1); expect(createFireHandlerParams.fireAction.mock.calls[0]).toMatchInlineSnapshot(` -Array [ - Object { - "basePath": "/s/default", - "id": "1", - "params": Object { - "contextVal": "My goes here", - "foo": true, - "stateVal": "My goes here", - }, - }, -] -`); + Array [ + Object { + "id": "1", + "params": Object { + "contextVal": "My goes here", + "foo": true, + "stateVal": "My goes here", + }, + "spaceId": "default", + }, + ] + `); }); test('limits fireAction per action group', async () => { @@ -72,18 +74,18 @@ test('context attribute gets parameterized', async () => { await fireHandler('default', { value: 'context-val' }, {}); expect(createFireHandlerParams.fireAction).toHaveBeenCalledTimes(1); expect(createFireHandlerParams.fireAction.mock.calls[0]).toMatchInlineSnapshot(` -Array [ - Object { - "basePath": "/s/default", - "id": "1", - "params": Object { - "contextVal": "My context-val goes here", - "foo": true, - "stateVal": "My goes here", - }, - }, -] -`); + Array [ + Object { + "id": "1", + "params": Object { + "contextVal": "My context-val goes here", + "foo": true, + "stateVal": "My goes here", + }, + "spaceId": "default", + }, + ] + `); }); test('state attribute gets parameterized', async () => { @@ -91,23 +93,23 @@ test('state attribute gets parameterized', async () => { await fireHandler('default', {}, { value: 'state-val' }); expect(createFireHandlerParams.fireAction).toHaveBeenCalledTimes(1); expect(createFireHandlerParams.fireAction.mock.calls[0]).toMatchInlineSnapshot(` -Array [ - Object { - "basePath": "/s/default", - "id": "1", - "params": Object { - "contextVal": "My goes here", - "foo": true, - "stateVal": "My state-val goes here", - }, - }, -] -`); + Array [ + Object { + "id": "1", + "params": Object { + "contextVal": "My goes here", + "foo": true, + "stateVal": "My state-val goes here", + }, + "spaceId": "default", + }, + ] + `); }); test('throws error if reference not found', async () => { const params = { - basePath: '/s/default', + spaceId: 'default', fireAction: jest.fn(), alertSavedObject: { id: '1', diff --git a/x-pack/legacy/plugins/alerting/server/lib/create_fire_handler.ts b/x-pack/legacy/plugins/alerting/server/lib/create_fire_handler.ts index 3a271365105c7..f51b374298a07 100644 --- a/x-pack/legacy/plugins/alerting/server/lib/create_fire_handler.ts +++ b/x-pack/legacy/plugins/alerting/server/lib/create_fire_handler.ts @@ -12,13 +12,13 @@ import { transformActionParams } from './transform_action_params'; interface CreateFireHandlerOptions { fireAction: ActionsPlugin['fire']; alertSavedObject: SavedObject; - basePath: string; + spaceId: string; } export function createFireHandler({ fireAction, alertSavedObject, - basePath, + spaceId, }: CreateFireHandlerOptions) { return async (actionGroup: string, context: Context, state: State) => { const alertActions: RawAlertAction[] = alertSavedObject.attributes.actions; @@ -43,7 +43,7 @@ export function createFireHandler({ await fireAction({ id: action.id, params: action.params, - basePath, + spaceId, }); } }; diff --git a/x-pack/legacy/plugins/alerting/server/lib/get_create_task_runner_function.test.ts b/x-pack/legacy/plugins/alerting/server/lib/get_create_task_runner_function.test.ts index f12751e2288dd..95969d3d9a17c 100644 --- a/x-pack/legacy/plugins/alerting/server/lib/get_create_task_runner_function.test.ts +++ b/x-pack/legacy/plugins/alerting/server/lib/get_create_task_runner_function.test.ts @@ -54,6 +54,8 @@ const getCreateTaskRunnerFunctionParams = { }, fireAction: jest.fn(), internalSavedObjectsRepository: savedObjectsClient, + spaceIdToNamespace: jest.fn().mockReturnValue(undefined), + getBasePath: jest.fn().mockReturnValue(undefined), }; const mockedAlertTypeSavedObject = { @@ -93,22 +95,22 @@ test('successfully executes the task', async () => { const runner = createTaskRunner({ taskInstance: mockedTaskInstance }); const runnerResult = await runner.run(); expect(runnerResult).toMatchInlineSnapshot(` - Object { - "runAt": 1970-01-01T00:00:10.000Z, - "state": Object { - "alertInstances": Object {}, - "alertTypeState": undefined, - "previousStartedAt": 1970-01-01T00:00:00.000Z, - }, - } - `); - expect(getCreateTaskRunnerFunctionParams.alertType.executor).toHaveBeenCalledTimes(1); - const call = getCreateTaskRunnerFunctionParams.alertType.executor.mock.calls[0][0]; - expect(call.params).toMatchInlineSnapshot(` Object { - "bar": true, + "runAt": 1970-01-01T00:00:10.000Z, + "state": Object { + "alertInstances": Object {}, + "alertTypeState": undefined, + "previousStartedAt": 1970-01-01T00:00:00.000Z, + }, } `); + expect(getCreateTaskRunnerFunctionParams.alertType.executor).toHaveBeenCalledTimes(1); + const call = getCreateTaskRunnerFunctionParams.alertType.executor.mock.calls[0][0]; + expect(call.params).toMatchInlineSnapshot(` + Object { + "bar": true, + } + `); expect(call.startedAt).toMatchInlineSnapshot(`1970-01-01T00:00:00.000Z`); expect(call.state).toMatchInlineSnapshot(`Object {}`); expect(call.services.alertInstanceFactory).toBeTruthy(); @@ -128,16 +130,16 @@ test('fireAction is called per alert instance that fired', async () => { await runner.run(); expect(getCreateTaskRunnerFunctionParams.fireAction).toHaveBeenCalledTimes(1); expect(getCreateTaskRunnerFunctionParams.fireAction.mock.calls[0]).toMatchInlineSnapshot(` - Array [ - Object { - "basePath": undefined, - "id": "1", - "params": Object { - "foo": true, - }, - }, - ] - `); + Array [ + Object { + "id": "1", + "params": Object { + "foo": true, + }, + "spaceId": undefined, + }, + ] + `); }); test('persists alertInstances passed in from state, only if they fire', async () => { @@ -162,17 +164,17 @@ test('persists alertInstances passed in from state, only if they fire', async () }); const runnerResult = await runner.run(); expect(runnerResult.state.alertInstances).toMatchInlineSnapshot(` - Object { - "1": Object { - "meta": Object { - "lastFired": 0, - }, - "state": Object { - "bar": false, - }, - }, - } - `); + Object { + "1": Object { + "meta": Object { + "lastFired": 0, + }, + "state": Object { + "bar": false, + }, + }, + } + `); }); test('validates params before executing the alert type', async () => { diff --git a/x-pack/legacy/plugins/alerting/server/lib/get_create_task_runner_function.ts b/x-pack/legacy/plugins/alerting/server/lib/get_create_task_runner_function.ts index 5591b63188b35..2ac3023a2079c 100644 --- a/x-pack/legacy/plugins/alerting/server/lib/get_create_task_runner_function.ts +++ b/x-pack/legacy/plugins/alerting/server/lib/get_create_task_runner_function.ts @@ -13,12 +13,15 @@ import { createAlertInstanceFactory } from './create_alert_instance_factory'; import { AlertInstance } from './alert_instance'; import { getNextRunAt } from './get_next_run_at'; import { validateAlertTypeParams } from './validate_alert_type_params'; +import { SpacesPlugin } from '../../../spaces'; interface CreateTaskRunnerFunctionOptions { getServices: (basePath: string) => Services; alertType: AlertType; fireAction: ActionsPlugin['fire']; internalSavedObjectsRepository: SavedObjectsClientContract; + spaceIdToNamespace: SpacesPlugin['spaceIdToNamespace']; + getBasePath: SpacesPlugin['getBasePath']; } interface TaskRunnerOptions { @@ -30,13 +33,17 @@ export function getCreateTaskRunnerFunction({ alertType, fireAction, internalSavedObjectsRepository, + spaceIdToNamespace, + getBasePath, }: CreateTaskRunnerFunctionOptions) { return ({ taskInstance }: TaskRunnerOptions) => { return { run: async () => { + const namespace = spaceIdToNamespace(taskInstance.params.spaceId); const alertSavedObject = await internalSavedObjectsRepository.get( 'alert', - taskInstance.params.alertId + taskInstance.params.alertId, + { namespace } ); // Validate @@ -48,7 +55,7 @@ export function getCreateTaskRunnerFunction({ const fireHandler = createFireHandler({ alertSavedObject, fireAction, - basePath: taskInstance.params.basePath, + spaceId: taskInstance.params.spaceId, }); const alertInstances: Record = {}; const alertInstancesData = taskInstance.state.alertInstances || {}; diff --git a/x-pack/test/api_integration/apis/actions/constants.ts b/x-pack/test/api_integration/apis/actions/constants.ts index f79a63072be79..b7c391507dcc2 100644 --- a/x-pack/test/api_integration/apis/actions/constants.ts +++ b/x-pack/test/api_integration/apis/actions/constants.ts @@ -5,3 +5,4 @@ */ export const ES_ARCHIVER_ACTION_ID = '60071910-1ef1-4a72-9a68-b94af6a836a7'; +export const SPACE_1_ES_ARCHIVER_ACTION_ID = '6c7d0f6b-2fb5-4821-b182-624fc3ccc7a3'; diff --git a/x-pack/test/api_integration/apis/actions/create.ts b/x-pack/test/api_integration/apis/actions/create.ts index d76166316d87a..d7df3fd97c696 100644 --- a/x-pack/test/api_integration/apis/actions/create.ts +++ b/x-pack/test/api_integration/apis/actions/create.ts @@ -38,6 +38,29 @@ export default function createActionTests({ getService }: KibanaFunctionalTestDe }); }); + it('should return 200 when creating an action inside a space and to not be accessible from another space', async () => { + const { body: createdAction } = await supertest + .post('/s/space_1/api/action') + .set('kbn-xsrf', 'foo') + .send({ + description: 'My action', + actionTypeId: 'test.index-record', + config: { + unencrypted: `This value shouldn't get encrypted`, + }, + secrets: { + encrypted: 'This value should be encrypted', + }, + }) + .expect(200); + expect(createdAction).to.eql({ + id: createdAction.id, + }); + expect(typeof createdAction.id).to.be('string'); + await supertest.get(`/s/space_1/api/action/${createdAction.id}`).expect(200); + await supertest.get(`/api/action/${createdAction.id}`).expect(404); + }); + it(`should return 400 when action type isn't registered`, async () => { await supertest .post('/api/action') diff --git a/x-pack/test/api_integration/apis/actions/delete.ts b/x-pack/test/api_integration/apis/actions/delete.ts index 92e513ea6f2b6..68f8924010cce 100644 --- a/x-pack/test/api_integration/apis/actions/delete.ts +++ b/x-pack/test/api_integration/apis/actions/delete.ts @@ -6,7 +6,7 @@ import expect from '@kbn/expect'; -import { ES_ARCHIVER_ACTION_ID } from './constants'; +import { ES_ARCHIVER_ACTION_ID, SPACE_1_ES_ARCHIVER_ACTION_ID } from './constants'; import { KibanaFunctionalTestDefaultProviders } from '../../../types/providers'; // eslint-disable-next-line import/no-default-export @@ -25,6 +25,20 @@ export default function deleteActionTests({ getService }: KibanaFunctionalTestDe .expect(204, ''); }); + it('should return 204 when deleting an action in a space', async () => { + await supertest + .delete(`/s/space_1/api/action/${SPACE_1_ES_ARCHIVER_ACTION_ID}`) + .set('kbn-xsrf', 'foo') + .expect(204, ''); + }); + + it('should return 404 when deleting an action in another space', async () => { + await supertest + .delete(`/api/action/${SPACE_1_ES_ARCHIVER_ACTION_ID}`) + .set('kbn-xsrf', 'foo') + .expect(404); + }); + it(`should return 404 when action doesn't exist`, async () => { await supertest .delete('/api/action/2') diff --git a/x-pack/test/api_integration/apis/actions/find.ts b/x-pack/test/api_integration/apis/actions/find.ts index 6bf863737735c..66a5e956db919 100644 --- a/x-pack/test/api_integration/apis/actions/find.ts +++ b/x-pack/test/api_integration/apis/actions/find.ts @@ -5,7 +5,7 @@ */ import expect from '@kbn/expect'; -import { ES_ARCHIVER_ACTION_ID } from './constants'; +import { ES_ARCHIVER_ACTION_ID, SPACE_1_ES_ARCHIVER_ACTION_ID } from './constants'; import { KibanaFunctionalTestDefaultProviders } from '../../../types/providers'; // eslint-disable-next-line import/no-default-export @@ -38,6 +38,27 @@ export default function findActionTests({ getService }: KibanaFunctionalTestDefa }); }); + it('should return 200 with individual responses in a space', async () => { + await supertest + .get( + '/s/space_1/api/action/_find?search=test.index-record&search_fields=actionTypeId&fields=description' + ) + .expect(200) + .then((resp: any) => { + expect(resp.body).to.eql({ + page: 1, + perPage: 20, + total: 1, + data: [ + { + id: SPACE_1_ES_ARCHIVER_ACTION_ID, + description: 'My action', + }, + ], + }); + }); + }); + it('should not return encrypted attributes', async () => { await supertest .get('/api/action/_find?search=test.index-record&search_fields=actionTypeId') diff --git a/x-pack/test/api_integration/apis/actions/fire.ts b/x-pack/test/api_integration/apis/actions/fire.ts index e1d659f356efb..60fe0d5f3dc61 100644 --- a/x-pack/test/api_integration/apis/actions/fire.ts +++ b/x-pack/test/api_integration/apis/actions/fire.ts @@ -5,7 +5,7 @@ */ import expect from '@kbn/expect'; -import { ES_ARCHIVER_ACTION_ID } from './constants'; +import { ES_ARCHIVER_ACTION_ID, SPACE_1_ES_ARCHIVER_ACTION_ID } from './constants'; import { KibanaFunctionalTestDefaultProviders } from '../../../types/providers'; // eslint-disable-next-line import/no-default-export @@ -110,6 +110,77 @@ export default function({ getService }: KibanaFunctionalTestDefaultProviders) { }); }); + it(`can't fire from another space`, async () => { + await supertest + .post(`/api/action/${SPACE_1_ES_ARCHIVER_ACTION_ID}/_fire`) + .set('kbn-xsrf', 'foo') + .send({ + params: { + index: esTestIndexName, + reference: 'actions-fire-2', + message: 'Testing 123', + }, + }) + .expect(404); + }); + + it('fire works in a space', async () => { + await supertest + .post(`/s/space_1/api/action/${SPACE_1_ES_ARCHIVER_ACTION_ID}/_fire`) + .set('kbn-xsrf', 'foo') + .send({ + params: { + index: esTestIndexName, + reference: 'actions-fire-3', + message: 'Testing 123', + }, + }) + .expect(200) + .then((resp: any) => { + expect(resp.body).to.be.an('object'); + }); + const indexedRecord = await retry.tryForTime(15000, async () => { + const searchResult = await es.search({ + index: esTestIndexName, + body: { + query: { + bool: { + must: [ + { + term: { + source: 'action:test.index-record', + }, + }, + { + term: { + reference: 'actions-fire-3', + }, + }, + ], + }, + }, + }, + }); + expect(searchResult.hits.total.value).to.eql(1); + return searchResult.hits.hits[0]; + }); + expect(indexedRecord._source).to.eql({ + params: { + index: esTestIndexName, + reference: 'actions-fire-3', + message: 'Testing 123', + }, + config: { + unencrypted: `This value shouldn't get encrypted`, + }, + secrets: { + encrypted: 'This value should be encrypted', + }, + reference: 'actions-fire-3', + source: 'action:test.index-record', + }); + }); + it('fire still works with encrypted attributes after updating an action', async () => { const { body: updatedAction } = await supertest .put(`/api/action/${ES_ARCHIVER_ACTION_ID}`) @@ -133,7 +204,7 @@ export default function({ getService }: KibanaFunctionalTestDefaultProviders) { .send({ params: { index: esTestIndexName, - reference: 'actions-fire-2', + reference: 'actions-fire-4', message: 'Testing 123', }, }) @@ -155,7 +226,7 @@ export default function({ getService }: KibanaFunctionalTestDefaultProviders) { }, { term: { - reference: 'actions-fire-2', + reference: 'actions-fire-4', }, }, ], @@ -169,7 +240,7 @@ export default function({ getService }: KibanaFunctionalTestDefaultProviders) { expect(indexedRecord._source).to.eql({ params: { index: esTestIndexName, - reference: 'actions-fire-2', + reference: 'actions-fire-4', message: 'Testing 123', }, config: { @@ -178,7 +249,7 @@ export default function({ getService }: KibanaFunctionalTestDefaultProviders) { secrets: { encrypted: 'This value should be encrypted', }, - reference: 'actions-fire-2', + reference: 'actions-fire-4', source: 'action:test.index-record', }); }); diff --git a/x-pack/test/api_integration/apis/actions/get.ts b/x-pack/test/api_integration/apis/actions/get.ts index 427e7d5df32d2..1b34b16c0ea07 100644 --- a/x-pack/test/api_integration/apis/actions/get.ts +++ b/x-pack/test/api_integration/apis/actions/get.ts @@ -5,7 +5,7 @@ */ import expect from '@kbn/expect'; -import { ES_ARCHIVER_ACTION_ID } from './constants'; +import { ES_ARCHIVER_ACTION_ID, SPACE_1_ES_ARCHIVER_ACTION_ID } from './constants'; import { KibanaFunctionalTestDefaultProviders } from '../../../types/providers'; // eslint-disable-next-line import/no-default-export @@ -33,6 +33,26 @@ export default function getActionTests({ getService }: KibanaFunctionalTestDefau }); }); + it('should return 404 when finding a record in another space', async () => { + await supertest.get(`/api/action/${SPACE_1_ES_ARCHIVER_ACTION_ID}`).expect(404); + }); + + it('should return 200 when finding a record in a space', async () => { + await supertest + .get(`/s/space_1/api/action/${SPACE_1_ES_ARCHIVER_ACTION_ID}`) + .expect(200) + .then((resp: any) => { + expect(resp.body).to.eql({ + id: SPACE_1_ES_ARCHIVER_ACTION_ID, + actionTypeId: 'test.index-record', + description: 'My action', + config: { + unencrypted: `This value shouldn't get encrypted`, + }, + }); + }); + }); + it('should return 404 when not finding a record', async () => { await supertest .get('/api/action/2') diff --git a/x-pack/test/api_integration/apis/actions/update.ts b/x-pack/test/api_integration/apis/actions/update.ts index fad9fed7aabd0..24b73ab723d98 100644 --- a/x-pack/test/api_integration/apis/actions/update.ts +++ b/x-pack/test/api_integration/apis/actions/update.ts @@ -5,7 +5,7 @@ */ import expect from '@kbn/expect'; -import { ES_ARCHIVER_ACTION_ID } from './constants'; +import { ES_ARCHIVER_ACTION_ID, SPACE_1_ES_ARCHIVER_ACTION_ID } from './constants'; import { KibanaFunctionalTestDefaultProviders } from '../../../types/providers'; // eslint-disable-next-line import/no-default-export @@ -38,6 +38,41 @@ export default function updateActionTests({ getService }: KibanaFunctionalTestDe }); }); + it('should return 200 when updating a document in a space', async () => { + await supertest + .put(`/s/space_1/api/action/${SPACE_1_ES_ARCHIVER_ACTION_ID}`) + .set('kbn-xsrf', 'foo') + .send({ + description: 'My action updated', + config: { + unencrypted: `This value shouldn't get encrypted`, + }, + secrets: { + encrypted: 'This value should be encrypted', + }, + }) + .expect(200) + .then((resp: any) => { + expect(resp.body).to.eql({ + id: SPACE_1_ES_ARCHIVER_ACTION_ID, + }); + }); + }); + + it('should return 404 when updating a document in another space', async () => { + await supertest + .put(`/api/action/${SPACE_1_ES_ARCHIVER_ACTION_ID}`) + .set('kbn-xsrf', 'foo') + .send({ + description: 'My action updated', + config: { + unencrypted: `This value shouldn't get encrypted`, + encrypted: 'This value should be encrypted', + }, + }) + .expect(404); + }); + it('should not be able to pass null config', async () => { await supertest .put(`/api/action/${ES_ARCHIVER_ACTION_ID}`) diff --git a/x-pack/test/api_integration/apis/alerting/alerts.ts b/x-pack/test/api_integration/apis/alerting/alerts.ts index e2376c364b9e2..7b4232f266e20 100644 --- a/x-pack/test/api_integration/apis/alerting/alerts.ts +++ b/x-pack/test/api_integration/apis/alerting/alerts.ts @@ -7,7 +7,7 @@ import expect from '@kbn/expect'; import { getTestAlertData, setupEsTestIndex, destroyEsTestIndex } from './utils'; import { KibanaFunctionalTestDefaultProviders } from '../../../types/providers'; -import { ES_ARCHIVER_ACTION_ID } from './constants'; +import { ES_ARCHIVER_ACTION_ID, SPACE_1_ES_ARCHIVER_ACTION_ID } from './constants'; // eslint-disable-next-line import/no-default-export export default function alertTests({ getService }: KibanaFunctionalTestDefaultProviders) { @@ -18,7 +18,7 @@ export default function alertTests({ getService }: KibanaFunctionalTestDefaultPr describe('alerts', () => { let esTestIndexName: string; - const createdAlertIds: string[] = []; + const createdAlertIds: Array<{ space: string; id: string }> = []; before(async () => { await destroyEsTestIndex(es); @@ -27,9 +27,10 @@ export default function alertTests({ getService }: KibanaFunctionalTestDefaultPr }); after(async () => { await Promise.all( - createdAlertIds.map(id => { + createdAlertIds.map(({ space, id }) => { + const urlPrefix = space !== 'default' ? `/s/${space}` : ''; return supertest - .delete(`/api/alert/${id}`) + .delete(`${urlPrefix}/api/alert/${id}`) .set('kbn-xsrf', 'foo') .expect(204, ''); }) @@ -66,7 +67,7 @@ export default function alertTests({ getService }: KibanaFunctionalTestDefaultPr ) .expect(200) .then((resp: any) => { - createdAlertIds.push(resp.body.id); + createdAlertIds.push({ space: 'default', id: resp.body.id }); }); const alertTestRecord = await retry.tryForTime(15000, async () => { const searchResult = await es.search({ @@ -144,6 +145,111 @@ export default function alertTests({ getService }: KibanaFunctionalTestDefaultPr }); }); + it('should schedule task, run alert and fire actions in a space', async () => { + const { body: createdAlert } = await supertest + .post('/s/space_1/api/alert') + .set('kbn-xsrf', 'foo') + .send( + getTestAlertData({ + interval: '1s', + alertTypeId: 'test.always-firing', + alertTypeParams: { + index: esTestIndexName, + reference: 'create-test-2', + }, + actions: [ + { + group: 'default', + id: SPACE_1_ES_ARCHIVER_ACTION_ID, + params: { + index: esTestIndexName, + reference: 'create-test-2', + message: + 'instanceContextValue: {{context.instanceContextValue}}, instanceStateValue: {{state.instanceStateValue}}', + }, + }, + ], + }) + ) + .expect(200); + createdAlertIds.push({ space: 'space_1', id: createdAlert.id }); + + const alertTestRecord = await retry.tryForTime(15000, async () => { + const searchResult = await es.search({ + index: esTestIndexName, + body: { + query: { + bool: { + must: [ + { + term: { + source: 'alert:test.always-firing', + }, + }, + { + term: { + reference: 'create-test-2', + }, + }, + ], + }, + }, + }, + }); + expect(searchResult.hits.total.value).to.eql(1); + return searchResult.hits.hits[0]; + }); + expect(alertTestRecord._source).to.eql({ + source: 'alert:test.always-firing', + reference: 'create-test-2', + state: {}, + params: { + index: esTestIndexName, + reference: 'create-test-2', + }, + }); + const actionTestRecord = await retry.tryForTime(15000, async () => { + const searchResult = await es.search({ + index: esTestIndexName, + body: { + query: { + bool: { + must: [ + { + term: { + source: 'action:test.index-record', + }, + }, + { + term: { + reference: 'create-test-2', + }, + }, + ], + }, + }, + }, + }); + expect(searchResult.hits.total.value).to.eql(1); + return searchResult.hits.hits[0]; + }); + expect(actionTestRecord._source).to.eql({ + config: { + unencrypted: `This value shouldn't get encrypted`, + }, + secrets: { + encrypted: 'This value should be encrypted', + }, + params: { + index: esTestIndexName, + reference: 'create-test-2', + message: 'instanceContextValue: true, instanceStateValue: true', + }, + reference: 'create-test-2', + source: 'action:test.index-record', + }); + }); + it('should handle custom retry logic', async () => { // We'll use this start time to query tasks created after this point const testStart = new Date(); @@ -175,7 +281,7 @@ export default function alertTests({ getService }: KibanaFunctionalTestDefaultPr }) ) .expect(200); - createdAlertIds.push(createdAlert.id); + createdAlertIds.push({ space: 'default', id: createdAlert.id }); const scheduledActionTask = await retry.tryForTime(15000, async () => { const searchResult = await es.search({ diff --git a/x-pack/test/api_integration/apis/alerting/constants.ts b/x-pack/test/api_integration/apis/alerting/constants.ts index d946111ae8bbb..8289111072b38 100644 --- a/x-pack/test/api_integration/apis/alerting/constants.ts +++ b/x-pack/test/api_integration/apis/alerting/constants.ts @@ -4,6 +4,10 @@ * you may not use this file except in compliance with the Elastic License. */ -import { ES_ARCHIVER_ACTION_ID as ActionArchiverActionId } from '../actions/constants'; +import { + ES_ARCHIVER_ACTION_ID as ActionArchiverActionId, + SPACE_1_ES_ARCHIVER_ACTION_ID as ActionArchiverSpace1ActionId, +} from '../actions/constants'; export const ES_ARCHIVER_ACTION_ID = ActionArchiverActionId; +export const SPACE_1_ES_ARCHIVER_ACTION_ID = ActionArchiverSpace1ActionId; diff --git a/x-pack/test/api_integration/apis/alerting/create.ts b/x-pack/test/api_integration/apis/alerting/create.ts index 4d9bceaee0d29..5923081a5f137 100644 --- a/x-pack/test/api_integration/apis/alerting/create.ts +++ b/x-pack/test/api_integration/apis/alerting/create.ts @@ -16,14 +16,15 @@ export default function createAlertTests({ getService }: KibanaFunctionalTestDef const es = getService('es'); describe('create', () => { - const createdAlertIds: string[] = []; + const createdAlertIds: Array<{ space: string; id: string }> = []; before(() => esArchiver.load('actions/basic')); after(async () => { await Promise.all( - createdAlertIds.map(id => { + createdAlertIds.map(({ space, id }) => { + const urlPrefix = space !== 'default' ? `/s/${space}` : ''; return supertest - .delete(`/api/alert/${id}`) + .delete(`${urlPrefix}/api/alert/${id}`) .set('kbn-xsrf', 'foo') .expect(204, ''); }) @@ -45,7 +46,7 @@ export default function createAlertTests({ getService }: KibanaFunctionalTestDef .send(getTestAlertData()) .expect(200) .then(async (resp: any) => { - createdAlertIds.push(resp.body.id); + createdAlertIds.push({ space: 'default', id: resp.body.id }); expect(resp.body).to.eql({ id: resp.body.id, actions: [ @@ -70,11 +71,46 @@ export default function createAlertTests({ getService }: KibanaFunctionalTestDef expect(taskRecord.task.taskType).to.eql('alerting:test.noop'); expect(JSON.parse(taskRecord.task.params)).to.eql({ alertId: resp.body.id, - basePath: '', + spaceId: 'default', }); }); }); + it('should return 200 when creating an alert in a space', async () => { + const { body: createdAlert } = await supertest + .post('/s/space_1/api/alert') + .set('kbn-xsrf', 'foo') + .send(getTestAlertData()) + .expect(200); + createdAlertIds.push({ space: 'space_1', id: createdAlert.id }); + expect(createdAlert).to.eql({ + id: createdAlert.id, + actions: [ + { + group: 'default', + id: ES_ARCHIVER_ACTION_ID, + params: { + message: + 'instanceContextValue: {{context.instanceContextValue}}, instanceStateValue: {{state.instanceStateValue}}', + }, + }, + ], + enabled: true, + alertTypeId: 'test.noop', + alertTypeParams: {}, + interval: '10s', + scheduledTaskId: createdAlert.scheduledTaskId, + }); + expect(typeof createdAlert.scheduledTaskId).to.be('string'); + const { _source: taskRecord } = await getScheduledTask(createdAlert.scheduledTaskId); + expect(taskRecord.type).to.eql('task'); + expect(taskRecord.task.taskType).to.eql('alerting:test.noop'); + expect(JSON.parse(taskRecord.task.params)).to.eql({ + alertId: createdAlert.id, + spaceId: 'space_1', + }); + }); + it('should not schedule a task when creating a disabled alert', async () => { const { body: createdAlert } = await supertest .post('/api/alert') diff --git a/x-pack/test/api_integration/apis/alerting/delete.ts b/x-pack/test/api_integration/apis/alerting/delete.ts index a287d75c85d4c..52e8ee4a9d8a5 100644 --- a/x-pack/test/api_integration/apis/alerting/delete.ts +++ b/x-pack/test/api_integration/apis/alerting/delete.ts @@ -17,6 +17,8 @@ export default function createDeleteTests({ getService }: KibanaFunctionalTestDe describe('delete', () => { let alertId: string; let scheduledTaskId: string; + let space1AlertId: string; + let space1ScheduledTaskId: string; before(async () => { await esArchiver.load('actions/basic'); @@ -29,6 +31,15 @@ export default function createDeleteTests({ getService }: KibanaFunctionalTestDe alertId = resp.body.id; scheduledTaskId = resp.body.scheduledTaskId; }); + await supertest + .post('/s/space_1/api/alert') + .set('kbn-xsrf', 'foo') + .send(getTestAlertData()) + .expect(200) + .then((resp: any) => { + space1AlertId = resp.body.id; + space1ScheduledTaskId = resp.body.scheduledTaskId; + }); }); after(() => esArchiver.unload('actions/basic')); @@ -53,5 +64,27 @@ export default function createDeleteTests({ getService }: KibanaFunctionalTestDe } expect(hasThrownError).to.eql(true); }); + + it('should return 404 when deleting an alert from another space', async () => { + await supertest + .delete(`/api/alert/${space1AlertId}`) + .set('kbn-xsrf', 'foo') + .expect(404); + }); + + it('should return 204 when deleting an alert in a space', async () => { + await supertest + .delete(`/s/space_1/api/alert/${space1AlertId}`) + .set('kbn-xsrf', 'foo') + .expect(204, ''); + let hasThrownError = false; + try { + await getScheduledTask(space1ScheduledTaskId); + } catch (e) { + hasThrownError = true; + expect(e.status).to.eql(404); + } + expect(hasThrownError).to.eql(true); + }); }); } diff --git a/x-pack/test/api_integration/apis/alerting/disable.ts b/x-pack/test/api_integration/apis/alerting/disable.ts index 44e49f3c2d1b6..a1dae1efa5bbe 100644 --- a/x-pack/test/api_integration/apis/alerting/disable.ts +++ b/x-pack/test/api_integration/apis/alerting/disable.ts @@ -15,7 +15,8 @@ export default function createDisableAlertTests({ const esArchiver = getService('esArchiver'); describe('disable', () => { - let createdAlert: any; + let alertId: string; + let space1AlertId: string; before(async () => { await esArchiver.load('actions/basic'); @@ -25,13 +26,25 @@ export default function createDisableAlertTests({ .send(getTestAlertData({ enabled: true })) .expect(200) .then((resp: any) => { - createdAlert = resp.body; + alertId = resp.body.id; + }); + await supertest + .post('/s/space_1/api/alert') + .set('kbn-xsrf', 'foo') + .send(getTestAlertData({ enabled: true })) + .expect(200) + .then((resp: any) => { + space1AlertId = resp.body.id; }); }); after(async () => { await supertest - .delete(`/api/alert/${createdAlert.id}`) + .delete(`/api/alert/${alertId}`) + .set('kbn-xsrf', 'foo') + .expect(204, ''); + await supertest + .delete(`/s/space_1/api/alert/${space1AlertId}`) .set('kbn-xsrf', 'foo') .expect(204, ''); await esArchiver.unload('actions/basic'); @@ -39,7 +52,21 @@ export default function createDisableAlertTests({ it('should return 204 when disabling an alert', async () => { await supertest - .post(`/api/alert/${createdAlert.id}/_disable`) + .post(`/api/alert/${alertId}/_disable`) + .set('kbn-xsrf', 'foo') + .expect(204); + }); + + it('should return 404 when disabling an alert from another space', async () => { + await supertest + .post(`/api/alert/${space1AlertId}/_disable`) + .set('kbn-xsrf', 'foo') + .expect(404); + }); + + it('should return 204 when disabling an alert in a space', async () => { + await supertest + .post(`/s/space_1/api/alert/${space1AlertId}/_disable`) .set('kbn-xsrf', 'foo') .expect(204); }); diff --git a/x-pack/test/api_integration/apis/alerting/enable.ts b/x-pack/test/api_integration/apis/alerting/enable.ts index 5222ea14b28e7..b91084aaa854d 100644 --- a/x-pack/test/api_integration/apis/alerting/enable.ts +++ b/x-pack/test/api_integration/apis/alerting/enable.ts @@ -15,7 +15,8 @@ export default function createEnableAlertTests({ const esArchiver = getService('esArchiver'); describe('enable', () => { - let createdAlert: any; + let alertId: string; + let space1AlertId: string; before(async () => { await esArchiver.load('actions/basic'); @@ -25,13 +26,25 @@ export default function createEnableAlertTests({ .send(getTestAlertData({ enabled: false })) .expect(200) .then((resp: any) => { - createdAlert = resp.body; + alertId = resp.body.id; + }); + await supertest + .post('/s/space_1/api/alert') + .set('kbn-xsrf', 'foo') + .send(getTestAlertData({ enabled: false })) + .expect(200) + .then((resp: any) => { + space1AlertId = resp.body.id; }); }); after(async () => { await supertest - .delete(`/api/alert/${createdAlert.id}`) + .delete(`/api/alert/${alertId}`) + .set('kbn-xsrf', 'foo') + .expect(204, ''); + await supertest + .delete(`/s/space_1/api/alert/${space1AlertId}`) .set('kbn-xsrf', 'foo') .expect(204, ''); await esArchiver.unload('actions/basic'); @@ -39,7 +52,21 @@ export default function createEnableAlertTests({ it('should return 204 when enabling an alert', async () => { await supertest - .post(`/api/alert/${createdAlert.id}/_enable`) + .post(`/api/alert/${alertId}/_enable`) + .set('kbn-xsrf', 'foo') + .expect(204); + }); + + it('should return 404 when enabling an alert from another space', async () => { + await supertest + .post(`/api/alert/${space1AlertId}/_enable`) + .set('kbn-xsrf', 'foo') + .expect(404); + }); + + it('should return 204 when enabling an alert in a space', async () => { + await supertest + .post(`/s/space_1/api/alert/${space1AlertId}/_enable`) .set('kbn-xsrf', 'foo') .expect(204); }); diff --git a/x-pack/test/api_integration/apis/alerting/find.ts b/x-pack/test/api_integration/apis/alerting/find.ts index 14b207db001b6..d438291ee212e 100644 --- a/x-pack/test/api_integration/apis/alerting/find.ts +++ b/x-pack/test/api_integration/apis/alerting/find.ts @@ -16,6 +16,7 @@ export default function createFindTests({ getService }: KibanaFunctionalTestDefa describe('find', () => { let alertId: string; + let space1AlertId: string; before(async () => { await esArchiver.load('actions/basic'); @@ -27,12 +28,24 @@ export default function createFindTests({ getService }: KibanaFunctionalTestDefa .then((resp: any) => { alertId = resp.body.id; }); + await supertest + .post('/s/space_1/api/alert') + .set('kbn-xsrf', 'foo') + .send(getTestAlertData()) + .expect(200) + .then((resp: any) => { + space1AlertId = resp.body.id; + }); }); after(async () => { await supertest .delete(`/api/alert/${alertId}`) .set('kbn-xsrf', 'foo') .expect(204, ''); + await supertest + .delete(`/s/space_1/api/alert/${space1AlertId}`) + .set('kbn-xsrf', 'foo') + .expect(204, ''); await esArchiver.unload('actions/basic'); }); @@ -66,5 +79,32 @@ export default function createFindTests({ getService }: KibanaFunctionalTestDefa }); }); }); + + it('should return 200 when finding alerts in a space', async () => { + await supertest + .get('/s/space_1/api/alert/_find') + .expect(200) + .then((resp: any) => { + const match = resp.body.data.find((obj: any) => obj.id === space1AlertId); + expect(match).to.eql({ + id: space1AlertId, + alertTypeId: 'test.noop', + interval: '10s', + enabled: true, + actions: [ + { + group: 'default', + id: ES_ARCHIVER_ACTION_ID, + params: { + message: + 'instanceContextValue: {{context.instanceContextValue}}, instanceStateValue: {{state.instanceStateValue}}', + }, + }, + ], + alertTypeParams: {}, + scheduledTaskId: match.scheduledTaskId, + }); + }); + }); }); } diff --git a/x-pack/test/api_integration/apis/alerting/get.ts b/x-pack/test/api_integration/apis/alerting/get.ts index d201307949f57..bb39c38a64e21 100644 --- a/x-pack/test/api_integration/apis/alerting/get.ts +++ b/x-pack/test/api_integration/apis/alerting/get.ts @@ -16,6 +16,7 @@ export default function createGetTests({ getService }: KibanaFunctionalTestDefau describe('get', () => { let alertId: string; + let space1AlertId: string; before(async () => { await esArchiver.load('actions/basic'); @@ -27,12 +28,24 @@ export default function createGetTests({ getService }: KibanaFunctionalTestDefau .then((resp: any) => { alertId = resp.body.id; }); + await supertest + .post('/s/space_1/api/alert') + .set('kbn-xsrf', 'foo') + .send(getTestAlertData()) + .expect(200) + .then((resp: any) => { + space1AlertId = resp.body.id; + }); }); after(async () => { await supertest .delete(`/api/alert/${alertId}`) .set('kbn-xsrf', 'foo') .expect(204, ''); + await supertest + .delete(`/s/space_1/api/alert/${space1AlertId}`) + .set('kbn-xsrf', 'foo') + .expect(204, ''); await esArchiver.unload('actions/basic'); }); @@ -61,5 +74,35 @@ export default function createGetTests({ getService }: KibanaFunctionalTestDefau }); }); }); + + it('should return 404 when gettin an alert in another space', async () => { + await supertest.get(`/api/alert/${space1AlertId}`).expect(404); + }); + + it('should return 200 when getting an alert in a space', async () => { + await supertest + .get(`/s/space_1/api/alert/${space1AlertId}`) + .expect(200) + .then((resp: any) => { + expect(resp.body).to.eql({ + id: space1AlertId, + alertTypeId: 'test.noop', + interval: '10s', + enabled: true, + actions: [ + { + group: 'default', + id: ES_ARCHIVER_ACTION_ID, + params: { + message: + 'instanceContextValue: {{context.instanceContextValue}}, instanceStateValue: {{state.instanceStateValue}}', + }, + }, + ], + alertTypeParams: {}, + scheduledTaskId: resp.body.scheduledTaskId, + }); + }); + }); }); } diff --git a/x-pack/test/api_integration/apis/alerting/update.ts b/x-pack/test/api_integration/apis/alerting/update.ts index 4c50f48244133..b08489fcef3b7 100644 --- a/x-pack/test/api_integration/apis/alerting/update.ts +++ b/x-pack/test/api_integration/apis/alerting/update.ts @@ -6,7 +6,7 @@ import expect from '@kbn/expect'; import { getTestAlertData } from './utils'; -import { ES_ARCHIVER_ACTION_ID } from './constants'; +import { ES_ARCHIVER_ACTION_ID, SPACE_1_ES_ARCHIVER_ACTION_ID } from './constants'; import { KibanaFunctionalTestDefaultProviders } from '../../../types/providers'; // eslint-disable-next-line import/no-default-export @@ -15,7 +15,8 @@ export default function createUpdateTests({ getService }: KibanaFunctionalTestDe const esArchiver = getService('esArchiver'); describe('update', () => { - let createdAlert: any; + let alertId: string; + let space1AlertId: string; before(async () => { await esArchiver.load('actions/basic'); @@ -25,20 +26,62 @@ export default function createUpdateTests({ getService }: KibanaFunctionalTestDe .send(getTestAlertData()) .expect(200) .then((resp: any) => { - createdAlert = resp.body; + alertId = resp.body.id; + }); + await supertest + .post('/s/space_1/api/alert') + .set('kbn-xsrf', 'foo') + .send(getTestAlertData()) + .expect(200) + .then((resp: any) => { + space1AlertId = resp.body.id; }); }); after(async () => { await supertest - .delete(`/api/alert/${createdAlert.id}`) + .delete(`/api/alert/${alertId}`) + .set('kbn-xsrf', 'foo') + .expect(204, ''); + await supertest + .delete(`/s/space_1/api/alert/${space1AlertId}`) .set('kbn-xsrf', 'foo') .expect(204, ''); await esArchiver.unload('actions/basic'); }); it('should return 200 when updating an alert', async () => { + const alert = { + alertTypeParams: { + foo: true, + }, + interval: '12s', + actions: [ + { + group: 'default', + id: ES_ARCHIVER_ACTION_ID, + params: { + message: + 'UPDATED: instanceContextValue: {{context.instanceContextValue}}, instanceStateValue: {{state.instanceStateValue}}', + }, + }, + ], + }; await supertest - .put(`/api/alert/${createdAlert.id}`) + .put(`/api/alert/${alertId}`) + .set('kbn-xsrf', 'foo') + .send(alert) + .expect(200) + .then((resp: any) => { + expect(resp.body).to.eql({ + ...alert, + id: alertId, + }); + }); + }); + + it('should return 404 when updating an alert from another space', async () => { + await supertest + .put(`/api/alert/${space1AlertId}`) .set('kbn-xsrf', 'foo') .send({ alertTypeParams: { @@ -56,31 +99,42 @@ export default function createUpdateTests({ getService }: KibanaFunctionalTestDe }, ], }) + .expect(404); + }); + + it('should return 200 when updating an alert in a space', async () => { + const alert = { + alertTypeParams: { + foo: true, + }, + interval: '12s', + actions: [ + { + group: 'default', + id: SPACE_1_ES_ARCHIVER_ACTION_ID, + params: { + message: + 'UPDATED: instanceContextValue: {{context.instanceContextValue}}, instanceStateValue: {{state.instanceStateValue}}', + }, + }, + ], + }; + await supertest + .put(`/s/space_1/api/alert/${space1AlertId}`) + .set('kbn-xsrf', 'foo') + .send(alert) .expect(200) .then((resp: any) => { expect(resp.body).to.eql({ - id: createdAlert.id, - alertTypeParams: { - foo: true, - }, - interval: '12s', - actions: [ - { - group: 'default', - id: ES_ARCHIVER_ACTION_ID, - params: { - message: - 'UPDATED: instanceContextValue: {{context.instanceContextValue}}, instanceStateValue: {{state.instanceStateValue}}', - }, - }, - ], + ...alert, + id: space1AlertId, }); }); }); it('should return 400 when attempting to change alert type', async () => { await supertest - .put(`/api/alert/${createdAlert.id}`) + .put(`/api/alert/${alertId}`) .set('kbn-xsrf', 'foo') .send({ alertTypeId: '1', @@ -115,7 +169,7 @@ export default function createUpdateTests({ getService }: KibanaFunctionalTestDe it('should return 400 when payload is empty and invalid', async () => { await supertest - .put(`/api/alert/${createdAlert.id}`) + .put(`/api/alert/${alertId}`) .set('kbn-xsrf', 'foo') .send({}) .expect(400) @@ -167,7 +221,7 @@ export default function createUpdateTests({ getService }: KibanaFunctionalTestDe it(`should return 400 when interval is wrong syntax`, async () => { const { body: error } = await supertest - .put(`/api/alert/${createdAlert.id}`) + .put(`/api/alert/${alertId}`) .set('kbn-xsrf', 'foo') .send(getTestAlertData({ interval: '10x', enabled: undefined })) .expect(400); diff --git a/x-pack/test/functional/es_archives/actions/basic/data.json b/x-pack/test/functional/es_archives/actions/basic/data.json index 2049fcd3f1b8c..b8dc9e307ee9e 100644 --- a/x-pack/test/functional/es_archives/actions/basic/data.json +++ b/x-pack/test/functional/es_archives/actions/basic/data.json @@ -48,3 +48,22 @@ } } } + +{ + "value": { + "id" : "space_1:action:6c7d0f6b-2fb5-4821-b182-624fc3ccc7a3", + "index" : ".kibana", + "source" : { + "action" : { + "description" : "My action", + "actionTypeId" : "test.index-record", + "config" : { + "unencrypted" : "This value shouldn't get encrypted" + }, + "secrets" : "LEepwqGVUSkkAZSYCGDz3Y0DGoRgBGPRnu+Ta0jmEz+TiGUj3SBYWL6t2jqncBhsWgzSzwCusY+z5B/k+4wjaXW5t/KxNZP7bGpLQK0hL9IwKxqmRzRbEvX0nzeExxgfSaMRjjn2SnrE21MTw6qyBwGNqnuvmN7ILde4ZUbR9Jyjl8A6Y0GDcWvN" + }, + "type": "action", + "namespace": "space_1" + } + } +}