diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index e54b7b56707e8..50d157a1da3dc 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -149,6 +149,7 @@ # Kibana Alerting Services /x-pack/legacy/plugins/alerting/ @elastic/kibana-alerting-services /x-pack/legacy/plugins/actions/ @elastic/kibana-alerting-services +/x-pack/plugins/alerting/ @elastic/kibana-alerting-services /x-pack/plugins/actions/ @elastic/kibana-alerting-services /x-pack/plugins/event_log/ @elastic/kibana-alerting-services /x-pack/plugins/task_manager/ @elastic/kibana-alerting-services @@ -156,6 +157,7 @@ /x-pack/test/plugin_api_integration/plugins/task_manager/ @elastic/kibana-alerting-services /x-pack/test/plugin_api_integration/test_suites/task_manager/ @elastic/kibana-alerting-services /x-pack/legacy/plugins/triggers_actions_ui/ @elastic/kibana-alerting-services +/x-pack/plugins/triggers_actions_ui/ @elastic/kibana-alerting-services /x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/ @elastic/kibana-alerting-services /x-pack/test/functional_with_es_ssl/fixtures/plugins/alerts/ @elastic/kibana-alerting-services diff --git a/docs/api/dashboard/import-dashboard.asciidoc b/docs/api/dashboard/import-dashboard.asciidoc index 0c6ea2bcf5933..14817719ec7ee 100644 --- a/docs/api/dashboard/import-dashboard.asciidoc +++ b/docs/api/dashboard/import-dashboard.asciidoc @@ -16,7 +16,7 @@ experimental[] Import dashboards and corresponding saved objects. `force`:: (Optional, boolean) Overwrite any existing objects on ID conflict. - + `exclude`:: (Optional, array) Saved object types that you want to exclude from the import. @@ -28,14 +28,14 @@ Use the complete response body from the < { +beforeAll(async () => { await del(TMP_DIR); await cpy('**/*', MOCK_REPO_DIR, { cwd: MOCK_REPO_SRC, @@ -42,7 +42,7 @@ beforeEach(async () => { }); }); -afterEach(async () => { +afterAll(async () => { await del(TMP_DIR); }); @@ -153,3 +153,32 @@ it('builds expected bundles, saves bundle counts to metadata', async () => { ] `); }); + +it('uses cache on second run and exist cleanly', async () => { + const config = OptimizerConfig.create({ + repoRoot: MOCK_REPO_DIR, + pluginScanDirs: [Path.resolve(MOCK_REPO_DIR, 'plugins')], + maxWorkerCount: 1, + }); + + const msgs = await runOptimizer(config) + .pipe( + tap(state => { + if (state.event?.type === 'worker stdio') { + // eslint-disable-next-line no-console + console.log('worker', state.event.stream, state.event.chunk.toString('utf8')); + } + }), + toArray() + ) + .toPromise(); + + expect(msgs.map(m => m.state.phase)).toMatchInlineSnapshot(` + Array [ + "initializing", + "initializing", + "initializing", + "initialized", + ] + `); +}); diff --git a/packages/kbn-optimizer/src/optimizer/handle_optimizer_completion.ts b/packages/kbn-optimizer/src/optimizer/handle_optimizer_completion.ts index fe2fa320818a2..9587902cc4187 100644 --- a/packages/kbn-optimizer/src/optimizer/handle_optimizer_completion.ts +++ b/packages/kbn-optimizer/src/optimizer/handle_optimizer_completion.ts @@ -44,6 +44,11 @@ export function handleOptimizerCompletion(config: OptimizerConfig) { return; } + if (prevState?.phase === 'initialized' && prevState.onlineBundles.length === 0) { + // all bundles cached + return; + } + if (prevState?.phase === 'issue') { throw createFailError('webpack issue'); } diff --git a/src/core/MIGRATION.md b/src/core/MIGRATION.md index fa0edd8faadd7..9e57fc4c36876 100644 --- a/src/core/MIGRATION.md +++ b/src/core/MIGRATION.md @@ -1197,8 +1197,8 @@ In server code, `core` can be accessed from either `server.newPlatform` or `kbnS | `server.route` | [`core.http.createRouter`](/docs/development/core/server/kibana-plugin-server.httpservicesetup.createrouter.md) | [Examples](./MIGRATION_EXAMPLES.md#route-registration) | | `server.renderApp()` / `server.renderAppWithDefaultConfig()` | [`context.rendering.render()`](/docs/development/core/server/kibana-plugin-server.iscopedrenderingclient.render.md) | [Examples](./MIGRATION_EXAMPLES.md#render-html-content) | | `request.getBasePath()` | [`core.http.basePath.get`](/docs/development/core/server/kibana-plugin-server.httpservicesetup.basepath.md) | | -| `server.plugins.elasticsearch.getCluster('data')` | [`context.elasticsearch.dataClient`](/docs/development/core/server/kibana-plugin-server.iscopedclusterclient.md) | | -| `server.plugins.elasticsearch.getCluster('admin')` | [`context.elasticsearch.adminClient`](/docs/development/core/server/kibana-plugin-server.iscopedclusterclient.md) | | +| `server.plugins.elasticsearch.getCluster('data')` | [`context.core.elasticsearch.dataClient`](/docs/development/core/server/kibana-plugin-server.iscopedclusterclient.md) | | +| `server.plugins.elasticsearch.getCluster('admin')` | [`context.core.elasticsearch.adminClient`](/docs/development/core/server/kibana-plugin-server.iscopedclusterclient.md) | | | `server.plugins.elasticsearch.createCluster(...)` | [`core.elasticsearch.createClient`](/docs/development/core/server/kibana-plugin-server.elasticsearchservicesetup.createclient.md) | | | `server.savedObjects.setScopedSavedObjectsClientFactory` | [`core.savedObjects.setClientFactory`](/docs/development/core/server/kibana-plugin-server.savedobjectsservicesetup.setclientfactory.md) | | | `server.savedObjects.addScopedSavedObjectsClientWrapperFactory` | [`core.savedObjects.addClientWrapper`](/docs/development/core/server/kibana-plugin-server.savedobjectsservicesetup.addclientwrapper.md) | | diff --git a/x-pack/.i18nrc.json b/x-pack/.i18nrc.json index 032efcee20afd..99882a6e235e2 100644 --- a/x-pack/.i18nrc.json +++ b/x-pack/.i18nrc.json @@ -3,7 +3,7 @@ "paths": { "xpack.actions": "plugins/actions", "xpack.advancedUiActions": "plugins/advanced_ui_actions", - "xpack.alerting": "legacy/plugins/alerting", + "xpack.alerting": "plugins/alerting", "xpack.triggersActionsUI": "plugins/triggers_actions_ui", "xpack.apm": ["legacy/plugins/apm", "plugins/apm"], "xpack.beatsManagement": "legacy/plugins/beats_management", diff --git a/x-pack/legacy/plugins/alerting/index.ts b/x-pack/legacy/plugins/alerting/index.ts index 5baec07fa1182..0d0a698841269 100644 --- a/x-pack/legacy/plugins/alerting/index.ts +++ b/x-pack/legacy/plugins/alerting/index.ts @@ -4,43 +4,4 @@ * you may not use this file except in compliance with the Elastic License. */ -import { Legacy } from 'kibana'; -import { Root } from 'joi'; -import { init } from './server'; -import mappings from './mappings.json'; - -export { - AlertingPlugin, - AlertsClient, - AlertType, - AlertExecutorOptions, - PluginSetupContract, - PluginStartContract, -} from './server'; - -export function alerting(kibana: any) { - return new kibana.Plugin({ - id: 'alerting', - configPrefix: 'xpack.alerting', - require: ['kibana', 'elasticsearch', 'actions', 'task_manager', 'encryptedSavedObjects'], - isEnabled(config: Legacy.KibanaConfig) { - return ( - config.get('xpack.alerting.enabled') === true && - config.get('xpack.actions.enabled') === true && - config.get('xpack.encryptedSavedObjects.enabled') === true && - config.get('xpack.task_manager.enabled') === true - ); - }, - config(Joi: Root) { - return Joi.object() - .keys({ - enabled: Joi.boolean().default(true), - }) - .default(); - }, - init, - uiExports: { - mappings, - }, - }); -} +export * from './server'; diff --git a/x-pack/legacy/plugins/alerting/server/extend_route_with_license_check.test.ts b/x-pack/legacy/plugins/alerting/server/extend_route_with_license_check.test.ts deleted file mode 100644 index 05c8424881625..0000000000000 --- a/x-pack/legacy/plugins/alerting/server/extend_route_with_license_check.test.ts +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ -import { extendRouteWithLicenseCheck } from './extend_route_with_license_check'; -import { LicenseState } from './lib/license_state'; -jest.mock('./lib/license_state', () => ({ - verifyApiAccessFactory: () => {}, -})); - -describe('extendRouteWithLicenseCheck', () => { - describe('#actionsextendRouteWithLicenseCheck', () => { - let licenseState: jest.Mocked; - - test('extends route object with license, if config property already exists', () => { - const newRoute = extendRouteWithLicenseCheck( - { config: { someTestProperty: 'test' } }, - licenseState - ); - expect(newRoute.config.pre.length > 0); - }); - test('extends route object with license check under options.pre', () => { - const newRoute = extendRouteWithLicenseCheck( - { options: { someProperty: 'test' } }, - licenseState - ); - expect(newRoute.options.pre.length > 0); - }); - }); -}); diff --git a/x-pack/legacy/plugins/alerting/server/extend_route_with_license_check.ts b/x-pack/legacy/plugins/alerting/server/extend_route_with_license_check.ts deleted file mode 100644 index f39dc125071b4..0000000000000 --- a/x-pack/legacy/plugins/alerting/server/extend_route_with_license_check.ts +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ -import { LicenseState, verifyApiAccessFactory } from './lib/license_state'; - -export function extendRouteWithLicenseCheck(route: any, licenseState: LicenseState) { - const verifyApiAccessPreRouting = verifyApiAccessFactory(licenseState); - - const key = route.options ? 'options' : 'config'; - return { - ...route, - [key]: { - ...route[key], - pre: [verifyApiAccessPreRouting], - }, - }; -} diff --git a/x-pack/legacy/plugins/alerting/server/index.ts b/x-pack/legacy/plugins/alerting/server/index.ts index 44ae1964ec98a..5bf7cda51bda6 100644 --- a/x-pack/legacy/plugins/alerting/server/index.ts +++ b/x-pack/legacy/plugins/alerting/server/index.ts @@ -4,10 +4,32 @@ * you may not use this file except in compliance with the Elastic License. */ -import { AlertsClient as AlertsClientClass } from './alerts_client'; +import { Legacy } from 'kibana'; +import { Root } from 'joi'; +import mappings from './mappings.json'; -export type AlertsClient = PublicMethodsOf; - -export { init } from './init'; -export { AlertType, AlertingPlugin, AlertExecutorOptions } from './types'; -export { PluginSetupContract, PluginStartContract } from './plugin'; +export function alerting(kibana: any) { + return new kibana.Plugin({ + id: 'alerting', + configPrefix: 'xpack.alerting', + require: ['kibana', 'elasticsearch', 'actions', 'task_manager', 'encryptedSavedObjects'], + isEnabled(config: Legacy.KibanaConfig) { + return ( + config.get('xpack.alerting.enabled') === true && + config.get('xpack.actions.enabled') === true && + config.get('xpack.encryptedSavedObjects.enabled') === true && + config.get('xpack.task_manager.enabled') === true + ); + }, + config(Joi: Root) { + return Joi.object() + .keys({ + enabled: Joi.boolean().default(true), + }) + .default(); + }, + uiExports: { + mappings, + }, + }); +} diff --git a/x-pack/legacy/plugins/alerting/server/init.ts b/x-pack/legacy/plugins/alerting/server/init.ts deleted file mode 100644 index d76bc33f3fa0a..0000000000000 --- a/x-pack/legacy/plugins/alerting/server/init.ts +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { Server, shim } from './shim'; -import { Plugin } from './plugin'; -import { AlertingPlugin } from './types'; - -export async function init(server: Server) { - const { initializerContext, coreSetup, coreStart, pluginsSetup, pluginsStart } = shim(server); - - const plugin = new Plugin(initializerContext); - - const setupContract = await plugin.setup(coreSetup, pluginsSetup); - const startContract = plugin.start(coreStart, pluginsStart); - - server.decorate('request', 'getAlertsClient', function() { - return startContract.getAlertsClientWithRequest(this); - }); - - const exposedFunctions: AlertingPlugin = { - setup: setupContract, - start: startContract, - }; - server.expose(exposedFunctions); -} diff --git a/x-pack/legacy/plugins/alerting/mappings.json b/x-pack/legacy/plugins/alerting/server/mappings.json similarity index 100% rename from x-pack/legacy/plugins/alerting/mappings.json rename to x-pack/legacy/plugins/alerting/server/mappings.json diff --git a/x-pack/legacy/plugins/alerting/server/plugin.ts b/x-pack/legacy/plugins/alerting/server/plugin.ts deleted file mode 100644 index 2567e470d2e79..0000000000000 --- a/x-pack/legacy/plugins/alerting/server/plugin.ts +++ /dev/null @@ -1,176 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import Hapi from 'hapi'; - -import { Services } from './types'; -import { AlertsClient } from './alerts_client'; -import { AlertTypeRegistry } from './alert_type_registry'; -import { TaskRunnerFactory } from './task_runner'; -import { AlertsClientFactory } from './alerts_client_factory'; -import { LicenseState } from './lib/license_state'; -import { IClusterClient, KibanaRequest, Logger } from '../../../../../src/core/server'; -import { - AlertingPluginInitializerContext, - AlertingCoreSetup, - AlertingCoreStart, - AlertingPluginsSetup, - AlertingPluginsStart, -} from './shim'; -import { - createAlertRoute, - deleteAlertRoute, - findAlertRoute, - getAlertRoute, - getAlertStateRoute, - listAlertTypesRoute, - updateAlertRoute, - enableAlertRoute, - disableAlertRoute, - updateApiKeyRoute, - muteAllAlertRoute, - unmuteAllAlertRoute, - muteAlertInstanceRoute, - unmuteAlertInstanceRoute, -} from './routes'; -import { extendRouteWithLicenseCheck } from './extend_route_with_license_check'; - -export interface PluginSetupContract { - registerType: AlertTypeRegistry['register']; -} -export interface PluginStartContract { - listTypes: AlertTypeRegistry['list']; - getAlertsClientWithRequest(request: Hapi.Request): PublicMethodsOf; -} - -export class Plugin { - private readonly logger: Logger; - private alertTypeRegistry?: AlertTypeRegistry; - private readonly taskRunnerFactory: TaskRunnerFactory; - private adminClient?: IClusterClient; - private serverBasePath?: string; - private licenseState: LicenseState | null = null; - private isESOUsingEphemeralEncryptionKey?: boolean; - - constructor(initializerContext: AlertingPluginInitializerContext) { - this.logger = initializerContext.logger.get('plugins', 'alerting'); - this.taskRunnerFactory = new TaskRunnerFactory(); - } - - public async setup( - core: AlertingCoreSetup, - plugins: AlertingPluginsSetup - ): Promise { - this.adminClient = core.elasticsearch.adminClient; - this.licenseState = new LicenseState(plugins.licensing.license$); - this.isESOUsingEphemeralEncryptionKey = - plugins.encryptedSavedObjects.usingEphemeralEncryptionKey; - - if (this.isESOUsingEphemeralEncryptionKey) { - this.logger.warn( - 'APIs are disabled due to the Encrypted Saved Objects plugin using an ephemeral encryption key. Please set xpack.encryptedSavedObjects.encryptionKey in kibana.yml.' - ); - } - - // Encrypted attributes - plugins.encryptedSavedObjects.registerType({ - type: 'alert', - attributesToEncrypt: new Set(['apiKey']), - attributesToExcludeFromAAD: new Set([ - 'scheduledTaskId', - 'muteAll', - 'mutedInstanceIds', - 'updatedBy', - ]), - }); - - const alertTypeRegistry = new AlertTypeRegistry({ - taskManager: plugins.taskManager, - taskRunnerFactory: this.taskRunnerFactory, - }); - this.alertTypeRegistry = alertTypeRegistry; - this.serverBasePath = core.http.basePath.serverBasePath; - - // Register routes - core.http.route(extendRouteWithLicenseCheck(createAlertRoute, this.licenseState)); - core.http.route(extendRouteWithLicenseCheck(deleteAlertRoute, this.licenseState)); - core.http.route(extendRouteWithLicenseCheck(findAlertRoute, this.licenseState)); - core.http.route(extendRouteWithLicenseCheck(getAlertRoute, this.licenseState)); - core.http.route(extendRouteWithLicenseCheck(getAlertStateRoute, this.licenseState)); - core.http.route(extendRouteWithLicenseCheck(listAlertTypesRoute, this.licenseState)); - core.http.route(extendRouteWithLicenseCheck(updateAlertRoute, this.licenseState)); - core.http.route(extendRouteWithLicenseCheck(enableAlertRoute, this.licenseState)); - core.http.route(extendRouteWithLicenseCheck(disableAlertRoute, this.licenseState)); - core.http.route(extendRouteWithLicenseCheck(updateApiKeyRoute, this.licenseState)); - core.http.route(extendRouteWithLicenseCheck(muteAllAlertRoute, this.licenseState)); - core.http.route(extendRouteWithLicenseCheck(unmuteAllAlertRoute, this.licenseState)); - core.http.route(extendRouteWithLicenseCheck(muteAlertInstanceRoute, this.licenseState)); - core.http.route(extendRouteWithLicenseCheck(unmuteAlertInstanceRoute, this.licenseState)); - - return { - registerType: alertTypeRegistry.register.bind(alertTypeRegistry), - }; - } - - public start(core: AlertingCoreStart, plugins: AlertingPluginsStart): PluginStartContract { - const { adminClient, serverBasePath, isESOUsingEphemeralEncryptionKey } = this; - - function spaceIdToNamespace(spaceId?: string): string | undefined { - const spacesPlugin = plugins.spaces(); - return spacesPlugin && spaceId ? spacesPlugin.spaceIdToNamespace(spaceId) : undefined; - } - - const alertsClientFactory = new AlertsClientFactory({ - alertTypeRegistry: this.alertTypeRegistry!, - logger: this.logger, - taskManager: plugins.taskManager, - securityPluginSetup: plugins.security, - encryptedSavedObjectsPlugin: plugins.encryptedSavedObjects, - spaceIdToNamespace, - getSpaceId(request: Hapi.Request) { - const spacesPlugin = plugins.spaces(); - return spacesPlugin ? spacesPlugin.getSpaceId(request) : undefined; - }, - }); - - this.taskRunnerFactory.initialize({ - logger: this.logger, - getServices(rawRequest: Hapi.Request): Services { - const request = KibanaRequest.from(rawRequest); - return { - callCluster: (...args) => adminClient!.asScoped(request).callAsCurrentUser(...args), - // rawRequest is actually a fake request, converting it to KibanaRequest causes issue in SO access - savedObjectsClient: core.savedObjects.getScopedSavedObjectsClient(rawRequest as any), - }; - }, - spaceIdToNamespace, - executeAction: plugins.actions.execute, - encryptedSavedObjectsPlugin: plugins.encryptedSavedObjects, - getBasePath(spaceId?: string): string { - const spacesPlugin = plugins.spaces(); - return spacesPlugin && spaceId ? spacesPlugin.getBasePath(spaceId) : serverBasePath!; - }, - }); - - return { - listTypes: this.alertTypeRegistry!.list.bind(this.alertTypeRegistry!), - getAlertsClientWithRequest: (request: Hapi.Request) => { - if (isESOUsingEphemeralEncryptionKey === true) { - throw new Error( - `Unable to create alerts client due to the Encrypted Saved Objects plugin using an ephemeral encryption key. Please set xpack.encryptedSavedObjects.encryptionKey in kibana.yml` - ); - } - return alertsClientFactory!.create(KibanaRequest.from(request), request); - }, - }; - } - - public stop() { - if (this.licenseState) { - this.licenseState.clean(); - } - } -} diff --git a/x-pack/legacy/plugins/alerting/server/routes/_mock_server.ts b/x-pack/legacy/plugins/alerting/server/routes/_mock_server.ts deleted file mode 100644 index 0f865a12067ad..0000000000000 --- a/x-pack/legacy/plugins/alerting/server/routes/_mock_server.ts +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import Hapi from 'hapi'; -import { alertsClientMock } from '../alerts_client.mock'; -import { alertTypeRegistryMock } from '../alert_type_registry.mock'; - -const defaultConfig = { - 'kibana.index': '.kibana', -}; - -export function createMockServer(config: Record = defaultConfig) { - const server = new Hapi.Server({ - port: 0, - }); - - const alertsClient = alertsClientMock.create(); - const alertTypeRegistry = alertTypeRegistryMock.create(); - - server.config = () => { - return { - get(key: string) { - return config[key]; - }, - has(key: string) { - return config.hasOwnProperty(key); - }, - }; - }; - - server.register({ - name: 'alerting', - register(pluginServer: Hapi.Server) { - pluginServer.expose({ - setup: { - registerType: alertTypeRegistry.register, - }, - start: { - listTypes: alertTypeRegistry.list, - }, - }); - }, - }); - - server.decorate('request', 'getAlertsClient', () => alertsClient); - server.decorate('request', 'getBasePath', () => '/s/default'); - - return { server, alertsClient, alertTypeRegistry }; -} diff --git a/x-pack/legacy/plugins/alerting/server/routes/create.test.ts b/x-pack/legacy/plugins/alerting/server/routes/create.test.ts deleted file mode 100644 index 2a0ae78fd78b2..0000000000000 --- a/x-pack/legacy/plugins/alerting/server/routes/create.test.ts +++ /dev/null @@ -1,136 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { omit } from 'lodash'; -import { createMockServer } from './_mock_server'; -import { createAlertRoute } from './create'; - -const { server, alertsClient } = createMockServer(); -server.route(createAlertRoute); - -const mockedAlert = { - alertTypeId: '1', - consumer: 'bar', - name: 'abc', - schedule: { interval: '10s' }, - tags: ['foo'], - params: { - bar: true, - }, - throttle: '30s', - actions: [ - { - group: 'default', - id: '2', - params: { - foo: true, - }, - }, - ], -}; - -beforeEach(() => jest.resetAllMocks()); - -test('creates an alert with proper parameters', async () => { - const request = { - method: 'POST', - url: '/api/alert', - payload: mockedAlert, - }; - - const createdAt = new Date(); - const updatedAt = new Date(); - alertsClient.create.mockResolvedValueOnce({ - ...mockedAlert, - enabled: true, - muteAll: false, - createdBy: '', - updatedBy: '', - apiKey: '', - apiKeyOwner: '', - mutedInstanceIds: [], - createdAt, - updatedAt, - id: '123', - actions: [ - { - ...mockedAlert.actions[0], - actionTypeId: 'test', - }, - ], - }); - const { payload, statusCode } = await server.inject(request); - expect(statusCode).toBe(200); - const response = JSON.parse(payload); - expect(new Date(response.createdAt)).toEqual(createdAt); - expect(omit(response, 'createdAt', 'updatedAt')).toMatchInlineSnapshot(` - Object { - "actions": Array [ - Object { - "actionTypeId": "test", - "group": "default", - "id": "2", - "params": Object { - "foo": true, - }, - }, - ], - "alertTypeId": "1", - "apiKey": "", - "apiKeyOwner": "", - "consumer": "bar", - "createdBy": "", - "enabled": true, - "id": "123", - "muteAll": false, - "mutedInstanceIds": Array [], - "name": "abc", - "params": Object { - "bar": true, - }, - "schedule": Object { - "interval": "10s", - }, - "tags": Array [ - "foo", - ], - "throttle": "30s", - "updatedBy": "", - } - `); - 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", - "consumer": "bar", - "enabled": true, - "name": "abc", - "params": Object { - "bar": true, - }, - "schedule": Object { - "interval": "10s", - }, - "tags": Array [ - "foo", - ], - "throttle": "30s", - }, - }, - ] - `); -}); diff --git a/x-pack/legacy/plugins/alerting/server/routes/create.ts b/x-pack/legacy/plugins/alerting/server/routes/create.ts deleted file mode 100644 index 362a23a3fa910..0000000000000 --- a/x-pack/legacy/plugins/alerting/server/routes/create.ts +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import Hapi from 'hapi'; -import Joi from 'joi'; -import { getDurationSchema } from '../lib'; -import { IntervalSchedule } from '../types'; - -interface ScheduleRequest extends Hapi.Request { - payload: { - enabled: boolean; - name: string; - tags: string[]; - alertTypeId: string; - consumer: string; - schedule: IntervalSchedule; - actions: Array<{ - group: string; - id: string; - params: Record; - }>; - params: Record; - throttle: string | null; - }; -} - -export const createAlertRoute = { - method: 'POST', - path: '/api/alert', - options: { - tags: ['access:alerting-all'], - validate: { - options: { - abortEarly: false, - }, - payload: Joi.object() - .keys({ - enabled: Joi.boolean().default(true), - name: Joi.string().required(), - tags: Joi.array() - .items(Joi.string()) - .default([]), - alertTypeId: Joi.string().required(), - consumer: Joi.string().required(), - throttle: getDurationSchema().default(null), - schedule: Joi.object() - .keys({ - interval: getDurationSchema().required(), - }) - .required(), - params: Joi.object().required(), - actions: Joi.array() - .items( - Joi.object().keys({ - group: Joi.string().required(), - id: Joi.string().required(), - params: Joi.object().required(), - }) - ) - .required(), - }) - .required(), - }, - }, - async handler(request: ScheduleRequest) { - const alertsClient = request.getAlertsClient!(); - return await alertsClient.create({ data: request.payload }); - }, -}; diff --git a/x-pack/legacy/plugins/alerting/server/routes/delete.test.ts b/x-pack/legacy/plugins/alerting/server/routes/delete.test.ts deleted file mode 100644 index b7b25538847fa..0000000000000 --- a/x-pack/legacy/plugins/alerting/server/routes/delete.test.ts +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { createMockServer } from './_mock_server'; -import { deleteAlertRoute } from './delete'; - -const { server, alertsClient } = createMockServer(); -server.route(deleteAlertRoute); - -beforeEach(() => jest.resetAllMocks()); - -test('deletes an alert with proper parameters', async () => { - const request = { - method: 'DELETE', - url: '/api/alert/1', - }; - - alertsClient.delete.mockResolvedValueOnce({}); - const { payload, statusCode } = await server.inject(request); - expect(statusCode).toBe(204); - expect(payload).toEqual(''); - expect(alertsClient.delete).toHaveBeenCalledTimes(1); - expect(alertsClient.delete.mock.calls[0]).toMatchInlineSnapshot(` -Array [ - Object { - "id": "1", - }, -] -`); -}); diff --git a/x-pack/legacy/plugins/alerting/server/routes/delete.ts b/x-pack/legacy/plugins/alerting/server/routes/delete.ts deleted file mode 100644 index 24a1f6777b17b..0000000000000 --- a/x-pack/legacy/plugins/alerting/server/routes/delete.ts +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import Hapi from 'hapi'; -import Joi from 'joi'; - -interface DeleteRequest extends Hapi.Request { - params: { - id: string; - }; -} - -export const deleteAlertRoute = { - method: 'DELETE', - path: '/api/alert/{id}', - config: { - tags: ['access:alerting-all'], - validate: { - params: Joi.object() - .keys({ - id: Joi.string().required(), - }) - .required(), - }, - }, - async handler(request: DeleteRequest, h: Hapi.ResponseToolkit) { - const { id } = request.params; - const alertsClient = request.getAlertsClient!(); - await alertsClient.delete({ id }); - return h.response().code(204); - }, -}; diff --git a/x-pack/legacy/plugins/alerting/server/routes/disable.test.ts b/x-pack/legacy/plugins/alerting/server/routes/disable.test.ts deleted file mode 100644 index d2214f5c7c175..0000000000000 --- a/x-pack/legacy/plugins/alerting/server/routes/disable.test.ts +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { createMockServer } from './_mock_server'; -import { disableAlertRoute } from './disable'; - -const { server, alertsClient } = createMockServer(); -server.route(disableAlertRoute); - -test('disables an alert', async () => { - const request = { - method: 'POST', - url: '/api/alert/1/_disable', - }; - - const { statusCode } = await server.inject(request); - expect(statusCode).toBe(204); - expect(alertsClient.disable).toHaveBeenCalledWith({ id: '1' }); -}); diff --git a/x-pack/legacy/plugins/alerting/server/routes/disable.ts b/x-pack/legacy/plugins/alerting/server/routes/disable.ts deleted file mode 100644 index 2e88d3b5b96da..0000000000000 --- a/x-pack/legacy/plugins/alerting/server/routes/disable.ts +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import Hapi from 'hapi'; - -export const disableAlertRoute = { - method: 'POST', - path: '/api/alert/{id}/_disable', - config: { - tags: ['access:alerting-all'], - response: { - emptyStatusCode: 204, - }, - }, - async handler(request: Hapi.Request, h: Hapi.ResponseToolkit) { - const alertsClient = request.getAlertsClient!(); - await alertsClient.disable({ id: request.params.id }); - return h.response(); - }, -}; diff --git a/x-pack/legacy/plugins/alerting/server/routes/enable.test.ts b/x-pack/legacy/plugins/alerting/server/routes/enable.test.ts deleted file mode 100644 index bc5498af61989..0000000000000 --- a/x-pack/legacy/plugins/alerting/server/routes/enable.test.ts +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { createMockServer } from './_mock_server'; -import { enableAlertRoute } from './enable'; - -const { server, alertsClient } = createMockServer(); -server.route(enableAlertRoute); - -test('enables an alert', async () => { - const request = { - method: 'POST', - url: '/api/alert/1/_enable', - }; - - const { statusCode } = await server.inject(request); - expect(statusCode).toBe(204); - expect(alertsClient.enable).toHaveBeenCalledWith({ id: '1' }); -}); diff --git a/x-pack/legacy/plugins/alerting/server/routes/enable.ts b/x-pack/legacy/plugins/alerting/server/routes/enable.ts deleted file mode 100644 index 28080db7b0230..0000000000000 --- a/x-pack/legacy/plugins/alerting/server/routes/enable.ts +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import Hapi from 'hapi'; - -export const enableAlertRoute = { - method: 'POST', - path: '/api/alert/{id}/_enable', - config: { - tags: ['access:alerting-all'], - response: { - emptyStatusCode: 204, - }, - }, - async handler(request: Hapi.Request, h: Hapi.ResponseToolkit) { - const alertsClient = request.getAlertsClient!(); - await alertsClient.enable({ id: request.params.id }); - return h.response(); - }, -}; diff --git a/x-pack/legacy/plugins/alerting/server/routes/find.test.ts b/x-pack/legacy/plugins/alerting/server/routes/find.test.ts deleted file mode 100644 index 7b86fb3fbd28e..0000000000000 --- a/x-pack/legacy/plugins/alerting/server/routes/find.test.ts +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { createMockServer } from './_mock_server'; -import { findAlertRoute } from './find'; - -const { server, alertsClient } = createMockServer(); -server.route(findAlertRoute); - -beforeEach(() => jest.resetAllMocks()); - -test('sends proper arguments to alert find function', async () => { - const request = { - method: 'GET', - url: - '/api/alert/_find?' + - 'per_page=1&' + - 'page=1&' + - 'search=text*&' + - 'default_search_operator=AND&' + - 'search_fields=description&' + - 'sort_field=description&' + - 'fields=description', - }; - - const expectedResult = { - page: 1, - perPage: 1, - total: 0, - data: [], - }; - - alertsClient.find.mockResolvedValueOnce(expectedResult); - const { payload, statusCode } = await server.inject(request); - expect(statusCode).toBe(200); - const response = JSON.parse(payload); - expect(response).toEqual(expectedResult); - expect(alertsClient.find).toHaveBeenCalledTimes(1); - expect(alertsClient.find.mock.calls[0]).toMatchInlineSnapshot(` - Array [ - Object { - "options": Object { - "defaultSearchOperator": "AND", - "fields": Array [ - "description", - ], - "filter": undefined, - "hasReference": undefined, - "page": 1, - "perPage": 1, - "search": "text*", - "searchFields": Array [ - "description", - ], - "sortField": "description", - }, - }, - ] - `); -}); diff --git a/x-pack/legacy/plugins/alerting/server/routes/find.ts b/x-pack/legacy/plugins/alerting/server/routes/find.ts deleted file mode 100644 index 75cf93b18398f..0000000000000 --- a/x-pack/legacy/plugins/alerting/server/routes/find.ts +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import Joi from 'joi'; -import Hapi from 'hapi'; - -import { WithoutQueryAndParams } from '../types'; - -interface FindRequest extends WithoutQueryAndParams { - query: { - per_page: number; - page: number; - search?: string; - default_search_operator: 'AND' | 'OR'; - search_fields?: string[]; - sort_field?: string; - has_reference?: { - type: string; - id: string; - }; - fields?: string[]; - filter?: string; - }; -} - -export const findAlertRoute = { - method: 'GET', - path: '/api/alert/_find', - config: { - tags: ['access:alerting-read'], - validate: { - query: Joi.object() - .keys({ - per_page: Joi.number() - .min(0) - .default(20), - page: Joi.number() - .min(1) - .default(1), - search: Joi.string() - .allow('') - .optional(), - default_search_operator: Joi.string() - .valid('OR', 'AND') - .default('OR'), - search_fields: Joi.array() - .items(Joi.string()) - .single(), - sort_field: Joi.string(), - has_reference: Joi.object() - .keys({ - type: Joi.string().required(), - id: Joi.string().required(), - }) - .optional(), - fields: Joi.array() - .items(Joi.string()) - .single(), - filter: Joi.string() - .allow('') - .optional(), - }) - .default(), - }, - }, - async handler(request: FindRequest) { - const { query } = request; - const alertsClient = request.getAlertsClient!(); - return await alertsClient.find({ - options: { - perPage: query.per_page, - page: query.page, - search: query.search, - defaultSearchOperator: query.default_search_operator, - searchFields: query.search_fields, - sortField: query.sort_field, - hasReference: query.has_reference, - fields: query.fields, - filter: query.filter, - }, - }); - }, -}; diff --git a/x-pack/legacy/plugins/alerting/server/routes/get.test.ts b/x-pack/legacy/plugins/alerting/server/routes/get.test.ts deleted file mode 100644 index 320e9042d87c5..0000000000000 --- a/x-pack/legacy/plugins/alerting/server/routes/get.test.ts +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { createMockServer } from './_mock_server'; -import { getAlertRoute } from './get'; - -const { server, alertsClient } = createMockServer(); -server.route(getAlertRoute); - -const mockedAlert = { - id: '1', - alertTypeId: '1', - schedule: { interval: '10s' }, - params: { - bar: true, - }, - createdAt: new Date(), - updatedAt: new Date(), - actions: [ - { - group: 'default', - id: '2', - actionTypeId: 'test', - params: { - foo: true, - }, - }, - ], - consumer: 'bar', - name: 'abc', - tags: ['foo'], - enabled: true, - muteAll: false, - createdBy: '', - updatedBy: '', - apiKey: '', - apiKeyOwner: '', - throttle: '30s', - mutedInstanceIds: [], -}; - -beforeEach(() => jest.resetAllMocks()); - -test('calls get with proper parameters', async () => { - const request = { - method: 'GET', - url: '/api/alert/1', - }; - - alertsClient.get.mockResolvedValueOnce(mockedAlert); - const { payload, statusCode } = await server.inject(request); - expect(statusCode).toBe(200); - const { createdAt, updatedAt, ...response } = JSON.parse(payload); - expect({ createdAt: new Date(createdAt), updatedAt: new Date(updatedAt), ...response }).toEqual( - mockedAlert - ); - expect(alertsClient.get).toHaveBeenCalledTimes(1); - expect(alertsClient.get.mock.calls[0]).toMatchInlineSnapshot(` -Array [ - Object { - "id": "1", - }, -] -`); -}); diff --git a/x-pack/legacy/plugins/alerting/server/routes/get.ts b/x-pack/legacy/plugins/alerting/server/routes/get.ts deleted file mode 100644 index 67991aa575a21..0000000000000 --- a/x-pack/legacy/plugins/alerting/server/routes/get.ts +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import Joi from 'joi'; -import Hapi from 'hapi'; - -interface GetRequest extends Hapi.Request { - params: { - id: string; - }; -} - -export const getAlertRoute = { - method: 'GET', - path: `/api/alert/{id}`, - options: { - tags: ['access:alerting-read'], - validate: { - params: Joi.object() - .keys({ - id: Joi.string().required(), - }) - .required(), - }, - }, - async handler(request: GetRequest) { - const { id } = request.params; - const alertsClient = request.getAlertsClient!(); - return await alertsClient.get({ id }); - }, -}; diff --git a/x-pack/legacy/plugins/alerting/server/routes/get_alert_state.test.ts b/x-pack/legacy/plugins/alerting/server/routes/get_alert_state.test.ts deleted file mode 100644 index 9e3b3b6579ead..0000000000000 --- a/x-pack/legacy/plugins/alerting/server/routes/get_alert_state.test.ts +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { createMockServer } from './_mock_server'; -import { getAlertStateRoute } from './get_alert_state'; -import { SavedObjectsErrorHelpers } from 'src/core/server'; - -const { server, alertsClient } = createMockServer(); -server.route(getAlertStateRoute); - -const mockedAlertState = { - alertTypeState: { - some: 'value', - }, - alertInstances: { - first_instance: { - state: {}, - meta: { - lastScheduledActions: { - group: 'first_group', - date: new Date(), - }, - }, - }, - second_instance: {}, - }, -}; - -beforeEach(() => jest.resetAllMocks()); - -test('gets alert state', async () => { - const request = { - method: 'GET', - url: '/api/alert/1/state', - }; - - alertsClient.getAlertState.mockResolvedValueOnce(mockedAlertState); - - const { statusCode } = await server.inject(request); - expect(statusCode).toBe(200); - expect(alertsClient.getAlertState).toHaveBeenCalledWith({ id: '1' }); -}); - -test('returns NO-CONTENT when alert exists but has no task state yet', async () => { - const request = { - method: 'GET', - url: '/api/alert/1/state', - }; - - alertsClient.getAlertState.mockResolvedValueOnce(undefined); - - const { statusCode } = await server.inject(request); - expect(statusCode).toBe(204); - expect(alertsClient.getAlertState).toHaveBeenCalledWith({ id: '1' }); -}); - -test('returns NOT-FOUND when alert is not found', async () => { - const request = { - method: 'GET', - url: '/api/alert/1/state', - }; - - alertsClient.getAlertState.mockRejectedValue( - SavedObjectsErrorHelpers.createGenericNotFoundError('alert', '1') - ); - - const { statusCode } = await server.inject(request); - expect(statusCode).toBe(404); - expect(alertsClient.getAlertState).toHaveBeenCalledWith({ id: '1' }); -}); diff --git a/x-pack/legacy/plugins/alerting/server/routes/get_alert_state.ts b/x-pack/legacy/plugins/alerting/server/routes/get_alert_state.ts deleted file mode 100644 index 12136a975bb19..0000000000000 --- a/x-pack/legacy/plugins/alerting/server/routes/get_alert_state.ts +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import Joi from 'joi'; -import Hapi from 'hapi'; - -interface GetAlertStateRequest extends Hapi.Request { - params: { - id: string; - }; -} - -export const getAlertStateRoute = { - method: 'GET', - path: '/api/alert/{id}/state', - options: { - tags: ['access:alerting-read'], - validate: { - params: Joi.object() - .keys({ - id: Joi.string().required(), - }) - .required(), - }, - }, - async handler(request: GetAlertStateRequest, h: Hapi.ResponseToolkit) { - const { id } = request.params; - const alertsClient = request.getAlertsClient!(); - const state = await alertsClient.getAlertState({ id }); - return state ? state : h.response().code(204); - }, -}; diff --git a/x-pack/legacy/plugins/alerting/server/routes/list_alert_types.test.ts b/x-pack/legacy/plugins/alerting/server/routes/list_alert_types.test.ts deleted file mode 100644 index 46f02812e0f6c..0000000000000 --- a/x-pack/legacy/plugins/alerting/server/routes/list_alert_types.test.ts +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { createMockServer } from './_mock_server'; -import { listAlertTypesRoute } from './list_alert_types'; - -const { server, alertTypeRegistry } = createMockServer(); -server.route(listAlertTypesRoute); - -beforeEach(() => jest.resetAllMocks()); - -test('calls the list function', async () => { - const request = { - method: 'GET', - url: '/api/alert/types', - }; - - alertTypeRegistry.list.mockReturnValueOnce([]); - const { payload, statusCode } = await server.inject(request); - expect(statusCode).toBe(200); - const response = JSON.parse(payload); - expect(response).toEqual([]); - expect(alertTypeRegistry.list).toHaveBeenCalledTimes(1); - expect(alertTypeRegistry.list.mock.calls[0]).toMatchInlineSnapshot(`Array []`); -}); diff --git a/x-pack/legacy/plugins/alerting/server/routes/list_alert_types.ts b/x-pack/legacy/plugins/alerting/server/routes/list_alert_types.ts deleted file mode 100644 index 13aaf6bfe452e..0000000000000 --- a/x-pack/legacy/plugins/alerting/server/routes/list_alert_types.ts +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import Hapi from 'hapi'; - -export const listAlertTypesRoute = { - method: 'GET', - path: `/api/alert/types`, - config: { - tags: ['access:alerting-read'], - }, - async handler(request: Hapi.Request) { - return request.server.plugins.alerting!.start.listTypes(); - }, -}; diff --git a/x-pack/legacy/plugins/alerting/server/routes/mute_all.test.ts b/x-pack/legacy/plugins/alerting/server/routes/mute_all.test.ts deleted file mode 100644 index 408a037edc345..0000000000000 --- a/x-pack/legacy/plugins/alerting/server/routes/mute_all.test.ts +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { createMockServer } from './_mock_server'; -import { muteAllAlertRoute } from './mute_all'; - -const { server, alertsClient } = createMockServer(); -server.route(muteAllAlertRoute); - -test('mutes an alert', async () => { - const request = { - method: 'POST', - url: '/api/alert/1/_mute_all', - }; - - const { statusCode } = await server.inject(request); - expect(statusCode).toBe(204); - expect(alertsClient.muteAll).toHaveBeenCalledWith({ id: '1' }); -}); diff --git a/x-pack/legacy/plugins/alerting/server/routes/mute_all.ts b/x-pack/legacy/plugins/alerting/server/routes/mute_all.ts deleted file mode 100644 index 90ae9b11b2f43..0000000000000 --- a/x-pack/legacy/plugins/alerting/server/routes/mute_all.ts +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import Hapi from 'hapi'; - -interface MuteAllRequest extends Hapi.Request { - params: { - id: string; - }; -} - -export const muteAllAlertRoute = { - method: 'POST', - path: '/api/alert/{id}/_mute_all', - config: { - tags: ['access:alerting-all'], - response: { - emptyStatusCode: 204, - }, - }, - async handler(request: MuteAllRequest, h: Hapi.ResponseToolkit) { - const alertsClient = request.getAlertsClient!(); - await alertsClient.muteAll(request.params); - return h.response(); - }, -}; diff --git a/x-pack/legacy/plugins/alerting/server/routes/mute_instance.test.ts b/x-pack/legacy/plugins/alerting/server/routes/mute_instance.test.ts deleted file mode 100644 index 697a68763adf6..0000000000000 --- a/x-pack/legacy/plugins/alerting/server/routes/mute_instance.test.ts +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { createMockServer } from './_mock_server'; -import { muteAlertInstanceRoute } from './mute_instance'; - -const { server, alertsClient } = createMockServer(); -server.route(muteAlertInstanceRoute); - -test('mutes an alert instance', async () => { - const request = { - method: 'POST', - url: '/api/alert/1/alert_instance/2/_mute', - }; - - const { statusCode } = await server.inject(request); - expect(statusCode).toBe(204); - expect(alertsClient.muteInstance).toHaveBeenCalledWith({ alertId: '1', alertInstanceId: '2' }); -}); diff --git a/x-pack/legacy/plugins/alerting/server/routes/mute_instance.ts b/x-pack/legacy/plugins/alerting/server/routes/mute_instance.ts deleted file mode 100644 index 503a07af88bca..0000000000000 --- a/x-pack/legacy/plugins/alerting/server/routes/mute_instance.ts +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import Hapi from 'hapi'; - -interface MuteInstanceRequest extends Hapi.Request { - params: { - alertId: string; - alertInstanceId: string; - }; -} - -export const muteAlertInstanceRoute = { - method: 'POST', - path: '/api/alert/{alertId}/alert_instance/{alertInstanceId}/_mute', - config: { - tags: ['access:alerting-all'], - response: { - emptyStatusCode: 204, - }, - }, - async handler(request: MuteInstanceRequest, h: Hapi.ResponseToolkit) { - const alertsClient = request.getAlertsClient!(); - await alertsClient.muteInstance(request.params); - return h.response(); - }, -}; diff --git a/x-pack/legacy/plugins/alerting/server/routes/unmute_all.test.ts b/x-pack/legacy/plugins/alerting/server/routes/unmute_all.test.ts deleted file mode 100644 index c28f51b405a3e..0000000000000 --- a/x-pack/legacy/plugins/alerting/server/routes/unmute_all.test.ts +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { createMockServer } from './_mock_server'; -import { unmuteAllAlertRoute } from './unmute_all'; - -const { server, alertsClient } = createMockServer(); -server.route(unmuteAllAlertRoute); - -test('unmutes an alert', async () => { - const request = { - method: 'POST', - url: '/api/alert/1/_unmute_all', - }; - - const { statusCode } = await server.inject(request); - expect(statusCode).toBe(204); - expect(alertsClient.unmuteAll).toHaveBeenCalledWith({ id: '1' }); -}); diff --git a/x-pack/legacy/plugins/alerting/server/routes/unmute_all.ts b/x-pack/legacy/plugins/alerting/server/routes/unmute_all.ts deleted file mode 100644 index 8bb119c600096..0000000000000 --- a/x-pack/legacy/plugins/alerting/server/routes/unmute_all.ts +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import Hapi from 'hapi'; - -interface UnmuteAllRequest extends Hapi.Request { - params: { - id: string; - }; -} - -export const unmuteAllAlertRoute = { - method: 'POST', - path: '/api/alert/{id}/_unmute_all', - config: { - tags: ['access:alerting-all'], - response: { - emptyStatusCode: 204, - }, - }, - async handler(request: UnmuteAllRequest, h: Hapi.ResponseToolkit) { - const alertsClient = request.getAlertsClient!(); - await alertsClient.unmuteAll(request.params); - return h.response(); - }, -}; diff --git a/x-pack/legacy/plugins/alerting/server/routes/unmute_instance.test.ts b/x-pack/legacy/plugins/alerting/server/routes/unmute_instance.test.ts deleted file mode 100644 index 5c8f23236e2b0..0000000000000 --- a/x-pack/legacy/plugins/alerting/server/routes/unmute_instance.test.ts +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { createMockServer } from './_mock_server'; -import { unmuteAlertInstanceRoute } from './unmute_instance'; - -const { server, alertsClient } = createMockServer(); -server.route(unmuteAlertInstanceRoute); - -test('unmutes an alert instance', async () => { - const request = { - method: 'POST', - url: '/api/alert/1/alert_instance/2/_unmute', - }; - - const { statusCode } = await server.inject(request); - expect(statusCode).toBe(204); - expect(alertsClient.unmuteInstance).toHaveBeenCalledWith({ alertId: '1', alertInstanceId: '2' }); -}); diff --git a/x-pack/legacy/plugins/alerting/server/routes/unmute_instance.ts b/x-pack/legacy/plugins/alerting/server/routes/unmute_instance.ts deleted file mode 100644 index 1d1145e010741..0000000000000 --- a/x-pack/legacy/plugins/alerting/server/routes/unmute_instance.ts +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import Hapi from 'hapi'; - -interface UnmuteInstanceRequest extends Hapi.Request { - params: { - alertId: string; - alertInstanceId: string; - }; -} - -export const unmuteAlertInstanceRoute = { - method: 'POST', - path: '/api/alert/{alertId}/alert_instance/{alertInstanceId}/_unmute', - config: { - tags: ['access:alerting-all'], - response: { - emptyStatusCode: 204, - }, - }, - async handler(request: UnmuteInstanceRequest, h: Hapi.ResponseToolkit) { - const alertsClient = request.getAlertsClient!(); - await alertsClient.unmuteInstance(request.params); - return h.response(); - }, -}; diff --git a/x-pack/legacy/plugins/alerting/server/routes/update.test.ts b/x-pack/legacy/plugins/alerting/server/routes/update.test.ts deleted file mode 100644 index 83b70b505234f..0000000000000 --- a/x-pack/legacy/plugins/alerting/server/routes/update.test.ts +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { createMockServer } from './_mock_server'; -import { updateAlertRoute } from './update'; - -const { server, alertsClient } = createMockServer(); -server.route(updateAlertRoute); - -beforeEach(() => jest.resetAllMocks()); - -const mockedResponse = { - id: '1', - alertTypeId: '1', - tags: ['foo'], - schedule: { interval: '12s' }, - params: { - otherField: false, - }, - createdAt: new Date(), - updatedAt: new Date(), - actions: [ - { - group: 'default', - id: '2', - actionTypeId: 'test', - params: { - baz: true, - }, - }, - ], -}; - -test('calls the update function with proper parameters', async () => { - const request = { - method: 'PUT', - url: '/api/alert/1', - payload: { - throttle: null, - name: 'abc', - tags: ['bar'], - schedule: { interval: '12s' }, - params: { - otherField: false, - }, - actions: [ - { - group: 'default', - id: '2', - params: { - baz: true, - }, - }, - ], - }, - }; - - alertsClient.update.mockResolvedValueOnce(mockedResponse); - const { payload, statusCode } = await server.inject(request); - expect(statusCode).toBe(200); - const { createdAt, updatedAt, ...response } = JSON.parse(payload); - expect({ createdAt: new Date(createdAt), updatedAt: new Date(updatedAt), ...response }).toEqual( - mockedResponse - ); - expect(alertsClient.update).toHaveBeenCalledTimes(1); - expect(alertsClient.update.mock.calls[0]).toMatchInlineSnapshot(` - Array [ - Object { - "data": Object { - "actions": Array [ - Object { - "group": "default", - "id": "2", - "params": Object { - "baz": true, - }, - }, - ], - "name": "abc", - "params": Object { - "otherField": false, - }, - "schedule": Object { - "interval": "12s", - }, - "tags": Array [ - "bar", - ], - "throttle": null, - }, - "id": "1", - }, - ] - `); -}); diff --git a/x-pack/legacy/plugins/alerting/server/routes/update.ts b/x-pack/legacy/plugins/alerting/server/routes/update.ts deleted file mode 100644 index bc55d48465602..0000000000000 --- a/x-pack/legacy/plugins/alerting/server/routes/update.ts +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import Joi from 'joi'; -import Hapi from 'hapi'; -import { getDurationSchema } from '../lib'; -import { IntervalSchedule } from '../types'; - -interface UpdateRequest extends Hapi.Request { - params: { - id: string; - }; - payload: { - alertTypeId: string; - name: string; - tags: string[]; - schedule: IntervalSchedule; - actions: Array<{ - group: string; - id: string; - params: Record; - }>; - params: Record; - throttle: string | null; - }; -} - -export const updateAlertRoute = { - method: 'PUT', - path: '/api/alert/{id}', - options: { - tags: ['access:alerting-all'], - validate: { - options: { - abortEarly: false, - }, - payload: Joi.object() - .keys({ - throttle: getDurationSchema() - .required() - .allow(null), - name: Joi.string().required(), - tags: Joi.array() - .items(Joi.string()) - .required(), - schedule: Joi.object() - .keys({ - interval: getDurationSchema().required(), - }) - .required(), - params: Joi.object().required(), - actions: Joi.array() - .items( - Joi.object().keys({ - group: Joi.string().required(), - id: Joi.string().required(), - params: Joi.object().required(), - }) - ) - .required(), - }) - .required(), - }, - }, - async handler(request: UpdateRequest) { - const { id } = request.params; - const alertsClient = request.getAlertsClient!(); - return await alertsClient.update({ id, data: request.payload }); - }, -}; diff --git a/x-pack/legacy/plugins/alerting/server/routes/update_api_key.test.ts b/x-pack/legacy/plugins/alerting/server/routes/update_api_key.test.ts deleted file mode 100644 index 9bfe26d56dabf..0000000000000 --- a/x-pack/legacy/plugins/alerting/server/routes/update_api_key.test.ts +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { createMockServer } from './_mock_server'; -import { updateApiKeyRoute } from './update_api_key'; - -const { server, alertsClient } = createMockServer(); -server.route(updateApiKeyRoute); - -test('updates api key for an alert', async () => { - const request = { - method: 'POST', - url: '/api/alert/1/_update_api_key', - }; - - const { statusCode } = await server.inject(request); - expect(statusCode).toBe(204); - expect(alertsClient.updateApiKey).toHaveBeenCalledWith({ id: '1' }); -}); diff --git a/x-pack/legacy/plugins/alerting/server/routes/update_api_key.ts b/x-pack/legacy/plugins/alerting/server/routes/update_api_key.ts deleted file mode 100644 index 754ea453a27cb..0000000000000 --- a/x-pack/legacy/plugins/alerting/server/routes/update_api_key.ts +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import Hapi from 'hapi'; - -export const updateApiKeyRoute = { - method: 'POST', - path: '/api/alert/{id}/_update_api_key', - config: { - tags: ['access:alerting-all'], - response: { - emptyStatusCode: 204, - }, - }, - async handler(request: Hapi.Request, h: Hapi.ResponseToolkit) { - const alertsClient = request.getAlertsClient!(); - await alertsClient.updateApiKey({ id: request.params.id }); - return h.response(); - }, -}; diff --git a/x-pack/legacy/plugins/alerting/server/shim.ts b/x-pack/legacy/plugins/alerting/server/shim.ts deleted file mode 100644 index bc8b0eb863634..0000000000000 --- a/x-pack/legacy/plugins/alerting/server/shim.ts +++ /dev/null @@ -1,145 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import Hapi from 'hapi'; -import { Legacy } from 'kibana'; -import { LegacySpacesPlugin as SpacesPluginStartContract } from '../../spaces'; -import { - TaskManagerStartContract, - TaskManagerSetupContract, -} from '../../../../plugins/task_manager/server'; -import { getTaskManagerSetup, getTaskManagerStart } from '../../task_manager/server'; -import { XPackMainPlugin } from '../../xpack_main/server/xpack_main'; -import KbnServer from '../../../../../src/legacy/server/kbn_server'; -import { - EncryptedSavedObjectsPluginSetup, - EncryptedSavedObjectsPluginStart, -} from '../../../../plugins/encrypted_saved_objects/server'; -import { SecurityPluginSetup } from '../../../../plugins/security/server'; -import { - CoreSetup, - LoggerFactory, - SavedObjectsLegacyService, -} from '../../../../../src/core/server'; -import { - ActionsPlugin, - PluginSetupContract as ActionsPluginSetupContract, - PluginStartContract as ActionsPluginStartContract, -} from '../../../../plugins/actions/server'; -import { LicensingPluginSetup } from '../../../../plugins/licensing/server'; - -// Extend PluginProperties to indicate which plugins are guaranteed to exist -// due to being marked as dependencies -interface Plugins extends Hapi.PluginProperties { - actions: ActionsPlugin; -} - -export interface Server extends Legacy.Server { - plugins: Plugins; -} - -/** - * Shim what we're thinking setup and start contracts will look like - */ -export type SecurityPluginSetupContract = Pick; -export type SecurityPluginStartContract = Pick; -export type XPackMainPluginSetupContract = Pick; - -/** - * New platform interfaces - */ -export interface AlertingPluginInitializerContext { - logger: LoggerFactory; -} -export interface AlertingCoreSetup { - elasticsearch: CoreSetup['elasticsearch']; - http: { - route: (route: Hapi.ServerRoute) => void; - basePath: { - serverBasePath: string; - }; - }; -} -export interface AlertingCoreStart { - savedObjects: SavedObjectsLegacyService; -} -export interface AlertingPluginsSetup { - security?: SecurityPluginSetupContract; - taskManager: TaskManagerSetupContract; - actions: ActionsPluginSetupContract; - xpack_main: XPackMainPluginSetupContract; - encryptedSavedObjects: EncryptedSavedObjectsPluginSetup; - licensing: LicensingPluginSetup; -} -export interface AlertingPluginsStart { - actions: ActionsPluginStartContract; - security?: SecurityPluginStartContract; - spaces: () => SpacesPluginStartContract | undefined; - encryptedSavedObjects: EncryptedSavedObjectsPluginStart; - taskManager: TaskManagerStartContract; -} - -/** - * Shim - * - * @param server Hapi server instance - */ -export function shim( - server: Server -): { - initializerContext: AlertingPluginInitializerContext; - coreSetup: AlertingCoreSetup; - coreStart: AlertingCoreStart; - pluginsSetup: AlertingPluginsSetup; - pluginsStart: AlertingPluginsStart; -} { - const newPlatform = ((server as unknown) as KbnServer).newPlatform; - - const initializerContext: AlertingPluginInitializerContext = { - logger: newPlatform.coreContext.logger, - }; - - const coreSetup: AlertingCoreSetup = { - elasticsearch: newPlatform.setup.core.elasticsearch, - http: { - route: server.route.bind(server), - basePath: newPlatform.setup.core.http.basePath, - }, - }; - - const coreStart: AlertingCoreStart = { - savedObjects: server.savedObjects, - }; - - const pluginsSetup: AlertingPluginsSetup = { - security: newPlatform.setup.plugins.security as SecurityPluginSetupContract | undefined, - taskManager: getTaskManagerSetup(server)!, - actions: newPlatform.setup.plugins.actions as ActionsPluginSetupContract, - xpack_main: server.plugins.xpack_main, - encryptedSavedObjects: newPlatform.setup.plugins - .encryptedSavedObjects as EncryptedSavedObjectsPluginSetup, - licensing: newPlatform.setup.plugins.licensing as LicensingPluginSetup, - }; - - const pluginsStart: AlertingPluginsStart = { - security: newPlatform.setup.plugins.security as SecurityPluginStartContract | undefined, - actions: newPlatform.start.plugins.actions as ActionsPluginStartContract, - // TODO: Currently a function because it's an optional dependency that - // initializes after this function is called - spaces: () => server.plugins.spaces, - encryptedSavedObjects: newPlatform.start.plugins - .encryptedSavedObjects as EncryptedSavedObjectsPluginStart, - taskManager: getTaskManagerStart(server)!, - }; - - return { - initializerContext, - coreSetup, - coreStart, - pluginsSetup, - pluginsStart, - }; -} diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/es_search_source/es_search_source.js b/x-pack/legacy/plugins/maps/public/layers/sources/es_search_source/es_search_source.js index 93ef40162a584..288dd117da137 100644 --- a/x-pack/legacy/plugins/maps/public/layers/sources/es_search_source/es_search_source.js +++ b/x-pack/legacy/plugins/maps/public/layers/sources/es_search_source/es_search_source.js @@ -54,6 +54,36 @@ function addFieldToDSL(dsl, field) { }; } +function getDocValueAndSourceFields(indexPattern, fieldNames) { + const docValueFields = []; + const sourceOnlyFields = []; + const scriptFields = {}; + fieldNames.forEach(fieldName => { + const field = getField(indexPattern, fieldName); + if (field.scripted) { + scriptFields[field.name] = { + script: { + source: field.script, + lang: field.lang, + }, + }; + } else if (field.readFromDocValues) { + const docValueField = + field.type === 'date' + ? { + field: fieldName, + format: 'epoch_millis', + } + : fieldName; + docValueFields.push(docValueField); + } else { + sourceOnlyFields.push(fieldName); + } + }); + + return { docValueFields, sourceOnlyFields, scriptFields }; +} + export class ESSearchSource extends AbstractESSource { static type = ES_SEARCH; static title = i18n.translate('xpack.maps.source.esSearchTitle', { @@ -244,63 +274,29 @@ export class ESSearchSource extends AbstractESSource { ]; } - async _excludeDateFields(fieldNames) { - const dateFieldNames = (await this.getDateFields()).map(field => field.getName()); - return fieldNames.filter(field => { - return !dateFieldNames.includes(field); - }); - } - - // Returns docvalue_fields array for the union of indexPattern's dateFields and request's field names. - async _getDateDocvalueFields(searchFields) { - const dateFieldNames = (await this.getDateFields()).map(field => field.getName()); - return searchFields - .filter(fieldName => { - return dateFieldNames.includes(fieldName); - }) - .map(fieldName => { - return { - field: fieldName, - format: 'epoch_millis', - }; - }); - } - async _getTopHits(layerName, searchFilters, registerCancelCallback) { const { topHitsSplitField: topHitsSplitFieldName, topHitsSize } = this._descriptor; const indexPattern = await this.getIndexPattern(); - const geoField = await this._getGeoField(); - - const scriptFields = {}; - searchFilters.fieldNames.forEach(fieldName => { - const field = indexPattern.fields.getByName(fieldName); - if (field && field.scripted) { - scriptFields[field.name] = { - script: { - source: field.script, - lang: field.lang, - }, - }; - } - }); + const { docValueFields, sourceOnlyFields, scriptFields } = getDocValueAndSourceFields( + indexPattern, + searchFilters.fieldNames + ); const topHits = { size: topHitsSize, script_fields: scriptFields, - docvalue_fields: await this._getDateDocvalueFields(searchFilters.fieldNames), + docvalue_fields: docValueFields, }; - const nonDateFieldNames = await this._excludeDateFields(searchFilters.fieldNames); if (this._hasSort()) { topHits.sort = this._buildEsSort(); } - if (geoField.type === ES_GEO_FIELD_TYPE.GEO_POINT) { + if (sourceOnlyFields.length === 0) { topHits._source = false; - topHits.docvalue_fields.push(...nonDateFieldNames); } else { topHits._source = { - includes: nonDateFieldNames, + includes: sourceOnlyFields, }; } @@ -364,41 +360,25 @@ export class ESSearchSource extends AbstractESSource { // searchFilters.fieldNames contains geo field and any fields needed for styling features // Performs Elasticsearch search request being careful to pull back only required fields to minimize response size async _getSearchHits(layerName, searchFilters, maxResultWindow, registerCancelCallback) { - const initialSearchContext = { - docvalue_fields: await this._getDateDocvalueFields(searchFilters.fieldNames), - }; - const geoField = await this._getGeoField(); + const indexPattern = await this.getIndexPattern(); - let searchSource; - if (geoField.type === ES_GEO_FIELD_TYPE.GEO_POINT) { - // Request geo_point and style fields in docvalue_fields insted of _source - // 1) Returns geo_point in a consistent format regardless of how geo_point is stored in source - // 2) Setting _source to false so we avoid pulling back unneeded fields. - initialSearchContext.docvalue_fields.push( - ...(await this._excludeDateFields(searchFilters.fieldNames)) - ); - searchSource = await this._makeSearchSource( - searchFilters, - maxResultWindow, - initialSearchContext - ); + const { docValueFields, sourceOnlyFields } = getDocValueAndSourceFields( + indexPattern, + searchFilters.fieldNames + ); + + const initialSearchContext = { docvalue_fields: docValueFields }; // Request fields in docvalue_fields insted of _source + const searchSource = await this._makeSearchSource( + searchFilters, + maxResultWindow, + initialSearchContext + ); + searchSource.setField('fields', searchFilters.fieldNames); // Setting "fields" filters out unused scripted fields + if (sourceOnlyFields.length === 0) { searchSource.setField('source', false); // do not need anything from _source - searchSource.setField('fields', searchFilters.fieldNames); // Setting "fields" filters out unused scripted fields } else { - // geo_shape fields do not support docvalue_fields yet, so still have to be pulled from _source - searchSource = await this._makeSearchSource( - searchFilters, - maxResultWindow, - initialSearchContext - ); - // Setting "fields" instead of "source: { includes: []}" - // because SearchSource automatically adds the following by default - // 1) all scripted fields - // 2) docvalue_fields value is added for each date field in an index - see getComputedFields - // By setting "fields", SearchSource removes all of defaults - searchSource.setField('fields', searchFilters.fieldNames); + searchSource.setField('source', sourceOnlyFields); } - if (this._hasSort()) { searchSource.setField('sort', this._buildEsSort()); } diff --git a/x-pack/legacy/plugins/ml/server/models/bucket_span_estimator/__tests__/bucket_span_estimator.js b/x-pack/legacy/plugins/ml/server/models/bucket_span_estimator/__tests__/bucket_span_estimator.js index c003d3117132f..a0dacc38e5835 100644 --- a/x-pack/legacy/plugins/ml/server/models/bucket_span_estimator/__tests__/bucket_span_estimator.js +++ b/x-pack/legacy/plugins/ml/server/models/bucket_span_estimator/__tests__/bucket_span_estimator.js @@ -4,7 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ -import sinon from 'sinon'; import expect from '@kbn/expect'; import { estimateBucketSpanFactory } from '../bucket_span_estimator'; @@ -32,9 +31,11 @@ const callWithRequest = method => { }); }; -// mock callWithInternalUserFactory -// we replace the return value of the factory with the above mocked callWithRequest -import * as mockModule from '../../../client/call_with_internal_user_factory'; +const callWithInternalUser = () => { + return new Promise(resolve => { + resolve({}); + }); +}; // mock xpack_main plugin function mockXpackMainPluginFactory(isEnabled = false, licenseType = 'platinum') { @@ -51,7 +52,6 @@ function mockXpackMainPluginFactory(isEnabled = false, licenseType = 'platinum') }; } -const mockElasticsearchPlugin = {}; // mock configuration to be passed to the estimator const formConfig = { aggTypes: ['count'], @@ -67,20 +67,9 @@ const formConfig = { }; describe('ML - BucketSpanEstimator', () => { - let mockCallWithInternalUserFactory; - - beforeEach(() => { - mockCallWithInternalUserFactory = sinon.mock(mockModule); - mockCallWithInternalUserFactory - .expects('callWithInternalUserFactory') - .once() - .returns(callWithRequest); - }); - it('call factory', () => { expect(function() { - estimateBucketSpanFactory(callWithRequest); - mockCallWithInternalUserFactory.verify(); + estimateBucketSpanFactory(callWithRequest, callWithInternalUser); }).to.not.throwError('Not initialized.'); }); @@ -88,13 +77,13 @@ describe('ML - BucketSpanEstimator', () => { expect(function() { const estimateBucketSpan = estimateBucketSpanFactory( callWithRequest, - mockElasticsearchPlugin, + callWithInternalUser, mockXpackMainPluginFactory() ); estimateBucketSpan(formConfig).catch(catchData => { expect(catchData).to.be('Unable to retrieve cluster setting search.max_buckets'); - mockCallWithInternalUserFactory.verify(); + done(); }); }).to.not.throwError('Not initialized.'); @@ -104,12 +93,12 @@ describe('ML - BucketSpanEstimator', () => { expect(function() { const estimateBucketSpan = estimateBucketSpanFactory( callWithRequest, - mockElasticsearchPlugin, + callWithInternalUser, mockXpackMainPluginFactory(true) ); estimateBucketSpan(formConfig).catch(catchData => { expect(catchData).to.be('Unable to retrieve cluster setting search.max_buckets'); - mockCallWithInternalUserFactory.verify(); + done(); }); }).to.not.throwError('Not initialized.'); @@ -119,19 +108,14 @@ describe('ML - BucketSpanEstimator', () => { expect(function() { const estimateBucketSpan = estimateBucketSpanFactory( callWithRequest, - mockElasticsearchPlugin, + callWithInternalUser, mockXpackMainPluginFactory(true) ); estimateBucketSpan(formConfig).catch(catchData => { expect(catchData).to.be('Insufficient permissions to call bucket span estimation.'); - mockCallWithInternalUserFactory.verify(); done(); }); }).to.not.throwError('Not initialized.'); }); - - afterEach(() => { - mockCallWithInternalUserFactory.restore(); - }); }); diff --git a/x-pack/legacy/plugins/ml/server/models/bucket_span_estimator/bucket_span_estimator.d.ts b/x-pack/legacy/plugins/ml/server/models/bucket_span_estimator/bucket_span_estimator.d.ts new file mode 100644 index 0000000000000..ea986feab4e99 --- /dev/null +++ b/x-pack/legacy/plugins/ml/server/models/bucket_span_estimator/bucket_span_estimator.d.ts @@ -0,0 +1,14 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { APICaller } from 'src/core/server'; +import { BucketSpanEstimatorData } from '../../../public/application/services/ml_api_service'; + +export function estimateBucketSpanFactory( + callAsCurrentUser: APICaller, + callAsInternalUser: APICaller, + xpackMainPlugin: any +): (config: BucketSpanEstimatorData) => Promise; diff --git a/x-pack/legacy/plugins/ml/server/models/bucket_span_estimator/bucket_span_estimator.js b/x-pack/legacy/plugins/ml/server/models/bucket_span_estimator/bucket_span_estimator.js index c0edcb5eeb537..aec677dd57d61 100644 --- a/x-pack/legacy/plugins/ml/server/models/bucket_span_estimator/bucket_span_estimator.js +++ b/x-pack/legacy/plugins/ml/server/models/bucket_span_estimator/bucket_span_estimator.js @@ -12,13 +12,11 @@ import { INTERVALS } from './intervals'; import { singleSeriesCheckerFactory } from './single_series_checker'; import { polledDataCheckerFactory } from './polled_data_checker'; -import { callWithInternalUserFactory } from '../../client/call_with_internal_user_factory'; import { isSecurityDisabled } from '../../lib/security_utils'; -export function estimateBucketSpanFactory(callWithRequest, elasticsearchPlugin, xpackMainPlugin) { - const callWithInternalUser = callWithInternalUserFactory(elasticsearchPlugin); - const PolledDataChecker = polledDataCheckerFactory(callWithRequest); - const SingleSeriesChecker = singleSeriesCheckerFactory(callWithRequest); +export function estimateBucketSpanFactory(callAsCurrentUser, callAsInternalUser, xpackMainPlugin) { + const PolledDataChecker = polledDataCheckerFactory(callAsCurrentUser); + const SingleSeriesChecker = singleSeriesCheckerFactory(callAsCurrentUser); class BucketSpanEstimator { constructor( @@ -245,7 +243,7 @@ export function estimateBucketSpanFactory(callWithRequest, elasticsearchPlugin, const getFieldCardinality = function(index, field) { return new Promise((resolve, reject) => { - callWithRequest('search', { + callAsCurrentUser('search', { index, size: 0, body: { @@ -277,7 +275,7 @@ export function estimateBucketSpanFactory(callWithRequest, elasticsearchPlugin, getFieldCardinality(index, field) .then(value => { const numPartitions = Math.floor(value / NUM_PARTITIONS) || 1; - callWithRequest('search', { + callAsCurrentUser('search', { index, size: 0, body: { @@ -337,7 +335,7 @@ export function estimateBucketSpanFactory(callWithRequest, elasticsearchPlugin, function getBucketSpanEstimation() { // fetch the `search.max_buckets` cluster setting so we're able to // adjust aggregations to not exceed that limit. - callWithInternalUser('cluster.getSettings', { + callAsInternalUser('cluster.getSettings', { flatSettings: true, includeDefaults: true, filterPath: '*.*max_buckets', @@ -402,7 +400,7 @@ export function estimateBucketSpanFactory(callWithRequest, elasticsearchPlugin, 'cluster:monitor/xpack/ml/datafeeds/stats/get', ], }; - callWithRequest('ml.privilegeCheck', { body }) + callAsCurrentUser('ml.privilegeCheck', { body }) .then(resp => { if ( resp.cluster['cluster:monitor/xpack/ml/job/get'] && diff --git a/x-pack/legacy/plugins/ml/server/models/bucket_span_estimator/index.js b/x-pack/legacy/plugins/ml/server/models/bucket_span_estimator/index.ts similarity index 100% rename from x-pack/legacy/plugins/ml/server/models/bucket_span_estimator/index.js rename to x-pack/legacy/plugins/ml/server/models/bucket_span_estimator/index.ts diff --git a/x-pack/legacy/plugins/ml/server/models/bucket_span_estimator/polled_data_checker.js b/x-pack/legacy/plugins/ml/server/models/bucket_span_estimator/polled_data_checker.js index 8b462989866e4..674875f8d5d16 100644 --- a/x-pack/legacy/plugins/ml/server/models/bucket_span_estimator/polled_data_checker.js +++ b/x-pack/legacy/plugins/ml/server/models/bucket_span_estimator/polled_data_checker.js @@ -12,7 +12,7 @@ import _ from 'lodash'; -export function polledDataCheckerFactory(callWithRequest) { +export function polledDataCheckerFactory(callAsCurrentUser) { class PolledDataChecker { constructor(index, timeField, duration, query) { this.index = index; @@ -68,7 +68,7 @@ export function polledDataCheckerFactory(callWithRequest) { performSearch(intervalMs) { const body = this.createSearch(intervalMs); - return callWithRequest('search', { + return callAsCurrentUser('search', { index: this.index, size: 0, body, diff --git a/x-pack/legacy/plugins/ml/server/models/bucket_span_estimator/single_series_checker.js b/x-pack/legacy/plugins/ml/server/models/bucket_span_estimator/single_series_checker.js index 0994d432802e3..71e692d089b49 100644 --- a/x-pack/legacy/plugins/ml/server/models/bucket_span_estimator/single_series_checker.js +++ b/x-pack/legacy/plugins/ml/server/models/bucket_span_estimator/single_series_checker.js @@ -13,7 +13,7 @@ import { mlLog } from '../../client/log'; import { INTERVALS, LONG_INTERVALS } from './intervals'; -export function singleSeriesCheckerFactory(callWithRequest) { +export function singleSeriesCheckerFactory(callAsCurrentUser) { const REF_DATA_INTERVAL = { name: '1h', ms: 3600000 }; class SingleSeriesChecker { @@ -187,7 +187,7 @@ export function singleSeriesCheckerFactory(callWithRequest) { performSearch(intervalMs) { const body = this.createSearch(intervalMs); - return callWithRequest('search', { + return callAsCurrentUser('search', { index: this.index, size: 0, body, diff --git a/x-pack/legacy/plugins/ml/server/models/calculate_model_memory_limit/calculate_model_memory_limit.d.ts b/x-pack/legacy/plugins/ml/server/models/calculate_model_memory_limit/calculate_model_memory_limit.d.ts new file mode 100644 index 0000000000000..87c9a8c978274 --- /dev/null +++ b/x-pack/legacy/plugins/ml/server/models/calculate_model_memory_limit/calculate_model_memory_limit.d.ts @@ -0,0 +1,20 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { APICaller } from 'src/core/server'; + +export function calculateModelMemoryLimitProvider( + callAsCurrentUser: APICaller +): ( + indexPattern: string, + splitFieldName: string, + query: any, + fieldNames: any, + influencerNames: any, // string[] ? + timeFieldName: string, + earliestMs: number, + latestMs: number +) => Promise; diff --git a/x-pack/legacy/plugins/ml/server/models/calculate_model_memory_limit/calculate_model_memory_limit.js b/x-pack/legacy/plugins/ml/server/models/calculate_model_memory_limit/calculate_model_memory_limit.js index ace6c63257c3a..8a06895762dc2 100644 --- a/x-pack/legacy/plugins/ml/server/models/calculate_model_memory_limit/calculate_model_memory_limit.js +++ b/x-pack/legacy/plugins/ml/server/models/calculate_model_memory_limit/calculate_model_memory_limit.js @@ -10,8 +10,8 @@ import numeral from '@elastic/numeral'; import { fieldsServiceProvider } from '../fields_service'; -export function calculateModelMemoryLimitProvider(callWithRequest) { - const fieldsService = fieldsServiceProvider(callWithRequest); +export function calculateModelMemoryLimitProvider(callAsCurrentUser) { + const fieldsService = fieldsServiceProvider(callAsCurrentUser); return function calculateModelMemoryLimit( indexPattern, @@ -26,7 +26,7 @@ export function calculateModelMemoryLimitProvider(callWithRequest) { ) { return new Promise((response, reject) => { const limits = {}; - callWithRequest('ml.info') + callAsCurrentUser('ml.info') .then(resp => { if (resp.limits !== undefined && resp.limits.max_model_memory_limit !== undefined) { limits.max_model_memory_limit = resp.limits.max_model_memory_limit; diff --git a/x-pack/legacy/plugins/ml/server/models/calculate_model_memory_limit/index.js b/x-pack/legacy/plugins/ml/server/models/calculate_model_memory_limit/index.ts similarity index 100% rename from x-pack/legacy/plugins/ml/server/models/calculate_model_memory_limit/index.js rename to x-pack/legacy/plugins/ml/server/models/calculate_model_memory_limit/index.ts diff --git a/x-pack/legacy/plugins/ml/server/models/data_visualizer/data_visualizer.ts b/x-pack/legacy/plugins/ml/server/models/data_visualizer/data_visualizer.ts index 6186a61c5075f..b0a61b1232dc0 100644 --- a/x-pack/legacy/plugins/ml/server/models/data_visualizer/data_visualizer.ts +++ b/x-pack/legacy/plugins/ml/server/models/data_visualizer/data_visualizer.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { CallAPIOptions, RequestHandlerContext } from 'kibana/server'; +import { CallAPIOptions, IScopedClusterClient } from 'src/core/server'; import _ from 'lodash'; import { ML_JOB_FIELD_TYPES } from '../../../common/constants/field_types'; import { getSafeAggregationName } from '../../../common/util/job_utils'; @@ -113,9 +113,8 @@ export class DataVisualizer { options?: CallAPIOptions ) => Promise; - constructor(client: RequestHandlerContext | (() => any)) { - this.callAsCurrentUser = - typeof client === 'object' ? client.ml!.mlClient.callAsCurrentUser : client; + constructor(callAsCurrentUser: IScopedClusterClient['callAsCurrentUser']) { + this.callAsCurrentUser = callAsCurrentUser; } // Obtains overall stats on the fields in the supplied index pattern, returning an object diff --git a/x-pack/legacy/plugins/ml/server/models/job_validation/index.js b/x-pack/legacy/plugins/ml/server/models/job_validation/index.ts similarity index 100% rename from x-pack/legacy/plugins/ml/server/models/job_validation/index.js rename to x-pack/legacy/plugins/ml/server/models/job_validation/index.ts diff --git a/x-pack/legacy/plugins/ml/server/models/job_validation/job_validation.d.ts b/x-pack/legacy/plugins/ml/server/models/job_validation/job_validation.d.ts new file mode 100644 index 0000000000000..4580602b0af23 --- /dev/null +++ b/x-pack/legacy/plugins/ml/server/models/job_validation/job_validation.d.ts @@ -0,0 +1,19 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { APICaller } from 'src/core/server'; +import { TypeOf } from '@kbn/config-schema'; +import { validateJobSchema } from '../../new_platform/job_validation_schema'; + +type ValidateJobPayload = TypeOf; + +export function validateJob( + callAsCurrentUser: APICaller, + payload: ValidateJobPayload, + kbnVersion: string, + callAsInternalUser: APICaller, + xpackMainPlugin: any +): string[]; diff --git a/x-pack/legacy/plugins/ml/server/models/job_validation/job_validation.js b/x-pack/legacy/plugins/ml/server/models/job_validation/job_validation.js index 07194b6aa226d..ab1fbb39ee706 100644 --- a/x-pack/legacy/plugins/ml/server/models/job_validation/job_validation.js +++ b/x-pack/legacy/plugins/ml/server/models/job_validation/job_validation.js @@ -23,7 +23,7 @@ export async function validateJob( callWithRequest, payload, kbnVersion = 'current', - elasticsearchPlugin, + callAsInternalUser, xpackMainPlugin ) { const messages = getMessages(); @@ -111,7 +111,7 @@ export async function validateJob( callWithRequest, job, duration, - elasticsearchPlugin, + callAsInternalUser, xpackMainPlugin )) ); diff --git a/x-pack/legacy/plugins/ml/server/models/job_validation/validate_bucket_span.js b/x-pack/legacy/plugins/ml/server/models/job_validation/validate_bucket_span.js index ea069bffaef2a..2914f086c1a83 100644 --- a/x-pack/legacy/plugins/ml/server/models/job_validation/validate_bucket_span.js +++ b/x-pack/legacy/plugins/ml/server/models/job_validation/validate_bucket_span.js @@ -50,7 +50,7 @@ export async function validateBucketSpan( callWithRequest, job, duration, - elasticsearchPlugin, + callAsInternalUser, xpackMainPlugin ) { validateJobObject(job); @@ -123,7 +123,7 @@ export async function validateBucketSpan( return new Promise(resolve => { estimateBucketSpanFactory( callWithRequest, - elasticsearchPlugin, + callAsInternalUser, xpackMainPlugin )(data) .then(resolve) diff --git a/x-pack/legacy/plugins/ml/server/models/job_validation/validate_cardinality.d.ts b/x-pack/legacy/plugins/ml/server/models/job_validation/validate_cardinality.d.ts new file mode 100644 index 0000000000000..dc10905533788 --- /dev/null +++ b/x-pack/legacy/plugins/ml/server/models/job_validation/validate_cardinality.d.ts @@ -0,0 +1,17 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { APICaller } from 'src/core/server'; +import { Job, Datafeed } from '../../../public/application/jobs/new_job/common/job_creator/configs'; + +interface ValidateCardinalityConfig extends Job { + datafeed_config?: Datafeed; +} + +export function validateCardinality( + callAsCurrentUser: APICaller, + job: ValidateCardinalityConfig +): any[]; diff --git a/x-pack/legacy/plugins/ml/server/new_platform/anomaly_detectors_schema.ts b/x-pack/legacy/plugins/ml/server/new_platform/anomaly_detectors_schema.ts index d728fbf312d76..a46ccd8664a62 100644 --- a/x-pack/legacy/plugins/ml/server/new_platform/anomaly_detectors_schema.ts +++ b/x-pack/legacy/plugins/ml/server/new_platform/anomaly_detectors_schema.ts @@ -54,10 +54,12 @@ export const anomalyDetectionUpdateJobSchema = { ) ), custom_settings: schema.maybe(customSettingsSchema), - analysis_limits: schema.object({ - categorization_examples_limit: schema.maybe(schema.number()), - model_memory_limit: schema.maybe(schema.string()), - }), + analysis_limits: schema.maybe( + schema.object({ + categorization_examples_limit: schema.maybe(schema.number()), + model_memory_limit: schema.maybe(schema.string()), + }) + ), groups: schema.maybe(schema.arrayOf(schema.maybe(schema.string()))), }; @@ -69,15 +71,19 @@ export const anomalyDetectionJobSchema = { influencers: schema.arrayOf(schema.maybe(schema.string())), categorization_field_name: schema.maybe(schema.string()), }), - analysis_limits: schema.object({ - categorization_examples_limit: schema.maybe(schema.number()), - model_memory_limit: schema.maybe(schema.string()), - }), + analysis_limits: schema.maybe( + schema.object({ + categorization_examples_limit: schema.maybe(schema.number()), + model_memory_limit: schema.maybe(schema.string()), + }) + ), + background_persist_interval: schema.maybe(schema.string()), create_time: schema.maybe(schema.number()), custom_settings: schema.maybe(customSettingsSchema), allow_lazy_open: schema.maybe(schema.any()), data_counts: schema.maybe(schema.any()), data_description: schema.object({ + format: schema.maybe(schema.string()), time_field: schema.string(), time_format: schema.maybe(schema.string()), }), @@ -94,6 +100,8 @@ export const anomalyDetectionJobSchema = { model_snapshot_id: schema.maybe(schema.string()), model_snapshot_min_version: schema.maybe(schema.string()), model_snapshot_retention_days: schema.maybe(schema.number()), + renormalization_window_days: schema.maybe(schema.number()), results_index_name: schema.maybe(schema.string()), + results_retention_days: schema.maybe(schema.number()), state: schema.maybe(schema.string()), }; diff --git a/x-pack/legacy/plugins/ml/server/new_platform/job_validation_schema.ts b/x-pack/legacy/plugins/ml/server/new_platform/job_validation_schema.ts new file mode 100644 index 0000000000000..1cc6e8a97ffc0 --- /dev/null +++ b/x-pack/legacy/plugins/ml/server/new_platform/job_validation_schema.ts @@ -0,0 +1,59 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { schema } from '@kbn/config-schema'; +import { anomalyDetectionJobSchema } from './anomaly_detectors_schema'; + +export const estimateBucketSpanSchema = schema.object({ + aggTypes: schema.arrayOf(schema.nullable(schema.string())), + duration: schema.object({ start: schema.number(), end: schema.number() }), + fields: schema.arrayOf(schema.nullable(schema.string())), + index: schema.string(), + query: schema.any(), + splitField: schema.maybe(schema.string()), + timeField: schema.maybe(schema.string()), +}); + +export const modelMemoryLimitSchema = schema.object({ + indexPattern: schema.string(), + splitFieldName: schema.string(), + query: schema.any(), + fieldNames: schema.arrayOf(schema.string()), + influencerNames: schema.arrayOf(schema.maybe(schema.string())), + timeFieldName: schema.string(), + earliestMs: schema.number(), + latestMs: schema.number(), +}); + +export const validateJobSchema = schema.object({ + duration: schema.object({ + start: schema.maybe(schema.number()), + end: schema.maybe(schema.number()), + }), + fields: schema.maybe(schema.any()), + job: schema.object(anomalyDetectionJobSchema), +}); + +const datafeedConfigSchema = schema.object({ + datafeed_id: schema.string(), + aggregations: schema.maybe(schema.any()), + aggs: schema.maybe(schema.any()), + chunking_config: schema.maybe(schema.any()), + frequency: schema.maybe(schema.string()), + indices: schema.arrayOf(schema.string()), + indexes: schema.maybe(schema.arrayOf(schema.string())), + job_id: schema.string(), + query: schema.any(), + query_delay: schema.maybe(schema.string()), + script_fields: schema.maybe(schema.any()), + scroll_size: schema.maybe(schema.number()), + delayed_data_check_config: schema.maybe(schema.any()), +}); + +export const validateCardinalitySchema = { + ...anomalyDetectionJobSchema, + datafeed_config: datafeedConfigSchema, +}; diff --git a/x-pack/legacy/plugins/ml/server/new_platform/plugin.ts b/x-pack/legacy/plugins/ml/server/new_platform/plugin.ts index 068bfc40f53e1..69b08bfeda13d 100644 --- a/x-pack/legacy/plugins/ml/server/new_platform/plugin.ts +++ b/x-pack/legacy/plugins/ml/server/new_platform/plugin.ts @@ -25,15 +25,12 @@ import { checkLicense } from '../lib/check_license'; // @ts-ignore: could not find declaration file for module import { mirrorPluginStatus } from '../../../../server/lib/mirror_plugin_status'; import { LICENSE_TYPE } from '../../common/constants/license'; -// @ts-ignore: could not find declaration file for module import { annotationRoutes } from '../routes/annotations'; -// @ts-ignore: could not find declaration file for module import { jobRoutes } from '../routes/anomaly_detectors'; // @ts-ignore: could not find declaration file for module import { dataFeedRoutes } from '../routes/datafeeds'; // @ts-ignore: could not find declaration file for module import { indicesRoutes } from '../routes/indices'; -// @ts-ignore: could not find declaration file for module import { jobValidationRoutes } from '../routes/job_validation'; import { makeMlUsageCollector } from '../lib/ml_telemetry'; // @ts-ignore: could not find declaration file for module @@ -41,17 +38,13 @@ import { notificationRoutes } from '../routes/notification_settings'; // @ts-ignore: could not find declaration file for module import { systemRoutes } from '../routes/system'; import { dataFrameAnalyticsRoutes } from '../routes/data_frame_analytics'; -// @ts-ignore: could not find declaration file for module import { dataRecognizer } from '../routes/modules'; -// @ts-ignore: could not find declaration file for module import { dataVisualizerRoutes } from '../routes/data_visualizer'; import { calendars } from '../routes/calendars'; // @ts-ignore: could not find declaration file for module import { fieldsService } from '../routes/fields_service'; import { filtersRoutes } from '../routes/filters'; -// @ts-ignore: could not find declaration file for module import { resultsServiceRoutes } from '../routes/results_service'; -// @ts-ignore: could not find declaration file for module import { jobServiceRoutes } from '../routes/job_service'; // @ts-ignore: could not find declaration file for module import { jobAuditMessagesRoutes } from '../routes/job_audit_messages'; diff --git a/x-pack/legacy/plugins/ml/server/routes/data_visualizer.ts b/x-pack/legacy/plugins/ml/server/routes/data_visualizer.ts index 235fc26d78441..df7e4b7010877 100644 --- a/x-pack/legacy/plugins/ml/server/routes/data_visualizer.ts +++ b/x-pack/legacy/plugins/ml/server/routes/data_visualizer.ts @@ -26,7 +26,7 @@ function getOverallStats( earliestMs: number, latestMs: number ) { - const dv = new DataVisualizer(context); + const dv = new DataVisualizer(context.ml!.mlClient.callAsCurrentUser); return dv.getOverallStats( indexPatternTitle, query, @@ -51,7 +51,7 @@ function getStatsForFields( interval: number, maxExamples: number ) { - const dv = new DataVisualizer(context); + const dv = new DataVisualizer(context.ml!.mlClient.callAsCurrentUser); return dv.getStatsForFields( indexPatternTitle, query, diff --git a/x-pack/legacy/plugins/ml/server/routes/job_validation.js b/x-pack/legacy/plugins/ml/server/routes/job_validation.js deleted file mode 100644 index 7ea3d6ebf1557..0000000000000 --- a/x-pack/legacy/plugins/ml/server/routes/job_validation.js +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import Boom from 'boom'; - -import { callWithRequestFactory } from '../client/call_with_request_factory'; -import { wrapError } from '../client/errors'; -import { estimateBucketSpanFactory } from '../models/bucket_span_estimator'; -import { calculateModelMemoryLimitProvider } from '../models/calculate_model_memory_limit'; -import { validateJob, validateCardinality } from '../models/job_validation'; - -export function jobValidationRoutes({ - commonRouteConfig, - config, - elasticsearchPlugin, - route, - xpackMainPlugin, -}) { - function calculateModelMemoryLimit(callWithRequest, payload) { - const { - indexPattern, - splitFieldName, - query, - fieldNames, - influencerNames, - timeFieldName, - earliestMs, - latestMs, - } = payload; - - return calculateModelMemoryLimitProvider(callWithRequest)( - indexPattern, - splitFieldName, - query, - fieldNames, - influencerNames, - timeFieldName, - earliestMs, - latestMs - ); - } - - route({ - method: 'POST', - path: '/api/ml/validate/estimate_bucket_span', - handler(request) { - const callWithRequest = callWithRequestFactory(elasticsearchPlugin, request); - try { - return ( - estimateBucketSpanFactory( - callWithRequest, - elasticsearchPlugin, - xpackMainPlugin - )(request.payload) - // this catch gets triggered when the estimation code runs without error - // but isn't able to come up with a bucket span estimation. - // this doesn't return a HTTP error but an object with an error message - // which the client is then handling. triggering a HTTP error would be - // too severe for this case. - .catch(resp => ({ - error: true, - message: resp, - })) - ); - // this catch gets triggered when an actual error gets thrown when running - // the estimation code, for example when the request payload is malformed - } catch (error) { - throw Boom.badRequest(error); - } - }, - config: { - ...commonRouteConfig, - }, - }); - - route({ - method: 'POST', - path: '/api/ml/validate/calculate_model_memory_limit', - handler(request) { - const callWithRequest = callWithRequestFactory(elasticsearchPlugin, request); - return calculateModelMemoryLimit(callWithRequest, request.payload).catch(resp => - wrapError(resp) - ); - }, - config: { - ...commonRouteConfig, - }, - }); - - route({ - method: 'POST', - path: '/api/ml/validate/cardinality', - handler(request, reply) { - const callWithRequest = callWithRequestFactory(elasticsearchPlugin, request); - return validateCardinality(callWithRequest, request.payload) - .then(reply) - .catch(resp => wrapError(resp)); - }, - config: { - ...commonRouteConfig, - }, - }); - - route({ - method: 'POST', - path: '/api/ml/validate/job', - handler(request) { - const callWithRequest = callWithRequestFactory(elasticsearchPlugin, request); - // pkg.branch corresponds to the version used in documentation links. - const version = config.get('pkg.branch'); - return validateJob( - callWithRequest, - request.payload, - version, - elasticsearchPlugin, - xpackMainPlugin - ).catch(resp => wrapError(resp)); - }, - config: { - ...commonRouteConfig, - }, - }); -} diff --git a/x-pack/legacy/plugins/ml/server/routes/job_validation.ts b/x-pack/legacy/plugins/ml/server/routes/job_validation.ts new file mode 100644 index 0000000000000..64c9ccd27720a --- /dev/null +++ b/x-pack/legacy/plugins/ml/server/routes/job_validation.ts @@ -0,0 +1,194 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import Boom from 'boom'; +import { RequestHandlerContext } from 'src/core/server'; +import { schema, TypeOf } from '@kbn/config-schema'; +import { licensePreRoutingFactory } from '../new_platform/licence_check_pre_routing_factory'; +import { wrapError } from '../client/error_wrapper'; +import { RouteInitialization } from '../new_platform/plugin'; +import { + estimateBucketSpanSchema, + modelMemoryLimitSchema, + validateCardinalitySchema, + validateJobSchema, +} from '../new_platform/job_validation_schema'; +import { estimateBucketSpanFactory } from '../models/bucket_span_estimator'; +import { calculateModelMemoryLimitProvider } from '../models/calculate_model_memory_limit'; +import { validateJob, validateCardinality } from '../models/job_validation'; + +type CalculateModelMemoryLimitPayload = TypeOf; + +/** + * Routes for job validation + */ +export function jobValidationRoutes({ config, xpackMainPlugin, router }: RouteInitialization) { + function calculateModelMemoryLimit( + context: RequestHandlerContext, + payload: CalculateModelMemoryLimitPayload + ) { + const { + indexPattern, + splitFieldName, + query, + fieldNames, + influencerNames, + timeFieldName, + earliestMs, + latestMs, + } = payload; + + return calculateModelMemoryLimitProvider(context.ml!.mlClient.callAsCurrentUser)( + indexPattern, + splitFieldName, + query, + fieldNames, + influencerNames, + timeFieldName, + earliestMs, + latestMs + ); + } + + /** + * @apiGroup JobValidation + * + * @api {post} /api/ml/validate/estimate_bucket_span Estimate bucket span + * @apiName EstimateBucketSpan + * @apiDescription Estimates minimum viable bucket span based on the characteristics of a pre-viewed subset of the data + */ + router.post( + { + path: '/api/ml/validate/estimate_bucket_span', + validate: { + body: estimateBucketSpanSchema, + }, + }, + licensePreRoutingFactory(xpackMainPlugin, async (context, request, response) => { + try { + let errorResp; + const resp = await estimateBucketSpanFactory( + context.ml!.mlClient.callAsCurrentUser, + context.core.elasticsearch.adminClient.callAsInternalUser, + xpackMainPlugin + )(request.body) + // this catch gets triggered when the estimation code runs without error + // but isn't able to come up with a bucket span estimation. + // this doesn't return a HTTP error but an object with an error message + // which the client is then handling. triggering a HTTP error would be + // too severe for this case. + .catch((error: any) => { + errorResp = { + error: true, + message: error, + }; + }); + + return response.ok({ + body: errorResp !== undefined ? errorResp : resp, + }); + } catch (e) { + // this catch gets triggered when an actual error gets thrown when running + // the estimation code, for example when the request payload is malformed + throw Boom.badRequest(e); + } + }) + ); + + /** + * @apiGroup JobValidation + * + * @api {post} /api/ml/validate/calculate_model_memory_limit Calculates model memory limit + * @apiName CalculateModelMemoryLimit + * @apiDescription Calculates the model memory limit + * + * @apiSuccess {String} modelMemoryLimit + */ + router.post( + { + path: '/api/ml/validate/calculate_model_memory_limit', + validate: { + body: modelMemoryLimitSchema, + }, + }, + licensePreRoutingFactory(xpackMainPlugin, async (context, request, response) => { + try { + const resp = await calculateModelMemoryLimit(context, request.body); + + return response.ok({ + body: resp, + }); + } catch (e) { + return response.customError(wrapError(e)); + } + }) + ); + + /** + * @apiGroup JobValidation + * + * @api {post} /api/ml/validate/cardinality Validate cardinality + * @apiName ValidateCardinality + * @apiDescription Validates cardinality for the given job configuration + */ + router.post( + { + path: '/api/ml/validate/cardinality', + validate: { + body: schema.object(validateCardinalitySchema), + }, + }, + licensePreRoutingFactory(xpackMainPlugin, async (context, request, response) => { + try { + const resp = await validateCardinality( + context.ml!.mlClient.callAsCurrentUser, + request.body + ); + + return response.ok({ + body: resp, + }); + } catch (e) { + return response.customError(wrapError(e)); + } + }) + ); + + /** + * @apiGroup JobValidation + * + * @api {post} /api/ml/validate/job Validates job + * @apiName ValidateJob + * @apiDescription Validates the given job configuration + */ + router.post( + { + path: '/api/ml/validate/job', + validate: { + body: validateJobSchema, + }, + }, + licensePreRoutingFactory(xpackMainPlugin, async (context, request, response) => { + try { + // pkg.branch corresponds to the version used in documentation links. + const version = config.get('pkg.branch'); + const resp = await validateJob( + context.ml!.mlClient.callAsCurrentUser, + request.body, + version, + context.core.elasticsearch.adminClient.callAsInternalUser, + xpackMainPlugin + ); + + return response.ok({ + body: resp, + }); + } catch (e) { + return response.customError(wrapError(e)); + } + }) + ); +} diff --git a/x-pack/legacy/plugins/monitoring/index.ts b/x-pack/legacy/plugins/monitoring/index.ts index 1186fde52dc46..3a23140104e16 100644 --- a/x-pack/legacy/plugins/monitoring/index.ts +++ b/x-pack/legacy/plugins/monitoring/index.ts @@ -108,15 +108,16 @@ export const monitoring = (kibana: LegacyPluginApi): LegacyPluginSpec => { }; const legacyPlugins = plugins as Partial & { infra?: InfraPlugin }; - const { xpack_main, elasticsearch, infra, alerting } = legacyPlugins; + const { xpack_main, elasticsearch, infra } = legacyPlugins; const { core: coreSetup, - plugins: { usageCollection, licensing }, + plugins: { usageCollection, licensing, alerting }, } = server.newPlatform.setup; const pluginsSetup: PluginsSetup = { usageCollection, licensing, + alerting, }; const __LEGACY: LegacySetup = { @@ -125,7 +126,6 @@ export const monitoring = (kibana: LegacyPluginApi): LegacyPluginSpec => { xpack_main, elasticsearch, infra, - alerting, }, }; diff --git a/x-pack/legacy/plugins/monitoring/public/components/alerts/status.tsx b/x-pack/legacy/plugins/monitoring/public/components/alerts/status.tsx index 0ee0015ed39a7..c1019cb64be52 100644 --- a/x-pack/legacy/plugins/monitoring/public/components/alerts/status.tsx +++ b/x-pack/legacy/plugins/monitoring/public/components/alerts/status.tsx @@ -19,7 +19,7 @@ import { import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; import { ELASTIC_WEBSITE_URL, DOC_LINK_VERSION } from 'ui/documentation_links'; -import { Alert } from '../../../../alerting/server/types'; +import { Alert } from '../../../../../../plugins/alerting/common'; import { getSetupModeState, addSetupModeCallback, toggleSetupMode } from '../../lib/setup_mode'; import { NUMBER_OF_MIGRATED_ALERTS, ALERT_TYPE_PREFIX } from '../../../common/constants'; import { AlertsConfiguration } from './configuration'; diff --git a/x-pack/legacy/plugins/monitoring/server/alerts/license_expiration.test.ts b/x-pack/legacy/plugins/monitoring/server/alerts/license_expiration.test.ts index 38b4e6c60ca48..1d52ab04dcdbd 100644 --- a/x-pack/legacy/plugins/monitoring/server/alerts/license_expiration.test.ts +++ b/x-pack/legacy/plugins/monitoring/server/alerts/license_expiration.test.ts @@ -11,9 +11,8 @@ import { MONITORING_CONFIG_ALERTING_EMAIL_ADDRESS, } from '../../common/constants'; import { Logger } from 'src/core/server'; -import { AlertServices } from '../../../alerting/server/types'; +import { AlertServices, AlertInstance } from '../../../../../plugins/alerting/server'; import { savedObjectsClientMock } from 'src/core/server/mocks'; -import { AlertInstance } from '../../../alerting/server/alert_instance'; import { AlertState, AlertClusterState, diff --git a/x-pack/legacy/plugins/monitoring/server/alerts/license_expiration.ts b/x-pack/legacy/plugins/monitoring/server/alerts/license_expiration.ts index 8688a2b08efc4..f968e90f70b2d 100644 --- a/x-pack/legacy/plugins/monitoring/server/alerts/license_expiration.ts +++ b/x-pack/legacy/plugins/monitoring/server/alerts/license_expiration.ts @@ -10,7 +10,7 @@ import { Legacy } from 'kibana'; import { Logger } from 'src/core/server'; import { i18n } from '@kbn/i18n'; import { ALERT_TYPE_LICENSE_EXPIRATION, INDEX_PATTERN_ELASTICSEARCH } from '../../common/constants'; -import { AlertType } from '../../../alerting'; +import { AlertType } from '../../../../../plugins/alerting/server'; import { fetchLicenses } from '../lib/alerts/fetch_licenses'; import { fetchDefaultEmailAddress } from '../lib/alerts/fetch_default_email_address'; import { fetchClusters } from '../lib/alerts/fetch_clusters'; diff --git a/x-pack/legacy/plugins/monitoring/server/alerts/types.d.ts b/x-pack/legacy/plugins/monitoring/server/alerts/types.d.ts index 6346ca00dabbd..76fc7074e411c 100644 --- a/x-pack/legacy/plugins/monitoring/server/alerts/types.d.ts +++ b/x-pack/legacy/plugins/monitoring/server/alerts/types.d.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ import { Moment } from 'moment'; -import { AlertExecutorOptions } from '../../../alerting'; +import { AlertExecutorOptions } from '../../../../../plugins/alerting/server'; export interface AlertLicense { status: string; diff --git a/x-pack/legacy/plugins/monitoring/server/lib/alerts/license_expiration.lib.ts b/x-pack/legacy/plugins/monitoring/server/lib/alerts/license_expiration.lib.ts index 8a75fc1fbbd82..520cb31e151ae 100644 --- a/x-pack/legacy/plugins/monitoring/server/lib/alerts/license_expiration.lib.ts +++ b/x-pack/legacy/plugins/monitoring/server/lib/alerts/license_expiration.lib.ts @@ -5,7 +5,7 @@ */ import { Moment } from 'moment-timezone'; import { i18n } from '@kbn/i18n'; -import { AlertInstance } from '../../../../alerting/server/alert_instance'; +import { AlertInstance } from '../../../../../../plugins/alerting/server'; import { AlertLicense } from '../../alerts/types'; const RESOLVED_SUBJECT = i18n.translate( diff --git a/x-pack/legacy/plugins/monitoring/server/plugin.js b/x-pack/legacy/plugins/monitoring/server/plugin.js index 304d2c08a1688..3d6d110a01949 100644 --- a/x-pack/legacy/plugins/monitoring/server/plugin.js +++ b/x-pack/legacy/plugins/monitoring/server/plugin.js @@ -34,7 +34,7 @@ export class Plugin { } = __LEGACY; const config = monitoringConfig(); - const { usageCollection, licensing } = pluginsSetup; + const { usageCollection, licensing, alerting } = pluginsSetup; registerMonitoringCollection(); /* * Register collector objects for stats to show up in the APIs @@ -152,7 +152,7 @@ export class Plugin { }; }); - if (KIBANA_ALERTING_ENABLED && plugins.alerting) { + if (KIBANA_ALERTING_ENABLED && alerting) { // this is not ready right away but we need to register alerts right away async function getMonitoringCluster() { const configs = config.get('xpack.monitoring.elasticsearch'); @@ -174,7 +174,7 @@ export class Plugin { function getLogger(contexts) { return logger.get('plugins', LOGGING_TAG, ...contexts); } - plugins.alerting.setup.registerType( + alerting.registerType( getLicenseExpiration( hapiServer, getMonitoringCluster, diff --git a/x-pack/legacy/plugins/siem/cypress/integration/smoke_tests/timeline/toggle_column.spec.ts b/x-pack/legacy/plugins/siem/cypress/integration/smoke_tests/timeline/toggle_column.spec.ts index d410a89cf0723..ccd6ae818c6cd 100644 --- a/x-pack/legacy/plugins/siem/cypress/integration/smoke_tests/timeline/toggle_column.spec.ts +++ b/x-pack/legacy/plugins/siem/cypress/integration/smoke_tests/timeline/toggle_column.spec.ts @@ -4,92 +4,66 @@ * you may not use this file except in compliance with the Elastic License. */ -import { drag, drop } from '../../lib/drag_n_drop/helpers'; -import { populateTimeline } from '../../lib/fields_browser/helpers'; -import { createNewTimeline, toggleFirstTimelineEventDetails } from '../../lib/timeline/helpers'; -import { HOSTS_PAGE } from '../../lib/urls'; -import { loginAndWaitForPage, DEFAULT_TIMEOUT } from '../../lib/util/helpers'; +import { HOSTS_PAGE } from '../../../urls/navigation'; +import { loginAndWaitForPage, DEFAULT_TIMEOUT } from '../../../tasks/login'; +import { + createNewTimeline, + populateTimeline, + expandFirstTimelineEventDetails, + uncheckTimestampToggleField, + checkIdToggleField, + dragAndDropIdToggleFieldToTimeline, +} from '../../../tasks/timeline/main'; +import { openTimeline } from '../../../tasks/siem_main'; +import { + TIMESTAMP_TOGGLE_FIELD, + ID_TOGGLE_FIELD, + TIMESTAMP_HEADER_FIELD, + ID_HEADER_FIELD, +} from '../../../screens/timeline/main'; describe('toggle column in timeline', () => { before(() => { loginAndWaitForPage(HOSTS_PAGE); }); + beforeEach(() => { + openTimeline(); + populateTimeline(); + }); + afterEach(() => { createNewTimeline(); }); - const timestampField = '@timestamp'; - const idField = '_id'; - it('displays a checked Toggle field checkbox for `@timestamp`, a default timeline column', () => { - populateTimeline(); - - toggleFirstTimelineEventDetails(); - - cy.get(`[data-test-subj="toggle-field-${timestampField}"]`).should('be.checked'); + expandFirstTimelineEventDetails(); + cy.get(TIMESTAMP_TOGGLE_FIELD).should('be.checked'); }); it('displays an Unchecked Toggle field checkbox for `_id`, because it is NOT a default timeline column', () => { - populateTimeline(); - - cy.get(`[data-test-subj="timeline"] [data-test-subj="toggle-field-${idField}"]`).should( - 'not.be.checked' - ); + cy.get(ID_TOGGLE_FIELD).should('not.be.checked'); }); it('removes the @timestamp field from the timeline when the user un-checks the toggle', () => { - populateTimeline(); - - toggleFirstTimelineEventDetails(); - - cy.get(`[data-test-subj="timeline"] [data-test-subj="header-text-${timestampField}"]`).should( - 'exist' - ); + expandFirstTimelineEventDetails(); + uncheckTimestampToggleField(); - cy.get(`[data-test-subj="timeline"] [data-test-subj="toggle-field-${timestampField}"]`, { - timeout: DEFAULT_TIMEOUT, - }).uncheck({ force: true }); - - cy.get(`[data-test-subj="timeline"] [data-test-subj="header-text-${timestampField}"]`).should( - 'not.exist' - ); + cy.get(TIMESTAMP_HEADER_FIELD).should('not.exist'); }); it('adds the _id field to the timeline when the user checks the field', () => { - populateTimeline(); - - toggleFirstTimelineEventDetails(); - - cy.get(`[data-test-subj="timeline"] [data-test-subj="header-text-${idField}"]`).should( - 'not.exist' - ); - - cy.get(`[data-test-subj="timeline"] [data-test-subj="toggle-field-${idField}"]`).check({ - force: true, - }); + expandFirstTimelineEventDetails(); + checkIdToggleField(); - cy.get(`[data-test-subj="timeline"] [data-test-subj="header-text-${idField}"]`).should('exist'); + cy.get(ID_HEADER_FIELD).should('exist'); }); it('adds the _id field to the timeline via drag and drop', () => { - populateTimeline(); - - toggleFirstTimelineEventDetails(); - - cy.get(`[data-test-subj="timeline"] [data-test-subj="header-text-${idField}"]`).should( - 'not.exist' - ); - - cy.get(`[data-test-subj="timeline"] [data-test-subj="field-name-${idField}"]`).then(field => - drag(field) - ); - - cy.get(`[data-test-subj="timeline"] [data-test-subj="headers-group"]`).then(headersDropArea => - drop(headersDropArea) - ); + expandFirstTimelineEventDetails(); + dragAndDropIdToggleFieldToTimeline(); - cy.get(`[data-test-subj="timeline"] [data-test-subj="header-text-${idField}"]`, { + cy.get(ID_HEADER_FIELD, { timeout: DEFAULT_TIMEOUT, }).should('exist'); }); diff --git a/x-pack/legacy/plugins/siem/cypress/screens/timeline/main.ts b/x-pack/legacy/plugins/siem/cypress/screens/timeline/main.ts index 4c722ffa5f215..7b47c159c4a0a 100644 --- a/x-pack/legacy/plugins/siem/cypress/screens/timeline/main.ts +++ b/x-pack/legacy/plugins/siem/cypress/screens/timeline/main.ts @@ -37,3 +37,15 @@ export const TIMELINE_FLYOUT_BODY = '[data-test-subj="eui-flyout-body"]'; export const TIMELINE_NOT_READY_TO_DROP_BUTTON = '[data-test-subj="flyout-button-not-ready-to-drop"]'; + +export const TOGGLE_TIMELINE_EXPAND_EVENT = '[data-test-subj="expand-event"]'; + +export const TIMESTAMP_TOGGLE_FIELD = '[data-test-subj="toggle-field-@timestamp"]'; + +export const ID_TOGGLE_FIELD = '[data-test-subj="toggle-field-_id"]'; + +export const TIMESTAMP_HEADER_FIELD = '[data-test-subj="header-text-@timestamp"]'; + +export const ID_HEADER_FIELD = '[data-test-subj="timeline"] [data-test-subj="header-text-_id"]'; + +export const ID_FIELD = '[data-test-subj="timeline"] [data-test-subj="field-name-_id"]'; diff --git a/x-pack/legacy/plugins/siem/cypress/tasks/timeline/main.ts b/x-pack/legacy/plugins/siem/cypress/tasks/timeline/main.ts index f347c072a3584..d26c0f2911f75 100644 --- a/x-pack/legacy/plugins/siem/cypress/tasks/timeline/main.ts +++ b/x-pack/legacy/plugins/siem/cypress/tasks/timeline/main.ts @@ -14,12 +14,19 @@ import { TIMELINE_INSPECT_BUTTON, CREATE_NEW_TIMELINE, CLOSE_TIMELINE_BTN, + TOGGLE_TIMELINE_EXPAND_EVENT, + TIMESTAMP_TOGGLE_FIELD, + ID_TOGGLE_FIELD, + ID_HEADER_FIELD, + ID_FIELD, } from '../../screens/timeline/main'; +import { drag, drop } from '../../tasks/common'; + export const hostExistsQuery = 'host.name: *'; export const populateTimeline = () => { - cy.get(`${SEARCH_OR_FILTER_CONTAINER} input`).type(`${hostExistsQuery} {enter}`); + executeTimelineKQL(hostExistsQuery); cy.get(SERVER_SIDE_EVENT_COUNT, { timeout: DEFAULT_TIMEOUT }) .invoke('text') .should('be.above', 0); @@ -47,3 +54,35 @@ export const createNewTimeline = () => { cy.get(CREATE_NEW_TIMELINE).click(); cy.get(CLOSE_TIMELINE_BTN).click({ force: true }); }; + +export const expandFirstTimelineEventDetails = () => { + cy.get(TOGGLE_TIMELINE_EXPAND_EVENT, { timeout: DEFAULT_TIMEOUT }) + .first() + .click({ force: true }); +}; + +export const uncheckTimestampToggleField = () => { + cy.get(TIMESTAMP_TOGGLE_FIELD).should('exist'); + + cy.get(TIMESTAMP_TOGGLE_FIELD, { + timeout: DEFAULT_TIMEOUT, + }).uncheck({ force: true }); +}; + +export const checkIdToggleField = () => { + cy.get(ID_TOGGLE_FIELD).should('not.exist'); + + cy.get(ID_TOGGLE_FIELD).check({ + force: true, + }); +}; + +export const dragAndDropIdToggleFieldToTimeline = () => { + cy.get(ID_HEADER_FIELD).should('not.exist'); + + cy.get(ID_FIELD).then(field => drag(field)); + + cy.get(`[data-test-subj="timeline"] [data-test-subj="headers-group"]`).then(headersDropArea => + drop(headersDropArea) + ); +}; diff --git a/x-pack/legacy/plugins/siem/index.ts b/x-pack/legacy/plugins/siem/index.ts index c786dad61c09d..731ef10aa225e 100644 --- a/x-pack/legacy/plugins/siem/index.ts +++ b/x-pack/legacy/plugins/siem/index.ts @@ -152,7 +152,6 @@ export const siem = (kibana: any) => { const initializerContext = { ...coreContext, env }; const __legacy = { config: server.config, - alerting: server.plugins.alerting, route: server.route.bind(server), }; diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/README.md b/x-pack/legacy/plugins/siem/server/lib/detection_engine/README.md index 1d33466a458d2..1e8e3d5e3dd75 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/README.md +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/README.md @@ -152,8 +152,8 @@ logging.events: ``` See these two README.md's pages for more references on the alerting and actions API: -https://github.com/elastic/kibana/blob/master/x-pack/legacy/plugins/alerting/README.md -https://github.com/elastic/kibana/tree/master/x-pack/legacy/plugins/actions +https://github.com/elastic/kibana/blob/master/x-pack/plugins/alerting/README.md +https://github.com/elastic/kibana/tree/master/x-pack/plugins/actions ### Signals API diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/__mocks__/clients_service_mock.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/__mocks__/clients_service_mock.ts index f89e938b8a636..29131429d12cb 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/__mocks__/clients_service_mock.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/__mocks__/clients_service_mock.ts @@ -8,7 +8,7 @@ import { elasticsearchServiceMock, savedObjectsClientMock, } from '../../../../../../../../../src/core/server/mocks'; -import { alertsClientMock } from '../../../../../../alerting/server/alerts_client.mock'; +import { alertsClientMock } from '../../../../../../../../plugins/alerting/server/mocks'; import { ActionsClient } from '../../../../../../../../plugins/actions/server'; import { actionsClientMock } from '../../../../../../../../plugins/actions/server/mocks'; import { GetScopedClients } from '../../../../services'; diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/create_rules.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/create_rules.ts index 61f2e87811509..c8205859407c0 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/create_rules.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/create_rules.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { Alert } from '../../../../../alerting/common'; +import { Alert } from '../../../../../../../plugins/alerting/common'; import { APP_ID, SIGNALS_ID } from '../../../../common/constants'; import { CreateRuleParams } from './types'; import { addTags } from './add_tags'; diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/find_rules.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/find_rules.ts index e193e123f4281..f333a7c340705 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/find_rules.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/find_rules.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { FindResult } from '../../../../../alerting/server/alerts_client'; +import { FindResult } from '../../../../../../../plugins/alerting/server'; import { SIGNALS_ID } from '../../../../common/constants'; import { FindRuleParams } from './types'; diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/get_existing_prepackaged_rules.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/get_existing_prepackaged_rules.test.ts index 25bac383ecf72..9774d10a37d6f 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/get_existing_prepackaged_rules.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/get_existing_prepackaged_rules.test.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { alertsClientMock } from '../../../../../alerting/server/alerts_client.mock'; +import { alertsClientMock } from '../../../../../../../plugins/alerting/server/mocks'; import { getResult, getFindResultWithSingleHit, diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/get_existing_prepackaged_rules.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/get_existing_prepackaged_rules.ts index a48957da7aa94..b5e826ed42723 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/get_existing_prepackaged_rules.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/get_existing_prepackaged_rules.ts @@ -5,7 +5,7 @@ */ import { INTERNAL_IMMUTABLE_KEY } from '../../../../common/constants'; -import { AlertsClient } from '../../../../../alerting'; +import { AlertsClient } from '../../../../../../../plugins/alerting/server'; import { RuleAlertType, isAlertTypes } from './types'; import { findRules } from './find_rules'; diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/get_export_all.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/get_export_all.test.ts index 35d3489dad6fc..304f9a741c6f4 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/get_export_all.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/get_export_all.test.ts @@ -4,12 +4,12 @@ * you may not use this file except in compliance with the Elastic License. */ -import { alertsClientMock } from '../../../../../alerting/server/alerts_client.mock'; import { getResult, getFindResultWithSingleHit, FindHit, } from '../routes/__mocks__/request_responses'; +import { alertsClientMock } from '../../../../../../../plugins/alerting/server/mocks'; import { getExportAll } from './get_export_all'; describe('getExportAll', () => { diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/get_export_all.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/get_export_all.ts index dca6eba4e6556..434919f80e149 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/get_export_all.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/get_export_all.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { AlertsClient } from '../../../../../alerting'; +import { AlertsClient } from '../../../../../../../plugins/alerting/server'; import { getNonPackagedRules } from './get_existing_prepackaged_rules'; import { getExportDetailsNdjson } from './get_export_details_ndjson'; import { transformAlertsToRules, transformRulesToNdjson } from '../routes/rules/utils'; diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/get_export_by_object_ids.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/get_export_by_object_ids.test.ts index 4b6ea527a2027..98f5df4852530 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/get_export_by_object_ids.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/get_export_by_object_ids.test.ts @@ -4,13 +4,13 @@ * you may not use this file except in compliance with the Elastic License. */ -import { alertsClientMock } from '../../../../../alerting/server/alerts_client.mock'; import { getExportByObjectIds, getRulesFromObjects, RulesErrors } from './get_export_by_object_ids'; import { getResult, getFindResultWithSingleHit, FindHit, } from '../routes/__mocks__/request_responses'; +import { alertsClientMock } from '../../../../../../../plugins/alerting/server/mocks'; describe('get_export_by_object_ids', () => { describe('getExportByObjectIds', () => { diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/get_export_by_object_ids.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/get_export_by_object_ids.ts index 7e0d61d040617..e3b38a879fc3d 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/get_export_by_object_ids.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/get_export_by_object_ids.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { AlertsClient } from '../../../../../alerting'; +import { AlertsClient } from '../../../../../../../plugins/alerting/server'; import { getExportDetailsNdjson } from './get_export_details_ndjson'; import { isAlertType } from '../rules/types'; import { readRules } from './read_rules'; diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/install_prepacked_rules.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/install_prepacked_rules.ts index 07e8c6940e747..3d9ec128963f6 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/install_prepacked_rules.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/install_prepacked_rules.ts @@ -4,9 +4,9 @@ * you may not use this file except in compliance with the Elastic License. */ +import { Alert } from '../../../../../../../plugins/alerting/common'; import { ActionsClient } from '../../../../../../../plugins/actions/server'; -import { AlertsClient } from '../../../../../alerting'; -import { Alert } from '../../../../../alerting/server/types'; +import { AlertsClient } from '../../../../../../../plugins/alerting/server'; import { createRules } from './create_rules'; import { PrepackagedRules } from '../types'; diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/patch_rules.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/patch_rules.ts index f560b67cdc587..1d904b2b349ae 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/patch_rules.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/patch_rules.ts @@ -5,7 +5,7 @@ */ import { defaults } from 'lodash/fp'; -import { PartialAlert } from '../../../../../alerting/server/types'; +import { PartialAlert } from '../../../../../../../plugins/alerting/server'; import { readRules } from './read_rules'; import { PatchRuleParams, IRuleSavedAttributesSavedObjectAttributes } from './types'; import { addTags } from './add_tags'; diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/read_rules.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/read_rules.test.ts index c637860c5646a..45507a69f50c2 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/read_rules.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/read_rules.test.ts @@ -4,8 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ -import { alertsClientMock } from '../../../../../alerting/server/alerts_client.mock'; import { readRules } from './read_rules'; +import { alertsClientMock } from '../../../../../../../plugins/alerting/server/mocks'; import { getResult, getFindResultWithSingleHit } from '../routes/__mocks__/request_responses'; describe('read_rules', () => { diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/read_rules.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/read_rules.ts index e8e883701c6a9..cbe6dbda8449f 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/read_rules.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/read_rules.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { SanitizedAlert } from '../../../../../alerting/common'; +import { SanitizedAlert } from '../../../../../../../plugins/alerting/common'; import { INTERNAL_RULE_ID_KEY } from '../../../../common/constants'; import { findRules } from './find_rules'; import { ReadRuleParams, isAlertType } from './types'; diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/types.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/types.ts index 8579447a74c69..fa22765c143e1 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/types.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/types.ts @@ -13,12 +13,12 @@ import { SavedObjectsFindResponse, SavedObjectsClientContract, } from 'kibana/server'; +import { AlertsClient } from '../../../../../../../plugins/alerting/server'; +import { Alert } from '../../../../../../../plugins/alerting/common'; import { SIGNALS_ID } from '../../../../common/constants'; import { LegacyRequest } from '../../../types'; -import { AlertsClient } from '../../../../../alerting/server'; import { ActionsClient } from '../../../../../../../plugins/actions/server'; import { RuleAlertParams, RuleTypeParams, RuleAlertParamsRest } from '../types'; -import { Alert } from '../../../../../alerting/server/types'; export type PatchRuleAlertParamsRest = Partial & { id: string | undefined; diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/update_prepacked_rules.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/update_prepacked_rules.ts index 2fa903f3d713f..c63237c93daf4 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/update_prepacked_rules.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/update_prepacked_rules.ts @@ -6,7 +6,7 @@ import { SavedObjectsClientContract } from 'kibana/server'; import { ActionsClient } from '../../../../../../../plugins/actions/server'; -import { AlertsClient } from '../../../../../alerting'; +import { AlertsClient } from '../../../../../../../plugins/alerting/server'; import { patchRules } from './patch_rules'; import { PrepackagedRules } from '../types'; diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/update_rules.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/update_rules.ts index 1dc5d8429fab8..9ead8313b2c91 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/update_rules.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/update_rules.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { PartialAlert } from '../../../../../alerting/server/types'; +import { PartialAlert } from '../../../../../../../plugins/alerting/server'; import { readRules } from './read_rules'; import { IRuleSavedAttributesSavedObjectAttributes, UpdateRuleParams } from './types'; import { addTags } from './add_tags'; diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/get_alert_instances.sh b/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/get_alert_instances.sh index f42d4a52594a7..b5f272d0a8a09 100755 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/get_alert_instances.sh +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/get_alert_instances.sh @@ -10,7 +10,7 @@ set -e ./check_env_variables.sh # Example: ./get_alert_instances.sh -# https://github.com/elastic/kibana/blob/master/x-pack/legacy/plugins/alerting/README.md#get-apialert_find-find-alerts +# https://github.com/elastic/kibana/blob/master/x-pack/plugins/alerting/README.md#get-apialert_find-find-alerts curl -s -k \ -u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \ -X GET ${KIBANA_URL}${SPACE_URL}/api/alert/_find \ diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/get_alert_types.sh b/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/get_alert_types.sh index a7c6fa567ecdd..28c250e9368a6 100755 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/get_alert_types.sh +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/get_alert_types.sh @@ -10,7 +10,7 @@ set -e ./check_env_variables.sh # Example: ./get_alert_types.sh -# https://github.com/elastic/kibana/blob/master/x-pack/legacy/plugins/alerting/README.md#get-apialerttypes-list-alert-types +# https://github.com/elastic/kibana/blob/master/x-pack/plugins/alerting/README.md#get-apialerttypes-list-alert-types curl -s -k \ -u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \ -X GET ${KIBANA_URL}${SPACE_URL}/api/alert/types \ diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/build_signal.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/build_signal.test.ts index dcd36ab811e6a..93e9c7f6e0d50 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/build_signal.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/build_signal.test.ts @@ -5,8 +5,14 @@ */ import { sampleDocNoSortId, sampleRule } from './__mocks__/es_results'; -import { buildSignal, buildAncestor, buildAncestorsSignal } from './build_signal'; +import { + buildSignal, + buildAncestor, + buildAncestorsSignal, + removeInternalTagsFromRule, +} from './build_signal'; import { Signal, Ancestor } from './types'; +import { INTERNAL_RULE_ID_KEY, INTERNAL_IMMUTABLE_KEY } from '../../../../common/constants'; describe('buildSignal', () => { beforeEach(() => { @@ -132,6 +138,77 @@ describe('buildSignal', () => { expect(signal).toEqual(expected); }); + test('it builds a signal as expected with original_event if is present and without internal tags in them', () => { + const doc = sampleDocNoSortId('d5e8eb51-a6a0-456d-8a15-4b79bfec3d71'); + doc._source.event = { + action: 'socket_opened', + dataset: 'socket', + kind: 'event', + module: 'system', + }; + const rule = sampleRule(); + rule.tags = [ + 'some fake tag 1', + 'some fake tag 2', + `${INTERNAL_RULE_ID_KEY}:rule-1`, + `${INTERNAL_IMMUTABLE_KEY}:true`, + ]; + const signal = buildSignal(doc, rule); + const expected: Signal = { + parent: { + rule: '04128c15-0d1b-4716-a4c5-46997ac7f3bd', + id: 'd5e8eb51-a6a0-456d-8a15-4b79bfec3d71', + type: 'event', + index: 'myFakeSignalIndex', + depth: 1, + }, + ancestors: [ + { + rule: '04128c15-0d1b-4716-a4c5-46997ac7f3bd', + id: 'd5e8eb51-a6a0-456d-8a15-4b79bfec3d71', + type: 'event', + index: 'myFakeSignalIndex', + depth: 1, + }, + ], + original_time: 'someTimeStamp', + original_event: { + action: 'socket_opened', + dataset: 'socket', + kind: 'event', + module: 'system', + }, + status: 'open', + rule: { + created_by: 'elastic', + description: 'Detecting root and admin users', + enabled: true, + false_positives: [], + from: 'now-6m', + id: '04128c15-0d1b-4716-a4c5-46997ac7f3bd', + immutable: false, + index: ['auditbeat-*', 'filebeat-*', 'packetbeat-*', 'winlogbeat-*'], + interval: '5m', + risk_score: 50, + rule_id: 'rule-1', + language: 'kuery', + max_signals: 100, + name: 'Detect Root/Admin Users', + output_index: '.siem-signals', + query: 'user.name: root or user.name: admin', + references: ['http://www.example.com', 'https://ww.example.com'], + severity: 'high', + updated_by: 'elastic', + tags: ['some fake tag 1', 'some fake tag 2'], + to: 'now', + type: 'query', + updated_at: signal.rule.updated_at, + created_at: signal.rule.created_at, + }, + }; + expect(signal).toEqual(expected); + }); + test('it builds a ancestor correctly if the parent does not exist', () => { const doc = sampleDocNoSortId('d5e8eb51-a6a0-456d-8a15-4b79bfec3d71'); doc._source.event = { @@ -190,6 +267,51 @@ describe('buildSignal', () => { expect(signal).toEqual(expected); }); + test('it builds a ancestor correctly if the parent does exist without internal tags in them', () => { + const doc = sampleDocNoSortId('d5e8eb51-a6a0-456d-8a15-4b79bfec3d71'); + doc._source.event = { + action: 'socket_opened', + dataset: 'socket', + kind: 'event', + module: 'system', + }; + doc._source.signal = { + parent: { + rule: '98c0bf9e-4d38-46f4-9a6a-8a820426256b', + id: '730ddf9e-5a00-4f85-9ddf-5878ca511a87', + type: 'event', + index: 'myFakeSignalIndex', + depth: 1, + }, + ancestors: [ + { + rule: '98c0bf9e-4d38-46f4-9a6a-8a820426256b', + id: '730ddf9e-5a00-4f85-9ddf-5878ca511a87', + type: 'event', + index: 'myFakeSignalIndex', + depth: 1, + }, + ], + }; + const rule = sampleRule(); + rule.tags = [ + 'some fake tag 1', + 'some fake tag 2', + `${INTERNAL_RULE_ID_KEY}:rule-1`, + `${INTERNAL_IMMUTABLE_KEY}:true`, + ]; + + const signal = buildAncestor(doc, rule); + const expected: Ancestor = { + rule: '04128c15-0d1b-4716-a4c5-46997ac7f3bd', + id: 'd5e8eb51-a6a0-456d-8a15-4b79bfec3d71', + type: 'signal', + index: 'myFakeSignalIndex', + depth: 2, + }; + expect(signal).toEqual(expected); + }); + test('it builds a signal ancestor correctly if the parent does not exist', () => { const doc = sampleDocNoSortId('d5e8eb51-a6a0-456d-8a15-4b79bfec3d71'); doc._source.event = { @@ -258,4 +380,40 @@ describe('buildSignal', () => { ]; expect(signal).toEqual(expected); }); + + test('it removes internal tags from a typical rule', () => { + const rule = sampleRule(); + rule.tags = [ + 'some fake tag 1', + 'some fake tag 2', + `${INTERNAL_RULE_ID_KEY}:rule-1`, + `${INTERNAL_IMMUTABLE_KEY}:true`, + ]; + const noInternals = removeInternalTagsFromRule(rule); + expect(noInternals).toEqual(sampleRule()); + }); + + test('it works with an empty array', () => { + const rule = sampleRule(); + rule.tags = []; + const noInternals = removeInternalTagsFromRule(rule); + const expected = sampleRule(); + expected.tags = []; + expect(noInternals).toEqual(expected); + }); + + test('it works if tags does not exist', () => { + const rule = sampleRule(); + delete rule.tags; + const noInternals = removeInternalTagsFromRule(rule); + const expected = sampleRule(); + delete expected.tags; + expect(noInternals).toEqual(expected); + }); + + test('it works if tags contains normal values and no internal values', () => { + const rule = sampleRule(); + const noInternals = removeInternalTagsFromRule(rule); + expect(noInternals).toEqual(rule); + }); }); diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/build_signal.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/build_signal.ts index 7a63d6831ea97..684cad99f09f0 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/build_signal.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/build_signal.ts @@ -4,6 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ +import { INTERNAL_IDENTIFIER } from '../../../../common/constants'; import { SignalSourceHit, Signal, Ancestor } from './types'; import { OutputRuleAlertRest } from '../types'; @@ -45,6 +46,7 @@ export const buildAncestorsSignal = ( }; export const buildSignal = (doc: SignalSourceHit, rule: Partial): Signal => { + const ruleWithoutInternalTags = removeInternalTagsFromRule(rule); const parent = buildAncestor(doc, rule); const ancestors = buildAncestorsSignal(doc, rule); const signal: Signal = { @@ -52,10 +54,24 @@ export const buildSignal = (doc: SignalSourceHit, rule: Partial +): Partial => { + if (rule.tags == null) { + return rule; + } else { + const ruleWithoutInternalTags: Partial = { + ...rule, + tags: rule.tags.filter(tag => !tag.startsWith(INTERNAL_IDENTIFIER)), + }; + return ruleWithoutInternalTags; + } +}; diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/get_filter.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/get_filter.test.ts index 534215f5a1228..b49f43ce9e7ac 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/get_filter.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/get_filter.test.ts @@ -6,8 +6,8 @@ import { getQueryFilter, getFilter } from './get_filter'; import { savedObjectsClientMock } from 'src/core/server/mocks'; -import { AlertServices } from '../../../../../alerting/server/types'; import { PartialFilter } from '../types'; +import { AlertServices } from '../../../../../../../plugins/alerting/server'; describe('get_filter', () => { let savedObjectsClient = savedObjectsClientMock.create(); diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/get_filter.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/get_filter.ts index cb6b8fc75f610..bcf091544e52a 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/get_filter.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/get_filter.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { AlertServices } from '../../../../../alerting/server/types'; +import { AlertServices } from '../../../../../../../plugins/alerting/server'; import { assertUnreachable } from '../../../utils/build_query'; import { Filter, diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/get_input_output_index.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/get_input_output_index.test.ts index bd7ba915af9b0..18286dc7754e0 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/get_input_output_index.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/get_input_output_index.test.ts @@ -6,9 +6,9 @@ import { savedObjectsClientMock } from 'src/core/server/mocks'; import { DEFAULT_INDEX_KEY } from '../../../../common/constants'; -import { AlertServices } from '../../../../../alerting/server/types'; import { getInputIndex } from './get_input_output_index'; import { defaultIndexPattern } from '../../../../default_index_pattern'; +import { AlertServices } from '../../../../../../../plugins/alerting/server'; describe('get_input_output_index', () => { let savedObjectsClient = savedObjectsClientMock.create(); diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/get_input_output_index.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/get_input_output_index.ts index 624e012717820..29d4d2182bd53 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/get_input_output_index.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/get_input_output_index.ts @@ -4,8 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ +import { AlertServices } from '../../../../../../../plugins/alerting/server'; import { defaultIndexPattern } from '../../../../default_index_pattern'; -import { AlertServices } from '../../../../../alerting/server/types'; import { DEFAULT_INDEX_KEY } from '../../../../common/constants'; export const getInputIndex = async ( diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/search_after_bulk_create.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/search_after_bulk_create.ts index 8c8cef5dd3669..1cfd2f812a195 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/search_after_bulk_create.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/search_after_bulk_create.ts @@ -4,8 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ +import { AlertServices } from '../../../../../../../plugins/alerting/server'; import { RuleTypeParams } from '../types'; -import { AlertServices } from '../../../../../alerting/server/types'; import { Logger } from '../../../../../../../../src/core/server'; import { singleSearchAfter } from './single_search_after'; import { singleBulkCreate } from './single_bulk_create'; diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/single_bulk_create.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/single_bulk_create.ts index adc7919a09758..7d6d6d99fa422 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/single_bulk_create.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/single_bulk_create.ts @@ -6,7 +6,7 @@ import { countBy, isEmpty } from 'lodash'; import { performance } from 'perf_hooks'; -import { AlertServices } from '../../../../../alerting/server/types'; +import { AlertServices } from '../../../../../../../plugins/alerting/server'; import { SignalSearchResponse, BulkResponse } from './types'; import { RuleTypeParams } from '../types'; import { generateId } from './utils'; diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/single_search_after.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/single_search_after.ts index 3a99500cb3433..a0e7047ad1cd6 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/single_search_after.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/single_search_after.ts @@ -4,8 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ +import { AlertServices } from '../../../../../../../plugins/alerting/server'; import { RuleTypeParams } from '../types'; -import { AlertServices } from '../../../../../alerting/server/types'; import { Logger } from '../../../../../../../../src/core/server'; import { SignalSearchResponse } from './types'; import { buildEventsSearchQuery } from './build_events_query'; diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/types.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/types.ts index e9159ab87a0c0..d8b7dd72b6a86 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/types.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/types.ts @@ -7,7 +7,11 @@ import { RuleAlertParams, OutputRuleAlertRest } from '../types'; import { SearchResponse } from '../../types'; import { LegacyRequest } from '../../../types'; -import { AlertType, State, AlertExecutorOptions } from '../../../../../alerting/server/types'; +import { + AlertType, + State, + AlertExecutorOptions, +} from '../../../../../../../plugins/alerting/server'; export interface SignalsParams { signalIds: string[] | undefined | null; diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/utils.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/utils.ts index 940ea8be2ac36..016aed9fabcd6 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/utils.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/utils.ts @@ -6,8 +6,7 @@ import { createHash } from 'crypto'; import moment from 'moment'; import dateMath from '@elastic/datemath'; - -import { parseDuration } from '../../../../../alerting/server/lib'; +import { parseDuration } from '../../../../../../../plugins/alerting/server'; export const generateId = ( docIndex: string, diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/tags/read_tags.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/tags/read_tags.test.ts index 940011924de79..80c107c991bb7 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/tags/read_tags.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/tags/read_tags.test.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { alertsClientMock } from '../../../../../alerting/server/alerts_client.mock'; +import { alertsClientMock } from '../../../../../../../plugins/alerting/server/mocks'; import { getResult, getFindResultWithMultiHits } from '../routes/__mocks__/request_responses'; import { INTERNAL_RULE_ID_KEY, INTERNAL_IDENTIFIER } from '../../../../common/constants'; import { readRawTags, readTags, convertTagsToSet, convertToTags, isTags } from './read_tags'; diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/tags/read_tags.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/tags/read_tags.ts index 02456732df3b4..d343bca8c97bb 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/tags/read_tags.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/tags/read_tags.ts @@ -6,7 +6,7 @@ import { has } from 'lodash/fp'; import { INTERNAL_IDENTIFIER } from '../../../../common/constants'; -import { AlertsClient } from '../../../../../alerting'; +import { AlertsClient } from '../../../../../../../plugins/alerting/server'; import { findRules } from '../rules/find_rules'; export interface TagType { diff --git a/x-pack/legacy/plugins/siem/server/plugin.ts b/x-pack/legacy/plugins/siem/server/plugin.ts index e15248e5200ee..6f28fd7d67bd0 100644 --- a/x-pack/legacy/plugins/siem/server/plugin.ts +++ b/x-pack/legacy/plugins/siem/server/plugin.ts @@ -6,6 +6,10 @@ import { i18n } from '@kbn/i18n'; +import { + PluginStartContract as AlertingStart, + PluginSetupContract as AlertingSetup, +} from '../../../../plugins/alerting/server'; import { CoreSetup, CoreStart, @@ -38,10 +42,12 @@ export interface SetupPlugins { features: FeaturesSetup; security: SecuritySetup; spaces?: SpacesSetup; + alerting: AlertingSetup; } export interface StartPlugins { actions: ActionsStart; + alerting: AlertingStart; } export class Plugin { @@ -130,13 +136,13 @@ export class Plugin { }, }); - if (__legacy.alerting != null) { + if (plugins.alerting != null) { const type = signalRulesAlertType({ logger: this.logger, version: this.context.env.packageInfo.version, }); if (isAlertExecutor(type)) { - __legacy.alerting.setup.registerType(type); + plugins.alerting.registerType(type); } } @@ -145,7 +151,7 @@ export class Plugin { } public start(core: CoreStart, plugins: StartPlugins) { - this.clients.start(core.savedObjects, plugins.actions); + this.clients.start(core.savedObjects, plugins.actions, plugins.alerting); this.legacyInitRoutes!(this.clients.createGetScoped()); } diff --git a/x-pack/legacy/plugins/siem/server/services/clients.test.ts b/x-pack/legacy/plugins/siem/server/services/clients.test.ts index 7f63a8f5e949c..f76494d075f08 100644 --- a/x-pack/legacy/plugins/siem/server/services/clients.test.ts +++ b/x-pack/legacy/plugins/siem/server/services/clients.test.ts @@ -6,6 +6,7 @@ import { coreMock, httpServerMock } from '../../../../../../src/core/server/mocks'; import { actionsMock } from '../../../../../plugins/actions/server/mocks'; +import { alertsMock } from '../../../../../plugins/alerting/server/mocks'; import { ClientsService } from './clients'; @@ -16,13 +17,14 @@ describe('ClientsService', () => { const clients = new ClientsService(); const actions = actionsMock.createStart(); + const alerting = alertsMock.createStart(); const { elasticsearch } = coreMock.createSetup(); const { savedObjects } = coreMock.createStart(); const request = httpServerMock.createRawRequest(); const spacesService = undefined; clients.setup(elasticsearch.dataClient, spacesService); - clients.start(savedObjects, actions); + clients.start(savedObjects, actions, alerting); const { spacesClient } = await clients.createGetScoped()(request); expect(spacesClient.getSpaceId()).toEqual('default'); diff --git a/x-pack/legacy/plugins/siem/server/services/clients.ts b/x-pack/legacy/plugins/siem/server/services/clients.ts index ca50eda4e7a6c..7ba7230f88493 100644 --- a/x-pack/legacy/plugins/siem/server/services/clients.ts +++ b/x-pack/legacy/plugins/siem/server/services/clients.ts @@ -12,23 +12,23 @@ import { SavedObjectsClientContract, } from '../../../../../../src/core/server'; import { ActionsClient } from '../../../../../plugins/actions/server'; -import { AlertsClient } from '../../../../../legacy/plugins/alerting/server'; +import { AlertsClient } from '../../../../../plugins/alerting/server'; import { SpacesServiceSetup } from '../../../../../plugins/spaces/server'; import { CoreStart, StartPlugins } from '../plugin'; export interface Clients { actionsClient?: ActionsClient; + alertsClient?: AlertsClient; clusterClient: IScopedClusterClient; spacesClient: { getSpaceId: () => string }; savedObjectsClient: SavedObjectsClientContract; } -interface LegacyClients { - alertsClient?: AlertsClient; -} -export type GetScopedClients = (request: LegacyRequest) => Promise; + +export type GetScopedClients = (request: LegacyRequest) => Promise; export class ClientsService { private actions?: StartPlugins['actions']; + private alerting?: StartPlugins['alerting']; private clusterClient?: IClusterClient; private savedObjects?: CoreStart['savedObjects']; private spacesService?: SpacesServiceSetup; @@ -38,9 +38,14 @@ export class ClientsService { this.spacesService = spacesService; } - public start(savedObjects: CoreStart['savedObjects'], actions: StartPlugins['actions']) { + public start( + savedObjects: CoreStart['savedObjects'], + actions: StartPlugins['actions'], + alerting: StartPlugins['alerting'] + ) { this.savedObjects = savedObjects; this.actions = actions; + this.alerting = alerting; } public createGetScoped(): GetScopedClients { @@ -52,7 +57,7 @@ export class ClientsService { const kibanaRequest = KibanaRequest.from(request); return { - alertsClient: request.getAlertsClient?.(), + alertsClient: await this.alerting?.getAlertsClientWithRequest?.(kibanaRequest), actionsClient: await this.actions?.getActionsClientWithRequest?.(kibanaRequest), clusterClient: this.clusterClient!.asScoped(kibanaRequest), savedObjectsClient: this.savedObjects!.getScopedClient(kibanaRequest), diff --git a/x-pack/plugins/actions/server/routes/find.ts b/x-pack/plugins/actions/server/routes/find.ts index 04656d19bfeb4..e791aff4fb598 100644 --- a/x-pack/plugins/actions/server/routes/find.ts +++ b/x-pack/plugins/actions/server/routes/find.ts @@ -12,7 +12,7 @@ import { IKibanaResponse, KibanaResponseFactory, } from 'kibana/server'; -import { FindOptions } from '../../../../legacy/plugins/alerting/server/alerts_client'; +import { FindOptions } from '../../../alerting/server'; import { LicenseState } from '../lib/license_state'; import { verifyApiAccess } from '../lib/license_api_access'; diff --git a/x-pack/legacy/plugins/alerting/README.md b/x-pack/plugins/alerting/README.md similarity index 99% rename from x-pack/legacy/plugins/alerting/README.md rename to x-pack/plugins/alerting/README.md index 2a10c41f12b85..32ca804198ebd 100644 --- a/x-pack/legacy/plugins/alerting/README.md +++ b/x-pack/plugins/alerting/README.md @@ -77,7 +77,7 @@ Note that the `manage_own_api_key` cluster privilege is not enough - it can be u ### Methods -**server.plugins.alerting.setup.registerType(options)** +**server.newPlatform.setup.plugins.alerting.registerType(options)** The following table describes the properties of the `options` object. @@ -119,7 +119,7 @@ This example receives server and threshold as parameters. It will read the CPU u ``` import { schema } from '@kbn/config-schema'; ... -server.plugins.alerting.setup.registerType({ +server.newPlatform.setup.plugins.alerting.registerType({ id: 'my-alert-type', name: 'My alert type', validate: { @@ -178,7 +178,7 @@ server.plugins.alerting.setup.registerType({ This example only receives threshold as a parameter. It will read the CPU usage of all the servers and schedule individual actions if the reading for a server is greater than the threshold. This is a better implementation than above as only one query is performed for all the servers instead of one query per server. ``` -server.plugins.alerting.setup.registerType({ +server.newPlatform.setup.plugins.alerting.registerType({ id: 'my-alert-type', name: 'My alert type', validate: { diff --git a/x-pack/legacy/plugins/alerting/common/alert.ts b/x-pack/plugins/alerting/common/alert.ts similarity index 100% rename from x-pack/legacy/plugins/alerting/common/alert.ts rename to x-pack/plugins/alerting/common/alert.ts diff --git a/x-pack/legacy/plugins/alerting/common/alert_instance.ts b/x-pack/plugins/alerting/common/alert_instance.ts similarity index 100% rename from x-pack/legacy/plugins/alerting/common/alert_instance.ts rename to x-pack/plugins/alerting/common/alert_instance.ts diff --git a/x-pack/legacy/plugins/alerting/common/alert_task_instance.ts b/x-pack/plugins/alerting/common/alert_task_instance.ts similarity index 100% rename from x-pack/legacy/plugins/alerting/common/alert_task_instance.ts rename to x-pack/plugins/alerting/common/alert_task_instance.ts diff --git a/x-pack/legacy/plugins/alerting/common/date_from_string.test.ts b/x-pack/plugins/alerting/common/date_from_string.test.ts similarity index 100% rename from x-pack/legacy/plugins/alerting/common/date_from_string.test.ts rename to x-pack/plugins/alerting/common/date_from_string.test.ts diff --git a/x-pack/legacy/plugins/alerting/common/date_from_string.ts b/x-pack/plugins/alerting/common/date_from_string.ts similarity index 100% rename from x-pack/legacy/plugins/alerting/common/date_from_string.ts rename to x-pack/plugins/alerting/common/date_from_string.ts diff --git a/x-pack/legacy/plugins/alerting/common/index.ts b/x-pack/plugins/alerting/common/index.ts similarity index 100% rename from x-pack/legacy/plugins/alerting/common/index.ts rename to x-pack/plugins/alerting/common/index.ts diff --git a/x-pack/plugins/alerting/kibana.json b/x-pack/plugins/alerting/kibana.json new file mode 100644 index 0000000000000..fb5003ede48ce --- /dev/null +++ b/x-pack/plugins/alerting/kibana.json @@ -0,0 +1,10 @@ +{ + "id": "alerting", + "server": true, + "version": "8.0.0", + "kibanaVersion": "kibana", + "configPath": ["xpack", "alerting"], + "requiredPlugins": ["licensing", "taskManager", "encryptedSavedObjects", "actions"], + "optionalPlugins": ["spaces", "security"], + "ui": false +} \ No newline at end of file diff --git a/x-pack/legacy/plugins/alerting/server/alert_instance/alert_instance.test.ts b/x-pack/plugins/alerting/server/alert_instance/alert_instance.test.ts similarity index 100% rename from x-pack/legacy/plugins/alerting/server/alert_instance/alert_instance.test.ts rename to x-pack/plugins/alerting/server/alert_instance/alert_instance.test.ts diff --git a/x-pack/legacy/plugins/alerting/server/alert_instance/alert_instance.ts b/x-pack/plugins/alerting/server/alert_instance/alert_instance.ts similarity index 100% rename from x-pack/legacy/plugins/alerting/server/alert_instance/alert_instance.ts rename to x-pack/plugins/alerting/server/alert_instance/alert_instance.ts diff --git a/x-pack/legacy/plugins/alerting/server/alert_instance/create_alert_instance_factory.test.ts b/x-pack/plugins/alerting/server/alert_instance/create_alert_instance_factory.test.ts similarity index 100% rename from x-pack/legacy/plugins/alerting/server/alert_instance/create_alert_instance_factory.test.ts rename to x-pack/plugins/alerting/server/alert_instance/create_alert_instance_factory.test.ts diff --git a/x-pack/legacy/plugins/alerting/server/alert_instance/create_alert_instance_factory.ts b/x-pack/plugins/alerting/server/alert_instance/create_alert_instance_factory.ts similarity index 100% rename from x-pack/legacy/plugins/alerting/server/alert_instance/create_alert_instance_factory.ts rename to x-pack/plugins/alerting/server/alert_instance/create_alert_instance_factory.ts diff --git a/x-pack/legacy/plugins/alerting/server/alert_instance/index.ts b/x-pack/plugins/alerting/server/alert_instance/index.ts similarity index 100% rename from x-pack/legacy/plugins/alerting/server/alert_instance/index.ts rename to x-pack/plugins/alerting/server/alert_instance/index.ts diff --git a/x-pack/legacy/plugins/alerting/server/alert_type_registry.mock.ts b/x-pack/plugins/alerting/server/alert_type_registry.mock.ts similarity index 100% rename from x-pack/legacy/plugins/alerting/server/alert_type_registry.mock.ts rename to x-pack/plugins/alerting/server/alert_type_registry.mock.ts diff --git a/x-pack/legacy/plugins/alerting/server/alert_type_registry.test.ts b/x-pack/plugins/alerting/server/alert_type_registry.test.ts similarity index 97% rename from x-pack/legacy/plugins/alerting/server/alert_type_registry.test.ts rename to x-pack/plugins/alerting/server/alert_type_registry.test.ts index 976bed884cd43..1a820d55af8a4 100644 --- a/x-pack/legacy/plugins/alerting/server/alert_type_registry.test.ts +++ b/x-pack/plugins/alerting/server/alert_type_registry.test.ts @@ -6,7 +6,7 @@ import { TaskRunnerFactory } from './task_runner'; import { AlertTypeRegistry } from './alert_type_registry'; -import { taskManagerMock } from '../../../../plugins/task_manager/server/task_manager.mock'; +import { taskManagerMock } from '../../../plugins/task_manager/server/task_manager.mock'; const taskManager = taskManagerMock.setup(); const alertTypeRegistryParams = { diff --git a/x-pack/legacy/plugins/alerting/server/alert_type_registry.ts b/x-pack/plugins/alerting/server/alert_type_registry.ts similarity index 95% rename from x-pack/legacy/plugins/alerting/server/alert_type_registry.ts rename to x-pack/plugins/alerting/server/alert_type_registry.ts index 8c9844d935163..b0976d67c70a0 100644 --- a/x-pack/legacy/plugins/alerting/server/alert_type_registry.ts +++ b/x-pack/plugins/alerting/server/alert_type_registry.ts @@ -6,7 +6,7 @@ import Boom from 'boom'; import { i18n } from '@kbn/i18n'; -import { RunContext, TaskManagerSetupContract } from '../../../../plugins/task_manager/server'; +import { RunContext, TaskManagerSetupContract } from '../../../plugins/task_manager/server'; import { TaskRunnerFactory } from './task_runner'; import { AlertType } from './types'; diff --git a/x-pack/legacy/plugins/alerting/server/alerts_client.mock.ts b/x-pack/plugins/alerting/server/alerts_client.mock.ts similarity index 100% rename from x-pack/legacy/plugins/alerting/server/alerts_client.mock.ts rename to x-pack/plugins/alerting/server/alerts_client.mock.ts diff --git a/x-pack/legacy/plugins/alerting/server/alerts_client.test.ts b/x-pack/plugins/alerting/server/alerts_client.test.ts similarity index 99% rename from x-pack/legacy/plugins/alerting/server/alerts_client.test.ts rename to x-pack/plugins/alerting/server/alerts_client.test.ts index 1555a0537158a..629bd05a8c76a 100644 --- a/x-pack/legacy/plugins/alerting/server/alerts_client.test.ts +++ b/x-pack/plugins/alerting/server/alerts_client.test.ts @@ -6,13 +6,13 @@ import uuid from 'uuid'; import { schema } from '@kbn/config-schema'; import { AlertsClient } from './alerts_client'; -import { savedObjectsClientMock, loggingServiceMock } from '../../../../../src/core/server/mocks'; -import { taskManagerMock } from '../../../../plugins/task_manager/server/task_manager.mock'; +import { savedObjectsClientMock, loggingServiceMock } from '../../../../src/core/server/mocks'; +import { taskManagerMock } from '../../../plugins/task_manager/server/task_manager.mock'; import { alertTypeRegistryMock } from './alert_type_registry.mock'; -import { TaskStatus } from '../../../../plugins/task_manager/server'; +import { TaskStatus } from '../../../plugins/task_manager/server'; import { IntervalSchedule } from './types'; import { resolvable } from './test_utils'; -import { encryptedSavedObjectsMock } from '../../../../plugins/encrypted_saved_objects/server/mocks'; +import { encryptedSavedObjectsMock } from '../../../plugins/encrypted_saved_objects/server/mocks'; const taskManager = taskManagerMock.start(); const alertTypeRegistry = alertTypeRegistryMock.create(); diff --git a/x-pack/legacy/plugins/alerting/server/alerts_client.ts b/x-pack/plugins/alerting/server/alerts_client.ts similarity index 98% rename from x-pack/legacy/plugins/alerting/server/alerts_client.ts rename to x-pack/plugins/alerting/server/alerts_client.ts index eef6f662a20a2..ad308f819da34 100644 --- a/x-pack/legacy/plugins/alerting/server/alerts_client.ts +++ b/x-pack/plugins/alerting/server/alerts_client.ts @@ -29,9 +29,9 @@ import { InvalidateAPIKeyParams, CreateAPIKeyResult as SecurityPluginCreateAPIKeyResult, InvalidateAPIKeyResult as SecurityPluginInvalidateAPIKeyResult, -} from '../../../../plugins/security/server'; -import { EncryptedSavedObjectsPluginStart } from '../../../../plugins/encrypted_saved_objects/server'; -import { TaskManagerStartContract } from '../../../../plugins/task_manager/server'; +} from '../../../plugins/security/server'; +import { EncryptedSavedObjectsPluginStart } from '../../../plugins/encrypted_saved_objects/server'; +import { TaskManagerStartContract } from '../../../plugins/task_manager/server'; import { taskInstanceToAlertTaskInstance } from './task_runner/alert_task_instance'; type NormalizedAlertAction = Omit; diff --git a/x-pack/legacy/plugins/alerting/server/alerts_client_factory.test.ts b/x-pack/plugins/alerting/server/alerts_client_factory.test.ts similarity index 72% rename from x-pack/legacy/plugins/alerting/server/alerts_client_factory.test.ts rename to x-pack/plugins/alerting/server/alerts_client_factory.test.ts index 14c685237bf92..b0558ef1ea98c 100644 --- a/x-pack/legacy/plugins/alerting/server/alerts_client_factory.test.ts +++ b/x-pack/plugins/alerting/server/alerts_client_factory.test.ts @@ -5,23 +5,23 @@ */ import { Request } from 'hapi'; -import { AlertsClientFactory, ConstructorOpts } from './alerts_client_factory'; +import { AlertsClientFactory, AlertsClientFactoryOpts } from './alerts_client_factory'; import { alertTypeRegistryMock } from './alert_type_registry.mock'; -import { taskManagerMock } from '../../../../plugins/task_manager/server/task_manager.mock'; -import { KibanaRequest } from '../../../../../src/core/server'; -import { loggingServiceMock } from '../../../../../src/core/server/mocks'; -import { encryptedSavedObjectsMock } from '../../../../plugins/encrypted_saved_objects/server/mocks'; +import { taskManagerMock } from '../../../plugins/task_manager/server/task_manager.mock'; +import { KibanaRequest } from '../../../../src/core/server'; +import { loggingServiceMock, savedObjectsClientMock } from '../../../../src/core/server/mocks'; +import { encryptedSavedObjectsMock } from '../../../plugins/encrypted_saved_objects/server/mocks'; jest.mock('./alerts_client'); -const savedObjectsClient = jest.fn(); +const savedObjectsClient = savedObjectsClientMock.create(); const securityPluginSetup = { authc: { createAPIKey: jest.fn(), getCurrentUser: jest.fn(), }, }; -const alertsClientFactoryParams: jest.Mocked = { +const alertsClientFactoryParams: jest.Mocked = { logger: loggingServiceMock.create().get(), taskManager: taskManagerMock.start(), alertTypeRegistry: alertTypeRegistryMock.create(), @@ -52,8 +52,9 @@ beforeEach(() => { }); test('creates an alerts client with proper constructor arguments', async () => { - const factory = new AlertsClientFactory(alertsClientFactoryParams); - factory.create(KibanaRequest.from(fakeRequest), fakeRequest); + const factory = new AlertsClientFactory(); + factory.initialize(alertsClientFactoryParams); + factory.create(KibanaRequest.from(fakeRequest), savedObjectsClient); expect(jest.requireMock('./alerts_client').AlertsClient).toHaveBeenCalledWith({ savedObjectsClient, @@ -70,8 +71,9 @@ test('creates an alerts client with proper constructor arguments', async () => { }); test('getUserName() returns null when security is disabled', async () => { - const factory = new AlertsClientFactory(alertsClientFactoryParams); - factory.create(KibanaRequest.from(fakeRequest), fakeRequest); + const factory = new AlertsClientFactory(); + factory.initialize(alertsClientFactoryParams); + factory.create(KibanaRequest.from(fakeRequest), savedObjectsClient); const constructorCall = jest.requireMock('./alerts_client').AlertsClient.mock.calls[0][0]; const userNameResult = await constructorCall.getUserName(); @@ -79,11 +81,12 @@ test('getUserName() returns null when security is disabled', async () => { }); test('getUserName() returns a name when security is enabled', async () => { - const factory = new AlertsClientFactory({ + const factory = new AlertsClientFactory(); + factory.initialize({ ...alertsClientFactoryParams, securityPluginSetup: securityPluginSetup as any, }); - factory.create(KibanaRequest.from(fakeRequest), fakeRequest); + factory.create(KibanaRequest.from(fakeRequest), savedObjectsClient); const constructorCall = jest.requireMock('./alerts_client').AlertsClient.mock.calls[0][0]; securityPluginSetup.authc.getCurrentUser.mockReturnValueOnce({ username: 'bob' }); @@ -92,8 +95,9 @@ test('getUserName() returns a name when security is enabled', async () => { }); test('createAPIKey() returns { apiKeysEnabled: false } when security is disabled', async () => { - const factory = new AlertsClientFactory(alertsClientFactoryParams); - factory.create(KibanaRequest.from(fakeRequest), fakeRequest); + const factory = new AlertsClientFactory(); + factory.initialize(alertsClientFactoryParams); + factory.create(KibanaRequest.from(fakeRequest), savedObjectsClient); const constructorCall = jest.requireMock('./alerts_client').AlertsClient.mock.calls[0][0]; const createAPIKeyResult = await constructorCall.createAPIKey(); @@ -101,8 +105,9 @@ test('createAPIKey() returns { apiKeysEnabled: false } when security is disabled }); test('createAPIKey() returns { apiKeysEnabled: false } when security is enabled but ES security is disabled', async () => { - const factory = new AlertsClientFactory(alertsClientFactoryParams); - factory.create(KibanaRequest.from(fakeRequest), fakeRequest); + const factory = new AlertsClientFactory(); + factory.initialize(alertsClientFactoryParams); + factory.create(KibanaRequest.from(fakeRequest), savedObjectsClient); const constructorCall = jest.requireMock('./alerts_client').AlertsClient.mock.calls[0][0]; securityPluginSetup.authc.createAPIKey.mockResolvedValueOnce(null); @@ -111,11 +116,12 @@ test('createAPIKey() returns { apiKeysEnabled: false } when security is enabled }); test('createAPIKey() returns an API key when security is enabled', async () => { - const factory = new AlertsClientFactory({ + const factory = new AlertsClientFactory(); + factory.initialize({ ...alertsClientFactoryParams, securityPluginSetup: securityPluginSetup as any, }); - factory.create(KibanaRequest.from(fakeRequest), fakeRequest); + factory.create(KibanaRequest.from(fakeRequest), savedObjectsClient); const constructorCall = jest.requireMock('./alerts_client').AlertsClient.mock.calls[0][0]; securityPluginSetup.authc.createAPIKey.mockResolvedValueOnce({ api_key: '123', id: 'abc' }); @@ -127,11 +133,12 @@ test('createAPIKey() returns an API key when security is enabled', async () => { }); test('createAPIKey() throws when security plugin createAPIKey throws an error', async () => { - const factory = new AlertsClientFactory({ + const factory = new AlertsClientFactory(); + factory.initialize({ ...alertsClientFactoryParams, securityPluginSetup: securityPluginSetup as any, }); - factory.create(KibanaRequest.from(fakeRequest), fakeRequest); + factory.create(KibanaRequest.from(fakeRequest), savedObjectsClient); const constructorCall = jest.requireMock('./alerts_client').AlertsClient.mock.calls[0][0]; securityPluginSetup.authc.createAPIKey.mockRejectedValueOnce(new Error('TLS disabled')); diff --git a/x-pack/legacy/plugins/alerting/server/alerts_client_factory.ts b/x-pack/plugins/alerting/server/alerts_client_factory.ts similarity index 66% rename from x-pack/legacy/plugins/alerting/server/alerts_client_factory.ts rename to x-pack/plugins/alerting/server/alerts_client_factory.ts index de789fba0ac38..c502c0e5bf1cf 100644 --- a/x-pack/legacy/plugins/alerting/server/alerts_client_factory.ts +++ b/x-pack/plugins/alerting/server/alerts_client_factory.ts @@ -4,36 +4,39 @@ * you may not use this file except in compliance with the Elastic License. */ -import Hapi from 'hapi'; import uuid from 'uuid'; import { AlertsClient } from './alerts_client'; import { AlertTypeRegistry, SpaceIdToNamespaceFunction } from './types'; -import { SecurityPluginStartContract } from './shim'; -import { KibanaRequest, Logger } from '../../../../../src/core/server'; -import { InvalidateAPIKeyParams } from '../../../../plugins/security/server'; -import { EncryptedSavedObjectsPluginStart } from '../../../../plugins/encrypted_saved_objects/server'; -import { TaskManagerStartContract } from '../../../../plugins/task_manager/server'; +import { KibanaRequest, Logger, SavedObjectsClientContract } from '../../../../src/core/server'; +import { InvalidateAPIKeyParams, SecurityPluginSetup } from '../../../plugins/security/server'; +import { EncryptedSavedObjectsPluginStart } from '../../../plugins/encrypted_saved_objects/server'; +import { TaskManagerStartContract } from '../../../plugins/task_manager/server'; -export interface ConstructorOpts { +export interface AlertsClientFactoryOpts { logger: Logger; taskManager: TaskManagerStartContract; alertTypeRegistry: AlertTypeRegistry; - securityPluginSetup?: SecurityPluginStartContract; - getSpaceId: (request: Hapi.Request) => string | undefined; + securityPluginSetup?: SecurityPluginSetup; + getSpaceId: (request: KibanaRequest) => string | undefined; spaceIdToNamespace: SpaceIdToNamespaceFunction; encryptedSavedObjectsPlugin: EncryptedSavedObjectsPluginStart; } export class AlertsClientFactory { - private readonly logger: Logger; - private readonly taskManager: TaskManagerStartContract; - private readonly alertTypeRegistry: AlertTypeRegistry; - private readonly securityPluginSetup?: SecurityPluginStartContract; - private readonly getSpaceId: (request: Hapi.Request) => string | undefined; - private readonly spaceIdToNamespace: SpaceIdToNamespaceFunction; - private readonly encryptedSavedObjectsPlugin: EncryptedSavedObjectsPluginStart; + private isInitialized = false; + private logger!: Logger; + private taskManager!: TaskManagerStartContract; + private alertTypeRegistry!: AlertTypeRegistry; + private securityPluginSetup?: SecurityPluginSetup; + private getSpaceId!: (request: KibanaRequest) => string | undefined; + private spaceIdToNamespace!: SpaceIdToNamespaceFunction; + private encryptedSavedObjectsPlugin!: EncryptedSavedObjectsPluginStart; - constructor(options: ConstructorOpts) { + public initialize(options: AlertsClientFactoryOpts) { + if (this.isInitialized) { + throw new Error('AlertsClientFactory already initialized'); + } + this.isInitialized = true; this.logger = options.logger; this.getSpaceId = options.getSpaceId; this.taskManager = options.taskManager; @@ -43,15 +46,18 @@ export class AlertsClientFactory { this.encryptedSavedObjectsPlugin = options.encryptedSavedObjectsPlugin; } - public create(request: KibanaRequest, legacyRequest: Hapi.Request): AlertsClient { + public create( + request: KibanaRequest, + savedObjectsClient: SavedObjectsClientContract + ): AlertsClient { const { securityPluginSetup } = this; - const spaceId = this.getSpaceId(legacyRequest); + const spaceId = this.getSpaceId(request); return new AlertsClient({ spaceId, logger: this.logger, taskManager: this.taskManager, alertTypeRegistry: this.alertTypeRegistry, - savedObjectsClient: legacyRequest.getSavedObjectsClient(), + savedObjectsClient, namespace: this.spaceIdToNamespace(spaceId), encryptedSavedObjectsPlugin: this.encryptedSavedObjectsPlugin, async getUserName() { diff --git a/x-pack/legacy/plugins/alerting/server/constants/plugin.ts b/x-pack/plugins/alerting/server/constants/plugin.ts similarity index 85% rename from x-pack/legacy/plugins/alerting/server/constants/plugin.ts rename to x-pack/plugins/alerting/server/constants/plugin.ts index e3435b09829c6..173aa50013b40 100644 --- a/x-pack/legacy/plugins/alerting/server/constants/plugin.ts +++ b/x-pack/plugins/alerting/server/constants/plugin.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { LICENSE_TYPE_BASIC, LicenseType } from '../../../../common/constants'; +import { LICENSE_TYPE_BASIC, LicenseType } from '../../../../legacy/common/constants'; export const PLUGIN = { ID: 'alerting', diff --git a/x-pack/plugins/alerting/server/index.ts b/x-pack/plugins/alerting/server/index.ts new file mode 100644 index 0000000000000..58b77f0f510f7 --- /dev/null +++ b/x-pack/plugins/alerting/server/index.ts @@ -0,0 +1,27 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { AlertsClient as AlertsClientClass } from './alerts_client'; +import { PluginInitializerContext } from '../../../../src/core/server'; +import { AlertingPlugin } from './plugin'; + +export type AlertsClient = PublicMethodsOf; + +export { + AlertType, + AlertingPlugin, + AlertExecutorOptions, + AlertActionParams, + AlertServices, + State, + PartialAlert, +} from './types'; +export { PluginSetupContract, PluginStartContract } from './plugin'; +export { FindOptions, FindResult } from './alerts_client'; +export { AlertInstance } from './alert_instance'; +export { parseDuration } from './lib'; + +export const plugin = (initContext: PluginInitializerContext) => new AlertingPlugin(initContext); diff --git a/x-pack/legacy/plugins/alerting/server/lib/index.ts b/x-pack/plugins/alerting/server/lib/index.ts similarity index 83% rename from x-pack/legacy/plugins/alerting/server/lib/index.ts rename to x-pack/plugins/alerting/server/lib/index.ts index c41ea4a5998ff..c84825cadbd16 100644 --- a/x-pack/legacy/plugins/alerting/server/lib/index.ts +++ b/x-pack/plugins/alerting/server/lib/index.ts @@ -4,6 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ -export { parseDuration, getDurationSchema } from './parse_duration'; +export { parseDuration, validateDurationSchema } from './parse_duration'; export { LicenseState } from './license_state'; export { validateAlertTypeParams } from './validate_alert_type_params'; diff --git a/x-pack/plugins/alerting/server/lib/license_api_access.ts b/x-pack/plugins/alerting/server/lib/license_api_access.ts new file mode 100644 index 0000000000000..2e650ebf5eb17 --- /dev/null +++ b/x-pack/plugins/alerting/server/lib/license_api_access.ts @@ -0,0 +1,18 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import Boom from 'boom'; +import { LicenseState } from './license_state'; + +export function verifyApiAccess(licenseState: LicenseState) { + const licenseCheckResults = licenseState.getLicenseInformation(); + + if (licenseCheckResults.showAppLink && licenseCheckResults.enableAppLink) { + return null; + } + + throw Boom.forbidden(licenseCheckResults.message); +} diff --git a/x-pack/plugins/alerting/server/lib/license_state.mock.ts b/x-pack/plugins/alerting/server/lib/license_state.mock.ts new file mode 100644 index 0000000000000..f36f3a9eaeade --- /dev/null +++ b/x-pack/plugins/alerting/server/lib/license_state.mock.ts @@ -0,0 +1,38 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { of } from 'rxjs'; +import { LicenseState } from './license_state'; +import { LICENSE_CHECK_STATE, ILicense } from '../../../licensing/server'; + +export const mockLicenseState = () => { + const license: ILicense = { + uid: '123', + status: 'active', + isActive: true, + signature: 'sig', + isAvailable: true, + toJSON: () => ({ + signature: 'sig', + }), + getUnavailableReason: () => undefined, + hasAtLeast() { + return true; + }, + check() { + return { + state: LICENSE_CHECK_STATE.Valid, + }; + }, + getFeature() { + return { + isAvailable: true, + isEnabled: true, + }; + }, + }; + return new LicenseState(of(license)); +}; diff --git a/x-pack/legacy/plugins/alerting/server/lib/license_state.test.ts b/x-pack/plugins/alerting/server/lib/license_state.test.ts similarity index 91% rename from x-pack/legacy/plugins/alerting/server/lib/license_state.test.ts rename to x-pack/plugins/alerting/server/lib/license_state.test.ts index 484dc49532567..8feb8d74976fd 100644 --- a/x-pack/legacy/plugins/alerting/server/lib/license_state.test.ts +++ b/x-pack/plugins/alerting/server/lib/license_state.test.ts @@ -6,8 +6,8 @@ import expect from '@kbn/expect'; import { LicenseState } from './license_state'; -import { licensingMock } from '../../../../../plugins/licensing/server/mocks'; -import { LICENSE_CHECK_STATE } from '../../../../../plugins/licensing/server'; +import { licensingMock } from '../../../../plugins/licensing/server/mocks'; +import { LICENSE_CHECK_STATE } from '../../../../plugins/licensing/server'; describe('license_state', () => { let getRawLicense: any; diff --git a/x-pack/legacy/plugins/alerting/server/lib/license_state.ts b/x-pack/plugins/alerting/server/lib/license_state.ts similarity index 94% rename from x-pack/legacy/plugins/alerting/server/lib/license_state.ts rename to x-pack/plugins/alerting/server/lib/license_state.ts index 344bc9c409edf..690eaed9e2b89 100644 --- a/x-pack/legacy/plugins/alerting/server/lib/license_state.ts +++ b/x-pack/plugins/alerting/server/lib/license_state.ts @@ -7,8 +7,8 @@ import Boom from 'boom'; import { i18n } from '@kbn/i18n'; import { Observable, Subscription } from 'rxjs'; -import { ILicense, LICENSE_CHECK_STATE } from '../../../../../plugins/licensing/common/types'; -import { assertNever } from '../../../../../../src/core/utils'; +import { ILicense, LICENSE_CHECK_STATE } from '../../../../plugins/licensing/common/types'; +import { assertNever } from '../../../../../src/core/utils'; import { PLUGIN } from '../constants/plugin'; export interface AlertingLicenseInformation { diff --git a/x-pack/legacy/plugins/alerting/server/lib/parse_duration.test.ts b/x-pack/plugins/alerting/server/lib/parse_duration.test.ts similarity index 100% rename from x-pack/legacy/plugins/alerting/server/lib/parse_duration.test.ts rename to x-pack/plugins/alerting/server/lib/parse_duration.test.ts diff --git a/x-pack/legacy/plugins/alerting/server/lib/parse_duration.ts b/x-pack/plugins/alerting/server/lib/parse_duration.ts similarity index 75% rename from x-pack/legacy/plugins/alerting/server/lib/parse_duration.ts rename to x-pack/plugins/alerting/server/lib/parse_duration.ts index e343ef6d21684..51f3d746a6869 100644 --- a/x-pack/legacy/plugins/alerting/server/lib/parse_duration.ts +++ b/x-pack/plugins/alerting/server/lib/parse_duration.ts @@ -3,9 +3,6 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ - -import Joi from 'joi'; - const SECONDS_REGEX = /^[1-9][0-9]*s$/; const MINUTES_REGEX = /^[1-9][0-9]*m$/; const HOURS_REGEX = /^[1-9][0-9]*h$/; @@ -27,21 +24,20 @@ export function parseDuration(duration: string): number { ); } -export function getDurationSchema() { - return Joi.alternatives().try( - Joi.string() - .regex(SECONDS_REGEX, 'seconds') - .required(), - Joi.string() - .regex(MINUTES_REGEX, 'minutes') - .required(), - Joi.string() - .regex(HOURS_REGEX, 'hours') - .required(), - Joi.string() - .regex(DAYS_REGEX, 'days') - .required() - ); +export function validateDurationSchema(duration: string) { + if (duration.match(SECONDS_REGEX)) { + return; + } + if (duration.match(MINUTES_REGEX)) { + return; + } + if (duration.match(HOURS_REGEX)) { + return; + } + if (duration.match(DAYS_REGEX)) { + return; + } + return 'string is not a valid duration: ' + duration; } function isSeconds(duration: string) { diff --git a/x-pack/legacy/plugins/alerting/server/lib/result_type.ts b/x-pack/plugins/alerting/server/lib/result_type.ts similarity index 100% rename from x-pack/legacy/plugins/alerting/server/lib/result_type.ts rename to x-pack/plugins/alerting/server/lib/result_type.ts diff --git a/x-pack/legacy/plugins/alerting/server/lib/types.test.ts b/x-pack/plugins/alerting/server/lib/types.test.ts similarity index 100% rename from x-pack/legacy/plugins/alerting/server/lib/types.test.ts rename to x-pack/plugins/alerting/server/lib/types.test.ts diff --git a/x-pack/legacy/plugins/alerting/server/lib/types.ts b/x-pack/plugins/alerting/server/lib/types.ts similarity index 100% rename from x-pack/legacy/plugins/alerting/server/lib/types.ts rename to x-pack/plugins/alerting/server/lib/types.ts diff --git a/x-pack/legacy/plugins/alerting/server/lib/validate_alert_type_params.test.ts b/x-pack/plugins/alerting/server/lib/validate_alert_type_params.test.ts similarity index 100% rename from x-pack/legacy/plugins/alerting/server/lib/validate_alert_type_params.test.ts rename to x-pack/plugins/alerting/server/lib/validate_alert_type_params.test.ts diff --git a/x-pack/legacy/plugins/alerting/server/lib/validate_alert_type_params.ts b/x-pack/plugins/alerting/server/lib/validate_alert_type_params.ts similarity index 100% rename from x-pack/legacy/plugins/alerting/server/lib/validate_alert_type_params.ts rename to x-pack/plugins/alerting/server/lib/validate_alert_type_params.ts diff --git a/x-pack/plugins/alerting/server/mocks.ts b/x-pack/plugins/alerting/server/mocks.ts new file mode 100644 index 0000000000000..55ad722dcf881 --- /dev/null +++ b/x-pack/plugins/alerting/server/mocks.ts @@ -0,0 +1,30 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { alertsClientMock } from './alerts_client.mock'; +import { PluginSetupContract, PluginStartContract } from './plugin'; + +export { alertsClientMock }; + +const createSetupMock = () => { + const mock: jest.Mocked = { + registerType: jest.fn(), + }; + return mock; +}; + +const createStartMock = () => { + const mock: jest.Mocked = { + listTypes: jest.fn(), + getAlertsClientWithRequest: jest.fn().mockResolvedValue(alertsClientMock.create()), + }; + return mock; +}; + +export const alertsMock = { + createSetup: createSetupMock, + createStart: createStartMock, +}; diff --git a/x-pack/legacy/plugins/alerting/server/plugin.test.ts b/x-pack/plugins/alerting/server/plugin.test.ts similarity index 91% rename from x-pack/legacy/plugins/alerting/server/plugin.test.ts rename to x-pack/plugins/alerting/server/plugin.test.ts index 872de720243b2..40e620dd92af0 100644 --- a/x-pack/legacy/plugins/alerting/server/plugin.test.ts +++ b/x-pack/plugins/alerting/server/plugin.test.ts @@ -4,16 +4,16 @@ * you may not use this file except in compliance with the Elastic License. */ -import { Plugin } from './plugin'; -import { coreMock } from '../../../../../src/core/server/mocks'; -import { licensingMock } from '../../../../plugins/licensing/server/mocks'; -import { encryptedSavedObjectsMock } from '../../../../plugins/encrypted_saved_objects/server/mocks'; +import { AlertingPlugin } from './plugin'; +import { coreMock } from '../../../../src/core/server/mocks'; +import { licensingMock } from '../../../plugins/licensing/server/mocks'; +import { encryptedSavedObjectsMock } from '../../../plugins/encrypted_saved_objects/server/mocks'; describe('Alerting Plugin', () => { describe('setup()', () => { it('should log warning when Encrypted Saved Objects plugin is using an ephemeral encryption key', async () => { const context = coreMock.createPluginInitializerContext(); - const plugin = new Plugin(context); + const plugin = new AlertingPlugin(context); const coreSetup = coreMock.createSetup(); const encryptedSavedObjectsSetup = encryptedSavedObjectsMock.createSetup(); @@ -49,7 +49,7 @@ describe('Alerting Plugin', () => { describe('getAlertsClientWithRequest()', () => { it('throws error when encryptedSavedObjects plugin has usingEphemeralEncryptionKey set to true', async () => { const context = coreMock.createPluginInitializerContext(); - const plugin = new Plugin(context); + const plugin = new AlertingPlugin(context); const coreSetup = coreMock.createSetup(); const encryptedSavedObjectsSetup = encryptedSavedObjectsMock.createSetup(); @@ -87,7 +87,7 @@ describe('Alerting Plugin', () => { it(`doesn't throw error when encryptedSavedObjects plugin has usingEphemeralEncryptionKey set to false`, async () => { const context = coreMock.createPluginInitializerContext(); - const plugin = new Plugin(context); + const plugin = new AlertingPlugin(context); const coreSetup = coreMock.createSetup(); const encryptedSavedObjectsSetup = { diff --git a/x-pack/plugins/alerting/server/plugin.ts b/x-pack/plugins/alerting/server/plugin.ts new file mode 100644 index 0000000000000..bed163878b5ac --- /dev/null +++ b/x-pack/plugins/alerting/server/plugin.ts @@ -0,0 +1,236 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { SecurityPluginSetup } from '../../security/server'; +import { + EncryptedSavedObjectsPluginSetup, + EncryptedSavedObjectsPluginStart, +} from '../../encrypted_saved_objects/server'; +import { TaskManagerSetupContract, TaskManagerStartContract } from '../../task_manager/server'; +import { SpacesPluginSetup, SpacesServiceSetup } from '../../spaces/server'; +import { AlertsClient } from './alerts_client'; +import { AlertTypeRegistry } from './alert_type_registry'; +import { TaskRunnerFactory } from './task_runner'; +import { AlertsClientFactory } from './alerts_client_factory'; +import { LicenseState } from './lib/license_state'; +import { + IClusterClient, + KibanaRequest, + Logger, + PluginInitializerContext, + CoreSetup, + CoreStart, + SavedObjectsServiceStart, + IContextProvider, + RequestHandler, +} from '../../../../src/core/server'; + +import { + createAlertRoute, + deleteAlertRoute, + findAlertRoute, + getAlertRoute, + getAlertStateRoute, + listAlertTypesRoute, + updateAlertRoute, + enableAlertRoute, + disableAlertRoute, + updateApiKeyRoute, + muteAllAlertRoute, + unmuteAllAlertRoute, + muteAlertInstanceRoute, + unmuteAlertInstanceRoute, +} from './routes'; +import { LicensingPluginSetup } from '../../licensing/server'; +import { + PluginSetupContract as ActionsPluginSetupContract, + PluginStartContract as ActionsPluginStartContract, +} from '../../../plugins/actions/server'; +import { Services } from './types'; + +export interface PluginSetupContract { + registerType: AlertTypeRegistry['register']; +} +export interface PluginStartContract { + listTypes: AlertTypeRegistry['list']; + getAlertsClientWithRequest(request: KibanaRequest): PublicMethodsOf; +} + +export interface AlertingPluginsSetup { + security?: SecurityPluginSetup; + taskManager: TaskManagerSetupContract; + actions: ActionsPluginSetupContract; + encryptedSavedObjects: EncryptedSavedObjectsPluginSetup; + licensing: LicensingPluginSetup; + spaces?: SpacesPluginSetup; +} +export interface AlertingPluginsStart { + actions: ActionsPluginStartContract; + encryptedSavedObjects: EncryptedSavedObjectsPluginStart; + taskManager: TaskManagerStartContract; +} + +export class AlertingPlugin { + private readonly logger: Logger; + private alertTypeRegistry?: AlertTypeRegistry; + private readonly taskRunnerFactory: TaskRunnerFactory; + private adminClient?: IClusterClient; + private serverBasePath?: string; + private licenseState: LicenseState | null = null; + private isESOUsingEphemeralEncryptionKey?: boolean; + private spaces?: SpacesServiceSetup; + private security?: SecurityPluginSetup; + private readonly alertsClientFactory: AlertsClientFactory; + + constructor(initializerContext: PluginInitializerContext) { + this.logger = initializerContext.logger.get('plugins', 'alerting'); + this.taskRunnerFactory = new TaskRunnerFactory(); + this.alertsClientFactory = new AlertsClientFactory(); + } + + public async setup(core: CoreSetup, plugins: AlertingPluginsSetup): Promise { + this.adminClient = core.elasticsearch.adminClient; + this.licenseState = new LicenseState(plugins.licensing.license$); + this.spaces = plugins.spaces?.spacesService; + this.security = plugins.security; + this.isESOUsingEphemeralEncryptionKey = + plugins.encryptedSavedObjects.usingEphemeralEncryptionKey; + + if (this.isESOUsingEphemeralEncryptionKey) { + this.logger.warn( + 'APIs are disabled due to the Encrypted Saved Objects plugin using an ephemeral encryption key. Please set xpack.encryptedSavedObjects.encryptionKey in kibana.yml.' + ); + } + + // Encrypted attributes + plugins.encryptedSavedObjects.registerType({ + type: 'alert', + attributesToEncrypt: new Set(['apiKey']), + attributesToExcludeFromAAD: new Set([ + 'scheduledTaskId', + 'muteAll', + 'mutedInstanceIds', + 'updatedBy', + ]), + }); + + const alertTypeRegistry = new AlertTypeRegistry({ + taskManager: plugins.taskManager, + taskRunnerFactory: this.taskRunnerFactory, + }); + this.alertTypeRegistry = alertTypeRegistry; + this.serverBasePath = core.http.basePath.serverBasePath; + + core.http.registerRouteHandlerContext('alerting', this.createRouteHandlerContext()); + + // Routes + const router = core.http.createRouter(); + // Register routes + createAlertRoute(router, this.licenseState); + deleteAlertRoute(router, this.licenseState); + findAlertRoute(router, this.licenseState); + getAlertRoute(router, this.licenseState); + getAlertStateRoute(router, this.licenseState); + listAlertTypesRoute(router, this.licenseState); + updateAlertRoute(router, this.licenseState); + enableAlertRoute(router, this.licenseState); + disableAlertRoute(router, this.licenseState); + updateApiKeyRoute(router, this.licenseState); + muteAllAlertRoute(router, this.licenseState); + unmuteAllAlertRoute(router, this.licenseState); + muteAlertInstanceRoute(router, this.licenseState); + unmuteAlertInstanceRoute(router, this.licenseState); + + return { + registerType: alertTypeRegistry.register.bind(alertTypeRegistry), + }; + } + + public start(core: CoreStart, plugins: AlertingPluginsStart): PluginStartContract { + const { + spaces, + isESOUsingEphemeralEncryptionKey, + logger, + taskRunnerFactory, + alertTypeRegistry, + alertsClientFactory, + security, + } = this; + + alertsClientFactory.initialize({ + alertTypeRegistry: alertTypeRegistry!, + logger, + taskManager: plugins.taskManager, + securityPluginSetup: security, + encryptedSavedObjectsPlugin: plugins.encryptedSavedObjects, + spaceIdToNamespace: this.spaceIdToNamespace, + getSpaceId(request: KibanaRequest) { + return spaces?.getSpaceId(request); + }, + }); + + taskRunnerFactory.initialize({ + logger, + getServices: this.getServicesFactory(core.savedObjects), + spaceIdToNamespace: this.spaceIdToNamespace, + executeAction: plugins.actions.execute, + encryptedSavedObjectsPlugin: plugins.encryptedSavedObjects, + getBasePath: this.getBasePath, + }); + + return { + listTypes: alertTypeRegistry!.list.bind(this.alertTypeRegistry!), + // Ability to get an alerts client from legacy code + getAlertsClientWithRequest(request: KibanaRequest) { + if (isESOUsingEphemeralEncryptionKey === true) { + throw new Error( + `Unable to create alerts client due to the Encrypted Saved Objects plugin using an ephemeral encryption key. Please set xpack.encryptedSavedObjects.encryptionKey in kibana.yml` + ); + } + return alertsClientFactory!.create(request, core.savedObjects.getScopedClient(request)); + }, + }; + } + + private createRouteHandlerContext = (): IContextProvider< + RequestHandler, + 'alerting' + > => { + const { alertTypeRegistry, alertsClientFactory } = this; + return async function alertsRouteHandlerContext(context, request) { + return { + getAlertsClient: () => { + return alertsClientFactory!.create(request, context.core!.savedObjects.client); + }, + listTypes: alertTypeRegistry!.list.bind(alertTypeRegistry!), + }; + }; + }; + + private getServicesFactory( + savedObjects: SavedObjectsServiceStart + ): (request: KibanaRequest) => Services { + const { adminClient } = this; + return request => ({ + callCluster: adminClient!.asScoped(request).callAsCurrentUser, + savedObjectsClient: savedObjects.getScopedClient(request), + }); + } + + private spaceIdToNamespace = (spaceId?: string): string | undefined => { + return this.spaces && spaceId ? this.spaces.spaceIdToNamespace(spaceId) : undefined; + }; + + private getBasePath = (spaceId?: string): string => { + return this.spaces && spaceId ? this.spaces.getBasePath(spaceId) : this.serverBasePath!; + }; + + public stop() { + if (this.licenseState) { + this.licenseState.clean(); + } + } +} diff --git a/x-pack/plugins/alerting/server/routes/_mock_handler_arguments.ts b/x-pack/plugins/alerting/server/routes/_mock_handler_arguments.ts new file mode 100644 index 0000000000000..9815ad5194af7 --- /dev/null +++ b/x-pack/plugins/alerting/server/routes/_mock_handler_arguments.ts @@ -0,0 +1,42 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { RequestHandlerContext, KibanaRequest, KibanaResponseFactory } from 'kibana/server'; +import { identity } from 'lodash'; +import { httpServerMock } from '../../../../../src/core/server/mocks'; +import { alertsClientMock } from '../alerts_client.mock'; + +export function mockHandlerArguments( + { alertsClient, listTypes: listTypesRes = [] }: any, + req: any, + res?: Array> +): [RequestHandlerContext, KibanaRequest, KibanaResponseFactory] { + const listTypes = jest.fn(() => listTypesRes); + return [ + ({ + alerting: { + listTypes, + getAlertsClient() { + return alertsClient || alertsClientMock.create(); + }, + }, + } as unknown) as RequestHandlerContext, + req as KibanaRequest, + mockResponseFactory(res), + ]; +} + +export const mockResponseFactory = (resToMock: Array> = []) => { + const factory: jest.Mocked = httpServerMock.createResponseFactory(); + resToMock.forEach((key: string) => { + if (key in factory) { + Object.defineProperty(factory, key, { + value: jest.fn(identity), + }); + } + }); + return (factory as unknown) as KibanaResponseFactory; +}; diff --git a/x-pack/plugins/alerting/server/routes/create.test.ts b/x-pack/plugins/alerting/server/routes/create.test.ts new file mode 100644 index 0000000000000..c6a0da2bd9191 --- /dev/null +++ b/x-pack/plugins/alerting/server/routes/create.test.ts @@ -0,0 +1,172 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { createAlertRoute } from './create'; +import { mockRouter, RouterMock } from '../../../../../src/core/server/http/router/router.mock'; +import { mockLicenseState } from '../lib/license_state.mock'; +import { verifyApiAccess } from '../lib/license_api_access'; +import { mockHandlerArguments } from './_mock_handler_arguments'; +import { alertsClientMock } from '../alerts_client.mock'; + +const alertsClient = alertsClientMock.create(); + +jest.mock('../lib/license_api_access.ts', () => ({ + verifyApiAccess: jest.fn(), +})); + +beforeEach(() => { + jest.resetAllMocks(); +}); + +describe('createAlertRoute', () => { + const createdAt = new Date(); + const updatedAt = new Date(); + + const mockedAlert = { + alertTypeId: '1', + consumer: 'bar', + name: 'abc', + schedule: { interval: '10s' }, + tags: ['foo'], + params: { + bar: true, + }, + throttle: '30s', + actions: [ + { + group: 'default', + id: '2', + params: { + foo: true, + }, + }, + ], + }; + + const createResult = { + ...mockedAlert, + enabled: true, + muteAll: false, + createdBy: '', + updatedBy: '', + apiKey: '', + apiKeyOwner: '', + mutedInstanceIds: [], + createdAt, + updatedAt, + id: '123', + actions: [ + { + ...mockedAlert.actions[0], + actionTypeId: 'test', + }, + ], + }; + + it('creates an alert with proper parameters', async () => { + const licenseState = mockLicenseState(); + const router: RouterMock = mockRouter.create(); + + createAlertRoute(router, licenseState); + + const [config, handler] = router.post.mock.calls[0]; + + expect(config.path).toMatchInlineSnapshot(`"/api/alert"`); + expect(config.options).toMatchInlineSnapshot(` + Object { + "tags": Array [ + "access:alerting-all", + ], + } + `); + + alertsClient.create.mockResolvedValueOnce(createResult); + + const [context, req, res] = mockHandlerArguments( + { alertsClient }, + { + body: mockedAlert, + }, + ['ok'] + ); + + expect(await handler(context, req, res)).toEqual({ body: createResult }); + + 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", + "consumer": "bar", + "name": "abc", + "params": Object { + "bar": true, + }, + "schedule": Object { + "interval": "10s", + }, + "tags": Array [ + "foo", + ], + "throttle": "30s", + }, + }, + ] + `); + + expect(res.ok).toHaveBeenCalledWith({ + body: createResult, + }); + }); + + it('ensures the license allows creating alerts', async () => { + const licenseState = mockLicenseState(); + const router: RouterMock = mockRouter.create(); + + createAlertRoute(router, licenseState); + + const [, handler] = router.post.mock.calls[0]; + + alertsClient.create.mockResolvedValueOnce(createResult); + + const [context, req, res] = mockHandlerArguments(alertsClient, {}); + + await handler(context, req, res); + + expect(verifyApiAccess).toHaveBeenCalledWith(licenseState); + }); + + it('ensures the license check prevents creating alerts', async () => { + const licenseState = mockLicenseState(); + const router: RouterMock = mockRouter.create(); + + (verifyApiAccess as jest.Mock).mockImplementation(() => { + throw new Error('OMG'); + }); + + createAlertRoute(router, licenseState); + + const [, handler] = router.post.mock.calls[0]; + + alertsClient.create.mockResolvedValueOnce(createResult); + + const [context, req, res] = mockHandlerArguments(alertsClient, {}); + + expect(handler(context, req, res)).rejects.toMatchInlineSnapshot(`[Error: OMG]`); + + expect(verifyApiAccess).toHaveBeenCalledWith(licenseState); + }); +}); diff --git a/x-pack/plugins/alerting/server/routes/create.ts b/x-pack/plugins/alerting/server/routes/create.ts new file mode 100644 index 0000000000000..8d854e0df8467 --- /dev/null +++ b/x-pack/plugins/alerting/server/routes/create.ts @@ -0,0 +1,68 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { schema, TypeOf } from '@kbn/config-schema'; +import { + IRouter, + RequestHandlerContext, + KibanaRequest, + IKibanaResponse, + KibanaResponseFactory, +} from 'kibana/server'; +import { LicenseState } from '../lib/license_state'; +import { verifyApiAccess } from '../lib/license_api_access'; +import { validateDurationSchema } from '../lib'; +import { Alert } from '../types'; + +export const bodySchema = schema.object({ + name: schema.string(), + alertTypeId: schema.string(), + enabled: schema.boolean({ defaultValue: true }), + consumer: schema.string(), + tags: schema.arrayOf(schema.string(), { defaultValue: [] }), + throttle: schema.nullable(schema.string({ validate: validateDurationSchema })), + params: schema.recordOf(schema.string(), schema.any(), { defaultValue: {} }), + schedule: schema.object({ + interval: schema.string({ validate: validateDurationSchema }), + }), + actions: schema.arrayOf( + schema.object({ + group: schema.string(), + id: schema.string(), + actionTypeId: schema.maybe(schema.string()), + params: schema.recordOf(schema.string(), schema.any(), { defaultValue: {} }), + }), + { defaultValue: [] } + ), +}); + +export const createAlertRoute = (router: IRouter, licenseState: LicenseState) => { + router.post( + { + path: '/api/alert', + validate: { + body: bodySchema, + }, + options: { + tags: ['access:alerting-all'], + }, + }, + router.handleLegacyErrors(async function( + context: RequestHandlerContext, + req: KibanaRequest, any>, + res: KibanaResponseFactory + ): Promise> { + verifyApiAccess(licenseState); + + const alertsClient = context.alerting.getAlertsClient(); + const alert = req.body; + const alertRes: Alert = await alertsClient.create({ data: alert }); + return res.ok({ + body: alertRes, + }); + }) + ); +}; diff --git a/x-pack/plugins/alerting/server/routes/delete.test.ts b/x-pack/plugins/alerting/server/routes/delete.test.ts new file mode 100644 index 0000000000000..36bb485817c15 --- /dev/null +++ b/x-pack/plugins/alerting/server/routes/delete.test.ts @@ -0,0 +1,108 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { deleteAlertRoute } from './delete'; +import { mockRouter, RouterMock } from '../../../../../src/core/server/http/router/router.mock'; +import { mockLicenseState } from '../lib/license_state.mock'; +import { verifyApiAccess } from '../lib/license_api_access'; +import { mockHandlerArguments } from './_mock_handler_arguments'; +import { alertsClientMock } from '../alerts_client.mock'; + +const alertsClient = alertsClientMock.create(); + +jest.mock('../lib/license_api_access.ts', () => ({ + verifyApiAccess: jest.fn(), +})); + +beforeEach(() => { + jest.resetAllMocks(); +}); + +describe('deleteAlertRoute', () => { + it('deletes an alert with proper parameters', async () => { + const licenseState = mockLicenseState(); + const router: RouterMock = mockRouter.create(); + + deleteAlertRoute(router, licenseState); + + const [config, handler] = router.delete.mock.calls[0]; + + expect(config.path).toMatchInlineSnapshot(`"/api/alert/{id}"`); + expect(config.options).toMatchInlineSnapshot(` + Object { + "tags": Array [ + "access:alerting-all", + ], + } + `); + + alertsClient.delete.mockResolvedValueOnce({}); + + const [context, req, res] = mockHandlerArguments( + { alertsClient }, + { + params: { + id: '1', + }, + }, + ['noContent'] + ); + + expect(await handler(context, req, res)).toEqual(undefined); + + expect(alertsClient.delete).toHaveBeenCalledTimes(1); + expect(alertsClient.delete.mock.calls[0]).toMatchInlineSnapshot(` + Array [ + Object { + "id": "1", + }, + ] + `); + + expect(res.noContent).toHaveBeenCalled(); + }); + + it('ensures the license allows deleting alerts', async () => { + const licenseState = mockLicenseState(); + const router: RouterMock = mockRouter.create(); + + deleteAlertRoute(router, licenseState); + + const [, handler] = router.delete.mock.calls[0]; + + alertsClient.delete.mockResolvedValueOnce({}); + + const [context, req, res] = mockHandlerArguments(alertsClient, { + params: { id: '1' }, + }); + + await handler(context, req, res); + + expect(verifyApiAccess).toHaveBeenCalledWith(licenseState); + }); + + it('ensures the license check prevents deleting alerts', async () => { + const licenseState = mockLicenseState(); + const router: RouterMock = mockRouter.create(); + + (verifyApiAccess as jest.Mock).mockImplementation(() => { + throw new Error('OMG'); + }); + + deleteAlertRoute(router, licenseState); + + const [, handler] = router.delete.mock.calls[0]; + + alertsClient.delete.mockResolvedValueOnce({}); + + const [context, req, res] = mockHandlerArguments(alertsClient, { + id: '1', + }); + + expect(handler(context, req, res)).rejects.toMatchInlineSnapshot(`[Error: OMG]`); + + expect(verifyApiAccess).toHaveBeenCalledWith(licenseState); + }); +}); diff --git a/x-pack/plugins/alerting/server/routes/delete.ts b/x-pack/plugins/alerting/server/routes/delete.ts new file mode 100644 index 0000000000000..0556ef3d66982 --- /dev/null +++ b/x-pack/plugins/alerting/server/routes/delete.ts @@ -0,0 +1,45 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { schema, TypeOf } from '@kbn/config-schema'; +import { + IRouter, + RequestHandlerContext, + KibanaRequest, + IKibanaResponse, + KibanaResponseFactory, +} from 'kibana/server'; +import { LicenseState } from '../lib/license_state'; +import { verifyApiAccess } from '../lib/license_api_access'; + +const paramSchema = schema.object({ + id: schema.string(), +}); + +export const deleteAlertRoute = (router: IRouter, licenseState: LicenseState) => { + router.delete( + { + path: '/api/alert/{id}', + validate: { + params: paramSchema, + }, + options: { + tags: ['access:alerting-all'], + }, + }, + router.handleLegacyErrors(async function( + context: RequestHandlerContext, + req: KibanaRequest, any, any, any>, + res: KibanaResponseFactory + ): Promise> { + verifyApiAccess(licenseState); + const alertsClient = context.alerting.getAlertsClient(); + const { id } = req.params; + await alertsClient.delete({ id }); + return res.noContent(); + }) + ); +}; diff --git a/x-pack/plugins/alerting/server/routes/disable.test.ts b/x-pack/plugins/alerting/server/routes/disable.test.ts new file mode 100644 index 0000000000000..622b562ec6911 --- /dev/null +++ b/x-pack/plugins/alerting/server/routes/disable.test.ts @@ -0,0 +1,66 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { disableAlertRoute } from './disable'; +import { mockRouter, RouterMock } from '../../../../../src/core/server/http/router/router.mock'; +import { mockLicenseState } from '../lib/license_state.mock'; +import { mockHandlerArguments } from './_mock_handler_arguments'; +import { alertsClientMock } from '../alerts_client.mock'; + +const alertsClient = alertsClientMock.create(); + +jest.mock('../lib/license_api_access.ts', () => ({ + verifyApiAccess: jest.fn(), +})); + +beforeEach(() => { + jest.resetAllMocks(); +}); + +describe('disableAlertRoute', () => { + it('disables an alert', async () => { + const licenseState = mockLicenseState(); + const router: RouterMock = mockRouter.create(); + + disableAlertRoute(router, licenseState); + + const [config, handler] = router.post.mock.calls[0]; + + expect(config.path).toMatchInlineSnapshot(`"/api/alert/{id}/_disable"`); + expect(config.options).toMatchInlineSnapshot(` + Object { + "tags": Array [ + "access:alerting-all", + ], + } + `); + + alertsClient.disable.mockResolvedValueOnce(); + + const [context, req, res] = mockHandlerArguments( + { alertsClient }, + { + params: { + id: '1', + }, + }, + ['noContent'] + ); + + expect(await handler(context, req, res)).toEqual(undefined); + + expect(alertsClient.disable).toHaveBeenCalledTimes(1); + expect(alertsClient.disable.mock.calls[0]).toMatchInlineSnapshot(` + Array [ + Object { + "id": "1", + }, + ] + `); + + expect(res.noContent).toHaveBeenCalled(); + }); +}); diff --git a/x-pack/plugins/alerting/server/routes/disable.ts b/x-pack/plugins/alerting/server/routes/disable.ts new file mode 100644 index 0000000000000..5c6d977e62c38 --- /dev/null +++ b/x-pack/plugins/alerting/server/routes/disable.ts @@ -0,0 +1,45 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { schema, TypeOf } from '@kbn/config-schema'; +import { + IRouter, + RequestHandlerContext, + KibanaRequest, + IKibanaResponse, + KibanaResponseFactory, +} from 'kibana/server'; +import { LicenseState } from '../lib/license_state'; +import { verifyApiAccess } from '../lib/license_api_access'; + +const paramSchema = schema.object({ + id: schema.string(), +}); + +export const disableAlertRoute = (router: IRouter, licenseState: LicenseState) => { + router.post( + { + path: '/api/alert/{id}/_disable', + validate: { + params: paramSchema, + }, + options: { + tags: ['access:alerting-all'], + }, + }, + router.handleLegacyErrors(async function( + context: RequestHandlerContext, + req: KibanaRequest, any, any, any>, + res: KibanaResponseFactory + ): Promise> { + verifyApiAccess(licenseState); + const alertsClient = context.alerting.getAlertsClient(); + const { id } = req.params; + await alertsClient.disable({ id }); + return res.noContent(); + }) + ); +}; diff --git a/x-pack/plugins/alerting/server/routes/enable.test.ts b/x-pack/plugins/alerting/server/routes/enable.test.ts new file mode 100644 index 0000000000000..5a7b027e8ef54 --- /dev/null +++ b/x-pack/plugins/alerting/server/routes/enable.test.ts @@ -0,0 +1,65 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { enableAlertRoute } from './enable'; +import { mockRouter, RouterMock } from '../../../../../src/core/server/http/router/router.mock'; +import { mockLicenseState } from '../lib/license_state.mock'; +import { mockHandlerArguments } from './_mock_handler_arguments'; +import { alertsClientMock } from '../alerts_client.mock'; + +const alertsClient = alertsClientMock.create(); + +jest.mock('../lib/license_api_access.ts', () => ({ + verifyApiAccess: jest.fn(), +})); + +beforeEach(() => { + jest.resetAllMocks(); +}); + +describe('enableAlertRoute', () => { + it('enables an alert', async () => { + const licenseState = mockLicenseState(); + const router: RouterMock = mockRouter.create(); + + enableAlertRoute(router, licenseState); + + const [config, handler] = router.post.mock.calls[0]; + + expect(config.path).toMatchInlineSnapshot(`"/api/alert/{id}/_enable"`); + expect(config.options).toMatchInlineSnapshot(` + Object { + "tags": Array [ + "access:alerting-all", + ], + } + `); + + alertsClient.enable.mockResolvedValueOnce(); + + const [context, req, res] = mockHandlerArguments( + { alertsClient }, + { + params: { + id: '1', + }, + }, + ['noContent'] + ); + + expect(await handler(context, req, res)).toEqual(undefined); + + expect(alertsClient.enable).toHaveBeenCalledTimes(1); + expect(alertsClient.enable.mock.calls[0]).toMatchInlineSnapshot(` + Array [ + Object { + "id": "1", + }, + ] + `); + + expect(res.noContent).toHaveBeenCalled(); + }); +}); diff --git a/x-pack/plugins/alerting/server/routes/enable.ts b/x-pack/plugins/alerting/server/routes/enable.ts new file mode 100644 index 0000000000000..f75344ad85998 --- /dev/null +++ b/x-pack/plugins/alerting/server/routes/enable.ts @@ -0,0 +1,45 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { schema, TypeOf } from '@kbn/config-schema'; +import { + IRouter, + RequestHandlerContext, + KibanaRequest, + IKibanaResponse, + KibanaResponseFactory, +} from 'kibana/server'; +import { LicenseState } from '../lib/license_state'; +import { verifyApiAccess } from '../lib/license_api_access'; + +const paramSchema = schema.object({ + id: schema.string(), +}); + +export const enableAlertRoute = (router: IRouter, licenseState: LicenseState) => { + router.post( + { + path: '/api/alert/{id}/_enable', + validate: { + params: paramSchema, + }, + options: { + tags: ['access:alerting-all'], + }, + }, + router.handleLegacyErrors(async function( + context: RequestHandlerContext, + req: KibanaRequest, any, any, any>, + res: KibanaResponseFactory + ): Promise> { + verifyApiAccess(licenseState); + const alertsClient = context.alerting.getAlertsClient(); + const { id } = req.params; + await alertsClient.enable({ id }); + return res.noContent(); + }) + ); +}; diff --git a/x-pack/plugins/alerting/server/routes/find.test.ts b/x-pack/plugins/alerting/server/routes/find.test.ts new file mode 100644 index 0000000000000..ba0114c99a9bd --- /dev/null +++ b/x-pack/plugins/alerting/server/routes/find.test.ts @@ -0,0 +1,150 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { findAlertRoute } from './find'; +import { mockRouter, RouterMock } from '../../../../../src/core/server/http/router/router.mock'; +import { mockLicenseState } from '../lib/license_state.mock'; +import { verifyApiAccess } from '../lib/license_api_access'; +import { mockHandlerArguments } from './_mock_handler_arguments'; +import { alertsClientMock } from '../alerts_client.mock'; + +const alertsClient = alertsClientMock.create(); + +jest.mock('../lib/license_api_access.ts', () => ({ + verifyApiAccess: jest.fn(), +})); + +beforeEach(() => { + jest.resetAllMocks(); +}); + +describe('findAlertRoute', () => { + it('finds alerts with proper parameters', async () => { + const licenseState = mockLicenseState(); + const router: RouterMock = mockRouter.create(); + + findAlertRoute(router, licenseState); + + const [config, handler] = router.get.mock.calls[0]; + + expect(config.path).toMatchInlineSnapshot(`"/api/alert/_find"`); + expect(config.options).toMatchInlineSnapshot(` + Object { + "tags": Array [ + "access:alerting-read", + ], + } + `); + + const findResult = { + page: 1, + perPage: 1, + total: 0, + data: [], + }; + alertsClient.find.mockResolvedValueOnce(findResult); + + const [context, req, res] = mockHandlerArguments( + { alertsClient }, + { + query: { + per_page: 1, + page: 1, + default_search_operator: 'OR', + }, + }, + ['ok'] + ); + + expect(await handler(context, req, res)).toMatchInlineSnapshot(` + Object { + "body": Object { + "data": Array [], + "page": 1, + "perPage": 1, + "total": 0, + }, + } + `); + + expect(alertsClient.find).toHaveBeenCalledTimes(1); + expect(alertsClient.find.mock.calls[0]).toMatchInlineSnapshot(` + Array [ + Object { + "options": Object { + "defaultSearchOperator": "OR", + "fields": undefined, + "filter": undefined, + "page": 1, + "perPage": 1, + "search": undefined, + "sortField": undefined, + }, + }, + ] + `); + + expect(res.ok).toHaveBeenCalledWith({ + body: findResult, + }); + }); + + it('ensures the license allows finding alerts', async () => { + const licenseState = mockLicenseState(); + const router: RouterMock = mockRouter.create(); + + findAlertRoute(router, licenseState); + + const [, handler] = router.get.mock.calls[0]; + + alertsClient.find.mockResolvedValueOnce({ + page: 1, + perPage: 1, + total: 0, + data: [], + }); + + const [context, req, res] = mockHandlerArguments(alertsClient, { + query: { + per_page: 1, + page: 1, + default_search_operator: 'OR', + }, + }); + + await handler(context, req, res); + + expect(verifyApiAccess).toHaveBeenCalledWith(licenseState); + }); + + it('ensures the license check prevents finding alerts', async () => { + const licenseState = mockLicenseState(); + const router: RouterMock = mockRouter.create(); + + (verifyApiAccess as jest.Mock).mockImplementation(() => { + throw new Error('OMG'); + }); + + findAlertRoute(router, licenseState); + + const [, handler] = router.get.mock.calls[0]; + + const [context, req, res] = mockHandlerArguments( + {}, + { + query: { + per_page: 1, + page: 1, + default_search_operator: 'OR', + }, + }, + ['ok'] + ); + expect(handler(context, req, res)).rejects.toMatchInlineSnapshot(`[Error: OMG]`); + + expect(verifyApiAccess).toHaveBeenCalledWith(licenseState); + }); +}); diff --git a/x-pack/plugins/alerting/server/routes/find.ts b/x-pack/plugins/alerting/server/routes/find.ts new file mode 100644 index 0000000000000..16f53aa218895 --- /dev/null +++ b/x-pack/plugins/alerting/server/routes/find.ts @@ -0,0 +1,90 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { schema, TypeOf } from '@kbn/config-schema'; +import { + IRouter, + RequestHandlerContext, + KibanaRequest, + IKibanaResponse, + KibanaResponseFactory, +} from 'kibana/server'; +import { FindOptions } from '../../../alerting/server'; +import { LicenseState } from '../lib/license_state'; +import { verifyApiAccess } from '../lib/license_api_access'; + +// config definition +const querySchema = schema.object({ + per_page: schema.number({ defaultValue: 10, min: 0 }), + page: schema.number({ defaultValue: 1, min: 1 }), + search: schema.maybe(schema.string()), + default_search_operator: schema.oneOf([schema.literal('OR'), schema.literal('AND')], { + defaultValue: 'OR', + }), + search_fields: schema.maybe(schema.oneOf([schema.arrayOf(schema.string()), schema.string()])), + sort_field: schema.maybe(schema.string()), + has_reference: schema.maybe( + // use nullable as maybe is currently broken + // in config-schema + schema.nullable( + schema.object({ + type: schema.string(), + id: schema.string(), + }) + ) + ), + fields: schema.maybe(schema.arrayOf(schema.string())), + filter: schema.maybe(schema.string()), +}); + +export const findAlertRoute = (router: IRouter, licenseState: LicenseState) => { + router.get( + { + path: '/api/alert/_find', + validate: { + query: querySchema, + }, + options: { + tags: ['access:alerting-read'], + }, + }, + router.handleLegacyErrors(async function( + context: RequestHandlerContext, + req: KibanaRequest, any, any>, + res: KibanaResponseFactory + ): Promise> { + verifyApiAccess(licenseState); + const alertsClient = context.alerting.getAlertsClient(); + const query = req.query; + const options: FindOptions['options'] = { + perPage: query.per_page, + page: query.page, + search: query.search, + defaultSearchOperator: query.default_search_operator, + sortField: query.sort_field, + fields: query.fields, + filter: query.filter, + }; + + if (query.search_fields) { + options.searchFields = Array.isArray(query.search_fields) + ? query.search_fields + : [query.search_fields]; + } + + if (query.has_reference) { + options.hasReference = query.has_reference; + } + + const findResult = await alertsClient.find({ + options, + }); + return res.ok({ + body: findResult, + }); + }) + ); +}; diff --git a/x-pack/plugins/alerting/server/routes/get.test.ts b/x-pack/plugins/alerting/server/routes/get.test.ts new file mode 100644 index 0000000000000..4506700c6d9cc --- /dev/null +++ b/x-pack/plugins/alerting/server/routes/get.test.ts @@ -0,0 +1,140 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { getAlertRoute } from './get'; +import { mockRouter, RouterMock } from '../../../../../src/core/server/http/router/router.mock'; +import { mockLicenseState } from '../lib/license_state.mock'; +import { verifyApiAccess } from '../lib/license_api_access'; +import { mockHandlerArguments } from './_mock_handler_arguments'; +import { alertsClientMock } from '../alerts_client.mock'; + +const alertsClient = alertsClientMock.create(); +jest.mock('../lib/license_api_access.ts', () => ({ + verifyApiAccess: jest.fn(), +})); + +beforeEach(() => { + jest.resetAllMocks(); +}); + +describe('getAlertRoute', () => { + const mockedAlert = { + id: '1', + alertTypeId: '1', + schedule: { interval: '10s' }, + params: { + bar: true, + }, + createdAt: new Date(), + updatedAt: new Date(), + actions: [ + { + group: 'default', + id: '2', + actionTypeId: 'test', + params: { + foo: true, + }, + }, + ], + consumer: 'bar', + name: 'abc', + tags: ['foo'], + enabled: true, + muteAll: false, + createdBy: '', + updatedBy: '', + apiKey: '', + apiKeyOwner: '', + throttle: '30s', + mutedInstanceIds: [], + }; + + it('gets an alert with proper parameters', async () => { + const licenseState = mockLicenseState(); + const router: RouterMock = mockRouter.create(); + + getAlertRoute(router, licenseState); + const [config, handler] = router.get.mock.calls[0]; + + expect(config.path).toMatchInlineSnapshot(`"/api/alert/{id}"`); + expect(config.options).toMatchInlineSnapshot(` + Object { + "tags": Array [ + "access:alerting-read", + ], + } + `); + + alertsClient.get.mockResolvedValueOnce(mockedAlert); + + const [context, req, res] = mockHandlerArguments( + { alertsClient }, + { + params: { id: '1' }, + }, + ['ok'] + ); + await handler(context, req, res); + + expect(alertsClient.get).toHaveBeenCalledTimes(1); + expect(alertsClient.get.mock.calls[0][0].id).toEqual('1'); + + expect(res.ok).toHaveBeenCalledWith({ + body: mockedAlert, + }); + }); + + it('ensures the license allows getting alerts', async () => { + const licenseState = mockLicenseState(); + const router: RouterMock = mockRouter.create(); + + getAlertRoute(router, licenseState); + + const [, handler] = router.get.mock.calls[0]; + + alertsClient.get.mockResolvedValueOnce(mockedAlert); + + const [context, req, res] = mockHandlerArguments( + alertsClient, + { + params: { id: '1' }, + }, + ['ok'] + ); + + await handler(context, req, res); + + expect(verifyApiAccess).toHaveBeenCalledWith(licenseState); + }); + + it('ensures the license check prevents getting alerts', async () => { + const licenseState = mockLicenseState(); + const router: RouterMock = mockRouter.create(); + + (verifyApiAccess as jest.Mock).mockImplementation(() => { + throw new Error('OMG'); + }); + + getAlertRoute(router, licenseState); + + const [, handler] = router.get.mock.calls[0]; + + alertsClient.get.mockResolvedValueOnce(mockedAlert); + + const [context, req, res] = mockHandlerArguments( + alertsClient, + { + params: { id: '1' }, + }, + ['ok'] + ); + + expect(handler(context, req, res)).rejects.toMatchInlineSnapshot(`[Error: OMG]`); + + expect(verifyApiAccess).toHaveBeenCalledWith(licenseState); + }); +}); diff --git a/x-pack/plugins/alerting/server/routes/get.ts b/x-pack/plugins/alerting/server/routes/get.ts new file mode 100644 index 0000000000000..407d80b0f87ab --- /dev/null +++ b/x-pack/plugins/alerting/server/routes/get.ts @@ -0,0 +1,46 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { schema, TypeOf } from '@kbn/config-schema'; +import { + IRouter, + RequestHandlerContext, + KibanaRequest, + IKibanaResponse, + KibanaResponseFactory, +} from 'kibana/server'; +import { LicenseState } from '../lib/license_state'; +import { verifyApiAccess } from '../lib/license_api_access'; + +const paramSchema = schema.object({ + id: schema.string(), +}); + +export const getAlertRoute = (router: IRouter, licenseState: LicenseState) => { + router.get( + { + path: `/api/alert/{id}`, + validate: { + params: paramSchema, + }, + options: { + tags: ['access:alerting-read'], + }, + }, + router.handleLegacyErrors(async function( + context: RequestHandlerContext, + req: KibanaRequest, any, any, any>, + res: KibanaResponseFactory + ): Promise> { + verifyApiAccess(licenseState); + const alertsClient = context.alerting.getAlertsClient(); + const { id } = req.params; + return res.ok({ + body: await alertsClient.get({ id }), + }); + }) + ); +}; diff --git a/x-pack/plugins/alerting/server/routes/get_alert_state.test.ts b/x-pack/plugins/alerting/server/routes/get_alert_state.test.ts new file mode 100644 index 0000000000000..eb51c96b88e5e --- /dev/null +++ b/x-pack/plugins/alerting/server/routes/get_alert_state.test.ts @@ -0,0 +1,170 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { getAlertStateRoute } from './get_alert_state'; +import { mockRouter, RouterMock } from '../../../../../src/core/server/http/router/router.mock'; +import { mockLicenseState } from '../lib/license_state.mock'; +import { mockHandlerArguments } from './_mock_handler_arguments'; +import { SavedObjectsErrorHelpers } from 'src/core/server/saved_objects'; +import { alertsClientMock } from '../alerts_client.mock'; + +const alertsClient = alertsClientMock.create(); +jest.mock('../lib/license_api_access.ts', () => ({ + verifyApiAccess: jest.fn(), +})); + +beforeEach(() => { + jest.resetAllMocks(); +}); + +describe('getAlertStateRoute', () => { + const mockedAlertState = { + alertTypeState: { + some: 'value', + }, + alertInstances: { + first_instance: { + state: {}, + meta: { + lastScheduledActions: { + group: 'first_group', + date: new Date(), + }, + }, + }, + second_instance: {}, + }, + }; + + it('gets alert state', async () => { + const licenseState = mockLicenseState(); + const router: RouterMock = mockRouter.create(); + + getAlertStateRoute(router, licenseState); + + const [config, handler] = router.get.mock.calls[0]; + + expect(config.path).toMatchInlineSnapshot(`"/api/alert/{id}/state"`); + expect(config.options).toMatchInlineSnapshot(` + Object { + "tags": Array [ + "access:alerting-read", + ], + } + `); + + alertsClient.getAlertState.mockResolvedValueOnce(mockedAlertState); + + const [context, req, res] = mockHandlerArguments( + { alertsClient }, + { + params: { + id: '1', + }, + }, + ['ok'] + ); + + await handler(context, req, res); + + expect(alertsClient.getAlertState).toHaveBeenCalledTimes(1); + expect(alertsClient.getAlertState.mock.calls[0]).toMatchInlineSnapshot(` + Array [ + Object { + "id": "1", + }, + ] + `); + + expect(res.ok).toHaveBeenCalled(); + }); + + it('returns NO-CONTENT when alert exists but has no task state yet', async () => { + const licenseState = mockLicenseState(); + const router: RouterMock = mockRouter.create(); + + getAlertStateRoute(router, licenseState); + + const [config, handler] = router.get.mock.calls[0]; + + expect(config.path).toMatchInlineSnapshot(`"/api/alert/{id}/state"`); + expect(config.options).toMatchInlineSnapshot(` + Object { + "tags": Array [ + "access:alerting-read", + ], + } + `); + + alertsClient.getAlertState.mockResolvedValueOnce(undefined); + + const [context, req, res] = mockHandlerArguments( + { alertsClient }, + { + params: { + id: '1', + }, + }, + ['noContent'] + ); + + expect(await handler(context, req, res)).toEqual(undefined); + + expect(alertsClient.getAlertState).toHaveBeenCalledTimes(1); + expect(alertsClient.getAlertState.mock.calls[0]).toMatchInlineSnapshot(` + Array [ + Object { + "id": "1", + }, + ] + `); + + expect(res.noContent).toHaveBeenCalled(); + }); + + it('returns NOT-FOUND when alert is not found', async () => { + const licenseState = mockLicenseState(); + const router: RouterMock = mockRouter.create(); + + getAlertStateRoute(router, licenseState); + + const [config, handler] = router.get.mock.calls[0]; + + expect(config.path).toMatchInlineSnapshot(`"/api/alert/{id}/state"`); + expect(config.options).toMatchInlineSnapshot(` + Object { + "tags": Array [ + "access:alerting-read", + ], + } + `); + + alertsClient.getAlertState = jest + .fn() + .mockResolvedValueOnce(SavedObjectsErrorHelpers.createGenericNotFoundError('alert', '1')); + + const [context, req, res] = mockHandlerArguments( + { alertsClient }, + { + params: { + id: '1', + }, + }, + ['notFound'] + ); + + expect(await handler(context, req, res)).toEqual(undefined); + + expect(alertsClient.getAlertState).toHaveBeenCalledTimes(1); + expect(alertsClient.getAlertState.mock.calls[0]).toMatchInlineSnapshot(` + Array [ + Object { + "id": "1", + }, + ] + `); + }); +}); diff --git a/x-pack/plugins/alerting/server/routes/get_alert_state.ts b/x-pack/plugins/alerting/server/routes/get_alert_state.ts new file mode 100644 index 0000000000000..b419889eea422 --- /dev/null +++ b/x-pack/plugins/alerting/server/routes/get_alert_state.ts @@ -0,0 +1,45 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { schema, TypeOf } from '@kbn/config-schema'; +import { + IRouter, + RequestHandlerContext, + KibanaRequest, + IKibanaResponse, + KibanaResponseFactory, +} from 'kibana/server'; +import { LicenseState } from '../lib/license_state'; +import { verifyApiAccess } from '../lib/license_api_access'; + +const paramSchema = schema.object({ + id: schema.string(), +}); + +export const getAlertStateRoute = (router: IRouter, licenseState: LicenseState) => { + router.get( + { + path: '/api/alert/{id}/state', + validate: { + params: paramSchema, + }, + options: { + tags: ['access:alerting-read'], + }, + }, + router.handleLegacyErrors(async function( + context: RequestHandlerContext, + req: KibanaRequest, any, any, any>, + res: KibanaResponseFactory + ): Promise> { + verifyApiAccess(licenseState); + const alertsClient = context.alerting.getAlertsClient(); + const { id } = req.params; + const state = await alertsClient.getAlertState({ id }); + return state ? res.ok({ body: state }) : res.noContent(); + }) + ); +}; diff --git a/x-pack/legacy/plugins/alerting/server/routes/index.ts b/x-pack/plugins/alerting/server/routes/index.ts similarity index 100% rename from x-pack/legacy/plugins/alerting/server/routes/index.ts rename to x-pack/plugins/alerting/server/routes/index.ts diff --git a/x-pack/plugins/alerting/server/routes/list_alert_types.test.ts b/x-pack/plugins/alerting/server/routes/list_alert_types.test.ts new file mode 100644 index 0000000000000..3e9f57d55122d --- /dev/null +++ b/x-pack/plugins/alerting/server/routes/list_alert_types.test.ts @@ -0,0 +1,147 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { listAlertTypesRoute } from './list_alert_types'; +import { mockRouter, RouterMock } from '../../../../../src/core/server/http/router/router.mock'; +import { mockLicenseState } from '../lib/license_state.mock'; +import { verifyApiAccess } from '../lib/license_api_access'; +import { mockHandlerArguments } from './_mock_handler_arguments'; + +jest.mock('../lib/license_api_access.ts', () => ({ + verifyApiAccess: jest.fn(), +})); + +beforeEach(() => { + jest.resetAllMocks(); +}); + +describe('listAlertTypesRoute', () => { + it('lists alert types with proper parameters', async () => { + const licenseState = mockLicenseState(); + const router: RouterMock = mockRouter.create(); + + listAlertTypesRoute(router, licenseState); + + const [config, handler] = router.get.mock.calls[0]; + + expect(config.path).toMatchInlineSnapshot(`"/api/alert/types"`); + expect(config.options).toMatchInlineSnapshot(` + Object { + "tags": Array [ + "access:alerting-read", + ], + } + `); + + const listTypes = [ + { + id: '1', + name: 'name', + actionGroups: [], + }, + ]; + + const [context, req, res] = mockHandlerArguments({ listTypes }, {}, ['ok']); + + expect(await handler(context, req, res)).toMatchInlineSnapshot(` + Object { + "body": Array [ + Object { + "actionGroups": Array [], + "id": "1", + "name": "name", + }, + ], + } + `); + + expect(context.alerting.listTypes).toHaveBeenCalledTimes(1); + + expect(res.ok).toHaveBeenCalledWith({ + body: listTypes, + }); + }); + + it('ensures the license allows listing alert types', async () => { + const licenseState = mockLicenseState(); + const router: RouterMock = mockRouter.create(); + + listAlertTypesRoute(router, licenseState); + + const [config, handler] = router.get.mock.calls[0]; + + expect(config.path).toMatchInlineSnapshot(`"/api/alert/types"`); + expect(config.options).toMatchInlineSnapshot(` + Object { + "tags": Array [ + "access:alerting-read", + ], + } + `); + + const listTypes = [ + { + id: '1', + name: 'name', + enabled: true, + }, + ]; + + const [context, req, res] = mockHandlerArguments( + { listTypes }, + { + params: { id: '1' }, + }, + ['ok'] + ); + + await handler(context, req, res); + + expect(verifyApiAccess).toHaveBeenCalledWith(licenseState); + }); + + it('ensures the license check prevents listing alert types', async () => { + const licenseState = mockLicenseState(); + const router: RouterMock = mockRouter.create(); + + (verifyApiAccess as jest.Mock).mockImplementation(() => { + throw new Error('OMG'); + }); + + listAlertTypesRoute(router, licenseState); + + const [config, handler] = router.get.mock.calls[0]; + + expect(config.path).toMatchInlineSnapshot(`"/api/alert/types"`); + expect(config.options).toMatchInlineSnapshot(` + Object { + "tags": Array [ + "access:alerting-read", + ], + } + `); + + const listTypes = [ + { + id: '1', + name: 'name', + actionGroups: [], + }, + ]; + + const [context, req, res] = mockHandlerArguments( + { listTypes }, + { + params: { id: '1' }, + }, + ['ok'] + ); + + expect(handler(context, req, res)).rejects.toMatchInlineSnapshot(`[Error: OMG]`); + + expect(verifyApiAccess).toHaveBeenCalledWith(licenseState); + }); +}); diff --git a/x-pack/plugins/alerting/server/routes/list_alert_types.ts b/x-pack/plugins/alerting/server/routes/list_alert_types.ts new file mode 100644 index 0000000000000..e33bb9a010bf7 --- /dev/null +++ b/x-pack/plugins/alerting/server/routes/list_alert_types.ts @@ -0,0 +1,37 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { + IRouter, + RequestHandlerContext, + KibanaRequest, + IKibanaResponse, + KibanaResponseFactory, +} from 'kibana/server'; +import { LicenseState } from '../lib/license_state'; +import { verifyApiAccess } from '../lib/license_api_access'; + +export const listAlertTypesRoute = (router: IRouter, licenseState: LicenseState) => { + router.get( + { + path: `/api/alert/types`, + validate: {}, + options: { + tags: ['access:alerting-read'], + }, + }, + router.handleLegacyErrors(async function( + context: RequestHandlerContext, + req: KibanaRequest, + res: KibanaResponseFactory + ): Promise> { + verifyApiAccess(licenseState); + return res.ok({ + body: context.alerting.listTypes(), + }); + }) + ); +}; diff --git a/x-pack/plugins/alerting/server/routes/mute_all.test.ts b/x-pack/plugins/alerting/server/routes/mute_all.test.ts new file mode 100644 index 0000000000000..4c880e176d2df --- /dev/null +++ b/x-pack/plugins/alerting/server/routes/mute_all.test.ts @@ -0,0 +1,65 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { muteAllAlertRoute } from './mute_all'; +import { mockRouter, RouterMock } from '../../../../../src/core/server/http/router/router.mock'; +import { mockLicenseState } from '../lib/license_state.mock'; +import { mockHandlerArguments } from './_mock_handler_arguments'; +import { alertsClientMock } from '../alerts_client.mock'; + +const alertsClient = alertsClientMock.create(); +jest.mock('../lib/license_api_access.ts', () => ({ + verifyApiAccess: jest.fn(), +})); + +beforeEach(() => { + jest.resetAllMocks(); +}); + +describe('muteAllAlertRoute', () => { + it('mute an alert', async () => { + const licenseState = mockLicenseState(); + const router: RouterMock = mockRouter.create(); + + muteAllAlertRoute(router, licenseState); + + const [config, handler] = router.post.mock.calls[0]; + + expect(config.path).toMatchInlineSnapshot(`"/api/alert/{id}/_mute_all"`); + expect(config.options).toMatchInlineSnapshot(` + Object { + "tags": Array [ + "access:alerting-all", + ], + } + `); + + alertsClient.muteAll.mockResolvedValueOnce(); + + const [context, req, res] = mockHandlerArguments( + { alertsClient }, + { + params: { + id: '1', + }, + }, + ['noContent'] + ); + + expect(await handler(context, req, res)).toEqual(undefined); + + expect(alertsClient.muteAll).toHaveBeenCalledTimes(1); + expect(alertsClient.muteAll.mock.calls[0]).toMatchInlineSnapshot(` + Array [ + Object { + "id": "1", + }, + ] + `); + + expect(res.noContent).toHaveBeenCalled(); + }); +}); diff --git a/x-pack/plugins/alerting/server/routes/mute_all.ts b/x-pack/plugins/alerting/server/routes/mute_all.ts new file mode 100644 index 0000000000000..796efd457f478 --- /dev/null +++ b/x-pack/plugins/alerting/server/routes/mute_all.ts @@ -0,0 +1,45 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { schema, TypeOf } from '@kbn/config-schema'; +import { + IRouter, + RequestHandlerContext, + KibanaRequest, + IKibanaResponse, + KibanaResponseFactory, +} from 'kibana/server'; +import { LicenseState } from '../lib/license_state'; +import { verifyApiAccess } from '../lib/license_api_access'; + +const paramSchema = schema.object({ + id: schema.string(), +}); + +export const muteAllAlertRoute = (router: IRouter, licenseState: LicenseState) => { + router.post( + { + path: '/api/alert/{id}/_mute_all', + validate: { + params: paramSchema, + }, + options: { + tags: ['access:alerting-all'], + }, + }, + router.handleLegacyErrors(async function( + context: RequestHandlerContext, + req: KibanaRequest, any, any, any>, + res: KibanaResponseFactory + ): Promise> { + verifyApiAccess(licenseState); + const alertsClient = context.alerting.getAlertsClient(); + const { id } = req.params; + await alertsClient.muteAll({ id }); + return res.noContent(); + }) + ); +}; diff --git a/x-pack/plugins/alerting/server/routes/mute_instance.test.ts b/x-pack/plugins/alerting/server/routes/mute_instance.test.ts new file mode 100644 index 0000000000000..939864972c35d --- /dev/null +++ b/x-pack/plugins/alerting/server/routes/mute_instance.test.ts @@ -0,0 +1,69 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { muteAlertInstanceRoute } from './mute_instance'; +import { mockRouter, RouterMock } from '../../../../../src/core/server/http/router/router.mock'; +import { mockLicenseState } from '../lib/license_state.mock'; +import { mockHandlerArguments } from './_mock_handler_arguments'; +import { alertsClientMock } from '../alerts_client.mock'; + +const alertsClient = alertsClientMock.create(); +jest.mock('../lib/license_api_access.ts', () => ({ + verifyApiAccess: jest.fn(), +})); + +beforeEach(() => { + jest.resetAllMocks(); +}); + +describe('muteAlertInstanceRoute', () => { + it('mutes an alert instance', async () => { + const licenseState = mockLicenseState(); + const router: RouterMock = mockRouter.create(); + + muteAlertInstanceRoute(router, licenseState); + + const [config, handler] = router.post.mock.calls[0]; + + expect(config.path).toMatchInlineSnapshot( + `"/api/alert/{alertId}/alert_instance/{alertInstanceId}/_mute"` + ); + expect(config.options).toMatchInlineSnapshot(` + Object { + "tags": Array [ + "access:alerting-all", + ], + } + `); + + alertsClient.muteInstance.mockResolvedValueOnce(); + + const [context, req, res] = mockHandlerArguments( + { alertsClient }, + { + params: { + alertId: '1', + alertInstanceId: '2', + }, + }, + ['noContent'] + ); + + expect(await handler(context, req, res)).toEqual(undefined); + + expect(alertsClient.muteInstance).toHaveBeenCalledTimes(1); + expect(alertsClient.muteInstance.mock.calls[0]).toMatchInlineSnapshot(` + Array [ + Object { + "alertId": "1", + "alertInstanceId": "2", + }, + ] + `); + + expect(res.noContent).toHaveBeenCalled(); + }); +}); diff --git a/x-pack/plugins/alerting/server/routes/mute_instance.ts b/x-pack/plugins/alerting/server/routes/mute_instance.ts new file mode 100644 index 0000000000000..bae7b00548a26 --- /dev/null +++ b/x-pack/plugins/alerting/server/routes/mute_instance.ts @@ -0,0 +1,46 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { schema, TypeOf } from '@kbn/config-schema'; +import { + IRouter, + RequestHandlerContext, + KibanaRequest, + IKibanaResponse, + KibanaResponseFactory, +} from 'kibana/server'; +import { LicenseState } from '../lib/license_state'; +import { verifyApiAccess } from '../lib/license_api_access'; + +const paramSchema = schema.object({ + alertId: schema.string(), + alertInstanceId: schema.string(), +}); + +export const muteAlertInstanceRoute = (router: IRouter, licenseState: LicenseState) => { + router.post( + { + path: '/api/alert/{alertId}/alert_instance/{alertInstanceId}/_mute', + validate: { + params: paramSchema, + }, + options: { + tags: ['access:alerting-all'], + }, + }, + router.handleLegacyErrors(async function( + context: RequestHandlerContext, + req: KibanaRequest, any, any, any>, + res: KibanaResponseFactory + ): Promise> { + verifyApiAccess(licenseState); + const alertsClient = context.alerting.getAlertsClient(); + const { alertId, alertInstanceId } = req.params; + await alertsClient.muteInstance({ alertId, alertInstanceId }); + return res.noContent(); + }) + ); +}; diff --git a/x-pack/plugins/alerting/server/routes/unmute_all.test.ts b/x-pack/plugins/alerting/server/routes/unmute_all.test.ts new file mode 100644 index 0000000000000..cd14e9b2e7172 --- /dev/null +++ b/x-pack/plugins/alerting/server/routes/unmute_all.test.ts @@ -0,0 +1,64 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { unmuteAllAlertRoute } from './unmute_all'; +import { mockRouter, RouterMock } from '../../../../../src/core/server/http/router/router.mock'; +import { mockLicenseState } from '../lib/license_state.mock'; +import { mockHandlerArguments } from './_mock_handler_arguments'; +import { alertsClientMock } from '../alerts_client.mock'; + +const alertsClient = alertsClientMock.create(); +jest.mock('../lib/license_api_access.ts', () => ({ + verifyApiAccess: jest.fn(), +})); + +beforeEach(() => { + jest.resetAllMocks(); +}); + +describe('unmuteAllAlertRoute', () => { + it('unmutes an alert', async () => { + const licenseState = mockLicenseState(); + const router: RouterMock = mockRouter.create(); + + unmuteAllAlertRoute(router, licenseState); + + const [config, handler] = router.post.mock.calls[0]; + + expect(config.path).toMatchInlineSnapshot(`"/api/alert/{id}/_unmute_all"`); + expect(config.options).toMatchInlineSnapshot(` + Object { + "tags": Array [ + "access:alerting-all", + ], + } + `); + + alertsClient.unmuteAll.mockResolvedValueOnce(); + + const [context, req, res] = mockHandlerArguments( + { alertsClient }, + { + params: { + id: '1', + }, + }, + ['noContent'] + ); + + expect(await handler(context, req, res)).toEqual(undefined); + + expect(alertsClient.unmuteAll).toHaveBeenCalledTimes(1); + expect(alertsClient.unmuteAll.mock.calls[0]).toMatchInlineSnapshot(` + Array [ + Object { + "id": "1", + }, + ] + `); + + expect(res.noContent).toHaveBeenCalled(); + }); +}); diff --git a/x-pack/plugins/alerting/server/routes/unmute_all.ts b/x-pack/plugins/alerting/server/routes/unmute_all.ts new file mode 100644 index 0000000000000..5483f691b5462 --- /dev/null +++ b/x-pack/plugins/alerting/server/routes/unmute_all.ts @@ -0,0 +1,45 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { schema, TypeOf } from '@kbn/config-schema'; +import { + IRouter, + RequestHandlerContext, + KibanaRequest, + IKibanaResponse, + KibanaResponseFactory, +} from 'kibana/server'; +import { LicenseState } from '../lib/license_state'; +import { verifyApiAccess } from '../lib/license_api_access'; + +const paramSchema = schema.object({ + id: schema.string(), +}); + +export const unmuteAllAlertRoute = (router: IRouter, licenseState: LicenseState) => { + router.post( + { + path: '/api/alert/{id}/_unmute_all', + validate: { + params: paramSchema, + }, + options: { + tags: ['access:alerting-all'], + }, + }, + router.handleLegacyErrors(async function( + context: RequestHandlerContext, + req: KibanaRequest, any, any, any>, + res: KibanaResponseFactory + ): Promise> { + verifyApiAccess(licenseState); + const alertsClient = context.alerting.getAlertsClient(); + const { id } = req.params; + await alertsClient.unmuteAll({ id }); + return res.noContent(); + }) + ); +}; diff --git a/x-pack/plugins/alerting/server/routes/unmute_instance.test.ts b/x-pack/plugins/alerting/server/routes/unmute_instance.test.ts new file mode 100644 index 0000000000000..d74934f691710 --- /dev/null +++ b/x-pack/plugins/alerting/server/routes/unmute_instance.test.ts @@ -0,0 +1,69 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { unmuteAlertInstanceRoute } from './unmute_instance'; +import { mockRouter, RouterMock } from '../../../../../src/core/server/http/router/router.mock'; +import { mockLicenseState } from '../lib/license_state.mock'; +import { mockHandlerArguments } from './_mock_handler_arguments'; +import { alertsClientMock } from '../alerts_client.mock'; + +const alertsClient = alertsClientMock.create(); +jest.mock('../lib/license_api_access.ts', () => ({ + verifyApiAccess: jest.fn(), +})); + +beforeEach(() => { + jest.resetAllMocks(); +}); + +describe('unmuteAlertInstanceRoute', () => { + it('unmutes an alert instance', async () => { + const licenseState = mockLicenseState(); + const router: RouterMock = mockRouter.create(); + + unmuteAlertInstanceRoute(router, licenseState); + + const [config, handler] = router.post.mock.calls[0]; + + expect(config.path).toMatchInlineSnapshot( + `"/api/alert/{alertId}/alert_instance/{alertInstanceId}/_unmute"` + ); + expect(config.options).toMatchInlineSnapshot(` + Object { + "tags": Array [ + "access:alerting-all", + ], + } + `); + + alertsClient.unmuteInstance.mockResolvedValueOnce(); + + const [context, req, res] = mockHandlerArguments( + { alertsClient }, + { + params: { + alertId: '1', + alertInstanceId: '2', + }, + }, + ['noContent'] + ); + + expect(await handler(context, req, res)).toEqual(undefined); + + expect(alertsClient.unmuteInstance).toHaveBeenCalledTimes(1); + expect(alertsClient.unmuteInstance.mock.calls[0]).toMatchInlineSnapshot(` + Array [ + Object { + "alertId": "1", + "alertInstanceId": "2", + }, + ] + `); + + expect(res.noContent).toHaveBeenCalled(); + }); +}); diff --git a/x-pack/plugins/alerting/server/routes/unmute_instance.ts b/x-pack/plugins/alerting/server/routes/unmute_instance.ts new file mode 100644 index 0000000000000..fc24ea88ddb67 --- /dev/null +++ b/x-pack/plugins/alerting/server/routes/unmute_instance.ts @@ -0,0 +1,46 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { schema, TypeOf } from '@kbn/config-schema'; +import { + IRouter, + RequestHandlerContext, + KibanaRequest, + IKibanaResponse, + KibanaResponseFactory, +} from 'kibana/server'; +import { LicenseState } from '../lib/license_state'; +import { verifyApiAccess } from '../lib/license_api_access'; + +const paramSchema = schema.object({ + alertId: schema.string(), + alertInstanceId: schema.string(), +}); + +export const unmuteAlertInstanceRoute = (router: IRouter, licenseState: LicenseState) => { + router.post( + { + path: '/api/alert/{alertId}/alert_instance/{alertInstanceId}/_unmute', + validate: { + params: paramSchema, + }, + options: { + tags: ['access:alerting-all'], + }, + }, + router.handleLegacyErrors(async function( + context: RequestHandlerContext, + req: KibanaRequest, any, any, any>, + res: KibanaResponseFactory + ): Promise> { + verifyApiAccess(licenseState); + const alertsClient = context.alerting.getAlertsClient(); + const { alertId, alertInstanceId } = req.params; + await alertsClient.unmuteInstance({ alertId, alertInstanceId }); + return res.noContent(); + }) + ); +}; diff --git a/x-pack/plugins/alerting/server/routes/update.test.ts b/x-pack/plugins/alerting/server/routes/update.test.ts new file mode 100644 index 0000000000000..005367ef7979c --- /dev/null +++ b/x-pack/plugins/alerting/server/routes/update.test.ts @@ -0,0 +1,217 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { updateAlertRoute } from './update'; +import { mockRouter, RouterMock } from '../../../../../src/core/server/http/router/router.mock'; +import { mockLicenseState } from '../lib/license_state.mock'; +import { verifyApiAccess } from '../lib/license_api_access'; +import { mockHandlerArguments } from './_mock_handler_arguments'; +import { alertsClientMock } from '../alerts_client.mock'; + +const alertsClient = alertsClientMock.create(); +jest.mock('../lib/license_api_access.ts', () => ({ + verifyApiAccess: jest.fn(), +})); + +beforeEach(() => { + jest.resetAllMocks(); +}); + +describe('updateAlertRoute', () => { + const mockedResponse = { + id: '1', + alertTypeId: '1', + tags: ['foo'], + schedule: { interval: '12s' }, + params: { + otherField: false, + }, + createdAt: new Date(), + updatedAt: new Date(), + actions: [ + { + group: 'default', + id: '2', + actionTypeId: 'test', + params: { + baz: true, + }, + }, + ], + }; + + it('updates an alert with proper parameters', async () => { + const licenseState = mockLicenseState(); + const router: RouterMock = mockRouter.create(); + + updateAlertRoute(router, licenseState); + + const [config, handler] = router.put.mock.calls[0]; + + expect(config.path).toMatchInlineSnapshot(`"/api/alert/{id}"`); + expect(config.options).toMatchInlineSnapshot(` + Object { + "tags": Array [ + "access:alerting-all", + ], + } + `); + + alertsClient.update.mockResolvedValueOnce(mockedResponse); + + const [context, req, res] = mockHandlerArguments( + { alertsClient }, + { + params: { + id: '1', + }, + body: { + throttle: null, + name: 'abc', + tags: ['bar'], + schedule: { interval: '12s' }, + params: { + otherField: false, + }, + actions: [ + { + group: 'default', + id: '2', + params: { + baz: true, + }, + }, + ], + }, + }, + ['ok'] + ); + + expect(await handler(context, req, res)).toEqual({ body: mockedResponse }); + + expect(alertsClient.update).toHaveBeenCalledTimes(1); + expect(alertsClient.update.mock.calls[0]).toMatchInlineSnapshot(` + Array [ + Object { + "data": Object { + "actions": Array [ + Object { + "group": "default", + "id": "2", + "params": Object { + "baz": true, + }, + }, + ], + "name": "abc", + "params": Object { + "otherField": false, + }, + "schedule": Object { + "interval": "12s", + }, + "tags": Array [ + "bar", + ], + }, + "id": "1", + }, + ] + `); + + expect(res.ok).toHaveBeenCalled(); + }); + + it('ensures the license allows updating alerts', async () => { + const licenseState = mockLicenseState(); + const router: RouterMock = mockRouter.create(); + + updateAlertRoute(router, licenseState); + + const [, handler] = router.put.mock.calls[0]; + + alertsClient.update.mockResolvedValueOnce(mockedResponse); + + const [context, req, res] = mockHandlerArguments( + { alertsClient }, + { + params: { + id: '1', + }, + body: { + throttle: null, + name: 'abc', + tags: ['bar'], + schedule: { interval: '12s' }, + params: { + otherField: false, + }, + actions: [ + { + group: 'default', + id: '2', + params: { + baz: true, + }, + }, + ], + }, + }, + ['ok'] + ); + + await handler(context, req, res); + + expect(verifyApiAccess).toHaveBeenCalledWith(licenseState); + }); + + it('ensures the license check prevents updating alerts', async () => { + const licenseState = mockLicenseState(); + const router: RouterMock = mockRouter.create(); + + (verifyApiAccess as jest.Mock).mockImplementation(() => { + throw new Error('OMG'); + }); + + updateAlertRoute(router, licenseState); + + const [, handler] = router.put.mock.calls[0]; + + alertsClient.update.mockResolvedValueOnce(mockedResponse); + + const [context, req, res] = mockHandlerArguments( + { alertsClient }, + { + params: { + id: '1', + }, + body: { + throttle: null, + name: 'abc', + tags: ['bar'], + schedule: { interval: '12s' }, + params: { + otherField: false, + }, + actions: [ + { + group: 'default', + id: '2', + params: { + baz: true, + }, + }, + ], + }, + }, + ['ok'] + ); + + expect(handler(context, req, res)).rejects.toMatchInlineSnapshot(`[Error: OMG]`); + + expect(verifyApiAccess).toHaveBeenCalledWith(licenseState); + }); +}); diff --git a/x-pack/plugins/alerting/server/routes/update.ts b/x-pack/plugins/alerting/server/routes/update.ts new file mode 100644 index 0000000000000..a402d13c5fbab --- /dev/null +++ b/x-pack/plugins/alerting/server/routes/update.ts @@ -0,0 +1,71 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { schema, TypeOf } from '@kbn/config-schema'; +import { + IRouter, + RequestHandlerContext, + KibanaRequest, + IKibanaResponse, + KibanaResponseFactory, +} from 'kibana/server'; +import { LicenseState } from '../lib/license_state'; +import { verifyApiAccess } from '../lib/license_api_access'; +import { validateDurationSchema } from '../lib'; + +const paramSchema = schema.object({ + id: schema.string(), +}); + +const bodySchema = schema.object({ + name: schema.string(), + tags: schema.arrayOf(schema.string(), { defaultValue: [] }), + schedule: schema.object({ + interval: schema.string({ validate: validateDurationSchema }), + }), + throttle: schema.nullable(schema.string({ validate: validateDurationSchema })), + params: schema.recordOf(schema.string(), schema.any(), { defaultValue: {} }), + actions: schema.arrayOf( + schema.object({ + group: schema.string(), + id: schema.string(), + params: schema.recordOf(schema.string(), schema.any(), { defaultValue: {} }), + actionTypeId: schema.maybe(schema.string()), + }), + { defaultValue: [] } + ), +}); + +export const updateAlertRoute = (router: IRouter, licenseState: LicenseState) => { + router.put( + { + path: '/api/alert/{id}', + validate: { + body: bodySchema, + params: paramSchema, + }, + options: { + tags: ['access:alerting-all'], + }, + }, + router.handleLegacyErrors(async function( + context: RequestHandlerContext, + req: KibanaRequest, any, TypeOf, any>, + res: KibanaResponseFactory + ): Promise> { + verifyApiAccess(licenseState); + const alertsClient = context.alerting.getAlertsClient(); + const { id } = req.params; + const { name, actions, params, schedule, tags } = req.body; + return res.ok({ + body: await alertsClient.update({ + id, + data: { name, actions, params, schedule, tags }, + }), + }); + }) + ); +}; diff --git a/x-pack/plugins/alerting/server/routes/update_api_key.test.ts b/x-pack/plugins/alerting/server/routes/update_api_key.test.ts new file mode 100644 index 0000000000000..5e9821ac005e2 --- /dev/null +++ b/x-pack/plugins/alerting/server/routes/update_api_key.test.ts @@ -0,0 +1,65 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { updateApiKeyRoute } from './update_api_key'; +import { mockRouter, RouterMock } from '../../../../../src/core/server/http/router/router.mock'; +import { mockLicenseState } from '../lib/license_state.mock'; +import { mockHandlerArguments } from './_mock_handler_arguments'; +import { alertsClientMock } from '../alerts_client.mock'; + +const alertsClient = alertsClientMock.create(); +jest.mock('../lib/license_api_access.ts', () => ({ + verifyApiAccess: jest.fn(), +})); + +beforeEach(() => { + jest.resetAllMocks(); +}); + +describe('updateApiKeyRoute', () => { + it('updates api key for an alert', async () => { + const licenseState = mockLicenseState(); + const router: RouterMock = mockRouter.create(); + + updateApiKeyRoute(router, licenseState); + + const [config, handler] = router.post.mock.calls[0]; + + expect(config.path).toMatchInlineSnapshot(`"/api/alert/{id}/_update_api_key"`); + expect(config.options).toMatchInlineSnapshot(` + Object { + "tags": Array [ + "access:alerting-all", + ], + } + `); + + alertsClient.updateApiKey.mockResolvedValueOnce(); + + const [context, req, res] = mockHandlerArguments( + { alertsClient }, + { + params: { + id: '1', + }, + }, + ['noContent'] + ); + + expect(await handler(context, req, res)).toEqual(undefined); + + expect(alertsClient.updateApiKey).toHaveBeenCalledTimes(1); + expect(alertsClient.updateApiKey.mock.calls[0]).toMatchInlineSnapshot(` + Array [ + Object { + "id": "1", + }, + ] + `); + + expect(res.noContent).toHaveBeenCalled(); + }); +}); diff --git a/x-pack/plugins/alerting/server/routes/update_api_key.ts b/x-pack/plugins/alerting/server/routes/update_api_key.ts new file mode 100644 index 0000000000000..0951b6c7b939e --- /dev/null +++ b/x-pack/plugins/alerting/server/routes/update_api_key.ts @@ -0,0 +1,45 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { schema, TypeOf } from '@kbn/config-schema'; +import { + IRouter, + RequestHandlerContext, + KibanaRequest, + IKibanaResponse, + KibanaResponseFactory, +} from 'kibana/server'; +import { LicenseState } from '../lib/license_state'; +import { verifyApiAccess } from '../lib/license_api_access'; + +const paramSchema = schema.object({ + id: schema.string(), +}); + +export const updateApiKeyRoute = (router: IRouter, licenseState: LicenseState) => { + router.post( + { + path: '/api/alert/{id}/_update_api_key', + validate: { + params: paramSchema, + }, + options: { + tags: ['access:alerting-all'], + }, + }, + router.handleLegacyErrors(async function( + context: RequestHandlerContext, + req: KibanaRequest, any, any, any>, + res: KibanaResponseFactory + ): Promise> { + verifyApiAccess(licenseState); + const alertsClient = context.alerting.getAlertsClient(); + const { id } = req.params; + await alertsClient.updateApiKey({ id }); + return res.noContent(); + }) + ); +}; diff --git a/x-pack/legacy/plugins/alerting/server/task_runner/alert_task_instance.test.ts b/x-pack/plugins/alerting/server/task_runner/alert_task_instance.test.ts similarity index 98% rename from x-pack/legacy/plugins/alerting/server/task_runner/alert_task_instance.test.ts rename to x-pack/plugins/alerting/server/task_runner/alert_task_instance.test.ts index 9cbe91a4dbced..28b3576dffc6e 100644 --- a/x-pack/legacy/plugins/alerting/server/task_runner/alert_task_instance.test.ts +++ b/x-pack/plugins/alerting/server/task_runner/alert_task_instance.test.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { ConcreteTaskInstance, TaskStatus } from '../../../../../plugins/task_manager/server'; +import { ConcreteTaskInstance, TaskStatus } from '../../../../plugins/task_manager/server'; import { AlertTaskInstance, taskInstanceToAlertTaskInstance } from './alert_task_instance'; import uuid from 'uuid'; import { SanitizedAlert } from '../types'; diff --git a/x-pack/legacy/plugins/alerting/server/task_runner/alert_task_instance.ts b/x-pack/plugins/alerting/server/task_runner/alert_task_instance.ts similarity index 94% rename from x-pack/legacy/plugins/alerting/server/task_runner/alert_task_instance.ts rename to x-pack/plugins/alerting/server/task_runner/alert_task_instance.ts index 6bc318070377d..9f5e3851aa29d 100644 --- a/x-pack/legacy/plugins/alerting/server/task_runner/alert_task_instance.ts +++ b/x-pack/plugins/alerting/server/task_runner/alert_task_instance.ts @@ -6,7 +6,7 @@ import * as t from 'io-ts'; import { pipe } from 'fp-ts/lib/pipeable'; import { fold } from 'fp-ts/lib/Either'; -import { ConcreteTaskInstance } from '../../../../../plugins/task_manager/server'; +import { ConcreteTaskInstance } from '../../../../plugins/task_manager/server'; import { SanitizedAlert, AlertTaskState, alertParamsSchema, alertStateSchema } from '../../common'; export interface AlertTaskInstance extends ConcreteTaskInstance { diff --git a/x-pack/legacy/plugins/alerting/server/task_runner/create_execution_handler.test.ts b/x-pack/plugins/alerting/server/task_runner/create_execution_handler.test.ts similarity index 98% rename from x-pack/legacy/plugins/alerting/server/task_runner/create_execution_handler.test.ts rename to x-pack/plugins/alerting/server/task_runner/create_execution_handler.test.ts index 02fa09ba97a65..1b3a9ed8d7b06 100644 --- a/x-pack/legacy/plugins/alerting/server/task_runner/create_execution_handler.test.ts +++ b/x-pack/plugins/alerting/server/task_runner/create_execution_handler.test.ts @@ -6,7 +6,7 @@ import { AlertType } from '../types'; import { createExecutionHandler } from './create_execution_handler'; -import { loggingServiceMock } from '../../../../../../src/core/server/mocks'; +import { loggingServiceMock } from '../../../../../src/core/server/mocks'; const alertType: AlertType = { id: 'test', diff --git a/x-pack/legacy/plugins/alerting/server/task_runner/create_execution_handler.ts b/x-pack/plugins/alerting/server/task_runner/create_execution_handler.ts similarity index 94% rename from x-pack/legacy/plugins/alerting/server/task_runner/create_execution_handler.ts rename to x-pack/plugins/alerting/server/task_runner/create_execution_handler.ts index 737f86a881c1f..9b2d218114c31 100644 --- a/x-pack/legacy/plugins/alerting/server/task_runner/create_execution_handler.ts +++ b/x-pack/plugins/alerting/server/task_runner/create_execution_handler.ts @@ -6,9 +6,9 @@ import { pluck } from 'lodash'; import { AlertAction, State, Context, AlertType } from '../types'; -import { Logger } from '../../../../../../src/core/server'; +import { Logger } from '../../../../../src/core/server'; import { transformActionParams } from './transform_action_params'; -import { PluginStartContract as ActionsPluginStartContract } from '../../../../../plugins/actions/server'; +import { PluginStartContract as ActionsPluginStartContract } from '../../../../plugins/actions/server'; interface CreateExecutionHandlerOptions { alertId: string; diff --git a/x-pack/legacy/plugins/alerting/server/task_runner/get_next_run_at.test.ts b/x-pack/plugins/alerting/server/task_runner/get_next_run_at.test.ts similarity index 100% rename from x-pack/legacy/plugins/alerting/server/task_runner/get_next_run_at.test.ts rename to x-pack/plugins/alerting/server/task_runner/get_next_run_at.test.ts diff --git a/x-pack/legacy/plugins/alerting/server/task_runner/get_next_run_at.ts b/x-pack/plugins/alerting/server/task_runner/get_next_run_at.ts similarity index 100% rename from x-pack/legacy/plugins/alerting/server/task_runner/get_next_run_at.ts rename to x-pack/plugins/alerting/server/task_runner/get_next_run_at.ts diff --git a/x-pack/legacy/plugins/alerting/server/task_runner/index.ts b/x-pack/plugins/alerting/server/task_runner/index.ts similarity index 100% rename from x-pack/legacy/plugins/alerting/server/task_runner/index.ts rename to x-pack/plugins/alerting/server/task_runner/index.ts diff --git a/x-pack/legacy/plugins/alerting/server/task_runner/task_runner.test.ts b/x-pack/plugins/alerting/server/task_runner/task_runner.test.ts similarity index 97% rename from x-pack/legacy/plugins/alerting/server/task_runner/task_runner.test.ts rename to x-pack/plugins/alerting/server/task_runner/task_runner.test.ts index b6dd4b3435fcb..af0e7b658240a 100644 --- a/x-pack/legacy/plugins/alerting/server/task_runner/task_runner.test.ts +++ b/x-pack/plugins/alerting/server/task_runner/task_runner.test.ts @@ -7,14 +7,11 @@ import sinon from 'sinon'; import { schema } from '@kbn/config-schema'; import { AlertExecutorOptions } from '../types'; -import { ConcreteTaskInstance, TaskStatus } from '../../../../../plugins/task_manager/server'; +import { ConcreteTaskInstance, TaskStatus } from '../../../../plugins/task_manager/server'; import { TaskRunnerContext } from './task_runner_factory'; import { TaskRunner } from './task_runner'; -import { encryptedSavedObjectsMock } from '../../../../../plugins/encrypted_saved_objects/server/mocks'; -import { - savedObjectsClientMock, - loggingServiceMock, -} from '../../../../../../src/core/server/mocks'; +import { encryptedSavedObjectsMock } from '../../../../plugins/encrypted_saved_objects/server/mocks'; +import { savedObjectsClientMock, loggingServiceMock } from '../../../../../src/core/server/mocks'; const alertType = { id: 'test', diff --git a/x-pack/legacy/plugins/alerting/server/task_runner/task_runner.ts b/x-pack/plugins/alerting/server/task_runner/task_runner.ts similarity index 97% rename from x-pack/legacy/plugins/alerting/server/task_runner/task_runner.ts rename to x-pack/plugins/alerting/server/task_runner/task_runner.ts index 2632decb125f0..34278ee62dbc4 100644 --- a/x-pack/legacy/plugins/alerting/server/task_runner/task_runner.ts +++ b/x-pack/plugins/alerting/server/task_runner/task_runner.ts @@ -5,10 +5,10 @@ */ import { pick, mapValues, omit } from 'lodash'; -import { Logger } from '../../../../../../src/core/server'; -import { SavedObject } from '../../../../../../src/core/server'; +import { Logger } from '../../../../../src/core/server'; +import { SavedObject } from '../../../../../src/core/server'; import { TaskRunnerContext } from './task_runner_factory'; -import { ConcreteTaskInstance } from '../../../../../plugins/task_manager/server'; +import { ConcreteTaskInstance } from '../../../../plugins/task_manager/server'; import { createExecutionHandler } from './create_execution_handler'; import { AlertInstance, createAlertInstanceFactory } from '../alert_instance'; import { getNextRunAt } from './get_next_run_at'; diff --git a/x-pack/legacy/plugins/alerting/server/task_runner/task_runner_factory.test.ts b/x-pack/plugins/alerting/server/task_runner/task_runner_factory.test.ts similarity index 89% rename from x-pack/legacy/plugins/alerting/server/task_runner/task_runner_factory.test.ts rename to x-pack/plugins/alerting/server/task_runner/task_runner_factory.test.ts index 7474fcfb4baaa..4dad46ef32f21 100644 --- a/x-pack/legacy/plugins/alerting/server/task_runner/task_runner_factory.test.ts +++ b/x-pack/plugins/alerting/server/task_runner/task_runner_factory.test.ts @@ -5,13 +5,10 @@ */ import sinon from 'sinon'; -import { ConcreteTaskInstance, TaskStatus } from '../../../../../plugins/task_manager/server'; +import { ConcreteTaskInstance, TaskStatus } from '../../../../plugins/task_manager/server'; import { TaskRunnerContext, TaskRunnerFactory } from './task_runner_factory'; -import { encryptedSavedObjectsMock } from '../../../../../plugins/encrypted_saved_objects/server/mocks'; -import { - savedObjectsClientMock, - loggingServiceMock, -} from '../../../../../../src/core/server/mocks'; +import { encryptedSavedObjectsMock } from '../../../../plugins/encrypted_saved_objects/server/mocks'; +import { savedObjectsClientMock, loggingServiceMock } from '../../../../../src/core/server/mocks'; const alertType = { id: 'test', diff --git a/x-pack/legacy/plugins/alerting/server/task_runner/task_runner_factory.ts b/x-pack/plugins/alerting/server/task_runner/task_runner_factory.ts similarity index 83% rename from x-pack/legacy/plugins/alerting/server/task_runner/task_runner_factory.ts rename to x-pack/plugins/alerting/server/task_runner/task_runner_factory.ts index d2ecfb64c8a81..c598b0f52f197 100644 --- a/x-pack/legacy/plugins/alerting/server/task_runner/task_runner_factory.ts +++ b/x-pack/plugins/alerting/server/task_runner/task_runner_factory.ts @@ -3,10 +3,10 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -import { Logger } from '../../../../../../src/core/server'; -import { RunContext } from '../../../../../plugins/task_manager/server'; -import { EncryptedSavedObjectsPluginStart } from '../../../../../plugins/encrypted_saved_objects/server'; -import { PluginStartContract as ActionsPluginStartContract } from '../../../../../plugins/actions/server'; +import { Logger } from '../../../../../src/core/server'; +import { RunContext } from '../../../../plugins/task_manager/server'; +import { EncryptedSavedObjectsPluginStart } from '../../../../plugins/encrypted_saved_objects/server'; +import { PluginStartContract as ActionsPluginStartContract } from '../../../../plugins/actions/server'; import { AlertType, GetBasePathFunction, diff --git a/x-pack/legacy/plugins/alerting/server/task_runner/transform_action_params.test.ts b/x-pack/plugins/alerting/server/task_runner/transform_action_params.test.ts similarity index 100% rename from x-pack/legacy/plugins/alerting/server/task_runner/transform_action_params.test.ts rename to x-pack/plugins/alerting/server/task_runner/transform_action_params.test.ts diff --git a/x-pack/legacy/plugins/alerting/server/task_runner/transform_action_params.ts b/x-pack/plugins/alerting/server/task_runner/transform_action_params.ts similarity index 100% rename from x-pack/legacy/plugins/alerting/server/task_runner/transform_action_params.ts rename to x-pack/plugins/alerting/server/task_runner/transform_action_params.ts diff --git a/x-pack/legacy/plugins/alerting/server/test_utils/index.ts b/x-pack/plugins/alerting/server/test_utils/index.ts similarity index 100% rename from x-pack/legacy/plugins/alerting/server/test_utils/index.ts rename to x-pack/plugins/alerting/server/test_utils/index.ts diff --git a/x-pack/legacy/plugins/alerting/server/types.ts b/x-pack/plugins/alerting/server/types.ts similarity index 91% rename from x-pack/legacy/plugins/alerting/server/types.ts rename to x-pack/plugins/alerting/server/types.ts index 95a96fa384c2c..202a73f076859 100644 --- a/x-pack/legacy/plugins/alerting/server/types.ts +++ b/x-pack/plugins/alerting/server/types.ts @@ -7,8 +7,9 @@ import { AlertInstance } from './alert_instance'; import { AlertTypeRegistry as OrigAlertTypeRegistry } from './alert_type_registry'; import { PluginSetupContract, PluginStartContract } from './plugin'; -import { SavedObjectAttributes, SavedObjectsClientContract } from '../../../../../src/core/server'; +import { SavedObjectAttributes, SavedObjectsClientContract } from '../../../../src/core/server'; import { Alert, AlertActionParams } from '../common'; +import { AlertsClient } from './alerts_client'; export * from '../common'; export type State = Record; @@ -18,6 +19,15 @@ export type GetServicesFunction = (request: any) => Services; export type GetBasePathFunction = (spaceId?: string) => string; export type SpaceIdToNamespaceFunction = (spaceId?: string) => string | undefined; +declare module 'src/core/server' { + interface RequestHandlerContext { + alerting: { + getAlertsClient: () => AlertsClient; + listTypes: AlertTypeRegistry['list']; + }; + } +} + export interface Services { callCluster(path: string, opts: any): Promise; savedObjectsClient: SavedObjectsClientContract; diff --git a/x-pack/plugins/endpoint/common/types.ts b/x-pack/plugins/endpoint/common/types.ts index 5ef9d22e4dd7b..6fc4d8d79a1c5 100644 --- a/x-pack/plugins/endpoint/common/types.ts +++ b/x-pack/plugins/endpoint/common/types.ts @@ -101,6 +101,7 @@ export interface EndpointMetadata { agent: { version: string; id: string; + name: string; }; host: { id: string; @@ -111,6 +112,7 @@ export interface EndpointMetadata { name: string; full: string; version: string; + variant: string; }; }; } diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/managing/index.test.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/managing/index.test.ts index dde0ba1e96a8a..56a606f430d9e 100644 --- a/x-pack/plugins/endpoint/public/applications/endpoint/store/managing/index.test.ts +++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/managing/index.test.ts @@ -30,6 +30,7 @@ describe('endpoint_list store concerns', () => { agent: { version: '', id: '', + name: '', }, host: { id: '', @@ -40,6 +41,7 @@ describe('endpoint_list store concerns', () => { name: '', full: '', version: '', + variant: '', }, }, }; diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/managing/middleware.test.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/managing/middleware.test.ts index 095e49a6c4306..250cbc6e312ed 100644 --- a/x-pack/plugins/endpoint/public/applications/endpoint/store/managing/middleware.test.ts +++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/managing/middleware.test.ts @@ -32,6 +32,7 @@ describe('endpoint list saga', () => { agent: { version: '', id: '', + name: '', }, host: { id: '', @@ -42,6 +43,7 @@ describe('endpoint list saga', () => { name: '', full: '', version: '', + variant: '', }, }, }; diff --git a/x-pack/plugins/endpoint/server/test_data/all_endpoints_data.json b/x-pack/plugins/endpoint/server/test_data/all_endpoints_data.json index f1ad5190c55ff..3c824185ec083 100644 --- a/x-pack/plugins/endpoint/server/test_data/all_endpoints_data.json +++ b/x-pack/plugins/endpoint/server/test_data/all_endpoints_data.json @@ -30,7 +30,8 @@ }, "agent" : { "version" : "6.8.3", - "id" : "56a75650-3c8a-4e4f-ac17-6dd729c650e2" + "id" : "56a75650-3c8a-4e4f-ac17-6dd729c650e2", + "name": "Elastic Endpoint" }, "host" : { "id" : "7141a48b-e19f-4ae3-89a0-6e7179a84265", @@ -41,7 +42,8 @@ "os" : { "name" : "windows 6.2", "full" : "Windows Server 2012", - "version" : "6.2" + "version" : "6.2", + "variant" : "Windows Server" } } }, @@ -78,7 +80,8 @@ }, "agent" : { "version" : "6.8.3", - "id" : "56a75650-3c8a-4e4f-ac17-6dd729c650e2" + "id" : "56a75650-3c8a-4e4f-ac17-6dd729c650e2", + "name": "Elastic Endpoint" }, "host" : { "id" : "7141a48b-e19f-4ae3-89a0-6e7179a84265", @@ -89,7 +92,8 @@ "os" : { "name" : "windows 6.2", "full" : "Windows Server 2012", - "version" : "6.2" + "version" : "6.2", + "variant" : "Windows Server" } } }, @@ -118,7 +122,8 @@ }, "agent" : { "version" : "6.4.3", - "id" : "c2d84d8f-d355-40de-8b54-5d318d4d1312" + "id" : "c2d84d8f-d355-40de-8b54-5d318d4d1312", + "name": "Elastic Endpoint" }, "host" : { "id" : "f35ec6c1-6562-45b1-818f-2f14c0854adf", @@ -129,7 +134,8 @@ "os" : { "name" : "windows 10.0", "full" : "Windows 10", - "version" : "10.0" + "version" : "10.0", + "variant" : "Windows Pro" } } }, @@ -166,7 +172,8 @@ }, "agent" : { "version" : "6.4.3", - "id" : "c2d84d8f-d355-40de-8b54-5d318d4d1312" + "id" : "c2d84d8f-d355-40de-8b54-5d318d4d1312", + "name": "Elastic Endpoint" }, "host" : { "id" : "f35ec6c1-6562-45b1-818f-2f14c0854adf", @@ -177,7 +184,8 @@ "os" : { "name" : "windows 10.0", "full" : "Windows 10", - "version" : "10.0" + "version" : "10.0", + "variant" : "Windows Pro" } } }, diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api.ts index 22fd01c1aee81..e0ecae976146c 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api.ts @@ -8,9 +8,9 @@ import { HttpSetup } from 'kibana/public'; import * as t from 'io-ts'; import { pipe } from 'fp-ts/lib/pipeable'; import { fold } from 'fp-ts/lib/Either'; +import { alertStateSchema } from '../../../../alerting/common'; import { BASE_ALERT_API_PATH } from '../constants'; import { Alert, AlertType, AlertWithoutId, AlertTaskState } from '../../types'; -import { alertStateSchema } from '../../../../../legacy/plugins/alerting/common'; export async function loadAlertTypes({ http }: { http: HttpSetup }): Promise { return await http.get(`${BASE_ALERT_API_PATH}/types`); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_instances.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_instances.tsx index 1f0e4f015f229..731489c61d60f 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_instances.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_instances.tsx @@ -12,8 +12,7 @@ import { EuiBasicTable, EuiButtonToggle, EuiBadge, EuiHealth } from '@elastic/eu import { RIGHT_ALIGNMENT, CENTER_ALIGNMENT } from '@elastic/eui/lib/services'; import { padLeft, difference } from 'lodash'; import { FormattedMessage } from '@kbn/i18n/react'; -import { RawAlertInstance } from '../../../../../../../legacy/plugins/alerting/common'; -import { Alert, AlertTaskState } from '../../../../types'; +import { Alert, AlertTaskState, RawAlertInstance } from '../../../../types'; import { ComponentOpts as AlertApis, withBulkAlertOperations, diff --git a/x-pack/plugins/triggers_actions_ui/public/types.ts b/x-pack/plugins/triggers_actions_ui/public/types.ts index 86853e88a81cd..aa1d3d068ed77 100644 --- a/x-pack/plugins/triggers_actions_ui/public/types.ts +++ b/x-pack/plugins/triggers_actions_ui/public/types.ts @@ -10,7 +10,7 @@ import { AlertAction, AlertTaskState, RawAlertInstance, -} from '../../../legacy/plugins/alerting/common'; +} from '../../../plugins/alerting/common'; export { Alert, AlertAction, AlertTaskState, RawAlertInstance }; export { ActionType }; diff --git a/x-pack/test/alerting_api_integration/common/fixtures/plugins/alerts/index.ts b/x-pack/test/alerting_api_integration/common/fixtures/plugins/alerts/index.ts index 0cc45a624bc1a..b06e0c9e0f8cd 100644 --- a/x-pack/test/alerting_api_integration/common/fixtures/plugins/alerts/index.ts +++ b/x-pack/test/alerting_api_integration/common/fixtures/plugins/alerts/index.ts @@ -5,7 +5,7 @@ */ import { times } from 'lodash'; import { schema } from '@kbn/config-schema'; -import { AlertExecutorOptions, AlertType } from '../../../../../../legacy/plugins/alerting'; +import { AlertExecutorOptions, AlertType } from '../../../../../../plugins/alerting/server'; import { ActionTypeExecutorOptions, ActionType } from '../../../../../../plugins/actions/server'; // eslint-disable-next-line import/no-default-export @@ -392,13 +392,13 @@ export default function(kibana: any) { actionGroups: [{ id: 'default', name: 'Default' }], async executor({ services, params, state }: AlertExecutorOptions) {}, }; - server.plugins.alerting.setup.registerType(alwaysFiringAlertType); - server.plugins.alerting.setup.registerType(cumulativeFiringAlertType); - server.plugins.alerting.setup.registerType(neverFiringAlertType); - server.plugins.alerting.setup.registerType(failingAlertType); - server.plugins.alerting.setup.registerType(validationAlertType); - server.plugins.alerting.setup.registerType(authorizationAlertType); - server.plugins.alerting.setup.registerType(noopAlertType); + server.newPlatform.setup.plugins.alerting.registerType(alwaysFiringAlertType); + server.newPlatform.setup.plugins.alerting.registerType(cumulativeFiringAlertType); + server.newPlatform.setup.plugins.alerting.registerType(neverFiringAlertType); + server.newPlatform.setup.plugins.alerting.registerType(failingAlertType); + server.newPlatform.setup.plugins.alerting.registerType(validationAlertType); + server.newPlatform.setup.plugins.alerting.registerType(authorizationAlertType); + server.newPlatform.setup.plugins.alerting.registerType(noopAlertType); }, }); } diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/create.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/create.ts index 003bb10c0adae..7dae7e4ab7941 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/create.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/create.ts @@ -213,12 +213,7 @@ export default function createAlertTests({ getService }: FtrProviderContext) { expect(response.body).to.eql({ statusCode: 400, error: 'Bad Request', - message: - 'child "name" fails because ["name" is required]. child "alertTypeId" fails because ["alertTypeId" is required]. child "consumer" fails because ["consumer" is required]. child "schedule" fails because ["schedule" is required]. child "params" fails because ["params" is required]. child "actions" fails because ["actions" is required]', - validation: { - source: 'payload', - keys: ['name', 'alertTypeId', 'consumer', 'schedule', 'params', 'actions'], - }, + message: '[request body.name]: expected value of type [string] but got [undefined]', }); break; default: @@ -285,19 +280,9 @@ export default function createAlertTests({ getService }: FtrProviderContext) { case 'space_1_all at space1': expect(response.statusCode).to.eql(400); expect(response.body).to.eql({ - statusCode: 400, error: 'Bad Request', - message: - 'child "schedule" fails because [child "interval" fails because ["interval" with value "10x" fails to match the seconds pattern, "interval" with value "10x" fails to match the minutes pattern, "interval" with value "10x" fails to match the hours pattern, "interval" with value "10x" fails to match the days pattern]]', - validation: { - source: 'payload', - keys: [ - 'schedule.interval', - 'schedule.interval', - 'schedule.interval', - 'schedule.interval', - ], - }, + message: '[request body.schedule.interval]: string is not a valid duration: 10x', + statusCode: 400, }); break; default: @@ -327,19 +312,9 @@ export default function createAlertTests({ getService }: FtrProviderContext) { case 'space_1_all at space1': expect(response.statusCode).to.eql(400); expect(response.body).to.eql({ - statusCode: 400, error: 'Bad Request', - message: - 'child "schedule" fails because [child "interval" fails because ["interval" with value "0s" fails to match the seconds pattern, "interval" with value "0s" fails to match the minutes pattern, "interval" with value "0s" fails to match the hours pattern, "interval" with value "0s" fails to match the days pattern]]', - validation: { - source: 'payload', - keys: [ - 'schedule.interval', - 'schedule.interval', - 'schedule.interval', - 'schedule.interval', - ], - }, + message: '[request body.schedule.interval]: string is not a valid duration: 0s', + statusCode: 400, }); break; default: diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/find.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/find.ts index 65ffa9ebe9dfa..f91c06c5299b8 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/find.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/find.ts @@ -202,7 +202,7 @@ export default function createFindTests({ getService }: FtrProviderContext) { expect(response.statusCode).to.eql(200); expect(response.body).to.eql({ page: 1, - perPage: 20, + perPage: 10, total: 0, data: [], }); diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/update.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/update.ts index 7a4f73885107c..f7ccc6c97bcf0 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/update.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/update.ts @@ -53,7 +53,7 @@ export default function createUpdateTests({ getService }: FtrProviderContext) { }, schedule: { interval: '12s' }, actions: [], - throttle: '2m', + throttle: '1m', }; const response = await supertestWithoutAuth .put(`${getUrlPrefix(space.id)}/api/alert/${createdAlert.id}`) @@ -134,7 +134,7 @@ export default function createUpdateTests({ getService }: FtrProviderContext) { }, schedule: { interval: '12s' }, actions: [], - throttle: '2m', + throttle: '1m', }; const response = await supertestWithoutAuth .put(`${getUrlPrefix(space.id)}/api/alert/${createdAlert.id}`) @@ -277,11 +277,7 @@ export default function createUpdateTests({ getService }: FtrProviderContext) { expect(response.body).to.eql({ statusCode: 400, error: 'Bad Request', - message: '"alertTypeId" is not allowed', - validation: { - source: 'payload', - keys: ['alertTypeId'], - }, + message: '[request body.alertTypeId]: definition for this key is missing', }); break; default: @@ -313,12 +309,7 @@ export default function createUpdateTests({ getService }: FtrProviderContext) { expect(response.body).to.eql({ statusCode: 400, error: 'Bad Request', - message: - 'child "throttle" fails because ["throttle" is required]. child "name" fails because ["name" is required]. child "tags" fails because ["tags" is required]. child "schedule" fails because ["schedule" is required]. child "params" fails because ["params" is required]. child "actions" fails because ["actions" is required]', - validation: { - source: 'payload', - keys: ['throttle', 'name', 'tags', 'schedule', 'params', 'actions'], - }, + message: '[request body.name]: expected value of type [string] but got [undefined]', }); break; default: @@ -410,18 +401,7 @@ export default function createUpdateTests({ getService }: FtrProviderContext) { expect(response.body).to.eql({ statusCode: 400, error: 'Bad Request', - message: - 'child "schedule" fails because [child "interval" fails because ["interval" with value "10x" fails to match the seconds pattern, "interval" with value "10x" fails to match the minutes pattern, "interval" with value "10x" fails to match the hours pattern, "interval" with value "10x" fails to match the days pattern]]. "alertTypeId" is not allowed', - validation: { - source: 'payload', - keys: [ - 'schedule.interval', - 'schedule.interval', - 'schedule.interval', - 'schedule.interval', - 'alertTypeId', - ], - }, + message: '[request body.schedule.interval]: string is not a valid duration: 10x', }); break; default: @@ -456,7 +436,7 @@ export default function createUpdateTests({ getService }: FtrProviderContext) { }, schedule: { interval: '1m' }, actions: [], - throttle: '2m', + throttle: '1m', }; const response = await supertestWithoutAuth .put(`${getUrlPrefix(space.id)}/api/alert/${createdAlert.id}`) diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/index.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/index.ts index 6a99ed366047b..c0f56c55ba850 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/index.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/index.ts @@ -18,7 +18,7 @@ export default function alertingApiIntegrationTests({ const esArchiver = getService('esArchiver'); describe('alerting api integration security and spaces enabled', function() { - this.tags('ciGroup1'); + this.tags('ciGroup3'); before(async () => { for (const space of Spaces) { diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/find.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/find.ts index 70935a462d03e..1dd0426c97cca 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/find.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/find.ts @@ -77,7 +77,7 @@ export default function createFindTests({ getService }: FtrProviderContext) { ) .expect(200, { page: 1, - perPage: 20, + perPage: 10, total: 0, data: [], }); diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/index.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/index.ts index cb2b17980d37a..b118a48fd642c 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/index.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/index.ts @@ -16,7 +16,7 @@ export default function alertingApiIntegrationTests({ const esArchiver = getService('esArchiver'); describe('alerting api integration spaces only', function() { - this.tags('ciGroup1'); + this.tags('ciGroup3'); before(async () => { for (const space of Object.values(Spaces)) { diff --git a/x-pack/test/api_integration/apis/endpoint/endpoints.ts b/x-pack/test/api_integration/apis/endpoint/endpoints.ts index 210e9d78d7e18..febe5f523cb6f 100644 --- a/x-pack/test/api_integration/apis/endpoint/endpoints.ts +++ b/x-pack/test/api_integration/apis/endpoint/endpoints.ts @@ -142,6 +142,25 @@ export default function({ getService }: FtrProviderContext) { expect(body.request_page_size).to.eql(10); expect(body.request_page_index).to.eql(0); }); + + it('endpoints api should return page based on host.os.variant filter.', async () => { + const variantValue = 'Windows Pro'; + const { body } = await supertest + .post('/api/endpoint/endpoints') + .set('kbn-xsrf', 'xxx') + .send({ + filter: `host.os.variant.keyword:${variantValue}`, + }) + .expect(200); + expect(body.total).to.eql(2); + const resultOsVariantValue: Set = new Set( + body.endpoints.map((metadata: Record) => metadata.host.os.variant) + ); + expect(Array.from(resultOsVariantValue)).to.eql([variantValue]); + expect(body.endpoints.length).to.eql(2); + expect(body.request_page_size).to.eql(10); + expect(body.request_page_index).to.eql(0); + }); }); }); } diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_rules_bulk.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_rules_bulk.ts index be008a34343c4..8e951a31b525c 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_rules_bulk.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_rules_bulk.ts @@ -79,7 +79,6 @@ export default ({ getService }: FtrProviderContext): void => { expect(bodyToCompare).to.eql(getSimpleRuleOutputWithoutRuleId()); }); - // TODO: This is a valid issue and will be fixed in an upcoming PR and then enabled once that PR is merged it('should return a 200 ok but have a 409 conflict if we attempt to create the same rule_id twice', async () => { const { body } = await supertest .post(`${DETECTION_ENGINE_RULES_URL}/_bulk_create`) diff --git a/x-pack/test/functional/apps/maps/documents_source/docvalue_fields.js b/x-pack/test/functional/apps/maps/documents_source/docvalue_fields.js index 48f225a467d8f..fdacd89722d3c 100644 --- a/x-pack/test/functional/apps/maps/documents_source/docvalue_fields.js +++ b/x-pack/test/functional/apps/maps/documents_source/docvalue_fields.js @@ -10,7 +10,6 @@ export default function({ getPageObjects, getService }) { const PageObjects = getPageObjects(['maps']); const inspector = getService('inspector'); const testSubjects = getService('testSubjects'); - const log = getService('log'); describe('docvalue_fields', () => { before(async () => { @@ -22,7 +21,6 @@ export default function({ getPageObjects, getService }) { await inspector.openInspectorRequestsView(); await testSubjects.click('inspectorRequestDetailResponse'); const responseBody = await testSubjects.getVisibleText('inspectorResponseBody'); - log.info(responseBody); await inspector.close(); return JSON.parse(responseBody); } diff --git a/x-pack/test/functional/es_archives/endpoint/endpoints/api_feature/data.json b/x-pack/test/functional/es_archives/endpoint/endpoints/api_feature/data.json index b481d56df4d52..87720b068f0e8 100644 --- a/x-pack/test/functional/es_archives/endpoint/endpoints/api_feature/data.json +++ b/x-pack/test/functional/es_archives/endpoint/endpoints/api_feature/data.json @@ -7,7 +7,8 @@ "@timestamp": 1579881969541, "agent": { "id": "963b081e-60d1-482c-befd-a5815fa8290f", - "version": "6.6.1" + "version": "6.6.1", + "name" : "Elastic Endpoint" }, "endpoint": { "policy": { @@ -33,7 +34,8 @@ "os": { "full": "Windows 10", "name": "windows 10.0", - "version": "10.0" + "version": "10.0", + "variant" : "Windows Pro" } } } @@ -49,7 +51,8 @@ "@timestamp": 1579881969541, "agent": { "id": "b3412d6f-b022-4448-8fee-21cc936ea86b", - "version": "6.0.0" + "version": "6.0.0", + "name" : "Elastic Endpoint" }, "endpoint": { "policy": { @@ -74,7 +77,8 @@ "os": { "full": "Windows Server 2016", "name": "windows 10.0", - "version": "10.0" + "version": "10.0", + "variant" : "Windows Server" } } } @@ -90,7 +94,8 @@ "@timestamp": 1579881969541, "agent": { "id": "3838df35-a095-4af4-8fce-0b6d78793f2e", - "version": "6.8.0" + "version": "6.8.0", + "name" : "Elastic Endpoint" }, "endpoint": { "policy": { @@ -113,7 +118,8 @@ "os": { "full": "Windows 10", "name": "windows 10.0", - "version": "10.0" + "version": "10.0", + "variant" : "Windows Pro" } } } @@ -129,7 +135,8 @@ "@timestamp": 1579878369541, "agent": { "id": "963b081e-60d1-482c-befd-a5815fa8290f", - "version": "6.6.1" + "version": "6.6.1", + "name" : "Elastic Endpoint" }, "endpoint": { "policy": { @@ -155,7 +162,8 @@ "os": { "full": "Windows Server 2016", "name": "windows 10.0", - "version": "10.0" + "version": "10.0", + "variant" : "Windows Server 2016" } } } @@ -171,7 +179,8 @@ "@timestamp": 1579878369541, "agent": { "id": "b3412d6f-b022-4448-8fee-21cc936ea86b", - "version": "6.0.0" + "version": "6.0.0", + "name" : "Elastic Endpoint" }, "endpoint": { "policy": { @@ -195,7 +204,8 @@ "os": { "full": "Windows Server 2012", "name": "windows 6.2", - "version": "6.2" + "version": "6.2", + "variant" : "Windows Server 2012" } } } @@ -211,7 +221,8 @@ "@timestamp": 1579878369541, "agent": { "id": "3838df35-a095-4af4-8fce-0b6d78793f2e", - "version": "6.8.0" + "version": "6.8.0", + "name" : "Elastic Endpoint" }, "endpoint": { "policy": { @@ -235,7 +246,8 @@ "os": { "full": "Windows Server 2012", "name": "windows 6.2", - "version": "6.2" + "version": "6.2", + "variant" : "Windows Server 2012" } } } @@ -251,7 +263,8 @@ "@timestamp": 1579874769541, "agent": { "id": "963b081e-60d1-482c-befd-a5815fa8290f", - "version": "6.6.1" + "version": "6.6.1", + "name" : "Elastic Endpoint" }, "endpoint": { "policy": { @@ -276,7 +289,8 @@ "os": { "full": "Windows Server 2012R2", "name": "windows 6.3", - "version": "6.3" + "version": "6.3", + "variant" : "Windows Server 2012 R2" } } } @@ -292,7 +306,8 @@ "@timestamp": 1579874769541, "agent": { "id": "b3412d6f-b022-4448-8fee-21cc936ea86b", - "version": "6.0.0" + "version": "6.0.0", + "name" : "Elastic Endpoint" }, "endpoint": { "policy": { @@ -316,7 +331,8 @@ "os": { "full": "Windows Server 2012R2", "name": "windows 6.3", - "version": "6.3" + "version": "6.3", + "variant" : "Windows Server 2012 R2" } } } @@ -332,7 +348,8 @@ "@timestamp": 1579874769541, "agent": { "id": "3838df35-a095-4af4-8fce-0b6d78793f2e", - "version": "6.8.0" + "version": "6.8.0", + "name" : "Elastic Endpoint" }, "endpoint": { "policy": { @@ -356,9 +373,10 @@ "os": { "full": "Windows Server 2012", "name": "windows 6.2", - "version": "6.2" + "version": "6.2", + "variant" : "Windows Server 2012" } } } } -} \ No newline at end of file +} diff --git a/x-pack/test/functional/es_archives/endpoint/endpoints/api_feature/mappings.json b/x-pack/test/functional/es_archives/endpoint/endpoints/api_feature/mappings.json index 11766c12b8fff..d6647e62b0191 100644 --- a/x-pack/test/functional/es_archives/endpoint/endpoints/api_feature/mappings.json +++ b/x-pack/test/functional/es_archives/endpoint/endpoints/api_feature/mappings.json @@ -28,6 +28,15 @@ } }, "type": "text" + }, + "name": { + "fields": { + "keyword": { + "ignore_above": 256, + "type": "keyword" + } + }, + "type": "text" } } }, @@ -122,6 +131,15 @@ }, "type": "text" }, + "variant": { + "fields": { + "keyword": { + "ignore_above": 256, + "type": "keyword" + } + }, + "type": "text" + }, "version": { "fields": { "keyword": { @@ -144,4 +162,4 @@ } } } -} \ No newline at end of file +} diff --git a/x-pack/test/functional_with_es_ssl/fixtures/plugins/alerts/index.ts b/x-pack/test/functional_with_es_ssl/fixtures/plugins/alerts/index.ts index d7551345203b4..678707af40bae 100644 --- a/x-pack/test/functional_with_es_ssl/fixtures/plugins/alerts/index.ts +++ b/x-pack/test/functional_with_es_ssl/fixtures/plugins/alerts/index.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { AlertType } from '../../../../../legacy/plugins/alerting'; +import { AlertType } from '../../../../../plugins/alerting/server'; // eslint-disable-next-line import/no-default-export export default function(kibana: any) { @@ -12,8 +12,8 @@ export default function(kibana: any) { require: ['alerting'], name: 'alerts', init(server: any) { - createNoopAlertType(server.plugins.alerting.setup); - createAlwaysFiringAlertType(server.plugins.alerting.setup); + createNoopAlertType(server.newPlatform.setup.plugins.alerting); + createAlwaysFiringAlertType(server.newPlatform.setup.plugins.alerting); }, }); } diff --git a/x-pack/typings/hapi.d.ts b/x-pack/typings/hapi.d.ts index 8d54d28794351..872e042eb1cdb 100644 --- a/x-pack/typings/hapi.d.ts +++ b/x-pack/typings/hapi.d.ts @@ -9,7 +9,7 @@ import 'hapi'; import { XPackMainPlugin } from '../legacy/plugins/xpack_main/server/xpack_main'; import { SecurityPlugin } from '../legacy/plugins/security'; import { ActionsPlugin, ActionsClient } from '../plugins/actions/server'; -import { AlertingPlugin, AlertsClient } from '../legacy/plugins/alerting'; +import { AlertingPlugin, AlertsClient } from '../plugins/alerting/server'; import { LegacyTaskManagerApi } from '../legacy/plugins/task_manager/server'; declare module 'hapi' {