From 981bbd9497d183ecdfa5f8040574bca42780b915 Mon Sep 17 00:00:00 2001 From: Dmitry Tomashevich <39378793+Dmitriynj@users.noreply.github.com> Date: Fri, 17 Dec 2021 15:17:27 +0300 Subject: [PATCH 01/40] [Discover] Improve query params validation for single doc view (#121036) * [Discover] improve query params validation * [Discover] update types * [Discover] add unit tests * [Discover] improve tests, add padding for error prompt * [Discover] apply comments * [Discover] fix unit tests, add period Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../application/context/context_app_route.tsx | 11 +--- .../application/doc/single_doc_route.tsx | 34 +++++------- .../application/main/discover_main_route.tsx | 26 +-------- .../discover/public/application/types.ts | 8 +++ .../public/components/common/error_alert.tsx | 45 +++++++++++++++ .../public/utils/with_query_params.test.tsx | 55 +++++++++++++++++++ .../public/utils/with_query_params.tsx | 47 ++++++++++++++++ .../translations/translations/ja-JP.json | 1 - .../translations/translations/zh-CN.json | 1 - 9 files changed, 175 insertions(+), 53 deletions(-) create mode 100644 src/plugins/discover/public/components/common/error_alert.tsx create mode 100644 src/plugins/discover/public/utils/with_query_params.test.tsx create mode 100644 src/plugins/discover/public/utils/with_query_params.tsx diff --git a/src/plugins/discover/public/application/context/context_app_route.tsx b/src/plugins/discover/public/application/context/context_app_route.tsx index 80feea833ec94..b58bbebcd0973 100644 --- a/src/plugins/discover/public/application/context/context_app_route.tsx +++ b/src/plugins/discover/public/application/context/context_app_route.tsx @@ -10,26 +10,19 @@ import { useParams } from 'react-router-dom'; import { i18n } from '@kbn/i18n'; import { EuiEmptyPrompt } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; -import { DiscoverServices } from '../../build_services'; import { ContextApp } from './context_app'; import { getRootBreadcrumbs } from '../../utils/breadcrumbs'; import { LoadingIndicator } from '../../components/common/loading_indicator'; import { useIndexPattern } from '../../utils/use_index_pattern'; +import { DiscoverRouteProps } from '../types'; import { useMainRouteBreadcrumb } from '../../utils/use_navigation_props'; -export interface ContextAppProps { - /** - * Kibana core services used by discover - */ - services: DiscoverServices; -} - export interface ContextUrlParams { indexPatternId: string; id: string; } -export function ContextAppRoute(props: ContextAppProps) { +export function ContextAppRoute(props: DiscoverRouteProps) { const { services } = props; const { chrome } = services; diff --git a/src/plugins/discover/public/application/doc/single_doc_route.tsx b/src/plugins/discover/public/application/doc/single_doc_route.tsx index 0a5cc3a8a82b6..0d65bbeb714cf 100644 --- a/src/plugins/discover/public/application/doc/single_doc_route.tsx +++ b/src/plugins/discover/public/application/doc/single_doc_route.tsx @@ -6,21 +6,22 @@ * Side Public License, v 1. */ import React, { useEffect } from 'react'; -import { useLocation, useParams } from 'react-router-dom'; +import { useParams } from 'react-router-dom'; import { EuiEmptyPrompt } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; -import { DiscoverServices } from '../../build_services'; import { getRootBreadcrumbs } from '../../utils/breadcrumbs'; -import { Doc } from './components/doc'; import { LoadingIndicator } from '../../components/common/loading_indicator'; import { useIndexPattern } from '../../utils/use_index_pattern'; +import { withQueryParams } from '../../utils/with_query_params'; import { useMainRouteBreadcrumb } from '../../utils/use_navigation_props'; +import { DiscoverRouteProps } from '../types'; +import { Doc } from './components/doc'; -export interface SingleDocRouteProps { +export interface SingleDocRouteProps extends DiscoverRouteProps { /** - * Kibana core services used by discover + * Document id */ - services: DiscoverServices; + id: string; } export interface DocUrlParams { @@ -28,28 +29,21 @@ export interface DocUrlParams { index: string; } -function useQuery() { - return new URLSearchParams(useLocation().search); -} - -export function SingleDocRoute(props: SingleDocRouteProps) { - const { services } = props; +const SingleDoc = (props: SingleDocRouteProps) => { + const { id, services } = props; const { chrome, timefilter } = services; const { indexPatternId, index } = useParams(); const breadcrumb = useMainRouteBreadcrumb(); - const query = useQuery(); - const docId = query.get('id') || ''; - useEffect(() => { chrome.setBreadcrumbs([ ...getRootBreadcrumbs(breadcrumb), { - text: `${index}#${docId}`, + text: `${index}#${id}`, }, ]); - }, [chrome, index, docId, breadcrumb]); + }, [chrome, index, id, breadcrumb]); useEffect(() => { timefilter.disableAutoRefreshSelector(); @@ -86,7 +80,9 @@ export function SingleDocRoute(props: SingleDocRouteProps) { return (
- +
); -} +}; + +export const SingleDocRoute = withQueryParams(SingleDoc, ['id']); diff --git a/src/plugins/discover/public/application/main/discover_main_route.tsx b/src/plugins/discover/public/application/main/discover_main_route.tsx index d226e5ef9748b..b2576a3b5d582 100644 --- a/src/plugins/discover/public/application/main/discover_main_route.tsx +++ b/src/plugins/discover/public/application/main/discover_main_route.tsx @@ -8,11 +8,8 @@ import React, { useEffect, useState, memo } from 'react'; import { History } from 'history'; import { useParams } from 'react-router-dom'; -import { i18n } from '@kbn/i18n'; -import { EuiEmptyPrompt } from '@elastic/eui'; import { IndexPatternAttributes, ISearchSource, SavedObject } from 'src/plugins/data/common'; -import { DiscoverServices } from '../../build_services'; import { SavedSearch, getSavedSearch, @@ -26,39 +23,22 @@ import { redirectWhenMissing } from '../../../../kibana_utils/public'; import { DataViewSavedObjectConflictError } from '../../../../data_views/common'; import { getUrlTracker } from '../../kibana_services'; import { LoadingIndicator } from '../../components/common/loading_indicator'; +import { DiscoverError } from '../../components/common/error_alert'; +import { DiscoverRouteProps } from '../types'; const DiscoverMainAppMemoized = memo(DiscoverMainApp); -export interface DiscoverMainProps { +export interface DiscoverMainProps extends DiscoverRouteProps { /** * Instance of browser history */ history: History; - /** - * Kibana core services used by discover - */ - services: DiscoverServices; } interface DiscoverLandingParams { id: string; } -const DiscoverError = ({ error }: { error: Error }) => ( - - {i18n.translate('discover.discoverError.title', { - defaultMessage: 'Error loading Discover', - })} - - } - body={

{error.message}

} - /> -); - export function DiscoverMainRoute({ services, history }: DiscoverMainProps) { const { core, diff --git a/src/plugins/discover/public/application/types.ts b/src/plugins/discover/public/application/types.ts index f04f3bf77c2f9..f33b8bb22b58c 100644 --- a/src/plugins/discover/public/application/types.ts +++ b/src/plugins/discover/public/application/types.ts @@ -6,6 +6,7 @@ * Side Public License, v 1. */ import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; +import { DiscoverServices } from '../build_services'; export enum FetchStatus { UNINITIALIZED = 'uninitialized', @@ -24,3 +25,10 @@ export type EsHitRecord = Required< isAnchor?: boolean; }; export type EsHitRecordList = EsHitRecord[]; + +export interface DiscoverRouteProps { + /** + * Kibana core services used by discover + */ + services: DiscoverServices; +} diff --git a/src/plugins/discover/public/components/common/error_alert.tsx b/src/plugins/discover/public/components/common/error_alert.tsx new file mode 100644 index 0000000000000..38e2a41f6f40e --- /dev/null +++ b/src/plugins/discover/public/components/common/error_alert.tsx @@ -0,0 +1,45 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React from 'react'; +import { EuiButton, EuiEmptyPrompt } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { useHistory } from 'react-router-dom'; + +export const DiscoverError = ({ error }: { error: Error }) => { + const history = useHistory(); + + const goToMain = () => { + history.push('/'); + }; + + return ( + + {i18n.translate('discover.discoverError.title', { + defaultMessage: 'Cannot load this page', + })} + + } + body={

{error.message}

} + actions={ + + + + } + /> + ); +}; diff --git a/src/plugins/discover/public/utils/with_query_params.test.tsx b/src/plugins/discover/public/utils/with_query_params.test.tsx new file mode 100644 index 0000000000000..d897c3327a06f --- /dev/null +++ b/src/plugins/discover/public/utils/with_query_params.test.tsx @@ -0,0 +1,55 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React, { ReactElement } from 'react'; +import { Router } from 'react-router-dom'; +import { createMemoryHistory } from 'history'; +import { mountWithIntl } from '@kbn/test/jest'; +import { withQueryParams } from './with_query_params'; +import { DiscoverServices } from '../build_services'; +import { DiscoverRouteProps } from '../application/types'; + +interface ComponentProps extends DiscoverRouteProps { + id: string; + query: string; +} + +const mountComponent = (children: ReactElement, query = '') => { + const history = createMemoryHistory({ + initialEntries: ['/' + query], + }); + return mountWithIntl({children}); +}; + +describe('withQueryParams', () => { + it('should display error message, when query does not contain required parameters', () => { + const Component = withQueryParams(() =>
, ['id', 'query']); + const component = mountComponent(); + + expect(component.html()).toContain('Cannot load this page'); + expect(component.html()).toContain('URL query string is missing id, query.'); + }); + + it('should not display error message, when query contain required parameters', () => { + const Component = withQueryParams( + ({ id, query }: ComponentProps) => ( +
+ {id} and {query} are presented +
+ ), + ['id', 'query'] + ); + const component = mountComponent( + , + '?id=one&query=another' + ); + + expect(component.html()).toContain('one and another are presented'); + expect(component.html()).not.toContain('URL query string is missing id, query.'); + }); +}); diff --git a/src/plugins/discover/public/utils/with_query_params.tsx b/src/plugins/discover/public/utils/with_query_params.tsx new file mode 100644 index 0000000000000..66f0dd72c64de --- /dev/null +++ b/src/plugins/discover/public/utils/with_query_params.tsx @@ -0,0 +1,47 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React, { useMemo } from 'react'; +import { i18n } from '@kbn/i18n'; +import { useLocation } from 'react-router-dom'; +import { DiscoverRouteProps } from '../application/types'; +import { DiscoverError } from '../components/common/error_alert'; + +function useQuery() { + const { search } = useLocation(); + return useMemo(() => new URLSearchParams(search), [search]); +} + +export const withQueryParams =

( + Component: React.ComponentType

, + requiredParams: string[] +) => { + return (routeProps: DiscoverRouteProps) => { + const query = useQuery(); + + const missingParamNames = useMemo( + () => requiredParams.filter((currentParamName) => !query.get(currentParamName)), + [query] + ); + + if (missingParamNames.length !== 0) { + const missingParamsList = missingParamNames.join(', '); + const errorMessage = i18n.translate('discover.discoverError.missingQueryParamsError', { + defaultMessage: 'URL query string is missing {missingParamsList}.', + values: { missingParamsList }, + }); + + return ; + } + + const queryProps = Object.fromEntries( + requiredParams.map((current) => [[current], query.get(current)]) + ); + return ; + }; +}; diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 76bfd54f80fa2..86d059f13e85c 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -1623,7 +1623,6 @@ "discover.discoverBreadcrumbTitle": "Discover", "discover.discoverDefaultSearchSessionName": "Discover", "discover.discoverDescription": "ドキュメントにクエリをかけたりフィルターを適用することで、データをインタラクティブに閲覧できます。", - "discover.discoverError.title": "Discoverの読み込みエラー", "discover.discoverSubtitle": "インサイトを検索して見つけます。", "discover.discoverTitle": "Discover", "discover.doc.couldNotFindDocumentsDescription": "そのIDに一致するドキュメントがありません。", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index cea1fcddd237d..b31ba78246b3f 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -1635,7 +1635,6 @@ "discover.discoverBreadcrumbTitle": "Discover", "discover.discoverDefaultSearchSessionName": "发现", "discover.discoverDescription": "通过查询和筛选原始文档来以交互方式浏览您的数据。", - "discover.discoverError.title": "加载 Discover 时出错", "discover.discoverSubtitle": "搜索和查找数据分析结果。", "discover.discoverTitle": "Discover", "discover.doc.couldNotFindDocumentsDescription": "无文档匹配该 ID。", From b6c9651031d8b2b24f88bfac5d313085c4aea4b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yulia=20=C4=8Cech?= <6585477+yuliacech@users.noreply.github.com> Date: Fri, 17 Dec 2021 13:23:50 +0100 Subject: [PATCH 02/40] [Snapshot & Restore] Migrated from _cat/plugins to _nodes/plugins to check for installed repo plugins (#121204) Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../server/routes/api/repositories.test.ts | 50 ++++++++++++++----- .../server/routes/api/repositories.ts | 35 +++++++------ 2 files changed, 59 insertions(+), 26 deletions(-) diff --git a/x-pack/plugins/snapshot_restore/server/routes/api/repositories.test.ts b/x-pack/plugins/snapshot_restore/server/routes/api/repositories.test.ts index ad180b8db45e0..eff500e34e885 100644 --- a/x-pack/plugins/snapshot_restore/server/routes/api/repositories.test.ts +++ b/x-pack/plugins/snapshot_restore/server/routes/api/repositories.test.ts @@ -5,7 +5,11 @@ * 2.0. */ -import { DEFAULT_REPOSITORY_TYPES, REPOSITORY_PLUGINS_MAP } from '../../../common/constants'; +import { + DEFAULT_REPOSITORY_TYPES, + REPOSITORY_PLUGINS_MAP, + REPOSITORY_TYPES, +} from '../../../common'; import { addBasePath } from '../helpers'; import { registerRepositoriesRoutes } from './repositories'; import { RouterMock, routeDependencies, RequestMock } from '../../test/helpers'; @@ -32,7 +36,7 @@ describe('[Snapshot and Restore API Routes] Repositories', () => { const getClusterSettingsFn = router.getMockApiFn('cluster.getSettings'); const getSnapshotFn = router.getMockApiFn('snapshot.get'); const verifyRepoFn = router.getMockApiFn('snapshot.verifyRepository'); - const catPluginsFn = router.getMockApiFn('cat.plugins'); + const nodesInfoFn = router.getMockApiFn('nodes.info'); beforeAll(() => { registerRepositoriesRoutes({ @@ -253,38 +257,60 @@ describe('[Snapshot and Restore API Routes] Repositories', () => { path: addBasePath('repository_types'), }; - it('should return default types if no repository plugins returned from ES', async () => { - catPluginsFn.mockResolvedValue({ body: {} }); + it('returns default types if no repository plugins returned from ES', async () => { + nodesInfoFn.mockResolvedValue({ body: { nodes: { testNodeId: { plugins: [] } } } }); const expectedResponse = [...DEFAULT_REPOSITORY_TYPES]; await expect(router.runRequest(mockRequest)).resolves.toEqual({ body: expectedResponse }); }); - it('should return default types with any repository plugins returned from ES', async () => { + it('returns default types with any repository plugins returned from ES', async () => { const pluginNames = Object.keys(REPOSITORY_PLUGINS_MAP); const pluginTypes = Object.entries(REPOSITORY_PLUGINS_MAP).map(([key, value]) => value); - const mockEsResponse = [...pluginNames.map((key) => ({ component: key }))]; - catPluginsFn.mockResolvedValue({ body: mockEsResponse }); + const mockEsResponse = { + nodes: { testNodeId: { plugins: [...pluginNames.map((key) => ({ name: key }))] } }, + }; + nodesInfoFn.mockResolvedValue({ body: mockEsResponse }); const expectedResponse = [...DEFAULT_REPOSITORY_TYPES, ...pluginTypes]; await expect(router.runRequest(mockRequest)).resolves.toEqual({ body: expectedResponse }); }); - it('should not return non-repository plugins returned from ES', async () => { + it(`doesn't return non-repository plugins returned from ES`, async () => { const pluginNames = ['foo-plugin', 'bar-plugin']; - const mockEsResponse = [...pluginNames.map((key) => ({ component: key }))]; - catPluginsFn.mockResolvedValue({ body: mockEsResponse }); + const mockEsResponse = { + nodes: { testNodeId: { plugins: [...pluginNames.map((key) => ({ name: key }))] } }, + }; + nodesInfoFn.mockResolvedValue({ body: mockEsResponse }); const expectedResponse = [...DEFAULT_REPOSITORY_TYPES]; await expect(router.runRequest(mockRequest)).resolves.toEqual({ body: expectedResponse }); }); + it(`doesn't return repository plugins that are not installed on all nodes`, async () => { + const dataNodePlugins = ['repository-s3', 'repository-azure']; + const masterNodePlugins = ['repository-azure']; + const mockEsResponse = { + nodes: { + dataNode: { plugins: [...dataNodePlugins.map((key) => ({ name: key }))] }, + masterNode: { plugins: [...masterNodePlugins.map((key) => ({ name: key }))] }, + }, + }; + nodesInfoFn.mockResolvedValue({ body: mockEsResponse }); + + const expectedResponse = [...DEFAULT_REPOSITORY_TYPES, REPOSITORY_TYPES.azure]; + + await expect(router.runRequest(mockRequest)).resolves.toEqual({ body: expectedResponse }); + }); + it('should throw if ES error', async () => { - catPluginsFn.mockRejectedValueOnce(new Error('Error getting plugins')); + nodesInfoFn.mockRejectedValueOnce(new Error('Error getting cluster stats')); - await expect(router.runRequest(mockRequest)).rejects.toThrowError('Error getting plugins'); + await expect(router.runRequest(mockRequest)).rejects.toThrowError( + 'Error getting cluster stats' + ); }); }); diff --git a/x-pack/plugins/snapshot_restore/server/routes/api/repositories.ts b/x-pack/plugins/snapshot_restore/server/routes/api/repositories.ts index e700c6bf9e04e..03a5ab26a65b0 100644 --- a/x-pack/plugins/snapshot_restore/server/routes/api/repositories.ts +++ b/x-pack/plugins/snapshot_restore/server/routes/api/repositories.ts @@ -9,9 +9,10 @@ import { TypeOf } from '@kbn/config-schema'; import type { SnapshotGetRepositoryResponse, SnapshotRepositorySettings, + PluginStats, } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; -import { DEFAULT_REPOSITORY_TYPES, REPOSITORY_PLUGINS_MAP } from '../../../common/constants'; +import { DEFAULT_REPOSITORY_TYPES, REPOSITORY_PLUGINS_MAP } from '../../../common'; import { Repository, RepositoryType } from '../../../common/types'; import { RouteDependencies } from '../../types'; import { addBasePath } from '../helpers'; @@ -162,21 +163,27 @@ export function registerRepositoriesRoutes({ const types: RepositoryType[] = isCloudEnabled ? [] : [...DEFAULT_REPOSITORY_TYPES]; try { - // Call with internal user so that the requesting user does not need `monitoring` cluster - // privilege just to see list of available repository types - const { body: plugins } = await clusterClient.asCurrentUser.cat.plugins({ format: 'json' }); + const { + body: { nodes }, + } = await clusterClient.asCurrentUser.nodes.info({ + node_id: '_all', + metric: 'plugins', + }); + const pluginNamesAllNodes = Object.keys(nodes).map((key: string) => { + // extract plugin names + return (nodes[key].plugins ?? []).map((plugin: PluginStats) => plugin.name); + }); // Filter list of plugins to repository-related ones - if (plugins && plugins.length) { - const pluginNames: string[] = [ - ...new Set(plugins.map((plugin) => plugin.component ?? '')), - ]; - pluginNames.forEach((pluginName) => { - if (REPOSITORY_PLUGINS_MAP[pluginName]) { - types.push(REPOSITORY_PLUGINS_MAP[pluginName]); - } - }); - } + Object.keys(REPOSITORY_PLUGINS_MAP).forEach((repoTypeName: string) => { + if ( + // check if this repository plugin is installed on every node + pluginNamesAllNodes.every((pluginNames: string[]) => pluginNames.includes(repoTypeName)) + ) { + types.push(REPOSITORY_PLUGINS_MAP[repoTypeName]); + } + }); + return res.ok({ body: types }); } catch (e) { return handleEsError({ error: e, response: res }); From 55b07a2a8814ed3be1747ba9f0ae4fd4add9faf9 Mon Sep 17 00:00:00 2001 From: Jonathan Buttner <56361221+jonathan-buttner@users.noreply.github.com> Date: Fri, 17 Dec 2021 09:11:28 -0500 Subject: [PATCH 03/40] [Cases] Handle undefined connectors config field (#120686) * Marking config as optional * Filtering out preconfigured connectors Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../server/client/configure/client.test.ts | 104 ++++++++++++++++++ .../cases/server/client/configure/client.ts | 21 ++-- 2 files changed, 117 insertions(+), 8 deletions(-) create mode 100644 x-pack/plugins/cases/server/client/configure/client.test.ts diff --git a/x-pack/plugins/cases/server/client/configure/client.test.ts b/x-pack/plugins/cases/server/client/configure/client.test.ts new file mode 100644 index 0000000000000..971748bd8295a --- /dev/null +++ b/x-pack/plugins/cases/server/client/configure/client.test.ts @@ -0,0 +1,104 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { CasesClientArgs } from '../types'; +import { loggingSystemMock } from '../../../../../../src/core/server/mocks'; +import { getConnectors } from './client'; +import { actionsClientMock } from '../../../../actions/server/mocks'; +import { ActionType } from '../../../../actions/common/types'; + +describe('client', () => { + describe('getConnectors', () => { + const logger = loggingSystemMock.createLogger(); + const actionsClient = actionsClientMock.create(); + + const args = { actionsClient, logger } as unknown as CasesClientArgs; + + const jiraType: ActionType = { + id: '.jira', + name: '1', + enabled: true, + enabledInConfig: true, + enabledInLicense: true, + minimumLicenseRequired: 'basic', + }; + + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('removes connectors without a config field defined', async () => { + actionsClient.listTypes.mockImplementation(async () => [jiraType]); + + actionsClient.getAll.mockImplementation(async () => [ + { + id: '1', + actionTypeId: '.jira', + name: '1', + isPreconfigured: false, + referencedByCount: 1, + }, + ]); + + expect(await getConnectors(args)).toEqual([]); + }); + + it('removes connectors that are pre configured', async () => { + actionsClient.listTypes.mockImplementation(async () => [jiraType]); + + actionsClient.getAll.mockImplementation(async () => [ + { + id: '1', + actionTypeId: '.jira', + name: '1', + config: {}, + isPreconfigured: true, + referencedByCount: 1, + }, + ]); + + expect(await getConnectors(args)).toEqual([]); + }); + + it('includes connectors that have a config and are not pre configured', async () => { + actionsClient.listTypes.mockImplementation(async () => [ + jiraType, + { + id: '.servicenow', + name: '2', + enabled: true, + enabledInConfig: true, + enabledInLicense: true, + minimumLicenseRequired: 'basic', + }, + ]); + + const connectors = [ + { + id: '1', + actionTypeId: '.jira', + name: '1', + config: {}, + isPreconfigured: false, + referencedByCount: 1, + }, + { + id: '2', + actionTypeId: '.servicenow', + name: '2', + config: {}, + isPreconfigured: false, + referencedByCount: 1, + }, + ]; + + actionsClient.getAll.mockImplementation(async () => connectors); + + expect(await getConnectors(args)).toEqual(connectors); + }); + }); +}); diff --git a/x-pack/plugins/cases/server/client/configure/client.ts b/x-pack/plugins/cases/server/client/configure/client.ts index c7b94df879142..831fe273af92e 100644 --- a/x-pack/plugins/cases/server/client/configure/client.ts +++ b/x-pack/plugins/cases/server/client/configure/client.ts @@ -203,17 +203,10 @@ async function get( } } -async function getConnectors({ +export async function getConnectors({ actionsClient, logger, }: CasesClientArgs): Promise { - const isConnectorSupported = ( - action: FindActionResult, - actionTypes: Record - ): boolean => - SUPPORTED_CONNECTORS.includes(action.actionTypeId) && - actionTypes[action.actionTypeId]?.enabledInLicense; - try { const actionTypes = (await actionsClient.listTypes()).reduce( (types, type) => ({ ...types, [type.id]: type }), @@ -228,6 +221,18 @@ async function getConnectors({ } } +function isConnectorSupported( + action: FindActionResult, + actionTypes: Record +): boolean { + return ( + SUPPORTED_CONNECTORS.includes(action.actionTypeId) && + actionTypes[action.actionTypeId]?.enabledInLicense && + action.config != null && + !action.isPreconfigured + ); +} + async function update( configurationId: string, req: CasesConfigurePatch, From 18e392225e18cb94cef626c8015b475b1e5b4e44 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 17 Dec 2021 15:14:04 +0100 Subject: [PATCH 04/40] Update dependency broadcast-channel to ^4.8.0 (#121481) Co-authored-by: Renovate Bot --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 0a9c763781ffa..8777bb476de80 100644 --- a/package.json +++ b/package.json @@ -197,7 +197,7 @@ "base64-js": "^1.3.1", "bitmap-sdf": "^1.0.3", "brace": "0.11.1", - "broadcast-channel": "^4.7.1", + "broadcast-channel": "^4.8.0", "canvg": "^3.0.9", "chalk": "^4.1.0", "cheerio": "^1.0.0-rc.10", diff --git a/yarn.lock b/yarn.lock index 5e85a1832f4f9..6d41bda2de51e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8754,10 +8754,10 @@ broadcast-channel@^3.4.1: rimraf "3.0.2" unload "2.2.0" -broadcast-channel@^4.7.1: - version "4.7.1" - resolved "https://registry.yarnpkg.com/broadcast-channel/-/broadcast-channel-4.7.1.tgz#f2a5129cf4bf69c9f944b34e777a4fc907be407e" - integrity sha512-liKNu7gwUtBVyTzqx3Thg//7ZooKXfnXxFm/pjLPaxG3t8CquwqnobH8jtFe2FQenFduC2dUzkL1bIrld7auqg== +broadcast-channel@^4.8.0: + version "4.8.0" + resolved "https://registry.yarnpkg.com/broadcast-channel/-/broadcast-channel-4.8.0.tgz#1f2c1f1fd97e186c26793b044218ed44a9d4d7d8" + integrity sha512-Ei6MylH1YfQQql52zNyZaLu8UvD+BeWNUKcKc8KrqyA7z3Ap2tb6A8KQJm0EBO0SDiKKNaAyzrnkRHhrtG9pYQ== dependencies: "@babel/runtime" "^7.16.0" detect-node "^2.1.0" From 2e86979e3d665defa6722ae4cf81aacee5d031ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20St=C3=BCrmer?= Date: Fri, 17 Dec 2021 15:40:15 +0100 Subject: [PATCH 05/40] [Metrics UI] Add migrations for saved object references (#121214) Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- ...t_inventory_default_view_reference.test.ts | 89 +++++++++++++++++++ ...xtract_inventory_default_view_reference.ts | 25 ++++++ ...cs_explorer_default_view_reference.test.ts | 89 +++++++++++++++++++ ...metrics_explorer_default_view_reference.ts | 25 ++++++ .../migrations/compose_migrations.test.ts | 38 ++++++++ .../sources/migrations/compose_migrations.ts | 35 ++++++++ .../create_test_source_configuration.ts | 46 ++++++++++ .../lib/sources/saved_object_references.ts | 32 ++++--- .../server/lib/sources/saved_object_type.ts | 7 ++ 9 files changed, 375 insertions(+), 11 deletions(-) create mode 100644 x-pack/plugins/infra/server/lib/sources/migrations/7_16_2_extract_inventory_default_view_reference.test.ts create mode 100644 x-pack/plugins/infra/server/lib/sources/migrations/7_16_2_extract_inventory_default_view_reference.ts create mode 100644 x-pack/plugins/infra/server/lib/sources/migrations/7_16_2_extract_metrics_explorer_default_view_reference.test.ts create mode 100644 x-pack/plugins/infra/server/lib/sources/migrations/7_16_2_extract_metrics_explorer_default_view_reference.ts create mode 100644 x-pack/plugins/infra/server/lib/sources/migrations/compose_migrations.test.ts create mode 100644 x-pack/plugins/infra/server/lib/sources/migrations/compose_migrations.ts create mode 100644 x-pack/plugins/infra/server/lib/sources/migrations/create_test_source_configuration.ts diff --git a/x-pack/plugins/infra/server/lib/sources/migrations/7_16_2_extract_inventory_default_view_reference.test.ts b/x-pack/plugins/infra/server/lib/sources/migrations/7_16_2_extract_inventory_default_view_reference.test.ts new file mode 100644 index 0000000000000..465545618c8d7 --- /dev/null +++ b/x-pack/plugins/infra/server/lib/sources/migrations/7_16_2_extract_inventory_default_view_reference.test.ts @@ -0,0 +1,89 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { migrationMocks } from 'src/core/server/mocks'; +import { SavedObjectReference } from '../../../../../../../src/core/server'; +import { + inventoryDefaultViewReferenceName, + logIndexPatternReferenceName, +} from '../saved_object_references'; +import { extractInventoryDefaultViewReference } from './7_16_2_extract_inventory_default_view_reference'; +import { createTestSourceConfiguration } from './create_test_source_configuration'; + +describe('infra source configuration migration function for inventory default views in 7.16.2', () => { + test('migrates the inventoryDefaultView to be a reference', () => { + const initialReferences: SavedObjectReference[] = [ + { + type: 'index-pattern', + name: logIndexPatternReferenceName, + id: 'TEST LOG INDEX PATTERN', + }, + ]; + const unmigratedConfiguration = createTestSourceConfiguration( + { + inventoryDefaultView: 'TEST UUID', + }, + initialReferences + ); + + const migratedConfiguration = extractInventoryDefaultViewReference( + unmigratedConfiguration, + migrationMocks.createContext() + ); + + expect(migratedConfiguration).toStrictEqual( + createTestSourceConfiguration( + { + inventoryDefaultView: inventoryDefaultViewReferenceName, + }, + [ + ...initialReferences, + { + type: 'inventory-view', + name: inventoryDefaultViewReferenceName, + id: 'TEST UUID', + }, + ] + ) + ); + }); + + test('ignores already migrated inventoryDefaultView references', () => { + const initialReferences: SavedObjectReference[] = [ + { + type: 'index-pattern', + name: logIndexPatternReferenceName, + id: 'TEST LOG INDEX PATTERN', + }, + { + type: 'inventory-view', + name: inventoryDefaultViewReferenceName, + id: 'TEST UUID', + }, + ]; + const unmigratedConfiguration = createTestSourceConfiguration( + { + inventoryDefaultView: inventoryDefaultViewReferenceName, + }, + initialReferences + ); + + const migratedConfiguration = extractInventoryDefaultViewReference( + unmigratedConfiguration, + migrationMocks.createContext() + ); + + expect(migratedConfiguration).toStrictEqual( + createTestSourceConfiguration( + { + inventoryDefaultView: inventoryDefaultViewReferenceName, + }, + initialReferences + ) + ); + }); +}); diff --git a/x-pack/plugins/infra/server/lib/sources/migrations/7_16_2_extract_inventory_default_view_reference.ts b/x-pack/plugins/infra/server/lib/sources/migrations/7_16_2_extract_inventory_default_view_reference.ts new file mode 100644 index 0000000000000..745359270cc4c --- /dev/null +++ b/x-pack/plugins/infra/server/lib/sources/migrations/7_16_2_extract_inventory_default_view_reference.ts @@ -0,0 +1,25 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { SavedObjectMigrationFn } from 'src/core/server'; +import { InfraSourceConfiguration } from '../../../../common/source_configuration/source_configuration'; +import { extractInventorySavedViewReferences } from '../saved_object_references'; + +export const extractInventoryDefaultViewReference: SavedObjectMigrationFn< + InfraSourceConfiguration, + InfraSourceConfiguration +> = (sourceConfigurationDocument) => { + const { attributes, references } = extractInventorySavedViewReferences( + sourceConfigurationDocument.attributes + ); + + return { + ...sourceConfigurationDocument, + attributes, + references: [...(sourceConfigurationDocument.references ?? []), ...references], + }; +}; diff --git a/x-pack/plugins/infra/server/lib/sources/migrations/7_16_2_extract_metrics_explorer_default_view_reference.test.ts b/x-pack/plugins/infra/server/lib/sources/migrations/7_16_2_extract_metrics_explorer_default_view_reference.test.ts new file mode 100644 index 0000000000000..6a97193211034 --- /dev/null +++ b/x-pack/plugins/infra/server/lib/sources/migrations/7_16_2_extract_metrics_explorer_default_view_reference.test.ts @@ -0,0 +1,89 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { migrationMocks } from 'src/core/server/mocks'; +import { SavedObjectReference } from '../../../../../../../src/core/server'; +import { + logIndexPatternReferenceName, + metricsExplorerDefaultViewReferenceName, +} from '../saved_object_references'; +import { extractMetricsExplorerDefaultViewReference } from './7_16_2_extract_metrics_explorer_default_view_reference'; +import { createTestSourceConfiguration } from './create_test_source_configuration'; + +describe('infra source configuration migration function for metrics explorer default views in 7.16.2', () => { + test('migrates the metricsExplorerDefaultView to be a reference', () => { + const initialReferences: SavedObjectReference[] = [ + { + type: 'index-pattern', + name: logIndexPatternReferenceName, + id: 'TEST LOG INDEX PATTERN', + }, + ]; + const unmigratedConfiguration = createTestSourceConfiguration( + { + metricsExplorerDefaultView: 'TEST UUID', + }, + initialReferences + ); + + const migratedConfiguration = extractMetricsExplorerDefaultViewReference( + unmigratedConfiguration, + migrationMocks.createContext() + ); + + expect(migratedConfiguration).toStrictEqual( + createTestSourceConfiguration( + { + metricsExplorerDefaultView: metricsExplorerDefaultViewReferenceName, + }, + [ + ...initialReferences, + { + type: 'metrics-explorer-view', + name: metricsExplorerDefaultViewReferenceName, + id: 'TEST UUID', + }, + ] + ) + ); + }); + + test('ignores already migrated metricsExplorerDefaultView references', () => { + const initialReferences: SavedObjectReference[] = [ + { + type: 'index-pattern', + name: logIndexPatternReferenceName, + id: 'TEST LOG INDEX PATTERN', + }, + { + type: 'metrics-explorer-view', + name: metricsExplorerDefaultViewReferenceName, + id: 'TEST UUID', + }, + ]; + const unmigratedConfiguration = createTestSourceConfiguration( + { + metricsExplorerDefaultView: metricsExplorerDefaultViewReferenceName, + }, + initialReferences + ); + + const migratedConfiguration = extractMetricsExplorerDefaultViewReference( + unmigratedConfiguration, + migrationMocks.createContext() + ); + + expect(migratedConfiguration).toStrictEqual( + createTestSourceConfiguration( + { + metricsExplorerDefaultView: metricsExplorerDefaultViewReferenceName, + }, + initialReferences + ) + ); + }); +}); diff --git a/x-pack/plugins/infra/server/lib/sources/migrations/7_16_2_extract_metrics_explorer_default_view_reference.ts b/x-pack/plugins/infra/server/lib/sources/migrations/7_16_2_extract_metrics_explorer_default_view_reference.ts new file mode 100644 index 0000000000000..4ef9024ccb9ce --- /dev/null +++ b/x-pack/plugins/infra/server/lib/sources/migrations/7_16_2_extract_metrics_explorer_default_view_reference.ts @@ -0,0 +1,25 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { SavedObjectMigrationFn } from 'src/core/server'; +import { InfraSourceConfiguration } from '../../../../common/source_configuration/source_configuration'; +import { extractMetricsExplorerSavedViewReferences } from '../saved_object_references'; + +export const extractMetricsExplorerDefaultViewReference: SavedObjectMigrationFn< + InfraSourceConfiguration, + InfraSourceConfiguration +> = (sourceConfigurationDocument) => { + const { attributes, references } = extractMetricsExplorerSavedViewReferences( + sourceConfigurationDocument.attributes + ); + + return { + ...sourceConfigurationDocument, + attributes, + references: [...(sourceConfigurationDocument.references ?? []), ...references], + }; +}; diff --git a/x-pack/plugins/infra/server/lib/sources/migrations/compose_migrations.test.ts b/x-pack/plugins/infra/server/lib/sources/migrations/compose_migrations.test.ts new file mode 100644 index 0000000000000..f7612fbe1dbcb --- /dev/null +++ b/x-pack/plugins/infra/server/lib/sources/migrations/compose_migrations.test.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 + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { SavedObjectUnsanitizedDoc } from '../../../../../../../src/core/server'; +import { migrationMocks } from '../../../../../../../src/core/server/mocks'; +import { composeMigrations } from './compose_migrations'; + +type TestDocument = SavedObjectUnsanitizedDoc<{ + a: number; +}>; + +describe('composeMigrations function', () => { + test('correctly composes two migration functions', () => { + const firstMigration = jest.fn( + (doc: TestDocument): TestDocument => ({ + ...doc, + attributes: { ...doc.attributes, a: doc.attributes.a + 1 }, + }) + ); + const secondMigration = jest.fn( + (doc: TestDocument): TestDocument => ({ + ...doc, + attributes: { ...doc.attributes, a: doc.attributes.a ** 2 }, + }) + ); + + const composedMigrations = composeMigrations(firstMigration, secondMigration); + const migrationContext = migrationMocks.createContext(); + + expect( + composedMigrations({ id: 'ID', type: 'TYPE', attributes: { a: 1 } }, migrationContext) + ).toStrictEqual({ id: 'ID', type: 'TYPE', attributes: { a: 4 } }); + }); +}); diff --git a/x-pack/plugins/infra/server/lib/sources/migrations/compose_migrations.ts b/x-pack/plugins/infra/server/lib/sources/migrations/compose_migrations.ts new file mode 100644 index 0000000000000..561685eb47127 --- /dev/null +++ b/x-pack/plugins/infra/server/lib/sources/migrations/compose_migrations.ts @@ -0,0 +1,35 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { SavedObjectMigrationFn } from '../../../../../../../src/core/server'; + +type InputAttributesOf = MigrationFn extends SavedObjectMigrationFn< + infer InputAttributes, + any +> + ? InputAttributes + : never; +type MigratedAttributesOf = MigrationFn extends SavedObjectMigrationFn< + any, + infer MigratedAttributes +> + ? MigratedAttributes + : never; + +export function composeMigrations< + FirstFn extends SavedObjectMigrationFn, + IntermediateFns extends SavedObjectMigrationFn[], + LastFn extends SavedObjectMigrationFn +>( + ...migrations: [FirstFn, ...IntermediateFns, LastFn] +): SavedObjectMigrationFn, MigratedAttributesOf> { + return (doc, context) => + migrations.reduce( + (migratedDoc, nextMigration) => nextMigration(migratedDoc, context), + doc as any + ); +} diff --git a/x-pack/plugins/infra/server/lib/sources/migrations/create_test_source_configuration.ts b/x-pack/plugins/infra/server/lib/sources/migrations/create_test_source_configuration.ts new file mode 100644 index 0000000000000..daf8f60885811 --- /dev/null +++ b/x-pack/plugins/infra/server/lib/sources/migrations/create_test_source_configuration.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 + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { + SavedObjectReference, + SavedObjectUnsanitizedDoc, +} from '../../../../../../../src/core/server'; +import { InfraSourceConfiguration } from '../../../../common/source_configuration/source_configuration'; +import { infraSourceConfigurationSavedObjectName } from '../saved_object_type'; + +export const createTestSourceConfiguration = ( + overrideAttributes: Partial = {}, + initialReferences: SavedObjectReference[] = [] +): SavedObjectUnsanitizedDoc => ({ + attributes: { + name: 'TEST CONFIGURATION', + description: '', + fields: { + message: ['TEST MESSAGE FIELD'], + }, + inventoryDefaultView: '0', + metricsExplorerDefaultView: '0', + logColumns: [ + { + fieldColumn: { + id: 'TEST FIELD COLUMN ID', + field: 'TEST FIELD COLUMN FIELD', + }, + }, + ], + logIndices: { + type: 'index_name', + indexName: 'TEST INDEX', + }, + metricAlias: 'metricbeat-*,metrics-*', + anomalyThreshold: 20, + ...overrideAttributes, + }, + id: 'TEST_ID', + type: infraSourceConfigurationSavedObjectName, + references: initialReferences, +}); diff --git a/x-pack/plugins/infra/server/lib/sources/saved_object_references.ts b/x-pack/plugins/infra/server/lib/sources/saved_object_references.ts index 9ad8c9951d4f3..1e3a3acce8926 100644 --- a/x-pack/plugins/infra/server/lib/sources/saved_object_references.ts +++ b/x-pack/plugins/infra/server/lib/sources/saved_object_references.ts @@ -12,7 +12,9 @@ import { } from '../../../common/source_configuration/source_configuration'; import { SavedObjectReferenceResolutionError } from './errors'; -const logIndexPatternReferenceName = 'log_index_pattern_0'; +export const logIndexPatternReferenceName = 'log_index_pattern_0'; +export const inventoryDefaultViewReferenceName = 'inventory-saved-view-0'; +export const metricsExplorerDefaultViewReferenceName = 'metrics-explorer-saved-view-0'; interface SavedObjectAttributesWithReferences { attributes: SavedObjectAttributes; @@ -91,15 +93,19 @@ const extractLogIndicesSavedObjectReferences = ( } }; -const extractInventorySavedViewReferences = ( +export const extractInventorySavedViewReferences = ( sourceConfiguration: InfraSourceConfiguration ): SavedObjectAttributesWithReferences => { const { inventoryDefaultView } = sourceConfiguration; - if (inventoryDefaultView && inventoryDefaultView !== '0') { + if ( + inventoryDefaultView && + inventoryDefaultView !== '0' && + inventoryDefaultView !== inventoryDefaultViewReferenceName + ) { const inventoryDefaultViewReference: SavedObjectReference = { id: inventoryDefaultView, type: 'inventory-view', - name: 'inventory-saved-view-0', + name: inventoryDefaultViewReferenceName, }; const attributes: InfraSourceConfiguration = { ...sourceConfiguration, @@ -117,15 +123,19 @@ const extractInventorySavedViewReferences = ( } }; -const extractMetricsExplorerSavedViewReferences = ( +export const extractMetricsExplorerSavedViewReferences = ( sourceConfiguration: InfraSourceConfiguration ): SavedObjectAttributesWithReferences => { const { metricsExplorerDefaultView } = sourceConfiguration; - if (metricsExplorerDefaultView && metricsExplorerDefaultView !== '0') { + if ( + metricsExplorerDefaultView && + metricsExplorerDefaultView !== '0' && + metricsExplorerDefaultView !== metricsExplorerDefaultViewReferenceName + ) { const metricsExplorerDefaultViewReference: SavedObjectReference = { id: metricsExplorerDefaultView, type: 'metrics-explorer-view', - name: 'metrics-explorer-saved-view-0', + name: metricsExplorerDefaultViewReferenceName, }; const attributes: InfraSourceConfiguration = { ...sourceConfiguration, @@ -176,12 +186,12 @@ const resolveInventoryViewSavedObjectReferences = ( ): InfraSavedSourceConfiguration => { if (attributes.inventoryDefaultView && attributes.inventoryDefaultView !== '0') { const inventoryViewReference = references.find( - (reference) => reference.name === 'inventory-saved-view-0' + (reference) => reference.name === inventoryDefaultViewReferenceName ); if (inventoryViewReference == null) { throw new SavedObjectReferenceResolutionError( - 'Failed to resolve Inventory default view "inventory-saved-view-0".' + `Failed to resolve Inventory default view "${inventoryDefaultViewReferenceName}".` ); } @@ -200,12 +210,12 @@ const resolveMetricsExplorerSavedObjectReferences = ( ): InfraSavedSourceConfiguration => { if (attributes.metricsExplorerDefaultView && attributes.metricsExplorerDefaultView !== '0') { const metricsExplorerViewReference = references.find( - (reference) => reference.name === 'metrics-explorer-saved-view-0' + (reference) => reference.name === metricsExplorerDefaultViewReferenceName ); if (metricsExplorerViewReference == null) { throw new SavedObjectReferenceResolutionError( - 'Failed to resolve Metrics Explorer default view "metrics-explorer-saved-view-0".' + `Failed to resolve Metrics Explorer default view "${metricsExplorerDefaultViewReferenceName}".` ); } diff --git a/x-pack/plugins/infra/server/lib/sources/saved_object_type.ts b/x-pack/plugins/infra/server/lib/sources/saved_object_type.ts index 135f715d8b604..c150a51d78c9c 100644 --- a/x-pack/plugins/infra/server/lib/sources/saved_object_type.ts +++ b/x-pack/plugins/infra/server/lib/sources/saved_object_type.ts @@ -8,6 +8,9 @@ import { SavedObjectsType } from 'src/core/server'; import { addNewIndexingStrategyIndexNames } from './migrations/7_9_0_add_new_indexing_strategy_index_names'; import { convertLogAliasToLogIndices } from './migrations/7_13_0_convert_log_alias_to_log_indices'; +import { composeMigrations } from './migrations/compose_migrations'; +import { extractInventoryDefaultViewReference } from './migrations/7_16_2_extract_inventory_default_view_reference'; +import { extractMetricsExplorerDefaultViewReference } from './migrations/7_16_2_extract_metrics_explorer_default_view_reference'; export const infraSourceConfigurationSavedObjectName = 'infrastructure-ui-source'; @@ -25,5 +28,9 @@ export const infraSourceConfigurationSavedObjectType: SavedObjectsType = { migrations: { '7.9.0': addNewIndexingStrategyIndexNames, '7.13.0': convertLogAliasToLogIndices, + '7.16.2': composeMigrations( + extractInventoryDefaultViewReference, + extractMetricsExplorerDefaultViewReference + ), }, }; From 38411efe3894476a0b55dcbed7799825a09b27c5 Mon Sep 17 00:00:00 2001 From: Marta Bondyra Date: Fri, 17 Dec 2021 16:12:19 +0100 Subject: [PATCH 06/40] [Lens] [Gauge] Show invalid dimension indicators when minimum value is equal or bigger to maximum value (#121201) --- .../public/components/index.scss | 4 +- .../public/components/utils.test.ts | 2 +- .../public/components/utils.ts | 2 +- .../expression_gauge/public/index.ts | 2 +- .../gauge/visualization.test.ts | 81 +++++++++++++++++++ .../visualizations/gauge/visualization.tsx | 36 ++++++++- 6 files changed, 121 insertions(+), 6 deletions(-) diff --git a/src/plugins/chart_expressions/expression_gauge/public/components/index.scss b/src/plugins/chart_expressions/expression_gauge/public/components/index.scss index a221ba25fcc5c..22a1e4318b5e5 100644 --- a/src/plugins/chart_expressions/expression_gauge/public/components/index.scss +++ b/src/plugins/chart_expressions/expression_gauge/public/components/index.scss @@ -4,7 +4,7 @@ width: 100%; // the FocusTrap is adding extra divs which are making the visualization redraw twice // with a visible glitch. This make the chart library resilient to this extra reflow - overflow: auto hidden; + overflow: hidden; user-select: text; padding: $euiSizeS; -} \ No newline at end of file +} diff --git a/src/plugins/chart_expressions/expression_gauge/public/components/utils.test.ts b/src/plugins/chart_expressions/expression_gauge/public/components/utils.test.ts index d45ff5c11a223..570ed0db986c9 100644 --- a/src/plugins/chart_expressions/expression_gauge/public/components/utils.test.ts +++ b/src/plugins/chart_expressions/expression_gauge/public/components/utils.test.ts @@ -51,7 +51,7 @@ describe('expression gauge utils', () => { expect(getMaxValue({ metric: 10 }, localState)).toEqual(15); expect(getMaxValue({ min: 0, metric: 2 }, localState)).toEqual(4); expect(getMaxValue({ min: -100, metric: 2 }, localState)).toEqual(50); - expect(getMaxValue({ min: -0.001, metric: 0 }, localState)).toEqual(0.001); + expect(getMaxValue({ min: -0.001, metric: 0 }, localState)).toEqual(1); expect(getMaxValue({ min: -2000, metric: -1000 }, localState)).toEqual(-500); expect(getMaxValue({ min: 0.5, metric: 1.5 }, localState)).toEqual(2); }); diff --git a/src/plugins/chart_expressions/expression_gauge/public/components/utils.ts b/src/plugins/chart_expressions/expression_gauge/public/components/utils.ts index a60c4a8a0a608..c2a11515a5b0a 100644 --- a/src/plugins/chart_expressions/expression_gauge/public/components/utils.ts +++ b/src/plugins/chart_expressions/expression_gauge/public/components/utils.ts @@ -40,7 +40,7 @@ function getNiceRange(min: number, max: number) { const tickSpacing = getNiceNumber(range / (maxTicks - 1)); return { min: Math.floor(min / tickSpacing) * tickSpacing, - max: Math.ceil(offsetMax / tickSpacing) * tickSpacing, + max: Math.ceil(Math.ceil(offsetMax / tickSpacing) * tickSpacing), }; } diff --git a/src/plugins/chart_expressions/expression_gauge/public/index.ts b/src/plugins/chart_expressions/expression_gauge/public/index.ts index c01a3a9a42b9b..62e287520b45a 100644 --- a/src/plugins/chart_expressions/expression_gauge/public/index.ts +++ b/src/plugins/chart_expressions/expression_gauge/public/index.ts @@ -12,5 +12,5 @@ export function plugin() { return new ExpressionGaugePlugin(); } -export { getGoalValue, getMaxValue, getMinValue } from './components/utils'; +export { getGoalValue, getMaxValue, getMinValue, getValueFromAccessor } from './components/utils'; export { GaugeIconVertical, GaugeIconHorizontal } from './components/gauge_icon'; diff --git a/x-pack/plugins/lens/public/visualizations/gauge/visualization.test.ts b/x-pack/plugins/lens/public/visualizations/gauge/visualization.test.ts index c564c168c5b0c..20d86e73d6521 100644 --- a/x-pack/plugins/lens/public/visualizations/gauge/visualization.test.ts +++ b/x-pack/plugins/lens/public/visualizations/gauge/visualization.test.ts @@ -280,6 +280,87 @@ describe('gauge', () => { ], }); }); + + test('resolves configuration when with group error when max < minimum', () => { + const state: GaugeVisualizationState = { + ...exampleState(), + layerId: 'first', + metricAccessor: 'metric-accessor', + minAccessor: 'min-accessor', + maxAccessor: 'max-accessor', + goalAccessor: 'goal-accessor', + }; + frame.activeData = { + first: { + type: 'datatable', + columns: [], + rows: [{ 'min-accessor': 10, 'max-accessor': 0 }], + }, + }; + + expect( + getGaugeVisualization({ + paletteService, + }).getConfiguration({ state, frame, layerId: 'first' }) + ).toEqual({ + groups: [ + { + layerId: 'first', + groupId: GROUP_ID.METRIC, + groupLabel: 'Metric', + accessors: [{ columnId: 'metric-accessor', triggerIcon: 'none' }], + filterOperations: isNumericDynamicMetric, + supportsMoreColumns: false, + required: true, + dataTestSubj: 'lnsGauge_metricDimensionPanel', + enableDimensionEditor: true, + supportFieldFormat: true, + }, + { + layerId: 'first', + groupId: GROUP_ID.MIN, + groupLabel: 'Minimum value', + accessors: [{ columnId: 'min-accessor' }], + filterOperations: isNumericMetric, + supportsMoreColumns: false, + dataTestSubj: 'lnsGauge_minDimensionPanel', + prioritizedOperation: 'min', + suggestedValue: expect.any(Function), + supportFieldFormat: false, + supportStaticValue: true, + invalid: true, + invalidMessage: 'Minimum value may not be greater than maximum value', + }, + { + layerId: 'first', + groupId: GROUP_ID.MAX, + groupLabel: 'Maximum value', + accessors: [{ columnId: 'max-accessor' }], + filterOperations: isNumericMetric, + supportsMoreColumns: false, + dataTestSubj: 'lnsGauge_maxDimensionPanel', + prioritizedOperation: 'max', + suggestedValue: expect.any(Function), + supportFieldFormat: false, + supportStaticValue: true, + invalid: true, + invalidMessage: 'Minimum value may not be greater than maximum value', + }, + { + layerId: 'first', + groupId: GROUP_ID.GOAL, + groupLabel: 'Goal value', + accessors: [{ columnId: 'goal-accessor' }], + filterOperations: isNumericMetric, + supportsMoreColumns: false, + required: false, + dataTestSubj: 'lnsGauge_goalDimensionPanel', + supportFieldFormat: false, + supportStaticValue: true, + }, + ], + }); + }); }); describe('#setDimension', () => { diff --git a/x-pack/plugins/lens/public/visualizations/gauge/visualization.tsx b/x-pack/plugins/lens/public/visualizations/gauge/visualization.tsx index 1ab7cf5d97648..2c409ddacaf0f 100644 --- a/x-pack/plugins/lens/public/visualizations/gauge/visualization.tsx +++ b/x-pack/plugins/lens/public/visualizations/gauge/visualization.tsx @@ -10,6 +10,7 @@ import { render } from 'react-dom'; import { i18n } from '@kbn/i18n'; import { FormattedMessage, I18nProvider } from '@kbn/i18n-react'; import { Ast } from '@kbn/interpreter/common'; +import { DatatableRow } from 'src/plugins/expressions'; import type { GaugeArguments } from '../../../../../../src/plugins/chart_expressions/expression_gauge/common'; import { GaugeShapes, @@ -19,6 +20,7 @@ import { getGoalValue, getMaxValue, getMinValue, + getValueFromAccessor, GaugeIconVertical, GaugeIconHorizontal, } from '../../../../../../src/plugins/chart_expressions/expression_gauge/public'; @@ -73,6 +75,35 @@ function computePaletteParams(params: CustomPaletteParams) { }; } +const checkInvalidConfiguration = (row?: DatatableRow, state?: GaugeVisualizationState) => { + if (!row || !state) { + return; + } + const minValue = getValueFromAccessor('minAccessor', row, state); + const maxValue = getValueFromAccessor('maxAccessor', row, state); + if (maxValue != null && minValue != null) { + if (maxValue < minValue) { + return { + invalid: true, + invalidMessage: i18n.translate( + 'xpack.lens.guageVisualization.chartCannotRenderMinGreaterMax', + { + defaultMessage: 'Minimum value may not be greater than maximum value', + } + ), + }; + } + if (maxValue === minValue) { + return { + invalid: true, + invalidMessage: i18n.translate('xpack.lens.guageVisualization.chartCannotRenderEqual', { + defaultMessage: 'Minimum and maximum values may not be equal', + }), + }; + } + } +}; + const toExpression = ( paletteService: PaletteRegistry, state: GaugeVisualizationState, @@ -194,6 +225,7 @@ export const getGaugeVisualization = ({ const displayStops = applyPaletteParams(paletteService, state?.palette, currentMinMax); palette = getStopsForFixedMode(displayStops, state?.palette?.params?.colorStops); } + const invalidProps = checkInvalidConfiguration(row, state) || {}; return { groups: [ @@ -238,6 +270,7 @@ export const getGaugeVisualization = ({ dataTestSubj: 'lnsGauge_minDimensionPanel', prioritizedOperation: 'min', suggestedValue: () => (state.metricAccessor ? getMinValue(row, state) : undefined), + ...invalidProps, }, { supportStaticValue: true, @@ -253,6 +286,7 @@ export const getGaugeVisualization = ({ dataTestSubj: 'lnsGauge_maxDimensionPanel', prioritizedOperation: 'max', suggestedValue: () => (state.metricAccessor ? getMaxValue(row, state) : undefined), + ...invalidProps, }, { supportStaticValue: true, @@ -401,7 +435,7 @@ export const getGaugeVisualization = ({ } const row = frame?.activeData?.[state.layerId]?.rows?.[0]; - if (!row) { + if (!row || checkInvalidConfiguration(row, state)) { return []; } const metricValue = row[metricAccessor]; From 4c1d1aef738084625fa0f9961e27b2fea0c750df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20S=C3=A1nchez?= Date: Fri, 17 Dec 2021 16:30:18 +0100 Subject: [PATCH 07/40] [Security Solution] [Endpoint] Unify loading states for artifacts lists (#121388) * Creates new page loader component under management and use it in different places (TBD) * Use new ManagementPageLoader component in artifact pages under management * Remove unused euiProgress component * Fix typo * Fix other typos Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../components/management_page_loader.tsx | 22 +++++++++++++++++++ .../view/event_filters_list_page.test.tsx | 2 +- .../view/event_filters_list_page.tsx | 5 +++++ .../host_isolation_exceptions_list.test.tsx | 4 ++-- .../view/host_isolation_exceptions_list.tsx | 5 +++++ .../layout/policy_event_filters_layout.tsx | 9 ++------ .../host_isolation_exceptions_tab.tsx | 8 ++----- .../layout/policy_trusted_apps_layout.tsx | 4 ++-- .../trusted_apps/view/trusted_apps_page.tsx | 16 +++----------- 9 files changed, 44 insertions(+), 31 deletions(-) create mode 100644 x-pack/plugins/security_solution/public/management/components/management_page_loader.tsx diff --git a/x-pack/plugins/security_solution/public/management/components/management_page_loader.tsx b/x-pack/plugins/security_solution/public/management/components/management_page_loader.tsx new file mode 100644 index 0000000000000..047b4ca1efd50 --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/components/management_page_loader.tsx @@ -0,0 +1,22 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { memo } from 'react'; +import { EuiLoadingSpinner } from '@elastic/eui'; +import { ManagementEmptyStateWraper } from './management_empty_state_wraper'; + +export const ManagementPageLoader = memo<{ 'data-test-subj': string }>( + ({ 'data-test-subj': dataTestSubj }) => { + return ( + + + + ); + } +); + +ManagementPageLoader.displayName = 'ManagementPageLoader'; diff --git a/x-pack/plugins/security_solution/public/management/pages/event_filters/view/event_filters_list_page.test.tsx b/x-pack/plugins/security_solution/public/management/pages/event_filters/view/event_filters_list_page.test.tsx index 63d3e64d295e0..094c964576f7e 100644 --- a/x-pack/plugins/security_solution/public/management/pages/event_filters/view/event_filters_list_page.test.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/event_filters/view/event_filters_list_page.test.tsx @@ -98,7 +98,7 @@ describe('When on the Event Filters List Page', () => { ); render(); - expect(renderResult.getByTestId('eventFiltersContent-loader')).toBeTruthy(); + expect(renderResult.getByTestId('eventFilterListLoader')).toBeTruthy(); const wasReceived = dataReceived(); releaseApiResponse!(); diff --git a/x-pack/plugins/security_solution/public/management/pages/event_filters/view/event_filters_list_page.tsx b/x-pack/plugins/security_solution/public/management/pages/event_filters/view/event_filters_list_page.tsx index 2b194be1ddc64..fcbf3d9489b96 100644 --- a/x-pack/plugins/security_solution/public/management/pages/event_filters/view/event_filters_list_page.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/event_filters/view/event_filters_list_page.tsx @@ -51,6 +51,7 @@ import { useGetEndpointSpecificPolicies } from '../../../services/policies/hooks import { useToasts } from '../../../../common/lib/kibana'; import { getLoadPoliciesError } from '../../../common/translations'; import { useEndpointPoliciesToArtifactPolicies } from '../../../components/artifact_entry_card/hooks/use_endpoint_policies_to_artifact_policies'; +import { ManagementPageLoader } from '../../../components/management_page_loader'; type ArtifactEntryCardType = typeof ArtifactEntryCard; @@ -234,6 +235,10 @@ export const EventFiltersListPage = memo(() => { [artifactCardPropsPerItem] ); + if (isLoading && !doesDataExist) { + return ; + } + return ( { await waitForApiCall(); // see if loader is present - expect(renderResult.getByTestId('hostIsolationExceptionsContent-loader')).toBeTruthy(); + expect(renderResult.getByTestId('hostIsolationExceptionListLoader')).toBeTruthy(); // release the request releaseApiResponse!(getFoundExceptionListItemSchemaMock()); // check the loader is gone await waitForElementToBeRemoved( - renderResult.getByTestId('hostIsolationExceptionsContent-loader') + renderResult.getByTestId('hostIsolationExceptionListLoader') ); }); diff --git a/x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/view/host_isolation_exceptions_list.tsx b/x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/view/host_isolation_exceptions_list.tsx index 21e511fed6c7d..cd27a88a4e66d 100644 --- a/x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/view/host_isolation_exceptions_list.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/view/host_isolation_exceptions_list.tsx @@ -24,6 +24,7 @@ import { getLoadPoliciesError } from '../../../common/translations'; import { AdministrationListPage } from '../../../components/administration_list_page'; import { ArtifactEntryCard, ArtifactEntryCardProps } from '../../../components/artifact_entry_card'; import { useEndpointPoliciesToArtifactPolicies } from '../../../components/artifact_entry_card/hooks/use_endpoint_policies_to_artifact_policies'; +import { ManagementPageLoader } from '../../../components/management_page_loader'; import { PaginatedContent, PaginatedContentProps } from '../../../components/paginated_content'; import { SearchExceptions } from '../../../components/search_exceptions'; import { useGetEndpointSpecificPolicies } from '../../../services/policies/hooks'; @@ -171,6 +172,10 @@ export const HostIsolationExceptionsList = () => { [navigateCallback] ); + if (isLoading && !hasDataToShow) { + return ; + } + return ( allAssigned && allAssigned.total === 0, [allAssigned]); if (!policyItem || isGlobalLoading) { - return ( - - - - ); + return ; } if (isEmptyState) { diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/host_isolation_exceptions/host_isolation_exceptions_tab.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/host_isolation_exceptions/host_isolation_exceptions_tab.tsx index c2df907917c5e..ab2ab26a08c29 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/host_isolation_exceptions/host_isolation_exceptions_tab.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/host_isolation_exceptions/host_isolation_exceptions_tab.tsx @@ -9,7 +9,6 @@ import { EuiPageContent, EuiPageHeader, EuiPageHeaderSection, - EuiProgress, EuiSpacer, EuiText, EuiTitle, @@ -31,6 +30,7 @@ import { usePolicyDetailsSelector } from '../policy_hooks'; import { PolicyHostIsolationExceptionsEmptyUnexisting } from './components/empty_unexisting'; import { PolicyHostIsolationExceptionsEmptyUnassigned } from './components/empty_unassigned'; import { PolicyHostIsolationExceptionsList } from './components/list'; +import { ManagementPageLoader } from '../../../../components/management_page_loader'; export const PolicyHostIsolationExceptionsTab = ({ policy }: { policy: PolicyData }) => { const { getAppUrl } = useAppUrl(); @@ -154,11 +154,7 @@ export const PolicyHostIsolationExceptionsTab = ({ policy }: { policy: PolicyDat

) : ( - + ); }; PolicyHostIsolationExceptionsTab.displayName = 'PolicyHostIsolationExceptionsTab'; diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/trusted_apps/layout/policy_trusted_apps_layout.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/trusted_apps/layout/policy_trusted_apps_layout.tsx index 3cf8e60c5e168..83fb3663104a6 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/trusted_apps/layout/policy_trusted_apps_layout.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/trusted_apps/layout/policy_trusted_apps_layout.tsx @@ -17,7 +17,6 @@ import { EuiText, EuiSpacer, EuiLink, - EuiProgress, } from '@elastic/eui'; import { PolicyTrustedAppsEmptyUnassigned, PolicyTrustedAppsEmptyUnexisting } from '../empty'; import { @@ -36,6 +35,7 @@ import { useAppUrl } from '../../../../../../common/lib/kibana'; import { APP_UI_ID } from '../../../../../../../common/constants'; import { getTrustedAppsListPath } from '../../../../../common/routing'; import { useUserPrivileges } from '../../../../../../common/components/user_privileges'; +import { ManagementPageLoader } from '../../../../../components/management_page_loader'; export const PolicyTrustedAppsLayout = React.memo(() => { const { getAppUrl } = useAppUrl(); @@ -168,7 +168,7 @@ export const PolicyTrustedAppsLayout = React.memo(() => { ) : displayHeaderAndContent ? ( ) : ( - + )} {canCreateArtifactsByPolicy && showListFlyout ? : null} diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/trusted_apps_page.tsx b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/trusted_apps_page.tsx index 2382877ce6682..63cb52785ea96 100644 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/trusted_apps_page.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/trusted_apps_page.tsx @@ -10,15 +10,7 @@ import { useDispatch } from 'react-redux'; import { Dispatch } from 'redux'; import { useLocation } from 'react-router-dom'; import { FormattedMessage } from '@kbn/i18n-react'; -import { - EuiButton, - EuiEmptyPrompt, - EuiFlexGroup, - EuiFlexItem, - EuiLoadingSpinner, - EuiSpacer, - EuiText, -} from '@elastic/eui'; +import { EuiButton, EuiFlexGroup, EuiFlexItem, EuiSpacer, EuiText } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { @@ -42,6 +34,7 @@ import { SearchExceptions } from '../../../components/search_exceptions'; import { BackToExternalAppButton } from '../../../components/back_to_external_app_button'; import { ListPageRouteState } from '../../../../../common/endpoint/types'; import { useIsExperimentalFeatureEnabled } from '../../../../common/hooks/use_experimental_features'; +import { ManagementPageLoader } from '../../../components/management_page_loader'; export const TrustedAppsPage = memo(() => { const isTrustedAppsByPolicyEnabled = useIsExperimentalFeatureEnabled( @@ -177,10 +170,7 @@ export const TrustedAppsPage = memo(() => { {isCheckingIfEntriesExists && !didEntriesExist ? ( - } - /> + ) : ( content )} From b1b3726458d0e286b09f0883f52ed110e6d397f3 Mon Sep 17 00:00:00 2001 From: Ashokaditya <1849116+ashokaditya@users.noreply.github.com> Date: Fri, 17 Dec 2021 16:53:01 +0100 Subject: [PATCH 08/40] [Security Solution][Endpoint] Fix UX bugs (#121410) * move back button over flyout header fixes elastic/security-team/issues/2394 * remove border on details and activity log tabs fixes elastic/security-team/issues/2395 * update isolation action icons fixes elastic/security-team/issues/2397 * update link copy fixes security-team/issues/2398 * fix gutter size fixes elastic/security-team/issues/2396 * fix refresh button fill color fixes elastic/security-team/issues/2396 * rename review changes * redundant classnames, implicit prop value review change * consistent prop name all the way through Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../query_string_input/query_bar_top_row.tsx | 39 ++++----- .../data/public/ui/search_bar/search_bar.tsx | 3 + .../view/components/search_bar.tsx | 1 + ...k_to_endpoint_details_flyout_subheader.tsx | 35 +++++--- .../components/endpoint_details_tabs.tsx | 4 +- .../endpoint_isolate_flyout_panel.tsx | 10 +-- .../components/flyout_body_no_top_padding.tsx | 19 ----- .../view/details/components/flyout_header.tsx | 5 ++ .../details/components/flyout_sub_header.tsx | 79 ------------------- .../view/details/endpoint_details.tsx | 14 ++-- .../view/hooks/use_endpoint_action_items.tsx | 4 +- .../pages/endpoint_hosts/view/index.test.tsx | 6 +- .../pages/endpoint_hosts/view/index.tsx | 2 +- .../pages/policy/view/policy_details.test.tsx | 2 +- .../pages/policy/view/policy_details.tsx | 2 +- 15 files changed, 74 insertions(+), 151 deletions(-) delete mode 100644 x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/components/flyout_body_no_top_padding.tsx delete mode 100644 x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/components/flyout_sub_header.tsx diff --git a/src/plugins/data/public/ui/query_string_input/query_bar_top_row.tsx b/src/plugins/data/public/ui/query_string_input/query_bar_top_row.tsx index a5f59b976d3ba..76d4b9dd8e801 100644 --- a/src/plugins/data/public/ui/query_string_input/query_bar_top_row.tsx +++ b/src/plugins/data/public/ui/query_string_input/query_bar_top_row.tsx @@ -32,33 +32,34 @@ const QueryStringInput = withKibana(QueryStringInputUI); // @internal export interface QueryBarTopRowProps { - query?: Query; - onSubmit: (payload: { dateRange: TimeRange; query?: Query }) => void; - onChange: (payload: { dateRange: TimeRange; query?: Query }) => void; - onRefresh?: (payload: { dateRange: TimeRange }) => void; + customSubmitButton?: any; dataTestSubj?: string; + dateRangeFrom?: string; + dateRangeTo?: string; disableAutoFocus?: boolean; - screenTitle?: string; + fillSubmitButton: boolean; + iconType?: EuiIconProps['type']; indexPatterns?: Array; + indicateNoData?: boolean; + isClearable?: boolean; + isDirty: boolean; isLoading?: boolean; + isRefreshPaused?: boolean; + nonKqlMode?: 'lucene' | 'text'; + nonKqlModeHelpText?: string; + onChange: (payload: { dateRange: TimeRange; query?: Query }) => void; + onRefresh?: (payload: { dateRange: TimeRange }) => void; + onRefreshChange?: (options: { isPaused: boolean; refreshInterval: number }) => void; + onSubmit: (payload: { dateRange: TimeRange; query?: Query }) => void; + placeholder?: string; prepend?: React.ComponentProps['prepend']; + query?: Query; + refreshInterval?: number; + screenTitle?: string; showQueryInput?: boolean; showDatePicker?: boolean; - dateRangeFrom?: string; - dateRangeTo?: string; - isRefreshPaused?: boolean; - refreshInterval?: number; showAutoRefreshOnly?: boolean; - onRefreshChange?: (options: { isPaused: boolean; refreshInterval: number }) => void; - customSubmitButton?: any; - isDirty: boolean; timeHistory?: TimeHistoryContract; - indicateNoData?: boolean; - iconType?: EuiIconProps['type']; - placeholder?: string; - isClearable?: boolean; - nonKqlMode?: 'lucene' | 'text'; - nonKqlModeHelpText?: string; timeRangeForSuggestionsOverride?: boolean; } @@ -229,7 +230,7 @@ export default function QueryBarTopRow(props: QueryBarTopRowProps) { isDisabled={isDateRangeInvalid} isLoading={props.isLoading} onClick={onClickSubmitButton} - fill={false} + fill={props.fillSubmitButton} data-test-subj="querySubmitButton" /> ); diff --git a/src/plugins/data/public/ui/search_bar/search_bar.tsx b/src/plugins/data/public/ui/search_bar/search_bar.tsx index 3fef455be41c3..87b6480096551 100644 --- a/src/plugins/data/public/ui/search_bar/search_bar.tsx +++ b/src/plugins/data/public/ui/search_bar/search_bar.tsx @@ -77,6 +77,8 @@ export interface SearchBarOwnProps { nonKqlModeHelpText?: string; // defines padding; use 'inPage' to avoid extra padding; use 'detached' if the searchBar appears at the very top of the view, without any wrapper displayStyle?: 'inPage' | 'detached'; + // super update button background fill control + fillSubmitButton?: boolean; } export type SearchBarProps = SearchBarOwnProps & SearchBarInjectedDeps; @@ -365,6 +367,7 @@ class SearchBarUI extends Component { onSubmit={this.onQueryBarSubmit} indexPatterns={this.props.indexPatterns} isLoading={this.props.isLoading} + fillSubmitButton={this.props.fillSubmitButton || false} prepend={this.props.showFilterBar ? savedQueryManagement : undefined} showDatePicker={this.props.showDatePicker} dateRangeFrom={this.state.dateRangeFrom} diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/components/search_bar.tsx b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/components/search_bar.tsx index ec65239d340d2..cc37f220fb533 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/components/search_bar.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/components/search_bar.tsx @@ -61,6 +61,7 @@ export const AdminSearchBar = memo(() => { indexPatterns={clonedIndexPatterns} timeHistory={timeHistory} onQuerySubmit={onQuerySubmit} + fillSubmitButton={true} isLoading={false} iconType="search" showFilterBar={false} diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/components/back_to_endpoint_details_flyout_subheader.tsx b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/components/back_to_endpoint_details_flyout_subheader.tsx index 6c9ae7631e6d3..bb60214d138b5 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/components/back_to_endpoint_details_flyout_subheader.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/components/back_to_endpoint_details_flyout_subheader.tsx @@ -5,15 +5,23 @@ * 2.0. */ -import React, { memo, useMemo } from 'react'; +import React, { memo, useMemo, MouseEventHandler } from 'react'; import { i18n } from '@kbn/i18n'; -import { FlyoutSubHeader, FlyoutSubHeaderProps } from './flyout_sub_header'; +import { EuiButtonEmpty, CommonProps } from '@elastic/eui'; import { getEndpointDetailsPath } from '../../../../../common/routing'; import { useNavigateByRouterEventHandler } from '../../../../../../common/hooks/endpoint/use_navigate_by_router_event_handler'; import { useEndpointSelector } from '../../hooks'; import { uiQueryParams } from '../../../store/selectors'; import { useAppUrl } from '../../../../../../common/lib/kibana/hooks'; +type BackButtonProps = CommonProps & { + backButton?: { + title: string; + onClick: MouseEventHandler; + href?: string; + }; +}; + export const BackToEndpointDetailsFlyoutSubHeader = memo<{ endpointId: string }>( ({ endpointId }) => { const { getAppUrl } = useAppUrl(); @@ -28,13 +36,11 @@ export const BackToEndpointDetailsFlyoutSubHeader = memo<{ endpointId: string }> }), [currentUrlQueryParams, endpointId] ); - const backToDetailsClickHandler = useNavigateByRouterEventHandler(detailsRoutePath); - - const backButtonProp = useMemo((): FlyoutSubHeaderProps['backButton'] => { + const backButtonProps = useMemo((): BackButtonProps['backButton'] => { return { title: i18n.translate('xpack.securitySolution.endpoint.policyResponse.backLinkTitle', { - defaultMessage: 'Endpoint Details', + defaultMessage: 'Endpoint details', }), href: getAppUrl({ path: detailsRoutePath }), onClick: backToDetailsClickHandler, @@ -42,10 +48,19 @@ export const BackToEndpointDetailsFlyoutSubHeader = memo<{ endpointId: string }> }, [backToDetailsClickHandler, getAppUrl, detailsRoutePath]); return ( - +
+ {/* eslint-disable-next-line @elastic/eui/href-or-on-click */} + + {backButtonProps?.title} + +
); } ); diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/components/endpoint_details_tabs.tsx b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/components/endpoint_details_tabs.tsx index 4c0e1b799a6be..7aa4f1e0a2848 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/components/endpoint_details_tabs.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/components/endpoint_details_tabs.tsx @@ -68,7 +68,9 @@ export const EndpointDetailsFlyoutTabs = memo( return ( <> - {renderTabs} + + {renderTabs} + {selectedTab?.content} diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/components/endpoint_isolate_flyout_panel.tsx b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/components/endpoint_isolate_flyout_panel.tsx index 527189a3ef394..d4c082b2d7b71 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/components/endpoint_isolate_flyout_panel.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/components/endpoint_isolate_flyout_panel.tsx @@ -9,10 +9,9 @@ import React, { memo, useCallback, useState } from 'react'; import { useHistory } from 'react-router-dom'; import { useDispatch } from 'react-redux'; import { Dispatch } from 'redux'; -import { EuiForm } from '@elastic/eui'; +import { EuiForm, EuiFlyoutBody } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { HostMetadata } from '../../../../../../../common/endpoint/types'; -import { BackToEndpointDetailsFlyoutSubHeader } from './back_to_endpoint_details_flyout_subheader'; import { EndpointIsolatedFormProps, EndpointIsolateForm, @@ -20,7 +19,6 @@ import { EndpointUnisolateForm, ActionCompletionReturnButton, } from '../../../../../../common/components/endpoint/host_isolation'; -import { FlyoutBodyNoTopPadding } from './flyout_body_no_top_padding'; import { getEndpointDetailsPath } from '../../../../../common/routing'; import { useEndpointSelector } from '../../hooks'; import { @@ -87,15 +85,13 @@ export const EndpointIsolationFlyoutPanel = memo<{ return ( <> - - {wasSuccessful && ( )} - + {wasSuccessful ? ( )} - + ); }); diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/components/flyout_body_no_top_padding.tsx b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/components/flyout_body_no_top_padding.tsx deleted file mode 100644 index 58bb361a0d88b..0000000000000 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/components/flyout_body_no_top_padding.tsx +++ /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 - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import styled from 'styled-components'; -import { EuiFlyoutBody } from '@elastic/eui'; - -/** - * Removes the `padding-top` from the `EuiFlyoutBody` component. Normally done when there is a - * sub-header present above the flyout body. - */ -export const FlyoutBodyNoTopPadding = styled(EuiFlyoutBody)` - .euiFlyoutBody__overflowContent { - padding-top: 0; - } -`; diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/components/flyout_header.tsx b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/components/flyout_header.tsx index 3c8cdf6bf3183..5e25c94d51e51 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/components/flyout_header.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/components/flyout_header.tsx @@ -9,13 +9,16 @@ import React, { memo } from 'react'; import { EuiFlyoutHeader, EuiLoadingContent, EuiToolTip, EuiTitle } from '@elastic/eui'; import { useEndpointSelector } from '../../hooks'; import { detailsLoading } from '../../../store/selectors'; +import { BackToEndpointDetailsFlyoutSubHeader } from './back_to_endpoint_details_flyout_subheader'; export const EndpointDetailsFlyoutHeader = memo( ({ + endpointId, hasBorder = false, hostname, children, }: { + endpointId?: string; hasBorder?: boolean; hostname?: string; children?: React.ReactNode | React.ReactNodeArray; @@ -24,6 +27,8 @@ export const EndpointDetailsFlyoutHeader = memo( return ( + {endpointId && } + {hostDetailsLoading ? ( ) : ( diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/components/flyout_sub_header.tsx b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/components/flyout_sub_header.tsx deleted file mode 100644 index 743def84dbe5e..0000000000000 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/components/flyout_sub_header.tsx +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React, { memo, MouseEventHandler } from 'react'; -import { EuiFlyoutHeader, CommonProps, EuiButtonEmpty } from '@elastic/eui'; -import styled from 'styled-components'; - -export type FlyoutSubHeaderProps = CommonProps & { - children?: React.ReactNode; - backButton?: { - title: string; - onClick: MouseEventHandler; - href?: string; - }; -}; - -const StyledEuiFlyoutHeader = styled(EuiFlyoutHeader)` - padding: ${(props) => props.theme.eui.paddingSizes.s}; - - &.hasButtons { - .buttons { - padding-bottom: ${(props) => props.theme.eui.paddingSizes.s}; - } - - .flyoutSubHeaderBackButton { - font-size: ${(props) => props.theme.eui.euiFontSizeXS}; - } - .back-button-content { - padding-left: 0; - &-text { - margin-left: 0; - } - } - } - - .flyout-content { - padding-left: ${(props) => props.theme.eui.paddingSizes.m}; - } -`; - -const BUTTON_CONTENT_PROPS = Object.freeze({ className: 'back-button-content' }); -const BUTTON_TEXT_PROPS = Object.freeze({ className: 'back-button-content-text' }); - -/** - * A Eui Flyout Header component that has its styles adjusted to display a panel sub-header. - * Component also provides a way to display a "back" button above the header title. - */ -export const FlyoutSubHeader = memo( - ({ children, backButton, ...otherProps }) => { - return ( - - {backButton && ( -
- {/* eslint-disable-next-line @elastic/eui/href-or-on-click */} - - {backButton?.title} - -
- )} -
{children}
-
- ); - } -); - -FlyoutSubHeader.displayName = 'FlyoutSubHeader'; diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/endpoint_details.tsx b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/endpoint_details.tsx index eafc76479b4f8..cc7f3318d99ab 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/endpoint_details.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/endpoint_details.tsx @@ -38,13 +38,11 @@ import { import { useEndpointSelector } from '../hooks'; import * as i18 from '../translations'; import { ActionsMenu } from './components/actions_menu'; -import { BackToEndpointDetailsFlyoutSubHeader } from './components/back_to_endpoint_details_flyout_subheader'; import { EndpointDetailsFlyoutTabs, EndpointDetailsTabsTypes, } from './components/endpoint_details_tabs'; import { EndpointIsolationFlyoutPanel } from './components/endpoint_isolate_flyout_panel'; -import { FlyoutBodyNoTopPadding } from './components/flyout_body_no_top_padding'; import { EndpointDetailsFlyoutHeader } from './components/flyout_header'; import { EndpointActivityLog } from './endpoint_activity_log'; import { EndpointDetailsContent } from './endpoint_details_content'; @@ -126,7 +124,11 @@ export const EndpointDetails = memo(() => { return ( <> {(show === 'policy_response' || show === 'isolate' || show === 'unisolate') && ( - + )} {hostDetails === undefined ? ( @@ -174,9 +176,7 @@ const PolicyResponseFlyoutPanel = memo<{ return ( <> - - - @@ -218,7 +218,7 @@ const PolicyResponseFlyoutPanel = memo<{ responseAttentionCount={responseAttentionCount} /> )} - + ); }); diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/hooks/use_endpoint_action_items.tsx b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/hooks/use_endpoint_action_items.tsx index 7370cabd8e080..cbaa6ce4bf56d 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/hooks/use_endpoint_action_items.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/hooks/use_endpoint_action_items.tsx @@ -65,7 +65,7 @@ export const useEndpointActionItems = ( // Un-isolate is always available to users regardless of license level isolationActions.push({ 'data-test-subj': 'unIsolateLink', - icon: 'logoSecurity', + icon: 'lockOpen', key: 'unIsolateHost', navigateAppId: APP_UI_ID, navigateOptions: { @@ -83,7 +83,7 @@ export const useEndpointActionItems = ( // For Platinum++ licenses, users also have ability to isolate isolationActions.push({ 'data-test-subj': 'isolateLink', - icon: 'logoSecurity', + icon: 'lock', key: 'isolateHost', navigateAppId: APP_UI_ID, navigateOptions: { diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.test.tsx b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.test.tsx index 2ad8a96b12123..4732e28c7f828 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.test.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.test.tsx @@ -1125,9 +1125,7 @@ describe('when on the endpoint list page', () => { }); it('should display policy response sub-panel', async () => { - expect( - await renderResult.findByTestId('endpointDetailsPolicyResponseFlyoutHeader') - ).not.toBeNull(); + expect(await renderResult.findByTestId('flyoutSubHeaderBackButton')).not.toBeNull(); expect( await renderResult.findByTestId('endpointDetailsPolicyResponseFlyoutBody') ).not.toBeNull(); @@ -1214,7 +1212,7 @@ describe('when on the endpoint list page', () => { it('should include the back to details link', async () => { const subHeaderBackLink = await renderResult.findByTestId('flyoutSubHeaderBackButton'); - expect(subHeaderBackLink.textContent).toBe('Endpoint Details'); + expect(subHeaderBackLink.textContent).toBe('Endpoint details'); expect(subHeaderBackLink.getAttribute('href')).toEqual( `${APP_PATH}${MANAGEMENT_PATH}/endpoints?page_index=0&page_size=10&selected_endpoint=1&show=details` ); diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.tsx b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.tsx index 07d5f9ab47bf6..3ac3eda0426f7 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.tsx @@ -658,7 +658,7 @@ export const EndpointList = () => { )} {transformFailedCallout} - + {shouldShowKQLBar && ( diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_details.test.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_details.test.tsx index fa99d72523531..2212c1053de33 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_details.test.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_details.test.tsx @@ -121,7 +121,7 @@ describe('Policy Details', () => { const backToListLink = policyView.find('BackToExternalAppButton'); expect(backToListLink.prop('backButtonUrl')).toBe(`/app/security${endpointListPath}`); - expect(backToListLink.text()).toBe('Back to endpoint hosts'); + expect(backToListLink.text()).toBe('View all endpoints'); const pageTitle = policyView.find('span[data-test-subj="header-page-title"]'); expect(pageTitle).toHaveLength(1); diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_details.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_details.tsx index 6acc48c55c6e3..b9dd3419062df 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_details.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_details.tsx @@ -57,7 +57,7 @@ export const PolicyDetails = React.memo(() => { backButtonLabel: i18n.translate( 'xpack.securitySolution.endpoint.policy.details.backToListTitle', { - defaultMessage: 'Back to endpoint hosts', + defaultMessage: 'View all endpoints', } ), backButtonUrl: getAppUrl({ path: endpointListPath }), From a547d4a087950dff559e6a2fd3ee7eb8ee1f4a2a Mon Sep 17 00:00:00 2001 From: Mark Hopkin Date: Fri, 17 Dec 2021 16:22:20 +0000 Subject: [PATCH 09/40] [Fleet] Add `installed_kibana_space_id` to `epm-packages` saved objects (#120517) --- .../kbn_client/kbn_client_saved_objects.ts | 11 +- .../plugins/fleet/common/types/models/epm.ts | 1 + .../cypress/fixtures/integrations/apache.json | 1 + x-pack/plugins/fleet/dev_docs/data_model.md | 4 +- x-pack/plugins/fleet/kibana.json | 2 +- .../epm/components/package_card.stories.tsx | 1 + x-pack/plugins/fleet/server/mocks/index.ts | 1 + x-pack/plugins/fleet/server/plugin.ts | 5 + .../server/routes/agent_policy/handlers.ts | 6 +- .../fleet/server/routes/epm/handlers.ts | 7 +- .../routes/package_policy/handlers.test.ts | 3 +- .../server/routes/package_policy/handlers.ts | 5 +- .../server/routes/preconfiguration/index.ts | 10 +- .../server/routes/setup/handlers.test.ts | 1 + .../fleet/server/saved_objects/index.ts | 1 + .../epm/packages/_install_package.test.ts | 3 + .../services/epm/packages/_install_package.ts | 9 +- .../epm/packages/bulk_install_packages.ts | 3 + .../epm/packages/get_install_type.test.ts | 2 + .../services/epm/packages/install.test.ts | 10 ++ .../server/services/epm/packages/install.ts | 30 +++- .../server/services/epm/packages/remove.ts | 44 +++-- .../fleet/server/services/package_policy.ts | 4 + .../server/services/preconfiguration.test.ts | 37 ++-- .../fleet/server/services/preconfiguration.ts | 4 +- x-pack/plugins/fleet/server/services/setup.ts | 5 +- .../fleet/server/types/request_context.ts | 1 + .../storybook/context/fixtures/packages.ts | 6 + .../epm/__snapshots__/install_by_upload.snap | 1 + .../fleet_api_integration/apis/epm/index.js | 1 + .../apis/epm/install_remove_assets.ts | 1 + .../epm/install_remove_kbn_assets_in_space.ts | 170 ++++++++++++++++++ .../apis/epm/update_assets.ts | 1 + .../only_dashboard/0.1.0/docs/README.md | 3 + .../0.1.0/img/logo_overrides_64_color.svg | 7 + .../kibana/dashboard/test_dashboard.json | 22 +++ .../0.1.0/kibana/search/test_search.json | 24 +++ .../visualization/test_visualization.json | 11 ++ .../only_dashboard/0.1.0/manifest.yml | 20 +++ 39 files changed, 425 insertions(+), 53 deletions(-) create mode 100644 x-pack/test/fleet_api_integration/apis/epm/install_remove_kbn_assets_in_space.ts create mode 100644 x-pack/test/fleet_api_integration/apis/fixtures/test_packages/only_dashboard/0.1.0/docs/README.md create mode 100644 x-pack/test/fleet_api_integration/apis/fixtures/test_packages/only_dashboard/0.1.0/img/logo_overrides_64_color.svg create mode 100644 x-pack/test/fleet_api_integration/apis/fixtures/test_packages/only_dashboard/0.1.0/kibana/dashboard/test_dashboard.json create mode 100644 x-pack/test/fleet_api_integration/apis/fixtures/test_packages/only_dashboard/0.1.0/kibana/search/test_search.json create mode 100644 x-pack/test/fleet_api_integration/apis/fixtures/test_packages/only_dashboard/0.1.0/kibana/visualization/test_visualization.json create mode 100644 x-pack/test/fleet_api_integration/apis/fixtures/test_packages/only_dashboard/0.1.0/manifest.yml diff --git a/packages/kbn-test/src/kbn_client/kbn_client_saved_objects.ts b/packages/kbn-test/src/kbn_client/kbn_client_saved_objects.ts index 904ccc385bd7d..f70123029e6c4 100644 --- a/packages/kbn-test/src/kbn_client/kbn_client_saved_objects.ts +++ b/packages/kbn-test/src/kbn_client/kbn_client_saved_objects.ts @@ -36,6 +36,7 @@ interface SavedObjectResponse> { interface GetOptions { type: string; id: string; + space?: string; } interface IndexOptions { @@ -110,11 +111,13 @@ export class KbnClientSavedObjects { * Get an object */ public async get>(options: GetOptions) { - this.log.debug('Gettings saved object: %j', options); + this.log.debug('Getting saved object: %j', options); const { data } = await this.requester.request>({ description: 'get saved object', - path: uriencode`/api/saved_objects/${options.type}/${options.id}`, + path: options.space + ? uriencode`/s/${options.space}/api/saved_objects/${options.type}/${options.id}` + : uriencode`/api/saved_objects/${options.type}/${options.id}`, method: 'GET', }); return data; @@ -174,7 +177,9 @@ export class KbnClientSavedObjects { const { data } = await this.requester.request({ description: 'delete saved object', - path: uriencode`/api/saved_objects/${options.type}/${options.id}`, + path: options.space + ? uriencode`/s/${options.space}/api/saved_objects/${options.type}/${options.id}` + : uriencode`/api/saved_objects/${options.type}/${options.id}`, method: 'DELETE', }); diff --git a/x-pack/plugins/fleet/common/types/models/epm.ts b/x-pack/plugins/fleet/common/types/models/epm.ts index f7b446cc53c7d..de92ddf1fcfae 100644 --- a/x-pack/plugins/fleet/common/types/models/epm.ts +++ b/x-pack/plugins/fleet/common/types/models/epm.ts @@ -402,6 +402,7 @@ export interface Installation extends SavedObjectAttributes { install_version: string; install_started_at: string; install_source: InstallSource; + installed_kibana_space_id?: string; keep_policies_up_to_date?: boolean; } diff --git a/x-pack/plugins/fleet/cypress/fixtures/integrations/apache.json b/x-pack/plugins/fleet/cypress/fixtures/integrations/apache.json index 397bc6d653409..b8c4c31767f3d 100644 --- a/x-pack/plugins/fleet/cypress/fixtures/integrations/apache.json +++ b/x-pack/plugins/fleet/cypress/fixtures/integrations/apache.json @@ -645,6 +645,7 @@ "updated_at": "2021-09-30T10:47:12.961Z", "version": "WzI1NjgsMV0=", "attributes": { + "installed_kibana_space_id": "default", "installed_kibana": [ { "id": "apache-Logs-Apache-Dashboard", diff --git a/x-pack/plugins/fleet/dev_docs/data_model.md b/x-pack/plugins/fleet/dev_docs/data_model.md index be0e06e5439dc..1399cec4c3318 100644 --- a/x-pack/plugins/fleet/dev_docs/data_model.md +++ b/x-pack/plugins/fleet/dev_docs/data_model.md @@ -127,12 +127,10 @@ used for other types of outputs like separate monitoring clusters, Logstash, etc - `installed_es` - array of assets installed into Elasticsearch - `installed_es.id` - ID in Elasticsearch of an asset (eg. `logs-system.application-1.1.2`) - `installed_es.type` - type of Elasticsearch asset (eg. `ingest_pipeline`) + - `installed_kibana_space_id` - the id of the space the assets were installed in (eg. `default`) - `installed_kibana` - array of assets that were installed into Kibana - `installed_kibana.id` - Saved Object ID (eg. `system-01c54730-fee6-11e9-8405-516218e3d268`) - `installed_kibana.type` - Saved Object type name (eg. `dashboard`) - - One caveat with this array is that the IDs are currently space-specific so if a package's assets were installed in - one space, they may not be visible in other spaces. We also do not keep track of which space these assets were - installed into. - `package_assets` - array of original file contents of the package as it was installed - `package_assets.id` - Saved Object ID for a `epm-package-assets` type - `package_assets.type` - Saved Object type for the asset. As of now, only `epm-packages-assets` are supported. diff --git a/x-pack/plugins/fleet/kibana.json b/x-pack/plugins/fleet/kibana.json index d516827ebf9a7..f78681bd725df 100644 --- a/x-pack/plugins/fleet/kibana.json +++ b/x-pack/plugins/fleet/kibana.json @@ -8,7 +8,7 @@ "server": true, "ui": true, "configPath": ["xpack", "fleet"], - "requiredPlugins": ["licensing", "data", "encryptedSavedObjects", "navigation", "customIntegrations", "share"], + "requiredPlugins": ["licensing", "data", "encryptedSavedObjects", "navigation", "customIntegrations", "share", "spaces"], "optionalPlugins": ["security", "features", "cloud", "usageCollection", "home", "globalSearch", "telemetry"], "extraPublicDirs": ["common"], "requiredBundles": ["kibanaReact", "esUiShared", "home", "infra", "kibanaUtils", "usageCollection"] diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/components/package_card.stories.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/components/package_card.stories.tsx index 86bac94bc50cd..499b5f2512e7c 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/components/package_card.stories.tsx +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/components/package_card.stories.tsx @@ -63,6 +63,7 @@ export const Installed = ({ width, ...props }: Args) => { install_version: props.version, es_index_patterns: {}, installed_kibana: [], + installed_kibana_space_id: 'default', installed_es: [], install_status: 'installed', install_source: 'registry', diff --git a/x-pack/plugins/fleet/server/mocks/index.ts b/x-pack/plugins/fleet/server/mocks/index.ts index acdc0ba5e3fdd..ab2e86e969f1c 100644 --- a/x-pack/plugins/fleet/server/mocks/index.ts +++ b/x-pack/plugins/fleet/server/mocks/index.ts @@ -80,6 +80,7 @@ export const createFleetRequestHandlerContextMock = (): jest.Mocked< epm: { internalSoClient: savedObjectsClientMock.create(), }, + spaceId: 'default', }; }; diff --git a/x-pack/plugins/fleet/server/plugin.ts b/x-pack/plugins/fleet/server/plugin.ts index 06fb78cbb67ce..9a7cc6535e41d 100644 --- a/x-pack/plugins/fleet/server/plugin.ts +++ b/x-pack/plugins/fleet/server/plugin.ts @@ -40,6 +40,7 @@ import type { PluginSetupContract as FeaturesPluginSetup } from '../../features/ import type { FleetConfigType, FleetAuthz } from '../common'; import { INTEGRATIONS_PLUGIN_ID } from '../common'; import type { CloudSetup } from '../../cloud/server'; +import type { SpacesPluginStart } from '../../spaces/server'; import { PLUGIN_ID, @@ -96,6 +97,7 @@ export interface FleetSetupDeps { encryptedSavedObjects: EncryptedSavedObjectsPluginSetup; cloud?: CloudSetup; usageCollection?: UsageCollectionSetup; + spaces: SpacesPluginStart; telemetry?: TelemetryPluginSetup; } @@ -297,6 +299,9 @@ export class FleetPlugin .getScopedClient(request, { excludedWrappers: ['security'] }); }, }, + get spaceId() { + return deps.spaces.spacesService.getSpaceId(request); + }, }; } ); diff --git a/x-pack/plugins/fleet/server/routes/agent_policy/handlers.ts b/x-pack/plugins/fleet/server/routes/agent_policy/handlers.ts index 57a9290d00a00..92504dfd7091d 100644 --- a/x-pack/plugins/fleet/server/routes/agent_policy/handlers.ts +++ b/x-pack/plugins/fleet/server/routes/agent_policy/handlers.ts @@ -22,6 +22,7 @@ import type { CopyAgentPolicyRequestSchema, DeleteAgentPolicyRequestSchema, GetFullAgentPolicyRequestSchema, + FleetRequestHandler, } from '../../types'; import { FLEET_SYSTEM_PACKAGE } from '../../../common'; import type { @@ -99,7 +100,7 @@ export const getOneAgentPolicyHandler: RequestHandler< } }; -export const createAgentPolicyHandler: RequestHandler< +export const createAgentPolicyHandler: FleetRequestHandler< undefined, TypeOf, TypeOf @@ -108,7 +109,7 @@ export const createAgentPolicyHandler: RequestHandler< const esClient = context.core.elasticsearch.client.asInternalUser; const user = (await appContextService.getSecurity()?.authc.getCurrentUser(request)) || undefined; const withSysMonitoring = request.query.sys_monitoring ?? false; - + const spaceId = context.fleet.spaceId; try { // eslint-disable-next-line prefer-const let [agentPolicy, newSysPackagePolicy] = await Promise.all([ @@ -132,6 +133,7 @@ export const createAgentPolicyHandler: RequestHandler< newSysPackagePolicy.name = await incrementPackageName(soClient, FLEET_SYSTEM_PACKAGE); await packagePolicyService.create(soClient, esClient, newSysPackagePolicy, { + spaceId, user, bumpRevision: false, }); diff --git a/x-pack/plugins/fleet/server/routes/epm/handlers.ts b/x-pack/plugins/fleet/server/routes/epm/handlers.ts index 4e43a19b3637e..4953cecbf211d 100644 --- a/x-pack/plugins/fleet/server/routes/epm/handlers.ts +++ b/x-pack/plugins/fleet/server/routes/epm/handlers.ts @@ -257,11 +257,13 @@ export const installPackageFromRegistryHandler: FleetRequestHandler< const esClient = context.core.elasticsearch.client.asInternalUser; const { pkgName, pkgVersion } = request.params; + const spaceId = context.fleet.spaceId; const res = await installPackage({ installSource: 'registry', savedObjectsClient, pkgkey: pkgVersion ? `${pkgName}-${pkgVersion}` : pkgName, esClient, + spaceId, force: request.body?.force, }); if (!res.error) { @@ -296,10 +298,12 @@ export const bulkInstallPackagesFromRegistryHandler: FleetRequestHandler< > = async (context, request, response) => { const savedObjectsClient = context.fleet.epm.internalSoClient; const esClient = context.core.elasticsearch.client.asInternalUser; + const spaceId = context.fleet.spaceId; const bulkInstalledResponses = await bulkInstallPackages({ savedObjectsClient, esClient, packagesToInstall: request.body.packages, + spaceId, }); const payload = bulkInstalledResponses.map(bulkInstallServiceResponseToHttpEntry); const body: BulkInstallPackagesResponse = { @@ -324,12 +328,13 @@ export const installPackageByUploadHandler: FleetRequestHandler< const esClient = context.core.elasticsearch.client.asInternalUser; const contentType = request.headers['content-type'] as string; // from types it could also be string[] or undefined but this is checked later const archiveBuffer = Buffer.from(request.body); - + const spaceId = context.fleet.spaceId; const res = await installPackage({ installSource: 'upload', savedObjectsClient, esClient, archiveBuffer, + spaceId, contentType, }); if (!res.error) { diff --git a/x-pack/plugins/fleet/server/routes/package_policy/handlers.test.ts b/x-pack/plugins/fleet/server/routes/package_policy/handlers.test.ts index 6ea64f95c92ac..c4cef2a4d8d3b 100644 --- a/x-pack/plugins/fleet/server/routes/package_policy/handlers.test.ts +++ b/x-pack/plugins/fleet/server/routes/package_policy/handlers.test.ts @@ -16,13 +16,14 @@ import type { PackagePolicyServiceInterface, PostPackagePolicyCreateCallback, PutPackagePolicyUpdateCallback, + FleetRequestHandlerContext, } from '../..'; import type { CreatePackagePolicyRequestSchema, UpdatePackagePolicyRequestSchema, } from '../../types/rest_spec'; import type { FleetAuthzRouter } from '../security'; -import type { FleetRequestHandler, FleetRequestHandlerContext } from '../../types'; +import type { FleetRequestHandler } from '../../types'; import type { PackagePolicy } from '../../types'; import { registerRoutes } from './index'; diff --git a/x-pack/plugins/fleet/server/routes/package_policy/handlers.ts b/x-pack/plugins/fleet/server/routes/package_policy/handlers.ts index 2d27070980d57..830553aa24dab 100644 --- a/x-pack/plugins/fleet/server/routes/package_policy/handlers.ts +++ b/x-pack/plugins/fleet/server/routes/package_policy/handlers.ts @@ -19,6 +19,7 @@ import type { DeletePackagePoliciesRequestSchema, UpgradePackagePoliciesRequestSchema, DryRunPackagePoliciesRequestSchema, + FleetRequestHandler, } from '../../types'; import type { CreatePackagePolicyResponse, @@ -80,7 +81,7 @@ export const getOnePackagePolicyHandler: RequestHandler< } }; -export const createPackagePolicyHandler: RequestHandler< +export const createPackagePolicyHandler: FleetRequestHandler< undefined, undefined, TypeOf @@ -89,6 +90,7 @@ export const createPackagePolicyHandler: RequestHandler< const esClient = context.core.elasticsearch.client.asInternalUser; const user = appContextService.getSecurity()?.authc.getCurrentUser(request) || undefined; const { force, ...newPolicy } = request.body; + const spaceId = context.fleet.spaceId; try { const newPackagePolicy = await packagePolicyService.enrichPolicyWithDefaultsFromPackage( soClient, @@ -106,6 +108,7 @@ export const createPackagePolicyHandler: RequestHandler< const packagePolicy = await packagePolicyService.create(soClient, esClient, newData, { user, force, + spaceId, }); const body: CreatePackagePolicyResponse = { item: packagePolicy }; return response.ok({ diff --git a/x-pack/plugins/fleet/server/routes/preconfiguration/index.ts b/x-pack/plugins/fleet/server/routes/preconfiguration/index.ts index d13c2ab5ab8e8..56cbbc9435a57 100644 --- a/x-pack/plugins/fleet/server/routes/preconfiguration/index.ts +++ b/x-pack/plugins/fleet/server/routes/preconfiguration/index.ts @@ -11,12 +11,13 @@ import type { TypeOf } from '@kbn/config-schema'; import type { PreconfiguredAgentPolicy } from '../../../common'; import { PRECONFIGURATION_API_ROUTES } from '../../constants'; +import type { FleetRequestHandler } from '../../types'; import { PutPreconfigurationSchema } from '../../types'; import { defaultIngestErrorHandler } from '../../errors'; import { ensurePreconfiguredPackagesAndPolicies, outputService } from '../../services'; import type { FleetAuthzRouter } from '../security'; -export const updatePreconfigurationHandler: RequestHandler< +export const updatePreconfigurationHandler: FleetRequestHandler< undefined, undefined, TypeOf @@ -24,7 +25,7 @@ export const updatePreconfigurationHandler: RequestHandler< const soClient = context.core.savedObjects.client; const esClient = context.core.elasticsearch.client.asInternalUser; const defaultOutput = await outputService.ensureDefaultOutput(soClient); - + const spaceId = context.fleet.spaceId; const { agentPolicies, packages } = request.body; try { @@ -33,7 +34,8 @@ export const updatePreconfigurationHandler: RequestHandler< esClient, (agentPolicies as PreconfiguredAgentPolicy[]) ?? [], packages ?? [], - defaultOutput + defaultOutput, + spaceId ); return response.ok({ body }); } catch (error) { @@ -50,6 +52,6 @@ export const registerRoutes = (router: FleetAuthzRouter) => { fleet: { all: true }, }, }, - updatePreconfigurationHandler + updatePreconfigurationHandler as RequestHandler ); }; diff --git a/x-pack/plugins/fleet/server/routes/setup/handlers.test.ts b/x-pack/plugins/fleet/server/routes/setup/handlers.test.ts index 035659185955d..bacbff982daec 100644 --- a/x-pack/plugins/fleet/server/routes/setup/handlers.test.ts +++ b/x-pack/plugins/fleet/server/routes/setup/handlers.test.ts @@ -45,6 +45,7 @@ describe('FleetSetupHandler', () => { epm: { internalSoClient: savedObjectsClientMock.create(), }, + spaceId: 'default', }, }; response = httpServerMock.createResponseFactory(); diff --git a/x-pack/plugins/fleet/server/saved_objects/index.ts b/x-pack/plugins/fleet/server/saved_objects/index.ts index 6058cfba12cad..96cb51af53b45 100644 --- a/x-pack/plugins/fleet/server/saved_objects/index.ts +++ b/x-pack/plugins/fleet/server/saved_objects/index.ts @@ -239,6 +239,7 @@ const getSavedObjectTypes = ( type: { type: 'keyword' }, }, }, + installed_kibana_space_id: { type: 'keyword' }, package_assets: { type: 'nested', properties: { diff --git a/x-pack/plugins/fleet/server/services/epm/packages/_install_package.test.ts b/x-pack/plugins/fleet/server/services/epm/packages/_install_package.test.ts index dbec18851cfc9..f1ac8382a9ba7 100644 --- a/x-pack/plugins/fleet/server/services/epm/packages/_install_package.test.ts +++ b/x-pack/plugins/fleet/server/services/epm/packages/_install_package.test.ts @@ -9,6 +9,8 @@ import type { SavedObjectsClientContract, ElasticsearchClient } from 'src/core/s import { savedObjectsClientMock, elasticsearchServiceMock } from 'src/core/server/mocks'; import { loggerMock } from '@kbn/logging/mocks'; +import { DEFAULT_SPACE_ID } from '../../../../../spaces/common/constants'; + import { appContextService } from '../../app_context'; import { createAppContextStartContractMock } from '../../../mocks'; @@ -78,6 +80,7 @@ describe('_installPackage', () => { }, installType: 'install', installSource: 'registry', + spaceId: DEFAULT_SPACE_ID, }); // if we have a .catch this will fail nicely (test pass) diff --git a/x-pack/plugins/fleet/server/services/epm/packages/_install_package.ts b/x-pack/plugins/fleet/server/services/epm/packages/_install_package.ts index ac0c7e1729913..05ed45a0e0c7a 100644 --- a/x-pack/plugins/fleet/server/services/epm/packages/_install_package.ts +++ b/x-pack/plugins/fleet/server/services/epm/packages/_install_package.ts @@ -56,6 +56,7 @@ export async function _installPackage({ packageInfo, installType, installSource, + spaceId, }: { savedObjectsClient: SavedObjectsClientContract; savedObjectsImporter: Pick; @@ -66,6 +67,7 @@ export async function _installPackage({ packageInfo: InstallablePackage; installType: InstallType; installSource: InstallSource; + spaceId: string; }): Promise { const { name: pkgName, version: pkgVersion } = packageInfo; @@ -99,15 +101,12 @@ export async function _installPackage({ savedObjectsClient, packageInfo, installSource, + spaceId, }); } const kibanaAssets = await getKibanaAssets(paths); - if (installedPkg) - await deleteKibanaSavedObjectsAssets( - savedObjectsClient, - installedPkg.attributes.installed_kibana - ); + if (installedPkg) await deleteKibanaSavedObjectsAssets({ savedObjectsClient, installedPkg }); // save new kibana refs before installing the assets const installedKibanaAssetsRefs = await saveKibanaAssetsRefs( savedObjectsClient, diff --git a/x-pack/plugins/fleet/server/services/epm/packages/bulk_install_packages.ts b/x-pack/plugins/fleet/server/services/epm/packages/bulk_install_packages.ts index 8a7fb9ae005d0..3178881b7ce09 100644 --- a/x-pack/plugins/fleet/server/services/epm/packages/bulk_install_packages.ts +++ b/x-pack/plugins/fleet/server/services/epm/packages/bulk_install_packages.ts @@ -20,12 +20,14 @@ interface BulkInstallPackagesParams { packagesToInstall: Array; esClient: ElasticsearchClient; force?: boolean; + spaceId: string; } export async function bulkInstallPackages({ savedObjectsClient, packagesToInstall, esClient, + spaceId, force, }: BulkInstallPackagesParams): Promise { const logger = appContextService.getLogger(); @@ -70,6 +72,7 @@ export async function bulkInstallPackages({ esClient, pkgkey: Registry.pkgToPkgKey(pkgKeyProps), installSource, + spaceId, force, }); if (installResult.error) { diff --git a/x-pack/plugins/fleet/server/services/epm/packages/get_install_type.test.ts b/x-pack/plugins/fleet/server/services/epm/packages/get_install_type.test.ts index 6bc962165f1d2..341cc90dab9ea 100644 --- a/x-pack/plugins/fleet/server/services/epm/packages/get_install_type.test.ts +++ b/x-pack/plugins/fleet/server/services/epm/packages/get_install_type.test.ts @@ -18,6 +18,7 @@ const mockInstallation: SavedObject = { type: 'epm-packages', attributes: { id: 'test-pkg', + installed_kibana_space_id: 'default', installed_kibana: [{ type: KibanaSavedObjectType.dashboard, id: 'dashboard-1' }], installed_es: [{ type: ElasticsearchAssetType.ingestPipeline, id: 'pipeline' }], package_assets: [], @@ -37,6 +38,7 @@ const mockInstallationUpdateFail: SavedObject = { type: 'epm-packages', attributes: { id: 'test-pkg', + installed_kibana_space_id: 'default', installed_kibana: [{ type: KibanaSavedObjectType.dashboard, id: 'dashboard-1' }], installed_es: [{ type: ElasticsearchAssetType.ingestPipeline, id: 'pipeline' }], package_assets: [], diff --git a/x-pack/plugins/fleet/server/services/epm/packages/install.test.ts b/x-pack/plugins/fleet/server/services/epm/packages/install.test.ts index 261a0d9a6d688..cb04b5f583c5a 100644 --- a/x-pack/plugins/fleet/server/services/epm/packages/install.test.ts +++ b/x-pack/plugins/fleet/server/services/epm/packages/install.test.ts @@ -9,6 +9,8 @@ import { savedObjectsClientMock } from 'src/core/server/mocks'; import type { ElasticsearchClient } from 'kibana/server'; +import { DEFAULT_SPACE_ID } from '../../../../../spaces/common/constants'; + import * as Registry from '../registry'; import { sendTelemetryEvents } from '../../upgrade_sender'; @@ -75,6 +77,7 @@ describe('install', () => { describe('registry', () => { it('should send telemetry on install failure, out of date', async () => { await installPackage({ + spaceId: DEFAULT_SPACE_ID, installSource: 'registry', pkgkey: 'apache-1.1.0', savedObjectsClient: savedObjectsClientMock.create(), @@ -96,6 +99,7 @@ describe('install', () => { it('should send telemetry on install failure, license error', async () => { jest.spyOn(licenseService, 'hasAtLeast').mockReturnValue(false); await installPackage({ + spaceId: DEFAULT_SPACE_ID, installSource: 'registry', pkgkey: 'apache-1.3.0', savedObjectsClient: savedObjectsClientMock.create(), @@ -117,6 +121,7 @@ describe('install', () => { it('should send telemetry on install success', async () => { jest.spyOn(licenseService, 'hasAtLeast').mockReturnValue(true); await installPackage({ + spaceId: DEFAULT_SPACE_ID, installSource: 'registry', pkgkey: 'apache-1.3.0', savedObjectsClient: savedObjectsClientMock.create(), @@ -140,6 +145,7 @@ describe('install', () => { .mockImplementationOnce(() => Promise.resolve({ attributes: { version: '1.2.0' } } as any)); jest.spyOn(licenseService, 'hasAtLeast').mockReturnValue(true); await installPackage({ + spaceId: DEFAULT_SPACE_ID, installSource: 'registry', pkgkey: 'apache-1.3.0', savedObjectsClient: savedObjectsClientMock.create(), @@ -163,6 +169,7 @@ describe('install', () => { .mockImplementation(() => Promise.reject(new Error('error'))); jest.spyOn(licenseService, 'hasAtLeast').mockReturnValue(true); await installPackage({ + spaceId: DEFAULT_SPACE_ID, installSource: 'registry', pkgkey: 'apache-1.3.0', savedObjectsClient: savedObjectsClientMock.create(), @@ -188,6 +195,7 @@ describe('install', () => { .spyOn(obj, 'getInstallationObject') .mockImplementationOnce(() => Promise.resolve({ attributes: { version: '1.2.0' } } as any)); await installPackage({ + spaceId: DEFAULT_SPACE_ID, installSource: 'upload', archiveBuffer: {} as Buffer, contentType: '', @@ -210,6 +218,7 @@ describe('install', () => { it('should send telemetry on install success', async () => { await installPackage({ + spaceId: DEFAULT_SPACE_ID, installSource: 'upload', archiveBuffer: {} as Buffer, contentType: '', @@ -233,6 +242,7 @@ describe('install', () => { .spyOn(install, '_installPackage') .mockImplementation(() => Promise.reject(new Error('error'))); await installPackage({ + spaceId: DEFAULT_SPACE_ID, installSource: 'upload', archiveBuffer: {} as Buffer, contentType: '', diff --git a/x-pack/plugins/fleet/server/services/epm/packages/install.ts b/x-pack/plugins/fleet/server/services/epm/packages/install.ts index 77fcc429b2084..a7e10ba4aa578 100644 --- a/x-pack/plugins/fleet/server/services/epm/packages/install.ts +++ b/x-pack/plugins/fleet/server/services/epm/packages/install.ts @@ -11,7 +11,7 @@ import type Boom from '@hapi/boom'; import type { ElasticsearchClient, SavedObject, SavedObjectsClientContract } from 'src/core/server'; import { generateESIndexPatterns } from '../elasticsearch/template/template'; - +import { DEFAULT_SPACE_ID } from '../../../../../spaces/common/constants'; import type { BulkInstallPackageInfo, EpmPackageInstallStatus, @@ -85,8 +85,9 @@ export async function ensureInstalledPackage(options: { pkgName: string; esClient: ElasticsearchClient; pkgVersion?: string; + spaceId?: string; }): Promise { - const { savedObjectsClient, pkgName, esClient, pkgVersion } = options; + const { savedObjectsClient, pkgName, esClient, pkgVersion, spaceId = DEFAULT_SPACE_ID } = options; // If pkgVersion isn't specified, find the latest package version const pkgKeyProps = pkgVersion @@ -106,6 +107,7 @@ export async function ensureInstalledPackage(options: { installSource: 'registry', savedObjectsClient, pkgkey, + spaceId, esClient, force: true, // Always force outdated packages to be installed if a later version isn't installed }); @@ -142,6 +144,7 @@ export async function handleInstallPackageFailure({ pkgVersion, installedPkg, esClient, + spaceId, }: { savedObjectsClient: SavedObjectsClientContract; error: IngestManagerError | Boom.Boom | Error; @@ -149,6 +152,7 @@ export async function handleInstallPackageFailure({ pkgVersion: string; installedPkg: SavedObject | undefined; esClient: ElasticsearchClient; + spaceId: string; }) { if (error instanceof IngestManagerError) { return; @@ -183,6 +187,7 @@ export async function handleInstallPackageFailure({ savedObjectsClient, pkgkey: prevVersion, esClient, + spaceId, force: true, }); } @@ -202,6 +207,7 @@ interface InstallRegistryPackageParams { savedObjectsClient: SavedObjectsClientContract; pkgkey: string; esClient: ElasticsearchClient; + spaceId: string; force?: boolean; } @@ -229,6 +235,7 @@ async function installPackageFromRegistry({ savedObjectsClient, pkgkey, esClient, + spaceId, force = false, }: InstallRegistryPackageParams): Promise { const logger = appContextService.getLogger(); @@ -317,6 +324,7 @@ async function installPackageFromRegistry({ paths, packageInfo, installType, + spaceId, installSource: 'registry', }) .then(async (assets) => { @@ -339,6 +347,7 @@ async function installPackageFromRegistry({ pkgName, pkgVersion, installedPkg, + spaceId, esClient, }); sendEvent({ @@ -364,6 +373,7 @@ interface InstallUploadedArchiveParams { esClient: ElasticsearchClient; archiveBuffer: Buffer; contentType: string; + spaceId: string; } async function installPackageByUpload({ @@ -371,6 +381,7 @@ async function installPackageByUpload({ esClient, archiveBuffer, contentType, + spaceId, }: InstallUploadedArchiveParams): Promise { const logger = appContextService.getLogger(); // if an error happens during getInstallType, report that we don't know @@ -427,6 +438,7 @@ async function installPackageByUpload({ packageInfo, installType, installSource, + spaceId, }) .then((assets) => { sendEvent({ @@ -451,9 +463,10 @@ async function installPackageByUpload({ } } -export type InstallPackageParams = +export type InstallPackageParams = { spaceId: string } & ( | ({ installSource: Extract } & InstallRegistryPackageParams) - | ({ installSource: Extract } & InstallUploadedArchiveParams); + | ({ installSource: Extract } & InstallUploadedArchiveParams) +); export async function installPackage(args: InstallPackageParams) { if (!('installSource' in args)) { @@ -463,23 +476,25 @@ export async function installPackage(args: InstallPackageParams) { const { savedObjectsClient, esClient } = args; if (args.installSource === 'registry') { - const { pkgkey, force } = args; + const { pkgkey, force, spaceId } = args; logger.debug(`kicking off install of ${pkgkey} from registry`); const response = installPackageFromRegistry({ savedObjectsClient, pkgkey, esClient, + spaceId, force, }); return response; } else if (args.installSource === 'upload') { - const { archiveBuffer, contentType } = args; + const { archiveBuffer, contentType, spaceId } = args; logger.debug(`kicking off install of uploaded package`); const response = installPackageByUpload({ savedObjectsClient, esClient, archiveBuffer, contentType, + spaceId, }); return response; } @@ -515,6 +530,7 @@ export async function createInstallation(options: { savedObjectsClient: SavedObjectsClientContract; packageInfo: InstallablePackage; installSource: InstallSource; + spaceId: string; }) { const { savedObjectsClient, packageInfo, installSource } = options; const { name: pkgName, version: pkgVersion } = packageInfo; @@ -534,6 +550,7 @@ export async function createInstallation(options: { PACKAGES_SAVED_OBJECT_TYPE, { installed_kibana: [], + installed_kibana_space_id: options.spaceId, installed_es: [], package_assets: [], es_index_patterns: toSaveESIndexPatterns, @@ -627,6 +644,7 @@ export async function ensurePackagesCompletedInstall( savedObjectsClient, pkgkey, esClient, + spaceId: pkg.attributes.installed_kibana_space_id || DEFAULT_SPACE_ID, }) ); } diff --git a/x-pack/plugins/fleet/server/services/epm/packages/remove.ts b/x-pack/plugins/fleet/server/services/epm/packages/remove.ts index 848d17f78c929..b3b99bfd05e03 100644 --- a/x-pack/plugins/fleet/server/services/epm/packages/remove.ts +++ b/x-pack/plugins/fleet/server/services/epm/packages/remove.ts @@ -6,9 +6,15 @@ */ import type { ElasticsearchClient, SavedObjectsClientContract } from 'src/core/server'; + import Boom from '@hapi/boom'; +import type { SavedObject } from 'src/core/server'; + +import { SavedObjectsClient } from '../../../../../../../src/core/server'; + import { PACKAGE_POLICY_SAVED_OBJECT_TYPE, PACKAGES_SAVED_OBJECT_TYPE } from '../../../constants'; +import { DEFAULT_SPACE_ID } from '../../../../../spaces/common/constants'; import { ElasticsearchAssetType } from '../../../types'; import type { AssetReference, @@ -25,6 +31,7 @@ import { packagePolicyService, appContextService } from '../..'; import { deletePackageCache } from '../archive'; import { deleteIlms } from '../elasticsearch/datastream_ilm/remove'; import { removeArchiveEntries } from '../archive/storage'; +import { SavedObjectsUtils } from '../../../../../../../src/core/server'; import { getInstallation, kibanaSavedObjectTypes } from './index'; @@ -80,10 +87,15 @@ export async function removeInstallation(options: { async function deleteKibanaAssets( installedObjects: KibanaAssetReference[], - savedObjectsClient: SavedObjectsClientContract + spaceId: string = DEFAULT_SPACE_ID ) { + const savedObjectsClient = new SavedObjectsClient( + appContextService.getSavedObjects().createInternalRepository() + ); + const namespace = SavedObjectsUtils.namespaceStringToId(spaceId); const { resolved_objects: resolvedObjects } = await savedObjectsClient.bulkResolve( - installedObjects + installedObjects, + { namespace } ); const foundObjects = resolvedObjects.filter( @@ -94,7 +106,7 @@ async function deleteKibanaAssets( // we filter these out before calling delete const assetsToDelete = foundObjects.map(({ saved_object: { id, type } }) => ({ id, type })); const promises = assetsToDelete.map(async ({ id, type }) => { - return savedObjectsClient.delete(type, id); + return savedObjectsClient.delete(type, id, { namespace }); }); return Promise.all(promises); @@ -123,12 +135,15 @@ function deleteESAssets( } async function deleteAssets( - { installed_es: installedEs, installed_kibana: installedKibana }: Installation, + { + installed_es: installedEs, + installed_kibana: installedKibana, + installed_kibana_space_id: spaceId = DEFAULT_SPACE_ID, + }: Installation, savedObjectsClient: SavedObjectsClientContract, esClient: ElasticsearchClient ) { const logger = appContextService.getLogger(); - // must delete index templates first, or component templates which reference them cannot be deleted // must delete ingestPipelines first, or ml models referenced in them cannot be deleted. // separate the assets into Index Templates and other assets. @@ -155,7 +170,7 @@ async function deleteAssets( // then the other asset types await Promise.all([ ...deleteESAssets(otherAssets, esClient), - deleteKibanaAssets(installedKibana, savedObjectsClient), + deleteKibanaAssets(installedKibana, spaceId), ]); } catch (err) { // in the rollback case, partial installs are likely, so missing assets are not an error @@ -187,19 +202,24 @@ async function deleteComponentTemplate(esClient: ElasticsearchClient, name: stri } } -export async function deleteKibanaSavedObjectsAssets( - savedObjectsClient: SavedObjectsClientContract, - installedRefs: KibanaAssetReference[] -) { +export async function deleteKibanaSavedObjectsAssets({ + savedObjectsClient, + installedPkg, +}: { + savedObjectsClient: SavedObjectsClientContract; + installedPkg: SavedObject; +}) { + const { installed_kibana: installedRefs, installed_kibana_space_id: spaceId } = + installedPkg.attributes; if (!installedRefs.length) return; const logger = appContextService.getLogger(); const assetsToDelete = installedRefs .filter(({ type }) => kibanaSavedObjectTypes.includes(type)) - .map(({ id, type }) => ({ id, type })); + .map(({ id, type }) => ({ id, type } as KibanaAssetReference)); try { - await deleteKibanaAssets(assetsToDelete, savedObjectsClient); + await deleteKibanaAssets(assetsToDelete, spaceId); } catch (err) { // in the rollback case, partial installs are likely, so missing assets are not an error if (!savedObjectsClient.errors.isNotFoundError(err)) { diff --git a/x-pack/plugins/fleet/server/services/package_policy.ts b/x-pack/plugins/fleet/server/services/package_policy.ts index e0bdda6e2449b..03b99a291d094 100644 --- a/x-pack/plugins/fleet/server/services/package_policy.ts +++ b/x-pack/plugins/fleet/server/services/package_policy.ts @@ -18,6 +18,8 @@ import type { import uuid from 'uuid'; import { safeLoad } from 'js-yaml'; +import { DEFAULT_SPACE_ID } from '../../../spaces/common/constants'; + import type { AuthenticatedUser } from '../../../security/server'; import { packageToPackagePolicy, @@ -92,6 +94,7 @@ class PackagePolicyService { esClient: ElasticsearchClient, packagePolicy: NewPackagePolicy, options?: { + spaceId?: string; id?: string; user?: AuthenticatedUser; bumpRevision?: boolean; @@ -134,6 +137,7 @@ class PackagePolicyService { const [, packageInfo] = await Promise.all([ ensureInstalledPackage({ esClient, + spaceId: options?.spaceId || DEFAULT_SPACE_ID, savedObjectsClient: soClient, pkgName: packagePolicy.package.name, pkgVersion: packagePolicy.package.version, diff --git a/x-pack/plugins/fleet/server/services/preconfiguration.test.ts b/x-pack/plugins/fleet/server/services/preconfiguration.test.ts index 8324079e10da8..3bba721d0dffb 100644 --- a/x-pack/plugins/fleet/server/services/preconfiguration.test.ts +++ b/x-pack/plugins/fleet/server/services/preconfiguration.test.ts @@ -9,6 +9,7 @@ import uuid from 'uuid'; import { elasticsearchServiceMock, savedObjectsClientMock } from 'src/core/server/mocks'; import { SavedObjectsErrorHelpers } from '../../../../../src/core/server'; +import { DEFAULT_SPACE_ID } from '../../../spaces/common/constants'; import type { InstallResult, @@ -225,7 +226,8 @@ describe('policy preconfiguration', () => { esClient, [], [], - mockDefaultOutput + mockDefaultOutput, + DEFAULT_SPACE_ID ); expect(policies.length).toBe(0); @@ -242,7 +244,8 @@ describe('policy preconfiguration', () => { esClient, [], [{ name: 'test_package', version: '3.0.0' }], - mockDefaultOutput + mockDefaultOutput, + DEFAULT_SPACE_ID ); expect(policies.length).toBe(0); @@ -271,7 +274,8 @@ describe('policy preconfiguration', () => { }, ] as PreconfiguredAgentPolicy[], [{ name: 'test_package', version: '3.0.0' }], - mockDefaultOutput + mockDefaultOutput, + DEFAULT_SPACE_ID ); expect(policies.length).toEqual(1); @@ -322,7 +326,8 @@ describe('policy preconfiguration', () => { }, ] as PreconfiguredAgentPolicy[], [{ name: 'test_package', version: '3.0.0' }], - mockDefaultOutput + mockDefaultOutput, + DEFAULT_SPACE_ID ); expect(mockedPackagePolicyService.create).not.toBeCalled(); @@ -371,7 +376,8 @@ describe('policy preconfiguration', () => { }, ] as PreconfiguredAgentPolicy[], [{ name: 'test_package', version: '3.0.0' }], - mockDefaultOutput + mockDefaultOutput, + DEFAULT_SPACE_ID ); expect(mockedPackagePolicyService.create).toBeCalledTimes(1); @@ -398,7 +404,8 @@ describe('policy preconfiguration', () => { { name: 'test_package', version: '3.0.0' }, { name: 'test_package', version: '2.0.0' }, ], - mockDefaultOutput + mockDefaultOutput, + DEFAULT_SPACE_ID ) ).rejects.toThrow( 'Duplicate packages specified in configuration: test_package-3.0.0, test_package-2.0.0' @@ -429,7 +436,8 @@ describe('policy preconfiguration', () => { esClient, policies, [{ name: 'test_package', version: '3.0.0' }], - mockDefaultOutput + mockDefaultOutput, + DEFAULT_SPACE_ID ) ).rejects.toThrow( '[Test policy] could not be added. [test_package] could not be installed due to error: [Error: REGISTRY ERROR]' @@ -460,7 +468,8 @@ describe('policy preconfiguration', () => { esClient, policies, [{ name: 'CANNOT_MATCH', version: 'x.y.z' }], - mockDefaultOutput + mockDefaultOutput, + DEFAULT_SPACE_ID ) ).rejects.toThrow( '[Test policy] could not be added. [test_package] is not installed, add [test_package] to [xpack.fleet.packages] or remove it from [Test package].' @@ -484,7 +493,8 @@ describe('policy preconfiguration', () => { }, ] as PreconfiguredAgentPolicy[], [], - mockDefaultOutput + mockDefaultOutput, + DEFAULT_SPACE_ID ); expect(policiesA.length).toEqual(1); @@ -509,7 +519,8 @@ describe('policy preconfiguration', () => { }, ] as PreconfiguredAgentPolicy[], [], - mockDefaultOutput + mockDefaultOutput, + DEFAULT_SPACE_ID ); expect(policiesB.length).toEqual(1); @@ -548,7 +559,8 @@ describe('policy preconfiguration', () => { }, ] as PreconfiguredAgentPolicy[], [], - mockDefaultOutput + mockDefaultOutput, + DEFAULT_SPACE_ID ); expect(spyAgentPolicyServiceUpdate).toBeCalled(); expect(spyAgentPolicyServiceUpdate).toBeCalledWith( @@ -584,7 +596,8 @@ describe('policy preconfiguration', () => { esClient, [policy], [], - mockDefaultOutput + mockDefaultOutput, + DEFAULT_SPACE_ID ); expect(spyAgentPolicyServiceUpdate).not.toBeCalled(); expect(policies.length).toEqual(1); diff --git a/x-pack/plugins/fleet/server/services/preconfiguration.ts b/x-pack/plugins/fleet/server/services/preconfiguration.ts index a41c7606287ee..8593f753de738 100644 --- a/x-pack/plugins/fleet/server/services/preconfiguration.ts +++ b/x-pack/plugins/fleet/server/services/preconfiguration.ts @@ -144,7 +144,8 @@ export async function ensurePreconfiguredPackagesAndPolicies( esClient: ElasticsearchClient, policies: PreconfiguredAgentPolicy[] = [], packages: PreconfiguredPackage[] = [], - defaultOutput: Output + defaultOutput: Output, + spaceId: string ): Promise { const logger = appContextService.getLogger(); @@ -179,6 +180,7 @@ export async function ensurePreconfiguredPackagesAndPolicies( pkg.version === PRECONFIGURATION_LATEST_KEYWORD ? pkg.name : pkg ), force: true, // Always force outdated packages to be installed if a later version isn't installed + spaceId, }); const fulfilledPackages = []; diff --git a/x-pack/plugins/fleet/server/services/setup.ts b/x-pack/plugins/fleet/server/services/setup.ts index 18c66e8267468..185be7bffdf54 100644 --- a/x-pack/plugins/fleet/server/services/setup.ts +++ b/x-pack/plugins/fleet/server/services/setup.ts @@ -12,6 +12,7 @@ import type { ElasticsearchClient, SavedObjectsClientContract } from 'src/core/s import { AUTO_UPDATE_PACKAGES } from '../../common'; import type { DefaultPackagesInstallationError, PreconfigurationError } from '../../common'; import { SO_SEARCH_LIMIT, DEFAULT_PACKAGES } from '../constants'; +import { DEFAULT_SPACE_ID } from '../../../spaces/common/constants'; import { appContextService } from './app_context'; import { agentPolicyService } from './agent_policy'; @@ -103,7 +104,8 @@ async function createSetupSideEffects( esClient, policies, packages, - defaultOutput + defaultOutput, + DEFAULT_SPACE_ID ); logger.debug('Cleaning up Fleet outputs'); @@ -160,6 +162,7 @@ export async function ensureFleetGlobalEsAssets( savedObjectsClient: soClient, pkgkey: pkgToPkgKey({ name: installation.name, version: installation.version }), esClient, + spaceId: DEFAULT_SPACE_ID, // Force install the package will update the index template and the datastream write indices force: true, }).catch((err) => { diff --git a/x-pack/plugins/fleet/server/types/request_context.ts b/x-pack/plugins/fleet/server/types/request_context.ts index aafa8765723d1..0f570f1877627 100644 --- a/x-pack/plugins/fleet/server/types/request_context.ts +++ b/x-pack/plugins/fleet/server/types/request_context.ts @@ -33,6 +33,7 @@ export interface FleetRequestHandlerContext extends RequestHandlerContext { */ readonly internalSoClient: SavedObjectsClientContract; }; + spaceId: string; }; } diff --git a/x-pack/plugins/fleet/storybook/context/fixtures/packages.ts b/x-pack/plugins/fleet/storybook/context/fixtures/packages.ts index 4c13b6b6bf8cb..a3e436b3f9718 100644 --- a/x-pack/plugins/fleet/storybook/context/fixtures/packages.ts +++ b/x-pack/plugins/fleet/storybook/context/fixtures/packages.ts @@ -50,6 +50,7 @@ export const items: GetPackagesResponse['items'] = [ id: 'ga_installed', attributes: { installed_kibana: [], + installed_kibana_space_id: 'default', installed_es: [], package_assets: [], es_index_patterns: {}, @@ -86,6 +87,7 @@ export const items: GetPackagesResponse['items'] = [ id: 'ga_installed_update', attributes: { installed_kibana: [], + installed_kibana_space_id: 'default', installed_es: [], package_assets: [], es_index_patterns: {}, @@ -147,6 +149,7 @@ export const items: GetPackagesResponse['items'] = [ id: 'beta_installed', attributes: { installed_kibana: [], + installed_kibana_space_id: 'default', installed_es: [], package_assets: [], es_index_patterns: {}, @@ -183,6 +186,7 @@ export const items: GetPackagesResponse['items'] = [ id: 'beta_installed_update', attributes: { installed_kibana: [], + installed_kibana_space_id: 'default', installed_es: [], package_assets: [], es_index_patterns: {}, @@ -253,6 +257,7 @@ export const items: GetPackagesResponse['items'] = [ id: 'exp_installed', attributes: { installed_kibana: [], + installed_kibana_space_id: 'default', installed_es: [], package_assets: [], es_index_patterns: {}, @@ -289,6 +294,7 @@ export const items: GetPackagesResponse['items'] = [ id: 'exp_installed_update', attributes: { installed_kibana: [], + installed_kibana_space_id: 'default', installed_es: [], package_assets: [], es_index_patterns: {}, diff --git a/x-pack/test/fleet_api_integration/apis/epm/__snapshots__/install_by_upload.snap b/x-pack/test/fleet_api_integration/apis/epm/__snapshots__/install_by_upload.snap index 5eb4ae1808d7b..2e0014b998bdc 100644 --- a/x-pack/test/fleet_api_integration/apis/epm/__snapshots__/install_by_upload.snap +++ b/x-pack/test/fleet_api_integration/apis/epm/__snapshots__/install_by_upload.snap @@ -446,6 +446,7 @@ Object { "type": "search", }, ], + "installed_kibana_space_id": "default", "name": "apache", "package_assets": Array [ Object { diff --git a/x-pack/test/fleet_api_integration/apis/epm/index.js b/x-pack/test/fleet_api_integration/apis/epm/index.js index 3428b4c1ded08..70c8748932f6e 100644 --- a/x-pack/test/fleet_api_integration/apis/epm/index.js +++ b/x-pack/test/fleet_api_integration/apis/epm/index.js @@ -19,6 +19,7 @@ export default function loadTests({ loadTestFile }) { loadTestFile(require.resolve('./install_overrides')); loadTestFile(require.resolve('./install_prerelease')); loadTestFile(require.resolve('./install_remove_assets')); + loadTestFile(require.resolve('./install_remove_kbn_assets_in_space')); loadTestFile(require.resolve('./install_remove_multiple')); loadTestFile(require.resolve('./install_update')); loadTestFile(require.resolve('./bulk_upgrade')); diff --git a/x-pack/test/fleet_api_integration/apis/epm/install_remove_assets.ts b/x-pack/test/fleet_api_integration/apis/epm/install_remove_assets.ts index 4c6976c463217..af807d4393daf 100644 --- a/x-pack/test/fleet_api_integration/apis/epm/install_remove_assets.ts +++ b/x-pack/test/fleet_api_integration/apis/epm/install_remove_assets.ts @@ -496,6 +496,7 @@ const expectAssetsInstalled = ({ package_assets: sortBy(res.attributes.package_assets, (o: AssetReference) => o.type), }; expect(sortedRes).eql({ + installed_kibana_space_id: 'default', installed_kibana: [ { id: 'sample_dashboard', diff --git a/x-pack/test/fleet_api_integration/apis/epm/install_remove_kbn_assets_in_space.ts b/x-pack/test/fleet_api_integration/apis/epm/install_remove_kbn_assets_in_space.ts new file mode 100644 index 0000000000000..9e364f28f8b3e --- /dev/null +++ b/x-pack/test/fleet_api_integration/apis/epm/install_remove_kbn_assets_in_space.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 + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import type { Client } from '@elastic/elasticsearch'; +import expect from '@kbn/expect'; +import { FtrProviderContext } from '../../../api_integration/ftr_provider_context'; +import { skipIfNoDockerRegistry } from '../../helpers'; +import { setupFleetAndAgents } from '../agents/services'; + +const testSpaceId = 'fleet_test_space'; + +export default function (providerContext: FtrProviderContext) { + const { getService } = providerContext; + const kibanaServer = getService('kibanaServer'); + const supertest = getService('supertest'); + const dockerServers = getService('dockerServers'); + const server = dockerServers.get('registry'); + const es: Client = getService('es'); + const pkgName = 'only_dashboard'; + const pkgVersion = '0.1.0'; + + const uninstallPackage = async (pkg: string, version: string) => { + await supertest.delete(`/api/fleet/epm/packages/${pkg}/${version}`).set('kbn-xsrf', 'xxxx'); + }; + + const installPackageInSpace = async (pkg: string, version: string, spaceId: string) => { + await supertest + .post(`/s/${spaceId}/api/fleet/epm/packages/${pkg}/${version}`) + .set('kbn-xsrf', 'xxxx') + .send({ force: true }); + }; + const createSpace = async (spaceId: string) => { + await supertest.post(`/api/spaces/space`).set('kbn-xsrf', 'xxxx').send({ + name: spaceId, + id: spaceId, + initials: 's', + color: '#D6BF57', + disabledFeatures: [], + imageUrl: '', + }); + }; + + const deleteSpace = async (spaceId: string) => { + await supertest.delete(`/api/spaces/space/${spaceId}`).set('kbn-xsrf', 'xxxx').send(); + }; + + describe('installs and uninstalls all assets (non default space)', async () => { + skipIfNoDockerRegistry(providerContext); + setupFleetAndAgents(providerContext); + before(async () => { + await createSpace(testSpaceId); + }); + + after(async () => { + await deleteSpace(testSpaceId); + }); + describe('installs all assets when installing a package for the first time in non default space', async () => { + before(async () => { + if (!server.enabled) return; + await installPackageInSpace(pkgName, pkgVersion, testSpaceId); + }); + after(async () => { + if (!server.enabled) return; + await uninstallPackage(pkgName, pkgVersion); + }); + + expectAssetsInstalled({ + pkgVersion, + pkgName, + es, + kibanaServer, + }); + }); + + describe('uninstalls all assets when uninstalling a package from a different space', async () => { + before(async () => { + if (!server.enabled) return; + await installPackageInSpace(pkgName, pkgVersion, testSpaceId); + await uninstallPackage(pkgName, pkgVersion); + }); + + it('should have uninstalled the kibana assets', async function () { + let resDashboard; + try { + resDashboard = await kibanaServer.savedObjects.get({ + type: 'dashboard', + id: 'test_dashboard', + space: testSpaceId, + }); + } catch (err) { + resDashboard = err; + } + expect(resDashboard.response.data.statusCode).equal(404); + + let resVis; + try { + resVis = await kibanaServer.savedObjects.get({ + type: 'visualization', + id: 'test_visualization', + space: testSpaceId, + }); + } catch (err) { + resVis = err; + } + expect(resVis.response.data.statusCode).equal(404); + let resSearch; + try { + resVis = await kibanaServer.savedObjects.get({ + type: 'search', + id: 'test_search', + space: testSpaceId, + }); + } catch (err) { + resSearch = err; + } + expect(resSearch.response.data.statusCode).equal(404); + }); + }); + }); +} + +const expectAssetsInstalled = ({ + pkgVersion, + pkgName, + es, + kibanaServer, +}: { + pkgVersion: string; + pkgName: string; + es: Client; + kibanaServer: any; +}) => { + it('should have installed the kibana assets', async function () { + // These are installed from Fleet along with every package + const resIndexPatternLogs = await kibanaServer.savedObjects.get({ + type: 'index-pattern', + id: 'logs-*', + }); + expect(resIndexPatternLogs.id).equal('logs-*'); + const resIndexPatternMetrics = await kibanaServer.savedObjects.get({ + type: 'index-pattern', + id: 'metrics-*', + }); + expect(resIndexPatternMetrics.id).equal('metrics-*'); + + // These are the assets from the package + const resDashboard = await kibanaServer.savedObjects.get({ + type: 'dashboard', + id: 'test_dashboard', + space: testSpaceId, + }); + expect(resDashboard.id).equal('test_dashboard'); + + const resVis = await kibanaServer.savedObjects.get({ + type: 'visualization', + id: 'test_visualization', + space: testSpaceId, + }); + expect(resVis.id).equal('test_visualization'); + const resSearch = await kibanaServer.savedObjects.get({ + type: 'search', + id: 'test_search', + space: testSpaceId, + }); + expect(resSearch.id).equal('test_search'); + }); +}; diff --git a/x-pack/test/fleet_api_integration/apis/epm/update_assets.ts b/x-pack/test/fleet_api_integration/apis/epm/update_assets.ts index faa95f3b65e60..1947396b8a2bd 100644 --- a/x-pack/test/fleet_api_integration/apis/epm/update_assets.ts +++ b/x-pack/test/fleet_api_integration/apis/epm/update_assets.ts @@ -328,6 +328,7 @@ export default function (providerContext: FtrProviderContext) { id: 'all_assets', }); expect(res.attributes).eql({ + installed_kibana_space_id: 'default', installed_kibana: [ { id: 'sample_dashboard', diff --git a/x-pack/test/fleet_api_integration/apis/fixtures/test_packages/only_dashboard/0.1.0/docs/README.md b/x-pack/test/fleet_api_integration/apis/fixtures/test_packages/only_dashboard/0.1.0/docs/README.md new file mode 100644 index 0000000000000..2617f1fcabe11 --- /dev/null +++ b/x-pack/test/fleet_api_integration/apis/fixtures/test_packages/only_dashboard/0.1.0/docs/README.md @@ -0,0 +1,3 @@ +# Test package + +For testing that a package installs its elasticsearch assets when installed for the first time (not updating) and removing the package diff --git a/x-pack/test/fleet_api_integration/apis/fixtures/test_packages/only_dashboard/0.1.0/img/logo_overrides_64_color.svg b/x-pack/test/fleet_api_integration/apis/fixtures/test_packages/only_dashboard/0.1.0/img/logo_overrides_64_color.svg new file mode 100644 index 0000000000000..b03007a76ffcc --- /dev/null +++ b/x-pack/test/fleet_api_integration/apis/fixtures/test_packages/only_dashboard/0.1.0/img/logo_overrides_64_color.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/x-pack/test/fleet_api_integration/apis/fixtures/test_packages/only_dashboard/0.1.0/kibana/dashboard/test_dashboard.json b/x-pack/test/fleet_api_integration/apis/fixtures/test_packages/only_dashboard/0.1.0/kibana/dashboard/test_dashboard.json new file mode 100644 index 0000000000000..915d2fea2438c --- /dev/null +++ b/x-pack/test/fleet_api_integration/apis/fixtures/test_packages/only_dashboard/0.1.0/kibana/dashboard/test_dashboard.json @@ -0,0 +1,22 @@ +{ + "attributes": { + "description": "Sample dashboard", + "hits": 0, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[],\"highlightAll\":true,\"query\":{\"language\":\"kuery\",\"query\":\"\"},\"version\":true}" + }, + "optionsJSON": "{\"darkTheme\":false}", + "panelsJSON": "[{\"embeddableConfig\":{},\"gridData\":{\"h\":12,\"i\":\"1\",\"w\":24,\"x\":0,\"y\":0},\"panelIndex\":\"1\",\"panelRefName\":\"panel_0\",\"version\":\"7.3.0\"},{\"embeddableConfig\":{\"columns\":[\"kafka.log.class\",\"kafka.log.trace.class\",\"kafka.log.trace.full\"],\"sort\":[\"@timestamp\",\"desc\"]},\"gridData\":{\"h\":12,\"i\":\"2\",\"w\":24,\"x\":24,\"y\":0},\"panelIndex\":\"2\",\"panelRefName\":\"panel_1\",\"version\":\"7.3.0\"},{\"embeddableConfig\":{\"columns\":[\"log.level\",\"kafka.log.component\",\"message\"],\"sort\":[\"@timestamp\",\"desc\"]},\"gridData\":{\"h\":20,\"i\":\"3\",\"w\":48,\"x\":0,\"y\":20},\"panelIndex\":\"3\",\"panelRefName\":\"panel_2\",\"version\":\"7.3.0\"},{\"embeddableConfig\":{},\"gridData\":{\"h\":8,\"i\":\"4\",\"w\":48,\"x\":0,\"y\":12},\"panelIndex\":\"4\",\"panelRefName\":\"panel_3\",\"version\":\"7.3.0\"}]", + "timeRestore": false, + "title": "[Logs Sample] Overview ECS", + "version": 1 + }, + "references": [ + { "id": "test_visualization", "name": "panel_0", "type": "visualization" }, + { "id": "test_search", "name": "panel_1", "type": "search" }, + { "id": "test_search", "name": "panel_2", "type": "search" }, + { "id": "test_visualization", "name": "panel_3", "type": "visualization" } + ], + "id": "test_dashboard", + "type": "dashboard" +} diff --git a/x-pack/test/fleet_api_integration/apis/fixtures/test_packages/only_dashboard/0.1.0/kibana/search/test_search.json b/x-pack/test/fleet_api_integration/apis/fixtures/test_packages/only_dashboard/0.1.0/kibana/search/test_search.json new file mode 100644 index 0000000000000..4e0d570fca763 --- /dev/null +++ b/x-pack/test/fleet_api_integration/apis/fixtures/test_packages/only_dashboard/0.1.0/kibana/search/test_search.json @@ -0,0 +1,24 @@ +{ + "attributes": { + "columns": [ + "log.level", + "kafka.log.component", + "message" + ], + "description": "", + "hits": 0, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[{\"$state\":{\"store\":\"appState\"},\"meta\":{\"alias\":null,\"disabled\":false,\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index\",\"key\":\"dataset.name\",\"negate\":false,\"params\":{\"query\":\"kafka.log\",\"type\":\"phrase\"},\"type\":\"phrase\",\"value\":\"log\"},\"query\":{\"match\":{\"dataset.name\":{\"query\":\"kafka.log\",\"type\":\"phrase\"}}}}],\"highlightAll\":true,\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\",\"query\":{\"language\":\"kuery\",\"query\":\"\"},\"version\":true}" + }, + "sort": [ + [ + "@timestamp", + "desc" + ] + ], + "title": "All logs [Logs Kafka] ECS", + "version": 1 + }, + "id": "test_search", + "type": "search" +} \ No newline at end of file diff --git a/x-pack/test/fleet_api_integration/apis/fixtures/test_packages/only_dashboard/0.1.0/kibana/visualization/test_visualization.json b/x-pack/test/fleet_api_integration/apis/fixtures/test_packages/only_dashboard/0.1.0/kibana/visualization/test_visualization.json new file mode 100644 index 0000000000000..7afd27374f3b1 --- /dev/null +++ b/x-pack/test/fleet_api_integration/apis/fixtures/test_packages/only_dashboard/0.1.0/kibana/visualization/test_visualization.json @@ -0,0 +1,11 @@ +{ + "attributes": { + "description": "sample visualization update", + "title": "sample vis title", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"aggs\":[{\"enabled\":true,\"id\":\"1\",\"params\":{},\"schema\":\"metric\",\"type\":\"count\"},{\"enabled\":true,\"id\":\"2\",\"params\":{\"extended_bounds\":{},\"field\":\"@timestamp\",\"interval\":\"auto\",\"min_doc_count\":1},\"schema\":\"segment\",\"type\":\"date_histogram\"},{\"enabled\":true,\"id\":\"3\",\"params\":{\"customLabel\":\"Log Level\",\"field\":\"log.level\",\"order\":\"desc\",\"orderBy\":\"1\",\"size\":5},\"schema\":\"group\",\"type\":\"terms\"}],\"params\":{\"addLegend\":true,\"addTimeMarker\":false,\"addTooltip\":true,\"categoryAxes\":[{\"id\":\"CategoryAxis-1\",\"labels\":{\"show\":true,\"truncate\":100},\"position\":\"bottom\",\"scale\":{\"type\":\"linear\"},\"show\":true,\"style\":{},\"title\":{\"text\":\"@timestamp per day\"},\"type\":\"category\"}],\"grid\":{\"categoryLines\":false,\"style\":{\"color\":\"#eee\"}},\"legendPosition\":\"right\",\"seriesParams\":[{\"data\":{\"id\":\"1\",\"label\":\"Count\"},\"drawLinesBetweenPoints\":true,\"mode\":\"stacked\",\"show\":\"true\",\"showCircles\":true,\"type\":\"histogram\",\"valueAxis\":\"ValueAxis-1\"}],\"times\":[],\"type\":\"histogram\",\"valueAxes\":[{\"id\":\"ValueAxis-1\",\"labels\":{\"filter\":false,\"rotate\":0,\"show\":true,\"truncate\":100},\"name\":\"LeftAxis-1\",\"position\":\"left\",\"scale\":{\"mode\":\"normal\",\"type\":\"linear\"},\"show\":true,\"style\":{},\"title\":{\"text\":\"Count\"},\"type\":\"value\"}]},\"title\":\"Log levels over time [Logs Kafka] ECS\",\"type\":\"histogram\"}" + }, + "id": "test_visualization", + "type": "visualization" +} \ No newline at end of file diff --git a/x-pack/test/fleet_api_integration/apis/fixtures/test_packages/only_dashboard/0.1.0/manifest.yml b/x-pack/test/fleet_api_integration/apis/fixtures/test_packages/only_dashboard/0.1.0/manifest.yml new file mode 100644 index 0000000000000..129ee4e2c133c --- /dev/null +++ b/x-pack/test/fleet_api_integration/apis/fixtures/test_packages/only_dashboard/0.1.0/manifest.yml @@ -0,0 +1,20 @@ +format_version: 1.0.0 +name: only_dashboard +title: Only installs one dashboard +description: This package is used to test installing assets into a non-default space. +version: 0.1.0 +categories: [] +release: beta +type: integration +license: basic + +requirement: + elasticsearch: + versions: '>7.7.0' + kibana: + versions: '>7.7.0' + +icons: + - src: '/img/logo_overrides_64_color.svg' + size: '16x16' + type: 'image/svg+xml' From d154bb29a6200c3f39947d59bdd7d6b51807e85d Mon Sep 17 00:00:00 2001 From: Anton Dosov Date: Fri, 17 Dec 2021 17:31:35 +0100 Subject: [PATCH 10/40] Update app services code owners (#121530) --- .github/CODEOWNERS | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index ad992066f8503..cd3188ad971af 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -63,6 +63,8 @@ /packages/elastic-datemath/ @elastic/kibana-app-services /packages/kbn-interpreter/ @elastic/kibana-app-services /packages/kbn-react-field/ @elastic/kibana-app-services +/packages/kbn-es-query/ @elastic/kibana-app-services +/packages/kbn-field-types/ @elastic/kibana-app-services /src/plugins/bfetch/ @elastic/kibana-app-services /src/plugins/data/ @elastic/kibana-app-services /src/plugins/data_views/ @elastic/kibana-app-services From e559b31320ad14d7baa0d9dc0312ed8676eb8123 Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Fri, 17 Dec 2021 19:07:47 +0200 Subject: [PATCH 11/40] [XY] Fixes the broken chart when an agg is placed in another axis and then is hidden (#121488) * [XY] Fixes the broken chart when an agg is placed in another axis and then is hidden * Fix yAxes ticks * Fix test borkem due to the change of the implementation --- .../xy/public/config/get_config.test.ts | 28 +++++++++++++++++-- .../vis_types/xy/public/config/get_config.ts | 20 +++++++------ 2 files changed, 37 insertions(+), 11 deletions(-) diff --git a/src/plugins/vis_types/xy/public/config/get_config.test.ts b/src/plugins/vis_types/xy/public/config/get_config.test.ts index d046ac17c2b27..52d68b40766a9 100644 --- a/src/plugins/vis_types/xy/public/config/get_config.test.ts +++ b/src/plugins/vis_types/xy/public/config/get_config.test.ts @@ -38,7 +38,31 @@ describe('getConfig', () => { it('assigns the correct formatter per y axis', () => { const config = getConfig(visData, visParamsWithTwoYAxes); expect(config.yAxes.length).toBe(2); - expect(config.yAxes[0].ticks?.formatter).toStrictEqual(config.aspects.y[1].formatter); - expect(config.yAxes[1].ticks?.formatter).toStrictEqual(config.aspects.y[0].formatter); + expect(config.yAxes[0].ticks?.formatter).toStrictEqual(config.aspects.y[0].formatter); + expect(config.yAxes[1].ticks?.formatter).toStrictEqual(config.aspects.y[1].formatter); + }); + + it('assigns the correct number of yAxes if the agg is hidden', () => { + // We have two axes but the one y dimension is hidden + const newVisParams = { + ...visParamsWithTwoYAxes, + dimensions: { + ...visParamsWithTwoYAxes.dimensions, + y: [ + { + label: 'Average memory', + aggType: 'avg', + params: {}, + accessor: 1, + format: { + id: 'number', + params: {}, + }, + }, + ], + }, + }; + const config = getConfig(visData, newVisParams); + expect(config.yAxes.length).toBe(1); }); }); diff --git a/src/plugins/vis_types/xy/public/config/get_config.ts b/src/plugins/vis_types/xy/public/config/get_config.ts index 76fe1b21a74d6..e23fff937ad9b 100644 --- a/src/plugins/vis_types/xy/public/config/get_config.ts +++ b/src/plugins/vis_types/xy/public/config/get_config.ts @@ -48,15 +48,17 @@ export function getConfig( } = params; const aspects = getAspects(table.columns, params.dimensions); const tooltip = getTooltip(aspects, params); - const yAxes = params.valueAxes.map((a) => { - // find the correct aspect for each value axis - const aspectsIdx = params.seriesParams.findIndex((s) => s.valueAxis === a.id); - return getAxis( - a, - params.grid, - aspects.y[aspectsIdx > -1 ? aspectsIdx : 0], - params.seriesParams - ); + + const yAxes: Array> = []; + + params.dimensions.y.forEach((y) => { + const accessor = y.accessor; + const aspect = aspects.y.find(({ column }) => column === accessor); + const serie = params.seriesParams.find(({ data: { id } }) => id === aspect?.aggId); + const valueAxis = params.valueAxes.find(({ id }) => id === serie?.valueAxis); + if (aspect && valueAxis) { + yAxes.push(getAxis(valueAxis, params.grid, aspect, params.seriesParams)); + } }); const rotation = getRotation(params.categoryAxes[0]); From b6a85e7b8344d257647dfad1708667b9bcde5fd4 Mon Sep 17 00:00:00 2001 From: Ersin Erdal <92688503+ersin-erdal@users.noreply.github.com> Date: Fri, 17 Dec 2021 18:35:05 +0100 Subject: [PATCH 12/40] [RAC][Refactoring] Rename alerting types in triggers_actions_ui (#121107) * [RAC][Refactoring] Rename alerting types in triggers_actions_ui --- .../public/alert_types/always_firing.tsx | 22 +-- .../public/alert_types/astros.tsx | 38 ++--- x-pack/plugins/alerting/README.md | 20 +-- .../error_count_alert_trigger.stories.tsx | 60 +++---- .../error_count_alert_trigger/index.tsx | 26 ++-- .../alerting/register_apm_alerts.ts | 8 +- .../alerting/service_alert_trigger/index.tsx | 8 +- .../service_alert_trigger.test.tsx | 4 +- .../index.stories.tsx | 12 +- .../index.tsx | 30 ++-- .../index.tsx | 22 +-- .../index.tsx | 28 ++-- .../alerts/register_error_count_alert_type.ts | 18 +-- ...egister_transaction_duration_alert_type.ts | 34 ++-- ...transaction_duration_anomaly_alert_type.ts | 17 +- ...ister_transaction_error_rate_alert_type.ts | 20 +-- .../inventory/components/expression.test.tsx | 44 +++--- .../inventory/components/expression.tsx | 140 ++++++++--------- .../infra/public/alerting/inventory/index.ts | 2 +- .../criterion_preview_chart.tsx | 2 +- .../components/expression_editor/editor.tsx | 74 ++++----- .../hooks/use_chart_preview_data.tsx | 8 +- .../log_threshold/log_threshold_rule_type.ts | 2 +- .../components/expression.test.tsx | 20 +-- .../metric_anomaly/components/expression.tsx | 86 +++++------ .../public/alerting/metric_anomaly/index.ts | 6 +- .../components/expression.test.tsx | 22 +-- .../components/expression.tsx | 146 +++++++++--------- .../components/expression_row.test.tsx | 2 +- .../components/expression_row.tsx | 34 ++-- .../public/alerting/metric_threshold/index.ts | 2 +- .../log_threshold/log_threshold_executor.ts | 26 ++-- ...aly_detection_jobs_health_rule_trigger.tsx | 30 ++-- .../register_jobs_health_alerting_rule.ts | 2 +- .../alerting/ml_anomaly_alert_trigger.tsx | 42 ++--- .../ml/public/alerting/register_ml_alerts.ts | 30 ++-- .../public/alerts/alert_form.test.tsx | 19 +-- .../ccr_read_exceptions_alert/index.tsx | 6 +- .../param_details_form/expression.tsx | 26 ++-- .../cpu_usage_alert/cpu_usage_alert.tsx | 6 +- .../public/alerts/disk_usage_alert/index.tsx | 6 +- .../alert_param_duration.tsx | 6 +- .../flyout_expressions/alert_param_number.tsx | 6 +- .../alert_param_percentage.tsx | 6 +- .../alert_param_textfield.tsx | 6 +- .../alerts/large_shard_size_alert/index.tsx | 6 +- .../public/alerts/legacy_alert/expression.tsx | 10 +- .../alerts/legacy_alert/legacy_alert.tsx | 6 +- .../alerts/memory_usage_alert/index.tsx | 6 +- .../expression.tsx | 16 +- .../missing_monitoring_data_alert.tsx | 6 +- .../thread_pool_rejections_alert/index.tsx | 6 +- .../plugins/monitoring/public/legacy_shims.ts | 4 +- ...create_observability_rule_type_registry.ts | 8 +- x-pack/plugins/stack_alerts/README.md | 2 +- .../alert_types/es_query/expression.test.tsx | 10 +- .../alert_types/es_query/expression.tsx | 20 +-- .../public/alert_types/es_query/index.ts | 6 +- .../alert_types/geo_containment/index.ts | 6 +- .../expressions/boundary_index_expression.tsx | 6 +- .../expressions/entity_index_expression.tsx | 4 +- ...containment_alert_type_expression.test.tsx | 6 +- .../geo_containment/query_builder/index.tsx | 40 ++--- .../alert_types/threshold/expression.test.tsx | 14 +- .../alert_types/threshold/expression.tsx | 46 +++--- .../public/alert_types/threshold/index.ts | 6 +- .../threshold/visualization.test.tsx | 8 +- .../alert_types/threshold/visualization.tsx | 8 +- .../register_transform_health_rule.ts | 10 +- .../transform_health_rule_trigger.tsx | 24 +-- x-pack/plugins/triggers_actions_ui/README.md | 98 ++++++------ .../application/lib/action_variables.test.ts | 4 +- .../lib/alert_api/common_transformations.ts | 4 +- .../application/lib/alert_api/create.ts | 6 +- .../application/lib/alert_api/get_rule.ts | 6 +- .../lib/alert_api/rule_types.test.ts | 4 +- .../application/lib/alert_api/rule_types.ts | 12 +- .../public/application/lib/alert_api/rules.ts | 8 +- .../application/lib/alert_api/update.test.ts | 4 +- .../application/lib/alert_api/update.ts | 6 +- .../lib/alert_type_compare.test.ts | 20 +-- .../application/lib/alert_type_compare.ts | 10 +- .../public/application/lib/capabilities.ts | 6 +- .../lib/check_alert_type_enabled.test.tsx | 6 +- .../lib/check_alert_type_enabled.tsx | 6 +- .../lib/execution_duration_utils.test.ts | 2 +- .../lib/execution_duration_utils.ts | 2 +- .../application/lib/value_validators.test.ts | 14 +- .../application/lib/value_validators.ts | 4 +- .../action_form.test.tsx | 4 +- .../components/alert_details.test.tsx | 14 +- .../components/alert_details.tsx | 8 +- .../components/alert_details_route.test.tsx | 6 +- .../components/alert_details_route.tsx | 6 +- .../alert_details/components/alerts.test.tsx | 6 +- .../alert_details/components/alerts.tsx | 10 +- .../components/alerts_route.test.tsx | 6 +- .../alert_details/components/alerts_route.tsx | 6 +- .../components/view_in_app.test.tsx | 4 +- .../alert_details/components/view_in_app.tsx | 6 +- .../sections/alert_form/alert_add.test.tsx | 14 +- .../sections/alert_form/alert_add.tsx | 20 +-- .../sections/alert_form/alert_edit.test.tsx | 12 +- .../sections/alert_form/alert_edit.tsx | 16 +- .../sections/alert_form/alert_errors.test.tsx | 12 +- .../sections/alert_form/alert_errors.ts | 20 +-- .../sections/alert_form/alert_form.test.tsx | 40 ++--- .../sections/alert_form/alert_form.tsx | 72 ++++----- .../alert_form/alert_notify_when.test.tsx | 4 +- .../sections/alert_form/alert_reducer.test.ts | 10 +- .../sections/alert_form/alert_reducer.ts | 24 +-- .../sections/alert_form/has_alert_changed.ts | 4 +- .../components/alerts_list.test.tsx | 16 +- .../alerts_list/components/alerts_list.tsx | 12 +- .../collapsed_item_actions.test.tsx | 6 +- .../components/rule_enabled_switch.tsx | 6 +- .../with_bulk_alert_api_operations.test.tsx | 34 ++-- .../with_bulk_alert_api_operations.tsx | 76 ++++----- .../public/application/type_registry.test.ts | 22 +-- .../triggers_actions_ui/public/index.ts | 8 +- .../triggers_actions_ui/public/mocks.ts | 4 +- .../triggers_actions_ui/public/plugin.ts | 10 +- .../triggers_actions_ui/public/types.ts | 58 +++---- .../alerts/uptime_edit_alert_flyout.tsx | 4 +- .../alert_monitor_status.tsx | 36 ++--- .../alerts/anomaly_alert/anomaly_alert.tsx | 20 +-- .../availability_expression_select.tsx | 28 ++-- .../down_number_select.test.tsx | 4 +- .../down_number_select.tsx | 8 +- .../filters_expression_select.test.tsx | 16 +- .../filters_expression_select.tsx | 16 +- .../status_expression_select.tsx | 24 +-- .../time_expression_select.test.tsx | 4 +- .../time_expression_select.tsx | 10 +- .../alert_monitor_status.test.tsx | 4 +- .../alert_monitor_status.tsx | 34 ++-- .../monitor_list_drawer/enabled_alerts.tsx | 4 +- .../monitor_list_drawer.tsx | 4 +- .../lib/alert_types/duration_anomaly.tsx | 2 +- .../uptime/public/lib/alert_types/index.ts | 4 +- .../lazy_wrapper/validate_monitor_status.ts | 13 +- .../lib/alert_types/monitor_status.test.ts | 2 +- .../public/lib/alert_types/monitor_status.tsx | 8 +- .../uptime/public/lib/alert_types/tls.tsx | 4 +- .../public/lib/alert_types/tls_legacy.tsx | 10 +- .../uptime/public/state/actions/alerts.ts | 4 +- .../fixtures/plugins/alerts/public/plugin.ts | 4 +- 147 files changed, 1249 insertions(+), 1274 deletions(-) diff --git a/x-pack/examples/alerting_example/public/alert_types/always_firing.tsx b/x-pack/examples/alerting_example/public/alert_types/always_firing.tsx index e897ea074046c..27b8684e1a17e 100644 --- a/x-pack/examples/alerting_example/public/alert_types/always_firing.tsx +++ b/x-pack/examples/alerting_example/public/alert_types/always_firing.tsx @@ -21,8 +21,8 @@ import { ActionGroupWithCondition, AlertConditions, AlertConditionsGroup, - AlertTypeModel, - AlertTypeParamsExpressionProps, + RuleTypeModel, + RuleTypeParamsExpressionProps, } from '../../../../plugins/triggers_actions_ui/public'; import { AlwaysFiringParams, @@ -30,13 +30,13 @@ import { DEFAULT_INSTANCES_TO_GENERATE, } from '../../common/constants'; -export function getAlertType(): AlertTypeModel { +export function getAlertType(): RuleTypeModel { return { id: 'example.always-firing', description: 'Alert when called', iconClass: 'bolt', documentationUrl: null, - alertParamsExpression: AlwaysFiringExpression, + ruleParamsExpression: AlwaysFiringExpression, validate: (alertParams: AlwaysFiringParams) => { const { instances } = alertParams; const validationResult = { @@ -64,12 +64,12 @@ const DEFAULT_THRESHOLDS: AlwaysFiringParams['thresholds'] = { }; export const AlwaysFiringExpression: React.FunctionComponent< - AlertTypeParamsExpressionProps -> = ({ alertParams, setAlertParams, actionGroups, defaultActionGroupId }) => { + RuleTypeParamsExpressionProps +> = ({ ruleParams, setRuleParams, actionGroups, defaultActionGroupId }) => { const { instances = DEFAULT_INSTANCES_TO_GENERATE, thresholds = pick(DEFAULT_THRESHOLDS, defaultActionGroupId), - } = alertParams; + } = ruleParams; const actionGroupsWithConditions = actionGroups.map((actionGroup) => Number.isInteger(thresholds[actionGroup.id as AlwaysFiringActionGroupIds]) @@ -92,7 +92,7 @@ export const AlwaysFiringExpression: React.FunctionComponent< name="instances" value={instances} onChange={(event) => { - setAlertParams('instances', event.target.valueAsNumber); + setRuleParams('instances', event.target.valueAsNumber); }} /> @@ -105,7 +105,7 @@ export const AlwaysFiringExpression: React.FunctionComponent< headline={'Set different thresholds for randomly generated T-Shirt sizes'} actionGroups={actionGroupsWithConditions} onInitializeConditionsFor={(actionGroup) => { - setAlertParams('thresholds', { + setRuleParams('thresholds', { ...thresholds, ...pick(DEFAULT_THRESHOLDS, actionGroup.id), }); @@ -113,12 +113,12 @@ export const AlwaysFiringExpression: React.FunctionComponent< > { - setAlertParams('thresholds', omit(thresholds, actionGroup.id)); + setRuleParams('thresholds', omit(thresholds, actionGroup.id)); }} > { - setAlertParams('thresholds', { + setRuleParams('thresholds', { ...thresholds, [actionGroup.id]: actionGroup.conditions, }); diff --git a/x-pack/examples/alerting_example/public/alert_types/astros.tsx b/x-pack/examples/alerting_example/public/alert_types/astros.tsx index 39be40540c1f4..63ffa48c94399 100644 --- a/x-pack/examples/alerting_example/public/alert_types/astros.tsx +++ b/x-pack/examples/alerting_example/public/alert_types/astros.tsx @@ -22,7 +22,7 @@ import { flatten } from 'lodash'; import { ALERTING_EXAMPLE_APP_ID, Craft, Operator } from '../../common/constants'; import { SanitizedAlert } from '../../../../plugins/alerting/common'; import { PluginSetupContract as AlertingSetup } from '../../../../plugins/alerting/public'; -import { AlertTypeModel } from '../../../../plugins/triggers_actions_ui/public'; +import { RuleTypeModel } from '../../../../plugins/triggers_actions_ui/public'; export function registerNavigation(alerting: AlertingSetup) { alerting.registerNavigation( @@ -33,8 +33,8 @@ export function registerNavigation(alerting: AlertingSetup) { } interface PeopleinSpaceParamsProps { - alertParams: { outerSpaceCapacity?: number; craft?: string; op?: string }; - setAlertParams: (property: string, value: any) => void; + ruleParams: { outerSpaceCapacity?: number; craft?: string; op?: string }; + setRuleParams: (property: string, value: any) => void; errors: { [key: string]: string[] }; } @@ -42,14 +42,14 @@ function isValueInEnum(enumeratin: Record, value: any): boolean { return !!Object.values(enumeratin).find((enumVal) => enumVal === value); } -export function getAlertType(): AlertTypeModel { +export function getAlertType(): RuleTypeModel { return { id: 'example.people-in-space', description: 'Alert when people are in space right now', iconClass: 'globe', documentationUrl: null, - alertParamsExpression: PeopleinSpaceExpression, - validate: (alertParams: PeopleinSpaceParamsProps['alertParams']) => { + ruleParamsExpression: PeopleinSpaceExpression, + validate: (alertParams: PeopleinSpaceParamsProps['ruleParams']) => { const { outerSpaceCapacity, craft, op } = alertParams; const validationResult = { @@ -93,24 +93,24 @@ export function getAlertType(): AlertTypeModel { } export const PeopleinSpaceExpression: React.FunctionComponent = ({ - alertParams, - setAlertParams, + ruleParams, + setRuleParams, errors, }) => { - const { outerSpaceCapacity = 0, craft = Craft.OuterSpace, op = Operator.AreAbove } = alertParams; + const { outerSpaceCapacity = 0, craft = Craft.OuterSpace, op = Operator.AreAbove } = ruleParams; // store defaults useEffect(() => { - if (outerSpaceCapacity !== alertParams.outerSpaceCapacity) { - setAlertParams('outerSpaceCapacity', outerSpaceCapacity); + if (outerSpaceCapacity !== ruleParams.outerSpaceCapacity) { + setRuleParams('outerSpaceCapacity', outerSpaceCapacity); } - if (craft !== alertParams.craft) { - setAlertParams('craft', craft); + if (craft !== ruleParams.craft) { + setRuleParams('craft', craft); } - if (op !== alertParams.op) { - setAlertParams('op', op); + if (op !== ruleParams.op) { + setRuleParams('op', op); } - }, [alertParams, craft, op, outerSpaceCapacity, setAlertParams]); + }, [ruleParams, craft, op, outerSpaceCapacity, setRuleParams]); const [craftTrigger, setCraftTrigger] = useState<{ craft: string; isOpen: boolean }>({ craft, @@ -179,7 +179,7 @@ export const PeopleinSpaceExpression: React.FunctionComponent { - setAlertParams('craft', event.target.value); + setRuleParams('craft', event.target.value); setCraftTrigger({ craft: event.target.value, isOpen: false, @@ -228,7 +228,7 @@ export const PeopleinSpaceExpression: React.FunctionComponent { - setAlertParams('op', event.target.value); + setRuleParams('op', event.target.value); setOuterSpaceCapacity({ ...outerSpaceCapacityTrigger, op: event.target.value, @@ -248,7 +248,7 @@ export const PeopleinSpaceExpression: React.FunctionComponent { - setAlertParams('outerSpaceCapacity', event.target.valueAsNumber); + setRuleParams('outerSpaceCapacity', event.target.valueAsNumber); setOuterSpaceCapacity({ ...outerSpaceCapacityTrigger, outerSpaceCapacity: event.target.valueAsNumber, diff --git a/x-pack/plugins/alerting/README.md b/x-pack/plugins/alerting/README.md index e01228d5cddc6..90c02b6e1c254 100644 --- a/x-pack/plugins/alerting/README.md +++ b/x-pack/plugins/alerting/README.md @@ -197,7 +197,7 @@ This example rule type receives server and threshold as parameters. It will read ```typescript import { schema } from '@kbn/config-schema'; -import { AlertType, AlertExecutorOptions } from '../../../alerting/server'; +import { RuleType, AlertExecutorOptions } from '../../../alerting/server'; // These type names will eventually be updated to reflect the new terminology import { AlertTypeParams, @@ -370,7 +370,7 @@ server.newPlatform.setup.plugins.alerting.registerType(myRuleType); ## Role Based Access-Control -Once you have registered your AlertType, you need to grant your users privileges to use it. +Once you have registered your RuleType, you need to grant your users privileges to use it. When registering a feature in Kibana you can specify multiple types of privileges which are granted to users when they're assigned certain roles. Assuming your feature introduces its own AlertTypes, you'll want to control which roles have all/read privileges for the rules and alerts for these AlertTypes when they're inside the feature. @@ -424,7 +424,7 @@ features.registerKibanaFeature({ 'my-application-id.my-alert-type', // grant `read` over the built-in IndexThreshold '.index-threshold', - // grant `read` over Uptime's TLS AlertType + // grant `read` over Uptime's TLS RuleType 'xpack.uptime.alerts.actionGroups.tls' ], }, @@ -434,7 +434,7 @@ features.registerKibanaFeature({ 'my-application-id.my-alert-type', // grant `read` over the built-in IndexThreshold '.index-threshold', - // grant `read` over Uptime's TLS AlertType + // grant `read` over Uptime's TLS RuleType 'xpack.uptime.alerts.actionGroups.tls' ], }, @@ -643,14 +643,14 @@ When registering a rule type, you'll likely want to provide a way of viewing rul In order for the Alerting Framework to know that your plugin has its own internal view for displaying a rule, you must register a navigation handler within the framework. -A navigation handler is nothing more than a function that receives a rule and its corresponding AlertType, and is expected to then return the path *within your plugin* which knows how to display this rule. +A navigation handler is nothing more than a function that receives a rule and its corresponding RuleType, and is expected to then return the path *within your plugin* which knows how to display this rule. The signature of such a handler is: ```typescript type AlertNavigationHandler = ( alert: SanitizedAlert, - alertType: AlertType + alertType: RuleType ) => string; ``` @@ -668,7 +668,7 @@ alerting.registerNavigation( ); ``` -This tells the Alerting Framework that, given a rule of the AlertType whose ID is `my-application-id.my-unique-rule-type`, if that rule's `consumer` value (which is set when the rule is created by your plugin) is your application (whose id is `my-application-id`), then it will navigate to your application using the path `/my-unique-rule/${the id of the rule}`. +This tells the Alerting Framework that, given a rule of the RuleType whose ID is `my-application-id.my-unique-rule-type`, if that rule's `consumer` value (which is set when the rule is created by your plugin) is your application (whose id is `my-application-id`), then it will navigate to your application using the path `/my-unique-rule/${the id of the rule}`. The navigation is handled using the `navigateToApp` API, meaning that the path will be automatically picked up by your `react-router-dom` **Route** component, so all you have top do is configure a Route that handles the path `/my-unique-rule/:id`. @@ -689,9 +689,9 @@ This tells the Alerting Framework that any rule whose `consumer` value is your a ### Balancing both APIs side by side As we mentioned, using `registerDefaultNavigation` will tell the Alerting Framework that your application can handle any type of rule we throw at it, as long as your application created it, using the handler you provided. -The only case in which this handler will not be used to evaluate the navigation for a rule (assuming your application is the `consumer`) is if you have also used the `registerNavigation` API, alongside your `registerDefaultNavigation` usage, to handle that rule's specific AlertType. +The only case in which this handler will not be used to evaluate the navigation for a rule (assuming your application is the `consumer`) is if you have also used the `registerNavigation` API, alongside your `registerDefaultNavigation` usage, to handle that rule's specific RuleType. -You can use the `registerNavigation` API to specify as many AlertType specific handlers as you like, but you can only use it once per AlertType as we wouldn't know which handler to use if you specified two for the same AlertType. For the same reason, you can only use `registerDefaultNavigation` once per plugin, as it covers all cases for your specific plugin. +You can use the `registerNavigation` API to specify as many RuleType specific handlers as you like, but you can only use it once per RuleType as we wouldn't know which handler to use if you specified two for the same RuleType. For the same reason, you can only use `registerDefaultNavigation` once per plugin, as it covers all cases for your specific plugin. ## Internal HTTP APIs @@ -758,7 +758,7 @@ Action Groups are static, and have to be define when the rule type is defined. Action Subgroups are dynamic, and can be defined on the fly. This approach enables users to specify actions under specific action groups, but they can't specify actions that are specific to subgroups. -As subgroups fall under action groups, we will schedule the actions specified for the action group, but the subgroup allows the AlertType implementer to reuse the same action group for multiple different active subgroups. +As subgroups fall under action groups, we will schedule the actions specified for the action group, but the subgroup allows the RuleType implementer to reuse the same action group for multiple different active subgroups. ## Templating Actions diff --git a/x-pack/plugins/apm/public/components/alerting/error_count_alert_trigger/error_count_alert_trigger.stories.tsx b/x-pack/plugins/apm/public/components/alerting/error_count_alert_trigger/error_count_alert_trigger.stories.tsx index 38ef94f9c1526..f1279674ed87f 100644 --- a/x-pack/plugins/apm/public/components/alerting/error_count_alert_trigger/error_count_alert_trigger.stories.tsx +++ b/x-pack/plugins/apm/public/components/alerting/error_count_alert_trigger/error_count_alert_trigger.stories.tsx @@ -7,7 +7,7 @@ import { Meta, Story } from '@storybook/react'; import React, { useState } from 'react'; -import { AlertParams, ErrorCountAlertTrigger } from '.'; +import { RuleParams, ErrorCountAlertTrigger } from '.'; import { CoreStart } from '../../../../../../../src/core/public'; import { createKibanaReactContext } from '../../../../../../../src/plugins/kibana_react/public'; import { ENVIRONMENT_ALL } from '../../../../common/environment_filter_values'; @@ -24,7 +24,7 @@ const coreMock = { const KibanaReactContext = createKibanaReactContext(coreMock); interface Args { - alertParams: AlertParams; + ruleParams: RuleParams; metadata?: AlertMetadata; } @@ -48,26 +48,26 @@ const stories: Meta<{}> = { export default stories; export const CreatingInApmFromInventory: Story = ({ - alertParams, + ruleParams, metadata, }) => { - const [params, setParams] = useState(alertParams); + const [params, setParams] = useState(ruleParams); - function setAlertParams(property: string, value: any) { + function setRuleParams(property: string, value: any) { setParams({ ...params, [property]: value }); } return ( {}} + setRuleParams={setRuleParams} + setRuleProperty={() => {}} /> ); }; CreatingInApmFromInventory.args = { - alertParams: {}, + ruleParams: {}, metadata: { end: '2021-09-10T14:14:04.789Z', environment: ENVIRONMENT_ALL.value, @@ -77,26 +77,26 @@ CreatingInApmFromInventory.args = { }; export const CreatingInApmFromService: Story = ({ - alertParams, + ruleParams, metadata, }) => { - const [params, setParams] = useState(alertParams); + const [params, setParams] = useState(ruleParams); - function setAlertParams(property: string, value: any) { + function setRuleParams(property: string, value: any) { setParams({ ...params, [property]: value }); } return ( {}} + setRuleParams={setRuleParams} + setRuleProperty={() => {}} /> ); }; CreatingInApmFromService.args = { - alertParams: {}, + ruleParams: {}, metadata: { end: '2021-09-10T14:14:04.789Z', environment: 'testEnvironment', @@ -106,26 +106,26 @@ CreatingInApmFromService.args = { }; export const EditingInStackManagement: Story = ({ - alertParams, + ruleParams, metadata, }) => { - const [params, setParams] = useState(alertParams); + const [params, setParams] = useState(ruleParams); - function setAlertParams(property: string, value: any) { + function setRuleParams(property: string, value: any) { setParams({ ...params, [property]: value }); } return ( {}} + setRuleParams={setRuleParams} + setRuleProperty={() => {}} /> ); }; EditingInStackManagement.args = { - alertParams: { + ruleParams: { environment: 'testEnvironment', serviceName: 'testServiceName', threshold: 25, @@ -136,25 +136,25 @@ EditingInStackManagement.args = { }; export const CreatingInStackManagement: Story = ({ - alertParams, + ruleParams, metadata, }) => { - const [params, setParams] = useState(alertParams); + const [params, setParams] = useState(ruleParams); - function setAlertParams(property: string, value: any) { + function setRuleParams(property: string, value: any) { setParams({ ...params, [property]: value }); } return ( {}} + setRuleParams={setRuleParams} + setRuleProperty={() => {}} /> ); }; CreatingInStackManagement.args = { - alertParams: {}, + ruleParams: {}, metadata: undefined, }; diff --git a/x-pack/plugins/apm/public/components/alerting/error_count_alert_trigger/index.tsx b/x-pack/plugins/apm/public/components/alerting/error_count_alert_trigger/index.tsx index 1c72d5f104854..91e1f552a5a55 100644 --- a/x-pack/plugins/apm/public/components/alerting/error_count_alert_trigger/index.tsx +++ b/x-pack/plugins/apm/public/components/alerting/error_count_alert_trigger/index.tsx @@ -20,7 +20,7 @@ import { EnvironmentField, IsAboveField, ServiceField } from '../fields'; import { AlertMetadata, getIntervalAndTimeRange, TimeUnit } from '../helper'; import { ServiceAlertTrigger } from '../service_alert_trigger'; -export interface AlertParams { +export interface RuleParams { windowSize?: number; windowUnit?: TimeUnit; threshold?: number; @@ -29,22 +29,22 @@ export interface AlertParams { } interface Props { - alertParams: AlertParams; + ruleParams: RuleParams; metadata?: AlertMetadata; - setAlertParams: (key: string, value: any) => void; - setAlertProperty: (key: string, value: any) => void; + setRuleParams: (key: string, value: any) => void; + setRuleProperty: (key: string, value: any) => void; } export function ErrorCountAlertTrigger(props: Props) { const { services } = useKibana(); - const { alertParams, metadata, setAlertParams, setAlertProperty } = props; + const { ruleParams, metadata, setRuleParams, setRuleProperty } = props; useEffect(() => { createCallApmApi(services as CoreStart); }, [services]); const params = defaults( - { ...omit(metadata, ['start', 'end']), ...alertParams }, + { ...omit(metadata, ['start', 'end']), ...ruleParams }, { threshold: 25, windowSize: 1, @@ -86,25 +86,25 @@ export function ErrorCountAlertTrigger(props: Props) { const fields = [ setAlertParams('serviceName', value)} + onChange={(value) => setRuleParams('serviceName', value)} />, setAlertParams('environment', value)} + onChange={(value) => setRuleParams('environment', value)} />, setAlertParams('threshold', value || 0)} + onChange={(value) => setRuleParams('threshold', value || 0)} />, - setAlertParams('windowSize', timeWindowSize || '') + setRuleParams('windowSize', timeWindowSize || '') } onChangeWindowUnit={(timeWindowUnit) => - setAlertParams('windowUnit', timeWindowUnit) + setRuleParams('windowUnit', timeWindowUnit) } timeWindowSize={params.windowSize} timeWindowUnit={params.windowUnit} @@ -128,8 +128,8 @@ export function ErrorCountAlertTrigger(props: Props) { ); diff --git a/x-pack/plugins/apm/public/components/alerting/register_apm_alerts.ts b/x-pack/plugins/apm/public/components/alerting/register_apm_alerts.ts index 20af3db34e508..6fb5546f37d87 100644 --- a/x-pack/plugins/apm/public/components/alerting/register_apm_alerts.ts +++ b/x-pack/plugins/apm/public/components/alerting/register_apm_alerts.ts @@ -70,7 +70,7 @@ export function registerApmAlerts( documentationUrl(docLinks) { return `${docLinks.links.alerting.apmRules}`; }, - alertParamsExpression: lazy(() => import('./error_count_alert_trigger')), + ruleParamsExpression: lazy(() => import('./error_count_alert_trigger')), validate: () => ({ errors: [], }), @@ -118,7 +118,7 @@ export function registerApmAlerts( documentationUrl(docLinks) { return `${docLinks.links.alerting.apmRules}`; }, - alertParamsExpression: lazy( + ruleParamsExpression: lazy( () => import('./transaction_duration_alert_trigger') ), validate: () => ({ @@ -169,7 +169,7 @@ export function registerApmAlerts( documentationUrl(docLinks) { return `${docLinks.links.alerting.apmRules}`; }, - alertParamsExpression: lazy( + ruleParamsExpression: lazy( () => import('./transaction_error_rate_alert_trigger') ), validate: () => ({ @@ -218,7 +218,7 @@ export function registerApmAlerts( documentationUrl(docLinks) { return `${docLinks.links.alerting.apmRules}`; }, - alertParamsExpression: lazy( + ruleParamsExpression: lazy( () => import('./transaction_duration_anomaly_alert_trigger') ), validate: () => ({ diff --git a/x-pack/plugins/apm/public/components/alerting/service_alert_trigger/index.tsx b/x-pack/plugins/apm/public/components/alerting/service_alert_trigger/index.tsx index 06fe569ac7eed..48f97fc0f3fed 100644 --- a/x-pack/plugins/apm/public/components/alerting/service_alert_trigger/index.tsx +++ b/x-pack/plugins/apm/public/components/alerting/service_alert_trigger/index.tsx @@ -9,15 +9,15 @@ import { EuiFlexGrid, EuiFlexItem, EuiSpacer } from '@elastic/eui'; import React, { useEffect } from 'react'; interface Props { - setAlertParams: (key: string, value: any) => void; - setAlertProperty: (key: string, value: any) => void; + setRuleParams: (key: string, value: any) => void; + setRuleProperty: (key: string, value: any) => void; defaults: Record; fields: React.ReactNode[]; chartPreview?: React.ReactNode; } export function ServiceAlertTrigger(props: Props) { - const { fields, setAlertParams, defaults, chartPreview } = props; + const { fields, setRuleParams, defaults, chartPreview } = props; const params: Record = { ...defaults, @@ -26,7 +26,7 @@ export function ServiceAlertTrigger(props: Props) { useEffect(() => { // we only want to run this on mount to set default values Object.keys(params).forEach((key) => { - setAlertParams(key, params[key]); + setRuleParams(key, params[key]); }); // eslint-disable-next-line react-hooks/exhaustive-deps diff --git a/x-pack/plugins/apm/public/components/alerting/service_alert_trigger/service_alert_trigger.test.tsx b/x-pack/plugins/apm/public/components/alerting/service_alert_trigger/service_alert_trigger.test.tsx index 2775fa65f8196..7ca4024e58286 100644 --- a/x-pack/plugins/apm/public/components/alerting/service_alert_trigger/service_alert_trigger.test.tsx +++ b/x-pack/plugins/apm/public/components/alerting/service_alert_trigger/service_alert_trigger.test.tsx @@ -21,8 +21,8 @@ describe('ServiceAlertTrigger', () => { {}} - setAlertProperty={() => {}} + setRuleParams={() => {}} + setRuleProperty={() => {}} />, { wrapper: Wrapper, diff --git a/x-pack/plugins/apm/public/components/alerting/transaction_duration_alert_trigger/index.stories.tsx b/x-pack/plugins/apm/public/components/alerting/transaction_duration_alert_trigger/index.stories.tsx index 3570630d35eef..de9f018fa3d97 100644 --- a/x-pack/plugins/apm/public/components/alerting/transaction_duration_alert_trigger/index.stories.tsx +++ b/x-pack/plugins/apm/public/components/alerting/transaction_duration_alert_trigger/index.stories.tsx @@ -7,7 +7,7 @@ import { Story } from '@storybook/react'; import React, { ComponentType, useState } from 'react'; -import { AlertParams, TransactionDurationAlertTrigger } from '.'; +import { RuleParams, TransactionDurationAlertTrigger } from '.'; import { CoreStart } from '../../../../../../../src/core/public'; import { createKibanaReactContext } from '../../../../../../../src/plugins/kibana_react/public'; @@ -32,7 +32,7 @@ export default { }; export const Example: Story = () => { - const [params, setParams] = useState({ + const [params, setParams] = useState({ aggregationType: 'avg' as const, environment: 'testEnvironment', serviceName: 'testServiceName', @@ -42,15 +42,15 @@ export const Example: Story = () => { windowUnit: 'm', }); - function setAlertParams(property: string, value: any) { + function setRuleParams(property: string, value: any) { setParams({ ...params, [property]: value }); } return ( {}} + ruleParams={params} + setRuleParams={setRuleParams} + setRuleProperty={() => {}} /> ); }; diff --git a/x-pack/plugins/apm/public/components/alerting/transaction_duration_alert_trigger/index.tsx b/x-pack/plugins/apm/public/components/alerting/transaction_duration_alert_trigger/index.tsx index fa75fcca579e5..d15f6e6aee8c5 100644 --- a/x-pack/plugins/apm/public/components/alerting/transaction_duration_alert_trigger/index.tsx +++ b/x-pack/plugins/apm/public/components/alerting/transaction_duration_alert_trigger/index.tsx @@ -31,7 +31,7 @@ import { AlertMetadata, getIntervalAndTimeRange, TimeUnit } from '../helper'; import { ServiceAlertTrigger } from '../service_alert_trigger'; import { PopoverExpression } from '../service_alert_trigger/popover_expression'; -export interface AlertParams { +export interface RuleParams { aggregationType: 'avg' | '95th' | '99th'; environment: string; serviceName: string; @@ -63,15 +63,15 @@ const TRANSACTION_ALERT_AGGREGATION_TYPES = { }; interface Props { - alertParams: AlertParams; + ruleParams: RuleParams; metadata?: AlertMetadata; - setAlertParams: (key: string, value: any) => void; - setAlertProperty: (key: string, value: any) => void; + setRuleParams: (key: string, value: any) => void; + setRuleProperty: (key: string, value: any) => void; } export function TransactionDurationAlertTrigger(props: Props) { const { services } = useKibana(); - const { alertParams, metadata, setAlertParams, setAlertProperty } = props; + const { ruleParams, metadata, setRuleParams, setRuleProperty } = props; useEffect(() => { createCallApmApi(services as CoreStart); @@ -80,7 +80,7 @@ export function TransactionDurationAlertTrigger(props: Props) { const params = defaults( { ...omit(metadata, ['start', 'end']), - ...alertParams, + ...ruleParams, }, { aggregationType: 'avg', @@ -147,15 +147,15 @@ export function TransactionDurationAlertTrigger(props: Props) { setAlertParams('serviceName', value)} + onChange={(value) => setRuleParams('serviceName', value)} />, setAlertParams('transactionType', value)} + onChange={(value) => setRuleParams('transactionType', value)} />, setAlertParams('environment', value)} + onChange={(value) => setRuleParams('environment', value)} />, setAlertParams('aggregationType', e.target.value)} + onChange={(e) => setRuleParams('aggregationType', e.target.value)} compressed /> , @@ -180,14 +180,14 @@ export function TransactionDurationAlertTrigger(props: Props) { unit={i18n.translate('xpack.apm.transactionDurationAlertTrigger.ms', { defaultMessage: 'ms', })} - onChange={(value) => setAlertParams('threshold', value || 0)} + onChange={(value) => setRuleParams('threshold', value || 0)} />, - setAlertParams('windowSize', timeWindowSize || '') + setRuleParams('windowSize', timeWindowSize || '') } onChangeWindowUnit={(timeWindowUnit) => - setAlertParams('windowUnit', timeWindowUnit) + setRuleParams('windowUnit', timeWindowUnit) } timeWindowSize={params.windowSize} timeWindowUnit={params.windowUnit} @@ -203,8 +203,8 @@ export function TransactionDurationAlertTrigger(props: Props) { chartPreview={chartPreview} defaults={params} fields={fields} - setAlertParams={setAlertParams} - setAlertProperty={setAlertProperty} + setRuleParams={setRuleParams} + setRuleProperty={setRuleProperty} /> ); } diff --git a/x-pack/plugins/apm/public/components/alerting/transaction_duration_anomaly_alert_trigger/index.tsx b/x-pack/plugins/apm/public/components/alerting/transaction_duration_anomaly_alert_trigger/index.tsx index 0aa00a05df2c3..b47b0b9f323de 100644 --- a/x-pack/plugins/apm/public/components/alerting/transaction_duration_anomaly_alert_trigger/index.tsx +++ b/x-pack/plugins/apm/public/components/alerting/transaction_duration_anomaly_alert_trigger/index.tsx @@ -40,15 +40,15 @@ interface AlertParams { } interface Props { - alertParams: AlertParams; + ruleParams: AlertParams; metadata?: AlertMetadata; - setAlertParams: (key: string, value: any) => void; - setAlertProperty: (key: string, value: any) => void; + setRuleParams: (key: string, value: any) => void; + setRuleProperty: (key: string, value: any) => void; } export function TransactionDurationAnomalyAlertTrigger(props: Props) { const { services } = useKibana(); - const { alertParams, metadata, setAlertParams, setAlertProperty } = props; + const { ruleParams, metadata, setRuleParams, setRuleProperty } = props; useEffect(() => { createCallApmApi(services as CoreStart); @@ -57,7 +57,7 @@ export function TransactionDurationAnomalyAlertTrigger(props: Props) { const params = defaults( { ...omit(metadata, ['start', 'end']), - ...alertParams, + ...ruleParams, }, { windowSize: 15, @@ -70,15 +70,15 @@ export function TransactionDurationAnomalyAlertTrigger(props: Props) { const fields = [ setAlertParams('serviceName', value)} + onChange={(value) => setRuleParams('serviceName', value)} />, setAlertParams('transactionType', value)} + onChange={(value) => setRuleParams('transactionType', value)} />, setAlertParams('environment', value)} + onChange={(value) => setRuleParams('environment', value)} />, } @@ -92,7 +92,7 @@ export function TransactionDurationAnomalyAlertTrigger(props: Props) { { - setAlertParams('anomalySeverityType', value); + setRuleParams('anomalySeverityType', value); }} /> , @@ -102,8 +102,8 @@ export function TransactionDurationAnomalyAlertTrigger(props: Props) { ); } diff --git a/x-pack/plugins/apm/public/components/alerting/transaction_error_rate_alert_trigger/index.tsx b/x-pack/plugins/apm/public/components/alerting/transaction_error_rate_alert_trigger/index.tsx index a818218cbf3ad..3a91117a82970 100644 --- a/x-pack/plugins/apm/public/components/alerting/transaction_error_rate_alert_trigger/index.tsx +++ b/x-pack/plugins/apm/public/components/alerting/transaction_error_rate_alert_trigger/index.tsx @@ -24,7 +24,7 @@ import { import { AlertMetadata, getIntervalAndTimeRange, TimeUnit } from '../helper'; import { ServiceAlertTrigger } from '../service_alert_trigger'; -interface AlertParams { +interface RuleParams { windowSize?: number; windowUnit?: string; threshold?: number; @@ -34,22 +34,22 @@ interface AlertParams { } interface Props { - alertParams: AlertParams; + ruleParams: RuleParams; metadata?: AlertMetadata; - setAlertParams: (key: string, value: any) => void; - setAlertProperty: (key: string, value: any) => void; + setRuleParams: (key: string, value: any) => void; + setRuleProperty: (key: string, value: any) => void; } export function TransactionErrorRateAlertTrigger(props: Props) { const { services } = useKibana(); - const { alertParams, metadata, setAlertParams, setAlertProperty } = props; + const { ruleParams, metadata, setRuleParams, setRuleProperty } = props; useEffect(() => { createCallApmApi(services as CoreStart); }, [services]); const params = defaults( - { ...omit(metadata, ['start', 'end']), ...alertParams }, + { ...omit(metadata, ['start', 'end']), ...ruleParams }, { threshold: 30, windowSize: 5, @@ -95,27 +95,27 @@ export function TransactionErrorRateAlertTrigger(props: Props) { const fields = [ setAlertParams('serviceName', value)} + onChange={(value) => setRuleParams('serviceName', value)} />, setAlertParams('transactionType', value)} + onChange={(value) => setRuleParams('transactionType', value)} />, setAlertParams('environment', value)} + onChange={(value) => setRuleParams('environment', value)} />, setAlertParams('threshold', value || 0)} + onChange={(value) => setRuleParams('threshold', value || 0)} />, - setAlertParams('windowSize', timeWindowSize || '') + setRuleParams('windowSize', timeWindowSize || '') } onChangeWindowUnit={(timeWindowUnit) => - setAlertParams('windowUnit', timeWindowUnit) + setRuleParams('windowUnit', timeWindowUnit) } timeWindowSize={params.windowSize} timeWindowUnit={params.windowUnit} @@ -139,8 +139,8 @@ export function TransactionErrorRateAlertTrigger(props: Props) { ); diff --git a/x-pack/plugins/apm/server/routes/alerts/register_error_count_alert_type.ts b/x-pack/plugins/apm/server/routes/alerts/register_error_count_alert_type.ts index 2dacb5ca1e895..968493dd64991 100644 --- a/x-pack/plugins/apm/server/routes/alerts/register_error_count_alert_type.ts +++ b/x-pack/plugins/apm/server/routes/alerts/register_error_count_alert_type.ts @@ -81,7 +81,7 @@ export function registerErrorCountAlertType({ isExportable: true, executor: async ({ services, params }) => { const config = await config$.pipe(take(1)).toPromise(); - const alertParams = params; + const ruleParams = params; const indices = await getApmIndices({ config, savedObjectsClient: services.savedObjectsClient, @@ -97,13 +97,13 @@ export function registerErrorCountAlertType({ { range: { '@timestamp': { - gte: `now-${alertParams.windowSize}${alertParams.windowUnit}`, + gte: `now-${ruleParams.windowSize}${ruleParams.windowUnit}`, }, }, }, { term: { [PROCESSOR_EVENT]: ProcessorEvent.error } }, - ...termQuery(SERVICE_NAME, alertParams.serviceName), - ...environmentQuery(alertParams.environment), + ...termQuery(SERVICE_NAME, ruleParams.serviceName), + ...environmentQuery(ruleParams.environment), ], }, }, @@ -136,7 +136,7 @@ export function registerErrorCountAlertType({ }) ?? []; errorCountResults - .filter((result) => result.errorCount >= alertParams.threshold) + .filter((result) => result.errorCount >= ruleParams.threshold) .forEach((result) => { const { serviceName, environment, errorCount } = result; @@ -150,10 +150,10 @@ export function registerErrorCountAlertType({ ...getEnvironmentEsField(environment), [PROCESSOR_EVENT]: ProcessorEvent.error, [ALERT_EVALUATION_VALUE]: errorCount, - [ALERT_EVALUATION_THRESHOLD]: alertParams.threshold, + [ALERT_EVALUATION_THRESHOLD]: ruleParams.threshold, [ALERT_REASON]: formatErrorCountReason({ serviceName, - threshold: alertParams.threshold, + threshold: ruleParams.threshold, measured: errorCount, }), }, @@ -161,9 +161,9 @@ export function registerErrorCountAlertType({ .scheduleActions(alertTypeConfig.defaultActionGroupId, { serviceName, environment: getEnvironmentLabel(environment), - threshold: alertParams.threshold, + threshold: ruleParams.threshold, triggerValue: errorCount, - interval: `${alertParams.windowSize}${alertParams.windowUnit}`, + interval: `${ruleParams.windowSize}${ruleParams.windowUnit}`, }); }); diff --git a/x-pack/plugins/apm/server/routes/alerts/register_transaction_duration_alert_type.ts b/x-pack/plugins/apm/server/routes/alerts/register_transaction_duration_alert_type.ts index 7ad78cdff9545..cddf9b7709e1e 100644 --- a/x-pack/plugins/apm/server/routes/alerts/register_transaction_duration_alert_type.ts +++ b/x-pack/plugins/apm/server/routes/alerts/register_transaction_duration_alert_type.ts @@ -93,7 +93,7 @@ export function registerTransactionDurationAlertType({ isExportable: true, executor: async ({ services, params }) => { const config = await config$.pipe(take(1)).toPromise(); - const alertParams = params; + const ruleParams = params; const indices = await getApmIndices({ config, savedObjectsClient: services.savedObjectsClient, @@ -124,32 +124,32 @@ export function registerTransactionDurationAlertType({ { range: { '@timestamp': { - gte: `now-${alertParams.windowSize}${alertParams.windowUnit}`, + gte: `now-${ruleParams.windowSize}${ruleParams.windowUnit}`, }, }, }, ...getDocumentTypeFilterForTransactions( searchAggregatedTransactions ), - { term: { [SERVICE_NAME]: alertParams.serviceName } }, + { term: { [SERVICE_NAME]: ruleParams.serviceName } }, { term: { - [TRANSACTION_TYPE]: alertParams.transactionType, + [TRANSACTION_TYPE]: ruleParams.transactionType, }, }, - ...environmentQuery(alertParams.environment), + ...environmentQuery(ruleParams.environment), ] as QueryDslQueryContainer[], }, }, aggs: { latency: - alertParams.aggregationType === 'avg' + ruleParams.aggregationType === 'avg' ? { avg: { field } } : { percentiles: { field, percents: [ - alertParams.aggregationType === '95th' ? 95 : 99, + ruleParams.aggregationType === '95th' ? 95 : 99, ], }, }, @@ -172,7 +172,7 @@ export function registerTransactionDurationAlertType({ 'values' in latency ? Object.values(latency.values)[0] : latency?.value; // Converts threshold to microseconds because this is the unit used on transactionDuration - const thresholdMicroseconds = alertParams.threshold * 1000; + const thresholdMicroseconds = ruleParams.threshold * 1000; if (transactionDuration && transactionDuration > thresholdMicroseconds) { const durationFormatter = getDurationFormatter(transactionDuration); @@ -182,30 +182,30 @@ export function registerTransactionDurationAlertType({ services .alertWithLifecycle({ id: `${AlertType.TransactionDuration}_${getEnvironmentLabel( - alertParams.environment + ruleParams.environment )}`, fields: { - [SERVICE_NAME]: alertParams.serviceName, - ...getEnvironmentEsField(alertParams.environment), - [TRANSACTION_TYPE]: alertParams.transactionType, + [SERVICE_NAME]: ruleParams.serviceName, + ...getEnvironmentEsField(ruleParams.environment), + [TRANSACTION_TYPE]: ruleParams.transactionType, [PROCESSOR_EVENT]: ProcessorEvent.transaction, [ALERT_EVALUATION_VALUE]: transactionDuration, [ALERT_EVALUATION_THRESHOLD]: thresholdMicroseconds, [ALERT_REASON]: formatTransactionDurationReason({ measured: transactionDuration, - serviceName: alertParams.serviceName, + serviceName: ruleParams.serviceName, threshold: thresholdMicroseconds, asDuration, }), }, }) .scheduleActions(alertTypeConfig.defaultActionGroupId, { - transactionType: alertParams.transactionType, - serviceName: alertParams.serviceName, - environment: getEnvironmentLabel(alertParams.environment), + transactionType: ruleParams.transactionType, + serviceName: ruleParams.serviceName, + environment: getEnvironmentLabel(ruleParams.environment), threshold: thresholdMicroseconds, triggerValue: transactionDurationFormatted, - interval: `${alertParams.windowSize}${alertParams.windowUnit}`, + interval: `${ruleParams.windowSize}${ruleParams.windowUnit}`, }); } diff --git a/x-pack/plugins/apm/server/routes/alerts/register_transaction_duration_anomaly_alert_type.ts b/x-pack/plugins/apm/server/routes/alerts/register_transaction_duration_anomaly_alert_type.ts index 5216d485bc31e..9296ca19daee0 100644 --- a/x-pack/plugins/apm/server/routes/alerts/register_transaction_duration_anomaly_alert_type.ts +++ b/x-pack/plugins/apm/server/routes/alerts/register_transaction_duration_anomaly_alert_type.ts @@ -94,7 +94,7 @@ export function registerTransactionDurationAnomalyAlertType({ if (!ml) { return {}; } - const alertParams = params; + const ruleParams = params; const request = {} as KibanaRequest; const { mlAnomalySearch } = ml.mlSystemProvider( request, @@ -107,16 +107,16 @@ export function registerTransactionDurationAnomalyAlertType({ const mlJobs = await getMLJobs( anomalyDetectors, - alertParams.environment + ruleParams.environment ); const selectedOption = ANOMALY_ALERT_SEVERITY_TYPES.find( - (option) => option.type === alertParams.anomalySeverityType + (option) => option.type === ruleParams.anomalySeverityType ); if (!selectedOption) { throw new Error( - `Anomaly alert severity type ${alertParams.anomalySeverityType} is not supported.` + `Anomaly alert severity type ${ruleParams.anomalySeverityType} is not supported.` ); } @@ -139,16 +139,13 @@ export function registerTransactionDurationAnomalyAlertType({ { range: { timestamp: { - gte: `now-${alertParams.windowSize}${alertParams.windowUnit}`, + gte: `now-${ruleParams.windowSize}${ruleParams.windowUnit}`, format: 'epoch_millis', }, }, }, - ...termQuery( - 'partition_field_value', - alertParams.serviceName - ), - ...termQuery('by_field_value', alertParams.transactionType), + ...termQuery('partition_field_value', ruleParams.serviceName), + ...termQuery('by_field_value', ruleParams.transactionType), ] as QueryDslQueryContainer[], }, }, diff --git a/x-pack/plugins/apm/server/routes/alerts/register_transaction_error_rate_alert_type.ts b/x-pack/plugins/apm/server/routes/alerts/register_transaction_error_rate_alert_type.ts index 773b1ce694125..39f07cd9248e1 100644 --- a/x-pack/plugins/apm/server/routes/alerts/register_transaction_error_rate_alert_type.ts +++ b/x-pack/plugins/apm/server/routes/alerts/register_transaction_error_rate_alert_type.ts @@ -88,7 +88,7 @@ export function registerTransactionErrorRateAlertType({ producer: APM_SERVER_FEATURE_ID, minimumLicenseRequired: 'basic', isExportable: true, - executor: async ({ services, params: alertParams }) => { + executor: async ({ services, params: ruleParams }) => { const config = await config$.pipe(take(1)).toPromise(); const indices = await getApmIndices({ config, @@ -116,7 +116,7 @@ export function registerTransactionErrorRateAlertType({ { range: { '@timestamp': { - gte: `now-${alertParams.windowSize}${alertParams.windowUnit}`, + gte: `now-${ruleParams.windowSize}${ruleParams.windowUnit}`, }, }, }, @@ -131,9 +131,9 @@ export function registerTransactionErrorRateAlertType({ ], }, }, - ...termQuery(SERVICE_NAME, alertParams.serviceName), - ...termQuery(TRANSACTION_TYPE, alertParams.transactionType), - ...environmentQuery(alertParams.environment), + ...termQuery(SERVICE_NAME, ruleParams.serviceName), + ...termQuery(TRANSACTION_TYPE, ruleParams.transactionType), + ...environmentQuery(ruleParams.environment), ], }, }, @@ -185,7 +185,7 @@ export function registerTransactionErrorRateAlertType({ )?.doc_count ?? 0; const errorRate = (failed / (failed + succesful)) * 100; - if (errorRate >= alertParams.threshold) { + if (errorRate >= ruleParams.threshold) { results.push({ serviceName, environment, @@ -215,9 +215,9 @@ export function registerTransactionErrorRateAlertType({ [TRANSACTION_TYPE]: transactionType, [PROCESSOR_EVENT]: ProcessorEvent.transaction, [ALERT_EVALUATION_VALUE]: errorRate, - [ALERT_EVALUATION_THRESHOLD]: alertParams.threshold, + [ALERT_EVALUATION_THRESHOLD]: ruleParams.threshold, [ALERT_REASON]: formatTransactionErrorRateReason({ - threshold: alertParams.threshold, + threshold: ruleParams.threshold, measured: errorRate, asPercent, serviceName, @@ -228,9 +228,9 @@ export function registerTransactionErrorRateAlertType({ serviceName, transactionType, environment: getEnvironmentLabel(environment), - threshold: alertParams.threshold, + threshold: ruleParams.threshold, triggerValue: asDecimalOrInteger(errorRate), - interval: `${alertParams.windowSize}${alertParams.windowUnit}`, + interval: `${ruleParams.windowSize}${ruleParams.windowUnit}`, }); }); diff --git a/x-pack/plugins/infra/public/alerting/inventory/components/expression.test.tsx b/x-pack/plugins/infra/public/alerting/inventory/components/expression.test.tsx index 0cef1077873df..ccb7a0e34d736 100644 --- a/x-pack/plugins/infra/public/alerting/inventory/components/expression.test.tsx +++ b/x-pack/plugins/infra/public/alerting/inventory/components/expression.test.tsx @@ -39,20 +39,20 @@ const exampleCustomMetric = { describe('Expression', () => { async function setup(currentOptions: AlertContextMeta) { - const alertParams = { + const ruleParams = { criteria: [], nodeType: undefined, filterQueryText: '', }; const wrapper = mountWithIntl( Reflect.set(alertParams, key, value)} - setAlertProperty={() => {}} + setRuleParams={(key, value) => Reflect.set(ruleParams, key, value)} + setRuleProperty={() => {}} metadata={currentOptions} /> ); @@ -65,7 +65,7 @@ describe('Expression', () => { await update(); - return { wrapper, update, alertParams }; + return { wrapper, update, ruleParams }; } it('should prefill the alert using the context metadata', async () => { @@ -75,10 +75,10 @@ describe('Expression', () => { customMetrics: [], options: { metric: { type: 'memory' } }, }; - const { alertParams } = await setup(currentOptions as AlertContextMeta); - expect(alertParams.nodeType).toBe('pod'); - expect(alertParams.filterQueryText).toBe('foo'); - expect(alertParams.criteria).toEqual([ + const { ruleParams } = await setup(currentOptions as AlertContextMeta); + expect(ruleParams.nodeType).toBe('pod'); + expect(ruleParams.filterQueryText).toBe('foo'); + expect(ruleParams.criteria).toEqual([ { metric: 'memory', comparator: Comparator.GT, @@ -94,7 +94,7 @@ describe('Expression', () => { const FILTER_QUERY = '{"bool":{"should":[{"match_phrase":{"host.name":"testHostName"}}],"minimum_should_match":1}}'; - const alertParams = { + const ruleParams = { criteria: [ { metric: 'cpu', @@ -111,13 +111,13 @@ describe('Expression', () => { const wrapper = shallowWithIntl( Reflect.set(alertParams, key, value)} - setAlertProperty={() => {}} + setRuleParams={(key, value) => Reflect.set(ruleParams, key, value)} + setRuleProperty={() => {}} metadata={{}} /> ); @@ -135,11 +135,11 @@ describe('Expression', () => { customMetrics: [exampleCustomMetric], options: { metric: exampleCustomMetric }, }; - const { alertParams, update } = await setup(currentOptions as AlertContextMeta); + const { ruleParams, update } = await setup(currentOptions as AlertContextMeta); await update(); - expect(alertParams.nodeType).toBe('tx'); - expect(alertParams.filterQueryText).toBe(''); - expect(alertParams.criteria).toEqual([ + expect(ruleParams.nodeType).toBe('tx'); + expect(ruleParams.filterQueryText).toBe(''); + expect(ruleParams.criteria).toEqual([ { metric: 'custom', comparator: Comparator.GT, @@ -163,7 +163,7 @@ describe('ExpressionRow', () => { addExpression={() => {}} key={1} expressionId={1} - setAlertParams={() => {}} + setRuleParams={() => {}} errors={{ aggField: [], timeSizeUnit: [], diff --git a/x-pack/plugins/infra/public/alerting/inventory/components/expression.tsx b/x-pack/plugins/infra/public/alerting/inventory/components/expression.tsx index 206429f8c26a6..5734347b8909d 100644 --- a/x-pack/plugins/infra/public/alerting/inventory/components/expression.tsx +++ b/x-pack/plugins/infra/public/alerting/inventory/components/expression.tsx @@ -37,7 +37,7 @@ import { } from '../../../../../triggers_actions_ui/public/common'; import { IErrorObject, - AlertTypeParamsExpressionProps, + RuleTypeParamsExpressionProps, } from '../../../../../triggers_actions_ui/public'; import { MetricsExplorerKueryBar } from '../../../pages/metrics/metrics_explorer/components/kuery_bar'; import { useSourceViaHttp } from '../../../containers/metrics_source/use_source_via_http'; @@ -81,7 +81,7 @@ export interface AlertContextMeta { type Criteria = InventoryMetricConditions[]; type Props = Omit< - AlertTypeParamsExpressionProps< + RuleTypeParamsExpressionProps< { criteria: Criteria; nodeType: InventoryItemType; @@ -111,7 +111,7 @@ export const defaultExpression = { export const Expressions: React.FC = (props) => { const { http, notifications } = useKibanaContextForPlugin().services; - const { setAlertParams, alertParams, errors, metadata } = props; + const { setRuleParams, ruleParams, errors, metadata } = props; const { source, createDerivedIndexPattern } = useSourceViaHttp({ sourceId: 'default', fetch: http.fetch, @@ -127,47 +127,47 @@ export const Expressions: React.FC = (props) => { const updateParams = useCallback( (id, e: InventoryMetricConditions) => { - const exp = alertParams.criteria ? alertParams.criteria.slice() : []; + const exp = ruleParams.criteria ? ruleParams.criteria.slice() : []; exp[id] = e; - setAlertParams('criteria', exp); + setRuleParams('criteria', exp); }, - [setAlertParams, alertParams.criteria] + [setRuleParams, ruleParams.criteria] ); const addExpression = useCallback(() => { - const exp = alertParams.criteria?.slice() || []; + const exp = ruleParams.criteria?.slice() || []; exp.push({ ...defaultExpression, timeSize: timeSize ?? defaultExpression.timeSize, timeUnit: timeUnit ?? defaultExpression.timeUnit, }); - setAlertParams('criteria', exp); - }, [setAlertParams, alertParams.criteria, timeSize, timeUnit]); + setRuleParams('criteria', exp); + }, [setRuleParams, ruleParams.criteria, timeSize, timeUnit]); const removeExpression = useCallback( (id: number) => { - const exp = alertParams.criteria.slice(); + const exp = ruleParams.criteria.slice(); if (exp.length > 1) { exp.splice(id, 1); - setAlertParams('criteria', exp); + setRuleParams('criteria', exp); } }, - [setAlertParams, alertParams.criteria] + [setRuleParams, ruleParams.criteria] ); const onFilterChange = useCallback( (filter: any) => { - setAlertParams('filterQueryText', filter || ''); + setRuleParams('filterQueryText', filter || ''); try { - setAlertParams( + setRuleParams( 'filterQuery', convertKueryToElasticSearchQuery(filter, derivedIndexPattern, false) || '' ); } catch (e) { - setAlertParams('filterQuery', QUERY_INVALID); + setRuleParams('filterQuery', QUERY_INVALID); } }, - [derivedIndexPattern, setAlertParams] + [derivedIndexPattern, setRuleParams] ); /* eslint-disable-next-line react-hooks/exhaustive-deps */ @@ -185,33 +185,33 @@ export const Expressions: React.FC = (props) => { const updateTimeSize = useCallback( (ts: number | undefined) => { - const criteria = alertParams.criteria.map((c) => ({ + const criteria = ruleParams.criteria.map((c) => ({ ...c, timeSize: ts, })); setTimeSize(ts || undefined); - setAlertParams('criteria', criteria as Criteria); + setRuleParams('criteria', criteria as Criteria); }, - [alertParams.criteria, setAlertParams] + [ruleParams.criteria, setRuleParams] ); const updateTimeUnit = useCallback( (tu: string) => { - const criteria = alertParams.criteria.map((c) => ({ + const criteria = ruleParams.criteria.map((c) => ({ ...c, timeUnit: tu, })); setTimeUnit(tu as Unit); - setAlertParams('criteria', criteria as Criteria); + setRuleParams('criteria', criteria as Criteria); }, - [alertParams.criteria, setAlertParams] + [ruleParams.criteria, setRuleParams] ); const updateNodeType = useCallback( (nt: any) => { - setAlertParams('nodeType', nt); + setRuleParams('nodeType', nt); }, - [setAlertParams] + [setRuleParams] ); const handleFieldSearchChange = useCallback( @@ -222,7 +222,7 @@ export const Expressions: React.FC = (props) => { const preFillAlertCriteria = useCallback(() => { const md = metadata; if (md && md.options) { - setAlertParams('criteria', [ + setRuleParams('criteria', [ { ...defaultExpression, metric: md.options.metric!.type, @@ -232,44 +232,44 @@ export const Expressions: React.FC = (props) => { } as InventoryMetricConditions, ]); } else { - setAlertParams('criteria', [defaultExpression]); + setRuleParams('criteria', [defaultExpression]); } - }, [metadata, setAlertParams]); + }, [metadata, setRuleParams]); const preFillAlertFilter = useCallback(() => { const md = metadata; if (md && md.filter) { - setAlertParams('filterQueryText', md.filter); - setAlertParams( + setRuleParams('filterQueryText', md.filter); + setRuleParams( 'filterQuery', convertKueryToElasticSearchQuery(md.filter, derivedIndexPattern) || '' ); } - }, [metadata, derivedIndexPattern, setAlertParams]); + }, [metadata, derivedIndexPattern, setRuleParams]); useEffect(() => { const md = metadata; - if (!alertParams.nodeType) { + if (!ruleParams.nodeType) { if (md && md.nodeType) { - setAlertParams('nodeType', md.nodeType); + setRuleParams('nodeType', md.nodeType); } else { - setAlertParams('nodeType', 'host'); + setRuleParams('nodeType', 'host'); } } - if (alertParams.criteria && alertParams.criteria.length) { - setTimeSize(alertParams.criteria[0].timeSize); - setTimeUnit(alertParams.criteria[0].timeUnit); + if (ruleParams.criteria && ruleParams.criteria.length) { + setTimeSize(ruleParams.criteria[0].timeSize); + setTimeUnit(ruleParams.criteria[0].timeUnit); } else { preFillAlertCriteria(); } - if (!alertParams.filterQuery) { + if (!ruleParams.filterQuery) { preFillAlertFilter(); } - if (!alertParams.sourceId) { - setAlertParams('sourceId', source?.id || 'default'); + if (!ruleParams.sourceId) { + setRuleParams('sourceId', source?.id || 'default'); } }, [metadata, derivedIndexPattern, defaultExpression, source]); // eslint-disable-line react-hooks/exhaustive-deps @@ -289,33 +289,33 @@ export const Expressions: React.FC = (props) => { - {alertParams.criteria && - alertParams.criteria.map((e, idx) => { + {ruleParams.criteria && + ruleParams.criteria.map((e, idx) => { return ( 1} + nodeType={ruleParams.nodeType} + canDelete={ruleParams.criteria.length > 1} remove={removeExpression} addExpression={addExpression} key={idx} // idx's don't usually make good key's but here the index has semantic meaning expressionId={idx} - setAlertParams={updateParams} + setRuleParams={updateParams} errors={(errors[idx] as IErrorObject) || emptyError} expression={e || {}} fields={derivedIndexPattern.fields} > @@ -365,8 +365,8 @@ export const Expressions: React.FC = (props) => { } - checked={alertParams.alertOnNoData} - onChange={(e) => setAlertParams('alertOnNoData', e.target.checked)} + checked={ruleParams.alertOnNoData} + onChange={(e) => setRuleParams('alertOnNoData', e.target.checked)} /> @@ -386,12 +386,12 @@ export const Expressions: React.FC = (props) => { derivedIndexPattern={derivedIndexPattern} onSubmit={onFilterChange} onChange={debouncedOnFilterChange} - value={alertParams.filterQueryText} + value={ruleParams.filterQueryText} /> )) || ( )} @@ -412,11 +412,11 @@ interface ExpressionRowProps { expression: Omit & { metric?: SnapshotMetricType; }; - errors: AlertTypeParamsExpressionProps['errors']; + errors: RuleTypeParamsExpressionProps['errors']; canDelete: boolean; addExpression(): void; remove(id: number): void; - setAlertParams(id: number, params: Partial): void; + setRuleParams(id: number, params: Partial): void; fields: DerivedIndexPattern['fields']; } @@ -442,7 +442,7 @@ export const ExpressionRow: React.FC = (props) => { const [isExpanded, setRowState] = useState(true); const toggleRowState = useCallback(() => setRowState(!isExpanded), [isExpanded]); - const { children, setAlertParams, expression, errors, expressionId, remove, canDelete, fields } = + const { children, setRuleParams, expression, errors, expressionId, remove, canDelete, fields } = props; const { metric, @@ -461,68 +461,68 @@ export const ExpressionRow: React.FC = (props) => { (m?: SnapshotMetricType | string) => { const newMetric = SnapshotMetricTypeRT.is(m) ? m : Boolean(m) ? 'custom' : undefined; const newAlertParams = { ...expression, metric: newMetric }; - setAlertParams(expressionId, newAlertParams); + setRuleParams(expressionId, newAlertParams); }, - [expressionId, expression, setAlertParams] + [expressionId, expression, setRuleParams] ); const updateCustomMetric = useCallback( (cm?: SnapshotCustomMetricInput) => { if (SnapshotCustomMetricInputRT.is(cm)) { - setAlertParams(expressionId, { ...expression, customMetric: cm }); + setRuleParams(expressionId, { ...expression, customMetric: cm }); } }, - [expressionId, expression, setAlertParams] + [expressionId, expression, setRuleParams] ); const updateComparator = useCallback( (c?: string) => { - setAlertParams(expressionId, { ...expression, comparator: c as Comparator | undefined }); + setRuleParams(expressionId, { ...expression, comparator: c as Comparator | undefined }); }, - [expressionId, expression, setAlertParams] + [expressionId, expression, setRuleParams] ); const updateWarningComparator = useCallback( (c?: string) => { - setAlertParams(expressionId, { ...expression, warningComparator: c as Comparator }); + setRuleParams(expressionId, { ...expression, warningComparator: c as Comparator }); }, - [expressionId, expression, setAlertParams] + [expressionId, expression, setRuleParams] ); const updateThreshold = useCallback( (t) => { if (t.join() !== expression.threshold.join()) { - setAlertParams(expressionId, { ...expression, threshold: t }); + setRuleParams(expressionId, { ...expression, threshold: t }); } }, - [expressionId, expression, setAlertParams] + [expressionId, expression, setRuleParams] ); const updateWarningThreshold = useCallback( (t) => { if (t.join() !== expression.warningThreshold?.join()) { - setAlertParams(expressionId, { ...expression, warningThreshold: t }); + setRuleParams(expressionId, { ...expression, warningThreshold: t }); } }, - [expressionId, expression, setAlertParams] + [expressionId, expression, setRuleParams] ); const toggleWarningThreshold = useCallback(() => { if (!displayWarningThreshold) { setDisplayWarningThreshold(true); - setAlertParams(expressionId, { + setRuleParams(expressionId, { ...expression, warningComparator: comparator, warningThreshold: [], }); } else { setDisplayWarningThreshold(false); - setAlertParams(expressionId, omit(expression, 'warningComparator', 'warningThreshold')); + setRuleParams(expressionId, omit(expression, 'warningComparator', 'warningThreshold')); } }, [ displayWarningThreshold, setDisplayWarningThreshold, - setAlertParams, + setRuleParams, comparator, expression, expressionId, diff --git a/x-pack/plugins/infra/public/alerting/inventory/index.ts b/x-pack/plugins/infra/public/alerting/inventory/index.ts index 4a724694bbad8..aa44369f8eb2c 100644 --- a/x-pack/plugins/infra/public/alerting/inventory/index.ts +++ b/x-pack/plugins/infra/public/alerting/inventory/index.ts @@ -33,7 +33,7 @@ export function createInventoryMetricRuleType(): ObservabilityRuleTypeModel import('./components/expression')), + ruleParamsExpression: React.lazy(() => import('./components/expression')), validate: validateMetricThreshold, defaultActionMessage: i18n.translate( 'xpack.infra.metrics.alerting.inventory.threshold.defaultActionMessage', diff --git a/x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/criterion_preview_chart.tsx b/x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/criterion_preview_chart.tsx index 6e26bb52e7921..3d9618e4b070b 100644 --- a/x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/criterion_preview_chart.tsx +++ b/x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/criterion_preview_chart.tsx @@ -134,7 +134,7 @@ const CriterionPreviewChart: React.FC = ({ chartPreviewData: series, } = useChartPreviewData({ sourceId, - alertParams: chartAlertParams, + ruleParams: chartAlertParams, buckets, }); diff --git a/x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/editor.tsx b/x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/editor.tsx index a9b74ad62d190..bdd6961ec86f2 100644 --- a/x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/editor.tsx +++ b/x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/editor.tsx @@ -11,7 +11,7 @@ import React, { useCallback, useMemo, useState } from 'react'; import useMount from 'react-use/lib/useMount'; import { useKibana } from '../../../../../../../../src/plugins/kibana_react/public'; import { - AlertTypeParamsExpressionProps, + RuleTypeParamsExpressionProps, ForLastExpression, } from '../../../../../../triggers_actions_ui/public'; import { @@ -90,7 +90,7 @@ const createDefaultRatioRuleParams = ( }); export const ExpressionEditor: React.FC< - AlertTypeParamsExpressionProps + RuleTypeParamsExpressionProps > = (props) => { const isInternal = props.metadata?.isInternal ?? false; const [sourceId] = useSourceId(); @@ -159,10 +159,10 @@ export const SourceStatusWrapper: React.FC = ({ children }) => { ); }; -export const Editor: React.FC< - AlertTypeParamsExpressionProps -> = (props) => { - const { setAlertParams, alertParams, errors } = props; +export const Editor: React.FC> = ( + props +) => { + const { setRuleParams, ruleParams, errors } = props; const [hasSetDefaults, setHasSetDefaults] = useState(false); const { sourceId, resolvedSourceConfiguration } = useLogSourceContext(); @@ -195,40 +195,40 @@ export const Editor: React.FC< const updateThreshold = useCallback( (thresholdParams) => { - const nextThresholdParams = { ...alertParams.count, ...thresholdParams }; - setAlertParams('count', nextThresholdParams); + const nextThresholdParams = { ...ruleParams.count, ...thresholdParams }; + setRuleParams('count', nextThresholdParams); }, - [alertParams.count, setAlertParams] + [ruleParams.count, setRuleParams] ); const updateCriteria = useCallback( (criteria: PartialCriteriaType) => { - setAlertParams('criteria', criteria); + setRuleParams('criteria', criteria); }, - [setAlertParams] + [setRuleParams] ); const updateTimeSize = useCallback( (ts: number | undefined) => { - setAlertParams('timeSize', ts); + setRuleParams('timeSize', ts); }, - [setAlertParams] + [setRuleParams] ); const updateTimeUnit = useCallback( (tu: string) => { if (timeUnitRT.is(tu)) { - setAlertParams('timeUnit', tu); + setRuleParams('timeUnit', tu); } }, - [setAlertParams] + [setRuleParams] ); const updateGroupBy = useCallback( (groups: string[]) => { - setAlertParams('groupBy', groups); + setRuleParams('groupBy', groups); }, - [setAlertParams] + [setRuleParams] ); const defaultCountAlertParams = useMemo( @@ -241,41 +241,41 @@ export const Editor: React.FC< const defaults = type === 'count' ? defaultCountAlertParams : createDefaultRatioRuleParams(supportedFields); // Reset properties that don't make sense switching from one context to the other - setAlertParams('count', defaults.count); - setAlertParams('criteria', defaults.criteria); + setRuleParams('count', defaults.count); + setRuleParams('criteria', defaults.criteria); }, - [defaultCountAlertParams, setAlertParams, supportedFields] + [defaultCountAlertParams, setRuleParams, supportedFields] ); useMount(() => { - const newAlertParams = { ...defaultCountAlertParams, ...alertParams }; + const newAlertParams = { ...defaultCountAlertParams, ...ruleParams }; for (const [key, value] of Object.entries(newAlertParams) as ObjectEntries< typeof newAlertParams >) { - setAlertParams(key, value); + setRuleParams(key, value); } setHasSetDefaults(true); }); const shouldShowGroupByOptimizationWarning = useMemo(() => { - const hasSetGroupBy = alertParams.groupBy && alertParams.groupBy.length > 0; + const hasSetGroupBy = ruleParams.groupBy && ruleParams.groupBy.length > 0; return ( hasSetGroupBy && - alertParams.count && - !isOptimizableGroupedThreshold(alertParams.count.comparator, alertParams.count.value) + ruleParams.count && + !isOptimizableGroupedThreshold(ruleParams.count.comparator, ruleParams.count.value) ); - }, [alertParams]); + }, [ruleParams]); // Wait until the alert param defaults have been set if (!hasSetDefaults) return null; - const criteriaComponent = alertParams.criteria ? ( + const criteriaComponent = ruleParams.criteria ? ( @@ -283,32 +283,32 @@ export const Editor: React.FC< return ( <> - + - {alertParams.criteria && !isRatioRule(alertParams.criteria) && criteriaComponent} + {ruleParams.criteria && !isRatioRule(ruleParams.criteria) && criteriaComponent} - {alertParams.criteria && isRatioRule(alertParams.criteria) && criteriaComponent} + {ruleParams.criteria && isRatioRule(ruleParams.criteria) && criteriaComponent} {shouldShowGroupByOptimizationWarning && ( <> diff --git a/x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/hooks/use_chart_preview_data.tsx b/x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/hooks/use_chart_preview_data.tsx index 42f826d09aca8..1568b5f0b7898 100644 --- a/x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/hooks/use_chart_preview_data.tsx +++ b/x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/hooks/use_chart_preview_data.tsx @@ -20,11 +20,11 @@ import { GetLogAlertsChartPreviewDataAlertParamsSubset } from '../../../../../.. interface Options { sourceId: string; - alertParams: GetLogAlertsChartPreviewDataAlertParamsSubset; + ruleParams: GetLogAlertsChartPreviewDataAlertParamsSubset; buckets: number; } -export const useChartPreviewData = ({ sourceId, alertParams, buckets }: Options) => { +export const useChartPreviewData = ({ sourceId, ruleParams, buckets }: Options) => { const { http } = useKibana().services; const [chartPreviewData, setChartPreviewData] = useState< @@ -36,7 +36,7 @@ export const useChartPreviewData = ({ sourceId, alertParams, buckets }: Options) cancelPreviousOn: 'creation', createPromise: async () => { setHasError(false); - return await callGetChartPreviewDataAPI(sourceId, http!.fetch, alertParams, buckets); + return await callGetChartPreviewDataAPI(sourceId, http!.fetch, ruleParams, buckets); }, onResolve: ({ data: { series } }) => { setHasError(false); @@ -46,7 +46,7 @@ export const useChartPreviewData = ({ sourceId, alertParams, buckets }: Options) setHasError(true); }, }, - [sourceId, http, alertParams, buckets] + [sourceId, http, ruleParams, buckets] ); const isLoading = useMemo( diff --git a/x-pack/plugins/infra/public/alerting/log_threshold/log_threshold_rule_type.ts b/x-pack/plugins/infra/public/alerting/log_threshold/log_threshold_rule_type.ts index 56c28074bd5fb..a6c1eaaa07e1b 100644 --- a/x-pack/plugins/infra/public/alerting/log_threshold/log_threshold_rule_type.ts +++ b/x-pack/plugins/infra/public/alerting/log_threshold/log_threshold_rule_type.ts @@ -25,7 +25,7 @@ export function createLogThresholdRuleType(): ObservabilityRuleTypeModel import('./components/expression_editor/editor')), + ruleParamsExpression: React.lazy(() => import('./components/expression_editor/editor')), validate: validateExpression, defaultActionMessage: i18n.translate( 'xpack.infra.logs.alerting.threshold.defaultActionMessage', diff --git a/x-pack/plugins/infra/public/alerting/metric_anomaly/components/expression.test.tsx b/x-pack/plugins/infra/public/alerting/metric_anomaly/components/expression.test.tsx index 6b99aff9f903d..3bbc3cb2662cb 100644 --- a/x-pack/plugins/infra/public/alerting/metric_anomaly/components/expression.test.tsx +++ b/x-pack/plugins/infra/public/alerting/metric_anomaly/components/expression.test.tsx @@ -40,20 +40,20 @@ jest.mock('../../../containers/ml/infra_ml_capabilities', () => ({ describe('Expression', () => { async function setup(currentOptions: AlertContextMeta) { - const alertParams = { + const ruleParams = { metric: undefined, nodeType: undefined, threshold: 50, }; const wrapper = mountWithIntl( Reflect.set(alertParams, key, value)} - setAlertProperty={() => {}} + setRuleParams={(key, value) => Reflect.set(ruleParams, key, value)} + setRuleProperty={() => {}} metadata={currentOptions} /> ); @@ -66,7 +66,7 @@ describe('Expression', () => { await update(); - return { wrapper, update, alertParams }; + return { wrapper, update, ruleParams }; } it('should prefill the alert using the context metadata', async () => { @@ -74,8 +74,8 @@ describe('Expression', () => { nodeType: 'pod', metric: { type: 'tx' }, }; - const { alertParams } = await setup(currentOptions as AlertContextMeta); - expect(alertParams.nodeType).toBe('k8s'); - expect(alertParams.metric).toBe('network_out'); + const { ruleParams } = await setup(currentOptions as AlertContextMeta); + expect(ruleParams.nodeType).toBe('k8s'); + expect(ruleParams.metric).toBe('network_out'); }); }); diff --git a/x-pack/plugins/infra/public/alerting/metric_anomaly/components/expression.tsx b/x-pack/plugins/infra/public/alerting/metric_anomaly/components/expression.tsx index ca38f98534db3..5a0060f229795 100644 --- a/x-pack/plugins/infra/public/alerting/metric_anomaly/components/expression.tsx +++ b/x-pack/plugins/infra/public/alerting/metric_anomaly/components/expression.tsx @@ -18,8 +18,8 @@ import { // eslint-disable-next-line @kbn/eslint/no-restricted-paths } from '../../../../../triggers_actions_ui/public/common'; import { - AlertTypeParams, - AlertTypeParamsExpressionProps, + RuleTypeParams, + RuleTypeParamsExpressionProps, // eslint-disable-next-line @kbn/eslint/no-restricted-paths } from '../../../../../triggers_actions_ui/public/types'; import { useSourceViaHttp } from '../../../containers/metrics_source/use_source_via_http'; @@ -39,11 +39,11 @@ export interface AlertContextMeta { nodeType?: InventoryItemType; } -type AlertParams = AlertTypeParams & +type AlertParams = RuleTypeParams & MetricAnomalyParams & { sourceId: string; spaceId: string; hasInfraMLCapabilities: boolean }; type Props = Omit< - AlertTypeParamsExpressionProps, + RuleTypeParamsExpressionProps, 'defaultActionGroupId' | 'actionGroups' | 'charts' | 'data' >; @@ -59,7 +59,7 @@ export const Expression: React.FC = (props) => { const { http, notifications } = useKibanaContextForPlugin().services; const { space } = useActiveKibanaSpace(); - const { setAlertParams, alertParams, alertInterval, metadata } = props; + const { setRuleParams, ruleParams, ruleInterval, metadata } = props; const { source, createDerivedIndexPattern } = useSourceViaHttp({ sourceId: 'default', fetch: http.fetch, @@ -72,103 +72,103 @@ export const Expression: React.FC = (props) => { ); const [influencerFieldName, updateInfluencerFieldName] = useState( - alertParams.influencerFilter?.fieldName ?? 'host.name' + ruleParams.influencerFilter?.fieldName ?? 'host.name' ); useEffect(() => { - setAlertParams('hasInfraMLCapabilities', hasInfraMLCapabilities); - }, [setAlertParams, hasInfraMLCapabilities]); + setRuleParams('hasInfraMLCapabilities', hasInfraMLCapabilities); + }, [setRuleParams, hasInfraMLCapabilities]); useEffect(() => { - if (alertParams.influencerFilter) { - setAlertParams('influencerFilter', { - ...alertParams.influencerFilter, + if (ruleParams.influencerFilter) { + setRuleParams('influencerFilter', { + ...ruleParams.influencerFilter, fieldName: influencerFieldName, }); } - }, [influencerFieldName, alertParams, setAlertParams]); + }, [influencerFieldName, ruleParams, setRuleParams]); const updateInfluencerFieldValue = useCallback( (value: string) => { if (value) { - setAlertParams('influencerFilter', { - ...alertParams.influencerFilter, + setRuleParams('influencerFilter', { + ...ruleParams.influencerFilter, fieldValue: value, } as MetricAnomalyParams['influencerFilter']); } else { - setAlertParams('influencerFilter', undefined); + setRuleParams('influencerFilter', undefined); } }, - [setAlertParams, alertParams] + [setRuleParams, ruleParams] ); useEffect(() => { - setAlertParams('alertInterval', alertInterval); - }, [setAlertParams, alertInterval]); + setRuleParams('alertInterval', ruleInterval); + }, [setRuleParams, ruleInterval]); const updateNodeType = useCallback( (nt: any) => { - setAlertParams('nodeType', nt); + setRuleParams('nodeType', nt); }, - [setAlertParams] + [setRuleParams] ); const updateMetric = useCallback( (metric: string) => { - setAlertParams('metric', metric as MetricAnomalyParams['metric']); + setRuleParams('metric', metric as MetricAnomalyParams['metric']); }, - [setAlertParams] + [setRuleParams] ); const updateSeverityThreshold = useCallback( (threshold: any) => { - setAlertParams('threshold', threshold); + setRuleParams('threshold', threshold); }, - [setAlertParams] + [setRuleParams] ); const prefillNodeType = useCallback(() => { const md = metadata; if (md && md.nodeType) { - setAlertParams( + setRuleParams( 'nodeType', getMLNodeTypeFromInventoryNodeType(md.nodeType) ?? defaultExpression.nodeType ); } else { - setAlertParams('nodeType', defaultExpression.nodeType); + setRuleParams('nodeType', defaultExpression.nodeType); } - }, [metadata, setAlertParams]); + }, [metadata, setRuleParams]); const prefillMetric = useCallback(() => { const md = metadata; if (md && md.metric) { - setAlertParams( + setRuleParams( 'metric', getMLMetricFromInventoryMetric(md.metric.type) ?? defaultExpression.metric ); } else { - setAlertParams('metric', defaultExpression.metric); + setRuleParams('metric', defaultExpression.metric); } - }, [metadata, setAlertParams]); + }, [metadata, setRuleParams]); useEffect(() => { - if (!alertParams.nodeType) { + if (!ruleParams.nodeType) { prefillNodeType(); } - if (!alertParams.threshold) { - setAlertParams('threshold', defaultExpression.threshold); + if (!ruleParams.threshold) { + setRuleParams('threshold', defaultExpression.threshold); } - if (!alertParams.metric) { + if (!ruleParams.metric) { prefillMetric(); } - if (!alertParams.sourceId) { - setAlertParams('sourceId', source?.id || 'default'); + if (!ruleParams.sourceId) { + setRuleParams('sourceId', source?.id || 'default'); } - if (!alertParams.spaceId) { - setAlertParams('spaceId', space?.id || 'default'); + if (!ruleParams.spaceId) { + setRuleParams('spaceId', space?.id || 'default'); } }, [metadata, derivedIndexPattern, defaultExpression, source, space]); // eslint-disable-line react-hooks/exhaustive-deps @@ -190,7 +190,7 @@ export const Expression: React.FC = (props) => { @@ -199,7 +199,7 @@ export const Expression: React.FC = (props) => { = (props) => { @@ -239,9 +239,9 @@ export const Expression: React.FC = (props) => { diff --git a/x-pack/plugins/infra/public/alerting/metric_anomaly/index.ts b/x-pack/plugins/infra/public/alerting/metric_anomaly/index.ts index 1fab0ea89fe5a..95931bff65b53 100644 --- a/x-pack/plugins/infra/public/alerting/metric_anomaly/index.ts +++ b/x-pack/plugins/infra/public/alerting/metric_anomaly/index.ts @@ -8,7 +8,7 @@ import { i18n } from '@kbn/i18n'; import React from 'react'; import { METRIC_ANOMALY_ALERT_TYPE_ID } from '../../../common/alerting/metrics'; -import { AlertTypeModel } from '../../../../triggers_actions_ui/public'; +import { RuleTypeModel } from '../../../../triggers_actions_ui/public'; import { AlertTypeParams as RuleTypeParams } from '../../../../alerting/common'; import { validateMetricAnomaly } from './components/validation'; @@ -16,7 +16,7 @@ interface MetricAnomalyRuleTypeParams extends RuleTypeParams { hasInfraMLCapabilities: boolean; } -export function createMetricAnomalyRuleType(): AlertTypeModel { +export function createMetricAnomalyRuleType(): RuleTypeModel { return { id: METRIC_ANOMALY_ALERT_TYPE_ID, description: i18n.translate('xpack.infra.metrics.anomaly.alertFlyout.alertDescription', { @@ -26,7 +26,7 @@ export function createMetricAnomalyRuleType(): AlertTypeModel import('./components/expression')), + ruleParamsExpression: React.lazy(() => import('./components/expression')), validate: validateMetricAnomaly, defaultActionMessage: i18n.translate( 'xpack.infra.metrics.alerting.anomaly.defaultActionMessage', diff --git a/x-pack/plugins/infra/public/alerting/metric_threshold/components/expression.test.tsx b/x-pack/plugins/infra/public/alerting/metric_threshold/components/expression.test.tsx index 667f5c061ce48..f5df605316e24 100644 --- a/x-pack/plugins/infra/public/alerting/metric_threshold/components/expression.test.tsx +++ b/x-pack/plugins/infra/public/alerting/metric_threshold/components/expression.test.tsx @@ -34,7 +34,7 @@ describe('Expression', () => { filterQuery?: string; groupBy?: string; }) { - const alertParams = { + const ruleParams = { criteria: [], groupBy: undefined, filterQueryText: '', @@ -42,13 +42,13 @@ describe('Expression', () => { }; const wrapper = mountWithIntl( Reflect.set(alertParams, key, value)} - setAlertProperty={() => {}} + setRuleParams={(key, value) => Reflect.set(ruleParams, key, value)} + setRuleProperty={() => {}} metadata={{ currentOptions, }} @@ -63,7 +63,7 @@ describe('Expression', () => { await update(); - return { wrapper, update, alertParams }; + return { wrapper, update, ruleParams }; } it('should prefill the alert using the context metadata', async () => { @@ -75,10 +75,10 @@ describe('Expression', () => { { aggregation: 'cardinality', field: 'system.cpu.user.pct' }, ] as MetricsExplorerMetric[], }; - const { alertParams } = await setup(currentOptions); - expect(alertParams.groupBy).toBe('host.hostname'); - expect(alertParams.filterQueryText).toBe('foo'); - expect(alertParams.criteria).toEqual([ + const { ruleParams } = await setup(currentOptions); + expect(ruleParams.groupBy).toBe('host.hostname'); + expect(ruleParams.filterQueryText).toBe('foo'); + expect(ruleParams.criteria).toEqual([ { metric: 'system.load.1', comparator: Comparator.GT, diff --git a/x-pack/plugins/infra/public/alerting/metric_threshold/components/expression.tsx b/x-pack/plugins/infra/public/alerting/metric_threshold/components/expression.tsx index 19817ab666e5a..f553d4b9dadf7 100644 --- a/x-pack/plugins/infra/public/alerting/metric_threshold/components/expression.tsx +++ b/x-pack/plugins/infra/public/alerting/metric_threshold/components/expression.tsx @@ -27,8 +27,8 @@ import { Comparator, Aggregators } from '../../../../common/alerting/metrics'; import { ForLastExpression } from '../../../../../triggers_actions_ui/public'; import { IErrorObject, - AlertTypeParams, - AlertTypeParamsExpressionProps, + RuleTypeParams, + RuleTypeParamsExpressionProps, } from '../../../../../triggers_actions_ui/public'; import { MetricsExplorerKueryBar } from '../../../pages/metrics/metrics_explorer/components/kuery_bar'; import { MetricsExplorerOptions } from '../../../pages/metrics/metrics_explorer/hooks/use_metrics_explorer_options'; @@ -45,7 +45,7 @@ const FILTER_TYPING_DEBOUNCE_MS = 500; export const QUERY_INVALID = Symbol('QUERY_INVALID'); type Props = Omit< - AlertTypeParamsExpressionProps, + RuleTypeParamsExpressionProps, 'defaultActionGroupId' | 'actionGroups' | 'charts' | 'data' >; @@ -59,7 +59,7 @@ const defaultExpression = { export { defaultExpression }; export const Expressions: React.FC = (props) => { - const { setAlertParams, alertParams, errors, metadata } = props; + const { setRuleParams, ruleParams, errors, metadata } = props; const { http, notifications, docLinks } = useKibanaContextForPlugin().services; const { source, createDerivedIndexPattern } = useSourceViaHttp({ sourceId: 'default', @@ -87,47 +87,47 @@ export const Expressions: React.FC = (props) => { const updateParams = useCallback( (id, e: MetricExpression) => { - const exp = alertParams.criteria ? alertParams.criteria.slice() : []; + const exp = ruleParams.criteria ? ruleParams.criteria.slice() : []; exp[id] = e; - setAlertParams('criteria', exp); + setRuleParams('criteria', exp); }, - [setAlertParams, alertParams.criteria] + [setRuleParams, ruleParams.criteria] ); const addExpression = useCallback(() => { - const exp = alertParams.criteria?.slice() || []; + const exp = ruleParams.criteria?.slice() || []; exp.push({ ...defaultExpression, timeSize: timeSize ?? defaultExpression.timeSize, timeUnit: timeUnit ?? defaultExpression.timeUnit, }); - setAlertParams('criteria', exp); - }, [setAlertParams, alertParams.criteria, timeSize, timeUnit]); + setRuleParams('criteria', exp); + }, [setRuleParams, ruleParams.criteria, timeSize, timeUnit]); const removeExpression = useCallback( (id: number) => { - const exp = alertParams.criteria?.slice() || []; + const exp = ruleParams.criteria?.slice() || []; if (exp.length > 1) { exp.splice(id, 1); - setAlertParams('criteria', exp); + setRuleParams('criteria', exp); } }, - [setAlertParams, alertParams.criteria] + [setRuleParams, ruleParams.criteria] ); const onFilterChange = useCallback( (filter: any) => { - setAlertParams('filterQueryText', filter); + setRuleParams('filterQueryText', filter); try { - setAlertParams( + setRuleParams( 'filterQuery', convertKueryToElasticSearchQuery(filter, derivedIndexPattern, false) || '' ); } catch (e) { - setAlertParams('filterQuery', QUERY_INVALID); + setRuleParams('filterQuery', QUERY_INVALID); } }, - [setAlertParams, derivedIndexPattern] + [setRuleParams, derivedIndexPattern] ); /* eslint-disable-next-line react-hooks/exhaustive-deps */ @@ -137,9 +137,9 @@ export const Expressions: React.FC = (props) => { const onGroupByChange = useCallback( (group: string | null | string[]) => { - setAlertParams('groupBy', group && group.length ? group : ''); + setRuleParams('groupBy', group && group.length ? group : ''); }, - [setAlertParams] + [setRuleParams] ); const emptyError = useMemo(() => { @@ -153,33 +153,33 @@ export const Expressions: React.FC = (props) => { const updateTimeSize = useCallback( (ts: number | undefined) => { const criteria = - alertParams.criteria?.map((c) => ({ + ruleParams.criteria?.map((c) => ({ ...c, timeSize: ts, })) || []; setTimeSize(ts || undefined); - setAlertParams('criteria', criteria); + setRuleParams('criteria', criteria); }, - [alertParams.criteria, setAlertParams] + [ruleParams.criteria, setRuleParams] ); const updateTimeUnit = useCallback( (tu: string) => { const criteria = - alertParams.criteria?.map((c) => ({ + ruleParams.criteria?.map((c) => ({ ...c, timeUnit: tu, })) || []; setTimeUnit(tu as Unit); - setAlertParams('criteria', criteria as AlertParams['criteria']); + setRuleParams('criteria', criteria as AlertParams['criteria']); }, - [alertParams.criteria, setAlertParams] + [ruleParams.criteria, setRuleParams] ); const preFillAlertCriteria = useCallback(() => { const md = metadata; if (md?.currentOptions?.metrics?.length) { - setAlertParams( + setRuleParams( 'criteria', md.currentOptions.metrics.map((metric) => ({ metric: metric.field, @@ -191,15 +191,15 @@ export const Expressions: React.FC = (props) => { })) as AlertParams['criteria'] ); } else { - setAlertParams('criteria', [defaultExpression]); + setRuleParams('criteria', [defaultExpression]); } - }, [metadata, setAlertParams, timeSize, timeUnit]); + }, [metadata, setRuleParams, timeSize, timeUnit]); const preFillAlertFilter = useCallback(() => { const md = metadata; if (md && md.currentOptions?.filterQuery) { - setAlertParams('filterQueryText', md.currentOptions.filterQuery); - setAlertParams( + setRuleParams('filterQueryText', md.currentOptions.filterQuery); + setRuleParams( 'filterQuery', convertKueryToElasticSearchQuery(md.currentOptions.filterQuery, derivedIndexPattern) || '' ); @@ -208,46 +208,46 @@ export const Expressions: React.FC = (props) => { const filter = Array.isArray(groupBy) ? groupBy.map((field, index) => `${field}: "${md.series?.keys?.[index]}"`).join(' and ') : `${groupBy}: "${md.series.id}"`; - setAlertParams('filterQueryText', filter); - setAlertParams( + setRuleParams('filterQueryText', filter); + setRuleParams( 'filterQuery', convertKueryToElasticSearchQuery(filter, derivedIndexPattern) || '' ); } - }, [metadata, derivedIndexPattern, setAlertParams]); + }, [metadata, derivedIndexPattern, setRuleParams]); const preFillAlertGroupBy = useCallback(() => { const md = metadata; if (md && md.currentOptions?.groupBy && !md.series) { - setAlertParams('groupBy', md.currentOptions.groupBy); + setRuleParams('groupBy', md.currentOptions.groupBy); } - }, [metadata, setAlertParams]); + }, [metadata, setRuleParams]); useEffect(() => { - if (alertParams.criteria && alertParams.criteria.length) { - setTimeSize(alertParams.criteria[0].timeSize); - setTimeUnit(alertParams.criteria[0].timeUnit); + if (ruleParams.criteria && ruleParams.criteria.length) { + setTimeSize(ruleParams.criteria[0].timeSize); + setTimeUnit(ruleParams.criteria[0].timeUnit); } else { preFillAlertCriteria(); } - if (!alertParams.filterQuery) { + if (!ruleParams.filterQuery) { preFillAlertFilter(); } - if (!alertParams.groupBy) { + if (!ruleParams.groupBy) { preFillAlertGroupBy(); } - if (!alertParams.sourceId) { - setAlertParams('sourceId', source?.id || 'default'); + if (!ruleParams.sourceId) { + setRuleParams('sourceId', source?.id || 'default'); } - if (typeof alertParams.alertOnNoData === 'undefined') { - setAlertParams('alertOnNoData', true); + if (typeof ruleParams.alertOnNoData === 'undefined') { + setRuleParams('alertOnNoData', true); } - if (typeof alertParams.alertOnGroupDisappear === 'undefined') { - setAlertParams('alertOnGroupDisappear', true); + if (typeof ruleParams.alertOnGroupDisappear === 'undefined') { + setRuleParams('alertOnGroupDisappear', true); } }, [metadata, source]); // eslint-disable-line react-hooks/exhaustive-deps @@ -257,36 +257,34 @@ export const Expressions: React.FC = (props) => { ); const areAllAggsRate = useMemo( - () => alertParams.criteria?.every((c) => c.aggType === Aggregators.RATE), - [alertParams.criteria] + () => ruleParams.criteria?.every((c) => c.aggType === Aggregators.RATE), + [ruleParams.criteria] ); const hasGroupBy = useMemo( - () => alertParams.groupBy && alertParams.groupBy.length > 0, - [alertParams.groupBy] + () => ruleParams.groupBy && ruleParams.groupBy.length > 0, + [ruleParams.groupBy] ); const disableNoData = useMemo( - () => alertParams.criteria?.every((c) => c.aggType === Aggregators.COUNT), - [alertParams.criteria] + () => ruleParams.criteria?.every((c) => c.aggType === Aggregators.COUNT), + [ruleParams.criteria] ); // Test to see if any of the group fields in groupBy are already filtered down to a single // group by the filterQuery. If this is the case, then a groupBy is unnecessary, as it would only // ever produce one group instance const groupByFilterTestPatterns = useMemo(() => { - if (!alertParams.groupBy) return null; - const groups = !Array.isArray(alertParams.groupBy) - ? [alertParams.groupBy] - : alertParams.groupBy; + if (!ruleParams.groupBy) return null; + const groups = !Array.isArray(ruleParams.groupBy) ? [ruleParams.groupBy] : ruleParams.groupBy; return groups.map((group: string) => ({ groupName: group, pattern: new RegExp(`{"match(_phrase)?":{"${group}":"(.*?)"}}`), })); - }, [alertParams.groupBy]); + }, [ruleParams.groupBy]); const redundantFilterGroupBy = useMemo(() => { - const { filterQuery } = alertParams; + const { filterQuery } = ruleParams; if (typeof filterQuery !== 'string' || !groupByFilterTestPatterns) return []; return groupByFilterTestPatterns .map(({ groupName, pattern }) => { @@ -295,7 +293,7 @@ export const Expressions: React.FC = (props) => { } }) .filter((g) => typeof g === 'string') as string[]; - }, [alertParams, groupByFilterTestPatterns]); + }, [ruleParams, groupByFilterTestPatterns]); return ( <> @@ -309,17 +307,17 @@ export const Expressions: React.FC = (props) => { - {alertParams.criteria && - alertParams.criteria.map((e, idx) => { + {ruleParams.criteria && + ruleParams.criteria.map((e, idx) => { return ( 1) || false} + canDelete={(ruleParams.criteria && ruleParams.criteria.length > 1) || false} fields={derivedIndexPattern.fields} remove={removeExpression} addExpression={addExpression} key={idx} // idx's don't usually make good key's but here the index has semantic meaning expressionId={idx} - setAlertParams={updateParams} + setRuleParams={updateParams} errors={(errors[idx] as IErrorObject) || emptyError} expression={e || {}} > @@ -327,8 +325,8 @@ export const Expressions: React.FC = (props) => { expression={e} derivedIndexPattern={derivedIndexPattern} source={source} - filterQuery={alertParams.filterQueryText} - groupBy={alertParams.groupBy} + filterQuery={ruleParams.filterQueryText} + groupBy={ruleParams.groupBy} /> ); @@ -389,8 +387,8 @@ export const Expressions: React.FC = (props) => { } - checked={alertParams.alertOnNoData} - onChange={(e) => setAlertParams('alertOnNoData', e.target.checked)} + checked={ruleParams.alertOnNoData} + onChange={(e) => setRuleParams('alertOnNoData', e.target.checked)} /> = (props) => { } - checked={areAllAggsRate || alertParams.shouldDropPartialBuckets} + checked={areAllAggsRate || ruleParams.shouldDropPartialBuckets} disabled={areAllAggsRate} - onChange={(e) => setAlertParams('shouldDropPartialBuckets', e.target.checked)} + onChange={(e) => setRuleParams('shouldDropPartialBuckets', e.target.checked)} /> @@ -436,12 +434,12 @@ export const Expressions: React.FC = (props) => { derivedIndexPattern={derivedIndexPattern} onChange={debouncedOnFilterChange} onSubmit={onFilterChange} - value={alertParams.filterQueryText} + value={ruleParams.filterQueryText} /> )) || ( )} @@ -464,7 +462,7 @@ export const Expressions: React.FC = (props) => { fields={derivedIndexPattern.fields} options={{ ...options, - groupBy: alertParams.groupBy || undefined, + groupBy: ruleParams.groupBy || undefined, }} errorOptions={redundantFilterGroupBy} /> @@ -516,8 +514,8 @@ export const Expressions: React.FC = (props) => { } disabled={disableNoData || !hasGroupBy} - checked={Boolean(hasGroupBy && alertParams.alertOnGroupDisappear)} - onChange={(e) => setAlertParams('alertOnGroupDisappear', e.target.checked)} + checked={Boolean(hasGroupBy && ruleParams.alertOnGroupDisappear)} + onChange={(e) => setRuleParams('alertOnGroupDisappear', e.target.checked)} /> diff --git a/x-pack/plugins/infra/public/alerting/metric_threshold/components/expression_row.test.tsx b/x-pack/plugins/infra/public/alerting/metric_threshold/components/expression_row.test.tsx index 4da8ef0501ec1..b4321dbfda320 100644 --- a/x-pack/plugins/infra/public/alerting/metric_threshold/components/expression_row.test.tsx +++ b/x-pack/plugins/infra/public/alerting/metric_threshold/components/expression_row.test.tsx @@ -45,7 +45,7 @@ describe('ExpressionRow', () => { addExpression={() => {}} key={1} expressionId={1} - setAlertParams={() => {}} + setRuleParams={() => {}} errors={{ aggField: [], timeSizeUnit: [], diff --git a/x-pack/plugins/infra/public/alerting/metric_threshold/components/expression_row.tsx b/x-pack/plugins/infra/public/alerting/metric_threshold/components/expression_row.tsx index 6acf7e6c4b153..cc5a58aee1303 100644 --- a/x-pack/plugins/infra/public/alerting/metric_threshold/components/expression_row.tsx +++ b/x-pack/plugins/infra/public/alerting/metric_threshold/components/expression_row.tsx @@ -53,7 +53,7 @@ interface ExpressionRowProps { canDelete: boolean; addExpression(): void; remove(id: number): void; - setAlertParams(id: number, params: MetricExpression): void; + setRuleParams(id: number, params: MetricExpression): void; } const StyledExpressionRow = euiStyled(EuiFlexGroup)` @@ -74,7 +74,7 @@ const StyledHealth = euiStyled(EuiHealth)` export const ExpressionRow: React.FC = (props) => { const [isExpanded, setRowState] = useState(true); const toggleRowState = useCallback(() => setRowState(!isExpanded), [isExpanded]); - const { children, setAlertParams, expression, errors, expressionId, remove, fields, canDelete } = + const { children, setRuleParams, expression, errors, expressionId, remove, fields, canDelete } = props; const { aggType = AGGREGATION_TYPES.MAX, @@ -92,34 +92,34 @@ export const ExpressionRow: React.FC = (props) => { const updateAggType = useCallback( (at: string) => { - setAlertParams(expressionId, { + setRuleParams(expressionId, { ...expression, aggType: at as MetricExpression['aggType'], metric: at === 'count' ? undefined : expression.metric, }); }, - [expressionId, expression, setAlertParams] + [expressionId, expression, setRuleParams] ); const updateMetric = useCallback( (m?: MetricExpression['metric']) => { - setAlertParams(expressionId, { ...expression, metric: m }); + setRuleParams(expressionId, { ...expression, metric: m }); }, - [expressionId, expression, setAlertParams] + [expressionId, expression, setRuleParams] ); const updateComparator = useCallback( (c?: string) => { - setAlertParams(expressionId, { ...expression, comparator: c as Comparator }); + setRuleParams(expressionId, { ...expression, comparator: c as Comparator }); }, - [expressionId, expression, setAlertParams] + [expressionId, expression, setRuleParams] ); const updateWarningComparator = useCallback( (c?: string) => { - setAlertParams(expressionId, { ...expression, warningComparator: c as Comparator }); + setRuleParams(expressionId, { ...expression, warningComparator: c as Comparator }); }, - [expressionId, expression, setAlertParams] + [expressionId, expression, setRuleParams] ); const convertThreshold = useCallback( @@ -132,38 +132,38 @@ export const ExpressionRow: React.FC = (props) => { (enteredThreshold) => { const t = convertThreshold(enteredThreshold); if (t.join() !== expression.threshold.join()) { - setAlertParams(expressionId, { ...expression, threshold: t }); + setRuleParams(expressionId, { ...expression, threshold: t }); } }, - [expressionId, expression, convertThreshold, setAlertParams] + [expressionId, expression, convertThreshold, setRuleParams] ); const updateWarningThreshold = useCallback( (enteredThreshold) => { const t = convertThreshold(enteredThreshold); if (t.join() !== expression.warningThreshold?.join()) { - setAlertParams(expressionId, { ...expression, warningThreshold: t }); + setRuleParams(expressionId, { ...expression, warningThreshold: t }); } }, - [expressionId, expression, convertThreshold, setAlertParams] + [expressionId, expression, convertThreshold, setRuleParams] ); const toggleWarningThreshold = useCallback(() => { if (!displayWarningThreshold) { setDisplayWarningThreshold(true); - setAlertParams(expressionId, { + setRuleParams(expressionId, { ...expression, warningComparator: comparator, warningThreshold: [], }); } else { setDisplayWarningThreshold(false); - setAlertParams(expressionId, omit(expression, 'warningComparator', 'warningThreshold')); + setRuleParams(expressionId, omit(expression, 'warningComparator', 'warningThreshold')); } }, [ displayWarningThreshold, setDisplayWarningThreshold, - setAlertParams, + setRuleParams, comparator, expression, expressionId, diff --git a/x-pack/plugins/infra/public/alerting/metric_threshold/index.ts b/x-pack/plugins/infra/public/alerting/metric_threshold/index.ts index d45d090e0ec92..cb938597ab432 100644 --- a/x-pack/plugins/infra/public/alerting/metric_threshold/index.ts +++ b/x-pack/plugins/infra/public/alerting/metric_threshold/index.ts @@ -31,7 +31,7 @@ export function createMetricThresholdRuleType(): ObservabilityRuleTypeModel import('./components/expression')), + ruleParamsExpression: React.lazy(() => import('./components/expression')), validate: validateMetricThreshold, defaultActionMessage: i18n.translate( 'xpack.infra.metrics.alerting.threshold.defaultActionMessage', diff --git a/x-pack/plugins/infra/server/lib/alerting/log_threshold/log_threshold_executor.ts b/x-pack/plugins/infra/server/lib/alerting/log_threshold/log_threshold_executor.ts index d7442848f9519..8ef8b3e21e7c8 100644 --- a/x-pack/plugins/infra/server/lib/alerting/log_threshold/log_threshold_executor.ts +++ b/x-pack/plugins/infra/server/lib/alerting/log_threshold/log_threshold_executor.ts @@ -143,30 +143,30 @@ export const createLogThresholdExecutor = (libs: InfraBackendLibs) => }); async function executeAlert( - alertParams: CountRuleParams, + ruleParams: CountRuleParams, timestampField: string, indexPattern: string, runtimeMappings: estypes.MappingRuntimeFields, esClient: ElasticsearchClient, alertFactory: LogThresholdAlertFactory ) { - const query = getESQuery(alertParams, timestampField, indexPattern, runtimeMappings); + const query = getESQuery(ruleParams, timestampField, indexPattern, runtimeMappings); if (!query) { throw new Error('ES query could not be built from the provided alert params'); } - if (hasGroupBy(alertParams)) { + if (hasGroupBy(ruleParams)) { processGroupByResults( await getGroupedResults(query, esClient), - alertParams, + ruleParams, alertFactory, updateAlert ); } else { processUngroupedResults( await getUngroupedResults(query, esClient), - alertParams, + ruleParams, alertFactory, updateAlert ); @@ -174,7 +174,7 @@ async function executeAlert( } async function executeRatioAlert( - alertParams: RatioRuleParams, + ruleParams: RatioRuleParams, timestampField: string, indexPattern: string, runtimeMappings: estypes.MappingRuntimeFields, @@ -183,13 +183,13 @@ async function executeRatioAlert( ) { // Ratio alert params are separated out into two standard sets of alert params const numeratorParams: RuleParams = { - ...alertParams, - criteria: getNumerator(alertParams.criteria), + ...ruleParams, + criteria: getNumerator(ruleParams.criteria), }; const denominatorParams: RuleParams = { - ...alertParams, - criteria: getDenominator(alertParams.criteria), + ...ruleParams, + criteria: getDenominator(ruleParams.criteria), }; const numeratorQuery = getESQuery(numeratorParams, timestampField, indexPattern, runtimeMappings); @@ -204,7 +204,7 @@ async function executeRatioAlert( throw new Error('ES query could not be built from the provided ratio alert params'); } - if (hasGroupBy(alertParams)) { + if (hasGroupBy(ruleParams)) { const [numeratorGroupedResults, denominatorGroupedResults] = await Promise.all([ getGroupedResults(numeratorQuery, esClient), getGroupedResults(denominatorQuery, esClient), @@ -212,7 +212,7 @@ async function executeRatioAlert( processGroupByRatioResults( numeratorGroupedResults, denominatorGroupedResults, - alertParams, + ruleParams, alertFactory, updateAlert ); @@ -224,7 +224,7 @@ async function executeRatioAlert( processUngroupedRatioResults( numeratorUngroupedResults, denominatorUngroupedResults, - alertParams, + ruleParams, alertFactory, updateAlert ); diff --git a/x-pack/plugins/ml/public/alerting/jobs_health_rule/anomaly_detection_jobs_health_rule_trigger.tsx b/x-pack/plugins/ml/public/alerting/jobs_health_rule/anomaly_detection_jobs_health_rule_trigger.tsx index 54ee446e7ac57..cadd471a1afab 100644 --- a/x-pack/plugins/ml/public/alerting/jobs_health_rule/anomaly_detection_jobs_health_rule_trigger.tsx +++ b/x-pack/plugins/ml/public/alerting/jobs_health_rule/anomaly_detection_jobs_health_rule_trigger.tsx @@ -10,7 +10,7 @@ import { EuiComboBoxOptionOption, EuiForm, EuiSpacer } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; import useDebounce from 'react-use/lib/useDebounce'; -import { AlertTypeParamsExpressionProps } from '../../../../triggers_actions_ui/public'; +import { RuleTypeParamsExpressionProps } from '../../../../triggers_actions_ui/public'; import { MlAnomalyDetectionJobsHealthRuleParams } from '../../../common/types/alerts'; import { JobSelectorControl } from '../job_selector'; import { jobsApiProvider } from '../../application/services/ml_api_service/jobs'; @@ -23,11 +23,11 @@ import { BetaBadge } from '../beta_badge'; import { isDefined } from '../../../common/types/guards'; export type MlAnomalyAlertTriggerProps = - AlertTypeParamsExpressionProps; + RuleTypeParamsExpressionProps; const AnomalyDetectionJobsHealthRuleTrigger: FC = ({ - alertParams, - setAlertParams, + ruleParams, + setRuleParams, errors, }) => { const { @@ -40,19 +40,19 @@ const AnomalyDetectionJobsHealthRuleTrigger: FC = ({ >([]); const includeJobsAndGroupIds: string[] = useMemo( - () => (Object.values(alertParams.includeJobs ?? {}) as string[][]).flat(), - [alertParams.includeJobs] + () => (Object.values(ruleParams.includeJobs ?? {}) as string[][]).flat(), + [ruleParams.includeJobs] ); const excludeJobsAndGroupIds: string[] = useMemo( - () => (Object.values(alertParams.excludeJobs ?? {}) as string[][]).flat(), - [alertParams.excludeJobs] + () => (Object.values(ruleParams.excludeJobs ?? {}) as string[][]).flat(), + [ruleParams.excludeJobs] ); const onAlertParamChange = useCallback( (param: T) => (update: MlAnomalyDetectionJobsHealthRuleParams[T]) => { - setAlertParams(param, update); + setRuleParams(param, update); }, [] ); @@ -62,16 +62,16 @@ const AnomalyDetectionJobsHealthRuleTrigger: FC = ({ useDebounce( function updateExcludeJobsOptions() { - const areAllJobsSelected = alertParams.includeJobs?.jobIds?.[0] === ALL_JOBS_SELECTION; + const areAllJobsSelected = ruleParams.includeJobs?.jobIds?.[0] === ALL_JOBS_SELECTION; - if (!areAllJobsSelected && !alertParams.includeJobs?.groupIds?.length) { + if (!areAllJobsSelected && !ruleParams.includeJobs?.groupIds?.length) { // It only makes sense to suggest excluded jobs options when at least one group or all jobs are selected setExcludeJobsOptions([]); return; } adJobsApiService - .jobs(areAllJobsSelected ? [] : (alertParams.includeJobs.groupIds as string[])) + .jobs(areAllJobsSelected ? [] : (ruleParams.includeJobs.groupIds as string[])) .then((jobs) => { setExcludeJobsOptions([ { @@ -89,7 +89,7 @@ const AnomalyDetectionJobsHealthRuleTrigger: FC = ({ jobs .map((v) => v.groups) .flat() - .filter((v) => isDefined(v) && !alertParams.includeJobs.groupIds?.includes(v)) + .filter((v) => isDefined(v) && !ruleParams.includeJobs.groupIds?.includes(v)) ), ].map((v) => ({ label: v! })), }, @@ -97,7 +97,7 @@ const AnomalyDetectionJobsHealthRuleTrigger: FC = ({ }); }, 500, - [alertParams.includeJobs] + [ruleParams.includeJobs] ); return ( @@ -158,7 +158,7 @@ const AnomalyDetectionJobsHealthRuleTrigger: FC = ({ diff --git a/x-pack/plugins/ml/public/alerting/jobs_health_rule/register_jobs_health_alerting_rule.ts b/x-pack/plugins/ml/public/alerting/jobs_health_rule/register_jobs_health_alerting_rule.ts index e6a7c0a648eea..006e765edd07c 100644 --- a/x-pack/plugins/ml/public/alerting/jobs_health_rule/register_jobs_health_alerting_rule.ts +++ b/x-pack/plugins/ml/public/alerting/jobs_health_rule/register_jobs_health_alerting_rule.ts @@ -28,7 +28,7 @@ export function registerJobsHealthAlertingRule( documentationUrl(docLinks) { return docLinks.links.ml.alertingRules; }, - alertParamsExpression: lazy(() => import('./anomaly_detection_jobs_health_rule_trigger')), + ruleParamsExpression: lazy(() => import('./anomaly_detection_jobs_health_rule_trigger')), validate: (alertParams: MlAnomalyDetectionJobsHealthRuleParams) => { const validationResult = { errors: { diff --git a/x-pack/plugins/ml/public/alerting/ml_anomaly_alert_trigger.tsx b/x-pack/plugins/ml/public/alerting/ml_anomaly_alert_trigger.tsx index 2be57ddf95431..609baf17a4c58 100644 --- a/x-pack/plugins/ml/public/alerting/ml_anomaly_alert_trigger.tsx +++ b/x-pack/plugins/ml/public/alerting/ml_anomaly_alert_trigger.tsx @@ -29,19 +29,19 @@ import { CombinedJobWithStats } from '../../common/types/anomaly_detection_jobs' import { AdvancedSettings } from './advanced_settings'; import { getLookbackInterval, getTopNBuckets } from '../../common/util/alerts'; import { isDefined } from '../../common/types/guards'; -import { AlertTypeParamsExpressionProps } from '../../../triggers_actions_ui/public'; +import { RuleTypeParamsExpressionProps } from '../../../triggers_actions_ui/public'; import { parseInterval } from '../../common/util/parse_interval'; import { BetaBadge } from './beta_badge'; export type MlAnomalyAlertTriggerProps = - AlertTypeParamsExpressionProps; + RuleTypeParamsExpressionProps; const MlAnomalyAlertTrigger: FC = ({ - alertParams, - setAlertParams, - setAlertProperty, + ruleParams, + setRuleParams, + setRuleProperty, errors, - alertInterval, + ruleInterval, alertNotifyWhen, }) => { const { @@ -57,14 +57,14 @@ const MlAnomalyAlertTrigger: FC = ({ const onAlertParamChange = useCallback( (param: T) => (update: MlAnomalyDetectionAlertParams[T]) => { - setAlertParams(param, update); + setRuleParams(param, update); }, [] ); const jobsAndGroupIds: string[] = useMemo( - () => (Object.values(alertParams.jobSelection ?? {}) as string[][]).flat(), - [alertParams.jobSelection] + () => (Object.values(ruleParams.jobSelection ?? {}) as string[][]).flat(), + [ruleParams.jobSelection] ); /** @@ -102,9 +102,9 @@ const MlAnomalyAlertTrigger: FC = ({ ); useMount(function setDefaults() { - const { jobSelection, ...rest } = alertParams; + const { jobSelection, ...rest } = ruleParams; if (Object.keys(rest).length === 0) { - setAlertProperty('params', { + setRuleProperty('params', { // Set defaults severity: ANOMALY_THRESHOLD.CRITICAL, resultType: ANOMALY_RESULT_TYPE.BUCKET, @@ -118,7 +118,7 @@ const MlAnomalyAlertTrigger: FC = ({ }); const advancedSettings = useMemo(() => { - let { lookbackInterval, topNBuckets } = alertParams; + let { lookbackInterval, topNBuckets } = ruleParams; if (!isDefined(lookbackInterval) && jobConfigs.length > 0) { lookbackInterval = getLookbackInterval(jobConfigs); @@ -130,14 +130,14 @@ const MlAnomalyAlertTrigger: FC = ({ lookbackInterval, topNBuckets, }; - }, [alertParams.lookbackInterval, alertParams.topNBuckets, jobConfigs]); + }, [ruleParams.lookbackInterval, ruleParams.topNBuckets, jobConfigs]); const resultParams = useMemo(() => { return { - ...alertParams, + ...ruleParams, ...advancedSettings, }; - }, [alertParams, advancedSettings]); + }, [ruleParams, advancedSettings]); const maxNumberOfBuckets = useMemo(() => { if (jobConfigs.length === 0) return; @@ -170,24 +170,24 @@ const MlAnomalyAlertTrigger: FC = ({ @@ -196,14 +196,14 @@ const MlAnomalyAlertTrigger: FC = ({ value={advancedSettings} onChange={useCallback((update) => { Object.keys(update).forEach((k) => { - setAlertParams(k, update[k as keyof MlAnomalyDetectionAlertAdvancedSettings]); + setRuleParams(k, update[k as keyof MlAnomalyDetectionAlertAdvancedSettings]); }); }, [])} /> - + diff --git a/x-pack/plugins/ml/public/alerting/register_ml_alerts.ts b/x-pack/plugins/ml/public/alerting/register_ml_alerts.ts index 9dc68211093fc..82925bd1c3175 100644 --- a/x-pack/plugins/ml/public/alerting/register_ml_alerts.ts +++ b/x-pack/plugins/ml/public/alerting/register_ml_alerts.ts @@ -29,8 +29,8 @@ export function registerMlAlerts( documentationUrl(docLinks) { return docLinks.links.ml.alertingRules; }, - alertParamsExpression: lazy(() => import('./ml_anomaly_alert_trigger')), - validate: (alertParams: MlAnomalyDetectionAlertParams) => { + ruleParamsExpression: lazy(() => import('./ml_anomaly_alert_trigger')), + validate: (ruleParams: MlAnomalyDetectionAlertParams) => { const validationResult = { errors: { jobSelection: new Array(), @@ -41,10 +41,7 @@ export function registerMlAlerts( } as Record, }; - if ( - !alertParams.jobSelection?.jobIds?.length && - !alertParams.jobSelection?.groupIds?.length - ) { + if (!ruleParams.jobSelection?.jobIds?.length && !ruleParams.jobSelection?.groupIds?.length) { validationResult.errors.jobSelection.push( i18n.translate('xpack.ml.alertTypes.anomalyDetection.jobSelection.errorMessage', { defaultMessage: 'Job selection is required', @@ -54,10 +51,10 @@ export function registerMlAlerts( // Since 7.13 we support single job selection only if ( - (Array.isArray(alertParams.jobSelection?.groupIds) && - alertParams.jobSelection?.groupIds.length > 0) || - (Array.isArray(alertParams.jobSelection?.jobIds) && - alertParams.jobSelection?.jobIds.length > 1) + (Array.isArray(ruleParams.jobSelection?.groupIds) && + ruleParams.jobSelection?.groupIds.length > 0) || + (Array.isArray(ruleParams.jobSelection?.jobIds) && + ruleParams.jobSelection?.jobIds.length > 1) ) { validationResult.errors.jobSelection.push( i18n.translate('xpack.ml.alertTypes.anomalyDetection.singleJobSelection.errorMessage', { @@ -66,7 +63,7 @@ export function registerMlAlerts( ); } - if (alertParams.severity === undefined) { + if (ruleParams.severity === undefined) { validationResult.errors.severity.push( i18n.translate('xpack.ml.alertTypes.anomalyDetection.severity.errorMessage', { defaultMessage: 'Anomaly severity is required', @@ -74,7 +71,7 @@ export function registerMlAlerts( ); } - if (alertParams.resultType === undefined) { + if (ruleParams.resultType === undefined) { validationResult.errors.resultType.push( i18n.translate('xpack.ml.alertTypes.anomalyDetection.resultType.errorMessage', { defaultMessage: 'Result type is required', @@ -82,10 +79,7 @@ export function registerMlAlerts( ); } - if ( - !!alertParams.lookbackInterval && - validateLookbackInterval(alertParams.lookbackInterval) - ) { + if (!!ruleParams.lookbackInterval && validateLookbackInterval(ruleParams.lookbackInterval)) { validationResult.errors.lookbackInterval.push( i18n.translate('xpack.ml.alertTypes.anomalyDetection.lookbackInterval.errorMessage', { defaultMessage: 'Lookback interval is invalid', @@ -94,8 +88,8 @@ export function registerMlAlerts( } if ( - typeof alertParams.topNBuckets === 'number' && - validateTopNBucket(alertParams.topNBuckets) + typeof ruleParams.topNBuckets === 'number' && + validateTopNBucket(ruleParams.topNBuckets) ) { validationResult.errors.topNBuckets.push( i18n.translate('xpack.ml.alertTypes.anomalyDetection.topNBuckets.errorMessage', { diff --git a/x-pack/plugins/monitoring/public/alerts/alert_form.test.tsx b/x-pack/plugins/monitoring/public/alerts/alert_form.test.tsx index 0a13476d625fe..a95eedb2b4cb1 100644 --- a/x-pack/plugins/monitoring/public/alerts/alert_form.test.tsx +++ b/x-pack/plugins/monitoring/public/alerts/alert_form.test.tsx @@ -18,9 +18,10 @@ import { actionTypeRegistryMock } from '../../../triggers_actions_ui/public/appl import { ruleTypeRegistryMock } from '../../../triggers_actions_ui/public/application/rule_type_registry.mock'; import { ValidationResult, - Alert, + Rule, ConnectorValidationResult, GenericValidationResult, + RuleTypeModel, } from '../../../triggers_actions_ui/public/types'; import { AlertForm } from '../../../triggers_actions_ui/public/application/sections/alert_form/alert_form'; import ActionForm from '../../../triggers_actions_ui/public/application/sections/action_connector_form/action_form'; @@ -70,13 +71,13 @@ describe('alert_form', () => { jest.resetAllMocks(); }); - const alertType = { + const ruleType: RuleTypeModel = { id: 'alert-type', iconClass: 'test', description: 'Testing', documentationUrl: 'https://...', validate: validationMethod, - alertParamsExpression: () => , + ruleParamsExpression: () => , requiresAppContext: false, }; @@ -105,8 +106,8 @@ describe('alert_form', () => { let wrapper: ReactWrapper; beforeEach(async () => { - ruleTypeRegistry.list.mockReturnValue([alertType]); - ruleTypeRegistry.get.mockReturnValue(alertType); + ruleTypeRegistry.list.mockReturnValue([ruleType]); + ruleTypeRegistry.get.mockReturnValue(ruleType); ruleTypeRegistry.has.mockReturnValue(true); actionTypeRegistry.list.mockReturnValue([actionType]); actionTypeRegistry.has.mockReturnValue(true); @@ -116,7 +117,7 @@ describe('alert_form', () => { const initialAlert = { name: 'test', - alertTypeId: alertType.id, + alertTypeId: ruleType.id, params: {}, consumer: ALERTS_FEATURE_ID, schedule: { @@ -127,7 +128,7 @@ describe('alert_form', () => { muteAll: false, enabled: false, mutedInstanceIds: [], - } as unknown as Alert; + } as unknown as Rule; wrapper = mountWithIntl( @@ -199,7 +200,7 @@ describe('alert_form', () => { const initialAlert = { name: 'test', - alertTypeId: alertType.id, + alertTypeId: ruleType.id, params: {}, consumer: ALERTS_FEATURE_ID, schedule: { @@ -219,7 +220,7 @@ describe('alert_form', () => { muteAll: false, enabled: false, mutedInstanceIds: [], - } as unknown as Alert; + } as unknown as Rule; const KibanaReactContext = createKibanaReactContext(Legacy.shims.kibanaServices); diff --git a/x-pack/plugins/monitoring/public/alerts/ccr_read_exceptions_alert/index.tsx b/x-pack/plugins/monitoring/public/alerts/ccr_read_exceptions_alert/index.tsx index cd09185b8ad93..6f847ab8d693a 100644 --- a/x-pack/plugins/monitoring/public/alerts/ccr_read_exceptions_alert/index.tsx +++ b/x-pack/plugins/monitoring/public/alerts/ccr_read_exceptions_alert/index.tsx @@ -8,7 +8,7 @@ import { i18n } from '@kbn/i18n'; import React from 'react'; import type { AlertTypeParams } from '../../../../alerting/common'; -import type { AlertTypeModel, ValidationResult } from '../../../../triggers_actions_ui/public'; +import type { RuleTypeModel, ValidationResult } from '../../../../triggers_actions_ui/public'; import { RULE_CCR_READ_EXCEPTIONS, RULE_DETAILS, @@ -42,7 +42,7 @@ const validate = (inputValues: ValidateOptions): ValidationResult => { export function createCCRReadExceptionsAlertType( config: MonitoringConfig -): AlertTypeModel { +): RuleTypeModel { return { id: RULE_CCR_READ_EXCEPTIONS, description: RULE_DETAILS[RULE_CCR_READ_EXCEPTIONS].description, @@ -50,7 +50,7 @@ export function createCCRReadExceptionsAlertType( documentationUrl(docLinks) { return `${docLinks.links.monitoring.alertsKibanaCCRReadExceptions}`; }, - alertParamsExpression: (props: LazyExpressionProps) => ( + ruleParamsExpression: (props: LazyExpressionProps) => ( void; - setAlertProperty: (property: string, value: any) => void; + ruleParams: { [property: string]: any }; + setRuleParams: (property: string, value: any) => void; + setRuleProperty: (property: string, value: any) => void; errors: { [key: string]: string[] }; paramDetails: CommonAlertParamDetails; data: DataPublicPluginStart; @@ -34,13 +34,13 @@ export interface Props { } export const Expression: React.FC = (props) => { - const { alertParams, paramDetails, setAlertParams, errors, config, data } = props; + const { ruleParams, paramDetails, setRuleParams, errors, config, data } = props; const { derivedIndexPattern } = useDerivedIndexPattern(data, config); const alertParamsUi = Object.keys(paramDetails).map((alertParamName) => { const details = paramDetails[alertParamName]; - const value = alertParams[alertParamName]; + const value = ruleParams[alertParamName]; switch (details?.type) { case AlertParamType.Duration: @@ -51,7 +51,7 @@ export const Expression: React.FC = (props) => { duration={value} label={details?.label} errors={errors[alertParamName]} - setAlertParams={setAlertParams} + setRuleParams={setRuleParams} /> ); case AlertParamType.Percentage: @@ -62,7 +62,7 @@ export const Expression: React.FC = (props) => { label={details?.label} percentage={value} errors={errors[alertParamName]} - setAlertParams={setAlertParams} + setRuleParams={setRuleParams} /> ); case AlertParamType.Number: @@ -73,7 +73,7 @@ export const Expression: React.FC = (props) => { details={details} value={value} errors={errors[alertParamName]} - setAlertParams={setAlertParams} + setRuleParams={setRuleParams} /> ); case AlertParamType.TextField: @@ -84,7 +84,7 @@ export const Expression: React.FC = (props) => { label={details?.label} value={value} errors={errors[alertParamName]} - setAlertParams={setAlertParams} + setRuleParams={setRuleParams} /> ); } @@ -92,13 +92,13 @@ export const Expression: React.FC = (props) => { const onFilterChange = useCallback( (filter: string) => { - setAlertParams('filterQueryText', filter); - setAlertParams( + setRuleParams('filterQueryText', filter); + setRuleParams( 'filterQuery', convertKueryToElasticSearchQuery(filter, derivedIndexPattern) || '' ); }, - [setAlertParams, derivedIndexPattern] + [setRuleParams, derivedIndexPattern] ); /* eslint-disable-next-line react-hooks/exhaustive-deps */ @@ -120,7 +120,7 @@ export const Expression: React.FC = (props) => { })} > { +): RuleTypeModel { return { id: RULE_CPU_USAGE, description: RULE_DETAILS[RULE_CPU_USAGE].description, @@ -25,7 +25,7 @@ export function createCpuUsageAlertType( documentationUrl(docLinks) { return `${docLinks.links.monitoring.alertsKibanaCpuThreshold}`; }, - alertParamsExpression: (props: LazyExpressionProps) => ( + ruleParamsExpression: (props: LazyExpressionProps) => ( { +): RuleTypeModel { return { id: RULE_DISK_USAGE, description: RULE_DETAILS[RULE_DISK_USAGE].description, @@ -29,7 +29,7 @@ export function createDiskUsageAlertType( documentationUrl(docLinks) { return `${docLinks.links.monitoring.alertsKibanaDiskThreshold}`; }, - alertParamsExpression: (props: LazyExpressionProps) => ( + ruleParamsExpression: (props: LazyExpressionProps) => ( void; + setRuleParams: (property: string, value: any) => void; } const parseRegex = /(\d+)([smhd]{1})/; export const AlertParamDuration: React.FC = (props: Props) => { - const { name, label, setAlertParams, errors } = props; + const { name, label, setRuleParams, errors } = props; const parsed = parseRegex.exec(props.duration); const defaultValue = parsed && parsed[1] ? parseInt(parsed[1], 10) : 1; const defaultUnit = parsed && parsed[2] ? parsed[2] : TIME_UNITS.MINUTE; @@ -66,7 +66,7 @@ export const AlertParamDuration: React.FC = (props: Props) => { })); React.useEffect(() => { - setAlertParams(name, `${value}${unit}`); + setRuleParams(name, `${value}${unit}`); // eslint-disable-next-line react-hooks/exhaustive-deps }, [unit, value]); diff --git a/x-pack/plugins/monitoring/public/alerts/flyout_expressions/alert_param_number.tsx b/x-pack/plugins/monitoring/public/alerts/flyout_expressions/alert_param_number.tsx index d49f23f477c43..fe41a3a77597f 100644 --- a/x-pack/plugins/monitoring/public/alerts/flyout_expressions/alert_param_number.tsx +++ b/x-pack/plugins/monitoring/public/alerts/flyout_expressions/alert_param_number.tsx @@ -13,10 +13,10 @@ interface Props { value: number; details: { [key: string]: unknown }; errors: string[]; - setAlertParams: (property: string, value: number) => void; + setRuleParams: (property: string, value: number) => void; } export const AlertParamNumber: React.FC = (props: Props) => { - const { name, details, setAlertParams, errors } = props; + const { name, details, setRuleParams, errors } = props; const [value, setValue] = useState(props.value); return ( 0}> @@ -30,7 +30,7 @@ export const AlertParamNumber: React.FC = (props: Props) => { newValue = 0; } setValue(newValue); - setAlertParams(name, newValue); + setRuleParams(name, newValue); }} /> diff --git a/x-pack/plugins/monitoring/public/alerts/flyout_expressions/alert_param_percentage.tsx b/x-pack/plugins/monitoring/public/alerts/flyout_expressions/alert_param_percentage.tsx index 3bd7bc54d03d0..cdcc43eb60cd0 100644 --- a/x-pack/plugins/monitoring/public/alerts/flyout_expressions/alert_param_percentage.tsx +++ b/x-pack/plugins/monitoring/public/alerts/flyout_expressions/alert_param_percentage.tsx @@ -13,10 +13,10 @@ interface Props { percentage: number; label: string; errors: string[]; - setAlertParams: (property: string, value: any) => void; + setRuleParams: (property: string, value: any) => void; } export const AlertParamPercentage: React.FC = (props: Props) => { - const { name, label, setAlertParams, errors } = props; + const { name, label, setRuleParams, errors } = props; const [value, setValue] = React.useState(props.percentage); return ( @@ -35,7 +35,7 @@ export const AlertParamPercentage: React.FC = (props: Props) => { newValue = 0; } setValue(newValue); - setAlertParams(name, newValue); + setRuleParams(name, newValue); }} /> diff --git a/x-pack/plugins/monitoring/public/alerts/flyout_expressions/alert_param_textfield.tsx b/x-pack/plugins/monitoring/public/alerts/flyout_expressions/alert_param_textfield.tsx index 8f7c9e4b6e93b..cff4507b0b77d 100644 --- a/x-pack/plugins/monitoring/public/alerts/flyout_expressions/alert_param_textfield.tsx +++ b/x-pack/plugins/monitoring/public/alerts/flyout_expressions/alert_param_textfield.tsx @@ -14,10 +14,10 @@ interface Props { placeholder?: string; label: string; errors: string[]; - setAlertParams: (property: string, value: string) => void; + setRuleParams: (property: string, value: string) => void; } export const AlertParamTextField: React.FC = (props: Props) => { - const { name, label, setAlertParams, errors, placeholder } = props; + const { name, label, setRuleParams, errors, placeholder } = props; const [value, setValue] = useState(props.value); return ( 0}> @@ -28,7 +28,7 @@ export const AlertParamTextField: React.FC = (props: Props) => { onChange={(e) => { const newValue = e.target.value; setValue(newValue); - setAlertParams(name, newValue); + setRuleParams(name, newValue); }} /> diff --git a/x-pack/plugins/monitoring/public/alerts/large_shard_size_alert/index.tsx b/x-pack/plugins/monitoring/public/alerts/large_shard_size_alert/index.tsx index 48d87158209d0..c80b65452ddb0 100644 --- a/x-pack/plugins/monitoring/public/alerts/large_shard_size_alert/index.tsx +++ b/x-pack/plugins/monitoring/public/alerts/large_shard_size_alert/index.tsx @@ -8,7 +8,7 @@ import { i18n } from '@kbn/i18n'; import React from 'react'; import type { AlertTypeParams } from '../../../../alerting/common'; -import type { AlertTypeModel, ValidationResult } from '../../../../triggers_actions_ui/public'; +import type { RuleTypeModel, ValidationResult } from '../../../../triggers_actions_ui/public'; import { RULE_DETAILS, RULE_LARGE_SHARD_SIZE, @@ -42,7 +42,7 @@ const validate = (inputValues: ValidateOptions): ValidationResult => { export function createLargeShardSizeAlertType( config: MonitoringConfig -): AlertTypeModel { +): RuleTypeModel { return { id: RULE_LARGE_SHARD_SIZE, description: RULE_DETAILS[RULE_LARGE_SHARD_SIZE].description, @@ -50,7 +50,7 @@ export function createLargeShardSizeAlertType( documentationUrl(docLinks) { return `${docLinks.links.monitoring.alertsKibanaLargeShardSize}`; }, - alertParamsExpression: (props: LazyExpressionProps) => ( + ruleParamsExpression: (props: LazyExpressionProps) => ( { +export const Expression = ({ ruleParams, config, setRuleParams, data }: Props) => { const { derivedIndexPattern } = useDerivedIndexPattern(data, config); const onFilterChange = useCallback( (filter: string) => { - setAlertParams('filterQueryText', filter); - setAlertParams( + setRuleParams('filterQueryText', filter); + setRuleParams( 'filterQuery', convertKueryToElasticSearchQuery(filter, derivedIndexPattern) || '' ); }, - [setAlertParams, derivedIndexPattern] + [setRuleParams, derivedIndexPattern] ); /* eslint-disable-next-line react-hooks/exhaustive-deps */ @@ -44,7 +44,7 @@ export const Expression = ({ alertParams, config, setAlertParams, data }: Props) })} > { return { id: legacyAlert, @@ -24,7 +24,7 @@ export function createLegacyAlertTypes(config: MonitoringConfig): AlertTypeModel documentationUrl(docLinks) { return `${docLinks.links.monitoring.alertsKibanaClusterAlerts}`; }, - alertParamsExpression: (props: LazyExpressionProps) => ( + ruleParamsExpression: (props: LazyExpressionProps) => ( ), defaultActionMessage: '{{context.internalFullMessage}}', diff --git a/x-pack/plugins/monitoring/public/alerts/memory_usage_alert/index.tsx b/x-pack/plugins/monitoring/public/alerts/memory_usage_alert/index.tsx index 18193fca860fa..c4aa9e13b5786 100644 --- a/x-pack/plugins/monitoring/public/alerts/memory_usage_alert/index.tsx +++ b/x-pack/plugins/monitoring/public/alerts/memory_usage_alert/index.tsx @@ -6,7 +6,7 @@ */ import React from 'react'; -import type { AlertTypeModel } from '../../../../triggers_actions_ui/public'; +import type { RuleTypeModel } from '../../../../triggers_actions_ui/public'; import { RULE_DETAILS, RULE_MEMORY_USAGE, @@ -21,7 +21,7 @@ import { MonitoringAlertTypeParams, validate } from '../components/param_details export function createMemoryUsageAlertType( config: MonitoringConfig -): AlertTypeModel { +): RuleTypeModel { return { id: RULE_MEMORY_USAGE, description: RULE_DETAILS[RULE_MEMORY_USAGE].description, @@ -29,7 +29,7 @@ export function createMemoryUsageAlertType( documentationUrl(docLinks) { return `${docLinks.links.monitoring.alertsKibanaJvmThreshold}`; }, - alertParamsExpression: (props: LazyExpressionProps) => ( + ruleParamsExpression: (props: LazyExpressionProps) => ( void; - setAlertProperty: (property: string, value: any) => void; + ruleParams: { [property: string]: any }; + setRuleParams: (property: string, value: any) => void; + setRuleProperty: (property: string, value: any) => void; errors: { [key: string]: string[] }; paramDetails: CommonAlertParamDetails; } export const Expression: React.FC = (props) => { - const { alertParams, paramDetails, setAlertParams, errors } = props; + const { ruleParams, paramDetails, setRuleParams, errors } = props; - const alertParamsUi = Object.keys(alertParams).map((alertParamName) => { + const alertParamsUi = Object.keys(ruleParams).map((alertParamName) => { const details = paramDetails[alertParamName]; - const value = alertParams[alertParamName]; + const value = ruleParams[alertParamName]; switch (details?.type) { case AlertParamType.Duration: @@ -36,7 +36,7 @@ export const Expression: React.FC = (props) => { duration={value} label={details.label} errors={errors[alertParamName]} - setAlertParams={setAlertParams} + setRuleParams={setRuleParams} /> ); case AlertParamType.Percentage: @@ -47,7 +47,7 @@ export const Expression: React.FC = (props) => { label={details.label} percentage={value} errors={errors[alertParamName]} - setAlertParams={setAlertParams} + setRuleParams={setRuleParams} /> ); } diff --git a/x-pack/plugins/monitoring/public/alerts/missing_monitoring_data_alert/missing_monitoring_data_alert.tsx b/x-pack/plugins/monitoring/public/alerts/missing_monitoring_data_alert/missing_monitoring_data_alert.tsx index 4d19a2c3865c3..975e537d868b8 100644 --- a/x-pack/plugins/monitoring/public/alerts/missing_monitoring_data_alert/missing_monitoring_data_alert.tsx +++ b/x-pack/plugins/monitoring/public/alerts/missing_monitoring_data_alert/missing_monitoring_data_alert.tsx @@ -6,7 +6,7 @@ */ import React from 'react'; -import type { AlertTypeModel } from '../../../../triggers_actions_ui/public'; +import type { RuleTypeModel } from '../../../../triggers_actions_ui/public'; import { RULE_DETAILS, RULE_MISSING_MONITORING_DATA, @@ -15,7 +15,7 @@ import { import { LazyExpression, LazyExpressionProps } from './lazy_expression'; import { validate } from './validation'; -export function createMissingMonitoringDataAlertType(): AlertTypeModel { +export function createMissingMonitoringDataAlertType(): RuleTypeModel { return { id: RULE_MISSING_MONITORING_DATA, description: RULE_DETAILS[RULE_MISSING_MONITORING_DATA].description, @@ -23,7 +23,7 @@ export function createMissingMonitoringDataAlertType(): AlertTypeModel { documentationUrl(docLinks) { return `${docLinks.links.monitoring.alertsKibanaMissingData}`; }, - alertParamsExpression: (props: LazyExpressionProps) => ( + ruleParamsExpression: (props: LazyExpressionProps) => ( ( + ruleParamsExpression: (props: LazyExpressionProps) => ( <> ; - ruleTypeRegistry: TypeRegistry; + ruleTypeRegistry: TypeRegistry; uiSettings: IUiSettingsClient; http: HttpSetup; kfetch: ( diff --git a/x-pack/plugins/observability/public/rules/create_observability_rule_type_registry.ts b/x-pack/plugins/observability/public/rules/create_observability_rule_type_registry.ts index 7dd3b3fb39110..07d18eb8083d0 100644 --- a/x-pack/plugins/observability/public/rules/create_observability_rule_type_registry.ts +++ b/x-pack/plugins/observability/public/rules/create_observability_rule_type_registry.ts @@ -6,8 +6,8 @@ */ import { - AlertTypeModel, - AlertTypeParams, + RuleTypeModel, + RuleTypeParams, RuleTypeRegistryContract, } from '../../../triggers_actions_ui/public'; import { ParsedTechnicalFields } from '../../../rule_registry/common/parse_technical_fields'; @@ -18,8 +18,8 @@ export type ObservabilityRuleTypeFormatter = (options: { formatters: { asDuration: AsDuration; asPercent: AsPercent }; }) => { reason: string; link: string }; -export interface ObservabilityRuleTypeModel - extends AlertTypeModel { +export interface ObservabilityRuleTypeModel + extends RuleTypeModel { format: ObservabilityRuleTypeFormatter; } diff --git a/x-pack/plugins/stack_alerts/README.md b/x-pack/plugins/stack_alerts/README.md index 952ec15b70d2e..da97af05790ef 100644 --- a/x-pack/plugins/stack_alerts/README.md +++ b/x-pack/plugins/stack_alerts/README.md @@ -18,6 +18,6 @@ export interface IService { } ``` -Each Stack AlertType is described in it's own README: +Each Stack RuleType is described in it's own README: - index threshold: [`server/alert_types/index_threshold`](server/alert_types/index_threshold/README.md) diff --git a/x-pack/plugins/stack_alerts/public/alert_types/es_query/expression.test.tsx b/x-pack/plugins/stack_alerts/public/alert_types/es_query/expression.test.tsx index ad624c345272d..994eeadd50d3b 100644 --- a/x-pack/plugins/stack_alerts/public/alert_types/es_query/expression.test.tsx +++ b/x-pack/plugins/stack_alerts/public/alert_types/es_query/expression.test.tsx @@ -141,12 +141,12 @@ describe('EsQueryAlertTypeExpression', () => { const wrapper = mountWithIntl( {}} - setAlertProperty={() => {}} + ruleParams={alertParams} + setRuleParams={() => {}} + setRuleProperty={() => {}} errors={errors} data={dataMock} defaultActionGroupId="" diff --git a/x-pack/plugins/stack_alerts/public/alert_types/es_query/expression.tsx b/x-pack/plugins/stack_alerts/public/alert_types/es_query/expression.tsx index 8963007b8b764..1f8e2dc435789 100644 --- a/x-pack/plugins/stack_alerts/public/alert_types/es_query/expression.tsx +++ b/x-pack/plugins/stack_alerts/public/alert_types/es_query/expression.tsx @@ -32,7 +32,7 @@ import { ThresholdExpression, ForLastExpression, ValueExpression, - AlertTypeParamsExpressionProps, + RuleTypeParamsExpressionProps, } from '../../../../triggers_actions_ui/public'; import { validateExpression } from './validation'; import { parseDuration } from '../../../../alerting/common'; @@ -76,8 +76,8 @@ interface KibanaDeps { } export const EsQueryAlertTypeExpression: React.FunctionComponent< - AlertTypeParamsExpressionProps -> = ({ alertParams, setAlertParams, setAlertProperty, errors, data }) => { + RuleTypeParamsExpressionProps +> = ({ ruleParams, setRuleParams, setRuleProperty, errors, data }) => { const { index, timeField, @@ -87,10 +87,10 @@ export const EsQueryAlertTypeExpression: React.FunctionComponent< threshold, timeWindowSize, timeWindowUnit, - } = alertParams; + } = ruleParams; const getDefaultParams = () => ({ - ...alertParams, + ...ruleParams, esQuery: esQuery ?? DEFAULT_VALUES.QUERY, size: size ?? DEFAULT_VALUES.SIZE, timeWindowSize: timeWindowSize ?? DEFAULT_VALUES.TIME_WINDOW_SIZE, @@ -121,7 +121,7 @@ export const EsQueryAlertTypeExpression: React.FunctionComponent< (errorKey) => expressionFieldsWithValidation.includes(errorKey) && errors[errorKey].length >= 1 && - alertParams[errorKey as keyof EsQueryAlertParams] !== undefined + ruleParams[errorKey as keyof EsQueryAlertParams] !== undefined ); const expressionErrorMessage = i18n.translate( @@ -132,7 +132,7 @@ export const EsQueryAlertTypeExpression: React.FunctionComponent< ); const setDefaultExpressionValues = async () => { - setAlertProperty('params', getDefaultParams()); + setRuleProperty('params', getDefaultParams()); setXJson(esQuery ?? DEFAULT_VALUES.QUERY); @@ -146,7 +146,7 @@ export const EsQueryAlertTypeExpression: React.FunctionComponent< ...currentAlertParams, [paramField]: paramValue, }); - setAlertParams(paramField, paramValue); + setRuleParams(paramField, paramValue); }; useEffect(() => { @@ -240,8 +240,8 @@ export const EsQueryAlertTypeExpression: React.FunctionComponent< // reset expression fields if indices are deleted if (indices.length === 0) { - setAlertProperty('params', { - ...alertParams, + setRuleProperty('params', { + ...ruleParams, index: indices, esQuery: DEFAULT_VALUES.QUERY, size: DEFAULT_VALUES.SIZE, diff --git a/x-pack/plugins/stack_alerts/public/alert_types/es_query/index.ts b/x-pack/plugins/stack_alerts/public/alert_types/es_query/index.ts index 43f36c3ea44db..b6cb8406dbb0a 100644 --- a/x-pack/plugins/stack_alerts/public/alert_types/es_query/index.ts +++ b/x-pack/plugins/stack_alerts/public/alert_types/es_query/index.ts @@ -9,9 +9,9 @@ import { lazy } from 'react'; import { i18n } from '@kbn/i18n'; import { validateExpression } from './validation'; import { EsQueryAlertParams } from './types'; -import { AlertTypeModel } from '../../../../triggers_actions_ui/public'; +import { RuleTypeModel } from '../../../../triggers_actions_ui/public'; -export function getAlertType(): AlertTypeModel { +export function getAlertType(): RuleTypeModel { return { id: '.es-query', description: i18n.translate('xpack.stackAlerts.esQuery.ui.alertType.descriptionText', { @@ -19,7 +19,7 @@ export function getAlertType(): AlertTypeModel { }), iconClass: 'logoElastic', documentationUrl: (docLinks) => docLinks.links.alerting.esQuery, - alertParamsExpression: lazy(() => import('./expression')), + ruleParamsExpression: lazy(() => import('./expression')), validate: validateExpression, defaultActionMessage: i18n.translate( 'xpack.stackAlerts.esQuery.ui.alertType.defaultActionMessage', diff --git a/x-pack/plugins/stack_alerts/public/alert_types/geo_containment/index.ts b/x-pack/plugins/stack_alerts/public/alert_types/geo_containment/index.ts index 49b14f4fc4d79..19e58d8d1a22f 100644 --- a/x-pack/plugins/stack_alerts/public/alert_types/geo_containment/index.ts +++ b/x-pack/plugins/stack_alerts/public/alert_types/geo_containment/index.ts @@ -9,9 +9,9 @@ import { lazy } from 'react'; import { i18n } from '@kbn/i18n'; import { validateExpression } from './validation'; import { GeoContainmentAlertParams } from './types'; -import { AlertTypeModel } from '../../../../triggers_actions_ui/public'; +import { RuleTypeModel } from '../../../../triggers_actions_ui/public'; -export function getAlertType(): AlertTypeModel { +export function getAlertType(): RuleTypeModel { return { id: '.geo-containment', description: i18n.translate('xpack.stackAlerts.geoContainment.descriptionText', { @@ -19,7 +19,7 @@ export function getAlertType(): AlertTypeModel { }), iconClass: 'globe', documentationUrl: null, - alertParamsExpression: lazy(() => import('./query_builder')), + ruleParamsExpression: lazy(() => import('./query_builder')), validate: validateExpression, requiresAppContext: false, }; diff --git a/x-pack/plugins/stack_alerts/public/alert_types/geo_containment/query_builder/expressions/boundary_index_expression.tsx b/x-pack/plugins/stack_alerts/public/alert_types/geo_containment/query_builder/expressions/boundary_index_expression.tsx index 04de40da1f59c..e44e0b571bba6 100644 --- a/x-pack/plugins/stack_alerts/public/alert_types/geo_containment/query_builder/expressions/boundary_index_expression.tsx +++ b/x-pack/plugins/stack_alerts/public/alert_types/geo_containment/query_builder/expressions/boundary_index_expression.tsx @@ -20,7 +20,7 @@ import { IFieldType } from '../../../../../../../../src/plugins/data/common'; import { IIndexPattern } from '../../../../../../../../src/plugins/data/common'; interface Props { - alertParams: GeoContainmentAlertParams; + ruleParams: GeoContainmentAlertParams; errors: IErrorObject; boundaryIndexPattern: IIndexPattern; boundaryNameField?: string; @@ -35,7 +35,7 @@ interface KibanaDeps { } export const BoundaryIndexExpression: FunctionComponent = ({ - alertParams, + ruleParams, errors, boundaryIndexPattern, boundaryNameField, @@ -48,7 +48,7 @@ export const BoundaryIndexExpression: FunctionComponent = ({ const BOUNDARY_NAME_ENTITY_TYPES = ['string', 'number', 'ip']; const { http } = useKibana().services; const IndexPatternSelect = (data.ui && data.ui.IndexPatternSelect) || null; - const { boundaryGeoField } = alertParams; + const { boundaryGeoField } = ruleParams; // eslint-disable-next-line react-hooks/exhaustive-deps const nothingSelected: IFieldType = { name: '', diff --git a/x-pack/plugins/stack_alerts/public/alert_types/geo_containment/query_builder/expressions/entity_index_expression.tsx b/x-pack/plugins/stack_alerts/public/alert_types/geo_containment/query_builder/expressions/entity_index_expression.tsx index 3b5d03a3e9c39..0db7c03d1259e 100644 --- a/x-pack/plugins/stack_alerts/public/alert_types/geo_containment/query_builder/expressions/entity_index_expression.tsx +++ b/x-pack/plugins/stack_alerts/public/alert_types/geo_containment/query_builder/expressions/entity_index_expression.tsx @@ -14,7 +14,7 @@ import { HttpSetup } from 'kibana/public'; import { useKibana } from '../../../../../../../../src/plugins/kibana_react/public'; import { IErrorObject, - AlertTypeParamsExpressionProps, + RuleTypeParamsExpressionProps, } from '../../../../../../triggers_actions_ui/public'; import { ES_GEO_FIELD_TYPES } from '../../types'; import { GeoIndexPatternSelect } from '../util_components/geo_index_pattern_select'; @@ -29,7 +29,7 @@ interface Props { errors: IErrorObject; setAlertParamsDate: (date: string) => void; setAlertParamsGeoField: (geoField: string) => void; - setAlertProperty: AlertTypeParamsExpressionProps['setAlertProperty']; + setRuleProperty: RuleTypeParamsExpressionProps['setRuleProperty']; setIndexPattern: (indexPattern: IIndexPattern) => void; indexPattern: IIndexPattern; isInvalid: boolean; diff --git a/x-pack/plugins/stack_alerts/public/alert_types/geo_containment/query_builder/geo_containment_alert_type_expression.test.tsx b/x-pack/plugins/stack_alerts/public/alert_types/geo_containment/query_builder/geo_containment_alert_type_expression.test.tsx index f199d2132f995..8f02c0e202176 100644 --- a/x-pack/plugins/stack_alerts/public/alert_types/geo_containment/query_builder/geo_containment_alert_type_expression.test.tsx +++ b/x-pack/plugins/stack_alerts/public/alert_types/geo_containment/query_builder/geo_containment_alert_type_expression.test.tsx @@ -35,7 +35,7 @@ test('should render EntityIndexExpression', async () => { errors={{} as IErrorObject} setAlertParamsDate={() => {}} setAlertParamsGeoField={() => {}} - setAlertProperty={() => {}} + setRuleProperty={() => {}} setIndexPattern={() => {}} indexPattern={'' as unknown as IIndexPattern} isInvalid={false} @@ -54,7 +54,7 @@ test('should render EntityIndexExpression w/ invalid flag if invalid', async () errors={{} as IErrorObject} setAlertParamsDate={() => {}} setAlertParamsGeoField={() => {}} - setAlertProperty={() => {}} + setRuleProperty={() => {}} setIndexPattern={() => {}} indexPattern={'' as unknown as IIndexPattern} isInvalid={true} @@ -68,7 +68,7 @@ test('should render EntityIndexExpression w/ invalid flag if invalid', async () test('should render BoundaryIndexExpression', async () => { const component = shallow( {}} diff --git a/x-pack/plugins/stack_alerts/public/alert_types/geo_containment/query_builder/index.tsx b/x-pack/plugins/stack_alerts/public/alert_types/geo_containment/query_builder/index.tsx index 3440914e13b2a..00ae177ca9cc7 100644 --- a/x-pack/plugins/stack_alerts/public/alert_types/geo_containment/query_builder/index.tsx +++ b/x-pack/plugins/stack_alerts/public/alert_types/geo_containment/query_builder/index.tsx @@ -9,7 +9,7 @@ import React, { Fragment, useEffect, useState } from 'react'; import { EuiCallOut, EuiFlexItem, EuiSpacer, EuiTitle } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; import { i18n } from '@kbn/i18n'; -import { AlertTypeParamsExpressionProps } from '../../../../../triggers_actions_ui/public'; +import { RuleTypeParamsExpressionProps } from '../../../../../triggers_actions_ui/public'; import { GeoContainmentAlertParams } from '../types'; import { EntityIndexExpression } from './expressions/entity_index_expression'; import { EntityByExpression } from './expressions/entity_by_expression'; @@ -50,8 +50,8 @@ function validateQuery(query: Query) { } export const GeoContainmentAlertTypeExpression: React.FunctionComponent< - AlertTypeParamsExpressionProps -> = ({ alertParams, alertInterval, setAlertParams, setAlertProperty, errors, data }) => { + RuleTypeParamsExpressionProps +> = ({ ruleParams, ruleInterval, setRuleParams, setRuleProperty, errors, data }) => { const { index, indexId, @@ -65,7 +65,7 @@ export const GeoContainmentAlertTypeExpression: React.FunctionComponent< boundaryIndexQuery, boundaryGeoField, boundaryNameField, - } = alertParams; + } = ruleParams; const [indexPattern, _setIndexPattern] = useState({ id: '', @@ -76,10 +76,10 @@ export const GeoContainmentAlertTypeExpression: React.FunctionComponent< if (_indexPattern) { _setIndexPattern(_indexPattern); if (_indexPattern.title) { - setAlertParams('index', _indexPattern.title); + setRuleParams('index', _indexPattern.title); } if (_indexPattern.id) { - setAlertParams('indexId', _indexPattern.id); + setRuleParams('indexId', _indexPattern.id); } } }; @@ -98,10 +98,10 @@ export const GeoContainmentAlertTypeExpression: React.FunctionComponent< if (_indexPattern) { _setBoundaryIndexPattern(_indexPattern); if (_indexPattern.title) { - setAlertParams('boundaryIndexTitle', _indexPattern.title); + setRuleParams('boundaryIndexTitle', _indexPattern.title); } if (_indexPattern.id) { - setAlertParams('boundaryIndexId', _indexPattern.id); + setRuleParams('boundaryIndexId', _indexPattern.id); } } }; @@ -122,8 +122,8 @@ export const GeoContainmentAlertTypeExpression: React.FunctionComponent< useEffect(() => { const initToDefaultParams = async () => { - setAlertProperty('params', { - ...alertParams, + setRuleProperty('params', { + ...ruleParams, index: index ?? DEFAULT_VALUES.INDEX, indexId: indexId ?? DEFAULT_VALUES.INDEX_ID, entity: entity ?? DEFAULT_VALUES.ENTITY, @@ -174,9 +174,9 @@ export const GeoContainmentAlertTypeExpression: React.FunctionComponent< dateField={dateField} geoField={geoField} errors={errors} - setAlertParamsDate={(_date) => setAlertParams('dateField', _date)} - setAlertParamsGeoField={(_geoField) => setAlertParams('geoField', _geoField)} - setAlertProperty={setAlertProperty} + setAlertParamsDate={(_date) => setRuleParams('dateField', _date)} + setAlertParamsGeoField={(_geoField) => setRuleParams('geoField', _geoField)} + setRuleProperty={setRuleProperty} setIndexPattern={setIndexPattern} indexPattern={indexPattern} isInvalid={!indexId || !dateField || !geoField} @@ -185,7 +185,7 @@ export const GeoContainmentAlertTypeExpression: React.FunctionComponent< setAlertParams('entity', entityName)} + setAlertParamsEntity={(entityName) => setRuleParams('entity', entityName)} indexFields={indexPattern.fields} isInvalid={indexId && dateField && geoField ? !entity : false} /> @@ -199,7 +199,7 @@ export const GeoContainmentAlertTypeExpression: React.FunctionComponent< onChange={(query) => { if (query.language) { if (validateQuery(query)) { - setAlertParams('indexQuery', query); + setRuleParams('indexQuery', query); } setIndexQueryInput(query); } @@ -217,17 +217,17 @@ export const GeoContainmentAlertTypeExpression: React.FunctionComponent< - _geoField && setAlertParams('boundaryGeoField', _geoField) + _geoField && setRuleParams('boundaryGeoField', _geoField) } setBoundaryNameField={(_boundaryNameField: string | undefined) => _boundaryNameField - ? setAlertParams('boundaryNameField', _boundaryNameField) - : setAlertParams('boundaryNameField', '') + ? setRuleParams('boundaryNameField', _boundaryNameField) + : setRuleParams('boundaryNameField', '') } boundaryNameField={boundaryNameField} data={data} @@ -242,7 +242,7 @@ export const GeoContainmentAlertTypeExpression: React.FunctionComponent< onChange={(query) => { if (query.language) { if (validateQuery(query)) { - setAlertParams('boundaryIndexQuery', query); + setRuleParams('boundaryIndexQuery', query); } setBoundaryIndexQueryInput(query); } diff --git a/x-pack/plugins/stack_alerts/public/alert_types/threshold/expression.test.tsx b/x-pack/plugins/stack_alerts/public/alert_types/threshold/expression.test.tsx index 28f0f3db19614..bf46df5eabfa7 100644 --- a/x-pack/plugins/stack_alerts/public/alert_types/threshold/expression.test.tsx +++ b/x-pack/plugins/stack_alerts/public/alert_types/threshold/expression.test.tsx @@ -82,17 +82,17 @@ describe('IndexThresholdAlertTypeExpression', () => { ...overrides, }; } - async function setup(alertParams: IndexThresholdAlertParams) { - const { errors } = validateExpression(alertParams); + async function setup(ruleParams: IndexThresholdAlertParams) { + const { errors } = validateExpression(ruleParams); const wrapper = mountWithIntl( {}} - setAlertProperty={() => {}} + ruleParams={ruleParams} + setRuleParams={() => {}} + setRuleProperty={() => {}} errors={errors} data={dataMock} defaultActionGroupId="" diff --git a/x-pack/plugins/stack_alerts/public/alert_types/threshold/expression.tsx b/x-pack/plugins/stack_alerts/public/alert_types/threshold/expression.tsx index 8ada4e2128fd8..d03f142bd2587 100644 --- a/x-pack/plugins/stack_alerts/public/alert_types/threshold/expression.tsx +++ b/x-pack/plugins/stack_alerts/public/alert_types/threshold/expression.tsx @@ -21,7 +21,7 @@ import { GroupByExpression, WhenExpression, builtInAggregationTypes, - AlertTypeParamsExpressionProps, + RuleTypeParamsExpressionProps, } from '../../../../triggers_actions_ui/public'; import { ThresholdVisualization } from './visualization'; import { IndexThresholdAlertParams } from './types'; @@ -64,8 +64,8 @@ function indexParamToArray(index: string | string[]): string[] { } export const IndexThresholdAlertTypeExpression: React.FunctionComponent< - AlertTypeParamsExpressionProps -> = ({ alertParams, alertInterval, setAlertParams, setAlertProperty, errors, charts, data }) => { + RuleTypeParamsExpressionProps +> = ({ ruleParams, ruleInterval, setRuleParams, setRuleProperty, errors, charts, data }) => { const { index, timeField, @@ -78,7 +78,7 @@ export const IndexThresholdAlertTypeExpression: React.FunctionComponent< threshold, timeWindowSize, timeWindowUnit, - } = alertParams; + } = ruleParams; const indexArray = indexParamToArray(index); const { http } = useKibana().services; @@ -97,7 +97,7 @@ export const IndexThresholdAlertTypeExpression: React.FunctionComponent< (errorKey) => expressionFieldsWithValidation.includes(errorKey) && errors[errorKey].length >= 1 && - alertParams[errorKey as keyof IndexThresholdAlertParams] !== undefined + ruleParams[errorKey as keyof IndexThresholdAlertParams] !== undefined ); const cannotShowVisualization = !!Object.keys(errors).find( @@ -112,8 +112,8 @@ export const IndexThresholdAlertTypeExpression: React.FunctionComponent< ); const setDefaultExpressionValues = async () => { - setAlertProperty('params', { - ...alertParams, + setRuleProperty('params', { + ...ruleParams, aggType: aggType ?? DEFAULT_VALUES.AGGREGATION_TYPE, termSize: termSize ?? DEFAULT_VALUES.TERM_SIZE, thresholdComparator: thresholdComparator ?? DEFAULT_VALUES.THRESHOLD_COMPARATOR, @@ -163,12 +163,12 @@ export const IndexThresholdAlertTypeExpression: React.FunctionComponent< timeField={timeField} errors={errors} onIndexChange={async (indices: string[]) => { - setAlertParams('index', indices); + setRuleParams('index', indices); // reset expression fields if indices are deleted if (indices.length === 0) { - setAlertProperty('params', { - ...alertParams, + setRuleProperty('params', { + ...ruleParams, index: indices, aggType: DEFAULT_VALUES.AGGREGATION_TYPE, termSize: DEFAULT_VALUES.TERM_SIZE, @@ -184,7 +184,7 @@ export const IndexThresholdAlertTypeExpression: React.FunctionComponent< } }} onTimeFieldChange={(updatedTimeField: string) => - setAlertParams('timeField', updatedTimeField) + setRuleParams('timeField', updatedTimeField) } /> - setAlertParams('aggType', selectedAggType) + setRuleParams('aggType', selectedAggType) } /> {aggType && builtInAggregationTypes[aggType].fieldRequired ? ( @@ -204,7 +204,7 @@ export const IndexThresholdAlertTypeExpression: React.FunctionComponent< errors={errors} display="fullWidth" onChangeSelectedAggField={(selectedAggField?: string) => - setAlertParams('aggField', selectedAggField) + setRuleParams('aggField', selectedAggField) } /> ) : null} @@ -216,13 +216,11 @@ export const IndexThresholdAlertTypeExpression: React.FunctionComponent< errors={errors} fields={esFields} display="fullWidth" - onChangeSelectedGroupBy={(selectedGroupBy) => setAlertParams('groupBy', selectedGroupBy)} + onChangeSelectedGroupBy={(selectedGroupBy) => setRuleParams('groupBy', selectedGroupBy)} onChangeSelectedTermField={(selectedTermField) => - setAlertParams('termField', selectedTermField) - } - onChangeSelectedTermSize={(selectedTermSize) => - setAlertParams('termSize', selectedTermSize) + setRuleParams('termField', selectedTermField) } + onChangeSelectedTermSize={(selectedTermSize) => setRuleParams('termSize', selectedTermSize)} /> @@ -242,10 +240,10 @@ export const IndexThresholdAlertTypeExpression: React.FunctionComponent< display="fullWidth" popupPosition={'upLeft'} onChangeSelectedThreshold={(selectedThresholds) => - setAlertParams('threshold', selectedThresholds) + setRuleParams('threshold', selectedThresholds) } onChangeSelectedThresholdComparator={(selectedThresholdComparator) => - setAlertParams('thresholdComparator', selectedThresholdComparator) + setRuleParams('thresholdComparator', selectedThresholdComparator) } /> - setAlertParams('timeWindowSize', selectedWindowSize) + setRuleParams('timeWindowSize', selectedWindowSize) } onChangeWindowUnit={(selectedWindowUnit: string) => - setAlertParams('timeWindowUnit', selectedWindowUnit) + setRuleParams('timeWindowUnit', selectedWindowUnit) } /> @@ -283,8 +281,8 @@ export const IndexThresholdAlertTypeExpression: React.FunctionComponent< { +export function getAlertType(): RuleTypeModel { return { id: '.index-threshold', description: i18n.translate('xpack.stackAlerts.threshold.ui.alertType.descriptionText', { @@ -19,7 +19,7 @@ export function getAlertType(): AlertTypeModel { }), iconClass: 'alert', documentationUrl: (docLinks) => docLinks.links.alerting.indexThreshold, - alertParamsExpression: lazy(() => import('./expression')), + ruleParamsExpression: lazy(() => import('./expression')), validate: validateExpression, defaultActionMessage: i18n.translate( 'xpack.stackAlerts.threshold.ui.alertType.defaultActionMessage', diff --git a/x-pack/plugins/stack_alerts/public/alert_types/threshold/visualization.test.tsx b/x-pack/plugins/stack_alerts/public/alert_types/threshold/visualization.test.tsx index fa34c25cd6154..a27646c1643fa 100644 --- a/x-pack/plugins/stack_alerts/public/alert_types/threshold/visualization.test.tsx +++ b/x-pack/plugins/stack_alerts/public/alert_types/threshold/visualization.test.tsx @@ -51,7 +51,7 @@ describe('ThresholdVisualization', () => { }); }); - const alertParams = { + const ruleParams = { index: 'test-index', aggType: 'count', thresholdComparator: '>', @@ -63,7 +63,7 @@ describe('ThresholdVisualization', () => { async function setup() { const wrapper = mountWithIntl( { const wrapper = mountWithIntl( { test('renders loading message on initial load', async () => { const wrapper = mountWithIntl( { }; interface Props { - alertParams: IndexThresholdAlertParams; + ruleParams: IndexThresholdAlertParams; alertInterval: string; aggregationTypes: { [key: string]: AggregationType }; comparators: { @@ -108,7 +108,7 @@ enum LoadingStateType { type MetricResult = [number, number]; // [epochMillis, value] export const ThresholdVisualization: React.FunctionComponent = ({ - alertParams, + ruleParams, alertInterval, aggregationTypes, comparators, @@ -128,7 +128,7 @@ export const ThresholdVisualization: React.FunctionComponent = ({ timeWindowUnit, groupBy, threshold, - } = alertParams; + } = ruleParams; const { http, uiSettings } = useKibana().services; const [loadingState, setLoadingState] = useState(null); const [hasError, setHasError] = useState(false); @@ -192,7 +192,7 @@ export const ThresholdVisualization: React.FunctionComponent = ({ }; // Fetching visualization data is independent of alert actions - const alertWithoutActions = { ...alertParams, actions: [], type: 'threshold' }; + const alertWithoutActions = { ...ruleParams, actions: [], type: 'threshold' }; if (loadingState === LoadingStateType.FirstLoad) { return ( diff --git a/x-pack/plugins/transform/public/alerting/transform_health_rule_type/register_transform_health_rule.ts b/x-pack/plugins/transform/public/alerting/transform_health_rule_type/register_transform_health_rule.ts index 83117966f73d1..779496da59501 100644 --- a/x-pack/plugins/transform/public/alerting/transform_health_rule_type/register_transform_health_rule.ts +++ b/x-pack/plugins/transform/public/alerting/transform_health_rule_type/register_transform_health_rule.ts @@ -9,9 +9,9 @@ import { lazy } from 'react'; import { i18n } from '@kbn/i18n'; import { TRANSFORM_RULE_TYPE } from '../../../common'; import type { TransformHealthRuleParams } from '../../../common/types/alerting'; -import type { AlertTypeModel } from '../../../../triggers_actions_ui/public'; +import type { RuleTypeModel } from '../../../../triggers_actions_ui/public'; -export function getTransformHealthRuleType(): AlertTypeModel { +export function getTransformHealthRuleType(): RuleTypeModel { return { id: TRANSFORM_RULE_TYPE.TRANSFORM_HEALTH, description: i18n.translate('xpack.transform.alertingRuleTypes.transformHealth.description', { @@ -21,15 +21,15 @@ export function getTransformHealthRuleType(): AlertTypeModel import('./transform_health_rule_trigger')), - validate: (alertParams: TransformHealthRuleParams) => { + ruleParamsExpression: lazy(() => import('./transform_health_rule_trigger')), + validate: (ruleParams: TransformHealthRuleParams) => { const validationResult = { errors: { includeTransforms: new Array(), } as Record, }; - if (!alertParams.includeTransforms?.length) { + if (!ruleParams.includeTransforms?.length) { validationResult.errors.includeTransforms?.push( i18n.translate( 'xpack.transform.alertTypes.transformHealth.includeTransforms.errorMessage', diff --git a/x-pack/plugins/transform/public/alerting/transform_health_rule_type/transform_health_rule_trigger.tsx b/x-pack/plugins/transform/public/alerting/transform_health_rule_type/transform_health_rule_trigger.tsx index 860ca3032964a..2bb857195a398 100644 --- a/x-pack/plugins/transform/public/alerting/transform_health_rule_type/transform_health_rule_trigger.tsx +++ b/x-pack/plugins/transform/public/alerting/transform_health_rule_type/transform_health_rule_trigger.tsx @@ -9,7 +9,7 @@ import { EuiForm, EuiSpacer } from '@elastic/eui'; import React, { FC, useCallback, useEffect, useMemo, useState } from 'react'; import { FormattedMessage } from '@kbn/i18n-react'; import { i18n } from '@kbn/i18n'; -import type { AlertTypeParamsExpressionProps } from '../../../../triggers_actions_ui/public'; +import type { RuleTypeParamsExpressionProps } from '../../../../triggers_actions_ui/public'; import type { TransformHealthRuleParams } from '../../../common/types/alerting'; import { TestsSelectionControl } from './tests_selection_control'; import { TransformSelectorControl } from './transform_selector_control'; @@ -19,11 +19,11 @@ import { GetTransformsResponseSchema } from '../../../common/api_schemas/transfo import { ALL_TRANSFORMS_SELECTION } from '../../../common/constants'; export type TransformHealthRuleTriggerProps = - AlertTypeParamsExpressionProps; + RuleTypeParamsExpressionProps; const TransformHealthRuleTrigger: FC = ({ - alertParams, - setAlertParams, + ruleParams, + setRuleParams, errors, }) => { const formErrors = Object.values(errors).flat(); @@ -36,9 +36,9 @@ const TransformHealthRuleTrigger: FC = ({ const onAlertParamChange = useCallback( (param: T) => (update: TransformHealthRuleParams[T]) => { - setAlertParams(param, update); + setRuleParams(param, update); }, - [setAlertParams] + [setRuleParams] ); useEffect( @@ -71,11 +71,11 @@ const TransformHealthRuleTrigger: FC = ({ ); const excludeTransformOptions = useMemo(() => { - if (alertParams.includeTransforms?.some((v) => v === ALL_TRANSFORMS_SELECTION)) { + if (ruleParams.includeTransforms?.some((v) => v === ALL_TRANSFORMS_SELECTION)) { return transformOptions; } return null; - }, [transformOptions, alertParams.includeTransforms]); + }, [transformOptions, ruleParams.includeTransforms]); return ( = ({ /> } options={transformOptions} - selectedOptions={alertParams.includeTransforms ?? []} + selectedOptions={ruleParams.includeTransforms ?? []} onChange={onAlertParamChange('includeTransforms')} allowSelectAll errors={errors.includeTransforms as string[]} @@ -100,7 +100,7 @@ const TransformHealthRuleTrigger: FC = ({ - {!!excludeTransformOptions?.length || !!alertParams.excludeTransforms?.length ? ( + {!!excludeTransformOptions?.length || !!ruleParams.excludeTransforms?.length ? ( <> = ({ /> } options={excludeTransformOptions ?? []} - selectedOptions={alertParams.excludeTransforms ?? []} + selectedOptions={ruleParams.excludeTransforms ?? []} onChange={onAlertParamChange('excludeTransforms')} /> @@ -118,7 +118,7 @@ const TransformHealthRuleTrigger: FC = ({ ) : null} diff --git a/x-pack/plugins/triggers_actions_ui/README.md b/x-pack/plugins/triggers_actions_ui/README.md index dacd26cdf7eeb..14bf6164979e7 100644 --- a/x-pack/plugins/triggers_actions_ui/README.md +++ b/x-pack/plugins/triggers_actions_ui/README.md @@ -62,38 +62,38 @@ ID: `threshold` In the Kibana UI, this alert type is available as a select card on the Create Alert flyout: ![Index Threshold select card](https://i.imgur.com/a0bqLwC.png) -AlertTypeModel: +RuleTypeModel: ``` -export function getAlertType(): AlertTypeModel { +export function getAlertType(): RuleTypeModel { return { id: '.index-threshold', name: 'Index threshold', iconClass: 'alert', - alertParamsExpression: lazy(() => import('./index_threshold_expression')), + ruleParamsExpression: lazy(() => import('./index_threshold_expression')), validate: validateAlertType, requiresAppContext: false, }; } ``` -alertParamsExpression should be a lazy loaded React component extending an expression using `EuiExpression` components: +ruleParamsExpression should be a lazy loaded React component extending an expression using `EuiExpression` components: ![Index Threshold Alert expression form](https://i.imgur.com/Ysk1ljY.png) ``` interface IndexThresholdProps { - alertParams: IndexThresholdAlertParams; - setAlertParams: (property: string, value: any) => void; - setAlertProperty: (key: string, value: any) => void; + ruleParams: IndexThresholdAlertParams; + setRuleParams: (property: string, value: any) => void; + setRuleProperty: (key: string, value: any) => void; errors: { [key: string]: string[] }; } ``` |Property|Description| |---|---| -|alertParams|Set of Alert params relevant for the index threshold alert type.| -|setAlertParams|Alert reducer method, which is used to create a new copy of alert object with the changed params property any subproperty value.| -|setAlertProperty|Alert reducer method, which is used to create a new copy of alert object with the changed any direct alert property value.| +|ruleParams|Set of Alert params relevant for the index threshold alert type.| +|setRuleParams|Alert reducer method, which is used to create a new copy of alert object with the changed params property any subproperty value.| +|setRuleProperty|Alert reducer method, which is used to create a new copy of alert object with the changed any direct alert property value.| |errors|Alert level errors tracking object.| @@ -104,12 +104,12 @@ const [{ alert }, dispatch] = useReducer(alertReducer, { alert: initialAlert }); ... -const setAlertProperty = (key: string, value: any) => { +const setRuleProperty = (key: string, value: any) => { dispatch({ command: { type: 'setProperty' }, payload: { key, value } }); }; - const setAlertParams = (key: string, value: any) => { - dispatch({ command: { type: 'setAlertParams' }, payload: { key, value } }); + const setRuleParams = (key: string, value: any) => { + dispatch({ command: { type: 'setRuleParams' }, payload: { key, value } }); }; const setScheduleProperty = (key: string, value: any) => { @@ -146,7 +146,7 @@ export const alertReducer = (state: any, action: AlertReducerAction) => { case 'setAlertActionParams': { // .... // create a new alert state with set new value to any subproperty for a 'params' alert property - case 'setAlertParams': { + case 'setRuleParams': { const { key, value } = payload; if (isEqual(alert.params[key], value)) { return state; @@ -175,9 +175,9 @@ The Expression component should be lazy loaded which means it'll have to be the ``` export const IndexThresholdAlertTypeExpression: React.FunctionComponent = ({ - alertParams, - setAlertParams, - setAlertProperty, + ruleParams, + setRuleParams, + setRuleProperty, errors, }) => { @@ -188,7 +188,7 @@ export const IndexThresholdAlertTypeExpression: React.FunctionComponent expressionFieldsWithValidation.includes(errorKey) && errors[errorKey].length >= 1 && - (alertParams as { [key: string]: any })[errorKey] !== undefined + (ruleParams as { [key: string]: any })[errorKey] !== undefined ); .... @@ -234,14 +234,14 @@ Index Threshold Alert form with validation: ## Alert type model definition -Each alert type should be defined as `AlertTypeModel` object with the these properties: +Each alert type should be defined as `RuleTypeModel` object with the these properties: ``` id: string; name: string; iconClass: string; - validate: (alertParams: any) => ValidationResult; - alertParamsExpression: React.LazyExoticComponent< - ComponentType> + validate: (ruleParams: any) => ValidationResult; + ruleParamsExpression: React.LazyExoticComponent< + ComponentType> >; defaultActionMessage?: string; ``` @@ -251,7 +251,7 @@ Each alert type should be defined as `AlertTypeModel` object with the these prop |name|Name of the alert type that will be displayed on the select card in the UI.| |iconClass|Icon of the alert type that will be displayed on the select card in the UI.| |validate|Validation function for the alert params.| -|alertParamsExpression| A lazy loaded React component for building UI of the current alert type params.| +|ruleParamsExpression| A lazy loaded React component for building UI of the current alert type params.| |defaultActionMessage|Optional property for providing default message for all added actions with `message` property.| |requiresAppContext|Define if alert type is enabled for create and edit in the alerting management UI.| @@ -259,7 +259,7 @@ IMPORTANT: The current UI supports a single action group only. Action groups are mapped from the server API result for [GET /api/alerts/list_alert_types: List alert types](https://github.com/elastic/kibana/tree/main/x-pack/plugins/alerting#get-apialerttypes-list-alert-types). Server side alert type model: ``` -export interface AlertType { +export interface RuleType { id: string; name: string; validate?: { @@ -286,7 +286,7 @@ It should be done by importing dependency `TriggersAndActionsUIPublicPluginSetup ``` function getSomeNewAlertType() { - return { ... } as AlertTypeModel; + return { ... } as RuleTypeModel; } triggersActionsUi.ruleTypeRegistry.register(getSomeNewAlertType()); @@ -296,31 +296,31 @@ triggersActionsUi.ruleTypeRegistry.register(getSomeNewAlertType()); Before registering a UI for a new Alert Type, you should first register the type on the server-side by following the Alerting guide: https://github.com/elastic/kibana/tree/main/x-pack/plugins/alerting#example -Alert type UI is expected to be defined as `AlertTypeModel` object. +Alert type UI is expected to be defined as `RuleTypeModel` object. Below is a list of steps that should be done to build and register a new alert type with the name `Example Alert Type`: -1. At any suitable place in Kibana, create a file, which will expose an object implementing interface [AlertTypeModel](https://github.com/elastic/kibana/blob/55b7905fb5265b73806006e7265739545d7521d0/x-pack/legacy/plugins/triggers_actions_ui/np_ready/public/types.ts#L83). Example: +1. At any suitable place in Kibana, create a file, which will expose an object implementing interface [RuleTypeModel](https://github.com/elastic/kibana/blob/55b7905fb5265b73806006e7265739545d7521d0/x-pack/legacy/plugins/triggers_actions_ui/np_ready/public/types.ts#L83). Example: ``` import { lazy } from 'react'; -import { AlertTypeModel } from '../../../../types'; +import { RuleTypeModel } from '../../../../types'; import { validateExampleAlertType } from './validation'; -export function getAlertType(): AlertTypeModel { +export function getAlertType(): RuleTypeModel { return { id: 'example', name: 'Example Alert Type', iconClass: 'bell', - alertParamsExpression: lazy(() => import('./expression')), + ruleParamsExpression: lazy(() => import('./expression')), validate: validateExampleAlertType, defaultActionMessage: 'Alert [{{ctx.metadata.name}}] has exceeded the threshold', requiresAppContext: false, }; } ``` -Fields of this object `AlertTypeModel` will be mapped properly in the UI below. +Fields of this object `RuleTypeModel` will be mapped properly in the UI below. -2. Define `alertParamsExpression` as `React.FunctionComponent` - this is the form for filling Alert params based on the current Alert type. +2. Define `ruleParamsExpression` as `React.FunctionComponent` - this is the form for filling Alert params based on the current Alert type. ``` import React, { useState } from 'react'; import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; @@ -377,7 +377,7 @@ This alert type form becomes available, when the card of `Example Alert Type` is Each expression word here is `EuiExpression` component and implements the basic aggregation, grouping and comparison methods. Expression components, which can be embedded to different alert types, are described here [Common expression components](#common-expression-components). -3. Define alert type params validation using the property of `AlertTypeModel` `validate`: +3. Define alert type params validation using the property of `RuleTypeModel` `validate`: ``` import { i18n } from '@kbn/i18n'; import { ValidationResult } from '../../../../types'; @@ -428,7 +428,7 @@ Click on the select card for `Example Alert Type` to open the expression form th - setAlertParams('aggType', selectedAggType) + setRuleParams('aggType', selectedAggType) } /> ``` @@ -464,7 +464,7 @@ OF expression is available, if aggregation type requires selecting data fields f aggType={aggType} errors={errors} onChangeSelectedAggField={(selectedAggField?: string) => - setAlertParams('aggField', selectedAggField) + setRuleParams('aggField', selectedAggField) } /> ``` @@ -506,12 +506,12 @@ interface OfExpressionProps { termSize={termSize} errors={errors} fields={esFields} - onChangeSelectedGroupBy={selectedGroupBy => setAlertParams('groupBy', selectedGroupBy)} + onChangeSelectedGroupBy={selectedGroupBy => setRuleParams('groupBy', selectedGroupBy)} onChangeSelectedTermField={selectedTermField => - setAlertParams('termField', selectedTermField) + setRuleParams('termField', selectedTermField) } onChangeSelectedTermSize={selectedTermSize => - setAlertParams('termSize', selectedTermSize) + setRuleParams('termSize', selectedTermSize) } /> ``` @@ -558,10 +558,10 @@ interface GroupByExpressionProps { timeWindowUnit={timeWindowUnit || ''} errors={errors} onChangeWindowSize={(selectedWindowSize: any) => - setAlertParams('timeWindowSize', selectedWindowSize) + setRuleParams('timeWindowSize', selectedWindowSize) } onChangeWindowUnit={(selectedWindowUnit: any) => - setAlertParams('timeWindowUnit', selectedWindowUnit) + setRuleParams('timeWindowUnit', selectedWindowUnit) } /> ``` @@ -598,10 +598,10 @@ interface ForLastExpressionProps { threshold={threshold} errors={errors} onChangeSelectedThreshold={selectedThresholds => - setAlertParams('threshold', selectedThresholds) + setRuleParams('threshold', selectedThresholds) } onChangeSelectedThresholdComparator={selectedThresholdComparator => - setAlertParams('thresholdComparator', selectedThresholdComparator) + setRuleParams('thresholdComparator', selectedThresholdComparator) } /> ``` @@ -675,7 +675,7 @@ const ThresholdSpecifier = ( ``` This component takes two props, one which is required (`actionGroup`) and one which is alert type specific (`setThreshold`). -The `actionGroup` will be populated by the `AlertConditions` component, but `setThreshold` will have to be provided by the AlertType itself. +The `actionGroup` will be populated by the `AlertConditions` component, but `setThreshold` will have to be provided by the RuleType itself. To understand how this is used, lets take a closer look at `actionGroup`: @@ -738,7 +738,7 @@ const DEFAULT_THRESHOLDS: ThresholdAlertTypeParams['threshold] = { }, ]} onInitializeConditionsFor={(actionGroup) => { - setAlertParams('thresholds', { + setRuleParams('thresholds', { ...thresholds, ...pick(DEFAULT_THRESHOLDS, actionGroup.id), }); @@ -746,12 +746,12 @@ const DEFAULT_THRESHOLDS: ThresholdAlertTypeParams['threshold] = { > { - setAlertParams('thresholds', omit(thresholds, actionGroup.id)); + setRuleParams('thresholds', omit(thresholds, actionGroup.id)); }} > { - setAlertParams('thresholds', { + setRuleParams('thresholds', { ...thresholds, [actionGroup.id]: actionGroup.conditions, }); @@ -1428,7 +1428,7 @@ Then this dependencies will be used to embed Actions form or register your own a setActionIdByIndex={(id: string, index: number) => { initialAlert.actions[index].id = id; }} - setAlertProperty={(_updatedActions: AlertAction[]) => {}} + setRuleProperty={(_updatedActions: AlertAction[]) => {}} setActionParamsProperty={(key: string, value: any, index: number) => (initialAlert.actions[index] = { ...initialAlert.actions[index], [key]: value }) } @@ -1451,7 +1451,7 @@ interface ActionAccordionFormProps { actionGroups?: ActionGroup[]; setActionIdByIndex: (id: string, index: number) => void; setActionGroupIdByIndex?: (group: string, index: number) => void; - setAlertProperty: (actions: AlertAction[]) => void; + setRuleProperty: (actions: AlertAction[]) => void; setActionParamsProperty: (key: string, value: any, index: number) => void; http: HttpSetup; actionTypeRegistry: ActionTypeRegistryContract; @@ -1472,7 +1472,7 @@ interface ActionAccordionFormProps { |actionGroups|Optional. List of action groups to which new action can be assigned. The RunWhen field is only displayed when these action groups are specified| |setActionIdByIndex|Function for changing action 'id' by the proper index in alert.actions array.| |setActionGroupIdByIndex|Function for changing action 'group' by the proper index in alert.actions array.| -|setAlertProperty|Function for changing alert property 'actions'. Used when deleting action from the array to reset it.| +|setRuleProperty|Function for changing alert property 'actions'. Used when deleting action from the array to reset it.| |setActionParamsProperty|Function for changing action key/value property by index in alert.actions array.| |http|HttpSetup needed for executing API calls.| |actionTypeRegistry|Registry for action types.| diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/action_variables.test.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/action_variables.test.ts index 1414242358d58..474fff65795a5 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/action_variables.test.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/action_variables.test.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { AlertType, ActionVariables } from '../../types'; +import { RuleType, ActionVariables } from '../../types'; import { transformActionVariables } from './action_variables'; import { ALERTS_FEATURE_ID } from '../../../../alerting/common'; @@ -572,7 +572,7 @@ describe('transformActionVariables', () => { }); }); -function getAlertType(actionVariables: ActionVariables): AlertType { +function getAlertType(actionVariables: ActionVariables): RuleType { return { id: 'test', name: 'Test', diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/common_transformations.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/common_transformations.ts index 6369937e59377..d380665658a81 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/common_transformations.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/common_transformations.ts @@ -6,7 +6,7 @@ */ import { AlertExecutionStatus } from '../../../../../alerting/common'; import { AsApiContract, RewriteRequestCase } from '../../../../../actions/common'; -import { Alert, AlertAction, ResolvedRule } from '../../../types'; +import { Rule, AlertAction, ResolvedRule } from '../../../types'; const transformAction: RewriteRequestCase = ({ group, @@ -30,7 +30,7 @@ const transformExecutionStatus: RewriteRequestCase = ({ ...rest, }); -export const transformAlert: RewriteRequestCase = ({ +export const transformAlert: RewriteRequestCase = ({ rule_type_id: alertTypeId, created_by: createdBy, updated_by: updatedBy, diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/create.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/create.ts index 36d2a17bcd4d5..d07d99eb8e8c8 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/create.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/create.ts @@ -6,7 +6,7 @@ */ import { HttpSetup } from 'kibana/public'; import { AsApiContract, RewriteResponseCase } from '../../../../../actions/common'; -import { Alert, AlertUpdates } from '../../../types'; +import { Rule, AlertUpdates } from '../../../types'; import { BASE_ALERTING_API_PATH } from '../../constants'; import { transformAlert } from './common_transformations'; @@ -36,8 +36,8 @@ export async function createAlert({ }: { http: HttpSetup; alert: AlertCreateBody; -}): Promise { - const res = await http.post>(`${BASE_ALERTING_API_PATH}/rule`, { +}): Promise { + const res = await http.post>(`${BASE_ALERTING_API_PATH}/rule`, { body: JSON.stringify(rewriteBodyRequest(alert)), }); return transformAlert(res); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/get_rule.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/get_rule.ts index fd4de0c3dae68..9fae26d80d506 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/get_rule.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/get_rule.ts @@ -6,7 +6,7 @@ */ import { HttpSetup } from 'kibana/public'; import { AsApiContract } from '../../../../../actions/common'; -import { Alert } from '../../../types'; +import { Rule } from '../../../types'; import { BASE_ALERTING_API_PATH } from '../../constants'; import { transformAlert } from './common_transformations'; @@ -16,8 +16,8 @@ export async function loadAlert({ }: { http: HttpSetup; alertId: string; -}): Promise { - const res = await http.get>( +}): Promise { + const res = await http.get>( `${BASE_ALERTING_API_PATH}/rule/${encodeURIComponent(alertId)}` ); return transformAlert(res); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/rule_types.test.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/rule_types.test.ts index 71513ed0c6e61..49f50858b6983 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/rule_types.test.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/rule_types.test.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { AlertType } from '../../../types'; +import { RuleType } from '../../../types'; import { httpServiceMock } from '../../../../../../../src/core/public/mocks'; import { loadAlertTypes } from './rule_types'; import { ALERTS_FEATURE_ID } from '../../../../../alerting/common'; @@ -14,7 +14,7 @@ const http = httpServiceMock.createStartContract(); describe('loadAlertTypes', () => { test('should call get alert types API', async () => { - const resolvedValue: AlertType[] = [ + const resolvedValue: RuleType[] = [ { id: 'test', name: 'Test', diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/rule_types.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/rule_types.ts index 530c158838c2b..99478f250f6a2 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/rule_types.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/rule_types.ts @@ -5,15 +5,15 @@ * 2.0. */ import { HttpSetup } from 'kibana/public'; -import { AlertType } from '../../../types'; +import { RuleType } from '../../../types'; import { BASE_ALERTING_API_PATH } from '../../constants'; import { AsApiContract, RewriteRequestCase } from '../../../../../actions/common'; -const rewriteResponseRes = (results: Array>): AlertType[] => { +const rewriteResponseRes = (results: Array>): RuleType[] => { return results.map((item) => rewriteBodyReq(item)); }; -const rewriteBodyReq: RewriteRequestCase = ({ +const rewriteBodyReq: RewriteRequestCase = ({ enabled_in_license: enabledInLicense, recovery_action_group: recoveryActionGroup, action_groups: actionGroups, @@ -23,7 +23,7 @@ const rewriteBodyReq: RewriteRequestCase = ({ authorized_consumers: authorizedConsumers, rule_task_timeout: ruleTaskTimeout, ...rest -}: AsApiContract) => ({ +}: AsApiContract) => ({ enabledInLicense, recoveryActionGroup, actionGroups, @@ -35,8 +35,8 @@ const rewriteBodyReq: RewriteRequestCase = ({ ...rest, }); -export async function loadAlertTypes({ http }: { http: HttpSetup }): Promise { - const res = await http.get>>>( +export async function loadAlertTypes({ http }: { http: HttpSetup }): Promise { + const res = await http.get>>>( `${BASE_ALERTING_API_PATH}/rule_types` ); return rewriteResponseRes(res); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/rules.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/rules.ts index 3475cbb04408e..5a0cce8c8bf1a 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/rules.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/rules.ts @@ -6,12 +6,12 @@ */ import { HttpSetup } from 'kibana/public'; import { BASE_ALERTING_API_PATH } from '../../constants'; -import { Alert, Pagination, Sorting } from '../../../types'; +import { Rule, Pagination, Sorting } from '../../../types'; import { AsApiContract } from '../../../../../actions/common'; import { mapFiltersToKql } from './map_filters_to_kql'; import { transformAlert } from './common_transformations'; -const rewriteResponseRes = (results: Array>): Alert[] => { +const rewriteResponseRes = (results: Array>): Rule[] => { return results.map((item) => transformAlert(item)); }; @@ -35,7 +35,7 @@ export async function loadAlerts({ page: number; perPage: number; total: number; - data: Alert[]; + data: Rule[]; }> { const filters = mapFiltersToKql({ typesFilter, actionTypesFilter, alertStatusesFilter }); const res = await http.get< @@ -43,7 +43,7 @@ export async function loadAlerts({ page: number; perPage: number; total: number; - data: Array>; + data: Array>; }> >(`${BASE_ALERTING_API_PATH}/rules/_find`, { query: { diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/update.test.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/update.test.ts index 3a6059248a3b0..bc6e7aedd122b 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/update.test.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/update.test.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { Alert } from '../../../types'; +import { Rule } from '../../../types'; import { httpServiceMock } from '../../../../../../../src/core/public/mocks'; import { updateAlert } from './update'; import { AlertNotifyWhenType } from '../../../../../alerting/common'; @@ -30,7 +30,7 @@ describe('updateAlert', () => { apiKeyOwner: null, notifyWhen: 'onThrottleInterval' as AlertNotifyWhenType, }; - const resolvedValue: Alert = { + const resolvedValue: Rule = { ...alertToUpdate, id: '12/3', enabled: true, diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/update.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/update.ts index 8b9365b0a4667..4867d33ab2dd1 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/update.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/update.ts @@ -7,7 +7,7 @@ import { HttpSetup } from 'kibana/public'; import { pick } from 'lodash'; import { BASE_ALERTING_API_PATH } from '../../constants'; -import { Alert, AlertUpdates } from '../../../types'; +import { Rule, AlertUpdates } from '../../../types'; import { RewriteResponseCase, AsApiContract } from '../../../../../actions/common'; import { transformAlert } from './common_transformations'; @@ -40,8 +40,8 @@ export async function updateAlert({ 'throttle' | 'name' | 'tags' | 'schedule' | 'params' | 'actions' | 'notifyWhen' >; id: string; -}): Promise { - const res = await http.put>( +}): Promise { + const res = await http.put>( `${BASE_ALERTING_API_PATH}/rule/${encodeURIComponent(id)}`, { body: JSON.stringify( diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_type_compare.test.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_type_compare.test.ts index 5fdb22f066232..faa32204ba66e 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_type_compare.test.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_type_compare.test.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { AlertTypeModel } from '../../types'; +import { RuleTypeModel } from '../../types'; import { alertTypeGroupCompare, alertTypeCompare } from './alert_type_compare'; import { IsEnabledResult, IsDisabledResult } from './check_alert_type_enabled'; @@ -17,7 +17,7 @@ test('should sort groups by containing enabled alert types first and then by nam id: string; name: string; checkEnabledResult: IsEnabledResult | IsDisabledResult; - alertTypeItem: AlertTypeModel; + alertTypeItem: RuleTypeModel; }> ] > = [ @@ -36,7 +36,7 @@ test('should sort groups by containing enabled alert types first and then by nam validate: () => { return { errors: {} }; }, - alertParamsExpression: () => null, + ruleParamsExpression: () => null, requiresAppContext: false, }, }, @@ -57,7 +57,7 @@ test('should sort groups by containing enabled alert types first and then by nam validate: () => { return { errors: {} }; }, - alertParamsExpression: () => null, + ruleParamsExpression: () => null, requiresAppContext: false, }, }, @@ -73,7 +73,7 @@ test('should sort groups by containing enabled alert types first and then by nam validate: () => { return { errors: {} }; }, - alertParamsExpression: () => null, + ruleParamsExpression: () => null, requiresAppContext: false, }, }, @@ -94,7 +94,7 @@ test('should sort groups by containing enabled alert types first and then by nam validate: () => { return { errors: {} }; }, - alertParamsExpression: () => null, + ruleParamsExpression: () => null, requiresAppContext: false, }, }, @@ -118,7 +118,7 @@ test('should sort alert types by enabled first and then by name', async () => { id: string; name: string; checkEnabledResult: IsEnabledResult | IsDisabledResult; - alertTypeItem: AlertTypeModel; + alertTypeItem: RuleTypeModel; }> = [ { id: '1', @@ -132,7 +132,7 @@ test('should sort alert types by enabled first and then by name', async () => { validate: () => { return { errors: {} }; }, - alertParamsExpression: () => null, + ruleParamsExpression: () => null, requiresAppContext: false, }, }, @@ -148,7 +148,7 @@ test('should sort alert types by enabled first and then by name', async () => { validate: () => { return { errors: {} }; }, - alertParamsExpression: () => null, + ruleParamsExpression: () => null, requiresAppContext: false, }, }, @@ -164,7 +164,7 @@ test('should sort alert types by enabled first and then by name', async () => { validate: () => { return { errors: {} }; }, - alertParamsExpression: () => null, + ruleParamsExpression: () => null, requiresAppContext: false, }, }, diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_type_compare.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_type_compare.ts index 078cecbe65122..d05fb400b5e56 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_type_compare.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_type_compare.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { AlertTypeModel } from '../../types'; +import { RuleTypeModel } from '../../types'; import { IsEnabledResult, IsDisabledResult } from './check_alert_type_enabled'; export function alertTypeGroupCompare( @@ -15,7 +15,7 @@ export function alertTypeGroupCompare( id: string; name: string; checkEnabledResult: IsEnabledResult | IsDisabledResult; - alertTypeItem: AlertTypeModel; + alertTypeItem: RuleTypeModel; }> ], right: [ @@ -24,7 +24,7 @@ export function alertTypeGroupCompare( id: string; name: string; checkEnabledResult: IsEnabledResult | IsDisabledResult; - alertTypeItem: AlertTypeModel; + alertTypeItem: RuleTypeModel; }> ], groupNames: Map | undefined @@ -59,13 +59,13 @@ export function alertTypeCompare( id: string; name: string; checkEnabledResult: IsEnabledResult | IsDisabledResult; - alertTypeItem: AlertTypeModel; + alertTypeItem: RuleTypeModel; }, b: { id: string; name: string; checkEnabledResult: IsEnabledResult | IsDisabledResult; - alertTypeItem: AlertTypeModel; + alertTypeItem: RuleTypeModel; } ) { if (a.checkEnabledResult.isEnabled === true && b.checkEnabledResult.isEnabled === false) { diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/capabilities.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/capabilities.ts index 52ea155b3a249..0cde499de8042 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/capabilities.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/capabilities.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { AlertType } from '../../types'; +import { RuleType } from '../../types'; import { InitialAlert } from '../sections/alert_form/alert_reducer'; /** @@ -23,9 +23,9 @@ export const hasExecuteActionsCapability = (capabilities: Capabilities) => export const hasDeleteActionsCapability = (capabilities: Capabilities) => capabilities?.actions?.delete; -export function hasAllPrivilege(alert: InitialAlert, alertType?: AlertType): boolean { +export function hasAllPrivilege(alert: InitialAlert, alertType?: RuleType): boolean { return alertType?.authorizedConsumers[alert.consumer]?.all ?? false; } -export function hasReadPrivilege(alert: InitialAlert, alertType?: AlertType): boolean { +export function hasReadPrivilege(alert: InitialAlert, alertType?: RuleType): boolean { return alertType?.authorizedConsumers[alert.consumer]?.read ?? false; } diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/check_alert_type_enabled.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/lib/check_alert_type_enabled.test.tsx index 44cb17262fb57..ebdea0c3e5878 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/check_alert_type_enabled.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/check_alert_type_enabled.test.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import { AlertType } from '../../types'; +import { RuleType } from '../../types'; import { checkAlertTypeEnabled } from './check_alert_type_enabled'; describe('checkAlertTypeEnabled', () => { @@ -18,7 +18,7 @@ describe('checkAlertTypeEnabled', () => { }); test('returns isEnabled:true when alert type is enabled', async () => { - const alertType: AlertType = { + const alertType: RuleType = { id: 'test', name: 'Test', actionVariables: { @@ -42,7 +42,7 @@ describe('checkAlertTypeEnabled', () => { }); test('returns isEnabled:false when alert type is disabled by license', async () => { - const alertType: AlertType = { + const alertType: RuleType = { id: 'test', name: 'Test', actionVariables: { diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/check_alert_type_enabled.tsx b/x-pack/plugins/triggers_actions_ui/public/application/lib/check_alert_type_enabled.tsx index 9348b367aee9b..3b53e8f9a6c33 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/check_alert_type_enabled.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/check_alert_type_enabled.tsx @@ -7,7 +7,7 @@ import { upperFirst } from 'lodash'; import { i18n } from '@kbn/i18n'; -import { AlertType } from '../../types'; +import { RuleType } from '../../types'; export interface IsEnabledResult { isEnabled: true; @@ -17,7 +17,7 @@ export interface IsDisabledResult { message: string; } -const getLicenseCheckResult = (alertType: AlertType) => { +const getLicenseCheckResult = (alertType: RuleType) => { return { isEnabled: false, message: i18n.translate( @@ -32,7 +32,7 @@ const getLicenseCheckResult = (alertType: AlertType) => { }; }; -export function checkAlertTypeEnabled(alertType?: AlertType): IsEnabledResult | IsDisabledResult { +export function checkAlertTypeEnabled(alertType?: RuleType): IsEnabledResult | IsDisabledResult { if (alertType?.enabledInLicense === false) { return getLicenseCheckResult(alertType); } diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/execution_duration_utils.test.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/execution_duration_utils.test.ts index da102405c179d..f7ade3778f287 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/execution_duration_utils.test.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/execution_duration_utils.test.ts @@ -6,7 +6,7 @@ */ import { formatMillisForDisplay, shouldShowDurationWarning } from './execution_duration_utils'; -import { AlertType as RuleType } from '../../types'; +import { RuleType } from '../../types'; describe('formatMillisForDisplay', () => { it('should return 0 for undefined', () => { diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/execution_duration_utils.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/execution_duration_utils.ts index ecad5475274ce..632217bdb06d5 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/execution_duration_utils.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/execution_duration_utils.ts @@ -7,7 +7,7 @@ import moment from 'moment'; import { padStart } from 'lodash'; -import { AlertType as RuleType } from '../../types'; +import { RuleType } from '../../types'; import { parseDuration } from '../../../../alerting/common'; export function formatMillisForDisplay(value: number | undefined) { diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/value_validators.test.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/value_validators.test.ts index 4ada6f4fc8992..c6524c9d05d56 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/value_validators.test.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/value_validators.test.ts @@ -13,7 +13,7 @@ import { getAlertWithInvalidatedFields, } from './value_validators'; import uuid from 'uuid'; -import { Alert, IErrorObject, UserConfiguredActionConnector } from '../../types'; +import { Rule, IErrorObject, UserConfiguredActionConnector } from '../../types'; describe('throwIfAbsent', () => { test('throws if value is absent', () => { @@ -157,7 +157,7 @@ describe('getConnectorWithInvalidatedFields', () => { describe('getAlertWithInvalidatedFields', () => { test('sets to null all fields that are required but undefined in alert', () => { - const alert: Alert = { + const alert: Rule = { params: {}, consumer: 'test', schedule: { @@ -181,7 +181,7 @@ describe('getAlertWithInvalidatedFields', () => { }); test('does not set to null any fields that are required and defined but invalid in alert', () => { - const alert: Alert = { + const alert: Rule = { name: 'test', id: '123', params: {}, @@ -203,7 +203,7 @@ describe('getAlertWithInvalidatedFields', () => { }); test('set to null all fields that are required but undefined in alert params', () => { - const alert: Alert = { + const alert: Rule = { name: 'test', alertTypeId: '.threshold', id: '123', @@ -238,7 +238,7 @@ describe('getAlertWithInvalidatedFields', () => { }); test('does not set to null any fields that are required and defined but invalid in alert params', () => { - const alert: Alert = { + const alert: Rule = { name: 'test', alertTypeId: '.threshold', id: '123', @@ -279,7 +279,7 @@ describe('getAlertWithInvalidatedFields', () => { }); test('set to null all fields that are required but undefined in alert actions', () => { - const alert: Alert = { + const alert: Rule = { name: 'test', alertTypeId: '.threshold', id: '123', @@ -324,7 +324,7 @@ describe('getAlertWithInvalidatedFields', () => { }); test('validates multiple alert actions with the same connector id', () => { - const alert: Alert = { + const alert: Rule = { name: 'test', alertTypeId: '.threshold', id: '123', diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/value_validators.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/value_validators.ts index 8ff561adc211f..161776dfe0a3e 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/value_validators.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/value_validators.ts @@ -6,7 +6,7 @@ */ import { constant, get, set } from 'lodash'; -import { UserConfiguredActionConnector, IErrorObject, Alert } from '../../types'; +import { UserConfiguredActionConnector, IErrorObject, Rule } from '../../types'; export function throwIfAbsent(message: string) { return (value: T | undefined): T => { @@ -71,7 +71,7 @@ export function getConnectorWithInvalidatedFields( } export function getAlertWithInvalidatedFields( - alert: Alert, + alert: Rule, paramsErrors: IErrorObject, baseAlertErrors: IErrorObject, actionsErrors: IErrorObject[] diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_form.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_form.test.tsx index a26349949446d..f7c15dade55e3 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_form.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_form.test.tsx @@ -13,7 +13,7 @@ import { act } from 'react-dom/test-utils'; import { actionTypeRegistryMock } from '../../action_type_registry.mock'; import { ValidationResult, - Alert, + Rule, AlertAction, ConnectorValidationResult, GenericValidationResult, @@ -246,7 +246,7 @@ describe('action_form', () => { muteAll: false, enabled: false, mutedInstanceIds: [], - } as unknown as Alert; + } as unknown as Rule; const defaultActionMessage = 'Alert [{{context.metadata.name}}] has exceeded the threshold'; const wrapper = mountWithIntl( diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details.test.tsx index c7c41ac4e8171..7694f289963a3 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details.test.tsx @@ -11,7 +11,7 @@ import { shallow } from 'enzyme'; import { mountWithIntl, nextTick } from '@kbn/test/jest'; import { act } from '@testing-library/react'; import { AlertDetails } from './alert_details'; -import { Alert, ActionType, AlertTypeModel, AlertType } from '../../../../types'; +import { Rule, ActionType, RuleTypeModel, RuleType } from '../../../../types'; import { EuiBadge, EuiFlexItem, @@ -64,7 +64,7 @@ const authorizedConsumers = { }; const recoveryActionGroup: ActionGroup<'recovered'> = { id: 'recovered', name: 'Recovered' }; -const alertType: AlertType = { +const alertType: RuleType = { id: '.noop', name: 'No Op', actionGroups: [{ id: 'default', name: 'Default' }], @@ -599,7 +599,7 @@ describe('edit button', () => { }, ]; ruleTypeRegistry.has.mockReturnValue(true); - const alertTypeR: AlertTypeModel = { + const alertTypeR: RuleTypeModel = { id: 'my-alert-type', iconClass: 'test', description: 'Alert when testing', @@ -607,7 +607,7 @@ describe('edit button', () => { validate: () => { return { errors: {} }; }, - alertParamsExpression: jest.fn(), + ruleParamsExpression: jest.fn(), requiresAppContext: false, }; ruleTypeRegistry.get.mockReturnValue(alertTypeR); @@ -738,7 +738,7 @@ describe('broken connector indicator', () => { }, ]; ruleTypeRegistry.has.mockReturnValue(true); - const alertTypeR: AlertTypeModel = { + const alertTypeR: RuleTypeModel = { id: 'my-alert-type', iconClass: 'test', description: 'Alert when testing', @@ -746,7 +746,7 @@ describe('broken connector indicator', () => { validate: () => { return { errors: {} }; }, - alertParamsExpression: jest.fn(), + ruleParamsExpression: jest.fn(), requiresAppContext: false, }; ruleTypeRegistry.get.mockReturnValue(alertTypeR); @@ -945,7 +945,7 @@ describe('refresh button', () => { }); }); -function mockAlert(overloads: Partial = {}): Alert { +function mockAlert(overloads: Partial = {}): Rule { return { id: uuid.v4(), enabled: true, diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details.tsx index a8e46864a0873..5b6ddc1b3345c 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details.tsx @@ -31,7 +31,7 @@ import { AlertExecutionStatusErrorReasons } from '../../../../../../alerting/com import { hasAllPrivilege, hasExecuteActionsCapability } from '../../../lib/capabilities'; import { getAlertingSectionBreadcrumb, getAlertDetailsBreadcrumb } from '../../../lib/breadcrumb'; import { getCurrentDocTitle } from '../../../lib/doc_title'; -import { Alert, AlertType, ActionType, ActionConnector } from '../../../../types'; +import { Rule, RuleType, ActionType, ActionConnector } from '../../../../types'; import { ComponentOpts as BulkOperationsComponentOpts, withBulkAlertOperations, @@ -46,8 +46,8 @@ import { alertReducer } from '../../alert_form/alert_reducer'; import { loadAllActions as loadConnectors } from '../../../lib/action_connector_api'; export type AlertDetailsProps = { - alert: Alert; - alertType: AlertType; + alert: Rule; + alertType: RuleType; actionTypes: ActionType[]; requestRefresh: () => Promise; } & Pick; @@ -72,7 +72,7 @@ export const AlertDetails: React.FunctionComponent = ({ http, } = useKibana().services; const [{}, dispatch] = useReducer(alertReducer, { alert }); - const setInitialAlert = (value: Alert) => { + const setInitialAlert = (value: Rule) => { dispatch({ command: { type: 'setAlert' }, payload: { key: 'alert', value } }); }; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details_route.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details_route.test.tsx index 847c6c65464b2..770b273b02c29 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details_route.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details_route.test.tsx @@ -13,7 +13,7 @@ import { act } from 'react-dom/test-utils'; import { createMemoryHistory, createLocation } from 'history'; import { ToastsApi } from 'kibana/public'; import { AlertDetailsRoute, getRuleData } from './alert_details_route'; -import { Alert } from '../../../../types'; +import { Rule } from '../../../../types'; import { CenterJustifiedSpinner } from '../../../components/center_justified_spinner'; import { spacesPluginMock } from '../../../../../../spaces/public/mocks'; import { useKibana } from '../../../../common/lib/kibana'; @@ -452,7 +452,7 @@ function mockStateSetter() { }; } -function mockRouterProps(rule: Alert) { +function mockRouterProps(rule: Rule) { return { match: { isExact: false, @@ -464,7 +464,7 @@ function mockRouterProps(rule: Alert) { location: createLocation(`/rule/${rule.id}`), }; } -function mockRule(overloads: Partial = {}): Alert { +function mockRule(overloads: Partial = {}): Rule { return { id: uuid.v4(), enabled: true, diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details_route.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details_route.tsx index e9a49d62d8923..08a15d1114796 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details_route.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details_route.tsx @@ -10,7 +10,7 @@ import React, { useState, useEffect } from 'react'; import { RouteComponentProps } from 'react-router-dom'; import { ToastsApi } from 'kibana/public'; import { EuiSpacer } from '@elastic/eui'; -import { AlertType, ActionType, ResolvedRule } from '../../../../types'; +import { RuleType, ActionType, ResolvedRule } from '../../../../types'; import { AlertDetailsWithApi as AlertDetails } from './alert_details'; import { throwIfAbsent, throwIfIsntContained } from '../../../lib/value_validators'; import { @@ -47,7 +47,7 @@ export const AlertDetailsRoute: React.FunctionComponent const { basePath } = http; const [alert, setAlert] = useState(null); - const [alertType, setAlertType] = useState(null); + const [alertType, setAlertType] = useState(null); const [actionTypes, setActionTypes] = useState(null); const [refreshToken, requestRefresh] = React.useState(); useEffect(() => { @@ -130,7 +130,7 @@ export async function getRuleData( resolveRule: AlertApis['resolveRule'], loadActionTypes: ActionApis['loadActionTypes'], setAlert: React.Dispatch>, - setAlertType: React.Dispatch>, + setAlertType: React.Dispatch>, setActionTypes: React.Dispatch>, toasts: Pick ) { diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alerts.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alerts.test.tsx index 918b71a85cc51..fb8892ece8b68 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alerts.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alerts.test.tsx @@ -11,7 +11,7 @@ import { shallow } from 'enzyme'; import { mountWithIntl, nextTick } from '@kbn/test/jest'; import { act } from 'react-dom/test-utils'; import { Alerts, AlertListItem, alertToListItem } from './alerts'; -import { Alert, AlertSummary, AlertStatus, AlertType } from '../../../../types'; +import { Rule, AlertSummary, AlertStatus, RuleType } from '../../../../types'; import { EuiBasicTable } from '@elastic/eui'; import { ExecutionDurationChart } from '../../common/components/execution_duration_chart'; @@ -382,7 +382,7 @@ describe('execution duration overview', () => { }); }); -function mockRule(overloads: Partial = {}): Alert { +function mockRule(overloads: Partial = {}): Rule { return { id: uuid.v4(), enabled: true, @@ -410,7 +410,7 @@ function mockRule(overloads: Partial = {}): Alert { }; } -function mockRuleType(overloads: Partial = {}): AlertType { +function mockRuleType(overloads: Partial = {}): RuleType { return { id: 'test.testRuleType', name: 'My Test Rule Type', diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alerts.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alerts.tsx index b0d9a504cc773..59a0adea64fed 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alerts.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alerts.tsx @@ -28,7 +28,7 @@ import { AlertExecutionStatusErrorReasons, AlertStatusValues, } from '../../../../../../alerting/common'; -import { Alert, AlertSummary, AlertStatus, AlertType, Pagination } from '../../../../types'; +import { Rule, AlertSummary, AlertStatus, RuleType, Pagination } from '../../../../types'; import { ComponentOpts as AlertApis, withBulkAlertOperations, @@ -48,8 +48,8 @@ import { import { ExecutionDurationChart } from '../../common/components/execution_duration_chart'; type AlertsProps = { - rule: Alert; - ruleType: AlertType; + rule: Rule; + ruleType: RuleType; readOnly: boolean; alertSummary: AlertSummary; requestRefresh: () => Promise; @@ -321,7 +321,7 @@ const INACTIVE_LABEL = i18n.translate( { defaultMessage: 'Recovered' } ); -function getActionGroupName(ruleType: AlertType, actionGroupId?: string): string | undefined { +function getActionGroupName(ruleType: RuleType, actionGroupId?: string): string | undefined { actionGroupId = actionGroupId || ruleType.defaultActionGroupId; const actionGroup = ruleType?.actionGroups?.find( (group: ActionGroup) => group.id === actionGroupId @@ -331,7 +331,7 @@ function getActionGroupName(ruleType: AlertType, actionGroupId?: string): string export function alertToListItem( durationEpoch: number, - ruleType: AlertType, + ruleType: RuleType, alertId: string, alert: AlertStatus ): AlertListItem { diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alerts_route.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alerts_route.test.tsx index 9544c94982980..36b73bbbf82c0 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alerts_route.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alerts_route.test.tsx @@ -10,7 +10,7 @@ import uuid from 'uuid'; import { shallow } from 'enzyme'; import { ToastsApi } from 'kibana/public'; import { AlertsRoute, getAlertSummary } from './alerts_route'; -import { Alert, AlertSummary, AlertType } from '../../../../types'; +import { Rule, AlertSummary, RuleType } from '../../../../types'; import { CenterJustifiedSpinner } from '../../../components/center_justified_spinner'; jest.mock('../../../../common/lib/kibana'); @@ -101,7 +101,7 @@ function mockStateSetter() { }; } -function mockRule(overloads: Partial = {}): Alert { +function mockRule(overloads: Partial = {}): Rule { return { id: uuid.v4(), enabled: true, @@ -129,7 +129,7 @@ function mockRule(overloads: Partial = {}): Alert { }; } -function mockRuleType(overloads: Partial = {}): AlertType { +function mockRuleType(overloads: Partial = {}): RuleType { return { id: 'test.testRuleType', name: 'My Test Rule Type', diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alerts_route.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alerts_route.tsx index e99df3dcbd233..e55181d9b1a04 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alerts_route.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alerts_route.tsx @@ -8,7 +8,7 @@ import { i18n } from '@kbn/i18n'; import { ToastsApi } from 'kibana/public'; import React, { useState, useEffect } from 'react'; -import { Alert, AlertSummary, AlertType } from '../../../../types'; +import { Rule, AlertSummary, RuleType } from '../../../../types'; import { ComponentOpts as AlertApis, withBulkAlertOperations, @@ -18,8 +18,8 @@ import { useKibana } from '../../../../common/lib/kibana'; import { CenterJustifiedSpinner } from '../../../components/center_justified_spinner'; type WithAlertSummaryProps = { - rule: Alert; - ruleType: AlertType; + rule: Rule; + ruleType: RuleType; readOnly: boolean; requestRefresh: () => Promise; } & Pick; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/view_in_app.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/view_in_app.test.tsx index f787ad76fd1bf..d177e94b8fd74 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/view_in_app.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/view_in_app.test.tsx @@ -10,7 +10,7 @@ import uuid from 'uuid'; import { mount, ReactWrapper } from 'enzyme'; import { act } from 'react-dom/test-utils'; -import { Alert } from '../../../../types'; +import { Rule } from '../../../../types'; import { ViewInApp } from './view_in_app'; import { useKibana } from '../../../../common/lib/kibana'; jest.mock('../../../../common/lib/kibana'); @@ -69,7 +69,7 @@ function waitForUseEffect() { }); } -function mockAlert(overloads: Partial = {}): Alert { +function mockAlert(overloads: Partial = {}): Rule { return { id: uuid.v4(), enabled: true, diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/view_in_app.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/view_in_app.tsx index 458501c9a6992..df80be989aaa7 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/view_in_app.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/view_in_app.tsx @@ -17,11 +17,11 @@ import { AlertStateNavigation, AlertUrlNavigation, } from '../../../../../../alerting/common'; -import { Alert } from '../../../../types'; +import { Rule } from '../../../../types'; import { useKibana } from '../../../../common/lib/kibana'; export interface ViewInAppProps { - alert: Alert; + alert: Rule; } const NO_NAVIGATION = false; @@ -82,7 +82,7 @@ function hasNavigation( function getNavigationHandler( alertNavigation: AlertNavigationLoadingState, - alert: Alert, + alert: Rule, navigateToApp: CoreStart['application']['navigateToApp'] ): object { return hasNavigation(alertNavigation) diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_add.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_add.test.tsx index 786d67168ecbd..d3a6e94c786a4 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_add.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_add.test.tsx @@ -16,7 +16,7 @@ import AlertAdd from './alert_add'; import { createAlert } from '../../lib/alert_api'; import { actionTypeRegistryMock } from '../../action_type_registry.mock'; import { - Alert, + Rule, AlertAddProps, AlertFlyoutCloseReason, ConnectorValidationResult, @@ -69,7 +69,7 @@ describe.skip('alert_add', () => { let wrapper: ReactWrapper; async function setup( - initialValues?: Partial, + initialValues?: Partial, onClose: AlertAddProps['onClose'] = jest.fn(), defaultScheduleInterval?: string ) { @@ -122,7 +122,7 @@ describe.skip('alert_add', () => { hasPermanentEncryptionKey: true, }); - const alertType = { + const ruleType = { id: 'my-alert-type', iconClass: 'test', description: 'test', @@ -130,7 +130,7 @@ describe.skip('alert_add', () => { validate: (): ValidationResult => { return { errors: {} }; }, - alertParamsExpression: TestExpression, + ruleParamsExpression: TestExpression, requiresAppContext: false, }; @@ -149,8 +149,8 @@ describe.skip('alert_add', () => { }); actionTypeRegistry.get.mockReturnValueOnce(actionTypeModel); actionTypeRegistry.has.mockReturnValue(true); - ruleTypeRegistry.list.mockReturnValue([alertType]); - ruleTypeRegistry.get.mockReturnValue(alertType); + ruleTypeRegistry.list.mockReturnValue([ruleType]); + ruleTypeRegistry.get.mockReturnValue(ruleType); ruleTypeRegistry.has.mockReturnValue(true); actionTypeRegistry.list.mockReturnValue([actionTypeModel]); actionTypeRegistry.has.mockReturnValue(true); @@ -268,7 +268,7 @@ describe.skip('alert_add', () => { }); }); -function mockAlert(overloads: Partial = {}): Alert { +function mockAlert(overloads: Partial = {}): Rule { return { id: uuid.v4(), enabled: true, diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_add.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_add.tsx index 8525960824002..8233012676183 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_add.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_add.tsx @@ -11,8 +11,8 @@ import { EuiTitle, EuiFlyoutHeader, EuiFlyout, EuiFlyoutBody, EuiPortal } from ' import { i18n } from '@kbn/i18n'; import { isEmpty } from 'lodash'; import { - Alert, - AlertTypeParams, + Rule, + RuleTypeParams, AlertUpdates, AlertFlyoutCloseReason, IErrorObject, @@ -68,7 +68,7 @@ const AlertAdd = ({ const [{ alert }, dispatch] = useReducer(alertReducer as InitialAlertReducer, { alert: initialAlert, }); - const [initialAlertParams, setInitialAlertParams] = useState({}); + const [initialAlertParams, setInitialAlertParams] = useState({}); const [isSaving, setIsSaving] = useState(false); const [isConfirmAlertSaveModalOpen, setIsConfirmAlertSaveModalOpen] = useState(false); const [isConfirmAlertCloseModalOpen, setIsConfirmAlertCloseModalOpen] = useState(false); @@ -81,7 +81,7 @@ const AlertAdd = ({ dispatch({ command: { type: 'setAlert' }, payload: { key: 'alert', value } }); }; - const setAlertProperty = (key: Key, value: Alert[Key] | null) => { + const setRuleProperty = (key: Key, value: Rule[Key] | null) => { dispatch({ command: { type: 'setProperty' }, payload: { key, value } }); }; @@ -95,7 +95,7 @@ const AlertAdd = ({ useEffect(() => { if (alertTypeId) { - setAlertProperty('alertTypeId', alertTypeId); + setRuleProperty('alertTypeId', alertTypeId); } }, [alertTypeId]); @@ -131,7 +131,7 @@ const AlertAdd = ({ useEffect(() => { (async () => { setIsLoading(true); - const res = await getAlertActionErrors(alert as Alert, actionTypeRegistry); + const res = await getAlertActionErrors(alert as Rule, actionTypeRegistry); setIsLoading(false); setAlertActionsErrors([...res]); })(); @@ -141,7 +141,7 @@ const AlertAdd = ({ if (alert.alertTypeId && ruleTypeIndex) { const type = ruleTypeIndex.get(alert.alertTypeId); if (type?.defaultScheduleInterval && !changedFromDefaultInterval) { - setAlertProperty('schedule', { interval: type.defaultScheduleInterval }); + setRuleProperty('schedule', { interval: type.defaultScheduleInterval }); } } }, [alert.alertTypeId, ruleTypeIndex, alert.schedule.interval, changedFromDefaultInterval]); @@ -177,7 +177,7 @@ const AlertAdd = ({ const alertType = alert.alertTypeId ? ruleTypeRegistry.get(alert.alertTypeId) : null; const { alertBaseErrors, alertErrors, alertParamsErrors } = getAlertErrors( - alert as Alert, + alert as Rule, alertType, alert.alertTypeId ? ruleTypeIndex?.get(alert.alertTypeId) : undefined ); @@ -185,7 +185,7 @@ const AlertAdd = ({ // Confirm before saving if user is able to add actions but hasn't added any to this alert const shouldConfirmSave = canShowActions && alert.actions?.length === 0; - async function onSaveAlert(): Promise { + async function onSaveAlert(): Promise { try { const newAlert = await createAlert({ http, alert: alert as AlertUpdates }); toasts.addSuccess( @@ -253,7 +253,7 @@ const AlertAdd = ({ if (isLoading || !isValidAlert(alert, alertErrors, alertActionsErrors)) { setAlert( getAlertWithInvalidatedFields( - alert as Alert, + alert as Rule, alertParamsErrors, alertBaseErrors, alertActionsErrors diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_edit.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_edit.test.tsx index 467f2af6ed704..226734ca46a67 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_edit.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_edit.test.tsx @@ -12,7 +12,7 @@ import { coreMock } from '../../../../../../../src/core/public/mocks'; import { actionTypeRegistryMock } from '../../action_type_registry.mock'; import { ValidationResult, - Alert, + Rule, ConnectorValidationResult, GenericValidationResult, } from '../../../types'; @@ -107,7 +107,7 @@ describe('alert_edit', () => { }, }, ]; - const alertType = { + const ruleType = { id: 'my-alert-type', iconClass: 'test', description: 'test', @@ -115,7 +115,7 @@ describe('alert_edit', () => { validate: (): ValidationResult => { return { errors: {} }; }, - alertParamsExpression: () => <>, + ruleParamsExpression: () => <>, requiresAppContext: false, }; @@ -133,7 +133,7 @@ describe('alert_edit', () => { actionConnectorFields: null, }); loadAlertTypes.mockResolvedValue(alertTypes); - const alert: Alert = { + const alert: Rule = { id: 'ab5661e0-197e-45ee-b477-302d89193b5e', params: { aggType: 'average', @@ -175,8 +175,8 @@ describe('alert_edit', () => { }; actionTypeRegistry.get.mockReturnValueOnce(actionTypeModel); actionTypeRegistry.has.mockReturnValue(true); - ruleTypeRegistry.list.mockReturnValue([alertType]); - ruleTypeRegistry.get.mockReturnValue(alertType); + ruleTypeRegistry.list.mockReturnValue([ruleType]); + ruleTypeRegistry.get.mockReturnValue(ruleType); ruleTypeRegistry.has.mockReturnValue(true); actionTypeRegistry.list.mockReturnValue([actionTypeModel]); actionTypeRegistry.has.mockReturnValue(true); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_edit.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_edit.tsx index 46a4190c611db..fc8f919f37901 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_edit.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_edit.tsx @@ -25,11 +25,11 @@ import { import { cloneDeep } from 'lodash'; import { i18n } from '@kbn/i18n'; import { - Alert, + Rule, AlertFlyoutCloseReason, AlertEditProps, IErrorObject, - AlertType, + RuleType, } from '../../../types'; import { AlertForm } from './alert_form'; import { getAlertActionErrors, getAlertErrors, isValidAlert } from './alert_errors'; @@ -63,7 +63,7 @@ export const AlertEdit = ({ const [isConfirmAlertCloseModalOpen, setIsConfirmAlertCloseModalOpen] = useState(false); const [alertActionsErrors, setAlertActionsErrors] = useState([]); const [isLoading, setIsLoading] = useState(false); - const [serverRuleType, setServerRuleType] = useState | undefined>( + const [serverRuleType, setServerRuleType] = useState | undefined>( props.ruleType ); @@ -71,7 +71,7 @@ export const AlertEdit = ({ http, notifications: { toasts }, } = useKibana().services; - const setAlert = (value: Alert) => { + const setAlert = (value: Rule) => { dispatch({ command: { type: 'setAlert' }, payload: { key: 'alert', value } }); }; @@ -80,7 +80,7 @@ export const AlertEdit = ({ useEffect(() => { (async () => { setIsLoading(true); - const res = await getAlertActionErrors(alert as Alert, actionTypeRegistry); + const res = await getAlertActionErrors(alert as Rule, actionTypeRegistry); setAlertActionsErrors([...res]); setIsLoading(false); })(); @@ -100,7 +100,7 @@ export const AlertEdit = ({ }, [props.ruleType, alertType.id, serverRuleType, http]); const { alertBaseErrors, alertErrors, alertParamsErrors } = getAlertErrors( - alert as Alert, + alert as Rule, alertType, serverRuleType ); @@ -113,7 +113,7 @@ export const AlertEdit = ({ } }; - async function onSaveAlert(): Promise { + async function onSaveAlert(): Promise { try { if ( !isLoading && @@ -133,7 +133,7 @@ export const AlertEdit = ({ } else { setAlert( getAlertWithInvalidatedFields( - alert as Alert, + alert as Rule, alertParamsErrors, alertBaseErrors, alertActionsErrors diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_errors.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_errors.test.tsx index 5ca0fd9dafc18..03e98b83615f2 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_errors.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_errors.test.tsx @@ -14,7 +14,7 @@ import { hasObjectErrors, isValidAlert, } from './alert_errors'; -import { Alert, AlertType, AlertTypeModel } from '../../../types'; +import { Rule, RuleType, RuleTypeModel } from '../../../types'; import { actionTypeRegistryMock } from '../../action_type_registry.mock'; describe('alert_errors', () => { @@ -218,8 +218,8 @@ describe('alert_errors', () => { }); function mockserverRuleType( - overloads: Partial> = {} -): AlertType { + overloads: Partial> = {} +): RuleType { return { actionGroups: [], defaultActionGroupId: 'default', @@ -242,20 +242,20 @@ function mockserverRuleType( }; } -function mockAlertTypeModel(overloads: Partial = {}): AlertTypeModel { +function mockAlertTypeModel(overloads: Partial = {}): RuleTypeModel { return { id: 'alertTypeModel', description: 'some alert', iconClass: 'something', documentationUrl: null, validate: () => ({ errors: {} }), - alertParamsExpression: () => , + ruleParamsExpression: () => , requiresAppContext: false, ...overloads, }; } -function mockAlert(overloads: Partial = {}): Alert { +function mockAlert(overloads: Partial = {}): Rule { return { id: uuid.v4(), enabled: true, diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_errors.ts b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_errors.ts index 3ca6a822b2d2d..d3e0f93d1d0b6 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_errors.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_errors.ts @@ -8,11 +8,11 @@ import { isObject } from 'lodash'; import { i18n } from '@kbn/i18n'; import { parseDuration } from '../../../../../alerting/common/parse_duration'; import { - AlertTypeModel, - Alert, + RuleTypeModel, + Rule, IErrorObject, AlertAction, - AlertType, + RuleType, ValidationResult, ActionTypeRegistryContract, } from '../../../types'; @@ -20,7 +20,7 @@ import { InitialAlert } from './alert_reducer'; export function validateBaseProperties( alertObject: InitialAlert, - serverRuleType?: AlertType + serverRuleType?: RuleType ): ValidationResult { const validationResult = { errors: {} }; const errors = { @@ -80,9 +80,9 @@ export function validateBaseProperties( } export function getAlertErrors( - alert: Alert, - alertTypeModel: AlertTypeModel | null, - serverRuleType?: AlertType + alert: Rule, + alertTypeModel: RuleTypeModel | null, + serverRuleType?: RuleType ) { const alertParamsErrors: IErrorObject = alertTypeModel ? alertTypeModel.validate(alert.params).errors @@ -101,7 +101,7 @@ export function getAlertErrors( } export async function getAlertActionErrors( - alert: Alert, + alert: Rule, actionTypeRegistry: ActionTypeRegistryContract ): Promise { return await Promise.all( @@ -121,10 +121,10 @@ export const hasObjectErrors: (errors: IErrorObject) => boolean = (errors) => }); export function isValidAlert( - alertObject: InitialAlert | Alert, + alertObject: InitialAlert | Rule, validationResult: IErrorObject, actionsErrors: IErrorObject[] -): alertObject is Alert { +): alertObject is Rule { return ( !hasObjectErrors(validationResult) && actionsErrors.every((error: IErrorObject) => !hasObjectErrors(error)) diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_form.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_form.test.tsx index 3d32d4ba4f908..37741dcaab6e5 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_form.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_form.test.tsx @@ -13,8 +13,8 @@ import { actionTypeRegistryMock } from '../../action_type_registry.mock'; import { ruleTypeRegistryMock } from '../../rule_type_registry.mock'; import { ValidationResult, - Alert, - AlertType, + Rule, + RuleType, ConnectorValidationResult, GenericValidationResult, } from '../../../types'; @@ -31,7 +31,7 @@ jest.mock('../../lib/alert_api', () => ({ jest.mock('../../../common/lib/kibana'); describe('alert_form', () => { - const alertType = { + const ruleType = { id: 'my-alert-type', iconClass: 'test', description: 'Alert when testing', @@ -39,7 +39,7 @@ describe('alert_form', () => { validate: (): ValidationResult => { return { errors: {} }; }, - alertParamsExpression: () => <>, + ruleParamsExpression: () => <>, requiresAppContext: false, }; @@ -64,7 +64,7 @@ describe('alert_form', () => { actionConnectorFields: null, }); - const alertTypeNonEditable = { + const ruleTypeNonEditable = { id: 'non-edit-alert-type', iconClass: 'test', description: 'test', @@ -72,11 +72,11 @@ describe('alert_form', () => { validate: (): ValidationResult => { return { errors: {} }; }, - alertParamsExpression: () => <>, + ruleParamsExpression: () => <>, requiresAppContext: true, }; - const disabledByLicenseAlertType = { + const disabledByLicenseRuleType = { id: 'disabled-by-license', iconClass: 'test', description: 'Alert when testing', @@ -84,7 +84,7 @@ describe('alert_form', () => { validate: (): ValidationResult => { return { errors: {} }; }, - alertParamsExpression: () => <>, + ruleParamsExpression: () => <>, requiresAppContext: false, }; @@ -96,7 +96,7 @@ describe('alert_form', () => { async function setup() { const mocks = coreMock.createSetup(); const { loadAlertTypes } = jest.requireMock('../../lib/alert_api'); - const alertTypes: AlertType[] = [ + const alertTypes: RuleType[] = [ { id: 'my-alert-type', name: 'Test', @@ -160,9 +160,9 @@ describe('alert_form', () => { }, }; ruleTypeRegistry.list.mockReturnValue([ - alertType, - alertTypeNonEditable, - disabledByLicenseAlertType, + ruleType, + ruleTypeNonEditable, + disabledByLicenseRuleType, ]); ruleTypeRegistry.has.mockReturnValue(true); actionTypeRegistry.list.mockReturnValue([actionType]); @@ -180,7 +180,7 @@ describe('alert_form', () => { muteAll: false, enabled: false, mutedInstanceIds: [], - } as unknown as Alert; + } as unknown as Rule; wrapper = mountWithIntl( { validate: (): ValidationResult => { return { errors: {} }; }, - alertParamsExpression: () => <>, + ruleParamsExpression: () => <>, requiresAppContext: true, }, { @@ -333,7 +333,7 @@ describe('alert_form', () => { validate: (): ValidationResult => { return { errors: {} }; }, - alertParamsExpression: () => <>, + ruleParamsExpression: () => <>, requiresAppContext: false, }, ]); @@ -352,7 +352,7 @@ describe('alert_form', () => { muteAll: false, enabled: false, mutedInstanceIds: [], - } as unknown as Alert; + } as unknown as Rule; wrapper = mountWithIntl( { let wrapper: ReactWrapper; async function setup() { - ruleTypeRegistry.list.mockReturnValue([alertType]); - ruleTypeRegistry.get.mockReturnValue(alertType); + ruleTypeRegistry.list.mockReturnValue([ruleType]); + ruleTypeRegistry.get.mockReturnValue(ruleType); ruleTypeRegistry.has.mockReturnValue(true); actionTypeRegistry.list.mockReturnValue([actionType]); actionTypeRegistry.has.mockReturnValue(true); @@ -403,7 +403,7 @@ describe('alert_form', () => { const initialAlert = { name: 'test', - alertTypeId: alertType.id, + alertTypeId: ruleType.id, params: {}, consumer: ALERTS_FEATURE_ID, schedule: { @@ -414,7 +414,7 @@ describe('alert_form', () => { muteAll: false, enabled: false, mutedInstanceIds: [], - } as unknown as Alert; + } as unknown as Rule; wrapper = mountWithIntl( (null); + const [alertTypeModel, setAlertTypeModel] = useState(null); const [alertInterval, setAlertInterval] = useState( alert.schedule.interval @@ -142,10 +142,10 @@ export const AlertForm = ({ const [ruleTypeIndex, setRuleTypeIndex] = useState(null); const [availableAlertTypes, setAvailableAlertTypes] = useState< - Array<{ alertTypeModel: AlertTypeModel; alertType: AlertType }> + Array<{ alertTypeModel: RuleTypeModel; alertType: RuleType }> >([]); const [filteredAlertTypes, setFilteredAlertTypes] = useState< - Array<{ alertTypeModel: AlertTypeModel; alertType: AlertType }> + Array<{ alertTypeModel: RuleTypeModel; alertType: RuleType }> >([]); const [searchText, setSearchText] = useState(); const [inputText, setInputText] = useState(); @@ -221,20 +221,20 @@ export const AlertForm = ({ } }, [alert.schedule.interval]); - const setAlertProperty = useCallback( - (key: Key, value: Alert[Key] | null) => { + const setRuleProperty = useCallback( + (key: Key, value: Rule[Key] | null) => { dispatch({ command: { type: 'setProperty' }, payload: { key, value } }); }, [dispatch] ); const setActions = useCallback( - (updatedActions: AlertAction[]) => setAlertProperty('actions', updatedActions), - [setAlertProperty] + (updatedActions: AlertAction[]) => setRuleProperty('actions', updatedActions), + [setRuleProperty] ); - const setAlertParams = (key: string, value: any) => { - dispatch({ command: { type: 'setAlertParams' }, payload: { key, value } }); + const setRuleParams = (key: string, value: any) => { + dispatch({ command: { type: 'setRuleParams' }, payload: { key, value } }); }; const setScheduleProperty = (key: string, value: any) => { @@ -276,13 +276,13 @@ export const AlertForm = ({ // eslint-disable-next-line react-hooks/exhaustive-deps }, [ruleTypeRegistry, availableAlertTypes, searchText, JSON.stringify(solutionsFilter)]); - const getAvailableAlertTypes = (alertTypesResult: AlertType[]) => + const getAvailableAlertTypes = (alertTypesResult: RuleType[]) => ruleTypeRegistry .list() .reduce( ( - arr: Array<{ alertType: AlertType; alertTypeModel: AlertTypeModel }>, - ruleTypeRegistryItem: AlertTypeModel + arr: Array<{ alertType: RuleType; alertTypeModel: RuleTypeModel }>, + ruleTypeRegistryItem: RuleTypeModel ) => { const alertType = alertTypesResult.find((item) => ruleTypeRegistryItem.id === item.id); if (alertType) { @@ -316,7 +316,7 @@ export const AlertForm = ({ const tagsOptions = alert.tags ? alert.tags.map((label: string) => ({ label })) : []; const isActionGroupDisabledForActionType = useCallback( - (alertType: AlertType, actionGroupId: string, actionTypeId: string): boolean => { + (alertType: RuleType, actionGroupId: string, actionTypeId: string): boolean => { return isActionGroupDisabledForActionTypeId( actionGroupId === alertType?.recoveryActionGroup?.id ? RecoveredActionGroup.id @@ -328,7 +328,7 @@ export const AlertForm = ({ ); const AlertParamsExpressionComponent = alertTypeModel - ? alertTypeModel.alertParamsExpression + ? alertTypeModel.ruleParamsExpression : null; const alertTypesByProducer = filteredAlertTypes.reduce( @@ -339,7 +339,7 @@ export const AlertForm = ({ id: string; name: string; checkEnabledResult: IsEnabledResult | IsDisabledResult; - alertTypeItem: AlertTypeModel; + alertTypeItem: RuleTypeModel; }> >, alertTypeValue @@ -422,10 +422,10 @@ export const AlertForm = ({ } isDisabled={!item.checkEnabledResult.isEnabled} onClick={() => { - setAlertProperty('alertTypeId', item.id); + setRuleProperty('alertTypeId', item.id); setActions([]); setAlertTypeModel(item.alertTypeItem); - setAlertProperty('params', {}); + setRuleProperty('params', {}); if (ruleTypeIndex && ruleTypeIndex.has(item.id)) { setDefaultActionGroupId(ruleTypeIndex.get(item.id)!.defaultActionGroupId); } @@ -463,9 +463,9 @@ export const AlertForm = ({ } )} onClick={() => { - setAlertProperty('alertTypeId', null); + setRuleProperty('alertTypeId', null); setAlertTypeModel(null); - setAlertProperty('params', {}); + setRuleProperty('params', {}); }} /> @@ -514,13 +514,13 @@ export const AlertForm = ({ } > { - setAlertProperty('name', e.target.value); + setRuleProperty('name', e.target.value); }} onBlur={() => { if (!alert.name) { - setAlertProperty('name', ''); + setRuleProperty('name', ''); } }} /> @@ -639,20 +639,20 @@ export const AlertForm = ({ selectedOptions={tagsOptions} onCreateOption={(searchValue: string) => { const newOptions = [...tagsOptions, { label: searchValue }]; - setAlertProperty( + setRuleProperty( 'tags', newOptions.map((newOption) => newOption.label) ); }} onChange={(selectedOptions: Array<{ label: string }>) => { - setAlertProperty( + setRuleProperty( 'tags', selectedOptions.map((selectedOption) => selectedOption.label) ); }} onBlur={() => { if (!alert.tags) { - setAlertProperty('tags', []); + setRuleProperty('tags', []); } }} /> @@ -708,17 +708,17 @@ export const AlertForm = ({ throttleUnit={alertThrottleUnit} onNotifyWhenChange={useCallback( (notifyWhen) => { - setAlertProperty('notifyWhen', notifyWhen); + setRuleProperty('notifyWhen', notifyWhen); }, - [setAlertProperty] + [setRuleProperty] )} onThrottleChange={useCallback( (throttle: number | null, throttleUnit: string) => { setAlertThrottle(throttle); setAlertThrottleUnit(throttleUnit); - setAlertProperty('throttle', throttle ? `${throttle}${throttleUnit}` : null); + setRuleProperty('throttle', throttle ? `${throttle}${throttleUnit}` : null); }, - [setAlertProperty] + [setRuleProperty] )} /> diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_notify_when.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_notify_when.test.tsx index 43624a7b69347..1534e0ed4f6b8 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_notify_when.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_notify_when.test.tsx @@ -9,7 +9,7 @@ import React from 'react'; import { mountWithIntl, nextTick } from '@kbn/test/jest'; import { ReactWrapper } from 'enzyme'; import { act } from 'react-dom/test-utils'; -import { Alert } from '../../../types'; +import { Rule } from '../../../types'; import { ALERTS_FEATURE_ID } from '../../../../../alerting/common'; import { AlertNotifyWhen } from './alert_notify_when'; @@ -39,7 +39,7 @@ describe('alert_notify_when', () => { mutedInstanceIds: [], notifyWhen: 'onActionGroupChange', ...overrides, - } as unknown as Alert; + } as unknown as Rule; wrapper = mountWithIntl( { - let initialAlert: Alert; + let initialAlert: Rule; beforeAll(() => { initialAlert = { params: {}, @@ -21,7 +21,7 @@ describe('alert reducer', () => { actions: [], tags: [], notifyWhen: 'onActionGroupChange', - } as unknown as Alert; + } as unknown as Rule; }); // setAlert @@ -83,7 +83,7 @@ describe('alert reducer', () => { const updatedAlert = alertReducer( { alert: initialAlert }, { - command: { type: 'setAlertParams' }, + command: { type: 'setRuleParams' }, payload: { key: 'testParam', value: 'new test params property', @@ -95,7 +95,7 @@ describe('alert reducer', () => { const updatedAlertParamsProperty = alertReducer( { alert: updatedAlert.alert }, { - command: { type: 'setAlertParams' }, + command: { type: 'setRuleParams' }, payload: { key: 'testParam', value: 'test params property updated', diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_reducer.ts b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_reducer.ts index a274e09a6fb7d..5099fa61bdb38 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_reducer.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_reducer.ts @@ -9,17 +9,17 @@ import { SavedObjectAttribute } from 'kibana/public'; import { isEqual } from 'lodash'; import { Reducer } from 'react'; import { AlertActionParam, IntervalSchedule } from '../../../../../alerting/common'; -import { Alert, AlertAction } from '../../../types'; +import { Rule, AlertAction } from '../../../types'; -export type InitialAlert = Partial & - Pick; +export type InitialAlert = Partial & + Pick; interface CommandType< T extends | 'setAlert' | 'setProperty' | 'setScheduleProperty' - | 'setAlertParams' + | 'setRuleParams' | 'setAlertActionParams' | 'setAlertActionProperty' > { @@ -36,9 +36,9 @@ interface Payload { index?: number; } -interface AlertPayload { +interface AlertPayload { key: Key; - value: Alert[Key] | null; + value: Rule[Key] | null; index?: number; } @@ -61,14 +61,14 @@ export type AlertReducerAction = } | { command: CommandType<'setProperty'>; - payload: AlertPayload; + payload: AlertPayload; } | { command: CommandType<'setScheduleProperty'>; payload: AlertSchedulePayload; } | { - command: CommandType<'setAlertParams'>; + command: CommandType<'setRuleParams'>; payload: Payload; } | { @@ -81,9 +81,9 @@ export type AlertReducerAction = }; export type InitialAlertReducer = Reducer<{ alert: InitialAlert }, AlertReducerAction>; -export type ConcreteAlertReducer = Reducer<{ alert: Alert }, AlertReducerAction>; +export type ConcreteAlertReducer = Reducer<{ alert: Rule }, AlertReducerAction>; -export const alertReducer = ( +export const alertReducer = ( state: { alert: AlertPhase }, action: AlertReducerAction ) => { @@ -102,7 +102,7 @@ export const alertReducer = ( } } case 'setProperty': { - const { key, value } = action.payload as AlertPayload; + const { key, value } = action.payload as AlertPayload; if (isEqual(alert[key], value)) { return state; } else { @@ -132,7 +132,7 @@ export const alertReducer = ( }; } } - case 'setAlertParams': { + case 'setRuleParams': { const { key, value } = action.payload as Payload>; if (isEqual(alert.params[key], value)) { return state; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/has_alert_changed.ts b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/has_alert_changed.ts index 8bd10e9c6126e..bcf34f6d4ad0f 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/has_alert_changed.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/has_alert_changed.ts @@ -7,7 +7,7 @@ import deepEqual from 'fast-deep-equal'; import { pick } from 'lodash'; -import { AlertTypeParams } from '../../../types'; +import { RuleTypeParams } from '../../../types'; import { InitialAlert } from './alert_reducer'; const DEEP_COMPARE_FIELDS = ['tags', 'schedule', 'actions', 'notifyWhen']; @@ -36,6 +36,6 @@ export function hasAlertChanged(a: InitialAlert, b: InitialAlert, compareParams: return !objectsAreEqual || !nonNullCompareFieldsAreEqual; } -export function haveAlertParamsChanged(a: AlertTypeParams, b: AlertTypeParams) { +export function haveAlertParamsChanged(a: RuleTypeParams, b: RuleTypeParams) { return !deepEqual(a, b); } diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.test.tsx index 65f82da69ccc5..c8e1f5d05ecc0 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.test.tsx @@ -13,7 +13,7 @@ import { act } from 'react-dom/test-utils'; import { actionTypeRegistryMock } from '../../../action_type_registry.mock'; import { ruleTypeRegistryMock } from '../../../rule_type_registry.mock'; import { AlertsList } from './alerts_list'; -import { AlertTypeModel, ValidationResult } from '../../../../types'; +import { RuleTypeModel, ValidationResult } from '../../../../types'; import { AlertExecutionStatusErrorReasons, ALERTS_FEATURE_ID, @@ -56,7 +56,7 @@ const { loadActionTypes, loadAllActions } = jest.requireMock('../../../lib/actio const actionTypeRegistry = actionTypeRegistryMock.create(); const ruleTypeRegistry = ruleTypeRegistryMock.create(); -const alertType = { +const ruleType = { id: 'test_alert_type', description: 'test', iconClass: 'test', @@ -64,7 +64,7 @@ const alertType = { validate: (): ValidationResult => { return { errors: {} }; }, - alertParamsExpression: () => null, + ruleParamsExpression: () => null, requiresAppContext: false, }; const alertTypeFromApi = { @@ -82,7 +82,7 @@ const alertTypeFromApi = { }, ruleTaskTimeout: '1m', }; -ruleTypeRegistry.list.mockReturnValue([alertType]); +ruleTypeRegistry.list.mockReturnValue([ruleType]); actionTypeRegistry.list.mockReturnValue([]); const useKibanaMock = useKibana as jest.Mocked; @@ -297,7 +297,7 @@ describe('alerts_list component with items', () => { loadAlertTypes.mockResolvedValue([alertTypeFromApi]); loadAllActions.mockResolvedValue([]); - const ruleTypeMock: AlertTypeModel = { + const ruleTypeMock: RuleTypeModel = { id: 'test_alert_type', iconClass: 'test', description: 'Alert when testing', @@ -305,7 +305,7 @@ describe('alerts_list component with items', () => { validate: () => { return { errors: {} }; }, - alertParamsExpression: jest.fn(), + ruleParamsExpression: jest.fn(), requiresAppContext: !editable, }; @@ -642,7 +642,7 @@ describe('alerts_list with show only capability', () => { loadAlertTypes.mockResolvedValue([alertTypeFromApi]); loadAllActions.mockResolvedValue([]); - const ruleTypeMock: AlertTypeModel = { + const ruleTypeMock: RuleTypeModel = { id: 'test_alert_type', iconClass: 'test', description: 'Alert when testing', @@ -650,7 +650,7 @@ describe('alerts_list with show only capability', () => { validate: () => { return { errors: {} }; }, - alertParamsExpression: jest.fn(), + ruleParamsExpression: jest.fn(), requiresAppContext: !editable, }; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.tsx index f1e88223e57b6..6c146680e9476 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.tsx @@ -40,9 +40,9 @@ import { useHistory } from 'react-router-dom'; import { isEmpty } from 'lodash'; import { ActionType, - Alert, + Rule, AlertTableItem, - AlertType, + RuleType, RuleTypeIndex, Pagination, } from '../../../../types'; @@ -95,7 +95,7 @@ interface AlertTypeState { } interface AlertState { isLoading: boolean; - data: Alert[]; + data: Rule[]; totalItemCount: number; } @@ -1043,7 +1043,7 @@ export const AlertsList: React.FunctionComponent = () => { actionTypeRegistry={actionTypeRegistry} ruleTypeRegistry={ruleTypeRegistry} ruleType={ - alertTypesState.data.get(currentRuleToEdit.alertTypeId) as AlertType + alertTypesState.data.get(currentRuleToEdit.alertTypeId) as RuleType } onSave={loadAlertsData} /> @@ -1077,12 +1077,12 @@ const noPermissionPrompt = ( /> ); -function filterAlertsById(alerts: Alert[], ids: string[]): Alert[] { +function filterAlertsById(alerts: Rule[], ids: string[]): Rule[] { return alerts.filter((alert) => ids.includes(alert.id)); } function convertAlertsToTableItems( - alerts: Alert[], + alerts: Rule[], ruleTypeIndex: RuleTypeIndex, canExecuteActions: boolean ) { diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/collapsed_item_actions.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/collapsed_item_actions.test.tsx index 807fe65a561f8..a54bc67244b35 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/collapsed_item_actions.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/collapsed_item_actions.test.tsx @@ -10,7 +10,7 @@ import { mountWithIntl, nextTick } from '@kbn/test/jest'; import { CollapsedItemActions } from './collapsed_item_actions'; import { act } from 'react-dom/test-utils'; import { ruleTypeRegistryMock } from '../../../rule_type_registry.mock'; -import { AlertTableItem, AlertTypeModel } from '../../../../types'; +import { AlertTableItem, RuleTypeModel } from '../../../../types'; import { useKibana } from '../../../../common/lib/kibana'; jest.mock('../../../../common/lib/kibana'); @@ -31,7 +31,7 @@ describe('CollapsedItemActions', () => { async function setup(editable: boolean = true) { const ruleTypeRegistry = ruleTypeRegistryMock.create(); ruleTypeRegistry.has.mockReturnValue(true); - const alertTypeR: AlertTypeModel = { + const alertTypeR: RuleTypeModel = { id: 'my-alert-type', iconClass: 'test', description: 'Alert when testing', @@ -39,7 +39,7 @@ describe('CollapsedItemActions', () => { validate: () => { return { errors: {} }; }, - alertParamsExpression: jest.fn(), + ruleParamsExpression: jest.fn(), requiresAppContext: !editable, }; ruleTypeRegistry.get.mockReturnValue(alertTypeR); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/rule_enabled_switch.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/rule_enabled_switch.tsx index 21652f1cce781..036bfa68b8dbd 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/rule_enabled_switch.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/rule_enabled_switch.tsx @@ -8,13 +8,13 @@ import React, { useState, useEffect } from 'react'; import { EuiSwitch, EuiLoadingSpinner } from '@elastic/eui'; -import { Alert, AlertTableItem } from '../../../../types'; +import { Rule, AlertTableItem } from '../../../../types'; export interface ComponentOpts { item: AlertTableItem; onAlertChanged: () => void; - enableAlert: (alert: Alert) => Promise; - disableAlert: (alert: Alert) => Promise; + enableAlert: (alert: Rule) => Promise; + disableAlert: (alert: Rule) => Promise; } export const RuleEnabledSwitch: React.FunctionComponent = ({ diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/common/components/with_bulk_alert_api_operations.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/common/components/with_bulk_alert_api_operations.test.tsx index 806f649e7d033..d2194d2246944 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/common/components/with_bulk_alert_api_operations.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/common/components/with_bulk_alert_api_operations.test.tsx @@ -10,7 +10,7 @@ import { shallow, mount } from 'enzyme'; import uuid from 'uuid'; import { withBulkAlertOperations, ComponentOpts } from './with_bulk_alert_api_operations'; import * as alertApi from '../../../lib/alert_api'; -import { Alert } from '../../../../types'; +import { Rule } from '../../../../types'; import { useKibana } from '../../../../common/lib/kibana'; jest.mock('../../../../common/lib/kibana'); @@ -47,7 +47,7 @@ describe('with_bulk_alert_api_operations', () => { // single alert it('muteAlert calls the muteAlert api', () => { const { http } = useKibanaMock().services; - const ComponentToExtend = ({ muteAlert, alert }: ComponentOpts & { alert: Alert }) => { + const ComponentToExtend = ({ muteAlert, alert }: ComponentOpts & { alert: Rule }) => { return ; }; @@ -62,7 +62,7 @@ describe('with_bulk_alert_api_operations', () => { it('unmuteAlert calls the unmuteAlert api', () => { const { http } = useKibanaMock().services; - const ComponentToExtend = ({ unmuteAlert, alert }: ComponentOpts & { alert: Alert }) => { + const ComponentToExtend = ({ unmuteAlert, alert }: ComponentOpts & { alert: Rule }) => { return ; }; @@ -77,7 +77,7 @@ describe('with_bulk_alert_api_operations', () => { it('enableAlert calls the muteAlerts api', () => { const { http } = useKibanaMock().services; - const ComponentToExtend = ({ enableAlert, alert }: ComponentOpts & { alert: Alert }) => { + const ComponentToExtend = ({ enableAlert, alert }: ComponentOpts & { alert: Rule }) => { return ; }; @@ -92,7 +92,7 @@ describe('with_bulk_alert_api_operations', () => { it('disableAlert calls the disableAlert api', () => { const { http } = useKibanaMock().services; - const ComponentToExtend = ({ disableAlert, alert }: ComponentOpts & { alert: Alert }) => { + const ComponentToExtend = ({ disableAlert, alert }: ComponentOpts & { alert: Rule }) => { return ; }; @@ -107,7 +107,7 @@ describe('with_bulk_alert_api_operations', () => { it('deleteAlert calls the deleteAlert api', () => { const { http } = useKibanaMock().services; - const ComponentToExtend = ({ deleteAlert, alert }: ComponentOpts & { alert: Alert }) => { + const ComponentToExtend = ({ deleteAlert, alert }: ComponentOpts & { alert: Rule }) => { return ; }; @@ -123,7 +123,7 @@ describe('with_bulk_alert_api_operations', () => { // bulk alerts it('muteAlerts calls the muteAlerts api', () => { const { http } = useKibanaMock().services; - const ComponentToExtend = ({ muteAlerts, alerts }: ComponentOpts & { alerts: Alert[] }) => { + const ComponentToExtend = ({ muteAlerts, alerts }: ComponentOpts & { alerts: Rule[] }) => { return ; }; @@ -138,7 +138,7 @@ describe('with_bulk_alert_api_operations', () => { it('unmuteAlerts calls the unmuteAlerts api', () => { const { http } = useKibanaMock().services; - const ComponentToExtend = ({ unmuteAlerts, alerts }: ComponentOpts & { alerts: Alert[] }) => { + const ComponentToExtend = ({ unmuteAlerts, alerts }: ComponentOpts & { alerts: Rule[] }) => { return ; }; @@ -153,7 +153,7 @@ describe('with_bulk_alert_api_operations', () => { it('enableAlerts calls the muteAlertss api', () => { const { http } = useKibanaMock().services; - const ComponentToExtend = ({ enableAlerts, alerts }: ComponentOpts & { alerts: Alert[] }) => { + const ComponentToExtend = ({ enableAlerts, alerts }: ComponentOpts & { alerts: Rule[] }) => { return ; }; @@ -172,7 +172,7 @@ describe('with_bulk_alert_api_operations', () => { it('disableAlerts calls the disableAlerts api', () => { const { http } = useKibanaMock().services; - const ComponentToExtend = ({ disableAlerts, alerts }: ComponentOpts & { alerts: Alert[] }) => { + const ComponentToExtend = ({ disableAlerts, alerts }: ComponentOpts & { alerts: Rule[] }) => { return ; }; @@ -190,7 +190,7 @@ describe('with_bulk_alert_api_operations', () => { it('deleteAlerts calls the deleteAlerts api', () => { const { http } = useKibanaMock().services; - const ComponentToExtend = ({ deleteAlerts, alerts }: ComponentOpts & { alerts: Alert[] }) => { + const ComponentToExtend = ({ deleteAlerts, alerts }: ComponentOpts & { alerts: Rule[] }) => { return ; }; @@ -205,10 +205,7 @@ describe('with_bulk_alert_api_operations', () => { it('loadAlert calls the loadAlert api', () => { const { http } = useKibanaMock().services; - const ComponentToExtend = ({ - loadAlert, - alertId, - }: ComponentOpts & { alertId: Alert['id'] }) => { + const ComponentToExtend = ({ loadAlert, alertId }: ComponentOpts & { alertId: Rule['id'] }) => { return ; }; @@ -223,10 +220,7 @@ describe('with_bulk_alert_api_operations', () => { it('resolveRule calls the resolveRule api', () => { const { http } = useKibanaMock().services; - const ComponentToExtend = ({ - resolveRule, - ruleId, - }: ComponentOpts & { ruleId: Alert['id'] }) => { + const ComponentToExtend = ({ resolveRule, ruleId }: ComponentOpts & { ruleId: Rule['id'] }) => { return ; }; @@ -254,7 +248,7 @@ describe('with_bulk_alert_api_operations', () => { }); }); -function mockAlert(overloads: Partial = {}): Alert { +function mockAlert(overloads: Partial = {}): Rule { return { id: uuid.v4(), enabled: true, diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/common/components/with_bulk_alert_api_operations.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/common/components/with_bulk_alert_api_operations.tsx index 1b2c4b01bc4df..1a4c3ad5f4df9 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/common/components/with_bulk_alert_api_operations.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/common/components/with_bulk_alert_api_operations.tsx @@ -8,8 +8,8 @@ import React from 'react'; import { - Alert, - AlertType, + Rule, + RuleType, RuleTaskState, AlertSummary, AlertingFrameworkHealth, @@ -37,30 +37,30 @@ import { import { useKibana } from '../../../../common/lib/kibana'; export interface ComponentOpts { - muteAlerts: (alerts: Alert[]) => Promise; - unmuteAlerts: (alerts: Alert[]) => Promise; - enableAlerts: (alerts: Alert[]) => Promise; - disableAlerts: (alerts: Alert[]) => Promise; - deleteAlerts: (alerts: Alert[]) => Promise<{ + muteAlerts: (alerts: Rule[]) => Promise; + unmuteAlerts: (alerts: Rule[]) => Promise; + enableAlerts: (alerts: Rule[]) => Promise; + disableAlerts: (alerts: Rule[]) => Promise; + deleteAlerts: (alerts: Rule[]) => Promise<{ successes: string[]; errors: string[]; }>; - muteAlert: (alert: Alert) => Promise; - unmuteAlert: (alert: Alert) => Promise; - muteAlertInstance: (alert: Alert, alertInstanceId: string) => Promise; - unmuteAlertInstance: (alert: Alert, alertInstanceId: string) => Promise; - enableAlert: (alert: Alert) => Promise; - disableAlert: (alert: Alert) => Promise; - deleteAlert: (alert: Alert) => Promise<{ + muteAlert: (alert: Rule) => Promise; + unmuteAlert: (alert: Rule) => Promise; + muteAlertInstance: (alert: Rule, alertInstanceId: string) => Promise; + unmuteAlertInstance: (alert: Rule, alertInstanceId: string) => Promise; + enableAlert: (alert: Rule) => Promise; + disableAlert: (alert: Rule) => Promise; + deleteAlert: (alert: Rule) => Promise<{ successes: string[]; errors: string[]; }>; - loadAlert: (id: Alert['id']) => Promise; - loadAlertState: (id: Alert['id']) => Promise; - loadAlertSummary: (id: Alert['id']) => Promise; - loadAlertTypes: () => Promise; + loadAlert: (id: Rule['id']) => Promise; + loadAlertState: (id: Rule['id']) => Promise; + loadAlertSummary: (id: Rule['id']) => Promise; + loadAlertTypes: () => Promise; getHealth: () => Promise; - resolveRule: (id: Alert['id']) => Promise; + resolveRule: (id: Rule['id']) => Promise; } export type PropsWithOptionalApiHandlers = Omit & Partial; @@ -73,77 +73,77 @@ export function withBulkAlertOperations( return ( + muteAlerts={async (items: Rule[]) => muteAlerts({ http, ids: items.filter((item) => !isAlertMuted(item)).map((item) => item.id), }) } - unmuteAlerts={async (items: Alert[]) => + unmuteAlerts={async (items: Rule[]) => unmuteAlerts({ http, ids: items.filter(isAlertMuted).map((item) => item.id) }) } - enableAlerts={async (items: Alert[]) => + enableAlerts={async (items: Rule[]) => enableAlerts({ http, ids: items.filter(isAlertDisabled).map((item) => item.id) }) } - disableAlerts={async (items: Alert[]) => + disableAlerts={async (items: Rule[]) => disableAlerts({ http, ids: items.filter((item) => !isAlertDisabled(item)).map((item) => item.id), }) } - deleteAlerts={async (items: Alert[]) => + deleteAlerts={async (items: Rule[]) => deleteAlerts({ http, ids: items.map((item) => item.id) }) } - muteAlert={async (alert: Alert) => { + muteAlert={async (alert: Rule) => { if (!isAlertMuted(alert)) { return await muteAlert({ http, id: alert.id }); } }} - unmuteAlert={async (alert: Alert) => { + unmuteAlert={async (alert: Rule) => { if (isAlertMuted(alert)) { return await unmuteAlert({ http, id: alert.id }); } }} - muteAlertInstance={async (alert: Alert, instanceId: string) => { + muteAlertInstance={async (alert: Rule, instanceId: string) => { if (!isAlertInstanceMuted(alert, instanceId)) { return muteAlertInstance({ http, id: alert.id, instanceId }); } }} - unmuteAlertInstance={async (alert: Alert, instanceId: string) => { + unmuteAlertInstance={async (alert: Rule, instanceId: string) => { if (isAlertInstanceMuted(alert, instanceId)) { return unmuteAlertInstance({ http, id: alert.id, instanceId }); } }} - enableAlert={async (alert: Alert) => { + enableAlert={async (alert: Rule) => { if (isAlertDisabled(alert)) { return await enableAlert({ http, id: alert.id }); } }} - disableAlert={async (alert: Alert) => { + disableAlert={async (alert: Rule) => { if (!isAlertDisabled(alert)) { return await disableAlert({ http, id: alert.id }); } }} - deleteAlert={async (alert: Alert) => deleteAlerts({ http, ids: [alert.id] })} - loadAlert={async (alertId: Alert['id']) => loadAlert({ http, alertId })} - loadAlertState={async (alertId: Alert['id']) => loadAlertState({ http, alertId })} - loadAlertSummary={async (ruleId: Alert['id']) => loadAlertSummary({ http, ruleId })} + deleteAlert={async (alert: Rule) => deleteAlerts({ http, ids: [alert.id] })} + loadAlert={async (alertId: Rule['id']) => loadAlert({ http, alertId })} + loadAlertState={async (alertId: Rule['id']) => loadAlertState({ http, alertId })} + loadAlertSummary={async (ruleId: Rule['id']) => loadAlertSummary({ http, ruleId })} loadAlertTypes={async () => loadAlertTypes({ http })} - resolveRule={async (ruleId: Alert['id']) => resolveRule({ http, ruleId })} + resolveRule={async (ruleId: Rule['id']) => resolveRule({ http, ruleId })} getHealth={async () => alertingFrameworkHealth({ http })} /> ); }; } -function isAlertDisabled(alert: Alert) { +function isAlertDisabled(alert: Rule) { return alert.enabled === false; } -function isAlertMuted(alert: Alert) { +function isAlertMuted(alert: Rule) { return alert.muteAll === true; } -function isAlertInstanceMuted(alert: Alert, instanceId: string) { +function isAlertInstanceMuted(alert: Rule, instanceId: string) { return alert.mutedInstanceIds.findIndex((muted) => muted === instanceId) >= 0; } diff --git a/x-pack/plugins/triggers_actions_ui/public/application/type_registry.test.ts b/x-pack/plugins/triggers_actions_ui/public/application/type_registry.test.ts index f39bbc1d998a3..4b4ab8e7415d1 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/type_registry.test.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/type_registry.test.ts @@ -8,7 +8,7 @@ import { TypeRegistry } from './type_registry'; import { ValidationResult, - AlertTypeModel, + RuleTypeModel, ActionTypeModel, ConnectorValidationResult, GenericValidationResult, @@ -19,7 +19,7 @@ export const ExpressionComponent: React.FunctionComponent = () => { return null; }; -const getTestAlertType = (id?: string, iconClass?: string) => { +const getTestRuleType = (id?: string, iconClass?: string) => { return { id: id || 'test-alet-type', description: 'Test description', @@ -28,7 +28,7 @@ const getTestAlertType = (id?: string, iconClass?: string) => { validate: (): ValidationResult => { return { errors: {} }; }, - alertParamsExpression: ExpressionComponent, + ruleParamsExpression: ExpressionComponent, requiresAppContext: false, }; }; @@ -57,16 +57,16 @@ beforeEach(() => jest.resetAllMocks()); describe('register()', () => { test('able to register alert types', () => { - const ruleTypeRegistry = new TypeRegistry(); - ruleTypeRegistry.register(getTestAlertType()); + const ruleTypeRegistry = new TypeRegistry(); + ruleTypeRegistry.register(getTestRuleType()); expect(ruleTypeRegistry.has('test-alet-type')).toEqual(true); }); test('throws error if alert type already registered', () => { - const ruleTypeRegistry = new TypeRegistry(); - ruleTypeRegistry.register(getTestAlertType('my-test-alert-type-1')); + const ruleTypeRegistry = new TypeRegistry(); + ruleTypeRegistry.register(getTestRuleType('my-test-alert-type-1')); expect(() => - ruleTypeRegistry.register(getTestAlertType('my-test-alert-type-1')) + ruleTypeRegistry.register(getTestRuleType('my-test-alert-type-1')) ).toThrowErrorMatchingInlineSnapshot( `"Object type \\"my-test-alert-type-1\\" is already registered."` ); @@ -128,13 +128,13 @@ describe('list()', () => { describe('has()', () => { test('returns false for unregistered alert types', () => { - const ruleTypeRegistry = new TypeRegistry(); + const ruleTypeRegistry = new TypeRegistry(); expect(ruleTypeRegistry.has('my-alert-type')).toEqual(false); }); test('returns true after registering an alert type', () => { - const ruleTypeRegistry = new TypeRegistry(); - ruleTypeRegistry.register(getTestAlertType()); + const ruleTypeRegistry = new TypeRegistry(); + ruleTypeRegistry.register(getTestRuleType()); expect(ruleTypeRegistry.has('test-alet-type')); }); }); diff --git a/x-pack/plugins/triggers_actions_ui/public/index.ts b/x-pack/plugins/triggers_actions_ui/public/index.ts index 97f4d847361f2..67b1518a4b8e2 100644 --- a/x-pack/plugins/triggers_actions_ui/public/index.ts +++ b/x-pack/plugins/triggers_actions_ui/public/index.ts @@ -12,18 +12,18 @@ import { Plugin } from './plugin'; export type { AlertAction, - Alert, - AlertTypeModel, + Rule, + RuleTypeModel, ActionType, ActionTypeRegistryContract, RuleTypeRegistryContract, - AlertTypeParamsExpressionProps, + RuleTypeParamsExpressionProps, ValidationResult, ActionVariables, ActionConnector, IErrorObject, AlertFlyoutCloseReason, - AlertTypeParams, + RuleTypeParams, AsApiContract, } from './types'; diff --git a/x-pack/plugins/triggers_actions_ui/public/mocks.ts b/x-pack/plugins/triggers_actions_ui/public/mocks.ts index 5da6d2ddbe8b6..0ef528ed921a3 100644 --- a/x-pack/plugins/triggers_actions_ui/public/mocks.ts +++ b/x-pack/plugins/triggers_actions_ui/public/mocks.ts @@ -17,14 +17,14 @@ import { ActionTypeModel, AlertAddProps, AlertEditProps, - AlertTypeModel, + RuleTypeModel, ConnectorAddFlyoutProps, ConnectorEditFlyoutProps, } from './types'; function createStartMock(): TriggersAndActionsUIPublicPluginStart { const actionTypeRegistry = new TypeRegistry(); - const ruleTypeRegistry = new TypeRegistry(); + const ruleTypeRegistry = new TypeRegistry(); return { actionTypeRegistry, ruleTypeRegistry, diff --git a/x-pack/plugins/triggers_actions_ui/public/plugin.ts b/x-pack/plugins/triggers_actions_ui/public/plugin.ts index 3e981a63c2b01..12ea42b6c6bf0 100644 --- a/x-pack/plugins/triggers_actions_ui/public/plugin.ts +++ b/x-pack/plugins/triggers_actions_ui/public/plugin.ts @@ -36,19 +36,19 @@ import type { ActionTypeModel, AlertAddProps, AlertEditProps, - AlertTypeModel, + RuleTypeModel, ConnectorAddFlyoutProps, ConnectorEditFlyoutProps, } from './types'; export interface TriggersAndActionsUIPublicPluginSetup { actionTypeRegistry: TypeRegistry; - ruleTypeRegistry: TypeRegistry>; + ruleTypeRegistry: TypeRegistry>; } export interface TriggersAndActionsUIPublicPluginStart { actionTypeRegistry: TypeRegistry; - ruleTypeRegistry: TypeRegistry>; + ruleTypeRegistry: TypeRegistry>; getAddConnectorFlyout: ( props: Omit ) => ReactElement; @@ -88,11 +88,11 @@ export class Plugin > { private actionTypeRegistry: TypeRegistry; - private ruleTypeRegistry: TypeRegistry; + private ruleTypeRegistry: TypeRegistry; constructor() { this.actionTypeRegistry = new TypeRegistry(); - this.ruleTypeRegistry = new TypeRegistry(); + this.ruleTypeRegistry = new TypeRegistry(); } public setup(core: CoreSetup, plugins: PluginsSetup): TriggersAndActionsUIPublicPluginSetup { diff --git a/x-pack/plugins/triggers_actions_ui/public/types.ts b/x-pack/plugins/triggers_actions_ui/public/types.ts index 37a816e78c334..bfb1c9b6280c3 100644 --- a/x-pack/plugins/triggers_actions_ui/public/types.ts +++ b/x-pack/plugins/triggers_actions_ui/public/types.ts @@ -23,7 +23,7 @@ import { TypeRegistry } from './application/type_registry'; import { ActionGroup, AlertActionParam, - SanitizedAlert, + SanitizedAlert as SanitizedRule, ResolvedSanitizedRule, AlertAction, AlertAggregations, @@ -34,18 +34,18 @@ import { RawAlertInstance, AlertingFrameworkHealth, AlertNotifyWhenType, - AlertTypeParams, + AlertTypeParams as RuleTypeParams, ActionVariable, RuleType as CommonRuleType, } from '../../alerting/common'; -// In Triggers and Actions we treat all `Alert`s as `SanitizedAlert` +// In Triggers and Actions we treat all `Alert`s as `SanitizedRule` // so the `Params` is a black-box of Record -type Alert = SanitizedAlert; -type ResolvedRule = ResolvedSanitizedRule; +type Rule = SanitizedRule; +type ResolvedRule = ResolvedSanitizedRule; export type { - Alert, + Rule, AlertAction, AlertAggregations, RuleTaskState, @@ -55,7 +55,7 @@ export type { RawAlertInstance, AlertingFrameworkHealth, AlertNotifyWhenType, - AlertTypeParams, + RuleTypeParams, ResolvedRule, }; export type { ActionType, AsApiContract }; @@ -67,12 +67,12 @@ export { }; export type ActionTypeIndex = Record; -export type RuleTypeIndex = Map; +export type RuleTypeIndex = Map; export type ActionTypeRegistryContract< ActionConnector = unknown, ActionParams = unknown > = PublicMethodsOf>>; -export type RuleTypeRegistryContract = PublicMethodsOf>; +export type RuleTypeRegistryContract = PublicMethodsOf>; export type ActionConnectorFieldsCallbacks = { beforeActionConnectorSave?: () => Promise; @@ -204,7 +204,7 @@ export const OPTIONAL_ACTION_VARIABLES = ['context'] as const; export type ActionVariables = AsActionVariables & Partial>; -export interface AlertType< +export interface RuleType< ActionGroupIds extends string = string, RecoveryActionGroupId extends string = string > extends Pick< @@ -225,31 +225,31 @@ export interface AlertType< enabledInLicense: boolean; } -export type SanitizedAlertType = Omit; +export type SanitizedRuleType = Omit; -export type AlertUpdates = Omit; +export type AlertUpdates = Omit; -export interface AlertTableItem extends Alert { - alertType: AlertType['name']; +export interface AlertTableItem extends Rule { + alertType: RuleType['name']; index: number; actionsCount: number; isEditable: boolean; enabledInLicense: boolean; } -export interface AlertTypeParamsExpressionProps< - Params extends AlertTypeParams = AlertTypeParams, +export interface RuleTypeParamsExpressionProps< + Params extends RuleTypeParams = RuleTypeParams, MetaData = Record, ActionGroupIds extends string = string > { - alertParams: Params; - alertInterval: string; - alertThrottle: string; + ruleParams: Params; + ruleInterval: string; + ruleThrottle: string; alertNotifyWhen: AlertNotifyWhenType; - setAlertParams: (property: Key, value: Params[Key] | undefined) => void; - setAlertProperty: ( + setRuleParams: (property: Key, value: Params[Key] | undefined) => void; + setRuleProperty: ( key: Prop, - value: SanitizedAlert[Prop] | null + value: SanitizedRule[Prop] | null ) => void; errors: IErrorObject; defaultActionGroupId: string; @@ -259,15 +259,15 @@ export interface AlertTypeParamsExpressionProps< data: DataPublicPluginStart; } -export interface AlertTypeModel { +export interface RuleTypeModel { id: string; description: string; iconClass: string; documentationUrl: string | ((docLinks: DocLinksStart) => string) | null; - validate: (alertParams: Params) => ValidationResult; - alertParamsExpression: + validate: (ruleParams: Params) => ValidationResult; + ruleParamsExpression: | React.FunctionComponent - | React.LazyExoticComponent>>; + | React.LazyExoticComponent>>; requiresAppContext: boolean; defaultActionMessage?: string; } @@ -299,7 +299,7 @@ export interface ConnectorEditFlyoutProps { } export interface AlertEditProps> { - initialAlert: Alert; + initialAlert: Rule; ruleTypeRegistry: RuleTypeRegistryContract; actionTypeRegistry: ActionTypeRegistryContract; onClose: (reason: AlertFlyoutCloseReason) => void; @@ -307,7 +307,7 @@ export interface AlertEditProps> { reloadAlerts?: () => Promise; onSave?: () => Promise; metadata?: MetaData; - ruleType?: AlertType; + ruleType?: RuleType; } export interface AlertAddProps> { @@ -317,7 +317,7 @@ export interface AlertAddProps> { onClose: (reason: AlertFlyoutCloseReason) => void; alertTypeId?: string; canChangeTrigger?: boolean; - initialValues?: Partial; + initialValues?: Partial; /** @deprecated use `onSave` as a callback after an alert is saved*/ reloadAlerts?: () => Promise; onSave?: () => Promise; diff --git a/x-pack/plugins/uptime/public/components/common/alerts/uptime_edit_alert_flyout.tsx b/x-pack/plugins/uptime/public/components/common/alerts/uptime_edit_alert_flyout.tsx index aab08bf225b05..fbad21cbfaed5 100644 --- a/x-pack/plugins/uptime/public/components/common/alerts/uptime_edit_alert_flyout.tsx +++ b/x-pack/plugins/uptime/public/components/common/alerts/uptime_edit_alert_flyout.tsx @@ -8,13 +8,13 @@ import React, { useMemo } from 'react'; import { useKibana } from '../../../../../../../src/plugins/kibana_react/public'; import { - Alert, + Rule, TriggersAndActionsUIPublicPluginStart, } from '../../../../../triggers_actions_ui/public'; interface Props { alertFlyoutVisible: boolean; - initialAlert: Alert; + initialAlert: Rule; setAlertFlyoutVisibility: React.Dispatch>; } diff --git a/x-pack/plugins/uptime/public/components/overview/alerts/alerts_containers/alert_monitor_status.tsx b/x-pack/plugins/uptime/public/components/overview/alerts/alerts_containers/alert_monitor_status.tsx index 39d19464caa2e..09b116c5bbc45 100644 --- a/x-pack/plugins/uptime/public/components/overview/alerts/alerts_containers/alert_monitor_status.tsx +++ b/x-pack/plugins/uptime/public/components/overview/alerts/alerts_containers/alert_monitor_status.tsx @@ -22,10 +22,10 @@ import { FILTER_FIELDS } from '../../../../../common/constants'; const { TYPE, TAGS, LOCATION, PORT } = FILTER_FIELDS; interface Props { - alertParams: { [key: string]: any }; + ruleParams: { [key: string]: any }; enabled: boolean; numTimes: number; - setAlertParams: (key: string, value: any) => void; + setRuleParams: (key: string, value: any) => void; timerange: { from: string; to: string; @@ -35,50 +35,50 @@ interface Props { export const AlertMonitorStatus: React.FC = ({ enabled, numTimes, - setAlertParams, + setRuleParams, timerange, - alertParams, + ruleParams, }) => { const dispatch = useDispatch(); useEffect(() => { - if (alertParams.search) { - dispatch(setSearchTextAction(alertParams.search)); + if (ruleParams.search) { + dispatch(setSearchTextAction(ruleParams.search)); } - }, [alertParams, dispatch]); + }, [ruleParams, dispatch]); const { count, loading } = useSnapShotCount({ - query: alertParams.search, - filters: alertParams.filters, + query: ruleParams.search, + filters: ruleParams.filters, }); const isOldAlert = React.useMemo( () => - Object.entries(alertParams).length > 0 && - !isRight(AtomicStatusCheckParamsType.decode(alertParams)) && - !isRight(GetMonitorAvailabilityParamsType.decode(alertParams)), - [alertParams] + Object.entries(ruleParams).length > 0 && + !isRight(AtomicStatusCheckParamsType.decode(ruleParams)) && + !isRight(GetMonitorAvailabilityParamsType.decode(ruleParams)), + [ruleParams] ); const selectedFilters = useSelector(selectedFiltersSelector); useEffect(() => { - if (!alertParams.filters && selectedFilters !== null) { - setAlertParams('filters', { + if (!ruleParams.filters && selectedFilters !== null) { + setRuleParams('filters', { [PORT]: selectedFilters?.ports ?? [], [LOCATION]: selectedFilters?.locations ?? [], [TYPE]: selectedFilters?.schemes ?? [], [TAGS]: selectedFilters?.tags ?? [], }); } - }, [alertParams, setAlertParams, selectedFilters]); + }, [ruleParams, setRuleParams, selectedFilters]); return ( void; + ruleParams: { [key: string]: any }; + setRuleParams: (key: string, value: any) => void; } -export function AnomalyAlertComponent({ setAlertParams, alertParams }: Props) { +export function AnomalyAlertComponent({ setRuleParams, ruleParams }: Props) { const [severity, setSeverity] = useState(DEFAULT_SEVERITY); const monitorIdStore = useSelector(monitorIdSelector); - const monitorId = monitorIdStore || alertParams?.monitorId; + const monitorId = monitorIdStore || ruleParams?.monitorId; useEffect(() => { - setAlertParams('monitorId', monitorId); - }, [monitorId, setAlertParams]); + setRuleParams('monitorId', monitorId); + }, [monitorId, setRuleParams]); useEffect(() => { - setAlertParams('severity', severity.val); - }, [severity, setAlertParams]); + setRuleParams('severity', severity.val); + }, [severity, setRuleParams]); useEffect(() => { - if (alertParams.severity !== undefined) { - setSeverity(SEVERITY_OPTIONS.find(({ val }) => val === alertParams.severity)!); + if (ruleParams.severity !== undefined) { + setSeverity(SEVERITY_OPTIONS.find(({ val }) => val === ruleParams.severity)!); } // eslint-disable-next-line react-hooks/exhaustive-deps diff --git a/x-pack/plugins/uptime/public/components/overview/alerts/monitor_expressions/availability_expression_select.tsx b/x-pack/plugins/uptime/public/components/overview/alerts/monitor_expressions/availability_expression_select.tsx index 4990d2aeaef42..b125189230ed4 100644 --- a/x-pack/plugins/uptime/public/components/overview/alerts/monitor_expressions/availability_expression_select.tsx +++ b/x-pack/plugins/uptime/public/components/overview/alerts/monitor_expressions/availability_expression_select.tsx @@ -13,9 +13,9 @@ import { AlertFieldNumber } from '../alert_field_number'; import { TimeRangeOption, TimeUnitSelectable } from './time_unit_selectable'; interface Props { - alertParams: { [param: string]: any }; + ruleParams: { [param: string]: any }; isOldAlert: boolean; - setAlertParams: (key: string, value: any) => void; + setRuleParams: (key: string, value: any) => void; hasFilters: boolean; } @@ -53,21 +53,21 @@ const DEFAULT_THRESHOLD = '99'; const isThresholdInvalid = (n: number): boolean => isNaN(n) || n <= 0 || n > 100; export const AvailabilityExpressionSelect: React.FC = ({ - alertParams, + ruleParams, isOldAlert, - setAlertParams, + setRuleParams, hasFilters, }) => { - const [range, setRange] = useState(alertParams?.availability?.range ?? DEFAULT_RANGE); + const [range, setRange] = useState(ruleParams?.availability?.range ?? DEFAULT_RANGE); const [rangeUnit, setRangeUnit] = useState( - alertParams?.availability?.rangeUnit ?? DEFAULT_TIMERANGE_UNIT + ruleParams?.availability?.rangeUnit ?? DEFAULT_TIMERANGE_UNIT ); const [threshold, setThreshold] = useState( - alertParams?.availability?.threshold ?? DEFAULT_THRESHOLD + ruleParams?.availability?.threshold ?? DEFAULT_THRESHOLD ); const [isEnabled, setIsEnabled] = useState( // if an older version of alert is displayed, this expression should default to disabled - alertParams?.shouldCheckAvailability ?? !isOldAlert + ruleParams?.shouldCheckAvailability ?? !isOldAlert ); const [timerangeUnitOptions, setTimerangeUnitOptions] = useState( TimeRangeOptions.map((opt) => @@ -79,19 +79,19 @@ export const AvailabilityExpressionSelect: React.FC = ({ useEffect(() => { if (thresholdIsInvalid) { - setAlertParams('availability', undefined); - setAlertParams('shouldCheckAvailability', false); + setRuleParams('availability', undefined); + setRuleParams('shouldCheckAvailability', false); } else if (isEnabled) { - setAlertParams('shouldCheckAvailability', true); - setAlertParams('availability', { + setRuleParams('shouldCheckAvailability', true); + setRuleParams('availability', { range, rangeUnit, threshold, }); } else { - setAlertParams('shouldCheckAvailability', false); + setRuleParams('shouldCheckAvailability', false); } - }, [isEnabled, range, rangeUnit, setAlertParams, threshold, thresholdIsInvalid]); + }, [isEnabled, range, rangeUnit, setRuleParams, threshold, thresholdIsInvalid]); return ( diff --git a/x-pack/plugins/uptime/public/components/overview/alerts/monitor_expressions/down_number_select.test.tsx b/x-pack/plugins/uptime/public/components/overview/alerts/monitor_expressions/down_number_select.test.tsx index ac2da42f7f0d9..0598b675cab35 100644 --- a/x-pack/plugins/uptime/public/components/overview/alerts/monitor_expressions/down_number_select.test.tsx +++ b/x-pack/plugins/uptime/public/components/overview/alerts/monitor_expressions/down_number_select.test.tsx @@ -12,14 +12,14 @@ import { DownNoExpressionSelect } from './down_number_select'; describe('DownNoExpressionSelect component', () => { it('should shallow renders against props', function () { const component = shallowWithIntl( - + ); expect(component).toMatchSnapshot(); }); it('should renders against props', function () { const component = renderWithIntl( - + ); expect(component).toMatchSnapshot(); }); diff --git a/x-pack/plugins/uptime/public/components/overview/alerts/monitor_expressions/down_number_select.tsx b/x-pack/plugins/uptime/public/components/overview/alerts/monitor_expressions/down_number_select.tsx index 5af72758e6c3a..a1d459f89f02e 100644 --- a/x-pack/plugins/uptime/public/components/overview/alerts/monitor_expressions/down_number_select.tsx +++ b/x-pack/plugins/uptime/public/components/overview/alerts/monitor_expressions/down_number_select.tsx @@ -14,20 +14,20 @@ interface Props { isEnabled?: boolean; defaultNumTimes?: number; hasFilters: boolean; - setAlertParams: (key: string, value: any) => void; + setRuleParams: (key: string, value: any) => void; } export const DownNoExpressionSelect: React.FC = ({ defaultNumTimes, hasFilters, isEnabled, - setAlertParams, + setRuleParams, }) => { const [numTimes, setNumTimes] = useState(defaultNumTimes ?? 5); useEffect(() => { - setAlertParams('numTimes', numTimes); - }, [numTimes, setAlertParams]); + setRuleParams('numTimes', numTimes); + }, [numTimes, setRuleParams]); return ( { it('is empty when no filters available', async () => { const { queryByLabelText } = render( ); @@ -52,10 +52,10 @@ describe('FiltersExpressionSelect', () => { ])('contains provided new filter values', async (newFilters, expectedLabels, absentLabels) => { const { getByLabelText, queryByLabelText } = render( ); @@ -75,10 +75,10 @@ describe('FiltersExpressionSelect', () => { const setAlertParamsMock = jest.fn(); const { getByLabelText } = render( ); @@ -115,10 +115,10 @@ describe('FiltersExpressionSelect', () => { spy.mockReturnValue({ loading: false, values: [{ label: 'test-label', count: 3 }] }); const { getByLabelText, getByText } = render( ); diff --git a/x-pack/plugins/uptime/public/components/overview/alerts/monitor_expressions/filters_expression_select.tsx b/x-pack/plugins/uptime/public/components/overview/alerts/monitor_expressions/filters_expression_select.tsx index 8a3343d57f19d..e10e9267df184 100644 --- a/x-pack/plugins/uptime/public/components/overview/alerts/monitor_expressions/filters_expression_select.tsx +++ b/x-pack/plugins/uptime/public/components/overview/alerts/monitor_expressions/filters_expression_select.tsx @@ -15,22 +15,22 @@ import { FILTER_FIELDS } from '../../../../../common/constants'; import { useGetUrlParams } from '../../../../hooks'; export interface FilterExpressionsSelectProps { - alertParams: { [key: string]: any }; + ruleParams: { [key: string]: any }; newFilters: string[]; onRemoveFilter: (val: string) => void; - setAlertParams: (key: string, value: any) => void; + setRuleParams: (key: string, value: any) => void; shouldUpdateUrl: boolean; } const { TYPE, TAGS, LOCATION, PORT } = FILTER_FIELDS; export const FiltersExpressionsSelect: React.FC = ({ - alertParams, + ruleParams, newFilters, onRemoveFilter, - setAlertParams, + setRuleParams, }) => { - const alertFilters = alertParams?.filters; + const alertFilters = ruleParams?.filters; const selectedPorts = alertFilters?.[PORT] ?? []; const selectedLocations = alertFilters?.[LOCATION] ?? []; @@ -40,10 +40,10 @@ export const FiltersExpressionsSelect: React.FC = const { dateRangeStart: from, dateRangeEnd: to } = useGetUrlParams(); const onFilterFieldChange = (fieldName: string, values?: string[]) => { // the `filters` field is no longer a string - if (alertParams.filters && typeof alertParams.filters !== 'string') { - setAlertParams('filters', { ...alertParams.filters, [fieldName]: values }); + if (ruleParams.filters && typeof ruleParams.filters !== 'string') { + setRuleParams('filters', { ...ruleParams.filters, [fieldName]: values }); } else { - setAlertParams( + setRuleParams( 'filters', Object.assign( {}, diff --git a/x-pack/plugins/uptime/public/components/overview/alerts/monitor_expressions/status_expression_select.tsx b/x-pack/plugins/uptime/public/components/overview/alerts/monitor_expressions/status_expression_select.tsx index 018b81e399752..7c2489c707c5c 100644 --- a/x-pack/plugins/uptime/public/components/overview/alerts/monitor_expressions/status_expression_select.tsx +++ b/x-pack/plugins/uptime/public/components/overview/alerts/monitor_expressions/status_expression_select.tsx @@ -12,21 +12,21 @@ import { TimeExpressionSelect } from './time_expression_select'; import { statusExpLabels } from './translations'; interface Props { - alertParams: { [param: string]: any }; + ruleParams: { [param: string]: any }; hasFilters: boolean; - setAlertParams: (key: string, value: any) => void; + setRuleParams: (key: string, value: any) => void; } export const StatusExpressionSelect: React.FC = ({ - alertParams, + ruleParams, hasFilters, - setAlertParams, + setRuleParams, }) => { - const [isEnabled, setIsEnabled] = useState(alertParams.shouldCheckStatus ?? true); + const [isEnabled, setIsEnabled] = useState(ruleParams.shouldCheckStatus ?? true); useEffect(() => { - setAlertParams('shouldCheckStatus', isEnabled); - }, [isEnabled, setAlertParams]); + setRuleParams('shouldCheckStatus', isEnabled); + }, [isEnabled, setRuleParams]); return ( @@ -40,18 +40,18 @@ export const StatusExpressionSelect: React.FC = ({ diff --git a/x-pack/plugins/uptime/public/components/overview/alerts/monitor_expressions/time_expression_select.test.tsx b/x-pack/plugins/uptime/public/components/overview/alerts/monitor_expressions/time_expression_select.test.tsx index d1a8ba12077de..4ce9720cfc4ce 100644 --- a/x-pack/plugins/uptime/public/components/overview/alerts/monitor_expressions/time_expression_select.test.tsx +++ b/x-pack/plugins/uptime/public/components/overview/alerts/monitor_expressions/time_expression_select.test.tsx @@ -11,12 +11,12 @@ import { TimeExpressionSelect } from './time_expression_select'; describe('TimeExpressionSelect component', () => { it('should shallow renders against props', function () { - const component = shallowWithIntl(); + const component = shallowWithIntl(); expect(component).toMatchSnapshot(); }); it('should renders against props', function () { - const component = renderWithIntl(); + const component = renderWithIntl(); expect(component).toMatchSnapshot(); }); }); diff --git a/x-pack/plugins/uptime/public/components/overview/alerts/monitor_expressions/time_expression_select.tsx b/x-pack/plugins/uptime/public/components/overview/alerts/monitor_expressions/time_expression_select.tsx index 5b562ab984cb4..957a289386564 100644 --- a/x-pack/plugins/uptime/public/components/overview/alerts/monitor_expressions/time_expression_select.tsx +++ b/x-pack/plugins/uptime/public/components/overview/alerts/monitor_expressions/time_expression_select.tsx @@ -17,7 +17,7 @@ interface Props { defaultTimerangeCount?: number; defaultTimerangeUnit?: string; isEnabled?: boolean; - setAlertParams: (key: string, value: any) => void; + setRuleParams: (key: string, value: any) => void; } const DEFAULT_TIMERANGE_UNIT = 'm'; @@ -53,7 +53,7 @@ export const TimeExpressionSelect: React.FC = ({ defaultTimerangeCount, defaultTimerangeUnit, isEnabled, - setAlertParams, + setRuleParams, }) => { const [numUnits, setNumUnits] = useState(defaultTimerangeCount ?? 15); @@ -65,9 +65,9 @@ export const TimeExpressionSelect: React.FC = ({ useEffect(() => { const timerangeUnit = timerangeUnitOptions.find(({ checked }) => checked === 'on')?.key ?? 'm'; - setAlertParams('timerangeUnit', timerangeUnit); - setAlertParams('timerangeCount', numUnits); - }, [numUnits, timerangeUnitOptions, setAlertParams]); + setRuleParams('timerangeUnit', timerangeUnit); + setRuleParams('timerangeCount', numUnits); + }, [numUnits, timerangeUnitOptions, setRuleParams]); return ( diff --git a/x-pack/plugins/uptime/public/components/overview/alerts/monitor_status_alert/alert_monitor_status.test.tsx b/x-pack/plugins/uptime/public/components/overview/alerts/monitor_status_alert/alert_monitor_status.test.tsx index aa5e2a7db6461..6dbef12159a18 100644 --- a/x-pack/plugins/uptime/public/components/overview/alerts/monitor_status_alert/alert_monitor_status.test.tsx +++ b/x-pack/plugins/uptime/public/components/overview/alerts/monitor_status_alert/alert_monitor_status.test.tsx @@ -50,7 +50,7 @@ describe('alert monitor status component', () => { describe('AlertMonitorStatus', () => { const defaultProps: AlertMonitorStatusProps = { - alertParams: { + ruleParams: { numTimes: 3, search: 'monitor.id: foo', timerangeUnit: 'h', @@ -61,7 +61,7 @@ describe('alert monitor status component', () => { snapshotCount: 0, snapshotLoading: false, numTimes: 14, - setAlertParams: jest.fn(), + setRuleParams: jest.fn(), timerange: { from: 'now-12h', to: 'now' }, }; diff --git a/x-pack/plugins/uptime/public/components/overview/alerts/monitor_status_alert/alert_monitor_status.tsx b/x-pack/plugins/uptime/public/components/overview/alerts/monitor_status_alert/alert_monitor_status.tsx index c3524dbc78533..577df06802e40 100644 --- a/x-pack/plugins/uptime/public/components/overview/alerts/monitor_status_alert/alert_monitor_status.tsx +++ b/x-pack/plugins/uptime/public/components/overview/alerts/monitor_status_alert/alert_monitor_status.tsx @@ -17,13 +17,13 @@ import { useGetUrlParams } from '../../../../hooks'; import { FILTER_FIELDS } from '../../../../../common/constants'; export interface AlertMonitorStatusProps { - alertParams: { [key: string]: any }; + ruleParams: { [key: string]: any }; enabled: boolean; isOldAlert: boolean; snapshotCount: number; snapshotLoading?: boolean; numTimes: number; - setAlertParams: (key: string, value: any) => void; + setRuleParams: (key: string, value: any) => void; timerange: { from: string; to: string; @@ -39,9 +39,9 @@ export const hasFilters = (filters?: { [key: string]: string[] }) => { }; export const AlertMonitorStatusComponent: React.FC = (props) => { - const { alertParams, isOldAlert, setAlertParams, snapshotCount, snapshotLoading } = props; + const { ruleParams, isOldAlert, setRuleParams, snapshotCount, snapshotLoading } = props; - const alertFilters = alertParams?.filters ?? {}; + const alertFilters = ruleParams?.filters ?? {}; const [newFilters, setNewFilters] = useState( Object.keys(alertFilters).filter((f) => alertFilters[f].length) ); @@ -50,16 +50,16 @@ export const AlertMonitorStatusComponent: React.FC = (p useEffect(() => { if (search) { - setAlertParams('search', search); + setRuleParams('search', search); } // eslint-disable-next-line react-hooks/exhaustive-deps }, []); const onSearchChange = useCallback( (value: string) => { - setAlertParams('search', value); + setRuleParams('search', value); }, - [setAlertParams] + [setRuleParams] ); return ( @@ -83,12 +83,12 @@ export const AlertMonitorStatusComponent: React.FC = (p - + { setNewFilters([...newFilters, newFilter]); @@ -96,32 +96,32 @@ export const AlertMonitorStatusComponent: React.FC = (p /> { if (newFilters.includes(removeFilter)) { setNewFilters(newFilters.filter((item) => item !== removeFilter)); } }} - setAlertParams={setAlertParams} + setRuleParams={setRuleParams} shouldUpdateUrl={false} /> diff --git a/x-pack/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/enabled_alerts.tsx b/x-pack/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/enabled_alerts.tsx index ca38dff4606ad..ca36cae058bb0 100644 --- a/x-pack/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/enabled_alerts.tsx +++ b/x-pack/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/enabled_alerts.tsx @@ -18,10 +18,10 @@ import { EuiListGroupItemProps } from '@elastic/eui/src/components/list_group/li import { i18n } from '@kbn/i18n'; import styled from 'styled-components'; import { UptimeSettingsContext } from '../../../../contexts'; -import { Alert } from '../../../../../../triggers_actions_ui/public'; +import { Rule } from '../../../../../../triggers_actions_ui/public'; interface Props { - monitorAlerts: Alert[]; + monitorAlerts: Rule[]; loading: boolean; } diff --git a/x-pack/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/monitor_list_drawer.tsx b/x-pack/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/monitor_list_drawer.tsx index 43a201b6c25b6..0596c76662a58 100644 --- a/x-pack/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/monitor_list_drawer.tsx +++ b/x-pack/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/monitor_list_drawer.tsx @@ -8,7 +8,7 @@ import React from 'react'; import styled from 'styled-components'; import { EuiFlexGroup, EuiFlexItem, EuiSpacer } from '@elastic/eui'; -import { Alert } from '../../../../../../triggers_actions_ui/public'; +import { Rule } from '../../../../../../triggers_actions_ui/public'; import { MostRecentError } from './most_recent_error'; import { MonitorStatusList } from './monitor_status_list'; import { MonitorDetails, MonitorSummary } from '../../../../../common/runtime_types'; @@ -67,7 +67,7 @@ export function MonitorListDrawerComponent({ - + {monitorDetails && monitorDetails.error && ( ( + ruleParamsExpression: (params: unknown) => ( ), description, diff --git a/x-pack/plugins/uptime/public/lib/alert_types/index.ts b/x-pack/plugins/uptime/public/lib/alert_types/index.ts index 9dc67340a0436..f2e721f9c8f40 100644 --- a/x-pack/plugins/uptime/public/lib/alert_types/index.ts +++ b/x-pack/plugins/uptime/public/lib/alert_types/index.ts @@ -7,7 +7,7 @@ import { CoreStart } from 'kibana/public'; import { ObservabilityRuleTypeModel } from '../../../../observability/public'; -import { AlertTypeModel } from '../../../../triggers_actions_ui/public'; +import { RuleTypeModel } from '../../../../triggers_actions_ui/public'; import { initMonitorStatusAlertType } from './monitor_status'; import { initTlsAlertType } from './tls'; import { initTlsLegacyAlertType } from './tls_legacy'; @@ -25,6 +25,6 @@ export const alertTypeInitializers: AlertTypeInitializer[] = [ initDurationAnomalyAlertType, ]; -export const legacyAlertTypeInitializers: Array> = [ +export const legacyAlertTypeInitializers: Array> = [ initTlsLegacyAlertType, ]; diff --git a/x-pack/plugins/uptime/public/lib/alert_types/lazy_wrapper/validate_monitor_status.ts b/x-pack/plugins/uptime/public/lib/alert_types/lazy_wrapper/validate_monitor_status.ts index 0c548863204be..45af394b65723 100644 --- a/x-pack/plugins/uptime/public/lib/alert_types/lazy_wrapper/validate_monitor_status.ts +++ b/x-pack/plugins/uptime/public/lib/alert_types/lazy_wrapper/validate_monitor_status.ts @@ -14,11 +14,11 @@ import { } from '../../../../common/runtime_types/alerts'; import { ValidationResult } from '../../../../../triggers_actions_ui/public'; -export function validateMonitorStatusParams(alertParams: any): ValidationResult { +export function validateMonitorStatusParams(ruleParams: any): ValidationResult { const errors: Record = {}; - const decoded = AtomicStatusCheckParamsType.decode(alertParams); - const oldDecoded = StatusCheckParamsType.decode(alertParams); - const availabilityDecoded = MonitorAvailabilityType.decode(alertParams); + const decoded = AtomicStatusCheckParamsType.decode(ruleParams); + const oldDecoded = StatusCheckParamsType.decode(ruleParams); + const availabilityDecoded = MonitorAvailabilityType.decode(ruleParams); if (!isRight(decoded) && !isRight(oldDecoded) && !isRight(availabilityDecoded)) { return { @@ -29,10 +29,7 @@ export function validateMonitorStatusParams(alertParams: any): ValidationResult }; } - if ( - !(alertParams.shouldCheckAvailability ?? false) && - !(alertParams.shouldCheckStatus ?? false) - ) { + if (!(ruleParams.shouldCheckAvailability ?? false) && !(ruleParams.shouldCheckStatus ?? false)) { return { errors: { noAlertSelected: 'Alert must check for monitor status or monitor availability.', diff --git a/x-pack/plugins/uptime/public/lib/alert_types/monitor_status.test.ts b/x-pack/plugins/uptime/public/lib/alert_types/monitor_status.test.ts index 16c20cf7666ee..201464a2425a7 100644 --- a/x-pack/plugins/uptime/public/lib/alert_types/monitor_status.test.ts +++ b/x-pack/plugins/uptime/public/lib/alert_types/monitor_status.test.ts @@ -202,7 +202,6 @@ describe('monitor status alert type', () => { }) ).toMatchInlineSnapshot(` Object { - "alertParamsExpression": [Function], "defaultActionMessage": "Monitor {{state.monitorName}} with url {{{state.monitorUrl}}} is {{state.statusMessage}} from {{state.observerLocation}}. The latest error message is {{{state.latestErrorMessage}}}", "description": "Alert when a monitor is down or an availability threshold is breached.", "documentationUrl": [Function], @@ -210,6 +209,7 @@ describe('monitor status alert type', () => { "iconClass": "uptimeApp", "id": "xpack.uptime.alerts.monitorStatus", "requiresAppContext": false, + "ruleParamsExpression": [Function], "validate": [Function], } `); diff --git a/x-pack/plugins/uptime/public/lib/alert_types/monitor_status.tsx b/x-pack/plugins/uptime/public/lib/alert_types/monitor_status.tsx index b71d1bab3fed8..98ca4a8bd7b7a 100644 --- a/x-pack/plugins/uptime/public/lib/alert_types/monitor_status.tsx +++ b/x-pack/plugins/uptime/public/lib/alert_types/monitor_status.tsx @@ -27,7 +27,7 @@ const { defaultActionMessage, description } = MonitorStatusTranslations; const MonitorStatusAlert = React.lazy(() => import('./lazy_wrapper/monitor_status')); -let validateFunc: (alertParams: any) => ValidationResult; +let validateFunc: (ruleParams: any) => ValidationResult; export const initMonitorStatusAlertType: AlertTypeInitializer = ({ core, @@ -39,10 +39,10 @@ export const initMonitorStatusAlertType: AlertTypeInitializer = ({ documentationUrl(docLinks) { return `${docLinks.links.observability.monitorStatus}`; }, - alertParamsExpression: (params: any) => ( + ruleParamsExpression: (params: any) => ( ), - validate: (alertParams: any) => { + validate: (ruleParams: any) => { if (!validateFunc) { (async function loadValidate() { const { validateMonitorStatusParams } = await import( @@ -51,7 +51,7 @@ export const initMonitorStatusAlertType: AlertTypeInitializer = ({ validateFunc = validateMonitorStatusParams; })(); } - return validateFunc ? validateFunc(alertParams) : ({} as ValidationResult); + return validateFunc ? validateFunc(ruleParams) : ({} as ValidationResult); }, defaultActionMessage, requiresAppContext: false, diff --git a/x-pack/plugins/uptime/public/lib/alert_types/tls.tsx b/x-pack/plugins/uptime/public/lib/alert_types/tls.tsx index ca98b74943f07..64bad0f4bed2f 100644 --- a/x-pack/plugins/uptime/public/lib/alert_types/tls.tsx +++ b/x-pack/plugins/uptime/public/lib/alert_types/tls.tsx @@ -25,9 +25,7 @@ export const initTlsAlertType: AlertTypeInitializer = ({ documentationUrl(docLinks) { return `${docLinks.links.observability.tlsCertificate}`; }, - alertParamsExpression: (params: any) => ( - - ), + ruleParamsExpression: (params: any) => , description, validate: () => ({ errors: {} }), defaultActionMessage, diff --git a/x-pack/plugins/uptime/public/lib/alert_types/tls_legacy.tsx b/x-pack/plugins/uptime/public/lib/alert_types/tls_legacy.tsx index bed5dae555710..a2b145ce716d6 100644 --- a/x-pack/plugins/uptime/public/lib/alert_types/tls_legacy.tsx +++ b/x-pack/plugins/uptime/public/lib/alert_types/tls_legacy.tsx @@ -6,25 +6,23 @@ */ import React from 'react'; -import { AlertTypeModel } from '../../../../triggers_actions_ui/public'; +import { RuleTypeModel } from '../../../../triggers_actions_ui/public'; import { CLIENT_ALERT_TYPES } from '../../../common/constants/alerts'; import { TlsTranslationsLegacy } from '../../../common/translations'; import { AlertTypeInitializer } from '.'; const { defaultActionMessage, description } = TlsTranslationsLegacy; const TLSAlert = React.lazy(() => import('./lazy_wrapper/tls_alert')); -export const initTlsLegacyAlertType: AlertTypeInitializer = ({ +export const initTlsLegacyAlertType: AlertTypeInitializer = ({ core, plugins, -}): AlertTypeModel => ({ +}): RuleTypeModel => ({ id: CLIENT_ALERT_TYPES.TLS_LEGACY, iconClass: 'uptimeApp', documentationUrl(docLinks) { return `${docLinks.ELASTIC_WEBSITE_URL}guide/en/observability/${docLinks.DOC_LINK_VERSION}/tls-certificate-alert.html`; }, - alertParamsExpression: (params: any) => ( - - ), + ruleParamsExpression: (params: any) => , description, validate: () => ({ errors: {} }), defaultActionMessage, diff --git a/x-pack/plugins/uptime/public/state/actions/alerts.ts b/x-pack/plugins/uptime/public/state/actions/alerts.ts index 311a52deb8dfe..e62e7830951a3 100644 --- a/x-pack/plugins/uptime/public/state/actions/alerts.ts +++ b/x-pack/plugins/uptime/public/state/actions/alerts.ts @@ -7,9 +7,9 @@ import { createAsyncAction } from './utils'; import { MonitorIdParam } from './types'; -import { Alert } from '../../../../triggers_actions_ui/public'; +import { Rule } from '../../../../triggers_actions_ui/public'; -export const getExistingAlertAction = createAsyncAction( +export const getExistingAlertAction = createAsyncAction( 'GET EXISTING ALERTS' ); diff --git a/x-pack/test/functional_with_es_ssl/fixtures/plugins/alerts/public/plugin.ts b/x-pack/test/functional_with_es_ssl/fixtures/plugins/alerts/public/plugin.ts index ee33c78fefbe5..a5fa20fca25ef 100644 --- a/x-pack/test/functional_with_es_ssl/fixtures/plugins/alerts/public/plugin.ts +++ b/x-pack/test/functional_with_es_ssl/fixtures/plugins/alerts/public/plugin.ts @@ -32,7 +32,7 @@ export class AlertingFixturePlugin implements Plugin React.createElement('div', null, 'Test Always Firing'), + ruleParamsExpression: () => React.createElement('div', null, 'Test Always Firing'), validate: () => { return { errors: {} }; }, @@ -44,7 +44,7 @@ export class AlertingFixturePlugin implements Plugin React.createElement('div', null, 'Test Noop'), + ruleParamsExpression: () => React.createElement('div', null, 'Test Noop'), validate: () => { return { errors: {} }; }, From 6e14dc727d20a35330cfb999d624d873588e1654 Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Fri, 17 Dec 2021 18:29:39 +0000 Subject: [PATCH 13/40] chore(NA): splits types from code on @kbn/plugin-generator (#121511) * chore(NA): splits types from code on @kbn/monaco * chore(NA): splits types from code on @kbn/plugin-generator --- package.json | 1 + packages/BUILD.bazel | 1 + packages/kbn-plugin-generator/BUILD.bazel | 26 ++++++++++++++++++---- packages/kbn-plugin-generator/package.json | 3 +-- yarn.lock | 4 ++++ 5 files changed, 29 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 8777bb476de80..1731de4cb4873 100644 --- a/package.json +++ b/package.json @@ -577,6 +577,7 @@ "@types/kbn__mapbox-gl": "link:bazel-bin/packages/kbn-mapbox-gl/npm_module_types", "@types/kbn__monaco": "link:bazel-bin/packages/kbn-monaco/npm_module_types", "@types/kbn__optimizer": "link:bazel-bin/packages/kbn-optimizer/npm_module_types", + "@types/kbn__plugin-generator": "link:bazel-bin/packages/kbn-plugin-generator/npm_module_types", "@types/license-checker": "15.0.0", "@types/listr": "^0.14.0", "@types/loader-utils": "^1.1.3", diff --git a/packages/BUILD.bazel b/packages/BUILD.bazel index 5045d919f21cb..5db97875ec3ee 100644 --- a/packages/BUILD.bazel +++ b/packages/BUILD.bazel @@ -96,6 +96,7 @@ filegroup( "//packages/kbn-mapbox-gl:build_types", "//packages/kbn-monaco:build_types", "//packages/kbn-optimizer:build_types", + "//packages/kbn-plugin-generator:build_types", ], ) diff --git a/packages/kbn-plugin-generator/BUILD.bazel b/packages/kbn-plugin-generator/BUILD.bazel index 488f09bdd5d52..3fef9531b57a1 100644 --- a/packages/kbn-plugin-generator/BUILD.bazel +++ b/packages/kbn-plugin-generator/BUILD.bazel @@ -1,9 +1,10 @@ -load("@npm//@bazel/typescript:index.bzl", "ts_config", "ts_project") -load("@build_bazel_rules_nodejs//:index.bzl", "js_library", "pkg_npm") -load("//src/dev/bazel:index.bzl", "jsts_transpiler") +load("@npm//@bazel/typescript:index.bzl", "ts_config") +load("@build_bazel_rules_nodejs//:index.bzl", "js_library") +load("//src/dev/bazel:index.bzl", "jsts_transpiler", "pkg_npm", "pkg_npm_types", "ts_project") PKG_BASE_NAME = "kbn-plugin-generator" PKG_REQUIRE_NAME = "@kbn/plugin-generator" +TYPES_PKG_REQUIRE_NAME = "@types/kbn__plugin-generator" SOURCE_FILES = glob( [ @@ -96,7 +97,7 @@ ts_project( js_library( name = PKG_BASE_NAME, srcs = NPM_MODULE_EXTRA_FILES, - deps = RUNTIME_DEPS + [":target_node", ":tsc_types"], + deps = RUNTIME_DEPS + [":target_node"], package_name = PKG_REQUIRE_NAME, visibility = ["//visibility:public"], ) @@ -115,3 +116,20 @@ filegroup( ], visibility = ["//visibility:public"], ) + +pkg_npm_types( + name = "npm_module_types", + srcs = SRCS, + deps = [":tsc_types"], + package_name = TYPES_PKG_REQUIRE_NAME, + tsconfig = ":tsconfig", + visibility = ["//visibility:public"], +) + +filegroup( + name = "build_types", + srcs = [ + ":npm_module_types", + ], + visibility = ["//visibility:public"], +) diff --git a/packages/kbn-plugin-generator/package.json b/packages/kbn-plugin-generator/package.json index d30f25e478dcf..28b7e849ab3c1 100644 --- a/packages/kbn-plugin-generator/package.json +++ b/packages/kbn-plugin-generator/package.json @@ -3,6 +3,5 @@ "version": "1.0.0", "private": true, "license": "SSPL-1.0 OR Elastic License 2.0", - "main": "target_node/index.js", - "types": "target_types/index.d.ts" + "main": "target_node/index.js" } \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 6d41bda2de51e..b56dea0f84187 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5877,6 +5877,10 @@ version "0.0.0" uid "" +"@types/kbn__plugin-generator@link:bazel-bin/packages/kbn-plugin-generator/npm_module_types": + version "0.0.0" + uid "" + "@types/keyv@*": version "3.1.1" resolved "https://registry.yarnpkg.com/@types/keyv/-/keyv-3.1.1.tgz#e45a45324fca9dab716ab1230ee249c9fb52cfa7" From ca98cc682c87153314a7d3eee6a9e8e52e4c2a56 Mon Sep 17 00:00:00 2001 From: Jonathan Budzenski Date: Fri, 17 Dec 2021 12:46:33 -0600 Subject: [PATCH 14/40] [build/docker] Replace CentOS base image with Ubuntu (#118928) * [build/docker] Replace CentOS base image with Ubuntu * update snapshots * cleanup * DEBIAN_FRONTEND=noninteractive Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .buildkite/scripts/build_kibana.sh | 2 +- src/dev/build/args.test.ts | 18 ++++++------- src/dev/build/args.ts | 6 ++--- src/dev/build/build_distributables.ts | 8 +++--- src/dev/build/cli.ts | 2 +- .../os_packages/create_os_package_tasks.ts | 8 ++++-- .../tasks/os_packages/docker_generator/run.ts | 7 ++++-- .../docker_generator/template_context.ts | 1 + .../templates/base/Dockerfile | 25 ++++++++++++++----- .../templates/dockerfile.template.ts | 2 +- 10 files changed, 50 insertions(+), 29 deletions(-) diff --git a/.buildkite/scripts/build_kibana.sh b/.buildkite/scripts/build_kibana.sh index 007dd19a2c0e1..d05fe178b72db 100755 --- a/.buildkite/scripts/build_kibana.sh +++ b/.buildkite/scripts/build_kibana.sh @@ -28,7 +28,7 @@ if [[ "${GITHUB_PR_LABELS:-}" == *"ci:deploy-cloud"* ]]; then --docker-tag-qualifier="$GIT_COMMIT" \ --docker-push \ --skip-docker-ubi \ - --skip-docker-centos \ + --skip-docker-ubuntu \ --skip-docker-contexts CLOUD_IMAGE=$(docker images --format "{{.Repository}}:{{.Tag}}" docker.elastic.co/kibana-ci/kibana-cloud) diff --git a/src/dev/build/args.test.ts b/src/dev/build/args.test.ts index 354de9fee60d7..5601e063414b3 100644 --- a/src/dev/build/args.test.ts +++ b/src/dev/build/args.test.ts @@ -28,10 +28,10 @@ it('build default and oss dist for current platform, without packages, by defaul "buildOptions": Object { "createArchives": true, "createDebPackage": false, - "createDockerCentOS": false, "createDockerCloud": false, "createDockerContexts": true, "createDockerUBI": false, + "createDockerUbuntu": false, "createExamplePlugins": false, "createGenericFolders": true, "createPlatformFolders": true, @@ -58,10 +58,10 @@ it('builds packages if --all-platforms is passed', () => { "buildOptions": Object { "createArchives": true, "createDebPackage": true, - "createDockerCentOS": true, "createDockerCloud": true, "createDockerContexts": true, "createDockerUBI": true, + "createDockerUbuntu": true, "createExamplePlugins": false, "createGenericFolders": true, "createPlatformFolders": true, @@ -88,10 +88,10 @@ it('limits packages if --rpm passed with --all-platforms', () => { "buildOptions": Object { "createArchives": true, "createDebPackage": false, - "createDockerCentOS": false, "createDockerCloud": false, "createDockerContexts": true, "createDockerUBI": false, + "createDockerUbuntu": false, "createExamplePlugins": false, "createGenericFolders": true, "createPlatformFolders": true, @@ -118,10 +118,10 @@ it('limits packages if --deb passed with --all-platforms', () => { "buildOptions": Object { "createArchives": true, "createDebPackage": true, - "createDockerCentOS": false, "createDockerCloud": false, "createDockerContexts": true, "createDockerUBI": false, + "createDockerUbuntu": false, "createExamplePlugins": false, "createGenericFolders": true, "createPlatformFolders": true, @@ -149,10 +149,10 @@ it('limits packages if --docker passed with --all-platforms', () => { "buildOptions": Object { "createArchives": true, "createDebPackage": false, - "createDockerCentOS": true, "createDockerCloud": true, "createDockerContexts": true, "createDockerUBI": true, + "createDockerUbuntu": true, "createExamplePlugins": false, "createGenericFolders": true, "createPlatformFolders": true, @@ -187,10 +187,10 @@ it('limits packages if --docker passed with --skip-docker-ubi and --all-platform "buildOptions": Object { "createArchives": true, "createDebPackage": false, - "createDockerCentOS": true, "createDockerCloud": true, "createDockerContexts": true, "createDockerUBI": false, + "createDockerUbuntu": true, "createExamplePlugins": false, "createGenericFolders": true, "createPlatformFolders": true, @@ -211,17 +211,17 @@ it('limits packages if --docker passed with --skip-docker-ubi and --all-platform `); }); -it('limits packages if --all-platforms passed with --skip-docker-centos', () => { - expect(readCliArgs(['node', 'scripts/build', '--all-platforms', '--skip-docker-centos'])) +it('limits packages if --all-platforms passed with --skip-docker-ubuntu', () => { + expect(readCliArgs(['node', 'scripts/build', '--all-platforms', '--skip-docker-ubuntu'])) .toMatchInlineSnapshot(` Object { "buildOptions": Object { "createArchives": true, "createDebPackage": true, - "createDockerCentOS": false, "createDockerCloud": true, "createDockerContexts": true, "createDockerUBI": true, + "createDockerUbuntu": false, "createExamplePlugins": false, "createGenericFolders": true, "createPlatformFolders": true, diff --git a/src/dev/build/args.ts b/src/dev/build/args.ts index 78f097b595c40..d890dbef4e74f 100644 --- a/src/dev/build/args.ts +++ b/src/dev/build/args.ts @@ -26,7 +26,7 @@ export function readCliArgs(argv: string[]) { 'docker-push', 'skip-docker-contexts', 'skip-docker-ubi', - 'skip-docker-centos', + 'skip-docker-ubuntu', 'skip-docker-cloud', 'release', 'skip-node-download', @@ -109,8 +109,8 @@ export function readCliArgs(argv: string[]) { createExamplePlugins: Boolean(flags['example-plugins']), createRpmPackage: isOsPackageDesired('rpm'), createDebPackage: isOsPackageDesired('deb'), - createDockerCentOS: - isOsPackageDesired('docker-images') && !Boolean(flags['skip-docker-centos']), + createDockerUbuntu: + isOsPackageDesired('docker-images') && !Boolean(flags['skip-docker-ubuntu']), createDockerCloud: isOsPackageDesired('docker-images') && !Boolean(flags['skip-docker-cloud']), createDockerUBI: isOsPackageDesired('docker-images') && !Boolean(flags['skip-docker-ubi']), createDockerContexts: !Boolean(flags['skip-docker-contexts']), diff --git a/src/dev/build/build_distributables.ts b/src/dev/build/build_distributables.ts index 9e3a419d63964..992486796dfad 100644 --- a/src/dev/build/build_distributables.ts +++ b/src/dev/build/build_distributables.ts @@ -24,7 +24,7 @@ export interface BuildOptions { createRpmPackage: boolean; createDebPackage: boolean; createDockerUBI: boolean; - createDockerCentOS: boolean; + createDockerUbuntu: boolean; createDockerCloud: boolean; createDockerContexts: boolean; versionQualifier: string | undefined; @@ -126,9 +126,9 @@ export async function buildDistributables(log: ToolingLog, options: BuildOptions await run(Tasks.CreateDockerUBI); } - if (options.createDockerCentOS) { - // control w/ --docker-images or --skip-docker-centos or --skip-os-packages - await run(Tasks.CreateDockerCentOS); + if (options.createDockerUbuntu) { + // control w/ --docker-images or --skip-docker-ubuntu or --skip-os-packages + await run(Tasks.CreateDockerUbuntu); } if (options.createDockerCloud) { diff --git a/src/dev/build/cli.ts b/src/dev/build/cli.ts index c727c26d7dcd3..323a40d02de66 100644 --- a/src/dev/build/cli.ts +++ b/src/dev/build/cli.ts @@ -41,7 +41,7 @@ if (showHelp) { --docker-images {dim Only build the Docker images} --docker-contexts {dim Only build the Docker build contexts} --skip-docker-ubi {dim Don't build the docker ubi image} - --skip-docker-centos {dim Don't build the docker centos image} + --skip-docker-ubuntu {dim Don't build the docker ubuntu image} --release {dim Produce a release-ready distributable} --version-qualifier {dim Suffix version with a qualifier} --skip-node-download {dim Reuse existing downloads of node.js} diff --git a/src/dev/build/tasks/os_packages/create_os_package_tasks.ts b/src/dev/build/tasks/os_packages/create_os_package_tasks.ts index ab9a7ce65cbc6..b4724085c5184 100644 --- a/src/dev/build/tasks/os_packages/create_os_package_tasks.ts +++ b/src/dev/build/tasks/os_packages/create_os_package_tasks.ts @@ -59,20 +59,22 @@ export const CreateRpmPackage: Task = { }; const dockerBuildDate = new Date().toISOString(); -export const CreateDockerCentOS: Task = { - description: 'Creating Docker CentOS image', +export const CreateDockerUbuntu: Task = { + description: 'Creating Docker Ubuntu image', async run(config, log, build) { await runDockerGenerator(config, log, build, { architecture: 'x64', context: false, image: true, + ubuntu: true, dockerBuildDate, }); await runDockerGenerator(config, log, build, { architecture: 'aarch64', context: false, image: true, + ubuntu: true, dockerBuildDate, }); }, @@ -99,12 +101,14 @@ export const CreateDockerCloud: Task = { architecture: 'x64', context: false, cloud: true, + ubuntu: true, image: true, }); await runDockerGenerator(config, log, build, { architecture: 'aarch64', context: false, cloud: true, + ubuntu: true, image: true, }); }, diff --git a/src/dev/build/tasks/os_packages/docker_generator/run.ts b/src/dev/build/tasks/os_packages/docker_generator/run.ts index 085b4393caa66..657efc8d7bd99 100644 --- a/src/dev/build/tasks/os_packages/docker_generator/run.ts +++ b/src/dev/build/tasks/os_packages/docker_generator/run.ts @@ -32,13 +32,15 @@ export async function runDockerGenerator( context: boolean; image: boolean; ubi?: boolean; + ubuntu?: boolean; ironbank?: boolean; cloud?: boolean; dockerBuildDate?: string; } ) { - // UBI var config - const baseOSImage = flags.ubi ? 'docker.elastic.co/ubi8/ubi-minimal:latest' : 'centos:8'; + let baseOSImage = ''; + if (flags.ubuntu) baseOSImage = 'ubuntu:20.04'; + if (flags.ubi) baseOSImage = 'docker.elastic.co/ubi8/ubi-minimal:latest'; const ubiVersionTag = 'ubi8'; let imageFlavor = ''; @@ -91,6 +93,7 @@ export async function runDockerGenerator( baseOSImage, dockerBuildDate, ubi: flags.ubi, + ubuntu: flags.ubuntu, cloud: flags.cloud, metricbeatTarball, filebeatTarball, diff --git a/src/dev/build/tasks/os_packages/docker_generator/template_context.ts b/src/dev/build/tasks/os_packages/docker_generator/template_context.ts index 143fcf16ace56..a715bfaa5d50d 100644 --- a/src/dev/build/tasks/os_packages/docker_generator/template_context.ts +++ b/src/dev/build/tasks/os_packages/docker_generator/template_context.ts @@ -23,6 +23,7 @@ export interface TemplateContext { dockerBuildDate: string; usePublicArtifact?: boolean; ubi?: boolean; + ubuntu?: boolean; cloud?: boolean; metricbeatTarball?: string; filebeatTarball?: string; diff --git a/src/dev/build/tasks/os_packages/docker_generator/templates/base/Dockerfile b/src/dev/build/tasks/os_packages/docker_generator/templates/base/Dockerfile index 90a622e64efe4..54af1c41b2da9 100644 --- a/src/dev/build/tasks/os_packages/docker_generator/templates/base/Dockerfile +++ b/src/dev/build/tasks/os_packages/docker_generator/templates/base/Dockerfile @@ -14,6 +14,9 @@ FROM {{{baseOSImage}}} AS builder {{#ubi}} RUN {{packageManager}} install -y findutils tar gzip {{/ubi}} +{{#ubuntu}} +RUN {{packageManager}} update && DEBIAN_FRONTEND=noninteractive {{packageManager}} install -y curl +{{/ubuntu}} {{#usePublicArtifact}} RUN cd /tmp && \ @@ -55,18 +58,28 @@ FROM {{{baseOSImage}}} EXPOSE 5601 {{#ubi}} - # https://github.com/rpm-software-management/microdnf/issues/50 - RUN mkdir -p /run/user/$(id -u) -{{/ubi}} - RUN for iter in {1..10}; do \ {{packageManager}} update --setopt=tsflags=nodocs -y && \ {{packageManager}} install --setopt=tsflags=nodocs -y \ - fontconfig freetype shadow-utils nss {{#ubi}}findutils{{/ubi}} && \ + fontconfig freetype shadow-utils nss findutils && \ {{packageManager}} clean all && exit_code=0 && break || exit_code=$? && echo "{{packageManager}} error: retry $iter in 10s" && \ sleep 10; \ done; \ (exit $exit_code) +{{/ubi}} +{{#ubuntu}} +RUN for iter in {1..10}; do \ + export DEBIAN_FRONTEND=noninteractive && \ + {{packageManager}} update && \ + {{packageManager}} upgrade -y && \ + {{packageManager}} install -y --no-install-recommends \ + fontconfig fonts-liberation libnss3 libfontconfig1 ca-certificates curl && \ + {{packageManager}} clean && \ + rm -rf /var/lib/apt/lists/* && exit_code=0 && break || exit_code=$? && echo "{{packageManager}} error: retry $iter in 10s" && \ + sleep 10; \ + done; \ + (exit $exit_code) +{{/ubuntu}} # Add an init process, check the checksum to make sure it's a match RUN set -e ; \ @@ -164,7 +177,7 @@ ENTRYPOINT ["/bin/tini", "--"] CMD ["/app/kibana.sh"] # Generate a stub command that will be overwritten at runtime RUN mkdir /app && \ - echo -e '#!/bin/sh\nexec /usr/local/bin/kibana-docker' > /app/kibana.sh && \ + echo -e '#!/bin/bash\nexec /usr/local/bin/kibana-docker' > /app/kibana.sh && \ chmod 0555 /app/kibana.sh {{/cloud}} diff --git a/src/dev/build/tasks/os_packages/docker_generator/templates/dockerfile.template.ts b/src/dev/build/tasks/os_packages/docker_generator/templates/dockerfile.template.ts index e668299a3acc3..94068f2b64b12 100755 --- a/src/dev/build/tasks/os_packages/docker_generator/templates/dockerfile.template.ts +++ b/src/dev/build/tasks/os_packages/docker_generator/templates/dockerfile.template.ts @@ -16,7 +16,7 @@ function generator(options: TemplateContext) { const dir = options.ironbank ? 'ironbank' : 'base'; const template = readFileSync(resolve(__dirname, dir, './Dockerfile')); return Mustache.render(template.toString(), { - packageManager: options.ubi ? 'microdnf' : 'yum', + packageManager: options.ubi ? 'microdnf' : 'apt-get', ...options, }); } From 3d6b71dfe4821661f69597cd36741977661e6b6a Mon Sep 17 00:00:00 2001 From: Pierre Gayvallet Date: Fri, 17 Dec 2021 19:53:46 +0100 Subject: [PATCH 15/40] Fix `import_saved_objects_between_versions` FTR test (#121498) --- .../import_saved_objects_between_versions.ts | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/x-pack/test/functional/apps/saved_objects_management/import_saved_objects_between_versions.ts b/x-pack/test/functional/apps/saved_objects_management/import_saved_objects_between_versions.ts index 4dce6bca8f67a..91407762f9f60 100644 --- a/x-pack/test/functional/apps/saved_objects_management/import_saved_objects_between_versions.ts +++ b/x-pack/test/functional/apps/saved_objects_management/import_saved_objects_between_versions.ts @@ -20,19 +20,15 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const testSubjects = getService('testSubjects'); const retry = getService('retry'); - // Failing: See https://github.com/elastic/kibana/issues/116058 - describe.skip('Export import saved objects between versions', function () { + describe('Export import saved objects between versions', function () { before(async function () { - await esArchiver.load('x-pack/test/functional/es_archives/logstash_functional'); - await esArchiver.load('x-pack/test/functional/es_archives/getting_started/shakespeare'); + await esArchiver.load('x-pack/test/functional/es_archives/empty_kibana'); await kibanaServer.uiSettings.replace({}); await PageObjects.settings.navigateTo(); await PageObjects.settings.clickKibanaSavedObjects(); }); after(async () => { - await esArchiver.unload('x-pack/test/functional/es_archives/logstash_functional'); - await esArchiver.unload('x-pack/test/functional/es_archives/getting_started/shakespeare'); await esArchiver.load('x-pack/test/functional/es_archives/empty_kibana'); }); From 47a5d972e8b526cfb88ffc7a7a738b8cd5d2e668 Mon Sep 17 00:00:00 2001 From: Madison Caldwell Date: Fri, 17 Dec 2021 13:57:09 -0500 Subject: [PATCH 16/40] [Security Solution] Enables telemetry for endpoint rules (#120225) * Add eventsTelemetry to query and threatMatch executors * Temp commit * Add query rule telemetry test * How'd that get in there? * Remove useless tests Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../rule_types/__mocks__/rule_type.ts | 18 ++- .../eql/create_eql_alert_type.test.ts | 67 --------- .../create_indicator_match_alert_type.test.ts | 135 ------------------ .../create_indicator_match_alert_type.ts | 4 +- .../ml/create_ml_alert_type.test.ts | 119 --------------- .../query/create_query_alert_type.test.ts | 89 +++++++++--- .../query/create_query_alert_type.ts | 4 +- .../create_threshold_alert_type.test.ts | 40 ------ .../lib/detection_engine/rule_types/types.ts | 2 + .../security_solution/server/plugin.ts | 1 + 10 files changed, 94 insertions(+), 385 deletions(-) delete mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/eql/create_eql_alert_type.test.ts delete mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/indicator_match/create_indicator_match_alert_type.test.ts delete mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/ml/create_ml_alert_type.test.ts delete mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/threshold/create_threshold_alert_type.test.ts diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/__mocks__/rule_type.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/__mocks__/rule_type.ts index aaab81a4c66ff..6a868098ed21d 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/__mocks__/rule_type.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/__mocks__/rule_type.ts @@ -20,10 +20,11 @@ import { ConfigType } from '../../../../config'; import { AlertAttributes } from '../../signals/types'; import { createRuleMock } from './rule'; import { listMock } from '../../../../../../lists/server/mocks'; -import { RuleParams } from '../../schemas/rule_schemas'; +import { QueryRuleParams, RuleParams } from '../../schemas/rule_schemas'; // this is only used in tests // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { createDefaultAlertExecutorOptions } from '../../../../../../rule_registry/server/utils/rule_executor_test_utils'; +import { getCompleteRuleMock } from '../../schemas/rule_schemas.mock'; export const createRuleTypeMocks = ( ruleType: string = 'query', @@ -89,9 +90,15 @@ export const createRuleTypeMocks = ( lists: listMock.createSetup(), logger: loggerMock, ml: mlPluginServerMock.createSetupContract(), - ruleDataClient: ruleRegistryMocks.createRuleDataClient( - '.alerts-security.alerts' - ) as IRuleDataClient, + ruleDataClient: { + ...(ruleRegistryMocks.createRuleDataClient('.alerts-security.alerts') as IRuleDataClient), + getReader: jest.fn((_options?: { namespace?: string }) => ({ + search: jest.fn().mockResolvedValue({ + aggregations: undefined, + }), + getDynamicIndexPattern: jest.fn(), + })), + }, eventLogService: eventLogServiceMock.create(), }, services, @@ -103,6 +110,9 @@ export const createRuleTypeMocks = ( alertId: v4(), state: {}, }), + runOpts: { + completeRule: getCompleteRuleMock(params as QueryRuleParams), + }, services, }); }, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/eql/create_eql_alert_type.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/eql/create_eql_alert_type.test.ts deleted file mode 100644 index 486a692ba29f4..0000000000000 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/eql/create_eql_alert_type.test.ts +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { elasticsearchClientMock } from 'src/core/server/elasticsearch/client/mocks'; - -import { allowedExperimentalValues } from '../../../../../common/experimental_features'; -import { createEqlAlertType } from './create_eql_alert_type'; -import { createRuleTypeMocks } from '../__mocks__/rule_type'; -import { getEqlRuleParams } from '../../schemas/rule_schemas.mock'; -import { createSecurityRuleTypeWrapper } from '../create_security_rule_type_wrapper'; -import { createMockConfig } from '../../routes/__mocks__'; - -jest.mock('../../rule_execution_log/rule_execution_log_client'); - -describe('Event correlation alerts', () => { - it('does not send an alert when no events found', async () => { - const params = { - ...getEqlRuleParams(), - query: 'any where false', - }; - const { services, dependencies, executor } = createRuleTypeMocks('eql', params); - const securityRuleTypeWrapper = createSecurityRuleTypeWrapper({ - lists: dependencies.lists, - logger: dependencies.logger, - config: createMockConfig(), - ruleDataClient: dependencies.ruleDataClient, - eventLogService: dependencies.eventLogService, - }); - const eqlAlertType = securityRuleTypeWrapper( - createEqlAlertType({ - experimentalFeatures: allowedExperimentalValues, - logger: dependencies.logger, - version: '1.0.0', - }) - ); - dependencies.alerting.registerType(eqlAlertType); - services.scopedClusterClient.asCurrentUser.search.mockReturnValue( - elasticsearchClientMock.createSuccessTransportRequestPromise({ - hits: { - hits: [], - sequences: [], - events: [], - total: { - relation: 'eq', - value: 0, - }, - }, - took: 0, - timed_out: false, - _shards: { - failed: 0, - skipped: 0, - successful: 1, - total: 1, - }, - }) - ); - - await executor({ params }); - expect(dependencies.ruleDataClient.getWriter).not.toBeCalled(); - }); -}); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/indicator_match/create_indicator_match_alert_type.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/indicator_match/create_indicator_match_alert_type.test.ts deleted file mode 100644 index 29054a4022715..0000000000000 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/indicator_match/create_indicator_match_alert_type.test.ts +++ /dev/null @@ -1,135 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { v4 } from 'uuid'; - -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { elasticsearchClientMock } from 'src/core/server/elasticsearch/client/mocks'; - -import { allowedExperimentalValues } from '../../../../../common/experimental_features'; -import { createRuleTypeMocks } from '../__mocks__/rule_type'; -import { createIndicatorMatchAlertType } from './create_indicator_match_alert_type'; -import { sampleDocNoSortId } from '../../signals/__mocks__/es_results'; -import { RuleParams } from '../../schemas/rule_schemas'; -import { createSecurityRuleTypeWrapper } from '../create_security_rule_type_wrapper'; -import { createMockConfig } from '../../routes/__mocks__'; - -jest.mock('../utils/get_list_client', () => ({ - getListClient: jest.fn().mockReturnValue({ - listClient: { - getListItemIndex: jest.fn(), - }, - exceptionsClient: jest.fn(), - }), -})); - -jest.mock('../../rule_execution_log/rule_execution_log_client'); - -describe('Indicator Match Alerts', () => { - const params: Partial = { - from: 'now-1m', - index: ['*'], - threatIndex: ['filebeat-*'], - threatLanguage: 'kuery', - threatMapping: [ - { - entries: [ - { - field: 'file.hash.md5', - type: 'mapping', - value: 'threat.indicator.file.hash.md5', - }, - ], - }, - ], - threatQuery: '*:*', - to: 'now', - type: 'threat_match', - query: '*:*', - language: 'kuery', - }; - const { services, dependencies, executor } = createRuleTypeMocks('threat_match', params); - const securityRuleTypeWrapper = createSecurityRuleTypeWrapper({ - lists: dependencies.lists, - logger: dependencies.logger, - config: createMockConfig(), - ruleDataClient: dependencies.ruleDataClient, - eventLogService: dependencies.eventLogService, - }); - - it('does not send an alert when no events found', async () => { - const indicatorMatchAlertType = securityRuleTypeWrapper( - createIndicatorMatchAlertType({ - experimentalFeatures: allowedExperimentalValues, - logger: dependencies.logger, - version: '1.0.0', - }) - ); - - dependencies.alerting.registerType(indicatorMatchAlertType); - - services.scopedClusterClient.asCurrentUser.search.mockReturnValueOnce( - elasticsearchClientMock.createSuccessTransportRequestPromise({ - hits: { - hits: [], - sequences: [], - events: [], - total: { - relation: 'eq', - value: 0, - }, - }, - took: 0, - timed_out: false, - _shards: { - failed: 0, - skipped: 0, - successful: 1, - total: 1, - }, - }) - ); - - await executor({ params }); - expect(dependencies.ruleDataClient.getWriter).not.toBeCalled(); - }); - - it('does not send an alert when no enrichments are found', async () => { - const indicatorMatchAlertType = securityRuleTypeWrapper( - createIndicatorMatchAlertType({ - experimentalFeatures: allowedExperimentalValues, - logger: dependencies.logger, - version: '1.0.0', - }) - ); - - dependencies.alerting.registerType(indicatorMatchAlertType); - - services.scopedClusterClient.asCurrentUser.search.mockReturnValueOnce( - elasticsearchClientMock.createSuccessTransportRequestPromise({ - hits: { - hits: [sampleDocNoSortId(v4()), sampleDocNoSortId(v4()), sampleDocNoSortId(v4())], - total: { - relation: 'eq', - value: 3, - }, - }, - took: 0, - timed_out: false, - _shards: { - failed: 0, - skipped: 0, - successful: 1, - total: 1, - }, - }) - ); - - await executor({ params }); - expect(dependencies.ruleDataClient.getWriter).not.toBeCalled(); - }); -}); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/indicator_match/create_indicator_match_alert_type.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/indicator_match/create_indicator_match_alert_type.ts index 6c487da07fda3..c4752751e529a 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/indicator_match/create_indicator_match_alert_type.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/indicator_match/create_indicator_match_alert_type.ts @@ -16,7 +16,7 @@ import { CreateRuleOptions, SecurityAlertType } from '../types'; export const createIndicatorMatchAlertType = ( createOptions: CreateRuleOptions ): SecurityAlertType => { - const { experimentalFeatures, logger, version } = createOptions; + const { eventsTelemetry, experimentalFeatures, logger, version } = createOptions; return { id: INDICATOR_RULE_TYPE_ID, name: 'Indicator Match Rule', @@ -68,7 +68,7 @@ export const createIndicatorMatchAlertType = ( bulkCreate, exceptionItems, experimentalFeatures, - eventsTelemetry: undefined, + eventsTelemetry, listClient, logger, completeRule, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/ml/create_ml_alert_type.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/ml/create_ml_alert_type.test.ts deleted file mode 100644 index b7a099b10e275..0000000000000 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/ml/create_ml_alert_type.test.ts +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { mlPluginServerMock } from '../../../../../../ml/server/mocks'; - -import { allowedExperimentalValues } from '../../../../../common/experimental_features'; -import { bulkCreateMlSignals } from '../../signals/bulk_create_ml_signals'; - -import { createRuleTypeMocks } from '../__mocks__/rule_type'; -import { createMlAlertType } from './create_ml_alert_type'; - -import { RuleParams } from '../../schemas/rule_schemas'; -import { createSecurityRuleTypeWrapper } from '../create_security_rule_type_wrapper'; -import { createMockConfig } from '../../routes/__mocks__'; - -jest.mock('../../signals/bulk_create_ml_signals'); - -jest.mock('../utils/get_list_client', () => ({ - getListClient: jest.fn().mockReturnValue({ - listClient: { - getListItemIndex: jest.fn(), - }, - exceptionsClient: jest.fn(), - }), -})); - -jest.mock('../../rule_execution_log/rule_execution_log_client'); - -jest.mock('../../signals/filters/filter_events_against_list', () => ({ - filterEventsAgainstList: jest.fn().mockReturnValue({ - _shards: { - failures: [], - }, - hits: { - hits: [ - { - is_interim: false, - }, - ], - }, - }), -})); - -let jobsSummaryMock: jest.Mock; -let mlMock: ReturnType; - -describe('Machine Learning Alerts', () => { - beforeEach(() => { - jobsSummaryMock = jest.fn(); - jobsSummaryMock.mockResolvedValue([ - { - id: 'test-ml-job', - jobState: 'started', - datafeedState: 'started', - }, - ]); - mlMock = mlPluginServerMock.createSetupContract(); - mlMock.jobServiceProvider.mockReturnValue({ - jobsSummary: jobsSummaryMock, - }); - - (bulkCreateMlSignals as jest.Mock).mockResolvedValue({ - success: true, - bulkCreateDuration: 0, - createdItemsCount: 1, - createdItems: [ - { - _id: '897234565234', - _index: 'test-index', - anomalyScore: 23, - }, - ], - errors: [], - }); - }); - - const params: Partial = { - anomalyThreshold: 23, - from: 'now-45m', - machineLearningJobId: ['test-ml-job'], - to: 'now', - type: 'machine_learning', - }; - - it('does not send an alert when no anomalies found', async () => { - jobsSummaryMock.mockResolvedValue([ - { - id: 'test-ml-job', - jobState: 'started', - datafeedState: 'started', - }, - ]); - const { dependencies, executor } = createRuleTypeMocks('machine_learning', params); - const securityRuleTypeWrapper = createSecurityRuleTypeWrapper({ - lists: dependencies.lists, - logger: dependencies.logger, - config: createMockConfig(), - ruleDataClient: dependencies.ruleDataClient, - eventLogService: dependencies.eventLogService, - }); - const mlAlertType = securityRuleTypeWrapper( - createMlAlertType({ - experimentalFeatures: allowedExperimentalValues, - logger: dependencies.logger, - ml: mlMock, - version: '1.0.0', - }) - ); - - dependencies.alerting.registerType(mlAlertType); - - await executor({ params }); - expect(dependencies.ruleDataClient.getWriter).not.toBeCalled(); - }); -}); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/query/create_query_alert_type.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/query/create_query_alert_type.test.ts index 07eb096d0dd83..a1d6214c6cb65 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/query/create_query_alert_type.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/query/create_query_alert_type.test.ts @@ -13,6 +13,14 @@ import { createQueryAlertType } from './create_query_alert_type'; import { createRuleTypeMocks } from '../__mocks__/rule_type'; import { createSecurityRuleTypeWrapper } from '../create_security_rule_type_wrapper'; import { createMockConfig } from '../../routes/__mocks__'; +import { createMockTelemetryEventsSender } from '../../../telemetry/__mocks__'; +import { sampleDocNoSortId } from '../../signals/__mocks__/es_results'; +import { getQueryRuleParams } from '../../schemas/rule_schemas.mock'; + +jest.mock('../../signals/utils', () => ({ + ...jest.requireActual('../../signals/utils'), + getExceptions: () => [], +})); jest.mock('../utils/get_list_client', () => ({ getListClient: jest.fn().mockReturnValue({ @@ -24,32 +32,33 @@ jest.mock('../utils/get_list_client', () => ({ jest.mock('../../rule_execution_log/rule_execution_log_client'); describe('Custom Query Alerts', () => { - const { services, dependencies, executor } = createRuleTypeMocks(); + const mocks = createRuleTypeMocks(); + const { dependencies, executor, services } = mocks; + const { alerting, eventLogService, lists, logger, ruleDataClient } = dependencies; const securityRuleTypeWrapper = createSecurityRuleTypeWrapper({ - lists: dependencies.lists, - logger: dependencies.logger, + lists, + logger, config: createMockConfig(), - ruleDataClient: dependencies.ruleDataClient, - eventLogService: dependencies.eventLogService, + ruleDataClient, + eventLogService, }); + const eventsTelemetry = createMockTelemetryEventsSender(true); + + afterEach(() => { + jest.clearAllMocks(); + }); + it('does not send an alert when no events found', async () => { const queryAlertType = securityRuleTypeWrapper( createQueryAlertType({ + eventsTelemetry, experimentalFeatures: allowedExperimentalValues, - logger: dependencies.logger, + logger, version: '1.0.0', }) ); - dependencies.alerting.registerType(queryAlertType); - - const params = { - query: 'dne:42', - index: ['*'], - from: 'now-1m', - to: 'now', - language: 'kuery', - }; + alerting.registerType(queryAlertType); services.scopedClusterClient.asCurrentUser.search.mockReturnValue( elasticsearchClientMock.createSuccessTransportRequestPromise({ @@ -73,7 +82,55 @@ describe('Custom Query Alerts', () => { }) ); + const params = getQueryRuleParams(); + + await executor({ + params, + }); + + expect(ruleDataClient.getWriter().bulk).not.toHaveBeenCalled(); + expect(eventsTelemetry.queueTelemetryEvents).not.toHaveBeenCalled(); + }); + + it('sends an alert when events are found', async () => { + const queryAlertType = securityRuleTypeWrapper( + createQueryAlertType({ + eventsTelemetry, + experimentalFeatures: allowedExperimentalValues, + logger, + version: '1.0.0', + }) + ); + + alerting.registerType(queryAlertType); + + services.scopedClusterClient.asCurrentUser.search.mockReturnValue( + elasticsearchClientMock.createSuccessTransportRequestPromise({ + hits: { + hits: [sampleDocNoSortId()], + sequences: [], + events: [], + total: { + relation: 'eq', + value: 1, + }, + }, + took: 0, + timed_out: false, + _shards: { + failed: 0, + skipped: 0, + successful: 1, + total: 1, + }, + }) + ); + + const params = getQueryRuleParams(); + await executor({ params }); - expect(dependencies.ruleDataClient.getWriter).not.toBeCalled(); + + expect(ruleDataClient.getWriter().bulk).toHaveBeenCalled(); + expect(eventsTelemetry.queueTelemetryEvents).toHaveBeenCalled(); }); }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/query/create_query_alert_type.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/query/create_query_alert_type.ts index ff032f65ac640..26512187010e9 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/query/create_query_alert_type.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/query/create_query_alert_type.ts @@ -16,7 +16,7 @@ import { CreateRuleOptions, SecurityAlertType } from '../types'; export const createQueryAlertType = ( createOptions: CreateRuleOptions ): SecurityAlertType => { - const { experimentalFeatures, logger, version } = createOptions; + const { eventsTelemetry, experimentalFeatures, logger, version } = createOptions; return { id: QUERY_RULE_TYPE_ID, name: 'Custom Query Rule', @@ -68,7 +68,7 @@ export const createQueryAlertType = ( bulkCreate, exceptionItems, experimentalFeatures, - eventsTelemetry: undefined, + eventsTelemetry, listClient, logger, completeRule, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/threshold/create_threshold_alert_type.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/threshold/create_threshold_alert_type.test.ts deleted file mode 100644 index 093ec0af78f59..0000000000000 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/threshold/create_threshold_alert_type.test.ts +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { allowedExperimentalValues } from '../../../../../common/experimental_features'; -import { createThresholdAlertType } from './create_threshold_alert_type'; -import { createRuleTypeMocks } from '../__mocks__/rule_type'; -import { getThresholdRuleParams } from '../../schemas/rule_schemas.mock'; -import { createSecurityRuleTypeWrapper } from '../create_security_rule_type_wrapper'; -import { createMockConfig } from '../../routes/__mocks__'; - -jest.mock('../../rule_execution_log/rule_execution_log_client'); - -describe('Threshold Alerts', () => { - it('does not send an alert when no events found', async () => { - const params = getThresholdRuleParams(); - const { dependencies, executor } = createRuleTypeMocks('threshold', params); - const securityRuleTypeWrapper = createSecurityRuleTypeWrapper({ - lists: dependencies.lists, - logger: dependencies.logger, - config: createMockConfig(), - ruleDataClient: dependencies.ruleDataClient, - eventLogService: dependencies.eventLogService, - }); - const thresholdAlertType = securityRuleTypeWrapper( - createThresholdAlertType({ - experimentalFeatures: allowedExperimentalValues, - logger: dependencies.logger, - version: '1.0.0', - }) - ); - dependencies.alerting.registerType(thresholdAlertType); - - await executor({ params }); - expect(dependencies.ruleDataClient.getWriter).not.toBeCalled(); - }); -}); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/types.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/types.ts index 095b3973edcb3..f9fc334f7fd37 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/types.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/types.ts @@ -36,6 +36,7 @@ import { import { ExperimentalFeatures } from '../../../../common/experimental_features'; import { IEventLogService } from '../../../../../event_log/server'; import { AlertsFieldMap, RulesFieldMap } from '../../../../common/field_maps'; +import { TelemetryEventsSender } from '../../telemetry/sender'; import { IRuleExecutionLogClient } from '../rule_execution_log'; export interface SecurityAlertTypeReturnValue { @@ -124,5 +125,6 @@ export interface CreateRuleOptions { experimentalFeatures: ExperimentalFeatures; logger: Logger; ml?: SetupPlugins['ml']; + eventsTelemetry?: TelemetryEventsSender | undefined; version: string; } diff --git a/x-pack/plugins/security_solution/server/plugin.ts b/x-pack/plugins/security_solution/server/plugin.ts index 98d63e1917f73..7d893756a3144 100644 --- a/x-pack/plugins/security_solution/server/plugin.ts +++ b/x-pack/plugins/security_solution/server/plugin.ts @@ -185,6 +185,7 @@ export class Plugin implements ISecuritySolutionPlugin { experimentalFeatures, logger: this.logger, ml: plugins.ml, + eventsTelemetry: this.telemetryEventsSender, version: pluginContext.env.packageInfo.version, }; From e5ba90c5e7beff01c545158cb676dc16c8c6db54 Mon Sep 17 00:00:00 2001 From: Nicolas Chaulet Date: Fri, 17 Dec 2021 14:07:10 -0500 Subject: [PATCH 17/40] [Fleet] Remove unused default permissions (#121325) --- .../full_agent_policy.test.ts.snap | 60 ------------------- .../agent_policies/full_agent_policy.ts | 12 ++-- ...kage_policies_to_agent_permissions.test.ts | 49 +++------------ .../package_policies_to_agent_permissions.ts | 22 ++----- 4 files changed, 19 insertions(+), 124 deletions(-) diff --git a/x-pack/plugins/fleet/server/services/agent_policies/__snapshots__/full_agent_policy.test.ts.snap b/x-pack/plugins/fleet/server/services/agent_policies/__snapshots__/full_agent_policy.test.ts.snap index 36044d35703ee..585eca4919eaf 100644 --- a/x-pack/plugins/fleet/server/services/agent_policies/__snapshots__/full_agent_policy.test.ts.snap +++ b/x-pack/plugins/fleet/server/services/agent_policies/__snapshots__/full_agent_policy.test.ts.snap @@ -25,26 +25,6 @@ Object { "monitor", ], }, - "_fallback": Object { - "cluster": Array [ - "monitor", - ], - "indices": Array [ - Object { - "names": Array [ - "logs-*", - "metrics-*", - "traces-*", - "synthetics-*", - ".logs-endpoint.diagnostic.collection-*", - ], - "privileges": Array [ - "auto_configure", - "create_doc", - ], - }, - ], - }, }, "default": Object { "_elastic_agent_checks": Object { @@ -111,26 +91,6 @@ Object { "monitor", ], }, - "_fallback": Object { - "cluster": Array [ - "monitor", - ], - "indices": Array [ - Object { - "names": Array [ - "logs-*", - "metrics-*", - "traces-*", - "synthetics-*", - ".logs-endpoint.diagnostic.collection-*", - ], - "privileges": Array [ - "auto_configure", - "create_doc", - ], - }, - ], - }, }, "monitoring-output-id": Object { "_elastic_agent_checks": Object { @@ -197,26 +157,6 @@ Object { "monitor", ], }, - "_fallback": Object { - "cluster": Array [ - "monitor", - ], - "indices": Array [ - Object { - "names": Array [ - "logs-*", - "metrics-*", - "traces-*", - "synthetics-*", - ".logs-endpoint.diagnostic.collection-*", - ], - "privileges": Array [ - "auto_configure", - "create_doc", - ], - }, - ], - }, }, "monitoring-output-id": Object { "_elastic_agent_checks": Object { diff --git a/x-pack/plugins/fleet/server/services/agent_policies/full_agent_policy.ts b/x-pack/plugins/fleet/server/services/agent_policies/full_agent_policy.ts index 166b2f77dc27b..0bb6b4a33f6a3 100644 --- a/x-pack/plugins/fleet/server/services/agent_policies/full_agent_policy.ts +++ b/x-pack/plugins/fleet/server/services/agent_policies/full_agent_policy.ts @@ -19,7 +19,7 @@ import { agentPolicyService } from '../agent_policy'; import { outputService } from '../output'; import { storedPackagePoliciesToAgentPermissions, - DEFAULT_PERMISSIONS, + DEFAULT_CLUSTER_PERMISSIONS, } from '../package_policies_to_agent_permissions'; import { storedPackagePoliciesToAgentInputs, dataTypes, outputType } from '../../../common'; import type { FullAgentPolicyOutputPermissions } from '../../../common'; @@ -110,13 +110,11 @@ export async function getFullAgentPolicy( }), }; - const dataPermissions = (await storedPackagePoliciesToAgentPermissions( - soClient, - agentPolicy.package_policies - )) || { _fallback: DEFAULT_PERMISSIONS }; + const dataPermissions = + (await storedPackagePoliciesToAgentPermissions(soClient, agentPolicy.package_policies)) || {}; dataPermissions._elastic_agent_checks = { - cluster: DEFAULT_PERMISSIONS.cluster, + cluster: DEFAULT_CLUSTER_PERMISSIONS, }; const monitoringPermissions = await getMonitoringPermissions( @@ -128,7 +126,7 @@ export async function getFullAgentPolicy( agentPolicy.namespace ); monitoringPermissions._elastic_agent_checks = { - cluster: DEFAULT_PERMISSIONS.cluster, + cluster: DEFAULT_CLUSTER_PERMISSIONS, }; // Only add permissions if output.type is "elasticsearch" diff --git a/x-pack/plugins/fleet/server/services/package_policies_to_agent_permissions.test.ts b/x-pack/plugins/fleet/server/services/package_policies_to_agent_permissions.test.ts index 72566a18bd66e..074484529bfb8 100644 --- a/x-pack/plugins/fleet/server/services/package_policies_to_agent_permissions.test.ts +++ b/x-pack/plugins/fleet/server/services/package_policies_to_agent_permissions.test.ts @@ -30,49 +30,18 @@ describe('storedPackagePoliciesToAgentPermissions()', () => { expect(permissions).toBeUndefined(); }); - it('Returns the default permissions for string package policies', async () => { - const permissions = await storedPackagePoliciesToAgentPermissions(soClient, ['foo']); - expect(permissions).toMatchObject({ - _fallback: { - cluster: ['monitor'], - indices: [ - { - names: [ - 'logs-*', - 'metrics-*', - 'traces-*', - 'synthetics-*', - '.logs-endpoint.diagnostic.collection-*', - ], - privileges: ['auto_configure', 'create_doc'], - }, - ], - }, - }); + it('Throw an error for string package policies', async () => { + await expect(() => storedPackagePoliciesToAgentPermissions(soClient, ['foo'])).rejects.toThrow( + /storedPackagePoliciesToAgentPermissions should be called with a PackagePolicy/ + ); }); it('Returns the default permissions if a package policy does not have a package', async () => { - const permissions = await storedPackagePoliciesToAgentPermissions(soClient, [ - { name: 'foo', package: undefined } as PackagePolicy, - ]); - - expect(permissions).toMatchObject({ - foo: { - cluster: ['monitor'], - indices: [ - { - names: [ - 'logs-*', - 'metrics-*', - 'traces-*', - 'synthetics-*', - '.logs-endpoint.diagnostic.collection-*', - ], - privileges: ['auto_configure', 'create_doc'], - }, - ], - }, - }); + await expect(() => + storedPackagePoliciesToAgentPermissions(soClient, [ + { name: 'foo', package: undefined } as PackagePolicy, + ]) + ).rejects.toThrow(/No package for package policy foo/); }); it('Returns the permissions for the enabled inputs', async () => { diff --git a/x-pack/plugins/fleet/server/services/package_policies_to_agent_permissions.ts b/x-pack/plugins/fleet/server/services/package_policies_to_agent_permissions.ts index 383747fe126c0..d361c583c8f6c 100644 --- a/x-pack/plugins/fleet/server/services/package_policies_to_agent_permissions.ts +++ b/x-pack/plugins/fleet/server/services/package_policies_to_agent_permissions.ts @@ -11,21 +11,7 @@ import { PACKAGE_POLICY_DEFAULT_INDEX_PRIVILEGES } from '../constants'; import { getPackageInfo } from '../../server/services/epm/packages'; import type { PackagePolicy } from '../types'; -export const DEFAULT_PERMISSIONS = { - cluster: ['monitor'], - indices: [ - { - names: [ - 'logs-*', - 'metrics-*', - 'traces-*', - 'synthetics-*', - '.logs-endpoint.diagnostic.collection-*', - ], - privileges: PACKAGE_POLICY_DEFAULT_INDEX_PRIVILEGES, - }, - ], -}; +export const DEFAULT_CLUSTER_PERMISSIONS = ['monitor']; export async function storedPackagePoliciesToAgentPermissions( soClient: SavedObjectsClientContract, @@ -37,13 +23,15 @@ export async function storedPackagePoliciesToAgentPermissions( // I'm not sure what permissions to return for this case, so let's return the defaults if (typeof packagePolicies[0] === 'string') { - return { _fallback: DEFAULT_PERMISSIONS }; + throw new Error( + 'storedPackagePoliciesToAgentPermissions should be called with a PackagePolicy' + ); } const permissionEntries = (packagePolicies as PackagePolicy[]).map>( async (packagePolicy) => { if (!packagePolicy.package) { - return [packagePolicy.name, DEFAULT_PERMISSIONS]; + throw new Error(`No package for package policy ${packagePolicy.name}`); } const pkg = await getPackageInfo({ From 03f5c3b34c4405cfa97fa32a9f00c0e3fe22e8f2 Mon Sep 17 00:00:00 2001 From: Jonathan Budzenski Date: Fri, 17 Dec 2021 13:27:14 -0600 Subject: [PATCH 18/40] [renovate] Add ftr related packages (#118622) * [renovate] Add ftr related packages * add v7.17.0 * Update renovate.json Co-authored-by: Tyler Smalley Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Tyler Smalley --- renovate.json | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/renovate.json b/renovate.json index a6b54a3dae349..badde12cebef4 100644 --- a/renovate.json +++ b/renovate.json @@ -126,6 +126,21 @@ "matchBaseBranches": ["main"], "labels": ["Team:Security", "release_note:skip", "auto-backport"], "enabled": true + }, + { + "groupName": "ftr", + "packageNames": [ + "@types/chromedriver", + "@types/selenium-webdriver", + "chromedriver", + "geckodriver", + "ms-chromium-edge-driver", + "selenium-webdriver" + ], + "reviewers": ["team:kibana-operations"], + "matchBaseBranches": ["main"], + "labels": ["Team:Operations", "release_note:skip"], + "enabled": true } ] } From b9c563c41c8ec9d46a95b2c0bf090d0504be44fa Mon Sep 17 00:00:00 2001 From: Kristof C Date: Fri, 17 Dec 2021 13:28:37 -0600 Subject: [PATCH 19/40] [118684] Add ability for case owner selection in flyout (#120150) * [118684] Add ability for case owner selection in flyout * Update Flyout to handle permissions internally * Fix failing tests * Code review changes * Fix tests * Update test provider to use merge * Add PR suggestions * further PR suggestions * Update language from case type to solution * Fixes from code review * Add key field for iteration and default to securitySolution * final revisions Co-authored-by: Kristof-Pierre Cummings --- x-pack/plugins/cases/common/constants.ts | 18 ++- .../public/common/mock/test_providers.tsx | 11 +- .../cases/public/common/translations.ts | 18 +++ .../app/use_available_owners.test.ts | 91 ++++++++++++ .../components/app/use_available_owners.ts | 31 ++++ .../public/components/create/form.test.tsx | 21 ++- .../cases/public/components/create/form.tsx | 24 +++- .../public/components/create/form_context.tsx | 8 +- .../components/create/owner_selector.test.tsx | 135 ++++++++++++++++++ .../components/create/owner_selector.tsx | 117 +++++++++++++++ .../cases/public/components/create/schema.tsx | 10 ++ 11 files changed, 461 insertions(+), 23 deletions(-) create mode 100644 x-pack/plugins/cases/public/components/app/use_available_owners.test.ts create mode 100644 x-pack/plugins/cases/public/components/app/use_available_owners.ts create mode 100644 x-pack/plugins/cases/public/components/create/owner_selector.test.tsx create mode 100644 x-pack/plugins/cases/public/components/create/owner_selector.tsx diff --git a/x-pack/plugins/cases/common/constants.ts b/x-pack/plugins/cases/common/constants.ts index a5ae6f6b28ffb..8e7d4934271ac 100644 --- a/x-pack/plugins/cases/common/constants.ts +++ b/x-pack/plugins/cases/common/constants.ts @@ -80,13 +80,19 @@ export const SUPPORTED_CONNECTORS = [ export const MAX_ALERTS_PER_SUB_CASE = 5000; export const MAX_GENERATED_ALERTS_PER_SUB_CASE = 50; -/** - * This must be the same value that the security solution plugin uses to define the case kind when it registers the - * feature for the 7.13 migration only. - * - * This variable is being also used by test files and mocks. - */ export const SECURITY_SOLUTION_OWNER = 'securitySolution'; +export const OBSERVABILITY_OWNER = 'observability'; + +export const OWNER_INFO = { + [SECURITY_SOLUTION_OWNER]: { + label: 'Security', + iconType: 'logoSecurity', + }, + [OBSERVABILITY_OWNER]: { + label: 'Observability', + iconType: 'logoObservability', + }, +}; /** * This flag governs enabling the case as a connector feature. It is disabled by default as the feature is not complete. diff --git a/x-pack/plugins/cases/public/common/mock/test_providers.tsx b/x-pack/plugins/cases/public/common/mock/test_providers.tsx index 179e1ad372c7a..b9b1e65e597ea 100644 --- a/x-pack/plugins/cases/public/common/mock/test_providers.tsx +++ b/x-pack/plugins/cases/public/common/mock/test_providers.tsx @@ -5,11 +5,12 @@ * 2.0. */ -import React from 'react'; import { merge } from 'lodash'; +import React from 'react'; import { euiDarkVars } from '@kbn/ui-shared-deps-src/theme'; import { I18nProvider } from '@kbn/i18n-react'; import { ThemeProvider } from 'styled-components'; + import { DEFAULT_FEATURES, SECURITY_SOLUTION_OWNER } from '../../../common/constants'; import { CasesFeatures } from '../../../common/ui/types'; import { CasesProvider } from '../../components/cases_context'; @@ -20,6 +21,7 @@ interface Props { children: React.ReactNode; userCanCrud?: boolean; features?: CasesFeatures; + owner?: string[]; } window.scrollTo = jest.fn(); @@ -28,8 +30,9 @@ const MockKibanaContextProvider = createKibanaContextProviderMock(); /** A utility for wrapping children in the providers required to run most tests */ const TestProvidersComponent: React.FC = ({ children, + features, + owner = [SECURITY_SOLUTION_OWNER], userCanCrud = true, - features = {}, }) => { /** * The empty object at the beginning avoids the mutation @@ -40,9 +43,7 @@ const TestProvidersComponent: React.FC = ({ ({ eui: euiDarkVars, darkMode: true })}> - + {children} diff --git a/x-pack/plugins/cases/public/common/translations.ts b/x-pack/plugins/cases/public/common/translations.ts index dc4fe9fd299f5..170b6d3ce27f5 100644 --- a/x-pack/plugins/cases/public/common/translations.ts +++ b/x-pack/plugins/cases/public/common/translations.ts @@ -56,6 +56,20 @@ export const DESCRIPTION_REQUIRED = i18n.translate( } ); +export const SOLUTION_REQUIRED = i18n.translate( + 'xpack.cases.createCase.solutionFieldRequiredError', + { + defaultMessage: 'A solution is required', + } +); + +export const ARIA_KEYPAD_LEGEND = i18n.translate( + 'xpack.cases.createCase.ariaKeypadSolutionSelection', + { + defaultMessage: 'Single solution select', + } +); + export const COMMENT_REQUIRED = i18n.translate('xpack.cases.caseView.commentFieldRequiredError', { defaultMessage: 'A comment is required.', }); @@ -108,6 +122,10 @@ export const TAGS = i18n.translate('xpack.cases.caseView.tags', { defaultMessage: 'Tags', }); +export const SOLUTION = i18n.translate('xpack.cases.caseView.solution', { + defaultMessage: 'Solution', +}); + export const ACTIONS = i18n.translate('xpack.cases.allCases.actions', { defaultMessage: 'Actions', }); diff --git a/x-pack/plugins/cases/public/components/app/use_available_owners.test.ts b/x-pack/plugins/cases/public/components/app/use_available_owners.test.ts new file mode 100644 index 0000000000000..32229d322162b --- /dev/null +++ b/x-pack/plugins/cases/public/components/app/use_available_owners.test.ts @@ -0,0 +1,91 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { renderHook } from '@testing-library/react-hooks'; +import { SECURITY_SOLUTION_OWNER } from '../../../common'; +import { OBSERVABILITY_OWNER } from '../../../common/constants'; + +import { useKibana } from '../../common/lib/kibana'; +import { useAvailableCasesOwners } from './use_available_owners'; + +jest.mock('../../common/lib/kibana'); + +const useKibanaMock = useKibana as jest.MockedFunction; + +const hasAll = { + securitySolutionCases: { + crud_cases: true, + read_cases: true, + }, + observabilityCases: { + crud_cases: true, + read_cases: true, + }, +}; + +const hasSecurityAsCrudAndObservabilityAsRead = { + securitySolutionCases: { + crud_cases: true, + }, + observabilityCases: { + read_cases: true, + }, +}; + +const unrelatedFeatures = { + bogusCapability: { + crud_cases: true, + read_cases: true, + }, +}; + +const mockKibana = (permissionType: unknown = hasAll) => { + useKibanaMock.mockReturnValue({ + services: { + application: { + capabilities: permissionType, + }, + }, + } as unknown as ReturnType); +}; + +describe('useAvailableCasesOwners correctly grabs user case permissions', () => { + it('returns all available owner types if user has access to all', () => { + mockKibana(); + const { result } = renderHook(useAvailableCasesOwners); + + expect(result.current).toEqual([SECURITY_SOLUTION_OWNER, OBSERVABILITY_OWNER]); + }); + + it('returns no owner types if user has access to none', () => { + mockKibana({}); + const { result } = renderHook(useAvailableCasesOwners); + + expect(result.current).toEqual([]); + }); + + it('returns only the permission it should have with CRUD as default', () => { + mockKibana(hasSecurityAsCrudAndObservabilityAsRead); + const { result } = renderHook(useAvailableCasesOwners); + + expect(result.current).toEqual([SECURITY_SOLUTION_OWNER]); + }); + + it('returns only the permission it should have with READ as default', () => { + mockKibana(hasSecurityAsCrudAndObservabilityAsRead); + const { result } = renderHook(() => useAvailableCasesOwners('read')); + + expect(result.current).toEqual([OBSERVABILITY_OWNER]); + }); + + it('returns no owners when the capabilities does not contain valid entries', () => { + mockKibana(unrelatedFeatures); + const { result } = renderHook(useAvailableCasesOwners); + + expect(result.current).toEqual([]); + }); +}); diff --git a/x-pack/plugins/cases/public/components/app/use_available_owners.ts b/x-pack/plugins/cases/public/components/app/use_available_owners.ts new file mode 100644 index 0000000000000..783e856bd36e9 --- /dev/null +++ b/x-pack/plugins/cases/public/components/app/use_available_owners.ts @@ -0,0 +1,31 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { useKibana } from '../../common/lib/kibana'; + +/** + * + * @param level : 'crud' | 'read' (default: 'crud') + * + * `securitySolution` owner uses cases capability feature id: 'securitySolutionCases'; //owner + * `observability` owner uses cases capability feature id: 'observabilityCases'; + * both solutions use `crud_cases` and `read_cases` capability names + **/ + +export const useAvailableCasesOwners = (level: 'crud' | 'read' = 'crud'): string[] => { + const { capabilities } = useKibana().services.application; + const capabilityName = `${level}_cases`; + return Object.entries(capabilities).reduce( + (availableOwners: string[], [featureId, capability]) => { + if (featureId.endsWith('Cases') && !!capability[capabilityName]) { + availableOwners.push(featureId.replace('Cases', '')); + } + return availableOwners; + }, + [] + ); +}; diff --git a/x-pack/plugins/cases/public/components/create/form.test.tsx b/x-pack/plugins/cases/public/components/create/form.test.tsx index 9a4af9bdc3a62..aa4194a2f1afc 100644 --- a/x-pack/plugins/cases/public/components/create/form.test.tsx +++ b/x-pack/plugins/cases/public/components/create/form.test.tsx @@ -23,6 +23,9 @@ jest.mock('../../containers/use_get_tags'); jest.mock('../../containers/configure/use_connectors'); jest.mock('../../containers/configure/use_configure'); jest.mock('../markdown_editor/plugins/lens/use_lens_draft_comment'); +jest.mock('../app/use_available_owners', () => ({ + useAvailableCasesOwners: () => ['securitySolution', 'observability'], +})); const useGetTagsMock = useGetTags as jest.Mock; const useConnectorsMock = useConnectors as jest.Mock; @@ -90,7 +93,7 @@ describe('CreateCaseForm', () => { expect(wrapper.find(`[data-test-subj="case-creation-form-steps"]`).exists()).toBeFalsy(); }); - it('it renders all form fields', async () => { + it('it renders all form fields except case selection', async () => { const wrapper = mount( @@ -102,6 +105,22 @@ describe('CreateCaseForm', () => { expect(wrapper.find(`[data-test-subj="caseDescription"]`).exists()).toBeTruthy(); expect(wrapper.find(`[data-test-subj="caseSyncAlerts"]`).exists()).toBeTruthy(); expect(wrapper.find(`[data-test-subj="caseConnectors"]`).exists()).toBeTruthy(); + expect(wrapper.find(`[data-test-subj="caseOwnerSelector"]`).exists()).toBeFalsy(); + }); + + it('renders all form fields including case selection if has permissions and no owner', async () => { + const wrapper = mount( + + + + ); + + expect(wrapper.find(`[data-test-subj="caseTitle"]`).exists()).toBeTruthy(); + expect(wrapper.find(`[data-test-subj="caseTags"]`).exists()).toBeTruthy(); + expect(wrapper.find(`[data-test-subj="caseDescription"]`).exists()).toBeTruthy(); + expect(wrapper.find(`[data-test-subj="caseSyncAlerts"]`).exists()).toBeTruthy(); + expect(wrapper.find(`[data-test-subj="caseConnectors"]`).exists()).toBeTruthy(); + expect(wrapper.find(`[data-test-subj="caseOwnerSelector"]`).exists()).toBeTruthy(); }); it('hides the sync alerts toggle', () => { diff --git a/x-pack/plugins/cases/public/components/create/form.tsx b/x-pack/plugins/cases/public/components/create/form.tsx index 396c72fa54c0d..a1784397a21a8 100644 --- a/x-pack/plugins/cases/public/components/create/form.tsx +++ b/x-pack/plugins/cases/public/components/create/form.tsx @@ -31,6 +31,9 @@ import { UsePostComment } from '../../containers/use_post_comment'; import { SubmitCaseButton } from './submit_button'; import { FormContext } from './form_context'; import { useCasesFeatures } from '../cases_context/use_cases_features'; +import { CreateCaseOwnerSelector } from './owner_selector'; +import { useCasesContext } from '../cases_context/use_cases_context'; +import { useAvailableCasesOwners } from '../app/use_available_owners'; interface ContainerProps { big?: boolean; @@ -70,6 +73,10 @@ export const CreateCaseFormFields: React.FC = React.m const { isSubmitting } = useFormContext(); const { isSyncAlertsEnabled } = useCasesFeatures(); + const { owner } = useCasesContext(); + const availableOwners = useAvailableCasesOwners(); + const canShowCaseSolutionSelection = !owner.length && availableOwners.length; + const firstStep = useMemo( () => ({ title: i18n.STEP_ONE_TITLE, @@ -79,13 +86,21 @@ export const CreateCaseFormFields: React.FC = React.m + {canShowCaseSolutionSelection && ( + + + + )} ), }), - [isSubmitting] + [isSubmitting, canShowCaseSolutionSelection, availableOwners] ); const secondStep = useMemo( @@ -156,12 +171,7 @@ export const CreateCaseForm: React.FC = React.memo( timelineIntegration, }) => ( - + Promise; caseType?: CaseType; children?: JSX.Element | JSX.Element[]; - hideConnectorServiceNowSir?: boolean; onSuccess?: (theCase: Case) => Promise; } @@ -41,7 +41,6 @@ export const FormContext: React.FC = ({ afterCaseCreated, caseType = CaseType.individual, children, - hideConnectorServiceNowSir, onSuccess, }) => { const { connectors, loading: isLoadingConnectors } = useConnectors(); @@ -62,6 +61,7 @@ export const FormContext: React.FC = ({ isValid ) => { if (isValid) { + const { selectedOwner, ...userFormData } = dataWithoutConnectorId; const caseConnector = getConnectorById(dataConnectorId, connectors); const connectorToUpdate = caseConnector @@ -69,11 +69,11 @@ export const FormContext: React.FC = ({ : getNoneConnector(); const updatedCase = await postCase({ - ...dataWithoutConnectorId, + ...userFormData, type: caseType, connector: connectorToUpdate, settings: { syncAlerts }, - owner: owner[0], + owner: selectedOwner ?? owner[0], }); if (afterCaseCreated && updatedCase) { diff --git a/x-pack/plugins/cases/public/components/create/owner_selector.test.tsx b/x-pack/plugins/cases/public/components/create/owner_selector.test.tsx new file mode 100644 index 0000000000000..527b37ceac011 --- /dev/null +++ b/x-pack/plugins/cases/public/components/create/owner_selector.test.tsx @@ -0,0 +1,135 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { mount } from 'enzyme'; +import { act, waitFor } from '@testing-library/react'; + +import { SECURITY_SOLUTION_OWNER } from '../../../common'; +import { OBSERVABILITY_OWNER } from '../../../common/constants'; +import { useForm, Form, FormHook } from '../../common/shared_imports'; +import { CreateCaseOwnerSelector } from './owner_selector'; +import { schema, FormProps } from './schema'; + +describe('Case Owner Selection', () => { + let globalForm: FormHook; + + const MockHookWrapperComponent: React.FC = ({ children }) => { + const { form } = useForm({ + defaultValue: { selectedOwner: '' }, + schema: { + selectedOwner: schema.selectedOwner, + }, + }); + + globalForm = form; + + return
{children}
; + }; + + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('renders', () => { + const wrapper = mount( + + + + ); + + expect(wrapper.find(`[data-test-subj="caseOwnerSelector"]`).exists()).toBeTruthy(); + }); + + it.each([ + [OBSERVABILITY_OWNER, SECURITY_SOLUTION_OWNER], + [SECURITY_SOLUTION_OWNER, OBSERVABILITY_OWNER], + ])('disables %s button if user only has %j', (disabledButton, permission) => { + const wrapper = mount( + + + + ); + + expect( + wrapper.find(`[data-test-subj="${disabledButton}RadioButton"] input`).first().props().disabled + ).toBeTruthy(); + expect( + wrapper.find(`[data-test-subj="${permission}RadioButton"] input`).first().props().disabled + ).toBeFalsy(); + expect( + wrapper.find(`[data-test-subj="${permission}RadioButton"] input`).first().props().checked + ).toBeTruthy(); + }); + + it('defaults to security Solution', async () => { + const wrapper = mount( + + + + ); + + expect( + wrapper.find(`[data-test-subj="observabilityRadioButton"] input`).first().props().checked + ).toBeFalsy(); + expect( + wrapper.find(`[data-test-subj="securitySolutionRadioButton"] input`).first().props().checked + ).toBeTruthy(); + }); + + it('it changes the selection', async () => { + const wrapper = mount( + + + + ); + + await act(async () => { + wrapper + .find(`[data-test-subj="observabilityRadioButton"] input`) + .first() + .simulate('change', OBSERVABILITY_OWNER); + }); + + await waitFor(() => { + wrapper.update(); + expect( + wrapper.find(`[data-test-subj="observabilityRadioButton"] input`).first().props().checked + ).toBeTruthy(); + expect( + wrapper.find(`[data-test-subj="securitySolutionRadioButton"] input`).first().props().checked + ).toBeFalsy(); + }); + + expect(globalForm.getFormData()).toEqual({ selectedOwner: OBSERVABILITY_OWNER }); + + await act(async () => { + wrapper + .find(`[data-test-subj="securitySolutionRadioButton"] input`) + .first() + .simulate('change', SECURITY_SOLUTION_OWNER); + }); + + await waitFor(() => { + wrapper.update(); + expect( + wrapper.find(`[data-test-subj="securitySolutionRadioButton"] input`).first().props().checked + ).toBeTruthy(); + expect( + wrapper.find(`[data-test-subj="observabilityRadioButton"] input`).first().props().checked + ).toBeFalsy(); + }); + + expect(globalForm.getFormData()).toEqual({ selectedOwner: SECURITY_SOLUTION_OWNER }); + }); +}); diff --git a/x-pack/plugins/cases/public/components/create/owner_selector.tsx b/x-pack/plugins/cases/public/components/create/owner_selector.tsx new file mode 100644 index 0000000000000..251681506d516 --- /dev/null +++ b/x-pack/plugins/cases/public/components/create/owner_selector.tsx @@ -0,0 +1,117 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { memo, useCallback, useEffect } from 'react'; + +import { + EuiFlexGroup, + EuiFlexItem, + EuiFormRow, + EuiIcon, + EuiKeyPadMenu, + EuiKeyPadMenuItem, + useGeneratedHtmlId, +} from '@elastic/eui'; + +import { euiStyled } from '../../../../../../src/plugins/kibana_react/common'; +import { SECURITY_SOLUTION_OWNER } from '../../../common'; +import { OBSERVABILITY_OWNER, OWNER_INFO } from '../../../common/constants'; + +import { FieldHook, getFieldValidityAndErrorMessage, UseField } from '../../common/shared_imports'; +import * as i18n from './translations'; + +interface OwnerSelectorProps { + field: FieldHook; + isLoading: boolean; + availableOwners: string[]; +} + +interface Props { + availableOwners: string[]; + isLoading: boolean; +} + +const DEFAULT_SELECTABLE_OWNERS = [SECURITY_SOLUTION_OWNER, OBSERVABILITY_OWNER] as const; + +const FIELD_NAME = 'selectedOwner'; + +const FullWidthKeyPadMenu = euiStyled(EuiKeyPadMenu)` + width: 100%; +`; + +const FullWidthKeyPadItem = euiStyled(EuiKeyPadMenuItem)` + + width: 100%; +`; + +const OwnerSelector = ({ + availableOwners, + field, + isLoading = false, +}: OwnerSelectorProps): JSX.Element => { + const { errorMessage, isInvalid } = getFieldValidityAndErrorMessage(field); + const radioGroupName = useGeneratedHtmlId({ prefix: 'caseOwnerRadioGroup' }); + + const onChange = useCallback((val: string) => field.setValue(val), [field]); + + useEffect(() => { + if (!field.value) { + onChange( + availableOwners.includes(SECURITY_SOLUTION_OWNER) + ? SECURITY_SOLUTION_OWNER + : availableOwners[0] + ); + } + }, [availableOwners, field.value, onChange]); + + return ( + + + + {DEFAULT_SELECTABLE_OWNERS.map((owner) => ( + + + + + + ))} + + + + ); +}; + +const CaseOwnerSelector: React.FC = ({ availableOwners, isLoading }) => { + return ( + + ); +}; + +CaseOwnerSelector.displayName = 'CaseOwnerSelectionComponent'; + +export const CreateCaseOwnerSelector = memo(CaseOwnerSelector); diff --git a/x-pack/plugins/cases/public/components/create/schema.tsx b/x-pack/plugins/cases/public/components/create/schema.tsx index 435ebefd943ca..632256fcbacec 100644 --- a/x-pack/plugins/cases/public/components/create/schema.tsx +++ b/x-pack/plugins/cases/public/components/create/schema.tsx @@ -36,6 +36,7 @@ export type FormProps = Omit = { @@ -62,6 +63,15 @@ export const schema: FormSchema = { }, ], }, + selectedOwner: { + label: i18n.SOLUTION, + type: FIELD_TYPES.RADIO_GROUP, + validations: [ + { + validator: emptyField(i18n.SOLUTION_REQUIRED), + }, + ], + }, tags: schemaTags, connectorId: { type: FIELD_TYPES.SUPER_SELECT, From a2d7a98353de70d058028b111ee7f451b605b2a4 Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Fri, 17 Dec 2021 19:56:32 +0000 Subject: [PATCH 20/40] chore(NA): splits types from code on @kbn/plugin-helpers (#121513) * chore(NA): splits types from code on @kbn/monaco * chore(NA): splits types from code on @kbn/plugin-helpers --- package.json | 1 + packages/BUILD.bazel | 1 + packages/kbn-plugin-helpers/BUILD.bazel | 26 ++++++++++++++++++++---- packages/kbn-plugin-helpers/package.json | 1 - yarn.lock | 4 ++++ 5 files changed, 28 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 1731de4cb4873..47a1aeb852533 100644 --- a/package.json +++ b/package.json @@ -578,6 +578,7 @@ "@types/kbn__monaco": "link:bazel-bin/packages/kbn-monaco/npm_module_types", "@types/kbn__optimizer": "link:bazel-bin/packages/kbn-optimizer/npm_module_types", "@types/kbn__plugin-generator": "link:bazel-bin/packages/kbn-plugin-generator/npm_module_types", + "@types/kbn__plugin-helpers": "link:bazel-bin/packages/kbn-plugin-helpers/npm_module_types", "@types/license-checker": "15.0.0", "@types/listr": "^0.14.0", "@types/loader-utils": "^1.1.3", diff --git a/packages/BUILD.bazel b/packages/BUILD.bazel index 5db97875ec3ee..64936ba71fcf2 100644 --- a/packages/BUILD.bazel +++ b/packages/BUILD.bazel @@ -97,6 +97,7 @@ filegroup( "//packages/kbn-monaco:build_types", "//packages/kbn-optimizer:build_types", "//packages/kbn-plugin-generator:build_types", + "//packages/kbn-plugin-helpers:build_types", ], ) diff --git a/packages/kbn-plugin-helpers/BUILD.bazel b/packages/kbn-plugin-helpers/BUILD.bazel index 84afabb354aa5..eeb5d06a3866a 100644 --- a/packages/kbn-plugin-helpers/BUILD.bazel +++ b/packages/kbn-plugin-helpers/BUILD.bazel @@ -1,10 +1,11 @@ -load("@npm//@bazel/typescript:index.bzl", "ts_config", "ts_project") -load("@build_bazel_rules_nodejs//:index.bzl", "js_library", "pkg_npm") -load("//src/dev/bazel:index.bzl", "jsts_transpiler") +load("@npm//@bazel/typescript:index.bzl", "ts_config") +load("@build_bazel_rules_nodejs//:index.bzl", "js_library") +load("//src/dev/bazel:index.bzl", "jsts_transpiler", "pkg_npm", "pkg_npm_types", "ts_project") PKG_BASE_NAME = "kbn-plugin-helpers" PKG_REQUIRE_NAME = "@kbn/plugin-helpers" +TYPES_PKG_REQUIRE_NAME = "@types/kbn__plugin-helpers" SOURCE_FILES = glob( [ @@ -89,7 +90,7 @@ ts_project( js_library( name = PKG_BASE_NAME, srcs = NPM_MODULE_EXTRA_FILES, - deps = RUNTIME_DEPS + [":target_node", ":tsc_types"], + deps = RUNTIME_DEPS + [":target_node"], package_name = PKG_REQUIRE_NAME, visibility = ["//visibility:public"], ) @@ -108,3 +109,20 @@ filegroup( ], visibility = ["//visibility:public"], ) + +pkg_npm_types( + name = "npm_module_types", + srcs = SRCS, + deps = [":tsc_types"], + package_name = TYPES_PKG_REQUIRE_NAME, + tsconfig = ":tsconfig", + visibility = ["//visibility:public"], +) + +filegroup( + name = "build_types", + srcs = [ + ":npm_module_types", + ], + visibility = ["//visibility:public"], +) diff --git a/packages/kbn-plugin-helpers/package.json b/packages/kbn-plugin-helpers/package.json index 21ed8f46f52fa..cac041762f739 100644 --- a/packages/kbn-plugin-helpers/package.json +++ b/packages/kbn-plugin-helpers/package.json @@ -8,7 +8,6 @@ "devOnly": true }, "main": "target_node/index.js", - "types": "target_types/index.d.ts", "bin": { "plugin-helpers": "bin/plugin-helpers.js" } diff --git a/yarn.lock b/yarn.lock index b56dea0f84187..27eb5da1810bb 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5881,6 +5881,10 @@ version "0.0.0" uid "" +"@types/kbn__plugin-helpers@link:bazel-bin/packages/kbn-plugin-helpers/npm_module_types": + version "0.0.0" + uid "" + "@types/keyv@*": version "3.1.1" resolved "https://registry.yarnpkg.com/@types/keyv/-/keyv-3.1.1.tgz#e45a45324fca9dab716ab1230ee249c9fb52cfa7" From c4ffd316a42baba1387f766bb068b5793e58292c Mon Sep 17 00:00:00 2001 From: Abdul Wahab Zahid Date: Fri, 17 Dec 2021 21:12:03 +0100 Subject: [PATCH 21/40] [Uptime] Monitor crud routes data validation (#120389) (elastic/uptime#410) * Monitor crud route data validation (io-ts typing). * Added unit tests to add, edit, delete and get monitor REST endpoints. --- .../monitor_management/locations.ts | 7 +- .../monitor_management/monitor_types.ts | 34 +- x-pack/plugins/uptime/common/types/index.ts | 4 +- .../public/components/fleet_package/types.tsx | 4 +- .../action_bar/action_bar.test.tsx | 8 +- .../action_bar/action_bar.tsx | 4 +- .../monitor_list/monitor_list.test.tsx | 7 +- .../monitor_list/monitor_list.tsx | 2 +- .../uptime/public/pages/edit_monitor.tsx | 3 +- .../public/state/api/monitor_management.ts | 15 +- .../synthetics_service/formatters/index.ts | 2 +- .../get_service_locations.ts | 7 +- .../synthetics_service/service_api_client.ts | 2 +- .../synthetics_service/synthetics_service.ts | 32 +- .../plugins/uptime/server/rest_api/index.ts | 2 +- .../synthetics_service/add_monitor.ts | 19 +- .../synthetics_service/delete_monitor.ts | 14 +- .../synthetics_service/edit_monitor.ts | 51 +- .../{get_monitors.ts => get_monitor.ts} | 17 +- .../monitor_validation.test.ts | 440 ++++++++++++++++++ .../synthetics_service/monitor_validation.ts | 81 ++++ .../synthetics_service/service_errors.ts | 12 + .../apis/uptime/rest/add_monitor.ts | 46 +- .../apis/uptime/rest/delete_monitor.ts | 68 ++- .../apis/uptime/rest/edit_monitor.ts | 99 +++- .../uptime/rest/fixtures/browser_monitor.json | 41 ++ .../uptime/rest/fixtures/http_monitor.json | 61 +++ .../uptime/rest/fixtures/icmp_monitor.json | 35 ++ .../uptime/rest/fixtures/tcp_monitor.json | 31 ++ .../apis/uptime/rest/get_monitor.ts | 112 ++++- .../uptime/rest/helper/get_fixture_json.ts | 21 + 31 files changed, 1150 insertions(+), 131 deletions(-) rename x-pack/plugins/uptime/server/rest_api/synthetics_service/{get_monitors.ts => get_monitor.ts} (72%) create mode 100644 x-pack/plugins/uptime/server/rest_api/synthetics_service/monitor_validation.test.ts create mode 100644 x-pack/plugins/uptime/server/rest_api/synthetics_service/monitor_validation.ts create mode 100644 x-pack/plugins/uptime/server/rest_api/synthetics_service/service_errors.ts create mode 100644 x-pack/test/api_integration/apis/uptime/rest/fixtures/browser_monitor.json create mode 100644 x-pack/test/api_integration/apis/uptime/rest/fixtures/http_monitor.json create mode 100644 x-pack/test/api_integration/apis/uptime/rest/fixtures/icmp_monitor.json create mode 100644 x-pack/test/api_integration/apis/uptime/rest/fixtures/tcp_monitor.json create mode 100644 x-pack/test/api_integration/apis/uptime/rest/helper/get_fixture_json.ts diff --git a/x-pack/plugins/uptime/common/runtime_types/monitor_management/locations.ts b/x-pack/plugins/uptime/common/runtime_types/monitor_management/locations.ts index 4e3052e6b66f5..b615c1d914ac1 100644 --- a/x-pack/plugins/uptime/common/runtime_types/monitor_management/locations.ts +++ b/x-pack/plugins/uptime/common/runtime_types/monitor_management/locations.ts @@ -30,6 +30,11 @@ export const ServiceLocationCodec = t.interface({ export const ServiceLocationsCodec = t.array(ServiceLocationCodec); +export const ServiceLocationsApiResponseCodec = t.interface({ + locations: ServiceLocationsCodec, +}); + +export type ManifestLocation = t.TypeOf; export type ServiceLocation = t.TypeOf; export type ServiceLocations = t.TypeOf; -export type ManifestLocation = t.TypeOf; +export type ServiceLocationsApiResponse = t.TypeOf; diff --git a/x-pack/plugins/uptime/common/runtime_types/monitor_management/monitor_types.ts b/x-pack/plugins/uptime/common/runtime_types/monitor_management/monitor_types.ts index 50dc0108d9a81..bda29aad2b227 100644 --- a/x-pack/plugins/uptime/common/runtime_types/monitor_management/monitor_types.ts +++ b/x-pack/plugins/uptime/common/runtime_types/monitor_management/monitor_types.ts @@ -7,15 +7,16 @@ import * as t from 'io-ts'; import { ConfigKey } from './config_key'; +import { ServiceLocationsCodec } from './locations'; import { DataStreamCodec, ModeCodec, ResponseBodyIndexPolicyCodec, ScheduleUnitCodec, + TLSVersionCodec, + VerificationModeCodec, } from './monitor_configs'; import { MetadataCodec } from './monitor_meta_data'; -import { TLSVersionCodec, VerificationModeCodec } from './monitor_configs'; -import { ServiceLocationsCodec } from './locations'; const Schedule = t.interface({ number: t.string, @@ -187,6 +188,7 @@ export type BrowserFields = t.TypeOf; export type BrowserSimpleFields = t.TypeOf; export type BrowserAdvancedFields = t.TypeOf; +// MonitorFields, represents any possible monitor type export const MonitorFieldsCodec = t.intersection([ HTTPFieldsCodec, TCPFieldsCodec, @@ -196,19 +198,27 @@ export const MonitorFieldsCodec = t.intersection([ export type MonitorFields = t.TypeOf; +// Monitor, represents one of (Icmp | Tcp | Http | Browser) +export const SyntheticsMonitorCodec = t.union([ + HTTPFieldsCodec, + TCPFieldsCodec, + ICMPSimpleFieldsCodec, + BrowserFieldsCodec, +]); + +export type SyntheticsMonitor = t.TypeOf; + +export const SyntheticsMonitorWithIdCodec = t.intersection([ + SyntheticsMonitorCodec, + t.interface({ id: t.string }), +]); +export type SyntheticsMonitorWithId = t.TypeOf; + export const MonitorManagementListResultCodec = t.type({ - monitors: t.array(t.interface({ id: t.string, attributes: MonitorFieldsCodec })), + monitors: t.array(t.interface({ id: t.string, attributes: SyntheticsMonitorCodec })), page: t.number, perPage: t.number, total: t.union([t.number, t.null]), }); -export type MonitorManagementListResult = Omit< - t.TypeOf, - 'monitors' -> & { - monitors: Array<{ - id: string; - attributes: Partial; - }>; -}; +export type MonitorManagementListResult = t.TypeOf; diff --git a/x-pack/plugins/uptime/common/types/index.ts b/x-pack/plugins/uptime/common/types/index.ts index 2b681f02686c2..dd448cb2706b5 100644 --- a/x-pack/plugins/uptime/common/types/index.ts +++ b/x-pack/plugins/uptime/common/types/index.ts @@ -6,7 +6,7 @@ */ import { SimpleSavedObject } from 'kibana/public'; -import { MonitorFields } from '../runtime_types/monitor_management'; +import { SyntheticsMonitor } from '../runtime_types'; /** Represents the average monitor duration ms at a point in time. */ export interface MonitorDurationAveragePoint { @@ -32,4 +32,4 @@ export interface MonitorIdParam { monitorId: string; } -export type SyntheticsMonitorSavedObject = SimpleSavedObject; +export type SyntheticsMonitorSavedObject = SimpleSavedObject; diff --git a/x-pack/plugins/uptime/public/components/fleet_package/types.tsx b/x-pack/plugins/uptime/public/components/fleet_package/types.tsx index 3e4c92f1b95e9..de35076a6a850 100644 --- a/x-pack/plugins/uptime/public/components/fleet_package/types.tsx +++ b/x-pack/plugins/uptime/public/components/fleet_package/types.tsx @@ -17,11 +17,9 @@ import { ThrottlingConfigKey, ThrottlingSuffix, ThrottlingSuffixType, -} from '../../../common/runtime_types/monitor_management'; +} from '../../../common/runtime_types'; export * from '../../../common/runtime_types/monitor_management'; -export type Monitor = Partial; - export interface PolicyConfig { [DataStream.HTTP]: HTTPFields; [DataStream.TCP]: TCPFields; diff --git a/x-pack/plugins/uptime/public/components/monitor_management/action_bar/action_bar.test.tsx b/x-pack/plugins/uptime/public/components/monitor_management/action_bar/action_bar.test.tsx index aad7f9289a78a..453d7eac44c5f 100644 --- a/x-pack/plugins/uptime/public/components/monitor_management/action_bar/action_bar.test.tsx +++ b/x-pack/plugins/uptime/public/components/monitor_management/action_bar/action_bar.test.tsx @@ -10,20 +10,20 @@ import { screen, waitFor, act } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { render } from '../../../lib/helper/rtl_helpers'; import * as fetchers from '../../../state/api/monitor_management'; -import { DataStream, ScheduleUnit } from '../../fleet_package/types'; +import { DataStream, HTTPFields, ScheduleUnit, SyntheticsMonitor } from '../../fleet_package/types'; import { ActionBar } from './action_bar'; describe('', () => { const setMonitor = jest.spyOn(fetchers, 'setMonitor'); - const monitor = { + const monitor: SyntheticsMonitor = { name: 'test-monitor', schedule: { unit: ScheduleUnit.MINUTES, number: '2', }, urls: 'https://elastic.co', - type: DataStream.BROWSER, - }; + type: DataStream.HTTP, + } as unknown as HTTPFields; beforeEach(() => { jest.clearAllMocks(); diff --git a/x-pack/plugins/uptime/public/components/monitor_management/action_bar/action_bar.tsx b/x-pack/plugins/uptime/public/components/monitor_management/action_bar/action_bar.tsx index 6049e4aafd0df..bb6d7d041b9c7 100644 --- a/x-pack/plugins/uptime/public/components/monitor_management/action_bar/action_bar.tsx +++ b/x-pack/plugins/uptime/public/components/monitor_management/action_bar/action_bar.tsx @@ -16,10 +16,10 @@ import { useKibana } from '../../../../../../../src/plugins/kibana_react/public' import { MONITOR_MANAGEMENT } from '../../../../common/constants'; import { setMonitor } from '../../../state/api'; -import { Monitor } from '../../fleet_package/types'; +import { SyntheticsMonitor } from '../../fleet_package/types'; interface Props { - monitor: Monitor; + monitor: SyntheticsMonitor; isValid: boolean; onSave?: () => void; } diff --git a/x-pack/plugins/uptime/public/components/monitor_management/monitor_list/monitor_list.test.tsx b/x-pack/plugins/uptime/public/components/monitor_management/monitor_list/monitor_list.test.tsx index 454bd10621f4f..8884eb197b99b 100644 --- a/x-pack/plugins/uptime/public/components/monitor_management/monitor_list/monitor_list.test.tsx +++ b/x-pack/plugins/uptime/public/components/monitor_management/monitor_list/monitor_list.test.tsx @@ -9,8 +9,9 @@ import React from 'react'; import { screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { render } from '../../../lib/helper/rtl_helpers'; -import { DataStream, ScheduleUnit } from '../../fleet_package/types'; +import { DataStream, HTTPFields, ScheduleUnit } from '../../fleet_package/types'; import { MonitorManagementList } from './monitor_list'; +import { MonitorManagementList as MonitorManagementListState } from '../../../state/reducers/monitor_management'; describe('', () => { const setRefresh = jest.fn(); @@ -29,7 +30,7 @@ describe('', () => { urls: `https://test-${i}.co`, type: DataStream.HTTP, tags: [`tag-${i}`], - }, + } as HTTPFields, }); } const state = { @@ -49,7 +50,7 @@ describe('', () => { monitorList: true, serviceLocations: false, }, - }, + } as MonitorManagementListState, }; it.each(monitors)('navigates to edit monitor flow on edit pencil', (monitor) => { diff --git a/x-pack/plugins/uptime/public/components/monitor_management/monitor_list/monitor_list.tsx b/x-pack/plugins/uptime/public/components/monitor_management/monitor_list/monitor_list.tsx index 6c4e92cae8888..1d145506e64f2 100644 --- a/x-pack/plugins/uptime/public/components/monitor_management/monitor_list/monitor_list.tsx +++ b/x-pack/plugins/uptime/public/components/monitor_management/monitor_list/monitor_list.tsx @@ -7,7 +7,7 @@ import React, { useContext, useMemo, useCallback } from 'react'; import { EuiBasicTable, EuiPanel, EuiSpacer, EuiLink } from '@elastic/eui'; import { MonitorManagementList as MonitorManagementListState } from '../../../state/reducers/monitor_management'; -import { MonitorFields } from '../../../../common/runtime_types/monitor_management'; +import { MonitorFields } from '../../../../common/runtime_types'; import { UptimeSettingsContext } from '../../../contexts'; import { Actions } from './actions'; import { MonitorTags } from './tags'; diff --git a/x-pack/plugins/uptime/public/pages/edit_monitor.tsx b/x-pack/plugins/uptime/public/pages/edit_monitor.tsx index 54840b5fff80b..16842e6ed0c5f 100644 --- a/x-pack/plugins/uptime/public/pages/edit_monitor.tsx +++ b/x-pack/plugins/uptime/public/pages/edit_monitor.tsx @@ -9,6 +9,7 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; import { useParams } from 'react-router-dom'; import { useTrackPageview, FETCH_STATUS, useFetcher } from '../../../observability/public'; +import { MonitorFields } from '../../common/runtime_types'; import { EditMonitorConfig } from '../components/monitor_management/edit_monitor_config'; import { Loader } from '../components/monitor_management/loader/loader'; import { getMonitor } from '../state/api'; @@ -24,7 +25,7 @@ export const EditMonitorPage: React.FC = () => { return getMonitor({ id: Buffer.from(monitorId, 'base64').toString('utf8') }); }, [monitorId]); - const monitor = data?.attributes; + const monitor = data?.attributes as MonitorFields; const { error: locationsError, loading: locationsLoading } = useLocations(); return ( diff --git a/x-pack/plugins/uptime/public/state/api/monitor_management.ts b/x-pack/plugins/uptime/public/state/api/monitor_management.ts index 13f64e44d85fb..33c04c060588d 100644 --- a/x-pack/plugins/uptime/public/state/api/monitor_management.ts +++ b/x-pack/plugins/uptime/public/state/api/monitor_management.ts @@ -11,13 +11,20 @@ import { MonitorManagementListResultCodec, MonitorManagementListResult, ServiceLocations, - ServiceLocationsCodec, + SyntheticsMonitor, + ServiceLocationsApiResponseCodec, } from '../../../common/runtime_types'; import { SyntheticsMonitorSavedObject } from '../../../common/types'; import { apiService } from './utils'; -// TODO, change to monitor runtime type -export const setMonitor = async ({ monitor, id }: { monitor: any; id?: string }): Promise => { +// TODO: Type the return type from runtime types +export const setMonitor = async ({ + monitor, + id, +}: { + monitor: SyntheticsMonitor; + id?: string; +}): Promise => { if (id) { return await apiService.put(`${API_URLS.SYNTHETICS_MONITORS}/${id}`, monitor); } else { @@ -48,7 +55,7 @@ export const fetchServiceLocations = async (): Promise => { const { locations } = await apiService.get( API_URLS.SERVICE_LOCATIONS, undefined, - ServiceLocationsCodec + ServiceLocationsApiResponseCodec ); return locations; }; diff --git a/x-pack/plugins/uptime/server/lib/synthetics_service/formatters/index.ts b/x-pack/plugins/uptime/server/lib/synthetics_service/formatters/index.ts index c17c4d2a68a4a..a086656c2c62e 100644 --- a/x-pack/plugins/uptime/server/lib/synthetics_service/formatters/index.ts +++ b/x-pack/plugins/uptime/server/lib/synthetics_service/formatters/index.ts @@ -10,7 +10,7 @@ import { tcpFormatters, TCPFormatMap } from './tcp'; import { icmpFormatters, ICMPFormatMap } from './icmp'; import { browserFormatters, BrowserFormatMap } from './browser'; import { commonFormatters, CommonFormatMap } from './common'; -import { DataStream } from '../../../../common/runtime_types/monitor_management'; +import { DataStream } from '../../../../common/runtime_types'; type Formatters = HTTPFormatMap & TCPFormatMap & ICMPFormatMap & BrowserFormatMap & CommonFormatMap; diff --git a/x-pack/plugins/uptime/server/lib/synthetics_service/get_service_locations.ts b/x-pack/plugins/uptime/server/lib/synthetics_service/get_service_locations.ts index 322a784c8c773..4802b4687e00d 100644 --- a/x-pack/plugins/uptime/server/lib/synthetics_service/get_service_locations.ts +++ b/x-pack/plugins/uptime/server/lib/synthetics_service/get_service_locations.ts @@ -9,7 +9,8 @@ import axios from 'axios'; import { ManifestLocation, ServiceLocations, -} from '../../../common/runtime_types/monitor_management'; + ServiceLocationsApiResponse, +} from '../../../common/runtime_types'; export async function getServiceLocations({ manifestUrl }: { manifestUrl: string }) { const locations: ServiceLocations = []; @@ -25,10 +26,10 @@ export async function getServiceLocations({ manifestUrl }: { manifestUrl: string }); }); - return { locations }; + return { locations } as ServiceLocationsApiResponse; } catch (e) { return { locations: [], - }; + } as ServiceLocationsApiResponse; } } diff --git a/x-pack/plugins/uptime/server/lib/synthetics_service/service_api_client.ts b/x-pack/plugins/uptime/server/lib/synthetics_service/service_api_client.ts index 934264914067d..bd5b0b13cc766 100644 --- a/x-pack/plugins/uptime/server/lib/synthetics_service/service_api_client.ts +++ b/x-pack/plugins/uptime/server/lib/synthetics_service/service_api_client.ts @@ -10,7 +10,7 @@ import { forkJoin, from as rxjsFrom, Observable, of } from 'rxjs'; import { catchError, tap } from 'rxjs/operators'; import { getServiceLocations } from './get_service_locations'; import { Logger } from '../../../../../../src/core/server'; -import { MonitorFields, ServiceLocations } from '../../../common/runtime_types/monitor_management'; +import { MonitorFields, ServiceLocations } from '../../../common/runtime_types'; const TEST_SERVICE_USERNAME = 'localKibanaIntegrationTestsUser'; diff --git a/x-pack/plugins/uptime/server/lib/synthetics_service/synthetics_service.ts b/x-pack/plugins/uptime/server/lib/synthetics_service/synthetics_service.ts index 2b200d6dbc25a..d90dc1de9a114 100644 --- a/x-pack/plugins/uptime/server/lib/synthetics_service/synthetics_service.ts +++ b/x-pack/plugins/uptime/server/lib/synthetics_service/synthetics_service.ts @@ -7,8 +7,6 @@ /* eslint-disable max-classes-per-file */ -import { ValuesType } from 'utility-types'; - import { CoreStart, KibanaRequest, @@ -24,15 +22,17 @@ import { UptimeServerSetup } from '../adapters'; import { installSyntheticsIndexTemplates } from '../../rest_api/synthetics_service/install_index_templates'; import { SyntheticsServiceApiKey } from '../../../common/runtime_types/synthetics_service_api_key'; import { getAPIKeyForSyntheticsService } from './get_api_key'; -import { SyntheticsMonitorSavedObject } from '../../../common/types'; import { syntheticsMonitorType } from '../saved_objects/synthetics_monitor'; import { getEsHosts } from './get_es_hosts'; import { UptimeConfig } from '../../../common/config'; import { ServiceAPIClient } from './service_api_client'; import { formatMonitorConfig } from './formatters/format_configs'; -import { ConfigKey, MonitorFields } from '../../../common/runtime_types/monitor_management'; - -export type MonitorFieldsWithID = MonitorFields & { id: string }; +import { + ConfigKey, + MonitorFields, + SyntheticsMonitor, + SyntheticsMonitorWithId, +} from '../../../common/runtime_types'; const SYNTHETICS_SERVICE_SYNC_MONITORS_TASK_TYPE = 'UPTIME:SyntheticsService:Sync-Saved-Monitor-Objects'; @@ -169,7 +169,7 @@ export class SyntheticsService { }; } - async pushConfigs(request?: KibanaRequest, configs?: MonitorFieldsWithID[]) { + async pushConfigs(request?: KibanaRequest, configs?: SyntheticsMonitorWithId[]) { const monitors = this.formatConfigs(configs || (await this.getMonitorConfigs())); if (monitors.length === 0) { return; @@ -187,7 +187,7 @@ export class SyntheticsService { } } - async deleteConfigs(request: KibanaRequest, configs: MonitorFieldsWithID[]) { + async deleteConfigs(request: KibanaRequest, configs: SyntheticsMonitorWithId[]) { const data = { monitors: this.formatConfigs(configs), output: await this.getOutput(request), @@ -197,20 +197,22 @@ export class SyntheticsService { async getMonitorConfigs() { const savedObjectsClient = this.server.savedObjectsClient; - const monitorsSavedObjects = await savedObjectsClient?.find< - SyntheticsMonitorSavedObject['attributes'] - >({ + + if (!savedObjectsClient?.find) { + return [] as SyntheticsMonitorWithId[]; + } + + const findResult = await savedObjectsClient.find({ type: syntheticsMonitorType, }); - const savedObjectsList = monitorsSavedObjects?.saved_objects ?? []; - return savedObjectsList.map>(({ attributes, id }) => ({ + return (findResult.saved_objects ?? []).map(({ attributes, id }) => ({ ...attributes, id, - })); + })) as SyntheticsMonitorWithId[]; } - formatConfigs(configs: MonitorFields[]) { + formatConfigs(configs: SyntheticsMonitorWithId[]) { return configs.map((config: Partial) => formatMonitorConfig(Object.keys(config) as ConfigKey[], config) ); diff --git a/x-pack/plugins/uptime/server/rest_api/index.ts b/x-pack/plugins/uptime/server/rest_api/index.ts index c4024795b344e..905af60961d9a 100644 --- a/x-pack/plugins/uptime/server/rest_api/index.ts +++ b/x-pack/plugins/uptime/server/rest_api/index.ts @@ -32,7 +32,7 @@ import { getServiceLocationsRoute } from './synthetics_service/get_service_locat import { getAllSyntheticsMonitorRoute, getSyntheticsMonitorRoute, -} from './synthetics_service/get_monitors'; +} from './synthetics_service/get_monitor'; import { addSyntheticsMonitorRoute } from './synthetics_service/add_monitor'; import { editSyntheticsMonitorRoute } from './synthetics_service/edit_monitor'; import { deleteSyntheticsMonitorRoute } from './synthetics_service/delete_monitor'; diff --git a/x-pack/plugins/uptime/server/rest_api/synthetics_service/add_monitor.ts b/x-pack/plugins/uptime/server/rest_api/synthetics_service/add_monitor.ts index c30b4f96e8145..319d68d1b6e8b 100644 --- a/x-pack/plugins/uptime/server/rest_api/synthetics_service/add_monitor.ts +++ b/x-pack/plugins/uptime/server/rest_api/synthetics_service/add_monitor.ts @@ -5,10 +5,11 @@ * 2.0. */ import { schema } from '@kbn/config-schema'; +import { MonitorFields, SyntheticsMonitor } from '../../../common/runtime_types'; import { UMRestApiRouteFactory } from '../types'; import { API_URLS } from '../../../common/constants'; -import { SyntheticsMonitorSavedObject } from '../../../common/types'; import { syntheticsMonitorType } from '../../lib/saved_objects/synthetics_monitor'; +import { validateMonitor } from './monitor_validation'; export const addSyntheticsMonitorRoute: UMRestApiRouteFactory = () => ({ method: 'POST', @@ -16,10 +17,20 @@ export const addSyntheticsMonitorRoute: UMRestApiRouteFactory = () => ({ validate: { body: schema.any(), }, - handler: async ({ request, savedObjectsClient, server }): Promise => { - const monitor = request.body as SyntheticsMonitorSavedObject['attributes']; + handler: async ({ request, response, savedObjectsClient, server }): Promise => { + const monitor: SyntheticsMonitor = request.body as SyntheticsMonitor; - const newMonitor = await savedObjectsClient.create(syntheticsMonitorType, monitor); + const validationResult = validateMonitor(monitor as MonitorFields); + + if (!validationResult.valid) { + const { reason: message, details, payload } = validationResult; + return response.badRequest({ body: { message, attributes: { details, ...payload } } }); + } + + const newMonitor = await savedObjectsClient.create( + syntheticsMonitorType, + monitor + ); const { syntheticsService } = server; diff --git a/x-pack/plugins/uptime/server/rest_api/synthetics_service/delete_monitor.ts b/x-pack/plugins/uptime/server/rest_api/synthetics_service/delete_monitor.ts index c951acae21633..c1a1ea8945606 100644 --- a/x-pack/plugins/uptime/server/rest_api/synthetics_service/delete_monitor.ts +++ b/x-pack/plugins/uptime/server/rest_api/synthetics_service/delete_monitor.ts @@ -6,26 +6,27 @@ */ import { schema } from '@kbn/config-schema'; import { SavedObjectsErrorHelpers } from '../../../../../../src/core/server'; +import { SyntheticsMonitor } from '../../../common/runtime_types'; import { UMRestApiRouteFactory } from '../types'; import { API_URLS } from '../../../common/constants'; import { syntheticsMonitorType } from '../../lib/saved_objects/synthetics_monitor'; -import { SyntheticsMonitorSavedObject } from '../../../common/types'; +import { getMonitorNotFoundResponse } from './service_errors'; export const deleteSyntheticsMonitorRoute: UMRestApiRouteFactory = () => ({ method: 'DELETE', path: API_URLS.SYNTHETICS_MONITORS + '/{monitorId}', validate: { params: schema.object({ - monitorId: schema.string(), + monitorId: schema.string({ minLength: 1, maxLength: 1024 }), }), }, - handler: async ({ request, savedObjectsClient, server }): Promise => { + handler: async ({ request, response, savedObjectsClient, server }): Promise => { const { monitorId } = request.params; const { syntheticsService } = server; try { - const monitor = await savedObjectsClient.get( + const monitor = await savedObjectsClient.get( syntheticsMonitorType, monitorId ); @@ -34,14 +35,17 @@ export const deleteSyntheticsMonitorRoute: UMRestApiRouteFactory = () => ({ const errors = await syntheticsService.deleteConfigs(request, [ { ...monitor.attributes, id: monitorId }, ]); + if (errors) { return errors; } + return monitorId; } catch (getErr) { if (SavedObjectsErrorHelpers.isNotFoundError(getErr)) { - return 'Not found'; + return getMonitorNotFoundResponse(response, monitorId); } + throw getErr; } }, diff --git a/x-pack/plugins/uptime/server/rest_api/synthetics_service/edit_monitor.ts b/x-pack/plugins/uptime/server/rest_api/synthetics_service/edit_monitor.ts index 684838ed4efe0..a95876d8d3ea6 100644 --- a/x-pack/plugins/uptime/server/rest_api/synthetics_service/edit_monitor.ts +++ b/x-pack/plugins/uptime/server/rest_api/synthetics_service/edit_monitor.ts @@ -4,12 +4,18 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ + import { schema } from '@kbn/config-schema'; +import { SavedObjectsUpdateResponse } from 'kibana/server'; +import { SavedObjectsErrorHelpers } from '../../../../../../src/core/server'; +import { MonitorFields, SyntheticsMonitor } from '../../../common/runtime_types'; import { UMRestApiRouteFactory } from '../types'; import { API_URLS } from '../../../common/constants'; -import { SyntheticsMonitorSavedObject } from '../../../common/types'; import { syntheticsMonitorType } from '../../lib/saved_objects/synthetics_monitor'; +import { validateMonitor } from './monitor_validation'; +import { getMonitorNotFoundResponse } from './service_errors'; +// Simplify return promise type and type it with runtime_types export const editSyntheticsMonitorRoute: UMRestApiRouteFactory = () => ({ method: 'PUT', path: API_URLS.SYNTHETICS_MONITORS + '/{monitorId}', @@ -19,26 +25,43 @@ export const editSyntheticsMonitorRoute: UMRestApiRouteFactory = () => ({ }), body: schema.any(), }, - handler: async ({ request, savedObjectsClient, server }): Promise => { - const monitor = request.body as SyntheticsMonitorSavedObject['attributes']; + handler: async ({ request, response, savedObjectsClient, server }): Promise => { + const monitor = request.body as SyntheticsMonitor; + + const validationResult = validateMonitor(monitor as MonitorFields); + + if (!validationResult.valid) { + const { reason: message, details, payload } = validationResult; + return response.badRequest({ body: { message, attributes: { details, ...payload } } }); + } const { monitorId } = request.params; const { syntheticsService } = server; - const editMonitor = await savedObjectsClient.update(syntheticsMonitorType, monitorId, monitor); + try { + const editMonitor: SavedObjectsUpdateResponse = + await savedObjectsClient.update(syntheticsMonitorType, monitorId, monitor); - const errors = await syntheticsService.pushConfigs(request, [ - { - ...(editMonitor.attributes as SyntheticsMonitorSavedObject['attributes']), - id: editMonitor.id, - }, - ]); + const errors = await syntheticsService.pushConfigs(request, [ + { + ...(editMonitor.attributes as SyntheticsMonitor), + id: editMonitor.id, + }, + ]); - if (errors) { - return errors; - } + // Return service sync errors in OK response + if (errors) { + return errors; + } + + return editMonitor; + } catch (updateErr) { + if (SavedObjectsErrorHelpers.isNotFoundError(updateErr)) { + return getMonitorNotFoundResponse(response, monitorId); + } - return editMonitor; + throw updateErr; + } }, }); diff --git a/x-pack/plugins/uptime/server/rest_api/synthetics_service/get_monitors.ts b/x-pack/plugins/uptime/server/rest_api/synthetics_service/get_monitor.ts similarity index 72% rename from x-pack/plugins/uptime/server/rest_api/synthetics_service/get_monitors.ts rename to x-pack/plugins/uptime/server/rest_api/synthetics_service/get_monitor.ts index ac54e3ffdb041..cfe409c9f06bd 100644 --- a/x-pack/plugins/uptime/server/rest_api/synthetics_service/get_monitors.ts +++ b/x-pack/plugins/uptime/server/rest_api/synthetics_service/get_monitor.ts @@ -4,22 +4,33 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ + import { schema } from '@kbn/config-schema'; +import { SavedObjectsErrorHelpers } from '../../../../../../src/core/server'; import { UMRestApiRouteFactory } from '../types'; import { API_URLS } from '../../../common/constants'; import { syntheticsMonitorType } from '../../lib/saved_objects/synthetics_monitor'; +import { getMonitorNotFoundResponse } from './service_errors'; export const getSyntheticsMonitorRoute: UMRestApiRouteFactory = () => ({ method: 'GET', path: API_URLS.SYNTHETICS_MONITORS + '/{monitorId}', validate: { params: schema.object({ - monitorId: schema.string(), + monitorId: schema.string({ minLength: 1, maxLength: 1024 }), }), }, - handler: async ({ request, savedObjectsClient }): Promise => { + handler: async ({ request, response, savedObjectsClient }): Promise => { const { monitorId } = request.params; - return await savedObjectsClient.get(syntheticsMonitorType, monitorId); + try { + return await savedObjectsClient.get(syntheticsMonitorType, monitorId); + } catch (getErr) { + if (SavedObjectsErrorHelpers.isNotFoundError(getErr)) { + return getMonitorNotFoundResponse(response, monitorId); + } + + throw getErr; + } }, }); diff --git a/x-pack/plugins/uptime/server/rest_api/synthetics_service/monitor_validation.test.ts b/x-pack/plugins/uptime/server/rest_api/synthetics_service/monitor_validation.test.ts new file mode 100644 index 0000000000000..7fbe0fe460593 --- /dev/null +++ b/x-pack/plugins/uptime/server/rest_api/synthetics_service/monitor_validation.test.ts @@ -0,0 +1,440 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { + BrowserAdvancedFields, + BrowserFields, + BrowserSimpleFields, + CommonFields, + ConfigKey, + DataStream, + HTTPAdvancedFields, + HTTPFields, + HTTPSimpleFields, + ICMPSimpleFields, + Metadata, + Mode, + MonitorFields, + ResponseBodyIndexPolicy, + ScheduleUnit, + TCPAdvancedFields, + TCPFields, + TCPSimpleFields, + TLSFields, + TLSVersion, + VerificationMode, + ZipUrlTLSFields, +} from '../../../common/runtime_types'; +import { validateMonitor } from './monitor_validation'; + +describe('validateMonitor', () => { + let testSchedule; + let testTags: string[]; + let testCommonFields: CommonFields; + let testMetaData: Metadata; + let testICMPFields: ICMPSimpleFields; + let testTCPSimpleFields: TCPSimpleFields; + let testTCPAdvancedFields: TCPAdvancedFields; + let testTCPFields: TCPFields; + let testTLSFields: TLSFields; + let testHTTPSimpleFields: HTTPSimpleFields; + let testHTTPAdvancedFields: HTTPAdvancedFields; + let testHTTPFields: HTTPFields; + let testZipUrlTLSFields: ZipUrlTLSFields; + let testBrowserSimpleFields: BrowserSimpleFields; + let testBrowserAdvancedFields: BrowserAdvancedFields; + let testBrowserFields: BrowserFields; + + beforeEach(() => { + testSchedule = { number: '5', unit: ScheduleUnit.MINUTES }; + testTags = ['tag1', 'tag2']; + testCommonFields = { + [ConfigKey.MONITOR_TYPE]: DataStream.ICMP, + [ConfigKey.NAME]: 'test-monitor-name', + [ConfigKey.ENABLED]: true, + [ConfigKey.TAGS]: testTags, + [ConfigKey.SCHEDULE]: testSchedule, + [ConfigKey.APM_SERVICE_NAME]: '', + [ConfigKey.TIMEOUT]: '3m', + [ConfigKey.LOCATIONS]: [ + { + id: 'eu-west-1', + label: 'EU West', + geo: { + lat: 33.4354332, + lon: 73.4453553, + }, + url: 'https://test-url.com', + }, + ], + }; + testMetaData = { + is_tls_enabled: false, + is_zip_url_tls_enabled: false, + script_source: { + is_generated_script: false, + file_name: 'test-file.name', + }, + }; + + testICMPFields = { + ...testCommonFields, + [ConfigKey.HOSTS]: 'test-hosts', + [ConfigKey.WAIT]: '', + [ConfigKey.MONITOR_TYPE]: DataStream.ICMP, + }; + + testTLSFields = { + [ConfigKey.TLS_CERTIFICATE_AUTHORITIES]: 't.string', + [ConfigKey.TLS_CERTIFICATE]: 't.string', + [ConfigKey.TLS_KEY]: 't.string', + [ConfigKey.TLS_KEY_PASSPHRASE]: 't.string', + [ConfigKey.TLS_VERIFICATION_MODE]: VerificationMode.CERTIFICATE, + [ConfigKey.TLS_VERSION]: [TLSVersion.ONE_ONE, TLSVersion.ONE_TWO], + }; + + testTCPSimpleFields = { + ...testCommonFields, + [ConfigKey.METADATA]: testMetaData, + [ConfigKey.HOSTS]: 'https://host1.com', + }; + + testTCPAdvancedFields = { + [ConfigKey.PROXY_URL]: 'http://proxy-url.com', + [ConfigKey.PROXY_USE_LOCAL_RESOLVER]: false, + [ConfigKey.RESPONSE_RECEIVE_CHECK]: '', + [ConfigKey.REQUEST_SEND_CHECK]: '', + }; + + testTCPFields = { + ...testTCPSimpleFields, + ...testTCPAdvancedFields, + ...testTLSFields, + [ConfigKey.MONITOR_TYPE]: DataStream.TCP, + }; + + testHTTPSimpleFields = { + ...testCommonFields, + [ConfigKey.METADATA]: testMetaData, + [ConfigKey.MAX_REDIRECTS]: '3', + [ConfigKey.URLS]: 'https://example.com', + }; + + testHTTPAdvancedFields = { + [ConfigKey.PASSWORD]: 'test', + [ConfigKey.PROXY_URL]: 'http://proxy.com', + [ConfigKey.RESPONSE_BODY_CHECK_NEGATIVE]: [], + [ConfigKey.RESPONSE_BODY_CHECK_POSITIVE]: [], + [ConfigKey.RESPONSE_BODY_INDEX]: ResponseBodyIndexPolicy.NEVER, + [ConfigKey.RESPONSE_HEADERS_CHECK]: {}, + [ConfigKey.RESPONSE_HEADERS_INDEX]: true, + [ConfigKey.RESPONSE_STATUS_CHECK]: ['200', '201'], + [ConfigKey.REQUEST_BODY_CHECK]: { value: 'testValue', type: Mode.JSON }, + [ConfigKey.REQUEST_HEADERS_CHECK]: {}, + [ConfigKey.REQUEST_METHOD_CHECK]: '', + [ConfigKey.USERNAME]: 'test-username', + }; + + testHTTPFields = { + ...testHTTPSimpleFields, + ...testHTTPAdvancedFields, + ...testTLSFields, + [ConfigKey.MONITOR_TYPE]: DataStream.HTTP, + }; + + testZipUrlTLSFields = { + [ConfigKey.ZIP_URL_TLS_CERTIFICATE_AUTHORITIES]: 'test', + [ConfigKey.ZIP_URL_TLS_CERTIFICATE]: 'test', + [ConfigKey.ZIP_URL_TLS_KEY]: 'key', + [ConfigKey.ZIP_URL_TLS_KEY_PASSPHRASE]: 'passphrase', + [ConfigKey.ZIP_URL_TLS_VERIFICATION_MODE]: VerificationMode.STRICT, + [ConfigKey.ZIP_URL_TLS_VERSION]: [TLSVersion.ONE_ONE, TLSVersion.ONE_TWO], + }; + + testBrowserSimpleFields = { + ...testZipUrlTLSFields, + ...testCommonFields, + [ConfigKey.METADATA]: testMetaData, + [ConfigKey.SOURCE_INLINE]: '', + [ConfigKey.SOURCE_ZIP_URL]: '', + [ConfigKey.SOURCE_ZIP_FOLDER]: '', + [ConfigKey.SOURCE_ZIP_USERNAME]: 'test-username', + [ConfigKey.SOURCE_ZIP_PASSWORD]: 'password', + [ConfigKey.SOURCE_ZIP_PROXY_URL]: 'http://proxy-url.com', + [ConfigKey.PARAMS]: '', + }; + + testBrowserAdvancedFields = { + [ConfigKey.SYNTHETICS_ARGS]: ['arg1', 'arg2'], + [ConfigKey.SCREENSHOTS]: 'false', + [ConfigKey.JOURNEY_FILTERS_MATCH]: 'false', + [ConfigKey.JOURNEY_FILTERS_TAGS]: testTags, + [ConfigKey.IGNORE_HTTPS_ERRORS]: false, + [ConfigKey.IS_THROTTLING_ENABLED]: true, + [ConfigKey.DOWNLOAD_SPEED]: '5', + [ConfigKey.UPLOAD_SPEED]: '3', + [ConfigKey.LATENCY]: '20', + [ConfigKey.THROTTLING_CONFIG]: '5d/3u/20l', + }; + + testBrowserFields = { + ...testBrowserSimpleFields, + ...testBrowserAdvancedFields, + [ConfigKey.MONITOR_TYPE]: DataStream.BROWSER, + }; + }); + + describe('should invalidate', () => { + it(`when 'type' is null or undefined`, () => { + const testMonitor = { type: undefined } as unknown as MonitorFields; + const result = validateMonitor(testMonitor); + expect(result).toMatchObject({ + valid: false, + reason: 'Monitor type is invalid', + details: expect.stringMatching(/(?=.*invalid)(?=.*DataStream)/i), + }); + }); + + it(`when 'type' is not an acceptable monitor type (DataStream)`, () => { + const monitor = { type: 'non-HTTP' } as unknown as MonitorFields; + const result = validateMonitor(monitor); + expect(result).toMatchObject({ + valid: false, + reason: 'Monitor type is invalid', + details: expect.stringMatching(/(?=.*invalid)(?=.*non-HTTP)(?=.*DataStream)/i), + }); + }); + }); + + describe('should validate', () => { + it('when payload is a correct ICMP monitor', () => { + const testMonitor = testICMPFields as MonitorFields; + const result = validateMonitor(testMonitor); + expect(result).toMatchObject({ + valid: true, + reason: '', + details: '', + payload: testMonitor, + }); + }); + + it('when payload is a correct TCP monitor', () => { + const testMonitor = testTCPFields as MonitorFields; + const result = validateMonitor(testMonitor); + expect(result).toMatchObject({ + valid: true, + reason: '', + details: '', + payload: testMonitor, + }); + }); + + it('when payload is a correct HTTP monitor', () => { + const testMonitor = testHTTPFields as MonitorFields; + + const result = validateMonitor(testMonitor); + expect(result).toMatchObject({ + valid: true, + reason: '', + details: '', + payload: testMonitor, + }); + }); + + it('when payload is a correct Browser monitor', () => { + const testMonitor = testBrowserFields as MonitorFields; + const result = validateMonitor(testMonitor); + expect(result).toMatchObject({ + valid: true, + reason: '', + details: '', + payload: testMonitor, + }); + }); + }); + + describe('should invalidate when incomplete properties are received', () => { + it('for ICMP monitor', () => { + const testMonitor = { + ...testICMPFields, + ...({ + [ConfigKey.HOSTS]: undefined, + } as unknown as Partial), + } as MonitorFields; + + const result = validateMonitor(testMonitor); + + expect(result.details).toEqual(expect.stringContaining('Invalid value')); + expect(result.details).toEqual(expect.stringContaining(ConfigKey.HOSTS)); + expect(result).toMatchObject({ + valid: false, + reason: `Monitor is not a valid monitor of type ${DataStream.ICMP}`, + payload: testMonitor, + }); + }); + + it('for TCP monitor', () => { + const testMonitor = { + ...testTCPFields, + ...({ + [ConfigKey.TIMEOUT]: undefined, + } as unknown as Partial), + } as MonitorFields; + + const result = validateMonitor(testMonitor); + + expect(result.details).toEqual(expect.stringContaining('Invalid value')); + expect(result.details).toEqual(expect.stringContaining(ConfigKey.TIMEOUT)); + expect(result).toMatchObject({ + valid: false, + reason: `Monitor is not a valid monitor of type ${DataStream.TCP}`, + payload: testMonitor, + }); + }); + + it('for HTTP monitor', () => { + const testMonitor = { + ...testHTTPFields, + ...({ + [ConfigKey.URLS]: undefined, + } as unknown as Partial), + } as MonitorFields; + + const result = validateMonitor(testMonitor); + + expect(result.details).toEqual(expect.stringContaining('Invalid value')); + expect(result.details).toEqual(expect.stringContaining(ConfigKey.URLS)); + expect(result).toMatchObject({ + valid: false, + reason: `Monitor is not a valid monitor of type ${DataStream.HTTP}`, + payload: testMonitor, + }); + }); + + it('for Browser monitor', () => { + const testMonitor = { + ...testBrowserFields, + ...({ + [ConfigKey.SOURCE_INLINE]: undefined, + } as unknown as Partial), + } as MonitorFields; + + const result = validateMonitor(testMonitor); + + expect(result.details).toEqual(expect.stringContaining('Invalid value')); + expect(result.details).toEqual(expect.stringContaining(ConfigKey.SOURCE_INLINE)); + expect(result).toMatchObject({ + valid: false, + reason: `Monitor is not a valid monitor of type ${DataStream.BROWSER}`, + payload: testMonitor, + }); + }); + }); + + // The following should fail when strict typing/validation is in place + describe('should pass validation when mixed props', () => { + it('of HTTP is provided into TCP', () => { + const testMonitor = { + ...testTCPFields, + ...({ + [ConfigKey.RESPONSE_HEADERS_CHECK]: undefined, + } as unknown as Partial), + } as MonitorFields; + + const result = validateMonitor(testMonitor); + + expect(result).toMatchObject({ + valid: true, + reason: '', + details: '', + payload: testMonitor, + }); + }); + }); + + describe('should validate payload', () => { + it('when parsed from serialized JSON', () => { + const testMonitor = getJsonPayload() as MonitorFields; + + const result = validateMonitor(testMonitor); + + expect(result).toMatchObject({ + valid: true, + reason: '', + details: '', + payload: testMonitor, + }); + }); + }); +}); + +function getJsonPayload() { + const json = + '{' + + ' "type": "http",' + + ' "enabled": true, ' + + ' "tags": [' + + ' "tag1",' + + ' "tag2"' + + ' ],' + + ' "schedule": {' + + ' "number": "5",' + + ' "unit": "m"' + + ' },' + + ' "service.name": "",' + + ' "timeout": "3m",' + + ' "__ui": {' + + ' "is_tls_enabled": false,' + + ' "is_zip_url_tls_enabled": false,' + + ' "script_source": {' + + ' "is_generated_script": false,' + + ' "file_name": "test-file.name"' + + ' }' + + ' },' + + ' "max_redirects": "3",' + + ' "password": "test",' + + ' "urls": "https://nextjs-test-synthetics.vercel.app/api/users",' + + ' "proxy_url": "http://proxy.com",' + + ' "check.response.body.negative": [],' + + ' "check.response.body.positive": [],' + + ' "response.include_body": "never",' + + ' "check.response.headers": {},' + + ' "response.include_headers": true,' + + ' "check.response.status": [' + + ' "200",' + + ' "201"' + + ' ],' + + ' "check.request.body": {' + + ' "value": "testValue",' + + ' "type": "json"' + + ' },' + + ' "check.request.headers": {},' + + ' "check.request.method": "",' + + ' "username": "test-username",' + + ' "ssl.certificate_authorities": "t.string",' + + ' "ssl.certificate": "t.string",' + + ' "ssl.key": "t.string",' + + ' "ssl.key_passphrase": "t.string",' + + ' "ssl.verification_mode": "certificate",' + + ' "ssl.supported_protocols": [' + + ' "TLSv1.1",' + + ' "TLSv1.2"' + + ' ],' + + ' "name": "test-monitor-name",' + + ' "locations": [{' + + ' "id": "eu-west-01",' + + ' "label": "Europe West",' + + ' "geo": {' + + ' "lat": 33.2343132435,' + + ' "lon": 73.2342343434' + + ' },' + + ' "url": "https://example-url.com"' + + ' }]' + + '}'; + + return JSON.parse(json); +} diff --git a/x-pack/plugins/uptime/server/rest_api/synthetics_service/monitor_validation.ts b/x-pack/plugins/uptime/server/rest_api/synthetics_service/monitor_validation.ts new file mode 100644 index 0000000000000..9f14c414e5cad --- /dev/null +++ b/x-pack/plugins/uptime/server/rest_api/synthetics_service/monitor_validation.ts @@ -0,0 +1,81 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { isLeft } from 'fp-ts/lib/Either'; +import { PathReporter } from 'io-ts/lib/PathReporter'; + +import { + BrowserFieldsCodec, + ConfigKey, + DataStream, + DataStreamCodec, + HTTPFieldsCodec, + ICMPSimpleFieldsCodec, + MonitorFields, + TCPFieldsCodec, +} from '../../../common/runtime_types'; + +type MonitorCodecType = + | typeof ICMPSimpleFieldsCodec + | typeof TCPFieldsCodec + | typeof HTTPFieldsCodec + | typeof BrowserFieldsCodec; + +const monitorTypeToCodecMap: Record = { + [DataStream.ICMP]: ICMPSimpleFieldsCodec, + [DataStream.TCP]: TCPFieldsCodec, + [DataStream.HTTP]: HTTPFieldsCodec, + [DataStream.BROWSER]: BrowserFieldsCodec, +}; + +/** + * Validates monitor fields with respect to the relevant Codec identified by object's 'type' property. + * @param monitorFields {MonitorFields} The mixed type representing the possible monitor payload from UI. + */ +export function validateMonitor(monitorFields: MonitorFields): { + valid: boolean; + reason: string; + details: string; + payload: object; +} { + const { [ConfigKey.MONITOR_TYPE]: monitorType } = monitorFields; + + const decodedType = DataStreamCodec.decode(monitorType); + if (isLeft(decodedType)) { + return { + valid: false, + reason: `Monitor type is invalid`, + details: PathReporter.report(decodedType).join(' | '), + payload: monitorFields, + }; + } + + const codec = monitorTypeToCodecMap[monitorType]; + + if (!codec) { + return { + valid: false, + reason: `Payload is not a valid monitor object`, + details: '', + payload: monitorFields, + }; + } + + // Cast it to ICMPCodec to satisfy typing. During runtime, correct codec will be used to decode. + const decodedMonitor = (codec as typeof ICMPSimpleFieldsCodec).decode(monitorFields); + + if (isLeft(decodedMonitor)) { + return { + valid: false, + reason: `Monitor is not a valid monitor of type ${monitorType}`, + details: PathReporter.report(decodedMonitor).join(' | '), + payload: monitorFields, + }; + } + + return { valid: true, reason: '', details: '', payload: monitorFields }; +} diff --git a/x-pack/plugins/uptime/server/rest_api/synthetics_service/service_errors.ts b/x-pack/plugins/uptime/server/rest_api/synthetics_service/service_errors.ts new file mode 100644 index 0000000000000..23921c4d1d6ef --- /dev/null +++ b/x-pack/plugins/uptime/server/rest_api/synthetics_service/service_errors.ts @@ -0,0 +1,12 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { KibanaResponseFactory } from '../../../../../../src/core/server'; + +export function getMonitorNotFoundResponse(response: KibanaResponseFactory, monitorId: string) { + return response.notFound({ body: { message: `Monitor id ${monitorId} not found!` } }); +} diff --git a/x-pack/test/api_integration/apis/uptime/rest/add_monitor.ts b/x-pack/test/api_integration/apis/uptime/rest/add_monitor.ts index a57a03fd3a1f5..b7c1fc6bc5852 100644 --- a/x-pack/test/api_integration/apis/uptime/rest/add_monitor.ts +++ b/x-pack/test/api_integration/apis/uptime/rest/add_monitor.ts @@ -5,19 +5,29 @@ * 2.0. */ import expect from '@kbn/expect'; +import { HTTPFields } from '../../../../../plugins/uptime/common/runtime_types'; import { FtrProviderContext } from '../../../ftr_provider_context'; import { API_URLS } from '../../../../../plugins/uptime/common/constants'; +import { getFixtureJson } from './helper/get_fixture_json'; export default function ({ getService }: FtrProviderContext) { - describe('add synthetics monitor', () => { + describe('[POST] /internal/uptime/service/monitors', () => { const supertest = getService('supertest'); - const newMonitor = { - type: 'http', - name: 'Test monitor', - urls: 'https://www.elastic.co', - }; + + let _httpMonitorJson: HTTPFields; + let httpMonitorJson: HTTPFields; + + before(() => { + _httpMonitorJson = getFixtureJson('http_monitor'); + }); + + beforeEach(() => { + httpMonitorJson = _httpMonitorJson; + }); it('returns the newly added monitor', async () => { + const newMonitor = httpMonitorJson; + const apiResponse = await supertest .post(API_URLS.SYNTHETICS_MONITORS) .set('kbn-xsrf', 'true') @@ -25,5 +35,29 @@ export default function ({ getService }: FtrProviderContext) { expect(apiResponse.body.attributes).eql(newMonitor); }); + + it('returns bad request if payload is invalid for HTTP monitor', async () => { + // Delete a required property to make payload invalid + const newMonitor = { ...httpMonitorJson, 'check.request.headers': undefined }; + + const apiResponse = await supertest + .post(API_URLS.SYNTHETICS_MONITORS) + .set('kbn-xsrf', 'true') + .send(newMonitor); + + expect(apiResponse.status).eql(400); + }); + + it('returns bad request if monitor type is invalid', async () => { + const newMonitor = { ...httpMonitorJson, type: 'invalid-data-steam' }; + + const apiResponse = await supertest + .post(API_URLS.SYNTHETICS_MONITORS) + .set('kbn-xsrf', 'true') + .send(newMonitor); + + expect(apiResponse.status).eql(400); + expect(apiResponse.body.message).eql('Monitor type is invalid'); + }); }); } diff --git a/x-pack/test/api_integration/apis/uptime/rest/delete_monitor.ts b/x-pack/test/api_integration/apis/uptime/rest/delete_monitor.ts index bc49587fab872..2c215b7ba103c 100644 --- a/x-pack/test/api_integration/apis/uptime/rest/delete_monitor.ts +++ b/x-pack/test/api_integration/apis/uptime/rest/delete_monitor.ts @@ -6,31 +6,77 @@ */ import expect from '@kbn/expect'; +import { HTTPFields, MonitorFields } from '../../../../../plugins/uptime/common/runtime_types'; import { FtrProviderContext } from '../../../ftr_provider_context'; import { API_URLS } from '../../../../../plugins/uptime/common/constants'; +import { getFixtureJson } from './helper/get_fixture_json'; export default function ({ getService }: FtrProviderContext) { - describe('delete synthetics monitor', () => { + describe('[DELETE] /internal/uptime/service/monitors', () => { const supertest = getService('supertest'); - const newMonitor = { - type: 'http', - name: 'Test monitor', - urls: 'https://www.elastic.co', - }; - it('deleted monitor by id', async () => { - const apiResponse = await supertest + let _httpMonitorJson: HTTPFields; + let httpMonitorJson: HTTPFields; + + const saveMonitor = async (monitor: MonitorFields) => { + const res = await supertest .post(API_URLS.SYNTHETICS_MONITORS) .set('kbn-xsrf', 'true') - .send(newMonitor); + .send(monitor); + + return res.body; + }; + + before(() => { + _httpMonitorJson = getFixtureJson('http_monitor'); + }); - const monitorId = apiResponse.body.id; + beforeEach(() => { + httpMonitorJson = _httpMonitorJson; + }); + + it('deletes monitor by id', async () => { + const { id: monitorId } = await saveMonitor(httpMonitorJson as MonitorFields); const deleteResponse = await supertest .delete(API_URLS.SYNTHETICS_MONITORS + '/' + monitorId) .set('kbn-xsrf', 'true'); - // + expect(deleteResponse.body).eql(monitorId); + + // Hit get endpoint and expect 404 as well + await supertest.get(API_URLS.SYNTHETICS_MONITORS + '/' + monitorId).expect(404); + }); + + it('returns 404 if monitor id is not found', async () => { + const invalidMonitorId = 'invalid-id'; + const expected404Message = `Monitor id ${invalidMonitorId} not found!`; + + const deleteResponse = await supertest + .delete(API_URLS.SYNTHETICS_MONITORS + '/' + invalidMonitorId) + .set('kbn-xsrf', 'true'); + + expect(deleteResponse.status).eql(404); + expect(deleteResponse.body.message).eql(expected404Message); + }); + + it('validates empty monitor id', async () => { + const emptyMonitorId = ''; + + // Route DELETE '/${SYNTHETICS_MONITORS}' should not exist + await supertest + .delete(API_URLS.SYNTHETICS_MONITORS + '/' + emptyMonitorId) + .set('kbn-xsrf', 'true') + .expect(404); + }); + + it('validates param length for sanity', async () => { + const veryLargeMonId = new Array(1050).fill('1').join(''); + + await supertest + .delete(API_URLS.SYNTHETICS_MONITORS + '/' + veryLargeMonId) + .set('kbn-xsrf', 'true') + .expect(400); }); }); } diff --git a/x-pack/test/api_integration/apis/uptime/rest/edit_monitor.ts b/x-pack/test/api_integration/apis/uptime/rest/edit_monitor.ts index f5d54c40a8646..13581bcfc3e7f 100644 --- a/x-pack/test/api_integration/apis/uptime/rest/edit_monitor.ts +++ b/x-pack/test/api_integration/apis/uptime/rest/edit_monitor.ts @@ -5,33 +5,108 @@ * 2.0. */ import expect from '@kbn/expect'; +import { SimpleSavedObject } from 'kibana/public'; +import { + ConfigKey, + HTTPFields, + MonitorFields, +} from '../../../../../plugins/uptime/common/runtime_types'; import { FtrProviderContext } from '../../../ftr_provider_context'; import { API_URLS } from '../../../../../plugins/uptime/common/constants'; +import { getFixtureJson } from './helper/get_fixture_json'; export default function ({ getService }: FtrProviderContext) { - describe('edit synthetics monitor', () => { + describe('[PUT] /internal/uptime/service/monitors', () => { const supertest = getService('supertest'); - const newMonitor = { - type: 'http', - name: 'Test monitor', - urls: 'https://www.elastic.co', + + let _httpMonitorJson: HTTPFields; + let httpMonitorJson: HTTPFields; + + const saveMonitor = async (monitor: MonitorFields) => { + const res = await supertest + .post(API_URLS.SYNTHETICS_MONITORS) + .set('kbn-xsrf', 'true') + .send(monitor) + .expect(200); + + return res.body as SimpleSavedObject; }; + before(() => { + _httpMonitorJson = getFixtureJson('http_monitor'); + }); + + beforeEach(() => { + httpMonitorJson = { ..._httpMonitorJson }; + }); + it('edits the monitor', async () => { - const apiResponse = await supertest - .post(API_URLS.SYNTHETICS_MONITORS) + const newMonitor = httpMonitorJson; + + const { id: monitorId, attributes: savedMonitor } = await saveMonitor( + newMonitor as MonitorFields + ); + + expect(savedMonitor).eql(newMonitor); + + const updates: Partial = { + [ConfigKey.URLS]: 'https://modified-host.com', + [ConfigKey.NAME]: 'Modified name', + }; + + const modifiedMonitor = { ...savedMonitor, ...updates }; + + const editResponse = await supertest + .put(API_URLS.SYNTHETICS_MONITORS + '/' + monitorId) .set('kbn-xsrf', 'true') - .send(newMonitor); + .send(modifiedMonitor) + .expect(200); - const monitorId = apiResponse.body.id; + expect(editResponse.body.attributes).eql(modifiedMonitor); + }); - expect(apiResponse.body.attributes).eql(newMonitor); + it('returns 404 if monitor id is not present', async () => { + const invalidMonitorId = 'invalid-id'; + const expected404Message = `Monitor id ${invalidMonitorId} not found!`; const editResponse = await supertest + .put(API_URLS.SYNTHETICS_MONITORS + '/' + invalidMonitorId) + .set('kbn-xsrf', 'true') + .send(httpMonitorJson) + .expect(404); + + expect(editResponse.body.message).eql(expected404Message); + }); + + it('returns bad request if payload is invalid for HTTP monitor', async () => { + const { id: monitorId, attributes: savedMonitor } = await saveMonitor( + httpMonitorJson as MonitorFields + ); + + // Delete a required property to make payload invalid + const toUpdate = { ...savedMonitor, 'check.request.headers': undefined }; + + const apiResponse = await supertest + .put(API_URLS.SYNTHETICS_MONITORS + '/' + monitorId) + .set('kbn-xsrf', 'true') + .send(toUpdate); + + expect(apiResponse.status).eql(400); + }); + + it('returns bad request if monitor type is invalid', async () => { + const { id: monitorId, attributes: savedMonitor } = await saveMonitor( + httpMonitorJson as MonitorFields + ); + + const toUpdate = { ...savedMonitor, type: 'invalid-data-steam' }; + + const apiResponse = await supertest .put(API_URLS.SYNTHETICS_MONITORS + '/' + monitorId) .set('kbn-xsrf', 'true') - .send({ ...newMonitor, name: 'New name' }); + .send(toUpdate); - expect(editResponse.body.attributes.name).eql('New name'); + expect(apiResponse.status).eql(400); + expect(apiResponse.body.message).eql('Monitor type is invalid'); }); }); } diff --git a/x-pack/test/api_integration/apis/uptime/rest/fixtures/browser_monitor.json b/x-pack/test/api_integration/apis/uptime/rest/fixtures/browser_monitor.json new file mode 100644 index 0000000000000..323fb39f4cce0 --- /dev/null +++ b/x-pack/test/api_integration/apis/uptime/rest/fixtures/browser_monitor.json @@ -0,0 +1,41 @@ +{ + "type": "browser", + "enabled": true, + "schedule": { + "number": "3", + "unit": "m" + }, + "service.name": "", + "tags": [ + "cookie-test", + "browser" + ], + "timeout": "16", + "__ui": { + "script_source": { + "is_generated_script": false, + "file_name": "" + }, + "is_zip_url_tls_enabled": false, + "is_tls_enabled": false + }, + "source.zip_url.url": "", + "source.zip_url.username": "", + "source.zip_url.password": "", + "source.zip_url.folder": "", + "source.zip_url.proxy_url": "", + "source.inline.script": "step(\"Visit /users api route\", async () => {\\n const response = await page.goto('https://nextjs-test-synthetics.vercel.app/api/users');\\n expect(response.status()).toEqual(200);\\n});", + "params": "", + "screenshots": "on", + "synthetics_args": [], + "filter_journeys.match": "", + "filter_journeys.tags": [], + "ignore_https_errors": false, + "throttling.is_enabled": true, + "throttling.download_speed": "5", + "throttling.upload_speed": "3", + "throttling.latency": "20", + "throttling.config": "5d/3u/20l", + "locations": [], + "name": "Test HTTP Monitor 03" +} diff --git a/x-pack/test/api_integration/apis/uptime/rest/fixtures/http_monitor.json b/x-pack/test/api_integration/apis/uptime/rest/fixtures/http_monitor.json new file mode 100644 index 0000000000000..33de681a6d2d2 --- /dev/null +++ b/x-pack/test/api_integration/apis/uptime/rest/fixtures/http_monitor.json @@ -0,0 +1,61 @@ +{ + "type": "http", + "enabled": true, + "tags": [ + "tag1", + "tag2" + ], + "schedule": { + "number": "5", + "unit": "m" + }, + "service.name": "", + "timeout": "3m", + "__ui": { + "is_tls_enabled": false, + "is_zip_url_tls_enabled": false, + "script_source": { + "is_generated_script": false, + "file_name": "test-file.name" + } + }, + "max_redirects": "3", + "password": "test", + "urls": "https://nextjs-test-synthetics.vercel.app/api/users", + "proxy_url": "http://proxy.com", + "check.response.body.negative": [], + "check.response.body.positive": [], + "response.include_body": "never", + "check.response.headers": {}, + "response.include_headers": true, + "check.response.status": [ + "200", + "201" + ], + "check.request.body": { + "value": "testValue", + "type": "json" + }, + "check.request.headers": {}, + "check.request.method": "", + "username": "test-username", + "ssl.certificate_authorities": "t.string", + "ssl.certificate": "t.string", + "ssl.key": "t.string", + "ssl.key_passphrase": "t.string", + "ssl.verification_mode": "certificate", + "ssl.supported_protocols": [ + "TLSv1.1", + "TLSv1.2" + ], + "name": "test-monitor-name", + "locations": [{ + "id": "eu-west-01", + "label": "Europe West", + "geo": { + "lat": 33.2343132435, + "lon": 73.2342343434 + }, + "url": "https://example-url.com" + }] +} diff --git a/x-pack/test/api_integration/apis/uptime/rest/fixtures/icmp_monitor.json b/x-pack/test/api_integration/apis/uptime/rest/fixtures/icmp_monitor.json new file mode 100644 index 0000000000000..05dee80770d10 --- /dev/null +++ b/x-pack/test/api_integration/apis/uptime/rest/fixtures/icmp_monitor.json @@ -0,0 +1,35 @@ +{ + "type": "tcp", + "locations": [], + "enabled": true, + "schedule": { + "number": "3", + "unit": "m" + }, + "service.name": "example-service-name", + "tags": [ + "tagT1", + "tagT2" + ], + "timeout": "16", + "__ui": { + "is_tls_enabled": true, + "is_zip_url_tls_enabled": false + }, + "hosts": "192.33.22.111:3333", + "proxy_url": "", + "proxy_use_local_resolver": false, + "check.receive": "", + "check.send": "", + "ssl.certificate_authorities": "", + "ssl.certificate": "", + "ssl.key": "", + "ssl.key_passphrase": "", + "ssl.verification_mode": "full", + "ssl.supported_protocols": [ + "TLSv1.1", + "TLSv1.2", + "TLSv1.3" + ], + "name": "Test HTTP Monitor 04" +} diff --git a/x-pack/test/api_integration/apis/uptime/rest/fixtures/tcp_monitor.json b/x-pack/test/api_integration/apis/uptime/rest/fixtures/tcp_monitor.json new file mode 100644 index 0000000000000..cd22efc2f1b53 --- /dev/null +++ b/x-pack/test/api_integration/apis/uptime/rest/fixtures/tcp_monitor.json @@ -0,0 +1,31 @@ +{ + "type": "tcp", + "locations": [], + "enabled": true, + "schedule": { + "number": "3", + "unit": "m" + }, + "service.name": "", + "tags": [], + "timeout": "16", + "__ui": { + "is_tls_enabled": true, + "is_zip_url_tls_enabled": false + }, + "hosts": "example-host:40", + "proxy_url": "", + "proxy_use_local_resolver": false, + "check.receive": "", + "check.send": "", + "ssl.certificate_authorities": "", + "ssl.certificate": "", + "ssl.key": "", + "ssl.key_passphrase": "examplepassphrase", + "ssl.verification_mode": "full", + "ssl.supported_protocols": [ + "TLSv1.1", + "TLSv1.3" + ], + "name": "Test HTTP Monitor 04" +} diff --git a/x-pack/test/api_integration/apis/uptime/rest/get_monitor.ts b/x-pack/test/api_integration/apis/uptime/rest/get_monitor.ts index 563e15aef929f..37339cc0e0594 100644 --- a/x-pack/test/api_integration/apis/uptime/rest/get_monitor.ts +++ b/x-pack/test/api_integration/apis/uptime/rest/get_monitor.ts @@ -6,46 +6,114 @@ */ import expect from '@kbn/expect'; +import { SimpleSavedObject } from 'kibana/public'; +import { MonitorFields } from '../../../../../plugins/uptime/common/runtime_types'; import { FtrProviderContext } from '../../../ftr_provider_context'; import { API_URLS } from '../../../../../plugins/uptime/common/constants'; +import { getFixtureJson } from './helper/get_fixture_json'; export default function ({ getService }: FtrProviderContext) { - describe('get synthetics monitor', () => { - const newMonitor = { - type: 'http', - name: 'Test monitor', - urls: 'https://www.elastic.co', - }; + describe('[GET] /internal/uptime/service/monitors', () => { + const supertest = getService('supertest'); + + let _monitors: MonitorFields[]; + let monitors: MonitorFields[]; - const addMonitor = async () => { + const saveMonitor = async (monitor: MonitorFields) => { const res = await supertest .post(API_URLS.SYNTHETICS_MONITORS) .set('kbn-xsrf', 'true') - .send(newMonitor); - return res.body.id; + .send(monitor) + .expect(200); + + return res.body as SimpleSavedObject; }; - const supertest = getService('supertest'); + before(() => { + _monitors = [ + getFixtureJson('icmp_monitor'), + getFixtureJson('tcp_monitor'), + getFixtureJson('http_monitor'), + getFixtureJson('browser_monitor'), + ]; + }); + + beforeEach(() => { + monitors = _monitors; + }); + + describe('get many monitors', () => { + it('without params', async () => { + const [{ id: id1, attributes: mon1 }, { id: id2, attributes: mon2 }] = await Promise.all( + monitors.map(saveMonitor) + ); - it('get all monitors', async () => { - const id1 = await addMonitor(); - const id2 = await addMonitor(); + const apiResponse = await supertest + .get(API_URLS.SYNTHETICS_MONITORS + '?perPage=1000') // 1000 to sort of load all saved monitors + .expect(200); - const apiResponse = await supertest.get(API_URLS.SYNTHETICS_MONITORS); + const found: Array> = apiResponse.body.monitors.filter( + ({ id }: SimpleSavedObject) => [id1, id2].includes(id) + ); + found.sort(({ id: a }) => (a === id2 ? 1 : a === id1 ? -1 : 0)); + const foundMonitors = found.map( + ({ attributes }: SimpleSavedObject) => attributes + ); - const monitor1 = apiResponse.body.monitors.find((obj: any) => obj.id === id1); - const monitor2 = apiResponse.body.monitors.find((obj: any) => obj.id === id2); + const expected = [mon1, mon2]; - expect(monitor1.id).eql(id1); - expect(monitor2.id).eql(id2); + expect(foundMonitors).eql(expected); + }); + + it('with page params', async () => { + await Promise.all([...monitors, ...monitors].map(saveMonitor)); + + const firstPageResp = await supertest + .get(`${API_URLS.SYNTHETICS_MONITORS}?page=1&perPage=2`) + .expect(200); + const secondPageResp = await supertest + .get(`${API_URLS.SYNTHETICS_MONITORS}?page=2&perPage=3`) + .expect(200); + + expect(firstPageResp.body.total).greaterThan(6); + expect(firstPageResp.body.monitors.length).eql(2); + expect(secondPageResp.body.monitors.length).eql(3); + + expect(firstPageResp.body.monitors[0].id).not.eql(secondPageResp.body.monitors[0].id); + }); }); - it('get monitor by id', async () => { - const monitorId = await addMonitor(); + describe('get one monitor', () => { + it('should get by id', async () => { + const [{ id: id1, attributes: mon1 }] = await Promise.all(monitors.map(saveMonitor)); + + const apiResponse = await supertest + .get(API_URLS.SYNTHETICS_MONITORS + '/' + id1) + .expect(200); + + expect(apiResponse.body.attributes).eql(mon1); + }); + + it('returns 404 if monitor id is not found', async () => { + const invalidMonitorId = 'invalid-id'; + const expected404Message = `Monitor id ${invalidMonitorId} not found!`; + + const getResponse = await supertest + .get(API_URLS.SYNTHETICS_MONITORS + '/' + invalidMonitorId) + .set('kbn-xsrf', 'true'); + + expect(getResponse.status).eql(404); + expect(getResponse.body.message).eql(expected404Message); + }); - const apiResponse = await supertest.get(API_URLS.SYNTHETICS_MONITORS + '/' + monitorId); + it('validates param length for sanity', async () => { + const veryLargeMonId = new Array(1050).fill('1').join(''); - expect(apiResponse.body.id).eql(monitorId); + await supertest + .get(API_URLS.SYNTHETICS_MONITORS + '/' + veryLargeMonId) + .set('kbn-xsrf', 'true') + .expect(400); + }); }); }); } diff --git a/x-pack/test/api_integration/apis/uptime/rest/helper/get_fixture_json.ts b/x-pack/test/api_integration/apis/uptime/rest/helper/get_fixture_json.ts new file mode 100644 index 0000000000000..9cc1640b7a583 --- /dev/null +++ b/x-pack/test/api_integration/apis/uptime/rest/helper/get_fixture_json.ts @@ -0,0 +1,21 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import fs from 'fs'; +import { join } from 'path'; + +const fixturesDir = join(__dirname, '..', 'fixtures'); + +export function getFixtureJson(fixtureName: string) { + try { + const fixturePath = join(fixturesDir, `${fixtureName}.json`); + const fileContents = fs.readFileSync(fixturePath, 'utf8'); + return JSON.parse(fileContents); + } catch (e) { + return {}; + } +} From a96a5e29d56dba85dd9c8fc86b24f318444a6855 Mon Sep 17 00:00:00 2001 From: Jean-Louis Leysens Date: Fri, 17 Dec 2021 21:21:48 +0100 Subject: [PATCH 22/40] [Drilldowns] Fix URL drilldown placeholder text and add placeholder capability to Monaco (#121420) * added logic for detecting the example URL and created a new content widget for the code editor to act as a placeholder * added file for placeholder widget * refactor widget ID * added jest tests for the CodeEditor changes * remove data-test-subj * remove unused let * added url drilldown test * minor refactor for code readability and updated test titles * fix ts issue * simplify switch statement * add member accessibility to placeholderwidget methods Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../__snapshots__/code_editor.test.tsx.snap | 1 + .../code_editor/code_editor.test.helpers.tsx | 12 +++++ .../public/code_editor/code_editor.test.tsx | 39 +++++++++++++++ .../public/code_editor/code_editor.tsx | 17 +++++++ .../public/code_editor/editor.scss | 6 +++ .../public/code_editor/placeholder_widget.ts | 49 +++++++++++++++++++ .../url_template_editor.tsx | 3 ++ .../public/lib/url_drilldown.test.ts | 35 ++++++++++++- .../public/lib/url_drilldown.tsx | 22 ++++++++- .../public/drilldowns/drilldown_definition.ts | 2 +- .../url_drilldown_collect_config/i18n.ts | 2 +- .../test_samples/demo.tsx | 1 + .../url_drilldown_collect_config.tsx | 9 ++-- 13 files changed, 190 insertions(+), 8 deletions(-) create mode 100644 src/plugins/kibana_react/public/code_editor/placeholder_widget.ts diff --git a/src/plugins/kibana_react/public/code_editor/__snapshots__/code_editor.test.tsx.snap b/src/plugins/kibana_react/public/code_editor/__snapshots__/code_editor.test.tsx.snap index 1cf6a3409539e..b05abbcece0b9 100644 --- a/src/plugins/kibana_react/public/code_editor/__snapshots__/code_editor.test.tsx.snap +++ b/src/plugins/kibana_react/public/code_editor/__snapshots__/code_editor.test.tsx.snap @@ -215,6 +215,7 @@ exports[` is rendered 1`] = ` " >
+