From f31330a01b21cda1a35a83a6dbf4998264817fc6 Mon Sep 17 00:00:00 2001 From: Quynh Nguyen <43350163+qn895@users.noreply.github.com> Date: Mon, 1 Jun 2020 10:53:33 -0500 Subject: [PATCH 01/24] [ML] Add ability to delete target index & index pattern when deleting DFA job (#66934) Co-authored-by: Elastic Machine --- .../ml/common/types/data_frame_analytics.ts | 11 + .../analytics_list/action_delete.test.tsx | 51 +++- .../analytics_list/action_delete.tsx | 150 +++++++++++- .../analytics_service/delete_analytics.ts | 131 ++++++++++- .../services/analytics_service/index.ts | 3 +- .../ml_api_service/data_frame_analytics.ts | 19 ++ .../services/ml_api_service/jobs.ts | 1 - .../ml/public/application/util/error_utils.ts | 32 +++ .../data_frame_analytics/index_patterns.ts | 32 +++ .../ml/server/routes/data_frame_analytics.ts | 112 ++++++++- .../routes/schemas/data_analytics_schema.ts | 8 + .../apis/ml/data_frame_analytics/delete.ts | 218 ++++++++++++++++++ .../apis/ml/data_frame_analytics/index.ts | 1 + x-pack/test/functional/services/ml/api.ts | 53 ++++- .../functional/services/ml/test_resources.ts | 17 +- 15 files changed, 797 insertions(+), 42 deletions(-) create mode 100644 x-pack/plugins/ml/common/types/data_frame_analytics.ts create mode 100644 x-pack/plugins/ml/public/application/util/error_utils.ts create mode 100644 x-pack/plugins/ml/server/models/data_frame_analytics/index_patterns.ts create mode 100644 x-pack/test/api_integration/apis/ml/data_frame_analytics/delete.ts diff --git a/x-pack/plugins/ml/common/types/data_frame_analytics.ts b/x-pack/plugins/ml/common/types/data_frame_analytics.ts new file mode 100644 index 00000000000000..5ba7f9c191a7fe --- /dev/null +++ b/x-pack/plugins/ml/common/types/data_frame_analytics.ts @@ -0,0 +1,11 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { CustomHttpResponseOptions, ResponseError } from 'kibana/server'; +export interface DeleteDataFrameAnalyticsWithIndexStatus { + success: boolean; + error?: CustomHttpResponseOptions; +} diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/action_delete.test.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/action_delete.test.tsx index 2ef1515726d1b3..33217f127f9982 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/action_delete.test.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/action_delete.test.tsx @@ -5,20 +5,41 @@ */ import React from 'react'; -import { render } from '@testing-library/react'; - +import { fireEvent, render } from '@testing-library/react'; import * as CheckPrivilige from '../../../../../capabilities/check_capabilities'; - -import { DeleteAction } from './action_delete'; - import mockAnalyticsListItem from './__mocks__/analytics_list_item.json'; +import { DeleteAction } from './action_delete'; +import { I18nProvider } from '@kbn/i18n/react'; +import { + coreMock as mockCoreServices, + i18nServiceMock, +} from '../../../../../../../../../../src/core/public/mocks'; jest.mock('../../../../../capabilities/check_capabilities', () => ({ checkPermission: jest.fn(() => false), createPermissionFailureMessage: jest.fn(), })); +jest.mock('../../../../../../application/util/dependency_cache', () => ({ + getToastNotifications: () => ({ addSuccess: jest.fn(), addDanger: jest.fn() }), +})); + +jest.mock('../../../../../contexts/kibana', () => ({ + useMlKibana: () => ({ + services: mockCoreServices.createStart(), + }), +})); +export const MockI18nService = i18nServiceMock.create(); +export const I18nServiceConstructor = jest.fn().mockImplementation(() => MockI18nService); +jest.doMock('@kbn/i18n', () => ({ + I18nService: I18nServiceConstructor, +})); + describe('DeleteAction', () => { + afterEach(() => { + jest.clearAllMocks(); + }); + test('When canDeleteDataFrameAnalytics permission is false, button should be disabled.', () => { const { getByTestId } = render(); expect(getByTestId('mlAnalyticsJobDeleteButton')).toHaveAttribute('disabled'); @@ -46,4 +67,24 @@ describe('DeleteAction', () => { expect(getByTestId('mlAnalyticsJobDeleteButton')).toHaveAttribute('disabled'); }); + + describe('When delete model is open', () => { + test('should allow to delete target index by default.', () => { + const mock = jest.spyOn(CheckPrivilige, 'checkPermission'); + mock.mockImplementation((p) => p === 'canDeleteDataFrameAnalytics'); + const { getByTestId, queryByTestId } = render( + + + + ); + const deleteButton = getByTestId('mlAnalyticsJobDeleteButton'); + fireEvent.click(deleteButton); + expect(getByTestId('mlAnalyticsJobDeleteModal')).toBeInTheDocument(); + expect(getByTestId('mlAnalyticsJobDeleteIndexSwitch')).toBeInTheDocument(); + const mlAnalyticsJobDeleteIndexSwitch = getByTestId('mlAnalyticsJobDeleteIndexSwitch'); + expect(mlAnalyticsJobDeleteIndexSwitch).toHaveAttribute('aria-checked', 'true'); + expect(queryByTestId('mlAnalyticsJobDeleteIndexPatternSwitch')).toBeNull(); + mock.mockRestore(); + }); + }); }); diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/action_delete.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/action_delete.tsx index 2923938ae68ace..2d433f6b184846 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/action_delete.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/action_delete.tsx @@ -4,24 +4,32 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { Fragment, FC, useState } from 'react'; +import React, { Fragment, FC, useState, useEffect } from 'react'; import { i18n } from '@kbn/i18n'; import { EuiButtonEmpty, EuiConfirmModal, EuiOverlayMask, EuiToolTip, + EuiSwitch, + EuiFlexGroup, + EuiFlexItem, EUI_MODAL_CONFIRM_BUTTON, } from '@elastic/eui'; - -import { deleteAnalytics } from '../../services/analytics_service'; - +import { IIndexPattern } from 'src/plugins/data/common'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { + deleteAnalytics, + deleteAnalyticsAndDestIndex, + canDeleteIndex, +} from '../../services/analytics_service'; import { checkPermission, createPermissionFailureMessage, } from '../../../../../capabilities/check_capabilities'; - +import { useMlKibana } from '../../../../../contexts/kibana'; import { isDataFrameAnalyticsRunning, DataFrameAnalyticsListRow } from './common'; +import { extractErrorMessage } from '../../../../../util/error_utils'; interface DeleteActionProps { item: DataFrameAnalyticsListRow; @@ -29,17 +37,99 @@ interface DeleteActionProps { export const DeleteAction: FC = ({ item }) => { const disabled = isDataFrameAnalyticsRunning(item.stats.state); - const canDeleteDataFrameAnalytics: boolean = checkPermission('canDeleteDataFrameAnalytics'); const [isModalVisible, setModalVisible] = useState(false); + const [deleteTargetIndex, setDeleteTargetIndex] = useState(true); + const [deleteIndexPattern, setDeleteIndexPattern] = useState(true); + const [userCanDeleteIndex, setUserCanDeleteIndex] = useState(false); + const [indexPatternExists, setIndexPatternExists] = useState(false); + + const { savedObjects, notifications } = useMlKibana().services; + const savedObjectsClient = savedObjects.client; + + const indexName = item.config.dest.index; + + const checkIndexPatternExists = async () => { + try { + const response = await savedObjectsClient.find({ + type: 'index-pattern', + perPage: 10, + search: `"${indexName}"`, + searchFields: ['title'], + fields: ['title'], + }); + const ip = response.savedObjects.find( + (obj) => obj.attributes.title.toLowerCase() === indexName.toLowerCase() + ); + if (ip !== undefined) { + setIndexPatternExists(true); + } + } catch (e) { + const { toasts } = notifications; + const error = extractErrorMessage(e); + + toasts.addDanger( + i18n.translate( + 'xpack.ml.dataframe.analyticsList.errorWithCheckingIfIndexPatternExistsNotificationErrorMessage', + { + defaultMessage: + 'An error occurred checking if index pattern {indexPattern} exists: {error}', + values: { indexPattern: indexName, error }, + } + ) + ); + } + }; + const checkUserIndexPermission = () => { + try { + const userCanDelete = canDeleteIndex(indexName); + if (userCanDelete) { + setUserCanDeleteIndex(true); + } + } catch (e) { + const { toasts } = notifications; + const error = extractErrorMessage(e); + + toasts.addDanger( + i18n.translate( + 'xpack.ml.dataframe.analyticsList.errorWithCheckingIfUserCanDeleteIndexNotificationErrorMessage', + { + defaultMessage: + 'An error occurred checking if user can delete {destinationIndex}: {error}', + values: { destinationIndex: indexName, error }, + } + ) + ); + } + }; + + useEffect(() => { + // Check if an index pattern exists corresponding to current DFA job + // if pattern does exist, show it to user + checkIndexPatternExists(); + + // Check if an user has permission to delete the index & index pattern + checkUserIndexPermission(); + }, []); const closeModal = () => setModalVisible(false); const deleteAndCloseModal = () => { setModalVisible(false); - deleteAnalytics(item); + + if ((userCanDeleteIndex && deleteTargetIndex) || (userCanDeleteIndex && deleteIndexPattern)) { + deleteAnalyticsAndDestIndex( + item, + deleteTargetIndex, + indexPatternExists && deleteIndexPattern + ); + } else { + deleteAnalytics(item); + } }; const openModal = () => setModalVisible(true); + const toggleDeleteIndex = () => setDeleteTargetIndex(!deleteTargetIndex); + const toggleDeleteIndexPattern = () => setDeleteIndexPattern(!deleteIndexPattern); const buttonDeleteText = i18n.translate('xpack.ml.dataframe.analyticsList.deleteActionName', { defaultMessage: 'Delete', @@ -84,8 +174,9 @@ export const DeleteAction: FC = ({ item }) => { {deleteButton} {isModalVisible && ( - + = ({ item }) => { buttonColor="danger" >

- {i18n.translate('xpack.ml.dataframe.analyticsList.deleteModalBody', { - defaultMessage: `Are you sure you want to delete this analytics job? The analytics job's destination index and optional Kibana index pattern will not be deleted.`, - })} +

+ + + + {userCanDeleteIndex && ( + + )} + + + {userCanDeleteIndex && indexPatternExists && ( + + )} + +
)} diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/services/analytics_service/delete_analytics.ts b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/services/analytics_service/delete_analytics.ts index 7383f565bd673a..26cefff0a3f594 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/services/analytics_service/delete_analytics.ts +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/services/analytics_service/delete_analytics.ts @@ -3,17 +3,15 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ - import { i18n } from '@kbn/i18n'; import { getToastNotifications } from '../../../../../util/dependency_cache'; import { ml } from '../../../../../services/ml_api_service'; - import { refreshAnalyticsList$, REFRESH_ANALYTICS_LIST_STATE } from '../../../../common'; - import { isDataFrameAnalyticsFailed, DataFrameAnalyticsListRow, } from '../../components/analytics_list/common'; +import { extractErrorMessage } from '../../../../../util/error_utils'; export const deleteAnalytics = async (d: DataFrameAnalyticsListRow) => { const toastNotifications = getToastNotifications(); @@ -24,18 +22,139 @@ export const deleteAnalytics = async (d: DataFrameAnalyticsListRow) => { await ml.dataFrameAnalytics.deleteDataFrameAnalytics(d.config.id); toastNotifications.addSuccess( i18n.translate('xpack.ml.dataframe.analyticsList.deleteAnalyticsSuccessMessage', { - defaultMessage: 'Request to delete data frame analytics {analyticsId} acknowledged.', + defaultMessage: 'Request to delete data frame analytics job {analyticsId} acknowledged.', values: { analyticsId: d.config.id }, }) ); } catch (e) { + const error = extractErrorMessage(e); + toastNotifications.addDanger( i18n.translate('xpack.ml.dataframe.analyticsList.deleteAnalyticsErrorMessage', { defaultMessage: - 'An error occurred deleting the data frame analytics {analyticsId}: {error}', - values: { analyticsId: d.config.id, error: JSON.stringify(e) }, + 'An error occurred deleting the data frame analytics job {analyticsId}: {error}', + values: { analyticsId: d.config.id, error }, }) ); } refreshAnalyticsList$.next(REFRESH_ANALYTICS_LIST_STATE.REFRESH); }; + +export const deleteAnalyticsAndDestIndex = async ( + d: DataFrameAnalyticsListRow, + deleteDestIndex: boolean, + deleteDestIndexPattern: boolean +) => { + const toastNotifications = getToastNotifications(); + const destinationIndex = Array.isArray(d.config.dest.index) + ? d.config.dest.index[0] + : d.config.dest.index; + try { + if (isDataFrameAnalyticsFailed(d.stats.state)) { + await ml.dataFrameAnalytics.stopDataFrameAnalytics(d.config.id, true); + } + const status = await ml.dataFrameAnalytics.deleteDataFrameAnalyticsAndDestIndex( + d.config.id, + deleteDestIndex, + deleteDestIndexPattern + ); + if (status.analyticsJobDeleted?.success) { + toastNotifications.addSuccess( + i18n.translate('xpack.ml.dataframe.analyticsList.deleteAnalyticsSuccessMessage', { + defaultMessage: 'Request to delete data frame analytics job {analyticsId} acknowledged.', + values: { analyticsId: d.config.id }, + }) + ); + } + if (status.analyticsJobDeleted?.error) { + const error = extractErrorMessage(status.analyticsJobDeleted.error); + toastNotifications.addDanger( + i18n.translate('xpack.ml.dataframe.analyticsList.deleteAnalyticsErrorMessage', { + defaultMessage: + 'An error occurred deleting the data frame analytics job {analyticsId}: {error}', + values: { analyticsId: d.config.id, error }, + }) + ); + } + + if (status.destIndexDeleted?.success) { + toastNotifications.addSuccess( + i18n.translate('xpack.ml.dataframe.analyticsList.deleteAnalyticsWithIndexSuccessMessage', { + defaultMessage: 'Request to delete destination index {destinationIndex} acknowledged.', + values: { destinationIndex }, + }) + ); + } + if (status.destIndexDeleted?.error) { + const error = extractErrorMessage(status.destIndexDeleted.error); + toastNotifications.addDanger( + i18n.translate('xpack.ml.dataframe.analyticsList.deleteAnalyticsWithIndexErrorMessage', { + defaultMessage: + 'An error occurred deleting destination index {destinationIndex}: {error}', + values: { destinationIndex, error }, + }) + ); + } + + if (status.destIndexPatternDeleted?.success) { + toastNotifications.addSuccess( + i18n.translate( + 'xpack.ml.dataframe.analyticsList.deleteAnalyticsWithIndexPatternSuccessMessage', + { + defaultMessage: 'Request to delete index pattern {destinationIndex} acknowledged.', + values: { destinationIndex }, + } + ) + ); + } + if (status.destIndexPatternDeleted?.error) { + const error = extractErrorMessage(status.destIndexPatternDeleted.error); + toastNotifications.addDanger( + i18n.translate( + 'xpack.ml.dataframe.analyticsList.deleteAnalyticsWithIndexPatternErrorMessage', + { + defaultMessage: 'An error occurred deleting index pattern {destinationIndex}: {error}', + values: { destinationIndex, error }, + } + ) + ); + } + } catch (e) { + const error = extractErrorMessage(e); + + toastNotifications.addDanger( + i18n.translate('xpack.ml.dataframe.analyticsList.deleteAnalyticsErrorMessage', { + defaultMessage: + 'An error occurred deleting the data frame analytics job {analyticsId}: {error}', + values: { analyticsId: d.config.id, error }, + }) + ); + } + refreshAnalyticsList$.next(REFRESH_ANALYTICS_LIST_STATE.REFRESH); +}; + +export const canDeleteIndex = async (indexName: string) => { + const toastNotifications = getToastNotifications(); + try { + const privilege = await ml.hasPrivileges({ + index: [ + { + names: [indexName], // uses wildcard + privileges: ['delete_index'], + }, + ], + }); + if (!privilege) { + return false; + } + return privilege.securityDisabled === true || privilege.has_all_requested === true; + } catch (e) { + const error = extractErrorMessage(e); + toastNotifications.addDanger( + i18n.translate('xpack.ml.dataframe.analyticsList.deleteAnalyticsPrivilegeErrorMessage', { + defaultMessage: 'User does not have permission to delete index {indexName}: {error}', + values: { indexName, error }, + }) + ); + } +}; diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/services/analytics_service/index.ts b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/services/analytics_service/index.ts index 0d1a87e7c4c1f6..68aa58e7e1f198 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/services/analytics_service/index.ts +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/services/analytics_service/index.ts @@ -3,8 +3,7 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ - export { getAnalyticsFactory } from './get_analytics'; -export { deleteAnalytics } from './delete_analytics'; +export { deleteAnalytics, deleteAnalyticsAndDestIndex, canDeleteIndex } from './delete_analytics'; export { startAnalytics } from './start_analytics'; export { stopAnalytics } from './stop_analytics'; diff --git a/x-pack/plugins/ml/public/application/services/ml_api_service/data_frame_analytics.ts b/x-pack/plugins/ml/public/application/services/ml_api_service/data_frame_analytics.ts index 89950a659f6099..7cdd5478e39835 100644 --- a/x-pack/plugins/ml/public/application/services/ml_api_service/data_frame_analytics.ts +++ b/x-pack/plugins/ml/public/application/services/ml_api_service/data_frame_analytics.ts @@ -10,6 +10,7 @@ import { basePath } from './index'; import { DataFrameAnalyticsStats } from '../../data_frame_analytics/pages/analytics_management/components/analytics_list/common'; import { DataFrameAnalyticsConfig } from '../../data_frame_analytics/common'; import { DeepPartial } from '../../../../common/types/common'; +import { DeleteDataFrameAnalyticsWithIndexStatus } from '../../../../common/types/data_frame_analytics'; export interface GetDataFrameAnalyticsStatsResponseOk { node_failures?: object; @@ -32,6 +33,13 @@ interface GetDataFrameAnalyticsResponse { data_frame_analytics: DataFrameAnalyticsConfig[]; } +interface DeleteDataFrameAnalyticsWithIndexResponse { + acknowledged: boolean; + analyticsJobDeleted: DeleteDataFrameAnalyticsWithIndexStatus; + destIndexDeleted: DeleteDataFrameAnalyticsWithIndexStatus; + destIndexPatternDeleted: DeleteDataFrameAnalyticsWithIndexStatus; +} + export const dataFrameAnalytics = { getDataFrameAnalytics(analyticsId?: string) { const analyticsIdString = analyticsId !== undefined ? `/${analyticsId}` : ''; @@ -86,6 +94,17 @@ export const dataFrameAnalytics = { method: 'DELETE', }); }, + deleteDataFrameAnalyticsAndDestIndex( + analyticsId: string, + deleteDestIndex: boolean, + deleteDestIndexPattern: boolean + ) { + return http({ + path: `${basePath()}/data_frame/analytics/${analyticsId}`, + query: { deleteDestIndex, deleteDestIndexPattern }, + method: 'DELETE', + }); + }, startDataFrameAnalytics(analyticsId: string) { return http({ path: `${basePath()}/data_frame/analytics/${analyticsId}/_start`, diff --git a/x-pack/plugins/ml/public/application/services/ml_api_service/jobs.ts b/x-pack/plugins/ml/public/application/services/ml_api_service/jobs.ts index 16e25067fd91e3..e2569f6217b347 100644 --- a/x-pack/plugins/ml/public/application/services/ml_api_service/jobs.ts +++ b/x-pack/plugins/ml/public/application/services/ml_api_service/jobs.ts @@ -95,7 +95,6 @@ export const jobs = { body, }); }, - closeJobs(jobIds: string[]) { const body = JSON.stringify({ jobIds }); return http({ diff --git a/x-pack/plugins/ml/public/application/util/error_utils.ts b/x-pack/plugins/ml/public/application/util/error_utils.ts new file mode 100644 index 00000000000000..2ce8f4ffc583a3 --- /dev/null +++ b/x-pack/plugins/ml/public/application/util/error_utils.ts @@ -0,0 +1,32 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { CustomHttpResponseOptions, ResponseError } from 'kibana/server'; + +export const extractErrorMessage = ( + error: CustomHttpResponseOptions | undefined | string +): string | undefined => { + if (typeof error === 'string') { + return error; + } + + if (error?.body) { + if (typeof error.body === 'string') { + return error.body; + } + if (typeof error.body === 'object' && 'message' in error.body) { + if (typeof error.body.message === 'string') { + return error.body.message; + } + // @ts-ignore + if (typeof (error.body.message?.msg === 'string')) { + // @ts-ignore + return error.body.message?.msg; + } + } + } + return undefined; +}; diff --git a/x-pack/plugins/ml/server/models/data_frame_analytics/index_patterns.ts b/x-pack/plugins/ml/server/models/data_frame_analytics/index_patterns.ts new file mode 100644 index 00000000000000..d1a4df768a6ae5 --- /dev/null +++ b/x-pack/plugins/ml/server/models/data_frame_analytics/index_patterns.ts @@ -0,0 +1,32 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { SavedObjectsClientContract } from 'kibana/server'; +import { IIndexPattern } from 'src/plugins/data/server'; + +export class IndexPatternHandler { + constructor(private savedObjectsClient: SavedObjectsClientContract) {} + // returns a id based on an index pattern name + async getIndexPatternId(indexName: string) { + const response = await this.savedObjectsClient.find({ + type: 'index-pattern', + perPage: 10, + search: `"${indexName}"`, + searchFields: ['title'], + fields: ['title'], + }); + + const ip = response.saved_objects.find( + (obj) => obj.attributes.title.toLowerCase() === indexName.toLowerCase() + ); + + return ip?.id; + } + + async deleteIndexPatternById(indexId: string) { + return await this.savedObjectsClient.delete('index-pattern', indexId); + } +} diff --git a/x-pack/plugins/ml/server/routes/data_frame_analytics.ts b/x-pack/plugins/ml/server/routes/data_frame_analytics.ts index 894c4739ef96ef..e2601c7ad6a2e7 100644 --- a/x-pack/plugins/ml/server/routes/data_frame_analytics.ts +++ b/x-pack/plugins/ml/server/routes/data_frame_analytics.ts @@ -4,6 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ +import { RequestHandlerContext } from 'kibana/server'; import { wrapError } from '../client/error_wrapper'; import { analyticsAuditMessagesProvider } from '../models/data_frame_analytics/analytics_audit_messages'; import { RouteInitialization } from '../types'; @@ -13,12 +14,48 @@ import { dataAnalyticsExplainSchema, analyticsIdSchema, stopsDataFrameAnalyticsJobQuerySchema, + deleteDataFrameAnalyticsJobSchema, } from './schemas/data_analytics_schema'; +import { IndexPatternHandler } from '../models/data_frame_analytics/index_patterns'; +import { DeleteDataFrameAnalyticsWithIndexStatus } from '../../common/types/data_frame_analytics'; + +function getIndexPatternId(context: RequestHandlerContext, patternName: string) { + const iph = new IndexPatternHandler(context.core.savedObjects.client); + return iph.getIndexPatternId(patternName); +} + +function deleteDestIndexPatternById(context: RequestHandlerContext, indexPatternId: string) { + const iph = new IndexPatternHandler(context.core.savedObjects.client); + return iph.deleteIndexPatternById(indexPatternId); +} /** * Routes for the data frame analytics */ export function dataFrameAnalyticsRoutes({ router, mlLicense }: RouteInitialization) { + async function userCanDeleteIndex( + context: RequestHandlerContext, + destinationIndex: string + ): Promise { + if (!mlLicense.isSecurityEnabled()) { + return true; + } + const privilege = await context.ml!.mlClient.callAsCurrentUser('ml.privilegeCheck', { + body: { + index: [ + { + names: [destinationIndex], // uses wildcard + privileges: ['delete_index'], + }, + ], + }, + }); + if (!privilege) { + return false; + } + return privilege.has_all_requested === true; + } + /** * @apiGroup DataFrameAnalytics * @@ -277,6 +314,7 @@ export function dataFrameAnalyticsRoutes({ router, mlLicense }: RouteInitializat path: '/api/ml/data_frame/analytics/{analyticsId}', validate: { params: analyticsIdSchema, + query: deleteDataFrameAnalyticsJobSchema, }, options: { tags: ['access:ml:canDeleteDataFrameAnalytics'], @@ -285,12 +323,78 @@ export function dataFrameAnalyticsRoutes({ router, mlLicense }: RouteInitializat mlLicense.fullLicenseAPIGuard(async (context, request, response) => { try { const { analyticsId } = request.params; - const results = await context.ml!.mlClient.callAsCurrentUser( - 'ml.deleteDataFrameAnalytics', - { + const { deleteDestIndex, deleteDestIndexPattern } = request.query; + let destinationIndex: string | undefined; + const analyticsJobDeleted: DeleteDataFrameAnalyticsWithIndexStatus = { success: false }; + const destIndexDeleted: DeleteDataFrameAnalyticsWithIndexStatus = { success: false }; + const destIndexPatternDeleted: DeleteDataFrameAnalyticsWithIndexStatus = { + success: false, + }; + + // Check if analyticsId is valid and get destination index + if (deleteDestIndex || deleteDestIndexPattern) { + try { + const dfa = await context.ml!.mlClient.callAsCurrentUser('ml.getDataFrameAnalytics', { + analyticsId, + }); + if (Array.isArray(dfa.data_frame_analytics) && dfa.data_frame_analytics.length > 0) { + destinationIndex = dfa.data_frame_analytics[0].dest.index; + } + } catch (e) { + return response.customError(wrapError(e)); + } + + // If user checks box to delete the destinationIndex associated with the job + if (destinationIndex && deleteDestIndex) { + // Verify if user has privilege to delete the destination index + const userCanDeleteDestIndex = await userCanDeleteIndex(context, destinationIndex); + // If user does have privilege to delete the index, then delete the index + if (userCanDeleteDestIndex) { + try { + await context.ml!.mlClient.callAsCurrentUser('indices.delete', { + index: destinationIndex, + }); + destIndexDeleted.success = true; + } catch (deleteIndexError) { + destIndexDeleted.error = wrapError(deleteIndexError); + } + } else { + return response.forbidden(); + } + } + + // Delete the index pattern if there's an index pattern that matches the name of dest index + if (destinationIndex && deleteDestIndexPattern) { + try { + const indexPatternId = await getIndexPatternId(context, destinationIndex); + if (indexPatternId) { + await deleteDestIndexPatternById(context, indexPatternId); + } + destIndexPatternDeleted.success = true; + } catch (deleteDestIndexPatternError) { + destIndexPatternDeleted.error = wrapError(deleteDestIndexPatternError); + } + } + } + // Grab the target index from the data frame analytics job id + // Delete the data frame analytics + + try { + await context.ml!.mlClient.callAsCurrentUser('ml.deleteDataFrameAnalytics', { analyticsId, + }); + analyticsJobDeleted.success = true; + } catch (deleteDFAError) { + analyticsJobDeleted.error = wrapError(deleteDFAError); + if (analyticsJobDeleted.error.statusCode === 404) { + return response.notFound(); } - ); + } + const results = { + analyticsJobDeleted, + destIndexDeleted, + destIndexPatternDeleted, + }; return response.ok({ body: results, }); diff --git a/x-pack/plugins/ml/server/routes/schemas/data_analytics_schema.ts b/x-pack/plugins/ml/server/routes/schemas/data_analytics_schema.ts index f1d4947a7abc5a..0b2469c103578c 100644 --- a/x-pack/plugins/ml/server/routes/schemas/data_analytics_schema.ts +++ b/x-pack/plugins/ml/server/routes/schemas/data_analytics_schema.ts @@ -60,6 +60,14 @@ export const analyticsIdSchema = schema.object({ analyticsId: schema.string(), }); +export const deleteDataFrameAnalyticsJobSchema = schema.object({ + /** + * Analytics Destination Index + */ + deleteDestIndex: schema.maybe(schema.boolean()), + deleteDestIndexPattern: schema.maybe(schema.boolean()), +}); + export const stopsDataFrameAnalyticsJobQuerySchema = schema.object({ force: schema.maybe(schema.boolean()), }); diff --git a/x-pack/test/api_integration/apis/ml/data_frame_analytics/delete.ts b/x-pack/test/api_integration/apis/ml/data_frame_analytics/delete.ts new file mode 100644 index 00000000000000..23bff0d0c28550 --- /dev/null +++ b/x-pack/test/api_integration/apis/ml/data_frame_analytics/delete.ts @@ -0,0 +1,218 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import expect from '@kbn/expect'; +import { FtrProviderContext } from '../../../ftr_provider_context'; +import { USER } from '../../../../functional/services/ml/security_common'; +import { DataFrameAnalyticsConfig } from '../../../../../plugins/ml/public/application/data_frame_analytics/common'; +import { DeepPartial } from '../../../../../plugins/ml/common/types/common'; +import { COMMON_REQUEST_HEADERS } from '../../../../functional/services/ml/common'; + +export default ({ getService }: FtrProviderContext) => { + const esArchiver = getService('esArchiver'); + const supertest = getService('supertestWithoutAuth'); + const ml = getService('ml'); + + const jobId = `bm_${Date.now()}`; + const generateDestinationIndex = (analyticsId: string) => `user-${analyticsId}`; + const commonJobConfig = { + source: { + index: ['ft_bank_marketing'], + query: { + match_all: {}, + }, + }, + analysis: { + classification: { + dependent_variable: 'y', + training_percent: 20, + }, + }, + analyzed_fields: { + includes: [], + excludes: [], + }, + model_memory_limit: '350mb', + }; + + const testJobConfigs: Array> = [ + 'Test delete job only', + 'Test delete job and target index', + 'Test delete job and index pattern', + 'Test delete job, target index, and index pattern', + ].map((description, idx) => { + const analyticsId = `${jobId}_${idx + 1}`; + return { + id: analyticsId, + description, + dest: { + index: generateDestinationIndex(analyticsId), + results_field: 'ml', + }, + ...commonJobConfig, + }; + }); + + async function createJobs(mockJobConfigs: Array>) { + for (const jobConfig of mockJobConfigs) { + await ml.api.createDataFrameAnalyticsJob(jobConfig as DataFrameAnalyticsConfig); + } + } + + describe('DELETE data_frame/analytics', () => { + before(async () => { + await esArchiver.loadIfNeeded('ml/bm_classification'); + await ml.testResources.setKibanaTimeZoneToUTC(); + await createJobs(testJobConfigs); + }); + + after(async () => { + await ml.api.cleanMlIndices(); + }); + + describe('DeleteDataFrameAnalytics', () => { + it('should delete analytics jobs by id', async () => { + const analyticsId = `${jobId}_1`; + const { body } = await supertest + .delete(`/api/ml/data_frame/analytics/${analyticsId}`) + .auth(USER.ML_POWERUSER, ml.securityCommon.getPasswordForUser(USER.ML_POWERUSER)) + .set(COMMON_REQUEST_HEADERS) + .expect(200); + + expect(body.analyticsJobDeleted.success).to.eql(true); + await ml.api.waitForDataFrameAnalyticsJobNotToExist(analyticsId); + }); + + it('should not allow to retrieve analytics jobs for unauthorized user', async () => { + const analyticsId = `${jobId}_2`; + const { body } = await supertest + .delete(`/api/ml/data_frame/analytics/${analyticsId}`) + .auth(USER.ML_UNAUTHORIZED, ml.securityCommon.getPasswordForUser(USER.ML_UNAUTHORIZED)) + .set(COMMON_REQUEST_HEADERS) + .expect(404); + + expect(body.error).to.eql('Not Found'); + expect(body.message).to.eql('Not Found'); + await ml.api.waitForDataFrameAnalyticsJobToExist(analyticsId); + }); + + it('should not allow to retrieve analytics jobs for the user with only view permission', async () => { + const analyticsId = `${jobId}_2`; + const { body } = await supertest + .delete(`/api/ml/data_frame/analytics/${analyticsId}`) + .auth(USER.ML_VIEWER, ml.securityCommon.getPasswordForUser(USER.ML_VIEWER)) + .set(COMMON_REQUEST_HEADERS) + .expect(404); + + expect(body.error).to.eql('Not Found'); + expect(body.message).to.eql('Not Found'); + await ml.api.waitForDataFrameAnalyticsJobToExist(analyticsId); + }); + + it('should show 404 error if job does not exist or has already been deleted', async () => { + const { body } = await supertest + .delete(`/api/ml/data_frame/analytics/${jobId}_invalid`) + .auth(USER.ML_POWERUSER, ml.securityCommon.getPasswordForUser(USER.ML_POWERUSER)) + .set(COMMON_REQUEST_HEADERS) + .expect(404); + + expect(body.error).to.eql('Not Found'); + expect(body.message).to.eql('Not Found'); + }); + + describe('with deleteDestIndex setting', function () { + const analyticsId = `${jobId}_2`; + const destinationIndex = generateDestinationIndex(analyticsId); + + before(async () => { + await ml.api.createIndices(destinationIndex); + await ml.api.assertIndicesExist(destinationIndex); + }); + + after(async () => { + await ml.api.deleteIndices(destinationIndex); + }); + + it('should delete job and destination index by id', async () => { + const { body } = await supertest + .delete(`/api/ml/data_frame/analytics/${analyticsId}`) + .query({ deleteDestIndex: true }) + .auth(USER.ML_POWERUSER, ml.securityCommon.getPasswordForUser(USER.ML_POWERUSER)) + .set(COMMON_REQUEST_HEADERS) + .expect(200); + + expect(body.analyticsJobDeleted.success).to.eql(true); + expect(body.destIndexDeleted.success).to.eql(true); + expect(body.destIndexPatternDeleted.success).to.eql(false); + await ml.api.waitForDataFrameAnalyticsJobNotToExist(analyticsId); + await ml.api.assertIndicesNotToExist(destinationIndex); + }); + }); + + describe('with deleteDestIndexPattern setting', function () { + const analyticsId = `${jobId}_3`; + const destinationIndex = generateDestinationIndex(analyticsId); + + before(async () => { + // Mimic real job by creating index pattern after job is created + await ml.testResources.createIndexPatternIfNeeded(destinationIndex); + }); + + after(async () => { + await ml.testResources.deleteIndexPattern(destinationIndex); + }); + + it('should delete job and index pattern by id', async () => { + const { body } = await supertest + .delete(`/api/ml/data_frame/analytics/${analyticsId}`) + .query({ deleteDestIndexPattern: true }) + .auth(USER.ML_POWERUSER, ml.securityCommon.getPasswordForUser(USER.ML_POWERUSER)) + .set(COMMON_REQUEST_HEADERS) + .expect(200); + + expect(body.analyticsJobDeleted.success).to.eql(true); + expect(body.destIndexDeleted.success).to.eql(false); + expect(body.destIndexPatternDeleted.success).to.eql(true); + await ml.api.waitForDataFrameAnalyticsJobNotToExist(analyticsId); + await ml.testResources.assertIndexPatternNotExist(destinationIndex); + }); + }); + + describe('with deleteDestIndex & deleteDestIndexPattern setting', function () { + const analyticsId = `${jobId}_4`; + const destinationIndex = generateDestinationIndex(analyticsId); + + before(async () => { + // Mimic real job by creating target index & index pattern after DFA job is created + await ml.api.createIndices(destinationIndex); + await ml.api.assertIndicesExist(destinationIndex); + await ml.testResources.createIndexPatternIfNeeded(destinationIndex); + }); + + after(async () => { + await ml.api.deleteIndices(destinationIndex); + await ml.testResources.deleteIndexPattern(destinationIndex); + }); + + it('deletes job, target index, and index pattern by id', async () => { + const { body } = await supertest + .delete(`/api/ml/data_frame/analytics/${analyticsId}`) + .query({ deleteDestIndex: true, deleteDestIndexPattern: true }) + .auth(USER.ML_POWERUSER, ml.securityCommon.getPasswordForUser(USER.ML_POWERUSER)) + .set(COMMON_REQUEST_HEADERS) + .expect(200); + + expect(body.analyticsJobDeleted.success).to.eql(true); + expect(body.destIndexDeleted.success).to.eql(true); + expect(body.destIndexPatternDeleted.success).to.eql(true); + await ml.api.waitForDataFrameAnalyticsJobNotToExist(analyticsId); + await ml.api.assertIndicesNotToExist(destinationIndex); + await ml.testResources.assertIndexPatternNotExist(destinationIndex); + }); + }); + }); + }); +}; diff --git a/x-pack/test/api_integration/apis/ml/data_frame_analytics/index.ts b/x-pack/test/api_integration/apis/ml/data_frame_analytics/index.ts index 9e0f952ad501ba..6693561076fdd6 100644 --- a/x-pack/test/api_integration/apis/ml/data_frame_analytics/index.ts +++ b/x-pack/test/api_integration/apis/ml/data_frame_analytics/index.ts @@ -9,5 +9,6 @@ import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ loadTestFile }: FtrProviderContext) { describe('data frame analytics', function () { loadTestFile(require.resolve('./get')); + loadTestFile(require.resolve('./delete')); }); } diff --git a/x-pack/test/functional/services/ml/api.ts b/x-pack/test/functional/services/ml/api.ts index 897f37821001e1..fc2ce4bb16b99f 100644 --- a/x-pack/test/functional/services/ml/api.ts +++ b/x-pack/test/functional/services/ml/api.ts @@ -9,9 +9,9 @@ import { DataFrameAnalyticsConfig } from '../../../../plugins/ml/public/applicat import { FtrProviderContext } from '../../ftr_provider_context'; -import { JOB_STATE, DATAFEED_STATE } from '../../../../plugins/ml/common/constants/states'; +import { DATAFEED_STATE, JOB_STATE } from '../../../../plugins/ml/common/constants/states'; import { DATA_FRAME_TASK_STATE } from '../../../../plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/common'; -import { Job, Datafeed } from '../../../../plugins/ml/common/types/anomaly_detection_jobs'; +import { Datafeed, Job } from '../../../../plugins/ml/common/types/anomaly_detection_jobs'; export type MlApi = ProvidedType; @@ -110,6 +110,21 @@ export function MachineLearningAPIProvider({ getService }: FtrProviderContext) { ); }, + async createIndices(indices: string) { + log.debug(`Creating indices: '${indices}'...`); + if ((await es.indices.exists({ index: indices, allowNoIndices: false })) === true) { + log.debug(`Indices '${indices}' already exist. Nothing to create.`); + return; + } + + const createResponse = await es.indices.create({ index: indices }); + expect(createResponse) + .to.have.property('acknowledged') + .eql(true, 'Response for create request indices should be acknowledged.'); + + await this.assertIndicesExist(indices); + }, + async deleteIndices(indices: string) { log.debug(`Deleting indices: '${indices}'...`); if ((await es.indices.exists({ index: indices, allowNoIndices: false })) === false) { @@ -122,15 +137,9 @@ export function MachineLearningAPIProvider({ getService }: FtrProviderContext) { }); expect(deleteResponse) .to.have.property('acknowledged') - .eql(true, 'Response for delete request should be acknowledged'); + .eql(true, 'Response for delete request should be acknowledged.'); - await retry.waitForWithTimeout(`'${indices}' indices to be deleted`, 30 * 1000, async () => { - if ((await es.indices.exists({ index: indices, allowNoIndices: false })) === false) { - return true; - } else { - throw new Error(`expected indices '${indices}' to be deleted`); - } - }); + await this.assertIndicesNotToExist(indices); }, async cleanMlIndices() { @@ -251,6 +260,16 @@ export function MachineLearningAPIProvider({ getService }: FtrProviderContext) { }); }, + async assertIndicesNotToExist(indices: string) { + await retry.tryForTime(30 * 1000, async () => { + if ((await es.indices.exists({ index: indices, allowNoIndices: false })) === false) { + return true; + } else { + throw new Error(`indices '${indices}' should not exist`); + } + }); + }, + async assertIndicesNotEmpty(indices: string) { await retry.tryForTime(30 * 1000, async () => { const response = await es.search({ @@ -394,9 +413,9 @@ export function MachineLearningAPIProvider({ getService }: FtrProviderContext) { await this.waitForJobState(jobConfig.job_id, JOB_STATE.CLOSED); }, - async getDataFrameAnalyticsJob(analyticsId: string) { + async getDataFrameAnalyticsJob(analyticsId: string, statusCode = 200) { log.debug(`Fetching data frame analytics job '${analyticsId}'...`); - return await esSupertest.get(`/_ml/data_frame/analytics/${analyticsId}`).expect(200); + return await esSupertest.get(`/_ml/data_frame/analytics/${analyticsId}`).expect(statusCode); }, async waitForDataFrameAnalyticsJobToExist(analyticsId: string) { @@ -409,6 +428,16 @@ export function MachineLearningAPIProvider({ getService }: FtrProviderContext) { }); }, + async waitForDataFrameAnalyticsJobNotToExist(analyticsId: string) { + await retry.waitForWithTimeout(`'${analyticsId}' not to exist`, 5 * 1000, async () => { + if (await this.getDataFrameAnalyticsJob(analyticsId, 404)) { + return true; + } else { + throw new Error(`expected data frame analytics job '${analyticsId}' not to exist`); + } + }); + }, + async createDataFrameAnalyticsJob(jobConfig: DataFrameAnalyticsConfig) { const { id: analyticsId, ...analyticsConfig } = jobConfig; log.debug(`Creating data frame analytic job with id '${analyticsId}'...`); diff --git a/x-pack/test/functional/services/ml/test_resources.ts b/x-pack/test/functional/services/ml/test_resources.ts index d349416ec90f7c..739fd844f11933 100644 --- a/x-pack/test/functional/services/ml/test_resources.ts +++ b/x-pack/test/functional/services/ml/test_resources.ts @@ -5,7 +5,6 @@ */ import { ProvidedType } from '@kbn/test/types/ftr'; - import { savedSearches } from './test_resources_data'; import { COMMON_REQUEST_HEADERS } from './common'; import { FtrProviderContext } from '../../ftr_provider_context'; @@ -24,6 +23,7 @@ export function MachineLearningTestResourcesProvider({ getService }: FtrProvider const kibanaServer = getService('kibanaServer'); const log = getService('log'); const supertest = getService('supertest'); + const retry = getService('retry'); return { async setKibanaTimeZoneToUTC() { @@ -98,6 +98,21 @@ export function MachineLearningTestResourcesProvider({ getService }: FtrProvider } }, + async assertIndexPatternNotExist(title: string) { + await retry.waitForWithTimeout( + `index pattern '${title}' to not exist`, + 5 * 1000, + async () => { + const indexPatternId = await this.getIndexPatternId(title); + if (!indexPatternId) { + return true; + } else { + throw new Error(`Index pattern '${title}' should not exist.`); + } + } + ); + }, + async createSavedSearch(title: string, body: object): Promise { log.debug(`Creating saved search with title '${title}'`); From daf26b90667677b310ca0d0ae28be47c5d58a734 Mon Sep 17 00:00:00 2001 From: Quynh Nguyen <43350163+qn895@users.noreply.github.com> Date: Mon, 1 Jun 2020 11:13:59 -0500 Subject: [PATCH 02/24] [ML] Add minor refresh button to DFA and AD Job Messages tabs (#67750) * [ML] Add minor refresh button to DFA and AD Job Messages tabs * [ML] Update refresh logic for DFA [ML] Update refresh logic for DFA * [ML] Update fetchMessages callback Co-authored-by: Elastic Machine --- .../components/job_messages/job_messages.tsx | 23 ++++++++++++++++--- .../expanded_row_messages_pane.tsx | 11 ++++++--- .../job_details/job_messages_pane.tsx | 15 ++++++++---- 3 files changed, 39 insertions(+), 10 deletions(-) diff --git a/x-pack/plugins/ml/public/application/components/job_messages/job_messages.tsx b/x-pack/plugins/ml/public/application/components/job_messages/job_messages.tsx index 9cea47ded09b4c..fd2b7902833a6c 100644 --- a/x-pack/plugins/ml/public/application/components/job_messages/job_messages.tsx +++ b/x-pack/plugins/ml/public/application/components/job_messages/job_messages.tsx @@ -6,7 +6,7 @@ import React, { FC } from 'react'; -import { EuiSpacer, EuiInMemoryTable } from '@elastic/eui'; +import { EuiSpacer, EuiInMemoryTable, EuiButtonIcon, EuiToolTip } from '@elastic/eui'; // @ts-ignore import { formatDate } from '@elastic/eui/lib/services/format'; import { i18n } from '@kbn/i18n'; @@ -21,16 +21,33 @@ interface JobMessagesProps { messages: JobMessage[]; loading: boolean; error: string; + refreshMessage?: React.MouseEventHandler; } /** * Component for rendering job messages for anomaly detection * and data frame analytics jobs. */ -export const JobMessages: FC = ({ messages, loading, error }) => { +export const JobMessages: FC = ({ messages, loading, error, refreshMessage }) => { const columns = [ { - name: '', + name: refreshMessage ? ( + + + + ) : ( + '' + ), render: (message: JobMessage) => , width: `${theme.euiSizeL}`, }, diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/expanded_row_messages_pane.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/expanded_row_messages_pane.tsx index fc860251bf83d7..0dd9eba172e1cc 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/expanded_row_messages_pane.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/expanded_row_messages_pane.tsx @@ -22,7 +22,6 @@ export const ExpandedRowMessagesPane: FC = ({ analyticsId }) => { const getMessagesFactory = () => { let concurrentLoads = 0; - return async function getMessages() { try { concurrentLoads++; @@ -52,8 +51,14 @@ export const ExpandedRowMessagesPane: FC = ({ analyticsId }) => { } }; }; - useRefreshAnalyticsList({ onRefresh: getMessagesFactory() }); - return ; + return ( + + ); }; diff --git a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/job_details/job_messages_pane.tsx b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/job_details/job_messages_pane.tsx index fbb64db94cd56c..486de90d2299c3 100644 --- a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/job_details/job_messages_pane.tsx +++ b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/job_details/job_messages_pane.tsx @@ -4,12 +4,10 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { FC, useEffect, useState } from 'react'; - +import React, { FC, useCallback, useEffect, useState } from 'react'; import { ml } from '../../../../services/ml_api_service'; import { JobMessages } from '../../../../components/job_messages'; import { JobMessage } from '../../../../../../common/types/audit_message'; - interface JobMessagesPaneProps { jobId: string; } @@ -32,9 +30,18 @@ export const JobMessagesPane: FC = ({ jobId }) => { } }; + const refreshMessage = useCallback(fetchMessages, [jobId]); + useEffect(() => { fetchMessages(); }, []); - return ; + return ( + + ); }; From ce47ef5d24656867121e8bd2c88cd3a86286e4c4 Mon Sep 17 00:00:00 2001 From: Brandon Kobel Date: Mon, 1 Jun 2020 10:09:07 -0700 Subject: [PATCH 03/24] Updating the licensed feature usage API response format (#67712) Co-authored-by: Elastic Machine --- .../licensing/server/routes/feature_usage.ts | 16 ++--- .../services/feature_usage_service.test.ts | 70 ++++++++++++------- .../server/services/feature_usage_service.ts | 37 ++++++---- .../feature_usage_test/server/plugin.ts | 6 +- .../licensed_feature_usage/feature_usage.ts | 25 +++++-- 5 files changed, 98 insertions(+), 56 deletions(-) diff --git a/x-pack/plugins/licensing/server/routes/feature_usage.ts b/x-pack/plugins/licensing/server/routes/feature_usage.ts index 5fbfbc3f577b81..fa26d09903dc31 100644 --- a/x-pack/plugins/licensing/server/routes/feature_usage.ts +++ b/x-pack/plugins/licensing/server/routes/feature_usage.ts @@ -15,15 +15,13 @@ export function registerFeatureUsageRoute( async (context, request, response) => { const [, , { featureUsage }] = await getStartServices(); return response.ok({ - body: [...featureUsage.getLastUsages().entries()].reduce( - (res, [featureName, lastUsage]) => { - return { - ...res, - [featureName]: new Date(lastUsage).toISOString(), - }; - }, - {} - ), + body: { + features: featureUsage.getLastUsages().map((usage) => ({ + name: usage.name, + last_used: usage.lastUsed, + license_level: usage.licenseType, + })), + }, }); } ); diff --git a/x-pack/plugins/licensing/server/services/feature_usage_service.test.ts b/x-pack/plugins/licensing/server/services/feature_usage_service.test.ts index f0ef0dbec0b220..39f7aa6503b35e 100644 --- a/x-pack/plugins/licensing/server/services/feature_usage_service.test.ts +++ b/x-pack/plugins/licensing/server/services/feature_usage_service.test.ts @@ -17,16 +17,13 @@ describe('FeatureUsageService', () => { jest.restoreAllMocks(); }); - const toObj = (map: ReadonlyMap): Record => - Object.fromEntries(map.entries()); - describe('#setup', () => { describe('#register', () => { it('throws when registering the same feature twice', () => { const setup = service.setup(); - setup.register('foo'); + setup.register('foo', 'basic'); expect(() => { - setup.register('foo'); + setup.register('foo', 'basic'); }).toThrowErrorMatchingInlineSnapshot(`"Feature 'foo' has already been registered."`); }); }); @@ -36,32 +33,50 @@ describe('FeatureUsageService', () => { describe('#notifyUsage', () => { it('allows to notify a feature usage', () => { const setup = service.setup(); - setup.register('feature'); + setup.register('feature', 'basic'); const start = service.start(); start.notifyUsage('feature', 127001); - expect(start.getLastUsages().get('feature')).toBe(127001); + expect(start.getLastUsages()).toEqual([ + { + lastUsed: new Date(127001), + licenseType: 'basic', + name: 'feature', + }, + ]); }); it('can receive a Date object', () => { const setup = service.setup(); - setup.register('feature'); + setup.register('feature', 'basic'); const start = service.start(); const usageTime = new Date(2015, 9, 21, 17, 54, 12); start.notifyUsage('feature', usageTime); - expect(start.getLastUsages().get('feature')).toBe(usageTime.getTime()); + expect(start.getLastUsages()).toEqual([ + { + lastUsed: usageTime, + licenseType: 'basic', + name: 'feature', + }, + ]); }); it('uses the current time when `usedAt` is unspecified', () => { jest.spyOn(Date, 'now').mockReturnValue(42); const setup = service.setup(); - setup.register('feature'); + setup.register('feature', 'basic'); const start = service.start(); start.notifyUsage('feature'); - expect(start.getLastUsages().get('feature')).toBe(42); + expect(start.getLastUsages()).toEqual([ + { + lastUsed: new Date(42), + licenseType: 'basic', + name: 'feature', + }, + ]); }); it('throws when notifying for an unregistered feature', () => { @@ -76,40 +91,41 @@ describe('FeatureUsageService', () => { describe('#getLastUsages', () => { it('returns the last usage for all used features', () => { const setup = service.setup(); - setup.register('featureA'); - setup.register('featureB'); + setup.register('featureA', 'basic'); + setup.register('featureB', 'gold'); const start = service.start(); start.notifyUsage('featureA', 127001); start.notifyUsage('featureB', 6666); - expect(toObj(start.getLastUsages())).toEqual({ - featureA: 127001, - featureB: 6666, - }); + expect(start.getLastUsages()).toEqual([ + { lastUsed: new Date(127001), licenseType: 'basic', name: 'featureA' }, + { lastUsed: new Date(6666), licenseType: 'gold', name: 'featureB' }, + ]); }); it('returns the last usage even after notifying for an older usage', () => { const setup = service.setup(); - setup.register('featureA'); + setup.register('featureA', 'basic'); const start = service.start(); start.notifyUsage('featureA', 1000); start.notifyUsage('featureA', 500); - expect(toObj(start.getLastUsages())).toEqual({ - featureA: 1000, - }); + expect(start.getLastUsages()).toEqual([ + { lastUsed: new Date(1000), licenseType: 'basic', name: 'featureA' }, + ]); }); - it('does not return entries for unused registered features', () => { + it('returns entries for unused registered features', () => { const setup = service.setup(); - setup.register('featureA'); - setup.register('featureB'); + setup.register('featureA', 'basic'); + setup.register('featureB', 'gold'); const start = service.start(); start.notifyUsage('featureA', 127001); - expect(toObj(start.getLastUsages())).toEqual({ - featureA: 127001, - }); + expect(start.getLastUsages()).toEqual([ + { lastUsed: new Date(127001), licenseType: 'basic', name: 'featureA' }, + { lastUsed: null, licenseType: 'gold', name: 'featureB' }, + ]); }); }); }); diff --git a/x-pack/plugins/licensing/server/services/feature_usage_service.ts b/x-pack/plugins/licensing/server/services/feature_usage_service.ts index 0c6613d37f63a3..9bfcb28f36b2a9 100644 --- a/x-pack/plugins/licensing/server/services/feature_usage_service.ts +++ b/x-pack/plugins/licensing/server/services/feature_usage_service.ts @@ -5,13 +5,20 @@ */ import { isDate } from 'lodash'; +import { LicenseType } from '../../common/types'; /** @public */ export interface FeatureUsageServiceSetup { /** * Register a feature to be able to notify of it's usages using the {@link FeatureUsageServiceStart | service start contract}. */ - register(featureName: string): void; + register(featureName: string, licenseType: LicenseType): void; +} + +export interface LastFeatureUsage { + name: string; + lastUsed: Date | null; + licenseType: LicenseType; } /** @public */ @@ -27,20 +34,23 @@ export interface FeatureUsageServiceStart { * Return a map containing last usage timestamp for all features. * Features that were not used yet do not appear in the map. */ - getLastUsages(): ReadonlyMap; + getLastUsages(): LastFeatureUsage[]; } export class FeatureUsageService { - private readonly features: string[] = []; - private readonly lastUsages = new Map(); + private readonly lastUsages = new Map(); public setup(): FeatureUsageServiceSetup { return { - register: (featureName) => { - if (this.features.includes(featureName)) { + register: (featureName, licenseType) => { + if (this.lastUsages.has(featureName)) { throw new Error(`Feature '${featureName}' has already been registered.`); } - this.features.push(featureName); + this.lastUsages.set(featureName, { + name: featureName, + lastUsed: null, + licenseType, + }); }, }; } @@ -48,16 +58,17 @@ export class FeatureUsageService { public start(): FeatureUsageServiceStart { return { notifyUsage: (featureName, usedAt = Date.now()) => { - if (!this.features.includes(featureName)) { + const usage = this.lastUsages.get(featureName); + if (!usage) { throw new Error(`Feature '${featureName}' is not registered.`); } - if (isDate(usedAt)) { - usedAt = usedAt.getTime(); + + const lastUsed = isDate(usedAt) ? usedAt : new Date(usedAt); + if (usage.lastUsed == null || lastUsed > usage.lastUsed) { + usage.lastUsed = lastUsed; } - const currentValue = this.lastUsages.get(featureName) ?? 0; - this.lastUsages.set(featureName, Math.max(usedAt, currentValue)); }, - getLastUsages: () => new Map(this.lastUsages.entries()), + getLastUsages: () => Array.from(this.lastUsages.values()), }; } } diff --git a/x-pack/test/plugin_api_integration/plugins/feature_usage_test/server/plugin.ts b/x-pack/test/plugin_api_integration/plugins/feature_usage_test/server/plugin.ts index b36d6dca077f74..af410d457fc059 100644 --- a/x-pack/test/plugin_api_integration/plugins/feature_usage_test/server/plugin.ts +++ b/x-pack/test/plugin_api_integration/plugins/feature_usage_test/server/plugin.ts @@ -38,9 +38,9 @@ export class FeatureUsageTestPlugin }: CoreSetup, { licensing }: FeatureUsageTestSetupDependencies ) { - licensing.featureUsage.register('test_feature_a'); - licensing.featureUsage.register('test_feature_b'); - licensing.featureUsage.register('test_feature_c'); + licensing.featureUsage.register('Test feature A', 'basic'); + licensing.featureUsage.register('Test feature B', 'gold'); + licensing.featureUsage.register('Test feature C', 'platinum'); registerRoutes(http.createRouter(), getStartServices); diff --git a/x-pack/test/plugin_api_integration/test_suites/licensed_feature_usage/feature_usage.ts b/x-pack/test/plugin_api_integration/test_suites/licensed_feature_usage/feature_usage.ts index dfbc41d883e021..5c8fac9586e3ac 100644 --- a/x-pack/test/plugin_api_integration/test_suites/licensed_feature_usage/feature_usage.ts +++ b/x-pack/test/plugin_api_integration/test_suites/licensed_feature_usage/feature_usage.ts @@ -20,15 +20,32 @@ export default function ({ getService }: FtrProviderContext) { describe('/api/licensing/feature_usage', () => { it('returns a map of last feature usages', async () => { const timeA = Date.now(); - await notifyUsage('test_feature_a', timeA); + await notifyUsage('Test feature C', timeA); const timeB = Date.now() - 4567; - await notifyUsage('test_feature_b', timeB); + await notifyUsage('Test feature B', timeB); const response = await supertest.get('/api/licensing/feature_usage').expect(200); - expect(response.body.test_feature_a).to.eql(toISO(timeA)); - expect(response.body.test_feature_b).to.eql(toISO(timeB)); + expect(response.body).to.eql({ + features: [ + { + last_used: null, + license_level: 'basic', + name: 'Test feature A', + }, + { + last_used: toISO(timeB), + license_level: 'gold', + name: 'Test feature B', + }, + { + last_used: toISO(timeA), + license_level: 'platinum', + name: 'Test feature C', + }, + ], + }); }); }); } From be51ca6041f77c48c53d6dd4ac30b2510bb73da3 Mon Sep 17 00:00:00 2001 From: Wylie Conlon Date: Mon, 1 Jun 2020 13:33:23 -0400 Subject: [PATCH 04/24] [Lens] Allow visualizations to provide a dimension editor (#67560) * [Lens] Allow visualizations to provide a dimension editor * Update to tab style * Remove table update * Update class name * typecheck fix * Add test * Require each dimension group to enable editor Co-authored-by: Elastic Machine Co-authored-by: Marta Bondyra --- .../config_panel/_layer_panel.scss | 3 + .../config_panel/config_panel.tsx | 3 - .../config_panel/dimension_popover.tsx | 2 +- .../config_panel/layer_panel.test.tsx | 271 ++++++++++++++++++ .../editor_frame/config_panel/layer_panel.tsx | 263 ++++++++++------- .../editor_frame/config_panel/types.ts | 1 + x-pack/plugins/lens/public/types.ts | 21 ++ 7 files changed, 458 insertions(+), 106 deletions(-) create mode 100644 x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.test.tsx diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/_layer_panel.scss b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/_layer_panel.scss index 3fbc42f9a25a09..924f44a37c4591 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/_layer_panel.scss +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/_layer_panel.scss @@ -31,3 +31,6 @@ min-height: $euiSizeXXL; } +.lnsLayerPanel__styleEditor { + width: $euiSize * 28; +} diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/config_panel.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/config_panel.tsx index 0d86a051b0faa0..e53e465c18950b 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/config_panel.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/config_panel.tsx @@ -45,7 +45,6 @@ function LayerPanels( } ) { const { - framePublicAPI, activeVisualization, visualizationState, dispatch, @@ -109,12 +108,10 @@ function LayerPanels( {...props} key={layerId} layerId={layerId} - activeVisualization={activeVisualization} visualizationState={visualizationState} updateVisualization={setVisualizationState} updateDatasource={updateDatasource} updateAll={updateAll} - frame={framePublicAPI} isOnlyLayer={layerIds.length === 1} onRemoveLayer={() => { dispatch({ diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/dimension_popover.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/dimension_popover.tsx index f89b6ef32d3f78..cc8d97a445016a 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/dimension_popover.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/dimension_popover.tsx @@ -36,7 +36,7 @@ export function DimensionPopover({ (popoverState.openId === accessor || (noMatch && popoverState.addingToGroupId === groupId)) } closePopover={() => { - setPopoverState({ isOpen: false, openId: null, addingToGroupId: null }); + setPopoverState({ isOpen: false, openId: null, addingToGroupId: null, tabId: null }); }} button={trigger} anchorPosition="leftUp" diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.test.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.test.tsx new file mode 100644 index 00000000000000..1f987f86d3950f --- /dev/null +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.test.tsx @@ -0,0 +1,271 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; +import { act } from 'react-dom/test-utils'; +import { + createMockVisualization, + createMockFramePublicAPI, + createMockDatasource, + DatasourceMock, +} from '../../mocks'; +import { EuiFormRow, EuiPopover } from '@elastic/eui'; +import { mount } from 'enzyme'; +import { mountWithIntl } from 'test_utils/enzyme_helpers'; +import { Visualization } from '../../../types'; +import { LayerPanel } from './layer_panel'; +import { coreMock } from 'src/core/public/mocks'; +import { generateId } from '../../../id_generator'; + +jest.mock('../../../id_generator'); + +describe('LayerPanel', () => { + let mockVisualization: jest.Mocked; + let mockDatasource: DatasourceMock; + + function getDefaultProps() { + const frame = createMockFramePublicAPI(); + frame.datasourceLayers = { + first: mockDatasource.publicAPIMock, + }; + return { + layerId: 'first', + activeVisualizationId: 'vis1', + visualizationMap: { + vis1: mockVisualization, + }, + activeDatasourceId: 'ds1', + datasourceMap: { + ds1: mockDatasource, + }, + datasourceStates: { + ds1: { + isLoading: false, + state: 'state', + }, + }, + visualizationState: 'state', + updateVisualization: jest.fn(), + updateDatasource: jest.fn(), + updateAll: jest.fn(), + framePublicAPI: frame, + isOnlyLayer: true, + onRemoveLayer: jest.fn(), + dispatch: jest.fn(), + core: coreMock.createStart(), + }; + } + + beforeEach(() => { + mockVisualization = { + ...createMockVisualization(), + id: 'testVis', + visualizationTypes: [ + { + icon: 'empty', + id: 'testVis', + label: 'TEST1', + }, + ], + }; + + mockVisualization.getLayerIds.mockReturnValue(['first']); + mockDatasource = createMockDatasource('ds1'); + }); + + it('should fail to render if the public API is out of date', () => { + const props = getDefaultProps(); + props.framePublicAPI.datasourceLayers = {}; + const component = mountWithIntl(); + expect(component.isEmptyRender()).toBe(true); + }); + + it('should fail to render if the active visualization is missing', () => { + const component = mountWithIntl( + + ); + expect(component.isEmptyRender()).toBe(true); + }); + + describe('layer reset and remove', () => { + it('should show the reset button when single layer', () => { + const component = mountWithIntl(); + expect(component.find('[data-test-subj="lns_layer_remove"]').first().text()).toContain( + 'Reset layer' + ); + }); + + it('should show the delete button when multiple layers', () => { + const component = mountWithIntl(); + expect(component.find('[data-test-subj="lns_layer_remove"]').first().text()).toContain( + 'Delete layer' + ); + }); + + it('should call the clear callback', () => { + const cb = jest.fn(); + const component = mountWithIntl(); + act(() => { + component.find('[data-test-subj="lns_layer_remove"]').first().simulate('click'); + }); + expect(cb).toHaveBeenCalled(); + }); + }); + + describe('single group', () => { + it('should render the non-editable state', () => { + mockVisualization.getConfiguration.mockReturnValue({ + groups: [ + { + groupLabel: 'A', + groupId: 'a', + accessors: ['x'], + filterOperations: () => true, + supportsMoreColumns: false, + dataTestSubj: 'lnsGroup', + }, + ], + }); + + const component = mountWithIntl(); + + const group = component.find('DragDrop[data-test-subj="lnsGroup"]'); + expect(group).toHaveLength(1); + }); + + it('should render the group with a way to add a new column', () => { + mockVisualization.getConfiguration.mockReturnValue({ + groups: [ + { + groupLabel: 'A', + groupId: 'a', + accessors: [], + filterOperations: () => true, + supportsMoreColumns: true, + dataTestSubj: 'lnsGroup', + }, + ], + }); + + const component = mountWithIntl(); + + const group = component.find('DragDrop[data-test-subj="lnsGroup"]'); + expect(group).toHaveLength(1); + }); + + it('should render the required warning when only one group is configured', () => { + mockVisualization.getConfiguration.mockReturnValue({ + groups: [ + { + groupLabel: 'A', + groupId: 'a', + accessors: ['x'], + filterOperations: () => true, + supportsMoreColumns: false, + dataTestSubj: 'lnsGroup', + }, + { + groupLabel: 'B', + groupId: 'b', + accessors: [], + filterOperations: () => true, + supportsMoreColumns: true, + dataTestSubj: 'lnsGroup', + required: true, + }, + ], + }); + + const component = mountWithIntl(); + + const group = component + .find(EuiFormRow) + .findWhere((e) => e.prop('error') === 'Required dimension'); + expect(group).toHaveLength(1); + }); + + it('should render the datasource and visualization panels inside the dimension popover', () => { + mockVisualization.getConfiguration.mockReturnValueOnce({ + groups: [ + { + groupLabel: 'A', + groupId: 'a', + accessors: ['newid'], + filterOperations: () => true, + supportsMoreColumns: false, + dataTestSubj: 'lnsGroup', + enableDimensionEditor: true, + }, + ], + }); + mockVisualization.renderDimensionEditor = jest.fn(); + + const component = mountWithIntl(); + + const group = component.find('DimensionPopover'); + const panel = mount(group.prop('panel')); + + expect(panel.find('EuiTabbedContent').prop('tabs')).toHaveLength(2); + act(() => { + panel.find('EuiTab#visualization').simulate('click'); + }); + expect(mockVisualization.renderDimensionEditor).toHaveBeenCalledWith( + expect.any(Element), + expect.objectContaining({ + groupId: 'a', + accessor: 'newid', + }) + ); + }); + + it('should keep the popover open when configuring a new dimension', () => { + /** + * The ID generation system for new dimensions has been messy before, so + * this tests that the ID used in the first render is used to keep the popover + * open in future renders + */ + (generateId as jest.Mock).mockReturnValueOnce(`newid`); + (generateId as jest.Mock).mockReturnValueOnce(`bad`); + mockVisualization.getConfiguration.mockReturnValueOnce({ + groups: [ + { + groupLabel: 'A', + groupId: 'a', + accessors: [], + filterOperations: () => true, + supportsMoreColumns: true, + dataTestSubj: 'lnsGroup', + }, + ], + }); + // Normally the configuration would change in response to a state update, + // but this test is updating it directly + mockVisualization.getConfiguration.mockReturnValueOnce({ + groups: [ + { + groupLabel: 'A', + groupId: 'a', + accessors: ['newid'], + filterOperations: () => true, + supportsMoreColumns: false, + dataTestSubj: 'lnsGroup', + }, + ], + }); + + const component = mountWithIntl(); + + const group = component.find('DimensionPopover'); + const triggerButton = mountWithIntl(group.prop('trigger')); + act(() => { + triggerButton.find('[data-test-subj="lns-empty-dimension"]').first().simulate('click'); + }); + component.update(); + + expect(component.find(EuiPopover).prop('isOpen')).toBe(true); + }); + }); +}); diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx index 814b7fc644c9c2..bd501db2b752a4 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx @@ -13,11 +13,12 @@ import { EuiFlexItem, EuiButtonEmpty, EuiFormRow, + EuiTabbedContent, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; import { NativeRenderer } from '../../../native_renderer'; -import { Visualization, FramePublicAPI, StateSetter } from '../../../types'; +import { StateSetter } from '../../../types'; import { DragContext, DragDrop, ChildDragDropProvider } from '../../../drag_drop'; import { LayerSettings } from './layer_settings'; import { trackUiEvent } from '../../../lens_ui_telemetry'; @@ -27,11 +28,8 @@ import { DimensionPopover } from './dimension_popover'; export function LayerPanel( props: Exclude & { - frame: FramePublicAPI; layerId: string; isOnlyLayer: boolean; - activeVisualization: Visualization; - visualizationState: unknown; updateVisualization: StateSetter; updateDatasource: (datasourceId: string, newState: unknown) => void; updateAll: ( @@ -47,13 +45,19 @@ export function LayerPanel( isOpen: false, openId: null, addingToGroupId: null, + tabId: null, }); - const { framePublicAPI, layerId, activeVisualization, isOnlyLayer, onRemoveLayer } = props; + const { framePublicAPI, layerId, isOnlyLayer, onRemoveLayer } = props; const datasourcePublicAPI = framePublicAPI.datasourceLayers[layerId]; - if (!datasourcePublicAPI) { + if ( + !datasourcePublicAPI || + !props.activeVisualizationId || + !props.visualizationMap[props.activeVisualizationId] + ) { return null; } + const activeVisualization = props.visualizationMap[props.activeVisualizationId]; const layerVisualizationConfigProps = { layerId, dragDropContext, @@ -158,104 +162,156 @@ export function LayerPanel( } > <> - {group.accessors.map((accessor) => ( - { - layerDatasource.onDrop({ - ...layerDatasourceDropProps, - droppedItem, - columnId: accessor, - filterOperations: group.filterOperations, - }); - }} - > - { - if (popoverState.isOpen) { - setPopoverState({ - isOpen: false, - openId: null, - addingToGroupId: null, - }); - } else { - setPopoverState({ - isOpen: true, - openId: accessor, - addingToGroupId: null, // not set for existing dimension - }); - } - }, - }} - /> - } - panel={ - - } - /> + {group.accessors.map((accessor) => { + const tabs = [ + { + id: 'datasource', + name: i18n.translate('xpack.lens.editorFrame.quickFunctionsLabel', { + defaultMessage: 'Quick functions', + }), + content: ( + <> + + + + ), + }, + ]; - { - trackUiEvent('indexpattern_dimension_removed'); - props.updateAll( - datasourceId, - layerDatasource.removeColumn({ - layerId, - columnId: accessor, - prevState: layerDatasourceState, - }), - props.activeVisualization.removeDimension({ - layerId, - columnId: accessor, - prevState: props.visualizationState, - }) - ); + if (activeVisualization.renderDimensionEditor) { + tabs.push({ + id: 'visualization', + name: i18n.translate('xpack.lens.editorFrame.formatStyleLabel', { + defaultMessage: 'Format & style', + }), + content: ( +
+ + +
+ ), + }); + } + + return ( + { + layerDatasource.onDrop({ + ...layerDatasourceDropProps, + droppedItem, + columnId: accessor, + filterOperations: group.filterOperations, + }); }} - /> - - ))} + > + { + if (popoverState.isOpen) { + setPopoverState({ + isOpen: false, + openId: null, + addingToGroupId: null, + tabId: null, + }); + } else { + setPopoverState({ + isOpen: true, + openId: accessor, + addingToGroupId: null, // not set for existing dimension + tabId: 'datasource', + }); + } + }, + }} + /> + } + panel={ + t.id === popoverState.tabId)} + size="s" + onTabClick={(tab) => { + setPopoverState({ + ...popoverState, + tabId: tab.id as typeof popoverState['tabId'], + }); + }} + /> + } + /> + + { + trackUiEvent('indexpattern_dimension_removed'); + props.updateAll( + datasourceId, + layerDatasource.removeColumn({ + layerId, + columnId: accessor, + prevState: layerDatasourceState, + }), + activeVisualization.removeDimension({ + layerId, + columnId: accessor, + prevState: props.visualizationState, + }) + ); + }} + /> +
+ ); + })} {group.supportsMoreColumns ? ( = VisualizationConfigProp setState: (newState: T) => void; }; +export type VisualizationDimensionEditorProps = VisualizationConfigProps & { + groupId: string; + accessor: string; + setState: (newState: T) => void; +}; + export type VisualizationDimensionGroupConfig = SharedDimensionProps & { groupLabel: string; @@ -300,6 +306,12 @@ export type VisualizationDimensionGroupConfig = SharedDimensionProps & { /** If required, a warning will appear if accessors are empty */ required?: boolean; dataTestSubj?: string; + + /** + * When the dimension editor is enabled for this group, all dimensions in the group + * will render the extra tab for the dimension editor + */ + enableDimensionEditor?: boolean; }; interface VisualizationDimensionChangeProps { @@ -459,6 +471,15 @@ export interface Visualization { */ removeDimension: (props: VisualizationDimensionChangeProps) => T; + /** + * Additional editor that gets rendered inside the dimension popover. + * This can be used to configure dimension-specific options + */ + renderDimensionEditor?: ( + domElement: Element, + props: VisualizationDimensionEditorProps + ) => void; + /** * The frame will call this function on all visualizations at different times. The * main use cases where visualization suggestions are requested are: From 5a6c77226c212ad51ce84a23d81c8781c6af8d72 Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Mon, 1 Jun 2020 18:37:56 +0100 Subject: [PATCH 05/24] skip flaky suite (#67833) --- .../__jest__/client_integration/template_create.test.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/index_management/__jest__/client_integration/template_create.test.tsx b/x-pack/plugins/index_management/__jest__/client_integration/template_create.test.tsx index 05abe284fab32e..8f464987418c03 100644 --- a/x-pack/plugins/index_management/__jest__/client_integration/template_create.test.tsx +++ b/x-pack/plugins/index_management/__jest__/client_integration/template_create.test.tsx @@ -59,7 +59,8 @@ const KEYWORD_MAPPING_FIELD = { type: 'keyword', }; -describe('', () => { +// FLAKY: https://github.com/elastic/kibana/issues/67833 +describe.skip('', () => { let testBed: TemplateFormTestBed; const { server, httpRequestsMockHelpers } = setupEnvironment(); From cdbcb9720b69d6373dbe8da06bd15d9d0324e795 Mon Sep 17 00:00:00 2001 From: Shahzad Date: Mon, 1 Jun 2020 19:53:16 +0200 Subject: [PATCH 06/24] [Uptime] Use date histogram in monitor states (#67558) Co-authored-by: Elastic Machine --- .../server/lib/requests/search/enrich_monitor_groups.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/uptime/server/lib/requests/search/enrich_monitor_groups.ts b/x-pack/plugins/uptime/server/lib/requests/search/enrich_monitor_groups.ts index 9f153e186420d5..53b1fe881cd929 100644 --- a/x-pack/plugins/uptime/server/lib/requests/search/enrich_monitor_groups.ts +++ b/x-pack/plugins/uptime/server/lib/requests/search/enrich_monitor_groups.ts @@ -15,6 +15,7 @@ import { SortOrder, } from '../../../../common/runtime_types'; import { MonitorEnricher } from './fetch_page'; +import { getHistogramInterval } from '../../helper/get_histogram_interval'; export const enrichMonitorGroups: MonitorEnricher = async ( queryContext: QueryContext, @@ -317,11 +318,13 @@ const getHistogramForMonitors = async ( }, aggs: { histogram: { - auto_date_histogram: { + date_histogram: { field: '@timestamp', // 12 seems to be a good size for performance given // long monitor lists of up to 100 on the overview page - buckets: 12, + fixed_interval: + getHistogramInterval(queryContext.dateRangeStart, queryContext.dateRangeEnd, 12) + + 'ms', missing: 0, }, aggs: { From 279b11b78d22b385d152fb66eee0dfa979366ce0 Mon Sep 17 00:00:00 2001 From: Yara Tercero Date: Mon, 1 Jun 2020 14:32:42 -0400 Subject: [PATCH 07/24] [SIEM][Exceptions] - Update exceptions hooks to include _find filtering (#67435) ### Summary - Updates exception list hooks to include filtering options and updates corresponding unit tests. - Adds refreshList callback to hook that fetches the list and its items - Updates hooks tests to test onError callback - Updates tests to use type checking more effectively per feedback from @FrankHassanabad (thanks!) --- x-pack/plugins/lists/common/constants.mock.ts | 10 + x-pack/plugins/lists/common/constants.ts | 6 + .../create_exception_list_item_schema.mock.ts | 34 +++ .../lists/public/exceptions/__mocks__/api.ts | 2 + .../lists/public/exceptions/api.test.ts | 220 +++++++++++++----- x-pack/plugins/lists/public/exceptions/api.ts | 69 +++++- .../hooks/persist_exception_item.test.tsx | 56 +++-- .../hooks/persist_exception_item.tsx | 7 + .../hooks/persist_exception_list.test.tsx | 53 +++-- .../hooks/persist_exception_list.tsx | 7 + .../hooks/use_exception_list.test.tsx | 200 ++++++++++------ .../exceptions/hooks/use_exception_list.tsx | 132 ++++++++--- .../plugins/lists/public/exceptions/types.ts | 27 ++- 13 files changed, 627 insertions(+), 196 deletions(-) create mode 100644 x-pack/plugins/lists/common/schemas/request/create_exception_list_item_schema.mock.ts diff --git a/x-pack/plugins/lists/common/constants.mock.ts b/x-pack/plugins/lists/common/constants.mock.ts index 8c5f6b0cbe56ce..d8e4dfba1599e7 100644 --- a/x-pack/plugins/lists/common/constants.mock.ts +++ b/x-pack/plugins/lists/common/constants.mock.ts @@ -29,3 +29,13 @@ export const TYPE = 'ip'; export const VALUE = '127.0.0.1'; export const VALUE_2 = '255.255.255'; export const NAMESPACE_TYPE = 'single'; + +// Exception List specific +export const ENDPOINT_TYPE = 'endpoint'; +export const ENTRIES = [ + { field: 'some.field', match: 'some value', match_any: undefined, operator: 'included' }, +]; +export const ITEM_TYPE = 'simple'; +export const _TAGS = []; +export const TAGS = []; +export const COMMENT = []; diff --git a/x-pack/plugins/lists/common/constants.ts b/x-pack/plugins/lists/common/constants.ts index 96d28bf618ce4d..6cb88b19483cef 100644 --- a/x-pack/plugins/lists/common/constants.ts +++ b/x-pack/plugins/lists/common/constants.ts @@ -16,3 +16,9 @@ export const LIST_ITEM_URL = `${LIST_URL}/items`; */ export const EXCEPTION_LIST_URL = '/api/exception_lists'; export const EXCEPTION_LIST_ITEM_URL = '/api/exception_lists/items'; + +/** + * Exception list spaces + */ +export const EXCEPTION_LIST_NAMESPACE_AGNOSTIC = 'exception-list-agnostic'; +export const EXCEPTION_LIST_NAMESPACE = 'exception-list'; diff --git a/x-pack/plugins/lists/common/schemas/request/create_exception_list_item_schema.mock.ts b/x-pack/plugins/lists/common/schemas/request/create_exception_list_item_schema.mock.ts new file mode 100644 index 00000000000000..f9af10245b7eee --- /dev/null +++ b/x-pack/plugins/lists/common/schemas/request/create_exception_list_item_schema.mock.ts @@ -0,0 +1,34 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { + COMMENT, + DESCRIPTION, + ENTRIES, + ITEM_TYPE, + LIST_ID, + META, + NAME, + NAMESPACE_TYPE, + TAGS, + _TAGS, +} from '../../constants.mock'; + +import { CreateExceptionListItemSchema } from './create_exception_list_item_schema'; + +export const getCreateExceptionListItemSchemaMock = (): CreateExceptionListItemSchema => ({ + _tags: _TAGS, + comment: COMMENT, + description: DESCRIPTION, + entries: ENTRIES, + item_id: undefined, + list_id: LIST_ID, + meta: META, + name: NAME, + namespace_type: NAMESPACE_TYPE, + tags: TAGS, + type: ITEM_TYPE, +}); diff --git a/x-pack/plugins/lists/public/exceptions/__mocks__/api.ts b/x-pack/plugins/lists/public/exceptions/__mocks__/api.ts index 787d374ab2cad0..ecc771279b3ab9 100644 --- a/x-pack/plugins/lists/public/exceptions/__mocks__/api.ts +++ b/x-pack/plugins/lists/public/exceptions/__mocks__/api.ts @@ -40,8 +40,10 @@ export const fetchExceptionListById = async ({ }: ApiCallByIdProps): Promise => Promise.resolve(getExceptionListSchemaMock()); export const fetchExceptionListItemsByListId = async ({ + filterOptions, http, listId, + pagination, signal, }: ApiCallByListIdProps): Promise => Promise.resolve({ data: [getExceptionListItemSchemaMock()], page: 1, per_page: 20, total: 1 }); diff --git a/x-pack/plugins/lists/public/exceptions/api.test.ts b/x-pack/plugins/lists/public/exceptions/api.test.ts index 18a89071e98875..b9512bb398745b 100644 --- a/x-pack/plugins/lists/public/exceptions/api.test.ts +++ b/x-pack/plugins/lists/public/exceptions/api.test.ts @@ -6,8 +6,9 @@ import { createKibanaCoreStartMock } from '../common/mocks/kibana_core'; import { getExceptionListSchemaMock } from '../../common/schemas/response/exception_list_schema.mock'; import { getExceptionListItemSchemaMock } from '../../common/schemas/response/exception_list_item_schema.mock'; +import { getCreateExceptionListSchemaMock } from '../../common/schemas/request/create_exception_list_schema.mock'; +import { getCreateExceptionListItemSchemaMock } from '../../common/schemas/request/create_exception_list_item_schema.mock'; -import { mockNewExceptionItem, mockNewExceptionList } from './mock'; import { addExceptionList, addExceptionListItem, @@ -37,188 +38,291 @@ const mockKibanaHttpService = ((createKibanaCoreStartMock() as unknown) as jest. ); describe('Exceptions Lists API', () => { - describe('addExceptionList', () => { + describe('#addExceptionList', () => { beforeEach(() => { fetchMock.mockClear(); fetchMock.mockResolvedValue(getExceptionListSchemaMock()); }); - test('check parameter url, body', async () => { - await addExceptionList({ + test('it uses POST when "list.id" does not exist', async () => { + const payload = getCreateExceptionListSchemaMock(); + const exceptionResponse = await addExceptionList({ http: mockKibanaHttpService(), - list: mockNewExceptionList, + list: payload, signal: abortCtrl.signal, }); + expect(fetchMock).toHaveBeenCalledWith('/api/exception_lists', { - body: - '{"_tags":["endpoint","process","malware","os:linux"],"description":"This is a sample endpoint type exception","list_id":"endpoint_list","name":"Sample Endpoint Exception List","tags":["user added string for a tag","malware"],"type":"endpoint"}', + body: JSON.stringify(payload), method: 'POST', signal: abortCtrl.signal, }); + expect(exceptionResponse).toEqual({ id: '1', ...getExceptionListSchemaMock() }); }); - test('check parameter url, body when "list.id" exists', async () => { - await addExceptionList({ + test('it uses PUT when "list.id" exists', async () => { + const payload = getExceptionListSchemaMock(); + const exceptionResponse = await addExceptionList({ http: mockKibanaHttpService(), list: getExceptionListSchemaMock(), signal: abortCtrl.signal, }); + expect(fetchMock).toHaveBeenCalledWith('/api/exception_lists', { - body: - '{"_tags":["endpoint","process","malware","os:linux"],"created_at":"2020-04-23T00:19:13.289Z","created_by":"user_name","description":"This is a sample endpoint type exception","id":"1","list_id":"endpoint_list","meta":{},"name":"Sample Endpoint Exception List","namespace_type":"single","tags":["user added string for a tag","malware"],"tie_breaker_id":"77fd1909-6786-428a-a671-30229a719c1f","type":"endpoint","updated_at":"2020-04-23T00:19:13.289Z","updated_by":"user_name"}', + body: JSON.stringify(payload), method: 'PUT', signal: abortCtrl.signal, }); - }); - - test('happy path', async () => { - const exceptionResponse = await addExceptionList({ - http: mockKibanaHttpService(), - list: mockNewExceptionList, - signal: abortCtrl.signal, - }); expect(exceptionResponse).toEqual(getExceptionListSchemaMock()); }); }); - describe('addExceptionListItem', () => { + describe('#addExceptionListItem', () => { beforeEach(() => { fetchMock.mockClear(); fetchMock.mockResolvedValue(getExceptionListItemSchemaMock()); }); - test('check parameter url, body', async () => { - await addExceptionListItem({ + test('it uses POST when "listItem.id" does not exist', async () => { + const payload = getCreateExceptionListItemSchemaMock(); + const exceptionResponse = await addExceptionListItem({ http: mockKibanaHttpService(), - listItem: mockNewExceptionItem, + listItem: payload, signal: abortCtrl.signal, }); + expect(fetchMock).toHaveBeenCalledWith('/api/exception_lists/items', { - body: - '{"_tags":["endpoint","process","malware","os:linux"],"description":"This is a sample endpoint type exception","entries":[{"field":"actingProcess.file.signer","match":"Elastic, N.V.","operator":"included"},{"field":"event.category","match_any":["process","malware"],"operator":"included"}],"item_id":"endpoint_list_item","list_id":"endpoint_list","name":"Sample Endpoint Exception List","tags":["user added string for a tag","malware"],"type":"simple"}', + body: JSON.stringify(payload), method: 'POST', signal: abortCtrl.signal, }); + expect(exceptionResponse).toEqual(getExceptionListItemSchemaMock()); }); test('check parameter url, body when "listItem.id" exists', async () => { - await addExceptionListItem({ + const payload = getExceptionListItemSchemaMock(); + const exceptionResponse = await addExceptionListItem({ http: mockKibanaHttpService(), listItem: getExceptionListItemSchemaMock(), signal: abortCtrl.signal, }); + expect(fetchMock).toHaveBeenCalledWith('/api/exception_lists/items', { - body: - '{"_tags":["endpoint","process","malware","os:linux"],"comment":[],"created_at":"2020-04-23T00:19:13.289Z","created_by":"user_name","description":"This is a sample endpoint type exception","entries":[{"field":"actingProcess.file.signer","match":"Elastic, N.V.","operator":"included"},{"field":"event.category","match_any":["process","malware"],"operator":"included"}],"id":"1","item_id":"endpoint_list_item","list_id":"endpoint_list","meta":{},"name":"Sample Endpoint Exception List","namespace_type":"single","tags":["user added string for a tag","malware"],"tie_breaker_id":"77fd1909-6786-428a-a671-30229a719c1f","type":"simple","updated_at":"2020-04-23T00:19:13.289Z","updated_by":"user_name"}', + body: JSON.stringify(payload), method: 'PUT', signal: abortCtrl.signal, }); - }); - - test('happy path', async () => { - const exceptionResponse = await addExceptionListItem({ - http: mockKibanaHttpService(), - listItem: mockNewExceptionItem, - signal: abortCtrl.signal, - }); expect(exceptionResponse).toEqual(getExceptionListItemSchemaMock()); }); }); - describe('fetchExceptionListById', () => { + describe('#fetchExceptionListById', () => { beforeEach(() => { fetchMock.mockClear(); fetchMock.mockResolvedValue(getExceptionListSchemaMock()); }); - test('check parameter url, body', async () => { + test('it invokes "fetchExceptionListById" with expected url and body values', async () => { await fetchExceptionListById({ http: mockKibanaHttpService(), id: '1', + namespaceType: 'single', signal: abortCtrl.signal, }); expect(fetchMock).toHaveBeenCalledWith('/api/exception_lists', { method: 'GET', query: { id: '1', + namespace_type: 'single', }, signal: abortCtrl.signal, }); }); - test('happy path', async () => { + test('it returns expected exception list on success', async () => { const exceptionResponse = await fetchExceptionListById({ http: mockKibanaHttpService(), id: '1', + namespaceType: 'single', signal: abortCtrl.signal, }); expect(exceptionResponse).toEqual(getExceptionListSchemaMock()); }); }); - describe('fetchExceptionListItemsByListId', () => { + describe('#fetchExceptionListItemsByListId', () => { beforeEach(() => { fetchMock.mockClear(); - fetchMock.mockResolvedValue([mockNewExceptionItem]); + fetchMock.mockResolvedValue([getExceptionListItemSchemaMock()]); }); - test('check parameter url, body', async () => { + test('it invokes "fetchExceptionListItemsByListId" with expected url and body values', async () => { await fetchExceptionListItemsByListId({ http: mockKibanaHttpService(), - listId: 'endpoint_list', + listId: 'myList', + namespaceType: 'single', + signal: abortCtrl.signal, + }); + + expect(fetchMock).toHaveBeenCalledWith('/api/exception_lists/items/_find', { + method: 'GET', + query: { + list_id: 'myList', + namespace_type: 'single', + page: 1, + per_page: 20, + }, + signal: abortCtrl.signal, + }); + }); + + test('it invokes with expected url and body values when a filter exists and "namespaceType" of "single"', async () => { + await fetchExceptionListItemsByListId({ + filterOptions: { + filter: 'hello world', + tags: [], + }, + http: mockKibanaHttpService(), + listId: 'myList', + namespaceType: 'single', signal: abortCtrl.signal, }); + + expect(fetchMock).toHaveBeenCalledWith('/api/exception_lists/items/_find', { + method: 'GET', + query: { + filter: 'exception-list.attributes.entries.field:hello world*', + list_id: 'myList', + namespace_type: 'single', + page: 1, + per_page: 20, + }, + signal: abortCtrl.signal, + }); + }); + + test('it invokes with expected url and body values when a filter exists and "namespaceType" of "agnostic"', async () => { + await fetchExceptionListItemsByListId({ + filterOptions: { + filter: 'hello world', + tags: [], + }, + http: mockKibanaHttpService(), + listId: 'myList', + namespaceType: 'agnostic', + signal: abortCtrl.signal, + }); + + expect(fetchMock).toHaveBeenCalledWith('/api/exception_lists/items/_find', { + method: 'GET', + query: { + filter: 'exception-list-agnostic.attributes.entries.field:hello world*', + list_id: 'myList', + namespace_type: 'agnostic', + page: 1, + per_page: 20, + }, + signal: abortCtrl.signal, + }); + }); + + test('it invokes with expected url and body values when tags exists', async () => { + await fetchExceptionListItemsByListId({ + filterOptions: { + filter: '', + tags: ['malware'], + }, + http: mockKibanaHttpService(), + listId: 'myList', + namespaceType: 'agnostic', + signal: abortCtrl.signal, + }); + + expect(fetchMock).toHaveBeenCalledWith('/api/exception_lists/items/_find', { + method: 'GET', + query: { + filter: 'exception-list-agnostic.attributes.tags:malware', + list_id: 'myList', + namespace_type: 'agnostic', + page: 1, + per_page: 20, + }, + signal: abortCtrl.signal, + }); + }); + + test('it invokes with expected url and body values when filter and tags exists', async () => { + await fetchExceptionListItemsByListId({ + filterOptions: { + filter: 'host.name', + tags: ['malware'], + }, + http: mockKibanaHttpService(), + listId: 'myList', + namespaceType: 'agnostic', + signal: abortCtrl.signal, + }); + expect(fetchMock).toHaveBeenCalledWith('/api/exception_lists/items/_find', { method: 'GET', query: { - list_id: 'endpoint_list', + filter: + 'exception-list-agnostic.attributes.entries.field:host.name* AND exception-list-agnostic.attributes.tags:malware', + list_id: 'myList', + namespace_type: 'agnostic', + page: 1, + per_page: 20, }, signal: abortCtrl.signal, }); }); - test('happy path', async () => { + test('it returns expected format when call succeeds', async () => { const exceptionResponse = await fetchExceptionListItemsByListId({ http: mockKibanaHttpService(), listId: 'endpoint_list', + namespaceType: 'single', signal: abortCtrl.signal, }); - expect(exceptionResponse).toEqual([mockNewExceptionItem]); + expect(exceptionResponse).toEqual([getExceptionListItemSchemaMock()]); }); }); - describe('fetchExceptionListItemById', () => { + describe('#fetchExceptionListItemById', () => { beforeEach(() => { fetchMock.mockClear(); - fetchMock.mockResolvedValue([mockNewExceptionItem]); + fetchMock.mockResolvedValue([getExceptionListItemSchemaMock()]); }); - test('check parameter url, body', async () => { + test('it invokes "fetchExceptionListItemById" with expected url and body values', async () => { await fetchExceptionListItemById({ http: mockKibanaHttpService(), id: '1', + namespaceType: 'single', signal: abortCtrl.signal, }); expect(fetchMock).toHaveBeenCalledWith('/api/exception_lists/items', { method: 'GET', query: { id: '1', + namespace_type: 'single', }, signal: abortCtrl.signal, }); }); - test('happy path', async () => { + test('it returns expected format when call succeeds', async () => { const exceptionResponse = await fetchExceptionListItemById({ http: mockKibanaHttpService(), id: '1', + namespaceType: 'single', signal: abortCtrl.signal, }); - expect(exceptionResponse).toEqual([mockNewExceptionItem]); + expect(exceptionResponse).toEqual([getExceptionListItemSchemaMock()]); }); }); - describe('deleteExceptionListById', () => { + describe('#deleteExceptionListById', () => { beforeEach(() => { fetchMock.mockClear(); fetchMock.mockResolvedValue(getExceptionListSchemaMock()); @@ -228,28 +332,31 @@ describe('Exceptions Lists API', () => { await deleteExceptionListById({ http: mockKibanaHttpService(), id: '1', + namespaceType: 'single', signal: abortCtrl.signal, }); expect(fetchMock).toHaveBeenCalledWith('/api/exception_lists', { method: 'DELETE', query: { id: '1', + namespace_type: 'single', }, signal: abortCtrl.signal, }); }); - test('happy path', async () => { + test('it returns expected format when call succeeds', async () => { const exceptionResponse = await deleteExceptionListById({ http: mockKibanaHttpService(), id: '1', + namespaceType: 'single', signal: abortCtrl.signal, }); expect(exceptionResponse).toEqual(getExceptionListSchemaMock()); }); }); - describe('deleteExceptionListItemById', () => { + describe('#deleteExceptionListItemById', () => { beforeEach(() => { fetchMock.mockClear(); fetchMock.mockResolvedValue(getExceptionListItemSchemaMock()); @@ -259,21 +366,24 @@ describe('Exceptions Lists API', () => { await deleteExceptionListItemById({ http: mockKibanaHttpService(), id: '1', + namespaceType: 'single', signal: abortCtrl.signal, }); expect(fetchMock).toHaveBeenCalledWith('/api/exception_lists/items', { method: 'DELETE', query: { id: '1', + namespace_type: 'single', }, signal: abortCtrl.signal, }); }); - test('happy path', async () => { + test('it returns expected format when call succeeds', async () => { const exceptionResponse = await deleteExceptionListItemById({ http: mockKibanaHttpService(), id: '1', + namespaceType: 'single', signal: abortCtrl.signal, }); expect(exceptionResponse).toEqual(getExceptionListItemSchemaMock()); diff --git a/x-pack/plugins/lists/public/exceptions/api.ts b/x-pack/plugins/lists/public/exceptions/api.ts index fdd9d62539e06e..6968ba5f50e727 100644 --- a/x-pack/plugins/lists/public/exceptions/api.ts +++ b/x-pack/plugins/lists/public/exceptions/api.ts @@ -4,7 +4,12 @@ * you may not use this file except in compliance with the Elastic License. */ -import { EXCEPTION_LIST_ITEM_URL, EXCEPTION_LIST_URL } from '../../common/constants'; +import { + EXCEPTION_LIST_ITEM_URL, + EXCEPTION_LIST_NAMESPACE, + EXCEPTION_LIST_NAMESPACE_AGNOSTIC, + EXCEPTION_LIST_URL, +} from '../../common/constants'; import { ExceptionListItemSchema, ExceptionListSchema, @@ -21,6 +26,7 @@ import { /** * Add provided ExceptionList * + * @param http Kibana http service * @param list exception list to add * @param signal to cancel request * @@ -43,6 +49,7 @@ export const addExceptionList = async ({ /** * Add provided ExceptionListItem * + * @param http Kibana http service * @param listItem exception list item to add * @param signal to cancel request * @@ -65,7 +72,9 @@ export const addExceptionListItem = async ({ /** * Fetch an ExceptionList by providing a ExceptionList ID * + * @param http Kibana http service * @param id ExceptionList ID (not list_id) + * @param namespaceType ExceptionList namespace_type * @param signal to cancel request * * @throws An error if response is not OK @@ -73,18 +82,23 @@ export const addExceptionListItem = async ({ export const fetchExceptionListById = async ({ http, id, + namespaceType, signal, }: ApiCallByIdProps): Promise => http.fetch(`${EXCEPTION_LIST_URL}`, { method: 'GET', - query: { id }, + query: { id, namespace_type: namespaceType }, signal, }); /** * Fetch an ExceptionList's ExceptionItems by providing a ExceptionList list_id * - * @param id ExceptionList list_id (not ID) + * @param http Kibana http service + * @param listId ExceptionList list_id (not ID) + * @param namespaceType ExceptionList namespace_type + * @param filterOptions optional - filter by field or tags + * @param pagination optional * @param signal to cancel request * * @throws An error if response is not OK @@ -92,18 +106,48 @@ export const fetchExceptionListById = async ({ export const fetchExceptionListItemsByListId = async ({ http, listId, + namespaceType, + filterOptions = { + filter: '', + tags: [], + }, + pagination = { + page: 1, + perPage: 20, + total: 0, + }, signal, -}: ApiCallByListIdProps): Promise => - http.fetch(`${EXCEPTION_LIST_ITEM_URL}/_find`, { +}: ApiCallByListIdProps): Promise => { + const namespace = + namespaceType === 'agnostic' ? EXCEPTION_LIST_NAMESPACE_AGNOSTIC : EXCEPTION_LIST_NAMESPACE; + const filters = [ + ...(filterOptions.filter.length + ? [`${namespace}.attributes.entries.field:${filterOptions.filter}*`] + : []), + ...(filterOptions.tags?.map((t) => `${namespace}.attributes.tags:${t}`) ?? []), + ]; + + const query = { + list_id: listId, + namespace_type: namespaceType, + page: pagination.page, + per_page: pagination.perPage, + ...(filters.length ? { filter: filters.join(' AND ') } : {}), + }; + + return http.fetch(`${EXCEPTION_LIST_ITEM_URL}/_find`, { method: 'GET', - query: { list_id: listId }, + query, signal, }); +}; /** * Fetch an ExceptionListItem by providing a ExceptionListItem ID * + * @param http Kibana http service * @param id ExceptionListItem ID (not item_id) + * @param namespaceType ExceptionList namespace_type * @param signal to cancel request * * @throws An error if response is not OK @@ -111,18 +155,21 @@ export const fetchExceptionListItemsByListId = async ({ export const fetchExceptionListItemById = async ({ http, id, + namespaceType, signal, }: ApiCallByIdProps): Promise => http.fetch(`${EXCEPTION_LIST_ITEM_URL}`, { method: 'GET', - query: { id }, + query: { id, namespace_type: namespaceType }, signal, }); /** * Delete an ExceptionList by providing a ExceptionList ID * + * @param http Kibana http service * @param id ExceptionList ID (not list_id) + * @param namespaceType ExceptionList namespace_type * @param signal to cancel request * * @throws An error if response is not OK @@ -130,18 +177,21 @@ export const fetchExceptionListItemById = async ({ export const deleteExceptionListById = async ({ http, id, + namespaceType, signal, }: ApiCallByIdProps): Promise => http.fetch(`${EXCEPTION_LIST_URL}`, { method: 'DELETE', - query: { id }, + query: { id, namespace_type: namespaceType }, signal, }); /** * Delete an ExceptionListItem by providing a ExceptionListItem ID * + * @param http Kibana http service * @param id ExceptionListItem ID (not item_id) + * @param namespaceType ExceptionList namespace_type * @param signal to cancel request * * @throws An error if response is not OK @@ -149,10 +199,11 @@ export const deleteExceptionListById = async ({ export const deleteExceptionListItemById = async ({ http, id, + namespaceType, signal, }: ApiCallByIdProps): Promise => http.fetch(`${EXCEPTION_LIST_ITEM_URL}`, { method: 'DELETE', - query: { id }, + query: { id, namespace_type: namespaceType }, signal, }); diff --git a/x-pack/plugins/lists/public/exceptions/hooks/persist_exception_item.test.tsx b/x-pack/plugins/lists/public/exceptions/hooks/persist_exception_item.test.tsx index b78ad250b8910e..1db18168b11fe9 100644 --- a/x-pack/plugins/lists/public/exceptions/hooks/persist_exception_item.test.tsx +++ b/x-pack/plugins/lists/public/exceptions/hooks/persist_exception_item.test.tsx @@ -6,8 +6,10 @@ import { act, renderHook } from '@testing-library/react-hooks'; +import * as api from '../api'; import { getExceptionListItemSchemaMock } from '../../../common/schemas/response/exception_list_item_schema.mock'; import { createKibanaCoreStartMock } from '../../common/mocks/kibana_core'; +import { PersistHookProps } from '../types'; import { ReturnPersistExceptionItem, usePersistExceptionItem } from './persist_exception_item'; @@ -16,38 +18,66 @@ jest.mock('../api'); const mockKibanaHttpService = createKibanaCoreStartMock().http; describe('usePersistExceptionItem', () => { - test('init', async () => { - const onError = jest.fn(); - const { result } = renderHook(() => + const onError = jest.fn(); + + afterEach(() => { + jest.clearAllMocks(); + }); + + test('initializes hook', async () => { + const { result } = renderHook(() => usePersistExceptionItem({ http: mockKibanaHttpService, onError }) ); expect(result.current).toEqual([{ isLoading: false, isSaved: false }, result.current[1]]); }); - test('saving exception item with isLoading === true', async () => { + test('"isLoading" is "true" when exception item is being saved', async () => { await act(async () => { - const onError = jest.fn(); - const { result, rerender, waitForNextUpdate } = renderHook( - () => usePersistExceptionItem({ http: mockKibanaHttpService, onError }) - ); + const { result, rerender, waitForNextUpdate } = renderHook< + PersistHookProps, + ReturnPersistExceptionItem + >(() => usePersistExceptionItem({ http: mockKibanaHttpService, onError })); + await waitForNextUpdate(); result.current[1](getExceptionListItemSchemaMock()); rerender(); + expect(result.current).toEqual([{ isLoading: true, isSaved: false }, result.current[1]]); }); }); - test('saved exception item with isSaved === true', async () => { - const onError = jest.fn(); + test('"isSaved" is "true" when exception item saved successfully', async () => { await act(async () => { - const { result, waitForNextUpdate } = renderHook(() => - usePersistExceptionItem({ http: mockKibanaHttpService, onError }) - ); + const { result, waitForNextUpdate } = renderHook< + PersistHookProps, + ReturnPersistExceptionItem + >(() => usePersistExceptionItem({ http: mockKibanaHttpService, onError })); + await waitForNextUpdate(); result.current[1](getExceptionListItemSchemaMock()); await waitForNextUpdate(); + expect(result.current).toEqual([{ isLoading: false, isSaved: true }, result.current[1]]); }); }); + + test('"onError" callback is invoked and "isSaved" is "false" when api call fails', async () => { + const error = new Error('persist rule failed'); + jest.spyOn(api, 'addExceptionListItem').mockRejectedValue(error); + + await act(async () => { + const { result, waitForNextUpdate } = renderHook< + PersistHookProps, + ReturnPersistExceptionItem + >(() => usePersistExceptionItem({ http: mockKibanaHttpService, onError })); + + await waitForNextUpdate(); + result.current[1](getExceptionListItemSchemaMock()); + await waitForNextUpdate(); + + expect(result.current).toEqual([{ isLoading: false, isSaved: false }, result.current[1]]); + expect(onError).toHaveBeenCalledWith(error); + }); + }); }); diff --git a/x-pack/plugins/lists/public/exceptions/hooks/persist_exception_item.tsx b/x-pack/plugins/lists/public/exceptions/hooks/persist_exception_item.tsx index 0ed007e8050132..d9fe3a82ac1774 100644 --- a/x-pack/plugins/lists/public/exceptions/hooks/persist_exception_item.tsx +++ b/x-pack/plugins/lists/public/exceptions/hooks/persist_exception_item.tsx @@ -19,6 +19,13 @@ export type ReturnPersistExceptionItem = [ Dispatch ]; +/** + * Hook for creating or updating ExceptionListItem + * + * @param http Kibana http service + * @param onError error callback + * + */ export const usePersistExceptionItem = ({ http, onError, diff --git a/x-pack/plugins/lists/public/exceptions/hooks/persist_exception_list.test.tsx b/x-pack/plugins/lists/public/exceptions/hooks/persist_exception_list.test.tsx index 605dd635aa4f5e..80d6e27043c996 100644 --- a/x-pack/plugins/lists/public/exceptions/hooks/persist_exception_list.test.tsx +++ b/x-pack/plugins/lists/public/exceptions/hooks/persist_exception_list.test.tsx @@ -6,8 +6,10 @@ import { act, renderHook } from '@testing-library/react-hooks'; +import * as api from '../api'; import { getExceptionListSchemaMock } from '../../../common/schemas/response/exception_list_schema.mock'; import { createKibanaCoreStartMock } from '../../common/mocks/kibana_core'; +import { PersistHookProps } from '../types'; import { ReturnPersistExceptionList, usePersistExceptionList } from './persist_exception_list'; @@ -16,38 +18,63 @@ jest.mock('../api'); const mockKibanaHttpService = createKibanaCoreStartMock().http; describe('usePersistExceptionList', () => { - test('init', async () => { - const onError = jest.fn(); - const { result } = renderHook(() => + const onError = jest.fn(); + + afterEach(() => { + jest.clearAllMocks(); + }); + + test('initializes hook', async () => { + const { result } = renderHook(() => usePersistExceptionList({ http: mockKibanaHttpService, onError }) ); expect(result.current).toEqual([{ isLoading: false, isSaved: false }, result.current[1]]); }); - test('saving exception list with isLoading === true', async () => { - const onError = jest.fn(); + test('"isLoading" is "true" when exception item is being saved', async () => { await act(async () => { - const { result, rerender, waitForNextUpdate } = renderHook( - () => usePersistExceptionList({ http: mockKibanaHttpService, onError }) - ); + const { result, rerender, waitForNextUpdate } = renderHook< + PersistHookProps, + ReturnPersistExceptionList + >(() => usePersistExceptionList({ http: mockKibanaHttpService, onError })); await waitForNextUpdate(); result.current[1](getExceptionListSchemaMock()); rerender(); + expect(result.current).toEqual([{ isLoading: true, isSaved: false }, result.current[1]]); }); }); - test('saved exception list with isSaved === true', async () => { - const onError = jest.fn(); + test('"isSaved" is "true" when exception item saved successfully', async () => { await act(async () => { - const { result, waitForNextUpdate } = renderHook(() => - usePersistExceptionList({ http: mockKibanaHttpService, onError }) - ); + const { result, waitForNextUpdate } = renderHook< + PersistHookProps, + ReturnPersistExceptionList + >(() => usePersistExceptionList({ http: mockKibanaHttpService, onError })); await waitForNextUpdate(); result.current[1](getExceptionListSchemaMock()); await waitForNextUpdate(); + expect(result.current).toEqual([{ isLoading: false, isSaved: true }, result.current[1]]); }); }); + + test('"onError" callback is invoked and "isSaved" is "false" when api call fails', async () => { + const error = new Error('persist rule failed'); + jest.spyOn(api, 'addExceptionList').mockRejectedValue(error); + + await act(async () => { + const { result, waitForNextUpdate } = renderHook< + PersistHookProps, + ReturnPersistExceptionList + >(() => usePersistExceptionList({ http: mockKibanaHttpService, onError })); + await waitForNextUpdate(); + result.current[1](getExceptionListSchemaMock()); + await waitForNextUpdate(); + + expect(result.current).toEqual([{ isLoading: false, isSaved: false }, result.current[1]]); + expect(onError).toHaveBeenCalledWith(error); + }); + }); }); diff --git a/x-pack/plugins/lists/public/exceptions/hooks/persist_exception_list.tsx b/x-pack/plugins/lists/public/exceptions/hooks/persist_exception_list.tsx index 45330c9725ae7b..5848a171451946 100644 --- a/x-pack/plugins/lists/public/exceptions/hooks/persist_exception_list.tsx +++ b/x-pack/plugins/lists/public/exceptions/hooks/persist_exception_list.tsx @@ -19,6 +19,13 @@ export type ReturnPersistExceptionList = [ Dispatch ]; +/** + * Hook for creating or updating ExceptionList + * + * @param http Kibana http service + * @param onError error callback + * + */ export const usePersistExceptionList = ({ http, onError, diff --git a/x-pack/plugins/lists/public/exceptions/hooks/use_exception_list.test.tsx b/x-pack/plugins/lists/public/exceptions/hooks/use_exception_list.test.tsx index 308d1cf4d1b176..a6a25ab4d4e9d8 100644 --- a/x-pack/plugins/lists/public/exceptions/hooks/use_exception_list.test.tsx +++ b/x-pack/plugins/lists/public/exceptions/hooks/use_exception_list.test.tsx @@ -8,6 +8,9 @@ import { act, renderHook } from '@testing-library/react-hooks'; import * as api from '../api'; import { createKibanaCoreStartMock } from '../../common/mocks/kibana_core'; +import { getExceptionListSchemaMock } from '../../../common/schemas/response/exception_list_schema.mock'; +import { getExceptionListItemSchemaMock } from '../../../common/schemas/response/exception_list_item_schema.mock'; +import { ExceptionListAndItems, UseExceptionListProps } from '../types'; import { ReturnExceptionListAndItems, useExceptionList } from './use_exception_list'; @@ -16,103 +19,166 @@ jest.mock('../api'); const mockKibanaHttpService = createKibanaCoreStartMock().http; describe('useExceptionList', () => { - test('init', async () => { - const onError = jest.fn(); + const onErrorMock = jest.fn(); + + afterEach(() => { + onErrorMock.mockClear(); + jest.clearAllMocks(); + }); + + test('initializes hook', async () => { await act(async () => { - const { result, waitForNextUpdate } = renderHook(() => - useExceptionList({ http: mockKibanaHttpService, id: 'myListId', onError }) + const { result, waitForNextUpdate } = renderHook< + UseExceptionListProps, + ReturnExceptionListAndItems + >(() => + useExceptionList({ + http: mockKibanaHttpService, + id: 'myListId', + namespaceType: 'single', + onError: onErrorMock, + }) ); await waitForNextUpdate(); - expect(result.current).toEqual([true, null]); + + expect(result.current).toEqual([true, null, result.current[2]]); + expect(typeof result.current[2]).toEqual('function'); }); }); test('fetch exception list and items', async () => { - const onError = jest.fn(); await act(async () => { - const { result, waitForNextUpdate } = renderHook(() => - useExceptionList({ http: mockKibanaHttpService, id: 'myListId', onError }) + const { result, waitForNextUpdate } = renderHook< + UseExceptionListProps, + ReturnExceptionListAndItems + >(() => + useExceptionList({ + http: mockKibanaHttpService, + id: 'myListId', + namespaceType: 'single', + onError: onErrorMock, + }) ); await waitForNextUpdate(); await waitForNextUpdate(); - expect(result.current).toEqual([ - false, - { - _tags: ['endpoint', 'process', 'malware', 'os:linux'], - created_at: '2020-04-23T00:19:13.289Z', - created_by: 'user_name', - description: 'This is a sample endpoint type exception', - exceptionItems: { - data: [ - { - _tags: ['endpoint', 'process', 'malware', 'os:linux'], - comment: [], - created_at: '2020-04-23T00:19:13.289Z', - created_by: 'user_name', - description: 'This is a sample endpoint type exception', - entries: [ - { - field: 'actingProcess.file.signer', - match: 'Elastic, N.V.', - match_any: undefined, - operator: 'included', - }, - { - field: 'event.category', - match: undefined, - match_any: ['process', 'malware'], - operator: 'included', - }, - ], - id: '1', - item_id: 'endpoint_list_item', - list_id: 'endpoint_list', - meta: {}, - name: 'Sample Endpoint Exception List', - namespace_type: 'single', - tags: ['user added string for a tag', 'malware'], - tie_breaker_id: '77fd1909-6786-428a-a671-30229a719c1f', - type: 'simple', - updated_at: '2020-04-23T00:19:13.289Z', - updated_by: 'user_name', - }, - ], + + const expectedResult: ExceptionListAndItems = { + ...getExceptionListSchemaMock(), + exceptionItems: { + items: [{ ...getExceptionListItemSchemaMock() }], + pagination: { page: 1, - per_page: 20, + perPage: 20, total: 1, }, - id: '1', - list_id: 'endpoint_list', - meta: {}, - name: 'Sample Endpoint Exception List', - namespace_type: 'single', - tags: ['user added string for a tag', 'malware'], - tie_breaker_id: '77fd1909-6786-428a-a671-30229a719c1f', - type: 'endpoint', - updated_at: '2020-04-23T00:19:13.289Z', - updated_by: 'user_name', }, - ]); + }; + + expect(result.current).toEqual([false, expectedResult, result.current[2]]); }); }); test('fetch a new exception list and its items', async () => { - const onError = jest.fn(); const spyOnfetchExceptionListById = jest.spyOn(api, 'fetchExceptionListById'); const spyOnfetchExceptionListItemsByListId = jest.spyOn(api, 'fetchExceptionListItemsByListId'); await act(async () => { - const { rerender, waitForNextUpdate } = renderHook( - (id) => useExceptionList({ http: mockKibanaHttpService, id, onError }), + const { rerender, waitForNextUpdate } = renderHook< + UseExceptionListProps, + ReturnExceptionListAndItems + >( + ({ filterOptions, http, id, namespaceType, pagination, onError }) => + useExceptionList({ filterOptions, http, id, namespaceType, onError, pagination }), { - initialProps: 'myListId', + initialProps: { + http: mockKibanaHttpService, + id: 'myListId', + namespaceType: 'single', + onError: onErrorMock, + }, } ); await waitForNextUpdate(); + rerender({ + http: mockKibanaHttpService, + id: 'newListId', + namespaceType: 'single', + onError: onErrorMock, + }); + await waitForNextUpdate(); + + expect(spyOnfetchExceptionListById).toHaveBeenCalledTimes(2); + expect(spyOnfetchExceptionListItemsByListId).toHaveBeenCalledTimes(2); + }); + }); + + test('fetches list and items when refreshExceptionList callback invoked', async () => { + const spyOnfetchExceptionListById = jest.spyOn(api, 'fetchExceptionListById'); + const spyOnfetchExceptionListItemsByListId = jest.spyOn(api, 'fetchExceptionListItemsByListId'); + await act(async () => { + const { result, waitForNextUpdate } = renderHook< + UseExceptionListProps, + ReturnExceptionListAndItems + >(() => + useExceptionList({ + http: mockKibanaHttpService, + id: 'myListId', + namespaceType: 'single', + onError: onErrorMock, + }) + ); + await waitForNextUpdate(); await waitForNextUpdate(); - rerender('newListId'); + result.current[2](); await waitForNextUpdate(); + expect(spyOnfetchExceptionListById).toHaveBeenCalledTimes(2); expect(spyOnfetchExceptionListItemsByListId).toHaveBeenCalledTimes(2); }); }); + + test('invokes "onError" callback if "fetchExceptionListItemsByListId" fails', async () => { + const mockError = new Error('failed to fetch list items'); + const spyOnfetchExceptionListById = jest.spyOn(api, 'fetchExceptionListById'); + const spyOnfetchExceptionListItemsByListId = jest + .spyOn(api, 'fetchExceptionListItemsByListId') + .mockRejectedValue(mockError); + await act(async () => { + const { waitForNextUpdate } = renderHook( + () => + useExceptionList({ + http: mockKibanaHttpService, + id: 'myListId', + namespaceType: 'single', + onError: onErrorMock, + }) + ); + await waitForNextUpdate(); + await waitForNextUpdate(); + + expect(spyOnfetchExceptionListById).toHaveBeenCalledTimes(1); + expect(onErrorMock).toHaveBeenCalledWith(mockError); + expect(spyOnfetchExceptionListItemsByListId).toHaveBeenCalledTimes(1); + }); + }); + + test('invokes "onError" callback if "fetchExceptionListById" fails', async () => { + const mockError = new Error('failed to fetch list'); + jest.spyOn(api, 'fetchExceptionListById').mockRejectedValue(mockError); + + await act(async () => { + const { waitForNextUpdate } = renderHook( + () => + useExceptionList({ + http: mockKibanaHttpService, + id: 'myListId', + namespaceType: 'single', + onError: onErrorMock, + }) + ); + await waitForNextUpdate(); + await waitForNextUpdate(); + + expect(onErrorMock).toHaveBeenCalledWith(mockError); + }); + }); }); diff --git a/x-pack/plugins/lists/public/exceptions/hooks/use_exception_list.tsx b/x-pack/plugins/lists/public/exceptions/hooks/use_exception_list.tsx index d0ac357e05aa0b..116233cd893481 100644 --- a/x-pack/plugins/lists/public/exceptions/hooks/use_exception_list.tsx +++ b/x-pack/plugins/lists/public/exceptions/hooks/use_exception_list.tsx @@ -4,66 +4,124 @@ * you may not use this file except in compliance with the Elastic License. */ -import { useEffect, useState } from 'react'; +import { useCallback, useEffect, useState } from 'react'; import { fetchExceptionListById, fetchExceptionListItemsByListId } from '../api'; import { ExceptionListAndItems, UseExceptionListProps } from '../types'; -export type ReturnExceptionListAndItems = [boolean, ExceptionListAndItems | null]; +export type ReturnExceptionListAndItems = [boolean, ExceptionListAndItems | null, () => void]; /** * Hook for using to get an ExceptionList and it's ExceptionListItems * + * @param http Kibana http service * @param id desired ExceptionList ID (not list_id) + * @param namespaceType list namespaceType determines list space + * @param onError error callback + * @param filterOptions optional - filter by fields or tags + * @param pagination optional * */ export const useExceptionList = ({ http, id, + namespaceType, + pagination = { + page: 1, + perPage: 20, + total: 0, + }, + filterOptions = { + filter: '', + tags: [], + }, onError, }: UseExceptionListProps): ReturnExceptionListAndItems => { const [exceptionListAndItems, setExceptionList] = useState(null); + const [shouldRefresh, setRefresh] = useState(true); + const refreshExceptionList = useCallback(() => setRefresh(true), [setRefresh]); const [loading, setLoading] = useState(true); + const tags = filterOptions.tags.sort().join(); - useEffect(() => { - let isSubscribed = true; - const abortCtrl = new AbortController(); + useEffect( + () => { + let isSubscribed = true; + const abortCtrl = new AbortController(); - const fetchData = async (idToFetch: string): Promise => { - try { - setLoading(true); - const exceptionList = await fetchExceptionListById({ - http, - id: idToFetch, - signal: abortCtrl.signal, - }); - const exceptionListItems = await fetchExceptionListItemsByListId({ - http, - listId: exceptionList.list_id, - signal: abortCtrl.signal, - }); - if (isSubscribed) { - setExceptionList({ ...exceptionList, exceptionItems: { ...exceptionListItems } }); + const fetchData = async (idToFetch: string): Promise => { + if (shouldRefresh) { + try { + setLoading(true); + + const { + list_id, + namespace_type, + ...restOfExceptionList + } = await fetchExceptionListById({ + http, + id: idToFetch, + namespaceType, + signal: abortCtrl.signal, + }); + const fetchListItemsResult = await fetchExceptionListItemsByListId({ + filterOptions, + http, + listId: list_id, + namespaceType: namespace_type, + pagination, + signal: abortCtrl.signal, + }); + + setRefresh(false); + + if (isSubscribed) { + setExceptionList({ + list_id, + namespace_type, + ...restOfExceptionList, + exceptionItems: { + items: [...fetchListItemsResult.data], + pagination: { + page: fetchListItemsResult.page, + perPage: fetchListItemsResult.per_page, + total: fetchListItemsResult.total, + }, + }, + }); + } + } catch (error) { + setRefresh(false); + if (isSubscribed) { + setExceptionList(null); + onError(error); + } + } } - } catch (error) { + if (isSubscribed) { - setExceptionList(null); - onError(error); + setLoading(false); } - } - if (isSubscribed) { - setLoading(false); - } - }; + }; - if (id != null) { - fetchData(id); - } - return (): void => { - isSubscribed = false; - abortCtrl.abort(); - }; - }, [http, id, onError]); + if (id != null) { + fetchData(id); + } + return (): void => { + isSubscribed = false; + abortCtrl.abort(); + }; + }, // eslint-disable-next-line react-hooks/exhaustive-deps + [ + http, + id, + onError, + shouldRefresh, + pagination.page, + pagination.perPage, + filterOptions.filter, + tags, + ] + ); - return [loading, exceptionListAndItems]; + return [loading, exceptionListAndItems, refreshExceptionList]; }; diff --git a/x-pack/plugins/lists/public/exceptions/types.ts b/x-pack/plugins/lists/public/exceptions/types.ts index fcf2108e7323a9..cf6b6c3ec1c598 100644 --- a/x-pack/plugins/lists/public/exceptions/types.ts +++ b/x-pack/plugins/lists/public/exceptions/types.ts @@ -9,12 +9,28 @@ import { CreateExceptionListSchemaPartial, ExceptionListItemSchema, ExceptionListSchema, - FoundExceptionListItemSchema, + NamespaceType, } from '../../common/schemas'; import { HttpStart } from '../../../../../src/core/public'; +export interface FilterExceptionsOptions { + filter: string; + tags: string[]; +} + +export interface Pagination { + page: number; + perPage: number; + total: number; +} + +export interface ExceptionItemsAndPagination { + items: ExceptionListItemSchema[]; + pagination: Pagination; +} + export interface ExceptionListAndItems extends ExceptionListSchema { - exceptionItems: FoundExceptionListItemSchema; + exceptionItems: ExceptionItemsAndPagination; } export type AddExceptionList = ExceptionListSchema | CreateExceptionListSchemaPartial; @@ -27,20 +43,27 @@ export interface PersistHookProps { } export interface UseExceptionListProps { + filterOptions?: FilterExceptionsOptions; http: HttpStart; id: string | undefined; + namespaceType: NamespaceType; onError: (arg: Error) => void; + pagination?: Pagination; } export interface ApiCallByListIdProps { http: HttpStart; listId: string; + namespaceType: NamespaceType; + filterOptions?: FilterExceptionsOptions; + pagination?: Pagination; signal: AbortSignal; } export interface ApiCallByIdProps { http: HttpStart; id: string; + namespaceType: NamespaceType; signal: AbortSignal; } From add5b11611973d58c5ed2806f525ab8a00250830 Mon Sep 17 00:00:00 2001 From: Brian Seeders Date: Mon, 1 Jun 2020 14:41:30 -0400 Subject: [PATCH 08/24] [CI] Fix packer cache node_modules references --- .ci/packer_cache.sh | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.ci/packer_cache.sh b/.ci/packer_cache.sh index ab68a60dcfc279..d47ef93172a9d5 100755 --- a/.ci/packer_cache.sh +++ b/.ci/packer_cache.sh @@ -35,20 +35,20 @@ mkdir -p ".geckodriver" cp "node_modules/geckodriver/geckodriver.tar.gz" .geckodriver/geckodriver.tar.gz echo "$geckodriverPkgVersion" > .geckodriver/pkgVersion +echo "Creating bootstrap_cache archive" + # archive cacheable directories mkdir -p "$HOME/.kibana/bootstrap_cache" tar -cf "$HOME/.kibana/bootstrap_cache/$branch.tar" \ - node_modules \ - packages/*/node_modules \ - x-pack/node_modules \ - x-pack/legacy/plugins/*/node_modules \ x-pack/legacy/plugins/reporting/.chromium \ - test/plugin_functional/plugins/*/node_modules \ - examples/*/node_modules \ .es \ .chromedriver \ .geckodriver; +echo "Adding node_modules" +# Find all of the node_modules directories that aren't test fixtures, and aren't inside other node_modules directories, and append them to the tar +find . -type d -name node_modules -not -path '*__fixtures__*' -prune -print0 | xargs -0I % tar -rf "$HOME/.kibana/bootstrap_cache/$branch.tar" "%" + echo "created $HOME/.kibana/bootstrap_cache/$branch.tar" if [ "$branch" == "master" ]; then From b061d85f9a85d467beae8da9a0d46758d6e194eb Mon Sep 17 00:00:00 2001 From: Wylie Conlon Date: Mon, 1 Jun 2020 15:05:27 -0400 Subject: [PATCH 09/24] [Lens] Warn if leaving with unsaved visualization (#67689) * [Lens] Warn if leaving with unsaved visualization * Made confirmation logic more robust and add title Co-authored-by: Elastic Machine --- .../lens/public/app_plugin/app.test.tsx | 216 +++++++++++++----- x-pack/plugins/lens/public/app_plugin/app.tsx | 57 ++++- .../lens/public/app_plugin/mounter.tsx | 1 + 3 files changed, 208 insertions(+), 66 deletions(-) diff --git a/x-pack/plugins/lens/public/app_plugin/app.test.tsx b/x-pack/plugins/lens/public/app_plugin/app.test.tsx index 17629654782923..f1a2edd2d554f7 100644 --- a/x-pack/plugins/lens/public/app_plugin/app.test.tsx +++ b/x-pack/plugins/lens/public/app_plugin/app.test.tsx @@ -9,6 +9,7 @@ import { ReactWrapper } from 'enzyme'; import { act } from 'react-dom/test-utils'; import { App } from './app'; import { EditorFrameInstance } from '../types'; +import { AppMountParameters } from 'kibana/public'; import { Storage } from '../../../../../src/plugins/kibana_utils/public'; import { Document, SavedObjectStore } from '../persistence'; import { mount } from 'enzyme'; @@ -111,6 +112,7 @@ describe('Lens App', () => { newlyCreated?: boolean ) => void; originatingApp: string | undefined; + onAppLeave: AppMountParameters['onAppLeave']; }> { return ({ navigation: navigationStartMock, @@ -153,6 +155,7 @@ describe('Lens App', () => { newlyCreated?: boolean ) => {} ), + onAppLeave: jest.fn(), } as unknown) as jest.Mocked<{ navigation: typeof navigationStartMock; editorFrame: EditorFrameInstance; @@ -168,6 +171,7 @@ describe('Lens App', () => { newlyCreated?: boolean ) => void; originatingApp: string | undefined; + onAppLeave: AppMountParameters['onAppLeave']; }>; } @@ -357,22 +361,7 @@ describe('Lens App', () => { newTitle: string; } - let defaultArgs: jest.Mocked<{ - editorFrame: EditorFrameInstance; - navigation: typeof navigationStartMock; - data: typeof dataStartMock; - core: typeof core; - storage: Storage; - docId?: string; - docStorage: SavedObjectStore; - redirectTo: ( - id?: string, - returnToOrigin?: boolean, - originatingApp?: string | undefined, - newlyCreated?: boolean - ) => void; - originatingApp: string | undefined; - }>; + let defaultArgs: ReturnType; beforeEach(() => { defaultArgs = makeDefaultArgs(); @@ -486,30 +475,6 @@ describe('Lens App', () => { expect(getButton(instance).disableButton).toEqual(true); }); - it('shows a disabled save button when there are no changes to the document', async () => { - const args = defaultArgs; - (args.docStorage.load as jest.Mock).mockResolvedValue({ - id: '1234', - title: 'My cool doc', - expression: '', - } as jest.ResolvedValue); - args.editorFrame = frame; - - instance = mount(); - expect(getButton(instance).disableButton).toEqual(true); - - const onChange = frame.mount.mock.calls[0][1].onChange; - - act(() => { - onChange({ - filterableIndexPatterns: [], - doc: ({ id: '1234', expression: 'valid expression' } as unknown) as Document, - }); - }); - instance.update(); - expect(getButton(instance).disableButton).toEqual(false); - }); - it('shows a save button that is enabled when the frame has provided its state', async () => { const args = defaultArgs; args.editorFrame = frame; @@ -691,21 +656,7 @@ describe('Lens App', () => { }); describe('query bar state management', () => { - let defaultArgs: jest.Mocked<{ - editorFrame: EditorFrameInstance; - data: typeof dataStartMock; - navigation: typeof navigationStartMock; - core: typeof core; - storage: Storage; - docId?: string; - docStorage: SavedObjectStore; - redirectTo: ( - id?: string, - returnToOrigin?: boolean, - originatingApp?: string | undefined, - newlyCreated?: boolean - ) => void; - }>; + let defaultArgs: ReturnType; beforeEach(() => { defaultArgs = makeDefaultArgs(); @@ -1001,4 +952,159 @@ describe('Lens App', () => { expect(args.core.notifications.toasts.addDanger).toHaveBeenCalled(); }); + + describe('showing a confirm message when leaving', () => { + let defaultArgs: ReturnType; + let defaultLeave: jest.Mock; + let confirmLeave: jest.Mock; + + beforeEach(() => { + defaultArgs = makeDefaultArgs(); + defaultLeave = jest.fn(); + confirmLeave = jest.fn(); + (defaultArgs.docStorage.load as jest.Mock).mockResolvedValue({ + id: '1234', + title: 'My cool doc', + expression: 'valid expression', + state: { + query: 'kuery', + datasourceMetaData: { filterableIndexPatterns: [{ id: '1', title: 'saved' }] }, + }, + } as jest.ResolvedValue); + }); + + it('should not show a confirm message if there is no expression to save', () => { + instance = mount(); + + const lastCall = + defaultArgs.onAppLeave.mock.calls[defaultArgs.onAppLeave.mock.calls.length - 1][0]; + lastCall({ default: defaultLeave, confirm: confirmLeave }); + + expect(defaultLeave).toHaveBeenCalled(); + expect(confirmLeave).not.toHaveBeenCalled(); + }); + + it('does not confirm if the user is missing save permissions', () => { + const args = defaultArgs; + args.core.application = { + ...args.core.application, + capabilities: { + ...args.core.application.capabilities, + visualize: { save: false, saveQuery: false, show: true }, + }, + }; + args.editorFrame = frame; + + instance = mount(); + + const onChange = frame.mount.mock.calls[0][1].onChange; + act(() => + onChange({ + filterableIndexPatterns: [], + doc: ({ id: undefined, expression: 'valid expression' } as unknown) as Document, + }) + ); + instance.update(); + + const lastCall = + defaultArgs.onAppLeave.mock.calls[defaultArgs.onAppLeave.mock.calls.length - 1][0]; + lastCall({ default: defaultLeave, confirm: confirmLeave }); + + expect(defaultLeave).toHaveBeenCalled(); + expect(confirmLeave).not.toHaveBeenCalled(); + }); + + it('should confirm when leaving with an unsaved doc', () => { + defaultArgs.editorFrame = frame; + instance = mount(); + + const onChange = frame.mount.mock.calls[0][1].onChange; + act(() => + onChange({ + filterableIndexPatterns: [], + doc: ({ id: undefined, expression: 'valid expression' } as unknown) as Document, + }) + ); + instance.update(); + + const lastCall = + defaultArgs.onAppLeave.mock.calls[defaultArgs.onAppLeave.mock.calls.length - 1][0]; + lastCall({ default: defaultLeave, confirm: confirmLeave }); + + expect(confirmLeave).toHaveBeenCalled(); + expect(defaultLeave).not.toHaveBeenCalled(); + }); + + it('should confirm when leaving with unsaved changes to an existing doc', async () => { + defaultArgs.editorFrame = frame; + instance = mount(); + await act(async () => { + instance.setProps({ docId: '1234' }); + }); + + const onChange = frame.mount.mock.calls[0][1].onChange; + act(() => + onChange({ + filterableIndexPatterns: [], + doc: ({ id: '1234', expression: 'different expression' } as unknown) as Document, + }) + ); + instance.update(); + + const lastCall = + defaultArgs.onAppLeave.mock.calls[defaultArgs.onAppLeave.mock.calls.length - 1][0]; + lastCall({ default: defaultLeave, confirm: confirmLeave }); + + expect(confirmLeave).toHaveBeenCalled(); + expect(defaultLeave).not.toHaveBeenCalled(); + }); + + it('should not confirm when changes are saved', async () => { + defaultArgs.editorFrame = frame; + instance = mount(); + await act(async () => { + instance.setProps({ docId: '1234' }); + }); + + const onChange = frame.mount.mock.calls[0][1].onChange; + act(() => + onChange({ + filterableIndexPatterns: [], + doc: ({ id: '1234', expression: 'valid expression' } as unknown) as Document, + }) + ); + instance.update(); + + const lastCall = + defaultArgs.onAppLeave.mock.calls[defaultArgs.onAppLeave.mock.calls.length - 1][0]; + lastCall({ default: defaultLeave, confirm: confirmLeave }); + + expect(defaultLeave).toHaveBeenCalled(); + expect(confirmLeave).not.toHaveBeenCalled(); + }); + + it('should confirm when the latest doc is invalid', async () => { + defaultArgs.editorFrame = frame; + instance = mount(); + await act(async () => { + instance.setProps({ docId: '1234' }); + }); + + const onChange = frame.mount.mock.calls[0][1].onChange; + act(() => + onChange({ + filterableIndexPatterns: [], + doc: ({ id: '1234', expression: null } as unknown) as Document, + }) + ); + instance.update(); + + const lastCall = + defaultArgs.onAppLeave.mock.calls[defaultArgs.onAppLeave.mock.calls.length - 1][0]; + lastCall({ default: defaultLeave, confirm: confirmLeave }); + + expect(confirmLeave).toHaveBeenCalled(); + expect(defaultLeave).not.toHaveBeenCalled(); + }); + }); }); diff --git a/x-pack/plugins/lens/public/app_plugin/app.tsx b/x-pack/plugins/lens/public/app_plugin/app.tsx index a77fbbb597564b..ffa59a6fb6bc98 100644 --- a/x-pack/plugins/lens/public/app_plugin/app.tsx +++ b/x-pack/plugins/lens/public/app_plugin/app.tsx @@ -10,7 +10,7 @@ import { I18nProvider } from '@kbn/i18n/react'; import { i18n } from '@kbn/i18n'; import { Query, DataPublicPluginStart } from 'src/plugins/data/public'; import { NavigationPublicPluginStart } from 'src/plugins/navigation/public'; -import { AppMountContext, NotificationsStart } from 'kibana/public'; +import { AppMountContext, AppMountParameters, NotificationsStart } from 'kibana/public'; import { IStorageWrapper } from 'src/plugins/kibana_utils/public'; import { KibanaContextProvider } from '../../../../../src/plugins/kibana_react/public'; import { @@ -57,6 +57,7 @@ export function App({ redirectTo, originatingAppFromUrl, navigation, + onAppLeave, }: { editorFrame: EditorFrameInstance; data: DataPublicPluginStart; @@ -72,6 +73,7 @@ export function App({ newlyCreated?: boolean ) => void; originatingAppFromUrl?: string | undefined; + onAppLeave: AppMountParameters['onAppLeave']; }) { const language = storage.get('kibana.userQueryLanguage') || core.uiSettings.get('search:queryLanguage'); @@ -94,6 +96,12 @@ export function App({ const { lastKnownDoc } = state; + const isSaveable = + lastKnownDoc && + lastKnownDoc.expression && + lastKnownDoc.expression.length > 0 && + core.application.capabilities.visualize.save; + useEffect(() => { // Clear app-specific filters when navigating to Lens. Necessary because Lens // can be loaded without a full page refresh @@ -123,7 +131,31 @@ export function App({ filterSubscription.unsubscribe(); timeSubscription.unsubscribe(); }; - }, []); + }, [data.query.filterManager, data.query.timefilter.timefilter]); + + useEffect(() => { + onAppLeave((actions) => { + // Confirm when the user has made any changes to an existing doc + // or when the user has configured something without saving + if ( + core.application.capabilities.visualize.save && + (state.persistedDoc?.expression + ? !_.isEqual(lastKnownDoc?.expression, state.persistedDoc.expression) + : lastKnownDoc?.expression) + ) { + return actions.confirm( + i18n.translate('xpack.lens.app.unsavedWorkMessage', { + defaultMessage: 'Leave Lens with unsaved work?', + }), + i18n.translate('xpack.lens.app.unsavedWorkTitle', { + defaultMessage: 'Unsaved changes', + }) + ); + } else { + return actions.default(); + } + }); + }, [lastKnownDoc, onAppLeave, state.persistedDoc, core.application.capabilities.visualize.save]); // Sync Kibana breadcrumbs any time the saved document's title changes useEffect(() => { @@ -144,7 +176,7 @@ export function App({ : i18n.translate('xpack.lens.breadcrumbsCreate', { defaultMessage: 'Create' }), }, ]); - }, [state.persistedDoc && state.persistedDoc.title]); + }, [core.application, core.chrome, core.http.basePath, state.persistedDoc]); useEffect(() => { if (docId && (!state.persistedDoc || state.persistedDoc.id !== docId)) { @@ -187,13 +219,16 @@ export function App({ redirectTo(); }); } - }, [docId]); - - const isSaveable = - lastKnownDoc && - lastKnownDoc.expression && - lastKnownDoc.expression.length > 0 && - core.application.capabilities.visualize.save; + }, [ + core.notifications, + data.indexPatterns, + data.query.filterManager, + docId, + // TODO: These dependencies are changing too often + // docStorage, + // redirectTo, + // state.persistedDoc, + ]); const runSave = ( saveProps: Omit & { @@ -257,7 +292,7 @@ export function App({ core.notifications.toasts.addDanger({ title: e.message, }), - [] + [core.notifications.toasts] ); const { TopNavMenu } = navigation.ui; diff --git a/x-pack/plugins/lens/public/app_plugin/mounter.tsx b/x-pack/plugins/lens/public/app_plugin/mounter.tsx index 7c875935f6320e..032ce8325dca1f 100644 --- a/x-pack/plugins/lens/public/app_plugin/mounter.tsx +++ b/x-pack/plugins/lens/public/app_plugin/mounter.tsx @@ -92,6 +92,7 @@ export async function mountApp( redirectTo(routeProps, id, returnToOrigin, originatingApp, newlyCreated) } originatingAppFromUrl={originatingAppFromUrl} + onAppLeave={params.onAppLeave} /> ); }; From 071f7ef20be94bf8f36007eddc5aa62d42b594df Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Mon, 1 Jun 2020 22:57:44 +0100 Subject: [PATCH 10/24] skip flaky suite (#67821) --- x-pack/test/accessibility/apps/search_profiler.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x-pack/test/accessibility/apps/search_profiler.ts b/x-pack/test/accessibility/apps/search_profiler.ts index 8a13940695f9e1..138231d3cf025d 100644 --- a/x-pack/test/accessibility/apps/search_profiler.ts +++ b/x-pack/test/accessibility/apps/search_profiler.ts @@ -14,7 +14,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const a11y = getService('a11y'); const flyout = getService('flyout'); - describe('Accessibility Search Profiler Editor', () => { + // FLAKY: https://github.com/elastic/kibana/issues/67821 + describe.skip('Accessibility Search Profiler Editor', () => { before(async () => { await PageObjects.common.navigateToApp('searchProfiler'); await a11y.testAppSnapshot(); From 571b3de667b1da2281ad49cbfec948869f7df86e Mon Sep 17 00:00:00 2001 From: Lisa Cawley Date: Mon, 1 Jun 2020 15:25:20 -0700 Subject: [PATCH 11/24] [DOCS] Replace docdir attribute with kib-repo-dir (#67907) --- docs/index.asciidoc | 6 +++--- docs/setup/settings.asciidoc | 26 +++++++++++++------------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/docs/index.asciidoc b/docs/index.asciidoc index 5474772ab7da81..add91600a34ea9 100644 --- a/docs/index.asciidoc +++ b/docs/index.asciidoc @@ -3,11 +3,11 @@ :include-xpack: true :lang: en -:kib-repo-dir: {docdir} +:kib-repo-dir: {kibana-root}/docs :blog-ref: https://www.elastic.co/blog/ :wikipedia: https://en.wikipedia.org/wiki -include::{asciidoc-dir}/../../shared/versions/stack/{source_branch}.asciidoc[] +include::{docs-root}/shared/versions/stack/{source_branch}.asciidoc[] :docker-repo: docker.elastic.co/kibana/kibana :docker-image: docker.elastic.co/kibana/kibana:{version} @@ -18,7 +18,7 @@ include::{asciidoc-dir}/../../shared/versions/stack/{source_branch}.asciidoc[] :blob: {repo}blob/{branch}/ :security-ref: https://www.elastic.co/community/security/ -include::{asciidoc-dir}/../../shared/attributes.asciidoc[] +include::{docs-root}/shared/attributes.asciidoc[] include::user/index.asciidoc[] diff --git a/docs/setup/settings.asciidoc b/docs/setup/settings.asciidoc index 6596f93a88f515..42d616c80119bf 100644 --- a/docs/setup/settings.asciidoc +++ b/docs/setup/settings.asciidoc @@ -627,17 +627,17 @@ Valid locales are: `en`, `zh-CN`, `ja-JP`. *Default: `en`* |=== -include::{docdir}/settings/alert-action-settings.asciidoc[] -include::{docdir}/settings/apm-settings.asciidoc[] -include::{docdir}/settings/dev-settings.asciidoc[] -include::{docdir}/settings/graph-settings.asciidoc[] -include::{docdir}/settings/infrastructure-ui-settings.asciidoc[] -include::{docdir}/settings/i18n-settings.asciidoc[] -include::{docdir}/settings/logs-ui-settings.asciidoc[] -include::{docdir}/settings/ml-settings.asciidoc[] -include::{docdir}/settings/monitoring-settings.asciidoc[] -include::{docdir}/settings/reporting-settings.asciidoc[] +include::{kib-repo-dir}/settings/alert-action-settings.asciidoc[] +include::{kib-repo-dir}/settings/apm-settings.asciidoc[] +include::{kib-repo-dir}/settings/dev-settings.asciidoc[] +include::{kib-repo-dir}/settings/graph-settings.asciidoc[] +include::{kib-repo-dir}/settings/infrastructure-ui-settings.asciidoc[] +include::{kib-repo-dir}/settings/i18n-settings.asciidoc[] +include::{kib-repo-dir}/settings/logs-ui-settings.asciidoc[] +include::{kib-repo-dir}/settings/ml-settings.asciidoc[] +include::{kib-repo-dir}/settings/monitoring-settings.asciidoc[] +include::{kib-repo-dir}/settings/reporting-settings.asciidoc[] include::secure-settings.asciidoc[] -include::{docdir}/settings/security-settings.asciidoc[] -include::{docdir}/settings/spaces-settings.asciidoc[] -include::{docdir}/settings/telemetry-settings.asciidoc[] +include::{kib-repo-dir}/settings/security-settings.asciidoc[] +include::{kib-repo-dir}/settings/spaces-settings.asciidoc[] +include::{kib-repo-dir}/settings/telemetry-settings.asciidoc[] From 0dca28b6dd0c6a3c5ef51e5c9578a5f1809c86a8 Mon Sep 17 00:00:00 2001 From: Frank Hassanabad Date: Mon, 1 Jun 2020 16:30:28 -0600 Subject: [PATCH 12/24] [SEIM][Detection Engine] Moves the io-ts schemas to the common folder from the server side ## Summary This moves the io-ts schemas from the common folder from the server side up to the common folder. ### Checklist - [x] [Unit or functional tests](https://github.com/elastic/kibana/blob/master/CONTRIBUTING.md#cross-browser-compatibility) were updated or added to match the most common scenarios --- .../schemas/common}/schemas.ts | 0 .../schemas/response/__mocks__/utils.ts | 0 .../schemas/response/error_schema.test.ts | 13 +- .../schemas/response/error_schema.ts | 2 +- .../response/find_rules_schema.test.ts | 13 +- .../schemas/response/find_rules_schema.ts | 2 +- .../response/import_rules_schema.test.ts | 13 +- .../schemas/response/import_rules_schema.ts | 2 +- .../response/prepackaged_rules_schema.test.ts | 13 +- .../response/prepackaged_rules_schema.ts | 2 +- .../prepackaged_rules_status_schema.test.ts | 13 +- .../prepackaged_rules_status_schema.ts | 2 +- .../response/rules_bulk_schema.test.ts | 13 +- .../schemas/response/rules_bulk_schema.ts | 0 .../schemas/response/rules_schema.test.ts} | 201 +++++++++++- .../schemas/response/rules_schema.ts | 110 +++++-- .../type_timeline_only_schema.test.ts | 13 +- .../response/type_timeline_only_schema.ts | 2 +- .../schemas/types/iso_date_string.test.ts | 2 +- .../schemas/types/iso_date_string.ts | 0 .../schemas/types/lists_default_array.test.ts | 2 +- .../schemas/types/lists_default_array.ts | 2 +- .../schemas/types/positive_integer.ts | 0 ...positive_integer_greater_than_zero.test.ts | 2 +- .../positive_integer_greater_than_zero.ts | 0 .../schemas/types/postive_integer.test.ts | 2 +- .../types/references_default_array.test.ts | 2 +- .../schemas/types/references_default_array.ts | 0 .../schemas/types/risk_score.test.ts | 2 +- .../schemas/types/risk_score.ts | 0 .../schemas/types/uuid.test.ts | 2 +- .../detection_engine}/schemas/types/uuid.ts | 0 .../rules/add_prepackaged_rules_route.ts | 8 +- .../routes/rules/create_rules_bulk_route.ts | 2 +- .../routes/rules/delete_rules_bulk_route.ts | 2 +- .../get_prepackaged_rules_status_route.ts | 8 +- .../routes/rules/import_rules_route.ts | 5 +- .../routes/rules/patch_rules_bulk_route.ts | 2 +- .../routes/rules/update_rules_bulk_route.ts | 2 +- .../routes/rules/validate.test.ts | 2 +- .../detection_engine/routes/rules/validate.ts | 7 +- .../schemas/response/check_type_dependents.ts | 97 ------ .../schemas/response/rules_schema.test.ts | 289 ------------------ .../signals/build_exceptions_query.test.ts | 2 +- .../signals/build_exceptions_query.ts | 6 +- .../signals/filter_events_with_list.ts | 2 +- .../siem/server/lib/detection_engine/types.ts | 2 +- .../timeline/routes/import_timelines_route.ts | 2 +- 48 files changed, 333 insertions(+), 535 deletions(-) rename x-pack/plugins/siem/{server/lib/detection_engine/routes/schemas/response => common/detection_engine/schemas/common}/schemas.ts (100%) rename x-pack/plugins/siem/{server/lib/detection_engine/routes => common/detection_engine}/schemas/response/__mocks__/utils.ts (100%) rename x-pack/plugins/siem/{server/lib/detection_engine/routes => common/detection_engine}/schemas/response/error_schema.test.ts (86%) rename x-pack/plugins/siem/{server/lib/detection_engine/routes => common/detection_engine}/schemas/response/error_schema.ts (92%) rename x-pack/plugins/siem/{server/lib/detection_engine/routes => common/detection_engine}/schemas/response/find_rules_schema.test.ts (93%) rename x-pack/plugins/siem/{server/lib/detection_engine/routes => common/detection_engine}/schemas/response/find_rules_schema.ts (89%) rename x-pack/plugins/siem/{server/lib/detection_engine/routes => common/detection_engine}/schemas/response/import_rules_schema.test.ts (92%) rename x-pack/plugins/siem/{server/lib/detection_engine/routes => common/detection_engine}/schemas/response/import_rules_schema.ts (91%) rename x-pack/plugins/siem/{server/lib/detection_engine/routes => common/detection_engine}/schemas/response/prepackaged_rules_schema.test.ts (90%) rename x-pack/plugins/siem/{server/lib/detection_engine/routes => common/detection_engine}/schemas/response/prepackaged_rules_schema.ts (89%) rename x-pack/plugins/siem/{server/lib/detection_engine/routes => common/detection_engine}/schemas/response/prepackaged_rules_status_schema.test.ts (92%) rename x-pack/plugins/siem/{server/lib/detection_engine/routes => common/detection_engine}/schemas/response/prepackaged_rules_status_schema.ts (96%) rename x-pack/plugins/siem/{server/lib/detection_engine/routes => common/detection_engine}/schemas/response/rules_bulk_schema.test.ts (93%) rename x-pack/plugins/siem/{server/lib/detection_engine/routes => common/detection_engine}/schemas/response/rules_bulk_schema.ts (100%) rename x-pack/plugins/siem/{server/lib/detection_engine/routes/schemas/response/check_type_dependents.test.ts => common/detection_engine/schemas/response/rules_schema.test.ts} (70%) rename x-pack/plugins/siem/{server/lib/detection_engine/routes => common/detection_engine}/schemas/response/rules_schema.ts (54%) rename x-pack/plugins/siem/{server/lib/detection_engine/routes => common/detection_engine}/schemas/response/type_timeline_only_schema.test.ts (85%) rename x-pack/plugins/siem/{server/lib/detection_engine/routes => common/detection_engine}/schemas/response/type_timeline_only_schema.ts (92%) rename x-pack/plugins/siem/{server/lib/detection_engine/routes => common/detection_engine}/schemas/types/iso_date_string.test.ts (95%) rename x-pack/plugins/siem/{server/lib/detection_engine/routes => common/detection_engine}/schemas/types/iso_date_string.ts (100%) rename x-pack/plugins/siem/{server/lib/detection_engine/routes => common/detection_engine}/schemas/types/lists_default_array.test.ts (98%) rename x-pack/plugins/siem/{server/lib/detection_engine/routes => common/detection_engine}/schemas/types/lists_default_array.ts (97%) rename x-pack/plugins/siem/{server/lib/detection_engine/routes => common/detection_engine}/schemas/types/positive_integer.ts (100%) rename x-pack/plugins/siem/{server/lib/detection_engine/routes => common/detection_engine}/schemas/types/positive_integer_greater_than_zero.test.ts (95%) rename x-pack/plugins/siem/{server/lib/detection_engine/routes => common/detection_engine}/schemas/types/positive_integer_greater_than_zero.ts (100%) rename x-pack/plugins/siem/{server/lib/detection_engine/routes => common/detection_engine}/schemas/types/postive_integer.test.ts (95%) rename x-pack/plugins/siem/{server/lib/detection_engine/routes => common/detection_engine}/schemas/types/references_default_array.test.ts (95%) rename x-pack/plugins/siem/{server/lib/detection_engine/routes => common/detection_engine}/schemas/types/references_default_array.ts (100%) rename x-pack/plugins/siem/{server/lib/detection_engine/routes => common/detection_engine}/schemas/types/risk_score.test.ts (96%) rename x-pack/plugins/siem/{server/lib/detection_engine/routes => common/detection_engine}/schemas/types/risk_score.ts (100%) rename x-pack/plugins/siem/{server/lib/detection_engine/routes => common/detection_engine}/schemas/types/uuid.test.ts (94%) rename x-pack/plugins/siem/{server/lib/detection_engine/routes => common/detection_engine}/schemas/types/uuid.ts (100%) delete mode 100644 x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/check_type_dependents.ts delete mode 100644 x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/rules_schema.test.ts diff --git a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/schemas.ts b/x-pack/plugins/siem/common/detection_engine/schemas/common/schemas.ts similarity index 100% rename from x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/schemas.ts rename to x-pack/plugins/siem/common/detection_engine/schemas/common/schemas.ts diff --git a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/__mocks__/utils.ts b/x-pack/plugins/siem/common/detection_engine/schemas/response/__mocks__/utils.ts similarity index 100% rename from x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/__mocks__/utils.ts rename to x-pack/plugins/siem/common/detection_engine/schemas/response/__mocks__/utils.ts diff --git a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/error_schema.test.ts b/x-pack/plugins/siem/common/detection_engine/schemas/response/error_schema.test.ts similarity index 86% rename from x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/error_schema.test.ts rename to x-pack/plugins/siem/common/detection_engine/schemas/response/error_schema.test.ts index 9bbde3d5236db5..2a4d75522d010b 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/error_schema.test.ts +++ b/x-pack/plugins/siem/common/detection_engine/schemas/response/error_schema.test.ts @@ -9,19 +9,10 @@ import { pipe } from 'fp-ts/lib/pipeable'; import { getErrorPayload } from './__mocks__/utils'; import { errorSchema, ErrorSchema } from './error_schema'; -import { setFeatureFlagsForTestsOnly, unSetFeatureFlagsForTestsOnly } from '../../../feature_flags'; -import { exactCheck } from '../../../../../../common/exact_check'; -import { getPaths, foldLeftRight } from '../../../../../../common/test_utils'; +import { exactCheck } from '../../../exact_check'; +import { foldLeftRight, getPaths } from '../../../test_utils'; describe('error_schema', () => { - beforeAll(() => { - setFeatureFlagsForTestsOnly(); - }); - - afterAll(() => { - unSetFeatureFlagsForTestsOnly(); - }); - test('it should validate an error with a UUID given for id', () => { const error = getErrorPayload(); const decoded = errorSchema.decode(getErrorPayload()); diff --git a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/error_schema.ts b/x-pack/plugins/siem/common/detection_engine/schemas/response/error_schema.ts similarity index 92% rename from x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/error_schema.ts rename to x-pack/plugins/siem/common/detection_engine/schemas/response/error_schema.ts index f9c776e3b3cdc1..986d3ad87ec851 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/error_schema.ts +++ b/x-pack/plugins/siem/common/detection_engine/schemas/response/error_schema.ts @@ -7,7 +7,7 @@ import * as t from 'io-ts'; /* eslint-disable @typescript-eslint/camelcase */ -import { rule_id, status_code, message } from './schemas'; +import { rule_id, status_code, message } from '../common/schemas'; /* eslint-enable @typescript-eslint/camelcase */ // We use id: t.string intentionally and _never_ the id from global schemas as diff --git a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/find_rules_schema.test.ts b/x-pack/plugins/siem/common/detection_engine/schemas/response/find_rules_schema.test.ts similarity index 93% rename from x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/find_rules_schema.test.ts rename to x-pack/plugins/siem/common/detection_engine/schemas/response/find_rules_schema.test.ts index 1b7d7994462c75..51163c3d76ed6e 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/find_rules_schema.test.ts +++ b/x-pack/plugins/siem/common/detection_engine/schemas/response/find_rules_schema.test.ts @@ -9,19 +9,10 @@ import { pipe } from 'fp-ts/lib/pipeable'; import { getFindResponseSingle, getBaseResponsePayload } from './__mocks__/utils'; import { left } from 'fp-ts/lib/Either'; import { RulesSchema } from './rules_schema'; -import { setFeatureFlagsForTestsOnly, unSetFeatureFlagsForTestsOnly } from '../../../feature_flags'; -import { getPaths, foldLeftRight } from '../../../../../../common/test_utils'; -import { exactCheck } from '../../../../../../common/exact_check'; +import { exactCheck } from '../../../exact_check'; +import { foldLeftRight, getPaths } from '../../../test_utils'; describe('find_rules_schema', () => { - beforeAll(() => { - setFeatureFlagsForTestsOnly(); - }); - - afterAll(() => { - unSetFeatureFlagsForTestsOnly(); - }); - test('it should validate a typical single find rules response', () => { const payload = getFindResponseSingle(); const decoded = findRulesSchema.decode(payload); diff --git a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/find_rules_schema.ts b/x-pack/plugins/siem/common/detection_engine/schemas/response/find_rules_schema.ts similarity index 89% rename from x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/find_rules_schema.ts rename to x-pack/plugins/siem/common/detection_engine/schemas/response/find_rules_schema.ts index d7e8a246cfe012..77077ce2e22ac4 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/find_rules_schema.ts +++ b/x-pack/plugins/siem/common/detection_engine/schemas/response/find_rules_schema.ts @@ -7,7 +7,7 @@ import * as t from 'io-ts'; import { rulesSchema } from './rules_schema'; -import { page, perPage, total } from './schemas'; +import { page, perPage, total } from '../common/schemas'; export const findRulesSchema = t.exact( t.type({ diff --git a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/import_rules_schema.test.ts b/x-pack/plugins/siem/common/detection_engine/schemas/response/import_rules_schema.test.ts similarity index 92% rename from x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/import_rules_schema.test.ts rename to x-pack/plugins/siem/common/detection_engine/schemas/response/import_rules_schema.test.ts index 18e17a319883ac..d7efe4b30af119 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/import_rules_schema.test.ts +++ b/x-pack/plugins/siem/common/detection_engine/schemas/response/import_rules_schema.test.ts @@ -8,20 +8,11 @@ import { pipe } from 'fp-ts/lib/pipeable'; import { left, Either } from 'fp-ts/lib/Either'; import { ImportRulesSchema, importRulesSchema } from './import_rules_schema'; import { ErrorSchema } from './error_schema'; -import { setFeatureFlagsForTestsOnly, unSetFeatureFlagsForTestsOnly } from '../../../feature_flags'; import { Errors } from 'io-ts'; -import { exactCheck } from '../../../../../../common/exact_check'; -import { foldLeftRight, getPaths } from '../../../../../../common/test_utils'; +import { exactCheck } from '../../../exact_check'; +import { foldLeftRight, getPaths } from '../../../test_utils'; describe('import_rules_schema', () => { - beforeAll(() => { - setFeatureFlagsForTestsOnly(); - }); - - afterAll(() => { - unSetFeatureFlagsForTestsOnly(); - }); - test('it should validate an empty import response with no errors', () => { const payload: ImportRulesSchema = { success: true, success_count: 0, errors: [] }; const decoded = importRulesSchema.decode(payload); diff --git a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/import_rules_schema.ts b/x-pack/plugins/siem/common/detection_engine/schemas/response/import_rules_schema.ts similarity index 91% rename from x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/import_rules_schema.ts rename to x-pack/plugins/siem/common/detection_engine/schemas/response/import_rules_schema.ts index dec32b18e2b24b..adea77e7b933f9 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/import_rules_schema.ts +++ b/x-pack/plugins/siem/common/detection_engine/schemas/response/import_rules_schema.ts @@ -7,7 +7,7 @@ import * as t from 'io-ts'; /* eslint-disable @typescript-eslint/camelcase */ -import { success, success_count } from './schemas'; +import { success, success_count } from '../common/schemas'; import { errorSchema } from './error_schema'; /* eslint-enable @typescript-eslint/camelcase */ diff --git a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/prepackaged_rules_schema.test.ts b/x-pack/plugins/siem/common/detection_engine/schemas/response/prepackaged_rules_schema.test.ts similarity index 90% rename from x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/prepackaged_rules_schema.test.ts rename to x-pack/plugins/siem/common/detection_engine/schemas/response/prepackaged_rules_schema.test.ts index 2d3fd759148220..fc3f89996daf10 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/prepackaged_rules_schema.test.ts +++ b/x-pack/plugins/siem/common/detection_engine/schemas/response/prepackaged_rules_schema.test.ts @@ -7,19 +7,10 @@ import { pipe } from 'fp-ts/lib/pipeable'; import { left } from 'fp-ts/lib/Either'; import { PrePackagedRulesSchema, prePackagedRulesSchema } from './prepackaged_rules_schema'; -import { setFeatureFlagsForTestsOnly, unSetFeatureFlagsForTestsOnly } from '../../../feature_flags'; -import { exactCheck } from '../../../../../../common/exact_check'; -import { getPaths, foldLeftRight } from '../../../../../../common/test_utils'; +import { exactCheck } from '../../../exact_check'; +import { foldLeftRight, getPaths } from '../../../test_utils'; describe('prepackaged_rules_schema', () => { - beforeAll(() => { - setFeatureFlagsForTestsOnly(); - }); - - afterAll(() => { - unSetFeatureFlagsForTestsOnly(); - }); - test('it should validate an empty prepackaged response with defaults', () => { const payload: PrePackagedRulesSchema = { rules_installed: 0, rules_updated: 0 }; const decoded = prePackagedRulesSchema.decode(payload); diff --git a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/prepackaged_rules_schema.ts b/x-pack/plugins/siem/common/detection_engine/schemas/response/prepackaged_rules_schema.ts similarity index 89% rename from x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/prepackaged_rules_schema.ts rename to x-pack/plugins/siem/common/detection_engine/schemas/response/prepackaged_rules_schema.ts index f0eff0ba19753f..3b0107c91fee00 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/prepackaged_rules_schema.ts +++ b/x-pack/plugins/siem/common/detection_engine/schemas/response/prepackaged_rules_schema.ts @@ -7,7 +7,7 @@ import * as t from 'io-ts'; /* eslint-disable @typescript-eslint/camelcase */ -import { rules_installed, rules_updated } from './schemas'; +import { rules_installed, rules_updated } from '../common/schemas'; /* eslint-enable @typescript-eslint/camelcase */ export const prePackagedRulesSchema = t.exact( diff --git a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/prepackaged_rules_status_schema.test.ts b/x-pack/plugins/siem/common/detection_engine/schemas/response/prepackaged_rules_status_schema.test.ts similarity index 92% rename from x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/prepackaged_rules_status_schema.test.ts rename to x-pack/plugins/siem/common/detection_engine/schemas/response/prepackaged_rules_status_schema.test.ts index abe601a546111b..eeae72209829e1 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/prepackaged_rules_status_schema.test.ts +++ b/x-pack/plugins/siem/common/detection_engine/schemas/response/prepackaged_rules_status_schema.test.ts @@ -10,19 +10,10 @@ import { PrePackagedRulesStatusSchema, prePackagedRulesStatusSchema, } from './prepackaged_rules_status_schema'; -import { setFeatureFlagsForTestsOnly, unSetFeatureFlagsForTestsOnly } from '../../../feature_flags'; -import { exactCheck } from '../../../../../../common/exact_check'; -import { foldLeftRight, getPaths } from '../../../../../../common/test_utils'; +import { exactCheck } from '../../../exact_check'; +import { foldLeftRight, getPaths } from '../../../test_utils'; describe('prepackaged_rules_schema', () => { - beforeAll(() => { - setFeatureFlagsForTestsOnly(); - }); - - afterAll(() => { - unSetFeatureFlagsForTestsOnly(); - }); - test('it should validate an empty prepackaged response with defaults', () => { const payload: PrePackagedRulesStatusSchema = { rules_installed: 0, diff --git a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/prepackaged_rules_status_schema.ts b/x-pack/plugins/siem/common/detection_engine/schemas/response/prepackaged_rules_status_schema.ts similarity index 96% rename from x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/prepackaged_rules_status_schema.ts rename to x-pack/plugins/siem/common/detection_engine/schemas/response/prepackaged_rules_status_schema.ts index 72e5821eb46975..ee8e7b48a58bc9 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/prepackaged_rules_status_schema.ts +++ b/x-pack/plugins/siem/common/detection_engine/schemas/response/prepackaged_rules_status_schema.ts @@ -12,7 +12,7 @@ import { rules_custom_installed, rules_not_installed, rules_not_updated, -} from './schemas'; +} from '../common/schemas'; /* eslint-enable @typescript-eslint/camelcase */ export const prePackagedRulesStatusSchema = t.exact( diff --git a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/rules_bulk_schema.test.ts b/x-pack/plugins/siem/common/detection_engine/schemas/response/rules_bulk_schema.test.ts similarity index 93% rename from x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/rules_bulk_schema.test.ts rename to x-pack/plugins/siem/common/detection_engine/schemas/response/rules_bulk_schema.test.ts index 98cb2ef058485c..04cf012f36dbac 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/rules_bulk_schema.test.ts +++ b/x-pack/plugins/siem/common/detection_engine/schemas/response/rules_bulk_schema.test.ts @@ -11,19 +11,10 @@ import { getBaseResponsePayload, getErrorPayload } from './__mocks__/utils'; import { RulesBulkSchema, rulesBulkSchema } from './rules_bulk_schema'; import { RulesSchema } from './rules_schema'; import { ErrorSchema } from './error_schema'; -import { setFeatureFlagsForTestsOnly, unSetFeatureFlagsForTestsOnly } from '../../../feature_flags'; -import { exactCheck } from '../../../../../../common/exact_check'; -import { foldLeftRight, getPaths } from '../../../../../../common/test_utils'; +import { exactCheck } from '../../../exact_check'; +import { foldLeftRight, getPaths } from '../../../test_utils'; describe('prepackaged_rule_schema', () => { - beforeAll(() => { - setFeatureFlagsForTestsOnly(); - }); - - afterAll(() => { - unSetFeatureFlagsForTestsOnly(); - }); - test('it should validate a regular message and and error together with a uuid', () => { const payload: RulesBulkSchema = [getBaseResponsePayload(), getErrorPayload()]; const decoded = rulesBulkSchema.decode(payload); diff --git a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/rules_bulk_schema.ts b/x-pack/plugins/siem/common/detection_engine/schemas/response/rules_bulk_schema.ts similarity index 100% rename from x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/rules_bulk_schema.ts rename to x-pack/plugins/siem/common/detection_engine/schemas/response/rules_bulk_schema.ts diff --git a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/check_type_dependents.test.ts b/x-pack/plugins/siem/common/detection_engine/schemas/response/rules_schema.test.ts similarity index 70% rename from x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/check_type_dependents.test.ts rename to x-pack/plugins/siem/common/detection_engine/schemas/response/rules_schema.test.ts index 0b0d3bf43b1e9c..8ed9c30507f4f8 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/check_type_dependents.test.ts +++ b/x-pack/plugins/siem/common/detection_engine/schemas/response/rules_schema.test.ts @@ -4,32 +4,209 @@ * you may not use this file except in compliance with the Elastic License. */ +import { left } from 'fp-ts/lib/Either'; import { pipe } from 'fp-ts/lib/pipeable'; import * as t from 'io-ts'; import { + rulesSchema, + RulesSchema, checkTypeDependents, getDependents, addSavedId, - addTimelineTitle, addQueryFields, + addTimelineTitle, addMlFields, -} from './check_type_dependents'; +} from './rules_schema'; import { getBaseResponsePayload, getMlRuleResponsePayload } from './__mocks__/utils'; -import { left } from 'fp-ts/lib/Either'; -import { RulesSchema } from './rules_schema'; +import { exactCheck } from '../../../exact_check'; +import { foldLeftRight, getPaths } from '../../../test_utils'; import { TypeAndTimelineOnly } from './type_timeline_only_schema'; -import { setFeatureFlagsForTestsOnly, unSetFeatureFlagsForTestsOnly } from '../../../feature_flags'; -import { exactCheck } from '../../../../../../common/exact_check'; -import { foldLeftRight, getPaths } from '../../../../../../common/test_utils'; -describe('check_type_dependents', () => { - beforeAll(() => { - setFeatureFlagsForTestsOnly(); +export const ANCHOR_DATE = '2020-02-20T03:57:54.037Z'; + +describe('rules_schema', () => { + test('it should validate a type of "query" without anything extra', () => { + const payload = getBaseResponsePayload(); + + const decoded = rulesSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + const expected = getBaseResponsePayload(); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(expected); + }); + + test('it should NOT validate a type of "query" when it has extra data', () => { + const payload: RulesSchema & { invalid_extra_data?: string } = getBaseResponsePayload(); + payload.invalid_extra_data = 'invalid_extra_data'; + + const decoded = rulesSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual(['invalid keys "invalid_extra_data"']); + expect(message.schema).toEqual({}); + }); + + test('it should NOT validate invalid_data for the type', () => { + const payload: Omit & { type: string } = getBaseResponsePayload(); + payload.type = 'invalid_data'; + + const decoded = rulesSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "invalid_data" supplied to "type"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should NOT validate a type of "query" with a saved_id together', () => { + const payload = getBaseResponsePayload(); + payload.type = 'query'; + payload.saved_id = 'save id 123'; + + const decoded = rulesSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual(['invalid keys "saved_id"']); + expect(message.schema).toEqual({}); + }); + + test('it should validate a type of "saved_query" with a "saved_id" dependent', () => { + const payload = getBaseResponsePayload(); + payload.type = 'saved_query'; + payload.saved_id = 'save id 123'; + + const decoded = rulesSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + const expected = getBaseResponsePayload(); + + expected.type = 'saved_query'; + expected.saved_id = 'save id 123'; + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(expected); + }); + + test('it should NOT validate a type of "saved_query" without a "saved_id" dependent', () => { + const payload = getBaseResponsePayload(); + payload.type = 'saved_query'; + delete payload.saved_id; + + const decoded = rulesSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "undefined" supplied to "saved_id"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should NOT validate a type of "saved_query" when it has extra data', () => { + const payload: RulesSchema & { invalid_extra_data?: string } = getBaseResponsePayload(); + payload.type = 'saved_query'; + payload.saved_id = 'save id 123'; + payload.invalid_extra_data = 'invalid_extra_data'; + + const decoded = rulesSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual(['invalid keys "invalid_extra_data"']); + expect(message.schema).toEqual({}); + }); + + test('it should validate a type of "timeline_id" if there is a "timeline_title" dependent', () => { + const payload = getBaseResponsePayload(); + payload.timeline_id = 'some timeline id'; + payload.timeline_title = 'some timeline title'; + + const decoded = rulesSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + const expected = getBaseResponsePayload(); + expected.timeline_id = 'some timeline id'; + expected.timeline_title = 'some timeline title'; + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(expected); }); - afterAll(() => { - unSetFeatureFlagsForTestsOnly(); + test('it should NOT validate a type of "timeline_id" if there is "timeline_title" dependent when it has extra invalid data', () => { + const payload: RulesSchema & { invalid_extra_data?: string } = getBaseResponsePayload(); + payload.timeline_id = 'some timeline id'; + payload.timeline_title = 'some timeline title'; + payload.invalid_extra_data = 'invalid_extra_data'; + + const decoded = rulesSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual(['invalid keys "invalid_extra_data"']); + expect(message.schema).toEqual({}); + }); + + test('it should NOT validate a type of "timeline_id" if there is NOT a "timeline_title" dependent', () => { + const payload = getBaseResponsePayload(); + payload.timeline_id = 'some timeline id'; + + const decoded = rulesSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "undefined" supplied to "timeline_title"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should NOT validate a type of "timeline_title" if there is NOT a "timeline_id" dependent', () => { + const payload = getBaseResponsePayload(); + payload.timeline_title = 'some timeline title'; + + const decoded = rulesSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual(['invalid keys "timeline_title"']); + expect(message.schema).toEqual({}); + }); + + test('it should NOT validate a type of "saved_query" with a "saved_id" dependent and a "timeline_title" but there is NOT a "timeline_id"', () => { + const payload = getBaseResponsePayload(); + payload.saved_id = 'some saved id'; + payload.type = 'saved_query'; + payload.timeline_title = 'some timeline title'; + + const decoded = rulesSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual(['invalid keys "timeline_title"']); + expect(message.schema).toEqual({}); + }); + + test('it should NOT validate a type of "saved_query" with a "saved_id" dependent and a "timeline_id" but there is NOT a "timeline_title"', () => { + const payload = getBaseResponsePayload(); + payload.saved_id = 'some saved id'; + payload.type = 'saved_query'; + payload.timeline_id = 'some timeline id'; + + const decoded = rulesSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "undefined" supplied to "timeline_title"', + ]); + expect(message.schema).toEqual({}); }); describe('checkTypeDependents', () => { diff --git a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/rules_schema.ts b/x-pack/plugins/siem/common/detection_engine/schemas/response/rules_schema.ts similarity index 54% rename from x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/rules_schema.ts rename to x-pack/plugins/siem/common/detection_engine/schemas/response/rules_schema.ts index fb1ee8e670e312..a7a31ec9e1b59e 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/rules_schema.ts +++ b/x-pack/plugins/siem/common/detection_engine/schemas/response/rules_schema.ts @@ -7,10 +7,11 @@ /* eslint-disable @typescript-eslint/camelcase */ import * as t from 'io-ts'; import { isObject } from 'lodash/fp'; -import { Either, fold, right, left } from 'fp-ts/lib/Either'; - +import { Either, left, fold } from 'fp-ts/lib/Either'; import { pipe } from 'fp-ts/lib/pipeable'; -import { checkTypeDependents } from './check_type_dependents'; +import { typeAndTimelineOnlySchema, TypeAndTimelineOnly } from './type_timeline_only_schema'; +import { isMlRule } from '../../../machine_learning/helpers'; + import { actions, anomaly_threshold, @@ -54,9 +55,8 @@ import { filters, meta, note, -} from './schemas'; +} from '../common/schemas'; import { ListsDefaultArray } from '../types/lists_default_array'; -import { hasListsFeature } from '../../../feature_flags'; /** * This is the required fields for the rules schema response. Put all required properties on @@ -155,32 +155,92 @@ export const rulesSchema = new t.Type< 'RulesSchema', (input: unknown): input is RulesWithoutTypeDependentsSchema => isObject(input), (input): Either => { - const output = checkTypeDependents(input); - if (!hasListsFeature()) { - // TODO: (LIST-FEATURE) Remove this after the lists feature is an accepted feature for a particular release - return removeList(output); - } else { - return output; - } + return checkTypeDependents(input); }, t.identity ); -// TODO: (LIST-FEATURE) Remove this after the lists feature is an accepted feature for a particular release -export const removeList = ( - decoded: Either -): Either => { - const onLeft = (errors: t.Errors): Either => left(errors); - const onRight = (decodedValue: RequiredRulesSchema): Either => { - delete decodedValue.exceptions_list; - return right(decodedValue); - }; - const folded = fold(onLeft, onRight); - return pipe(decoded, folded); -}; - /** * This is the correct type you want to use for Rules that are outputted from the * REST interface. This has all base and all optional properties merged together. */ export type RulesSchema = t.TypeOf; + +export const addSavedId = (typeAndTimelineOnly: TypeAndTimelineOnly): t.Mixed[] => { + if (typeAndTimelineOnly.type === 'saved_query') { + return [t.exact(t.type({ saved_id: dependentRulesSchema.props.saved_id }))]; + } else { + return []; + } +}; + +export const addTimelineTitle = (typeAndTimelineOnly: TypeAndTimelineOnly): t.Mixed[] => { + if (typeAndTimelineOnly.timeline_id != null) { + return [ + t.exact(t.type({ timeline_title: dependentRulesSchema.props.timeline_title })), + t.exact(t.type({ timeline_id: dependentRulesSchema.props.timeline_id })), + ]; + } else { + return []; + } +}; + +export const addQueryFields = (typeAndTimelineOnly: TypeAndTimelineOnly): t.Mixed[] => { + if (typeAndTimelineOnly.type === 'query' || typeAndTimelineOnly.type === 'saved_query') { + return [ + t.exact(t.type({ query: dependentRulesSchema.props.query })), + t.exact(t.type({ language: dependentRulesSchema.props.language })), + ]; + } else { + return []; + } +}; + +export const addMlFields = (typeAndTimelineOnly: TypeAndTimelineOnly): t.Mixed[] => { + if (isMlRule(typeAndTimelineOnly.type)) { + return [ + t.exact(t.type({ anomaly_threshold: dependentRulesSchema.props.anomaly_threshold })), + t.exact( + t.type({ machine_learning_job_id: dependentRulesSchema.props.machine_learning_job_id }) + ), + ]; + } else { + return []; + } +}; + +export const getDependents = (typeAndTimelineOnly: TypeAndTimelineOnly): t.Mixed => { + const dependents: t.Mixed[] = [ + t.exact(requiredRulesSchema), + t.exact(partialRulesSchema), + ...addSavedId(typeAndTimelineOnly), + ...addTimelineTitle(typeAndTimelineOnly), + ...addQueryFields(typeAndTimelineOnly), + ...addMlFields(typeAndTimelineOnly), + ]; + + if (dependents.length > 1) { + // This unsafe cast is because t.intersection does not use an array but rather a set of + // tuples and really does not look like they expected us to ever dynamically build up + // intersections, but here we are doing that. Looking at their code, although they limit + // the array elements to 5, it looks like you have N number of intersections + const unsafeCast: [t.Mixed, t.Mixed] = dependents as [t.Mixed, t.Mixed]; + return t.intersection(unsafeCast); + } else { + // We are not allowed to call t.intersection with a single value so we return without + // it here normally. + return dependents[0]; + } +}; + +export const checkTypeDependents = (input: unknown): Either => { + const typeOnlyDecoded = typeAndTimelineOnlySchema.decode(input); + const onLeft = (errors: t.Errors): Either => left(errors); + const onRight = ( + typeAndTimelineOnly: TypeAndTimelineOnly + ): Either => { + const intersections = getDependents(typeAndTimelineOnly); + return intersections.decode(input); + }; + return pipe(typeOnlyDecoded, fold(onLeft, onRight)); +}; diff --git a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/type_timeline_only_schema.test.ts b/x-pack/plugins/siem/common/detection_engine/schemas/response/type_timeline_only_schema.test.ts similarity index 85% rename from x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/type_timeline_only_schema.test.ts rename to x-pack/plugins/siem/common/detection_engine/schemas/response/type_timeline_only_schema.test.ts index 8f06e2c6e49b0c..c7335ffd62f021 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/type_timeline_only_schema.test.ts +++ b/x-pack/plugins/siem/common/detection_engine/schemas/response/type_timeline_only_schema.test.ts @@ -8,19 +8,10 @@ import { left } from 'fp-ts/lib/Either'; import { pipe } from 'fp-ts/lib/pipeable'; import { TypeAndTimelineOnly, typeAndTimelineOnlySchema } from './type_timeline_only_schema'; -import { setFeatureFlagsForTestsOnly, unSetFeatureFlagsForTestsOnly } from '../../../feature_flags'; -import { exactCheck } from '../../../../../../common/exact_check'; -import { foldLeftRight, getPaths } from '../../../../../../common/test_utils'; +import { exactCheck } from '../../../exact_check'; +import { foldLeftRight, getPaths } from '../../../test_utils'; describe('prepackaged_rule_schema', () => { - beforeAll(() => { - setFeatureFlagsForTestsOnly(); - }); - - afterAll(() => { - unSetFeatureFlagsForTestsOnly(); - }); - test('it should validate a a type and timeline_id together', () => { const payload: TypeAndTimelineOnly = { type: 'query', diff --git a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/type_timeline_only_schema.ts b/x-pack/plugins/siem/common/detection_engine/schemas/response/type_timeline_only_schema.ts similarity index 92% rename from x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/type_timeline_only_schema.ts rename to x-pack/plugins/siem/common/detection_engine/schemas/response/type_timeline_only_schema.ts index 6d11ff03563d1d..d23d4ad2e83d49 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/type_timeline_only_schema.ts +++ b/x-pack/plugins/siem/common/detection_engine/schemas/response/type_timeline_only_schema.ts @@ -7,7 +7,7 @@ import * as t from 'io-ts'; /* eslint-disable @typescript-eslint/camelcase */ -import { timeline_id, type } from './schemas'; +import { timeline_id, type } from '../common/schemas'; /* eslint-enable @typescript-eslint/camelcase */ /** diff --git a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/types/iso_date_string.test.ts b/x-pack/plugins/siem/common/detection_engine/schemas/types/iso_date_string.test.ts similarity index 95% rename from x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/types/iso_date_string.test.ts rename to x-pack/plugins/siem/common/detection_engine/schemas/types/iso_date_string.test.ts index 9f9181359d44a7..e8bce3f38f4b3d 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/types/iso_date_string.test.ts +++ b/x-pack/plugins/siem/common/detection_engine/schemas/types/iso_date_string.test.ts @@ -7,7 +7,7 @@ import { IsoDateString } from './iso_date_string'; import { pipe } from 'fp-ts/lib/pipeable'; import { left } from 'fp-ts/lib/Either'; -import { getPaths, foldLeftRight } from '../../../../../../common/test_utils'; +import { foldLeftRight, getPaths } from '../../../test_utils'; describe('ios_date_string', () => { test('it should validate a iso string', () => { diff --git a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/types/iso_date_string.ts b/x-pack/plugins/siem/common/detection_engine/schemas/types/iso_date_string.ts similarity index 100% rename from x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/types/iso_date_string.ts rename to x-pack/plugins/siem/common/detection_engine/schemas/types/iso_date_string.ts diff --git a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/types/lists_default_array.test.ts b/x-pack/plugins/siem/common/detection_engine/schemas/types/lists_default_array.test.ts similarity index 98% rename from x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/types/lists_default_array.test.ts rename to x-pack/plugins/siem/common/detection_engine/schemas/types/lists_default_array.test.ts index dc0bd6cacf0d64..31e0a8e5c2c73b 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/types/lists_default_array.test.ts +++ b/x-pack/plugins/siem/common/detection_engine/schemas/types/lists_default_array.test.ts @@ -7,7 +7,7 @@ import { ListsDefaultArray } from './lists_default_array'; import { pipe } from 'fp-ts/lib/pipeable'; import { left } from 'fp-ts/lib/Either'; -import { getPaths, foldLeftRight } from '../../../../../../common/test_utils'; +import { foldLeftRight, getPaths } from '../../../test_utils'; describe('lists_default_array', () => { test('it should validate an empty array', () => { diff --git a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/types/lists_default_array.ts b/x-pack/plugins/siem/common/detection_engine/schemas/types/lists_default_array.ts similarity index 97% rename from x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/types/lists_default_array.ts rename to x-pack/plugins/siem/common/detection_engine/schemas/types/lists_default_array.ts index 743914ad070a2e..8244f4a29e193b 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/types/lists_default_array.ts +++ b/x-pack/plugins/siem/common/detection_engine/schemas/types/lists_default_array.ts @@ -11,7 +11,7 @@ import { list_and as listAnd, list_values as listValues, list_values_operator as listOperator, -} from '../response/schemas'; +} from '../common/schemas'; export type ListsDefaultArrayC = t.Type; export type List = t.TypeOf; diff --git a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/types/positive_integer.ts b/x-pack/plugins/siem/common/detection_engine/schemas/types/positive_integer.ts similarity index 100% rename from x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/types/positive_integer.ts rename to x-pack/plugins/siem/common/detection_engine/schemas/types/positive_integer.ts diff --git a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/types/positive_integer_greater_than_zero.test.ts b/x-pack/plugins/siem/common/detection_engine/schemas/types/positive_integer_greater_than_zero.test.ts similarity index 95% rename from x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/types/positive_integer_greater_than_zero.test.ts rename to x-pack/plugins/siem/common/detection_engine/schemas/types/positive_integer_greater_than_zero.test.ts index a3338c878bd715..821eb066a65315 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/types/positive_integer_greater_than_zero.test.ts +++ b/x-pack/plugins/siem/common/detection_engine/schemas/types/positive_integer_greater_than_zero.test.ts @@ -7,7 +7,7 @@ import { PositiveIntegerGreaterThanZero } from './positive_integer_greater_than_zero'; import { pipe } from 'fp-ts/lib/pipeable'; import { left } from 'fp-ts/lib/Either'; -import { getPaths, foldLeftRight } from '../../../../../../common/test_utils'; +import { foldLeftRight, getPaths } from '../../../test_utils'; describe('positive_integer_greater_than_zero', () => { test('it should validate a positive number', () => { diff --git a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/types/positive_integer_greater_than_zero.ts b/x-pack/plugins/siem/common/detection_engine/schemas/types/positive_integer_greater_than_zero.ts similarity index 100% rename from x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/types/positive_integer_greater_than_zero.ts rename to x-pack/plugins/siem/common/detection_engine/schemas/types/positive_integer_greater_than_zero.ts diff --git a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/types/postive_integer.test.ts b/x-pack/plugins/siem/common/detection_engine/schemas/types/postive_integer.test.ts similarity index 95% rename from x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/types/postive_integer.test.ts rename to x-pack/plugins/siem/common/detection_engine/schemas/types/postive_integer.test.ts index 48ea2025b9b12c..ea00ecf5efe0d1 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/types/postive_integer.test.ts +++ b/x-pack/plugins/siem/common/detection_engine/schemas/types/postive_integer.test.ts @@ -7,7 +7,7 @@ import { PositiveInteger } from './positive_integer'; import { pipe } from 'fp-ts/lib/pipeable'; import { left } from 'fp-ts/lib/Either'; -import { foldLeftRight, getPaths } from '../../../../../../common/test_utils'; +import { foldLeftRight, getPaths } from '../../../test_utils'; describe('positive_integer_greater_than_zero', () => { test('it should validate a positive number', () => { diff --git a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/types/references_default_array.test.ts b/x-pack/plugins/siem/common/detection_engine/schemas/types/references_default_array.test.ts similarity index 95% rename from x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/types/references_default_array.test.ts rename to x-pack/plugins/siem/common/detection_engine/schemas/types/references_default_array.test.ts index 3aaff7e00ad51a..43e2dbdac1fe19 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/types/references_default_array.test.ts +++ b/x-pack/plugins/siem/common/detection_engine/schemas/types/references_default_array.test.ts @@ -7,7 +7,7 @@ import { ReferencesDefaultArray } from './references_default_array'; import { pipe } from 'fp-ts/lib/pipeable'; import { left } from 'fp-ts/lib/Either'; -import { getPaths, foldLeftRight } from '../../../../../../common/test_utils'; +import { foldLeftRight, getPaths } from '../../../test_utils'; describe('references_default_array', () => { test('it should validate an empty array', () => { diff --git a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/types/references_default_array.ts b/x-pack/plugins/siem/common/detection_engine/schemas/types/references_default_array.ts similarity index 100% rename from x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/types/references_default_array.ts rename to x-pack/plugins/siem/common/detection_engine/schemas/types/references_default_array.ts diff --git a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/types/risk_score.test.ts b/x-pack/plugins/siem/common/detection_engine/schemas/types/risk_score.test.ts similarity index 96% rename from x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/types/risk_score.test.ts rename to x-pack/plugins/siem/common/detection_engine/schemas/types/risk_score.test.ts index 41c0faf4d608d7..cf849f28a09636 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/types/risk_score.test.ts +++ b/x-pack/plugins/siem/common/detection_engine/schemas/types/risk_score.test.ts @@ -7,7 +7,7 @@ import { RiskScore } from './risk_score'; import { pipe } from 'fp-ts/lib/pipeable'; import { left } from 'fp-ts/lib/Either'; -import { foldLeftRight, getPaths } from '../../../../../../common/test_utils'; +import { foldLeftRight, getPaths } from '../../../test_utils'; describe('risk_score', () => { test('it should validate a positive number', () => { diff --git a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/types/risk_score.ts b/x-pack/plugins/siem/common/detection_engine/schemas/types/risk_score.ts similarity index 100% rename from x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/types/risk_score.ts rename to x-pack/plugins/siem/common/detection_engine/schemas/types/risk_score.ts diff --git a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/types/uuid.test.ts b/x-pack/plugins/siem/common/detection_engine/schemas/types/uuid.test.ts similarity index 94% rename from x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/types/uuid.test.ts rename to x-pack/plugins/siem/common/detection_engine/schemas/types/uuid.test.ts index b640b449e6b8ac..d3a68a7575487f 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/types/uuid.test.ts +++ b/x-pack/plugins/siem/common/detection_engine/schemas/types/uuid.test.ts @@ -7,7 +7,7 @@ import { UUID } from './uuid'; import { pipe } from 'fp-ts/lib/pipeable'; import { left } from 'fp-ts/lib/Either'; -import { foldLeftRight, getPaths } from '../../../../../../common/test_utils'; +import { foldLeftRight, getPaths } from '../../../test_utils'; describe('uuid', () => { test('it should validate a uuid', () => { diff --git a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/types/uuid.ts b/x-pack/plugins/siem/common/detection_engine/schemas/types/uuid.ts similarity index 100% rename from x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/types/uuid.ts rename to x-pack/plugins/siem/common/detection_engine/schemas/types/uuid.ts diff --git a/x-pack/plugins/siem/server/lib/detection_engine/routes/rules/add_prepackaged_rules_route.ts b/x-pack/plugins/siem/server/lib/detection_engine/routes/rules/add_prepackaged_rules_route.ts index 83dd87002e8f9b..6268451042da13 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/routes/rules/add_prepackaged_rules_route.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/routes/rules/add_prepackaged_rules_route.ts @@ -4,6 +4,10 @@ * you may not use this file except in compliance with the Elastic License. */ +import { + PrePackagedRulesSchema, + prePackagedRulesSchema, +} from '../../../../../common/detection_engine/schemas/response/prepackaged_rules_schema'; import { IRouter } from '../../../../../../../../src/core/server'; import { DETECTION_ENGINE_PREPACKAGED_URL } from '../../../../../common/constants'; import { getIndexExists } from '../../index/get_index_exists'; @@ -14,10 +18,6 @@ import { updatePrepackagedRules } from '../../rules/update_prepacked_rules'; import { getRulesToInstall } from '../../rules/get_rules_to_install'; import { getRulesToUpdate } from '../../rules/get_rules_to_update'; import { getExistingPrepackagedRules } from '../../rules/get_existing_prepackaged_rules'; -import { - PrePackagedRulesSchema, - prePackagedRulesSchema, -} from '../schemas/response/prepackaged_rules_schema'; import { validate } from './validate'; export const addPrepackedRulesRoute = (router: IRouter) => { diff --git a/x-pack/plugins/siem/server/lib/detection_engine/routes/rules/create_rules_bulk_route.ts b/x-pack/plugins/siem/server/lib/detection_engine/routes/rules/create_rules_bulk_route.ts index ff6d212deb5842..d88cc7fcde504c 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/routes/rules/create_rules_bulk_route.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/routes/rules/create_rules_bulk_route.ts @@ -6,6 +6,7 @@ import uuid from 'uuid'; +import { rulesBulkSchema } from '../../../../../common/detection_engine/schemas/response/rules_bulk_schema'; import { IRouter } from '../../../../../../../../src/core/server'; import { DETECTION_ENGINE_RULES_URL } from '../../../../../common/constants'; import { SetupPlugins } from '../../../../plugin'; @@ -24,7 +25,6 @@ import { buildSiemResponse, } from '../utils'; import { createRulesBulkSchema } from '../schemas/create_rules_bulk_schema'; -import { rulesBulkSchema } from '../schemas/response/rules_bulk_schema'; import { updateRulesNotifications } from '../../rules/update_rules_notifications'; export const createRulesBulkRoute = (router: IRouter, ml: SetupPlugins['ml']) => { diff --git a/x-pack/plugins/siem/server/lib/detection_engine/routes/rules/delete_rules_bulk_route.ts b/x-pack/plugins/siem/server/lib/detection_engine/routes/rules/delete_rules_bulk_route.ts index 036e29aa0ebe76..01ad3c7d4e7261 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/routes/rules/delete_rules_bulk_route.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/routes/rules/delete_rules_bulk_route.ts @@ -4,10 +4,10 @@ * you may not use this file except in compliance with the Elastic License. */ +import { rulesBulkSchema } from '../../../../../common/detection_engine/schemas/response/rules_bulk_schema'; import { IRouter, RouteConfig, RequestHandler } from '../../../../../../../../src/core/server'; import { DETECTION_ENGINE_RULES_URL } from '../../../../../common/constants'; import { queryRulesBulkSchema } from '../schemas/query_rules_bulk_schema'; -import { rulesBulkSchema } from '../schemas/response/rules_bulk_schema'; import { getIdBulkError } from './utils'; import { transformValidateBulkError, validate } from './validate'; import { transformBulkError, buildRouteValidation, buildSiemResponse } from '../utils'; diff --git a/x-pack/plugins/siem/server/lib/detection_engine/routes/rules/get_prepackaged_rules_status_route.ts b/x-pack/plugins/siem/server/lib/detection_engine/routes/rules/get_prepackaged_rules_status_route.ts index 67a54f3ba492a5..90380b0483c826 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/routes/rules/get_prepackaged_rules_status_route.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/routes/rules/get_prepackaged_rules_status_route.ts @@ -4,6 +4,10 @@ * you may not use this file except in compliance with the Elastic License. */ +import { + PrePackagedRulesStatusSchema, + prePackagedRulesStatusSchema, +} from '../../../../../common/detection_engine/schemas/response/prepackaged_rules_status_schema'; import { IRouter } from '../../../../../../../../src/core/server'; import { DETECTION_ENGINE_PREPACKAGED_URL } from '../../../../../common/constants'; import { transformError, buildSiemResponse } from '../utils'; @@ -12,10 +16,6 @@ import { getRulesToInstall } from '../../rules/get_rules_to_install'; import { getRulesToUpdate } from '../../rules/get_rules_to_update'; import { findRules } from '../../rules/find_rules'; import { getExistingPrepackagedRules } from '../../rules/get_existing_prepackaged_rules'; -import { - PrePackagedRulesStatusSchema, - prePackagedRulesStatusSchema, -} from '../schemas/response/prepackaged_rules_status_schema'; import { validate } from './validate'; export const getPrepackagedRulesStatusRoute = (router: IRouter) => { diff --git a/x-pack/plugins/siem/server/lib/detection_engine/routes/rules/import_rules_route.ts b/x-pack/plugins/siem/server/lib/detection_engine/routes/rules/import_rules_route.ts index 901e7403bb9536..311149087cc496 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/routes/rules/import_rules_route.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/routes/rules/import_rules_route.ts @@ -7,6 +7,10 @@ import { chunk } from 'lodash/fp'; import { extname } from 'path'; +import { + ImportRulesSchema, + importRulesSchema, +} from '../../../../../common/detection_engine/schemas/response/import_rules_schema'; import { IRouter } from '../../../../../../../../src/core/server'; import { createPromiseFromStreams } from '../../../../../../../../src/legacy/utils/streams'; import { DETECTION_ENGINE_RULES_URL } from '../../../../../common/constants'; @@ -31,7 +35,6 @@ import { import { ImportRuleAlertRest } from '../../types'; import { patchRules } from '../../rules/patch_rules'; import { importRulesQuerySchema, importRulesPayloadSchema } from '../schemas/import_rules_schema'; -import { ImportRulesSchema, importRulesSchema } from '../schemas/response/import_rules_schema'; import { getTupleDuplicateErrorsAndUniqueRules } from './utils'; import { validate } from './validate'; import { createRulesStreamFromNdJson } from '../../rules/create_rules_stream_from_ndjson'; diff --git a/x-pack/plugins/siem/server/lib/detection_engine/routes/rules/patch_rules_bulk_route.ts b/x-pack/plugins/siem/server/lib/detection_engine/routes/rules/patch_rules_bulk_route.ts index cc4d5e03500a4e..0d0cd28738c928 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/routes/rules/patch_rules_bulk_route.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/routes/rules/patch_rules_bulk_route.ts @@ -4,6 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ +import { rulesBulkSchema } from '../../../../../common/detection_engine/schemas/response/rules_bulk_schema'; import { IRouter } from '../../../../../../../../src/core/server'; import { DETECTION_ENGINE_RULES_URL } from '../../../../../common/constants'; import { SetupPlugins } from '../../../../plugin'; @@ -14,7 +15,6 @@ import { transformBulkError, buildRouteValidation, buildSiemResponse } from '../ import { getIdBulkError } from './utils'; import { transformValidateBulkError, validate } from './validate'; import { patchRulesBulkSchema } from '../schemas/patch_rules_bulk_schema'; -import { rulesBulkSchema } from '../schemas/response/rules_bulk_schema'; import { patchRules } from '../../rules/patch_rules'; import { updateRulesNotifications } from '../../rules/update_rules_notifications'; import { ruleStatusSavedObjectsClientFactory } from '../../signals/rule_status_saved_objects_client'; diff --git a/x-pack/plugins/siem/server/lib/detection_engine/routes/rules/update_rules_bulk_route.ts b/x-pack/plugins/siem/server/lib/detection_engine/routes/rules/update_rules_bulk_route.ts index c0dfecc71ce05f..335684dc38b32d 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/routes/rules/update_rules_bulk_route.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/routes/rules/update_rules_bulk_route.ts @@ -4,6 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ +import { rulesBulkSchema } from '../../../../../common/detection_engine/schemas/response/rules_bulk_schema'; import { IRouter } from '../../../../../../../../src/core/server'; import { DETECTION_ENGINE_RULES_URL } from '../../../../../common/constants'; import { SetupPlugins } from '../../../../plugin'; @@ -15,7 +16,6 @@ import { transformValidateBulkError, validate } from './validate'; import { buildRouteValidation, transformBulkError, buildSiemResponse } from '../utils'; import { updateRulesBulkSchema } from '../schemas/update_rules_bulk_schema'; import { updateRules } from '../../rules/update_rules'; -import { rulesBulkSchema } from '../schemas/response/rules_bulk_schema'; import { updateRulesNotifications } from '../../rules/update_rules_notifications'; import { ruleStatusSavedObjectsClientFactory } from '../../signals/rule_status_saved_objects_client'; diff --git a/x-pack/plugins/siem/server/lib/detection_engine/routes/rules/validate.test.ts b/x-pack/plugins/siem/server/lib/detection_engine/routes/rules/validate.test.ts index 9069202d4d3aa5..13a5bbd2afc0cb 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/routes/rules/validate.test.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/routes/rules/validate.test.ts @@ -14,9 +14,9 @@ import { } from './validate'; import { getResult } from '../__mocks__/request_responses'; import { FindResult } from '../../../../../../alerting/server'; -import { RulesSchema } from '../schemas/response/rules_schema'; import { BulkError } from '../utils'; import { setFeatureFlagsForTestsOnly, unSetFeatureFlagsForTestsOnly } from '../../feature_flags'; +import { RulesSchema } from '../../../../../common/detection_engine/schemas/response/rules_schema'; export const ruleOutput: RulesSchema = { actions: [], diff --git a/x-pack/plugins/siem/server/lib/detection_engine/routes/rules/validate.ts b/x-pack/plugins/siem/server/lib/detection_engine/routes/rules/validate.ts index cda3a4b81ed9b1..1220b12d1d1b12 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/routes/rules/validate.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/routes/rules/validate.ts @@ -9,6 +9,11 @@ import { fold } from 'fp-ts/lib/Either'; import { pipe } from 'fp-ts/lib/pipeable'; import * as t from 'io-ts'; +import { findRulesSchema } from '../../../../../common/detection_engine/schemas/response/find_rules_schema'; +import { + RulesSchema, + rulesSchema, +} from '../../../../../common/detection_engine/schemas/response/rules_schema'; import { formatErrors } from '../../../../../common/format_errors'; import { exactCheck } from '../../../../../common/exact_check'; import { PartialAlert, FindResult } from '../../../../../../alerting/server'; @@ -19,9 +24,7 @@ import { } from '../../rules/types'; import { OutputRuleAlertRest } from '../../types'; import { createBulkErrorObject, BulkError } from '../utils'; -import { rulesSchema, RulesSchema } from '../schemas/response/rules_schema'; import { transformFindAlerts, transform, transformAlertToRule } from './utils'; -import { findRulesSchema } from '../schemas/response/find_rules_schema'; import { RuleActions } from '../../rule_actions/types'; export const transformValidateFindAlerts = ( diff --git a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/check_type_dependents.ts b/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/check_type_dependents.ts deleted file mode 100644 index 1c1bee58f0c973..00000000000000 --- a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/check_type_dependents.ts +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import * as t from 'io-ts'; -import { Either, left, fold } from 'fp-ts/lib/Either'; -import { pipe } from 'fp-ts/lib/pipeable'; - -import { isMlRule } from '../../../../../../common/machine_learning/helpers'; -import { - dependentRulesSchema, - RequiredRulesSchema, - partialRulesSchema, - requiredRulesSchema, -} from './rules_schema'; -import { typeAndTimelineOnlySchema, TypeAndTimelineOnly } from './type_timeline_only_schema'; - -export const addSavedId = (typeAndTimelineOnly: TypeAndTimelineOnly): t.Mixed[] => { - if (typeAndTimelineOnly.type === 'saved_query') { - return [t.exact(t.type({ saved_id: dependentRulesSchema.props.saved_id }))]; - } else { - return []; - } -}; - -export const addTimelineTitle = (typeAndTimelineOnly: TypeAndTimelineOnly): t.Mixed[] => { - if (typeAndTimelineOnly.timeline_id != null) { - return [ - t.exact(t.type({ timeline_title: dependentRulesSchema.props.timeline_title })), - t.exact(t.type({ timeline_id: dependentRulesSchema.props.timeline_id })), - ]; - } else { - return []; - } -}; - -export const addQueryFields = (typeAndTimelineOnly: TypeAndTimelineOnly): t.Mixed[] => { - if (typeAndTimelineOnly.type === 'query' || typeAndTimelineOnly.type === 'saved_query') { - return [ - t.exact(t.type({ query: dependentRulesSchema.props.query })), - t.exact(t.type({ language: dependentRulesSchema.props.language })), - ]; - } else { - return []; - } -}; - -export const addMlFields = (typeAndTimelineOnly: TypeAndTimelineOnly): t.Mixed[] => { - if (isMlRule(typeAndTimelineOnly.type)) { - return [ - t.exact(t.type({ anomaly_threshold: dependentRulesSchema.props.anomaly_threshold })), - t.exact( - t.type({ machine_learning_job_id: dependentRulesSchema.props.machine_learning_job_id }) - ), - ]; - } else { - return []; - } -}; - -export const getDependents = (typeAndTimelineOnly: TypeAndTimelineOnly): t.Mixed => { - const dependents: t.Mixed[] = [ - t.exact(requiredRulesSchema), - t.exact(partialRulesSchema), - ...addSavedId(typeAndTimelineOnly), - ...addTimelineTitle(typeAndTimelineOnly), - ...addQueryFields(typeAndTimelineOnly), - ...addMlFields(typeAndTimelineOnly), - ]; - - if (dependents.length > 1) { - // This unsafe cast is because t.intersection does not use an array but rather a set of - // tuples and really does not look like they expected us to ever dynamically build up - // intersections, but here we are doing that. Looking at their code, although they limit - // the array elements to 5, it looks like you have N number of intersections - const unsafeCast: [t.Mixed, t.Mixed] = dependents as [t.Mixed, t.Mixed]; - return t.intersection(unsafeCast); - } else { - // We are not allowed to call t.intersection with a single value so we return without - // it here normally. - return dependents[0]; - } -}; - -export const checkTypeDependents = (input: unknown): Either => { - const typeOnlyDecoded = typeAndTimelineOnlySchema.decode(input); - const onLeft = (errors: t.Errors): Either => left(errors); - const onRight = ( - typeAndTimelineOnly: TypeAndTimelineOnly - ): Either => { - const intersections = getDependents(typeAndTimelineOnly); - return intersections.decode(input); - }; - return pipe(typeOnlyDecoded, fold(onLeft, onRight)); -}; diff --git a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/rules_schema.test.ts b/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/rules_schema.test.ts deleted file mode 100644 index ade4d12517aca2..00000000000000 --- a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/rules_schema.test.ts +++ /dev/null @@ -1,289 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { left } from 'fp-ts/lib/Either'; -import { pipe } from 'fp-ts/lib/pipeable'; - -import { rulesSchema, RulesSchema, removeList } from './rules_schema'; -import { getBaseResponsePayload } from './__mocks__/utils'; -import { setFeatureFlagsForTestsOnly, unSetFeatureFlagsForTestsOnly } from '../../../feature_flags'; -import { foldLeftRight, getPaths } from '../../../../../../common/test_utils'; -import { exactCheck } from '../../../../../../common/exact_check'; - -export const ANCHOR_DATE = '2020-02-20T03:57:54.037Z'; - -describe('rules_schema', () => { - beforeAll(() => { - setFeatureFlagsForTestsOnly(); - }); - - afterAll(() => { - unSetFeatureFlagsForTestsOnly(); - }); - - test('it should validate a type of "query" without anything extra', () => { - const payload = getBaseResponsePayload(); - - const decoded = rulesSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - const expected = getBaseResponsePayload(); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(expected); - }); - - test('it should NOT validate a type of "query" when it has extra data', () => { - const payload: RulesSchema & { invalid_extra_data?: string } = getBaseResponsePayload(); - payload.invalid_extra_data = 'invalid_extra_data'; - - const decoded = rulesSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual(['invalid keys "invalid_extra_data"']); - expect(message.schema).toEqual({}); - }); - - test('it should NOT validate invalid_data for the type', () => { - const payload: Omit & { type: string } = getBaseResponsePayload(); - payload.type = 'invalid_data'; - - const decoded = rulesSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "invalid_data" supplied to "type"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should NOT validate a type of "query" with a saved_id together', () => { - const payload = getBaseResponsePayload(); - payload.type = 'query'; - payload.saved_id = 'save id 123'; - - const decoded = rulesSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual(['invalid keys "saved_id"']); - expect(message.schema).toEqual({}); - }); - - test('it should validate a type of "saved_query" with a "saved_id" dependent', () => { - const payload = getBaseResponsePayload(); - payload.type = 'saved_query'; - payload.saved_id = 'save id 123'; - - const decoded = rulesSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - const expected = getBaseResponsePayload(); - - expected.type = 'saved_query'; - expected.saved_id = 'save id 123'; - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(expected); - }); - - test('it should NOT validate a type of "saved_query" without a "saved_id" dependent', () => { - const payload = getBaseResponsePayload(); - payload.type = 'saved_query'; - delete payload.saved_id; - - const decoded = rulesSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "undefined" supplied to "saved_id"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should NOT validate a type of "saved_query" when it has extra data', () => { - const payload: RulesSchema & { invalid_extra_data?: string } = getBaseResponsePayload(); - payload.type = 'saved_query'; - payload.saved_id = 'save id 123'; - payload.invalid_extra_data = 'invalid_extra_data'; - - const decoded = rulesSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual(['invalid keys "invalid_extra_data"']); - expect(message.schema).toEqual({}); - }); - - test('it should validate a type of "timeline_id" if there is a "timeline_title" dependent', () => { - const payload = getBaseResponsePayload(); - payload.timeline_id = 'some timeline id'; - payload.timeline_title = 'some timeline title'; - - const decoded = rulesSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - const expected = getBaseResponsePayload(); - expected.timeline_id = 'some timeline id'; - expected.timeline_title = 'some timeline title'; - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(expected); - }); - - test('it should NOT validate a type of "timeline_id" if there is "timeline_title" dependent when it has extra invalid data', () => { - const payload: RulesSchema & { invalid_extra_data?: string } = getBaseResponsePayload(); - payload.timeline_id = 'some timeline id'; - payload.timeline_title = 'some timeline title'; - payload.invalid_extra_data = 'invalid_extra_data'; - - const decoded = rulesSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual(['invalid keys "invalid_extra_data"']); - expect(message.schema).toEqual({}); - }); - - test('it should NOT validate a type of "timeline_id" if there is NOT a "timeline_title" dependent', () => { - const payload = getBaseResponsePayload(); - payload.timeline_id = 'some timeline id'; - - const decoded = rulesSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "undefined" supplied to "timeline_title"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should NOT validate a type of "timeline_title" if there is NOT a "timeline_id" dependent', () => { - const payload = getBaseResponsePayload(); - payload.timeline_title = 'some timeline title'; - - const decoded = rulesSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual(['invalid keys "timeline_title"']); - expect(message.schema).toEqual({}); - }); - - test('it should NOT validate a type of "saved_query" with a "saved_id" dependent and a "timeline_title" but there is NOT a "timeline_id"', () => { - const payload = getBaseResponsePayload(); - payload.saved_id = 'some saved id'; - payload.type = 'saved_query'; - payload.timeline_title = 'some timeline title'; - - const decoded = rulesSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual(['invalid keys "timeline_title"']); - expect(message.schema).toEqual({}); - }); - - test('it should NOT validate a type of "saved_query" with a "saved_id" dependent and a "timeline_id" but there is NOT a "timeline_title"', () => { - const payload = getBaseResponsePayload(); - payload.saved_id = 'some saved id'; - payload.type = 'saved_query'; - payload.timeline_id = 'some timeline id'; - - const decoded = rulesSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "undefined" supplied to "timeline_title"', - ]); - expect(message.schema).toEqual({}); - }); - - // TODO: (LIST-FEATURE) Remove this test once the feature flag is deployed - test('it should remove exceptions_list when we need it to be removed because the feature is off but there exists a list in the data', () => { - const payload = getBaseResponsePayload(); - const decoded = rulesSchema.decode(payload); - const listRemoved = removeList(decoded); - const message = pipe(listRemoved, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual({ - id: '7a7065d7-6e8b-4aae-8d20-c93613dec9f9', - created_at: '2020-02-20T03:57:54.037Z', - updated_at: '2020-02-20T03:57:54.037Z', - created_by: 'elastic', - description: 'some description', - enabled: true, - false_positives: ['false positive 1', 'false positive 2'], - from: 'now-6m', - immutable: false, - name: 'Query with a rule id', - query: 'user.name: root or user.name: admin', - references: ['test 1', 'test 2'], - severity: 'high', - updated_by: 'elastic_kibana', - tags: [], - to: 'now', - type: 'query', - threat: [], - version: 1, - output_index: '.siem-signals-hassanabad-frank-default', - max_signals: 100, - risk_score: 55, - language: 'kuery', - rule_id: 'query-rule-id', - interval: '5m', - status: 'succeeded', - status_date: '2020-02-22T16:47:50.047Z', - last_success_at: '2020-02-22T16:47:50.047Z', - last_success_message: 'succeeded', - }); - }); - - test('it should work with exceptions_list that are not there and not cause invalidation or errors', () => { - const payload = getBaseResponsePayload(); - const { exceptions_list, ...payloadWithoutLists } = payload; - const decoded = rulesSchema.decode(payloadWithoutLists); - const listRemoved = removeList(decoded); - const message = pipe(listRemoved, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual({ - id: '7a7065d7-6e8b-4aae-8d20-c93613dec9f9', - created_at: '2020-02-20T03:57:54.037Z', - updated_at: '2020-02-20T03:57:54.037Z', - created_by: 'elastic', - description: 'some description', - enabled: true, - false_positives: ['false positive 1', 'false positive 2'], - from: 'now-6m', - immutable: false, - name: 'Query with a rule id', - query: 'user.name: root or user.name: admin', - references: ['test 1', 'test 2'], - severity: 'high', - updated_by: 'elastic_kibana', - tags: [], - to: 'now', - type: 'query', - threat: [], - version: 1, - output_index: '.siem-signals-hassanabad-frank-default', - max_signals: 100, - risk_score: 55, - language: 'kuery', - rule_id: 'query-rule-id', - interval: '5m', - status: 'succeeded', - status_date: '2020-02-22T16:47:50.047Z', - last_success_at: '2020-02-22T16:47:50.047Z', - last_success_message: 'succeeded', - }); - }); -}); diff --git a/x-pack/plugins/siem/server/lib/detection_engine/signals/build_exceptions_query.test.ts b/x-pack/plugins/siem/server/lib/detection_engine/signals/build_exceptions_query.test.ts index ec8db77dac725d..772ebd932698b8 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/signals/build_exceptions_query.test.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/signals/build_exceptions_query.test.ts @@ -15,7 +15,7 @@ import { formatQuery, getLanguageBooleanOperator, } from './build_exceptions_query'; -import { List } from '../routes/schemas/types/lists_default_array'; +import { List } from '../../../../common/detection_engine/schemas/types/lists_default_array'; describe('build_exceptions_query', () => { describe('getLanguageBooleanOperator', () => { diff --git a/x-pack/plugins/siem/server/lib/detection_engine/signals/build_exceptions_query.ts b/x-pack/plugins/siem/server/lib/detection_engine/signals/build_exceptions_query.ts index e7be5025a51f0c..b33a2376589ef8 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/signals/build_exceptions_query.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/signals/build_exceptions_query.ts @@ -3,8 +3,12 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ +import { + ListOperator, + ListValues, + List, +} from '../../../../common/detection_engine/schemas/types/lists_default_array'; import { Query } from '../../../../../../../src/plugins/data/server'; -import { List, ListOperator, ListValues } from '../routes/schemas/types/lists_default_array'; import { RuleAlertParams, Language } from '../types'; type Operators = 'and' | 'or' | 'not'; diff --git a/x-pack/plugins/siem/server/lib/detection_engine/signals/filter_events_with_list.ts b/x-pack/plugins/siem/server/lib/detection_engine/signals/filter_events_with_list.ts index 400bb5dda46e75..07435fda0da2e3 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/signals/filter_events_with_list.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/signals/filter_events_with_list.ts @@ -6,11 +6,11 @@ import { get } from 'lodash/fp'; import { Logger } from 'src/core/server'; +import { List } from '../../../../common/detection_engine/schemas/types/lists_default_array'; import { type } from '../../../../../lists/common/schemas/common'; import { ListClient } from '../../../../../lists/server'; import { SignalSearchResponse, SearchTypes } from './types'; import { RuleAlertParams } from '../types'; -import { List } from '../routes/schemas/types/lists_default_array'; interface FilterEventsAgainstList { listClient: ListClient; diff --git a/x-pack/plugins/siem/server/lib/detection_engine/types.ts b/x-pack/plugins/siem/server/lib/detection_engine/types.ts index f2026804da51ab..53c8a9bf0a7e77 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/types.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/types.ts @@ -4,10 +4,10 @@ * you may not use this file except in compliance with the Elastic License. */ +import { ListsDefaultArraySchema } from '../../../common/detection_engine/schemas/types/lists_default_array'; import { CallAPIOptions } from '../../../../../../src/core/server'; import { Filter } from '../../../../../../src/plugins/data/server'; import { IRuleStatusAttributes } from './rules/types'; -import { ListsDefaultArraySchema } from './routes/schemas/types/lists_default_array'; import { RuleAlertAction, RuleType } from '../../../common/detection_engine/types'; export type PartialFilter = Partial; diff --git a/x-pack/plugins/siem/server/lib/timeline/routes/import_timelines_route.ts b/x-pack/plugins/siem/server/lib/timeline/routes/import_timelines_route.ts index 48c6081e855a01..c16b73ff51b566 100644 --- a/x-pack/plugins/siem/server/lib/timeline/routes/import_timelines_route.ts +++ b/x-pack/plugins/siem/server/lib/timeline/routes/import_timelines_route.ts @@ -7,6 +7,7 @@ import { extname } from 'path'; import { chunk, omit } from 'lodash/fp'; +import { importRulesSchema } from '../../../../common/detection_engine/schemas/response/import_rules_schema'; import { createPromiseFromStreams } from '../../../../../../../src/legacy/utils'; import { IRouter } from '../../../../../../../src/core/server'; @@ -16,7 +17,6 @@ import { SetupPlugins } from '../../../plugin'; import { ConfigType } from '../../../config'; import { buildRouteValidation } from '../../../utils/build_validation/route_validation'; -import { importRulesSchema } from '../../detection_engine/routes/schemas/response/import_rules_schema'; import { validate } from '../../detection_engine/routes/rules/validate'; import { buildSiemResponse, From 78d5026fbd833ddb8deb0f6bb968f4f82d2559e3 Mon Sep 17 00:00:00 2001 From: Nathan L Smith Date: Mon, 1 Jun 2020 20:38:53 -0500 Subject: [PATCH 13/24] APM-specific Jest configuration (#67858) Update the x-pack `createJestConfig` function to take the `rootDir` as an argument, which allows for easier overriding of the Jest configuration for a specific directory. Previously we would run Jest in development from the x-pack directory by running something like: ``` node scripts/jest.js --testPathPattern=plugins/apm --watch ``` Currently (for me anyway) this is failing with: ``` Error: EMFILE: too many open files, watch at FSEvent.FSWatcher._handle.onchange (internal/fs/watchers.js:123:28) ``` and it would sometimes not correctly test only the changed files when a change in APM was made. It was also difficult to configure correctly with the [VSCode Jest extension](https://marketplace.visualstudio.com/items?itemName=Orta.vscode-jest). Add a jest.config.js for APM. This makes running with `--watch` better about which files it chooses to re-run and makes the VSCode extension work (including coverage mapping) with minimal configuration. --- x-pack/dev-tools/jest/create_jest_config.js | 12 +++--- x-pack/dev-tools/jest/index.js | 1 + x-pack/plugins/apm/dev_docs/vscode_setup.md | 28 +++++-------- x-pack/plugins/apm/jest.config.js | 44 +++++++++++++++++++++ x-pack/plugins/apm/readme.md | 14 +++++-- 5 files changed, 71 insertions(+), 28 deletions(-) create mode 100644 x-pack/plugins/apm/jest.config.js diff --git a/x-pack/dev-tools/jest/create_jest_config.js b/x-pack/dev-tools/jest/create_jest_config.js index 3d8b45e7d1b837..a222e11d28f4ac 100644 --- a/x-pack/dev-tools/jest/create_jest_config.js +++ b/x-pack/dev-tools/jest/create_jest_config.js @@ -4,10 +4,10 @@ * you may not use this file except in compliance with the Elastic License. */ -export function createJestConfig({ kibanaDirectory, xPackKibanaDirectory }) { +export function createJestConfig({ kibanaDirectory, rootDir, xPackKibanaDirectory }) { const fileMockPath = `${kibanaDirectory}/src/dev/jest/mocks/file_mock.js`; return { - rootDir: xPackKibanaDirectory, + rootDir, roots: ['/plugins', '/legacy/plugins', '/legacy/server'], moduleFileExtensions: ['js', 'json', 'ts', 'tsx'], moduleNameMapper: { @@ -44,15 +44,15 @@ export function createJestConfig({ kibanaDirectory, xPackKibanaDirectory }) { '!**/plugins/apm/e2e/**', ], coveragePathIgnorePatterns: ['.*\\.d\\.ts'], - coverageDirectory: '/../target/kibana-coverage/jest', + coverageDirectory: `${kibanaDirectory}/target/kibana-coverage/jest`, coverageReporters: !!process.env.CODE_COVERAGE ? ['json'] : ['html'], setupFiles: [ `${kibanaDirectory}/src/dev/jest/setup/babel_polyfill.js`, - `/dev-tools/jest/setup/polyfills.js`, - `/dev-tools/jest/setup/enzyme.js`, + `${xPackKibanaDirectory}/dev-tools/jest/setup/polyfills.js`, + `${xPackKibanaDirectory}/dev-tools/jest/setup/enzyme.js`, ], setupFilesAfterEnv: [ - `/dev-tools/jest/setup/setup_test.js`, + `${xPackKibanaDirectory}/dev-tools/jest/setup/setup_test.js`, `${kibanaDirectory}/src/dev/jest/setup/mocks.js`, `${kibanaDirectory}/src/dev/jest/setup/react_testing_library.js`, ], diff --git a/x-pack/dev-tools/jest/index.js b/x-pack/dev-tools/jest/index.js index f61c50f989503e..2f831e33cdd112 100644 --- a/x-pack/dev-tools/jest/index.js +++ b/x-pack/dev-tools/jest/index.js @@ -14,6 +14,7 @@ export function runJest() { const config = JSON.stringify( createJestConfig({ kibanaDirectory: resolve(__dirname, '../../..'), + rootDir: resolve(__dirname, '../..'), xPackKibanaDirectory: resolve(__dirname, '../..'), }) ); diff --git a/x-pack/plugins/apm/dev_docs/vscode_setup.md b/x-pack/plugins/apm/dev_docs/vscode_setup.md index 1c80d1476520d1..c7adad4fd09420 100644 --- a/x-pack/plugins/apm/dev_docs/vscode_setup.md +++ b/x-pack/plugins/apm/dev_docs/vscode_setup.md @@ -1,8 +1,8 @@ -### Visual Studio Code +# Visual Studio Code When using [Visual Studio Code](https://code.visualstudio.com/) with APM it's best to set up a [multi-root workspace](https://code.visualstudio.com/docs/editor/multi-root-workspaces) and add the `x-pack/plugins/apm` directory, the `x-pack` directory, and the root of the Kibana repository to the workspace. This makes it so you can navigate and search within APM and use the wider workspace roots when you need to widen your search. -#### Using the Jest extension +## Using the Jest extension The [vscode-jest extension](https://marketplace.visualstudio.com/items?itemName=Orta.vscode-jest) is a good way to run your Jest tests inside the editor. @@ -22,31 +22,21 @@ If you have a workspace configured as described above you should have: "jest.disabledWorkspaceFolders": ["kibana", "x-pack"] ``` -in your Workspace settings, and: - -```json -"jest.pathToJest": "node scripts/jest.js --testPathPattern=plugins/apm", -"jest.rootPath": "../../.." -``` - -in the settings for the APM folder. - -#### Jest debugging +## Jest debugging To make the [VSCode debugger](https://vscode.readthedocs.io/en/latest/editor/debugging/) work with Jest (you can set breakpoints in the code and tests and use the VSCode debugger) you'll need the [Node Debug extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode.node-debug2) installed and can set up a launch configuration like: ```json { "type": "node", - "name": "APM Jest", + "name": "vscode-jest-tests", "request": "launch", - "args": ["--runInBand", "--testPathPattern=plugins/apm"], - "cwd": "${workspaceFolder}/../../..", - "console": "internalConsole", - "internalConsoleOptions": "openOnSessionStart", + "args": ["--runInBand"], + "cwd": "${workspaceFolder}", + "console": "integratedTerminal", + "internalConsoleOptions": "neverOpen", "disableOptimisticBPs": true, - "program": "${workspaceFolder}/../../../scripts/jest.js", - "runtimeVersion": "10.15.2" + "program": "${workspaceFolder}/../../../node_modules/jest/bin/jest" } ``` diff --git a/x-pack/plugins/apm/jest.config.js b/x-pack/plugins/apm/jest.config.js new file mode 100644 index 00000000000000..c3ae694fe8e142 --- /dev/null +++ b/x-pack/plugins/apm/jest.config.js @@ -0,0 +1,44 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +// This is an APM-specific Jest configuration which overrides the x-pack +// configuration. It's intended for use in development and does not run in CI, +// which runs the entire x-pack suite. Run `npx jest`. + +require('../../../src/setup_node_env'); + +const { createJestConfig } = require('../../dev-tools/jest/create_jest_config'); +const { resolve } = require('path'); + +const rootDir = resolve(__dirname, '.'); +const xPackKibanaDirectory = resolve(__dirname, '../..'); +const kibanaDirectory = resolve(__dirname, '../../..'); + +const jestConfig = createJestConfig({ + kibanaDirectory, + rootDir, + xPackKibanaDirectory, +}); + +module.exports = { + ...jestConfig, + reporters: ['default'], + roots: [`${rootDir}/common`, `${rootDir}/public`, `${rootDir}/server`], + collectCoverage: true, + collectCoverageFrom: [ + '**/*.{js,jsx,ts,tsx}', + '!**/{__test__,__snapshots__,__examples__,integration_tests,tests}/**', + '!**/*.test.{js,ts,tsx}', + '!**/dev_docs/**', + '!**/e2e/**', + '!**/scripts/**', + '!**/target/**', + '!**/typings/**', + '!**/mocks/**', + ], + coverageDirectory: `${rootDir}/target/coverage/jest`, + coverageReporters: ['html'], +}; diff --git a/x-pack/plugins/apm/readme.md b/x-pack/plugins/apm/readme.md index 62465e920d793c..ceed5e6c39716a 100644 --- a/x-pack/plugins/apm/readme.md +++ b/x-pack/plugins/apm/readme.md @@ -39,18 +39,26 @@ _Starts Kibana (:5701), APM Server (:8201) and Elasticsearch (:9201). Ingests sa ### Unit testing -Note: Run the following commands from `kibana/x-pack`. +Note: Run the following commands from `kibana/x-pack/plugins/apm`. #### Run unit tests ``` -node scripts/jest.js plugins/apm --watch +npx jest --watch ``` #### Update snapshots ``` -node scripts/jest.js plugins/apm --updateSnapshot +npx jest --updateSnapshot +``` + +#### Coverage + +HTML coverage report can be found in target/coverage/jest after tests have run. + +``` +open target/coverage/jest/index.html ``` ### Functional tests From ce45dad8b6979b0123d15920e25b4289bc5bdb9b Mon Sep 17 00:00:00 2001 From: Yuliia Naumenko Date: Mon, 1 Jun 2020 18:45:36 -0700 Subject: [PATCH 14/24] Changed alerting API endpoints urls, bodies and params to follow Kibana STYLEGUIDE (#66838) * Changed alerting API endpoints urls, bodies and params to follow Kibana STYLEGUIDE * Changed alerting REST API to keep the pattern 'alerts/alert/{id}' * fixed tests * fixed tests * Fixed jest tests * Renamed plugin from alerting to alerts * fixed tests * fixed tests * Fixed alert type check error * Fixed find api * fixed type checks * fixed tests security issues * Fixed view in app * - Co-authored-by: Elastic Machine --- .github/CODEOWNERS | 2 +- examples/alerting_example/kibana.json | 2 +- .../public/alert_types/astros.tsx | 8 +- .../public/alert_types/index.ts | 10 +-- .../public/components/view_alert.tsx | 2 +- .../public/components/view_astros_alert.tsx | 2 +- examples/alerting_example/public/plugin.tsx | 10 +-- .../server/alert_types/always_firing.ts | 2 +- .../server/alert_types/astros.ts | 2 +- examples/alerting_example/server/plugin.ts | 10 +-- rfcs/text/0003_handler_interface.md | 2 +- x-pack/.i18nrc.json | 2 +- x-pack/legacy/plugins/monitoring/index.ts | 2 +- x-pack/plugins/actions/README.md | 4 +- x-pack/plugins/alerting_builtins/README.md | 2 +- x-pack/plugins/alerting_builtins/kibana.json | 2 +- .../server/alert_types/index.ts | 2 +- .../index_threshold/action_context.ts | 2 +- .../alert_types/index_threshold/index.ts | 6 +- .../index_threshold/lib/date_range_info.ts | 2 +- .../index_threshold/lib/time_series_types.ts | 2 +- .../alerting_builtins/server/plugin.test.ts | 6 +- .../alerting_builtins/server/plugin.ts | 4 +- .../plugins/alerting_builtins/server/types.ts | 6 +- x-pack/plugins/{alerting => alerts}/README.md | 62 +++++++-------- .../{alerting => alerts}/common/alert.ts | 0 .../common/alert_instance.ts | 0 .../common/alert_navigation.ts | 0 .../common/alert_task_instance.ts | 0 .../{alerting => alerts}/common/alert_type.ts | 0 .../common/date_from_string.test.ts | 0 .../common/date_from_string.ts | 0 .../{alerting => alerts}/common/index.ts | 2 +- .../common/parse_duration.test.ts | 0 .../common/parse_duration.ts | 0 .../plugins/{alerting => alerts}/kibana.json | 4 +- .../public/alert_api.test.ts | 12 +-- .../{alerting => alerts}/public/alert_api.ts | 10 +-- .../alert_navigation_registry.mock.ts | 0 .../alert_navigation_registry.test.ts | 0 .../alert_navigation_registry.ts | 6 +- .../public/alert_navigation_registry/index.ts | 0 .../public/alert_navigation_registry/types.ts | 0 .../{alerting => alerts}/public/index.ts | 0 .../{alerting => alerts}/public/mocks.ts | 0 .../{alerting => alerts}/public/plugin.ts | 0 .../alert_instance/alert_instance.test.ts | 0 .../server/alert_instance/alert_instance.ts | 0 .../create_alert_instance_factory.test.ts | 0 .../create_alert_instance_factory.ts | 0 .../server/alert_instance/index.ts | 0 .../server/alert_type_registry.mock.ts | 0 .../server/alert_type_registry.test.ts | 2 +- .../server/alert_type_registry.ts | 6 +- .../server/alerts_client.mock.ts | 0 .../server/alerts_client.test.ts | 8 +- .../server/alerts_client.ts | 55 +++++++------- .../server/alerts_client_factory.test.ts | 8 +- .../server/alerts_client_factory.ts | 6 +- .../server/constants/plugin.ts | 6 +- .../{alerting => alerts}/server/index.ts | 0 .../lib/delete_task_if_it_exists.test.ts | 0 .../server/lib/delete_task_if_it_exists.ts | 0 .../{alerting => alerts}/server/lib/index.ts | 0 .../lib/is_alert_not_found_error.test.ts | 0 .../server/lib/is_alert_not_found_error.ts | 0 .../server/lib/license_api_access.ts | 0 .../server/lib/license_state.mock.ts | 0 .../server/lib/license_state.test.ts | 2 +- .../server/lib/license_state.ts | 6 +- .../server/lib/result_type.ts | 0 .../server/lib/types.test.ts | 0 .../{alerting => alerts}/server/lib/types.ts | 0 .../lib/validate_alert_type_params.test.ts | 0 .../server/lib/validate_alert_type_params.ts | 0 .../{alerting => alerts}/server/mocks.ts | 0 .../server/plugin.test.ts | 4 +- .../{alerting => alerts}/server/plugin.ts | 2 +- .../server/routes/_mock_handler_arguments.ts | 0 .../server/routes/create.test.ts | 2 +- .../server/routes/create.ts | 2 +- .../server/routes/delete.test.ts | 2 +- .../server/routes/delete.ts | 2 +- .../server/routes/disable.test.ts | 2 +- .../server/routes/disable.ts | 2 +- .../server/routes/enable.test.ts | 2 +- .../server/routes/enable.ts | 2 +- .../server/routes/find.test.ts | 7 +- .../server/routes/find.ts | 33 ++++---- .../server/routes/get.test.ts | 2 +- .../{alerting => alerts}/server/routes/get.ts | 2 +- .../server/routes/get_alert_state.test.ts | 6 +- .../server/routes/get_alert_state.ts | 2 +- .../server/routes/health.test.ts | 2 +- .../server/routes/health.ts | 2 +- .../server/routes/index.ts | 0 .../server/routes/lib/error_handler.ts | 2 +- .../alerts/server/routes/lib/rename_keys.ts | 16 ++++ .../server/routes/list_alert_types.test.ts | 6 +- .../server/routes/list_alert_types.ts | 2 +- .../server/routes/mute_all.test.ts | 2 +- .../server/routes/mute_all.ts | 2 +- .../server/routes/mute_instance.test.ts | 6 +- .../server/routes/mute_instance.ts | 18 +++-- .../server/routes/unmute_all.test.ts | 2 +- .../server/routes/unmute_all.ts | 2 +- .../server/routes/unmute_instance.test.ts | 2 +- .../server/routes/unmute_instance.ts | 2 +- .../server/routes/update.test.ts | 2 +- .../server/routes/update.ts | 2 +- .../server/routes/update_api_key.test.ts | 2 +- .../server/routes/update_api_key.ts | 2 +- .../server/saved_objects/index.ts | 0 .../server/saved_objects/mappings.json | 0 .../task_runner/alert_task_instance.test.ts | 2 +- .../server/task_runner/alert_task_instance.ts | 2 +- .../create_execution_handler.test.ts | 0 .../task_runner/create_execution_handler.ts | 2 +- .../task_runner/get_next_run_at.test.ts | 0 .../server/task_runner/get_next_run_at.ts | 0 .../server/task_runner/index.ts | 0 .../server/task_runner/task_runner.test.ts | 4 +- .../server/task_runner/task_runner.ts | 2 +- .../task_runner/task_runner_factory.test.ts | 4 +- .../server/task_runner/task_runner_factory.ts | 6 +- .../transform_action_params.test.ts | 0 .../task_runner/transform_action_params.ts | 0 .../server/test_utils/index.ts | 0 .../{alerting => alerts}/server/types.ts | 0 .../server/usage/alerts_telemetry.test.ts | 0 .../server/usage/alerts_telemetry.ts | 0 .../usage/alerts_usage_collector.test.ts | 0 .../server/usage/alerts_usage_collector.ts | 0 .../server/usage/index.ts | 0 .../{alerting => alerts}/server/usage/task.ts | 0 .../server/usage/types.ts | 0 x-pack/plugins/apm/kibana.json | 2 +- .../components/app/ServiceDetails/index.tsx | 2 +- x-pack/plugins/apm/public/plugin.ts | 6 +- .../server/lib/alerts/register_apm_alerts.ts | 8 +- .../alerts/register_error_rate_alert_type.ts | 8 +- ...egister_transaction_duration_alert_type.ts | 8 +- x-pack/plugins/apm/server/plugin.ts | 8 +- x-pack/plugins/infra/kibana.json | 2 +- .../lib/adapters/framework/adapter_types.ts | 4 +- .../inventory_metric_threshold_executor.ts | 2 +- .../log_threshold_executor.test.ts | 4 +- .../log_threshold/log_threshold_executor.ts | 2 +- .../register_log_threshold_alert_type.ts | 2 +- .../metric_threshold_executor.test.ts | 4 +- .../metric_threshold_executor.ts | 2 +- .../lib/alerting/register_alert_types.ts | 2 +- x-pack/plugins/infra/server/plugin.ts | 2 +- x-pack/plugins/monitoring/kibana.json | 2 +- .../public/components/alerts/status.tsx | 2 +- .../server/alerts/cluster_state.test.ts | 2 +- .../monitoring/server/alerts/cluster_state.ts | 2 +- .../server/alerts/license_expiration.test.ts | 2 +- .../server/alerts/license_expiration.ts | 2 +- .../monitoring/server/alerts/types.d.ts | 2 +- .../server/lib/alerts/cluster_state.lib.ts | 2 +- .../server/lib/alerts/fetch_status.ts | 2 +- .../server/lib/alerts/get_prepared_alert.ts | 2 +- .../lib/alerts/license_expiration.lib.ts | 2 +- x-pack/plugins/monitoring/server/plugin.ts | 12 +-- .../schemas/common/schemas.ts | 2 +- .../detection_engine/transform_actions.ts | 2 +- .../siem/common/detection_engine/types.ts | 2 +- x-pack/plugins/siem/kibana.json | 2 +- .../description_step/actions_description.tsx | 2 +- .../rules/rule_actions_field/index.tsx | 2 +- .../detection_engine/rules/types.ts | 2 +- .../pages/detection_engine/rules/types.ts | 2 +- .../server/lib/detection_engine/README.md | 2 +- .../create_notifications.test.ts | 2 +- .../notifications/create_notifications.ts | 2 +- .../delete_notifications.test.ts | 2 +- .../notifications/find_notifications.ts | 2 +- .../notifications/get_signals_count.ts | 2 +- .../notifications/read_notifications.test.ts | 2 +- .../notifications/read_notifications.ts | 2 +- .../rules_notification_alert_type.test.ts | 2 +- .../schedule_notification_actions.ts | 2 +- .../detection_engine/notifications/types.ts | 4 +- .../update_notifications.test.ts | 2 +- .../notifications/update_notifications.ts | 2 +- .../routes/__mocks__/request_context.ts | 2 +- .../routes/rules/utils.test.ts | 4 +- .../detection_engine/routes/rules/utils.ts | 2 +- .../routes/rules/validate.test.ts | 2 +- .../detection_engine/routes/rules/validate.ts | 2 +- .../add_prepackaged_rules_schema.test.ts | 2 +- .../schemas/create_rules_schema.test.ts | 2 +- .../schemas/import_rules_schema.test.ts | 2 +- .../routes/schemas/patch_rules_schema.test.ts | 2 +- .../schemas/update_rules_schema.test.ts | 2 +- .../create_rule_actions_saved_object.ts | 2 +- .../delete_rule_actions_saved_object.ts | 2 +- .../get_rule_actions_saved_object.ts | 2 +- ...ate_or_create_rule_actions_saved_object.ts | 2 +- .../update_rule_actions_saved_object.ts | 2 +- .../rules/create_rules.test.ts | 2 +- .../detection_engine/rules/create_rules.ts | 2 +- .../rules/delete_rules.test.ts | 2 +- .../lib/detection_engine/rules/find_rules.ts | 2 +- .../get_existing_prepackaged_rules.test.ts | 2 +- .../rules/get_existing_prepackaged_rules.ts | 2 +- .../rules/get_export_all.test.ts | 2 +- .../detection_engine/rules/get_export_all.ts | 2 +- .../rules/get_export_by_object_ids.test.ts | 2 +- .../rules/get_export_by_object_ids.ts | 2 +- .../rules/install_prepacked_rules.ts | 4 +- .../rules/patch_rules.test.ts | 2 +- .../lib/detection_engine/rules/patch_rules.ts | 2 +- .../detection_engine/rules/read_rules.test.ts | 2 +- .../lib/detection_engine/rules/read_rules.ts | 2 +- .../lib/detection_engine/rules/types.ts | 4 +- .../rules/update_prepacked_rules.test.ts | 2 +- .../rules/update_prepacked_rules.ts | 2 +- .../rules/update_rules.test.ts | 2 +- .../detection_engine/rules/update_rules.ts | 2 +- .../rules/update_rules_notifications.ts | 2 +- .../scripts/get_alert_instances.sh | 4 +- .../scripts/get_alert_types.sh | 4 +- .../signals/bulk_create_ml_signals.ts | 2 +- .../signals/get_filter.test.ts | 2 +- .../detection_engine/signals/get_filter.ts | 2 +- .../signals/get_input_output_index.test.ts | 2 +- .../signals/get_input_output_index.ts | 2 +- .../signals/search_after_bulk_create.test.ts | 2 +- .../signals/search_after_bulk_create.ts | 2 +- .../signals/signal_rule_alert_type.test.ts | 2 +- .../signals/single_bulk_create.test.ts | 2 +- .../signals/single_bulk_create.ts | 2 +- .../signals/single_search_after.test.ts | 2 +- .../signals/single_search_after.ts | 2 +- .../lib/detection_engine/signals/types.ts | 2 +- .../lib/detection_engine/signals/utils.ts | 2 +- .../detection_engine/tags/read_tags.test.ts | 2 +- .../lib/detection_engine/tags/read_tags.ts | 2 +- x-pack/plugins/siem/server/plugin.ts | 10 +-- .../translations/translations/ja-JP.json | 20 ++--- .../translations/translations/zh-CN.json | 20 ++--- x-pack/plugins/triggers_actions_ui/README.md | 6 +- .../plugins/triggers_actions_ui/kibana.json | 2 +- .../public/application/app.tsx | 4 +- .../threshold/visualization.tsx | 2 +- .../public/application/constants/index.ts | 2 +- .../application/lib/action_variables.ts | 2 +- .../public/application/lib/alert_api.test.ts | 76 +++++++++---------- .../public/application/lib/alert_api.ts | 26 +++---- .../action_form.test.tsx | 2 +- .../actions_connectors_list.test.tsx | 2 +- .../components/view_in_app.test.tsx | 8 +- .../alert_details/components/view_in_app.tsx | 11 +-- .../sections/alert_form/alert_add.test.tsx | 6 +- .../sections/alert_form/alert_edit.test.tsx | 2 +- .../sections/alert_form/alert_form.test.tsx | 4 +- .../sections/alert_form/alert_form.tsx | 5 +- .../sections/alert_form/alert_reducer.test.ts | 2 +- .../components/alerts_list.test.tsx | 2 +- .../alerts_list/components/alerts_list.tsx | 2 +- .../triggers_actions_ui/public/plugin.ts | 6 +- .../triggers_actions_ui/public/types.ts | 4 +- x-pack/plugins/uptime/kibana.json | 2 +- .../lib/adapters/framework/adapter_types.ts | 2 +- .../lib/alerts/__tests__/status_check.test.ts | 6 +- .../uptime/server/lib/alerts/status_check.ts | 2 +- .../plugins/uptime/server/lib/alerts/types.ts | 2 +- x-pack/plugins/uptime/server/uptime_server.ts | 2 +- .../fixtures/plugins/alerts/kibana.json | 2 +- .../plugins/alerts/server/alert_types.ts | 22 +++--- .../fixtures/plugins/alerts/server/plugin.ts | 21 +++-- .../common/lib/alert_utils.ts | 28 ++++--- .../common/lib/object_remover.ts | 5 +- .../security_and_spaces/scenarios.ts | 4 +- .../tests/actions/get_all.ts | 4 +- .../tests/alerting/alerts.ts | 16 ++-- .../tests/alerting/create.ts | 18 ++--- .../tests/alerting/delete.ts | 18 ++--- .../tests/alerting/disable.ts | 12 +-- .../tests/alerting/enable.ts | 16 ++-- .../tests/alerting/find.ts | 20 ++--- .../security_and_spaces/tests/alerting/get.ts | 14 ++-- .../tests/alerting/get_alert_state.ts | 14 ++-- .../tests/alerting/index.ts | 2 +- .../tests/alerting/list_alert_types.ts | 2 +- .../tests/alerting/mute_all.ts | 6 +- .../tests/alerting/mute_instance.ts | 16 ++-- .../tests/alerting/unmute_all.ts | 8 +- .../tests/alerting/unmute_instance.ts | 10 ++- .../tests/alerting/update.ts | 40 +++++----- .../tests/alerting/update_api_key.ts | 16 ++-- .../spaces_only/tests/alerting/alerts_base.ts | 12 +-- .../index_threshold/alert.ts | 4 +- .../spaces_only/tests/alerting/create.ts | 8 +- .../spaces_only/tests/alerting/delete.ts | 8 +- .../spaces_only/tests/alerting/disable.ts | 8 +- .../spaces_only/tests/alerting/enable.ts | 10 +-- .../spaces_only/tests/alerting/find.ts | 12 +-- .../spaces_only/tests/alerting/get.ts | 14 ++-- .../tests/alerting/get_alert_state.ts | 26 ++++--- .../tests/alerting/list_alert_types.ts | 16 +++- .../spaces_only/tests/alerting/mute_all.ts | 6 +- .../tests/alerting/mute_instance.ts | 6 +- .../spaces_only/tests/alerting/unmute_all.ts | 6 +- .../tests/alerting/unmute_instance.ts | 6 +- .../spaces_only/tests/alerting/update.ts | 12 +-- .../tests/alerting/update_api_key.ts | 10 +-- .../case_api_integration/common/config.ts | 1 - .../apps/triggers_actions_ui/alerts.ts | 2 +- .../apps/uptime/alert_flyout.ts | 8 +- .../fixtures/plugins/alerts/kibana.json | 2 +- .../fixtures/plugins/alerts/public/plugin.ts | 10 +-- .../fixtures/plugins/alerts/server/plugin.ts | 18 ++--- .../services/alerting/alerts.ts | 12 +-- x-pack/typings/hapi.d.ts | 4 +- 317 files changed, 766 insertions(+), 734 deletions(-) rename x-pack/plugins/{alerting => alerts}/README.md (91%) rename x-pack/plugins/{alerting => alerts}/common/alert.ts (100%) rename x-pack/plugins/{alerting => alerts}/common/alert_instance.ts (100%) rename x-pack/plugins/{alerting => alerts}/common/alert_navigation.ts (100%) rename x-pack/plugins/{alerting => alerts}/common/alert_task_instance.ts (100%) rename x-pack/plugins/{alerting => alerts}/common/alert_type.ts (100%) rename x-pack/plugins/{alerting => alerts}/common/date_from_string.test.ts (100%) rename x-pack/plugins/{alerting => alerts}/common/date_from_string.ts (100%) rename x-pack/plugins/{alerting => alerts}/common/index.ts (92%) rename x-pack/plugins/{alerting => alerts}/common/parse_duration.test.ts (100%) rename x-pack/plugins/{alerting => alerts}/common/parse_duration.ts (100%) rename x-pack/plugins/{alerting => alerts}/kibana.json (80%) rename x-pack/plugins/{alerting => alerts}/public/alert_api.test.ts (92%) rename x-pack/plugins/{alerting => alerts}/public/alert_api.ts (84%) rename x-pack/plugins/{alerting => alerts}/public/alert_navigation_registry/alert_navigation_registry.mock.ts (100%) rename x-pack/plugins/{alerting => alerts}/public/alert_navigation_registry/alert_navigation_registry.test.ts (100%) rename x-pack/plugins/{alerting => alerts}/public/alert_navigation_registry/alert_navigation_registry.ts (90%) rename x-pack/plugins/{alerting => alerts}/public/alert_navigation_registry/index.ts (100%) rename x-pack/plugins/{alerting => alerts}/public/alert_navigation_registry/types.ts (100%) rename x-pack/plugins/{alerting => alerts}/public/index.ts (100%) rename x-pack/plugins/{alerting => alerts}/public/mocks.ts (100%) rename x-pack/plugins/{alerting => alerts}/public/plugin.ts (100%) rename x-pack/plugins/{alerting => alerts}/server/alert_instance/alert_instance.test.ts (100%) rename x-pack/plugins/{alerting => alerts}/server/alert_instance/alert_instance.ts (100%) rename x-pack/plugins/{alerting => alerts}/server/alert_instance/create_alert_instance_factory.test.ts (100%) rename x-pack/plugins/{alerting => alerts}/server/alert_instance/create_alert_instance_factory.ts (100%) rename x-pack/plugins/{alerting => alerts}/server/alert_instance/index.ts (100%) rename x-pack/plugins/{alerting => alerts}/server/alert_type_registry.mock.ts (100%) rename x-pack/plugins/{alerting => alerts}/server/alert_type_registry.test.ts (98%) rename x-pack/plugins/{alerting => alerts}/server/alert_type_registry.ts (89%) rename x-pack/plugins/{alerting => alerts}/server/alerts_client.mock.ts (100%) rename x-pack/plugins/{alerting => alerts}/server/alerts_client.test.ts (99%) rename x-pack/plugins/{alerting => alerts}/server/alerts_client.ts (96%) rename x-pack/plugins/{alerting => alerts}/server/alerts_client_factory.test.ts (95%) rename x-pack/plugins/{alerting => alerts}/server/alerts_client_factory.ts (95%) rename x-pack/plugins/{alerting => alerts}/server/constants/plugin.ts (86%) rename x-pack/plugins/{alerting => alerts}/server/index.ts (100%) rename x-pack/plugins/{alerting => alerts}/server/lib/delete_task_if_it_exists.test.ts (100%) rename x-pack/plugins/{alerting => alerts}/server/lib/delete_task_if_it_exists.ts (100%) rename x-pack/plugins/{alerting => alerts}/server/lib/index.ts (100%) rename x-pack/plugins/{alerting => alerts}/server/lib/is_alert_not_found_error.test.ts (100%) rename x-pack/plugins/{alerting => alerts}/server/lib/is_alert_not_found_error.ts (100%) rename x-pack/plugins/{alerting => alerts}/server/lib/license_api_access.ts (100%) rename x-pack/plugins/{alerting => alerts}/server/lib/license_state.mock.ts (100%) rename x-pack/plugins/{alerting => alerts}/server/lib/license_state.test.ts (95%) rename x-pack/plugins/{alerting => alerts}/server/lib/license_state.ts (90%) rename x-pack/plugins/{alerting => alerts}/server/lib/result_type.ts (100%) rename x-pack/plugins/{alerting => alerts}/server/lib/types.test.ts (100%) rename x-pack/plugins/{alerting => alerts}/server/lib/types.ts (100%) rename x-pack/plugins/{alerting => alerts}/server/lib/validate_alert_type_params.test.ts (100%) rename x-pack/plugins/{alerting => alerts}/server/lib/validate_alert_type_params.ts (100%) rename x-pack/plugins/{alerting => alerts}/server/mocks.ts (100%) rename x-pack/plugins/{alerting => alerts}/server/plugin.test.ts (97%) rename x-pack/plugins/{alerting => alerts}/server/plugin.ts (99%) rename x-pack/plugins/{alerting => alerts}/server/routes/_mock_handler_arguments.ts (100%) rename x-pack/plugins/{alerting => alerts}/server/routes/create.test.ts (98%) rename x-pack/plugins/{alerting => alerts}/server/routes/create.ts (98%) rename x-pack/plugins/{alerting => alerts}/server/routes/delete.test.ts (97%) rename x-pack/plugins/{alerting => alerts}/server/routes/delete.ts (96%) rename x-pack/plugins/{alerting => alerts}/server/routes/disable.test.ts (95%) rename x-pack/plugins/{alerting => alerts}/server/routes/disable.ts (96%) rename x-pack/plugins/{alerting => alerts}/server/routes/enable.test.ts (95%) rename x-pack/plugins/{alerting => alerts}/server/routes/enable.ts (96%) rename x-pack/plugins/{alerting => alerts}/server/routes/find.test.ts (93%) rename x-pack/plugins/{alerting => alerts}/server/routes/find.ts (81%) rename x-pack/plugins/{alerting => alerts}/server/routes/get.test.ts (97%) rename x-pack/plugins/{alerting => alerts}/server/routes/get.ts (96%) rename x-pack/plugins/{alerting => alerts}/server/routes/get_alert_state.test.ts (94%) rename x-pack/plugins/{alerting => alerts}/server/routes/get_alert_state.ts (96%) rename x-pack/plugins/{alerting => alerts}/server/routes/health.test.ts (99%) rename x-pack/plugins/{alerting => alerts}/server/routes/health.ts (98%) rename x-pack/plugins/{alerting => alerts}/server/routes/index.ts (100%) rename x-pack/plugins/{alerting => alerts}/server/routes/lib/error_handler.ts (94%) create mode 100644 x-pack/plugins/alerts/server/routes/lib/rename_keys.ts rename x-pack/plugins/{alerting => alerts}/server/routes/list_alert_types.test.ts (94%) rename x-pack/plugins/{alerting => alerts}/server/routes/list_alert_types.ts (95%) rename x-pack/plugins/{alerting => alerts}/server/routes/mute_all.test.ts (95%) rename x-pack/plugins/{alerting => alerts}/server/routes/mute_all.ts (96%) rename x-pack/plugins/{alerting => alerts}/server/routes/mute_instance.test.ts (92%) rename x-pack/plugins/{alerting => alerts}/server/routes/mute_instance.ts (72%) rename x-pack/plugins/{alerting => alerts}/server/routes/unmute_all.test.ts (95%) rename x-pack/plugins/{alerting => alerts}/server/routes/unmute_all.ts (96%) rename x-pack/plugins/{alerting => alerts}/server/routes/unmute_instance.test.ts (95%) rename x-pack/plugins/{alerting => alerts}/server/routes/unmute_instance.ts (94%) rename x-pack/plugins/{alerting => alerts}/server/routes/update.test.ts (98%) rename x-pack/plugins/{alerting => alerts}/server/routes/update.ts (98%) rename x-pack/plugins/{alerting => alerts}/server/routes/update_api_key.test.ts (95%) rename x-pack/plugins/{alerting => alerts}/server/routes/update_api_key.ts (96%) rename x-pack/plugins/{alerting => alerts}/server/saved_objects/index.ts (100%) rename x-pack/plugins/{alerting => alerts}/server/saved_objects/mappings.json (100%) rename x-pack/plugins/{alerting => alerts}/server/task_runner/alert_task_instance.test.ts (98%) rename x-pack/plugins/{alerting => alerts}/server/task_runner/alert_task_instance.ts (95%) rename x-pack/plugins/{alerting => alerts}/server/task_runner/create_execution_handler.test.ts (100%) rename x-pack/plugins/{alerting => alerts}/server/task_runner/create_execution_handler.ts (98%) rename x-pack/plugins/{alerting => alerts}/server/task_runner/get_next_run_at.test.ts (100%) rename x-pack/plugins/{alerting => alerts}/server/task_runner/get_next_run_at.ts (100%) rename x-pack/plugins/{alerting => alerts}/server/task_runner/index.ts (100%) rename x-pack/plugins/{alerting => alerts}/server/task_runner/task_runner.test.ts (99%) rename x-pack/plugins/{alerting => alerts}/server/task_runner/task_runner.ts (99%) rename x-pack/plugins/{alerting => alerts}/server/task_runner/task_runner_factory.test.ts (93%) rename x-pack/plugins/{alerting => alerts}/server/task_runner/task_runner_factory.ts (87%) rename x-pack/plugins/{alerting => alerts}/server/task_runner/transform_action_params.test.ts (100%) rename x-pack/plugins/{alerting => alerts}/server/task_runner/transform_action_params.ts (100%) rename x-pack/plugins/{alerting => alerts}/server/test_utils/index.ts (100%) rename x-pack/plugins/{alerting => alerts}/server/types.ts (100%) rename x-pack/plugins/{alerting => alerts}/server/usage/alerts_telemetry.test.ts (100%) rename x-pack/plugins/{alerting => alerts}/server/usage/alerts_telemetry.ts (100%) rename x-pack/plugins/{alerting => alerts}/server/usage/alerts_usage_collector.test.ts (100%) rename x-pack/plugins/{alerting => alerts}/server/usage/alerts_usage_collector.ts (100%) rename x-pack/plugins/{alerting => alerts}/server/usage/index.ts (100%) rename x-pack/plugins/{alerting => alerts}/server/usage/task.ts (100%) rename x-pack/plugins/{alerting => alerts}/server/usage/types.ts (100%) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index c3da7c7f00e967..83f4f90b9204d7 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -176,7 +176,7 @@ /x-pack/plugins/telemetry_collection_xpack/ @elastic/pulse # Kibana Alerting Services -/x-pack/plugins/alerting/ @elastic/kibana-alerting-services +/x-pack/plugins/alerts/ @elastic/kibana-alerting-services /x-pack/plugins/actions/ @elastic/kibana-alerting-services /x-pack/plugins/event_log/ @elastic/kibana-alerting-services /x-pack/plugins/task_manager/ @elastic/kibana-alerting-services diff --git a/examples/alerting_example/kibana.json b/examples/alerting_example/kibana.json index 76c549adc79700..2b6389649cef98 100644 --- a/examples/alerting_example/kibana.json +++ b/examples/alerting_example/kibana.json @@ -4,6 +4,6 @@ "kibanaVersion": "kibana", "server": true, "ui": true, - "requiredPlugins": ["triggers_actions_ui", "charts", "data", "alerting", "actions"], + "requiredPlugins": ["triggers_actions_ui", "charts", "data", "alerts", "actions"], "optionalPlugins": [] } diff --git a/examples/alerting_example/public/alert_types/astros.tsx b/examples/alerting_example/public/alert_types/astros.tsx index 2e263e454fa0c1..d52223cb6b988c 100644 --- a/examples/alerting_example/public/alert_types/astros.tsx +++ b/examples/alerting_example/public/alert_types/astros.tsx @@ -32,12 +32,12 @@ import { import { i18n } from '@kbn/i18n'; import { flatten } from 'lodash'; import { ALERTING_EXAMPLE_APP_ID, Craft, Operator } from '../../common/constants'; -import { SanitizedAlert } from '../../../../x-pack/plugins/alerting/common'; -import { PluginSetupContract as AlertingSetup } from '../../../../x-pack/plugins/alerting/public'; +import { SanitizedAlert } from '../../../../x-pack/plugins/alerts/common'; +import { PluginSetupContract as AlertingSetup } from '../../../../x-pack/plugins/alerts/public'; import { AlertTypeModel } from '../../../../x-pack/plugins/triggers_actions_ui/public'; -export function registerNavigation(alerting: AlertingSetup) { - alerting.registerNavigation( +export function registerNavigation(alerts: AlertingSetup) { + alerts.registerNavigation( ALERTING_EXAMPLE_APP_ID, 'example.people-in-space', (alert: SanitizedAlert) => `/astros/${alert.id}` diff --git a/examples/alerting_example/public/alert_types/index.ts b/examples/alerting_example/public/alert_types/index.ts index 96d9c09d15836f..db9f855b573e85 100644 --- a/examples/alerting_example/public/alert_types/index.ts +++ b/examples/alerting_example/public/alert_types/index.ts @@ -19,15 +19,15 @@ import { registerNavigation as registerPeopleInSpaceNavigation } from './astros'; import { ALERTING_EXAMPLE_APP_ID } from '../../common/constants'; -import { SanitizedAlert } from '../../../../x-pack/plugins/alerting/common'; -import { PluginSetupContract as AlertingSetup } from '../../../../x-pack/plugins/alerting/public'; +import { SanitizedAlert } from '../../../../x-pack/plugins/alerts/common'; +import { PluginSetupContract as AlertingSetup } from '../../../../x-pack/plugins/alerts/public'; -export function registerNavigation(alerting: AlertingSetup) { +export function registerNavigation(alerts: AlertingSetup) { // register default navigation - alerting.registerDefaultNavigation( + alerts.registerDefaultNavigation( ALERTING_EXAMPLE_APP_ID, (alert: SanitizedAlert) => `/alert/${alert.id}` ); - registerPeopleInSpaceNavigation(alerting); + registerPeopleInSpaceNavigation(alerts); } diff --git a/examples/alerting_example/public/components/view_alert.tsx b/examples/alerting_example/public/components/view_alert.tsx index e418ed91096eb9..75a515bfa1b257 100644 --- a/examples/alerting_example/public/components/view_alert.tsx +++ b/examples/alerting_example/public/components/view_alert.tsx @@ -36,7 +36,7 @@ import { Alert, AlertTaskState, BASE_ALERT_API_PATH, -} from '../../../../x-pack/plugins/alerting/common'; +} from '../../../../x-pack/plugins/alerts/common'; import { ALERTING_EXAMPLE_APP_ID } from '../../common/constants'; type Props = RouteComponentProps & { diff --git a/examples/alerting_example/public/components/view_astros_alert.tsx b/examples/alerting_example/public/components/view_astros_alert.tsx index 296269180dd7b6..19f235a3f3e4e2 100644 --- a/examples/alerting_example/public/components/view_astros_alert.tsx +++ b/examples/alerting_example/public/components/view_astros_alert.tsx @@ -38,7 +38,7 @@ import { Alert, AlertTaskState, BASE_ALERT_API_PATH, -} from '../../../../x-pack/plugins/alerting/common'; +} from '../../../../x-pack/plugins/alerts/common'; import { ALERTING_EXAMPLE_APP_ID } from '../../common/constants'; type Props = RouteComponentProps & { diff --git a/examples/alerting_example/public/plugin.tsx b/examples/alerting_example/public/plugin.tsx index e3748e3235f476..524ff18bd434ec 100644 --- a/examples/alerting_example/public/plugin.tsx +++ b/examples/alerting_example/public/plugin.tsx @@ -18,7 +18,7 @@ */ import { Plugin, CoreSetup, AppMountParameters } from 'kibana/public'; -import { PluginSetupContract as AlertingSetup } from '../../../x-pack/plugins/alerting/public'; +import { PluginSetupContract as AlertingSetup } from '../../../x-pack/plugins/alerts/public'; import { ChartsPluginStart } from '../../../src/plugins/charts/public'; import { TriggersAndActionsUIPublicPluginSetup } from '../../../x-pack/plugins/triggers_actions_ui/public'; import { DataPublicPluginStart } from '../../../src/plugins/data/public'; @@ -30,12 +30,12 @@ export type Setup = void; export type Start = void; export interface AlertingExamplePublicSetupDeps { - alerting: AlertingSetup; + alerts: AlertingSetup; triggers_actions_ui: TriggersAndActionsUIPublicPluginSetup; } export interface AlertingExamplePublicStartDeps { - alerting: AlertingSetup; + alerts: AlertingSetup; triggers_actions_ui: TriggersAndActionsUIPublicPluginSetup; charts: ChartsPluginStart; data: DataPublicPluginStart; @@ -44,7 +44,7 @@ export interface AlertingExamplePublicStartDeps { export class AlertingExamplePlugin implements Plugin { public setup( core: CoreSetup, - { alerting, triggers_actions_ui }: AlertingExamplePublicSetupDeps + { alerts, triggers_actions_ui }: AlertingExamplePublicSetupDeps ) { core.application.register({ id: 'AlertingExample', @@ -59,7 +59,7 @@ export class AlertingExamplePlugin implements Plugin { - public setup(core: CoreSetup, { alerting }: AlertingExampleDeps) { - alerting.registerType(alwaysFiringAlert); - alerting.registerType(peopleInSpaceAlert); + public setup(core: CoreSetup, { alerts }: AlertingExampleDeps) { + alerts.registerType(alwaysFiringAlert); + alerts.registerType(peopleInSpaceAlert); } public start() {} diff --git a/rfcs/text/0003_handler_interface.md b/rfcs/text/0003_handler_interface.md index 51e78cf7c9f547..197f2a20123766 100644 --- a/rfcs/text/0003_handler_interface.md +++ b/rfcs/text/0003_handler_interface.md @@ -65,7 +65,7 @@ application.registerApp({ }); // Alerting -alerting.registerType({ +alerts.registerType({ id: 'myAlert', async execute(context, params, state) { const indexPatterns = await context.core.savedObjects.find('indexPattern'); diff --git a/x-pack/.i18nrc.json b/x-pack/.i18nrc.json index 7ac27dd47ad64e..a479b08ef90697 100644 --- a/x-pack/.i18nrc.json +++ b/x-pack/.i18nrc.json @@ -4,7 +4,7 @@ "xpack.actions": "plugins/actions", "xpack.advancedUiActions": "plugins/advanced_ui_actions", "xpack.uiActionsEnhanced": "examples/ui_actions_enhanced_examples", - "xpack.alerting": "plugins/alerting", + "xpack.alerts": "plugins/alerts", "xpack.alertingBuiltins": "plugins/alerting_builtins", "xpack.apm": ["legacy/plugins/apm", "plugins/apm"], "xpack.beatsManagement": ["legacy/plugins/beats_management", "plugins/beats_management"], diff --git a/x-pack/legacy/plugins/monitoring/index.ts b/x-pack/legacy/plugins/monitoring/index.ts index 1a0fecb9ef5b57..ee31a3037a0cb2 100644 --- a/x-pack/legacy/plugins/monitoring/index.ts +++ b/x-pack/legacy/plugins/monitoring/index.ts @@ -15,7 +15,7 @@ import { KIBANA_ALERTING_ENABLED } from '../../../plugins/monitoring/common/cons */ const deps = ['kibana', 'elasticsearch', 'xpack_main']; if (KIBANA_ALERTING_ENABLED) { - deps.push(...['alerting', 'actions']); + deps.push(...['alerts', 'actions']); } export const monitoring = (kibana: any) => { return new kibana.Plugin({ diff --git a/x-pack/plugins/actions/README.md b/x-pack/plugins/actions/README.md index 847172ae972fd0..96d5f04ac088f6 100644 --- a/x-pack/plugins/actions/README.md +++ b/x-pack/plugins/actions/README.md @@ -26,7 +26,7 @@ Table of Contents - [Executor](#executor) - [Example](#example) - [RESTful API](#restful-api) - - [`POST /api/action`: Create action](#post-apiaction-create-action) + - [`POST /api/actions/action`: Create action](#post-apiaction-create-action) - [`DELETE /api/actions/action/{id}`: Delete action](#delete-apiactionid-delete-action) - [`GET /api/actions`: Get all actions](#get-apiactiongetall-get-all-actions) - [`GET /api/actions/action/{id}`: Get action](#get-apiactionid-get-action) @@ -163,7 +163,7 @@ The built-in email action type provides a good example of creating an action typ Using an action type requires an action to be created that will contain and encrypt configuration for a given action type. See below for CRUD operations using the API. -### `POST /api/action`: Create action +### `POST /api/actions/action`: Create action Payload: diff --git a/x-pack/plugins/alerting_builtins/README.md b/x-pack/plugins/alerting_builtins/README.md index 233984a1ff23fa..2944247e4714c3 100644 --- a/x-pack/plugins/alerting_builtins/README.md +++ b/x-pack/plugins/alerting_builtins/README.md @@ -1,7 +1,7 @@ # alerting_builtins plugin This plugin provides alertTypes shipped with Kibana for use with the -[the alerting plugin](../alerting/README.md). When enabled, it will register +[the alerts plugin](../alerts/README.md). When enabled, it will register the built-in alertTypes with the alerting plugin, register associated HTTP routes, etc. diff --git a/x-pack/plugins/alerting_builtins/kibana.json b/x-pack/plugins/alerting_builtins/kibana.json index 78de9a1ae01659..cc613d5247ef4d 100644 --- a/x-pack/plugins/alerting_builtins/kibana.json +++ b/x-pack/plugins/alerting_builtins/kibana.json @@ -3,7 +3,7 @@ "server": true, "version": "8.0.0", "kibanaVersion": "kibana", - "requiredPlugins": ["alerting"], + "requiredPlugins": ["alerts"], "configPath": ["xpack", "alerting_builtins"], "ui": false } diff --git a/x-pack/plugins/alerting_builtins/server/alert_types/index.ts b/x-pack/plugins/alerting_builtins/server/alert_types/index.ts index 475efc87b443a7..d9232195b0f522 100644 --- a/x-pack/plugins/alerting_builtins/server/alert_types/index.ts +++ b/x-pack/plugins/alerting_builtins/server/alert_types/index.ts @@ -10,7 +10,7 @@ import { register as registerIndexThreshold } from './index_threshold'; interface RegisterBuiltInAlertTypesParams { service: Service; router: IRouter; - alerting: AlertingSetup; + alerts: AlertingSetup; baseRoute: string; } diff --git a/x-pack/plugins/alerting_builtins/server/alert_types/index_threshold/action_context.ts b/x-pack/plugins/alerting_builtins/server/alert_types/index_threshold/action_context.ts index 15139ae34c93d8..c3a132bc609d6d 100644 --- a/x-pack/plugins/alerting_builtins/server/alert_types/index_threshold/action_context.ts +++ b/x-pack/plugins/alerting_builtins/server/alert_types/index_threshold/action_context.ts @@ -6,7 +6,7 @@ import { i18n } from '@kbn/i18n'; import { Params } from './alert_type_params'; -import { AlertExecutorOptions } from '../../../../alerting/server'; +import { AlertExecutorOptions } from '../../../../alerts/server'; // alert type context provided to actions diff --git a/x-pack/plugins/alerting_builtins/server/alert_types/index_threshold/index.ts b/x-pack/plugins/alerting_builtins/server/alert_types/index_threshold/index.ts index fbe107054ce9df..9787ece323c593 100644 --- a/x-pack/plugins/alerting_builtins/server/alert_types/index_threshold/index.ts +++ b/x-pack/plugins/alerting_builtins/server/alert_types/index_threshold/index.ts @@ -23,14 +23,14 @@ export function getService() { interface RegisterParams { service: Service; router: IRouter; - alerting: AlertingSetup; + alerts: AlertingSetup; baseRoute: string; } export function register(params: RegisterParams) { - const { service, router, alerting, baseRoute } = params; + const { service, router, alerts, baseRoute } = params; - alerting.registerType(getAlertType(service)); + alerts.registerType(getAlertType(service)); const baseBuiltInRoute = `${baseRoute}/index_threshold`; registerRoutes({ service, router, baseRoute: baseBuiltInRoute }); diff --git a/x-pack/plugins/alerting_builtins/server/alert_types/index_threshold/lib/date_range_info.ts b/x-pack/plugins/alerting_builtins/server/alert_types/index_threshold/lib/date_range_info.ts index 0a4accc983d79b..fa991786a60b60 100644 --- a/x-pack/plugins/alerting_builtins/server/alert_types/index_threshold/lib/date_range_info.ts +++ b/x-pack/plugins/alerting_builtins/server/alert_types/index_threshold/lib/date_range_info.ts @@ -6,7 +6,7 @@ import { i18n } from '@kbn/i18n'; import { times } from 'lodash'; -import { parseDuration } from '../../../../../alerting/server'; +import { parseDuration } from '../../../../../alerts/server'; import { MAX_INTERVALS } from '../index'; // dates as numbers are epoch millis diff --git a/x-pack/plugins/alerting_builtins/server/alert_types/index_threshold/lib/time_series_types.ts b/x-pack/plugins/alerting_builtins/server/alert_types/index_threshold/lib/time_series_types.ts index 40e6f187ce18f4..a22395cb0961bc 100644 --- a/x-pack/plugins/alerting_builtins/server/alert_types/index_threshold/lib/time_series_types.ts +++ b/x-pack/plugins/alerting_builtins/server/alert_types/index_threshold/lib/time_series_types.ts @@ -10,7 +10,7 @@ import { i18n } from '@kbn/i18n'; import { schema, TypeOf } from '@kbn/config-schema'; -import { parseDuration } from '../../../../../alerting/server'; +import { parseDuration } from '../../../../../alerts/server'; import { MAX_INTERVALS } from '../index'; import { CoreQueryParamsSchemaProperties, validateCoreQueryBody } from './core_query_types'; import { diff --git a/x-pack/plugins/alerting_builtins/server/plugin.test.ts b/x-pack/plugins/alerting_builtins/server/plugin.test.ts index f93041fa3c142c..71a904dcbab3da 100644 --- a/x-pack/plugins/alerting_builtins/server/plugin.test.ts +++ b/x-pack/plugins/alerting_builtins/server/plugin.test.ts @@ -6,7 +6,7 @@ import { AlertingBuiltinsPlugin } from './plugin'; import { coreMock } from '../../../../src/core/server/mocks'; -import { alertsMock } from '../../../plugins/alerting/server/mocks'; +import { alertsMock } from '../../alerts/server/mocks'; describe('AlertingBuiltins Plugin', () => { describe('setup()', () => { @@ -22,7 +22,7 @@ describe('AlertingBuiltins Plugin', () => { it('should register built-in alert types', async () => { const alertingSetup = alertsMock.createSetup(); - await plugin.setup(coreSetup, { alerting: alertingSetup }); + await plugin.setup(coreSetup, { alerts: alertingSetup }); expect(alertingSetup.registerType).toHaveBeenCalledTimes(1); @@ -44,7 +44,7 @@ describe('AlertingBuiltins Plugin', () => { it('should return a service in the expected shape', async () => { const alertingSetup = alertsMock.createSetup(); - const service = await plugin.setup(coreSetup, { alerting: alertingSetup }); + const service = await plugin.setup(coreSetup, { alerts: alertingSetup }); expect(typeof service.indexThreshold.timeSeriesQuery).toBe('function'); }); diff --git a/x-pack/plugins/alerting_builtins/server/plugin.ts b/x-pack/plugins/alerting_builtins/server/plugin.ts index 9a9483f9c9dfa2..12d1b080c7c639 100644 --- a/x-pack/plugins/alerting_builtins/server/plugin.ts +++ b/x-pack/plugins/alerting_builtins/server/plugin.ts @@ -22,11 +22,11 @@ export class AlertingBuiltinsPlugin implements Plugin { }; } - public async setup(core: CoreSetup, { alerting }: AlertingBuiltinsDeps): Promise { + public async setup(core: CoreSetup, { alerts }: AlertingBuiltinsDeps): Promise { registerBuiltInAlertTypes({ service: this.service, router: core.http.createRouter(), - alerting, + alerts, baseRoute: '/api/alerting_builtins', }); return this.service; diff --git a/x-pack/plugins/alerting_builtins/server/types.ts b/x-pack/plugins/alerting_builtins/server/types.ts index ff07b85fd3038a..95d34371a6d1e6 100644 --- a/x-pack/plugins/alerting_builtins/server/types.ts +++ b/x-pack/plugins/alerting_builtins/server/types.ts @@ -5,7 +5,7 @@ */ import { Logger, ScopedClusterClient } from '../../../../src/core/server'; -import { PluginSetupContract as AlertingSetup } from '../../alerting/server'; +import { PluginSetupContract as AlertingSetup } from '../../alerts/server'; import { getService as getServiceIndexThreshold } from './alert_types/index_threshold'; export { Logger, IRouter } from '../../../../src/core/server'; @@ -14,11 +14,11 @@ export { PluginSetupContract as AlertingSetup, AlertType, AlertExecutorOptions, -} from '../../alerting/server'; +} from '../../alerts/server'; // this plugin's dependendencies export interface AlertingBuiltinsDeps { - alerting: AlertingSetup; + alerts: AlertingSetup; } // external service exposed through plugin setup/start diff --git a/x-pack/plugins/alerting/README.md b/x-pack/plugins/alerts/README.md similarity index 91% rename from x-pack/plugins/alerting/README.md rename to x-pack/plugins/alerts/README.md index dfa2991895429e..811478426a8d34 100644 --- a/x-pack/plugins/alerting/README.md +++ b/x-pack/plugins/alerts/README.md @@ -20,20 +20,20 @@ Table of Contents - [Example](#example) - [Alert Navigation](#alert-navigation) - [RESTful API](#restful-api) - - [`POST /api/alert`: Create alert](#post-apialert-create-alert) - - [`DELETE /api/alert/{id}`: Delete alert](#delete-apialertid-delete-alert) - - [`GET /api/alert/_find`: Find alerts](#get-apialertfind-find-alerts) - - [`GET /api/alert/{id}`: Get alert](#get-apialertid-get-alert) - - [`GET /api/alert/{id}/state`: Get alert state](#get-apialertidstate-get-alert-state) - - [`GET /api/alert/types`: List alert types](#get-apialerttypes-list-alert-types) - - [`PUT /api/alert/{id}`: Update alert](#put-apialertid-update-alert) - - [`POST /api/alert/{id}/_enable`: Enable an alert](#post-apialertidenable-enable-an-alert) - - [`POST /api/alert/{id}/_disable`: Disable an alert](#post-apialertiddisable-disable-an-alert) - - [`POST /api/alert/{id}/_mute_all`: Mute all alert instances](#post-apialertidmuteall-mute-all-alert-instances) - - [`POST /api/alert/{alertId}/alert_instance/{alertInstanceId}/_mute`: Mute alert instance](#post-apialertalertidalertinstancealertinstanceidmute-mute-alert-instance) - - [`POST /api/alert/{id}/_unmute_all`: Unmute all alert instances](#post-apialertidunmuteall-unmute-all-alert-instances) - - [`POST /api/alert/{alertId}/alert_instance/{alertInstanceId}/_unmute`: Unmute an alert instance](#post-apialertalertidalertinstancealertinstanceidunmute-unmute-an-alert-instance) - - [`POST /api/alert/{id}/_update_api_key`: Update alert API key](#post-apialertidupdateapikey-update-alert-api-key) + - [`POST /api/alerts/alert`: Create alert](#post-apialert-create-alert) + - [`DELETE /api/alerts/alert/{id}`: Delete alert](#delete-apialertid-delete-alert) + - [`GET /api/alerts/_find`: Find alerts](#get-apialertfind-find-alerts) + - [`GET /api/alerts/alert/{id}`: Get alert](#get-apialertid-get-alert) + - [`GET /api/alerts/alert/{id}/state`: Get alert state](#get-apialertidstate-get-alert-state) + - [`GET /api/alerts/list_alert_types`: List alert types](#get-apialerttypes-list-alert-types) + - [`PUT /api/alerts/alert/{id}`: Update alert](#put-apialertid-update-alert) + - [`POST /api/alerts/alert/{id}/_enable`: Enable an alert](#post-apialertidenable-enable-an-alert) + - [`POST /api/alerts/alert/{id}/_disable`: Disable an alert](#post-apialertiddisable-disable-an-alert) + - [`POST /api/alerts/alert/{id}/_mute_all`: Mute all alert instances](#post-apialertidmuteall-mute-all-alert-instances) + - [`POST /api/alerts/alert/{alert_id}/alert_instance/{alert_instance_id}/_mute`: Mute alert instance](#post-apialertalertidalertinstancealertinstanceidmute-mute-alert-instance) + - [`POST /api/alerts/alert/{id}/_unmute_all`: Unmute all alert instances](#post-apialertidunmuteall-unmute-all-alert-instances) + - [`POST /api/alerts/alert/{alertId}/alert_instance/{alertInstanceId}/_unmute`: Unmute an alert instance](#post-apialertalertidalertinstancealertinstanceidunmute-unmute-an-alert-instance) + - [`POST /api/alerts/alert/{id}/_update_api_key`: Update alert API key](#post-apialertidupdateapikey-update-alert-api-key) - [Schedule Formats](#schedule-formats) - [Alert instance factory](#alert-instance-factory) - [Templating actions](#templating-actions) @@ -78,7 +78,7 @@ Note that the `manage_own_api_key` cluster privilege is not enough - it can be u ### Methods -**server.newPlatform.setup.plugins.alerting.registerType(options)** +**server.newPlatform.setup.plugins.alerts.registerType(options)** The following table describes the properties of the `options` object. @@ -139,7 +139,7 @@ This example receives server and threshold as parameters. It will read the CPU u ```typescript import { schema } from '@kbn/config-schema'; ... -server.newPlatform.setup.plugins.alerting.registerType({ +server.newPlatform.setup.plugins.alerts.registerType({ id: 'my-alert-type', name: 'My alert type', validate: { @@ -220,7 +220,7 @@ server.newPlatform.setup.plugins.alerting.registerType({ This example only receives threshold as a parameter. It will read the CPU usage of all the servers and schedule individual actions if the reading for a server is greater than the threshold. This is a better implementation than above as only one query is performed for all the servers instead of one query per server. ```typescript -server.newPlatform.setup.plugins.alerting.registerType({ +server.newPlatform.setup.plugins.alerts.registerType({ id: 'my-alert-type', name: 'My alert type', validate: { @@ -352,7 +352,7 @@ You can use the `registerNavigation` api to specify as many AlertType specific h Using an alert type requires you to create an alert that will contain parameters and actions for a given alert type. See below for CRUD operations using the API. -### `POST /api/alert`: Create alert +### `POST /api/alerts/alert`: Create alert Payload: @@ -367,7 +367,7 @@ Payload: |params|The parameters to pass in to the alert type executor `params` value. This will also validate against the alert type params validator if defined.|object| |actions|Array of the following:
- `group` (string): We support grouping actions in the scenario of escalations or different types of alert instances. If you don't need this, feel free to use `default` as a value.
- `id` (string): The id of the action saved object to execute.
- `params` (object): The map to the `params` the action type will receive. In order to help apply context to strings, we handle them as mustache templates and pass in a default set of context. (see templating actions).|array| -### `DELETE /api/alert/{id}`: Delete alert +### `DELETE /api/alerts/alert/{id}`: Delete alert Params: @@ -375,13 +375,13 @@ Params: |---|---|---| |id|The id of the alert you're trying to delete.|string| -### `GET /api/alert/_find`: Find alerts +### `GET /api/alerts/_find`: Find alerts Params: See the saved objects API documentation for find. All the properties are the same except you cannot pass in `type`. -### `GET /api/alert/{id}`: Get alert +### `GET /api/alerts/alert/{id}`: Get alert Params: @@ -389,7 +389,7 @@ Params: |---|---|---| |id|The id of the alert you're trying to get.|string| -### `GET /api/alert/{id}/state`: Get alert state +### `GET /api/alerts/alert/{id}/state`: Get alert state Params: @@ -397,11 +397,11 @@ Params: |---|---|---| |id|The id of the alert whose state you're trying to get.|string| -### `GET /api/alert/types`: List alert types +### `GET /api/alerts/list_alert_types`: List alert types No parameters. -### `PUT /api/alert/{id}`: Update alert +### `PUT /api/alerts/alert/{id}`: Update alert Params: @@ -420,7 +420,7 @@ Payload: |params|The parameters to pass in to the alert type executor `params` value. This will also validate against the alert type params validator if defined.|object| |actions|Array of the following:
- `group` (string): We support grouping actions in the scenario of escalations or different types of alert instances. If you don't need this, feel free to use `default` as a value.
- `id` (string): The id of the action saved object to execute.
- `params` (object): There map to the `params` the action type will receive. In order to help apply context to strings, we handle them as mustache templates and pass in a default set of context. (see templating actions).|array| -### `POST /api/alert/{id}/_enable`: Enable an alert +### `POST /api/alerts/alert/{id}/_enable`: Enable an alert Params: @@ -428,7 +428,7 @@ Params: |---|---|---| |id|The id of the alert you're trying to enable.|string| -### `POST /api/alert/{id}/_disable`: Disable an alert +### `POST /api/alerts/alert/{id}/_disable`: Disable an alert Params: @@ -436,7 +436,7 @@ Params: |---|---|---| |id|The id of the alert you're trying to disable.|string| -### `POST /api/alert/{id}/_mute_all`: Mute all alert instances +### `POST /api/alerts/alert/{id}/_mute_all`: Mute all alert instances Params: @@ -444,7 +444,7 @@ Params: |---|---|---| |id|The id of the alert you're trying to mute all alert instances for.|string| -### `POST /api/alert/{alertId}/alert_instance/{alertInstanceId}/_mute`: Mute alert instance +### `POST /api/alerts/alert/{alert_id}/alert_instance/{alert_instance_id}/_mute`: Mute alert instance Params: @@ -453,7 +453,7 @@ Params: |alertId|The id of the alert you're trying to mute an instance for.|string| |alertInstanceId|The instance id of the alert instance you're trying to mute.|string| -### `POST /api/alert/{id}/_unmute_all`: Unmute all alert instances +### `POST /api/alerts/alert/{id}/_unmute_all`: Unmute all alert instances Params: @@ -461,7 +461,7 @@ Params: |---|---|---| |id|The id of the alert you're trying to unmute all alert instances for.|string| -### `POST /api/alert/{alertId}/alert_instance/{alertInstanceId}/_unmute`: Unmute an alert instance +### `POST /api/alerts/alert/{alertId}/alert_instance/{alertInstanceId}/_unmute`: Unmute an alert instance Params: @@ -470,7 +470,7 @@ Params: |alertId|The id of the alert you're trying to unmute an instance for.|string| |alertInstanceId|The instance id of the alert instance you're trying to unmute.|string| -### `POST /api/alert/{id}/_update_api_key`: Update alert API key +### `POST /api/alerts/alert/{id}/_update_api_key`: Update alert API key |Property|Description|Type| |---|---|---| diff --git a/x-pack/plugins/alerting/common/alert.ts b/x-pack/plugins/alerts/common/alert.ts similarity index 100% rename from x-pack/plugins/alerting/common/alert.ts rename to x-pack/plugins/alerts/common/alert.ts diff --git a/x-pack/plugins/alerting/common/alert_instance.ts b/x-pack/plugins/alerts/common/alert_instance.ts similarity index 100% rename from x-pack/plugins/alerting/common/alert_instance.ts rename to x-pack/plugins/alerts/common/alert_instance.ts diff --git a/x-pack/plugins/alerting/common/alert_navigation.ts b/x-pack/plugins/alerts/common/alert_navigation.ts similarity index 100% rename from x-pack/plugins/alerting/common/alert_navigation.ts rename to x-pack/plugins/alerts/common/alert_navigation.ts diff --git a/x-pack/plugins/alerting/common/alert_task_instance.ts b/x-pack/plugins/alerts/common/alert_task_instance.ts similarity index 100% rename from x-pack/plugins/alerting/common/alert_task_instance.ts rename to x-pack/plugins/alerts/common/alert_task_instance.ts diff --git a/x-pack/plugins/alerting/common/alert_type.ts b/x-pack/plugins/alerts/common/alert_type.ts similarity index 100% rename from x-pack/plugins/alerting/common/alert_type.ts rename to x-pack/plugins/alerts/common/alert_type.ts diff --git a/x-pack/plugins/alerting/common/date_from_string.test.ts b/x-pack/plugins/alerts/common/date_from_string.test.ts similarity index 100% rename from x-pack/plugins/alerting/common/date_from_string.test.ts rename to x-pack/plugins/alerts/common/date_from_string.test.ts diff --git a/x-pack/plugins/alerting/common/date_from_string.ts b/x-pack/plugins/alerts/common/date_from_string.ts similarity index 100% rename from x-pack/plugins/alerting/common/date_from_string.ts rename to x-pack/plugins/alerts/common/date_from_string.ts diff --git a/x-pack/plugins/alerting/common/index.ts b/x-pack/plugins/alerts/common/index.ts similarity index 92% rename from x-pack/plugins/alerting/common/index.ts rename to x-pack/plugins/alerts/common/index.ts index 2574e73dd4f9ae..88a8da5a3e575f 100644 --- a/x-pack/plugins/alerting/common/index.ts +++ b/x-pack/plugins/alerts/common/index.ts @@ -20,4 +20,4 @@ export interface AlertingFrameworkHealth { hasPermanentEncryptionKey: boolean; } -export const BASE_ALERT_API_PATH = '/api/alert'; +export const BASE_ALERT_API_PATH = '/api/alerts'; diff --git a/x-pack/plugins/alerting/common/parse_duration.test.ts b/x-pack/plugins/alerts/common/parse_duration.test.ts similarity index 100% rename from x-pack/plugins/alerting/common/parse_duration.test.ts rename to x-pack/plugins/alerts/common/parse_duration.test.ts diff --git a/x-pack/plugins/alerting/common/parse_duration.ts b/x-pack/plugins/alerts/common/parse_duration.ts similarity index 100% rename from x-pack/plugins/alerting/common/parse_duration.ts rename to x-pack/plugins/alerts/common/parse_duration.ts diff --git a/x-pack/plugins/alerting/kibana.json b/x-pack/plugins/alerts/kibana.json similarity index 80% rename from x-pack/plugins/alerting/kibana.json rename to x-pack/plugins/alerts/kibana.json index 59c4bb2221b0af..3509f79dbbe4d3 100644 --- a/x-pack/plugins/alerting/kibana.json +++ b/x-pack/plugins/alerts/kibana.json @@ -1,10 +1,10 @@ { - "id": "alerting", + "id": "alerts", "server": true, "ui": true, "version": "8.0.0", "kibanaVersion": "kibana", - "configPath": ["xpack", "alerting"], + "configPath": ["xpack", "alerts"], "requiredPlugins": ["licensing", "taskManager", "encryptedSavedObjects", "actions", "eventLog"], "optionalPlugins": ["usageCollection", "spaces", "security"] } diff --git a/x-pack/plugins/alerting/public/alert_api.test.ts b/x-pack/plugins/alerts/public/alert_api.test.ts similarity index 92% rename from x-pack/plugins/alerting/public/alert_api.test.ts rename to x-pack/plugins/alerts/public/alert_api.test.ts index 1149e6fc249a9a..45b9f5ba8fe2e0 100644 --- a/x-pack/plugins/alerting/public/alert_api.test.ts +++ b/x-pack/plugins/alerts/public/alert_api.test.ts @@ -31,7 +31,7 @@ describe('loadAlertTypes', () => { expect(result).toEqual(resolvedValue); expect(http.get.mock.calls[0]).toMatchInlineSnapshot(` Array [ - "/api/alert/types", + "/api/alerts/list_alert_types", ] `); }); @@ -53,7 +53,7 @@ describe('loadAlertType', () => { expect(http.get.mock.calls[0]).toMatchInlineSnapshot(` Array [ - "/api/alert/types", + "/api/alerts/list_alert_types", ] `); }); @@ -111,7 +111,7 @@ describe('loadAlert', () => { http.get.mockResolvedValueOnce(resolvedValue); expect(await loadAlert({ http, alertId })).toEqual(resolvedValue); - expect(http.get).toHaveBeenCalledWith(`/api/alert/${alertId}`); + expect(http.get).toHaveBeenCalledWith(`/api/alerts/alert/${alertId}`); }); }); @@ -130,7 +130,7 @@ describe('loadAlertState', () => { http.get.mockResolvedValueOnce(resolvedValue); expect(await loadAlertState({ http, alertId })).toEqual(resolvedValue); - expect(http.get).toHaveBeenCalledWith(`/api/alert/${alertId}/state`); + expect(http.get).toHaveBeenCalledWith(`/api/alerts/alert/${alertId}/state`); }); test('should parse AlertInstances', async () => { @@ -167,7 +167,7 @@ describe('loadAlertState', () => { }, }, }); - expect(http.get).toHaveBeenCalledWith(`/api/alert/${alertId}/state`); + expect(http.get).toHaveBeenCalledWith(`/api/alerts/alert/${alertId}/state`); }); test('should handle empty response from api', async () => { @@ -175,6 +175,6 @@ describe('loadAlertState', () => { http.get.mockResolvedValueOnce(''); expect(await loadAlertState({ http, alertId })).toEqual({}); - expect(http.get).toHaveBeenCalledWith(`/api/alert/${alertId}/state`); + expect(http.get).toHaveBeenCalledWith(`/api/alerts/alert/${alertId}/state`); }); }); diff --git a/x-pack/plugins/alerting/public/alert_api.ts b/x-pack/plugins/alerts/public/alert_api.ts similarity index 84% rename from x-pack/plugins/alerting/public/alert_api.ts rename to x-pack/plugins/alerts/public/alert_api.ts index ee9432885d6712..5b7cd2128f3868 100644 --- a/x-pack/plugins/alerting/public/alert_api.ts +++ b/x-pack/plugins/alerts/public/alert_api.ts @@ -16,7 +16,7 @@ import { BASE_ALERT_API_PATH, alertStateSchema } from '../common'; import { Alert, AlertType, AlertTaskState } from '../common'; export async function loadAlertTypes({ http }: { http: HttpSetup }): Promise { - return await http.get(`${BASE_ALERT_API_PATH}/types`); + return await http.get(`${BASE_ALERT_API_PATH}/list_alert_types`); } export async function loadAlertType({ @@ -27,11 +27,11 @@ export async function loadAlertType({ id: AlertType['id']; }): Promise { const maybeAlertType = findFirst((type) => type.id === id)( - await http.get(`${BASE_ALERT_API_PATH}/types`) + await http.get(`${BASE_ALERT_API_PATH}/list_alert_types`) ); if (isNone(maybeAlertType)) { throw new Error( - i18n.translate('xpack.alerting.loadAlertType.missingAlertTypeError', { + i18n.translate('xpack.alerts.loadAlertType.missingAlertTypeError', { defaultMessage: 'Alert type "{id}" is not registered.', values: { id, @@ -49,7 +49,7 @@ export async function loadAlert({ http: HttpSetup; alertId: string; }): Promise { - return await http.get(`${BASE_ALERT_API_PATH}/${alertId}`); + return await http.get(`${BASE_ALERT_API_PATH}/alert/${alertId}`); } type EmptyHttpResponse = ''; @@ -61,7 +61,7 @@ export async function loadAlertState({ alertId: string; }): Promise { return await http - .get(`${BASE_ALERT_API_PATH}/${alertId}/state`) + .get(`${BASE_ALERT_API_PATH}/alert/${alertId}/state`) .then((state: AlertTaskState | EmptyHttpResponse) => (state ? state : {})) .then((state: AlertTaskState) => { return pipe( diff --git a/x-pack/plugins/alerting/public/alert_navigation_registry/alert_navigation_registry.mock.ts b/x-pack/plugins/alerts/public/alert_navigation_registry/alert_navigation_registry.mock.ts similarity index 100% rename from x-pack/plugins/alerting/public/alert_navigation_registry/alert_navigation_registry.mock.ts rename to x-pack/plugins/alerts/public/alert_navigation_registry/alert_navigation_registry.mock.ts diff --git a/x-pack/plugins/alerting/public/alert_navigation_registry/alert_navigation_registry.test.ts b/x-pack/plugins/alerts/public/alert_navigation_registry/alert_navigation_registry.test.ts similarity index 100% rename from x-pack/plugins/alerting/public/alert_navigation_registry/alert_navigation_registry.test.ts rename to x-pack/plugins/alerts/public/alert_navigation_registry/alert_navigation_registry.test.ts diff --git a/x-pack/plugins/alerting/public/alert_navigation_registry/alert_navigation_registry.ts b/x-pack/plugins/alerts/public/alert_navigation_registry/alert_navigation_registry.ts similarity index 90% rename from x-pack/plugins/alerting/public/alert_navigation_registry/alert_navigation_registry.ts rename to x-pack/plugins/alerts/public/alert_navigation_registry/alert_navigation_registry.ts index f30629789b4edb..933ed442523c82 100644 --- a/x-pack/plugins/alerting/public/alert_navigation_registry/alert_navigation_registry.ts +++ b/x-pack/plugins/alerts/public/alert_navigation_registry/alert_navigation_registry.ts @@ -36,7 +36,7 @@ export class AlertNavigationRegistry { public registerDefault(consumer: string, handler: AlertNavigationHandler) { if (this.hasDefaultHandler(consumer)) { throw new Error( - i18n.translate('xpack.alerting.alertNavigationRegistry.register.duplicateDefaultError', { + i18n.translate('xpack.alerts.alertNavigationRegistry.register.duplicateDefaultError', { defaultMessage: 'Default Navigation within "{consumer}" is already registered.', values: { consumer, @@ -54,7 +54,7 @@ export class AlertNavigationRegistry { public register(consumer: string, alertType: AlertType, handler: AlertNavigationHandler) { if (this.hasTypedHandler(consumer, alertType)) { throw new Error( - i18n.translate('xpack.alerting.alertNavigationRegistry.register.duplicateNavigationError', { + i18n.translate('xpack.alerts.alertNavigationRegistry.register.duplicateNavigationError', { defaultMessage: 'Navigation for Alert type "{alertType}" within "{consumer}" is already registered.', values: { @@ -78,7 +78,7 @@ export class AlertNavigationRegistry { } throw new Error( - i18n.translate('xpack.alerting.alertNavigationRegistry.get.missingNavigationError', { + i18n.translate('xpack.alerts.alertNavigationRegistry.get.missingNavigationError', { defaultMessage: 'Navigation for Alert type "{alertType}" within "{consumer}" is not registered.', values: { diff --git a/x-pack/plugins/alerting/public/alert_navigation_registry/index.ts b/x-pack/plugins/alerts/public/alert_navigation_registry/index.ts similarity index 100% rename from x-pack/plugins/alerting/public/alert_navigation_registry/index.ts rename to x-pack/plugins/alerts/public/alert_navigation_registry/index.ts diff --git a/x-pack/plugins/alerting/public/alert_navigation_registry/types.ts b/x-pack/plugins/alerts/public/alert_navigation_registry/types.ts similarity index 100% rename from x-pack/plugins/alerting/public/alert_navigation_registry/types.ts rename to x-pack/plugins/alerts/public/alert_navigation_registry/types.ts diff --git a/x-pack/plugins/alerting/public/index.ts b/x-pack/plugins/alerts/public/index.ts similarity index 100% rename from x-pack/plugins/alerting/public/index.ts rename to x-pack/plugins/alerts/public/index.ts diff --git a/x-pack/plugins/alerting/public/mocks.ts b/x-pack/plugins/alerts/public/mocks.ts similarity index 100% rename from x-pack/plugins/alerting/public/mocks.ts rename to x-pack/plugins/alerts/public/mocks.ts diff --git a/x-pack/plugins/alerting/public/plugin.ts b/x-pack/plugins/alerts/public/plugin.ts similarity index 100% rename from x-pack/plugins/alerting/public/plugin.ts rename to x-pack/plugins/alerts/public/plugin.ts diff --git a/x-pack/plugins/alerting/server/alert_instance/alert_instance.test.ts b/x-pack/plugins/alerts/server/alert_instance/alert_instance.test.ts similarity index 100% rename from x-pack/plugins/alerting/server/alert_instance/alert_instance.test.ts rename to x-pack/plugins/alerts/server/alert_instance/alert_instance.test.ts diff --git a/x-pack/plugins/alerting/server/alert_instance/alert_instance.ts b/x-pack/plugins/alerts/server/alert_instance/alert_instance.ts similarity index 100% rename from x-pack/plugins/alerting/server/alert_instance/alert_instance.ts rename to x-pack/plugins/alerts/server/alert_instance/alert_instance.ts diff --git a/x-pack/plugins/alerting/server/alert_instance/create_alert_instance_factory.test.ts b/x-pack/plugins/alerts/server/alert_instance/create_alert_instance_factory.test.ts similarity index 100% rename from x-pack/plugins/alerting/server/alert_instance/create_alert_instance_factory.test.ts rename to x-pack/plugins/alerts/server/alert_instance/create_alert_instance_factory.test.ts diff --git a/x-pack/plugins/alerting/server/alert_instance/create_alert_instance_factory.ts b/x-pack/plugins/alerts/server/alert_instance/create_alert_instance_factory.ts similarity index 100% rename from x-pack/plugins/alerting/server/alert_instance/create_alert_instance_factory.ts rename to x-pack/plugins/alerts/server/alert_instance/create_alert_instance_factory.ts diff --git a/x-pack/plugins/alerting/server/alert_instance/index.ts b/x-pack/plugins/alerts/server/alert_instance/index.ts similarity index 100% rename from x-pack/plugins/alerting/server/alert_instance/index.ts rename to x-pack/plugins/alerts/server/alert_instance/index.ts diff --git a/x-pack/plugins/alerting/server/alert_type_registry.mock.ts b/x-pack/plugins/alerts/server/alert_type_registry.mock.ts similarity index 100% rename from x-pack/plugins/alerting/server/alert_type_registry.mock.ts rename to x-pack/plugins/alerts/server/alert_type_registry.mock.ts diff --git a/x-pack/plugins/alerting/server/alert_type_registry.test.ts b/x-pack/plugins/alerts/server/alert_type_registry.test.ts similarity index 98% rename from x-pack/plugins/alerting/server/alert_type_registry.test.ts rename to x-pack/plugins/alerts/server/alert_type_registry.test.ts index e78e5ab7932c2b..6d7cf621ab0cae 100644 --- a/x-pack/plugins/alerting/server/alert_type_registry.test.ts +++ b/x-pack/plugins/alerts/server/alert_type_registry.test.ts @@ -7,7 +7,7 @@ import { TaskRunnerFactory } from './task_runner'; import { AlertTypeRegistry } from './alert_type_registry'; import { AlertType } from './types'; -import { taskManagerMock } from '../../../plugins/task_manager/server/task_manager.mock'; +import { taskManagerMock } from '../../task_manager/server/task_manager.mock'; const taskManager = taskManagerMock.setup(); const alertTypeRegistryParams = { diff --git a/x-pack/plugins/alerting/server/alert_type_registry.ts b/x-pack/plugins/alerts/server/alert_type_registry.ts similarity index 89% rename from x-pack/plugins/alerting/server/alert_type_registry.ts rename to x-pack/plugins/alerts/server/alert_type_registry.ts index 0163cb71166e8a..8f36afe062aa54 100644 --- a/x-pack/plugins/alerting/server/alert_type_registry.ts +++ b/x-pack/plugins/alerts/server/alert_type_registry.ts @@ -6,7 +6,7 @@ import Boom from 'boom'; import { i18n } from '@kbn/i18n'; -import { RunContext, TaskManagerSetupContract } from '../../../plugins/task_manager/server'; +import { RunContext, TaskManagerSetupContract } from '../../task_manager/server'; import { TaskRunnerFactory } from './task_runner'; import { AlertType } from './types'; @@ -32,7 +32,7 @@ export class AlertTypeRegistry { public register(alertType: AlertType) { if (this.has(alertType.id)) { throw new Error( - i18n.translate('xpack.alerting.alertTypeRegistry.register.duplicateAlertTypeError', { + i18n.translate('xpack.alerts.alertTypeRegistry.register.duplicateAlertTypeError', { defaultMessage: 'Alert type "{id}" is already registered.', values: { id: alertType.id, @@ -55,7 +55,7 @@ export class AlertTypeRegistry { public get(id: string): AlertType { if (!this.has(id)) { throw Boom.badRequest( - i18n.translate('xpack.alerting.alertTypeRegistry.get.missingAlertTypeError', { + i18n.translate('xpack.alerts.alertTypeRegistry.get.missingAlertTypeError', { defaultMessage: 'Alert type "{id}" is not registered.', values: { id, diff --git a/x-pack/plugins/alerting/server/alerts_client.mock.ts b/x-pack/plugins/alerts/server/alerts_client.mock.ts similarity index 100% rename from x-pack/plugins/alerting/server/alerts_client.mock.ts rename to x-pack/plugins/alerts/server/alerts_client.mock.ts diff --git a/x-pack/plugins/alerting/server/alerts_client.test.ts b/x-pack/plugins/alerts/server/alerts_client.test.ts similarity index 99% rename from x-pack/plugins/alerting/server/alerts_client.test.ts rename to x-pack/plugins/alerts/server/alerts_client.test.ts index 12106100602e7d..9685f58b8fb31c 100644 --- a/x-pack/plugins/alerting/server/alerts_client.test.ts +++ b/x-pack/plugins/alerts/server/alerts_client.test.ts @@ -7,12 +7,12 @@ import uuid from 'uuid'; import { schema } from '@kbn/config-schema'; import { AlertsClient, CreateOptions } from './alerts_client'; import { savedObjectsClientMock, loggingServiceMock } from '../../../../src/core/server/mocks'; -import { taskManagerMock } from '../../../plugins/task_manager/server/task_manager.mock'; +import { taskManagerMock } from '../../task_manager/server/task_manager.mock'; import { alertTypeRegistryMock } from './alert_type_registry.mock'; -import { TaskStatus } from '../../../plugins/task_manager/server'; +import { TaskStatus } from '../../task_manager/server'; import { IntervalSchedule } from './types'; import { resolvable } from './test_utils'; -import { encryptedSavedObjectsMock } from '../../../plugins/encrypted_saved_objects/server/mocks'; +import { encryptedSavedObjectsMock } from '../../encrypted_saved_objects/server/mocks'; import { actionsClientMock } from '../../actions/server/mocks'; const taskManager = taskManagerMock.start(); @@ -1677,7 +1677,7 @@ describe('find()', () => { }, ], }); - const result = await alertsClient.find(); + const result = await alertsClient.find({ options: {} }); expect(result).toMatchInlineSnapshot(` Object { "data": Array [ diff --git a/x-pack/plugins/alerting/server/alerts_client.ts b/x-pack/plugins/alerts/server/alerts_client.ts similarity index 96% rename from x-pack/plugins/alerting/server/alerts_client.ts rename to x-pack/plugins/alerts/server/alerts_client.ts index 382e9d1a616adc..6b091a5a4503b8 100644 --- a/x-pack/plugins/alerting/server/alerts_client.ts +++ b/x-pack/plugins/alerts/server/alerts_client.ts @@ -30,9 +30,9 @@ import { InvalidateAPIKeyParams, GrantAPIKeyResult as SecurityPluginGrantAPIKeyResult, InvalidateAPIKeyResult as SecurityPluginInvalidateAPIKeyResult, -} from '../../../plugins/security/server'; -import { EncryptedSavedObjectsClient } from '../../../plugins/encrypted_saved_objects/server'; -import { TaskManagerStartContract } from '../../../plugins/task_manager/server'; +} from '../../security/server'; +import { EncryptedSavedObjectsClient } from '../../encrypted_saved_objects/server'; +import { TaskManagerStartContract } from '../../task_manager/server'; import { taskInstanceToAlertTaskInstance } from './task_runner/alert_task_instance'; import { deleteTaskIfItExists } from './lib/delete_task_if_it_exists'; @@ -58,22 +58,29 @@ interface ConstructorOptions { getActionsClient: () => Promise; } -export interface FindOptions { - options?: { - perPage?: number; - page?: number; - search?: string; - defaultSearchOperator?: 'AND' | 'OR'; - searchFields?: string[]; - sortField?: string; - sortOrder?: string; - hasReference?: { - type: string; - id: string; - }; - fields?: string[]; - filter?: string; +export interface MuteOptions extends IndexType { + alertId: string; + alertInstanceId: string; +} + +export interface FindOptions extends IndexType { + perPage?: number; + page?: number; + search?: string; + defaultSearchOperator?: 'AND' | 'OR'; + searchFields?: string[]; + sortField?: string; + sortOrder?: string; + hasReference?: { + type: string; + id: string; }; + fields?: string[]; + filter?: string; +} + +interface IndexType { + [key: string]: unknown; } export interface FindResult { @@ -225,7 +232,7 @@ export class AlertsClient { } } - public async find({ options = {} }: FindOptions = {}): Promise { + public async find({ options = {} }: { options: FindOptions }): Promise { const { page, per_page: perPage, @@ -533,13 +540,7 @@ export class AlertsClient { }); } - public async muteInstance({ - alertId, - alertInstanceId, - }: { - alertId: string; - alertInstanceId: string; - }) { + public async muteInstance({ alertId, alertInstanceId }: MuteOptions) { const { attributes, version } = await this.savedObjectsClient.get('alert', alertId); const mutedInstanceIds = attributes.mutedInstanceIds || []; if (!attributes.muteAll && !mutedInstanceIds.includes(alertInstanceId)) { @@ -652,7 +653,7 @@ export class AlertsClient { ); if (invalidActionGroups.length) { throw Boom.badRequest( - i18n.translate('xpack.alerting.alertsClient.validateActions.invalidGroups', { + i18n.translate('xpack.alerts.alertsClient.validateActions.invalidGroups', { defaultMessage: 'Invalid action groups: {groups}', values: { groups: invalidActionGroups.join(', '), diff --git a/x-pack/plugins/alerting/server/alerts_client_factory.test.ts b/x-pack/plugins/alerts/server/alerts_client_factory.test.ts similarity index 95% rename from x-pack/plugins/alerting/server/alerts_client_factory.test.ts rename to x-pack/plugins/alerts/server/alerts_client_factory.test.ts index d1a7c60bb9a68e..50dafba00a7e48 100644 --- a/x-pack/plugins/alerting/server/alerts_client_factory.test.ts +++ b/x-pack/plugins/alerts/server/alerts_client_factory.test.ts @@ -7,12 +7,12 @@ import { Request } from 'hapi'; import { AlertsClientFactory, AlertsClientFactoryOpts } from './alerts_client_factory'; import { alertTypeRegistryMock } from './alert_type_registry.mock'; -import { taskManagerMock } from '../../../plugins/task_manager/server/task_manager.mock'; +import { taskManagerMock } from '../../task_manager/server/task_manager.mock'; import { KibanaRequest } from '../../../../src/core/server'; import { loggingServiceMock, savedObjectsClientMock } from '../../../../src/core/server/mocks'; -import { encryptedSavedObjectsMock } from '../../../plugins/encrypted_saved_objects/server/mocks'; -import { AuthenticatedUser } from '../../../plugins/security/public'; -import { securityMock } from '../../../plugins/security/server/mocks'; +import { encryptedSavedObjectsMock } from '../../encrypted_saved_objects/server/mocks'; +import { AuthenticatedUser } from '../../security/public'; +import { securityMock } from '../../security/server/mocks'; import { actionsMock } from '../../actions/server/mocks'; jest.mock('./alerts_client'); diff --git a/x-pack/plugins/alerting/server/alerts_client_factory.ts b/x-pack/plugins/alerts/server/alerts_client_factory.ts similarity index 95% rename from x-pack/plugins/alerting/server/alerts_client_factory.ts rename to x-pack/plugins/alerts/server/alerts_client_factory.ts index 2924736330abd2..af546f965d7df7 100644 --- a/x-pack/plugins/alerting/server/alerts_client_factory.ts +++ b/x-pack/plugins/alerts/server/alerts_client_factory.ts @@ -8,9 +8,9 @@ import { PluginStartContract as ActionsPluginStartContract } from '../../actions import { AlertsClient } from './alerts_client'; import { AlertTypeRegistry, SpaceIdToNamespaceFunction } from './types'; import { KibanaRequest, Logger, SavedObjectsClientContract } from '../../../../src/core/server'; -import { InvalidateAPIKeyParams, SecurityPluginSetup } from '../../../plugins/security/server'; -import { EncryptedSavedObjectsClient } from '../../../plugins/encrypted_saved_objects/server'; -import { TaskManagerStartContract } from '../../../plugins/task_manager/server'; +import { InvalidateAPIKeyParams, SecurityPluginSetup } from '../../security/server'; +import { EncryptedSavedObjectsClient } from '../../encrypted_saved_objects/server'; +import { TaskManagerStartContract } from '../../task_manager/server'; export interface AlertsClientFactoryOpts { logger: Logger; diff --git a/x-pack/plugins/alerting/server/constants/plugin.ts b/x-pack/plugins/alerts/server/constants/plugin.ts similarity index 86% rename from x-pack/plugins/alerting/server/constants/plugin.ts rename to x-pack/plugins/alerts/server/constants/plugin.ts index 9c276ed1d75dee..c180b68680841c 100644 --- a/x-pack/plugins/alerting/server/constants/plugin.ts +++ b/x-pack/plugins/alerts/server/constants/plugin.ts @@ -7,12 +7,12 @@ import { LICENSE_TYPE_BASIC, LicenseType } from '../../../../legacy/common/constants'; export const PLUGIN = { - ID: 'alerting', + ID: 'alerts', MINIMUM_LICENSE_REQUIRED: LICENSE_TYPE_BASIC as LicenseType, // TODO: supposed to be changed up on requirements // all plugins seem to use getI18nName with any // eslint-disable-next-line @typescript-eslint/no-explicit-any getI18nName: (i18n: any): string => - i18n.translate('xpack.alerting.appName', { - defaultMessage: 'Alerting', + i18n.translate('xpack.alerts.appName', { + defaultMessage: 'Alerts', }), }; diff --git a/x-pack/plugins/alerting/server/index.ts b/x-pack/plugins/alerts/server/index.ts similarity index 100% rename from x-pack/plugins/alerting/server/index.ts rename to x-pack/plugins/alerts/server/index.ts diff --git a/x-pack/plugins/alerting/server/lib/delete_task_if_it_exists.test.ts b/x-pack/plugins/alerts/server/lib/delete_task_if_it_exists.test.ts similarity index 100% rename from x-pack/plugins/alerting/server/lib/delete_task_if_it_exists.test.ts rename to x-pack/plugins/alerts/server/lib/delete_task_if_it_exists.test.ts diff --git a/x-pack/plugins/alerting/server/lib/delete_task_if_it_exists.ts b/x-pack/plugins/alerts/server/lib/delete_task_if_it_exists.ts similarity index 100% rename from x-pack/plugins/alerting/server/lib/delete_task_if_it_exists.ts rename to x-pack/plugins/alerts/server/lib/delete_task_if_it_exists.ts diff --git a/x-pack/plugins/alerting/server/lib/index.ts b/x-pack/plugins/alerts/server/lib/index.ts similarity index 100% rename from x-pack/plugins/alerting/server/lib/index.ts rename to x-pack/plugins/alerts/server/lib/index.ts diff --git a/x-pack/plugins/alerting/server/lib/is_alert_not_found_error.test.ts b/x-pack/plugins/alerts/server/lib/is_alert_not_found_error.test.ts similarity index 100% rename from x-pack/plugins/alerting/server/lib/is_alert_not_found_error.test.ts rename to x-pack/plugins/alerts/server/lib/is_alert_not_found_error.test.ts diff --git a/x-pack/plugins/alerting/server/lib/is_alert_not_found_error.ts b/x-pack/plugins/alerts/server/lib/is_alert_not_found_error.ts similarity index 100% rename from x-pack/plugins/alerting/server/lib/is_alert_not_found_error.ts rename to x-pack/plugins/alerts/server/lib/is_alert_not_found_error.ts diff --git a/x-pack/plugins/alerting/server/lib/license_api_access.ts b/x-pack/plugins/alerts/server/lib/license_api_access.ts similarity index 100% rename from x-pack/plugins/alerting/server/lib/license_api_access.ts rename to x-pack/plugins/alerts/server/lib/license_api_access.ts diff --git a/x-pack/plugins/alerting/server/lib/license_state.mock.ts b/x-pack/plugins/alerts/server/lib/license_state.mock.ts similarity index 100% rename from x-pack/plugins/alerting/server/lib/license_state.mock.ts rename to x-pack/plugins/alerts/server/lib/license_state.mock.ts diff --git a/x-pack/plugins/alerting/server/lib/license_state.test.ts b/x-pack/plugins/alerts/server/lib/license_state.test.ts similarity index 95% rename from x-pack/plugins/alerting/server/lib/license_state.test.ts rename to x-pack/plugins/alerts/server/lib/license_state.test.ts index cbab98a6311ddc..50b4e6b4439f70 100644 --- a/x-pack/plugins/alerting/server/lib/license_state.test.ts +++ b/x-pack/plugins/alerts/server/lib/license_state.test.ts @@ -6,7 +6,7 @@ import expect from '@kbn/expect'; import { LicenseState } from './license_state'; -import { licensingMock } from '../../../../plugins/licensing/server/mocks'; +import { licensingMock } from '../../../licensing/server/mocks'; describe('license_state', () => { const getRawLicense = jest.fn(); diff --git a/x-pack/plugins/alerting/server/lib/license_state.ts b/x-pack/plugins/alerts/server/lib/license_state.ts similarity index 90% rename from x-pack/plugins/alerting/server/lib/license_state.ts rename to x-pack/plugins/alerts/server/lib/license_state.ts index 211d7a75dc4fa2..ea0106f717b023 100644 --- a/x-pack/plugins/alerting/server/lib/license_state.ts +++ b/x-pack/plugins/alerts/server/lib/license_state.ts @@ -7,7 +7,7 @@ import Boom from 'boom'; import { i18n } from '@kbn/i18n'; import { Observable, Subscription } from 'rxjs'; -import { ILicense } from '../../../../plugins/licensing/common/types'; +import { ILicense } from '../../../licensing/common/types'; import { assertNever } from '../../../../../src/core/server'; import { PLUGIN } from '../constants/plugin'; @@ -43,10 +43,10 @@ export class LicenseState { showAppLink: true, enableAppLink: false, message: i18n.translate( - 'xpack.alerting.serverSideErrors.unavailableLicenseInformationErrorMessage', + 'xpack.alerts.serverSideErrors.unavailableLicenseInformationErrorMessage', { defaultMessage: - 'Alerting is unavailable - license information is not available at this time.', + 'Alerts is unavailable - license information is not available at this time.', } ), }; diff --git a/x-pack/plugins/alerting/server/lib/result_type.ts b/x-pack/plugins/alerts/server/lib/result_type.ts similarity index 100% rename from x-pack/plugins/alerting/server/lib/result_type.ts rename to x-pack/plugins/alerts/server/lib/result_type.ts diff --git a/x-pack/plugins/alerting/server/lib/types.test.ts b/x-pack/plugins/alerts/server/lib/types.test.ts similarity index 100% rename from x-pack/plugins/alerting/server/lib/types.test.ts rename to x-pack/plugins/alerts/server/lib/types.test.ts diff --git a/x-pack/plugins/alerting/server/lib/types.ts b/x-pack/plugins/alerts/server/lib/types.ts similarity index 100% rename from x-pack/plugins/alerting/server/lib/types.ts rename to x-pack/plugins/alerts/server/lib/types.ts diff --git a/x-pack/plugins/alerting/server/lib/validate_alert_type_params.test.ts b/x-pack/plugins/alerts/server/lib/validate_alert_type_params.test.ts similarity index 100% rename from x-pack/plugins/alerting/server/lib/validate_alert_type_params.test.ts rename to x-pack/plugins/alerts/server/lib/validate_alert_type_params.test.ts diff --git a/x-pack/plugins/alerting/server/lib/validate_alert_type_params.ts b/x-pack/plugins/alerts/server/lib/validate_alert_type_params.ts similarity index 100% rename from x-pack/plugins/alerting/server/lib/validate_alert_type_params.ts rename to x-pack/plugins/alerts/server/lib/validate_alert_type_params.ts diff --git a/x-pack/plugins/alerting/server/mocks.ts b/x-pack/plugins/alerts/server/mocks.ts similarity index 100% rename from x-pack/plugins/alerting/server/mocks.ts rename to x-pack/plugins/alerts/server/mocks.ts diff --git a/x-pack/plugins/alerting/server/plugin.test.ts b/x-pack/plugins/alerts/server/plugin.test.ts similarity index 97% rename from x-pack/plugins/alerting/server/plugin.test.ts rename to x-pack/plugins/alerts/server/plugin.test.ts index 0411899290ab2d..008a9bb804c5bf 100644 --- a/x-pack/plugins/alerting/server/plugin.test.ts +++ b/x-pack/plugins/alerts/server/plugin.test.ts @@ -6,8 +6,8 @@ import { AlertingPlugin, AlertingPluginsSetup, AlertingPluginsStart } from './plugin'; import { coreMock } from '../../../../src/core/server/mocks'; -import { licensingMock } from '../../../plugins/licensing/server/mocks'; -import { encryptedSavedObjectsMock } from '../../../plugins/encrypted_saved_objects/server/mocks'; +import { licensingMock } from '../../licensing/server/mocks'; +import { encryptedSavedObjectsMock } from '../../encrypted_saved_objects/server/mocks'; import { taskManagerMock } from '../../task_manager/server/mocks'; import { eventLogServiceMock } from '../../event_log/server/event_log_service.mock'; import { KibanaRequest, CoreSetup } from 'kibana/server'; diff --git a/x-pack/plugins/alerting/server/plugin.ts b/x-pack/plugins/alerts/server/plugin.ts similarity index 99% rename from x-pack/plugins/alerting/server/plugin.ts rename to x-pack/plugins/alerts/server/plugin.ts index e789e655774a08..324bc9fbfb72bf 100644 --- a/x-pack/plugins/alerting/server/plugin.ts +++ b/x-pack/plugins/alerts/server/plugin.ts @@ -53,7 +53,7 @@ import { LicensingPluginSetup } from '../../licensing/server'; import { PluginSetupContract as ActionsPluginSetupContract, PluginStartContract as ActionsPluginStartContract, -} from '../../../plugins/actions/server'; +} from '../../actions/server'; import { Services } from './types'; import { registerAlertsUsageCollector } from './usage'; import { initializeAlertingTelemetry, scheduleAlertingTelemetry } from './usage/task'; diff --git a/x-pack/plugins/alerting/server/routes/_mock_handler_arguments.ts b/x-pack/plugins/alerts/server/routes/_mock_handler_arguments.ts similarity index 100% rename from x-pack/plugins/alerting/server/routes/_mock_handler_arguments.ts rename to x-pack/plugins/alerts/server/routes/_mock_handler_arguments.ts diff --git a/x-pack/plugins/alerting/server/routes/create.test.ts b/x-pack/plugins/alerts/server/routes/create.test.ts similarity index 98% rename from x-pack/plugins/alerting/server/routes/create.test.ts rename to x-pack/plugins/alerts/server/routes/create.test.ts index a4910495c8a40a..9e941903eeaedf 100644 --- a/x-pack/plugins/alerting/server/routes/create.test.ts +++ b/x-pack/plugins/alerts/server/routes/create.test.ts @@ -74,7 +74,7 @@ describe('createAlertRoute', () => { const [config, handler] = router.post.mock.calls[0]; - expect(config.path).toMatchInlineSnapshot(`"/api/alert"`); + expect(config.path).toMatchInlineSnapshot(`"/api/alerts/alert"`); expect(config.options).toMatchInlineSnapshot(` Object { "tags": Array [ diff --git a/x-pack/plugins/alerting/server/routes/create.ts b/x-pack/plugins/alerts/server/routes/create.ts similarity index 98% rename from x-pack/plugins/alerting/server/routes/create.ts rename to x-pack/plugins/alerts/server/routes/create.ts index cc3b7d48162e3f..6238fca024e553 100644 --- a/x-pack/plugins/alerting/server/routes/create.ts +++ b/x-pack/plugins/alerts/server/routes/create.ts @@ -43,7 +43,7 @@ export const bodySchema = schema.object({ export const createAlertRoute = (router: IRouter, licenseState: LicenseState) => { router.post( { - path: BASE_ALERT_API_PATH, + path: `${BASE_ALERT_API_PATH}/alert`, validate: { body: bodySchema, }, diff --git a/x-pack/plugins/alerting/server/routes/delete.test.ts b/x-pack/plugins/alerts/server/routes/delete.test.ts similarity index 97% rename from x-pack/plugins/alerting/server/routes/delete.test.ts rename to x-pack/plugins/alerts/server/routes/delete.test.ts index 416628d015b5a1..9ba4e20312e170 100644 --- a/x-pack/plugins/alerting/server/routes/delete.test.ts +++ b/x-pack/plugins/alerts/server/routes/delete.test.ts @@ -29,7 +29,7 @@ describe('deleteAlertRoute', () => { const [config, handler] = router.delete.mock.calls[0]; - expect(config.path).toMatchInlineSnapshot(`"/api/alert/{id}"`); + expect(config.path).toMatchInlineSnapshot(`"/api/alerts/alert/{id}"`); expect(config.options).toMatchInlineSnapshot(` Object { "tags": Array [ diff --git a/x-pack/plugins/alerting/server/routes/delete.ts b/x-pack/plugins/alerts/server/routes/delete.ts similarity index 96% rename from x-pack/plugins/alerting/server/routes/delete.ts rename to x-pack/plugins/alerts/server/routes/delete.ts index f5a7add632edc7..2034bd21fbed65 100644 --- a/x-pack/plugins/alerting/server/routes/delete.ts +++ b/x-pack/plugins/alerts/server/routes/delete.ts @@ -23,7 +23,7 @@ const paramSchema = schema.object({ export const deleteAlertRoute = (router: IRouter, licenseState: LicenseState) => { router.delete( { - path: `${BASE_ALERT_API_PATH}/{id}`, + path: `${BASE_ALERT_API_PATH}/alert/{id}`, validate: { params: paramSchema, }, diff --git a/x-pack/plugins/alerting/server/routes/disable.test.ts b/x-pack/plugins/alerts/server/routes/disable.test.ts similarity index 95% rename from x-pack/plugins/alerting/server/routes/disable.test.ts rename to x-pack/plugins/alerts/server/routes/disable.test.ts index fde095e9145b66..a82d09854a6043 100644 --- a/x-pack/plugins/alerting/server/routes/disable.test.ts +++ b/x-pack/plugins/alerts/server/routes/disable.test.ts @@ -29,7 +29,7 @@ describe('disableAlertRoute', () => { const [config, handler] = router.post.mock.calls[0]; - expect(config.path).toMatchInlineSnapshot(`"/api/alert/{id}/_disable"`); + expect(config.path).toMatchInlineSnapshot(`"/api/alerts/alert/{id}/_disable"`); expect(config.options).toMatchInlineSnapshot(` Object { "tags": Array [ diff --git a/x-pack/plugins/alerting/server/routes/disable.ts b/x-pack/plugins/alerts/server/routes/disable.ts similarity index 96% rename from x-pack/plugins/alerting/server/routes/disable.ts rename to x-pack/plugins/alerts/server/routes/disable.ts index e1eb089cf4e859..dfc5dfbdd5aa28 100644 --- a/x-pack/plugins/alerting/server/routes/disable.ts +++ b/x-pack/plugins/alerts/server/routes/disable.ts @@ -23,7 +23,7 @@ const paramSchema = schema.object({ export const disableAlertRoute = (router: IRouter, licenseState: LicenseState) => { router.post( { - path: `${BASE_ALERT_API_PATH}/{id}/_disable`, + path: `${BASE_ALERT_API_PATH}/alert/{id}/_disable`, validate: { params: paramSchema, }, diff --git a/x-pack/plugins/alerting/server/routes/enable.test.ts b/x-pack/plugins/alerts/server/routes/enable.test.ts similarity index 95% rename from x-pack/plugins/alerting/server/routes/enable.test.ts rename to x-pack/plugins/alerts/server/routes/enable.test.ts index e4e89e3f06380b..4ee3a12a59dc75 100644 --- a/x-pack/plugins/alerting/server/routes/enable.test.ts +++ b/x-pack/plugins/alerts/server/routes/enable.test.ts @@ -28,7 +28,7 @@ describe('enableAlertRoute', () => { const [config, handler] = router.post.mock.calls[0]; - expect(config.path).toMatchInlineSnapshot(`"/api/alert/{id}/_enable"`); + expect(config.path).toMatchInlineSnapshot(`"/api/alerts/alert/{id}/_enable"`); expect(config.options).toMatchInlineSnapshot(` Object { "tags": Array [ diff --git a/x-pack/plugins/alerting/server/routes/enable.ts b/x-pack/plugins/alerts/server/routes/enable.ts similarity index 96% rename from x-pack/plugins/alerting/server/routes/enable.ts rename to x-pack/plugins/alerts/server/routes/enable.ts index 90e8f552898d92..b6f86b97d6a3a2 100644 --- a/x-pack/plugins/alerting/server/routes/enable.ts +++ b/x-pack/plugins/alerts/server/routes/enable.ts @@ -24,7 +24,7 @@ const paramSchema = schema.object({ export const enableAlertRoute = (router: IRouter, licenseState: LicenseState) => { router.post( { - path: `${BASE_ALERT_API_PATH}/{id}/_enable`, + path: `${BASE_ALERT_API_PATH}/alert/{id}/_enable`, validate: { params: paramSchema, }, diff --git a/x-pack/plugins/alerting/server/routes/find.test.ts b/x-pack/plugins/alerts/server/routes/find.test.ts similarity index 93% rename from x-pack/plugins/alerting/server/routes/find.test.ts rename to x-pack/plugins/alerts/server/routes/find.test.ts index cc601bd42b8cae..f20ee0a54dcd94 100644 --- a/x-pack/plugins/alerting/server/routes/find.test.ts +++ b/x-pack/plugins/alerts/server/routes/find.test.ts @@ -30,7 +30,7 @@ describe('findAlertRoute', () => { const [config, handler] = router.get.mock.calls[0]; - expect(config.path).toMatchInlineSnapshot(`"/api/alert/_find"`); + expect(config.path).toMatchInlineSnapshot(`"/api/alerts/_find"`); expect(config.options).toMatchInlineSnapshot(` Object { "tags": Array [ @@ -76,13 +76,8 @@ describe('findAlertRoute', () => { Object { "options": Object { "defaultSearchOperator": "OR", - "fields": undefined, - "filter": undefined, "page": 1, "perPage": 1, - "search": undefined, - "sortField": undefined, - "sortOrder": undefined, }, }, ] diff --git a/x-pack/plugins/alerting/server/routes/find.ts b/x-pack/plugins/alerts/server/routes/find.ts similarity index 81% rename from x-pack/plugins/alerting/server/routes/find.ts rename to x-pack/plugins/alerts/server/routes/find.ts index 3de95c9580cd49..80c9c20eec7da2 100644 --- a/x-pack/plugins/alerting/server/routes/find.ts +++ b/x-pack/plugins/alerts/server/routes/find.ts @@ -12,10 +12,11 @@ import { IKibanaResponse, KibanaResponseFactory, } from 'kibana/server'; -import { FindOptions } from '../../../alerting/server'; import { LicenseState } from '../lib/license_state'; import { verifyApiAccess } from '../lib/license_api_access'; import { BASE_ALERT_API_PATH } from '../../common'; +import { renameKeys } from './lib/rename_keys'; +import { FindOptions } from '..'; // config definition const querySchema = schema.object({ @@ -63,31 +64,29 @@ export const findAlertRoute = (router: IRouter, licenseState: LicenseState) => { return res.badRequest({ body: 'RouteHandlerContext is not registered for alerting' }); } const alertsClient = context.alerting.getAlertsClient(); + const query = req.query; - const options: FindOptions['options'] = { - perPage: query.per_page, - page: query.page, - search: query.search, - defaultSearchOperator: query.default_search_operator, - sortField: query.sort_field, - fields: query.fields, - filter: query.filter, - sortOrder: query.sort_order, + const renameMap = { + default_search_operator: 'defaultSearchOperator', + fields: 'fields', + has_reference: 'hasReference', + page: 'page', + per_page: 'perPage', + search: 'search', + sort_field: 'sortField', + sort_order: 'sortOrder', + filter: 'filter', }; + const options = renameKeys>(renameMap, query); + if (query.search_fields) { options.searchFields = Array.isArray(query.search_fields) ? query.search_fields : [query.search_fields]; } - if (query.has_reference) { - options.hasReference = query.has_reference; - } - - const findResult = await alertsClient.find({ - options, - }); + const findResult = await alertsClient.find({ options }); return res.ok({ body: findResult, }); diff --git a/x-pack/plugins/alerting/server/routes/get.test.ts b/x-pack/plugins/alerts/server/routes/get.test.ts similarity index 97% rename from x-pack/plugins/alerting/server/routes/get.test.ts rename to x-pack/plugins/alerts/server/routes/get.test.ts index 7335f13c85a4d1..b11224ff4794e2 100644 --- a/x-pack/plugins/alerting/server/routes/get.test.ts +++ b/x-pack/plugins/alerts/server/routes/get.test.ts @@ -60,7 +60,7 @@ describe('getAlertRoute', () => { getAlertRoute(router, licenseState); const [config, handler] = router.get.mock.calls[0]; - expect(config.path).toMatchInlineSnapshot(`"/api/alert/{id}"`); + expect(config.path).toMatchInlineSnapshot(`"/api/alerts/alert/{id}"`); expect(config.options).toMatchInlineSnapshot(` Object { "tags": Array [ diff --git a/x-pack/plugins/alerting/server/routes/get.ts b/x-pack/plugins/alerts/server/routes/get.ts similarity index 96% rename from x-pack/plugins/alerting/server/routes/get.ts rename to x-pack/plugins/alerts/server/routes/get.ts index cd78e7fbacddb4..ae9ebe1299371b 100644 --- a/x-pack/plugins/alerting/server/routes/get.ts +++ b/x-pack/plugins/alerts/server/routes/get.ts @@ -23,7 +23,7 @@ const paramSchema = schema.object({ export const getAlertRoute = (router: IRouter, licenseState: LicenseState) => { router.get( { - path: `${BASE_ALERT_API_PATH}/{id}`, + path: `${BASE_ALERT_API_PATH}/alert/{id}`, validate: { params: paramSchema, }, diff --git a/x-pack/plugins/alerting/server/routes/get_alert_state.test.ts b/x-pack/plugins/alerts/server/routes/get_alert_state.test.ts similarity index 94% rename from x-pack/plugins/alerting/server/routes/get_alert_state.test.ts rename to x-pack/plugins/alerts/server/routes/get_alert_state.test.ts index 20a420ca00986f..8c9051093f85b9 100644 --- a/x-pack/plugins/alerting/server/routes/get_alert_state.test.ts +++ b/x-pack/plugins/alerts/server/routes/get_alert_state.test.ts @@ -47,7 +47,7 @@ describe('getAlertStateRoute', () => { const [config, handler] = router.get.mock.calls[0]; - expect(config.path).toMatchInlineSnapshot(`"/api/alert/{id}/state"`); + expect(config.path).toMatchInlineSnapshot(`"/api/alerts/alert/{id}/state"`); expect(config.options).toMatchInlineSnapshot(` Object { "tags": Array [ @@ -90,7 +90,7 @@ describe('getAlertStateRoute', () => { const [config, handler] = router.get.mock.calls[0]; - expect(config.path).toMatchInlineSnapshot(`"/api/alert/{id}/state"`); + expect(config.path).toMatchInlineSnapshot(`"/api/alerts/alert/{id}/state"`); expect(config.options).toMatchInlineSnapshot(` Object { "tags": Array [ @@ -133,7 +133,7 @@ describe('getAlertStateRoute', () => { const [config, handler] = router.get.mock.calls[0]; - expect(config.path).toMatchInlineSnapshot(`"/api/alert/{id}/state"`); + expect(config.path).toMatchInlineSnapshot(`"/api/alerts/alert/{id}/state"`); expect(config.options).toMatchInlineSnapshot(` Object { "tags": Array [ diff --git a/x-pack/plugins/alerting/server/routes/get_alert_state.ts b/x-pack/plugins/alerts/server/routes/get_alert_state.ts similarity index 96% rename from x-pack/plugins/alerting/server/routes/get_alert_state.ts rename to x-pack/plugins/alerts/server/routes/get_alert_state.ts index a5cb14154db670..b27ae3758e1b9d 100644 --- a/x-pack/plugins/alerting/server/routes/get_alert_state.ts +++ b/x-pack/plugins/alerts/server/routes/get_alert_state.ts @@ -23,7 +23,7 @@ const paramSchema = schema.object({ export const getAlertStateRoute = (router: IRouter, licenseState: LicenseState) => { router.get( { - path: `${BASE_ALERT_API_PATH}/{id}/state`, + path: `${BASE_ALERT_API_PATH}/alert/{id}/state`, validate: { params: paramSchema, }, diff --git a/x-pack/plugins/alerting/server/routes/health.test.ts b/x-pack/plugins/alerts/server/routes/health.test.ts similarity index 99% rename from x-pack/plugins/alerting/server/routes/health.test.ts rename to x-pack/plugins/alerts/server/routes/health.test.ts index 9b1c95c393f567..b3f41e03ebdc98 100644 --- a/x-pack/plugins/alerting/server/routes/health.test.ts +++ b/x-pack/plugins/alerts/server/routes/health.test.ts @@ -31,7 +31,7 @@ describe('healthRoute', () => { const [config] = router.get.mock.calls[0]; - expect(config.path).toMatchInlineSnapshot(`"/api/alert/_health"`); + expect(config.path).toMatchInlineSnapshot(`"/api/alerts/_health"`); }); it('queries the usage api', async () => { diff --git a/x-pack/plugins/alerting/server/routes/health.ts b/x-pack/plugins/alerts/server/routes/health.ts similarity index 98% rename from x-pack/plugins/alerting/server/routes/health.ts rename to x-pack/plugins/alerts/server/routes/health.ts index fb4c5e76a02c92..b66e28b24e8a74 100644 --- a/x-pack/plugins/alerting/server/routes/health.ts +++ b/x-pack/plugins/alerts/server/routes/health.ts @@ -34,7 +34,7 @@ export function healthRoute( ) { router.get( { - path: '/api/alert/_health', + path: '/api/alerts/_health', validate: false, }, router.handleLegacyErrors(async function ( diff --git a/x-pack/plugins/alerting/server/routes/index.ts b/x-pack/plugins/alerts/server/routes/index.ts similarity index 100% rename from x-pack/plugins/alerting/server/routes/index.ts rename to x-pack/plugins/alerts/server/routes/index.ts diff --git a/x-pack/plugins/alerting/server/routes/lib/error_handler.ts b/x-pack/plugins/alerts/server/routes/lib/error_handler.ts similarity index 94% rename from x-pack/plugins/alerting/server/routes/lib/error_handler.ts rename to x-pack/plugins/alerts/server/routes/lib/error_handler.ts index b3cf48c52fe17d..e0c620b0670c9f 100644 --- a/x-pack/plugins/alerting/server/routes/lib/error_handler.ts +++ b/x-pack/plugins/alerts/server/routes/lib/error_handler.ts @@ -27,7 +27,7 @@ export function handleDisabledApiKeysError( if (isApiKeyDisabledError(e)) { return response.badRequest({ body: new Error( - i18n.translate('xpack.alerting.api.error.disabledApiKeys', { + i18n.translate('xpack.alerts.api.error.disabledApiKeys', { defaultMessage: 'Alerting relies upon API keys which appear to be disabled', }) ), diff --git a/x-pack/plugins/alerts/server/routes/lib/rename_keys.ts b/x-pack/plugins/alerts/server/routes/lib/rename_keys.ts new file mode 100644 index 00000000000000..bfe60a0ecc648a --- /dev/null +++ b/x-pack/plugins/alerts/server/routes/lib/rename_keys.ts @@ -0,0 +1,16 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export const renameKeys = , U extends Record>( + keysMap: Record, + obj: Record +): T => + Object.keys(obj).reduce((acc, key) => { + return { + ...acc, + ...{ [keysMap[key] || key]: obj[key] }, + }; + }, {} as T); diff --git a/x-pack/plugins/alerting/server/routes/list_alert_types.test.ts b/x-pack/plugins/alerts/server/routes/list_alert_types.test.ts similarity index 94% rename from x-pack/plugins/alerting/server/routes/list_alert_types.test.ts rename to x-pack/plugins/alerts/server/routes/list_alert_types.test.ts index e940b2d1020451..3192154f6664c4 100644 --- a/x-pack/plugins/alerting/server/routes/list_alert_types.test.ts +++ b/x-pack/plugins/alerts/server/routes/list_alert_types.test.ts @@ -27,7 +27,7 @@ describe('listAlertTypesRoute', () => { const [config, handler] = router.get.mock.calls[0]; - expect(config.path).toMatchInlineSnapshot(`"/api/alert/types"`); + expect(config.path).toMatchInlineSnapshot(`"/api/alerts/list_alert_types"`); expect(config.options).toMatchInlineSnapshot(` Object { "tags": Array [ @@ -89,7 +89,7 @@ describe('listAlertTypesRoute', () => { const [config, handler] = router.get.mock.calls[0]; - expect(config.path).toMatchInlineSnapshot(`"/api/alert/types"`); + expect(config.path).toMatchInlineSnapshot(`"/api/alerts/list_alert_types"`); expect(config.options).toMatchInlineSnapshot(` Object { "tags": Array [ @@ -140,7 +140,7 @@ describe('listAlertTypesRoute', () => { const [config, handler] = router.get.mock.calls[0]; - expect(config.path).toMatchInlineSnapshot(`"/api/alert/types"`); + expect(config.path).toMatchInlineSnapshot(`"/api/alerts/list_alert_types"`); expect(config.options).toMatchInlineSnapshot(` Object { "tags": Array [ diff --git a/x-pack/plugins/alerting/server/routes/list_alert_types.ts b/x-pack/plugins/alerts/server/routes/list_alert_types.ts similarity index 95% rename from x-pack/plugins/alerting/server/routes/list_alert_types.ts rename to x-pack/plugins/alerts/server/routes/list_alert_types.ts index f5b4e3263f341a..51a4558108e293 100644 --- a/x-pack/plugins/alerting/server/routes/list_alert_types.ts +++ b/x-pack/plugins/alerts/server/routes/list_alert_types.ts @@ -18,7 +18,7 @@ import { BASE_ALERT_API_PATH } from '../../common'; export const listAlertTypesRoute = (router: IRouter, licenseState: LicenseState) => { router.get( { - path: `${BASE_ALERT_API_PATH}/types`, + path: `${BASE_ALERT_API_PATH}/list_alert_types`, validate: {}, options: { tags: ['access:alerting-read'], diff --git a/x-pack/plugins/alerting/server/routes/mute_all.test.ts b/x-pack/plugins/alerts/server/routes/mute_all.test.ts similarity index 95% rename from x-pack/plugins/alerting/server/routes/mute_all.test.ts rename to x-pack/plugins/alerts/server/routes/mute_all.test.ts index 5ef9e3694f8f45..bcdb8cbd022ac0 100644 --- a/x-pack/plugins/alerting/server/routes/mute_all.test.ts +++ b/x-pack/plugins/alerts/server/routes/mute_all.test.ts @@ -28,7 +28,7 @@ describe('muteAllAlertRoute', () => { const [config, handler] = router.post.mock.calls[0]; - expect(config.path).toMatchInlineSnapshot(`"/api/alert/{id}/_mute_all"`); + expect(config.path).toMatchInlineSnapshot(`"/api/alerts/alert/{id}/_mute_all"`); expect(config.options).toMatchInlineSnapshot(` Object { "tags": Array [ diff --git a/x-pack/plugins/alerting/server/routes/mute_all.ts b/x-pack/plugins/alerts/server/routes/mute_all.ts similarity index 96% rename from x-pack/plugins/alerting/server/routes/mute_all.ts rename to x-pack/plugins/alerts/server/routes/mute_all.ts index b43a1ec30ed1fb..5b05d7231c3857 100644 --- a/x-pack/plugins/alerting/server/routes/mute_all.ts +++ b/x-pack/plugins/alerts/server/routes/mute_all.ts @@ -23,7 +23,7 @@ const paramSchema = schema.object({ export const muteAllAlertRoute = (router: IRouter, licenseState: LicenseState) => { router.post( { - path: `${BASE_ALERT_API_PATH}/{id}/_mute_all`, + path: `${BASE_ALERT_API_PATH}/alert/{id}/_mute_all`, validate: { params: paramSchema, }, diff --git a/x-pack/plugins/alerting/server/routes/mute_instance.test.ts b/x-pack/plugins/alerts/server/routes/mute_instance.test.ts similarity index 92% rename from x-pack/plugins/alerting/server/routes/mute_instance.test.ts rename to x-pack/plugins/alerts/server/routes/mute_instance.test.ts index 2e6adedb76df9b..c382c12de21cd8 100644 --- a/x-pack/plugins/alerting/server/routes/mute_instance.test.ts +++ b/x-pack/plugins/alerts/server/routes/mute_instance.test.ts @@ -29,7 +29,7 @@ describe('muteAlertInstanceRoute', () => { const [config, handler] = router.post.mock.calls[0]; expect(config.path).toMatchInlineSnapshot( - `"/api/alert/{alertId}/alert_instance/{alertInstanceId}/_mute"` + `"/api/alerts/alert/{alert_id}/alert_instance/{alert_instance_id}/_mute"` ); expect(config.options).toMatchInlineSnapshot(` Object { @@ -45,8 +45,8 @@ describe('muteAlertInstanceRoute', () => { { alertsClient }, { params: { - alertId: '1', - alertInstanceId: '2', + alert_id: '1', + alert_instance_id: '2', }, }, ['noContent'] diff --git a/x-pack/plugins/alerting/server/routes/mute_instance.ts b/x-pack/plugins/alerts/server/routes/mute_instance.ts similarity index 72% rename from x-pack/plugins/alerting/server/routes/mute_instance.ts rename to x-pack/plugins/alerts/server/routes/mute_instance.ts index c0c69fe9653da4..00550f4af34185 100644 --- a/x-pack/plugins/alerting/server/routes/mute_instance.ts +++ b/x-pack/plugins/alerts/server/routes/mute_instance.ts @@ -15,16 +15,18 @@ import { import { LicenseState } from '../lib/license_state'; import { verifyApiAccess } from '../lib/license_api_access'; import { BASE_ALERT_API_PATH } from '../../common'; +import { renameKeys } from './lib/rename_keys'; +import { MuteOptions } from '../alerts_client'; const paramSchema = schema.object({ - alertId: schema.string(), - alertInstanceId: schema.string(), + alert_id: schema.string(), + alert_instance_id: schema.string(), }); export const muteAlertInstanceRoute = (router: IRouter, licenseState: LicenseState) => { router.post( { - path: `${BASE_ALERT_API_PATH}/{alertId}/alert_instance/{alertInstanceId}/_mute`, + path: `${BASE_ALERT_API_PATH}/alert/{alert_id}/alert_instance/{alert_instance_id}/_mute`, validate: { params: paramSchema, }, @@ -42,8 +44,14 @@ export const muteAlertInstanceRoute = (router: IRouter, licenseState: LicenseSta return res.badRequest({ body: 'RouteHandlerContext is not registered for alerting' }); } const alertsClient = context.alerting.getAlertsClient(); - const { alertId, alertInstanceId } = req.params; - await alertsClient.muteInstance({ alertId, alertInstanceId }); + + const renameMap = { + alert_id: 'alertId', + alert_instance_id: 'alertInstanceId', + }; + + const renamedQuery = renameKeys>(renameMap, req.params); + await alertsClient.muteInstance(renamedQuery); return res.noContent(); }) ); diff --git a/x-pack/plugins/alerting/server/routes/unmute_all.test.ts b/x-pack/plugins/alerts/server/routes/unmute_all.test.ts similarity index 95% rename from x-pack/plugins/alerting/server/routes/unmute_all.test.ts rename to x-pack/plugins/alerts/server/routes/unmute_all.test.ts index 1756dbd3fb41d0..e13af38fe4cb1b 100644 --- a/x-pack/plugins/alerting/server/routes/unmute_all.test.ts +++ b/x-pack/plugins/alerts/server/routes/unmute_all.test.ts @@ -27,7 +27,7 @@ describe('unmuteAllAlertRoute', () => { const [config, handler] = router.post.mock.calls[0]; - expect(config.path).toMatchInlineSnapshot(`"/api/alert/{id}/_unmute_all"`); + expect(config.path).toMatchInlineSnapshot(`"/api/alerts/alert/{id}/_unmute_all"`); expect(config.options).toMatchInlineSnapshot(` Object { "tags": Array [ diff --git a/x-pack/plugins/alerting/server/routes/unmute_all.ts b/x-pack/plugins/alerts/server/routes/unmute_all.ts similarity index 96% rename from x-pack/plugins/alerting/server/routes/unmute_all.ts rename to x-pack/plugins/alerts/server/routes/unmute_all.ts index d4b6e8b7d61b10..1efc9ed40054e2 100644 --- a/x-pack/plugins/alerting/server/routes/unmute_all.ts +++ b/x-pack/plugins/alerts/server/routes/unmute_all.ts @@ -23,7 +23,7 @@ const paramSchema = schema.object({ export const unmuteAllAlertRoute = (router: IRouter, licenseState: LicenseState) => { router.post( { - path: `${BASE_ALERT_API_PATH}/{id}/_unmute_all`, + path: `${BASE_ALERT_API_PATH}/alert/{id}/_unmute_all`, validate: { params: paramSchema, }, diff --git a/x-pack/plugins/alerting/server/routes/unmute_instance.test.ts b/x-pack/plugins/alerts/server/routes/unmute_instance.test.ts similarity index 95% rename from x-pack/plugins/alerting/server/routes/unmute_instance.test.ts rename to x-pack/plugins/alerts/server/routes/unmute_instance.test.ts index 9b9542c606741c..b2e2f24e91de9f 100644 --- a/x-pack/plugins/alerting/server/routes/unmute_instance.test.ts +++ b/x-pack/plugins/alerts/server/routes/unmute_instance.test.ts @@ -29,7 +29,7 @@ describe('unmuteAlertInstanceRoute', () => { const [config, handler] = router.post.mock.calls[0]; expect(config.path).toMatchInlineSnapshot( - `"/api/alert/{alertId}/alert_instance/{alertInstanceId}/_unmute"` + `"/api/alerts/alert/{alertId}/alert_instance/{alertInstanceId}/_unmute"` ); expect(config.options).toMatchInlineSnapshot(` Object { diff --git a/x-pack/plugins/alerting/server/routes/unmute_instance.ts b/x-pack/plugins/alerts/server/routes/unmute_instance.ts similarity index 94% rename from x-pack/plugins/alerting/server/routes/unmute_instance.ts rename to x-pack/plugins/alerts/server/routes/unmute_instance.ts index 97ccd8f0adce7d..967f9f890c9fb8 100644 --- a/x-pack/plugins/alerting/server/routes/unmute_instance.ts +++ b/x-pack/plugins/alerts/server/routes/unmute_instance.ts @@ -24,7 +24,7 @@ const paramSchema = schema.object({ export const unmuteAlertInstanceRoute = (router: IRouter, licenseState: LicenseState) => { router.post( { - path: `${BASE_ALERT_API_PATH}/{alertId}/alert_instance/{alertInstanceId}/_unmute`, + path: `${BASE_ALERT_API_PATH}/alert/{alertId}/alert_instance/{alertInstanceId}/_unmute`, validate: { params: paramSchema, }, diff --git a/x-pack/plugins/alerting/server/routes/update.test.ts b/x-pack/plugins/alerts/server/routes/update.test.ts similarity index 98% rename from x-pack/plugins/alerting/server/routes/update.test.ts rename to x-pack/plugins/alerts/server/routes/update.test.ts index cd96f289b87147..c7d23f2670b45c 100644 --- a/x-pack/plugins/alerting/server/routes/update.test.ts +++ b/x-pack/plugins/alerts/server/routes/update.test.ts @@ -51,7 +51,7 @@ describe('updateAlertRoute', () => { const [config, handler] = router.put.mock.calls[0]; - expect(config.path).toMatchInlineSnapshot(`"/api/alert/{id}"`); + expect(config.path).toMatchInlineSnapshot(`"/api/alerts/alert/{id}"`); expect(config.options).toMatchInlineSnapshot(` Object { "tags": Array [ diff --git a/x-pack/plugins/alerting/server/routes/update.ts b/x-pack/plugins/alerts/server/routes/update.ts similarity index 98% rename from x-pack/plugins/alerting/server/routes/update.ts rename to x-pack/plugins/alerts/server/routes/update.ts index 23fea7dc4002fe..99b81dfc5b56e9 100644 --- a/x-pack/plugins/alerting/server/routes/update.ts +++ b/x-pack/plugins/alerts/server/routes/update.ts @@ -44,7 +44,7 @@ const bodySchema = schema.object({ export const updateAlertRoute = (router: IRouter, licenseState: LicenseState) => { router.put( { - path: `${BASE_ALERT_API_PATH}/{id}`, + path: `${BASE_ALERT_API_PATH}/alert/{id}`, validate: { body: bodySchema, params: paramSchema, diff --git a/x-pack/plugins/alerting/server/routes/update_api_key.test.ts b/x-pack/plugins/alerts/server/routes/update_api_key.test.ts similarity index 95% rename from x-pack/plugins/alerting/server/routes/update_api_key.test.ts rename to x-pack/plugins/alerts/server/routes/update_api_key.test.ts index 0347feb24a2359..babae59553b5b3 100644 --- a/x-pack/plugins/alerting/server/routes/update_api_key.test.ts +++ b/x-pack/plugins/alerts/server/routes/update_api_key.test.ts @@ -28,7 +28,7 @@ describe('updateApiKeyRoute', () => { const [config, handler] = router.post.mock.calls[0]; - expect(config.path).toMatchInlineSnapshot(`"/api/alert/{id}/_update_api_key"`); + expect(config.path).toMatchInlineSnapshot(`"/api/alerts/alert/{id}/_update_api_key"`); expect(config.options).toMatchInlineSnapshot(` Object { "tags": Array [ diff --git a/x-pack/plugins/alerting/server/routes/update_api_key.ts b/x-pack/plugins/alerts/server/routes/update_api_key.ts similarity index 96% rename from x-pack/plugins/alerting/server/routes/update_api_key.ts rename to x-pack/plugins/alerts/server/routes/update_api_key.ts index 9d88201d7cd432..4736351a25cbd1 100644 --- a/x-pack/plugins/alerting/server/routes/update_api_key.ts +++ b/x-pack/plugins/alerts/server/routes/update_api_key.ts @@ -24,7 +24,7 @@ const paramSchema = schema.object({ export const updateApiKeyRoute = (router: IRouter, licenseState: LicenseState) => { router.post( { - path: `${BASE_ALERT_API_PATH}/{id}/_update_api_key`, + path: `${BASE_ALERT_API_PATH}/alert/{id}/_update_api_key`, validate: { params: paramSchema, }, diff --git a/x-pack/plugins/alerting/server/saved_objects/index.ts b/x-pack/plugins/alerts/server/saved_objects/index.ts similarity index 100% rename from x-pack/plugins/alerting/server/saved_objects/index.ts rename to x-pack/plugins/alerts/server/saved_objects/index.ts diff --git a/x-pack/plugins/alerting/server/saved_objects/mappings.json b/x-pack/plugins/alerts/server/saved_objects/mappings.json similarity index 100% rename from x-pack/plugins/alerting/server/saved_objects/mappings.json rename to x-pack/plugins/alerts/server/saved_objects/mappings.json diff --git a/x-pack/plugins/alerting/server/task_runner/alert_task_instance.test.ts b/x-pack/plugins/alerts/server/task_runner/alert_task_instance.test.ts similarity index 98% rename from x-pack/plugins/alerting/server/task_runner/alert_task_instance.test.ts rename to x-pack/plugins/alerts/server/task_runner/alert_task_instance.test.ts index 28b3576dffc6ed..efac4c5dcdc01f 100644 --- a/x-pack/plugins/alerting/server/task_runner/alert_task_instance.test.ts +++ b/x-pack/plugins/alerts/server/task_runner/alert_task_instance.test.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { ConcreteTaskInstance, TaskStatus } from '../../../../plugins/task_manager/server'; +import { ConcreteTaskInstance, TaskStatus } from '../../../task_manager/server'; import { AlertTaskInstance, taskInstanceToAlertTaskInstance } from './alert_task_instance'; import uuid from 'uuid'; import { SanitizedAlert } from '../types'; diff --git a/x-pack/plugins/alerting/server/task_runner/alert_task_instance.ts b/x-pack/plugins/alerts/server/task_runner/alert_task_instance.ts similarity index 95% rename from x-pack/plugins/alerting/server/task_runner/alert_task_instance.ts rename to x-pack/plugins/alerts/server/task_runner/alert_task_instance.ts index 4be506b78493b1..a290f3fa33c70d 100644 --- a/x-pack/plugins/alerting/server/task_runner/alert_task_instance.ts +++ b/x-pack/plugins/alerts/server/task_runner/alert_task_instance.ts @@ -6,7 +6,7 @@ import * as t from 'io-ts'; import { pipe } from 'fp-ts/lib/pipeable'; import { fold } from 'fp-ts/lib/Either'; -import { ConcreteTaskInstance } from '../../../../plugins/task_manager/server'; +import { ConcreteTaskInstance } from '../../../task_manager/server'; import { SanitizedAlert, AlertTaskState, diff --git a/x-pack/plugins/alerting/server/task_runner/create_execution_handler.test.ts b/x-pack/plugins/alerts/server/task_runner/create_execution_handler.test.ts similarity index 100% rename from x-pack/plugins/alerting/server/task_runner/create_execution_handler.test.ts rename to x-pack/plugins/alerts/server/task_runner/create_execution_handler.test.ts diff --git a/x-pack/plugins/alerting/server/task_runner/create_execution_handler.ts b/x-pack/plugins/alerts/server/task_runner/create_execution_handler.ts similarity index 98% rename from x-pack/plugins/alerting/server/task_runner/create_execution_handler.ts rename to x-pack/plugins/alerts/server/task_runner/create_execution_handler.ts index 61bbab50b12229..3c58c6d9ba2883 100644 --- a/x-pack/plugins/alerting/server/task_runner/create_execution_handler.ts +++ b/x-pack/plugins/alerts/server/task_runner/create_execution_handler.ts @@ -8,7 +8,7 @@ import { pluck } from 'lodash'; import { AlertAction, State, Context, AlertType } from '../types'; import { Logger } from '../../../../../src/core/server'; import { transformActionParams } from './transform_action_params'; -import { PluginStartContract as ActionsPluginStartContract } from '../../../../plugins/actions/server'; +import { PluginStartContract as ActionsPluginStartContract } from '../../../actions/server'; import { IEventLogger, IEvent, SAVED_OBJECT_REL_PRIMARY } from '../../../event_log/server'; import { EVENT_LOG_ACTIONS } from '../plugin'; diff --git a/x-pack/plugins/alerting/server/task_runner/get_next_run_at.test.ts b/x-pack/plugins/alerts/server/task_runner/get_next_run_at.test.ts similarity index 100% rename from x-pack/plugins/alerting/server/task_runner/get_next_run_at.test.ts rename to x-pack/plugins/alerts/server/task_runner/get_next_run_at.test.ts diff --git a/x-pack/plugins/alerting/server/task_runner/get_next_run_at.ts b/x-pack/plugins/alerts/server/task_runner/get_next_run_at.ts similarity index 100% rename from x-pack/plugins/alerting/server/task_runner/get_next_run_at.ts rename to x-pack/plugins/alerts/server/task_runner/get_next_run_at.ts diff --git a/x-pack/plugins/alerting/server/task_runner/index.ts b/x-pack/plugins/alerts/server/task_runner/index.ts similarity index 100% rename from x-pack/plugins/alerting/server/task_runner/index.ts rename to x-pack/plugins/alerts/server/task_runner/index.ts diff --git a/x-pack/plugins/alerting/server/task_runner/task_runner.test.ts b/x-pack/plugins/alerts/server/task_runner/task_runner.test.ts similarity index 99% rename from x-pack/plugins/alerting/server/task_runner/task_runner.test.ts rename to x-pack/plugins/alerts/server/task_runner/task_runner.test.ts index 98824b8cf4e1a4..983dff86d5602b 100644 --- a/x-pack/plugins/alerting/server/task_runner/task_runner.test.ts +++ b/x-pack/plugins/alerts/server/task_runner/task_runner.test.ts @@ -7,10 +7,10 @@ import sinon from 'sinon'; import { schema } from '@kbn/config-schema'; import { AlertExecutorOptions } from '../types'; -import { ConcreteTaskInstance, TaskStatus } from '../../../../plugins/task_manager/server'; +import { ConcreteTaskInstance, TaskStatus } from '../../../task_manager/server'; import { TaskRunnerContext } from './task_runner_factory'; import { TaskRunner } from './task_runner'; -import { encryptedSavedObjectsMock } from '../../../../plugins/encrypted_saved_objects/server/mocks'; +import { encryptedSavedObjectsMock } from '../../../encrypted_saved_objects/server/mocks'; import { loggingServiceMock } from '../../../../../src/core/server/mocks'; import { PluginStartContract as ActionsPluginStart } from '../../../actions/server'; import { actionsMock } from '../../../actions/server/mocks'; diff --git a/x-pack/plugins/alerting/server/task_runner/task_runner.ts b/x-pack/plugins/alerts/server/task_runner/task_runner.ts similarity index 99% rename from x-pack/plugins/alerting/server/task_runner/task_runner.ts rename to x-pack/plugins/alerts/server/task_runner/task_runner.ts index 0831163d1d326e..be399893088e33 100644 --- a/x-pack/plugins/alerting/server/task_runner/task_runner.ts +++ b/x-pack/plugins/alerts/server/task_runner/task_runner.ts @@ -7,7 +7,7 @@ import { pick, mapValues, omit, without } from 'lodash'; import { Logger, SavedObject, KibanaRequest } from '../../../../../src/core/server'; import { TaskRunnerContext } from './task_runner_factory'; -import { ConcreteTaskInstance } from '../../../../plugins/task_manager/server'; +import { ConcreteTaskInstance } from '../../../task_manager/server'; import { createExecutionHandler } from './create_execution_handler'; import { AlertInstance, createAlertInstanceFactory } from '../alert_instance'; import { getNextRunAt } from './get_next_run_at'; diff --git a/x-pack/plugins/alerting/server/task_runner/task_runner_factory.test.ts b/x-pack/plugins/alerts/server/task_runner/task_runner_factory.test.ts similarity index 93% rename from x-pack/plugins/alerting/server/task_runner/task_runner_factory.test.ts rename to x-pack/plugins/alerts/server/task_runner/task_runner_factory.test.ts index c1318bac48dfbc..7d9710d8a3e082 100644 --- a/x-pack/plugins/alerting/server/task_runner/task_runner_factory.test.ts +++ b/x-pack/plugins/alerts/server/task_runner/task_runner_factory.test.ts @@ -5,9 +5,9 @@ */ import sinon from 'sinon'; -import { ConcreteTaskInstance, TaskStatus } from '../../../../plugins/task_manager/server'; +import { ConcreteTaskInstance, TaskStatus } from '../../../task_manager/server'; import { TaskRunnerContext, TaskRunnerFactory } from './task_runner_factory'; -import { encryptedSavedObjectsMock } from '../../../../plugins/encrypted_saved_objects/server/mocks'; +import { encryptedSavedObjectsMock } from '../../../encrypted_saved_objects/server/mocks'; import { loggingServiceMock } from '../../../../../src/core/server/mocks'; import { actionsMock } from '../../../actions/server/mocks'; import { alertsMock } from '../mocks'; diff --git a/x-pack/plugins/alerting/server/task_runner/task_runner_factory.ts b/x-pack/plugins/alerts/server/task_runner/task_runner_factory.ts similarity index 87% rename from x-pack/plugins/alerting/server/task_runner/task_runner_factory.ts rename to x-pack/plugins/alerts/server/task_runner/task_runner_factory.ts index c50e288d2e5203..ca762cf2b2105f 100644 --- a/x-pack/plugins/alerting/server/task_runner/task_runner_factory.ts +++ b/x-pack/plugins/alerts/server/task_runner/task_runner_factory.ts @@ -4,9 +4,9 @@ * you may not use this file except in compliance with the Elastic License. */ import { Logger } from '../../../../../src/core/server'; -import { RunContext } from '../../../../plugins/task_manager/server'; -import { EncryptedSavedObjectsClient } from '../../../../plugins/encrypted_saved_objects/server'; -import { PluginStartContract as ActionsPluginStartContract } from '../../../../plugins/actions/server'; +import { RunContext } from '../../../task_manager/server'; +import { EncryptedSavedObjectsClient } from '../../../encrypted_saved_objects/server'; +import { PluginStartContract as ActionsPluginStartContract } from '../../../actions/server'; import { AlertType, GetBasePathFunction, diff --git a/x-pack/plugins/alerting/server/task_runner/transform_action_params.test.ts b/x-pack/plugins/alerts/server/task_runner/transform_action_params.test.ts similarity index 100% rename from x-pack/plugins/alerting/server/task_runner/transform_action_params.test.ts rename to x-pack/plugins/alerts/server/task_runner/transform_action_params.test.ts diff --git a/x-pack/plugins/alerting/server/task_runner/transform_action_params.ts b/x-pack/plugins/alerts/server/task_runner/transform_action_params.ts similarity index 100% rename from x-pack/plugins/alerting/server/task_runner/transform_action_params.ts rename to x-pack/plugins/alerts/server/task_runner/transform_action_params.ts diff --git a/x-pack/plugins/alerting/server/test_utils/index.ts b/x-pack/plugins/alerts/server/test_utils/index.ts similarity index 100% rename from x-pack/plugins/alerting/server/test_utils/index.ts rename to x-pack/plugins/alerts/server/test_utils/index.ts diff --git a/x-pack/plugins/alerting/server/types.ts b/x-pack/plugins/alerts/server/types.ts similarity index 100% rename from x-pack/plugins/alerting/server/types.ts rename to x-pack/plugins/alerts/server/types.ts diff --git a/x-pack/plugins/alerting/server/usage/alerts_telemetry.test.ts b/x-pack/plugins/alerts/server/usage/alerts_telemetry.test.ts similarity index 100% rename from x-pack/plugins/alerting/server/usage/alerts_telemetry.test.ts rename to x-pack/plugins/alerts/server/usage/alerts_telemetry.test.ts diff --git a/x-pack/plugins/alerting/server/usage/alerts_telemetry.ts b/x-pack/plugins/alerts/server/usage/alerts_telemetry.ts similarity index 100% rename from x-pack/plugins/alerting/server/usage/alerts_telemetry.ts rename to x-pack/plugins/alerts/server/usage/alerts_telemetry.ts diff --git a/x-pack/plugins/alerting/server/usage/alerts_usage_collector.test.ts b/x-pack/plugins/alerts/server/usage/alerts_usage_collector.test.ts similarity index 100% rename from x-pack/plugins/alerting/server/usage/alerts_usage_collector.test.ts rename to x-pack/plugins/alerts/server/usage/alerts_usage_collector.test.ts diff --git a/x-pack/plugins/alerting/server/usage/alerts_usage_collector.ts b/x-pack/plugins/alerts/server/usage/alerts_usage_collector.ts similarity index 100% rename from x-pack/plugins/alerting/server/usage/alerts_usage_collector.ts rename to x-pack/plugins/alerts/server/usage/alerts_usage_collector.ts diff --git a/x-pack/plugins/alerting/server/usage/index.ts b/x-pack/plugins/alerts/server/usage/index.ts similarity index 100% rename from x-pack/plugins/alerting/server/usage/index.ts rename to x-pack/plugins/alerts/server/usage/index.ts diff --git a/x-pack/plugins/alerting/server/usage/task.ts b/x-pack/plugins/alerts/server/usage/task.ts similarity index 100% rename from x-pack/plugins/alerting/server/usage/task.ts rename to x-pack/plugins/alerts/server/usage/task.ts diff --git a/x-pack/plugins/alerting/server/usage/types.ts b/x-pack/plugins/alerts/server/usage/types.ts similarity index 100% rename from x-pack/plugins/alerting/server/usage/types.ts rename to x-pack/plugins/alerts/server/usage/types.ts diff --git a/x-pack/plugins/apm/kibana.json b/x-pack/plugins/apm/kibana.json index dd89fac66f6e8f..2de3c9c97065d2 100644 --- a/x-pack/plugins/apm/kibana.json +++ b/x-pack/plugins/apm/kibana.json @@ -15,7 +15,7 @@ "usageCollection", "taskManager", "actions", - "alerting", + "alerts", "observability", "security" ], diff --git a/x-pack/plugins/apm/public/components/app/ServiceDetails/index.tsx b/x-pack/plugins/apm/public/components/app/ServiceDetails/index.tsx index c3d426a6275a75..0dbde5ea86a187 100644 --- a/x-pack/plugins/apm/public/components/app/ServiceDetails/index.tsx +++ b/x-pack/plugins/apm/public/components/app/ServiceDetails/index.tsx @@ -28,7 +28,7 @@ export function ServiceDetails({ tab }: Props) { const canSaveAlerts = !!plugin.core.application.capabilities.apm[ 'alerting:save' ]; - const isAlertingPluginEnabled = 'alerting' in plugin.plugins; + const isAlertingPluginEnabled = 'alerts' in plugin.plugins; const isAlertingAvailable = isAlertingPluginEnabled && (canReadAlerts || canSaveAlerts); diff --git a/x-pack/plugins/apm/public/plugin.ts b/x-pack/plugins/apm/public/plugin.ts index e732e695b36b13..76320efe617eae 100644 --- a/x-pack/plugins/apm/public/plugin.ts +++ b/x-pack/plugins/apm/public/plugin.ts @@ -17,7 +17,7 @@ import { DEFAULT_APP_CATEGORIES } from '../../../../src/core/public'; import { PluginSetupContract as AlertingPluginPublicSetup, PluginStartContract as AlertingPluginPublicStart, -} from '../../alerting/public'; +} from '../../alerts/public'; import { FeaturesPluginSetup } from '../../features/public'; import { DataPublicPluginSetup, @@ -44,7 +44,7 @@ export type ApmPluginSetup = void; export type ApmPluginStart = void; export interface ApmPluginSetupDeps { - alerting?: AlertingPluginPublicSetup; + alerts?: AlertingPluginPublicSetup; data: DataPublicPluginSetup; features: FeaturesPluginSetup; home: HomePublicPluginSetup; @@ -53,7 +53,7 @@ export interface ApmPluginSetupDeps { } export interface ApmPluginStartDeps { - alerting?: AlertingPluginPublicStart; + alerts?: AlertingPluginPublicStart; data: DataPublicPluginStart; home: void; licensing: void; diff --git a/x-pack/plugins/apm/server/lib/alerts/register_apm_alerts.ts b/x-pack/plugins/apm/server/lib/alerts/register_apm_alerts.ts index 8af9f386ecebf8..4b8e9cf937a2b2 100644 --- a/x-pack/plugins/apm/server/lib/alerts/register_apm_alerts.ts +++ b/x-pack/plugins/apm/server/lib/alerts/register_apm_alerts.ts @@ -5,25 +5,25 @@ */ import { Observable } from 'rxjs'; -import { AlertingPlugin } from '../../../../alerting/server'; +import { AlertingPlugin } from '../../../../alerts/server'; import { ActionsPlugin } from '../../../../actions/server'; import { registerTransactionDurationAlertType } from './register_transaction_duration_alert_type'; import { registerErrorRateAlertType } from './register_error_rate_alert_type'; import { APMConfig } from '../..'; interface Params { - alerting: AlertingPlugin['setup']; + alerts: AlertingPlugin['setup']; actions: ActionsPlugin['setup']; config$: Observable; } export function registerApmAlerts(params: Params) { registerTransactionDurationAlertType({ - alerting: params.alerting, + alerts: params.alerts, config$: params.config$, }); registerErrorRateAlertType({ - alerting: params.alerting, + alerts: params.alerts, config$: params.config$, }); } diff --git a/x-pack/plugins/apm/server/lib/alerts/register_error_rate_alert_type.ts b/x-pack/plugins/apm/server/lib/alerts/register_error_rate_alert_type.ts index ee7bd9eeb4b6f4..53843b7f7412b2 100644 --- a/x-pack/plugins/apm/server/lib/alerts/register_error_rate_alert_type.ts +++ b/x-pack/plugins/apm/server/lib/alerts/register_error_rate_alert_type.ts @@ -19,12 +19,12 @@ import { SERVICE_NAME, SERVICE_ENVIRONMENT, } from '../../../common/elasticsearch_fieldnames'; -import { AlertingPlugin } from '../../../../alerting/server'; +import { AlertingPlugin } from '../../../../alerts/server'; import { getApmIndices } from '../settings/apm_indices/get_apm_indices'; import { APMConfig } from '../..'; interface RegisterAlertParams { - alerting: AlertingPlugin['setup']; + alerts: AlertingPlugin['setup']; config$: Observable; } @@ -39,10 +39,10 @@ const paramsSchema = schema.object({ const alertTypeConfig = ALERT_TYPES_CONFIG[AlertType.ErrorRate]; export function registerErrorRateAlertType({ - alerting, + alerts, config$, }: RegisterAlertParams) { - alerting.registerType({ + alerts.registerType({ id: AlertType.ErrorRate, name: alertTypeConfig.name, actionGroups: alertTypeConfig.actionGroups, diff --git a/x-pack/plugins/apm/server/lib/alerts/register_transaction_duration_alert_type.ts b/x-pack/plugins/apm/server/lib/alerts/register_transaction_duration_alert_type.ts index afb402200a07bc..1fd1aef4c8b70d 100644 --- a/x-pack/plugins/apm/server/lib/alerts/register_transaction_duration_alert_type.ts +++ b/x-pack/plugins/apm/server/lib/alerts/register_transaction_duration_alert_type.ts @@ -18,12 +18,12 @@ import { TRANSACTION_DURATION, SERVICE_ENVIRONMENT, } from '../../../common/elasticsearch_fieldnames'; -import { AlertingPlugin } from '../../../../alerting/server'; +import { AlertingPlugin } from '../../../../alerts/server'; import { getApmIndices } from '../settings/apm_indices/get_apm_indices'; import { APMConfig } from '../..'; interface RegisterAlertParams { - alerting: AlertingPlugin['setup']; + alerts: AlertingPlugin['setup']; config$: Observable; } @@ -44,10 +44,10 @@ const paramsSchema = schema.object({ const alertTypeConfig = ALERT_TYPES_CONFIG[AlertType.TransactionDuration]; export function registerTransactionDurationAlertType({ - alerting, + alerts, config$, }: RegisterAlertParams) { - alerting.registerType({ + alerts.registerType({ id: AlertType.TransactionDuration, name: alertTypeConfig.name, actionGroups: alertTypeConfig.actionGroups, diff --git a/x-pack/plugins/apm/server/plugin.ts b/x-pack/plugins/apm/server/plugin.ts index b9ad14f7ec47d0..d32d16d4c3cc83 100644 --- a/x-pack/plugins/apm/server/plugin.ts +++ b/x-pack/plugins/apm/server/plugin.ts @@ -17,7 +17,7 @@ import { ObservabilityPluginSetup } from '../../observability/server'; import { SecurityPluginSetup } from '../../security/public'; import { UsageCollectionSetup } from '../../../../src/plugins/usage_collection/server'; import { TaskManagerSetupContract } from '../../task_manager/server'; -import { AlertingPlugin } from '../../alerting/server'; +import { AlertingPlugin } from '../../alerts/server'; import { ActionsPlugin } from '../../actions/server'; import { APMOSSPluginSetup } from '../../../../src/plugins/apm_oss/server'; import { createApmAgentConfigurationIndex } from './lib/settings/agent_configuration/create_agent_config_index'; @@ -57,7 +57,7 @@ export class APMPlugin implements Plugin { cloud?: CloudSetup; usageCollection?: UsageCollectionSetup; taskManager?: TaskManagerSetupContract; - alerting?: AlertingPlugin['setup']; + alerts?: AlertingPlugin['setup']; actions?: ActionsPlugin['setup']; observability?: ObservabilityPluginSetup; features: FeaturesPluginSetup; @@ -73,9 +73,9 @@ export class APMPlugin implements Plugin { core.savedObjects.registerType(apmIndices); core.savedObjects.registerType(apmTelemetry); - if (plugins.actions && plugins.alerting) { + if (plugins.actions && plugins.alerts) { registerApmAlerts({ - alerting: plugins.alerting, + alerts: plugins.alerts, actions: plugins.actions, config$: mergedConfig$, }); diff --git a/x-pack/plugins/infra/kibana.json b/x-pack/plugins/infra/kibana.json index ea66ae7a46d4e2..4701182c96813b 100644 --- a/x-pack/plugins/infra/kibana.json +++ b/x-pack/plugins/infra/kibana.json @@ -10,7 +10,7 @@ "data", "dataEnhanced", "visTypeTimeseries", - "alerting", + "alerts", "triggers_actions_ui" ], "server": true, diff --git a/x-pack/plugins/infra/server/lib/adapters/framework/adapter_types.ts b/x-pack/plugins/infra/server/lib/adapters/framework/adapter_types.ts index 4bbbf8dcdee03a..d00afbc7b497a6 100644 --- a/x-pack/plugins/infra/server/lib/adapters/framework/adapter_types.ts +++ b/x-pack/plugins/infra/server/lib/adapters/framework/adapter_types.ts @@ -13,7 +13,7 @@ import { SpacesPluginSetup } from '../../../../../../plugins/spaces/server'; import { VisTypeTimeseriesSetup } from '../../../../../../../src/plugins/vis_type_timeseries/server'; import { APMPluginSetup } from '../../../../../../plugins/apm/server'; import { HomeServerPluginSetup } from '../../../../../../../src/plugins/home/server'; -import { PluginSetupContract as AlertingPluginContract } from '../../../../../../plugins/alerting/server'; +import { PluginSetupContract as AlertingPluginContract } from '../../../../../alerts/server'; // NP_TODO: Compose real types from plugins we depend on, no "any" export interface InfraServerPluginDeps { @@ -23,7 +23,7 @@ export interface InfraServerPluginDeps { visTypeTimeseries: VisTypeTimeseriesSetup; features: FeaturesPluginSetup; apm: APMPluginSetup; - alerting: AlertingPluginContract; + alerts: AlertingPluginContract; } export interface CallWithRequestParams extends GenericParams { diff --git a/x-pack/plugins/infra/server/lib/alerting/inventory_metric_threshold/inventory_metric_threshold_executor.ts b/x-pack/plugins/infra/server/lib/alerting/inventory_metric_threshold/inventory_metric_threshold_executor.ts index b36de2a3bd0917..5a34a6665e781d 100644 --- a/x-pack/plugins/infra/server/lib/alerting/inventory_metric_threshold/inventory_metric_threshold_executor.ts +++ b/x-pack/plugins/infra/server/lib/alerting/inventory_metric_threshold/inventory_metric_threshold_executor.ts @@ -11,7 +11,7 @@ import { CallWithRequestParams, } from '../../adapters/framework/adapter_types'; import { Comparator, AlertStates, InventoryMetricConditions } from './types'; -import { AlertServices, AlertExecutorOptions } from '../../../../../alerting/server'; +import { AlertServices, AlertExecutorOptions } from '../../../../../alerts/server'; import { InfraSnapshot } from '../../snapshot'; import { parseFilterQuery } from '../../../utils/serialized_query'; import { InventoryItemType, SnapshotMetricType } from '../../../../common/inventory_models/types'; diff --git a/x-pack/plugins/infra/server/lib/alerting/log_threshold/log_threshold_executor.test.ts b/x-pack/plugins/infra/server/lib/alerting/log_threshold/log_threshold_executor.test.ts index 995d415ef3c8f2..a3b9e854584161 100644 --- a/x-pack/plugins/infra/server/lib/alerting/log_threshold/log_threshold_executor.test.ts +++ b/x-pack/plugins/infra/server/lib/alerting/log_threshold/log_threshold_executor.test.ts @@ -11,12 +11,12 @@ import { LogDocumentCountAlertParams, Criterion, } from '../../../../common/alerting/logs/types'; -import { AlertExecutorOptions } from '../../../../../alerting/server'; +import { AlertExecutorOptions } from '../../../../../alerts/server'; import { alertsMock, AlertInstanceMock, AlertServicesMock, -} from '../../../../../alerting/server/mocks'; +} from '../../../../../alerts/server/mocks'; import { libsMock } from './mocks'; interface AlertTestInstance { 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 eedaf4202b37d9..ee4e1fcb3f6e2a 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 @@ -5,7 +5,7 @@ */ import { i18n } from '@kbn/i18n'; -import { AlertExecutorOptions, AlertServices } from '../../../../../alerting/server'; +import { AlertExecutorOptions, AlertServices } from '../../../../../alerts/server'; import { AlertStates, Comparator, diff --git a/x-pack/plugins/infra/server/lib/alerting/log_threshold/register_log_threshold_alert_type.ts b/x-pack/plugins/infra/server/lib/alerting/log_threshold/register_log_threshold_alert_type.ts index cdb4d2d968479a..ed7e82fe29e4cd 100644 --- a/x-pack/plugins/infra/server/lib/alerting/log_threshold/register_log_threshold_alert_type.ts +++ b/x-pack/plugins/infra/server/lib/alerting/log_threshold/register_log_threshold_alert_type.ts @@ -6,7 +6,7 @@ import uuid from 'uuid'; import { i18n } from '@kbn/i18n'; import { schema } from '@kbn/config-schema'; -import { PluginSetupContract } from '../../../../../alerting/server'; +import { PluginSetupContract } from '../../../../../alerts/server'; import { createLogThresholdExecutor, FIRED_ACTIONS } from './log_threshold_executor'; import { LOG_DOCUMENT_COUNT_ALERT_TYPE_ID, diff --git a/x-pack/plugins/infra/server/lib/alerting/metric_threshold/metric_threshold_executor.test.ts b/x-pack/plugins/infra/server/lib/alerting/metric_threshold/metric_threshold_executor.test.ts index 19efc88e216cab..8260ebed846222 100644 --- a/x-pack/plugins/infra/server/lib/alerting/metric_threshold/metric_threshold_executor.test.ts +++ b/x-pack/plugins/infra/server/lib/alerting/metric_threshold/metric_threshold_executor.test.ts @@ -6,12 +6,12 @@ import { createMetricThresholdExecutor, FIRED_ACTIONS } from './metric_threshold_executor'; import { Comparator, AlertStates } from './types'; import * as mocks from './test_mocks'; -import { AlertExecutorOptions } from '../../../../../alerting/server'; +import { AlertExecutorOptions } from '../../../../../alerts/server'; import { alertsMock, AlertServicesMock, AlertInstanceMock, -} from '../../../../../alerting/server/mocks'; +} from '../../../../../alerts/server/mocks'; import { InfraSources } from '../../sources'; interface AlertTestInstance { diff --git a/x-pack/plugins/infra/server/lib/alerting/metric_threshold/metric_threshold_executor.ts b/x-pack/plugins/infra/server/lib/alerting/metric_threshold/metric_threshold_executor.ts index d1cb60112aa429..233a34a67d1ec9 100644 --- a/x-pack/plugins/infra/server/lib/alerting/metric_threshold/metric_threshold_executor.ts +++ b/x-pack/plugins/infra/server/lib/alerting/metric_threshold/metric_threshold_executor.ts @@ -17,7 +17,7 @@ import { DOCUMENT_COUNT_I18N, stateToAlertMessage, } from './messages'; -import { AlertServices, AlertExecutorOptions } from '../../../../../alerting/server'; +import { AlertServices, AlertExecutorOptions } from '../../../../../alerts/server'; import { getIntervalInSeconds } from '../../../utils/get_interval_in_seconds'; import { getDateHistogramOffset } from '../../snapshot/query_helpers'; import { InfraBackendLibs } from '../../infra_types'; diff --git a/x-pack/plugins/infra/server/lib/alerting/register_alert_types.ts b/x-pack/plugins/infra/server/lib/alerting/register_alert_types.ts index ae74ed82038fde..989a2917b05209 100644 --- a/x-pack/plugins/infra/server/lib/alerting/register_alert_types.ts +++ b/x-pack/plugins/infra/server/lib/alerting/register_alert_types.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { PluginSetupContract } from '../../../../alerting/server'; +import { PluginSetupContract } from '../../../../alerts/server'; import { registerMetricThresholdAlertType } from './metric_threshold/register_metric_threshold_alert_type'; import { registerMetricInventoryThresholdAlertType } from './inventory_metric_threshold/register_inventory_metric_threshold_alert_type'; import { registerLogThresholdAlertType } from './log_threshold/register_log_threshold_alert_type'; diff --git a/x-pack/plugins/infra/server/plugin.ts b/x-pack/plugins/infra/server/plugin.ts index a265d53fc1bf86..2fd614830c05df 100644 --- a/x-pack/plugins/infra/server/plugin.ts +++ b/x-pack/plugins/infra/server/plugin.ts @@ -149,7 +149,7 @@ export class InfraServerPlugin { ]); initInfraServer(this.libs); - registerAlertTypes(plugins.alerting, this.libs); + registerAlertTypes(plugins.alerts, this.libs); // Telemetry UsageCollector.registerUsageCollector(plugins.usageCollection); diff --git a/x-pack/plugins/monitoring/kibana.json b/x-pack/plugins/monitoring/kibana.json index 115cc08871ea45..4ed693464712d5 100644 --- a/x-pack/plugins/monitoring/kibana.json +++ b/x-pack/plugins/monitoring/kibana.json @@ -4,7 +4,7 @@ "kibanaVersion": "kibana", "configPath": ["monitoring"], "requiredPlugins": ["licensing", "features", "data", "navigation", "kibanaLegacy"], - "optionalPlugins": ["alerting", "actions", "infra", "telemetryCollectionManager", "usageCollection", "home"], + "optionalPlugins": ["alerts", "actions", "infra", "telemetryCollectionManager", "usageCollection", "home"], "server": true, "ui": true } diff --git a/x-pack/plugins/monitoring/public/components/alerts/status.tsx b/x-pack/plugins/monitoring/public/components/alerts/status.tsx index cdddbf10313038..6f72168f5069b3 100644 --- a/x-pack/plugins/monitoring/public/components/alerts/status.tsx +++ b/x-pack/plugins/monitoring/public/components/alerts/status.tsx @@ -18,7 +18,7 @@ import { import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; import { Legacy } from '../../legacy_shims'; -import { Alert, BASE_ALERT_API_PATH } from '../../../../../plugins/alerting/common'; +import { Alert, BASE_ALERT_API_PATH } from '../../../../alerts/common'; import { getSetupModeState, addSetupModeCallback, toggleSetupMode } from '../../lib/setup_mode'; import { NUMBER_OF_MIGRATED_ALERTS, ALERT_TYPE_PREFIX } from '../../../common/constants'; import { AlertsConfiguration } from './configuration'; diff --git a/x-pack/plugins/monitoring/server/alerts/cluster_state.test.ts b/x-pack/plugins/monitoring/server/alerts/cluster_state.test.ts index bcc1a8abe5cb0f..62620360377122 100644 --- a/x-pack/plugins/monitoring/server/alerts/cluster_state.test.ts +++ b/x-pack/plugins/monitoring/server/alerts/cluster_state.test.ts @@ -10,7 +10,7 @@ import { AlertCommonParams, AlertCommonState, AlertClusterStatePerClusterState } import { getPreparedAlert } from '../lib/alerts/get_prepared_alert'; import { executeActions } from '../lib/alerts/cluster_state.lib'; import { AlertClusterStateState } from './enums'; -import { alertsMock, AlertServicesMock } from '../../../alerting/server/mocks'; +import { alertsMock, AlertServicesMock } from '../../../alerts/server/mocks'; jest.mock('../lib/alerts/cluster_state.lib', () => ({ executeActions: jest.fn(), diff --git a/x-pack/plugins/monitoring/server/alerts/cluster_state.ts b/x-pack/plugins/monitoring/server/alerts/cluster_state.ts index 6567d1c6def310..5b6521179002a0 100644 --- a/x-pack/plugins/monitoring/server/alerts/cluster_state.ts +++ b/x-pack/plugins/monitoring/server/alerts/cluster_state.ts @@ -8,7 +8,7 @@ import moment from 'moment-timezone'; import { i18n } from '@kbn/i18n'; import { Logger, ICustomClusterClient, UiSettingsServiceStart } from 'src/core/server'; import { ALERT_TYPE_CLUSTER_STATE } from '../../common/constants'; -import { AlertType } from '../../../alerting/server'; +import { AlertType } from '../../../alerts/server'; import { executeActions, getUiMessage } from '../lib/alerts/cluster_state.lib'; import { AlertCommonExecutorOptions, diff --git a/x-pack/plugins/monitoring/server/alerts/license_expiration.test.ts b/x-pack/plugins/monitoring/server/alerts/license_expiration.test.ts index 6ffe937679f4cc..fb8d10884fdc7b 100644 --- a/x-pack/plugins/monitoring/server/alerts/license_expiration.test.ts +++ b/x-pack/plugins/monitoring/server/alerts/license_expiration.test.ts @@ -16,7 +16,7 @@ import { } from './types'; import { executeActions } from '../lib/alerts/license_expiration.lib'; import { PreparedAlert, getPreparedAlert } from '../lib/alerts/get_prepared_alert'; -import { alertsMock, AlertServicesMock } from '../../../alerting/server/mocks'; +import { alertsMock, AlertServicesMock } from '../../../alerts/server/mocks'; jest.mock('../lib/alerts/license_expiration.lib', () => ({ executeActions: jest.fn(), diff --git a/x-pack/plugins/monitoring/server/alerts/license_expiration.ts b/x-pack/plugins/monitoring/server/alerts/license_expiration.ts index 00402bca57a7e9..d57f1a7655b187 100644 --- a/x-pack/plugins/monitoring/server/alerts/license_expiration.ts +++ b/x-pack/plugins/monitoring/server/alerts/license_expiration.ts @@ -8,7 +8,7 @@ import moment from 'moment-timezone'; import { Logger, ICustomClusterClient, UiSettingsServiceStart } from 'src/core/server'; import { i18n } from '@kbn/i18n'; import { ALERT_TYPE_LICENSE_EXPIRATION } from '../../common/constants'; -import { AlertType } from '../../../../plugins/alerting/server'; +import { AlertType } from '../../../alerts/server'; import { fetchLicenses } from '../lib/alerts/fetch_licenses'; import { AlertCommonState, diff --git a/x-pack/plugins/monitoring/server/alerts/types.d.ts b/x-pack/plugins/monitoring/server/alerts/types.d.ts index b689d008b51a78..67c74635b4e36d 100644 --- a/x-pack/plugins/monitoring/server/alerts/types.d.ts +++ b/x-pack/plugins/monitoring/server/alerts/types.d.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ import { Moment } from 'moment'; -import { AlertExecutorOptions } from '../../../alerting/server'; +import { AlertExecutorOptions } from '../../../alerts/server'; import { AlertClusterStateState, AlertCommonPerClusterMessageTokenType } from './enums'; export interface AlertLicense { diff --git a/x-pack/plugins/monitoring/server/lib/alerts/cluster_state.lib.ts b/x-pack/plugins/monitoring/server/lib/alerts/cluster_state.lib.ts index ae66d603507ca6..c4553d87980da5 100644 --- a/x-pack/plugins/monitoring/server/lib/alerts/cluster_state.lib.ts +++ b/x-pack/plugins/monitoring/server/lib/alerts/cluster_state.lib.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ import { i18n } from '@kbn/i18n'; -import { AlertInstance } from '../../../../alerting/server'; +import { AlertInstance } from '../../../../alerts/server'; import { AlertCommonCluster, AlertCommonPerClusterMessage, diff --git a/x-pack/plugins/monitoring/server/lib/alerts/fetch_status.ts b/x-pack/plugins/monitoring/server/lib/alerts/fetch_status.ts index 7a6c38865ebe8d..614658baf5c799 100644 --- a/x-pack/plugins/monitoring/server/lib/alerts/fetch_status.ts +++ b/x-pack/plugins/monitoring/server/lib/alerts/fetch_status.ts @@ -6,7 +6,7 @@ import moment from 'moment'; import { Logger } from '../../../../../../src/core/server'; import { AlertCommonPerClusterState } from '../../alerts/types'; -import { AlertsClient } from '../../../../alerting/server'; +import { AlertsClient } from '../../../../alerts/server'; export async function fetchStatus( alertsClient: AlertsClient, diff --git a/x-pack/plugins/monitoring/server/lib/alerts/get_prepared_alert.ts b/x-pack/plugins/monitoring/server/lib/alerts/get_prepared_alert.ts index 83a9e26e4c5890..cfaaeb36535a01 100644 --- a/x-pack/plugins/monitoring/server/lib/alerts/get_prepared_alert.ts +++ b/x-pack/plugins/monitoring/server/lib/alerts/get_prepared_alert.ts @@ -6,7 +6,7 @@ import { Logger, ICustomClusterClient, UiSettingsServiceStart } from 'kibana/server'; import { CallCluster } from 'src/legacy/core_plugins/elasticsearch'; -import { AlertServices } from '../../../../alerting/server'; +import { AlertServices } from '../../../../alerts/server'; import { AlertCommonCluster } from '../../alerts/types'; import { INDEX_PATTERN_ELASTICSEARCH } from '../../../common/constants'; import { fetchAvailableCcs } from './fetch_available_ccs'; diff --git a/x-pack/plugins/monitoring/server/lib/alerts/license_expiration.lib.ts b/x-pack/plugins/monitoring/server/lib/alerts/license_expiration.lib.ts index cfe9f02b9bd6ae..97ef2790b516df 100644 --- a/x-pack/plugins/monitoring/server/lib/alerts/license_expiration.lib.ts +++ b/x-pack/plugins/monitoring/server/lib/alerts/license_expiration.lib.ts @@ -5,7 +5,7 @@ */ import { Moment } from 'moment-timezone'; import { i18n } from '@kbn/i18n'; -import { AlertInstance } from '../../../../alerting/server'; +import { AlertInstance } from '../../../../alerts/server'; import { AlertCommonPerClusterMessageLinkToken, AlertCommonPerClusterMessageTimeToken, diff --git a/x-pack/plugins/monitoring/server/plugin.ts b/x-pack/plugins/monitoring/server/plugin.ts index 14bef307b2f85e..f4f38f70b1ccba 100644 --- a/x-pack/plugins/monitoring/server/plugin.ts +++ b/x-pack/plugins/monitoring/server/plugin.ts @@ -47,7 +47,7 @@ import { MonitoringLicenseService } from './types'; import { PluginStartContract as AlertingPluginStartContract, PluginSetupContract as AlertingPluginSetupContract, -} from '../../alerting/server'; +} from '../../alerts/server'; import { getLicenseExpiration } from './alerts/license_expiration'; import { getClusterState } from './alerts/cluster_state'; import { InfraPluginSetup } from '../../infra/server'; @@ -61,12 +61,12 @@ interface PluginsSetup { usageCollection?: UsageCollectionSetup; licensing: LicensingPluginSetup; features: FeaturesPluginSetupContract; - alerting: AlertingPluginSetupContract; + alerts: AlertingPluginSetupContract; infra: InfraPluginSetup; } interface PluginsStart { - alerting: AlertingPluginStartContract; + alerts: AlertingPluginStartContract; } interface MonitoringCoreConfig { @@ -156,7 +156,7 @@ export class Plugin { await this.licenseService.refresh(); if (KIBANA_ALERTING_ENABLED) { - plugins.alerting.registerType( + plugins.alerts.registerType( getLicenseExpiration( async () => { const coreStart = (await core.getStartServices())[0]; @@ -167,7 +167,7 @@ export class Plugin { config.ui.ccs.enabled ) ); - plugins.alerting.registerType( + plugins.alerts.registerType( getClusterState( async () => { const coreStart = (await core.getStartServices())[0]; @@ -357,7 +357,7 @@ export class Plugin { payload: req.body, getKibanaStatsCollector: () => this.legacyShimDependencies.kibanaStatsCollector, getUiSettingsService: () => context.core.uiSettings.client, - getAlertsClient: () => plugins.alerting.getAlertsClientWithRequest(req), + getAlertsClient: () => plugins.alerts.getAlertsClientWithRequest(req), server: { config: legacyConfigWrapper, newPlatform: { diff --git a/x-pack/plugins/siem/common/detection_engine/schemas/common/schemas.ts b/x-pack/plugins/siem/common/detection_engine/schemas/common/schemas.ts index faae1dde835452..9eb2d9abccbd3c 100644 --- a/x-pack/plugins/siem/common/detection_engine/schemas/common/schemas.ts +++ b/x-pack/plugins/siem/common/detection_engine/schemas/common/schemas.ts @@ -27,7 +27,7 @@ export const filters = t.array(t.unknown); // Filters are not easily type-able y /** * Params is an "object", since it is a type of AlertActionParams which is action templates. - * @see x-pack/plugins/alerting/common/alert.ts + * @see x-pack/plugins/alerts/common/alert.ts */ export const action_group = t.string; export const action_id = t.string; diff --git a/x-pack/plugins/siem/common/detection_engine/transform_actions.ts b/x-pack/plugins/siem/common/detection_engine/transform_actions.ts index 4ce38235758336..7c15bc143e0fda 100644 --- a/x-pack/plugins/siem/common/detection_engine/transform_actions.ts +++ b/x-pack/plugins/siem/common/detection_engine/transform_actions.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { AlertAction } from '../../../alerting/common'; +import { AlertAction } from '../../../alerts/common'; import { RuleAlertAction } from './types'; export const transformRuleToAlertAction = ({ diff --git a/x-pack/plugins/siem/common/detection_engine/types.ts b/x-pack/plugins/siem/common/detection_engine/types.ts index 5a91cfd4809c6b..431d716a9f205c 100644 --- a/x-pack/plugins/siem/common/detection_engine/types.ts +++ b/x-pack/plugins/siem/common/detection_engine/types.ts @@ -5,7 +5,7 @@ */ import * as t from 'io-ts'; -import { AlertAction } from '../../../alerting/common'; +import { AlertAction } from '../../../alerts/common'; export type RuleAlertAction = Omit & { action_type_id: string; diff --git a/x-pack/plugins/siem/kibana.json b/x-pack/plugins/siem/kibana.json index 6b43b41df8eeec..df40ad4650f456 100644 --- a/x-pack/plugins/siem/kibana.json +++ b/x-pack/plugins/siem/kibana.json @@ -5,7 +5,7 @@ "configPath": ["xpack", "siem"], "requiredPlugins": [ "actions", - "alerting", + "alerts", "data", "dataEnhanced", "embeddable", diff --git a/x-pack/plugins/siem/public/alerts/components/rules/description_step/actions_description.tsx b/x-pack/plugins/siem/public/alerts/components/rules/description_step/actions_description.tsx index be96ab10bd2b5b..43416abe6e2886 100644 --- a/x-pack/plugins/siem/public/alerts/components/rules/description_step/actions_description.tsx +++ b/x-pack/plugins/siem/public/alerts/components/rules/description_step/actions_description.tsx @@ -6,7 +6,7 @@ import React from 'react'; import { startCase } from 'lodash/fp'; -import { AlertAction } from '../../../../../../alerting/common'; +import { AlertAction } from '../../../../../../alerts/common'; const ActionsDescription = ({ actions }: { actions: AlertAction[] }) => { if (!actions.length) return null; diff --git a/x-pack/plugins/siem/public/alerts/components/rules/rule_actions_field/index.tsx b/x-pack/plugins/siem/public/alerts/components/rules/rule_actions_field/index.tsx index d8064eb4ad391d..5823612faac135 100644 --- a/x-pack/plugins/siem/public/alerts/components/rules/rule_actions_field/index.tsx +++ b/x-pack/plugins/siem/public/alerts/components/rules/rule_actions_field/index.tsx @@ -18,7 +18,7 @@ import { ActionType, loadActionTypes, } from '../../../../../../triggers_actions_ui/public'; -import { AlertAction } from '../../../../../../alerting/common'; +import { AlertAction } from '../../../../../../alerts/common'; import { useKibana } from '../../../../common/lib/kibana'; import { FORM_ERRORS_TITLE } from './translations'; diff --git a/x-pack/plugins/siem/public/alerts/containers/detection_engine/rules/types.ts b/x-pack/plugins/siem/public/alerts/containers/detection_engine/rules/types.ts index 897568cdbf16ed..ab9b88fb81fa7e 100644 --- a/x-pack/plugins/siem/public/alerts/containers/detection_engine/rules/types.ts +++ b/x-pack/plugins/siem/public/alerts/containers/detection_engine/rules/types.ts @@ -10,7 +10,7 @@ import { RuleTypeSchema } from '../../../../../common/detection_engine/types'; /** * Params is an "record", since it is a type of AlertActionParams which is action templates. - * @see x-pack/plugins/alerting/common/alert.ts + * @see x-pack/plugins/alerts/common/alert.ts */ export const action = t.exact( t.type({ diff --git a/x-pack/plugins/siem/public/alerts/pages/detection_engine/rules/types.ts b/x-pack/plugins/siem/public/alerts/pages/detection_engine/rules/types.ts index 92c9780a117221..5f81409010a280 100644 --- a/x-pack/plugins/siem/public/alerts/pages/detection_engine/rules/types.ts +++ b/x-pack/plugins/siem/public/alerts/pages/detection_engine/rules/types.ts @@ -5,7 +5,7 @@ */ import { RuleAlertAction, RuleType } from '../../../../../common/detection_engine/types'; -import { AlertAction } from '../../../../../../alerting/common'; +import { AlertAction } from '../../../../../../alerts/common'; import { Filter } from '../../../../../../../../src/plugins/data/common'; import { FormData, FormHook } from '../../../../shared_imports'; import { FieldValueQueryBar } from '../../../components/rules/query_bar'; diff --git a/x-pack/plugins/siem/server/lib/detection_engine/README.md b/x-pack/plugins/siem/server/lib/detection_engine/README.md index 695165e1990a9f..4c90869a9fe842 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/README.md +++ b/x-pack/plugins/siem/server/lib/detection_engine/README.md @@ -152,7 +152,7 @@ logging.events: ``` See these two README.md's pages for more references on the alerting and actions API: -https://github.com/elastic/kibana/blob/master/x-pack/plugins/alerting/README.md +https://github.com/elastic/kibana/blob/master/x-pack/plugins/alerts/README.md https://github.com/elastic/kibana/tree/master/x-pack/plugins/actions ### Signals API diff --git a/x-pack/plugins/siem/server/lib/detection_engine/notifications/create_notifications.test.ts b/x-pack/plugins/siem/server/lib/detection_engine/notifications/create_notifications.test.ts index e0414f842ceb3c..440efc8d0d5a3e 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/notifications/create_notifications.test.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/notifications/create_notifications.test.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { alertsClientMock } from '../../../../../alerting/server/mocks'; +import { alertsClientMock } from '../../../../../alerts/server/mocks'; import { createNotifications } from './create_notifications'; describe('createNotifications', () => { diff --git a/x-pack/plugins/siem/server/lib/detection_engine/notifications/create_notifications.ts b/x-pack/plugins/siem/server/lib/detection_engine/notifications/create_notifications.ts index 35a737177ad49d..a472d8a4df4a49 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/notifications/create_notifications.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/notifications/create_notifications.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { Alert } from '../../../../../alerting/common'; +import { Alert } from '../../../../../alerts/common'; import { APP_ID, NOTIFICATIONS_ID } from '../../../../common/constants'; import { CreateNotificationParams } from './types'; import { addTags } from './add_tags'; diff --git a/x-pack/plugins/siem/server/lib/detection_engine/notifications/delete_notifications.test.ts b/x-pack/plugins/siem/server/lib/detection_engine/notifications/delete_notifications.test.ts index 089822f486aeb1..2f754c126771a4 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/notifications/delete_notifications.test.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/notifications/delete_notifications.test.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { alertsClientMock } from '../../../../../alerting/server/mocks'; +import { alertsClientMock } from '../../../../../alerts/server/mocks'; import { deleteNotifications } from './delete_notifications'; import { readNotifications } from './read_notifications'; jest.mock('./read_notifications'); diff --git a/x-pack/plugins/siem/server/lib/detection_engine/notifications/find_notifications.ts b/x-pack/plugins/siem/server/lib/detection_engine/notifications/find_notifications.ts index b47ea348bd4d6c..5d3a328dd6fbb9 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/notifications/find_notifications.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/notifications/find_notifications.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { FindResult } from '../../../../../alerting/server'; +import { FindResult } from '../../../../../alerts/server'; import { NOTIFICATIONS_ID } from '../../../../common/constants'; import { FindNotificationParams } from './types'; diff --git a/x-pack/plugins/siem/server/lib/detection_engine/notifications/get_signals_count.ts b/x-pack/plugins/siem/server/lib/detection_engine/notifications/get_signals_count.ts index 69f37da1e225b8..038a8916c87d56 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/notifications/get_signals_count.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/notifications/get_signals_count.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { AlertServices } from '../../../../../alerting/server'; +import { AlertServices } from '../../../../../alerts/server'; import { buildSignalsSearchQuery } from './build_signals_query'; interface GetSignalsCount { diff --git a/x-pack/plugins/siem/server/lib/detection_engine/notifications/read_notifications.test.ts b/x-pack/plugins/siem/server/lib/detection_engine/notifications/read_notifications.test.ts index 961aac15c484d3..a46f65da580435 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/notifications/read_notifications.test.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/notifications/read_notifications.test.ts @@ -5,7 +5,7 @@ */ import { readNotifications } from './read_notifications'; -import { alertsClientMock } from '../../../../../alerting/server/mocks'; +import { alertsClientMock } from '../../../../../alerts/server/mocks'; import { getNotificationResult, getFindNotificationsResultWithSingleHit, diff --git a/x-pack/plugins/siem/server/lib/detection_engine/notifications/read_notifications.ts b/x-pack/plugins/siem/server/lib/detection_engine/notifications/read_notifications.ts index c585c474556a18..fe9101335b4f54 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/notifications/read_notifications.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/notifications/read_notifications.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { SanitizedAlert } from '../../../../../alerting/common'; +import { SanitizedAlert } from '../../../../../alerts/common'; import { ReadNotificationParams, isAlertType } from './types'; import { findNotifications } from './find_notifications'; import { INTERNAL_RULE_ALERT_ID_KEY } from '../../../../common/constants'; diff --git a/x-pack/plugins/siem/server/lib/detection_engine/notifications/rules_notification_alert_type.test.ts b/x-pack/plugins/siem/server/lib/detection_engine/notifications/rules_notification_alert_type.test.ts index e8d778bddadc2b..47356679c8075e 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/notifications/rules_notification_alert_type.test.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/notifications/rules_notification_alert_type.test.ts @@ -8,7 +8,7 @@ import { loggingServiceMock } from 'src/core/server/mocks'; import { getResult } from '../routes/__mocks__/request_responses'; import { rulesNotificationAlertType } from './rules_notification_alert_type'; import { buildSignalsSearchQuery } from './build_signals_query'; -import { alertsMock, AlertServicesMock } from '../../../../../../plugins/alerting/server/mocks'; +import { alertsMock, AlertServicesMock } from '../../../../../alerts/server/mocks'; import { NotificationExecutorOptions } from './types'; jest.mock('./build_signals_query'); diff --git a/x-pack/plugins/siem/server/lib/detection_engine/notifications/schedule_notification_actions.ts b/x-pack/plugins/siem/server/lib/detection_engine/notifications/schedule_notification_actions.ts index a0bd5e092c6eab..a26ddfc90434aa 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/notifications/schedule_notification_actions.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/notifications/schedule_notification_actions.ts @@ -5,7 +5,7 @@ */ import { mapKeys, snakeCase } from 'lodash/fp'; -import { AlertInstance } from '../../../../../alerting/server'; +import { AlertInstance } from '../../../../../alerts/server'; import { RuleTypeParams } from '../types'; export type NotificationRuleTypeParams = RuleTypeParams & { diff --git a/x-pack/plugins/siem/server/lib/detection_engine/notifications/types.ts b/x-pack/plugins/siem/server/lib/detection_engine/notifications/types.ts index d0bb1bfdfb1c40..1345bf2ac63393 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/notifications/types.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/notifications/types.ts @@ -10,8 +10,8 @@ import { AlertType, State, AlertExecutorOptions, -} from '../../../../../alerting/server'; -import { Alert } from '../../../../../alerting/common'; +} from '../../../../../alerts/server'; +import { Alert } from '../../../../../alerts/common'; import { NOTIFICATIONS_ID } from '../../../../common/constants'; import { RuleAlertAction } from '../../../../common/detection_engine/types'; diff --git a/x-pack/plugins/siem/server/lib/detection_engine/notifications/update_notifications.test.ts b/x-pack/plugins/siem/server/lib/detection_engine/notifications/update_notifications.test.ts index b9dc42b96696d2..c7763c7ed7e773 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/notifications/update_notifications.test.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/notifications/update_notifications.test.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { alertsClientMock } from '../../../../../alerting/server/mocks'; +import { alertsClientMock } from '../../../../../alerts/server/mocks'; import { updateNotifications } from './update_notifications'; import { readNotifications } from './read_notifications'; import { createNotifications } from './create_notifications'; diff --git a/x-pack/plugins/siem/server/lib/detection_engine/notifications/update_notifications.ts b/x-pack/plugins/siem/server/lib/detection_engine/notifications/update_notifications.ts index 5889b0e4dcfb89..17024c7c0d75fe 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/notifications/update_notifications.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/notifications/update_notifications.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { PartialAlert } from '../../../../../alerting/server'; +import { PartialAlert } from '../../../../../alerts/server'; import { readNotifications } from './read_notifications'; import { UpdateNotificationParams } from './types'; import { addTags } from './add_tags'; diff --git a/x-pack/plugins/siem/server/lib/detection_engine/routes/__mocks__/request_context.ts b/x-pack/plugins/siem/server/lib/detection_engine/routes/__mocks__/request_context.ts index a24375c368d634..65f38507605a5a 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/routes/__mocks__/request_context.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/routes/__mocks__/request_context.ts @@ -10,7 +10,7 @@ import { elasticsearchServiceMock, savedObjectsClientMock, } from '../../../../../../../../src/core/server/mocks'; -import { alertsClientMock } from '../../../../../../alerting/server/mocks'; +import { alertsClientMock } from '../../../../../../alerts/server/mocks'; import { licensingMock } from '../../../../../../licensing/server/mocks'; import { siemMock } from '../../../../mocks'; diff --git a/x-pack/plugins/siem/server/lib/detection_engine/routes/rules/utils.test.ts b/x-pack/plugins/siem/server/lib/detection_engine/routes/rules/utils.test.ts index ec9e84d4fa6bbb..df158d23c0e247 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/routes/rules/utils.test.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/routes/rules/utils.test.ts @@ -23,8 +23,8 @@ import { ImportRuleAlertRest, RuleAlertParamsRest, RuleTypeParams } from '../../ import { BulkError, ImportSuccessError } from '../utils'; import { getSimpleRule, getOutputRuleAlertForRest } from '../__mocks__/utils'; import { createPromiseFromStreams } from '../../../../../../../../src/legacy/utils/streams'; -import { PartialAlert } from '../../../../../../alerting/server'; -import { SanitizedAlert } from '../../../../../../alerting/server/types'; +import { PartialAlert } from '../../../../../../alerts/server'; +import { SanitizedAlert } from '../../../../../../alerts/server/types'; import { createRulesStreamFromNdJson } from '../../rules/create_rules_stream_from_ndjson'; import { RuleAlertType } from '../../rules/types'; import { setFeatureFlagsForTestsOnly, unSetFeatureFlagsForTestsOnly } from '../../feature_flags'; diff --git a/x-pack/plugins/siem/server/lib/detection_engine/routes/rules/utils.ts b/x-pack/plugins/siem/server/lib/detection_engine/routes/rules/utils.ts index 861e6463533fb1..5329ff04435cae 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/routes/rules/utils.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/routes/rules/utils.ts @@ -8,7 +8,7 @@ import { pickBy, countBy } from 'lodash/fp'; import { SavedObject, SavedObjectsFindResponse } from 'kibana/server'; import uuid from 'uuid'; -import { PartialAlert, FindResult } from '../../../../../../alerting/server'; +import { PartialAlert, FindResult } from '../../../../../../alerts/server'; import { INTERNAL_IDENTIFIER } from '../../../../../common/constants'; import { RuleAlertType, diff --git a/x-pack/plugins/siem/server/lib/detection_engine/routes/rules/validate.test.ts b/x-pack/plugins/siem/server/lib/detection_engine/routes/rules/validate.test.ts index 13a5bbd2afc0cb..07b891e2bf0211 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/routes/rules/validate.test.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/routes/rules/validate.test.ts @@ -13,7 +13,7 @@ import { transformValidateBulkError, } from './validate'; import { getResult } from '../__mocks__/request_responses'; -import { FindResult } from '../../../../../../alerting/server'; +import { FindResult } from '../../../../../../alerts/server'; import { BulkError } from '../utils'; import { setFeatureFlagsForTestsOnly, unSetFeatureFlagsForTestsOnly } from '../../feature_flags'; import { RulesSchema } from '../../../../../common/detection_engine/schemas/response/rules_schema'; diff --git a/x-pack/plugins/siem/server/lib/detection_engine/routes/rules/validate.ts b/x-pack/plugins/siem/server/lib/detection_engine/routes/rules/validate.ts index 1220b12d1d1b12..5fc239ed48263b 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/routes/rules/validate.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/routes/rules/validate.ts @@ -16,7 +16,7 @@ import { } from '../../../../../common/detection_engine/schemas/response/rules_schema'; import { formatErrors } from '../../../../../common/format_errors'; import { exactCheck } from '../../../../../common/exact_check'; -import { PartialAlert, FindResult } from '../../../../../../alerting/server'; +import { PartialAlert, FindResult } from '../../../../../../alerts/server'; import { isAlertType, IRuleSavedAttributesSavedObjectAttributes, diff --git a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/add_prepackaged_rules_schema.test.ts b/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/add_prepackaged_rules_schema.test.ts index 226dea7c20344b..66356a1d65352f 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/add_prepackaged_rules_schema.test.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/add_prepackaged_rules_schema.test.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { AlertAction } from '../../../../../../alerting/common'; +import { AlertAction } from '../../../../../../alerts/common'; import { RuleAlertAction } from '../../../../../common/detection_engine/types'; import { ThreatParams, PrepackagedRules } from '../../types'; import { addPrepackagedRulesSchema } from './add_prepackaged_rules_schema'; diff --git a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/create_rules_schema.test.ts b/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/create_rules_schema.test.ts index 1e2941015b7355..013db2020a1469 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/create_rules_schema.test.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/create_rules_schema.test.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { AlertAction } from '../../../../../../alerting/common'; +import { AlertAction } from '../../../../../../alerts/common'; import { createRulesSchema } from './create_rules_schema'; import { PatchRuleAlertParamsRest } from '../../rules/types'; import { RuleAlertAction } from '../../../../../common/detection_engine/types'; diff --git a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/import_rules_schema.test.ts b/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/import_rules_schema.test.ts index d28530ffb789e3..cb03c4781cb6c1 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/import_rules_schema.test.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/import_rules_schema.test.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { AlertAction } from '../../../../../../alerting/common'; +import { AlertAction } from '../../../../../../alerts/common'; import { importRulesSchema, importRulesQuerySchema, diff --git a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/patch_rules_schema.test.ts b/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/patch_rules_schema.test.ts index 755c0b2ccaa3f9..218cae68db036e 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/patch_rules_schema.test.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/patch_rules_schema.test.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { AlertAction } from '../../../../../../alerting/common'; +import { AlertAction } from '../../../../../../alerts/common'; import { patchRulesSchema } from './patch_rules_schema'; import { PatchRuleAlertParamsRest } from '../../rules/types'; import { RuleAlertAction } from '../../../../../common/detection_engine/types'; diff --git a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/update_rules_schema.test.ts b/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/update_rules_schema.test.ts index b89df0fc0f3ab2..8bda16de977751 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/update_rules_schema.test.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/update_rules_schema.test.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { AlertAction } from '../../../../../../alerting/common'; +import { AlertAction } from '../../../../../../alerts/common'; import { updateRulesSchema } from './update_rules_schema'; import { PatchRuleAlertParamsRest } from '../../rules/types'; import { RuleAlertAction } from '../../../../../common/detection_engine/types'; diff --git a/x-pack/plugins/siem/server/lib/detection_engine/rule_actions/create_rule_actions_saved_object.ts b/x-pack/plugins/siem/server/lib/detection_engine/rule_actions/create_rule_actions_saved_object.ts index 26c3b29ff2c511..2ff6d6ac646ae6 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/rule_actions/create_rule_actions_saved_object.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/rule_actions/create_rule_actions_saved_object.ts @@ -5,7 +5,7 @@ */ import { RuleAlertAction } from '../../../../common/detection_engine/types'; -import { AlertServices } from '../../../../../alerting/server'; +import { AlertServices } from '../../../../../alerts/server'; import { ruleActionsSavedObjectType } from './saved_object_mappings'; import { IRuleActionsAttributesSavedObjectAttributes } from './types'; import { getThrottleOptions, getRuleActionsFromSavedObject } from './utils'; diff --git a/x-pack/plugins/siem/server/lib/detection_engine/rule_actions/delete_rule_actions_saved_object.ts b/x-pack/plugins/siem/server/lib/detection_engine/rule_actions/delete_rule_actions_saved_object.ts index 251f9155f9331c..3d5734b13ea48a 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/rule_actions/delete_rule_actions_saved_object.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/rule_actions/delete_rule_actions_saved_object.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { AlertServices } from '../../../../../alerting/server'; +import { AlertServices } from '../../../../../alerts/server'; import { ruleActionsSavedObjectType } from './saved_object_mappings'; import { getRuleActionsSavedObject } from './get_rule_actions_saved_object'; diff --git a/x-pack/plugins/siem/server/lib/detection_engine/rule_actions/get_rule_actions_saved_object.ts b/x-pack/plugins/siem/server/lib/detection_engine/rule_actions/get_rule_actions_saved_object.ts index 83cd59f0a1cde9..c36f6ca831c575 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/rule_actions/get_rule_actions_saved_object.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/rule_actions/get_rule_actions_saved_object.ts @@ -5,7 +5,7 @@ */ import { RuleAlertAction } from '../../../../common/detection_engine/types'; -import { AlertServices } from '../../../../../alerting/server'; +import { AlertServices } from '../../../../../alerts/server'; import { ruleActionsSavedObjectType } from './saved_object_mappings'; import { IRuleActionsAttributesSavedObjectAttributes } from './types'; import { getRuleActionsFromSavedObject } from './utils'; diff --git a/x-pack/plugins/siem/server/lib/detection_engine/rule_actions/update_or_create_rule_actions_saved_object.ts b/x-pack/plugins/siem/server/lib/detection_engine/rule_actions/update_or_create_rule_actions_saved_object.ts index 3364827d397d2c..c650de2a5e2b91 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/rule_actions/update_or_create_rule_actions_saved_object.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/rule_actions/update_or_create_rule_actions_saved_object.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { AlertServices } from '../../../../../alerting/server'; +import { AlertServices } from '../../../../../alerts/server'; import { RuleAlertAction } from '../../../../common/detection_engine/types'; import { getRuleActionsSavedObject } from './get_rule_actions_saved_object'; import { createRuleActionsSavedObject } from './create_rule_actions_saved_object'; diff --git a/x-pack/plugins/siem/server/lib/detection_engine/rule_actions/update_rule_actions_saved_object.ts b/x-pack/plugins/siem/server/lib/detection_engine/rule_actions/update_rule_actions_saved_object.ts index c8a3b1bbc38ad5..fd3d107103f197 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/rule_actions/update_rule_actions_saved_object.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/rule_actions/update_rule_actions_saved_object.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { AlertServices } from '../../../../../alerting/server'; +import { AlertServices } from '../../../../../alerts/server'; import { ruleActionsSavedObjectType } from './saved_object_mappings'; import { RulesActionsSavedObject } from './get_rule_actions_saved_object'; import { RuleAlertAction } from '../../../../common/detection_engine/types'; diff --git a/x-pack/plugins/siem/server/lib/detection_engine/rules/create_rules.test.ts b/x-pack/plugins/siem/server/lib/detection_engine/rules/create_rules.test.ts index f4f0a8042d0a5c..f086166d0685e1 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/rules/create_rules.test.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/rules/create_rules.test.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { alertsClientMock } from '../../../../../alerting/server/mocks'; +import { alertsClientMock } from '../../../../../alerts/server/mocks'; import { getMlResult } from '../routes/__mocks__/request_responses'; import { createRules } from './create_rules'; diff --git a/x-pack/plugins/siem/server/lib/detection_engine/rules/create_rules.ts b/x-pack/plugins/siem/server/lib/detection_engine/rules/create_rules.ts index a007fe35b407ed..67e066c6670fb6 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/rules/create_rules.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/rules/create_rules.ts @@ -5,7 +5,7 @@ */ import { transformRuleToAlertAction } from '../../../../common/detection_engine/transform_actions'; -import { Alert } from '../../../../../alerting/common'; +import { Alert } from '../../../../../alerts/common'; import { APP_ID, SIGNALS_ID } from '../../../../common/constants'; import { CreateRuleParams } from './types'; import { addTags } from './add_tags'; diff --git a/x-pack/plugins/siem/server/lib/detection_engine/rules/delete_rules.test.ts b/x-pack/plugins/siem/server/lib/detection_engine/rules/delete_rules.test.ts index 6bc5fc2a88b6d2..f96a9e38d6a6cf 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/rules/delete_rules.test.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/rules/delete_rules.test.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { alertsClientMock } from '../../../../../alerting/server/mocks'; +import { alertsClientMock } from '../../../../../alerts/server/mocks'; import { deleteRules } from './delete_rules'; import { readRules } from './read_rules'; jest.mock('./read_rules'); diff --git a/x-pack/plugins/siem/server/lib/detection_engine/rules/find_rules.ts b/x-pack/plugins/siem/server/lib/detection_engine/rules/find_rules.ts index ac600b0b5b2186..c634f073878255 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/rules/find_rules.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/rules/find_rules.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { FindResult } from '../../../../../alerting/server'; +import { FindResult } from '../../../../../alerts/server'; import { SIGNALS_ID } from '../../../../common/constants'; import { FindRuleParams } from './types'; diff --git a/x-pack/plugins/siem/server/lib/detection_engine/rules/get_existing_prepackaged_rules.test.ts b/x-pack/plugins/siem/server/lib/detection_engine/rules/get_existing_prepackaged_rules.test.ts index d79b428a2f76db..203a23402f0972 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/rules/get_existing_prepackaged_rules.test.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/rules/get_existing_prepackaged_rules.test.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { alertsClientMock } from '../../../../../alerting/server/mocks'; +import { alertsClientMock } from '../../../../../alerts/server/mocks'; import { getResult, getFindResultWithSingleHit, diff --git a/x-pack/plugins/siem/server/lib/detection_engine/rules/get_existing_prepackaged_rules.ts b/x-pack/plugins/siem/server/lib/detection_engine/rules/get_existing_prepackaged_rules.ts index 512164fc3d2e10..a3119131a0037b 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/rules/get_existing_prepackaged_rules.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/rules/get_existing_prepackaged_rules.ts @@ -5,7 +5,7 @@ */ import { INTERNAL_IMMUTABLE_KEY } from '../../../../common/constants'; -import { AlertsClient } from '../../../../../alerting/server'; +import { AlertsClient } from '../../../../../alerts/server'; import { RuleAlertType, isAlertTypes } from './types'; import { findRules } from './find_rules'; diff --git a/x-pack/plugins/siem/server/lib/detection_engine/rules/get_export_all.test.ts b/x-pack/plugins/siem/server/lib/detection_engine/rules/get_export_all.test.ts index 6df250f1cf513f..ee21c335400245 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/rules/get_export_all.test.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/rules/get_export_all.test.ts @@ -9,7 +9,7 @@ import { getFindResultWithSingleHit, FindHit, } from '../routes/__mocks__/request_responses'; -import { alertsClientMock } from '../../../../../alerting/server/mocks'; +import { alertsClientMock } from '../../../../../alerts/server/mocks'; import { getExportAll } from './get_export_all'; import { unSetFeatureFlagsForTestsOnly, setFeatureFlagsForTestsOnly } from '../feature_flags'; diff --git a/x-pack/plugins/siem/server/lib/detection_engine/rules/get_export_all.ts b/x-pack/plugins/siem/server/lib/detection_engine/rules/get_export_all.ts index 06e70f0bad184f..433da2be6b3478 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/rules/get_export_all.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/rules/get_export_all.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { AlertsClient } from '../../../../../alerting/server'; +import { AlertsClient } from '../../../../../alerts/server'; import { getNonPackagedRules } from './get_existing_prepackaged_rules'; import { getExportDetailsNdjson } from './get_export_details_ndjson'; import { transformAlertsToRules } from '../routes/rules/utils'; diff --git a/x-pack/plugins/siem/server/lib/detection_engine/rules/get_export_by_object_ids.test.ts b/x-pack/plugins/siem/server/lib/detection_engine/rules/get_export_by_object_ids.test.ts index 092a9a8faf395b..b00b7353a370f6 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/rules/get_export_by_object_ids.test.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/rules/get_export_by_object_ids.test.ts @@ -11,7 +11,7 @@ import { FindHit, } from '../routes/__mocks__/request_responses'; import * as readRules from './read_rules'; -import { alertsClientMock } from '../../../../../alerting/server/mocks'; +import { alertsClientMock } from '../../../../../alerts/server/mocks'; import { setFeatureFlagsForTestsOnly, unSetFeatureFlagsForTestsOnly } from '../feature_flags'; describe('get_export_by_object_ids', () => { diff --git a/x-pack/plugins/siem/server/lib/detection_engine/rules/get_export_by_object_ids.ts b/x-pack/plugins/siem/server/lib/detection_engine/rules/get_export_by_object_ids.ts index beaaaa8701c87a..38cf8008f65c80 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/rules/get_export_by_object_ids.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/rules/get_export_by_object_ids.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { AlertsClient } from '../../../../../alerting/server'; +import { AlertsClient } from '../../../../../alerts/server'; import { getExportDetailsNdjson } from './get_export_details_ndjson'; import { isAlertType } from '../rules/types'; import { readRules } from './read_rules'; diff --git a/x-pack/plugins/siem/server/lib/detection_engine/rules/install_prepacked_rules.ts b/x-pack/plugins/siem/server/lib/detection_engine/rules/install_prepacked_rules.ts index 0266d702b3dcc7..7b2cef9060f8cc 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/rules/install_prepacked_rules.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/rules/install_prepacked_rules.ts @@ -4,8 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ -import { Alert } from '../../../../../alerting/common'; -import { AlertsClient } from '../../../../../alerting/server'; +import { Alert } from '../../../../../alerts/common'; +import { AlertsClient } from '../../../../../alerts/server'; import { createRules } from './create_rules'; import { PrepackagedRules } from '../types'; diff --git a/x-pack/plugins/siem/server/lib/detection_engine/rules/patch_rules.test.ts b/x-pack/plugins/siem/server/lib/detection_engine/rules/patch_rules.test.ts index a42500223012e1..3c1267c9393454 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/rules/patch_rules.test.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/rules/patch_rules.test.ts @@ -5,7 +5,7 @@ */ import { savedObjectsClientMock } from '../../../../../../../src/core/server/mocks'; -import { alertsClientMock } from '../../../../../alerting/server/mocks'; +import { alertsClientMock } from '../../../../../alerts/server/mocks'; import { getResult, getMlResult } from '../routes/__mocks__/request_responses'; import { patchRules } from './patch_rules'; diff --git a/x-pack/plugins/siem/server/lib/detection_engine/rules/patch_rules.ts b/x-pack/plugins/siem/server/lib/detection_engine/rules/patch_rules.ts index 6dfb72532afbbf..1e728ae7b8d0bf 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/rules/patch_rules.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/rules/patch_rules.ts @@ -5,7 +5,7 @@ */ import { defaults } from 'lodash/fp'; -import { PartialAlert } from '../../../../../alerting/server'; +import { PartialAlert } from '../../../../../alerts/server'; import { PatchRuleParams } from './types'; import { addTags } from './add_tags'; import { calculateVersion, calculateName, calculateInterval } from './utils'; diff --git a/x-pack/plugins/siem/server/lib/detection_engine/rules/read_rules.test.ts b/x-pack/plugins/siem/server/lib/detection_engine/rules/read_rules.test.ts index 600848948be0c4..ef8e70c78422c9 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/rules/read_rules.test.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/rules/read_rules.test.ts @@ -5,7 +5,7 @@ */ import { readRules } from './read_rules'; -import { alertsClientMock } from '../../../../../alerting/server/mocks'; +import { alertsClientMock } from '../../../../../alerts/server/mocks'; import { getResult, getFindResultWithSingleHit } from '../routes/__mocks__/request_responses'; export class TestError extends Error { diff --git a/x-pack/plugins/siem/server/lib/detection_engine/rules/read_rules.ts b/x-pack/plugins/siem/server/lib/detection_engine/rules/read_rules.ts index 9e0d5b3d05b3fc..a8b76aeb8c4535 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/rules/read_rules.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/rules/read_rules.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { SanitizedAlert } from '../../../../../alerting/common'; +import { SanitizedAlert } from '../../../../../alerts/common'; import { INTERNAL_RULE_ID_KEY } from '../../../../common/constants'; import { findRules } from './find_rules'; import { ReadRuleParams, isAlertType } from './types'; diff --git a/x-pack/plugins/siem/server/lib/detection_engine/rules/types.ts b/x-pack/plugins/siem/server/lib/detection_engine/rules/types.ts index d65261549232b4..70d53090f81cc9 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/rules/types.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/rules/types.ts @@ -13,8 +13,8 @@ import { SavedObjectsFindResponse, SavedObjectsClientContract, } from 'kibana/server'; -import { AlertsClient, PartialAlert } from '../../../../../alerting/server'; -import { Alert, SanitizedAlert } from '../../../../../alerting/common'; +import { AlertsClient, PartialAlert } from '../../../../../alerts/server'; +import { Alert, SanitizedAlert } from '../../../../../alerts/common'; import { SIGNALS_ID } from '../../../../common/constants'; import { RuleAlertParams, RuleTypeParams, RuleAlertParamsRest } from '../types'; diff --git a/x-pack/plugins/siem/server/lib/detection_engine/rules/update_prepacked_rules.test.ts b/x-pack/plugins/siem/server/lib/detection_engine/rules/update_prepacked_rules.test.ts index 2d77e9a707f746..ede5c51d1e5e76 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/rules/update_prepacked_rules.test.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/rules/update_prepacked_rules.test.ts @@ -5,7 +5,7 @@ */ import { savedObjectsClientMock } from '../../../../../../../src/core/server/mocks'; -import { alertsClientMock } from '../../../../../alerting/server/mocks'; +import { alertsClientMock } from '../../../../../alerts/server/mocks'; import { mockPrepackagedRule, getFindResultWithSingleHit, diff --git a/x-pack/plugins/siem/server/lib/detection_engine/rules/update_prepacked_rules.ts b/x-pack/plugins/siem/server/lib/detection_engine/rules/update_prepacked_rules.ts index 5063ddd5e52e2e..c793d7eb9b6dc1 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/rules/update_prepacked_rules.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/rules/update_prepacked_rules.ts @@ -5,7 +5,7 @@ */ import { SavedObjectsClientContract } from 'kibana/server'; -import { AlertsClient } from '../../../../../alerting/server'; +import { AlertsClient } from '../../../../../alerts/server'; import { patchRules } from './patch_rules'; import { PrepackagedRules } from '../types'; import { readRules } from './read_rules'; diff --git a/x-pack/plugins/siem/server/lib/detection_engine/rules/update_rules.test.ts b/x-pack/plugins/siem/server/lib/detection_engine/rules/update_rules.test.ts index 13c601b40e4f14..222411deb37abb 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/rules/update_rules.test.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/rules/update_rules.test.ts @@ -5,7 +5,7 @@ */ import { savedObjectsClientMock } from '../../../../../../../src/core/server/mocks'; -import { alertsClientMock } from '../../../../../alerting/server/mocks'; +import { alertsClientMock } from '../../../../../alerts/server/mocks'; import { getResult, getMlResult } from '../routes/__mocks__/request_responses'; import { updateRules } from './update_rules'; diff --git a/x-pack/plugins/siem/server/lib/detection_engine/rules/update_rules.ts b/x-pack/plugins/siem/server/lib/detection_engine/rules/update_rules.ts index 711f019458096d..54031b6e35bf1b 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/rules/update_rules.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/rules/update_rules.ts @@ -5,7 +5,7 @@ */ import { transformRuleToAlertAction } from '../../../../common/detection_engine/transform_actions'; -import { PartialAlert } from '../../../../../alerting/server'; +import { PartialAlert } from '../../../../../alerts/server'; import { readRules } from './read_rules'; import { UpdateRuleParams } from './types'; import { addTags } from './add_tags'; diff --git a/x-pack/plugins/siem/server/lib/detection_engine/rules/update_rules_notifications.ts b/x-pack/plugins/siem/server/lib/detection_engine/rules/update_rules_notifications.ts index c5cf85d7ba0145..8fceb8ef720b5b 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/rules/update_rules_notifications.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/rules/update_rules_notifications.ts @@ -5,7 +5,7 @@ */ import { RuleAlertAction } from '../../../../common/detection_engine/types'; -import { AlertsClient, AlertServices } from '../../../../../alerting/server'; +import { AlertsClient, AlertServices } from '../../../../../alerts/server'; import { updateOrCreateRuleActionsSavedObject } from '../rule_actions/update_or_create_rule_actions_saved_object'; import { updateNotifications } from '../notifications/update_notifications'; import { RuleActions } from '../rule_actions/types'; diff --git a/x-pack/plugins/siem/server/lib/detection_engine/scripts/get_alert_instances.sh b/x-pack/plugins/siem/server/lib/detection_engine/scripts/get_alert_instances.sh index b5f272d0a8a093..a052123f0cc345 100755 --- a/x-pack/plugins/siem/server/lib/detection_engine/scripts/get_alert_instances.sh +++ b/x-pack/plugins/siem/server/lib/detection_engine/scripts/get_alert_instances.sh @@ -10,8 +10,8 @@ set -e ./check_env_variables.sh # Example: ./get_alert_instances.sh -# https://github.com/elastic/kibana/blob/master/x-pack/plugins/alerting/README.md#get-apialert_find-find-alerts +# https://github.com/elastic/kibana/blob/master/x-pack/plugins/alerts/README.md#get-apialert_find-find-alerts curl -s -k \ -u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \ - -X GET ${KIBANA_URL}${SPACE_URL}/api/alert/_find \ + -X GET ${KIBANA_URL}${SPACE_URL}/api/alerts/_find \ | jq . diff --git a/x-pack/plugins/siem/server/lib/detection_engine/scripts/get_alert_types.sh b/x-pack/plugins/siem/server/lib/detection_engine/scripts/get_alert_types.sh index 28c250e9368a62..edade604d74ce2 100755 --- a/x-pack/plugins/siem/server/lib/detection_engine/scripts/get_alert_types.sh +++ b/x-pack/plugins/siem/server/lib/detection_engine/scripts/get_alert_types.sh @@ -10,8 +10,8 @@ set -e ./check_env_variables.sh # Example: ./get_alert_types.sh -# https://github.com/elastic/kibana/blob/master/x-pack/plugins/alerting/README.md#get-apialerttypes-list-alert-types +# https://github.com/elastic/kibana/blob/master/x-pack/plugins/alerts/README.md#get-apialerttypes-list-alert-types curl -s -k \ -u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \ - -X GET ${KIBANA_URL}${SPACE_URL}/api/alert/types \ + -X GET ${KIBANA_URL}${SPACE_URL}/api/alerts/list_alert_types \ | jq . diff --git a/x-pack/plugins/siem/server/lib/detection_engine/signals/bulk_create_ml_signals.ts b/x-pack/plugins/siem/server/lib/detection_engine/signals/bulk_create_ml_signals.ts index 5862e6c481431d..80839545951d55 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/signals/bulk_create_ml_signals.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/signals/bulk_create_ml_signals.ts @@ -9,7 +9,7 @@ import set from 'set-value'; import { SearchResponse } from 'elasticsearch'; import { Logger } from '../../../../../../../src/core/server'; -import { AlertServices } from '../../../../../alerting/server'; +import { AlertServices } from '../../../../../alerts/server'; import { RuleAlertAction } from '../../../../common/detection_engine/types'; import { RuleTypeParams, RefreshTypes } from '../types'; import { singleBulkCreate, SingleBulkCreateResponse } from './single_bulk_create'; diff --git a/x-pack/plugins/siem/server/lib/detection_engine/signals/get_filter.test.ts b/x-pack/plugins/siem/server/lib/detection_engine/signals/get_filter.test.ts index 35ec1950cedaab..0930fbdb534f5c 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/signals/get_filter.test.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/signals/get_filter.test.ts @@ -6,7 +6,7 @@ import { getQueryFilter, getFilter } from './get_filter'; import { PartialFilter } from '../types'; -import { alertsMock, AlertServicesMock } from '../../../../../alerting/server/mocks'; +import { alertsMock, AlertServicesMock } from '../../../../../alerts/server/mocks'; describe('get_filter', () => { let servicesMock: AlertServicesMock; diff --git a/x-pack/plugins/siem/server/lib/detection_engine/signals/get_filter.ts b/x-pack/plugins/siem/server/lib/detection_engine/signals/get_filter.ts index 3c226130faf250..1630192b3c03ab 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/signals/get_filter.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/signals/get_filter.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { AlertServices } from '../../../../../alerting/server'; +import { AlertServices } from '../../../../../alerts/server'; import { assertUnreachable } from '../../../utils/build_query'; import { Filter, diff --git a/x-pack/plugins/siem/server/lib/detection_engine/signals/get_input_output_index.test.ts b/x-pack/plugins/siem/server/lib/detection_engine/signals/get_input_output_index.test.ts index 6fc99ada16ece0..a4ddec13ac5136 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/signals/get_input_output_index.test.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/signals/get_input_output_index.test.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { alertsMock, AlertServicesMock } from '../../../../../alerting/server/mocks'; +import { alertsMock, AlertServicesMock } from '../../../../../alerts/server/mocks'; import { DEFAULT_INDEX_KEY, DEFAULT_INDEX_PATTERN } from '../../../../common/constants'; import { getInputIndex } from './get_input_output_index'; diff --git a/x-pack/plugins/siem/server/lib/detection_engine/signals/get_input_output_index.ts b/x-pack/plugins/siem/server/lib/detection_engine/signals/get_input_output_index.ts index 85e3eeac476e41..c001312fbf2f5d 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/signals/get_input_output_index.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/signals/get_input_output_index.ts @@ -5,7 +5,7 @@ */ import { DEFAULT_INDEX_KEY, DEFAULT_INDEX_PATTERN } from '../../../../common/constants'; -import { AlertServices } from '../../../../../alerting/server'; +import { AlertServices } from '../../../../../alerts/server'; export const getInputIndex = async ( services: AlertServices, diff --git a/x-pack/plugins/siem/server/lib/detection_engine/signals/search_after_bulk_create.test.ts b/x-pack/plugins/siem/server/lib/detection_engine/signals/search_after_bulk_create.test.ts index a306a016b4205a..163ed76d0c6c3d 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/signals/search_after_bulk_create.test.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/signals/search_after_bulk_create.test.ts @@ -13,7 +13,7 @@ import { } from './__mocks__/es_results'; import { searchAfterAndBulkCreate } from './search_after_bulk_create'; import { DEFAULT_SIGNALS_INDEX } from '../../../../common/constants'; -import { alertsMock, AlertServicesMock } from '../../../../../alerting/server/mocks'; +import { alertsMock, AlertServicesMock } from '../../../../../alerts/server/mocks'; import uuid from 'uuid'; import { getListItemResponseMock } from '../../../../../lists/common/schemas/response/list_item_schema.mock'; import { listMock } from '../../../../../lists/server/mocks'; diff --git a/x-pack/plugins/siem/server/lib/detection_engine/signals/search_after_bulk_create.ts b/x-pack/plugins/siem/server/lib/detection_engine/signals/search_after_bulk_create.ts index 59c685ec3e815d..e44b82224d1cee 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/signals/search_after_bulk_create.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/signals/search_after_bulk_create.ts @@ -4,8 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ +import { AlertServices } from '../../../../../alerts/server'; import { ListClient } from '../../../../../lists/server'; -import { AlertServices } from '../../../../../alerting/server'; import { RuleAlertAction } from '../../../../common/detection_engine/types'; import { RuleTypeParams, RefreshTypes, RuleAlertParams } from '../types'; import { Logger } from '../../../../../../../src/core/server'; diff --git a/x-pack/plugins/siem/server/lib/detection_engine/signals/signal_rule_alert_type.test.ts b/x-pack/plugins/siem/server/lib/detection_engine/signals/signal_rule_alert_type.test.ts index 8e7034b0063270..f94eb7006829e5 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/signals/signal_rule_alert_type.test.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/signals/signal_rule_alert_type.test.ts @@ -8,7 +8,7 @@ import moment from 'moment'; import { loggingServiceMock } from 'src/core/server/mocks'; import { getResult, getMlResult } from '../routes/__mocks__/request_responses'; import { signalRulesAlertType } from './signal_rule_alert_type'; -import { alertsMock, AlertServicesMock } from '../../../../../alerting/server/mocks'; +import { alertsMock, AlertServicesMock } from '../../../../../alerts/server/mocks'; import { ruleStatusServiceFactory } from './rule_status_service'; import { getGapBetweenRuns } from './utils'; import { RuleExecutorOptions } from './types'; diff --git a/x-pack/plugins/siem/server/lib/detection_engine/signals/single_bulk_create.test.ts b/x-pack/plugins/siem/server/lib/detection_engine/signals/single_bulk_create.test.ts index 265f9865331349..8b9fb0574efe97 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/signals/single_bulk_create.test.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/signals/single_bulk_create.test.ts @@ -18,7 +18,7 @@ import { } from './__mocks__/es_results'; import { DEFAULT_SIGNALS_INDEX } from '../../../../common/constants'; import { singleBulkCreate, filterDuplicateRules } from './single_bulk_create'; -import { alertsMock, AlertServicesMock } from '../../../../../alerting/server/mocks'; +import { alertsMock, AlertServicesMock } from '../../../../../alerts/server/mocks'; describe('singleBulkCreate', () => { const mockService: AlertServicesMock = alertsMock.createAlertServices(); diff --git a/x-pack/plugins/siem/server/lib/detection_engine/signals/single_bulk_create.ts b/x-pack/plugins/siem/server/lib/detection_engine/signals/single_bulk_create.ts index 39aecde470e0bf..6f4d01ea73a796 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/signals/single_bulk_create.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/signals/single_bulk_create.ts @@ -6,7 +6,7 @@ import { countBy, isEmpty } from 'lodash'; import { performance } from 'perf_hooks'; -import { AlertServices } from '../../../../../alerting/server'; +import { AlertServices } from '../../../../../alerts/server'; import { SignalSearchResponse, BulkResponse } from './types'; import { RuleAlertAction } from '../../../../common/detection_engine/types'; import { RuleTypeParams, RefreshTypes } from '../types'; diff --git a/x-pack/plugins/siem/server/lib/detection_engine/signals/single_search_after.test.ts b/x-pack/plugins/siem/server/lib/detection_engine/signals/single_search_after.test.ts index 2aa42234460d82..50b0cb27990f8f 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/signals/single_search_after.test.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/signals/single_search_after.test.ts @@ -10,7 +10,7 @@ import { sampleDocSearchResultsWithSortId, } from './__mocks__/es_results'; import { singleSearchAfter } from './single_search_after'; -import { alertsMock, AlertServicesMock } from '../../../../../alerting/server/mocks'; +import { alertsMock, AlertServicesMock } from '../../../../../alerts/server/mocks'; describe('singleSearchAfter', () => { const mockService: AlertServicesMock = alertsMock.createAlertServices(); diff --git a/x-pack/plugins/siem/server/lib/detection_engine/signals/single_search_after.ts b/x-pack/plugins/siem/server/lib/detection_engine/signals/single_search_after.ts index a7086a4fb229eb..409f374d7df1e6 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/signals/single_search_after.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/signals/single_search_after.ts @@ -5,7 +5,7 @@ */ import { performance } from 'perf_hooks'; -import { AlertServices } from '../../../../../alerting/server'; +import { AlertServices } from '../../../../../alerts/server'; import { Logger } from '../../../../../../../src/core/server'; import { SignalSearchResponse } from './types'; import { buildEventsSearchQuery } from './build_events_query'; diff --git a/x-pack/plugins/siem/server/lib/detection_engine/signals/types.ts b/x-pack/plugins/siem/server/lib/detection_engine/signals/types.ts index 32b13c5251a6b0..90497b6e34cb4e 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/signals/types.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/signals/types.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { AlertType, State, AlertExecutorOptions } from '../../../../../alerting/server'; +import { AlertType, State, AlertExecutorOptions } from '../../../../../alerts/server'; import { RuleAlertAction } from '../../../../common/detection_engine/types'; import { RuleAlertParams, OutputRuleAlertRest } from '../types'; import { SearchResponse } from '../../types'; diff --git a/x-pack/plugins/siem/server/lib/detection_engine/signals/utils.ts b/x-pack/plugins/siem/server/lib/detection_engine/signals/utils.ts index 989c919244d658..f0ca08b73fac6c 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/signals/utils.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/signals/utils.ts @@ -7,7 +7,7 @@ import { createHash } from 'crypto'; import moment from 'moment'; import dateMath from '@elastic/datemath'; -import { parseDuration } from '../../../../../alerting/server'; +import { parseDuration } from '../../../../../alerts/server'; import { BulkResponse, BulkResponseErrorAggregation } from './types'; export const generateId = ( diff --git a/x-pack/plugins/siem/server/lib/detection_engine/tags/read_tags.test.ts b/x-pack/plugins/siem/server/lib/detection_engine/tags/read_tags.test.ts index d29d885f9797ad..d07fa382e114a7 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/tags/read_tags.test.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/tags/read_tags.test.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { alertsClientMock } from '../../../../../alerting/server/mocks'; +import { alertsClientMock } from '../../../../../alerts/server/mocks'; import { getResult, getFindResultWithMultiHits } from '../routes/__mocks__/request_responses'; import { INTERNAL_RULE_ID_KEY, INTERNAL_IDENTIFIER } from '../../../../common/constants'; import { readRawTags, readTags, convertTagsToSet, convertToTags, isTags } from './read_tags'; diff --git a/x-pack/plugins/siem/server/lib/detection_engine/tags/read_tags.ts b/x-pack/plugins/siem/server/lib/detection_engine/tags/read_tags.ts index 003c852cb80af1..2bb2b5ec47e2f5 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/tags/read_tags.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/tags/read_tags.ts @@ -6,7 +6,7 @@ import { has } from 'lodash/fp'; import { INTERNAL_IDENTIFIER } from '../../../../common/constants'; -import { AlertsClient } from '../../../../../alerting/server'; +import { AlertsClient } from '../../../../../alerts/server'; import { findRules } from '../rules/find_rules'; export interface TagType { diff --git a/x-pack/plugins/siem/server/plugin.ts b/x-pack/plugins/siem/server/plugin.ts index 5a47efd4588884..a8858c91d677c5 100644 --- a/x-pack/plugins/siem/server/plugin.ts +++ b/x-pack/plugins/siem/server/plugin.ts @@ -15,7 +15,7 @@ import { PluginInitializerContext, Logger, } from '../../../../src/core/server'; -import { PluginSetupContract as AlertingSetup } from '../../alerting/server'; +import { PluginSetupContract as AlertingSetup } from '../../alerts/server'; import { SecurityPluginSetup as SecuritySetup } from '../../security/server'; import { PluginSetupContract as FeaturesSetup } from '../../features/server'; import { MlPluginSetup as MlSetup } from '../../ml/server'; @@ -46,7 +46,7 @@ import { EndpointAppContext } from './endpoint/types'; import { IngestIndexPatternRetriever } from './endpoint/alerts/index_pattern'; export interface SetupPlugins { - alerting: AlertingSetup; + alerts: AlertingSetup; encryptedSavedObjects?: EncryptedSavedObjectsSetup; features: FeaturesSetup; licensing: LicensingPluginSetup; @@ -191,7 +191,7 @@ export class Plugin implements IPlugin import('./home')); @@ -34,7 +34,7 @@ export interface AppDeps { dataPlugin: DataPublicPluginStart; charts: ChartsPluginStart; chrome: ChromeStart; - alerting?: AlertingStart; + alerts?: AlertingStart; navigateToApp: CoreStart['application']['navigateToApp']; docLinks: DocLinksStart; toastNotifications: ToastsSetup; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_alert_types/threshold/visualization.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_alert_types/threshold/visualization.tsx index 84cbc73ca92cab..244d431930f2eb 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_alert_types/threshold/visualization.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_alert_types/threshold/visualization.tsx @@ -33,7 +33,7 @@ import { getThresholdAlertVisualizationData } from '../../../../common/lib/index import { AggregationType, Comparator } from '../../../../common/types'; import { AlertsContextValue } from '../../../context/alerts_context'; import { IndexThresholdAlertParams } from './types'; -import { parseDuration } from '../../../../../../alerting/common/parse_duration'; +import { parseDuration } from '../../../../../../alerts/common/parse_duration'; const customTheme = () => { return { diff --git a/x-pack/plugins/triggers_actions_ui/public/application/constants/index.ts b/x-pack/plugins/triggers_actions_ui/public/application/constants/index.ts index 265cfddab4c062..47b55f44bfb92b 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/constants/index.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/constants/index.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -export { BASE_ALERT_API_PATH } from '../../../../alerting/common'; +export { BASE_ALERT_API_PATH } from '../../../../alerts/common'; export { BASE_ACTION_API_PATH } from '../../../../actions/common'; export const BASE_PATH = '/management/insightsAndAlerting/triggersActions'; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/action_variables.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/action_variables.ts index c35dd06385448c..714dc5210e3908 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/action_variables.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/action_variables.ts @@ -23,7 +23,7 @@ function prefixKeys(actionVariables: ActionVariable[], prefix: string): ActionVa } // this list should be the same as in: -// x-pack/plugins/alerting/server/task_runner/transform_action_params.ts +// x-pack/plugins/alerts/server/task_runner/transform_action_params.ts function getAlwaysProvidedActionVariables(): ActionVariable[] { const result: ActionVariable[] = []; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api.test.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api.test.ts index f384a78e2e0808..94d9166b409099 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api.test.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api.test.ts @@ -53,7 +53,7 @@ describe('loadAlertTypes', () => { expect(result).toEqual(resolvedValue); expect(http.get.mock.calls[0]).toMatchInlineSnapshot(` Array [ - "/api/alert/types", + "/api/alerts/list_alert_types", ] `); }); @@ -80,7 +80,7 @@ describe('loadAlert', () => { http.get.mockResolvedValueOnce(resolvedValue); expect(await loadAlert({ http, alertId })).toEqual(resolvedValue); - expect(http.get).toHaveBeenCalledWith(`/api/alert/${alertId}`); + expect(http.get).toHaveBeenCalledWith(`/api/alerts/alert/${alertId}`); }); }); @@ -99,7 +99,7 @@ describe('loadAlertState', () => { http.get.mockResolvedValueOnce(resolvedValue); expect(await loadAlertState({ http, alertId })).toEqual(resolvedValue); - expect(http.get).toHaveBeenCalledWith(`/api/alert/${alertId}/state`); + expect(http.get).toHaveBeenCalledWith(`/api/alerts/alert/${alertId}/state`); }); test('should parse AlertInstances', async () => { @@ -136,7 +136,7 @@ describe('loadAlertState', () => { }, }, }); - expect(http.get).toHaveBeenCalledWith(`/api/alert/${alertId}/state`); + expect(http.get).toHaveBeenCalledWith(`/api/alerts/alert/${alertId}/state`); }); test('should handle empty response from api', async () => { @@ -144,7 +144,7 @@ describe('loadAlertState', () => { http.get.mockResolvedValueOnce(''); expect(await loadAlertState({ http, alertId })).toEqual({}); - expect(http.get).toHaveBeenCalledWith(`/api/alert/${alertId}/state`); + expect(http.get).toHaveBeenCalledWith(`/api/alerts/alert/${alertId}/state`); }); }); @@ -162,7 +162,7 @@ describe('loadAlerts', () => { expect(result).toEqual(resolvedValue); expect(http.get.mock.calls[0]).toMatchInlineSnapshot(` Array [ - "/api/alert/_find", + "/api/alerts/_find", Object { "query": Object { "default_search_operator": "AND", @@ -192,7 +192,7 @@ describe('loadAlerts', () => { expect(result).toEqual(resolvedValue); expect(http.get.mock.calls[0]).toMatchInlineSnapshot(` Array [ - "/api/alert/_find", + "/api/alerts/_find", Object { "query": Object { "default_search_operator": "AND", @@ -226,7 +226,7 @@ describe('loadAlerts', () => { expect(result).toEqual(resolvedValue); expect(http.get.mock.calls[0]).toMatchInlineSnapshot(` Array [ - "/api/alert/_find", + "/api/alerts/_find", Object { "query": Object { "default_search_operator": "AND", @@ -260,7 +260,7 @@ describe('loadAlerts', () => { expect(result).toEqual(resolvedValue); expect(http.get.mock.calls[0]).toMatchInlineSnapshot(` Array [ - "/api/alert/_find", + "/api/alerts/_find", Object { "query": Object { "default_search_operator": "AND", @@ -295,7 +295,7 @@ describe('loadAlerts', () => { expect(result).toEqual(resolvedValue); expect(http.get.mock.calls[0]).toMatchInlineSnapshot(` Array [ - "/api/alert/_find", + "/api/alerts/_find", Object { "query": Object { "default_search_operator": "AND", @@ -330,7 +330,7 @@ describe('loadAlerts', () => { expect(result).toEqual(resolvedValue); expect(http.get.mock.calls[0]).toMatchInlineSnapshot(` Array [ - "/api/alert/_find", + "/api/alerts/_find", Object { "query": Object { "default_search_operator": "AND", @@ -356,13 +356,13 @@ describe('deleteAlerts', () => { expect(http.delete.mock.calls).toMatchInlineSnapshot(` Array [ Array [ - "/api/alert/1", + "/api/alerts/alert/1", ], Array [ - "/api/alert/2", + "/api/alerts/alert/2", ], Array [ - "/api/alert/3", + "/api/alerts/alert/3", ], ] `); @@ -373,7 +373,7 @@ describe('createAlert', () => { test('should call create alert API', async () => { const alertToCreate = { name: 'test', - consumer: 'alerting', + consumer: 'alerts', tags: ['foo'], enabled: true, alertTypeId: 'test', @@ -402,9 +402,9 @@ describe('createAlert', () => { expect(result).toEqual(resolvedValue); expect(http.post.mock.calls[0]).toMatchInlineSnapshot(` Array [ - "/api/alert", + "/api/alerts/alert", Object { - "body": "{\\"name\\":\\"test\\",\\"consumer\\":\\"alerting\\",\\"tags\\":[\\"foo\\"],\\"enabled\\":true,\\"alertTypeId\\":\\"test\\",\\"schedule\\":{\\"interval\\":\\"1m\\"},\\"actions\\":[],\\"params\\":{},\\"throttle\\":null,\\"createdAt\\":\\"1970-01-01T00:00:00.000Z\\",\\"updatedAt\\":\\"1970-01-01T00:00:00.000Z\\",\\"apiKey\\":null,\\"apiKeyOwner\\":null}", + "body": "{\\"name\\":\\"test\\",\\"consumer\\":\\"alerts\\",\\"tags\\":[\\"foo\\"],\\"enabled\\":true,\\"alertTypeId\\":\\"test\\",\\"schedule\\":{\\"interval\\":\\"1m\\"},\\"actions\\":[],\\"params\\":{},\\"throttle\\":null,\\"createdAt\\":\\"1970-01-01T00:00:00.000Z\\",\\"updatedAt\\":\\"1970-01-01T00:00:00.000Z\\",\\"apiKey\\":null,\\"apiKeyOwner\\":null}", }, ] `); @@ -415,7 +415,7 @@ describe('updateAlert', () => { test('should call alert update API', async () => { const alertToUpdate = { throttle: '1m', - consumer: 'alerting', + consumer: 'alerts', name: 'test', tags: ['foo'], schedule: { @@ -444,7 +444,7 @@ describe('updateAlert', () => { expect(result).toEqual(resolvedValue); expect(http.put.mock.calls[0]).toMatchInlineSnapshot(` Array [ - "/api/alert/123", + "/api/alerts/alert/123", Object { "body": "{\\"throttle\\":\\"1m\\",\\"name\\":\\"test\\",\\"tags\\":[\\"foo\\"],\\"schedule\\":{\\"interval\\":\\"1m\\"},\\"params\\":{},\\"actions\\":[]}", }, @@ -460,7 +460,7 @@ describe('enableAlert', () => { expect(http.post.mock.calls).toMatchInlineSnapshot(` Array [ Array [ - "/api/alert/1/_enable", + "/api/alerts/alert/1/_enable", ], ] `); @@ -474,7 +474,7 @@ describe('disableAlert', () => { expect(http.post.mock.calls).toMatchInlineSnapshot(` Array [ Array [ - "/api/alert/1/_disable", + "/api/alerts/alert/1/_disable", ], ] `); @@ -488,7 +488,7 @@ describe('muteAlertInstance', () => { expect(http.post.mock.calls).toMatchInlineSnapshot(` Array [ Array [ - "/api/alert/1/alert_instance/123/_mute", + "/api/alerts/alert/1/alert_instance/123/_mute", ], ] `); @@ -502,7 +502,7 @@ describe('unmuteAlertInstance', () => { expect(http.post.mock.calls).toMatchInlineSnapshot(` Array [ Array [ - "/api/alert/1/alert_instance/123/_unmute", + "/api/alerts/alert/1/alert_instance/123/_unmute", ], ] `); @@ -516,7 +516,7 @@ describe('muteAlert', () => { expect(http.post.mock.calls).toMatchInlineSnapshot(` Array [ Array [ - "/api/alert/1/_mute_all", + "/api/alerts/alert/1/_mute_all", ], ] `); @@ -530,7 +530,7 @@ describe('unmuteAlert', () => { expect(http.post.mock.calls).toMatchInlineSnapshot(` Array [ Array [ - "/api/alert/1/_unmute_all", + "/api/alerts/alert/1/_unmute_all", ], ] `); @@ -545,13 +545,13 @@ describe('enableAlerts', () => { expect(http.post.mock.calls).toMatchInlineSnapshot(` Array [ Array [ - "/api/alert/1/_enable", + "/api/alerts/alert/1/_enable", ], Array [ - "/api/alert/2/_enable", + "/api/alerts/alert/2/_enable", ], Array [ - "/api/alert/3/_enable", + "/api/alerts/alert/3/_enable", ], ] `); @@ -566,13 +566,13 @@ describe('disableAlerts', () => { expect(http.post.mock.calls).toMatchInlineSnapshot(` Array [ Array [ - "/api/alert/1/_disable", + "/api/alerts/alert/1/_disable", ], Array [ - "/api/alert/2/_disable", + "/api/alerts/alert/2/_disable", ], Array [ - "/api/alert/3/_disable", + "/api/alerts/alert/3/_disable", ], ] `); @@ -587,13 +587,13 @@ describe('muteAlerts', () => { expect(http.post.mock.calls).toMatchInlineSnapshot(` Array [ Array [ - "/api/alert/1/_mute_all", + "/api/alerts/alert/1/_mute_all", ], Array [ - "/api/alert/2/_mute_all", + "/api/alerts/alert/2/_mute_all", ], Array [ - "/api/alert/3/_mute_all", + "/api/alerts/alert/3/_mute_all", ], ] `); @@ -608,13 +608,13 @@ describe('unmuteAlerts', () => { expect(http.post.mock.calls).toMatchInlineSnapshot(` Array [ Array [ - "/api/alert/1/_unmute_all", + "/api/alerts/alert/1/_unmute_all", ], Array [ - "/api/alert/2/_unmute_all", + "/api/alerts/alert/2/_unmute_all", ], Array [ - "/api/alert/3/_unmute_all", + "/api/alerts/alert/3/_unmute_all", ], ] `); @@ -628,7 +628,7 @@ describe('health', () => { expect(http.get.mock.calls).toMatchInlineSnapshot(` Array [ Array [ - "/api/alert/_health", + "/api/alerts/_health", ], ] `); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api.ts index 2176f978822ca4..35fdc3974a2962 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api.ts @@ -9,12 +9,12 @@ import * as t from 'io-ts'; import { pipe } from 'fp-ts/lib/pipeable'; import { fold } from 'fp-ts/lib/Either'; import { pick } from 'lodash'; -import { alertStateSchema, AlertingFrameworkHealth } from '../../../../alerting/common'; +import { alertStateSchema, AlertingFrameworkHealth } from '../../../../alerts/common'; import { BASE_ALERT_API_PATH } from '../constants'; import { Alert, AlertType, AlertWithoutId, AlertTaskState } from '../../types'; export async function loadAlertTypes({ http }: { http: HttpSetup }): Promise { - return await http.get(`${BASE_ALERT_API_PATH}/types`); + return await http.get(`${BASE_ALERT_API_PATH}/list_alert_types`); } export async function loadAlert({ @@ -24,7 +24,7 @@ export async function loadAlert({ http: HttpSetup; alertId: string; }): Promise { - return await http.get(`${BASE_ALERT_API_PATH}/${alertId}`); + return await http.get(`${BASE_ALERT_API_PATH}/alert/${alertId}`); } type EmptyHttpResponse = ''; @@ -36,7 +36,7 @@ export async function loadAlertState({ alertId: string; }): Promise { return await http - .get(`${BASE_ALERT_API_PATH}/${alertId}/state`) + .get(`${BASE_ALERT_API_PATH}/alert/${alertId}/state`) .then((state: AlertTaskState | EmptyHttpResponse) => (state ? state : {})) .then((state: AlertTaskState) => { return pipe( @@ -104,7 +104,7 @@ export async function deleteAlerts({ }): Promise<{ successes: string[]; errors: string[] }> { const successes: string[] = []; const errors: string[] = []; - await Promise.all(ids.map((id) => http.delete(`${BASE_ALERT_API_PATH}/${id}`))).then( + await Promise.all(ids.map((id) => http.delete(`${BASE_ALERT_API_PATH}/alert/${id}`))).then( function (fulfilled) { successes.push(...fulfilled); }, @@ -122,7 +122,7 @@ export async function createAlert({ http: HttpSetup; alert: Omit; }): Promise { - return await http.post(`${BASE_ALERT_API_PATH}`, { + return await http.post(`${BASE_ALERT_API_PATH}/alert`, { body: JSON.stringify(alert), }); } @@ -136,7 +136,7 @@ export async function updateAlert({ alert: Pick; id: string; }): Promise { - return await http.put(`${BASE_ALERT_API_PATH}/${id}`, { + return await http.put(`${BASE_ALERT_API_PATH}/alert/${id}`, { body: JSON.stringify( pick(alert, ['throttle', 'name', 'tags', 'schedule', 'params', 'actions']) ), @@ -144,7 +144,7 @@ export async function updateAlert({ } export async function enableAlert({ id, http }: { id: string; http: HttpSetup }): Promise { - await http.post(`${BASE_ALERT_API_PATH}/${id}/_enable`); + await http.post(`${BASE_ALERT_API_PATH}/alert/${id}/_enable`); } export async function enableAlerts({ @@ -158,7 +158,7 @@ export async function enableAlerts({ } export async function disableAlert({ id, http }: { id: string; http: HttpSetup }): Promise { - await http.post(`${BASE_ALERT_API_PATH}/${id}/_disable`); + await http.post(`${BASE_ALERT_API_PATH}/alert/${id}/_disable`); } export async function disableAlerts({ @@ -180,7 +180,7 @@ export async function muteAlertInstance({ instanceId: string; http: HttpSetup; }): Promise { - await http.post(`${BASE_ALERT_API_PATH}/${id}/alert_instance/${instanceId}/_mute`); + await http.post(`${BASE_ALERT_API_PATH}/alert/${id}/alert_instance/${instanceId}/_mute`); } export async function unmuteAlertInstance({ @@ -192,11 +192,11 @@ export async function unmuteAlertInstance({ instanceId: string; http: HttpSetup; }): Promise { - await http.post(`${BASE_ALERT_API_PATH}/${id}/alert_instance/${instanceId}/_unmute`); + await http.post(`${BASE_ALERT_API_PATH}/alert/${id}/alert_instance/${instanceId}/_unmute`); } export async function muteAlert({ id, http }: { id: string; http: HttpSetup }): Promise { - await http.post(`${BASE_ALERT_API_PATH}/${id}/_mute_all`); + await http.post(`${BASE_ALERT_API_PATH}/alert/${id}/_mute_all`); } export async function muteAlerts({ ids, http }: { ids: string[]; http: HttpSetup }): Promise { @@ -204,7 +204,7 @@ export async function muteAlerts({ ids, http }: { ids: string[]; http: HttpSetup } export async function unmuteAlert({ id, http }: { id: string; http: HttpSetup }): Promise { - await http.post(`${BASE_ALERT_API_PATH}/${id}/_unmute_all`); + await http.post(`${BASE_ALERT_API_PATH}/alert/${id}/_unmute_all`); } export async function unmuteAlerts({ 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 df7d1e64c8e91c..7ce952e9b3e0ad 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 @@ -160,7 +160,7 @@ describe('action_form', () => { const initialAlert = ({ name: 'test', params: {}, - consumer: 'alerting', + consumer: 'alerts', alertTypeId: alertType.id, schedule: { interval: '1m', diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/actions_connectors_list/components/actions_connectors_list.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/actions_connectors_list/components/actions_connectors_list.test.tsx index 4a4fce5094f0df..3d16bdfa61a007 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/actions_connectors_list/components/actions_connectors_list.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/actions_connectors_list/components/actions_connectors_list.test.tsx @@ -13,7 +13,7 @@ import { actionTypeRegistryMock } from '../../../action_type_registry.mock'; import { AppContextProvider } from '../../../app_context'; import { chartPluginMock } from '../../../../../../../../src/plugins/charts/public/mocks'; import { dataPluginMock } from '../../../../../../../../src/plugins/data/public/mocks'; -import { alertingPluginMock } from '../../../../../../alerting/public/mocks'; +import { alertingPluginMock } from '../../../../../../alerts/public/mocks'; jest.mock('../../../lib/action_connector_api', () => ({ loadAllActions: jest.fn(), 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 e2d9c5cb7fffec..54d335aaba5aa9 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 @@ -13,7 +13,7 @@ import { ViewInApp } from './view_in_app'; import { useAppDependencies } from '../../../app_context'; jest.mock('../../../app_context', () => { - const alerting = { + const alerts = { getNavigation: jest.fn(async (id) => id === 'alert-with-nav' ? { path: '/alert' } : undefined ), @@ -23,7 +23,7 @@ jest.mock('../../../app_context', () => { useAppDependencies: jest.fn(() => ({ http: jest.fn(), navigateToApp, - alerting, + alerts, legacy: { capabilities: { get: jest.fn(() => ({})), @@ -41,7 +41,7 @@ describe('view in app', () => { describe('link to the app that created the alert', () => { it('is disabled when there is no navigation', async () => { const alert = mockAlert(); - const { alerting } = useAppDependencies(); + const { alerts } = useAppDependencies(); let component: ReactWrapper; await act(async () => { @@ -53,7 +53,7 @@ describe('view in app', () => { expect(component!.find('button').prop('disabled')).toBe(true); expect(component!.text()).toBe('View in app'); - expect(alerting!.getNavigation).toBeCalledWith(alert.id); + expect(alerts!.getNavigation).toBeCalledWith(alert.id); }); }); 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 f1f5d8323c22a0..5b5de070a94e63 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 @@ -16,7 +16,7 @@ import { AlertNavigation, AlertStateNavigation, AlertUrlNavigation, -} from '../../../../../../alerting/common'; +} from '../../../../../../alerts/common'; import { Alert } from '../../../../types'; export interface ViewInAppProps { @@ -28,7 +28,7 @@ const NO_NAVIGATION = false; type AlertNavigationLoadingState = AlertNavigation | false | null; export const ViewInApp: React.FunctionComponent = ({ alert }) => { - const { navigateToApp, alerting: maybeAlerting } = useAppDependencies(); + const { navigateToApp, alerts: maybeAlerting } = useAppDependencies(); const [alertNavigation, setAlertNavigation] = useState(null); useEffect(() => { @@ -40,13 +40,14 @@ export const ViewInApp: React.FunctionComponent = ({ alert }) => * navigation isn't supported */ () => setAlertNavigation(NO_NAVIGATION), - (alerting) => - alerting + (alerts) => { + return alerts .getNavigation(alert.id) .then((nav) => (nav ? setAlertNavigation(nav) : setAlertNavigation(NO_NAVIGATION))) .catch(() => { setAlertNavigation(NO_NAVIGATION); - }) + }); + } ) ); }, [alert.id, maybeAlerting]); 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 56874f3d38b647..f6e8dc49ec2753 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 @@ -120,11 +120,7 @@ describe('alert_add', () => { }, }} > - {}} - /> + {}} /> ); 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 bb7e593170f8bf..e408c7fcb81441 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 @@ -84,7 +84,7 @@ describe('alert_edit', () => { window: '1s', comparator: 'between', }, - consumer: 'alerting', + consumer: 'alerts', alertTypeId: 'my-alert-type', enabled: false, schedule: { interval: '1m' }, 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 ed36bc6c8d5803..c9ce2848c56704 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 @@ -85,7 +85,7 @@ describe('alert_form', () => { const initialAlert = ({ name: 'test', params: {}, - consumer: 'alerting', + consumer: 'alerts', schedule: { interval: '1m', }, @@ -302,7 +302,7 @@ describe('alert_form', () => { name: 'test', alertTypeId: alertType.id, params: {}, - consumer: 'alerting', + consumer: 'alerts', schedule: { interval: '1m', }, diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_form.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_form.tsx index 87e018ebe33767..874091b2bb7a84 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_form.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_form.tsx @@ -30,7 +30,7 @@ import { pipe } from 'fp-ts/lib/pipeable'; import { getDurationNumberInItsUnit, getDurationUnitValue, -} from '../../../../../alerting/common/parse_duration'; +} from '../../../../../alerts/common/parse_duration'; import { loadAlertTypes } from '../../lib/alert_api'; import { actionVariablesFromAlertType } from '../../lib/action_variables'; import { AlertReducerAction } from './alert_reducer'; @@ -168,7 +168,7 @@ export const AlertForm = ({ : null; const alertTypeRegistryList = - alert.consumer === 'alerting' + alert.consumer === 'alerts' ? alertTypeRegistry .list() .filter( @@ -179,6 +179,7 @@ export const AlertForm = ({ .filter( (alertTypeRegistryItem: AlertTypeModel) => alertTypesIndex && + alertTypesIndex[alertTypeRegistryItem.id] && alertTypesIndex[alertTypeRegistryItem.id].producer === alert.consumer ); const alertTypeNodes = alertTypeRegistryList.map(function (item, index) { diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_reducer.test.ts b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_reducer.test.ts index bd320de1440248..4e4d8e237aa2fd 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_reducer.test.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_reducer.test.ts @@ -11,7 +11,7 @@ describe('alert reducer', () => { beforeAll(() => { initialAlert = ({ params: {}, - consumer: 'alerting', + consumer: 'alerts', alertTypeId: null, schedule: { interval: '1m', 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 cf1524094b41dc..a59a4a37bec1f4 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 @@ -15,7 +15,7 @@ import { ValidationResult } from '../../../../types'; import { AppContextProvider } from '../../../app_context'; import { chartPluginMock } from '../../../../../../../../src/plugins/charts/public/mocks'; import { dataPluginMock } from '../../../../../../../../src/plugins/data/public/mocks'; -import { alertingPluginMock } from '../../../../../../alerting/public/mocks'; +import { alertingPluginMock } from '../../../../../../alerts/public/mocks'; jest.mock('../../../lib/action_connector_api', () => ({ loadActionTypes: jest.fn(), 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 bd4676cd830713..2929ce6defeaf9 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 @@ -439,7 +439,7 @@ export const AlertsList: React.FunctionComponent = () => { }} > diff --git a/x-pack/plugins/triggers_actions_ui/public/plugin.ts b/x-pack/plugins/triggers_actions_ui/public/plugin.ts index dcf120d37ef8b6..3453165a15f698 100644 --- a/x-pack/plugins/triggers_actions_ui/public/plugin.ts +++ b/x-pack/plugins/triggers_actions_ui/public/plugin.ts @@ -15,7 +15,7 @@ import { TypeRegistry } from './application/type_registry'; import { ManagementStart, ManagementSectionId } from '../../../../src/plugins/management/public'; import { boot } from './application/boot'; import { ChartsPluginStart } from '../../../../src/plugins/charts/public'; -import { PluginStartContract as AlertingStart } from '../../alerting/public'; +import { PluginStartContract as AlertingStart } from '../../alerts/public'; import { DataPublicPluginStart } from '../../../../src/plugins/data/public'; export interface TriggersAndActionsUIPublicPluginSetup { @@ -32,7 +32,7 @@ interface PluginsStart { data: DataPublicPluginStart; charts: ChartsPluginStart; management: ManagementStart; - alerting?: AlertingStart; + alerts?: AlertingStart; navigateToApp: CoreStart['application']['navigateToApp']; } @@ -83,7 +83,7 @@ export class Plugin boot({ dataPlugin: plugins.data, charts: plugins.charts, - alerting: plugins.alerting, + alerts: plugins.alerts, element: params.element, toastNotifications: core.notifications.toasts, http: core.http, diff --git a/x-pack/plugins/triggers_actions_ui/public/types.ts b/x-pack/plugins/triggers_actions_ui/public/types.ts index 11152c56c49ecc..52179dd35767ca 100644 --- a/x-pack/plugins/triggers_actions_ui/public/types.ts +++ b/x-pack/plugins/triggers_actions_ui/public/types.ts @@ -5,7 +5,7 @@ */ import { HttpSetup, DocLinksStart } from 'kibana/public'; import { ComponentType } from 'react'; -import { ActionGroup } from '../../alerting/common'; +import { ActionGroup } from '../../alerts/common'; import { ActionType } from '../../actions/common'; import { TypeRegistry } from './application/type_registry'; import { @@ -14,7 +14,7 @@ import { AlertTaskState, RawAlertInstance, AlertingFrameworkHealth, -} from '../../../plugins/alerting/common'; +} from '../../alerts/common'; export { Alert, AlertAction, AlertTaskState, RawAlertInstance, AlertingFrameworkHealth }; export { ActionType }; diff --git a/x-pack/plugins/uptime/kibana.json b/x-pack/plugins/uptime/kibana.json index ce8b64ce07254a..5fbd6129fd18f4 100644 --- a/x-pack/plugins/uptime/kibana.json +++ b/x-pack/plugins/uptime/kibana.json @@ -4,7 +4,7 @@ "kibanaVersion": "kibana", "optionalPlugins": ["capabilities", "data", "home"], "requiredPlugins": [ - "alerting", + "alerts", "embeddable", "features", "licensing", diff --git a/x-pack/plugins/uptime/server/lib/adapters/framework/adapter_types.ts b/x-pack/plugins/uptime/server/lib/adapters/framework/adapter_types.ts index f4d1c72770494d..5ffc71945caefb 100644 --- a/x-pack/plugins/uptime/server/lib/adapters/framework/adapter_types.ts +++ b/x-pack/plugins/uptime/server/lib/adapters/framework/adapter_types.ts @@ -36,7 +36,7 @@ export interface UptimeCoreSetup { export interface UptimeCorePlugins { features: PluginSetupContract; - alerting: any; + alerts: any; elasticsearch: any; usageCollection: UsageCollectionSetup; } diff --git a/x-pack/plugins/uptime/server/lib/alerts/__tests__/status_check.test.ts b/x-pack/plugins/uptime/server/lib/alerts/__tests__/status_check.test.ts index 73d104c1d21aef..8c487c85c57208 100644 --- a/x-pack/plugins/uptime/server/lib/alerts/__tests__/status_check.test.ts +++ b/x-pack/plugins/uptime/server/lib/alerts/__tests__/status_check.test.ts @@ -11,12 +11,12 @@ import { fullListByIdAndLocation, } from '../status_check'; import { GetMonitorStatusResult } from '../../requests'; -import { AlertType } from '../../../../../alerting/server'; +import { AlertType } from '../../../../../alerts/server'; import { IRouter } from 'kibana/server'; import { UMServerLibs } from '../../lib'; import { UptimeCoreSetup } from '../../adapters'; import { DYNAMIC_SETTINGS_DEFAULTS } from '../../../../common/constants'; -import { alertsMock, AlertServicesMock } from '../../../../../alerting/server/mocks'; +import { alertsMock, AlertServicesMock } from '../../../../../alerts/server/mocks'; /** * The alert takes some dependencies as parameters; these are things like @@ -39,7 +39,7 @@ const bootstrapDependencies = (customRequests?: any) => { * This function aims to provide an easy way to give mock props that will * reduce boilerplate for tests. * @param params the params received at alert creation time - * @param services the core services provided by kibana/alerting platforms + * @param services the core services provided by kibana/alerts platforms * @param state the state the alert maintains */ const mockOptions = ( diff --git a/x-pack/plugins/uptime/server/lib/alerts/status_check.ts b/x-pack/plugins/uptime/server/lib/alerts/status_check.ts index 17479bb451b18f..3dd1558f5da919 100644 --- a/x-pack/plugins/uptime/server/lib/alerts/status_check.ts +++ b/x-pack/plugins/uptime/server/lib/alerts/status_check.ts @@ -8,7 +8,7 @@ import { schema } from '@kbn/config-schema'; import { isRight } from 'fp-ts/lib/Either'; import { ThrowReporter } from 'io-ts/lib/ThrowReporter'; import { i18n } from '@kbn/i18n'; -import { AlertExecutorOptions } from '../../../../alerting/server'; +import { AlertExecutorOptions } from '../../../../alerts/server'; import { UptimeAlertTypeFactory } from './types'; import { GetMonitorStatusResult } from '../requests'; import { StatusCheckExecutorParamsType } from '../../../common/runtime_types'; diff --git a/x-pack/plugins/uptime/server/lib/alerts/types.ts b/x-pack/plugins/uptime/server/lib/alerts/types.ts index bc1e82224f7b02..a321cc124ac22e 100644 --- a/x-pack/plugins/uptime/server/lib/alerts/types.ts +++ b/x-pack/plugins/uptime/server/lib/alerts/types.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { AlertType } from '../../../../alerting/server'; +import { AlertType } from '../../../../alerts/server'; import { UptimeCoreSetup } from '../adapters'; import { UMServerLibs } from '../lib'; diff --git a/x-pack/plugins/uptime/server/uptime_server.ts b/x-pack/plugins/uptime/server/uptime_server.ts index 180067c0abde2c..fb90dfe2be6c53 100644 --- a/x-pack/plugins/uptime/server/uptime_server.ts +++ b/x-pack/plugins/uptime/server/uptime_server.ts @@ -19,6 +19,6 @@ export const initUptimeServer = ( ); uptimeAlertTypeFactories.forEach((alertTypeFactory) => - plugins.alerting.registerType(alertTypeFactory(server, libs)) + plugins.alerts.registerType(alertTypeFactory(server, libs)) ); }; diff --git a/x-pack/test/alerting_api_integration/common/fixtures/plugins/alerts/kibana.json b/x-pack/test/alerting_api_integration/common/fixtures/plugins/alerts/kibana.json index 98c57db16c9148..fc42e3199095d6 100644 --- a/x-pack/test/alerting_api_integration/common/fixtures/plugins/alerts/kibana.json +++ b/x-pack/test/alerting_api_integration/common/fixtures/plugins/alerts/kibana.json @@ -3,7 +3,7 @@ "version": "1.0.0", "kibanaVersion": "kibana", "configPath": ["xpack"], - "requiredPlugins": ["taskManager", "features", "actions", "alerting"], + "requiredPlugins": ["taskManager", "features", "actions", "alerts"], "optionalPlugins": ["spaces"], "server": true, "ui": false diff --git a/x-pack/test/alerting_api_integration/common/fixtures/plugins/alerts/server/alert_types.ts b/x-pack/test/alerting_api_integration/common/fixtures/plugins/alerts/server/alert_types.ts index bfabbb81693915..8e3d6b6909a149 100644 --- a/x-pack/test/alerting_api_integration/common/fixtures/plugins/alerts/server/alert_types.ts +++ b/x-pack/test/alerting_api_integration/common/fixtures/plugins/alerts/server/alert_types.ts @@ -8,11 +8,11 @@ import { CoreSetup } from 'src/core/server'; import { schema } from '@kbn/config-schema'; import { times } from 'lodash'; import { FixtureStartDeps, FixtureSetupDeps } from './plugin'; -import { AlertType, AlertExecutorOptions } from '../../../../../../../plugins/alerting/server'; +import { AlertType, AlertExecutorOptions } from '../../../../../../../plugins/alerts/server'; export function defineAlertTypes( core: CoreSetup, - { alerting }: Pick + { alerts }: Pick ) { const clusterClient = core.elasticsearch.legacy.client; const alwaysFiringAlertType: AlertType = { @@ -286,13 +286,13 @@ export function defineAlertTypes( }, async executor(opts: AlertExecutorOptions) {}, }; - alerting.registerType(alwaysFiringAlertType); - alerting.registerType(cumulativeFiringAlertType); - alerting.registerType(neverFiringAlertType); - alerting.registerType(failingAlertType); - alerting.registerType(validationAlertType); - alerting.registerType(authorizationAlertType); - alerting.registerType(noopAlertType); - alerting.registerType(onlyContextVariablesAlertType); - alerting.registerType(onlyStateVariablesAlertType); + alerts.registerType(alwaysFiringAlertType); + alerts.registerType(cumulativeFiringAlertType); + alerts.registerType(neverFiringAlertType); + alerts.registerType(failingAlertType); + alerts.registerType(validationAlertType); + alerts.registerType(authorizationAlertType); + alerts.registerType(noopAlertType); + alerts.registerType(onlyContextVariablesAlertType); + alerts.registerType(onlyStateVariablesAlertType); } diff --git a/x-pack/test/alerting_api_integration/common/fixtures/plugins/alerts/server/plugin.ts b/x-pack/test/alerting_api_integration/common/fixtures/plugins/alerts/server/plugin.ts index af8dd0282c5782..47563f8a5f078c 100644 --- a/x-pack/test/alerting_api_integration/common/fixtures/plugins/alerts/server/plugin.ts +++ b/x-pack/test/alerting_api_integration/common/fixtures/plugins/alerts/server/plugin.ts @@ -6,7 +6,7 @@ import { Plugin, CoreSetup } from 'kibana/server'; import { PluginSetupContract as ActionsPluginSetup } from '../../../../../../../plugins/actions/server/plugin'; -import { PluginSetupContract as AlertingPluginSetup } from '../../../../../../../plugins/alerting/server/plugin'; +import { PluginSetupContract as AlertingPluginSetup } from '../../../../../../../plugins/alerts/server/plugin'; import { EncryptedSavedObjectsPluginStart } from '../../../../../../../plugins/encrypted_saved_objects/server'; import { PluginSetupContract as FeaturesPluginSetup } from '../../../../../../../plugins/features/server'; import { defineAlertTypes } from './alert_types'; @@ -16,7 +16,7 @@ import { defineRoutes } from './routes'; export interface FixtureSetupDeps { features: FeaturesPluginSetup; actions: ActionsPluginSetup; - alerting: AlertingPluginSetup; + alerts: AlertingPluginSetup; } export interface FixtureStartDeps { @@ -24,17 +24,14 @@ export interface FixtureStartDeps { } export class FixturePlugin implements Plugin { - public setup( - core: CoreSetup, - { features, actions, alerting }: FixtureSetupDeps - ) { + public setup(core: CoreSetup, { features, actions, alerts }: FixtureSetupDeps) { features.registerFeature({ - id: 'alerting', - name: 'Alerting', - app: ['alerting', 'kibana'], + id: 'alerts', + name: 'Alerts', + app: ['alerts', 'kibana'], privileges: { all: { - app: ['alerting', 'kibana'], + app: ['alerts', 'kibana'], savedObject: { all: ['alert'], read: [], @@ -43,7 +40,7 @@ export class FixturePlugin implements Plugin { - const pluginPath = plugin ? `/${plugin}` : ''; return this.supertest - .delete(`${getUrlPrefix(spaceId)}/api${pluginPath}/${type}/${id}`) + .delete(`${getUrlPrefix(spaceId)}/api/${plugin}/${type}/${id}`) .set('kbn-xsrf', 'foo') .expect(204); }) diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/scenarios.ts b/x-pack/test/alerting_api_integration/security_and_spaces/scenarios.ts index d58fcd29e29fce..c72ee6a192bf28 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/scenarios.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/scenarios.ts @@ -47,7 +47,7 @@ const GlobalRead: User = { kibana: [ { feature: { - alerting: ['read'], + alerts: ['read'], actions: ['read'], }, spaces: ['*'], @@ -75,7 +75,7 @@ const Space1All: User = { kibana: [ { feature: { - alerting: ['all'], + alerts: ['all'], actions: ['all'], }, spaces: ['space1'], diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/get_all.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/get_all.ts index 785285f6d455c5..45491aa2d28fcf 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/get_all.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/get_all.ts @@ -121,7 +121,7 @@ export default function getAllActionTests({ getService }: FtrProviderContext) { objectRemover.add(space.id, createdAction.id, 'action', 'actions'); const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(space.id)}/api/alert`) + .post(`${getUrlPrefix(space.id)}/api/alerts/alert`) .set('kbn-xsrf', 'foo') .send( getTestAlertData({ @@ -142,7 +142,7 @@ export default function getAllActionTests({ getService }: FtrProviderContext) { }) ) .expect(200); - objectRemover.add(space.id, createdAlert.id, 'alert', undefined); + objectRemover.add(space.id, createdAlert.id, 'alert', 'alerts'); const response = await supertestWithoutAuth .get(`${getUrlPrefix(space.id)}/api/actions`) diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/alerts.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/alerts.ts index 02cd661cbaf04d..ab58a205f9d470 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/alerts.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/alerts.ts @@ -336,7 +336,7 @@ instanceStateValue: true const reference = alertUtils.generateReference(); const response = await supertestWithoutAuth - .post(`${getUrlPrefix(space.id)}/api/alert`) + .post(`${getUrlPrefix(space.id)}/api/alerts/alert`) .set('kbn-xsrf', 'foo') .auth(user.username, user.password) .send( @@ -374,7 +374,7 @@ instanceStateValue: true case 'superuser at space1': case 'space_1_all at space1': expect(response.statusCode).to.eql(200); - objectRemover.add(space.id, response.body.id, 'alert', undefined); + objectRemover.add(space.id, response.body.id, 'alert', 'alerts'); // Wait for the task to be attempted once and idle const scheduledActionTask = await retry.try(async () => { @@ -428,7 +428,7 @@ instanceStateValue: true const testStart = new Date(); const reference = alertUtils.generateReference(); const response = await supertestWithoutAuth - .post(`${getUrlPrefix(space.id)}/api/alert`) + .post(`${getUrlPrefix(space.id)}/api/alerts/alert`) .set('kbn-xsrf', 'foo') .auth(user.username, user.password) .send( @@ -457,7 +457,7 @@ instanceStateValue: true break; case 'space_1_all at space1': expect(response.statusCode).to.eql(200); - objectRemover.add(space.id, response.body.id, 'alert', undefined); + objectRemover.add(space.id, response.body.id, 'alert', 'alerts'); // Wait for test.authorization to index a document before disabling the alert and waiting for tasks to finish await esTestIndexTool.waitForDocs('alert:test.authorization', reference); @@ -490,7 +490,7 @@ instanceStateValue: true break; case 'superuser at space1': expect(response.statusCode).to.eql(200); - objectRemover.add(space.id, response.body.id, 'alert', undefined); + objectRemover.add(space.id, response.body.id, 'alert', 'alerts'); // Wait for test.authorization to index a document before disabling the alert and waiting for tasks to finish await esTestIndexTool.waitForDocs('alert:test.authorization', reference); @@ -532,7 +532,7 @@ instanceStateValue: true .expect(200); objectRemover.add(space.id, createdAction.id, 'action', 'actions'); const response = await supertestWithoutAuth - .post(`${getUrlPrefix(space.id)}/api/alert`) + .post(`${getUrlPrefix(space.id)}/api/alerts/alert`) .set('kbn-xsrf', 'foo') .auth(user.username, user.password) .send( @@ -571,7 +571,7 @@ instanceStateValue: true break; case 'space_1_all at space1': expect(response.statusCode).to.eql(200); - objectRemover.add(space.id, response.body.id, 'alert', undefined); + objectRemover.add(space.id, response.body.id, 'alert', 'alerts'); // Ensure test.authorization indexed 1 document before disabling the alert and waiting for tasks to finish await esTestIndexTool.waitForDocs('action:test.authorization', reference); @@ -604,7 +604,7 @@ instanceStateValue: true break; case 'superuser at space1': expect(response.statusCode).to.eql(200); - objectRemover.add(space.id, response.body.id, 'alert', undefined); + objectRemover.add(space.id, response.body.id, 'alert', 'alerts'); // Ensure test.authorization indexed 1 document before disabling the alert and waiting for tasks to finish await esTestIndexTool.waitForDocs('action:test.authorization', reference); diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/create.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/create.ts index ad9fd117c36042..4ca943f3e188a8 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/create.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/create.ts @@ -43,7 +43,7 @@ export default function createAlertTests({ getService }: FtrProviderContext) { .expect(200); const response = await supertestWithoutAuth - .post(`${getUrlPrefix(space.id)}/api/alert`) + .post(`${getUrlPrefix(space.id)}/api/alerts/alert`) .set('kbn-xsrf', 'foo') .auth(user.username, user.password) .send( @@ -72,7 +72,7 @@ export default function createAlertTests({ getService }: FtrProviderContext) { case 'superuser at space1': case 'space_1_all at space1': expect(response.statusCode).to.eql(200); - objectRemover.add(space.id, response.body.id, 'alert', undefined); + objectRemover.add(space.id, response.body.id, 'alert', 'alerts'); expect(response.body).to.eql({ id: response.body.id, name: 'abc', @@ -126,7 +126,7 @@ export default function createAlertTests({ getService }: FtrProviderContext) { it('should handle create alert request appropriately when an alert is disabled ', async () => { const response = await supertestWithoutAuth - .post(`${getUrlPrefix(space.id)}/api/alert`) + .post(`${getUrlPrefix(space.id)}/api/alerts/alert`) .set('kbn-xsrf', 'foo') .auth(user.username, user.password) .send(getTestAlertData({ enabled: false })); @@ -145,7 +145,7 @@ export default function createAlertTests({ getService }: FtrProviderContext) { case 'superuser at space1': case 'space_1_all at space1': expect(response.statusCode).to.eql(200); - objectRemover.add(space.id, response.body.id, 'alert', undefined); + objectRemover.add(space.id, response.body.id, 'alert', 'alerts'); expect(response.body.scheduledTaskId).to.eql(undefined); break; default: @@ -155,7 +155,7 @@ export default function createAlertTests({ getService }: FtrProviderContext) { it('should handle create alert request appropriately when alert type is unregistered', async () => { const response = await supertestWithoutAuth - .post(`${getUrlPrefix(space.id)}/api/alert`) + .post(`${getUrlPrefix(space.id)}/api/alerts/alert`) .set('kbn-xsrf', 'foo') .auth(user.username, user.password) .send( @@ -191,7 +191,7 @@ export default function createAlertTests({ getService }: FtrProviderContext) { it('should handle create alert request appropriately when payload is empty and invalid', async () => { const response = await supertestWithoutAuth - .post(`${getUrlPrefix(space.id)}/api/alert`) + .post(`${getUrlPrefix(space.id)}/api/alerts/alert`) .set('kbn-xsrf', 'foo') .auth(user.username, user.password) .send({}); @@ -223,7 +223,7 @@ export default function createAlertTests({ getService }: FtrProviderContext) { it(`should handle create alert request appropriately when params isn't valid`, async () => { const response = await supertestWithoutAuth - .post(`${getUrlPrefix(space.id)}/api/alert`) + .post(`${getUrlPrefix(space.id)}/api/alerts/alert`) .set('kbn-xsrf', 'foo') .auth(user.username, user.password) .send( @@ -260,7 +260,7 @@ export default function createAlertTests({ getService }: FtrProviderContext) { it('should handle create alert request appropriately when interval schedule is wrong syntax', async () => { const response = await supertestWithoutAuth - .post(`${getUrlPrefix(space.id)}/api/alert`) + .post(`${getUrlPrefix(space.id)}/api/alerts/alert`) .set('kbn-xsrf', 'foo') .auth(user.username, user.password) .send(getTestAlertData(getTestAlertData({ schedule: { interval: '10x' } }))); @@ -292,7 +292,7 @@ export default function createAlertTests({ getService }: FtrProviderContext) { it('should handle create alert request appropriately when interval schedule is 0', async () => { const response = await supertestWithoutAuth - .post(`${getUrlPrefix(space.id)}/api/alert`) + .post(`${getUrlPrefix(space.id)}/api/alerts/alert`) .set('kbn-xsrf', 'foo') .auth(user.username, user.password) .send(getTestAlertData(getTestAlertData({ schedule: { interval: '0s' } }))); diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/delete.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/delete.ts index 593ae574e6f343..6f8ae010b9cd82 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/delete.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/delete.ts @@ -32,13 +32,13 @@ export default function createDeleteTests({ getService }: FtrProviderContext) { describe(scenario.id, () => { it('should handle delete alert request appropriately', async () => { const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(space.id)}/api/alert`) + .post(`${getUrlPrefix(space.id)}/api/alerts/alert`) .set('kbn-xsrf', 'foo') .send(getTestAlertData()) .expect(200); const response = await supertestWithoutAuth - .delete(`${getUrlPrefix(space.id)}/api/alert/${createdAlert.id}`) + .delete(`${getUrlPrefix(space.id)}/api/alerts/alert/${createdAlert.id}`) .set('kbn-xsrf', 'foo') .auth(user.username, user.password); @@ -52,7 +52,7 @@ export default function createDeleteTests({ getService }: FtrProviderContext) { error: 'Not Found', message: 'Not Found', }); - objectRemover.add(space.id, createdAlert.id, 'alert', undefined); + objectRemover.add(space.id, createdAlert.id, 'alert', 'alerts'); // Ensure task still exists await getScheduledTask(createdAlert.scheduledTaskId); break; @@ -74,14 +74,14 @@ export default function createDeleteTests({ getService }: FtrProviderContext) { it(`shouldn't delete alert from another space`, async () => { const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(space.id)}/api/alert`) + .post(`${getUrlPrefix(space.id)}/api/alerts/alert`) .set('kbn-xsrf', 'foo') .send(getTestAlertData()) .expect(200); - objectRemover.add(space.id, createdAlert.id, 'alert', undefined); + objectRemover.add(space.id, createdAlert.id, 'alert', 'alerts'); const response = await supertestWithoutAuth - .delete(`${getUrlPrefix('other')}/api/alert/${createdAlert.id}`) + .delete(`${getUrlPrefix('other')}/api/alerts/alert/${createdAlert.id}`) .set('kbn-xsrf', 'foo') .auth(user.username, user.password); @@ -111,7 +111,7 @@ export default function createDeleteTests({ getService }: FtrProviderContext) { it('should still be able to delete alert when AAD is broken', async () => { const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(space.id)}/api/alert`) + .post(`${getUrlPrefix(space.id)}/api/alerts/alert`) .set('kbn-xsrf', 'foo') .send(getTestAlertData()) .expect(200); @@ -129,7 +129,7 @@ export default function createDeleteTests({ getService }: FtrProviderContext) { .expect(200); const response = await supertestWithoutAuth - .delete(`${getUrlPrefix(space.id)}/api/alert/${createdAlert.id}`) + .delete(`${getUrlPrefix(space.id)}/api/alerts/alert/${createdAlert.id}`) .set('kbn-xsrf', 'foo') .auth(user.username, user.password); @@ -143,7 +143,7 @@ export default function createDeleteTests({ getService }: FtrProviderContext) { error: 'Not Found', message: 'Not Found', }); - objectRemover.add(space.id, createdAlert.id, 'alert', undefined); + objectRemover.add(space.id, createdAlert.id, 'alert', 'alerts'); // Ensure task still exists await getScheduledTask(createdAlert.scheduledTaskId); break; diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/disable.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/disable.ts index dbbccba70a7152..589942a7ac11ce 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/disable.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/disable.ts @@ -40,11 +40,11 @@ export default function createDisableAlertTests({ getService }: FtrProviderConte describe(scenario.id, () => { it('should handle disable alert request appropriately', async () => { const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(space.id)}/api/alert`) + .post(`${getUrlPrefix(space.id)}/api/alerts/alert`) .set('kbn-xsrf', 'foo') .send(getTestAlertData({ enabled: true })) .expect(200); - objectRemover.add(space.id, createdAlert.id, 'alert', undefined); + objectRemover.add(space.id, createdAlert.id, 'alert', 'alerts'); const response = await alertUtils.getDisableRequest(createdAlert.id); @@ -86,11 +86,11 @@ export default function createDisableAlertTests({ getService }: FtrProviderConte it('should still be able to disable alert when AAD is broken', async () => { const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(space.id)}/api/alert`) + .post(`${getUrlPrefix(space.id)}/api/alerts/alert`) .set('kbn-xsrf', 'foo') .send(getTestAlertData({ enabled: true })) .expect(200); - objectRemover.add(space.id, createdAlert.id, 'alert', undefined); + objectRemover.add(space.id, createdAlert.id, 'alert', 'alerts'); await supertest .put( @@ -144,11 +144,11 @@ export default function createDisableAlertTests({ getService }: FtrProviderConte it(`shouldn't disable alert from another space`, async () => { const { body: createdAlert } = await supertest - .post(`${getUrlPrefix('other')}/api/alert`) + .post(`${getUrlPrefix('other')}/api/alerts/alert`) .set('kbn-xsrf', 'foo') .send(getTestAlertData({ enabled: true })) .expect(200); - objectRemover.add('other', createdAlert.id, 'alert', undefined); + objectRemover.add('other', createdAlert.id, 'alert', 'alerts'); const response = await alertUtils.getDisableRequest(createdAlert.id); diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/enable.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/enable.ts index 611556aaf1feff..8cb0eeb0092a37 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/enable.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/enable.ts @@ -40,11 +40,11 @@ export default function createEnableAlertTests({ getService }: FtrProviderContex describe(scenario.id, () => { it('should handle enable alert request appropriately', async () => { const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(space.id)}/api/alert`) + .post(`${getUrlPrefix(space.id)}/api/alerts/alert`) .set('kbn-xsrf', 'foo') .send(getTestAlertData({ enabled: false })) .expect(200); - objectRemover.add(space.id, createdAlert.id, 'alert', undefined); + objectRemover.add(space.id, createdAlert.id, 'alert', 'alerts'); const response = await alertUtils.getEnableRequest(createdAlert.id); @@ -64,7 +64,7 @@ export default function createEnableAlertTests({ getService }: FtrProviderContex expect(response.statusCode).to.eql(204); expect(response.body).to.eql(''); const { body: updatedAlert } = await supertestWithoutAuth - .get(`${getUrlPrefix(space.id)}/api/alert/${createdAlert.id}`) + .get(`${getUrlPrefix(space.id)}/api/alerts/alert/${createdAlert.id}`) .set('kbn-xsrf', 'foo') .auth(user.username, user.password) .expect(200); @@ -91,11 +91,11 @@ export default function createEnableAlertTests({ getService }: FtrProviderContex it('should still be able to enable alert when AAD is broken', async () => { const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(space.id)}/api/alert`) + .post(`${getUrlPrefix(space.id)}/api/alerts/alert`) .set('kbn-xsrf', 'foo') .send(getTestAlertData({ enabled: false })) .expect(200); - objectRemover.add(space.id, createdAlert.id, 'alert', undefined); + objectRemover.add(space.id, createdAlert.id, 'alert', 'alerts'); await supertest .put( @@ -127,7 +127,7 @@ export default function createEnableAlertTests({ getService }: FtrProviderContex expect(response.statusCode).to.eql(204); expect(response.body).to.eql(''); const { body: updatedAlert } = await supertestWithoutAuth - .get(`${getUrlPrefix(space.id)}/api/alert/${createdAlert.id}`) + .get(`${getUrlPrefix(space.id)}/api/alerts/alert/${createdAlert.id}`) .set('kbn-xsrf', 'foo') .auth(user.username, user.password) .expect(200); @@ -154,11 +154,11 @@ export default function createEnableAlertTests({ getService }: FtrProviderContex it(`shouldn't enable alert from another space`, async () => { const { body: createdAlert } = await supertest - .post(`${getUrlPrefix('other')}/api/alert`) + .post(`${getUrlPrefix('other')}/api/alerts/alert`) .set('kbn-xsrf', 'foo') .send(getTestAlertData({ enabled: false })) .expect(200); - objectRemover.add('other', createdAlert.id, 'alert', undefined); + objectRemover.add('other', createdAlert.id, 'alert', 'alerts'); const response = await alertUtils.getEnableRequest(createdAlert.id); diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/find.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/find.ts index 1c4d684eb78de7..5fe9edeb91ec9d 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/find.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/find.ts @@ -24,15 +24,17 @@ export default function createFindTests({ getService }: FtrProviderContext) { describe(scenario.id, () => { it('should handle find alert request appropriately', async () => { const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(space.id)}/api/alert`) + .post(`${getUrlPrefix(space.id)}/api/alerts/alert`) .set('kbn-xsrf', 'foo') .send(getTestAlertData()) .expect(200); - objectRemover.add(space.id, createdAlert.id, 'alert', undefined); + objectRemover.add(space.id, createdAlert.id, 'alert', 'alerts'); const response = await supertestWithoutAuth .get( - `${getUrlPrefix(space.id)}/api/alert/_find?search=test.noop&search_fields=alertTypeId` + `${getUrlPrefix( + space.id + )}/api/alerts/_find?search=test.noop&search_fields=alertTypeId` ) .auth(user.username, user.password); @@ -95,7 +97,7 @@ export default function createFindTests({ getService }: FtrProviderContext) { .expect(200); const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(space.id)}/api/alert`) + .post(`${getUrlPrefix(space.id)}/api/alerts/alert`) .set('kbn-xsrf', 'foo') .send( getTestAlertData({ @@ -110,13 +112,13 @@ export default function createFindTests({ getService }: FtrProviderContext) { }) ) .expect(200); - objectRemover.add(space.id, createdAlert.id, 'alert', undefined); + objectRemover.add(space.id, createdAlert.id, 'alert', 'alerts'); const response = await supertestWithoutAuth .get( `${getUrlPrefix( space.id - )}/api/alert/_find?filter=alert.attributes.actions:{ actionTypeId: test.noop }` + )}/api/alerts/_find?filter=alert.attributes.actions:{ actionTypeId: test.noop }` ) .auth(user.username, user.password); @@ -174,15 +176,15 @@ export default function createFindTests({ getService }: FtrProviderContext) { it(`shouldn't find alert from another space`, async () => { const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(space.id)}/api/alert`) + .post(`${getUrlPrefix(space.id)}/api/alerts/alert`) .set('kbn-xsrf', 'foo') .send(getTestAlertData()) .expect(200); - objectRemover.add(space.id, createdAlert.id, 'alert', undefined); + objectRemover.add(space.id, createdAlert.id, 'alert', 'alerts'); const response = await supertestWithoutAuth .get( - `${getUrlPrefix('other')}/api/alert/_find?search=test.noop&search_fields=alertTypeId` + `${getUrlPrefix('other')}/api/alerts/_find?search=test.noop&search_fields=alertTypeId` ) .auth(user.username, user.password); diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/get.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/get.ts index 5800273dce75d3..a203b7d7c151b3 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/get.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/get.ts @@ -24,14 +24,14 @@ export default function createGetTests({ getService }: FtrProviderContext) { describe(scenario.id, () => { it('should handle get alert request appropriately', async () => { const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(space.id)}/api/alert`) + .post(`${getUrlPrefix(space.id)}/api/alerts/alert`) .set('kbn-xsrf', 'foo') .send(getTestAlertData()) .expect(200); - objectRemover.add(space.id, createdAlert.id, 'alert', undefined); + objectRemover.add(space.id, createdAlert.id, 'alert', 'alerts'); const response = await supertestWithoutAuth - .get(`${getUrlPrefix(space.id)}/api/alert/${createdAlert.id}`) + .get(`${getUrlPrefix(space.id)}/api/alerts/alert/${createdAlert.id}`) .auth(user.username, user.password); switch (scenario.id) { @@ -78,14 +78,14 @@ export default function createGetTests({ getService }: FtrProviderContext) { it(`shouldn't get alert from another space`, async () => { const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(space.id)}/api/alert`) + .post(`${getUrlPrefix(space.id)}/api/alerts/alert`) .set('kbn-xsrf', 'foo') .send(getTestAlertData()) .expect(200); - objectRemover.add(space.id, createdAlert.id, 'alert', undefined); + objectRemover.add(space.id, createdAlert.id, 'alert', 'alerts'); const response = await supertestWithoutAuth - .get(`${getUrlPrefix('other')}/api/alert/${createdAlert.id}`) + .get(`${getUrlPrefix('other')}/api/alerts/alert/${createdAlert.id}`) .auth(user.username, user.password); expect(response.statusCode).to.eql(404); @@ -114,7 +114,7 @@ export default function createGetTests({ getService }: FtrProviderContext) { it(`should handle get alert request appropriately when alert doesn't exist`, async () => { const response = await supertestWithoutAuth - .get(`${getUrlPrefix(space.id)}/api/alert/1`) + .get(`${getUrlPrefix(space.id)}/api/alerts/alert/1`) .auth(user.username, user.password); switch (scenario.id) { diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/get_alert_state.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/get_alert_state.ts index 42a6b36df0f97f..fd071bd55b3776 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/get_alert_state.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/get_alert_state.ts @@ -24,14 +24,14 @@ export default function createGetAlertStateTests({ getService }: FtrProviderCont describe(scenario.id, () => { it('should handle getAlertState alert request appropriately', async () => { const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(space.id)}/api/alert`) + .post(`${getUrlPrefix(space.id)}/api/alerts/alert`) .set('kbn-xsrf', 'foo') .send(getTestAlertData()) .expect(200); - objectRemover.add(space.id, createdAlert.id, 'alert', undefined); + objectRemover.add(space.id, createdAlert.id, 'alert', 'alerts'); const response = await supertestWithoutAuth - .get(`${getUrlPrefix(space.id)}/api/alert/${createdAlert.id}/state`) + .get(`${getUrlPrefix(space.id)}/api/alerts/alert/${createdAlert.id}/state`) .auth(user.username, user.password); switch (scenario.id) { @@ -57,14 +57,14 @@ export default function createGetAlertStateTests({ getService }: FtrProviderCont it(`shouldn't getAlertState for an alert from another space`, async () => { const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(space.id)}/api/alert`) + .post(`${getUrlPrefix(space.id)}/api/alerts/alert`) .set('kbn-xsrf', 'foo') .send(getTestAlertData()) .expect(200); - objectRemover.add(space.id, createdAlert.id, 'alert', undefined); + objectRemover.add(space.id, createdAlert.id, 'alert', 'alerts'); const response = await supertestWithoutAuth - .get(`${getUrlPrefix('other')}/api/alert/${createdAlert.id}/state`) + .get(`${getUrlPrefix('other')}/api/alerts/alert/${createdAlert.id}/state`) .auth(user.username, user.password); expect(response.statusCode).to.eql(404); @@ -93,7 +93,7 @@ export default function createGetAlertStateTests({ getService }: FtrProviderCont it(`should handle getAlertState request appropriately when alert doesn't exist`, async () => { const response = await supertestWithoutAuth - .get(`${getUrlPrefix(space.id)}/api/alert/1/state`) + .get(`${getUrlPrefix(space.id)}/api/alerts/alert/1/state`) .auth(user.username, user.password); switch (scenario.id) { diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/index.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/index.ts index 91b0ca0a37c92d..f14f66f66fe2f7 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/index.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/index.ts @@ -8,7 +8,7 @@ import { FtrProviderContext } from '../../../common/ftr_provider_context'; // eslint-disable-next-line import/no-default-export export default function alertingTests({ loadTestFile }: FtrProviderContext) { - describe('Alerting', () => { + describe('Alerts', () => { loadTestFile(require.resolve('./create')); loadTestFile(require.resolve('./delete')); loadTestFile(require.resolve('./disable')); diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/list_alert_types.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/list_alert_types.ts index 4f6b26dfb67fac..dd31e2dbbb5b8c 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/list_alert_types.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/list_alert_types.ts @@ -19,7 +19,7 @@ export default function listAlertTypes({ getService }: FtrProviderContext) { describe(scenario.id, () => { it('should return 200 with list of alert types', async () => { const response = await supertestWithoutAuth - .get(`${getUrlPrefix(space.id)}/api/alert/types`) + .get(`${getUrlPrefix(space.id)}/api/alerts/list_alert_types`) .auth(user.username, user.password); switch (scenario.id) { diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/mute_all.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/mute_all.ts index 0196615629e231..2416bc2ea1d12d 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/mute_all.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/mute_all.ts @@ -32,11 +32,11 @@ export default function createMuteAlertTests({ getService }: FtrProviderContext) describe(scenario.id, () => { it('should handle mute alert request appropriately', async () => { const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(space.id)}/api/alert`) + .post(`${getUrlPrefix(space.id)}/api/alerts/alert`) .set('kbn-xsrf', 'foo') .send(getTestAlertData({ enabled: false })) .expect(200); - objectRemover.add(space.id, createdAlert.id, 'alert', undefined); + objectRemover.add(space.id, createdAlert.id, 'alert', 'alerts'); const response = await alertUtils.getMuteAllRequest(createdAlert.id); @@ -56,7 +56,7 @@ export default function createMuteAlertTests({ getService }: FtrProviderContext) expect(response.statusCode).to.eql(204); expect(response.body).to.eql(''); const { body: updatedAlert } = await supertestWithoutAuth - .get(`${getUrlPrefix(space.id)}/api/alert/${createdAlert.id}`) + .get(`${getUrlPrefix(space.id)}/api/alerts/alert/${createdAlert.id}`) .set('kbn-xsrf', 'foo') .auth(user.username, user.password) .expect(200); diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/mute_instance.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/mute_instance.ts index 0c05dbdf558429..c59b9f4503a03d 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/mute_instance.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/mute_instance.ts @@ -32,11 +32,11 @@ export default function createMuteAlertInstanceTests({ getService }: FtrProvider describe(scenario.id, () => { it('should handle mute alert instance request appropriately', async () => { const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(space.id)}/api/alert`) + .post(`${getUrlPrefix(space.id)}/api/alerts/alert`) .set('kbn-xsrf', 'foo') .send(getTestAlertData({ enabled: false })) .expect(200); - objectRemover.add(space.id, createdAlert.id, 'alert', undefined); + objectRemover.add(space.id, createdAlert.id, 'alert', 'alerts'); const response = await alertUtils.getMuteInstanceRequest(createdAlert.id, '1'); @@ -56,7 +56,7 @@ export default function createMuteAlertInstanceTests({ getService }: FtrProvider expect(response.statusCode).to.eql(204); expect(response.body).to.eql(''); const { body: updatedAlert } = await supertestWithoutAuth - .get(`${getUrlPrefix(space.id)}/api/alert/${createdAlert.id}`) + .get(`${getUrlPrefix(space.id)}/api/alerts/alert/${createdAlert.id}`) .set('kbn-xsrf', 'foo') .auth(user.username, user.password) .expect(200); @@ -76,14 +76,16 @@ export default function createMuteAlertInstanceTests({ getService }: FtrProvider it('should handle mute alert instance request appropriately and not duplicate mutedInstanceIds when muting an instance already muted', async () => { const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(space.id)}/api/alert`) + .post(`${getUrlPrefix(space.id)}/api/alerts/alert`) .set('kbn-xsrf', 'foo') .send(getTestAlertData({ enabled: false })) .expect(200); - objectRemover.add(space.id, createdAlert.id, 'alert', undefined); + objectRemover.add(space.id, createdAlert.id, 'alert', 'alerts'); await supertest - .post(`${getUrlPrefix(space.id)}/api/alert/${createdAlert.id}/alert_instance/1/_mute`) + .post( + `${getUrlPrefix(space.id)}/api/alerts/alert/${createdAlert.id}/alert_instance/1/_mute` + ) .set('kbn-xsrf', 'foo') .expect(204, ''); @@ -105,7 +107,7 @@ export default function createMuteAlertInstanceTests({ getService }: FtrProvider expect(response.statusCode).to.eql(204); expect(response.body).to.eql(''); const { body: updatedAlert } = await supertestWithoutAuth - .get(`${getUrlPrefix(space.id)}/api/alert/${createdAlert.id}`) + .get(`${getUrlPrefix(space.id)}/api/alerts/alert/${createdAlert.id}`) .set('kbn-xsrf', 'foo') .auth(user.username, user.password) .expect(200); diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/unmute_all.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/unmute_all.ts index ebe9f1f645ed7d..fd22752ccc11af 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/unmute_all.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/unmute_all.ts @@ -32,14 +32,14 @@ export default function createUnmuteAlertTests({ getService }: FtrProviderContex describe(scenario.id, () => { it('should handle unmute alert request appropriately', async () => { const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(space.id)}/api/alert`) + .post(`${getUrlPrefix(space.id)}/api/alerts/alert`) .set('kbn-xsrf', 'foo') .send(getTestAlertData({ enabled: false })) .expect(200); - objectRemover.add(space.id, createdAlert.id, 'alert', undefined); + objectRemover.add(space.id, createdAlert.id, 'alert', 'alerts'); await supertest - .post(`${getUrlPrefix(space.id)}/api/alert/${createdAlert.id}/_mute_all`) + .post(`${getUrlPrefix(space.id)}/api/alerts/alert/${createdAlert.id}/_mute_all`) .set('kbn-xsrf', 'foo') .expect(204, ''); @@ -61,7 +61,7 @@ export default function createUnmuteAlertTests({ getService }: FtrProviderContex expect(response.statusCode).to.eql(204); expect(response.body).to.eql(''); const { body: updatedAlert } = await supertestWithoutAuth - .get(`${getUrlPrefix(space.id)}/api/alert/${createdAlert.id}`) + .get(`${getUrlPrefix(space.id)}/api/alerts/alert/${createdAlert.id}`) .set('kbn-xsrf', 'foo') .auth(user.username, user.password) .expect(200); diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/unmute_instance.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/unmute_instance.ts index 7142fd7d91adf0..72b524282354a1 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/unmute_instance.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/unmute_instance.ts @@ -32,14 +32,16 @@ export default function createMuteAlertInstanceTests({ getService }: FtrProvider describe(scenario.id, () => { it('should handle unmute alert instance request appropriately', async () => { const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(space.id)}/api/alert`) + .post(`${getUrlPrefix(space.id)}/api/alerts/alert`) .set('kbn-xsrf', 'foo') .send(getTestAlertData({ enabled: false })) .expect(200); - objectRemover.add(space.id, createdAlert.id, 'alert', undefined); + objectRemover.add(space.id, createdAlert.id, 'alert', 'alerts'); await supertest - .post(`${getUrlPrefix(space.id)}/api/alert/${createdAlert.id}/alert_instance/1/_mute`) + .post( + `${getUrlPrefix(space.id)}/api/alerts/alert/${createdAlert.id}/alert_instance/1/_mute` + ) .set('kbn-xsrf', 'foo') .expect(204, ''); @@ -61,7 +63,7 @@ export default function createMuteAlertInstanceTests({ getService }: FtrProvider expect(response.statusCode).to.eql(204); expect(response.body).to.eql(''); const { body: updatedAlert } = await supertestWithoutAuth - .get(`${getUrlPrefix(space.id)}/api/alert/${createdAlert.id}`) + .get(`${getUrlPrefix(space.id)}/api/alerts/alert/${createdAlert.id}`) .set('kbn-xsrf', 'foo') .auth(user.username, user.password) .expect(200); diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/update.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/update.ts index 0af1e22584643e..2bcc035beb7a93 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/update.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/update.ts @@ -39,11 +39,11 @@ export default function createUpdateTests({ getService }: FtrProviderContext) { describe(scenario.id, () => { it('should handle update alert request appropriately', async () => { const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(space.id)}/api/alert`) + .post(`${getUrlPrefix(space.id)}/api/alerts/alert`) .set('kbn-xsrf', 'foo') .send(getTestAlertData()) .expect(200); - objectRemover.add(space.id, createdAlert.id, 'alert', undefined); + objectRemover.add(space.id, createdAlert.id, 'alert', 'alerts'); const updatedData = { name: 'bcd', @@ -56,7 +56,7 @@ export default function createUpdateTests({ getService }: FtrProviderContext) { throttle: '1m', }; const response = await supertestWithoutAuth - .put(`${getUrlPrefix(space.id)}/api/alert/${createdAlert.id}`) + .put(`${getUrlPrefix(space.id)}/api/alerts/alert/${createdAlert.id}`) .set('kbn-xsrf', 'foo') .auth(user.username, user.password) .send(updatedData); @@ -110,11 +110,11 @@ export default function createUpdateTests({ getService }: FtrProviderContext) { it('should still be able to update when AAD is broken', async () => { const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(space.id)}/api/alert`) + .post(`${getUrlPrefix(space.id)}/api/alerts/alert`) .set('kbn-xsrf', 'foo') .send(getTestAlertData()) .expect(200); - objectRemover.add(space.id, createdAlert.id, 'alert', undefined); + objectRemover.add(space.id, createdAlert.id, 'alert', 'alerts'); await supertest .put( @@ -139,7 +139,7 @@ export default function createUpdateTests({ getService }: FtrProviderContext) { throttle: '1m', }; const response = await supertestWithoutAuth - .put(`${getUrlPrefix(space.id)}/api/alert/${createdAlert.id}`) + .put(`${getUrlPrefix(space.id)}/api/alerts/alert/${createdAlert.id}`) .set('kbn-xsrf', 'foo') .auth(user.username, user.password) .send(updatedData); @@ -193,14 +193,14 @@ export default function createUpdateTests({ getService }: FtrProviderContext) { it(`shouldn't update alert from another space`, async () => { const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(space.id)}/api/alert`) + .post(`${getUrlPrefix(space.id)}/api/alerts/alert`) .set('kbn-xsrf', 'foo') .send(getTestAlertData()) .expect(200); - objectRemover.add(space.id, createdAlert.id, 'alert', undefined); + objectRemover.add(space.id, createdAlert.id, 'alert', 'alerts'); const response = await supertestWithoutAuth - .put(`${getUrlPrefix('other')}/api/alert/${createdAlert.id}`) + .put(`${getUrlPrefix('other')}/api/alerts/alert/${createdAlert.id}`) .set('kbn-xsrf', 'foo') .auth(user.username, user.password) .send({ @@ -240,14 +240,14 @@ export default function createUpdateTests({ getService }: FtrProviderContext) { it('should handle update alert request appropriately when attempting to change alert type', async () => { const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(space.id)}/api/alert`) + .post(`${getUrlPrefix(space.id)}/api/alerts/alert`) .set('kbn-xsrf', 'foo') .send(getTestAlertData()) .expect(200); - objectRemover.add(space.id, createdAlert.id, 'alert', undefined); + objectRemover.add(space.id, createdAlert.id, 'alert', 'alerts'); const response = await supertestWithoutAuth - .put(`${getUrlPrefix(space.id)}/api/alert/${createdAlert.id}`) + .put(`${getUrlPrefix(space.id)}/api/alerts/alert/${createdAlert.id}`) .set('kbn-xsrf', 'foo') .auth(user.username, user.password) .send({ @@ -289,7 +289,7 @@ export default function createUpdateTests({ getService }: FtrProviderContext) { it('should handle update alert request appropriately when payload is empty and invalid', async () => { const response = await supertestWithoutAuth - .put(`${getUrlPrefix(space.id)}/api/alert/1`) + .put(`${getUrlPrefix(space.id)}/api/alerts/alert/1`) .set('kbn-xsrf', 'foo') .auth(user.username, user.password) .send({}); @@ -321,7 +321,7 @@ export default function createUpdateTests({ getService }: FtrProviderContext) { it(`should handle update alert request appropriately when alertTypeConfig isn't valid`, async () => { const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(space.id)}/api/alert`) + .post(`${getUrlPrefix(space.id)}/api/alerts/alert`) .set('kbn-xsrf', 'foo') .send( getTestAlertData({ @@ -332,10 +332,10 @@ export default function createUpdateTests({ getService }: FtrProviderContext) { }) ) .expect(200); - objectRemover.add(space.id, createdAlert.id, 'alert', undefined); + objectRemover.add(space.id, createdAlert.id, 'alert', 'alerts'); const response = await supertestWithoutAuth - .put(`${getUrlPrefix(space.id)}/api/alert/${createdAlert.id}`) + .put(`${getUrlPrefix(space.id)}/api/alerts/alert/${createdAlert.id}`) .set('kbn-xsrf', 'foo') .auth(user.username, user.password) .send({ @@ -375,7 +375,7 @@ export default function createUpdateTests({ getService }: FtrProviderContext) { it('should handle update alert request appropriately when interval schedule is wrong syntax', async () => { const response = await supertestWithoutAuth - .put(`${getUrlPrefix(space.id)}/api/alert/1`) + .put(`${getUrlPrefix(space.id)}/api/alerts/alert/1`) .set('kbn-xsrf', 'foo') .auth(user.username, user.password) .send( @@ -413,7 +413,7 @@ export default function createUpdateTests({ getService }: FtrProviderContext) { it('should handle updates to an alert schedule by rescheduling the underlying task', async () => { const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(space.id)}/api/alert`) + .post(`${getUrlPrefix(space.id)}/api/alerts/alert`) .set('kbn-xsrf', 'foo') .send( getTestAlertData({ @@ -421,7 +421,7 @@ export default function createUpdateTests({ getService }: FtrProviderContext) { }) ) .expect(200); - objectRemover.add(space.id, createdAlert.id, 'alert', undefined); + objectRemover.add(space.id, createdAlert.id, 'alert', 'alerts'); await retry.try(async () => { const alertTask = (await getAlertingTaskById(createdAlert.scheduledTaskId)).docs[0]; @@ -441,7 +441,7 @@ export default function createUpdateTests({ getService }: FtrProviderContext) { throttle: '1m', }; const response = await supertestWithoutAuth - .put(`${getUrlPrefix(space.id)}/api/alert/${createdAlert.id}`) + .put(`${getUrlPrefix(space.id)}/api/alerts/alert/${createdAlert.id}`) .set('kbn-xsrf', 'foo') .auth(user.username, user.password) .send(updatedData); diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/update_api_key.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/update_api_key.ts index 6349919c15cd29..bf72b970dc0f1a 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/update_api_key.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/update_api_key.ts @@ -32,11 +32,11 @@ export default function createUpdateApiKeyTests({ getService }: FtrProviderConte describe(scenario.id, () => { it('should handle update alert api key request appropriately', async () => { const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(space.id)}/api/alert`) + .post(`${getUrlPrefix(space.id)}/api/alerts/alert`) .set('kbn-xsrf', 'foo') .send(getTestAlertData()) .expect(200); - objectRemover.add(space.id, createdAlert.id, 'alert', undefined); + objectRemover.add(space.id, createdAlert.id, 'alert', 'alerts'); const response = await alertUtils.getUpdateApiKeyRequest(createdAlert.id); @@ -56,7 +56,7 @@ export default function createUpdateApiKeyTests({ getService }: FtrProviderConte expect(response.statusCode).to.eql(204); expect(response.body).to.eql(''); const { body: updatedAlert } = await supertestWithoutAuth - .get(`${getUrlPrefix(space.id)}/api/alert/${createdAlert.id}`) + .get(`${getUrlPrefix(space.id)}/api/alerts/alert/${createdAlert.id}`) .set('kbn-xsrf', 'foo') .auth(user.username, user.password) .expect(200); @@ -76,11 +76,11 @@ export default function createUpdateApiKeyTests({ getService }: FtrProviderConte it('should still be able to update API key when AAD is broken', async () => { const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(space.id)}/api/alert`) + .post(`${getUrlPrefix(space.id)}/api/alerts/alert`) .set('kbn-xsrf', 'foo') .send(getTestAlertData()) .expect(200); - objectRemover.add(space.id, createdAlert.id, 'alert', undefined); + objectRemover.add(space.id, createdAlert.id, 'alert', 'alerts'); await supertest .put( @@ -112,7 +112,7 @@ export default function createUpdateApiKeyTests({ getService }: FtrProviderConte expect(response.statusCode).to.eql(204); expect(response.body).to.eql(''); const { body: updatedAlert } = await supertestWithoutAuth - .get(`${getUrlPrefix(space.id)}/api/alert/${createdAlert.id}`) + .get(`${getUrlPrefix(space.id)}/api/alerts/alert/${createdAlert.id}`) .set('kbn-xsrf', 'foo') .auth(user.username, user.password) .expect(200); @@ -132,11 +132,11 @@ export default function createUpdateApiKeyTests({ getService }: FtrProviderConte it(`shouldn't update alert api key from another space`, async () => { const { body: createdAlert } = await supertest - .post(`${getUrlPrefix('other')}/api/alert`) + .post(`${getUrlPrefix('other')}/api/alerts/alert`) .set('kbn-xsrf', 'foo') .send(getTestAlertData()) .expect(200); - objectRemover.add('other', createdAlert.id, 'alert', undefined); + objectRemover.add('other', createdAlert.id, 'alert', 'alerts'); const response = await alertUtils.getUpdateApiKeyRequest(createdAlert.id); diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/alerts_base.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/alerts_base.ts index d3c914942bd90a..8ffe65a8ebb48b 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/alerts_base.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/alerts_base.ts @@ -186,7 +186,7 @@ instanceStateValue: true const reference = alertUtils.generateReference(); const response = await supertestWithoutAuth - .post(`${getUrlPrefix(space.id)}/api/alert`) + .post(`${getUrlPrefix(space.id)}/api/alerts/alert`) .set('kbn-xsrf', 'foo') .send( getTestAlertData({ @@ -211,7 +211,7 @@ instanceStateValue: true ); expect(response.statusCode).to.eql(200); - objectRemover.add(space.id, response.body.id, 'alert', undefined); + objectRemover.add(space.id, response.body.id, 'alert', 'alerts'); const scheduledActionTask = await retry.try(async () => { const searchResult = await es.search({ index: '.kibana_task_manager', @@ -255,7 +255,7 @@ instanceStateValue: true it('should have proper callCluster and savedObjectsClient authorization for alert type executor', async () => { const reference = alertUtils.generateReference(); const response = await supertestWithoutAuth - .post(`${getUrlPrefix(space.id)}/api/alert`) + .post(`${getUrlPrefix(space.id)}/api/alerts/alert`) .set('kbn-xsrf', 'foo') .send( getTestAlertData({ @@ -271,7 +271,7 @@ instanceStateValue: true ); expect(response.statusCode).to.eql(200); - objectRemover.add(space.id, response.body.id, 'alert', undefined); + objectRemover.add(space.id, response.body.id, 'alert', 'alerts'); const alertTestRecord = ( await esTestIndexTool.waitForDocs('alert:test.authorization', reference) )[0]; @@ -301,7 +301,7 @@ instanceStateValue: true .expect(200); objectRemover.add(space.id, createdAction.id, 'action', 'actions'); const response = await supertestWithoutAuth - .post(`${getUrlPrefix(space.id)}/api/alert`) + .post(`${getUrlPrefix(space.id)}/api/alerts/alert`) .set('kbn-xsrf', 'foo') .send( getTestAlertData({ @@ -327,7 +327,7 @@ instanceStateValue: true ); expect(response.statusCode).to.eql(200); - objectRemover.add(space.id, response.body.id, 'alert', undefined); + objectRemover.add(space.id, response.body.id, 'alert', 'alerts'); const actionTestRecord = ( await esTestIndexTool.waitForDocs('action:test.authorization', reference) )[0]; diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/builtin_alert_types/index_threshold/alert.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/builtin_alert_types/index_threshold/alert.ts index 353f7d02f6b0b5..8412c09eefcda3 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/builtin_alert_types/index_threshold/alert.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/builtin_alert_types/index_threshold/alert.ts @@ -342,7 +342,7 @@ export default function alertTests({ getService }: FtrProviderContext) { }; const { status, body: createdAlert } = await supertest - .post(`${getUrlPrefix(Spaces.space1.id)}/api/alert`) + .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerts/alert`) .set('kbn-xsrf', 'foo') .send({ name: params.name, @@ -372,7 +372,7 @@ export default function alertTests({ getService }: FtrProviderContext) { expect(status).to.be(200); const alertId = createdAlert.id; - objectRemover.add(Spaces.space1.id, alertId, 'alert', undefined); + objectRemover.add(Spaces.space1.id, alertId, 'alert', 'alerts'); return alertId; } diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/create.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/create.ts index b10c356cf40d5f..fa256712a012b0 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/create.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/create.ts @@ -39,7 +39,7 @@ export default function createAlertTests({ getService }: FtrProviderContext) { .expect(200); const response = await supertest - .post(`${getUrlPrefix(Spaces.space1.id)}/api/alert`) + .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerts/alert`) .set('kbn-xsrf', 'foo') .send( getTestAlertData({ @@ -54,7 +54,7 @@ export default function createAlertTests({ getService }: FtrProviderContext) { ); expect(response.status).to.eql(200); - objectRemover.add(Spaces.space1.id, response.body.id, 'alert', undefined); + objectRemover.add(Spaces.space1.id, response.body.id, 'alert', 'alerts'); expect(response.body).to.eql({ id: response.body.id, name: 'abc', @@ -104,12 +104,12 @@ export default function createAlertTests({ getService }: FtrProviderContext) { it('should handle create alert request appropriately when an alert is disabled ', async () => { const response = await supertest - .post(`${getUrlPrefix(Spaces.space1.id)}/api/alert`) + .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerts/alert`) .set('kbn-xsrf', 'foo') .send(getTestAlertData({ enabled: false })); expect(response.status).to.eql(200); - objectRemover.add(Spaces.space1.id, response.body.id, 'alert', undefined); + objectRemover.add(Spaces.space1.id, response.body.id, 'alert', 'alerts'); expect(response.body.scheduledTaskId).to.eql(undefined); }); }); diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/delete.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/delete.ts index 3aea982f948ea2..e9dfe1607d32d5 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/delete.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/delete.ts @@ -28,13 +28,13 @@ export default function createDeleteTests({ getService }: FtrProviderContext) { it('should handle delete alert request appropriately', async () => { const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(Spaces.space1.id)}/api/alert`) + .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerts/alert`) .set('kbn-xsrf', 'foo') .send(getTestAlertData()) .expect(200); await supertest - .delete(`${getUrlPrefix(Spaces.space1.id)}/api/alert/${createdAlert.id}`) + .delete(`${getUrlPrefix(Spaces.space1.id)}/api/alerts/alert/${createdAlert.id}`) .set('kbn-xsrf', 'foo') .expect(204, ''); @@ -48,13 +48,13 @@ export default function createDeleteTests({ getService }: FtrProviderContext) { it(`shouldn't delete alert from another space`, async () => { const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(Spaces.space1.id)}/api/alert`) + .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerts/alert`) .set('kbn-xsrf', 'foo') .send(getTestAlertData()) .expect(200); await supertest - .delete(`${getUrlPrefix(Spaces.other.id)}/api/alert/${createdAlert.id}`) + .delete(`${getUrlPrefix(Spaces.other.id)}/api/alerts/alert/${createdAlert.id}`) .set('kbn-xsrf', 'foo') .expect(404, { statusCode: 404, diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/disable.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/disable.ts index 7152a76fa167fb..afa4f03e23b306 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/disable.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/disable.ts @@ -35,11 +35,11 @@ export default function createDisableAlertTests({ getService }: FtrProviderConte it('should handle disable alert request appropriately', async () => { const { body: createdAlert } = await supertestWithoutAuth - .post(`${getUrlPrefix(Spaces.space1.id)}/api/alert`) + .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerts/alert`) .set('kbn-xsrf', 'foo') .send(getTestAlertData({ enabled: true })) .expect(200); - objectRemover.add(Spaces.space1.id, createdAlert.id, 'alert', undefined); + objectRemover.add(Spaces.space1.id, createdAlert.id, 'alert', 'alerts'); await alertUtils.disable(createdAlert.id); @@ -61,11 +61,11 @@ export default function createDisableAlertTests({ getService }: FtrProviderConte it(`shouldn't disable alert from another space`, async () => { const { body: createdAlert } = await supertestWithoutAuth - .post(`${getUrlPrefix(Spaces.other.id)}/api/alert`) + .post(`${getUrlPrefix(Spaces.other.id)}/api/alerts/alert`) .set('kbn-xsrf', 'foo') .send(getTestAlertData({ enabled: true })) .expect(200); - objectRemover.add(Spaces.other.id, createdAlert.id, 'alert', undefined); + objectRemover.add(Spaces.other.id, createdAlert.id, 'alert', 'alerts'); await alertUtils.getDisableRequest(createdAlert.id).expect(404, { statusCode: 404, diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/enable.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/enable.ts index 3d556d09360221..05b212bb064f3f 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/enable.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/enable.ts @@ -35,16 +35,16 @@ export default function createEnableAlertTests({ getService }: FtrProviderContex it('should handle enable alert request appropriately', async () => { const { body: createdAlert } = await supertestWithoutAuth - .post(`${getUrlPrefix(Spaces.space1.id)}/api/alert`) + .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerts/alert`) .set('kbn-xsrf', 'foo') .send(getTestAlertData({ enabled: false })) .expect(200); - objectRemover.add(Spaces.space1.id, createdAlert.id, 'alert', undefined); + objectRemover.add(Spaces.space1.id, createdAlert.id, 'alert', 'alerts'); await alertUtils.enable(createdAlert.id); const { body: updatedAlert } = await supertestWithoutAuth - .get(`${getUrlPrefix(Spaces.space1.id)}/api/alert/${createdAlert.id}`) + .get(`${getUrlPrefix(Spaces.space1.id)}/api/alerts/alert/${createdAlert.id}`) .set('kbn-xsrf', 'foo') .expect(200); expect(typeof updatedAlert.scheduledTaskId).to.eql('string'); @@ -67,11 +67,11 @@ export default function createEnableAlertTests({ getService }: FtrProviderContex it(`shouldn't enable alert from another space`, async () => { const { body: createdAlert } = await supertestWithoutAuth - .post(`${getUrlPrefix(Spaces.other.id)}/api/alert`) + .post(`${getUrlPrefix(Spaces.other.id)}/api/alerts/alert`) .set('kbn-xsrf', 'foo') .send(getTestAlertData({ enabled: false })) .expect(200); - objectRemover.add(Spaces.other.id, createdAlert.id, 'alert', undefined); + objectRemover.add(Spaces.other.id, createdAlert.id, 'alert', 'alerts'); await alertUtils.getEnableRequest(createdAlert.id).expect(404, { statusCode: 404, diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/find.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/find.ts index f57b136b9637a9..06f27d666c3dac 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/find.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/find.ts @@ -20,16 +20,16 @@ export default function createFindTests({ getService }: FtrProviderContext) { it('should handle find alert request appropriately', async () => { const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(Spaces.space1.id)}/api/alert`) + .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerts/alert`) .set('kbn-xsrf', 'foo') .send(getTestAlertData()) .expect(200); - objectRemover.add(Spaces.space1.id, createdAlert.id, 'alert', undefined); + objectRemover.add(Spaces.space1.id, createdAlert.id, 'alert', 'alerts'); const response = await supertest.get( `${getUrlPrefix( Spaces.space1.id - )}/api/alert/_find?search=test.noop&search_fields=alertTypeId` + )}/api/alerts/_find?search=test.noop&search_fields=alertTypeId` ); expect(response.status).to.eql(200); @@ -63,17 +63,17 @@ export default function createFindTests({ getService }: FtrProviderContext) { it(`shouldn't find alert from another space`, async () => { const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(Spaces.space1.id)}/api/alert`) + .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerts/alert`) .set('kbn-xsrf', 'foo') .send(getTestAlertData()) .expect(200); - objectRemover.add(Spaces.space1.id, createdAlert.id, 'alert', undefined); + objectRemover.add(Spaces.space1.id, createdAlert.id, 'alert', 'alerts'); await supertest .get( `${getUrlPrefix( Spaces.other.id - )}/api/alert/_find?search=test.noop&search_fields=alertTypeId` + )}/api/alerts/_find?search=test.noop&search_fields=alertTypeId` ) .expect(200, { page: 1, diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/get.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/get.ts index 6b216d2ba265f2..ff671e16654b55 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/get.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/get.ts @@ -20,14 +20,14 @@ export default function createGetTests({ getService }: FtrProviderContext) { it('should handle get alert request appropriately', async () => { const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(Spaces.space1.id)}/api/alert`) + .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerts/alert`) .set('kbn-xsrf', 'foo') .send(getTestAlertData()) .expect(200); - objectRemover.add(Spaces.space1.id, createdAlert.id, 'alert', undefined); + objectRemover.add(Spaces.space1.id, createdAlert.id, 'alert', 'alerts'); const response = await supertest.get( - `${getUrlPrefix(Spaces.space1.id)}/api/alert/${createdAlert.id}` + `${getUrlPrefix(Spaces.space1.id)}/api/alerts/alert/${createdAlert.id}` ); expect(response.status).to.eql(200); @@ -57,14 +57,14 @@ export default function createGetTests({ getService }: FtrProviderContext) { it(`shouldn't find alert from another space`, async () => { const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(Spaces.space1.id)}/api/alert`) + .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerts/alert`) .set('kbn-xsrf', 'foo') .send(getTestAlertData()) .expect(200); - objectRemover.add(Spaces.space1.id, createdAlert.id, 'alert', undefined); + objectRemover.add(Spaces.space1.id, createdAlert.id, 'alert', 'alerts'); await supertest - .get(`${getUrlPrefix(Spaces.other.id)}/api/alert/${createdAlert.id}`) + .get(`${getUrlPrefix(Spaces.other.id)}/api/alerts/alert/${createdAlert.id}`) .expect(404, { statusCode: 404, error: 'Not Found', @@ -73,7 +73,7 @@ export default function createGetTests({ getService }: FtrProviderContext) { }); it(`should handle get alert request appropriately when alert doesn't exist`, async () => { - await supertest.get(`${getUrlPrefix(Spaces.space1.id)}/api/alert/1`).expect(404, { + await supertest.get(`${getUrlPrefix(Spaces.space1.id)}/api/alerts/alert/1`).expect(404, { statusCode: 404, error: 'Not Found', message: 'Saved object [alert/1] not found', diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/get_alert_state.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/get_alert_state.ts index 06f5f5542780c7..d3f08d7c509a09 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/get_alert_state.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/get_alert_state.ts @@ -21,14 +21,14 @@ export default function createGetAlertStateTests({ getService }: FtrProviderCont it('should handle getAlertState request appropriately', async () => { const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(Spaces.space1.id)}/api/alert`) + .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerts/alert`) .set('kbn-xsrf', 'foo') .send(getTestAlertData()) .expect(200); - objectRemover.add(Spaces.space1.id, createdAlert.id, 'alert', undefined); + objectRemover.add(Spaces.space1.id, createdAlert.id, 'alert', 'alerts'); const response = await supertest.get( - `${getUrlPrefix(Spaces.space1.id)}/api/alert/${createdAlert.id}/state` + `${getUrlPrefix(Spaces.space1.id)}/api/alerts/alert/${createdAlert.id}/state` ); expect(response.status).to.eql(200); @@ -37,7 +37,7 @@ export default function createGetAlertStateTests({ getService }: FtrProviderCont it('should fetch updated state', async () => { const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(Spaces.space1.id)}/api/alert`) + .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerts/alert`) .set('kbn-xsrf', 'foo') .send({ enabled: true, @@ -51,12 +51,12 @@ export default function createGetAlertStateTests({ getService }: FtrProviderCont params: {}, }) .expect(200); - objectRemover.add(Spaces.space1.id, createdAlert.id, 'alert', undefined); + objectRemover.add(Spaces.space1.id, createdAlert.id, 'alert', 'alerts'); // wait for alert to actually execute await retry.try(async () => { const response = await supertest.get( - `${getUrlPrefix(Spaces.space1.id)}/api/alert/${createdAlert.id}/state` + `${getUrlPrefix(Spaces.space1.id)}/api/alerts/alert/${createdAlert.id}/state` ); expect(response.status).to.eql(200); @@ -65,7 +65,7 @@ export default function createGetAlertStateTests({ getService }: FtrProviderCont }); const response = await supertest.get( - `${getUrlPrefix(Spaces.space1.id)}/api/alert/${createdAlert.id}/state` + `${getUrlPrefix(Spaces.space1.id)}/api/alerts/alert/${createdAlert.id}/state` ); expect(response.body.alertTypeState.runCount).to.greaterThan(0); @@ -79,11 +79,13 @@ export default function createGetAlertStateTests({ getService }: FtrProviderCont }); it(`should handle getAlertState request appropriately when alert doesn't exist`, async () => { - await supertest.get(`${getUrlPrefix(Spaces.space1.id)}/api/alert/1/state`).expect(404, { - statusCode: 404, - error: 'Not Found', - message: 'Saved object [alert/1] not found', - }); + await supertest + .get(`${getUrlPrefix(Spaces.space1.id)}/api/alerts/alert/1/state`) + .expect(404, { + statusCode: 404, + error: 'Not Found', + message: 'Saved object [alert/1] not found', + }); }); }); } diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/list_alert_types.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/list_alert_types.ts index 845a6f79557394..aef87eefba2ade 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/list_alert_types.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/list_alert_types.ts @@ -15,7 +15,9 @@ export default function listAlertTypes({ getService }: FtrProviderContext) { describe('list_alert_types', () => { it('should return 200 with list of alert types', async () => { - const response = await supertest.get(`${getUrlPrefix(Spaces.space1.id)}/api/alert/types`); + const response = await supertest.get( + `${getUrlPrefix(Spaces.space1.id)}/api/alerts/list_alert_types` + ); expect(response.status).to.eql(200); const fixtureAlertType = response.body.find((alertType: any) => alertType.id === 'test.noop'); expect(fixtureAlertType).to.eql({ @@ -32,7 +34,9 @@ export default function listAlertTypes({ getService }: FtrProviderContext) { }); it('should return actionVariables with both context and state', async () => { - const response = await supertest.get(`${getUrlPrefix(Spaces.space1.id)}/api/alert/types`); + const response = await supertest.get( + `${getUrlPrefix(Spaces.space1.id)}/api/alerts/list_alert_types` + ); expect(response.status).to.eql(200); const fixtureAlertType = response.body.find( @@ -46,7 +50,9 @@ export default function listAlertTypes({ getService }: FtrProviderContext) { }); it('should return actionVariables with just context', async () => { - const response = await supertest.get(`${getUrlPrefix(Spaces.space1.id)}/api/alert/types`); + const response = await supertest.get( + `${getUrlPrefix(Spaces.space1.id)}/api/alerts/list_alert_types` + ); expect(response.status).to.eql(200); const fixtureAlertType = response.body.find( @@ -60,7 +66,9 @@ export default function listAlertTypes({ getService }: FtrProviderContext) { }); it('should return actionVariables with just state', async () => { - const response = await supertest.get(`${getUrlPrefix(Spaces.space1.id)}/api/alert/types`); + const response = await supertest.get( + `${getUrlPrefix(Spaces.space1.id)}/api/alerts/list_alert_types` + ); expect(response.status).to.eql(200); const fixtureAlertType = response.body.find( diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/mute_all.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/mute_all.ts index b2ba38ac984700..f881d0c677bb5f 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/mute_all.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/mute_all.ts @@ -27,16 +27,16 @@ export default function createMuteTests({ getService }: FtrProviderContext) { it('should handle mute alert request appropriately', async () => { const { body: createdAlert } = await supertestWithoutAuth - .post(`${getUrlPrefix(Spaces.space1.id)}/api/alert`) + .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerts/alert`) .set('kbn-xsrf', 'foo') .send(getTestAlertData({ enabled: false })) .expect(200); - objectRemover.add(Spaces.space1.id, createdAlert.id, 'alert', undefined); + objectRemover.add(Spaces.space1.id, createdAlert.id, 'alert', 'alerts'); await alertUtils.muteAll(createdAlert.id); const { body: updatedAlert } = await supertestWithoutAuth - .get(`${getUrlPrefix(Spaces.space1.id)}/api/alert/${createdAlert.id}`) + .get(`${getUrlPrefix(Spaces.space1.id)}/api/alerts/alert/${createdAlert.id}`) .set('kbn-xsrf', 'foo') .expect(200); expect(updatedAlert.muteAll).to.eql(true); diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/mute_instance.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/mute_instance.ts index d9f52d3321e323..ca0d72aedf7a18 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/mute_instance.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/mute_instance.ts @@ -27,16 +27,16 @@ export default function createMuteInstanceTests({ getService }: FtrProviderConte it('should handle mute alert instance request appropriately', async () => { const { body: createdAlert } = await supertestWithoutAuth - .post(`${getUrlPrefix(Spaces.space1.id)}/api/alert`) + .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerts/alert`) .set('kbn-xsrf', 'foo') .send(getTestAlertData({ enabled: false })) .expect(200); - objectRemover.add(Spaces.space1.id, createdAlert.id, 'alert', undefined); + objectRemover.add(Spaces.space1.id, createdAlert.id, 'alert', 'alerts'); await alertUtils.muteInstance(createdAlert.id, '1'); const { body: updatedAlert } = await supertestWithoutAuth - .get(`${getUrlPrefix(Spaces.space1.id)}/api/alert/${createdAlert.id}`) + .get(`${getUrlPrefix(Spaces.space1.id)}/api/alerts/alert/${createdAlert.id}`) .set('kbn-xsrf', 'foo') .expect(200); expect(updatedAlert.mutedInstanceIds).to.eql(['1']); diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/unmute_all.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/unmute_all.ts index 7c5f1e0a621305..1df99540903d0c 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/unmute_all.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/unmute_all.ts @@ -27,17 +27,17 @@ export default function createUnmuteTests({ getService }: FtrProviderContext) { it('should handle unmute alert request appropriately', async () => { const { body: createdAlert } = await supertestWithoutAuth - .post(`${getUrlPrefix(Spaces.space1.id)}/api/alert`) + .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerts/alert`) .set('kbn-xsrf', 'foo') .send(getTestAlertData({ enabled: false })) .expect(200); - objectRemover.add(Spaces.space1.id, createdAlert.id, 'alert', undefined); + objectRemover.add(Spaces.space1.id, createdAlert.id, 'alert', 'alerts'); await alertUtils.muteAll(createdAlert.id); await alertUtils.unmuteAll(createdAlert.id); const { body: updatedAlert } = await supertestWithoutAuth - .get(`${getUrlPrefix(Spaces.space1.id)}/api/alert/${createdAlert.id}`) + .get(`${getUrlPrefix(Spaces.space1.id)}/api/alerts/alert/${createdAlert.id}`) .set('kbn-xsrf', 'foo') .expect(200); expect(updatedAlert.muteAll).to.eql(false); diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/unmute_instance.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/unmute_instance.ts index 86464c3d6bb64c..332842ce8015fe 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/unmute_instance.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/unmute_instance.ts @@ -27,17 +27,17 @@ export default function createUnmuteInstanceTests({ getService }: FtrProviderCon it('should handle unmute alert instance request appropriately', async () => { const { body: createdAlert } = await supertestWithoutAuth - .post(`${getUrlPrefix(Spaces.space1.id)}/api/alert`) + .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerts/alert`) .set('kbn-xsrf', 'foo') .send(getTestAlertData({ enabled: false })) .expect(200); - objectRemover.add(Spaces.space1.id, createdAlert.id, 'alert', undefined); + objectRemover.add(Spaces.space1.id, createdAlert.id, 'alert', 'alerts'); await alertUtils.muteInstance(createdAlert.id, '1'); await alertUtils.unmuteInstance(createdAlert.id, '1'); const { body: updatedAlert } = await supertestWithoutAuth - .get(`${getUrlPrefix(Spaces.space1.id)}/api/alert/${createdAlert.id}`) + .get(`${getUrlPrefix(Spaces.space1.id)}/api/alerts/alert/${createdAlert.id}`) .set('kbn-xsrf', 'foo') .expect(200); expect(updatedAlert.mutedInstanceIds).to.eql([]); diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/update.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/update.ts index fc0aeb71d9066b..b01a1b140f2d62 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/update.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/update.ts @@ -20,11 +20,11 @@ export default function createUpdateTests({ getService }: FtrProviderContext) { it('should handle update alert request appropriately', async () => { const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(Spaces.space1.id)}/api/alert`) + .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerts/alert`) .set('kbn-xsrf', 'foo') .send(getTestAlertData()) .expect(200); - objectRemover.add(Spaces.space1.id, createdAlert.id, 'alert', undefined); + objectRemover.add(Spaces.space1.id, createdAlert.id, 'alert', 'alerts'); const updatedData = { name: 'bcd', @@ -37,7 +37,7 @@ export default function createUpdateTests({ getService }: FtrProviderContext) { throttle: '1m', }; const response = await supertest - .put(`${getUrlPrefix(Spaces.space1.id)}/api/alert/${createdAlert.id}`) + .put(`${getUrlPrefix(Spaces.space1.id)}/api/alerts/alert/${createdAlert.id}`) .set('kbn-xsrf', 'foo') .send(updatedData) .expect(200); @@ -75,14 +75,14 @@ export default function createUpdateTests({ getService }: FtrProviderContext) { it(`shouldn't update alert from another space`, async () => { const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(Spaces.space1.id)}/api/alert`) + .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerts/alert`) .set('kbn-xsrf', 'foo') .send(getTestAlertData()) .expect(200); - objectRemover.add(Spaces.space1.id, createdAlert.id, 'alert', undefined); + objectRemover.add(Spaces.space1.id, createdAlert.id, 'alert', 'alerts'); await supertest - .put(`${getUrlPrefix(Spaces.other.id)}/api/alert/${createdAlert.id}`) + .put(`${getUrlPrefix(Spaces.other.id)}/api/alerts/alert/${createdAlert.id}`) .set('kbn-xsrf', 'foo') .send({ name: 'bcd', diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/update_api_key.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/update_api_key.ts index 9c7b4dcc8b1a39..93f91bdc731504 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/update_api_key.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/update_api_key.ts @@ -31,16 +31,16 @@ export default function createUpdateApiKeyTests({ getService }: FtrProviderConte it('should handle update alert api key appropriately', async () => { const { body: createdAlert } = await supertestWithoutAuth - .post(`${getUrlPrefix(Spaces.space1.id)}/api/alert`) + .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerts/alert`) .set('kbn-xsrf', 'foo') .send(getTestAlertData()) .expect(200); - objectRemover.add(Spaces.space1.id, createdAlert.id, 'alert', undefined); + objectRemover.add(Spaces.space1.id, createdAlert.id, 'alert', 'alerts'); await alertUtils.updateApiKey(createdAlert.id); const { body: updatedAlert } = await supertestWithoutAuth - .get(`${getUrlPrefix(Spaces.space1.id)}/api/alert/${createdAlert.id}`) + .get(`${getUrlPrefix(Spaces.space1.id)}/api/alerts/alert/${createdAlert.id}`) .set('kbn-xsrf', 'foo') .expect(200); expect(updatedAlert.apiKeyOwner).to.eql(null); @@ -56,11 +56,11 @@ export default function createUpdateApiKeyTests({ getService }: FtrProviderConte it(`shouldn't update alert api key from another space`, async () => { const { body: createdAlert } = await supertestWithoutAuth - .post(`${getUrlPrefix(Spaces.other.id)}/api/alert`) + .post(`${getUrlPrefix(Spaces.other.id)}/api/alerts/alert`) .set('kbn-xsrf', 'foo') .send(getTestAlertData()) .expect(200); - objectRemover.add(Spaces.other.id, createdAlert.id, 'alert', undefined); + objectRemover.add(Spaces.other.id, createdAlert.id, 'alert', 'alerts'); await alertUtils.getUpdateApiKeyRequest(createdAlert.id).expect(404, { statusCode: 404, diff --git a/x-pack/test/case_api_integration/common/config.ts b/x-pack/test/case_api_integration/common/config.ts index 9eb62c2fe07b0b..45b34b7d26940e 100644 --- a/x-pack/test/case_api_integration/common/config.ts +++ b/x-pack/test/case_api_integration/common/config.ts @@ -78,7 +78,6 @@ export function createTestConfig(name: string, options: CreateTestConfigOptions) 'some.non.existent.com', ])}`, `--xpack.actions.enabledActionTypes=${JSON.stringify(enabledActionTypes)}`, - '--xpack.alerting.enabled=true', '--xpack.eventLog.logEntries=true', ...disabledPlugins.map((key) => `--xpack.${key}.enabled=false`), `--plugin-path=${path.join(__dirname, 'fixtures', 'plugins', 'alerts')}`, diff --git a/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/alerts.ts b/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/alerts.ts index 89ce3742adf645..13bf47676cc09c 100644 --- a/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/alerts.ts +++ b/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/alerts.ts @@ -21,7 +21,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { async function createAlert(overwrites: Record = {}) { const { body: createdAlert } = await supertest - .post(`/api/alert`) + .post(`/api/alerts/alert`) .set('kbn-xsrf', 'foo') .send({ enabled: true, diff --git a/x-pack/test/functional_with_es_ssl/apps/uptime/alert_flyout.ts b/x-pack/test/functional_with_es_ssl/apps/uptime/alert_flyout.ts index d78053cf926dc2..6cb74aff95be2d 100644 --- a/x-pack/test/functional_with_es_ssl/apps/uptime/alert_flyout.ts +++ b/x-pack/test/functional_with_es_ssl/apps/uptime/alert_flyout.ts @@ -89,7 +89,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { // put the fetch code in a retry block with a timeout. let alert: any; await retry.tryForTime(15000, async () => { - const apiResponse = await supertest.get('/api/alert/_find?search=uptime-test'); + const apiResponse = await supertest.get('/api/alerts/_find?search=uptime-test'); const alertsFromThisTest = apiResponse.body.data.filter( ({ name }: { name: string }) => name === 'uptime-test' ); @@ -129,7 +129,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { '"minimum_should_match":1}},{"bool":{"should":[{"match":{"monitor.type":"http"}}],"minimum_should_match":1}}]}}]}}]}}' ); } finally { - await supertest.delete(`/api/alert/${id}`).set('kbn-xsrf', 'true').expect(204); + await supertest.delete(`/api/alerts/alert/${id}`).set('kbn-xsrf', 'true').expect(204); } }); }); @@ -176,7 +176,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { it('has created a valid alert with expected parameters', async () => { let alert: any; await retry.tryForTime(15000, async () => { - const apiResponse = await supertest.get(`/api/alert/_find?search=${alertId}`); + const apiResponse = await supertest.get(`/api/alerts/_find?search=${alertId}`); const alertsFromThisTest = apiResponse.body.data.filter( ({ name }: { name: string }) => name === alertId ); @@ -204,7 +204,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { expect(params).to.eql({}); expect(interval).to.eql('11m'); } finally { - await supertest.delete(`/api/alert/${id}`).set('kbn-xsrf', 'true').expect(204); + await supertest.delete(`/api/alerts/alert/${id}`).set('kbn-xsrf', 'true').expect(204); } }); }); diff --git a/x-pack/test/functional_with_es_ssl/fixtures/plugins/alerts/kibana.json b/x-pack/test/functional_with_es_ssl/fixtures/plugins/alerts/kibana.json index 1715f30b822606..74f740f52a8b2d 100644 --- a/x-pack/test/functional_with_es_ssl/fixtures/plugins/alerts/kibana.json +++ b/x-pack/test/functional_with_es_ssl/fixtures/plugins/alerts/kibana.json @@ -3,7 +3,7 @@ "version": "1.0.0", "kibanaVersion": "kibana", "configPath": ["xpack"], - "requiredPlugins": ["alerting", "triggers_actions_ui"], + "requiredPlugins": ["alerts", "triggers_actions_ui"], "server": true, "ui": true } 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 4c68a3aa15b30c..2bc299ede930bb 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 @@ -6,21 +6,21 @@ import React from 'react'; import { Plugin, CoreSetup, AppMountParameters } from 'kibana/public'; -import { PluginSetupContract as AlertingSetup } from '../../../../../../plugins/alerting/public'; -import { AlertType, SanitizedAlert } from '../../../../../../plugins/alerting/common'; +import { PluginSetupContract as AlertingSetup } from '../../../../../../plugins/alerts/public'; +import { AlertType, SanitizedAlert } from '../../../../../../plugins/alerts/common'; import { TriggersAndActionsUIPublicPluginSetup } from '../../../../../../plugins/triggers_actions_ui/public'; export type Setup = void; export type Start = void; export interface AlertingExamplePublicSetupDeps { - alerting: AlertingSetup; + alerts: AlertingSetup; triggers_actions_ui: TriggersAndActionsUIPublicPluginSetup; } export class AlertingFixturePlugin implements Plugin { - public setup(core: CoreSetup, { alerting, triggers_actions_ui }: AlertingExamplePublicSetupDeps) { - alerting.registerNavigation( + public setup(core: CoreSetup, { alerts, triggers_actions_ui }: AlertingExamplePublicSetupDeps) { + alerts.registerNavigation( 'consumer-noop', 'test.noop', (alert: SanitizedAlert, alertType: AlertType) => `/alert/${alert.id}` diff --git a/x-pack/test/functional_with_es_ssl/fixtures/plugins/alerts/server/plugin.ts b/x-pack/test/functional_with_es_ssl/fixtures/plugins/alerts/server/plugin.ts index 123c0c550e71e0..fb431351a382dc 100644 --- a/x-pack/test/functional_with_es_ssl/fixtures/plugins/alerts/server/plugin.ts +++ b/x-pack/test/functional_with_es_ssl/fixtures/plugins/alerts/server/plugin.ts @@ -8,24 +8,24 @@ import { Plugin, CoreSetup } from 'kibana/server'; import { PluginSetupContract as AlertingSetup, AlertType, -} from '../../../../../../plugins/alerting/server'; +} from '../../../../../../plugins/alerts/server'; // this plugin's dependendencies export interface AlertingExampleDeps { - alerting: AlertingSetup; + alerts: AlertingSetup; } export class AlertingFixturePlugin implements Plugin { - public setup(core: CoreSetup, { alerting }: AlertingExampleDeps) { - createNoopAlertType(alerting); - createAlwaysFiringAlertType(alerting); + public setup(core: CoreSetup, { alerts }: AlertingExampleDeps) { + createNoopAlertType(alerts); + createAlwaysFiringAlertType(alerts); } public start() {} public stop() {} } -function createNoopAlertType(alerting: AlertingSetup) { +function createNoopAlertType(alerts: AlertingSetup) { const noopAlertType: AlertType = { id: 'test.noop', name: 'Test: Noop', @@ -34,10 +34,10 @@ function createNoopAlertType(alerting: AlertingSetup) { async executor() {}, producer: 'alerting', }; - alerting.registerType(noopAlertType); + alerts.registerType(noopAlertType); } -function createAlwaysFiringAlertType(alerting: AlertingSetup) { +function createAlwaysFiringAlertType(alerts: AlertingSetup) { // Alert types const alwaysFiringAlertType: any = { id: 'test.always-firing', @@ -63,5 +63,5 @@ function createAlwaysFiringAlertType(alerting: AlertingSetup) { }; }, }; - alerting.registerType(alwaysFiringAlertType); + alerts.registerType(alwaysFiringAlertType); } diff --git a/x-pack/test/functional_with_es_ssl/services/alerting/alerts.ts b/x-pack/test/functional_with_es_ssl/services/alerting/alerts.ts index 2a0d28f2467655..25f4c6a932d5eb 100644 --- a/x-pack/test/functional_with_es_ssl/services/alerting/alerts.ts +++ b/x-pack/test/functional_with_es_ssl/services/alerting/alerts.ts @@ -38,7 +38,7 @@ export class Alerts { ) { this.log.debug(`creating alert ${name}`); - const { data: alert, status, statusText } = await this.axios.post(`/api/alert`, { + const { data: alert, status, statusText } = await this.axios.post(`/api/alerts/alert`, { enabled: true, name, tags, @@ -63,7 +63,7 @@ export class Alerts { public async createNoOp(name: string) { this.log.debug(`creating alert ${name}`); - const { data: alert, status, statusText } = await this.axios.post(`/api/alert`, { + const { data: alert, status, statusText } = await this.axios.post(`/api/alerts/alert`, { enabled: true, name, tags: ['foo'], @@ -96,7 +96,7 @@ export class Alerts { ) { this.log.debug(`creating alert ${name}`); - const { data: alert, status, statusText } = await this.axios.post(`/api/alert`, { + const { data: alert, status, statusText } = await this.axios.post(`/api/alerts/alert`, { enabled: true, name, tags: ['foo'], @@ -132,7 +132,7 @@ export class Alerts { public async deleteAlert(id: string) { this.log.debug(`deleting alert ${id}`); - const { data: alert, status, statusText } = await this.axios.delete(`/api/alert/${id}`); + const { data: alert, status, statusText } = await this.axios.delete(`/api/alerts/alert/${id}`); if (status !== 204) { throw new Error( `Expected status code of 204, received ${status} ${statusText}: ${util.inspect(alert)}` @@ -144,7 +144,7 @@ export class Alerts { public async getAlertState(id: string) { this.log.debug(`getting alert ${id} state`); - const { data } = await this.axios.get(`/api/alert/${id}/state`); + const { data } = await this.axios.get(`/api/alerts/alert/${id}/state`); return data; } @@ -152,7 +152,7 @@ export class Alerts { this.log.debug(`muting instance ${instanceId} under alert ${id}`); const { data: alert, status, statusText } = await this.axios.post( - `/api/alert/${id}/alert_instance/${instanceId}/_mute` + `/api/alerts/alert/${id}/alert_instance/${instanceId}/_mute` ); if (status !== 204) { throw new Error( diff --git a/x-pack/typings/hapi.d.ts b/x-pack/typings/hapi.d.ts index ed86a961cd1db3..6af723101fc223 100644 --- a/x-pack/typings/hapi.d.ts +++ b/x-pack/typings/hapi.d.ts @@ -9,7 +9,7 @@ import 'hapi'; import { XPackMainPlugin } from '../legacy/plugins/xpack_main/server/xpack_main'; import { SecurityPlugin } from '../legacy/plugins/security'; import { ActionsPlugin, ActionsClient } from '../plugins/actions/server'; -import { AlertingPlugin, AlertsClient } from '../plugins/alerting/server'; +import { AlertingPlugin, AlertsClient } from '../plugins/alerts/server'; import { TaskManager } from '../plugins/task_manager/server'; declare module 'hapi' { @@ -21,7 +21,7 @@ declare module 'hapi' { xpack_main: XPackMainPlugin; security?: SecurityPlugin; actions?: ActionsPlugin; - alerting?: AlertingPlugin; + alerts?: AlertingPlugin; task_manager?: TaskManager; } } From e2ddf8bc2d829e357e7f9ea06f8e3d291a86619e Mon Sep 17 00:00:00 2001 From: patrykkopycinski Date: Tue, 2 Jun 2020 11:28:25 +0200 Subject: [PATCH 15/24] [SIEM] Fix draft timeline can be attached to a case (#67844) --- .../components/flyout/header/index.tsx | 4 ++ .../timeline/properties/helpers.tsx | 63 ++++++++-------- .../timeline/properties/index.test.tsx | 71 +++++++++++++++++++ .../components/timeline/properties/index.tsx | 4 ++ .../timeline/properties/properties_right.tsx | 4 ++ .../containers/one/index.gql_query.ts | 1 + 6 files changed, 118 insertions(+), 29 deletions(-) diff --git a/x-pack/plugins/siem/public/timelines/components/flyout/header/index.tsx b/x-pack/plugins/siem/public/timelines/components/flyout/header/index.tsx index b332260597f223..ab8a24889e9bf5 100644 --- a/x-pack/plugins/siem/public/timelines/components/flyout/header/index.tsx +++ b/x-pack/plugins/siem/public/timelines/components/flyout/header/index.tsx @@ -39,6 +39,7 @@ const StatefulFlyoutHeader = React.memo( title, noteIds, notesById, + status, timelineId, toggleLock, updateDescription, @@ -62,6 +63,7 @@ const StatefulFlyoutHeader = React.memo( isFavorite={isFavorite} title={title} noteIds={noteIds} + status={status} timelineId={timelineId} toggleLock={toggleLock} updateDescription={updateDescription} @@ -94,6 +96,7 @@ const makeMapStateToProps = () => { kqlQuery, title = '', noteIds = emptyNotesId, + status, } = timeline; const history = emptyHistory; // TODO: get history from store via selector @@ -107,6 +110,7 @@ const makeMapStateToProps = () => { isFavorite, isDatepickerLocked: globalInput.linkTo.includes('timeline'), noteIds, + status, title, }; }; diff --git a/x-pack/plugins/siem/public/timelines/components/timeline/properties/helpers.tsx b/x-pack/plugins/siem/public/timelines/components/timeline/properties/helpers.tsx index 3b1d324f3444d7..3ef10d394bc7bf 100644 --- a/x-pack/plugins/siem/public/timelines/components/timeline/properties/helpers.tsx +++ b/x-pack/plugins/siem/public/timelines/components/timeline/properties/helpers.tsx @@ -23,6 +23,7 @@ import styled from 'styled-components'; import { useHistory } from 'react-router-dom'; import { useSelector } from 'react-redux'; +import { TimelineStatus } from '../../../../../common/types/timeline'; import { Note } from '../../../../common/lib/note'; import { Notes } from '../../notes'; import { AssociateNote, UpdateNote } from '../../notes/helpers'; @@ -119,40 +120,44 @@ Name.displayName = 'Name'; interface NewCaseProps { onClosePopover: () => void; timelineId: string; + timelineStatus: TimelineStatus; timelineTitle: string; } -export const NewCase = React.memo(({ onClosePopover, timelineId, timelineTitle }) => { - const history = useHistory(); - const { savedObjectId } = useSelector((state: State) => - timelineSelectors.selectTimeline(state, timelineId) - ); - const handleClick = useCallback(() => { - onClosePopover(); - history.push({ - pathname: `/${SiemPageName.case}/create`, - state: { - insertTimeline: { - timelineId, - timelineSavedObjectId: savedObjectId, - timelineTitle: timelineTitle.length > 0 ? timelineTitle : i18n.UNTITLED_TIMELINE, +export const NewCase = React.memo( + ({ onClosePopover, timelineId, timelineStatus, timelineTitle }) => { + const history = useHistory(); + const { savedObjectId } = useSelector((state: State) => + timelineSelectors.selectTimeline(state, timelineId) + ); + const handleClick = useCallback(() => { + onClosePopover(); + history.push({ + pathname: `/${SiemPageName.case}/create`, + state: { + insertTimeline: { + timelineId, + timelineSavedObjectId: savedObjectId, + timelineTitle: timelineTitle.length > 0 ? timelineTitle : i18n.UNTITLED_TIMELINE, + }, }, - }, - }); - }, [onClosePopover, history, timelineId, timelineTitle]); + }); + }, [onClosePopover, history, timelineId, timelineTitle]); - return ( - - {i18n.ATTACH_TIMELINE_TO_NEW_CASE} - - ); -}); + return ( + + {i18n.ATTACH_TIMELINE_TO_NEW_CASE} + + ); + } +); NewCase.displayName = 'NewCase'; interface NewTimelineProps { diff --git a/x-pack/plugins/siem/public/timelines/components/timeline/properties/index.test.tsx b/x-pack/plugins/siem/public/timelines/components/timeline/properties/index.test.tsx index bfa32fecac89ba..8bdec78ec8da2a 100644 --- a/x-pack/plugins/siem/public/timelines/components/timeline/properties/index.test.tsx +++ b/x-pack/plugins/siem/public/timelines/components/timeline/properties/index.test.tsx @@ -8,6 +8,7 @@ import { mount } from 'enzyme'; import React from 'react'; import { Provider as ReduxStoreProvider } from 'react-redux'; +import { TimelineStatus } from '../../../../../common/types/timeline'; import { mockGlobalState, apolloClientObservable, @@ -25,6 +26,24 @@ jest.mock('../../../../common/components/utils'); width: mockedWidth, })); +jest.mock('react-redux', () => { + const originalModule = jest.requireActual('react-redux'); + + return { + ...originalModule, + useSelector: jest.fn().mockReturnValue({ savedObjectId: '1' }), + }; +}); + +jest.mock('react-router-dom', () => { + const originalModule = jest.requireActual('react-router-dom'); + + return { + ...originalModule, + useHistory: jest.fn(), + }; +}); + describe('Properties', () => { const usersViewing = ['elastic']; @@ -50,6 +69,7 @@ describe('Properties', () => { description="" getNotesByIds={jest.fn()} noteIds={[]} + status={TimelineStatus.active} timelineId="abc" toggleLock={jest.fn()} updateDescription={jest.fn()} @@ -60,7 +80,45 @@ describe('Properties', () => { /> ); + + wrapper.find('[data-test-subj="settings-gear"]').at(0).simulate('click'); + expect(wrapper.find('[data-test-subj="timeline-properties"]').exists()).toEqual(true); + expect(wrapper.find('button[data-test-subj="attach-timeline-case"]').prop('disabled')).toEqual( + false + ); + }); + + test('renders correctly draft timeline', () => { + const wrapper = mount( + + + + ); + + wrapper.find('[data-test-subj="settings-gear"]').at(0).simulate('click'); + + expect(wrapper.find('button[data-test-subj="attach-timeline-case"]').prop('disabled')).toEqual( + true + ); }); test('it renders an empty star icon when it is NOT a favorite', () => { @@ -76,6 +134,7 @@ describe('Properties', () => { description="" getNotesByIds={jest.fn()} noteIds={[]} + status={TimelineStatus.active} timelineId="abc" toggleLock={jest.fn()} updateDescription={jest.fn()} @@ -103,6 +162,7 @@ describe('Properties', () => { description="" getNotesByIds={jest.fn()} noteIds={[]} + status={TimelineStatus.active} timelineId="abc" toggleLock={jest.fn()} updateDescription={jest.fn()} @@ -132,6 +192,7 @@ describe('Properties', () => { description="" getNotesByIds={jest.fn()} noteIds={[]} + status={TimelineStatus.active} timelineId="abc" toggleLock={jest.fn()} updateDescription={jest.fn()} @@ -159,6 +220,7 @@ describe('Properties', () => { description="" getNotesByIds={jest.fn()} noteIds={[]} + status={TimelineStatus.active} timelineId="abc" toggleLock={jest.fn()} updateDescription={jest.fn()} @@ -191,6 +253,7 @@ describe('Properties', () => { description="" getNotesByIds={jest.fn()} noteIds={[]} + status={TimelineStatus.active} timelineId="abc" toggleLock={jest.fn()} updateDescription={jest.fn()} @@ -222,6 +285,7 @@ describe('Properties', () => { description="" getNotesByIds={jest.fn()} noteIds={[]} + status={TimelineStatus.active} timelineId="abc" toggleLock={jest.fn()} updateDescription={jest.fn()} @@ -256,6 +320,7 @@ describe('Properties', () => { description={description} getNotesByIds={jest.fn()} noteIds={[]} + status={TimelineStatus.active} timelineId="abc" toggleLock={jest.fn()} updateDescription={jest.fn()} @@ -292,6 +357,7 @@ describe('Properties', () => { description={description} getNotesByIds={jest.fn()} noteIds={[]} + status={TimelineStatus.active} timelineId="abc" toggleLock={jest.fn()} updateDescription={jest.fn()} @@ -326,6 +392,7 @@ describe('Properties', () => { description="" getNotesByIds={jest.fn()} noteIds={[]} + status={TimelineStatus.active} timelineId="abc" toggleLock={jest.fn()} updateDescription={jest.fn()} @@ -360,6 +427,7 @@ describe('Properties', () => { description="" getNotesByIds={jest.fn()} noteIds={[]} + status={TimelineStatus.active} timelineId="abc" toggleLock={jest.fn()} updateDescription={jest.fn()} @@ -392,6 +460,7 @@ describe('Properties', () => { description="" getNotesByIds={jest.fn()} noteIds={[]} + status={TimelineStatus.active} timelineId="abc" toggleLock={jest.fn()} updateDescription={jest.fn()} @@ -421,6 +490,7 @@ describe('Properties', () => { description="" getNotesByIds={jest.fn()} noteIds={[]} + status={TimelineStatus.active} timelineId="abc" toggleLock={jest.fn()} updateDescription={jest.fn()} @@ -448,6 +518,7 @@ describe('Properties', () => { description="" getNotesByIds={jest.fn()} noteIds={[]} + status={TimelineStatus.active} timelineId="abc" toggleLock={jest.fn()} updateDescription={jest.fn()} diff --git a/x-pack/plugins/siem/public/timelines/components/timeline/properties/index.tsx b/x-pack/plugins/siem/public/timelines/components/timeline/properties/index.tsx index 502cc85ce907aa..d8966a58748eda 100644 --- a/x-pack/plugins/siem/public/timelines/components/timeline/properties/index.tsx +++ b/x-pack/plugins/siem/public/timelines/components/timeline/properties/index.tsx @@ -6,6 +6,7 @@ import React, { useState, useCallback, useMemo } from 'react'; +import { TimelineStatus } from '../../../../../common/types/timeline'; import { useThrottledResizeObserver } from '../../../../common/components/utils'; import { Note } from '../../../../common/lib/note'; import { InputsModelId } from '../../../../common/store/inputs/constants'; @@ -31,6 +32,7 @@ interface Props { isFavorite: boolean; noteIds: string[]; timelineId: string; + status: TimelineStatus; title: string; toggleLock: ToggleLock; updateDescription: UpdateDescription; @@ -62,6 +64,7 @@ export const Properties = React.memo( isDatepickerLocked, isFavorite, noteIds, + status, timelineId, title, toggleLock, @@ -140,6 +143,7 @@ export const Properties = React.memo( showNotesFromWidth={width < showNotesThreshold} showTimelineModal={showTimelineModal} showUsersView={title.length > 0} + status={status} timelineId={timelineId} title={title} updateDescription={updateDescription} diff --git a/x-pack/plugins/siem/public/timelines/components/timeline/properties/properties_right.tsx b/x-pack/plugins/siem/public/timelines/components/timeline/properties/properties_right.tsx index 4149a958e889dd..963b67838e811a 100644 --- a/x-pack/plugins/siem/public/timelines/components/timeline/properties/properties_right.tsx +++ b/x-pack/plugins/siem/public/timelines/components/timeline/properties/properties_right.tsx @@ -22,6 +22,7 @@ import { InspectButton, InspectButtonContainer } from '../../../../common/compon import * as i18n from './translations'; import { AssociateNote } from '../../notes/helpers'; import { Note } from '../../../../common/lib/note'; +import { TimelineStatus } from '../../../../../common/types/timeline'; export const PropertiesRightStyle = styled(EuiFlexGroup)` margin-right: 5px; @@ -79,6 +80,7 @@ interface Props { onCloseTimelineModal: () => void; onOpenTimelineModal: () => void; showTimelineModal: boolean; + status: TimelineStatus; title: string; updateNote: UpdateNote; } @@ -106,6 +108,7 @@ const PropertiesRightComponent: React.FC = ({ onCloseTimelineModal, onOpenTimelineModal, title, + status, }) => ( @@ -142,6 +145,7 @@ const PropertiesRightComponent: React.FC = ({ onClosePopover={onClosePopover} timelineId={timelineId} timelineTitle={title} + timelineStatus={status} /> diff --git a/x-pack/plugins/siem/public/timelines/containers/one/index.gql_query.ts b/x-pack/plugins/siem/public/timelines/containers/one/index.gql_query.ts index d70a419b99a3b7..47e80b005fb995 100644 --- a/x-pack/plugins/siem/public/timelines/containers/one/index.gql_query.ts +++ b/x-pack/plugins/siem/public/timelines/containers/one/index.gql_query.ts @@ -128,6 +128,7 @@ export const oneTimelineQuery = gql` updatedBy version } + status title timelineType templateTimelineId From 8373eb8680935a95c0f61e731b1f1e6332a968de Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Tue, 2 Jun 2020 12:47:13 +0300 Subject: [PATCH 16/24] Fix bug in tsvb metric add color rules (#67763) Co-authored-by: Elastic Machine --- .../public/application/components/color_rules.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/vis_type_timeseries/public/application/components/color_rules.js b/src/plugins/vis_type_timeseries/public/application/components/color_rules.js index a0cd2068222237..46b2886daf36d5 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/color_rules.js +++ b/src/plugins/vis_type_timeseries/public/application/components/color_rules.js @@ -54,7 +54,7 @@ class ColorRulesUI extends Component { renderRow(row, i, items) { const defaults = { value: 0 }; const model = { ...defaults, ...row }; - const handleAdd = collectionActions.handleAdd.bind(null, this.props); + const handleAdd = () => collectionActions.handleAdd(this.props); const handleDelete = collectionActions.handleDelete.bind(null, this.props, model); const { intl } = this.props; const operatorOptions = [ From 7da774ff5a72dec56c70ece69cb83172e2f9f3cd Mon Sep 17 00:00:00 2001 From: Kevin Logan <56395104+kevinlog@users.noreply.github.com> Date: Tue, 2 Jun 2020 07:26:22 -0400 Subject: [PATCH 17/24] [Endpoint] add new policy fields (#67323) --- .../policy/store/policy_list/middleware.ts | 8 +- .../policy_list/mock_policy_result_list.ts | 40 ++++ .../pages/policy/view/policy_list.test.tsx | 65 ++++++ .../pages/policy/view/policy_list.tsx | 202 ++++++++++++++---- 4 files changed, 265 insertions(+), 50 deletions(-) create mode 100644 x-pack/plugins/siem/public/management/pages/policy/store/policy_list/mock_policy_result_list.ts create mode 100644 x-pack/plugins/siem/public/management/pages/policy/view/policy_list.test.tsx diff --git a/x-pack/plugins/siem/public/management/pages/policy/store/policy_list/middleware.ts b/x-pack/plugins/siem/public/management/pages/policy/store/policy_list/middleware.ts index 6054ec34b2d01a..7259c0fd4d21cd 100644 --- a/x-pack/plugins/siem/public/management/pages/policy/store/policy_list/middleware.ts +++ b/x-pack/plugins/siem/public/management/pages/policy/store/policy_list/middleware.ts @@ -9,6 +9,7 @@ import { sendGetEndpointSpecificDatasources } from './services/ingest'; import { isOnPolicyListPage, urlSearchParams } from './selectors'; import { ImmutableMiddlewareFactory } from '../../../../../common/store'; import { Immutable } from '../../../../../../common/endpoint/types'; +import { initialPolicyListState } from './reducer'; export const policyListMiddlewareFactory: ImmutableMiddlewareFactory> = ( coreStart @@ -19,6 +20,7 @@ export const policyListMiddlewareFactory: ImmutableMiddlewareFactory GetPolicyListResponse = (options = {}) => { + const { + total = 1, + request_page_size: requestPageSize = 10, + request_page_index: requestPageIndex = 0, + } = options; + + // Skip any that are before the page we're on + const numberToSkip = requestPageSize * requestPageIndex; + + // total - numberToSkip is the count of non-skipped ones, but return no more than a pageSize, and no less than 0 + const actualCountToReturn = Math.max(Math.min(total - numberToSkip, requestPageSize), 0); + + const policies = []; + for (let index = 0; index < actualCountToReturn; index++) { + const generator = new EndpointDocGenerator('seed'); + policies.push(generator.generatePolicyDatasource()); + } + const mock: GetPolicyListResponse = { + items: policies, + total, + page: requestPageIndex, + perPage: requestPageSize, + success: true, + }; + return mock; +}; diff --git a/x-pack/plugins/siem/public/management/pages/policy/view/policy_list.test.tsx b/x-pack/plugins/siem/public/management/pages/policy/view/policy_list.test.tsx new file mode 100644 index 00000000000000..a2901ab6bfe5ce --- /dev/null +++ b/x-pack/plugins/siem/public/management/pages/policy/view/policy_list.test.tsx @@ -0,0 +1,65 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; +import * as reactTestingLibrary from '@testing-library/react'; + +import { PolicyList } from './index'; +import { mockPolicyResultList } from '../store/policy_list/mock_policy_result_list'; +import { AppContextTestRender, createAppRootMockRenderer } from '../../../../common/mock/endpoint'; +import { AppAction } from '../../../../common/store/actions'; + +describe('when on the policies page', () => { + let render: () => ReturnType; + let history: AppContextTestRender['history']; + let store: AppContextTestRender['store']; + + beforeEach(() => { + const mockedContext = createAppRootMockRenderer(); + ({ history, store } = mockedContext); + render = () => mockedContext.render(); + }); + + it('should show a table', async () => { + const renderResult = render(); + const table = await renderResult.findByTestId('policyTable'); + expect(table).not.toBeNull(); + }); + + describe('when list data loads', () => { + let firstPolicyID: string; + beforeEach(() => { + reactTestingLibrary.act(() => { + history.push('/management/policy'); + reactTestingLibrary.act(() => { + const policyListData = mockPolicyResultList({ total: 3 }); + firstPolicyID = policyListData.items[0].id; + const action: AppAction = { + type: 'serverReturnedPolicyListData', + payload: { + policyItems: policyListData.items, + total: policyListData.total, + pageSize: policyListData.perPage, + pageIndex: policyListData.page, + }, + }; + store.dispatch(action); + }); + }); + }); + it('should display rows in the table', async () => { + const renderResult = render(); + const rows = await renderResult.findAllByRole('row'); + expect(rows).toHaveLength(4); + }); + it('should display policy name value as a link', async () => { + const renderResult = render(); + const policyNameLink = (await renderResult.findAllByTestId('policyNameLink'))[0]; + expect(policyNameLink).not.toBeNull(); + expect(policyNameLink.getAttribute('href')).toContain(`policy/${firstPolicyID}`); + }); + }); +}); diff --git a/x-pack/plugins/siem/public/management/pages/policy/view/policy_list.tsx b/x-pack/plugins/siem/public/management/pages/policy/view/policy_list.tsx index 3a8004aa2ec6d7..2826289dab8e23 100644 --- a/x-pack/plugins/siem/public/management/pages/policy/view/policy_list.tsx +++ b/x-pack/plugins/siem/public/management/pages/policy/view/policy_list.tsx @@ -4,20 +4,27 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { useCallback, useEffect, useMemo } from 'react'; -import { EuiBasicTable, EuiText, EuiTableFieldDataColumnType, EuiLink } from '@elastic/eui'; +import React, { useCallback, useEffect, useMemo, CSSProperties, useState } from 'react'; +import { + EuiBasicTable, + EuiText, + EuiFlexGroup, + EuiFlexItem, + EuiTableFieldDataColumnType, + EuiLink, + EuiPopover, + EuiContextMenuPanelProps, + EuiContextMenuItem, + EuiButtonIcon, + EuiContextMenuPanel, +} from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; import { useDispatch } from 'react-redux'; import { useLocation, useHistory } from 'react-router-dom'; -import { - selectApiError, - selectIsLoading, - selectPageIndex, - selectPageSize, - selectPolicyItems, - selectTotal, -} from '../store/policy_list/selectors'; +import { createStructuredSelector } from 'reselect'; +import { CreateStructuredSelector } from '../../../../common/store'; +import * as selectors from '../store/policy_list/selectors'; import { usePolicyListSelector } from './policy_hooks'; import { PolicyListAction } from '../store/policy_list'; import { useKibana } from '../../../../../../../../src/plugins/kibana_react/public'; @@ -27,11 +34,53 @@ import { LinkToApp } from '../../../../common/components/endpoint/link_to_app'; import { ManagementPageView } from '../../../components/management_page_view'; import { SpyRoute } from '../../../../common/utils/route/spy_routes'; import { getManagementUrl } from '../../../common/routing'; +import { FormattedDateAndTime } from '../../../../common/components/endpoint/formatted_date_time'; interface TableChangeCallbackArguments { page: { index: number; size: number }; } +interface PackageData { + name: string; + title: string; + version: string; +} + +const NO_WRAP_TRUNCATE_STYLE: CSSProperties = Object.freeze({ + overflow: 'hidden', + textOverflow: 'ellipsis', + whiteSpace: 'nowrap', +}); + +// eslint-disable-next-line react/display-name +export const TableRowActions = React.memo<{ items: EuiContextMenuPanelProps['items'] }>( + ({ items }) => { + const [isOpen, setIsOpen] = useState(false); + const handleCloseMenu = useCallback(() => setIsOpen(false), [setIsOpen]); + const handleToggleMenu = useCallback(() => setIsOpen(!isOpen), [isOpen]); + + return ( + + } + isOpen={isOpen} + closePopover={handleCloseMenu} + > + + + ); + } +); + const PolicyLink: React.FC<{ name: string; route: string; href: string }> = ({ name, route, @@ -40,24 +89,32 @@ const PolicyLink: React.FC<{ name: string; route: string; href: string }> = ({ const clickHandler = useNavigateByRouterEventHandler(route); return ( // eslint-disable-next-line @elastic/eui/href-or-on-click - + {name} ); }; +const selector = (createStructuredSelector as CreateStructuredSelector)(selectors); export const PolicyList = React.memo(() => { const { services, notifications } = useKibana(); const history = useHistory(); const location = useLocation(); const dispatch = useDispatch<(action: PolicyListAction) => void>(); - const policyItems = usePolicyListSelector(selectPolicyItems); - const pageIndex = usePolicyListSelector(selectPageIndex); - const pageSize = usePolicyListSelector(selectPageSize); - const totalItemCount = usePolicyListSelector(selectTotal); - const loading = usePolicyListSelector(selectIsLoading); - const apiError = usePolicyListSelector(selectApiError); + const { + selectPolicyItems: policyItems, + selectPageIndex: pageIndex, + selectPageSize: pageSize, + selectTotal: totalItemCount, + selectIsLoading: loading, + selectApiError: apiError, + } = usePolicyListSelector(selector); useEffect(() => { if (apiError) { @@ -94,58 +151,110 @@ export const PolicyList = React.memo(() => { defaultMessage: 'Policy Name', }), // eslint-disable-next-line react/display-name - render: (value: string, item: Immutable) => { + render: (name: string, item: Immutable) => { const routePath = getManagementUrl({ name: 'policyDetails', policyId: item.id, excludePrefix: true, }); const routeUrl = getManagementUrl({ name: 'policyDetails', policyId: item.id }); - return ; + return ( + + + + + + + + + + + ); }, - truncateText: true, }, { - field: 'revision', - name: i18n.translate('xpack.siem.endpoint.policyList.revisionField', { - defaultMessage: 'Revision', + field: 'created_by', + name: i18n.translate('xpack.siem.endpoint.policyList.createdBy', { + defaultMessage: 'Created By', }), - dataType: 'number', + truncateText: true, }, { - field: 'package', - name: i18n.translate('xpack.siem.endpoint.policyList.versionField', { - defaultMessage: 'Version', + field: 'created_at', + name: i18n.translate('xpack.siem.endpoint.policyList.createdAt', { + defaultMessage: 'Created Date', }), - render(pkg) { - return `${pkg.title} v${pkg.version}`; + render(createdAt: string) { + return ; }, }, { - field: 'description', - name: i18n.translate('xpack.siem.endpoint.policyList.descriptionField', { - defaultMessage: 'Description', + field: 'updated_by', + name: i18n.translate('xpack.siem.endpoint.policyList.updatedBy', { + defaultMessage: 'Last Updated By', }), truncateText: true, }, { - field: 'config_id', - name: i18n.translate('xpack.siem.endpoint.policyList.agentConfigField', { - defaultMessage: 'Agent Configuration', + field: 'updated_at', + name: i18n.translate('xpack.siem.endpoint.policyList.updatedAt', { + defaultMessage: 'Last Updated', }), - render(version: string) { - return ( - - {version} - - ); + render(updatedAt: string) { + return ; }, }, + { + field: 'package', + name: i18n.translate('xpack.siem.endpoint.policyList.versionFieldLabel', { + defaultMessage: 'Version', + }), + render(pkg: Immutable) { + return i18n.translate('xpack.siem.endpoint.policyList.versionField', { + defaultMessage: '{title} v{version}', + values: { + title: pkg.title, + version: pkg.version, + }, + }); + }, + }, + { + field: '', + name: 'Actions', + actions: [ + { + // eslint-disable-next-line react/display-name + render: (item: Immutable) => { + return ( + + + + + , + ]} + /> + ); + }, + }, + ], + }, ], [services.application] ); @@ -174,6 +283,7 @@ export const PolicyList = React.memo(() => { pagination={paginationSetup} onChange={handleTableChange} data-test-subj="policyTable" + hasActions={false} /> From 7d0ffb53bcb9d302e9f1f675b70d36b235f78e8d Mon Sep 17 00:00:00 2001 From: Sandra Gonzales Date: Tue, 2 Jun 2020 08:32:24 -0400 Subject: [PATCH 18/24] install default packages in parallel (#67893) --- .../server/services/epm/packages/install.ts | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/x-pack/plugins/ingest_manager/server/services/epm/packages/install.ts b/x-pack/plugins/ingest_manager/server/services/epm/packages/install.ts index 7c0d5d571f6a5b..736711f9152e9a 100644 --- a/x-pack/plugins/ingest_manager/server/services/epm/packages/install.ts +++ b/x-pack/plugins/ingest_manager/server/services/epm/packages/install.ts @@ -52,22 +52,22 @@ export async function ensureInstalledDefaultPackages( const installations = []; for (const pkgName in DefaultPackages) { if (!DefaultPackages.hasOwnProperty(pkgName)) continue; - const installation = await ensureInstalledPackage({ + const installation = ensureInstalledPackage({ savedObjectsClient, pkgName, callCluster, }); - if (installation) installations.push(installation); + installations.push(installation); } - return installations; + return Promise.all(installations); } export async function ensureInstalledPackage(options: { savedObjectsClient: SavedObjectsClientContract; pkgName: string; callCluster: CallESAsCurrentUser; -}): Promise { +}): Promise { const { savedObjectsClient, pkgName, callCluster } = options; const installedPackage = await getInstallation({ savedObjectsClient, pkgName }); if (installedPackage) { @@ -79,7 +79,9 @@ export async function ensureInstalledPackage(options: { pkgName, callCluster, }); - return await getInstallation({ savedObjectsClient, pkgName }); + const installation = await getInstallation({ savedObjectsClient, pkgName }); + if (!installation) throw new Error(`could not get installation ${pkgName}`); + return installation; } export async function installPackage(options: { From a091124fab89573e6744582a09d4eea19e4c28f7 Mon Sep 17 00:00:00 2001 From: Pierre Gayvallet Date: Tue, 2 Jun 2020 15:15:21 +0200 Subject: [PATCH 19/24] Move application.applications$ to public contract (#67463) * expose applications$ on public contract * review comments --- ...e-public.applicationstart.applications_.md | 18 ++++++ ...ana-plugin-core-public.applicationstart.md | 1 + ...ana-plugin-core-public.legacyapp.appurl.md | 11 ++++ ...-public.legacyapp.disablesuburltracking.md | 11 ++++ ...-core-public.legacyapp.linktolastsuburl.md | 11 ++++ .../kibana-plugin-core-public.legacyapp.md | 22 +++++++ ...plugin-core-public.legacyapp.suburlbase.md | 11 ++++ .../core/public/kibana-plugin-core-public.md | 3 + ...kibana-plugin-core-public.publicappinfo.md | 15 +++++ ...-plugin-core-public.publiclegacyappinfo.md | 15 +++++ .../application/application_service.mock.ts | 7 ++- .../application/application_service.test.ts | 15 ++++- .../application/application_service.tsx | 7 ++- src/core/public/application/index.ts | 4 +- src/core/public/application/types.ts | 42 +++++++++----- src/core/public/application/utils.test.ts | 57 ++++++++++++++++++- src/core/public/application/utils.ts | 18 +++++- src/core/public/chrome/chrome_service.test.ts | 7 ++- .../chrome/nav_links/to_nav_link.test.ts | 9 +-- .../public/chrome/nav_links/to_nav_link.ts | 9 ++- src/core/public/index.ts | 3 + src/core/public/legacy/legacy_service.ts | 1 + src/core/public/plugins/plugin_context.ts | 1 + src/core/public/public.api.md | 23 ++++++++ 24 files changed, 286 insertions(+), 35 deletions(-) create mode 100644 docs/development/core/public/kibana-plugin-core-public.applicationstart.applications_.md create mode 100644 docs/development/core/public/kibana-plugin-core-public.legacyapp.appurl.md create mode 100644 docs/development/core/public/kibana-plugin-core-public.legacyapp.disablesuburltracking.md create mode 100644 docs/development/core/public/kibana-plugin-core-public.legacyapp.linktolastsuburl.md create mode 100644 docs/development/core/public/kibana-plugin-core-public.legacyapp.md create mode 100644 docs/development/core/public/kibana-plugin-core-public.legacyapp.suburlbase.md create mode 100644 docs/development/core/public/kibana-plugin-core-public.publicappinfo.md create mode 100644 docs/development/core/public/kibana-plugin-core-public.publiclegacyappinfo.md diff --git a/docs/development/core/public/kibana-plugin-core-public.applicationstart.applications_.md b/docs/development/core/public/kibana-plugin-core-public.applicationstart.applications_.md new file mode 100644 index 00000000000000..d428faa500faf0 --- /dev/null +++ b/docs/development/core/public/kibana-plugin-core-public.applicationstart.applications_.md @@ -0,0 +1,18 @@ + + +[Home](./index.md) > [kibana-plugin-core-public](./kibana-plugin-core-public.md) > [ApplicationStart](./kibana-plugin-core-public.applicationstart.md) > [applications$](./kibana-plugin-core-public.applicationstart.applications_.md) + +## ApplicationStart.applications$ property + +Observable emitting the list of currently registered apps and their associated status. + +Signature: + +```typescript +applications$: Observable>; +``` + +## Remarks + +Applications disabled by [Capabilities](./kibana-plugin-core-public.capabilities.md) will not be present in the map. Applications manually disabled from the client-side using an [application updater](./kibana-plugin-core-public.appupdater.md) are present, with their status properly set as `inaccessible`. + diff --git a/docs/development/core/public/kibana-plugin-core-public.applicationstart.md b/docs/development/core/public/kibana-plugin-core-public.applicationstart.md index 6f45bab3ebd2d0..896de2de32dd51 100644 --- a/docs/development/core/public/kibana-plugin-core-public.applicationstart.md +++ b/docs/development/core/public/kibana-plugin-core-public.applicationstart.md @@ -15,6 +15,7 @@ export interface ApplicationStart | Property | Type | Description | | --- | --- | --- | +| [applications$](./kibana-plugin-core-public.applicationstart.applications_.md) | Observable<ReadonlyMap<string, PublicAppInfo | PublicLegacyAppInfo>> | Observable emitting the list of currently registered apps and their associated status. | | [capabilities](./kibana-plugin-core-public.applicationstart.capabilities.md) | RecursiveReadonly<Capabilities> | Gets the read-only capabilities. | | [currentAppId$](./kibana-plugin-core-public.applicationstart.currentappid_.md) | Observable<string | undefined> | An observable that emits the current application id and each subsequent id update. | diff --git a/docs/development/core/public/kibana-plugin-core-public.legacyapp.appurl.md b/docs/development/core/public/kibana-plugin-core-public.legacyapp.appurl.md new file mode 100644 index 00000000000000..292bf299628397 --- /dev/null +++ b/docs/development/core/public/kibana-plugin-core-public.legacyapp.appurl.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-core-public](./kibana-plugin-core-public.md) > [LegacyApp](./kibana-plugin-core-public.legacyapp.md) > [appUrl](./kibana-plugin-core-public.legacyapp.appurl.md) + +## LegacyApp.appUrl property + +Signature: + +```typescript +appUrl: string; +``` diff --git a/docs/development/core/public/kibana-plugin-core-public.legacyapp.disablesuburltracking.md b/docs/development/core/public/kibana-plugin-core-public.legacyapp.disablesuburltracking.md new file mode 100644 index 00000000000000..af4d0eb7969d3d --- /dev/null +++ b/docs/development/core/public/kibana-plugin-core-public.legacyapp.disablesuburltracking.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-core-public](./kibana-plugin-core-public.md) > [LegacyApp](./kibana-plugin-core-public.legacyapp.md) > [disableSubUrlTracking](./kibana-plugin-core-public.legacyapp.disablesuburltracking.md) + +## LegacyApp.disableSubUrlTracking property + +Signature: + +```typescript +disableSubUrlTracking?: boolean; +``` diff --git a/docs/development/core/public/kibana-plugin-core-public.legacyapp.linktolastsuburl.md b/docs/development/core/public/kibana-plugin-core-public.legacyapp.linktolastsuburl.md new file mode 100644 index 00000000000000..fa1314b74fd83f --- /dev/null +++ b/docs/development/core/public/kibana-plugin-core-public.legacyapp.linktolastsuburl.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-core-public](./kibana-plugin-core-public.md) > [LegacyApp](./kibana-plugin-core-public.legacyapp.md) > [linkToLastSubUrl](./kibana-plugin-core-public.legacyapp.linktolastsuburl.md) + +## LegacyApp.linkToLastSubUrl property + +Signature: + +```typescript +linkToLastSubUrl?: boolean; +``` diff --git a/docs/development/core/public/kibana-plugin-core-public.legacyapp.md b/docs/development/core/public/kibana-plugin-core-public.legacyapp.md new file mode 100644 index 00000000000000..06533aaa991708 --- /dev/null +++ b/docs/development/core/public/kibana-plugin-core-public.legacyapp.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [kibana-plugin-core-public](./kibana-plugin-core-public.md) > [LegacyApp](./kibana-plugin-core-public.legacyapp.md) + +## LegacyApp interface + + +Signature: + +```typescript +export interface LegacyApp extends AppBase +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [appUrl](./kibana-plugin-core-public.legacyapp.appurl.md) | string | | +| [disableSubUrlTracking](./kibana-plugin-core-public.legacyapp.disablesuburltracking.md) | boolean | | +| [linkToLastSubUrl](./kibana-plugin-core-public.legacyapp.linktolastsuburl.md) | boolean | | +| [subUrlBase](./kibana-plugin-core-public.legacyapp.suburlbase.md) | string | | + diff --git a/docs/development/core/public/kibana-plugin-core-public.legacyapp.suburlbase.md b/docs/development/core/public/kibana-plugin-core-public.legacyapp.suburlbase.md new file mode 100644 index 00000000000000..44a1e52ccd2447 --- /dev/null +++ b/docs/development/core/public/kibana-plugin-core-public.legacyapp.suburlbase.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-core-public](./kibana-plugin-core-public.md) > [LegacyApp](./kibana-plugin-core-public.legacyapp.md) > [subUrlBase](./kibana-plugin-core-public.legacyapp.suburlbase.md) + +## LegacyApp.subUrlBase property + +Signature: + +```typescript +subUrlBase?: string; +``` diff --git a/docs/development/core/public/kibana-plugin-core-public.md b/docs/development/core/public/kibana-plugin-core-public.md index b2524ec48c757d..9e4afe0f5133cd 100644 --- a/docs/development/core/public/kibana-plugin-core-public.md +++ b/docs/development/core/public/kibana-plugin-core-public.md @@ -90,6 +90,7 @@ The plugin integrates with the core system via lifecycle events: `setup` | [IHttpResponseInterceptorOverrides](./kibana-plugin-core-public.ihttpresponseinterceptoroverrides.md) | Properties that can be returned by HttpInterceptor.request to override the response. | | [ImageValidation](./kibana-plugin-core-public.imagevalidation.md) | | | [IUiSettingsClient](./kibana-plugin-core-public.iuisettingsclient.md) | Client-side client that provides access to the advanced settings stored in elasticsearch. The settings provide control over the behavior of the Kibana application. For example, a user can specify how to display numeric or date fields. Users can adjust the settings via Management UI. [IUiSettingsClient](./kibana-plugin-core-public.iuisettingsclient.md) | +| [LegacyApp](./kibana-plugin-core-public.legacyapp.md) | | | [LegacyCoreSetup](./kibana-plugin-core-public.legacycoresetup.md) | Setup interface exposed to the legacy platform via the ui/new_platform module. | | [LegacyCoreStart](./kibana-plugin-core-public.legacycorestart.md) | Start interface exposed to the legacy platform via the ui/new_platform module. | | [LegacyNavLink](./kibana-plugin-core-public.legacynavlink.md) | | @@ -162,6 +163,8 @@ The plugin integrates with the core system via lifecycle events: `setup` | [NavType](./kibana-plugin-core-public.navtype.md) | | | [PluginInitializer](./kibana-plugin-core-public.plugininitializer.md) | The plugin export at the root of a plugin's public directory should conform to this interface. | | [PluginOpaqueId](./kibana-plugin-core-public.pluginopaqueid.md) | | +| [PublicAppInfo](./kibana-plugin-core-public.publicappinfo.md) | Public information about a registered [application](./kibana-plugin-core-public.app.md) | +| [PublicLegacyAppInfo](./kibana-plugin-core-public.publiclegacyappinfo.md) | Information about a registered [legacy application](./kibana-plugin-core-public.legacyapp.md) | | [PublicUiSettingsParams](./kibana-plugin-core-public.publicuisettingsparams.md) | A sub-set of [UiSettingsParams](./kibana-plugin-core-public.uisettingsparams.md) exposed to the client-side. | | [RecursiveReadonly](./kibana-plugin-core-public.recursivereadonly.md) | | | [SavedObjectAttribute](./kibana-plugin-core-public.savedobjectattribute.md) | Type definition for a Saved Object attribute value | diff --git a/docs/development/core/public/kibana-plugin-core-public.publicappinfo.md b/docs/development/core/public/kibana-plugin-core-public.publicappinfo.md new file mode 100644 index 00000000000000..c70f3a97a8882f --- /dev/null +++ b/docs/development/core/public/kibana-plugin-core-public.publicappinfo.md @@ -0,0 +1,15 @@ + + +[Home](./index.md) > [kibana-plugin-core-public](./kibana-plugin-core-public.md) > [PublicAppInfo](./kibana-plugin-core-public.publicappinfo.md) + +## PublicAppInfo type + +Public information about a registered [application](./kibana-plugin-core-public.app.md) + +Signature: + +```typescript +export declare type PublicAppInfo = Omit & { + legacy: false; +}; +``` diff --git a/docs/development/core/public/kibana-plugin-core-public.publiclegacyappinfo.md b/docs/development/core/public/kibana-plugin-core-public.publiclegacyappinfo.md new file mode 100644 index 00000000000000..cc3e9de3193cb8 --- /dev/null +++ b/docs/development/core/public/kibana-plugin-core-public.publiclegacyappinfo.md @@ -0,0 +1,15 @@ + + +[Home](./index.md) > [kibana-plugin-core-public](./kibana-plugin-core-public.md) > [PublicLegacyAppInfo](./kibana-plugin-core-public.publiclegacyappinfo.md) + +## PublicLegacyAppInfo type + +Information about a registered [legacy application](./kibana-plugin-core-public.legacyapp.md) + +Signature: + +```typescript +export declare type PublicLegacyAppInfo = Omit & { + legacy: true; +}; +``` diff --git a/src/core/public/application/application_service.mock.ts b/src/core/public/application/application_service.mock.ts index 24c0e66359afa5..300b09e17d15df 100644 --- a/src/core/public/application/application_service.mock.ts +++ b/src/core/public/application/application_service.mock.ts @@ -25,8 +25,8 @@ import { InternalApplicationStart, ApplicationStart, InternalApplicationSetup, - App, - LegacyApp, + PublicAppInfo, + PublicLegacyAppInfo, } from './types'; import { ApplicationServiceContract } from './test_types'; @@ -47,6 +47,7 @@ const createStartContractMock = (): jest.Mocked => { const currentAppId$ = new Subject(); return { + applications$: new BehaviorSubject>(new Map()), currentAppId$: currentAppId$.asObservable(), capabilities: capabilitiesServiceMock.createStartContract().capabilities, navigateToApp: jest.fn(), @@ -60,7 +61,7 @@ const createInternalStartContractMock = (): jest.Mocked(); return { - applications$: new BehaviorSubject>(new Map()), + applications$: new BehaviorSubject>(new Map()), capabilities: capabilitiesServiceMock.createStartContract().capabilities, currentAppId$: currentAppId$.asObservable(), getComponent: jest.fn(), diff --git a/src/core/public/application/application_service.test.ts b/src/core/public/application/application_service.test.ts index b65a8581e5b588..400d1881a5af86 100644 --- a/src/core/public/application/application_service.test.ts +++ b/src/core/public/application/application_service.test.ts @@ -34,7 +34,15 @@ import { httpServiceMock } from '../http/http_service.mock'; import { overlayServiceMock } from '../overlays/overlay_service.mock'; import { MockLifecycle } from './test_types'; import { ApplicationService } from './application_service'; -import { App, AppNavLinkStatus, AppStatus, AppUpdater, LegacyApp } from './types'; +import { + App, + PublicAppInfo, + AppNavLinkStatus, + AppStatus, + AppUpdater, + LegacyApp, + PublicLegacyAppInfo, +} from './types'; import { act } from 'react-dom/test-utils'; const createApp = (props: Partial): App => { @@ -366,7 +374,10 @@ describe('#setup()', () => { setup.registerAppUpdater(statusUpdater); const start = await service.start(startDeps); - let latestValue: ReadonlyMap = new Map(); + let latestValue: ReadonlyMap = new Map< + string, + PublicAppInfo | PublicLegacyAppInfo + >(); start.applications$.subscribe((apps) => { latestValue = apps; }); diff --git a/src/core/public/application/application_service.tsx b/src/core/public/application/application_service.tsx index b52b4984fb5e12..2224f72e2bd913 100644 --- a/src/core/public/application/application_service.tsx +++ b/src/core/public/application/application_service.tsx @@ -46,7 +46,7 @@ import { Mounter, } from './types'; import { getLeaveAction, isConfirmAction } from './application_leave'; -import { appendAppPath, parseAppUrl, relativeToAbsolute } from './utils'; +import { appendAppPath, parseAppUrl, relativeToAbsolute, getAppInfo } from './utils'; interface SetupDeps { context: ContextSetup; @@ -291,7 +291,10 @@ export class ApplicationService { }; return { - applications$, + applications$: applications$.pipe( + map((apps) => new Map([...apps.entries()].map(([id, app]) => [id, getAppInfo(app)]))), + shareReplay(1) + ), capabilities, currentAppId$: this.currentAppId$.pipe( filter((appId) => appId !== undefined), diff --git a/src/core/public/application/index.ts b/src/core/public/application/index.ts index ec10d2bc22871d..d51a4c0d69d42b 100644 --- a/src/core/public/application/index.ts +++ b/src/core/public/application/index.ts @@ -39,7 +39,9 @@ export { AppLeaveAction, AppLeaveDefaultAction, AppLeaveConfirmAction, + LegacyApp, + PublicAppInfo, + PublicLegacyAppInfo, // Internal types InternalApplicationStart, - LegacyApp, } from './types'; diff --git a/src/core/public/application/types.ts b/src/core/public/application/types.ts index c07d929fc5ceaf..2269fd0a4ca480 100644 --- a/src/core/public/application/types.ts +++ b/src/core/public/application/types.ts @@ -235,7 +235,7 @@ export interface App extends AppBase { appRoute?: string; } -/** @internal */ +/** @public */ export interface LegacyApp extends AppBase { appUrl: string; subUrlBase?: string; @@ -243,6 +243,24 @@ export interface LegacyApp extends AppBase { disableSubUrlTracking?: boolean; } +/** + * Public information about a registered {@link App | application} + * + * @public + */ +export type PublicAppInfo = Omit & { + legacy: false; +}; + +/** + * Information about a registered {@link LegacyApp | legacy application} + * + * @public + */ +export type PublicLegacyAppInfo = Omit & { + legacy: true; +}; + /** * A mount function called when the user navigates to this app's route. * @@ -649,6 +667,15 @@ export interface ApplicationStart { */ capabilities: RecursiveReadonly; + /** + * Observable emitting the list of currently registered apps and their associated status. + * + * @remarks + * Applications disabled by {@link Capabilities} will not be present in the map. Applications manually disabled from + * the client-side using an {@link AppUpdater | application updater} are present, with their status properly set as `inaccessible`. + */ + applications$: Observable>; + /** * Navigate to a given app * @@ -721,18 +748,7 @@ export interface ApplicationStart { } /** @internal */ -export interface InternalApplicationStart - extends Pick< - ApplicationStart, - 'capabilities' | 'navigateToApp' | 'navigateToUrl' | 'getUrlForApp' | 'currentAppId$' - > { - /** - * Apps available based on the current capabilities. - * Should be used to show navigation links and make routing decisions. - * Applications manually disabled from the client-side using {@link AppUpdater} - */ - applications$: Observable>; - +export interface InternalApplicationStart extends Omit { /** * Register a context provider for application mounting. Will only be available to applications that depend on the * plugin that registered this context. Deprecated, use {@link CoreSetup.getStartServices}. diff --git a/src/core/public/application/utils.test.ts b/src/core/public/application/utils.test.ts index a86a1206fc983a..b41945aa436829 100644 --- a/src/core/public/application/utils.test.ts +++ b/src/core/public/application/utils.test.ts @@ -17,7 +17,8 @@ * under the License. */ -import { LegacyApp, App } from './types'; +import { of } from 'rxjs'; +import { LegacyApp, App, AppStatus, AppNavLinkStatus } from './types'; import { BasePath } from '../http/base_path'; import { removeSlashes, @@ -25,6 +26,7 @@ import { isLegacyApp, relativeToAbsolute, parseAppUrl, + getAppInfo, } from './utils'; describe('removeSlashes', () => { @@ -459,3 +461,56 @@ describe('parseAppUrl', () => { }); }); }); + +describe('getAppInfo', () => { + const createApp = (props: Partial = {}): App => ({ + mount: () => () => undefined, + updater$: of(() => undefined), + id: 'some-id', + title: 'some-title', + status: AppStatus.accessible, + navLinkStatus: AppNavLinkStatus.default, + appRoute: `/app/some-id`, + legacy: false, + ...props, + }); + + const createLegacyApp = (props: Partial = {}): LegacyApp => ({ + appUrl: '/my-app-url', + updater$: of(() => undefined), + id: 'some-id', + title: 'some-title', + status: AppStatus.accessible, + navLinkStatus: AppNavLinkStatus.default, + legacy: true, + ...props, + }); + + it('converts an application and remove sensitive properties', () => { + const app = createApp(); + const info = getAppInfo(app); + + expect(info).toEqual({ + id: 'some-id', + title: 'some-title', + status: AppStatus.accessible, + navLinkStatus: AppNavLinkStatus.default, + appRoute: `/app/some-id`, + legacy: false, + }); + }); + + it('converts a legacy application and remove sensitive properties', () => { + const app = createLegacyApp(); + const info = getAppInfo(app); + + expect(info).toEqual({ + appUrl: '/my-app-url', + id: 'some-id', + title: 'some-title', + status: AppStatus.accessible, + navLinkStatus: AppNavLinkStatus.default, + legacy: true, + }); + }); +}); diff --git a/src/core/public/application/utils.ts b/src/core/public/application/utils.ts index 8987a9402f2db2..1abd7105487453 100644 --- a/src/core/public/application/utils.ts +++ b/src/core/public/application/utils.ts @@ -18,7 +18,7 @@ */ import { IBasePath } from '../http'; -import { App, LegacyApp } from './types'; +import { App, LegacyApp, PublicAppInfo, PublicLegacyAppInfo } from './types'; export interface AppUrlInfo { app: string; @@ -119,3 +119,19 @@ const removeBasePath = (url: string, basePath: IBasePath, origin: string): strin } return basePath.remove(url); }; + +export function getAppInfo(app: App | LegacyApp): PublicAppInfo | PublicLegacyAppInfo { + if (isLegacyApp(app)) { + const { updater$, ...infos } = app; + return { + ...infos, + legacy: true, + }; + } else { + const { updater$, mount, ...infos } = app; + return { + ...infos, + legacy: false, + }; + } +} diff --git a/src/core/public/chrome/chrome_service.test.ts b/src/core/public/chrome/chrome_service.test.ts index 0bc305ed9e28c9..e39733cc10de70 100644 --- a/src/core/public/chrome/chrome_service.test.ts +++ b/src/core/public/chrome/chrome_service.test.ts @@ -21,7 +21,7 @@ import { shallow } from 'enzyme'; import React from 'react'; import * as Rx from 'rxjs'; import { take, toArray } from 'rxjs/operators'; -import { App } from '../application'; +import { App, PublicAppInfo } from '../application'; import { applicationServiceMock } from '../application/application_service.mock'; import { docLinksServiceMock } from '../doc_links/doc_links_service.mock'; import { httpServiceMock } from '../http/http_service.mock'; @@ -29,6 +29,7 @@ import { injectedMetadataServiceMock } from '../injected_metadata/injected_metad import { notificationServiceMock } from '../notifications/notifications_service.mock'; import { uiSettingsServiceMock } from '../ui_settings/ui_settings_service.mock'; import { ChromeService } from './chrome_service'; +import { getAppInfo } from '../application/utils'; class FakeApp implements App { public title = `${this.id} App`; @@ -55,8 +56,8 @@ function defaultStartDeps(availableApps?: App[]) { }; if (availableApps) { - deps.application.applications$ = new Rx.BehaviorSubject>( - new Map(availableApps.map((app) => [app.id, app])) + deps.application.applications$ = new Rx.BehaviorSubject>( + new Map(availableApps.map((app) => [app.id, getAppInfo(app) as PublicAppInfo])) ); } diff --git a/src/core/public/chrome/nav_links/to_nav_link.test.ts b/src/core/public/chrome/nav_links/to_nav_link.test.ts index 4c319873af804e..ba04dbed49cd44 100644 --- a/src/core/public/chrome/nav_links/to_nav_link.test.ts +++ b/src/core/public/chrome/nav_links/to_nav_link.test.ts @@ -17,15 +17,12 @@ * under the License. */ -import { App, AppMount, AppNavLinkStatus, AppStatus, LegacyApp } from '../../application'; +import { PublicAppInfo, AppNavLinkStatus, AppStatus, PublicLegacyAppInfo } from '../../application'; import { toNavLink } from './to_nav_link'; import { httpServiceMock } from '../../mocks'; -function mount() {} - -const app = (props: Partial = {}): App => ({ - mount: (mount as unknown) as AppMount, +const app = (props: Partial = {}): PublicAppInfo => ({ id: 'some-id', title: 'some-title', status: AppStatus.accessible, @@ -35,7 +32,7 @@ const app = (props: Partial = {}): App => ({ ...props, }); -const legacyApp = (props: Partial = {}): LegacyApp => ({ +const legacyApp = (props: Partial = {}): PublicLegacyAppInfo => ({ appUrl: '/my-app-url', id: 'some-id', title: 'some-title', diff --git a/src/core/public/chrome/nav_links/to_nav_link.ts b/src/core/public/chrome/nav_links/to_nav_link.ts index 24744fe53c82cc..b8f97f9ddc0053 100644 --- a/src/core/public/chrome/nav_links/to_nav_link.ts +++ b/src/core/public/chrome/nav_links/to_nav_link.ts @@ -17,12 +17,15 @@ * under the License. */ -import { App, AppNavLinkStatus, AppStatus, LegacyApp } from '../../application'; +import { PublicAppInfo, AppNavLinkStatus, AppStatus, PublicLegacyAppInfo } from '../../application'; import { IBasePath } from '../../http'; import { NavLinkWrapper } from './nav_link'; import { appendAppPath } from '../../application/utils'; -export function toNavLink(app: App | LegacyApp, basePath: IBasePath): NavLinkWrapper { +export function toNavLink( + app: PublicAppInfo | PublicLegacyAppInfo, + basePath: IBasePath +): NavLinkWrapper { const useAppStatus = app.navLinkStatus === AppNavLinkStatus.default; const relativeBaseUrl = isLegacyApp(app) ? basePath.prepend(app.appUrl) @@ -63,6 +66,6 @@ export function relativeToAbsolute(url: string) { return a.href; } -function isLegacyApp(app: App | LegacyApp): app is LegacyApp { +function isLegacyApp(app: PublicAppInfo | PublicLegacyAppInfo): app is PublicLegacyAppInfo { return app.legacy === true; } diff --git a/src/core/public/index.ts b/src/core/public/index.ts index 3698fdcfe95129..aa037329b1b6ea 100644 --- a/src/core/public/index.ts +++ b/src/core/public/index.ts @@ -104,6 +104,7 @@ export { ApplicationSetup, ApplicationStart, App, + PublicAppInfo, AppBase, AppMount, AppMountDeprecated, @@ -120,6 +121,8 @@ export { AppUpdatableFields, AppUpdater, ScopedHistory, + LegacyApp, + PublicLegacyAppInfo, } from './application'; export { diff --git a/src/core/public/legacy/legacy_service.ts b/src/core/public/legacy/legacy_service.ts index 810416cdbfe16d..d77676b350f934 100644 --- a/src/core/public/legacy/legacy_service.ts +++ b/src/core/public/legacy/legacy_service.ts @@ -131,6 +131,7 @@ export class LegacyPlatformService { const legacyCore: LegacyCoreStart = { ...core, application: { + applications$: core.application.applications$, currentAppId$: core.application.currentAppId$, capabilities: core.application.capabilities, getUrlForApp: core.application.getUrlForApp, diff --git a/src/core/public/plugins/plugin_context.ts b/src/core/public/plugins/plugin_context.ts index c688373630a07f..65c6b6ce4edba6 100644 --- a/src/core/public/plugins/plugin_context.ts +++ b/src/core/public/plugins/plugin_context.ts @@ -135,6 +135,7 @@ export function createPluginStartContext< ): CoreStart { return { application: { + applications$: deps.application.applications$, currentAppId$: deps.application.currentAppId$, capabilities: deps.application.capabilities, navigateToApp: deps.application.navigateToApp, diff --git a/src/core/public/public.api.md b/src/core/public/public.api.md index 90c5dbb5f6558c..bae0f9a2281cf1 100644 --- a/src/core/public/public.api.md +++ b/src/core/public/public.api.md @@ -106,6 +106,7 @@ export interface ApplicationSetup { // @public (undocumented) export interface ApplicationStart { + applications$: Observable>; capabilities: RecursiveReadonly; currentAppId$: Observable; getUrlForApp(appId: string, options?: { @@ -857,6 +858,18 @@ export interface IUiSettingsClient { set: (key: string, value: any) => Promise; } +// @public (undocumented) +export interface LegacyApp extends AppBase { + // (undocumented) + appUrl: string; + // (undocumented) + disableSubUrlTracking?: boolean; + // (undocumented) + linkToLastSubUrl?: boolean; + // (undocumented) + subUrlBase?: string; +} + // @public @deprecated export interface LegacyCoreSetup extends CoreSetup { // Warning: (ae-forgotten-export) The symbol "InjectedMetadataSetup" needs to be exported by the entry point index.d.ts @@ -993,6 +1006,16 @@ export interface PluginInitializerContext // @public (undocumented) export type PluginOpaqueId = symbol; +// @public +export type PublicAppInfo = Omit & { + legacy: false; +}; + +// @public +export type PublicLegacyAppInfo = Omit & { + legacy: true; +}; + // @public export type PublicUiSettingsParams = Omit; From d550131b9e4fdff8e43b51838cd70fa96e477de7 Mon Sep 17 00:00:00 2001 From: Nathan Reese Date: Tue, 2 Jun 2020 08:38:25 -0600 Subject: [PATCH 20/24] [Maps] fix mapbox glyphs error when EMS access is turned off (#67427) * [Maps] fix mapbox glyphs error when EMS access is turned off * santize range parameter * add api test * clean up mapbox view * add jest test for getGlyphsUrl * add license file * remove unneeded font files Co-authored-by: Elastic Machine --- src/dev/precommit_hook/casing_check_config.js | 2 + x-pack/plugins/maps/common/constants.ts | 1 + .../connected_components/map/mb/view.js | 5 +- x-pack/plugins/maps/public/meta.js | 23 ++++--- x-pack/plugins/maps/public/meta.test.js | 50 +++++++++++++- .../maps/server/fonts/open_sans/0-255.pbf | Bin 0 -> 74696 bytes .../maps/server/fonts/open_sans/1024-1279.pbf | Bin 0 -> 122545 bytes .../maps/server/fonts/open_sans/256-511.pbf | Bin 0 -> 66481 bytes .../maps/server/fonts/open_sans/768-1023.pbf | Bin 0 -> 33767 bytes .../maps/server/fonts/open_sans/8192-8447.pbf | Bin 0 -> 9663 bytes .../maps/server/fonts/open_sans/license.txt | 53 +++++++++++++++ x-pack/plugins/maps/server/routes.js | 62 ++++++++++++++---- .../api_integration/apis/maps/fonts_api.js | 21 ++++++ .../test/api_integration/apis/maps/index.js | 1 + 14 files changed, 189 insertions(+), 29 deletions(-) create mode 100644 x-pack/plugins/maps/server/fonts/open_sans/0-255.pbf create mode 100644 x-pack/plugins/maps/server/fonts/open_sans/1024-1279.pbf create mode 100644 x-pack/plugins/maps/server/fonts/open_sans/256-511.pbf create mode 100644 x-pack/plugins/maps/server/fonts/open_sans/768-1023.pbf create mode 100644 x-pack/plugins/maps/server/fonts/open_sans/8192-8447.pbf create mode 100644 x-pack/plugins/maps/server/fonts/open_sans/license.txt create mode 100644 x-pack/test/api_integration/apis/maps/fonts_api.js diff --git a/src/dev/precommit_hook/casing_check_config.js b/src/dev/precommit_hook/casing_check_config.js index 2eee3b2c53bd3a..d968a365e7bb92 100644 --- a/src/dev/precommit_hook/casing_check_config.js +++ b/src/dev/precommit_hook/casing_check_config.js @@ -64,6 +64,8 @@ export const IGNORE_FILE_GLOBS = [ 'x-pack/plugins/apm/public/**/*', 'x-pack/plugins/apm/scripts/**/*', 'x-pack/plugins/apm/e2e/**/*', + + 'x-pack/plugins/maps/server/fonts/**/*', ]; /** diff --git a/x-pack/plugins/maps/common/constants.ts b/x-pack/plugins/maps/common/constants.ts index 8fa44c512df4b5..d357f11f5e3e1e 100644 --- a/x-pack/plugins/maps/common/constants.ts +++ b/x-pack/plugins/maps/common/constants.ts @@ -30,6 +30,7 @@ export const TELEMETRY_TYPE = 'maps-telemetry'; export const MAP_APP_PATH = `app/${APP_ID}`; export const GIS_API_PATH = `api/${APP_ID}`; export const INDEX_SETTINGS_API_PATH = `${GIS_API_PATH}/indexSettings`; +export const FONTS_API_PATH = `${GIS_API_PATH}/fonts`; export const MAP_BASE_URL = `/${MAP_APP_PATH}#/${MAP_SAVED_OBJECT_TYPE}`; diff --git a/x-pack/plugins/maps/public/connected_components/map/mb/view.js b/x-pack/plugins/maps/public/connected_components/map/mb/view.js index c4b28e33747ee0..42235bfd5442ee 100644 --- a/x-pack/plugins/maps/public/connected_components/map/mb/view.js +++ b/x-pack/plugins/maps/public/connected_components/map/mb/view.js @@ -118,11 +118,8 @@ export class MBMapContainer extends React.Component { version: 8, sources: {}, layers: [], + glyphs: getGlyphUrl(), }; - const glyphUrl = getGlyphUrl(); - if (glyphUrl) { - mbStyle.glyphs = glyphUrl; - } const options = { attributionControl: false, diff --git a/x-pack/plugins/maps/public/meta.js b/x-pack/plugins/maps/public/meta.js index 3ffd0578796cec..46c5e5cda36175 100644 --- a/x-pack/plugins/maps/public/meta.js +++ b/x-pack/plugins/maps/public/meta.js @@ -5,15 +5,7 @@ */ import { - GIS_API_PATH, - EMS_FILES_CATALOGUE_PATH, - EMS_TILES_CATALOGUE_PATH, - EMS_GLYPHS_PATH, - EMS_APP_NAME, -} from '../common/constants'; -import { i18n } from '@kbn/i18n'; -import { EMSClient } from '@elastic/ems-client'; -import { + getHttp, getLicenseId, getIsEmsEnabled, getRegionmapLayers, @@ -25,6 +17,17 @@ import { getProxyElasticMapsServiceInMaps, getKibanaVersion, } from './kibana_services'; +import { + GIS_API_PATH, + EMS_FILES_CATALOGUE_PATH, + EMS_TILES_CATALOGUE_PATH, + EMS_GLYPHS_PATH, + EMS_APP_NAME, + FONTS_API_PATH, +} from '../common/constants'; +import { i18n } from '@kbn/i18n'; +import { EMSClient } from '@elastic/ems-client'; + import fetch from 'node-fetch'; const GIS_API_RELATIVE = `../${GIS_API_PATH}`; @@ -95,7 +98,7 @@ export function getEMSClient() { export function getGlyphUrl() { if (!getIsEmsEnabled()) { - return ''; + return getHttp().basePath.prepend(`/${FONTS_API_PATH}/{fontstack}/{range}`); } return getProxyElasticMapsServiceInMaps() ? relativeToAbsolute(`../${GIS_API_PATH}/${EMS_TILES_CATALOGUE_PATH}/${EMS_GLYPHS_PATH}`) + diff --git a/x-pack/plugins/maps/public/meta.test.js b/x-pack/plugins/maps/public/meta.test.js index 24dc65e9fc71c9..5c04a57c00058f 100644 --- a/x-pack/plugins/maps/public/meta.test.js +++ b/x-pack/plugins/maps/public/meta.test.js @@ -5,7 +5,7 @@ */ import { EMSClient } from '@elastic/ems-client'; -import { getEMSClient } from './meta'; +import { getEMSClient, getGlyphUrl } from './meta'; jest.mock('@elastic/ems-client'); @@ -22,10 +22,56 @@ describe('default use without proxy', () => { require('./kibana_services').getEmsLandingPageUrl = () => 'http://test.com'; }); - it('should construct EMSClient with absolute file and tile API urls', async () => { + test('should construct EMSClient with absolute file and tile API urls', async () => { getEMSClient(); const mockEmsClientCall = EMSClient.mock.calls[0]; expect(mockEmsClientCall[0].fileApiUrl.startsWith('https://file-api')).toBe(true); expect(mockEmsClientCall[0].tileApiUrl.startsWith('https://tile-api')).toBe(true); }); }); + +describe('getGlyphUrl', () => { + describe('EMS enabled', () => { + const EMS_FONTS_URL_MOCK = 'ems/fonts'; + beforeAll(() => { + require('./kibana_services').getIsEmsEnabled = () => true; + require('./kibana_services').getEmsFontLibraryUrl = () => EMS_FONTS_URL_MOCK; + }); + + describe('EMS proxy enabled', () => { + beforeAll(() => { + require('./kibana_services').getProxyElasticMapsServiceInMaps = () => true; + }); + + test('should return proxied EMS fonts URL', async () => { + expect(getGlyphUrl()).toBe('http://localhost/api/maps/ems/tiles/fonts/{fontstack}/{range}'); + }); + }); + + describe('EMS proxy disabled', () => { + beforeAll(() => { + require('./kibana_services').getProxyElasticMapsServiceInMaps = () => false; + }); + + test('should return EMS fonts URL', async () => { + expect(getGlyphUrl()).toBe(EMS_FONTS_URL_MOCK); + }); + }); + }); + + describe('EMS disabled', () => { + beforeAll(() => { + const mockHttp = { + basePath: { + prepend: (path) => `abc${path}`, + }, + }; + require('./kibana_services').getHttp = () => mockHttp; + require('./kibana_services').getIsEmsEnabled = () => false; + }); + + test('should return kibana fonts URL', async () => { + expect(getGlyphUrl()).toBe('abc/api/maps/fonts/{fontstack}/{range}'); + }); + }); +}); diff --git a/x-pack/plugins/maps/server/fonts/open_sans/0-255.pbf b/x-pack/plugins/maps/server/fonts/open_sans/0-255.pbf new file mode 100644 index 0000000000000000000000000000000000000000..ab811ae10a2e7bd58ab67fd120cc313a03e3bc11 GIT binary patch literal 74696 zcmeFaWpGGFG4eoT(={Ozeq|;$$a2z|1A%>Wl8D+~d$;`}bnPifgNw#HXX3HRR zlJ~y%-m3X(W`4}fpQ)NZGtWMfobiQ+xb=rN}^H*g zTb6eFi$g6LsZ9$zeMQ0M?kSD)LViJ@sasP0>{d^HfQf5j-ONT;9?Qr%zGiBjm+NQf z6jwdD){*U_?+{ZtzS^GUt!E$29b0bAVCvXLmJbXxr+aFAqh@Lgh(U#X;TX4NX`v~N;m_{g94T*Fm}^L;`{i~D z221K^r)v{ky)&D;It#11x+-IxoFlXIa^utT@>2aR^j&;eEDtXh%ga{%(ht{vpq}uj z%ZDeVZb`44pwVbo<&4QZ_&T!!16j_xuP)MPKR?uTBU6$Qo`Yu&vsbxE`~K{6L*E?C zPIYPnpWl$;p?Uw@ttW5H$)k-|Ww)=+)F;zF-g~U1YiJu$HncxjTF|pKRvGK4E~9Gg z>E{<$J+akOP&&9bP!jIrq-W}voSU25IJerKTQ$Dfou8W(?r0xX&O=+7$H(jnJ{r@R zWVY7V*LOGYv9-0nzS5TI1=<+eF8SCR#|LQEA$eRbcTC9VatHSZaaa;hcMXk;ORAq) z?a09`dJ5uVBE8Ln^Qx<>TbJgWsvG83+N-O%sZ2f0z~toQ`V>A^=J7$+4QOj?YoG9M zZJ&G)S~#bpqhoZtx1(cVcL;~g*}jHe3EA0o(}K?2%CRkeVRm+Aq>F~RyH9${;$m|; zb#GrEZ)g2C3PxnErt31vy;B+bdg?MaWG#Y=dIh6gc9&ohOHB32y~{s=R@l+mS)A9_ z*;yKC_x?{Q8tu6rBQ7T=Cm|*~Cnw3rLy+k3r*yD8HL5kO#Pt3-mMiL z=_dq-dRr;^WVS4Pn(rsyNBh}pzjlc&@15TlZcX!>%hE%fG|hZd%i2fgg`YR)`s=cT ztUp-$#Id>f{nFmXa807SsjDYFkUVeO*zQV4t{*clJwB9r{`M8ffxrk(OI2|$xgT$B zy_@Y1jvwpmM89|O^K?Th6aD>d!OF&(aBGIgiLj3<;|)*EEePf&dYd`%Ohex!c3DjW z`F(L_G}BDck`WjgPhCI3$Jt2nzM_t~omXbt$VhF1i<<21vp1i}esqf~E-zp9BA$d3*YD|94V%q?^9G{#M`KJ2WDU=nU<(9z8s*xU3{Q#@oz*nV4Ttkmhgx_A*V)F+9JXKRnn{n&4?4Uev|sS42A~ zU85QMW;9N$Z*0%;3d4NT8>TkfgfVTkUtJ+|@FbyE4)2asak<5*A�)=*d|8?b+lyoy!YtJ!4}%by-1H?;T^f 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 Zcu8yF`^@02wfGfSi!Wd;wqN+@zXQQuMRx!I literal 0 HcmV?d00001 diff --git a/x-pack/plugins/maps/server/fonts/open_sans/1024-1279.pbf b/x-pack/plugins/maps/server/fonts/open_sans/1024-1279.pbf new file mode 100644 index 0000000000000000000000000000000000000000..7cda8da1d0388a7d800e57f25a5a2844722cf6ed GIT binary patch literal 122545 zcmeFaXK-8BvMvZqvP}m`78NW@wk0d)94LvBNKt_jbIv({APF$%OoCv}Ip>^7FlT}h z1PSIW`J8)i-G5UxRWmg|re><9d+iMnls)tAoA>6Pcc#34kl4KgHuhSpSNGRn_x`qN z@au1nXt_IZ&g$84yw2)6FnwHYJmtT+d*|MRn|JR$esXGIzg)PS{K)>pH{Mx$1qTNQ zx*95;J-}d`d~E2IR9IM;8|9>S`xt}qv!Z!$VgJBDONO7B(#3;coPKH=kkcmI8Y+)* z)m6Cu%e^-`&fKETx%rN~FbCcD3h%AlyuD*fyXPhv)4VTKS+1~1Fq*@+zJbYgwX_2n<_3{HQ0ea#rg3` zg+=KhcBmT9i0uP%SbU|0qu<6M%o z7>@3P;|zvVOto}E@%bHjfu_$+gEY2wS~9OzR{9Hq%xEc2t=Sba!6Z`q$U>N3^}-cC znP%y}MhZWD!7${6bGh*-&BVzT=%DuE=IO7M4Q%Y4DQReEXh@(t(^U1rFIN;^eW0Yp z(281_T6#=J#w&*Ty=@NWD!=eY3mPK9@Q<{E0LqmK&Adw>4QE z!<4x~W^F}9)8zJGak#CFG`~+UHoYZk&-68tNX7ij{Jz!orOB=mo{I)Z-?L(?T3Q;@ zvYR`*`E^BcUM4RY2hJ;7`X{8MB!@+&WaVTggt?o)y}@9dxTj{#hRJO2=Qa0FF!6sl+jgYnHpWiz*+;NTz+bCt`m{nJBjc5HrOVL=>6=N{u* z`Ri|Nf^+!;12}&l{dDE-b0g2BTEWglW4gD&Lr~HB&fMbOm6^7@Fvkx>MF(WJ3TN62 zqNFOiaA2st5JuL2ZxtQsX(~~X_So#CDmu{gH8ho{*CqQ+pI*3d;r!1(>~5brZ~&Gh znQi##|CnuYUw)wJ(^KU8DHWX)U_x(GDmuzV`yQ;9&?ptnG<M=m{mZ(!}}8vskMm#c$`ngT=q>I*$buh96cyuyOK^u#D%2hHyTY^541eDqPySExH@wJmX!(}{oTtYnp z{aL!WLbNZ6+cT+UG%nCqQ}H6$0R!7p^h#P^lkBd2_lQ+k$FRc)+Iao4$f1h2O>RY$F?i3ve=@2(v(@`3MN?8x#X%d3lx zzS^^k8m{qmQ#(VYFHXp*R~NQLtAqlC272fsOZFujBp3lrzU=x&C|b& z`dTW>DykaVo08qM?x-^(3fiaEH|EAi$0laQEh%nVXP@eGqVt=3#}`)DSC{4{Pjp`>KiwJ0iWLv1fc~YpN|b$m0BcC4Ht(WJ*Czm+;eAWi<1- zmbs;ov4clQLQ(gMs5zOV>l_&B<6>`P>z>*wUT95qw++oH&WPtmM8@WIEsL6xILw%u zfwsEJvhu3-iJjr{XeJ1z7N!NzN#gD4woE@$5RA@353McDjI*%b|~o!wm> z^<|k+Y@-*ttib4mw5*)$%!H@_Cqt!cidyE5t{y&qzTRwmGY!S7M^Ao7_sqdl(_hH( z{y9G$7Pv&M#h6p-jNsXX7t8L@*qr=uS;xcP<$8u zsXkN6`n-zdVVXSs<(cGxYTr9@L-J5xUO4&H+2gRVLk;j*l5>+bFC z#hTN5R%$O=Zzb(rbKC2P~3)Wl7=e7RaUwJ_IISy?qf z?K*wzdveJW4-I@W`76_PanbayS5NO>`x)=@6u7{7y&L0|(ey5+jj8UtN9TWFFwVS$ zm2U9kNNG5=D=sEHz{5`a`8CFY3o3B$W83|O!DLr`T}?@DN{F+OGK0GJ$+hkrKiIV` z6wXWzb=T!^Il7EPH#OLajkAleio&jsb93UAtu;Yweuy>W=sjJ}v^Kc+Bo4V?a|^#; z_;I`@o-Kby!PqaSYjvtNmPK~urDPTJ7neG7{BJ#fV;NF3urpd7=|JrY@QJIL+y+nJ zovDKZw^Fb@P#7ZF03uVZ1kGuPiWuB^Pcdqvoo#G!Uo z*EI1bKTg&qaNJT_Mh1l&gQbxUxNB>2a!RzkE#w!5+Q2UH#2HrYd;x>^%y zaJ8o9w(gN-v^8z0J^4w{$Q>l7{*b6w+=7poZ zwOOGKs?zmA*CVw7hgx$(d45tbQ(Hm0TD*4%O~#RyoER14X`}u0ymak&qGL~R7>=uh zx&GUS=MSDdMlaDP(2Y;{6F1(=N&nf|{hzz>=@HBiHx7R0#`Bxg(3bwpjbEayX_TTi zquuy%TKbtAUzWhpy7R;5ZhVxFrOK_J|KP@ZIKgoF+>Osm<@wltxc^5tKE>bawZs9Y z-1wcfxq+(0Ke+J|t4p1^f8xe}`hy!E5Srh+`MJaX=We{d;h(ti-v4bf!_(9z%9EL@_ueppHuk+-Yo+AtjSIhS@ZhYkdxkNenOkDN$ z`tjcq#j?khhn4PI`D1&$`4bHA=O65G=r0^YmtMxF*Vk0KL6n-*t@ri@%!txv#Iui1 zh1C=QBU+6W7w2&^>M67c=LeX+p`k%(bt5a2&Ds7^P}M)RW@v4?EiZzlDVeRwRf8+S zj)EvxeMMT7SlPcU>MV-p7{0hni^}^J#oZ;b?#9Yjk5Hnr9`RytS-hvI%JrkPChMAC z?ypGnHh*&yKrHGg(duAzvai*<+b1QOaCm)=y7Z+(5>emiCf2yiKI4QzHlDk^sFu!1x@I$5HY*Dvz?g$o#QmM-T2h zd1Vhe|KP#>8^7LrtqmhZg3#;h>+8LLqKY8B6s5-{&_T!42Rxz-ZahKixfz+6X`%4D z%D~>u_KPhjE=tI0tpcnm-}yY02FEy&vm!94XjSpC%Hke=Yuu)NwHN!iz^F@YqQ-&;b=>l`z67* zq*b-|4Fcp_0wW19MF(yLfYinD83jc}xv5z&l)!?ri!AA1nQ2P%4h{_o;Fb@s2&&_p zjl5GEXEp{(qL`Kz-f7LE<*vM7%l8fu#eHkS=5!wuyNKfc&B?k%H=U=3o+%B}n}elX zC-9t-k172T$XCGeJyDDnQ@D0iDS)FN2@zj6(vx0#~a{VL(t)k;=#*6HaP3e_(un<&8~9e%JDR zTkG6HdzQc13s}Y(ryl9LCDe?692wjaRK>bz-aX2YJ9Xubigj>a=i=P-a#wDUrSkc& z7-t^5(bmyn$5)Si+D5}z=fMdE1NzU+!^tivhrh7e119n7%ivnPvI{Q;_dEe`u_26 z)at}Ic^HNl1L8}EPNf}?OOcb0*fpOoKhU&;IE9y!fz)n(pkf`A0;4O^Nop`Z{oou| z%jdU1D2ZBW08gNU1)1IwmHSUFdjE$X#A31NFp1)cjW*V4j3 zABr5bw6xwS9KUe$Hi`WW4T0#*?b}y>VSIHEmKyIA2;yXgSSz19Oh&=s8>%)w5bX(Y z(te05Gpt$|u)-L{|FT~$RZjk2F-F1o`oc?N7aw0=H;XsdakVDx=v^(>=&bDQcuzwG z+R0*kapt+He|CFUS5cUa$|VXUXB@n)>Igbu4Q2>i>n??)qt5b7ZJHG?@v{TWo}c*w zFZtDlSJt5gz3W5kBjsGj_t$YcFy!xOy2jN^EO*X{np0uM6I|d|MH9cQj>VYo~v8wNbNdv&5F&QN0GBU_y0J~J0ak0JeE#Y?`^eIv z?X7ie8~-w19qX)d`=PFTa^uYMDAmLOu=^T6Rk8$NcXg_+3|qBed$1(Z{;j%mY?WYh zpf~~>+{H{olDn?4Z$_JVzBS$3#>NINBwX#z3$n0_C>h)ytBPT%DZ?)-9ooUqoTP>s zG46eN?ioG@p3n-PArVMY};3QaDg6-a3`R)R!9}H53Gc|2F zxVC6LI?ed;J{6;K&;w0CMWzVOorXBKEQaIl%{G5c=>Y=Vz@In|1EK|_;mN`f3!(!g zfk{cgT^jJ02&57J%1nPr{D=zqbAy-~5~aanIzU9pQtK%#h>wfm275aiy}3mR>H%b_ zC`^m;G$Zik4#X*DrpEf4OJY5Y@d)&C#Y-ET!p`C-N9vTOrk3`;$?e(ZRHCiGdrwMD zFKnIP=%p}Xbm!f@c;Gt^7KPAX8^~lw?-B4~60=%FFc5;MBdV)w8rsJ;ak!vhdlH}! z;o?VeZ)ubR3drQ1pBmPbs@yqM&o-nIEQYH`}?tA@U6>c+T6j58jCeFeDH%GPvSZXg-Fq_YsNYA)cqYD;^o z1yHJt2#(Q9X?LXnIOW4MP`)PZFE=*J+EDH3m7^!Wr=fzwr^5HkWys0@&-?aFrCr_( zHI@Df%1WF%X%H4Whr;>)Uk7EWfXer${^fvNrkwl|anOHJ&~fyXFfZI~KHR~D=vz?c zVQCVrmgHyt;w(rH-_~+VZj|V@N?1WJf%NEo00`TcC=EAR&2xN<(i4vWv*=u%Mbu_f zDy4wr&57#D%3i7Ti?d2rp+$o`qh;K@UFkPh-Z_Gj*#UG`M*72TO-@qd>`G@g0MgPc zzzXh@*|8*QN|up+iOX4G|JHaFPeyw97A!Q?U=bIE%1BQs82Z3UBW}y^*_9r>{Msh0 zXkcriCf0dZdg}$)z_pXGAck5?rMKUjGD9HJH`|it#iFH5vv;Om(FwKVTZ5%&2$B1Z z%T=JIx6HE|i!<+p{w>4GAu2QEgBYkovnmOG_8qh&e?5 zv@YyQ)6gQq1~9*j)B}woH#Y?TWu-b`7inqBNQZ{n(mZtT-oAYYr9(sTZT1nl#l?j& zE*iHEAHJ#X%qsvaCDK9PB?#Cs?xsrTzxwjrOB1&cfKyxz00qME(h-FpaGq!Vr_b}h z_;JXOqvrWRg#WV<{#UaBuhpAv@kWF4Ui4oYIfKB{QOCj`3085QCd7#WU{=qSAR%G) zj=V~k=_NciE3Sr*;4Cvd51iP>WH+$6dKN_;`H?s{5ugpqY?@pFuZ(Ttl~6gjHr0|H zLhbQQsU6)KZ%79cpQT@F-PpFEK8@NFUoo&c-I5dPpnms*U2s|_re^% zAktY!;g?5RP7(R-vrE0DxF@fDcCoiKhGX#j!Z+uh>9eEBk+`R6a&53O(aTi%$`6b~ zmz9h?sH?-C(XFw%R6nb?H&Lk_z5dz)?`TsXTmpx_IV;%i!#!d)t^`;0x;7~()YbFgm=)hINwQ>s-(#b&+ibe^p~R_1?NN-;JuW_u=Q==sZWu6rS^Enl=Uso@$(~`*on2H;>o6r zKek1Ox^8aU4xQJU;jR z8)KG#R8m%6L4I};*VkU_$yswxZdO?nzqh}S-&md&?5J_aCAOq>cy@Vnb8C5aur33@ z22ee-o1)SF-huI@^^po5OPv*6(lgjxUXWK<)3-j?1_+I@XJSQpdUS9=a7yFsMo+H4 z@f$1OXs$2Q(ZxHtVHVIif8%=}>;t2tqj~XZrM+vEpJeErR#jD1UDwt>x7c6KWxju6 z0t$O#Vsd6dBypU=u!f ziO#L8C)ALJio94KE7e=ZYza3cksHJ|SG{*yNn1(|aj?))zH|CZVuT@Y=OEIua*>ur z;i3qaU3g*WyayL`(YpIc+vPJ{)ada$+u+Y|QKOe8-haSFb^gLd{{grtrTFC$TF)cU z`k&yUe+G;G87g}6Pe4(C)<45UwJB&_77~?0>rzBi7FwqO(a(ku;-P;SLgs%Bp?|;- zl0fSd`{nZG@yY@kuRM0=gEfZ1=*F>l2PV#$7iJ`UK|6c#UPcVYmlxmIlkf%Y>%lq&k=(2A z2?LCVvPR1yY#20MN3?)$-uU<@N+^F<$0H5=j7B8a57ELCj|_aWI#+0cngRvrhTpCb2t*KgA_Sscp_Dq5%#jL# zG-TuloO!q-C#ewVLJ%J6-v+vnom5DOLKS036ta*CDT>g{5>SMUq(USImGrC#8xq-C zQX!!Qi8clR`+6%Y6mKjE8dJTEU!YKozlfp&)nS3Oz)5@gJ7Azu-7g`7 zVtD+)i8Ig5sEj3=8w$M0AWNll*WUv;4gKGd8-U2e%8CFH*P94WH;Yyz%+Y0n&KfMN+1!hb_$m}a{|l|Ki(R^WG8Zw zwXUHZ0KAz9{LZ&d?20=U(Uc3$>s}QA%Sw85?ZkR-K}bxMV6|ITT-dh(h;~NX{7i$a zIJ-r((4JX5ur*Y=M_e%qNO}D(QyZ`}LtmIl!KG)p5M6}gM5xJV0q$k3yB7-X_N%r#jK;2@q_ufRHsfNbqxB4Ia zjnu=D+ysT-k>@3bheM&Mm|5|P3W?_iQ2mL?6%q=sE7g~nUvYy9h&vfVM2gbzN056} zjNB^#-LAjXFt&I14-5|UgX9`jg$ods`tg>Ev9oVva&CTMes+8)SZ9it4*hWLwV8W# zW@$rr-#{O~wlFo6rT6T&bBc!Uu|@5JGpk!Wn@f{@4cQS8puelhiYn?9ZZAy^4Ghn$ zt_zwoA(a2j)IY0LxINKXTU=Pu&@;OYL4S_+8~8!PJ2TC>31Ptz$;F*OnauGw)nq5s zPjB`WMY3(o?LFcu1s{h?Bkhd+viM62ZRy^IDvEC{gA4jLXX+E#7672{OaYHh{pQb) z47@Yi7M40Pya>^M9F~dK7Z2akU?(-rA$yl3UZAJ->fHA?2)6(^8AQk8v2QLRT$oH9 zi%FeZGxZ6s8n=I=;KGIW3@?EF+7}i(GJTBSDSxmEg+r!l<6Sf$L1b#Hzl7`IVB_SS zP(6-mQ8p^J;U$Bcvuy<_Q4zfKvhJmY4vc0Q`=&Qd{XWy%SYBGuGBCe3SOGD%+iH&C z`E65cYr?S+!QAH3P+h98x$>DO`W#4PA&D3(q7i;gTCn|xI|nZ-8@Wb6b^)Yf@LS5W z;{)xr9{<8PdhMk?q!?f_MrK-c0NYxfgs9~&Kc&+#ygis^S}OO?e9Iu*D|GDSaZ;D; zPHM?gFB7tsfV#UOYkHB#f0B^3BwPjm>8PwUUer>m_N!*K@B z>5JxOr^iM3IqJW-LJI)fZwJ7uyS1)3+549#x~z!YhJl&I&Fw9C7X3vb zH{UyiWj9Z3tj~@Ng1!KJ%kP=7Pio`%wy3YEtf;iIxwo9hdgl;XI*H3)_9mzVk3erBpKl-}!`7^prp2j#jo(~!u~x_jog z>kpqiy74omP=N*U_VPFT4xaew$3tHbZHmE6mGgw%0FRESQlwA7QTx9=a{Smq21#NQ z0xeGK?r-O>-g)@o`p@uKgQ8IrigcnaAEo8%dDxxh>j zRG_+y^tQkxV|#lftB}~Sy`9NB$S{8Wn($Y!Patnp@#ewP=PnTb3La&ra^d_hK;_tf zT1G_o-2d1jvFsp27cdt*ostOKSM=QJ|@N!(t-rhi9 zp7bMP&j-=Ltw0BtQ1iSw7H_W~KL5gyg;=J9ng>qp(_imsI7jWF=0!TZzX`OQIF z&pt5!NX`3T^cOV`p8G$Xnny((4xxivi4HE@)7Qt&+*Gyk3JMDHu~)x)?gYi-JACfm z^Y=#H$pr-kY5rzvN)Ik!Qq}RRikfCj_mHCg{{Hev50<5l(#@at%inlqzzP7yco7hb z%Cpj9f?ZAC-udwogf%0x%A5J!gCECw_^nlWJTHsa*A#SJd4(+_qUH6Cf8W?xnHO}F z#(Nk&hlqJz`^=7bd>D=%8k^gi>n`H5)R6!+`EjDHx&*}Kl`X*KhA6X<1-h?ut+}y4 z7a1$(Wj0O|kJ&b|eC%&yl{{pTfMJVFr>UA4rXvs1q`+OE#h48sgvczDS3SA4N4yHb zU01LO#y)|UNgs_d?5UbqCopgOHfEdB!>Mbh)IyFi@U{)SQk#Vv!}Y~PTv$7>I^Uk< zXY!uOg#g?7)Bqpuq@#agb-AxB)9UGb>vwOaHpGyty{pTb=A}rgRNLYjK5* zT|=W|p9G^r-AyHl0XFY${dniSgeIJ60KfpPtSrJ-S5@))PlTy?=$9*x4LlQaa&nS}hcUmLGW_A+^KUXJnOU3JJ*?pc}<&9&!**uJ{~s{YHf z&kQ{hsz-O$2FiFY+CWxgF!ueTpvQ@;9NtYP(oqnX_8dE;FA*gMhS?z;g0c6=5?`%VIJLciMqTUtwwSiWz zuRb^SO0FBn>tl(9QJ~)%Jk@gMmh`PoHKh6k=XA^iPdCC*%{nN%O|;mRA7<|Ym))Fh zO7}Ada`%{cq$-}H2VdVbEgpij5<>WH$_%i0^}!*c5OhDt2;n;=+)3l^69YFMD1S&a zA%t%TG(I~EDcOi|VVVi%o>Pi{>VRCeoP776v2zuF+eI*>{9N^$#8f62hLoYJ_WwGP|L#sRqfcAl&7SL5I6xhARjOmlvrj*f4V*g~;ZW zG)Ppg85#m1@Ocl@?Ys(P&_ivOI1Z&N$lp_sA&VZEy`JWPmP$Ci5WcR#@HJsV0V_g* zMoL{w2n!1GLf+9JDUU-KPz}i(&DZu=*a_fK%s&rOI+NXw-pteDENzfE*B)HSqrkIdjP7D2h4bDzYsGqd8g)p?ZY zaAHe4MrP*4bJHVTYH1dYRjnW+oxG`}ZDtRdJZu&% zVH~}9U-6BGmX>7H1VW(T*sqtNXUNmQ?;_7#?%-i6r5-&ox<3%qC&iy~{3SgE%whs( zM!85NM{Y`-F)4@e2PC%C?#XMF0GF>+EiuC3Y9^ikG=Ax(Yl2B2nX&FtB|yQL`9Ws3 z5v8E_3xcJZK!RqP;c~np!+SNAV1Q(gEF<=>J^6tw77d!RSn%9ro(^gfg0SKT**%m# z0XUZWAU#K-RD>UCKAcc;HCfF8BiUySp)tro^d95c; zalewQlav2nzmz6WQw%DWA!PqUG}Mc!`{Y;YP_qieM35+F4ZBd_PNs!9s9uu}HdZXK zsF1e8PHF(}F~wc`j&!)O;_7BrurL89xp^L|hfw=)?wH~}QncW&egB2$B28vwNuC)? z^2{s=uNs~wjz97|;Xn1PWBsjVmo|9TWGG@k)IoYL?vo70`3-t7E-uf^pl1*J{$w~V zBM|U^Ga!Yy@6U&1J=yoCgA&kGJa5k;NG*k0Nb$mbvTGr|CZgd&*qky;BE2$ReoHu= z1w3bomfijT%Ts+L?62zeQYUK0vS+>zY+ehOp!Lofl|MDn7Wh{F9Aqd z?*R>~Nf8rRsP$!ph?NjnSt{Jk?8YN6{7h<8$kb=^)>l<|cK_Px?-(-W*}Pd!wosSi z@uefX)!8!R<6eOm0+wNwHzRPKK|p?B$dGvPp2KzPk>ootOugE|UjGq)=RC8G<_0#6!#b&!rcCfh&A|tm+x*Jj zD?oZiLkHw~*F!-U$ulD<8p5TWxvpZFdmw{gerl+_G!CRduoS58V!$u5IDo90w$o(;#{F9ogc-E2-rCr^ko%1C&;*p!kUV02RJ#8w}4WNmYg zh}8bVE0a!~o?G3RY=QJzlu&DlNYK}q7wN1i?Yx$z%EGiT2#)No8%ewnZwI{>mv>hS zHq+AJy~3pHsVVY|`zYeLmmY$(;soD7j`B_Q40G6@J zjpGdWgu>dE&ORXJHdSPC-A!Iym-o-@nVetU+TL0gjkFf?+>BqI_kv92`EjVYH@*CE zmR}g{qIKUPvbeXkss`-wN-&AbXz^tZmVwX7k!*;gv_lc-kJP`87Q$gKEJs-(F&D- z)oz1ma()@Ik>|&{8*-zdSn#iI38ghH9T3)St;|abb1`^%>3d7>h}hH&jOoS%x!dbL zzar0gXKc&z@Pa&KPnL!5TZM}NaLdFgGQX5-mXqi1nxHq8wap>a6BHca?PRX?>dvVH zyH$MNngS0vHLsvBFP#^_wg4LNKIvq%@QTT;XzuOr?`^Hfi3if~?PECX$h7oKDCLie z*0;Ae=SKPEiN0pam+0Xi=mOOAxsMBi!NHNaEpcyoEL)G%(n0eScs!ylK}U5_QAO*} zs+gZ2YNaeWFRE;4XR0wfE;NLfUNib}ygG*UL9!20Pd9oC!dU z&+T3lx2AdNE5FcWCpHMzd-H=y8roJLOzDB0soFSawHue;LVo?|-*7nKc@zQU?}Q+5 z;S3>EeZmdgM=?Rj$a!@Rv;P6-#Dpa3`+yPxbsCXaaOo_TvZ6NVdr5+L%yeD6tHv#o zj0Ck<>F>ke4tchV?HS&NZ&Y+JC26fYFVLJmFA(ZCBHh8w)|MTb%U>2XL)Br)zVPCK z?U|Omq{xW)oQCmFf=V9qy=0$HM)UN}WOsdeSuKBbLj>TRrL^8M)OGBb-dLF$8=Y9% z5_OeAr9uKzk9i3erPy~_ZVgBZZ zJf%v2W1?ih^a_j5$jZ)0j__fbyuNjEx0vS@#SaEHY%gD5Z#R1*wHG&j{DJ`{A;miR z2`T?AxE?T0+<2~OYU}LbMaTx84%)|Wsu;5ZBI42@+5#;##P-rFQ}^hs(%RMz@U!{+ z`b5_UI-Ho|w&5x9GExnoq!(M+GANHf`*CS{0wUE*D8a;I!QUqNbyel%?L?vniDt0G z<|RZ&LdF>$gPg)u_`q?%95j^ZdBKPImUMrPL;^``&9j>Wm03v%^f5qfD;wHa9O5@N zG*D+Ln)qfk3@?JJo1LA-3$lU15t>oeDfk`N4I(-7LTyj4IDc%N5)J-B`BW5gY$qcZRNZ?sOp%ti)fw^^J zyq};vFy{bPHqvoVft-z+1Q#|JY`H|Jk)diI0wtRg1FelPM8>hvc&I>FUu+u$|WTurcZcma$0IqR3JFE4|Unx^y1p)w$_I7 z>?jxA=TL^TWngk{aX~oRnj2;d%*29T2wQ{d+nE4%+9Q3Bl%|2Unu;pGfgzRrnyO<| zesM-@R1DNsgW$wN4I3|RN=|WkWz{g4Ji%{mgOW;Gx(3I_Cnj;*z$2-)Z+2BYJ3U3V z?ZOLtRz&@+jkPtSxF5W^sm+1X^u*W-0d8YikrjScaAbP32)CWd{>sAAhT&D*whb@t zUtbs<7@86HZ^1SseAEw%aAtJ##jyRsJ~+LyxvPucPy{@>yr9Qg4*t<8+1Z(ikwBp1 zvd=wHH)ndlsOMmw3!nkto;?WFPJg26jkjUFaX{ivURPArF|l%R_3#UZl2MHB&fI%r z?C9mlT;bgfR;_fLxECVJ~LVUR#nWdKM# zYoe*?)wNM5Zl-4wQ8xPb*}49vnueaG70BJTK)CB)MEuf}nB=OF-^VJV9TD%^87PVI zVg}~*tj|KJH$3YTX04MFZ&;HC!X|?d+~zt+CCKbr2Cfy`A^@~GlCHE+MJ{#bMTf+d z4}Tmki?oOA&WfR}$+q&MsxC2B>N5o*az^vy*4*&Gn0R%#3QA<(fueM|ErYYGYd}V; zO%Jeoedd|IOJsUkOJ^6qwkRdgLH*vLOD_zZe7R{^P!%=I%T`0-4CBZZMKu$qrw^1x z1rprDv){rjkcTcj4C&&_`{df?!-FRJV7P=DPCSSTFNe)`5WhVoQ#X3UW1jds_p`?_lG&*qduB z-8u=Jn1fIh11CoQKDiD#`S{(Z>g?=f2KF8ONaN#qUZ&FLSzlik?Fe^-PY&8l9rX(U zRKZ4X0sMlklpF~UH5H+oXfNt{0YnB73#RE?`j@+FzkmPk#ZBqfovT;Q%I|LdAh(Yp zvjvXDf8CatKypYsAWx|dxG%R6<{*!VRFXhfh&&&vgamAzl%pTcw`hp-DZ&ur=SLdW z)Ub1Qw$oEQN2+t!1;tS#FFuT=c^614_jEi`YN)YS54FjjLfD$r=A@n%s8q9OKR9_iy&}LYpbRs9qq&+kD})CTMX1OTO+;LO zTl1jos&)t{RzZWKswz`Fk!dvX3GN)*&cp;@pD6mB@_^v)0z#fB+U}=DWlPJ%uBd<- zm{qc(ARL)Ml$JRv$V^KMi*3@P!9ns&-hgOu5H4@(msV6%lat6HRcCgX-u+)xkJ=RZ-2@f#c!9u{TzKcKOJWYf6SrK9R|3 zX-SbjPWmq{pMPP*j)Dr(ZEcMu84)h}PakSKbMspVg$s)_gHUCHss73uvgoEaMPtAd zfK*L?6Lpt3jIB18SF}uQ0Xx>7P+uVc%Z&!201#t2n9ROOafq7dmcjQ#ZT*qZjK;@B z_#@GZ+~qxk0JZnWK?&=*0n$8y!MY=;LdG++#84(^>JF#`l^qin8IzWu7HFXfwZ|uS z1g(`No?%SU7=hw$^;C>ly*GBg)Mt0F(*W!^$rPT>T(BRUlBn6e;4mB~{ng zmqd9dg0_Z`BnI(&g~GPH@bq>enE7#Ky}}tN1)~p6Y2^SE`^y5m4CF;!^CEsh6iXen zR7%b8##lX(L%K24@r&UYKEM0HHZUFP=nPlm@hwx!{pInVCdyZTeyHIPlHEMH+z;{w z0aVRP^09bx&@sEv1J}o7<%WzPJGJ|#6||kgb6cmE`z!E% z8pk(AYE%8;8{lrTnkL{j$o=QEOsx)9;T_ooWi$vN{W*>Ly-@UTsSkdEk!wQjh`}0&<%mTn8eZiH#ZOV@ASrv7Sa6HkbwM zXh1AYR|{2=U*KZ)=A=9*J_f7`M@}{Elk1j~FWRT5ZfMP7yL-B`Y>ZSN{ig5a6BY|a zW>RCrz3emnbYh`xiw}#vb3?lNlcsmC`V~04O-T*e5E#w6vn3XJw)>k$n#l!I6=%nH4CN z7;aC2i7RUxLY#vRDtZK+?CPCbn}j#_n7IcbgqdAfKd}K>Mx40(?8L~>kSHkk51B|% z%Yt8DUR+exJhRqU7z_lQ<>}F(!I9bRsfH9!*qoeOURzt99c?WLx8uYW);9CO!fdR} zisI;-dxXX&XJ%(-B}E4~8!Ell2jd%xM)`0YEi|8BIr$B3UVeuPrblXda7>3vogAWG$_TWm))V)YaG5rh3AHCKQ5ohD;El5R?P1a9EiaosT8N zrDy?iKo-Yj1V{iG5y%LD{UK}_-xUD$W2LuhVpjmX4?(T0pmi3=EwX}9>rTyQDidKi)6 zUzNy?$b}`KIMR-c{?^n`P|L!>3a$f=bubx00j>|%r1+WB+qEO8<^yd%P}^05tK*GX zAr2Z3sqOydsg|5Drk284vfZ;V(*`vzbf2F4mTb?ifd2F{Q@L@BG9SA@Z zI=^ROuCtiOF?@NM+OFtZn(cs+N&1S^b}AH-Mg8xrOK-!vvpxx5iN^387@2|T^`qi( zC~zk+GlMh1(}!(|p~=M3%#z^_rY2R8*0*d+{zoTjnYbezm zja3*2&M83=MB3Dh=eiqW_=pu#PFtGwMeydIg9Kcq3{4@f_KYK>5X{0n+U7e%a_j+d z^rpZ(@7p30v_*lEI9?eC?-e2vFhv8QyfGv-(h@B41AgL1@EmE0+@-e;V2Ex2@Jmb1 z-&1#iy8MvuBauAUGV_8=QYh>rk?1)GL;{TwP&0|dE-a@I>Sgp51WP1<81#?rfMWKN zk<6@2@e9K2KqAIFbc(6!;5QV;xS9Cpz(NPWlFVgE0>ouSCVWT9Iri)BtBZvh(`l3rtbjcOaxi*`{>dc;C&Ha3}Sw5Iy{`tJFbRCmMA?t{8@ z$wi5~ibr5Bl0=ngImJ|hE}_>>bXqO^ph~lhGI{jD3Trg)Svx_S3=wT|6d)BNJFwy0 zSQgqU_kR9b{yO?R5s9hkDKUW@s}B!;x$wf66O~?E-`dtvnVaATmBb$DxI`DU4oojB z&X09gCi|Md0x&DLeR@kgE|^(c9H;=?0TV^WcLZ&f6%D=Og|6H{Gm?@gYR!&`O0FOY zzz){PV1_bY&h~!Uo$%&`WH;ja3>Jl2X}dw#4eH{M>?7jzk|;1f3i_B3OTeDs7%GWy z=%b)A+R@4v@F@W0-A(+n`OCA-8Id7zr32geY9B2K&5v)5G?o-qcZ%RGL_rpBkPthw zE*u&ZEUb>yBzfooZI)NqHa5RPV5y8i>$ewxDbCHUY~^>s3(!)795wDAz46M(B`}7D zrNGQR%Q$}Rg|-FMo26i>N56eXv4qu7V1`Oq$42A+Kh38`?1EYWvDd!=! zL+fk&2N)u15CTiV=9)x6V5$20j^$kez*6}31Q~%ldC`KbfN1*vTmcSW{8}-7tz`JZ zn?GPFtcqnxmcsXrxE3b(TS{OT>g@z5>i9HVmKYUZ*t05xY%4rvtvffs&OHS^_AFl%AX`Tl*fEov z5JN%%(KP6~d*_Eod>kr`4T;Y0TA6K3aw7r}zpALLb?PHx zwkQxyi3Nj0qMezx>_CD-irWR^)wTK2jzTU=3y)Z6t!eA*YOBnNb~AoX2~yLuvom4> zU5#H|rv(uF@p3lTQo4SO7PuQ4>Z!lHck$>hg!btEt#ijAO%9Hjo+e1y(ZeM1&N;Tc zwzeXHt$B;Cjce``9UIH@wNjzqFv3tZb98cIn!l%OGy`h(-PxlAQiSU8`{YK^76i2A zIi-5sT>v^z*i}5K@$gfjZidjgN&XRa$2(zSYpr-ot*5c&&%mS1Lup$cQ z(_}|YRb4w4&L!$*t|Kol8WI8`nD3z!VgJ@db&RWni;oXW{~4u5io>kmXc!u5C|;KI zl-aXej~?B*beuxSi$W}w-f8P=y}AVu%g_!g8K?$@1!7J7C>6sE=O#maSbb114bxjA z&1Iz(jeLNJ-`GbKcFk?fj*JLqfyrp`)W9vSv~z480v2TZs*1T+ET|R;-!)aF`eT?#kAL4DuJTd#C1SYAH9Uha4PY+dH+uF#-U#>;mdX@f%0Wc>mW7$X~A!p(DC0- zK+Fwga?#Vz%mOi<2IV!B&cc^4jz7>L;WRKb6bM7*8wiVGG!0A7!|)|a1yPOfPA%=1 z6UfPr?f*a`2-X^p?yFe>WW};lw?e!dULE-EwY5L>$u%BPDTuW|uUEmRBsYkI%n0%2 zcBcM0okAhfLn2Edd{PzTj8&SU3@gvc7>X;CT9jD%0{B3I<{D755=&}C1R)sMkmRPL zOll|tEr?e+wmk%R;aj@Ya%WCz3zTe!%=BlZBJ<$(Xa&@chQbvt8n@xCV0dj&bM-j9 zLk0^(Lw#qw_Ihs{NMSlW`U&c0l4}e4H{qQn%=b`k`bX;8qH&-%1wt**eT?s@Yp*T9 z9dNYo9cTP_@g`ooO$^nXBkbNlZTEXB8d`?l=`C~OHcXR;L_RPuI4)Ss9&1CZ--6x- z8zea)w{d!<8?0gF^Po1sOlfXtn;`Y>^&VmEZM0-&#XG|GyfE80SD^GBN(56=Lv2OT zu6m?kJi|URzp|#LG%MQO7$_Flp$617lFsbV0?G>{wqy0iBRj+gWtYqe0N?!!^+h^ znj8uI4QtIO7Y_akg_i>3vdbIb^v24Z_#mdv)3bNg>_gIPI!0y}eqWd!<=12a2J+yg zg?}0ZvDan`MjPDNJn!*U0z#|B=WZKwa@A|cTM=kg?pPaZ-4eF81+X9W9mGJpR}*AA37pn)1r2>t;I zsOc1jAAHOKi~^Ni5$|Rlm<1ooW1LI^a`{4GS7C%_V$G;Iv zmangUbN;!JM*{TF{5(HD+)@4RkBnn@UUuv3&QN8%yP@Jax$iG2nR+MJj&0G`eR%rJ z6FqiJS^w%3E9I{_xh;^}z~wm+4AhDbH0G za@?whHzyiWBMZA0U}+9R51?B-*O43Q7GF6eo@|85m0?Bbw)0#q{IG;2)Uq`6hykEF z*2_eVl&(w;b$D&+Ou6lpZ3wk1rT+W(7?qpEsGJ0AdaQ|&IfyCVq^v`58Q5TAn=#Y*PV zq3iFgy&)(EFOwxvIRv~8qvJn?MvkQWo)gdzKp08qX*-9*bO8iL(sgEmIWSxDLo6j7 zhmoxODfK%3Gj$6b*{;2*|@a`pX5duLyAnTxIeY51e=a^d9IZp1T3{T)(}f zOdC2XGM*F6j%nH1L9`Koh{ldENo;i_fGkl-Tz4Z6EJ$9VH-sq_+Pb4n*=Y2de0f^I|_3|>( z`2Vb*g}CESdb*zM_+wu~>JOf=t5<3Dia9~JL#kb65`qQ?VTVM=?q&mp=M)tc#JWl} zE&c!JC;HBTAtAx;W+#sl-sevW$4`~)mlMj#r|jQ#DnZv?Mzb8+u44)MmRRo~+;uKN z=f0#(BdLRlx=QKn>0<|hRVQ&We<#A1Pw(IOP5uko(WLaAwUvqPhvzp>QO+i%48z02 z0z8~d-rXY(C#4WeOG`^~Q=`4DRMF`q`g{~vRwF~*t)+=Rrr>x|L|`OZ!7H~#y}dc?RCG$!I0yrGg+RQjsvnpax5ImrCD$AW z^3^!h0~^I$9$HAvdeL})eP*Bqu_}qK#%bSFRg@9}#0iE(U*n8V;)QzIX*~g3mDbrf z-`PwnBh|;}z=WhC6F*>NVwOZE&_P8g{HO0OD;XjLV6QJx+^;X6e56NEfN*Dt*4DfQ zl{X0xkb}S!ndI7c&MJ|v0%$@qzqMZ~5*emsN@UhnW_oy>j10OZ(9?O340|OnjVCL^ zP8sfLEl=7#7uj#C>+5rU<-4*4(j{Z-qGlOcE9sG`jUHJU(BM+4CicoIDk|%sKiXuD zqq<{#v}#vIx&yMP8Zt{q#PHjsbF{g-I5iZ_&AcG#^h}Nk^LEEN1zfgtW^&*Kyu{=L z(x@adA&&-%NDA$e?jTbI<^{q97@4?C7n$1a2s(*ww-=?HoXlEwfj z*6AYGpc#jluR;*T1$f$-*}ymy)&unzP5~7e6~%)W*y8&nC;>7Ko1x}@Rg4VdP_zMz zLmGs{M?6n44z+h5Z~fLD_u(_+r!h%#oFNglZ!u? zx&`wR)6!GoLcJVypI>&3$**c`18NCm&^YTq1=~y@T3ixO^fl&0Ff{;PnVp*&pI+LS zXvzpMhkBo4g_mqMcajEsi$Y9XBQmnEVYSe_Kn@RHLl+(%M_t)HAh)BjvZS;IzZbk? zfCOd*!^0B`n|N1<5D0}(9zY^QCFNE=B)SB)-Nh4BO9o%;Xd$t zFR7V2xFKPmZEK_kPeX=V0tVf@tU>oe(^P_oKfiiZIu?Pq6$LytM8yr2t{st#NOx{_ z8M4^`8)q9mKM%c4v;(iGZCJR72sykPU5gH*r-lMwY)d2%%)u*!vV*N((Ss$vW@2-s zrLwXWUIPJdR8gbF+5ANaz(hx90#MYN?gjbLIJAn3hdx5Y#|eOPq~nBG5-~h*0X*I? zD*#!CagiX=QW90ePB15W9vusEG=$bLp*++Q0?$-!Vd~o1CBqm6a$xeR#<%)mKP66u z_^7ZkH7q=>391K!r9+Do>ZaBQ>x+x)hc@u_&Sbj4*bgs+*P~7i4o1;;}F0qV{+eSPaV zQ6&)ASJAU{M(iDeZOS)){6cEb9Yt*-Ms4!N$(t{=Ozc3zlV+=Z;ue%^@Q(nMLRyI9 zWvJ-D%Ps|X6&sjtkaGb=ABLtWTo+6>B}lpGn)@i81&IhvwGZ%K2*K85MLG9Ld{fKR`b8*8rUWovwRs z0%&IXQobnl-dW$j_@e(Yz9>cDKX?iXV=iK0%rAd?rqA*Z4RZ4g4)Jx=e*E*1+v-k{ zS;hJBNrgq}A$F?QPCf>JtYxUHxV(3;9+;u`0QCnj0%~DVB`pmf{q)4dKeuycxUyC- z)tcc2+2{8GFDtIAOwX>VEr8-(xBg=C{+F4&M*zCSLVUsla!YdZeFuJ4&}I7g`C;(v z?(It_F&20qilb%ZgZ?zeE@9z57rP0>F$QiI1dlD)%)8}oz228F%6Y}8ngso zGel9Dyvnw=xrH`tur|KAC`Vopzqq!s2o2*zZy85M$*xfVTJT;A~8vutY#1?9@RYGBg~0*zt!ODVgLBtta4Jlm4) zN!dB1>{w`UY_JvCXhpWZ-FIEuEI6~ctT-deMdu9*?K%gDh<9*Uh?kwN6o?LV9J(&9 zq+@JmqN6Br?rwa8-hn%H3BOg>%{oaOZ0xjU=zYC>{!uwD4vg@&EtirEqA=@2JAQ$SUk2mqn#j z^{wn2?5#|+6DyD4e5x0dcf9f^^I|C+6(vDd(3DlBbuam#5`G^AODy}ytxsN=m1<=`gJG!I?F zAm4z9#8?kqkXb6mR(q=patq2EO2f=04E-~kr&cHXdk4m-S3+PeEEjBNFtvHZczorx zmP2T2VGYr=OELnC4&RnwENbFthxu5mV5!9z*C1%yo9imQ+E!u5E@X*zAt&yT;Z7%=OJRrTga}3n6t_oCoo~v5<#pPDE4$Ut^kkIakNP-E+D;R#s>L#MHUPYo!KO$q`j&zJ1ehdbfG2H4G%{q2fHEozA;=DWs8S>lXJ^( z+U~DS_tDq$PA;r!XlZS(E=~@xQj|Ay4~a@lNllIk@i12r6NSVkc*CyFwx&S1U*5J0 znf*$5Sy-g+tEV!b^bIuSL_eCi`a{K9LEkGOFF(o0$R?_+x34nRo@lU3_>2I~j6p8b z(IgI425wqm zWVp@w1g6S@iG;kAkA1@zYy9TK`q#p%!Xgu23++8GxcNdS9t!UN-pdk#CU5|f{rpxS za*sKSQbRZh6f(ape1ASH8FK7`0;4(fN_b25i{)zuQ&p8v&8^4rQ60}J*3rAZn zU1DbyoEXa&3v^4Zuc_`>CUpVSKc=S^7drF&4e;mi*bpRDoi!Yyb4tpJGh^NKrSzP9 zL&8G59Sme1NWx^!%tTlD{lnA8_F%I7inYOivrBkQSY!jMs_md}AW|L()K+$2zr)@2 zC9bf-Gg67$A>bv^w(@s~=ZHh_gR8j~yaSmhFJ>6Env zP36egNb@n{%Ppxmv5Y+7^2WkYO@fmeA36>a5KfFEch%ZGwblhB3LilZRxF(;2Wd99 zV~r$rw}9mA1SY2puqh#v%i+nF+cUk`38b`|nV!8dU?+f;k5$IfnrVW_w7f494asgy zqitar%paU!NyM44zPfA}rZSLuRa&5pBLSOn)RgC^2EsFy!a@x}O2`z(zE9toF zGdNP18{l?aevwslaEloz36&i=J}!@qF@VrwQ3pU$tnJ6!TggU()^eJYh*Qq-)IOGT z+#c7(eSZJ=vIsSJ#C?3YChq6!?P6=FBzBdH({x|U@Y1t0xWz4dOK5t``a+M29h+RQPcOLIFD}c)UVTI z>`2ci8%@NyyF1?Bm=|fQ$R>x%SUX$7uG7t}C}0B7Y_f>)HZmmqI^A2OGp(T~%}pML z@*fGqP6x7@ijbdXj5#W1AEsNoLAQ4M7V7SebJQibZ=n=`VFfa2`xeUHO!ei3Z+C2= z;sY%{+!5Kag{n(&ReOAN`xa^rxfAs#JGW2>{ljdb+J3)<8m`Rv@7hBBBkz~~!sP{9 zC?4C#ZO(N{4)N&8MWJR}Q=T;I!x669TQeVrH?h){#)d|OzoCiSCHz@f#1CWijvdWq zJl?*ixhJ%H$FAnl;qzCw?`t03eI>PhXLFPAE1P>80auE@yV>GO!Ls>ZxKikHj4Q=0 z-S$z|@5_Hz)^E@6vVPYt9%uW_&8(mFn}>8?HnV=4M3hi2>$gQjDGUqC%*o!t47Bt9 z7iJ)C)edH0)o$Tmg+;P<|AEAL@eeG{JAa^YJ_ZOTvvaX8KQ6v}4Ac;3m4|;Ia^9ET z$>ik2dAF#X1nlMDyo}2kw-e6mpu*7G8j~ONHU7AyifO)Sb|y}4MaM7n9SMGDEFf%Fb=fvz;= z07)0^3j(?y<=)&!29N~SkZMIh?A9;Gg!Z0zs_q;MuwI<2hUh6;Y#zELW#9~`o{OQ( zZGdZ0X(8D#B0Bw--NJto7Wuck4_p?N`DE_w?%`r@tgd2UOMhAGe0&8d!i&$9&0Is0 z(lb)x0-f!BqmuDgxQC_2>#K(zE13Hy7Sy+QG*{=vMPyYpwYN7Fr-az5y*&Tk&?lv~ zXL@ygX`(wPrDb$+b!~0{Y!{0U_f#EXA%(Co&@&83Moi_{Xm20y*YWyfcWrTfznqTc z*|yT$(zc>dzwEZk+=8ZwF92znqZU>=g7syTZ)AF$yF+AptY2t$`|?a}oIMn{YM}L) z?51bu?qsL~WkW-IuydA>HUXQoWof!5)>d9gLq$rG{xJ*1b$W9B^a%(Fvr%*D_n(ON zqAdf&>QsGB}#6iUuZf>U7X=9iTfV~4hB%ET@RI5s)ojg0}= zqofaL1-l^WE=^a**vgYy7J-43P^TM)qN2Zn1GJ)ujHI@C3Kn@&q`#lUq{V)aSv*qWqGksfE_^f`Z!K z<%PCP6h-CCgY(*Ef8`DKkIb$uL%VKZa$}*VB+5?t$qNm~$il{fx#i{g@$Qb%Iq+RZ zTFc_y^(3y`e5d0QmRi!#LV6 z*y(?GeCC_oC+~=V)Ul%Sw~dj3m4l0`ldXZ$+lS}ik9U~0ze7)s(F*&oXobyjHgp)c z+EhaVY&}9Y8Y(j8Mwpv9L99WHd9Gp`h`)TztRpet7e~1f&^kBJ_`SYw7XC_h1ubi0 zqB+GQy%qWkF}4bntel{98nkkta}@}P0pJ22OyHBCLz?bsEkt1q1Qx!(APBxvrK6As zc2ls8t{h)#P4h?r+yL_6RuJTwgG}Z-phbJXfQS)UPS^oY+JsC&?+*5hs8@rs;Ro?8 zu)f^DjHJc|DIYu&qKVMW_%kSuuvBGci@&^uza;TYGTWAhmfJGoswSA|)=Bsm!o6#z zwtQr+ZDpV+ziVZ<3>K~6g@BnkveMRq4ffPjO~W*=Im27;%{Ld{=mXQg-Ulv7VgGX5 z&?=N0Y?K}eop?fdUf$U3NIAaY;2I?GV6Jjm=)g@`5KJI$+LnoL$eU=$@H3Xab65y8 z6J762j6&rx%mV<@#Y#_4V|je+kuoy?iS}YrBk0^>mxkJ zi$HGV#}i?@h5uSu;_VAR~16!gWbwza*+bnM&Qfap@R^Z@-d( zIAArZP{?S>$-cRN8qxP!7S^=orNP(Q+EQ1B_SK)9!lI+OV03hJ1UA)|z7aAG%7?ZN zJrn<{E{U*|y|GV-61LOR(~HaaXL=gKeR4N{V8QY6@p(?;iXSuz%mH(XUQP_E0ze{e zofKBL4+;%<$$G*JnDB_f-<&$}@PQn%ahz{`sXg+{vwATx>#Gao?9=2y1Q&+Bu*p zE~2SfZx31^*Usb`w*^DO<3Lm&L27<02(jDR4&F+8mXE$5I8VyeGs{Z@5KK`P1ZiTL zndomXPxdyF6xx3alAq-?HPzNs7G}hFnaV#ALT(U>f!t0yD#+78U;fzzuq_^bFyOY) zHde-3$cJ6{0T4KDvvu%9(=Oq^5f&-hC35zKnvIu_kC%xXv8CPz@fA#i((v@TTwwV^nENE7_*UX z_{7BgJSECl_5|Q9ij9xY&EcMpvlyBiyWR9!-0UK7aNgcOx1a7d_a5QlpqBowS3Rg43qf}ULM`H;ZvGk!^EJhg;q)6ubY7(5)@dL8g z5_pR;VBL_56U2}iLGr7OYL(`jv6#T1u=HkDtlW$dimPe(b04Ur{1|0JknQ?)su5`z zL5$tHvA%|!P^%C7k@<{*eh|BJ$Z>_*YrYajPM3e*SbEFy)7zWdZX1``61#a6P`Lxx zBOeBqA86%2GqkcUnRt-K^iye7JxgbIPY+i+b3G-AO{r-yMPn!5@FcQXj1BX)*D+*X zERYaZwen5KE^TP*>}aVi&5UHcaHeo;8JJ$xIWoJtvA#4j+{T%GP~F4WKfP{bZE?J} zyJu*62F8m5EmlY^jQl#@f*B~cxUQ=(#9VN~Hydiu4O!6v-oBv;Suht9oWPK~0g)nG zOe7Z0UM@xg*OrKs7U;q!aljl|E?vx9-)dw5_n`8?H-9UsgYIJ)zVL`es@cKo-=j@m+I z<5(;Z*M%iWKo+DBSEmO1kxrwuE4>F(thFV@+Tl4A;pQg?ablZXZD4#B^TKUFxzW#H zQ>#`STD3mmkWNl!_Kr#dUl=hGs@1cEfHIjp>L|YCqm>UmIYMaV|AElT0+x@cteOD| zopx4`K7AnoU_w9Ak5xN^+%2Hf!KyFTHvUOD<#i2pm4zvO0)ZL3z|5NN(b@Ugi9XyE z$_rwW@PsPY1{FVfeW;N0gTPUJW);x-CUD#ML71M;)TM+6hbN?jTFZ06Y-pkk6k+Y0 zot@;RE6##gS`U6EECUX#03|5MKyH7-WkqRO$bz#3ar@lG{u>vMbgNmC>Mc_BEW5HT&1)rpXun(H}cDB1|hk>w||U`8tIyp4Fh(J)l8=; zC@73MhCosph11%i6fa#tl}u1QjSKNKR}hrUUMWDdno6iw++9d!V8C%I}oKMf|}?(<^W9#|ev)f5v0HxO9B_<+j?f zxq8^eH^X{D?A)a{dOn$L%i~pLh!zk0+FuxACUf)dM=PKzA<0NlIMh$qB{-`+dr8ub zb1$ zMPmMjs69>DpyBC2XvD_wqaF#0ny4_70kU*Ec;Y{OE&Q*9MgE>{{oac&8I#LgTUsCw zfZMW9D%L7u82x8F6#=eellE8(MjBUV=Wh zIsHCUw2iK4YAWCCXEffk`4^;e7)66X?y3FiJoPofZ1aiw@!--jP~#S|w~27Qd4~1n zaR`hixoC)<-hPJq^XESN#)5t)c#Q@1>G`H@eUE#V5g2GoITM|6gph9?%5XcAtX=Qk%h!)a{J;(rVC)6@LEc#yjqHUjlC^G#6TsBT@H z27yHF=_&3^Ml|~RNEyylRgA5H%a39?{|wq41_e5Ueg|nJJ4L}3j2Sh{`rA_wWc`;J8@@Ho9FR2=e)h%f&LHXJ$ELuYy__lVk-E& z4q&fwZSIV3RtJyFF1QI9)sFILI|7iEL^x3U0@Ie8kmJ0yM+Hq~0vMeg4rnWYd2I%S}XKs?lz4Hvf zpb$a>bgaKQH%rsznv~AWX@Ir`Li!+|NGL*nyZ@T}cF#RmF>_=}|C|3LBU& z;nC@@j^C3p^h&NCS?-43_*7$NAe8;i?G-xpSl-M#HnVe<~ zZs^wm^hUYccRnB$L$fb*4u*G?Gj9sXK~riHm@3>k1w1yB1174)+C-2wUj~m&<(R{zSfemg z<7?q@bGhM1=C@0;_aRzr{~KCtpLr^;PkW%v!$j@%)tk~L-rNp||8J13%F~A`P6?=e zZ5>&D`mdR=HMjL)|1!653bxFw(S{nw%t{|g``lBojmU4~jn`8GrEjIb0{TB|^rr$+=?>kj&;^_9iS}( z1;qoOX)by%iNqf!_(B%Ml5C<1m}0Cfs-b?L%7WNkA^+yBi|kkX%4m5c3kKvs;S8!M z=4@`y_DyNwfyV-w46HYu&1q2u?m%KSH1hoT{aAOejX&~Hvd#WX5-XsE9>1wT}z|2Dc*Xo`KLTWa$9Go8Z&}m zLNhzrm=RzuFPIuV93l$47V&Jnn%9~WVy*K00{>NLVKJqBtAmxbV+$Px;r8mUE+61d zaV@QKXtj4}rKcn%#7XPzb#{tB+oQoPFRo4v^)%#%JHRxaUd2uKGk?1@HNG@EF%8YH zoB$K48{7qHzMqUc*Lfjv$%w#x&g%RF5jSI%fmo3nQmC41A^spzZ@ z2;yv+<3KJ7d0XxjIynjs4odv*#l*WqxKrVjoXzkQSk9Eea3;%}J4GqseryN%uWFdZ zVB^G{qM?UG!{`*&@|`?b9tj*qqAJFD@L}NQeX?cF@w2BHA66rjp{+@A<0gJ$J%&24 zDIcCAEJ+(+$<6$fpS93rb3V*RSe_PibV2t0C}rROErTvGYARDV-Zn^xU*RTy(mB~W zprHJioBm1dWb4R;b`vH(%=?fP*gDW{4EU6@^ma%raL4R&?^Zmnuw!nFlCBl~D}Ch& zZkhu9v*@DEc?hy{Ly4!6>|JiU1#+plYhk#$mqrsWV-y=$oDW%EA6y#jtj~{f)Oc}@ z;u{2A{&jtJdSiNKWx72t7;6>wd?1(y*8A$(R{Lr@7p6$z^BH%2tkNcH(h$!p9U^^w zBfhTzBzSuBS)8x>#8>U8CA}UNr|klk|JvMjux%|3XYl}}`mvs7kS1Q;2oJ(%X>BA6 z0f8oHi}F<=v6zc>NygeB+b0YoOm(7@U~v%RZSGI213Naz_?d1l4-Az}=oWv{rLCK> z(k682rxQK9h5wzfNX_n>QuzjpF5sQn(lAu6Syrm1US z?vHxj0H(To@6?TKT|IpR!V)US(MJ6AB=sBvqEj<-@=I%bmZs|xUDP$4qw^|TI(qs? zX4Ymqk+8&f&u<@JMB#02tgopk##QsBQ&RoZ@<2yRQ+;`UT9~V$f+$?xRwt@5Q&Q5> z)6q|?^t7~g3^QdBJ=z1lWH}~`p4)pf{ zRZkIlQ}3uWkZlU`^KuI+>Qmj+pUE58c=!ec2Kf8=21F$US;?P&^j03<%FsYx4??aE z=4xUPy+`Yk01 zzKI2uRfQ4OjL1LHTbWwbJ2{Cwm8iONTxs{xK-JL7a4#N9Lts9qtZxltetB+An56;~ z=DS9x*ZyW_Vo7k5A-%>dVO+DsvVeJ zTv}Y1pC@l?S@VF@;<~2hrpAVbW;}kbY~dLemz128n2?Z~pWu4=g}jcXy_2((ql1H| zzq|3li}%H46_iz!l@t}V^)+Qdhd7M<{g05w!zST>qD_K~z8#homKs{nPj<4=R<*#6 z!QSwLmRmHI6d~63;rL&Kqh%0w46&Yiup?nU>fTvcOyu~wB-b}JVd+cXV-YP2*n>rk zT+rBOm<9?{A(b!+T$rhk{ey+6CcwWoR-EkGM$7~$$B!Szx{V=-^`P{)FFfih7PMfU&Pn(cbuZ8Lx7Oiv*R}M`K#2{~dE(lxapm2-jpf;y zzv9gDA$ z3Qy1OJ^w^n%gV(!h`nWypNo~Y%+rg|ll%F$xSX0E{{w=V^7`%>Dy8l^eDOACl80YV z@73)~N0{)PkkGfMS(ph3y*m3Xp2{7t&4URTO%%FfX9Yo|Yac4v1haWUBzsUE?|$!=T^2cJGAIz%{4lei3t?nKOhLB zC4P`3%Nr6Mm3IWeQ-;NkyT*asQ`B@%g&|#M6$LBXrcl5SGLghi zs;FmvWdbapzOf|~Y2)3rUcS+Biz{v&oLOA^vNSu?R-WWzEOY0sjGzxL}+DPB$+9y-mBjS$83Ah{o&s^c*$aOI}bt5}B4=)c_atwQN5v}&&1aJp7k#GmHCh3FEBc%m` zqkfLBc-{Nk5DXOs$MjNjvNH*_{At@ZZE=;>JPcMev~6oQL(hdgD;U#dn_IzpBo`AM znP@K38P06?NO%xtoeIRr=tfsgke#u`kvfOzqF)9!-%Aq%eS_0$^F7G)Xo;VBs%#UKUfmAG zM-b5Zno8rnjHK`Ua9JF@#Mn$$Dy%I?iSRJ``1og`AFqii8{2z_0~(YR9}Epm*~dTt z35i^OCaJ7z$tqB$njhcXKLc3MLF6Q9R1zWC#sB3WUg&&ndT4NLVVltTdVg(EVMW`_ zHlg#Hq>#X#Wr)?|HF0^pG&4-`qy7u){m$_YrG zx_uJ;88Wa}-$VD83UesOPhe**e-BJ}q0`SvA9!K97Vs@3?u_3vzSfU-NuGXewY_|d z+0DLSVp;%N7!tN?2=o@v=@lOwrlC*h6hn5+PItgG3>LH3XyQkG61XmW>*WFX@4{U6 z99L!i<|6m4muAR$_mM!U^f>pedBb4G$nYm)A))>BttoQ|Zm;-x{>lLZuo#y(gt8!& zJo(4rJ6zt-LgURVok@=XN1LrDgrMUiF0t3#A$;zQ+P?UAor z7^^DG@G~Hk+$dQKW86gx2=cX7rX()e41{9PrTeR|{OYM1jH0n&PDpN?XE*opC4qjef@RRoPuu*@> zYI8zI9xB;KW@crjW@Kfh2b;^?VYOB0WnjzO)m7El)m5M5ruFLL5y(=Vd7_3b#l%WS z1xiwdp;q!w9^JWe;;FQf8Roy4iJJOx9)wy9)K%pr9uCK;x2M7G|BK!J2)2pe0_KG*;Qv8c zz`xM&{z9w!eUtm{YoXt@w?|jcipcG3Y;uw>Zpi4^ZEIC9wnQa?Y_-$;*sDcLe zFQdx;!%>9;UhX^bufG-kkHRAB-wN$HdG`(6PMqDnJl$*!RHYzfE|?Oy|9^e*o}`YA zM`&VtR%TkXpQEvY=*4~CvDswLcQ|%o@h++B9GO;B-`>^HT9F;+W2X4@+ST)icMBiK z=8yim^hy&&9hfDoeO_N2?WjujHI|Wa2Bul-WX6OMN)eRYLdITfu_FrvfGFw9lwwWJaGvO;hBJ*?&jejBbx z4Dt4j&TU)d)nRK`gnB#1c&KX?hFQo%%7zT!z0I`^?4!ytLDx-8kfgc}tgko(J19TG=jbBSdrR4yOw%a&1^gso zgdgGaXmx6Ye_(t;$1*KkF#WFe^}yx8&j4Gv=bG-x$mI?;mF5@Jbn=$qC}>7M#rP=u zF`k|j!UiiSr+I39Zm54?a&^83YT_E^SdPulvmXNhf%DJR?89;>MZCPk8)z<#_cVA9 z%vQ?+E5L~Z@^$d~TTT0r#N4W;mgf56v`7!54PyvVs*&)zF2 ztEni;zLG;_zPhqu7%BQN%MW+i)QV2A`{2)4?>>0^@ZK$k;+>x4t;4R=LtFgfRw72| z`$In+J^Ukw!5tbM>4KA`*~j~QNZYm^_WgBf^N{SK(vlqH1eHb4Z@UElS=cda*!e<@ z$IsbRMeG+YQM2s|_*3M@8x=icGgEyvSe-ak?cYB1k zyF*cB$6~uH#+G3VoT5nba)(34rhN-@jrzS?#-^DIbCCMIa|Ym9+ufvo=c2)kUbdX2 ze&?vccCcG6Q@?Z9fSTM6$0=IdZ#iukkn!#|_o=FJwEIoh4cefQIf5qNi3+HnZ4Mm# z*KWE}?R4Wn7vJenwZoa?4{lWegl)NW{K2^@rfPi4vEvUeR)D{4xp(}*(Q1d2$M4;( zcDj1(bh`Sz!^aNSt3SAXU_Wy4{wo;;6*XltDEpoB2U_6XBj<12efao+)E`|ySX15o zgUFGiSLy8U96>f4^0${kq4~W#$mUt0pP&8FDP-&H{;Pk%HRKpvL&&x3I6>zAGePFq z3-ajthb}#mP%*L#O3lrQ_p#Ld zD0cfaQdS4Ah$-mX`$t1mv!^mC!qZYy=FypN_rc%Z$~!u{s&Vw!mG1h|)KFJL*$1b7 zexhUSSYXbXtC1Vq#I}(lj)S)#Ts-J+=x2xPzC! zpQ~F;#R!r*zWS=pAPUXaCpc+n>+5|o@WDKRjkX0QjLvm7VZ40(N>mNiBgPnGvlY!W z62F{3cZV@mC|R*>irT{$YN0d}mwbXY{JYDSF5Uydu9iovH7!g)z_UbIe-M{=E5?!Q zFo`NVCe-of83b(v_06tMPW}1I@XY!| zM`5Ic+Vc|+1#^PkYren8S3$eRC?kTukxu08#rXYF8bsV)8R(qUZk;H5|J zRE#_$6XJo3Rd{vh%-#dPyinA)_lqxUs4t50veH5`I{#eB0*$DwXMVP=I4#T#P3Zmy zbN{sR_EFy7{%LBcsW9GM|J@s1kK~%+#hH;mZHz7O;A{<>XUhmEZ;muI_4n1bPcF8@ zZ52E(LLsGR z-z>(P{!fCrI$Bz&;4o7eJv1z2s6t%uTYxb}o@cD(P1h#lSBuT0RZ+AJ+GyF}#z0+N zS7&9@=vp^&ByaRQQ))+7Ck8fFV4pQu8SkR`;G;!AdR5ot;@|zv{5VKPKE~1)UZ_A7 zE3c||WwE_7JI2RM;VC+vvXOI8QdMh9S!{rV0al{O7e1BJF!hZ~O^x<6R(xb!N3o|_`bPNw5*8We$Yw(OEe4H)xG}S*m|7CMs{No)N6W{pUqN41?a9>YXbET)q4{^uo7n-gK zRb4|vUG=3onaO@e@2~K4+`BKl!7tsj3UV25q^AS&CPYGGhwv3i@Q_BvDw~>m#(1QB zA~?0@rz_BHEpBXtEnR1MZ9`ckqpsmq2hZIR`vA0kVq$6MOmALXLX4;W+dJp*`43%r zA**TO;_l&^RMj!jl#_X8XjP#@sS2L9t7eU0c z3`{C)0AaVPqBtYBs=1@Rz9128ZJu}*|)Q+-} zQ`S(Clv2@9{s6nkDQ3Axb+e&jc)dJ)`sl`uM^7J0l5ZbXaM&ouaA_BgAKic8=<$nh zsc}E)|g50gg?>|Dx8!A}XQ>-E{ zq=N1?H%Fj9ad|^qTf>jy+yWdCu;nTzR@<}uEcGqCV`984^sNG5uSJ9LJKD7TI$E6) z>K~d?QJx&?AC^`-{<#k&#&P z@XW@>%y9o8?^oCyBLj3nOw%d4xM`3GyAwUVlUP#E4m1`( zV&kKO+-*&bwPhbNGRXcLmD}!BdtDPEcNpVqqL`!O}8ah_j+TYUAKfD<}g$-IN>Q6(<|T>s(x(ag4tUh5c=ly0|{kAYy0TxzOe@QGTEAal0lz2d0$4~-WUFp)tz;j z;c*GE!7k`8mu^VvxkYDHKxsQaArpd&wfWRn9zR!iiYaLug%@Q{eiL*Tr+Uy&4Btzc z2Ih7yEDn#LoKP_~HZ;9HR1xE#LiF^>h4!-2hKi`1mWrYVSk`BHX{!@89i^hMpnwMO8(TH|K%LxH*JBfBgK}BQ~p3 z#g3j0wJvY%_bOT%a!@~qf)bvskK!aODS<^^Yx24fGDqtqzpIgjH14KC+;7 zXcki2{Syl-i{o9DFqnFG?X3>P7SR-&>x-%zTbn9#6TD47J~(#wy|$efc;m_Op|Odn zNiqJ;Ci2hD?>=$ot)i}_ql>e>1#RE#jG&tiCg~wS8os`>7c=kQ?-Bm5!Xj%lNq_b8 z69r>C$kp->NJ$I);l2l-#3zi`%%dRY}r3~0S0d_P;=gO^ztLf z#@9_l$d(-p(n@3PSCErb)VISXbFiUdth1pJvITB5)?PVydRc2nTT^L9S~7pEO`@^(v9c{>$%iq*&JWeaWp|A)t*!CL+INP& znJqJ4U_Uv5-DpzX^wwAlZ{3QK--hb)@@tyXJVOfGw#HhZnwl498`FXU64OGhU1QUs zlE5Epq3+4ng_fqyZZ@9`9Nq2p1Y<1;u0yrm-#|uHLqSqfMMG(GthIn=2@Fv^HJ{7r zn3|axtH0eGYpH(BP+F3P9-(1jK@J+5W9{ZCC5PCOva;MDllOvQm|YOA9G@u5F*XL7 zPbtAzOPcbegrLt7Af)fajkS0-Iz!Kw5s1KTp4uF1xy5H)qMHe&Ug%YuV=Z;Ksg0iE z_<#WJlz&(%jkN`VhEirhdEG1X-PHy8>_}VBKohe$6E2br)aF@+WOHGZM%j4 zo3Kb38s`sJU#pv0Zyr_#@=tyy${RRycEQn`N0hg*6zn07T)6g1&o_%m@Z3=m=b(81 zmy`FzrKI(t8!;_78k3xqr6r!Qa*tbb1Fxl}bF!snaIhrYYD;?|thlQzrMfFGt+_dE zhxS4uDK6y3hGk}k{Uz-MJ1?)lti8Y>vj4=|3nPDnjMg8t7cyEG|DyIn+3;V~UTANg z|BKoS{eyp5dtsdSPp-Xi2q}F+^(^id_S-8mgr>9q;v-2_LrX_@PfvG8D`O4Wr?eST zFm&{fNJ!7jOizd+*IsI?=H9V6<@Ifyoo)4%`H7x7ryeVq`KDBKjLoiWY^-3e!{+g( zq@ho8)!_2#R9|=R;KX7Zs9Vp}onk8o)}}ja3vzM`t6D&bQ+jU{klVR3+maU_?Bg30 zla}OTAOT=s)%cg*!f;1Z9X(?^cNbHI$Jj|UE+AXurX~II)q4dEHQ8qu+1hKI>?ZG? zJ$v>3(fhBs@ zXPWbp!h(V$LmX%td8Ur?_ULcZ-Ss6!MU|*}zP+^PwyddtX2a;p25)3&h{5cC`35C( zo8SzKVYZ4}CeRj+t~Q!)xU3zSK+7nr$-lnEUj}(YYGV!uVOsIPMJ6H8CnzSwUiB#o23WZFR_Esw)BqwQ{>~T%7&9xA zeLejweNT&<{FZ^~rIkOi$5_=kg{K$QHg~ME$4BqJ*Ru5ti3j_KJ>Grd_8UcQGot;l z$3kBpx%@zkS; z!6Gmoc?fke$aXwZFm-Xa)$vHFD@zNuloQ&2?VW*lQhJ03(hNfliB8HwC!VT0M3=Xe zq-3#naBFSZdk{f&2umw# z8(ZhKm8FHd8@rI2IXJD6H&)+2H;aUXzE4I=UtM0= z;9^^Lar1CRtRpte!%I_LVD6ShBde3{V`$))Ufw>wIyu_}ytGfKpOum_rQ@o*KQ9h7 z7AE?`#Z~O3qJevI{nX^ZJg>I~-{I+H*hz;M_pNl5wvG}d1iOrv+Fof*v!hjUnU!te zk4WFxPbOOJeKzOiYM4yw;ifBg&)?(FK)C_6((H#=mP4hY?n(sS^SjrP!!R907# zz@|y))ctp=#&)&_at|Ipd2sa@MjT+MDGPBJ>RPE^2?y>K3H|EgBMCWq99otR_U0OI zVK-}Shr=Z-As#>qIWzC5I64aJ>vDrkQD`ZzqoWbL&lnp6RAy$`u`~lAE6U@n^)dDJ z#)1%Yuz2g+Yf?yjJt^E-6Rz1w>4^c(IGn7sC10tb;)ikq4&@KxccA>e3%Xxq4uMY= zM4#;3%?FRgq?A7C>U>fVzjN|A`jd{CgNLt=yS0|&9c42|SMQKGsI#O&s^T?}+bNmF zHEliJO@-k$iVpELEggd(WzEhuW2s2k$c3qa)|wVxFJP<=33aVa)rIM?mF?~D7dH<| z!7@MC(>FOiJ=XP;swEsLphs@*>W&nEH2Y-V00;QP30>jfy(03Wa+(GP+8;%g%-sD0 zJj|3#{F3qulKo7a6RP_Ls^jcEu|J<_OmuQjZ<}q%k8yH{E$trf%mM*6uA~{>Lvn`h zQK`9c&JShuY}|ca44@Fc`^r8f@8}Ie@CMI5Xc-u2eR!^D>Jbp&VXkcKn^cgW>~Crt zTh-rRlj!1*P(O`yrI$l=em$=}!vk+P)>9Ve2$8QA6t0Y%Vsq1?+zsXQoqgSH^kpAy zdxP9>gv0iV#PV}bNSdE{DE&#_#85{;{FVU9Lp@k%&%q-nPd}11aPbcb^s?4`d+p@$ zpZLywLWj=Xe)LRK#?U=Bzql|x3?{&$Pwrnm&i2?xuD_5|`Sj_NYg|d!@IX^ekeRxs zx}wB`^LX*0>u=S~ojp99{qlOo28Je(o^|zfv(x+df@U!8~Vjtk0v)6J$jx<+W^tj_-G>i%w|r8RWnNi@+|)r1un>@mHnDm|^L3fLGi z8Ow;`s*rP~VTuu*U09GA;cR89 zCVm--3kxgP=-i_Gv`~9c^P@`J2k<4rtmU6y`|0O6YG!^uu3@FagFS#Dqi0BISJ}9FBDa>mdhDU%hed>Fgo5XE7<+_fLNT3w_Udq?7}qg6&jnQRE&d z2sDz{GP5#PetG$T&_OhEWT3MGOaZ!H?9BAiwe|>!^0QQaehwHb73ZY7(V9dz000-8 zlU=+bQj4n60?cG@2_1f@Ov)U+MG>fM4c5knrIj@G57z=^{$St9n}omXpKHwuKr_h+ z%|cRcs-qk$YOp>p#tZwjmr90ahE|{$HRS}G85$bc zh2=Dtrv^J3e0*|U!O|}*z|%dsBslu|RY^9Ty}3wAe>6My~sgE4rjp|0MDSpIzT**g^z*YK>Ocn>3e0|NuEg!0A= zAAMtUL*>1{yw=&&!K-RDJeA zQ%7C${wcPy{`%mFpHITE)ym7y&qZJI#=Yk+pWZyRIVWIsc3DhOTTf3z>cPpMPMtn+ zkb23nBWDSeMM_DtdxRtQiuCQ-byif_)WOlwR#)A^(b3Ubi8~YTRAV1z$a!9CYY%C0NPi<&y#QONvStzN%wgKtF@rt80EudjDZ`5+Ug5-?Aug6%`drbXTGS1(?a*XWHoP%Oowz=Lg%VLd6bLQoxK`)7(FC)L;vc zfzA+RzHyF0TW^cY!mn6H?B)1Ds8@Ot7x-~0ft z;ve=1?Y;8eBp^K}CnwHX{VCcncYJkPRMi?Pe?BgTQnvsvK7lpow*d7Q?G}#QE0VPP z+FM3Mv-(7`a*h@d%KzrE3L~L~+B!!iB_+ai;Lf2x$VNXp@|@TYAfLr0l#eZ-c&+mA zFbPLD@TOYp>bsdvhy0yGQc#XA>6u%c1?{sX+t=X3ol%$z6Cw7U z#;JWP-p37rXlRs|k3jER`2pP{s?_;qf}%t9^39M-eAVdO;EqseK{t5pJ3@f6jiZ9Q z?S(PrLkn%iJ3_e~O9Q1lLv@o=^(i|;{mXz{aUm@k`dpK33)muYp&&Qru{qA5y(~m^ z5PBAG6(mY4gqDVfn^Va9eVPX?P!}wEhItE+#^p?PYBT+e$*Qp);POy2#ybXZSs7-H z0JG6emm~ccTL713@aqmkTh~>cbGnv!`3R~J^*p+i0_Smk<2(ZctHULlXv@^ZWitn_jK2j4%saLB?hyaKOlZ1k)Zrk`WV|l-)SH(30*UhFJ&C8bqO zqn}BeioI%sls+dHHl|u2V8tS&70H_(?yk!Xw^yaP9)kyt>XMw~AP3F2qTIj%CSi1_ zudBJb#H|~fNms*(>*QKCZL)D_K=wEFSy;&kSqk$5 zDHRRrrK9WUtNcLi;7B!kPID?>E;Og4Z>@ju*Abvw*)yOG(L28Ud7=>nZ3_H(b#--l zo;OjS9b(Pz#cgdZjU5X^l_1>kFPfH;SkgNR1ePj4;Njv9jEu-$3@%5 z!F4)QRWTaa=->;tA3|!WeV1_TUXgN6wadiV23~i@>hPfvVVWI5fv9DZYqW~k6*kjl z7gW{NSHmybN<|cNz{E&Z&jjk%-F4{!rm}1{8CVz_9GYDptxa;*q5yPNDhi7mMhRtX zOo3l}^P>WxPP+gAm<bdg{`4_8xTKajW>xaEr`{ zXOwaF3|u3=xUIB$atZ4|dd8QYyn>$j4#F=-+p@q^Q!$o%!=HQd0*ojyHOd?8?pgXg z4ka}W3d}SN@|KooS9^*g?D&4wrS;7%^}}g~r^T21zz$nN(*mM=Z*JomV`FT~{Yv(uH0+-&!V1dD3Q2MJ!p+;) z&m9uda)wZRhzrb}zWw3AK4bwUHHqk|B_$9`q+q+(5CTW+2<8J z{`8iP7c32r3DV=!vx_)Ri)@~Mc4GH#H1KVY*;oJPc+7P{PC#*64`iR`_Z{%}{f|j2 z>;?{D---FJh2!^%3^2Ohv3rsl28LiIiVN-j?z_uxb)5qO1AT2&VHI@kuDoqzeqmu| zfDw#>q@_t{ZE%pP-wcDYKdVUft?Y$*vv!bc1lbWE79MED)qge1^x!(aJ1aR*qb{DmK% z^$g)z02ZB7Xo;>zEOzR%btrEn z`okB(EUcutd3u)0AK+su+@%HJ(i%xTaY6-n+}=Jt+m54-v?=tx6VvJS>FMdILFO_y z$|(VS*g7^_jGg7r=cM)cJ+{8v4xKE^xj1gA?`8PtbfEL%wJP6mrZKe`~?++ zH@T+0rK$M+3t61+(rxMcE3B?cRiLMkB(656twU~ z4*W);%>jHXwAsoFV+c^kZ`NEp$4>G6wS7BVx|R$;di;gsH-g_E?qvD||b6#Dbp#`?zU%wT&-f`{IFPK&Llv!iQpW^JLjB-%;s z3HQRxjI83i-o=Fu6bjyOp$reN(1gVnWGaD*$yK6hiG zzZrTW8d<8Yc^yQ4s&j((-RYOUuL^XBJEV3 zat5h`lRVy3Z)0(+o9^4I2X4t(hGZ01K`^iY>M;5;_l|vYK}^HeD};nvgWW83q#wZY z0Hh5i9aE@$TA6FCiuhDyx5GXD+ z<%U>8Zjuz#9Zdbwkxzwx%}3+FytX+AE(Q>Qj^;Jbr0S8y-V!W5Vb}u&Ba&z2PPxRE z^{)?ACb;Xpg{3+qS2qSL;<-~$W?UMpP4zLBy-!L;$u*-ZJ;hP%f;p|T(@mKH779kdmxPh9oS@ouev>OWK6ywa-sa%#W=1mBu)0 zy}n9I2cg~E2Wgs~Z^s2E7ds2WY*q0gu-H1r|M;st^fB7zc+FXX_~=*8{q*Av3GPb| zRnjT^GUIjhg(byqUw)M&flv+5Imqbzf z|6uBu(Kv-S59dw=WYPy>#xVN038U;IbEh0a^E(!%$H%G7MwRvRMyeA%=zft)sZ(@G zFK?MOXXeO}?q_OF>VD8|)^ks&tYt(1&X^<6N=cgu{NZB*zRI2AY*oxu#iU8&4`S_4 zq)hLo&hUvT&n{E$v+ytYGnsM(70RgxILQxGSaakx((P z(pyT;uN|H5D2TAv5WjlrfgIqQ=rx^)R}DdMINDk3?Tw$lJp=3}I@V{cWNmPky%mhfuMQp3gNSDN!Oi ze3W}lFc=o++R(K#r>>v{-MmEc?rsR=aSaH)@w@+^XwF zZbl%bBv|5hWB?!um8JwqsKb{Z=vX)~9T^7;9hI{`eRf;h+9NpjB?M$rV?&;ssh{5k zddZi0l?|=!Z4DLqi9Qw&juE{i3LTb~mS@H~OOrixuMoZDlDLnL9Is7r2fbtr(M$Ts(MuYxq?hzqNiPWzFQ`Pw(MwLX6NfkpX%7;;q`1GSFwR}) zGP5A$HnkuI85f2&Wf{F>_xM~N;;(Y_lKBmj;F|FS~?!WQO#^t%Ylc~m)eT?tqGer4mN3n6oe_p5f zT50n?th=Q8)X3T%5-`@rT6fR2h4~wBfVXnJ!5Z&p{<3NL*p}&$(gZUNRpd6L}r#$HQHB&Q^1_>iyGY|#bn)8dA#P=}{i_wqK3u#*!_IpI#)*MAm5MLEqAC~t~% z*12)$D}MWt)hM27%a3-|yY&l*l(=i_MKj3WyBjFeaLwCBej6-L@-V!6Y&)eM=)AW> z)8fVMqG%WGt9uDnMYg|_w6)Gmx8#J{YMk4Nd<1vD{NnBje?Mn(qOJ>t*va)~MGUKH1rmcI7U7i@-z9uNLu zbd+`?WMi8XbN{qDv1H}$M8gh|$&Pn)o-`zN6~W0&&L#~@9llN2W_BKzzQtMqokFm2 z$y+P}I9nH0e{!kY&(J7D$b=$q`@&2Wpx>|g?FXVV87v1t z6FkIQnG6glp$@VA2gF;M2uz5x)&;f!DT=WV}QG>xMtML0dXedF8#?j9|Z6kh&Fi4g{7mzhYs4F;OHubc6 zBo%MnxMOg=Vy4pOMm{?OeYSJzj6xocEc0W6RXy*KFpaK=UQ5%E-W?4JXkQvIj~@o^Iy(1%=sGUzGQkkIP-R~t0>x8 z>&hNP4axL(lJ+^tR7-Y<&7-s5ujhTYajtE4toD^JCRA*MY%D ztsY(IEQoS?dTBTRA-Y>KnBtYYkcJc<*}`eD5DT@C`s@{Lmk2DyqTF@mTvpc>lfRw8U2O14 zgqLzTGwT$yls5OT<5kgA?~_cjjq$_tyocG@kW6xvuZ0TlVKI?0kV!60a-)6=538ss z%5N2o)}c_6>*{^t$`*;JJ~Pyo0ZJxo`o^0k#u~E19JH?TXF#^DF`EWoe&65sr}3to z2#{M{7ndoJD|J3)t;|vXBb6sx|JWW&pdE#&SezvnX z-opr+W#L^$XF7@irn<{>xA%?Li>BML-8Q(xpCPUUb7@-v_`1m;q3;3~8KU$y<2@`U zGJ<|t7=#_a4H7k2+qyjyJnTLaHBeE~zXR{s$g(V-?Rd9dFfj)WYpFofLmKzB6AUheW#{>o0=N0l=%p3LnD-Xuar57 zXKHaVt}QNZl6i@>Zx{;*)6XExbNoC)y9en!$H#bAI10199mG5*^Au+LJ4tKnjLcQ| zYx9bGmZiSJ+tQPvt0*{&V8hmTT<{jb<`tgXHp{z<)aDlZrgxe17pcw2E2RpG667$< z#^yQ8!1RU*<}tEJH1_{_oXg09(NK|zlcyQ#S6Bo8_WFn{#u0RT-33MxjZJ!)>Qj6iCrMzU6_+RDUx*tX!R3*1I@Z^(w&nkzgt5 zS%6SVpv~ipf~9MI1W8(3&C7c^%S7AkXk9w;xL5Xl!CA&9CXoAN1LdcjWqGJ+k{-S- zsDOq06m-tx(fgU*KQ34jO8eg<-~GbimSBlbtr_KRCA9=+@wbv%oFWT4C1bUz9zrlY z#{PK%MCTw#A%w%dO-xB}EY>6#;uk86$&K#26GBw?j9cugN><@Bgx9GyI#j)BnKQoo%@@AUz zex=T#e@otM=7-xautd9HmJqnjHUx1INi%}3FyCVVE@(tN)^d(6goBfL15_h9i5v@+ zTLv#kfDp{2*fpNuMRFdUKh8p2P_wux6*2mfs8;e&OtJnNi#Ac-$@~cy2sc(gr(sW_ zCTc-;hHQ=mvf98m9iB+-wU1j21(^Wppr8f|hGS8UjfV(w^X^Z`NSH6s{DO^6#v zo*rQ;N=6Y0ID$G8P1z)8uVEL6s5oN=uN|Fg&5J}}3B<;x*49AhzwfRdrg7ShqDuSE z-@ZqX{rBAi2=T-{H@Jg6%Es^ZF&zJUEGDZ`xn(S#CAXh@-`%$~fbb3$yCi-r7%K*r zda)!ny?+vEQo)#0HH3vT0#)~p%NbwSjv$7F_fnQK24~cdO}B#(;+bHC6q$b*qDNR4 z2brRB2|M`LcsiMW6gncMLKwH6(y$MqXXB{L)-Dlr`S15#*RsO=YW?gsTfNxoDKo`T z3hI2mV|asNzS8Dq*>-Y2hyqD4*iPrhe(5}W>XHs&J5$0PG)_rZq8Bh1Yp;IsLVi?a&s6y6$7=@OFR{~%aYmOH+Bc`vEj_W-bTvdz;&z&wXg^cH z_A~rW^Q5vaErgAD1TW@Wqgy(zVW6y&B6zXeRI>=p<08o%f|qbQG#Q9!U}6Yff*~;z zO#(8L0h==9P53}Gi^!SqxwErZn0T2XxwDwuAf$j7AdB16 z;O*<{W%q>N&uj^a0@h++!T+A zo%%61F0dZQrg$JJWPP5QSr+^Q=5|6eCAdk?R#BnFcK--L~pa%W%o=bE{3gPkqmZz3Bx`1Ly~34k?`?sZRG ze8vqUb`MW(kwY&2!VM%yVC_{R3N>!e%P7=}XkO>{y!Uxurzc_ZP>3vX-p-zbuZ0j@ z;{2T+Lus-*9E2iFyvNfFkHKe!*D%R3IV11&nBp;V@@Go@J~A@GhediOYverN+S-UM z;U`!4{=J*0>GDm;hhzx&)(E!k2e=2vD!LU>eNnX@ka^nB{3PmUFJmc`*Hra@D%PP+d62{F!rO_7@^q*M8(+;j9Bou`IK* z1lPDOe@qH$8TfV0F)6yGi$AuJq3{H!fMbV{@D97~^r2l}6XZ;A7$Cc_EoaA`Y2LeX za{t#KNssB=V(byP&fC+)Qt#1?BRf78uG8V~mks{f1KQ%X$BeC^l?^4aI95j_|2gqnYq}=ZOi&HS2d=wMI~oG7ec%|Q{iJ)* zF*ZnW`b6|wgGCn{3UD;(NP*-96DjgHB8tMPY0?=HmKwZLQMj{>V-N%vL2+d-U2!nE zq9`N+qhelHkJ9%ulP)~A9J&Ioi{{44vhpU7q}9Fh(v_`0IL?b1@u+BKdAvC{%=V#d z8NwYK7@L>?=s7RS?HSk)gti6cxzU+L6;)N`ImuzJ`pUluM@SIjmVt|JSWH}8bdbBX zj`A_8{1e)?%_9FV_NB9-$p7Tn;% zZp5uepWM5A{KwCPK?m>2mZZMku9mtF&hL?;K)Ho-c2-70sHf$li#!S_!HHX#c6GK@ z<)`>rsYw@BWdHLL@bu!I3NU^N3mfkIu9lYG@g)$131}eUJ{f5R4dXxsc}iD3!ooL< zB$7aj#}AJD`(}89gL?tombRQKhm1epoY8cOOe-ucFG-Jqw&e-I{@KsB z^c=iGBV!``9F3uEDcGTabmO7Eg^it+@#EX4e^f$Rox_uFAw)(AAu<%!Z$ElNUDwLZ z)7!_>)doZ-j79pR9iS(%_Y6&Bd_UpfJJ~_?1e<@k0~V841=X$X?X9&y%3nLN2LTDN z3%f<Fez5t51oG@Uqjnd1&)xJuj4QzU!+i z%E~GztF9_~g^==W1hyj}td!c0WAn(W>qrO`ZxJKcm<5!%mF>Zhjal8J>9 zWe(sV1aXl40}!Y~Il4V31WcbIjU+AKpa8y5K|iXZF!-)qM|M@*i@hi8l0M&pG8^Pv{+0u zy(Fh(ACy3HN=*&b_1z;A6J%D{au9{@E`CwTFH=+EU*(onRh6Xs8{uglxOi97$j;UM zg`1N{P;^{eB+~GB%Afym=*$hZ$6$1Rq+^Pa%EnmpDwbiC3nrnZnDvUKN}K(>b$$EprEv_slFIBQfiDu zR#cgkS`E^`$pKWv>M@$1{z6djzaMTNc}Kb|9Mex$LiNN{eP+w-7^r4x?!k%|)NW(t zc$@(Ky3koD4+1VwRK}a=xxeo#!~>hCepx>SeQgI6yMpznG704S?L|GfeW)dgy9_RM zSLC+MjdxCqYhOh;-!}0}s~63Wc6Idsc~R6@RiEh#I_~hS+CItb{ETR7X>tP5;Jw#$ zoI=uyY8#sB%Nx4;2VnZ@@J%f{Xs*3XO^!*;DJaZMdI7fWtB>?;oZUgG>FFOF68ORl zPipVUYpRc)Jk?a!F*GwX)=|aI>2sEv|AI-c|Tw z-<8Ma?!nQ?0Li>cj0$iw{%r51$F`uTEvo}crn#mxJI?L<^BSNLF7Ftf0wQ-_GTff! z`^!CZ1W5-ML1)wn6)%8dUXUhd#lTx}TV-BWZedj`h-B`V2Z6~|(wLJF>>Ci40FD-2 zbqDApz3_Cgtlx1y z$FX&-+qe~!tRFG&TuIg?$JM2@{`Z@{-m;OS>JowaOa1tdyMI1*`qZKCST|wsc>ns% zyXsFK-#zmH*o@(K_*`yQ#-^4I?k=Veh^MP_8J|8WEHF4c?q$4}fk4yMQB#;#P+Zwi z9BwIiAauZb`v=7YKo`7kKr*>7J3Tf46Z1T9bd~msnj7jWie3kplGkZkxcDX$P$MZe z)YncE&vE~SCsMG6g`oy5C=jQU0XD9k*~gg>4ium>+X#q2k++rDnGb+x)}Uw5O*rt( z7dsNxE0!y54ge3=&zJ9OnG$@)*6f+;IT^QqLKM-uN!k4Cs>|{q#xCXWpO%bvkz%_s z3g<_br-lYbCrOWjepy77>)#ZW))5MdvGR}Czm5!zN=XT{yeH$~vUPX2*1X7hC-HDS zc>46gC5DpVHls(*oH-`&a3Rrr?ao6@^&3aP64mvNc(?$80^H;lWH=gT#;TG-0zgF@ z>8uIP{PL0Io~pduf{F&vm)rrK02k$WUnBqOnRo87hazplvw9bJuRQ>^-6(Xa;(^>s!R)de7)g*3EtWeq6aaO)Vl(DdeR3`AUiqoFFp0H zse`ZwE}Q8K?v#Pqp27%Qbpvnk`+$wk%gE<-;}lkaUIzA2#eMHVgl7lVx#GTG`-&s& z>DUA)z4dQ^Yp-51)sW_cJ3t&E%tnn^z6*NZ^}mTuZ=M;ePJW?#bvND{7_%9 z9k~E^9^G>M0rbN=mj{Wswj#kz`_itDA+q{BsR}fl!*i`!f#y^?zxl|0qM=?GUVu2Y z%hU5%folg1G8F}hTcRwK0eDrHXnV@0If^Zxs;ha z9k^rUO#8Tf>%Y)M5M~Qy{$ALWGd*@x7~kein|VR@NWfS5z0W7m2(Tp3jQ-stv`1!< zX3oQ=*DF?|40AoFSZHwnBv`^Ylo}H(q=dmuLl21DM0gr8y}}~M9ifn@2#Vz~4vujM z9+E^7NeqcZpd^6yBb0=&FtmVJC6g4=c|og3OoqtFqeM)9vtK61d)m}tNsonpShi54*n zSEZ@&FR_e_;LiMgKo(;gNd?kTb`O4GbX~vOb6uaSkT$rnm+MDckFgSID9Rrv5GP;( zUon*UXXKnU|6MvKiErA;%`N=`_nT_ zQD+n+BJU^hhZvL2awbd>Z^ih-OwgC0OjsE%#NQI2!%pvl3N!L$uVE@i+RVpXsZ=AH&Ki^<>qM+34Zj|kMhpBopG}L)y`S_U)ec5{z~U; zXU_SA69W~T^C>xJC~1MNa?VEHIUyGh=RCngO368yXxhoIzu0%nMCP1e#QNgLi;pdc zCytym+1&zK)JIgU0^;Kf`(|hQi{j(y@j~Y+2BnSj^a&yUn!8{V)2oAoXGaqQMO-M-vKLJCnJ?(CqSlx{{mmP zU&|Hygp}A9P?RCkm6Q|!$xk6(gDJBh!d^quF$&}?l5=f2QUL;kH%(mu)AS!xYk6sY@5gqI=iFMJw{1c>E0zt_HlBCxS68!Zs zRzCD4bgA(MJ$_D4!azky@Pw!CJcmN+IxoUK#rF|X(r>@Vg>_+hGo8zjML0K)+ zqhf@ReNFBhCN;H>cM;ETaqqi%Qe%3^Rq(EZvXcV{p_Uhz5qCt}a#Y1U0AD~fiObo0 z10(_ULn4KmZS7vBPpNeYQ1WjnbO2hY{*tOui1h?pJph*c@EsFh5JTb8tkzi(_%;Yk zep%ZU)KK&$u@KVfDT*RAxw;KF?B<5c6Wo1X)lW<{F*rG7b!x>^P3it-5C`d85-~iv zZPc4S39fQ@j1N&c!U4)*L>EiJqC>?zFq21=8+lTBP=M8g z;x25dXvs*%Q@7Mm5p?&$zR&pu9T~Upr-HJBKWyYzc^s;om6Cmm7VbNA;o%-7Q}fANtCR>hEh1YJJ+&)uUBto*PphSsB+l|TIl;V1BXVNffG zay0UKiT~t-Sqik7n6;ar;t}(vSJYP$Wux-QJ|e$kexxcTB)fG+1Q@UJ&Fi}E@umIC zJ;k631R+Q;3RjQZHSqzCX}YDPpP*9UR^9y_QB-HQOpDs)2vlWo>)@A4yDtO3g#I)! z0isR|Rhp)@(50z0WA6w81tjr~&pz9CO~);+q;HwO`Hi#tcb@_i2V`%v9Y-4ZC^=TC}MNB3_Ox5h5)imsT!GUFDiQNXN2BY)V(xVo)83J^7tUd z`=ET+EE%s)^8;COm*iL8L};BbJN*|)<*iF(dg@GgSYUPToBLs4C(|t*hKSWu=2mcI z`sM08_KiX35=r8>U` z+m1?}iH{WE?ktT(>8SRk+q(|6Rncj?K&#+Y{ z90V=PVY@d>Q0t%dKSe^34h zHi(gt+&2w>f#)mz)U=mGqu-#kFv?DPgk{lb>p40zJvxfCiu4HMoZ|n?Qo@CfU5K zjkp}Ty4WY6-uelZx3c#0UCUr_Sf__rkzKB>+j)sG75X@6Z`;BTOj`)P(n#=?t53{m zzs@&PK#{B<-ti6g*%*_A7J$tKe!?4jz{HJlNocQtJ1mTXjMs{dd}mfQwi)Q&N63sH zQw*W7(NUNs^o`Vx@SFPKxkh>luLLupII6i-lR)B9xTu`PMMY%pdK#qWc`U1->DF$IO_8IW2qET_~-`nb&`N z{07xSQ)7iToLOPRH+yO7LgUMA=q;)79fcL$WMpNf`kTmg5e3D)H*}qRP-0n~)G7k9q|Cu>`01X#HDfXLeTY(rXfWOQPhV6zS{?E7u7zPz-&rnzU7 zJ5S#`t#SNU$=K+a1WHew?U6%7Zhg<_gk*fAr-{Gc4PBS;w7jya>dKNl;eNl|*0J*n z#_A(Bl7C+#^=-dgd#Gy${E59aXTu&v;j6tTFWtr?WZ))nFap4fL1oJi`$0y^ek(9F zVpYtZlH$fMI7K(f001}ZyDGkcm6uxH=rJ@zKO@vPZ6{h@o3fqqoC-_Pm#}nSw zC3_DAg>^q&Hw0I3c2=~btjBCSO&q~MHX2B4=)NRQ%Vy%VR5QY80^Xd9zf4 zK2Ws=bIwDX01PQX_K(jn6>D(kK-3JQNjl=6P&i`jIfN?~vA`y29#OX@%lp69V(;Q7 zTt!|Zu6Pxjm|4c($0@FSbZMZbvbJ?-o{OuR1?9F)zMCAMTo~og(RPb1XzU&u>}{>& zEUI^mUPPtl6&K}auseee`oukb2alkz&|vlni-5uV?%0ioI;Ixpreql$1X5)E)_un> z-c(kmZ^7YH@tD8*eh-3@giL0TQ})Ad;9$ud2$lP(873KBh7c$GkH3Wr5XxDE2%+p8 zhDJ@nvPOnRB`7KvBV?SG1b+IQ9#{)KFsXkZQ!(OpIa2SwqW2<}ZE>W&eH%nG*x#gi zN0PyWMDLq)%+y0L47Ec#w)jiO(t#G{gl+4)Jbz>A@DJ2EjjHQLw0uEMw%@+z;i z3im1C?sMd(maS(HfFsg(-+J&09x5OgQVS%6PY_tUyIc5#V49%7?8}^-*ZlXw1H-Rw z<>l}10}BC(U{TzCQBBtnEZkHR^ol*yjX#~ZbQO36st5|UWsmujufN~P*8^ET=vom+a>#-jGHRn;~cwNb^cZ#bRUFxqH>uDdIr~AR1+NYK$r>5ox*>OmW6vCIr z`$~uzS9x?jcmIMl#^tE*^oH}{zz|OC+_m-L-X+BoJbNS@% zO;VG+lbef!xt{vvgN!Z{7lI2QB`G$<%SP+k&)kJs#g#Qk%NC@D*gxg#13Cu9lQYvI z)Yk`Fva-$JrWYimg99Uz??})^|D*s}lfr8E<1tW^LTZof{B8?9Y^KjlQPyaxFPe;>7m-q69wCuc`SFygRq2dgcEnU6cO=YhF zELE7HU9>PevoJeQndHW5R2OE4+8XNHr{&@xhgwZ7uxB7gC*ITn>5eUg@w zeoi2(4hFKq&!qIjiQAfnHtt?NkXC_4<%PWnx+8!DRSiQ3c_qKj%t(WzrRC#Gdp9b4 zx@r4~`=%Z-8O61&fQUi?GXPq`KYVxa_z7i0kAxx!ge|>Wo*V6`ObswsIePZ)BMt1l zi@K(PsO#t)oLU;Fgmm&1bu%Xy7hpQ4XTWcgnN`v-3YBGu;hOr!Cnd#{3@&#UCx-g? zMNu>jC>f}&mDSZ1HcU^}CVN_%m^eb3mITkV!B#QdSJ3?{(iK6}pi2ud+c!8i&Eg}J%fGJU8l6+yF+4rDFh4Uf+{K-*u;mwJ1DCMm z?9%$i=BE0pl3b|a5xPTR`?1^4Y}^B)lV83{O^%NY^srDnN%r;I4qsK(GJ$sPi|4M6 zHYQNn`-#!s5Xfo^fvh?SWVL?dS6}^j`py$0sOCCa=|Q+``}$pnPnsibXCGBpk)ag?*xMNR^uqLfwJFbZkfXHc{fwpBqMdv)Uxg8%thYY&XgUAs^h9s1WW}DJHb-|?nw!IeM zC>AN{g$`(d(Tmh3@#Nrwh>NQX{020giDOhw!-)8uWSnng({~BaC~0085vmv}!H0KS z#~~2dxrypTuHGp&)amIj*PlQZd}smh8{i)tok|ppTMpbrMyq3CwEj&_PC;1%(KK>7 zt+M{b$-(aKzEMWysPM%vDrUZ^<#;&r^E2c9z&<=uSoi(eht_^c*`*ClcrHchp*E@r zyzaU55E9txx0_kFBKwz&!~@c4fo8Y&G3tA= zVx0>v4`*LLKW|&j^GtCOBLzvm(ntZ$f^On0_#bhkh%OyjHB#I%4b1IaTrpC7cM&qZ z75xkHBgLj8q%B_FE}Hqn{Is}_A1Tf~vi6V9spx*+SyxrajTE2lL5jgO7{JiZtoSf* zex%rY?k)q10#IhDkCB3&(Pula3O4<#r}nctZ3SlQ?Bs;Q7;*{7mW$9%!geyA7b&2N zHy^!kO4MiWCii{;q6Q-mDJ4*0+{j0YJxZHfJ`xJ!gk`m`mZtS4Eue*U=fB0jv>+8$ zp~BKrSZ_k)j;r7L-Hw0wSg{x74bk=(6 z+tM%e>1HZu&ct3h-J1%4iQx9^H(zaBx8vs%r?C0YfXYi=LSpF%Al&XxPM<{H>HG|b|`}ZS)`0`^Dpyl{$;w&zs$Dzm&rE&5w6XDglh9IGi|OV+SKKUHrMcM z=5aim=(4M5HYXU(=Gh-U{xHj?+5HpSKSZ)wmgoT@&<}8I68osD&b1FyZ03gBYkrtv zvmrgu>ca$^P;GO=5$t$Z^J^l-?)D*iO)U741pNTJrWvs$nm%K(9B6Ep}!w5jD9Q%FRcFFE4LEMh|7 zh&Eq<{)&jXh^P|8u#)DSa3b1N0YMHicombKxDWE9T!?7%G>A4q^)*98o8|qWKZteL zzkPU%;;wTKtwDt;k~B{b^(=zn($nbfF=SJvM4MoMogQUGo9BO2Qs9X;nB@%C8^WBR@yIFwj3-2xAWRQB7Je@)IRJ%prU3g`Kar$* zJQF4hucXr6MdCBDmeOg1;bflYGU3mP1Jl`9eI{ZkR|IZ5VlDzd8sjkG&LV=o<{WUS zT?c;&XKI#Aw&X>6JDWegc8HJBj8C`KLgzKX-~QS419aAV@t+oFrp9_(Dqe>u1E)$WSoXo`Xw~M@z3>Rvl8l*Aex)dh4XZKKJP>+3b zk{dtYx!YMg2W7V}gZkT+roI;m8Obq`aTztDx1Fd2Lop)KckOi*rR6O{@1e!^Li_7I zj5ZbV)UmlmQ3DCFDePj5sUzc(@!_^A2su8zsKCflb4n_!D@(HyLYyGM#_^;EM#ja* zM)|v1Xx%uxRbj)PE2L<=Cvb<0#Y8HujQljo+E`EaKQ*wFVLQiQDr$#5KrmoXlmh5B3c=09G zCEfFV#um}XZ_^b&+K)_4AMinDZ%znFVX9%=|9ZRjCLUmXU(#- zQ~llDgOl$@YEr%Qx$ARs-qa1h>nmWN2ltEjk4#4eKxLd0cRbz^6#$~SCa#7X#~sbN zI4o0-q1RM$@D@ryTrelbo&-z@63oSNyltIgE5>FZbcRrlS4wV5R7_Sg1Z!d)PTw{T z%BVK`#hLLw zmXFSD`sJ>nlTTPwbhy8ZsrrRIAANh`wuYgFjjg5elRF@=m6CyP-0}ZzP#jd+{2vK* zxdmwhf%pLKEtF30yrRo;2XJp{eAP5kOlFio5E?6(rfm8>k~t7c12|+hW)ToF#Z-tV zxy?FX3XR1lv7FaTj#bdUUZ{{p9RpxzFB@lvOCn^&(LX}}`+^Rln2Y@gdX*J0jy z6~#Hkfa7l@XQrfDaDLx17*K^uX;f`P1Q6N&H^a!Fy^uHK^Fc9iYzj*#dGl9HjdF4v z^;mM|%{$L&A^I?n&u1lPK5`jFfCyCd<_DR|%!lsim?P6d-N090Fk5Kf)OQaFi7g+U zh8mF;Z;k{lrCnrReoo6Q%K7ZoIdf@gc91FZFFl=NfUASem6=&Iipmt0>Rz1gCxA6q z+bmgbhmgEOY)L~)LrH3Y**&hTd0@Ie#r>&?cg)MQ)F@vB^r)iwW&I@83#zFZ+q-)_ zw>NrpY40JZDdu&*qk=}|)3?>1Xg^iId1lYn(*OupjxDyPgEw`@{$pp(ojJaD`|r6T z<*qJLAvmp|xM}OQt(y_5{rdD{=NRx2WRR{et)IC@a2z*e8hss~UHu8UX%1NRC2b;y zXcLJGkKJWdf(*i7u65(UM({9Q({m>_LDqsDpPbvF@aaJn>xg^-x{&E_eElcF0>dTw z5(KUoL}3##=GRP4kd-eGQP@Js^m^cx%gWl8`SZRxtM%el^ZY&&d7FyB}wBql#=M<|gpRP-m+V)?0s0Y!(NpWSYVAH&&Y3m&f ztTdh?XAAYW*E9`C%c>cjZ_V&wwxXhRe`BD1I#FQ=ZeFqt0cRCzP6(ix;f4%4s zpeW4p?#Wwfk2N1EpZocaMM!=xyWO?z`;VSHb?gAg4%XQTb!t>3DSr9YmQBA0^1&?N zRaRC83Wve$#5JhL+Iac;`ZzSc3~r}9fnt|9b|k<(zSz(ZArRzkZ~>1^vI=Y zq)bb)8B@x$^pBZj981Dk)Wa}RzeV9SZ{j+byh-R_(z9f&DXemxE?mW!!re=VGfFSS ze9uK0IHHZR&u=PEY{WWA}Zt}4$>hQQvPBMP7WeB02~KRh-*E+*)?HAMK3 ziQ9f$`KghWy(3W)s9rs~eLWIG93{af(u^CCYQ}-d_v9U(UrCx6(sAS1m86Lw%P@{x ziH!wRAx(}#%t|z0vyy2tP^GnCkg_WA1`JRu&XD6(VQ0v5s*qs?p9(ueo=b(hsT_|A zXGoB7sPG0Ee~Ms`a;K1i@um>d1$}gBV#q$WLQLi1++rfY&)GGph6$Up#87ljMtn5L zDo7y}YygBUYON}78eM2d`ees`hANt!Ku%-;3koeZGA1LVE*@#C0O#Xfg)fghunBpY zUs8d^Ux2m7IVhE1d1~Vk5*ZU2=xUB!5$%YssOgwn+gY1Fy?cJ&7f3$|nsI;Hpg5|u zd0LKBNseXh(+@bl#;LDA;QKlkzokyU%KgD9^Z{4+r>(MaK<-?*p+IJp9}T3|6$1iq zT{R}~)>XrT^bRXV2B~$$;2^WEk49To}CW7Ye=NY zsJ#W^wONex+R_n&XI>Lw>TrB01lTz0wTzbe{z6P1Lb&bfp5tnE2#~WC$WxB9vw?;Fv4~4TWYo0yYst0MM#| z1_&w@VX=nq42WKlBVY^V!VHj51_^NylxQqMbGZn^e>((hoP;t4FzLX-ODNNrCJ?ak z63WtHPEJBu7z*Vilt~nfV6So#%A^Yh$|ZRTWzxO7zfwXOgy3r=ln;E%CIxv3WhE>c z{|O0Y3eHlT`9G~!98=o-_cHX76rCtVFG;b9QuLA(nTVC~DqP}93pvfd&>}@6a`%zP zBCbUuuEim)MIo-mAg)CquEih9(1$^hToggT9vb?kGyP{mCNbn8+b)id(RXKVq{wX` z{~)220keF>S(e)b^b)8K*;TkkKrhKGUY1W+UImQIB{@r17w)TbfmsxOxUA=y3@?sz zeR77ge0}19g$F#?-T2NSprAMh{*n|F#Z+-DprE8ULjeXQg&7JsC@IPi+ryP0Lk{^Q zhcWzT8)N{(HTc3c@WM6d!ZqN+RoKD+BLP|%TTsNo4|!xE-fR$bSV&}Zz(NN8otBV+ z;R?A4dW7Sn#m~sLE`%E9m}un;5#XSu-&uM_gwy64ZyfYn{JgS8P^PS|x%)G*8-cg1`YS2OGVx_PdjgANJ@N}!;U=?6UGT=U8UnQe=MZ=#ZF zCPvFw9!RYgLkKXteW@#V)q#TEcU^gs}7*_RaDV94T#IC12JVIb1gj!tr`pbzOE8>>p-4nzjn@&}#+f-=1b4PZrCsLdj|rtdH5dL+?uAi(rC^;T)ya$MD1SP^XdiXTtD-VwDyaYAYH|GoYm z3nQ35zP#r+_g{i8f6CSbQ319OPhQt&?8^jS-o};%WzZzQiY|r~0wBzWS9f92EfVz= zhFRZ7MRlD>#D%eFDgG!X>FbF%B>co*Kl{($38=8g-BG< zzSxz^SsydGlF`!KuKAWBNiCphy?LR2wi?{I@S5JK{vxuXn#Hqw>pb=e3QbaqpCp6$x_s&x1ojn@$PE|^4aO9=ZJ}6>j_me zV8rY0l_Tp-FsEN;Ksc~%u;fA{0aKsjvnFf_5eMD zF~4g8qn5?JL#zOQ^D(xQm}wm61T9<()OPnyh9`isD*Bx z!}w{Zb_}Jl+Yo?^uMkbvy{r(CD67t;9~6GK`?{eo7!Yt0P6r>FIdo+=9erSnaS=oO zt5@|?qKbHkB!7dti|23-aWl1{`IdAa!|S_$zC+W=+vNpni+>s@j<9)f^t1-SrDuA% zCV`_q*S_Q>GuYf2kuokQzP(4GS9DG*}ZYg0ZnNZY08};H__p zq5W+iO_JB~&5%c;|2tsDl5pe*0C2pl$5qL04zW-%D8+qFZ9S%D8(B={*={D}eN5l? z(>*r~J;7Ta7huWK#+-unHzzs%dSr~h;4{XIpJ|HtnN|ZMR>L7ygCJJ-rqvy3bsyTd zs{5PSeK}l6&dTqmw z7iQ*`chAvllYs2@g~{HIu2D(*GG2q}qO4myhon+-g9NWt%zTmxtD2xXmxYn?wf9+d zbC1w?;QnHQ@RUOAe>$(KXXy;gp95v_t+aT4rY{gRMcw)|)vW`i`{PATYY#tfM;9O8 z=N1poG7J{n(<%7~>D_}&uI?-xxw@+`IiqSUsLZIdXPsVQT7|l8g{*)guHFR@!%6&n1XCcVvJg zB&a`zQQ_+&NE7hRt$F<*P(x^|oEz2Ee{=4CL+7USiV`;00qYfKls5mL>sI%p)m>=Y zs@}7@R%ohSQ_DS zuKbZtz-{C8s$oi8-Lg1T%;408U^T_fzCNL58&TBvwkwa0gi+ynh>w*zy!a=q+*9i( zCu#w@XEZE`vKc|q~A*p*wN4U^wajfG* z1oKp_!}5`0EQ-L9aV}*?AjtK*F6%r`t{IS2;*-zS2#0NY1TUE?c8=UOxUO^m7VV5o#^X;4dqis!;` zd7QH*h@M*($3|GA-`X**d_>Zk6=14NM}*>G?g*xXvUry#$RfY2;;V;A*~F0P5XJhM z-Z`dbL&Y*(TWrK^7J&{h!o*m(v=0lFXM@9Y~HpA;YJY4_~rVPf@pm0wm}TV0YB zmGP#!uDT#K%<0)xqV{R%8I?>)hMEf+2PM%{AYxGcc*oQ`F~7RG8x!|9Pn*CBdY?GQm2^$aV*($VSmgSLSd9HW zCi2!fF|^Z5Fv~S^-J_x+)q|VRa}uNDt0tD)vsmYdi3zX~CiW04#r{0-CO#k_B=&WR zuQ8k8+Y2%aYKFz)*^aW@oPzS^l1OVvwYJYodOCW=ldWx%+2Nk)|l{1X~Lf# zZZ0UO?+5gMdaMWbd;G*-Uz!ya@#+nh#`P8Xh`9(0faK@^Cp+)x>AJS~IMJmwf@RwzL`43 zb&BfRIy;-Ia#9j}Ot^3kKk$@B+fmd&7;Q+CgI-JN@A=nK%KPjvD>{x43>!>B0-dF> zFP7ss)8ew0ZRuRXCD4{vlH*e?Y2JEty^v%9Yc11&e9u36;q`JG|HvWh%D*4?sDoq* zr{w*17Eh%K6Xx?4x_9{VIZqFDR)oDj!L(4aUU5NbbK06^_tn+wZnf15@29I*-WW+%VvQfJ zUVGnOvv=ElMaw=oDW{^gwjw7v&{p%pX>iJ<*K~?dQcJ4BmZ~?5z&kUtB<|}QUxrA$ z$1_#i$ddkdy;b=I)hOV~4KjP;mRvgtHl&bHgw9YB3m^^Ds>WxKtYn<4e1(pf z;!yO1%JO3{P$}#CP9Zf`4*V;~ao_b9@r5}tewNy_?HjL74fhWUPfSnr(g&wq>Cn6W zn!=p?qUu&EgEH|?uNS?Y=|w9@~dy)pd`}F3eAjQhCL`YtNiQQ}ZfOB2|Zs zw6_8AAKQ9_#G^JUDJBS|S_)eaURBqzaCU$3+{GUBwC52HCAw5vj{jfl6&IB@|9SmS z7u5AF9Nav-eY`zgQ4)3Q#P^B{C)6yxLt|6YGEhdB2(<-6mD9UE0n_-)f{OYUl3A!O zNQ?0>eQ;_AL*0$dEWUmBb`e!kMG0QUcYa|;Y;w9gI{PPT7@TWU)3b277v zYC5N9!4GA5_X4VK(-I?t{C#}GlZrYQ#8p`3pH(q(@o=#*HPkn-_Q2+Pz6HBTRWo-l zX#Uk!n%e7`E#)DtKO}>GIKHiZY$+BGgjdDu#B!BB6EkUj^x_W%foF zp`?Jdrui}W1UOAw7Qlp#xU#P=y*sF}Egrih)<`5ZiSCxhHlB&x=hVAz6In96*pZ(Q z5*U$E4CS~g2zG-VIkRy>(pFWFS5(_IGuxIE3O(~DjuClnGYjJW-T}$msg5_XuFo$2 zd_xzCj!k_Na|^TL?)sv5Ph;i7Umv||>=^^H>&E7Ws{GVQ4-?f>JJx-BO4ZoGJ3Q&- ztCWNg4?6>u)6i4?Wap_{kMylwz(Ve9sjGJ5#P^?&qzJce+eDSAOH`S9=$g8&8H(VX zq5iIZ=^dPRGZWoqH^CyVLQzJ=HL+g1eI3^@hlVjZtB%^VS zAVzcJ!-Iq4ixOyO*r}1BrK$w%BOTKdsC0bB&5;qIVTnZu6z2w-K644-hNXzt&GR5} zv$qJz;RmLYY6(3O%x}WbG%`u(e?m9B<(5>U89IX)RCp1fx@iJ=AijW@LIK8Ggphkc z3s3Qc>G6ib%z`@66qVYk*+&<5&yV+Z^p4MU7ezTfx@qW{T-q@*yD&T4R+8vpaPx$! zg->Emc|&tUSysH4nd~D|!&By;~9~XAKAX$4yBx|%s zUGsFvNJUj!wycg~ZP~&XRh?QDj5u{lT2!UE(&<+eRmpq)%AzV4dXy0XqoySv#5%TqA|^y_dK1#U+B-DtQmAZL-$AV)WKDK`NToK zcuHkuPgao?Pp#<3^2JlK0cln7R8TAxPelg&t$2!xy#8GjPjNw+O@waxmC#LV`_0-O zvu2+9YRyEo_3-6YGu4*c+SY8U%1CF+8};+M^jtz-=2bR8XQiPcKhekH!7j znWd#=Xi=0Vd+1&P)#BKaxVOEvV_$I|+|t6Y_r$l?9_m@x(T>s0-dz9D^<&?D z%+O7=JlVM8Kde*ygVN@ub*mCMt0uiwqXUz0kgaJit$fy+R_?MPz*^NZFgfc@MGY|p z#Cx0HcTT8k9~4VKD9~P#^jrs&D#Mc_{ryAp^IdtNRtRFX)s+?$l(i%F8}InkH8wLP zDmXa4d~6ay);mV7!RfiV1?4TXkZrX;aUZNrJw1J+vm-TW0cMBj<=E`}qKF~`<~JWh zd8MJDsdadMs3Or#*U8%_Atfa>6QW6|wsD9|PmKxk^9=@55o&V4;L=c*pPiLgH9Fsx z=>ziUnUUVE&YrQiRMUW$L*p~x%oqb7Qh=#LWM)Yf=rS5A3SS00XzDrpMZ|$A<7I59 zr_IxAw=|8d9o?TlcXzPRSG#;*n-afO`;-XKej@@jnQ$w`*ImpXo|S2~Qbaw$*Yvhj zu9Zc#u&-joeL_%+sjV>!AGH|#!zlFE9vJ(-=7QxVU9%%~ndsDy?ffaMl~me2H&UAs zitO0c$Hr!szR9IM^TRdiAr9Kt5AHjwYU!6!*0V5F^E%jG>-xbh+s|viNGb1K7_81r zj`p#AdhGz`;)|0*{q1!nY53OXIE#3EVs>e&pMI7*J1#u)NUf--tZM8S`<24YPYz#D zw+W1iiH%RqshgNbLfjC}VQ6k=XYcepyrB0tIEU#<=b&>T7;)`#EioS9SFbExqE!{<1{3r(d0TXcwBCobo!qb_B5d0MpHTuW8%3y1IFV z0w6Ym$nxRKcke%V{8&@Z4VrG^idg$w&n#_CwI1HrbRh#eo|bcHbl?joTPJ^jB!<8m zZ5otamYo_O9rwC|bRGPSiK4Hgv8ueHd0=T6(hpC_@OE~5OguL;h%#l1duGA8b?v>w zBSYPdWoaRHn#3%bl9~Ueq&P1%#?Myk>V9Qio98}3;SpiM9uB6Dt{mKa^x6Y0eN#&- z3sW8S+h_M~QefbIPL3pPgW{jSNV7rV^Mkkbh*zC8M}s@RY+ASNz_}+bk>qOBqTRGF z9RA_Nt%nAFSwiwEH_-U;z4N5Gj$%E|>wuVouRdau>)CvTMOPjpT{dNe3&4P}Rtvi2 zSO*GE%{IRRp487TuC$-g_>zr^X$=f)!*bi_{*Jh11)G+MFI4|#N91ifZt|kp(dE1}g_2Nn` zf>cTGClNuy&giz(#_3Ih@?GuZeMZDy5@XNBvcsLVF73U^c-UjTjiIiN`Ip2Rx1D4( z><%VssP@`|nD(a-=nGq~_-Ccf|7qQtp0Ry(w>W-nWuLeXuzwEi=#tkTZ-z_4u`)oI$eG1)TwT!$@V) z3-Vr)M`j?q>Wlz$2Ec?gGQ-!!hdF98hDpdGGiaTo^|KpySk1n<27L5OJczH5_6Otd z-x1^Q2e;pA7M>g7x2qN+Cz;=_+@kP)E4MwNIhONV@Q-n^l4Lo*O^pik@D7iBwZd;- zSn3Ax_PcKq7PPI3m0EZ0L^jhG`x3EcynAG{+| z-$-8J$aW-M@i#|ij?6I^eX#?Tbbn8kbbl$3w#Jtk(bw9IB*om;ZYIgP8rgGh4-lN+ z9J#5@@IC37w5f!bU)uNimj`d@I)$d>mN&O`v^A7uCiq!HH0GyEPn{yN>N-c~mftN+ z54Dx224WUDu4Wya)hK#9J=E3FJvz70UzLLS2DM->!AmjNSdyKYQ{FQ67BiE>!`sNB zcE1~~N{jaQ35?5b1XIQ<4;>^T%0`yjvV!c5^~{_j3;W+Ar zLXA7do;aD`4VOmQs{ZmFs*{kFoJL_Jr|e1j!M$G`VH^=eGEX@xS{|F+_zBWaG%2!$ z15pPM)#B}aUmk@DP31V93w4opFvJ0ve)Pw)9Pvh7vMUpsoB}vm`_6S0LqD7{ToPqV za+FMV*V)|I$}PHh;B9AifHBkhTq4!o;J~n?yyof2dV+~+KToP0{=;Z%MSgBUb;rbf zX92*gNAE*-qjq?nGLC6sx<`sVxG!Pza}=`d+H`SzBG07X89l<(O3Ob84cmu`uqr-i@2a zex&#*0b%6)R7XLi!z22U2j2D978aDZN+z37kjx&2xF$6`IIf`U9YDn9P$B79MqRs& z*^AW1SrU~uLGBWT`+kNGQU5$nAzeyCzeTmQF1NE4GSw*VHvktY&f&UG*L$jOFofds z46?0G*o!a)&>-K)+>W<>g`t+v04*C>?y1PhDsB|dw4{6M-zK>n0Ajj2`zPmm-o!dT zhN@0jW^Es+tKl6b$zDdvP_1?hff8s#Qv<4Ny)7P`{Oa%xEn6>=<4uY7cQsWzw+m{# zS03tHlNztRF_LIMe#-cx2~zesRXY3wRXPCZb?E8?Z8IkiFCVWLbe`0EZJr0krKM-4 zr$+hxmwWFNytfOfzm?uw&%C#3h4+qBrA5JeW3!vUfhzUh_N*W~6BAP>)D_6Q7noy~?~ z3k%9xCgi-XDkUs9CST6$`U)5hfWY*e*S!W{5qRCizq;3PAgs@6F8*KsTb_&m=FNZ> zEsg#A^ky!r&F13&_22JY{I^kI&0GvE?f;FmwAU`4sElfLoyG@N<_A{={ZXTF`w4)? zD=Rb%T>K&vu!~9;>NCDpaEi_>scvjTmZ!4dWr)+W8;3RnYCJSCxBPZ_M%+^adOYo` zyIm71MkfY(I=e?^-j6k8hT5v1G!M#dttraQ$|-9c`BhYx>TPfxw2H42!vlN+V=`(+ zf9)%Zuu;8Z=2Z zimepzx#B<3TKfa06&Ln0^_D$1q_>X&kwiz9pTU&}PYi76L)d{{eG>Dx`_rd3?yk1r zShscy%kAJMZ=2whq^OX;EFFd_jo z>M4og-cDv3m!S@RN87~O*%b&U7i)bD<#T(9I+wJxX+ix3<$nI-zclxAV`c6~$I(YV z_hT2HUQ|(w)K6w273Sc3^oysaC4+4huL3O}pq^}Oe0X4FdU3Ea*;5bIIxW?23QOyU zmwSN4)Afi?ONa=GPH&tU!#u5O6PCe6)rP@k=Ltr(S}q8SnK{#kUQRL2{P|yjxP9c5 zP{q;Y<7k%eGrPcK#*U9t<>uF34q+*Ap#cGr+?|~~)9NbC)^Zf1Ae_OkM=X0wJ9e9pG-Eae3c;9dkzlM%$Zc z-9NwgOS#-n-3G<~U1@Xv28G{mUK5Z0X#0T^t0RwxzCU^Uk><+iqsF~+Ofg430_o;r zrp}4x$fA(QAo-Xu-JAw4B8|TYZUGo=irRtM(aJ;?l}2&YY=3d2jTVHgM;AMCf-MaU zP<0@lX+je7o_Sz)+q|eM$^DsWAo2i1r7@0=Edf+sI3`CiKK__m0j3I>^P(082x78 zeGdxDiy~~*j(??~xZ}XFn@lErd}O964P(Ua-zn@ob^GC?Ms!=zIxux+M1mci ztxUaNAx+zo>1TWk!p{mQKa5Sl^02T6T$r)WnrC+))}UgILdBx|lD4Vo=1hN6<)5kC zQ-yFggMC9Y^WDYKPDp=IRDv04!`o4s`ff;BEHc1Sv5qC|m_Y zTmpPEF4Wyj?d&d2uleX*J#3v`xH(zq++#anIuqE=Ehv@v6dS0B4T}GV(&qoXeodfy z)0#;2cgL@<>?}7) zmF~UJS}S3B`!m}=-p$>+9~$033{y2OR$Vs+&(}g=CJe+4|mmOh1xx4J?j5!@5-X0 zy0U1sl3t}jyPMcC(6&jOqH&0&n23r%K*a$BL9q}Jkg+t%=ult@${?VC2ntw1B7>j_ zU_b<^0D>rBpimTBptCyt(I36~qvv_r=ic|K-Ye3*T&vfL{o(z~EL}PMio%mr0U#upASb8H+ZAB$qTB40SgQhQ`7KUwdGWJ%qo{)L_@K zx?>zijIldb(uBsBB*GESfP>5%#kw$ILUXvVuJ3+B?Oja#)6CdY(%Rlo+SuMyCc~bO z%Zg9y%Pv`9k1i?ZxNai$_y)ej9yHnu#~x1mKb;PIf5#4dCv>ifJxej&vRl1A*RoHk zT$tv(xkI^Q(j)Cn`}w5D)RiP?#QTvPjnIi+qS1>-qc?y?-@~K_QK`Rw-l)_!EhalF z<~dlzE_*sbc~mk~B_!-uGwwd|)EArY83zUKJ{qdBKno^O9;Mk0r}_LJnfkQwv744pScG zC;g0b?+=6sk4Q#;_oh`L~H%ss5mY&)z{2JnQddP z+gVv5Elu(DX_Lil@LBs2i)lS&CidJt_2ZbGi{ro^ZgZUpbho#gp7*x1Ae$YtV zW%up;@Ll}2LM`RcF6FTyw1GiiU9Y5lTpm~8cT## zdJ09s*B8W747k3tBAhWKSKbMEq%dGBh>+C1IRui>v zkB_A_JL~jGZCtqg-L74$t@)%RKlPC0GmN8q_4RhPe5%ioSic$^92^>c_O!S0lEgZC zyRNRGv90$j?Cr_EMQ;B2GAP(I7t5^Ln1s{SJy5ogTO&V${x|lHW!8+0GZ$|RVoOVE zt*(afY4;d*lFZhzdno;n#s`|qB%gtiELpdweWxDJdtkaT?j{%FD|ymz>Lx+B#7>S{kmLH9v0@Bg&JfPX}&a zG24bP=G?s11>OOPt)Z~6sJQn2Pgt~ugoAF zV1-F1PY3G)TO3V{js0Q_g(MxA!2o(&0~Kya%+u2|^eFrpAqo6ll2PJeU8w8fSnsv zwhHl+X6Yh5K3HsPB^qjLArrNlXcze_41It%|24FQ&kRNb%^0}Bl{uos;2fkqSFD#9 zHbVC3*5@Fur6HR3bz!K!_h_^m{_4ZVR5E^&n6pE#AG)%dQP%cc!;9Jcxe?+f1J<+y+tCoyEaiY;9VkFvJQpRD{FF&fTPk^cz= zBQc#XLU@W7$WATU#{7=wmz1YRuv1Dl8uv4HX;vRFkI3({AVOhGzMK}}u`iB~r!*?3 z#i{pd^NMJE#fjMY)JXLP@Fd;5IWR=$Wni}OR*^E&lV@W%O=GITRBvq^7-~f!Wnopw z@$)5ge@RIRsShvpia5-luRV730ND2&g!iw+*^!siq95?j*VV(?VxEmcsB`%bG}cN_ z@*l&9*jAP;W7#m4?PA$1maSshD3)!)7!4_DWU5`KmGbYH-CAu7q>Pc6;4RBnYzvLS zCD^3>;i_ zhi+PeV})1V!j?%+^_}q%1Bsx#Pp4Lse!SX;VwgvtGTSQhml^ME2~WZ2?jORv^se7m zOr7t%hf)$Aw|0!)tvwru1Pi+#Axe<461r>Z`kvgzxA1WLV96$mB^<zx9bkKfjPABSm1}A;i>w11N9x2>;b(=4@rMv$sSOeTr24__HN+s z-R{0-fu!HqzOxMUz3J8%IRQpes$6uN&g{_r7}~}hqwhRUP(U!h|?Aq1wCSc z^bcAft@RFtOGgE4hCVL4aQBGP!KX712YN=4pj>8R0QivL)fL%?g0)4sxRM#AK33e+ z-`}iHjXQJoPJb6Nsla!6#W2!aSD04TI^5rYF5$8#F|W288><&?Ja`C!^62f$+(SWv z^Z;aL-OoCza$@})XT1-eq_UPr{kNYWs^Dml+q+ZdI{L-tpdSqmHkGDCY*Wot%%J-& zG&~q@ugs3wwQkPq)(f2d6LM?1Muq1(t$fqL2kOvmPwuxo>Z>nG3J>y7ZA18P)1dKY zb>~=DO>Vk2+%4#6!Ke40eo;|a*ZcT(72Fu+k%x*brqX;07GEM1h?#In|aZ5Rqy| zfvo310Y8QUxW|M7NN8HT$W-z}>x@)nia=>ea6C%Cw zyS5DX0tGN3xp>Tf4JU6h8Xy1kXG55}P(bloX2T-x!Ph^hr5v-Z4@@q>LazBCp#Uzx z_v5e%il|uv3IL<`=j7Zo76xc#2P+t!lpJo9CR3KlXSyuMp06xtE z3cQ71EgG||13{Ywu`kASIZ631ibVZ3J#>XfxXeQXT%7{71$xm#^_Pgk2&sUWCUuMt z4&oo0hA`o<;o;%0e>sA`wARNo=99+8#%}V^#>NI{Jg-wH=A1WqXu&CsCzy|<9tvJ3 zxrf5%cE~(*pb=3>NgLfFsYs0*7 LW<3p12tWEa8bYCv literal 0 HcmV?d00001 diff --git a/x-pack/plugins/maps/server/fonts/open_sans/256-511.pbf b/x-pack/plugins/maps/server/fonts/open_sans/256-511.pbf new file mode 100644 index 0000000000000000000000000000000000000000..6e108e53a26f8579a46c9626a5e312c95c8ed83f GIT binary patch literal 66481 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 JiG}#ce*j4AU_Af; literal 0 HcmV?d00001 diff --git a/x-pack/plugins/maps/server/fonts/open_sans/768-1023.pbf b/x-pack/plugins/maps/server/fonts/open_sans/768-1023.pbf new file mode 100644 index 0000000000000000000000000000000000000000..a3efbb9361d4d603042de3f2bdb4e261b8170763 GIT binary patch literal 33767 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 literal 0 HcmV?d00001 diff --git a/x-pack/plugins/maps/server/fonts/open_sans/license.txt b/x-pack/plugins/maps/server/fonts/open_sans/license.txt new file mode 100644 index 00000000000000..7783de532a3314 --- /dev/null +++ b/x-pack/plugins/maps/server/fonts/open_sans/license.txt @@ -0,0 +1,53 @@ +Apache License + +Version 2.0, January 2004 + +http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. + +"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: + +You must give any other recipients of the Work or Derivative Works a copy of this License; and +You must cause any modified files to carry prominent notices stating that You changed the files; and +You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and +If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. + +You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. +5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS diff --git a/x-pack/plugins/maps/server/routes.js b/x-pack/plugins/maps/server/routes.js index 63895ea8b98222..ad66712eb3ad6b 100644 --- a/x-pack/plugins/maps/server/routes.js +++ b/x-pack/plugins/maps/server/routes.js @@ -21,12 +21,15 @@ import { GIS_API_PATH, EMS_SPRITES_PATH, INDEX_SETTINGS_API_PATH, + FONTS_API_PATH, } from '../common/constants'; import { EMSClient } from '@elastic/ems-client'; import fetch from 'node-fetch'; import { i18n } from '@kbn/i18n'; import { getIndexPatternSettings } from './lib/get_index_pattern_settings'; import { schema } from '@kbn/config-schema'; +import fs from 'fs'; +import path from 'path'; const ROOT = `/${GIS_API_PATH}`; @@ -76,7 +79,7 @@ export function initRoutes(router, licenseUid, mapConfig, kbnVersion, logger) { }), }, }, - async (con, request, { ok, badRequest }) => { + async (context, request, { ok, badRequest }) => { if (!checkEMSProxyEnabled()) { return badRequest('map.proxyElasticMapsServiceInMaps disabled'); } @@ -108,7 +111,7 @@ export function initRoutes(router, licenseUid, mapConfig, kbnVersion, logger) { path: `${ROOT}/${EMS_TILES_API_PATH}/${EMS_TILES_RASTER_TILE_PATH}`, validate: false, }, - async (con, request, { ok, badRequest }) => { + async (context, request, { ok, badRequest }) => { if (!checkEMSProxyEnabled()) { return badRequest('map.proxyElasticMapsServiceInMaps disabled'); } @@ -144,7 +147,7 @@ export function initRoutes(router, licenseUid, mapConfig, kbnVersion, logger) { path: `${ROOT}/${EMS_CATALOGUE_PATH}`, validate: false, }, - async (con, request, { ok, badRequest }) => { + async (context, request, { ok, badRequest }) => { if (!checkEMSProxyEnabled()) { return badRequest('map.proxyElasticMapsServiceInMaps disabled'); } @@ -180,7 +183,7 @@ export function initRoutes(router, licenseUid, mapConfig, kbnVersion, logger) { path: `${ROOT}/${EMS_FILES_CATALOGUE_PATH}/{emsVersion}/manifest`, validate: false, }, - async (con, request, { ok, badRequest }) => { + async (context, request, { ok, badRequest }) => { if (!checkEMSProxyEnabled()) { return badRequest('map.proxyElasticMapsServiceInMaps disabled'); } @@ -210,7 +213,7 @@ export function initRoutes(router, licenseUid, mapConfig, kbnVersion, logger) { path: `${ROOT}/${EMS_TILES_CATALOGUE_PATH}/{emsVersion}/manifest`, validate: false, }, - async (con, request, { ok, badRequest }) => { + async (context, request, { ok, badRequest }) => { if (!checkEMSProxyEnabled()) { return badRequest('map.proxyElasticMapsServiceInMaps disabled'); } @@ -258,7 +261,7 @@ export function initRoutes(router, licenseUid, mapConfig, kbnVersion, logger) { }), }, }, - async (con, request, { ok, badRequest }) => { + async (context, request, { ok, badRequest }) => { if (!checkEMSProxyEnabled()) { return badRequest('map.proxyElasticMapsServiceInMaps disabled'); } @@ -294,7 +297,7 @@ export function initRoutes(router, licenseUid, mapConfig, kbnVersion, logger) { }), }, }, - async (con, request, { ok, badRequest }) => { + async (context, request, { ok, badRequest }) => { if (!checkEMSProxyEnabled()) { return badRequest('map.proxyElasticMapsServiceInMaps disabled'); } @@ -344,7 +347,7 @@ export function initRoutes(router, licenseUid, mapConfig, kbnVersion, logger) { }), }, }, - async (con, request, { ok, badRequest }) => { + async (context, request, { ok, badRequest }) => { if (!checkEMSProxyEnabled()) { return badRequest('map.proxyElasticMapsServiceInMaps disabled'); } @@ -386,7 +389,7 @@ export function initRoutes(router, licenseUid, mapConfig, kbnVersion, logger) { }), }, }, - async (con, request, { ok, badRequest }) => { + async (context, request, { ok, badRequest }) => { if (!checkEMSProxyEnabled()) { return badRequest('map.proxyElasticMapsServiceInMaps disabled'); } @@ -423,7 +426,7 @@ export function initRoutes(router, licenseUid, mapConfig, kbnVersion, logger) { path: `${ROOT}/${EMS_TILES_API_PATH}/${EMS_GLYPHS_PATH}/{fontstack}/{range}`, validate: false, }, - async (con, request, { ok, badRequest }) => { + async (context, request, { ok, badRequest }) => { if (!checkEMSProxyEnabled()) { return badRequest('map.proxyElasticMapsServiceInMaps disabled'); } @@ -444,7 +447,7 @@ export function initRoutes(router, licenseUid, mapConfig, kbnVersion, logger) { }), }, }, - async (con, request, { ok, badRequest }) => { + async (context, request, { ok, badRequest }) => { if (!checkEMSProxyEnabled()) { return badRequest('map.proxyElasticMapsServiceInMaps disabled'); } @@ -481,6 +484,39 @@ export function initRoutes(router, licenseUid, mapConfig, kbnVersion, logger) { } ); + router.get( + { + path: `/${FONTS_API_PATH}/{fontstack}/{range}`, + validate: { + params: schema.object({ + fontstack: schema.string(), + range: schema.string(), + }), + }, + }, + (context, request, response) => { + return new Promise((resolve, reject) => { + const santizedRange = path.normalize(request.params.range); + const fontPath = path.join(__dirname, 'fonts', 'open_sans', `${santizedRange}.pbf`); + fs.readFile(fontPath, (error, data) => { + if (error) { + reject( + response.custom({ + statusCode: 404, + }) + ); + } else { + resolve( + response.ok({ + body: data, + }) + ); + } + }); + }); + } + ); + router.get( { path: `/${INDEX_SETTINGS_API_PATH}`, @@ -490,7 +526,7 @@ export function initRoutes(router, licenseUid, mapConfig, kbnVersion, logger) { }), }, }, - async (con, request, response) => { + async (context, request, response) => { const { query } = request; if (!query.indexPatternTitle) { @@ -502,7 +538,7 @@ export function initRoutes(router, licenseUid, mapConfig, kbnVersion, logger) { } try { - const resp = await con.core.elasticsearch.legacy.client.callAsCurrentUser( + const resp = await context.core.elasticsearch.legacy.client.callAsCurrentUser( 'indices.getSettings', { index: query.indexPatternTitle, diff --git a/x-pack/test/api_integration/apis/maps/fonts_api.js b/x-pack/test/api_integration/apis/maps/fonts_api.js new file mode 100644 index 00000000000000..d367fb6a0610bc --- /dev/null +++ b/x-pack/test/api_integration/apis/maps/fonts_api.js @@ -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; + * you may not use this file except in compliance with the Elastic License. + */ + +import expect from '@kbn/expect'; + +export default function ({ getService }) { + const supertest = getService('supertest'); + + describe('fonts', () => { + it('should return fonts', async () => { + const resp = await supertest + .get(`/api/maps/fonts/Open%20Sans%20Regular,Arial%20Unicode%20MS%20Regular/0-255`) + .expect(200); + + expect(resp.body.length).to.be(74696); + }); + }); +} diff --git a/x-pack/test/api_integration/apis/maps/index.js b/x-pack/test/api_integration/apis/maps/index.js index a6267b9fd0ceaa..f9dff19229645a 100644 --- a/x-pack/test/api_integration/apis/maps/index.js +++ b/x-pack/test/api_integration/apis/maps/index.js @@ -13,6 +13,7 @@ export default function ({ loadTestFile, getService }) { }); describe('', () => { + loadTestFile(require.resolve('./fonts_api')); loadTestFile(require.resolve('./index_settings')); loadTestFile(require.resolve('./migrations')); }); From a1a1d5d2f79ff781d85e0bb7d845f1f7094527aa Mon Sep 17 00:00:00 2001 From: Chris Roberson Date: Tue, 2 Jun 2020 10:41:04 -0400 Subject: [PATCH 21/24] Ensure we query for more than 10 (#67172) --- .../lib/setup/collection/get_collection_status.js | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/monitoring/server/lib/setup/collection/get_collection_status.js b/x-pack/plugins/monitoring/server/lib/setup/collection/get_collection_status.js index fcb54e92f649cd..607503673276ba 100644 --- a/x-pack/plugins/monitoring/server/lib/setup/collection/get_collection_status.js +++ b/x-pack/plugins/monitoring/server/lib/setup/collection/get_collection_status.js @@ -18,7 +18,7 @@ import { getLivesNodes } from '../../elasticsearch/nodes/get_nodes/get_live_node const NUMBER_OF_SECONDS_AGO_TO_LOOK = 30; -const getRecentMonitoringDocuments = async (req, indexPatterns, clusterUuid, nodeUuid) => { +const getRecentMonitoringDocuments = async (req, indexPatterns, clusterUuid, nodeUuid, size) => { const start = get(req.payload, 'timeRange.min') || `now-${NUMBER_OF_SECONDS_AGO_TO_LOOK}s`; const end = get(req.payload, 'timeRange.max') || 'now'; @@ -73,6 +73,7 @@ const getRecentMonitoringDocuments = async (req, indexPatterns, clusterUuid, nod es_uuids: { terms: { field: 'node_stats.node_id', + size, }, aggs: { by_timestamp: { @@ -85,6 +86,7 @@ const getRecentMonitoringDocuments = async (req, indexPatterns, clusterUuid, nod kibana_uuids: { terms: { field: 'kibana_stats.kibana.uuid', + size, }, aggs: { by_timestamp: { @@ -97,6 +99,7 @@ const getRecentMonitoringDocuments = async (req, indexPatterns, clusterUuid, nod beats_uuids: { terms: { field: 'beats_stats.beat.uuid', + size, }, aggs: { by_timestamp: { @@ -107,11 +110,13 @@ const getRecentMonitoringDocuments = async (req, indexPatterns, clusterUuid, nod beat_type: { terms: { field: 'beats_stats.beat.type', + size, }, }, cluster_uuid: { terms: { field: 'cluster_uuid', + size, }, }, }, @@ -119,6 +124,7 @@ const getRecentMonitoringDocuments = async (req, indexPatterns, clusterUuid, nod logstash_uuids: { terms: { field: 'logstash_stats.logstash.uuid', + size, }, aggs: { by_timestamp: { @@ -129,6 +135,7 @@ const getRecentMonitoringDocuments = async (req, indexPatterns, clusterUuid, nod cluster_uuid: { terms: { field: 'cluster_uuid', + size, }, }, }, @@ -348,6 +355,7 @@ export const getCollectionStatus = async ( ) => { const config = req.server.config(); const kibanaUuid = config.get('server.uuid'); + const size = config.get('monitoring.ui.max_bucket_size'); const hasPermissions = await hasNecessaryPermissions(req); if (!hasPermissions) { @@ -369,7 +377,7 @@ export const getCollectionStatus = async ( ]; const [recentDocuments, detectedProducts] = await Promise.all([ - await getRecentMonitoringDocuments(req, indexPatterns, clusterUuid, nodeUuid), + await getRecentMonitoringDocuments(req, indexPatterns, clusterUuid, nodeUuid, size), await detectProducts(req, isLiveCluster), ]); From 52c518a6aed5a46ed2d25152a70833898c134e67 Mon Sep 17 00:00:00 2001 From: Andrew Goldstein Date: Tue, 2 Jun 2020 08:57:11 -0600 Subject: [PATCH 22/24] [SIEM] Fixes column drag and drop in timeline-based views (#67799) ## Summary Fixes a bug in timeline-based views, (e.g. the Host Events table), where a column would revert back to it's original position after being dragged and dropped to a new position. Only timeline-based views were effected, not the timeline itself. To reproduce: 1) On the SIEM Overview page, click the `View events` button 2) Drag and drop any column in the Events table to a new position **Expected Result** - The column is relocated to the position where it was dropped **Actual Result** - The column reverts to it's original position ## Testing - This PR adds a Cypress test for this scenario - The new test was successfully run (at least) 10 times via `node x-pack/plugins/siem/scripts/loop_cypress_tests.js` - This fix was desk tested in: - Chrome `83.0.4103.61` - Firefox `76.0.1` - Safari `13.1` --- .../cypress/integration/events_viewer.spec.ts | 26 +++++++++++++++++++ .../plugins/siem/cypress/screens/timeline.ts | 5 ++++ .../siem/cypress/tasks/hosts/events.ts | 23 ++++++++++++++++ .../drag_drop_context_wrapper.tsx | 3 ++- .../components/drag_and_drop/helpers.test.ts | 13 ++++++++++ .../components/drag_and_drop/helpers.ts | 3 +++ 6 files changed, 72 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/siem/cypress/integration/events_viewer.spec.ts b/x-pack/plugins/siem/cypress/integration/events_viewer.spec.ts index 26ebaeb8448250..82b4f4f0fbe342 100644 --- a/x-pack/plugins/siem/cypress/integration/events_viewer.spec.ts +++ b/x-pack/plugins/siem/cypress/integration/events_viewer.spec.ts @@ -17,6 +17,7 @@ import { LOAD_MORE, LOCAL_EVENTS_COUNT, } from '../screens/hosts/events'; +import { HEADERS_GROUP } from '../screens/timeline'; import { closeFieldsBrowser, filterFieldsBrowser } from '../tasks/fields_browser'; import { loginAndWaitForPage } from '../tasks/login'; @@ -25,6 +26,7 @@ import { addsHostGeoCityNameToHeader, addsHostGeoCountryNameToHeader, closeModal, + dragAndDropColumn, openEventsViewerFieldsBrowser, opensInspectQueryModal, resetFields, @@ -150,4 +152,28 @@ describe('Events Viewer', () => { cy.get(LOCAL_EVENTS_COUNT).invoke('text').should('not.equal', defaultNumberOfLoadedEvents); }); }); + + context.skip('Events columns', () => { + before(() => { + loginAndWaitForPage(HOSTS_PAGE); + openEvents(); + waitsForEventsToBeLoaded(); + }); + + afterEach(() => { + openEventsViewerFieldsBrowser(); + resetFields(); + }); + + it('re-orders columns via drag and drop', () => { + const originalColumnOrder = + '@timestampmessagehost.nameevent.moduleevent.datasetevent.actionuser.namesource.ipdestination.ip'; + const expectedOrderAfterDragAndDrop = + 'message@timestamphost.nameevent.moduleevent.datasetevent.actionuser.namesource.ipdestination.ip'; + + cy.get(HEADERS_GROUP).invoke('text').should('equal', originalColumnOrder); + dragAndDropColumn({ column: 0, newPosition: 1 }); + cy.get(HEADERS_GROUP).invoke('text').should('equal', expectedOrderAfterDragAndDrop); + }); + }); }); diff --git a/x-pack/plugins/siem/cypress/screens/timeline.ts b/x-pack/plugins/siem/cypress/screens/timeline.ts index ed1dc97454fb35..bb232b752994ae 100644 --- a/x-pack/plugins/siem/cypress/screens/timeline.ts +++ b/x-pack/plugins/siem/cypress/screens/timeline.ts @@ -8,6 +8,11 @@ export const CLOSE_TIMELINE_BTN = '[data-test-subj="close-timeline"]'; export const CREATE_NEW_TIMELINE = '[data-test-subj="timeline-new"]'; +export const DRAGGABLE_HEADER = + '[data-test-subj="headers-group"] [data-test-subj="draggable-header"]'; + +export const HEADERS_GROUP = '[data-test-subj="headers-group"]'; + export const ID_HEADER_FIELD = '[data-test-subj="timeline"] [data-test-subj="header-text-_id"]'; export const ID_FIELD = '[data-test-subj="timeline"] [data-test-subj="field-name-_id"]'; diff --git a/x-pack/plugins/siem/cypress/tasks/hosts/events.ts b/x-pack/plugins/siem/cypress/tasks/hosts/events.ts index dff58b4b0e9eae..a5936509892596 100644 --- a/x-pack/plugins/siem/cypress/tasks/hosts/events.ts +++ b/x-pack/plugins/siem/cypress/tasks/hosts/events.ts @@ -4,6 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ +import { drag, drop } from '../common'; import { CLOSE_MODAL, EVENTS_VIEWER_FIELDS_BUTTON, @@ -15,6 +16,7 @@ import { RESET_FIELDS, SERVER_SIDE_EVENT_COUNT, } from '../../screens/hosts/events'; +import { DRAGGABLE_HEADER } from '../../screens/timeline'; export const addsHostGeoCityNameToHeader = () => { cy.get(HOST_GEO_CITY_NAME_CHECKBOX).check({ @@ -58,3 +60,24 @@ export const resetFields = () => { export const waitsForEventsToBeLoaded = () => { cy.get(SERVER_SIDE_EVENT_COUNT).should('exist').invoke('text').should('not.equal', '0'); }; + +export const dragAndDropColumn = ({ + column, + newPosition, +}: { + column: number; + newPosition: number; +}) => { + cy.get(DRAGGABLE_HEADER).first().should('exist'); + cy.get(DRAGGABLE_HEADER) + .eq(column) + .then((header) => drag(header)); + + cy.wait(3000); // wait for DOM updates before moving + + cy.get(DRAGGABLE_HEADER) + .eq(newPosition) + .then((targetPosition) => { + drop(targetPosition); + }); +}; diff --git a/x-pack/plugins/siem/public/common/components/drag_and_drop/drag_drop_context_wrapper.tsx b/x-pack/plugins/siem/public/common/components/drag_and_drop/drag_drop_context_wrapper.tsx index 3bd2a3da1c88b3..c71cd8e60596ce 100644 --- a/x-pack/plugins/siem/public/common/components/drag_and_drop/drag_drop_context_wrapper.tsx +++ b/x-pack/plugins/siem/public/common/components/drag_and_drop/drag_drop_context_wrapper.tsx @@ -27,6 +27,7 @@ import { addFieldToTimelineColumns, addProviderToTimeline, fieldWasDroppedOnTimelineColumns, + getTimelineIdFromColumnDroppableId, IS_DRAGGING_CLASS_NAME, IS_TIMELINE_FIELD_DRAGGING_CLASS_NAME, providerWasDroppedOnTimeline, @@ -82,7 +83,7 @@ const onDragEndHandler = ({ browserFields, dispatch, result, - timelineId: ACTIVE_TIMELINE_REDUX_ID, + timelineId: getTimelineIdFromColumnDroppableId(result.destination?.droppableId ?? ''), }); } }; diff --git a/x-pack/plugins/siem/public/common/components/drag_and_drop/helpers.test.ts b/x-pack/plugins/siem/public/common/components/drag_and_drop/helpers.test.ts index 69fbedb6462cbc..be58381fbca1b6 100644 --- a/x-pack/plugins/siem/public/common/components/drag_and_drop/helpers.test.ts +++ b/x-pack/plugins/siem/public/common/components/drag_and_drop/helpers.test.ts @@ -32,6 +32,7 @@ import { getDroppableId, getFieldIdFromDraggable, getProviderIdFromDraggable, + getTimelineIdFromColumnDroppableId, getTimelineProviderDraggableId, getTimelineProviderDroppableId, providerWasDroppedOnTimeline, @@ -984,4 +985,16 @@ describe('helpers', () => { }); }); }); + + describe('getTimelineIdFromColumnDroppableId', () => { + test('it returns the expected timelineId from a column droppableId', () => { + expect(getTimelineIdFromColumnDroppableId(DROPPABLE_ID_TIMELINE_COLUMNS)).toEqual( + 'timeline-1' + ); + }); + + test('it returns an empty string when the droppableId is an empty string', () => { + expect(getTimelineIdFromColumnDroppableId('')).toEqual(''); + }); + }); }); diff --git a/x-pack/plugins/siem/public/common/components/drag_and_drop/helpers.ts b/x-pack/plugins/siem/public/common/components/drag_and_drop/helpers.ts index ad370f647738f3..0037fc3cae628a 100644 --- a/x-pack/plugins/siem/public/common/components/drag_and_drop/helpers.ts +++ b/x-pack/plugins/siem/public/common/components/drag_and_drop/helpers.ts @@ -332,3 +332,6 @@ export const allowTopN = ({ return isWhitelistedNonBrowserField || (isAggregatable && isAllowedType); }; + +export const getTimelineIdFromColumnDroppableId = (droppableId: string) => + droppableId.slice(droppableId.lastIndexOf('.') + 1); From ce7940adc28ba53627bfa401a97b8bf4af0aa901 Mon Sep 17 00:00:00 2001 From: Corey Robertson Date: Tue, 2 Jun 2020 11:31:30 -0400 Subject: [PATCH 23/24] Allow functions to return falsy values (#67796) Co-authored-by: Elastic Machine --- .../create_streaming_batched_function.test.ts | 41 +++++++++++++++++++ .../create_streaming_batched_function.ts | 2 +- 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/src/plugins/bfetch/public/batching/create_streaming_batched_function.test.ts b/src/plugins/bfetch/public/batching/create_streaming_batched_function.test.ts index 26c1d9e5033e01..da6c940c48d0ae 100644 --- a/src/plugins/bfetch/public/batching/create_streaming_batched_function.test.ts +++ b/src/plugins/bfetch/public/batching/create_streaming_batched_function.test.ts @@ -303,6 +303,47 @@ describe('createStreamingBatchedFunction()', () => { expect(await promise3).toEqual({ foo: 'bar 2' }); }); + test('resolves falsy results', async () => { + const { fetchStreaming, stream } = setup(); + const fn = createStreamingBatchedFunction({ + url: '/test', + fetchStreaming, + maxItemAge: 5, + flushOnMaxItems: 3, + }); + + const promise1 = fn({ a: '1' }); + const promise2 = fn({ b: '2' }); + const promise3 = fn({ c: '3' }); + await new Promise((r) => setTimeout(r, 6)); + + stream.next( + JSON.stringify({ + id: 0, + result: false, + }) + '\n' + ); + stream.next( + JSON.stringify({ + id: 1, + result: 0, + }) + '\n' + ); + stream.next( + JSON.stringify({ + id: 2, + result: '', + }) + '\n' + ); + + expect(await isPending(promise1)).toBe(false); + expect(await isPending(promise2)).toBe(false); + expect(await isPending(promise3)).toBe(false); + expect(await promise1).toEqual(false); + expect(await promise2).toEqual(0); + expect(await promise3).toEqual(''); + }); + test('rejects promise on error response', async () => { const { fetchStreaming, stream } = setup(); const fn = createStreamingBatchedFunction({ diff --git a/src/plugins/bfetch/public/batching/create_streaming_batched_function.ts b/src/plugins/bfetch/public/batching/create_streaming_batched_function.ts index f80a97137d1ab2..89793fff6b3259 100644 --- a/src/plugins/bfetch/public/batching/create_streaming_batched_function.ts +++ b/src/plugins/bfetch/public/batching/create_streaming_batched_function.ts @@ -106,7 +106,7 @@ export const createStreamingBatchedFunction = ( if (response.error) { responsesReceived++; items[response.id].future.reject(response.error); - } else if (response.result) { + } else if (response.result !== undefined) { responsesReceived++; items[response.id].future.resolve(response.result); } From 77e7e0bb49f38cd008e96d1e1c808e21f0732c2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez?= Date: Tue, 2 Jun 2020 17:36:47 +0200 Subject: [PATCH 24/24] [Logs UI] Ensure live stream always gets latest entries (#67935) LogPositionState doesn't always reevaluate the value of `endTimestamp` when live stream is on. The [dependencies][1] for it to change rely on the scroll position to update. If there's less than one scroll page or the previous API call didn't return any entries, the `endTimestamp` would not update. We force `Date.now()` as an `endTimestamp` on the API call to ensure it always gets the latest entries possible, regardless of the state. This introduces some inconsistency that will be fixed once work beings on #65493. [1]: https://github.com/elastic/kibana/blob/fe4c164681e92ef5bf0c28f7ab3dfe00a5aacd6f/x-pack/plugins/infra/public/containers/logs/log_position/log_position_state.ts#L160-L173 --- .../containers/logs/log_entries/index.ts | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/x-pack/plugins/infra/public/containers/logs/log_entries/index.ts b/x-pack/plugins/infra/public/containers/logs/log_entries/index.ts index a6d66d47975c00..5fe9a45a7ceede 100644 --- a/x-pack/plugins/infra/public/containers/logs/log_entries/index.ts +++ b/x-pack/plugins/infra/public/containers/logs/log_entries/index.ts @@ -194,7 +194,10 @@ const useFetchEntriesEffect = ( } }; - const runFetchMoreEntriesRequest = async (direction: ShouldFetchMoreEntries) => { + const runFetchMoreEntriesRequest = async ( + direction: ShouldFetchMoreEntries, + overrides: Partial = {} + ) => { if (!props.startTimestamp || !props.endTimestamp) { return; } @@ -209,10 +212,10 @@ const useFetchEntriesEffect = ( try { const commonFetchArgs: LogEntriesBaseRequest = { - sourceId: props.sourceId, - startTimestamp: props.startTimestamp, - endTimestamp: props.endTimestamp, - query: props.filterQuery, + sourceId: overrides.sourceId || props.sourceId, + startTimestamp: overrides.startTimestamp || props.startTimestamp, + endTimestamp: overrides.endTimestamp || props.endTimestamp, + query: overrides.filterQuery || props.filterQuery, }; const fetchArgs: LogEntriesRequest = getEntriesBefore @@ -279,10 +282,10 @@ const useFetchEntriesEffect = ( const streamEntriesEffect = () => { (async () => { if (props.isStreaming && !state.isLoadingMore && !state.isReloading) { + const endTimestamp = Date.now(); if (startedStreaming) { await new Promise((res) => setTimeout(res, LIVE_STREAM_INTERVAL)); } else { - const endTimestamp = Date.now(); props.jumpToTargetPosition({ tiebreaker: 0, time: endTimestamp }); setStartedStreaming(true); if (state.hasMoreAfterEnd) { @@ -290,7 +293,9 @@ const useFetchEntriesEffect = ( return; } } - const newEntriesEnd = await runFetchMoreEntriesRequest(ShouldFetchMoreEntries.After); + const newEntriesEnd = await runFetchMoreEntriesRequest(ShouldFetchMoreEntries.After, { + endTimestamp, + }); if (newEntriesEnd) { props.jumpToTargetPosition(newEntriesEnd); }